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:
Greg Kroah-Hartman 2018-10-02 09:38:04 -07:00
commit 018d52e8b5
8 changed files with 484 additions and 138 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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 @@ static int sdw_bank_switch(struct sdw_bus *bus)
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 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
* 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(&params, &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(&params, &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);

View File

@ -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);