clear-pkgs-linux-iot-lts2018/0201-SDW-Support-async-mess...

212 lines
7.3 KiB
Diff
Raw Normal View History

From 77f6b97b56921f08be31465abfb02ce9956deed2 Mon Sep 17 00:00:00 2001
From: Hardik Shah <hardik.t.shah@intel.com>
Date: Fri, 29 Apr 2016 14:22:16 +0530
Subject: [PATCH 029/296] SDW: Support async messages for bus driver.
All of the messages to the master controller for read/write
register are synchronous for calling function. Master controller
completes the operation and return back to calling function
with result.
But when audio stream is split between two masters (aggregation)
bus driver needs to send the message asynchronously to master
controller part of aggregation. Bus driver waits for result
outide master controller context, instead of master controller waiting.
Change-Id: Icf5d44c372bca742da21408f074dbf90080a3ea9
Signed-off-by: Hardik Shah <hardik.t.shah@intel.com>
Reviewed-on:
---
drivers/sdw/sdw.c | 51 +++++++++++++++++++++++++++++++++++------
drivers/sdw/sdw_priv.h | 7 ++++++
include/linux/sdw_bus.h | 26 +++++++++++++++++++++
3 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c
index 71f1532657da..5f4aea04a85b 100644
--- a/drivers/sdw/sdw.c
+++ b/drivers/sdw/sdw.c
@@ -419,13 +419,12 @@ void sdw_lock_mstr(struct sdw_master *mstr)
{
rt_mutex_lock(&mstr->bus_lock);
}
-EXPORT_SYMBOL_GPL(sdw_lock_mstr);
/**
* sdw_trylock_mstr - Try to get exclusive access to an SDW bus segment
* @mstr: Target SDW bus segment
*/
-static int sdw_trylock_mstr(struct sdw_master *mstr)
+int sdw_trylock_mstr(struct sdw_master *mstr)
{
return rt_mutex_trylock(&mstr->bus_lock);
}
@@ -439,7 +438,6 @@ void sdw_unlock_mstr(struct sdw_master *mstr)
{
rt_mutex_unlock(&mstr->bus_lock);
}
-EXPORT_SYMBOL_GPL(sdw_unlock_mstr);
static int sdw_assign_slv_number(struct sdw_master *mstr,
@@ -663,7 +661,8 @@ static int sdw_register_slave(struct sdw_master *mstr)
* Adapter lock must be held when calling this function. No debug logging
* takes place. mstr->algo->master_xfer existence isn't checked.
*/
-int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num)
+int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num,
+ struct sdw_async_xfer_data *async_data)
{
unsigned long orig_jiffies;
int ret = 0, try, i;
@@ -707,8 +706,24 @@ int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num)
program_scp_addr_page =
slv_cap->paging_supported;
}
- ret = mstr->driver->mstr_ops->xfer_msg(mstr,
+ /* Call async or sync handler based on call */
+ if (!async_data)
+ ret = mstr->driver->mstr_ops->xfer_msg(mstr,
msg, program_scp_addr_page);
+ /* Async transfer is not mandatory to support
+ * It requires only if stream is split across the
+ * masters, where bus driver need to send the commands
+ * for bank switch individually and wait for them
+ * to complete out side of the master context
+ */
+ else if (mstr->driver->mstr_ops->xfer_msg_async &&
+ async_data)
+ ret = mstr->driver->mstr_ops->xfer_msg_async(
+ mstr, msg,
+ program_scp_addr_page,
+ async_data);
+ else
+ return -ENOTSUPP;
if (ret != -EAGAIN)
break;
if (time_after(jiffies,
@@ -740,13 +755,34 @@ static int sdw_slave_transfer_nopm(struct sdw_master *mstr, struct sdw_msg *msg,
int ret;
if (mstr->driver->mstr_ops->xfer_msg) {
- ret = __sdw_transfer(mstr, msg, num);
+ ret = __sdw_transfer(mstr, msg, num, NULL);
return ret;
}
dev_dbg(&mstr->dev, "SDW level transfers not supported\n");
return -EOPNOTSUPP;
}
+int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg,
+ int num,
+ struct sdw_async_xfer_data *async_data)
+{
+ int ret;
+ /* Currently we support only message asynchronously, This is mainly
+ * used to do bank switch for multiple controllers
+ */
+ if (num != 1)
+ return -EINVAL;
+ if (!(mstr->driver->mstr_ops->xfer_msg)) {
+ dev_dbg(&mstr->dev, "SDW level transfers not supported\n");
+ return -EOPNOTSUPP;
+ }
+ pm_runtime_get_sync(&mstr->dev);
+ ret = __sdw_transfer(mstr, msg, num, async_data);
+ pm_runtime_mark_last_busy(&mstr->dev);
+ pm_runtime_put_sync_autosuspend(&mstr->dev);
+ return ret;
+}
+
/**
* sdw_slave_transfer: Transfer message between slave and mstr on the bus.
* @mstr: mstr master which will transfer the message
@@ -789,7 +825,7 @@ int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num)
} else {
sdw_lock_mstr(mstr);
}
- ret = __sdw_transfer(mstr, msg, num);
+ ret = __sdw_transfer(mstr, msg, num, NULL);
sdw_unlock_mstr(mstr);
out:
pm_runtime_mark_last_busy(&mstr->dev);
@@ -1125,6 +1161,7 @@ static int sdw_register_master(struct sdw_master *mstr)
if (!sdw_bus)
goto bus_alloc_failed;
sdw_bus->mstr = mstr;
+ init_completion(&sdw_bus->async_data.xfer_complete);
mutex_lock(&sdw_core.core_lock);
list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list);
diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h
index 5ec3edc30d27..42e948440481 100644
--- a/drivers/sdw/sdw_priv.h
+++ b/drivers/sdw/sdw_priv.h
@@ -196,6 +196,7 @@ struct sdw_bus {
struct kthread_work kwork;
struct list_head status_list;
spinlock_t spinlock;
+ struct sdw_async_xfer_data async_data;
};
/** Holds supported Row-Column combination related information */
@@ -239,5 +240,11 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs);
int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable);
int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare);
int sdw_chn_enable(void);
+void sdw_unlock_mstr(struct sdw_master *mstr);
+int sdw_trylock_mstr(struct sdw_master *mstr);
+void sdw_lock_mstr(struct sdw_master *mstr);
+int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg,
+ int num,
+ struct sdw_async_xfer_data *async_data);
#endif /* _LINUX_SDW_PRIV_H */
diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h
index 01c846b0c695..d16579b35f8a 100644
--- a/include/linux/sdw_bus.h
+++ b/include/linux/sdw_bus.h
@@ -337,6 +337,29 @@ struct sdw_slv_bra_capabilities {
unsigned int mode_block_alignment;
};
+/**
+ * struct sdw_async_xfer_data: Data to be provided by bus driver to
+ * to master controller, in case bus driver
+ * driver doesnt want to call synchronous
+ * xfer message API. This is used by bus
+ * driver during aggregation, where it calls
+ * the bank switch of multiple master
+ * if the stream is split between two master.
+ * In this case bus driver will wait outside
+ * master controller context for bank switch
+ * to happen.
+ * @result: Result of the asynchronous transfer.
+ * @xfer_complete Bus driver will wait on this. Master controller
+ * needs to ack on this for transfer complete.
+ * @msg Message to be transferred.
+ */
+
+struct sdw_async_xfer_data {
+ int result;
+ struct completion xfer_complete;
+ struct sdw_msg *msg;
+};
+
/**
* struct sdw_slv_dp0_capabilities: Capabilities of the Data Port 0 of Slave.
*
@@ -831,6 +854,9 @@ struct sdw_master_port_ops {
struct sdw_master_ops {
enum sdw_command_response (*xfer_msg)(struct sdw_master *mstr,
struct sdw_msg *msg, bool program_scp_addr_page);
+ enum sdw_command_response (*xfer_msg_async)(struct sdw_master *mstr,
+ struct sdw_msg *msg, bool program_scp_addr_page,
+ struct sdw_async_xfer_data *data);
int (*xfer_bulk)(struct sdw_master *mstr,
struct sdw_bra_block *block);
int (*monitor_handover)(struct sdw_master *mstr,
--
2.19.1