2018-12-27 17:53:01 +08:00
|
|
|
From 77f6b97b56921f08be31465abfb02ce9956deed2 Mon Sep 17 00:00:00 2001
|
2018-10-16 02:05:43 +08:00
|
|
|
From: Hardik Shah <hardik.t.shah@intel.com>
|
|
|
|
Date: Fri, 29 Apr 2016 14:22:16 +0530
|
2018-12-27 17:53:01 +08:00
|
|
|
Subject: [PATCH 029/296] SDW: Support async messages for bus driver.
|
2018-10-16 02:05:43 +08:00
|
|
|
|
|
|
|
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,
|
|
|
|
--
|
2018-12-27 17:53:01 +08:00
|
|
|
2.19.1
|
2018-10-16 02:05:43 +08:00
|
|
|
|