mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 15:41:58 +00:00
soundwire updates for 4.20-rc1
- support for multi-link streaming - updates in intel driver for multi-link streaming - Update Vinod's email - Fix rst formatting -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJbs33VAAoJEHwUBw8lI4NHpoAP/jgDfhuOBUjgAOVmVTDeiQD8 pY29nPTakD1Ej8y+2fNzRjW67R0XsQErTAcaZ62qNgVVidtWlaD/Kfra06hEF6/e zp2eqG25zwoFfgTb/JxVYLBys+tRMl7W2fIEnTuUsXzx+m+BaSuR0mi+NhKFJbzg /ZBEtq+9QUwMgSpqvXbvo3i/tuHhnHW9+JQ3xufrwkN22XivkNkAdAVILdhBRrBm S0R/ZW445QlCeEFY0ExAcWO64+Xp1v+bkXRP7wtt8VvGKdi942TRNO/6OJX1k5PA JZ1Nzrw7nXZ4NbYoev6gLg4x9DKL6YBXO0YCU7SgtbQEH72IkQ8grMm0LwzEIiZX JAi/qF4+2M/DBAM7xneFPpvis8Te1JQwLsLa34VqMjBC97zL7rHdT2gTq28UQ1Y8 A91Y0isMna5EoxxtfTGuTWOsS999YRmzLjKYX35RR+vQhc5PWe4ZKkd26LBz5ItT NyeISlx/2ZtCaOidLKIrazL9BsJzWLw2S43SBkbksfUBgAL2T26tEdUtcE44zGy1 fV3OwVXeImTpM3OcPfNaIz7k4ByR2MpW880ua5XZ9vUyjjc2JuToeEhXMW0nCWO5 CjUWXY/KoNaoWRjsRuCITrN99HzE/nvUIqweRaKFxhq3gQNg5Th9tQfgCG+wv1f1 lSpuZoZqQKa44QSMyYvp =YbE0 -----END PGP SIGNATURE----- Merge tag 'soundwire-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next Vinod writes: soundwire updates for 4.20-rc1 - support for multi-link streaming - updates in intel driver for multi-link streaming - Update Vinod's email - Fix rst formatting * tag 'soundwire-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: Documentation: soundwire: fix stream.rst markup warnings soundwire: intel: Remove duplicate assignment MAINTAINERS: Update Vinod's email soundwire: intel: Fix uninitialized adev deref soundwire: intel: Add pre/post bank switch ops soundwire: keep track of Masters in a stream soundwire: Add support for multi link bank switch soundwire: Handle multiple master instances in a stream soundwire: Add support to lock across bus instances soundwire: Initialize completion for defer messages Documentation: soundwire: Add documentation for multi link
This commit is contained in:
commit
018d52e8b5
@ -101,6 +101,34 @@ interface. ::
|
||||
+--------------------+ | |
|
||||
+----------------+
|
||||
|
||||
Example 5: Stereo Stream with L and R channel is rendered by 2 Masters, each
|
||||
rendering one channel, and is received by two different Slaves, each
|
||||
receiving one channel. Both Masters and both Slaves are using single port. ::
|
||||
|
||||
+---------------+ Clock Signal +---------------+
|
||||
| Master +----------------------------------+ Slave |
|
||||
| Interface | | Interface |
|
||||
| 1 | | 1 |
|
||||
| | Data Signal | |
|
||||
| L +----------------------------------+ L |
|
||||
| (Data) | Data Direction | (Data) |
|
||||
+---------------+ +-----------------------> +---------------+
|
||||
|
||||
+---------------+ Clock Signal +---------------+
|
||||
| Master +----------------------------------+ Slave |
|
||||
| Interface | | Interface |
|
||||
| 2 | | 2 |
|
||||
| | Data Signal | |
|
||||
| R +----------------------------------+ R |
|
||||
| (Data) | Data Direction | (Data) |
|
||||
+---------------+ +-----------------------> +---------------+
|
||||
|
||||
Note: In multi-link cases like above, to lock, one would acquire a global
|
||||
lock and then go on locking bus instances. But, in this case the caller
|
||||
framework(ASoC DPCM) guarantees that stream operations on a card are
|
||||
always serialized. So, there is no race condition and hence no need for
|
||||
global lock.
|
||||
|
||||
SoundWire Stream Management flow
|
||||
================================
|
||||
|
||||
@ -174,6 +202,7 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to
|
||||
.startup() operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_alloc_stream(char * stream_name);
|
||||
|
||||
|
||||
@ -200,6 +229,7 @@ only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM
|
||||
framework, this stream state is linked to .hw_params() operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_stream_add_master(struct sdw_bus * bus,
|
||||
struct sdw_stream_config * stream_config,
|
||||
struct sdw_ports_config * ports_config,
|
||||
@ -245,6 +275,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
|
||||
.prepare() operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_prepare_stream(struct sdw_stream_runtime * stream);
|
||||
|
||||
|
||||
@ -274,6 +305,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
|
||||
.trigger() start operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_enable_stream(struct sdw_stream_runtime * stream);
|
||||
|
||||
SDW_STREAM_DISABLED
|
||||
@ -301,6 +333,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
|
||||
.trigger() stop operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_disable_stream(struct sdw_stream_runtime * stream);
|
||||
|
||||
|
||||
@ -325,6 +358,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
|
||||
.trigger() stop operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_deprepare_stream(struct sdw_stream_runtime * stream);
|
||||
|
||||
|
||||
@ -349,6 +383,7 @@ all the Master(s) and Slave(s) associated with stream. From ASoC DPCM
|
||||
framework, this stream state is linked to .hw_free() operation.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int sdw_stream_remove_master(struct sdw_bus * bus,
|
||||
struct sdw_stream_runtime * stream);
|
||||
int sdw_stream_remove_slave(struct sdw_slave * slave,
|
||||
@ -361,6 +396,7 @@ stream assigned as part of ALLOCATED state.
|
||||
In .shutdown() the data structure maintaining stream state are freed up.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void sdw_release_stream(struct sdw_stream_runtime * stream);
|
||||
|
||||
Not Supported
|
||||
|
@ -13619,7 +13619,7 @@ F: sound/soc/
|
||||
F: include/sound/soc*
|
||||
|
||||
SOUNDWIRE SUBSYSTEM
|
||||
M: Vinod Koul <vinod.koul@intel.com>
|
||||
M: Vinod Koul <vkoul@kernel.org>
|
||||
M: Sanyog Kale <sanyog.r.kale@intel.com>
|
||||
R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus)
|
||||
INIT_LIST_HEAD(&bus->slaves);
|
||||
INIT_LIST_HEAD(&bus->m_rt_list);
|
||||
|
||||
/*
|
||||
* Initialize multi_link flag
|
||||
* TODO: populate this flag by reading property from FW node
|
||||
*/
|
||||
bus->multi_link = false;
|
||||
if (bus->ops->read_prop) {
|
||||
ret = bus->ops->read_prop(bus);
|
||||
if (ret < 0) {
|
||||
@ -175,6 +180,7 @@ static inline int do_transfer_defer(struct sdw_bus *bus,
|
||||
|
||||
defer->msg = msg;
|
||||
defer->length = msg->len;
|
||||
init_completion(&defer->complete);
|
||||
|
||||
for (i = 0; i <= retry; i++) {
|
||||
resp = bus->ops->xfer_msg_defer(bus, msg, defer);
|
||||
|
@ -4,6 +4,8 @@
|
||||
#ifndef __SDW_BUS_H
|
||||
#define __SDW_BUS_H
|
||||
|
||||
#define DEFAULT_BANK_SWITCH_TIMEOUT 3000
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
int sdw_acpi_find_slaves(struct sdw_bus *bus);
|
||||
#else
|
||||
@ -99,6 +101,7 @@ struct sdw_slave_runtime {
|
||||
* this stream, can be zero.
|
||||
* @slave_rt_list: Slave runtime list
|
||||
* @port_list: List of Master Ports configured for this stream, can be zero.
|
||||
* @stream_node: sdw_stream_runtime master_list node
|
||||
* @bus_node: sdw_bus m_rt_list node
|
||||
*/
|
||||
struct sdw_master_runtime {
|
||||
@ -108,6 +111,7 @@ struct sdw_master_runtime {
|
||||
unsigned int ch_count;
|
||||
struct list_head slave_rt_list;
|
||||
struct list_head port_list;
|
||||
struct list_head stream_node;
|
||||
struct list_head bus_node;
|
||||
};
|
||||
|
||||
|
@ -397,6 +397,69 @@ static int intel_config_stream(struct sdw_intel *sdw,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* bank switch routines
|
||||
*/
|
||||
|
||||
static int intel_pre_bank_switch(struct sdw_bus *bus)
|
||||
{
|
||||
struct sdw_cdns *cdns = bus_to_cdns(bus);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
void __iomem *shim = sdw->res->shim;
|
||||
int sync_reg;
|
||||
|
||||
/* Write to register only for multi-link */
|
||||
if (!bus->multi_link)
|
||||
return 0;
|
||||
|
||||
/* Read SYNC register */
|
||||
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||
sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance;
|
||||
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_post_bank_switch(struct sdw_bus *bus)
|
||||
{
|
||||
struct sdw_cdns *cdns = bus_to_cdns(bus);
|
||||
struct sdw_intel *sdw = cdns_to_intel(cdns);
|
||||
void __iomem *shim = sdw->res->shim;
|
||||
int sync_reg, ret;
|
||||
|
||||
/* Write to register only for multi-link */
|
||||
if (!bus->multi_link)
|
||||
return 0;
|
||||
|
||||
/* Read SYNC register */
|
||||
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
|
||||
|
||||
/*
|
||||
* post_bank_switch() ops is called from the bus in loop for
|
||||
* all the Masters in the steam with the expectation that
|
||||
* we trigger the bankswitch for the only first Master in the list
|
||||
* and do nothing for the other Masters
|
||||
*
|
||||
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
|
||||
*/
|
||||
if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Set SyncGO bit to synchronously trigger a bank switch for
|
||||
* all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
|
||||
* the Masters.
|
||||
*/
|
||||
sync_reg |= SDW_SHIM_SYNC_SYNCGO;
|
||||
|
||||
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
|
||||
SDW_SHIM_SYNC_SYNCGO);
|
||||
if (ret < 0)
|
||||
dev_err(sdw->cdns.dev, "Post bank switch failed: %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* DAI routines
|
||||
*/
|
||||
@ -750,6 +813,8 @@ static struct sdw_master_ops sdw_intel_ops = {
|
||||
.xfer_msg_defer = cdns_xfer_msg_defer,
|
||||
.reset_page_addr = cdns_reset_page_addr,
|
||||
.set_bus_conf = cdns_bus_conf,
|
||||
.pre_bank_switch = intel_pre_bank_switch,
|
||||
.post_bank_switch = intel_post_bank_switch,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -780,9 +845,6 @@ static int intel_probe(struct platform_device *pdev)
|
||||
sdw_intel_ops.read_prop = intel_prop_read;
|
||||
sdw->cdns.bus.ops = &sdw_intel_ops;
|
||||
|
||||
sdw_intel_ops.read_prop = intel_prop_read;
|
||||
sdw->cdns.bus.ops = &sdw_intel_ops;
|
||||
|
||||
platform_set_drvdata(pdev, sdw);
|
||||
|
||||
ret = sdw_add_bus_master(&sdw->cdns.bus);
|
||||
|
@ -151,7 +151,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev)) {
|
||||
dev_err(&adev->dev, "Couldn't find ACPI handle\n");
|
||||
pr_err("%s: Couldn't find ACPI handle\n", __func__);
|
||||
return AE_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdw_bank_switch(struct sdw_bus *bus)
|
||||
static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
|
||||
{
|
||||
int col_index, row_index;
|
||||
bool multi_link;
|
||||
struct sdw_msg *wr_msg;
|
||||
u8 *wbuf = NULL;
|
||||
int ret = 0;
|
||||
@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus)
|
||||
if (!wr_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
bus->defer_msg.msg = wr_msg;
|
||||
|
||||
wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
|
||||
if (!wbuf) {
|
||||
ret = -ENOMEM;
|
||||
@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus)
|
||||
SDW_MSG_FLAG_WRITE, wbuf);
|
||||
wr_msg->ssp_sync = true;
|
||||
|
||||
ret = sdw_transfer(bus, wr_msg);
|
||||
/*
|
||||
* Set the multi_link flag only when both the hardware supports
|
||||
* and there is a stream handled by multiple masters
|
||||
*/
|
||||
multi_link = bus->multi_link && (m_rt_count > 1);
|
||||
|
||||
if (multi_link)
|
||||
ret = sdw_transfer_defer(bus, wr_msg, &bus->defer_msg);
|
||||
else
|
||||
ret = sdw_transfer(bus, wr_msg);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Slave frame_ctrl reg write failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kfree(wr_msg);
|
||||
kfree(wbuf);
|
||||
bus->defer_msg.msg = NULL;
|
||||
bus->params.curr_bank = !bus->params.curr_bank;
|
||||
bus->params.next_bank = !bus->params.next_bank;
|
||||
if (!multi_link) {
|
||||
kfree(wr_msg);
|
||||
kfree(wbuf);
|
||||
bus->defer_msg.msg = NULL;
|
||||
bus->params.curr_bank = !bus->params.curr_bank;
|
||||
bus->params.next_bank = !bus->params.next_bank;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -679,37 +694,138 @@ error_1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdw_ml_sync_bank_switch: Multilink register bank switch
|
||||
*
|
||||
* @bus: SDW bus instance
|
||||
*
|
||||
* Caller function should free the buffers on error
|
||||
*/
|
||||
static int sdw_ml_sync_bank_switch(struct sdw_bus *bus)
|
||||
{
|
||||
unsigned long time_left;
|
||||
|
||||
if (!bus->multi_link)
|
||||
return 0;
|
||||
|
||||
/* Wait for completion of transfer */
|
||||
time_left = wait_for_completion_timeout(&bus->defer_msg.complete,
|
||||
bus->bank_switch_timeout);
|
||||
|
||||
if (!time_left) {
|
||||
dev_err(bus->dev, "Controller Timed out on bank switch");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
bus->params.curr_bank = !bus->params.curr_bank;
|
||||
bus->params.next_bank = !bus->params.next_bank;
|
||||
|
||||
if (bus->defer_msg.msg) {
|
||||
kfree(bus->defer_msg.msg->buf);
|
||||
kfree(bus->defer_msg.msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_bank_switch(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
const struct sdw_master_ops *ops;
|
||||
struct sdw_bus *bus = m_rt->bus;
|
||||
struct sdw_bus *bus = NULL;
|
||||
bool multi_link = false;
|
||||
int ret = 0;
|
||||
|
||||
ops = bus->ops;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
ops = bus->ops;
|
||||
|
||||
/* Pre-bank switch */
|
||||
if (ops->pre_bank_switch) {
|
||||
ret = ops->pre_bank_switch(bus);
|
||||
if (bus->multi_link) {
|
||||
multi_link = true;
|
||||
mutex_lock(&bus->msg_lock);
|
||||
}
|
||||
|
||||
/* Pre-bank switch */
|
||||
if (ops->pre_bank_switch) {
|
||||
ret = ops->pre_bank_switch(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev,
|
||||
"Pre bank switch op failed: %d", ret);
|
||||
goto msg_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform Bank switch operation.
|
||||
* For multi link cases, the actual bank switch is
|
||||
* synchronized across all Masters and happens later as a
|
||||
* part of post_bank_switch ops.
|
||||
*/
|
||||
ret = sdw_bank_switch(bus, stream->m_rt_count);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Pre bank switch op failed: %d", ret);
|
||||
return ret;
|
||||
dev_err(bus->dev, "Bank switch failed: %d", ret);
|
||||
goto error;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Bank switch */
|
||||
ret = sdw_bank_switch(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Bank switch failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* For multi link cases, it is expected that the bank switch is
|
||||
* triggered by the post_bank_switch for the first Master in the list
|
||||
* and for the other Masters the post_bank_switch() should return doing
|
||||
* nothing.
|
||||
*/
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
ops = bus->ops;
|
||||
|
||||
/* Post-bank switch */
|
||||
if (ops->post_bank_switch) {
|
||||
ret = ops->post_bank_switch(bus);
|
||||
/* Post-bank switch */
|
||||
if (ops->post_bank_switch) {
|
||||
ret = ops->post_bank_switch(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev,
|
||||
"Post bank switch op failed: %d", ret);
|
||||
goto error;
|
||||
}
|
||||
} else if (bus->multi_link && stream->m_rt_count > 1) {
|
||||
dev_err(bus->dev,
|
||||
"Post bank switch ops not implemented");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set the bank switch timeout to default, if not set */
|
||||
if (!bus->bank_switch_timeout)
|
||||
bus->bank_switch_timeout = DEFAULT_BANK_SWITCH_TIMEOUT;
|
||||
|
||||
/* Check if bank switch was successful */
|
||||
ret = sdw_ml_sync_bank_switch(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev,
|
||||
"Post bank switch op failed: %d", ret);
|
||||
"multi link bank switch failed: %d", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mutex_unlock(&bus->msg_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
|
||||
bus = m_rt->bus;
|
||||
|
||||
kfree(bus->defer_msg.msg->buf);
|
||||
kfree(bus->defer_msg.msg);
|
||||
}
|
||||
|
||||
msg_unlock:
|
||||
|
||||
if (multi_link) {
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
if (mutex_is_locked(&bus->msg_lock))
|
||||
mutex_unlock(&bus->msg_lock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,12 +863,29 @@ struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name)
|
||||
return NULL;
|
||||
|
||||
stream->name = stream_name;
|
||||
INIT_LIST_HEAD(&stream->master_list);
|
||||
stream->state = SDW_STREAM_ALLOCATED;
|
||||
stream->m_rt_count = 0;
|
||||
|
||||
return stream;
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_alloc_stream);
|
||||
|
||||
static struct sdw_master_runtime
|
||||
*sdw_find_master_rt(struct sdw_bus *bus,
|
||||
struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
|
||||
/* Retrieve Bus handle if already available */
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
if (m_rt->bus == bus)
|
||||
return m_rt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdw_alloc_master_rt() - Allocates and initialize Master runtime handle
|
||||
*
|
||||
@ -769,12 +902,11 @@ static struct sdw_master_runtime
|
||||
{
|
||||
struct sdw_master_runtime *m_rt;
|
||||
|
||||
m_rt = stream->m_rt;
|
||||
|
||||
/*
|
||||
* check if Master is already allocated (as a result of Slave adding
|
||||
* it first), if so skip allocation and go to configure
|
||||
*/
|
||||
m_rt = sdw_find_master_rt(bus, stream);
|
||||
if (m_rt)
|
||||
goto stream_config;
|
||||
|
||||
@ -785,7 +917,7 @@ static struct sdw_master_runtime
|
||||
/* Initialization of Master runtime handle */
|
||||
INIT_LIST_HEAD(&m_rt->port_list);
|
||||
INIT_LIST_HEAD(&m_rt->slave_rt_list);
|
||||
stream->m_rt = m_rt;
|
||||
list_add_tail(&m_rt->stream_node, &stream->master_list);
|
||||
|
||||
list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
|
||||
|
||||
@ -843,17 +975,21 @@ static void sdw_slave_port_release(struct sdw_bus *bus,
|
||||
struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_port_runtime *p_rt, *_p_rt;
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_master_runtime *m_rt;
|
||||
struct sdw_slave_runtime *s_rt;
|
||||
|
||||
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
|
||||
if (s_rt->slave != slave)
|
||||
continue;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
|
||||
|
||||
list_for_each_entry_safe(p_rt, _p_rt,
|
||||
&s_rt->port_list, port_node) {
|
||||
list_del(&p_rt->port_node);
|
||||
kfree(p_rt);
|
||||
if (s_rt->slave != slave)
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(p_rt, _p_rt,
|
||||
&s_rt->port_list, port_node) {
|
||||
|
||||
list_del(&p_rt->port_node);
|
||||
kfree(p_rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -870,16 +1006,18 @@ static void sdw_release_slave_stream(struct sdw_slave *slave,
|
||||
struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_slave_runtime *s_rt, *_s_rt;
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_master_runtime *m_rt;
|
||||
|
||||
/* Retrieve Slave runtime handle */
|
||||
list_for_each_entry_safe(s_rt, _s_rt,
|
||||
&m_rt->slave_rt_list, m_rt_node) {
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
/* Retrieve Slave runtime handle */
|
||||
list_for_each_entry_safe(s_rt, _s_rt,
|
||||
&m_rt->slave_rt_list, m_rt_node) {
|
||||
|
||||
if (s_rt->slave == slave) {
|
||||
list_del(&s_rt->m_rt_node);
|
||||
kfree(s_rt);
|
||||
return;
|
||||
if (s_rt->slave == slave) {
|
||||
list_del(&s_rt->m_rt_node);
|
||||
kfree(s_rt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -887,6 +1025,7 @@ static void sdw_release_slave_stream(struct sdw_slave *slave,
|
||||
/**
|
||||
* sdw_release_master_stream() - Free Master runtime handle
|
||||
*
|
||||
* @m_rt: Master runtime node
|
||||
* @stream: Stream runtime handle.
|
||||
*
|
||||
* This function is to be called with bus_lock held
|
||||
@ -894,9 +1033,9 @@ static void sdw_release_slave_stream(struct sdw_slave *slave,
|
||||
* handle. If this is called first then sdw_release_slave_stream() will have
|
||||
* no effect as Slave(s) runtime handle would already be freed up.
|
||||
*/
|
||||
static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
|
||||
static void sdw_release_master_stream(struct sdw_master_runtime *m_rt,
|
||||
struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_slave_runtime *s_rt, *_s_rt;
|
||||
|
||||
list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) {
|
||||
@ -904,7 +1043,9 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
|
||||
sdw_release_slave_stream(s_rt->slave, stream);
|
||||
}
|
||||
|
||||
list_del(&m_rt->stream_node);
|
||||
list_del(&m_rt->bus_node);
|
||||
kfree(m_rt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -918,13 +1059,23 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
|
||||
int sdw_stream_remove_master(struct sdw_bus *bus,
|
||||
struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt, *_m_rt;
|
||||
|
||||
mutex_lock(&bus->bus_lock);
|
||||
|
||||
sdw_release_master_stream(stream);
|
||||
sdw_master_port_release(bus, stream->m_rt);
|
||||
stream->state = SDW_STREAM_RELEASED;
|
||||
kfree(stream->m_rt);
|
||||
stream->m_rt = NULL;
|
||||
list_for_each_entry_safe(m_rt, _m_rt,
|
||||
&stream->master_list, stream_node) {
|
||||
|
||||
if (m_rt->bus != bus)
|
||||
continue;
|
||||
|
||||
sdw_master_port_release(bus, m_rt);
|
||||
sdw_release_master_stream(m_rt, stream);
|
||||
stream->m_rt_count--;
|
||||
}
|
||||
|
||||
if (list_empty(&stream->master_list))
|
||||
stream->state = SDW_STREAM_RELEASED;
|
||||
|
||||
mutex_unlock(&bus->bus_lock);
|
||||
|
||||
@ -1107,6 +1258,18 @@ int sdw_stream_add_master(struct sdw_bus *bus,
|
||||
|
||||
mutex_lock(&bus->bus_lock);
|
||||
|
||||
/*
|
||||
* For multi link streams, add the second master only if
|
||||
* the bus supports it.
|
||||
* Check if bus->multi_link is set
|
||||
*/
|
||||
if (!bus->multi_link && stream->m_rt_count > 0) {
|
||||
dev_err(bus->dev,
|
||||
"Multilink not supported, link %d", bus->link_id);
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
|
||||
if (!m_rt) {
|
||||
dev_err(bus->dev,
|
||||
@ -1124,10 +1287,12 @@ int sdw_stream_add_master(struct sdw_bus *bus,
|
||||
if (ret)
|
||||
goto stream_error;
|
||||
|
||||
stream->m_rt_count++;
|
||||
|
||||
goto unlock;
|
||||
|
||||
stream_error:
|
||||
sdw_release_master_stream(stream);
|
||||
sdw_release_master_stream(m_rt, stream);
|
||||
unlock:
|
||||
mutex_unlock(&bus->bus_lock);
|
||||
return ret;
|
||||
@ -1205,7 +1370,7 @@ stream_error:
|
||||
* we hit error so cleanup the stream, release all Slave(s) and
|
||||
* Master runtime
|
||||
*/
|
||||
sdw_release_master_stream(stream);
|
||||
sdw_release_master_stream(m_rt, stream);
|
||||
error:
|
||||
mutex_unlock(&slave->bus->bus_lock);
|
||||
return ret;
|
||||
@ -1245,33 +1410,82 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdw_acquire_bus_lock: Acquire bus lock for all Master runtime(s)
|
||||
*
|
||||
* @stream: SoundWire stream
|
||||
*
|
||||
* Acquire bus_lock for each of the master runtime(m_rt) part of this
|
||||
* stream to reconfigure the bus.
|
||||
* NOTE: This function is called from SoundWire stream ops and is
|
||||
* expected that a global lock is held before acquiring bus_lock.
|
||||
*/
|
||||
static void sdw_acquire_bus_lock(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
|
||||
/* Iterate for all Master(s) in Master list */
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
|
||||
mutex_lock(&bus->bus_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sdw_release_bus_lock: Release bus lock for all Master runtime(s)
|
||||
*
|
||||
* @stream: SoundWire stream
|
||||
*
|
||||
* Release the previously held bus_lock after reconfiguring the bus.
|
||||
* NOTE: This function is called from SoundWire stream ops and is
|
||||
* expected that a global lock is held before releasing bus_lock.
|
||||
*/
|
||||
static void sdw_release_bus_lock(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
|
||||
/* Iterate for all Master(s) in Master list */
|
||||
list_for_each_entry_reverse(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
mutex_unlock(&bus->bus_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_bus *bus = m_rt->bus;
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
struct sdw_master_prop *prop = NULL;
|
||||
struct sdw_bus_params params;
|
||||
int ret;
|
||||
|
||||
prop = &bus->prop;
|
||||
memcpy(¶ms, &bus->params, sizeof(params));
|
||||
/* Prepare Master(s) and Slave(s) port(s) associated with stream */
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
prop = &bus->prop;
|
||||
memcpy(¶ms, &bus->params, sizeof(params));
|
||||
|
||||
/* TODO: Support Asynchronous mode */
|
||||
if ((prop->max_freq % stream->params.rate) != 0) {
|
||||
dev_err(bus->dev, "Async mode not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* TODO: Support Asynchronous mode */
|
||||
if ((prop->max_freq % stream->params.rate) != 0) {
|
||||
dev_err(bus->dev, "Async mode not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Increment cumulative bus bandwidth */
|
||||
/* TODO: Update this during Device-Device support */
|
||||
bus->params.bandwidth += m_rt->stream->params.rate *
|
||||
m_rt->ch_count * m_rt->stream->params.bps;
|
||||
/* Increment cumulative bus bandwidth */
|
||||
/* TODO: Update this during Device-Device support */
|
||||
bus->params.bandwidth += m_rt->stream->params.rate *
|
||||
m_rt->ch_count * m_rt->stream->params.bps;
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
goto restore_params;
|
||||
}
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
goto restore_params;
|
||||
}
|
||||
|
||||
ret = do_bank_switch(stream);
|
||||
@ -1280,12 +1494,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
|
||||
goto restore_params;
|
||||
}
|
||||
|
||||
/* Prepare port(s) on the new clock configuration */
|
||||
ret = sdw_prep_deprep_ports(m_rt, true);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Prepare port(s) failed ret = %d",
|
||||
ret);
|
||||
return ret;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
|
||||
/* Prepare port(s) on the new clock configuration */
|
||||
ret = sdw_prep_deprep_ports(m_rt, true);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Prepare port(s) failed ret = %d",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
stream->state = SDW_STREAM_PREPARED;
|
||||
@ -1313,35 +1531,40 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_acquire_bus_lock(stream);
|
||||
|
||||
ret = _sdw_prepare_stream(stream);
|
||||
if (ret < 0)
|
||||
pr_err("Prepare for stream:%s failed: %d", stream->name, ret);
|
||||
|
||||
mutex_unlock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_release_bus_lock(stream);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_prepare_stream);
|
||||
|
||||
static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_bus *bus = m_rt->bus;
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
int ret;
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Enable Master(s) and Slave(s) port(s) associated with stream */
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
|
||||
/* Enable port(s) */
|
||||
ret = sdw_enable_disable_ports(m_rt, true);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Enable port(s) failed ret: %d", ret);
|
||||
return ret;
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable port(s) */
|
||||
ret = sdw_enable_disable_ports(m_rt, true);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Enable port(s) failed ret: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = do_bank_switch(stream);
|
||||
@ -1370,37 +1593,42 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_acquire_bus_lock(stream);
|
||||
|
||||
ret = _sdw_enable_stream(stream);
|
||||
if (ret < 0)
|
||||
pr_err("Enable for stream:%s failed: %d", stream->name, ret);
|
||||
|
||||
mutex_unlock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_release_bus_lock(stream);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_enable_stream);
|
||||
|
||||
static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_bus *bus = m_rt->bus;
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
int ret;
|
||||
|
||||
/* Disable port(s) */
|
||||
ret = sdw_enable_disable_ports(m_rt, false);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Disable port(s) failed: %d", ret);
|
||||
return ret;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
/* Disable port(s) */
|
||||
ret = sdw_enable_disable_ports(m_rt, false);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Disable port(s) failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
stream->state = SDW_STREAM_DISABLED;
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return do_bank_switch(stream);
|
||||
@ -1422,43 +1650,46 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_acquire_bus_lock(stream);
|
||||
|
||||
ret = _sdw_disable_stream(stream);
|
||||
if (ret < 0)
|
||||
pr_err("Disable for stream:%s failed: %d", stream->name, ret);
|
||||
|
||||
mutex_unlock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_release_bus_lock(stream);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_disable_stream);
|
||||
|
||||
static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
|
||||
{
|
||||
struct sdw_master_runtime *m_rt = stream->m_rt;
|
||||
struct sdw_bus *bus = m_rt->bus;
|
||||
struct sdw_master_runtime *m_rt = NULL;
|
||||
struct sdw_bus *bus = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* De-prepare port(s) */
|
||||
ret = sdw_prep_deprep_ports(m_rt, false);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "De-prepare port(s) failed: %d", ret);
|
||||
return ret;
|
||||
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
|
||||
bus = m_rt->bus;
|
||||
/* De-prepare port(s) */
|
||||
ret = sdw_prep_deprep_ports(m_rt, false);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "De-prepare port(s) failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: Update this during Device-Device support */
|
||||
bus->params.bandwidth -= m_rt->stream->params.rate *
|
||||
m_rt->ch_count * m_rt->stream->params.bps;
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stream->state = SDW_STREAM_DEPREPARED;
|
||||
|
||||
/* TODO: Update this during Device-Device support */
|
||||
bus->params.bandwidth -= m_rt->stream->params.rate *
|
||||
m_rt->ch_count * m_rt->stream->params.bps;
|
||||
|
||||
/* Program params */
|
||||
ret = sdw_program_params(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(bus->dev, "Program params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return do_bank_switch(stream);
|
||||
}
|
||||
|
||||
@ -1478,13 +1709,12 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&stream->m_rt->bus->bus_lock);
|
||||
|
||||
sdw_acquire_bus_lock(stream);
|
||||
ret = _sdw_deprepare_stream(stream);
|
||||
if (ret < 0)
|
||||
pr_err("De-prepare for stream:%d failed: %d", ret, ret);
|
||||
|
||||
mutex_unlock(&stream->m_rt->bus->bus_lock);
|
||||
sdw_release_bus_lock(stream);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_deprepare_stream);
|
||||
|
@ -678,6 +678,9 @@ struct sdw_master_ops {
|
||||
* @defer_msg: Defer message
|
||||
* @clk_stop_timeout: Clock stop timeout computed
|
||||
* @bank_switch_timeout: Bank switch timeout computed
|
||||
* @multi_link: Store bus property that indicates if multi links
|
||||
* are supported. This flag is populated by drivers after reading
|
||||
* appropriate firmware (ACPI/DT).
|
||||
*/
|
||||
struct sdw_bus {
|
||||
struct device *dev;
|
||||
@ -694,6 +697,7 @@ struct sdw_bus {
|
||||
struct sdw_defer defer_msg;
|
||||
unsigned int clk_stop_timeout;
|
||||
u32 bank_switch_timeout;
|
||||
bool multi_link;
|
||||
};
|
||||
|
||||
int sdw_add_bus_master(struct sdw_bus *bus);
|
||||
@ -768,14 +772,18 @@ struct sdw_stream_params {
|
||||
* @params: Stream parameters
|
||||
* @state: Current state of the stream
|
||||
* @type: Stream type PCM or PDM
|
||||
* @m_rt: Master runtime
|
||||
* @master_list: List of Master runtime(s) in this stream.
|
||||
* master_list can contain only one m_rt per Master instance
|
||||
* for a stream
|
||||
* @m_rt_count: Count of Master runtime(s) in this stream
|
||||
*/
|
||||
struct sdw_stream_runtime {
|
||||
char *name;
|
||||
struct sdw_stream_params params;
|
||||
enum sdw_stream_state state;
|
||||
enum sdw_stream_type type;
|
||||
struct sdw_master_runtime *m_rt;
|
||||
struct list_head master_list;
|
||||
int m_rt_count;
|
||||
};
|
||||
|
||||
struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name);
|
||||
|
Loading…
Reference in New Issue
Block a user