Commit 018d52e8 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'soundwire-4.20-rc1' of...

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
parents 91c45a7a 502c00d9
...@@ -101,6 +101,34 @@ interface. :: ...@@ -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 SoundWire Stream Management flow
================================ ================================
...@@ -174,6 +202,7 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to ...@@ -174,6 +202,7 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to
.startup() operation. .startup() operation.
.. code-block:: c .. code-block:: c
int sdw_alloc_stream(char * stream_name); 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 ...@@ -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. framework, this stream state is linked to .hw_params() operation.
.. code-block:: c .. code-block:: c
int sdw_stream_add_master(struct sdw_bus * bus, int sdw_stream_add_master(struct sdw_bus * bus,
struct sdw_stream_config * stream_config, struct sdw_stream_config * stream_config,
struct sdw_ports_config * ports_config, struct sdw_ports_config * ports_config,
...@@ -245,6 +275,7 @@ stream. From ASoC DPCM framework, this stream state is linked to ...@@ -245,6 +275,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
.prepare() operation. .prepare() operation.
.. code-block:: c .. code-block:: c
int sdw_prepare_stream(struct sdw_stream_runtime * stream); int sdw_prepare_stream(struct sdw_stream_runtime * stream);
...@@ -274,6 +305,7 @@ stream. From ASoC DPCM framework, this stream state is linked to ...@@ -274,6 +305,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
.trigger() start operation. .trigger() start operation.
.. code-block:: c .. code-block:: c
int sdw_enable_stream(struct sdw_stream_runtime * stream); int sdw_enable_stream(struct sdw_stream_runtime * stream);
SDW_STREAM_DISABLED SDW_STREAM_DISABLED
...@@ -301,6 +333,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to ...@@ -301,6 +333,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation. .trigger() stop operation.
.. code-block:: c .. code-block:: c
int sdw_disable_stream(struct sdw_stream_runtime * stream); 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 ...@@ -325,6 +358,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation. .trigger() stop operation.
.. code-block:: c .. code-block:: c
int sdw_deprepare_stream(struct sdw_stream_runtime * stream); 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 ...@@ -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. framework, this stream state is linked to .hw_free() operation.
.. code-block:: c .. code-block:: c
int sdw_stream_remove_master(struct sdw_bus * bus, int sdw_stream_remove_master(struct sdw_bus * bus,
struct sdw_stream_runtime * stream); struct sdw_stream_runtime * stream);
int sdw_stream_remove_slave(struct sdw_slave * slave, int sdw_stream_remove_slave(struct sdw_slave * slave,
...@@ -361,6 +396,7 @@ stream assigned as part of ALLOCATED state. ...@@ -361,6 +396,7 @@ stream assigned as part of ALLOCATED state.
In .shutdown() the data structure maintaining stream state are freed up. In .shutdown() the data structure maintaining stream state are freed up.
.. code-block:: c .. code-block:: c
void sdw_release_stream(struct sdw_stream_runtime * stream); void sdw_release_stream(struct sdw_stream_runtime * stream);
Not Supported Not Supported
......
...@@ -13619,7 +13619,7 @@ F: sound/soc/ ...@@ -13619,7 +13619,7 @@ F: sound/soc/
F: include/sound/soc* F: include/sound/soc*
SOUNDWIRE SUBSYSTEM SOUNDWIRE SUBSYSTEM
M: Vinod Koul <vinod.koul@intel.com> M: Vinod Koul <vkoul@kernel.org>
M: Sanyog Kale <sanyog.r.kale@intel.com> M: Sanyog Kale <sanyog.r.kale@intel.com>
R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
......
...@@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus) ...@@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus)
INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->slaves);
INIT_LIST_HEAD(&bus->m_rt_list); 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) { if (bus->ops->read_prop) {
ret = bus->ops->read_prop(bus); ret = bus->ops->read_prop(bus);
if (ret < 0) { if (ret < 0) {
...@@ -175,6 +180,7 @@ static inline int do_transfer_defer(struct sdw_bus *bus, ...@@ -175,6 +180,7 @@ static inline int do_transfer_defer(struct sdw_bus *bus,
defer->msg = msg; defer->msg = msg;
defer->length = msg->len; defer->length = msg->len;
init_completion(&defer->complete);
for (i = 0; i <= retry; i++) { for (i = 0; i <= retry; i++) {
resp = bus->ops->xfer_msg_defer(bus, msg, defer); resp = bus->ops->xfer_msg_defer(bus, msg, defer);
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#ifndef __SDW_BUS_H #ifndef __SDW_BUS_H
#define __SDW_BUS_H #define __SDW_BUS_H
#define DEFAULT_BANK_SWITCH_TIMEOUT 3000
#if IS_ENABLED(CONFIG_ACPI) #if IS_ENABLED(CONFIG_ACPI)
int sdw_acpi_find_slaves(struct sdw_bus *bus); int sdw_acpi_find_slaves(struct sdw_bus *bus);
#else #else
...@@ -99,6 +101,7 @@ struct sdw_slave_runtime { ...@@ -99,6 +101,7 @@ struct sdw_slave_runtime {
* this stream, can be zero. * this stream, can be zero.
* @slave_rt_list: Slave runtime list * @slave_rt_list: Slave runtime list
* @port_list: List of Master Ports configured for this stream, can be zero. * @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 * @bus_node: sdw_bus m_rt_list node
*/ */
struct sdw_master_runtime { struct sdw_master_runtime {
...@@ -108,6 +111,7 @@ struct sdw_master_runtime { ...@@ -108,6 +111,7 @@ struct sdw_master_runtime {
unsigned int ch_count; unsigned int ch_count;
struct list_head slave_rt_list; struct list_head slave_rt_list;
struct list_head port_list; struct list_head port_list;
struct list_head stream_node;
struct list_head bus_node; struct list_head bus_node;
}; };
......
...@@ -397,6 +397,69 @@ static int intel_config_stream(struct sdw_intel *sdw, ...@@ -397,6 +397,69 @@ static int intel_config_stream(struct sdw_intel *sdw,
return -EIO; 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 * DAI routines
*/ */
...@@ -750,6 +813,8 @@ static struct sdw_master_ops sdw_intel_ops = { ...@@ -750,6 +813,8 @@ static struct sdw_master_ops sdw_intel_ops = {
.xfer_msg_defer = cdns_xfer_msg_defer, .xfer_msg_defer = cdns_xfer_msg_defer,
.reset_page_addr = cdns_reset_page_addr, .reset_page_addr = cdns_reset_page_addr,
.set_bus_conf = cdns_bus_conf, .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) ...@@ -780,9 +845,6 @@ static int intel_probe(struct platform_device *pdev)
sdw_intel_ops.read_prop = intel_prop_read; sdw_intel_ops.read_prop = intel_prop_read;
sdw->cdns.bus.ops = &sdw_intel_ops; 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); platform_set_drvdata(pdev, sdw);
ret = sdw_add_bus_master(&sdw->cdns.bus); ret = sdw_add_bus_master(&sdw->cdns.bus);
......
...@@ -151,7 +151,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, ...@@ -151,7 +151,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
struct acpi_device *adev; struct acpi_device *adev;
if (acpi_bus_get_device(handle, &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; return AE_NOT_FOUND;
} }
......
...@@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus) ...@@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus)
return ret; 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; int col_index, row_index;
bool multi_link;
struct sdw_msg *wr_msg; struct sdw_msg *wr_msg;
u8 *wbuf = NULL; u8 *wbuf = NULL;
int ret = 0; int ret = 0;
...@@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus)
if (!wr_msg) if (!wr_msg)
return -ENOMEM; return -ENOMEM;
bus->defer_msg.msg = wr_msg;
wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
if (!wbuf) { if (!wbuf) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus)
SDW_MSG_FLAG_WRITE, wbuf); SDW_MSG_FLAG_WRITE, wbuf);
wr_msg->ssp_sync = true; 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) { if (ret < 0) {
dev_err(bus->dev, "Slave frame_ctrl reg write failed"); dev_err(bus->dev, "Slave frame_ctrl reg write failed");
goto error; goto error;
} }
kfree(wr_msg); if (!multi_link) {
kfree(wbuf); kfree(wr_msg);
bus->defer_msg.msg = NULL; kfree(wbuf);
bus->params.curr_bank = !bus->params.curr_bank; bus->defer_msg.msg = NULL;
bus->params.next_bank = !bus->params.next_bank; bus->params.curr_bank = !bus->params.curr_bank;
bus->params.next_bank = !bus->params.next_bank;
}
return 0; return 0;
...@@ -679,37 +694,138 @@ static int sdw_bank_switch(struct sdw_bus *bus) ...@@ -679,37 +694,138 @@ static int sdw_bank_switch(struct sdw_bus *bus)
return ret; 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) 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; 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; int ret = 0;
ops = bus->ops; list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
ops = bus->ops;
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;
}
}
/* Pre-bank switch */ /*
if (ops->pre_bank_switch) { * Perform Bank switch operation.
ret = ops->pre_bank_switch(bus); * 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) { if (ret < 0) {
dev_err(bus->dev, "Pre bank switch op failed: %d", ret); dev_err(bus->dev, "Bank switch failed: %d", ret);
return ret; goto error;
} }
} }
/* Bank switch */ /*
ret = sdw_bank_switch(bus); * For multi link cases, it is expected that the bank switch is
if (ret < 0) { * triggered by the post_bank_switch for the first Master in the list
dev_err(bus->dev, "Bank switch failed: %d", ret); * and for the other Masters the post_bank_switch() should return doing
return ret; * 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);
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;
/* Post-bank switch */ /* Check if bank switch was successful */
if (ops->post_bank_switch) { ret = sdw_ml_sync_bank_switch(bus);
ret = ops->post_bank_switch(bus);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, 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) ...@@ -747,12 +863,29 @@ struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name)
return NULL; return NULL;
stream->name = stream_name; stream->name = stream_name;
INIT_LIST_HEAD(&stream->master_list);
stream->state = SDW_STREAM_ALLOCATED; stream->state = SDW_STREAM_ALLOCATED;
stream->m_rt_count = 0;
return stream; return stream;
} }
EXPORT_SYMBOL(sdw_alloc_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 * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle
* *
...@@ -769,12 +902,11 @@ static struct sdw_master_runtime ...@@ -769,12 +902,11 @@ static struct sdw_master_runtime
{ {
struct sdw_master_runtime *m_rt; struct sdw_master_runtime *m_rt;
m_rt = stream->m_rt;
/* /*
* check if Master is already allocated (as a result of Slave adding * check if Master is already allocated (as a result of Slave adding
* it first), if so skip allocation and go to configure * it first), if so skip allocation and go to configure
*/ */
m_rt = sdw_find_master_rt(bus, stream);
if (m_rt) if (m_rt)
goto stream_config; goto stream_config;
...@@ -785,7 +917,7 @@ static struct sdw_master_runtime ...@@ -785,7 +917,7 @@ static struct sdw_master_runtime
/* Initialization of Master runtime handle */ /* Initialization of Master runtime handle */
INIT_LIST_HEAD(&m_rt->port_list); INIT_LIST_HEAD(&m_rt->port_list);
INIT_LIST_HEAD(&m_rt->slave_rt_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); 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, ...@@ -843,17 +975,21 @@ static void sdw_slave_port_release(struct sdw_bus *bus,
struct sdw_stream_runtime *stream) struct sdw_stream_runtime *stream)
{ {
struct sdw_port_runtime *p_rt, *_p_rt; 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; struct sdw_slave_runtime *s_rt;
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { list_for_each_entry(m_rt, &stream->master_list, stream_node) {
if (s_rt->slave != slave) list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
continue;
list_for_each_entry_safe(p_rt, _p_rt, if (s_rt->slave != slave)
&s_rt->port_list, port_node) { continue;
list_del(&p_rt->port_node);
kfree(p_rt); 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, ...@@ -870,16 +1006,18 @@ static void sdw_release_slave_stream(struct sdw_slave *slave,
struct sdw_stream_runtime *stream) struct sdw_stream_runtime *stream)
{ {
struct sdw_slave_runtime *s_rt, *_s_rt; 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(m_rt, &stream->master_list, stream_node) {
list_for_each_entry_safe(s_rt, _s_rt, /* Retrieve Slave runtime handle */
&m_rt->slave_rt_list, m_rt_node) { list_for_each_entry_safe(s_rt, _s_rt,
&m_rt->slave_rt_list, m_rt_node) {
if (s_rt->slave == slave) { if (s_rt->slave == slave) {
list_del(&s_rt->m_rt_node); list_del(&s_rt->m_rt_node);
kfree(s_rt); kfree(s_rt);
return; return;
}
} }
} }
} }
...@@ -887,6 +1025,7 @@ static void sdw_release_slave_stream(struct sdw_slave *slave, ...@@ -887,6 +1025,7 @@ static void sdw_release_slave_stream(struct sdw_slave *slave,
/** /**
* sdw_release_master_stream() - Free Master runtime handle * sdw_release_master_stream() - Free Master runtime handle
* *
* @m_rt: Master runtime node
* @stream: Stream runtime handle. * @stream: Stream runtime handle.
* *
* This function is to be called with bus_lock held * This function is to be called with bus_lock held
...@@ -894,9 +1033,9 @@ static void sdw_release_slave_stream(struct sdw_slave *slave, ...@@ -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 * 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. * 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; 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) { 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) ...@@ -904,7 +1043,9 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
sdw_release_slave_stream(s_rt->slave, stream); sdw_release_slave_stream(s_rt->slave, stream);
} }
list_del(&m_rt->stream_node);
list_del(&m_rt->bus_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) ...@@ -918,13 +1059,23 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
int sdw_stream_remove_master(struct sdw_bus *bus, int sdw_stream_remove_master(struct sdw_bus *bus,
struct sdw_stream_runtime *stream) struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt, *_m_rt;
mutex_lock(&bus->bus_lock); mutex_lock(&bus->bus_lock);
sdw_release_master_stream(stream); list_for_each_entry_safe(m_rt, _m_rt,
sdw_master_port_release(bus, stream->m_rt); &stream->master_list, stream_node) {
stream->state = SDW_STREAM_RELEASED;
kfree(stream->m_rt); if (m_rt->bus != bus)
stream->m_rt = NULL; 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); mutex_unlock(&bus->bus_lock);
...@@ -1107,6 +1258,18 @@ int sdw_stream_add_master(struct sdw_bus *bus, ...@@ -1107,6 +1258,18 @@ int sdw_stream_add_master(struct sdw_bus *bus,
mutex_lock(&bus->bus_lock); 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); m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
if (!m_rt) { if (!m_rt) {
dev_err(bus->dev, dev_err(bus->dev,
...@@ -1124,10 +1287,12 @@ int sdw_stream_add_master(struct sdw_bus *bus, ...@@ -1124,10 +1287,12 @@ int sdw_stream_add_master(struct sdw_bus *bus,
if (ret) if (ret)
goto stream_error; goto stream_error;
stream->m_rt_count++;
goto unlock; goto unlock;
stream_error: stream_error:
sdw_release_master_stream(stream); sdw_release_master_stream(m_rt, stream);
unlock: unlock:
mutex_unlock(&bus->bus_lock); mutex_unlock(&bus->bus_lock);
return ret; return ret;
...@@ -1205,7 +1370,7 @@ int sdw_stream_add_slave(struct sdw_slave *slave, ...@@ -1205,7 +1370,7 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
* we hit error so cleanup the stream, release all Slave(s) and * we hit error so cleanup the stream, release all Slave(s) and
* Master runtime * Master runtime
*/ */
sdw_release_master_stream(stream); sdw_release_master_stream(m_rt, stream);
error: error:
mutex_unlock(&slave->bus->bus_lock); mutex_unlock(&slave->bus->bus_lock);
return ret; return ret;
...@@ -1245,33 +1410,82 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, ...@@ -1245,33 +1410,82 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
return NULL; 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) static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = stream->m_rt; struct sdw_master_runtime *m_rt = NULL;
struct sdw_bus *bus = m_rt->bus; struct sdw_bus *bus = NULL;
struct sdw_master_prop *prop = NULL; struct sdw_master_prop *prop = NULL;
struct sdw_bus_params params; struct sdw_bus_params params;
int ret; int ret;
prop = &bus->prop; /* Prepare Master(s) and Slave(s) port(s) associated with stream */
memcpy(&params, &bus->params, sizeof(params)); list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
prop = &bus->prop;
memcpy(&params, &bus->params, sizeof(params));
/* TODO: Support Asynchronous mode */ /* TODO: Support Asynchronous mode */
if ((prop->max_freq % stream->params.rate) != 0) { if ((prop->max_freq % stream->params.rate) != 0) {
dev_err(bus->dev, "Async mode not supported"); dev_err(bus->dev, "Async mode not supported");
return -EINVAL; return -EINVAL;
} }
/* Increment cumulative bus bandwidth */ /* Increment cumulative bus bandwidth */
/* TODO: Update this during Device-Device support */ /* TODO: Update this during Device-Device support */
bus->params.bandwidth += m_rt->stream->params.rate * bus->params.bandwidth += m_rt->stream->params.rate *
m_rt->ch_count * m_rt->stream->params.bps; 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); ret = do_bank_switch(stream);
...@@ -1280,12 +1494,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) ...@@ -1280,12 +1494,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
goto restore_params; goto restore_params;
} }
/* Prepare port(s) on the new clock configuration */ list_for_each_entry(m_rt, &stream->master_list, stream_node) {
ret = sdw_prep_deprep_ports(m_rt, true); bus = m_rt->bus;
if (ret < 0) {
dev_err(bus->dev, "Prepare port(s) failed ret = %d", /* Prepare port(s) on the new clock configuration */
ret); ret = sdw_prep_deprep_ports(m_rt, true);
return ret; if (ret < 0) {
dev_err(bus->dev, "Prepare port(s) failed ret = %d",
ret);
return ret;
}
} }
stream->state = SDW_STREAM_PREPARED; stream->state = SDW_STREAM_PREPARED;
...@@ -1313,35 +1531,40 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) ...@@ -1313,35 +1531,40 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&stream->m_rt->bus->bus_lock); sdw_acquire_bus_lock(stream);
ret = _sdw_prepare_stream(stream); ret = _sdw_prepare_stream(stream);
if (ret < 0) if (ret < 0)
pr_err("Prepare for stream:%s failed: %d", stream->name, ret); 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; return ret;
} }
EXPORT_SYMBOL(sdw_prepare_stream); EXPORT_SYMBOL(sdw_prepare_stream);
static int _sdw_enable_stream(struct sdw_stream_runtime *stream) static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = stream->m_rt; struct sdw_master_runtime *m_rt = NULL;
struct sdw_bus *bus = m_rt->bus; struct sdw_bus *bus = NULL;
int ret; int ret;
/* Program params */ /* Enable Master(s) and Slave(s) port(s) associated with stream */
ret = sdw_program_params(bus); list_for_each_entry(m_rt, &stream->master_list, stream_node) {
if (ret < 0) { bus = m_rt->bus;
dev_err(bus->dev, "Program params failed: %d", ret);
return ret;
}
/* Enable port(s) */ /* Program params */
ret = sdw_enable_disable_ports(m_rt, true); ret = sdw_program_params(bus);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, "Enable port(s) failed ret: %d", ret); dev_err(bus->dev, "Program params failed: %d", ret);
return 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); ret = do_bank_switch(stream);
...@@ -1370,37 +1593,42 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) ...@@ -1370,37 +1593,42 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&stream->m_rt->bus->bus_lock); sdw_acquire_bus_lock(stream);
ret = _sdw_enable_stream(stream); ret = _sdw_enable_stream(stream);
if (ret < 0) if (ret < 0)
pr_err("Enable for stream:%s failed: %d", stream->name, ret); 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; return ret;
} }
EXPORT_SYMBOL(sdw_enable_stream); EXPORT_SYMBOL(sdw_enable_stream);
static int _sdw_disable_stream(struct sdw_stream_runtime *stream) static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = stream->m_rt; struct sdw_master_runtime *m_rt = NULL;
struct sdw_bus *bus = m_rt->bus; struct sdw_bus *bus = NULL;
int ret; int ret;
/* Disable port(s) */ list_for_each_entry(m_rt, &stream->master_list, stream_node) {
ret = sdw_enable_disable_ports(m_rt, false); bus = m_rt->bus;
if (ret < 0) { /* Disable port(s) */
dev_err(bus->dev, "Disable port(s) failed: %d", ret); ret = sdw_enable_disable_ports(m_rt, false);
return ret; if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d", ret);
return ret;
}
} }
stream->state = SDW_STREAM_DISABLED; stream->state = SDW_STREAM_DISABLED;
/* Program params */ list_for_each_entry(m_rt, &stream->master_list, stream_node) {
ret = sdw_program_params(bus); bus = m_rt->bus;
if (ret < 0) { /* Program params */
dev_err(bus->dev, "Program params failed: %d", ret); ret = sdw_program_params(bus);
return ret; if (ret < 0) {
dev_err(bus->dev, "Program params failed: %d", ret);
return ret;
}
} }
return do_bank_switch(stream); return do_bank_switch(stream);
...@@ -1422,43 +1650,46 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) ...@@ -1422,43 +1650,46 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&stream->m_rt->bus->bus_lock); sdw_acquire_bus_lock(stream);
ret = _sdw_disable_stream(stream); ret = _sdw_disable_stream(stream);
if (ret < 0) if (ret < 0)
pr_err("Disable for stream:%s failed: %d", stream->name, ret); 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; return ret;
} }
EXPORT_SYMBOL(sdw_disable_stream); EXPORT_SYMBOL(sdw_disable_stream);
static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = stream->m_rt; struct sdw_master_runtime *m_rt = NULL;
struct sdw_bus *bus = m_rt->bus; struct sdw_bus *bus = NULL;
int ret = 0; int ret = 0;
/* De-prepare port(s) */ list_for_each_entry(m_rt, &stream->master_list, stream_node) {
ret = sdw_prep_deprep_ports(m_rt, false); bus = m_rt->bus;
if (ret < 0) { /* De-prepare port(s) */
dev_err(bus->dev, "De-prepare port(s) failed: %d", ret); ret = sdw_prep_deprep_ports(m_rt, false);
return ret; if (ret < 0) {
} dev_err(bus->dev, "De-prepare port(s) 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;
/* TODO: Update this during Device-Device support */ /* Program params */
bus->params.bandwidth -= m_rt->stream->params.rate * ret = sdw_program_params(bus);
m_rt->ch_count * m_rt->stream->params.bps; if (ret < 0) {
dev_err(bus->dev, "Program params failed: %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;
} }
stream->state = SDW_STREAM_DEPREPARED;
return do_bank_switch(stream); return do_bank_switch(stream);
} }
...@@ -1478,13 +1709,12 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) ...@@ -1478,13 +1709,12 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&stream->m_rt->bus->bus_lock); sdw_acquire_bus_lock(stream);
ret = _sdw_deprepare_stream(stream); ret = _sdw_deprepare_stream(stream);
if (ret < 0) if (ret < 0)
pr_err("De-prepare for stream:%d failed: %d", ret, ret); 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; return ret;
} }
EXPORT_SYMBOL(sdw_deprepare_stream); EXPORT_SYMBOL(sdw_deprepare_stream);
...@@ -678,6 +678,9 @@ struct sdw_master_ops { ...@@ -678,6 +678,9 @@ struct sdw_master_ops {
* @defer_msg: Defer message * @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed * @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch 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 sdw_bus {
struct device *dev; struct device *dev;
...@@ -694,6 +697,7 @@ struct sdw_bus { ...@@ -694,6 +697,7 @@ struct sdw_bus {
struct sdw_defer defer_msg; struct sdw_defer defer_msg;
unsigned int clk_stop_timeout; unsigned int clk_stop_timeout;
u32 bank_switch_timeout; u32 bank_switch_timeout;
bool multi_link;
}; };
int sdw_add_bus_master(struct sdw_bus *bus); int sdw_add_bus_master(struct sdw_bus *bus);
...@@ -768,14 +772,18 @@ struct sdw_stream_params { ...@@ -768,14 +772,18 @@ struct sdw_stream_params {
* @params: Stream parameters * @params: Stream parameters
* @state: Current state of the stream * @state: Current state of the stream
* @type: Stream type PCM or PDM * @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 { struct sdw_stream_runtime {
char *name; char *name;
struct sdw_stream_params params; struct sdw_stream_params params;
enum sdw_stream_state state; enum sdw_stream_state state;
enum sdw_stream_type type; 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); struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment