From d1b3285570581992462ffb8500d56ad9383306ef Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:15:27 +0800 Subject: soundwire: bus: add dev_warn() messages to track UNATTACHED devices In rare cases, some devices seem to lose sync and never re-attach on the bus. This seems to happen only when there are more than one device per link, which suggests either an electrical issue, a race condition or a state machine issue. Add two dev_warn() messages to identify the sequence by which the devices become UNATTACHED. BugLink: https://github.com/thesofproject/linux/issues/3063 BugLink: https://github.com/thesofproject/linux/issues/3325 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011527.27930-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 67369e941d0d..354d3f89366f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1749,8 +1749,11 @@ int sdw_handle_slave_status(struct sdw_bus *bus, continue; if (status[i] == SDW_SLAVE_UNATTACHED && - slave->status != SDW_SLAVE_UNATTACHED) + slave->status != SDW_SLAVE_UNATTACHED) { + dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } } if (status[0] == SDW_SLAVE_ATTACHED) { @@ -1785,6 +1788,9 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (slave->status == SDW_SLAVE_UNATTACHED) break; + dev_warn(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n", + i, slave->status); + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); break; -- cgit v1.2.3 From 3957db3ae3dae6f8b8168791f154567fe49e1fd7 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 26 Jan 2022 09:14:51 +0800 Subject: soundwire: intel: fix wrong register name in intel_shim_wake When clearing the sdw wakests status, we should use SDW_SHIM_WAKESTS. Fixes: 4a17c441c7cb ("soundwire: intel: revisit SHIM programming sequences.") Signed-off-by: Libin Yang Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011451.27853-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 122f7a29d8ca..63101f1ba271 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -448,8 +448,8 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) /* Clear wake status */ wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); - wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); - intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts); } mutex_unlock(sdw->link_res->shim_lock); } -- cgit v1.2.3 From 8733729e63001942a40710497bfce39e957d10da Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 26 Jan 2022 20:50:39 +0100 Subject: soundwire: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/3173256.44csPzL39Z@kreacher Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d99807765dfe..824f4f32d4dc 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -180,7 +180,8 @@ static struct sdw_intel_ctx if (!res) return NULL; - if (acpi_bus_get_device(res->handle, &adev)) + adev = acpi_fetch_acpi_dev(res->handle); + if (!adev) return NULL; if (!res->count) @@ -294,13 +295,13 @@ err: static int sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) { - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(ctx->handle); struct sdw_intel_link_dev *ldev; u32 caps; u32 link_mask; int i; - if (acpi_bus_get_device(ctx->handle, &adev)) + if (!adev) return -EINVAL; /* Check SNDWLCAP.LCOUNT */ -- cgit v1.2.3 From ad027fa2986661708b7bb668d984d499dd5aed89 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:16:57 +0800 Subject: soundwire: stream: remove unused parameter in sdw_stream_add_slave The stream parameter is not used, remove before further simplifications. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 980f26d49b66..a30d0fb4871b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -968,14 +968,12 @@ stream_config: * * @slave: Slave handle * @stream_config: Stream configuration - * @stream: Stream runtime handle * * This function is to be called with bus_lock held. */ static struct sdw_slave_runtime *sdw_alloc_slave_rt(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_stream_runtime *stream) + struct sdw_stream_config *stream_config) { struct sdw_slave_runtime *s_rt; @@ -1367,7 +1365,7 @@ int sdw_stream_add_slave(struct sdw_slave *slave, goto error; } - s_rt = sdw_alloc_slave_rt(slave, stream_config, stream); + s_rt = sdw_alloc_slave_rt(slave, stream_config); if (!s_rt) { dev_err(&slave->dev, "Slave runtime config failed for stream:%s\n", -- cgit v1.2.3 From 823ca8853d6074bb70995d08104930b30cf38f55 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:16:58 +0800 Subject: soundwire: stream: add slave runtime to list earlier sdw_config_stream() only verifies the compatibility between information provided by the Slave driver and the stream configuration. There is no problem if we add the slave runtime to the list earlier. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a30d0fb4871b..a75d3576bfcf 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1373,20 +1373,11 @@ int sdw_stream_add_slave(struct sdw_slave *slave, ret = -ENOMEM; goto stream_error; } + list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); ret = sdw_config_stream(&slave->dev, stream, stream_config, true); - if (ret) { - /* - * sdw_release_master_stream will release s_rt in slave_rt_list in - * stream_error case, but s_rt is only added to slave_rt_list - * when sdw_config_stream is successful, so free s_rt explicitly - * when sdw_config_stream is failed. - */ - kfree(s_rt); + if (ret) goto stream_error; - } - - list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); if (ret) -- cgit v1.2.3 From 5ec17b98f1d4af7035ca1824f7218a0026ebbf1b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:16:59 +0800 Subject: soundwire: stream: simplify check on port range Pass the index directly to sdw_is_valid_port_range(), this will be useful for further simplifications. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a75d3576bfcf..3ac2e5a66700 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1177,12 +1177,10 @@ static int sdw_config_stream(struct device *dev, return 0; } -static int sdw_is_valid_port_range(struct device *dev, - struct sdw_port_runtime *p_rt) +static int sdw_is_valid_port_range(struct device *dev, int num) { - if (!SDW_VALID_PORT_RANGE(p_rt->num)) { - dev_err(dev, - "SoundWire: Invalid port number :%d\n", p_rt->num); + if (!SDW_VALID_PORT_RANGE(num)) { + dev_err(dev, "SoundWire: Invalid port number :%d\n", num); return -EINVAL; } @@ -1249,7 +1247,7 @@ static int sdw_slave_port_config(struct sdw_slave *slave, * TODO: Check valid port range as defined by DisCo/ * slave */ - ret = sdw_is_valid_port_range(&slave->dev, p_rt); + ret = sdw_is_valid_port_range(&slave->dev, port_config[i].num); if (ret < 0) { kfree(p_rt); return ret; -- cgit v1.2.3 From 6ccf3292e4ff1d2244677239eba27532bd7b71f9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:00 +0800 Subject: soundwire: stream: add alloc/config/free helpers for ports The existing code only has a config helper that allocates memory, start adding alloc/config/free for ports, as a first step in the simplification of the stream API. This change removes a kfree() on a configuration error, this should have not impact on existing platforms and error handling will be revisited in follow-up patches to make sure invalid configurations have not impact on memory allocation. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 83 +++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 38 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 3ac2e5a66700..49d3a8d2fa31 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -865,6 +865,39 @@ msg_unlock: return ret; } +static struct sdw_port_runtime *sdw_port_alloc(struct list_head *port_list) +{ + struct sdw_port_runtime *p_rt; + + p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); + if (!p_rt) + return NULL; + + list_add_tail(&p_rt->port_node, port_list); + + return p_rt; +} + +static int sdw_port_config(struct sdw_port_runtime *p_rt, + struct sdw_port_config *port_config, + int port_index) +{ + p_rt->ch_mask = port_config[port_index].ch_mask; + p_rt->num = port_config[port_index].num; + + /* + * TODO: Check port capabilities for requested configuration + */ + + return 0; +} + +static void sdw_port_free(struct sdw_port_runtime *p_rt) +{ + list_del(&p_rt->port_node); + kfree(p_rt); +} + /** * sdw_release_stream() - Free the assigned stream runtime * @@ -995,8 +1028,7 @@ static void sdw_master_port_release(struct sdw_bus *bus, struct sdw_port_runtime *p_rt, *_p_rt; list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); + sdw_port_free(p_rt); } } @@ -1015,8 +1047,7 @@ static void sdw_slave_port_release(struct sdw_bus *bus, list_for_each_entry_safe(p_rt, _p_rt, &s_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); + sdw_port_free(p_rt); } } } @@ -1187,43 +1218,24 @@ static int sdw_is_valid_port_range(struct device *dev, int num) return 0; } -static struct sdw_port_runtime -*sdw_port_alloc(struct device *dev, - struct sdw_port_config *port_config, - int port_index) -{ - struct sdw_port_runtime *p_rt; - - p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); - if (!p_rt) - return NULL; - - p_rt->ch_mask = port_config[port_index].ch_mask; - p_rt->num = port_config[port_index].num; - - return p_rt; -} - static int sdw_master_port_config(struct sdw_bus *bus, struct sdw_master_runtime *m_rt, struct sdw_port_config *port_config, unsigned int num_ports) { struct sdw_port_runtime *p_rt; + int ret; int i; /* Iterate for number of ports to perform initialization */ for (i = 0; i < num_ports; i++) { - p_rt = sdw_port_alloc(bus->dev, port_config, i); + p_rt = sdw_port_alloc(&m_rt->port_list); if (!p_rt) return -ENOMEM; - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - - list_add_tail(&p_rt->port_node, &m_rt->port_list); + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; } return 0; @@ -1239,7 +1251,7 @@ static int sdw_slave_port_config(struct sdw_slave *slave, /* Iterate for number of ports to perform initialization */ for (i = 0; i < num_config; i++) { - p_rt = sdw_port_alloc(&slave->dev, port_config, i); + p_rt = sdw_port_alloc(&s_rt->port_list); if (!p_rt) return -ENOMEM; @@ -1248,17 +1260,12 @@ static int sdw_slave_port_config(struct sdw_slave *slave, * slave */ ret = sdw_is_valid_port_range(&slave->dev, port_config[i].num); - if (ret < 0) { - kfree(p_rt); + if (ret < 0) return ret; - } - - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - list_add_tail(&p_rt->port_node, &s_rt->port_list); + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; } return 0; -- cgit v1.2.3 From 2811221a3f64511363b830702ec112ee5a78f85b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:01 +0800 Subject: soundwire: stream: split port allocation and configuration loops Split loops before moving the allocation and configuration to separate functions. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 49d3a8d2fa31..b97c59e71bdb 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1232,10 +1232,14 @@ static int sdw_master_port_config(struct sdw_bus *bus, p_rt = sdw_port_alloc(&m_rt->port_list); if (!p_rt) return -ENOMEM; + } + i = 0; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { ret = sdw_port_config(p_rt, port_config, i); if (ret < 0) return ret; + i++; } return 0; @@ -1254,7 +1258,10 @@ static int sdw_slave_port_config(struct sdw_slave *slave, p_rt = sdw_port_alloc(&s_rt->port_list); if (!p_rt) return -ENOMEM; + } + i = 0; + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { /* * TODO: Check valid port range as defined by DisCo/ * slave @@ -1266,6 +1273,7 @@ static int sdw_slave_port_config(struct sdw_slave *slave, ret = sdw_port_config(p_rt, port_config, i); if (ret < 0) return ret; + i++; } return 0; -- cgit v1.2.3 From 1508876f02553792ca2683b4e7ea9a07ef9361b4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:02 +0800 Subject: soundwire: stream: split alloc and config in two functions Continue the split with two functions for master and slave, and remove unused arguments. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-7-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 49 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b97c59e71bdb..e3cb55de0d12 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1218,13 +1218,10 @@ static int sdw_is_valid_port_range(struct device *dev, int num) return 0; } -static int sdw_master_port_config(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt, - struct sdw_port_config *port_config, - unsigned int num_ports) +static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, + unsigned int num_ports) { struct sdw_port_runtime *p_rt; - int ret; int i; /* Iterate for number of ports to perform initialization */ @@ -1234,6 +1231,16 @@ static int sdw_master_port_config(struct sdw_bus *bus, return -ENOMEM; } + return 0; +} + +static int sdw_master_port_config(struct sdw_master_runtime *m_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; + i = 0; list_for_each_entry(p_rt, &m_rt->port_list, port_node) { ret = sdw_port_config(p_rt, port_config, i); @@ -1245,13 +1252,12 @@ static int sdw_master_port_config(struct sdw_bus *bus, return 0; } -static int sdw_slave_port_config(struct sdw_slave *slave, - struct sdw_slave_runtime *s_rt, - struct sdw_port_config *port_config, - unsigned int num_config) +static int sdw_slave_port_alloc(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + unsigned int num_config) { struct sdw_port_runtime *p_rt; - int i, ret; + int i; /* Iterate for number of ports to perform initialization */ for (i = 0; i < num_config; i++) { @@ -1260,6 +1266,17 @@ static int sdw_slave_port_config(struct sdw_slave *slave, return -ENOMEM; } + return 0; +} + +static int sdw_slave_port_config(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; + i = 0; list_for_each_entry(p_rt, &s_rt->port_list, port_node) { /* @@ -1324,7 +1341,11 @@ int sdw_stream_add_master(struct sdw_bus *bus, if (ret) goto stream_error; - ret = sdw_master_port_config(bus, m_rt, port_config, num_ports); + ret = sdw_master_port_alloc(m_rt, num_ports); + if (ret) + goto stream_error; + + ret = sdw_master_port_config(m_rt, port_config); if (ret) goto stream_error; @@ -1392,7 +1413,11 @@ int sdw_stream_add_slave(struct sdw_slave *slave, if (ret) goto stream_error; - ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); + if (ret) + goto stream_error; + + ret = sdw_slave_port_config(slave, s_rt, port_config); if (ret) goto stream_error; -- cgit v1.2.3 From 4bbd6d55a6789e99e74df96cb87a4ed29bbf7f7f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:03 +0800 Subject: soundwire: stream: add 'slave' prefix for port range checks We can only check for Slave port ranges, the ports are not defined at the Master level. Also move the function to the 'slave port' block. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-8-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e3cb55de0d12..c326298a0fe2 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1208,16 +1208,6 @@ static int sdw_config_stream(struct device *dev, return 0; } -static int sdw_is_valid_port_range(struct device *dev, int num) -{ - if (!SDW_VALID_PORT_RANGE(num)) { - dev_err(dev, "SoundWire: Invalid port number :%d\n", num); - return -EINVAL; - } - - return 0; -} - static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, unsigned int num_ports) { @@ -1269,6 +1259,16 @@ static int sdw_slave_port_alloc(struct sdw_slave *slave, return 0; } +static int sdw_slave_port_is_valid_range(struct device *dev, int num) +{ + if (!SDW_VALID_PORT_RANGE(num)) { + dev_err(dev, "SoundWire: Invalid port number :%d\n", num); + return -EINVAL; + } + + return 0; +} + static int sdw_slave_port_config(struct sdw_slave *slave, struct sdw_slave_runtime *s_rt, struct sdw_port_config *port_config) @@ -1283,7 +1283,7 @@ static int sdw_slave_port_config(struct sdw_slave *slave, * TODO: Check valid port range as defined by DisCo/ * slave */ - ret = sdw_is_valid_port_range(&slave->dev, port_config[i].num); + ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num); if (ret < 0) return ret; -- cgit v1.2.3 From c7aa9d770e23ab295ce45729a6134833426e1473 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:04 +0800 Subject: soundwire: stream: group sdw_port and sdw_master/slave_port functions re-group all the helpers in one location with a code move. For consistency the 'slave' helpers are placed before the 'master' helpers. Also remove unused arguments and rename the 'release' function to 'free' for consistency. No functional change in this patch. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-9-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 242 ++++++++++++++++++++++----------------------- 1 file changed, 120 insertions(+), 122 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index c326298a0fe2..5e2d29448aaf 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -898,6 +898,123 @@ static void sdw_port_free(struct sdw_port_runtime *p_rt) kfree(p_rt); } +static void sdw_slave_port_free(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + struct sdw_port_runtime *p_rt, *_p_rt; + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + + 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) { + if (s_rt->slave != slave) + continue; + + list_for_each_entry_safe(p_rt, _p_rt, + &s_rt->port_list, port_node) { + sdw_port_free(p_rt); + } + } + } +} + +static int sdw_slave_port_alloc(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + unsigned int num_config) +{ + struct sdw_port_runtime *p_rt; + int i; + + /* Iterate for number of ports to perform initialization */ + for (i = 0; i < num_config; i++) { + p_rt = sdw_port_alloc(&s_rt->port_list); + if (!p_rt) + return -ENOMEM; + } + + return 0; +} + +static int sdw_slave_port_is_valid_range(struct device *dev, int num) +{ + if (!SDW_VALID_PORT_RANGE(num)) { + dev_err(dev, "SoundWire: Invalid port number :%d\n", num); + return -EINVAL; + } + + return 0; +} + +static int sdw_slave_port_config(struct sdw_slave *slave, + struct sdw_slave_runtime *s_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; + + i = 0; + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + /* + * TODO: Check valid port range as defined by DisCo/ + * slave + */ + ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num); + if (ret < 0) + return ret; + + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; + i++; + } + + return 0; +} + +static void sdw_master_port_free(struct sdw_master_runtime *m_rt) +{ + struct sdw_port_runtime *p_rt, *_p_rt; + + list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) { + sdw_port_free(p_rt); + } +} + +static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, + unsigned int num_ports) +{ + struct sdw_port_runtime *p_rt; + int i; + + /* Iterate for number of ports to perform initialization */ + for (i = 0; i < num_ports; i++) { + p_rt = sdw_port_alloc(&m_rt->port_list); + if (!p_rt) + return -ENOMEM; + } + + return 0; +} + +static int sdw_master_port_config(struct sdw_master_runtime *m_rt, + struct sdw_port_config *port_config) +{ + struct sdw_port_runtime *p_rt; + int ret; + int i; + + i = 0; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + ret = sdw_port_config(p_rt, port_config, i); + if (ret < 0) + return ret; + i++; + } + + return 0; +} + /** * sdw_release_stream() - Free the assigned stream runtime * @@ -1022,37 +1139,6 @@ static struct sdw_slave_runtime return s_rt; } -static void sdw_master_port_release(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt) -{ - struct sdw_port_runtime *p_rt, *_p_rt; - - list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) { - sdw_port_free(p_rt); - } -} - -static void sdw_slave_port_release(struct sdw_bus *bus, - struct sdw_slave *slave, - struct sdw_stream_runtime *stream) -{ - struct sdw_port_runtime *p_rt, *_p_rt; - struct sdw_master_runtime *m_rt; - struct sdw_slave_runtime *s_rt; - - 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) { - if (s_rt->slave != slave) - continue; - - list_for_each_entry_safe(p_rt, _p_rt, - &s_rt->port_list, port_node) { - sdw_port_free(p_rt); - } - } - } -} - /** * sdw_release_slave_stream() - Free Slave(s) runtime handle * @@ -1097,7 +1183,7 @@ static void sdw_release_master_stream(struct sdw_master_runtime *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) { - sdw_slave_port_release(s_rt->slave->bus, s_rt->slave, stream); + sdw_slave_port_free(s_rt->slave, stream); sdw_release_slave_stream(s_rt->slave, stream); } @@ -1126,7 +1212,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus, if (m_rt->bus != bus) continue; - sdw_master_port_release(bus, m_rt); + sdw_master_port_free(m_rt); sdw_release_master_stream(m_rt, stream); stream->m_rt_count--; } @@ -1153,7 +1239,7 @@ int sdw_stream_remove_slave(struct sdw_slave *slave, { mutex_lock(&slave->bus->bus_lock); - sdw_slave_port_release(slave->bus, slave, stream); + sdw_slave_port_free(slave, stream); sdw_release_slave_stream(slave, stream); mutex_unlock(&slave->bus->bus_lock); @@ -1208,94 +1294,6 @@ static int sdw_config_stream(struct device *dev, return 0; } -static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, - unsigned int num_ports) -{ - struct sdw_port_runtime *p_rt; - int i; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_ports; i++) { - p_rt = sdw_port_alloc(&m_rt->port_list); - if (!p_rt) - return -ENOMEM; - } - - return 0; -} - -static int sdw_master_port_config(struct sdw_master_runtime *m_rt, - struct sdw_port_config *port_config) -{ - struct sdw_port_runtime *p_rt; - int ret; - int i; - - i = 0; - list_for_each_entry(p_rt, &m_rt->port_list, port_node) { - ret = sdw_port_config(p_rt, port_config, i); - if (ret < 0) - return ret; - i++; - } - - return 0; -} - -static int sdw_slave_port_alloc(struct sdw_slave *slave, - struct sdw_slave_runtime *s_rt, - unsigned int num_config) -{ - struct sdw_port_runtime *p_rt; - int i; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_config; i++) { - p_rt = sdw_port_alloc(&s_rt->port_list); - if (!p_rt) - return -ENOMEM; - } - - return 0; -} - -static int sdw_slave_port_is_valid_range(struct device *dev, int num) -{ - if (!SDW_VALID_PORT_RANGE(num)) { - dev_err(dev, "SoundWire: Invalid port number :%d\n", num); - return -EINVAL; - } - - return 0; -} - -static int sdw_slave_port_config(struct sdw_slave *slave, - struct sdw_slave_runtime *s_rt, - struct sdw_port_config *port_config) -{ - struct sdw_port_runtime *p_rt; - int ret; - int i; - - i = 0; - list_for_each_entry(p_rt, &s_rt->port_list, port_node) { - /* - * TODO: Check valid port range as defined by DisCo/ - * slave - */ - ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num); - if (ret < 0) - return ret; - - ret = sdw_port_config(p_rt, port_config, i); - if (ret < 0) - return ret; - i++; - } - - return 0; -} - /** * sdw_stream_add_master() - Allocate and add master runtime to a stream * -- cgit v1.2.3 From 1a21892d59b985f634371fa9ce4c8b4f4a286f87 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:05 +0800 Subject: soundwire: stream: simplify sdw_alloc_master_rt() Only do the allocation in that function, and move check for allocation in the caller. This will it easier to split allocation and configuration. No functionality change in this patch. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-10-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 5e2d29448aaf..263b76230f8f 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1085,14 +1085,6 @@ static struct sdw_master_runtime { struct sdw_master_runtime *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; - m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); if (!m_rt) return NULL; @@ -1104,7 +1096,6 @@ static struct sdw_master_runtime list_add_tail(&m_rt->bus_node, &bus->m_rt_list); -stream_config: m_rt->ch_count = stream_config->ch_count; m_rt->bus = bus; m_rt->stream = stream; @@ -1326,6 +1317,14 @@ int sdw_stream_add_master(struct sdw_bus *bus, goto unlock; } + /* + * check if Master is already allocated (e.g. as a result of Slave adding + * it first), if so skip allocation and go to configuration + */ + m_rt = sdw_find_master_rt(bus, stream); + if (m_rt) + goto skip_alloc_master_rt; + m_rt = sdw_alloc_master_rt(bus, stream_config, stream); if (!m_rt) { dev_err(bus->dev, @@ -1335,6 +1334,7 @@ int sdw_stream_add_master(struct sdw_bus *bus, goto unlock; } +skip_alloc_master_rt: ret = sdw_config_stream(bus->dev, stream, stream_config, false); if (ret) goto stream_error; @@ -1384,6 +1384,14 @@ int sdw_stream_add_slave(struct sdw_slave *slave, mutex_lock(&slave->bus->bus_lock); + /* + * check if Master is already allocated, if so skip allocation + * and go to configuration + */ + m_rt = sdw_find_master_rt(slave->bus, stream); + if (m_rt) + goto skip_alloc_master_rt; + /* * If this API is invoked by Slave first then m_rt is not valid. * So, allocate m_rt and add Slave to it. @@ -1397,6 +1405,7 @@ int sdw_stream_add_slave(struct sdw_slave *slave, goto error; } +skip_alloc_master_rt: s_rt = sdw_alloc_slave_rt(slave, stream_config); if (!s_rt) { dev_err(&slave->dev, -- cgit v1.2.3 From bb10659a6f84c21a5a04bff43908eb4014393cf2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:06 +0800 Subject: soundwire: stream: split sdw_alloc_master_rt() in alloc and config Split the two parts so that we can do multiple configurations during ALSA/ASoC hw_params stage. Also follow existing convention sdw__ used at lower level. No functionality change here. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-11-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 51 ++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 17 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 263b76230f8f..e38c9208c77b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1055,7 +1055,7 @@ struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) EXPORT_SYMBOL(sdw_alloc_stream); static struct sdw_master_runtime -*sdw_find_master_rt(struct sdw_bus *bus, +*sdw_master_rt_find(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; @@ -1070,17 +1070,15 @@ static struct sdw_master_runtime } /** - * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle + * sdw_master_rt_alloc() - Allocates a Master runtime handle * * @bus: SDW bus instance - * @stream_config: Stream configuration * @stream: Stream runtime handle. * * This function is to be called with bus_lock held. */ static struct sdw_master_runtime -*sdw_alloc_master_rt(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, +*sdw_master_rt_alloc(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; @@ -1096,14 +1094,30 @@ static struct sdw_master_runtime list_add_tail(&m_rt->bus_node, &bus->m_rt_list); - m_rt->ch_count = stream_config->ch_count; m_rt->bus = bus; m_rt->stream = stream; - m_rt->direction = stream_config->direction; return m_rt; } +/** + * sdw_master_rt_config() - Configure Master runtime handle + * + * @m_rt: Master runtime handle + * @stream_config: Stream configuration + * + * This function is to be called with bus_lock held. + */ + +static int sdw_master_rt_config(struct sdw_master_runtime *m_rt, + struct sdw_stream_config *stream_config) +{ + m_rt->ch_count = stream_config->ch_count; + m_rt->direction = stream_config->direction; + + return 0; +} + /** * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. * @@ -1321,19 +1335,21 @@ int sdw_stream_add_master(struct sdw_bus *bus, * check if Master is already allocated (e.g. as a result of Slave adding * it first), if so skip allocation and go to configuration */ - m_rt = sdw_find_master_rt(bus, stream); + m_rt = sdw_master_rt_find(bus, stream); if (m_rt) goto skip_alloc_master_rt; - m_rt = sdw_alloc_master_rt(bus, stream_config, stream); + m_rt = sdw_master_rt_alloc(bus, stream); if (!m_rt) { - dev_err(bus->dev, - "Master runtime config failed for stream:%s\n", - stream->name); + dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name); ret = -ENOMEM; goto unlock; } + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret < 0) + goto unlock; + skip_alloc_master_rt: ret = sdw_config_stream(bus->dev, stream, stream_config, false); if (ret) @@ -1388,7 +1404,7 @@ int sdw_stream_add_slave(struct sdw_slave *slave, * check if Master is already allocated, if so skip allocation * and go to configuration */ - m_rt = sdw_find_master_rt(slave->bus, stream); + m_rt = sdw_master_rt_find(slave->bus, stream); if (m_rt) goto skip_alloc_master_rt; @@ -1396,14 +1412,15 @@ int sdw_stream_add_slave(struct sdw_slave *slave, * If this API is invoked by Slave first then m_rt is not valid. * So, allocate m_rt and add Slave to it. */ - m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream); + m_rt = sdw_master_rt_alloc(slave->bus, stream); if (!m_rt) { - dev_err(&slave->dev, - "alloc master runtime failed for stream:%s\n", - stream->name); + dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name); ret = -ENOMEM; goto error; } + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret < 0) + goto stream_error; skip_alloc_master_rt: s_rt = sdw_alloc_slave_rt(slave, stream_config); -- cgit v1.2.3 From bf75ba4bdba85c9f53245526040d9a75399918a7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:07 +0800 Subject: soundwire: stream: move sdw_alloc_slave_rt() before 'master' helpers Code move before splitting the function in two. No functionality change. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-12-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e38c9208c77b..eef2e5fd245e 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1054,6 +1054,32 @@ struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) } EXPORT_SYMBOL(sdw_alloc_stream); +/** + * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. + * + * @slave: Slave handle + * @stream_config: Stream configuration + * + * This function is to be called with bus_lock held. + */ +static struct sdw_slave_runtime +*sdw_alloc_slave_rt(struct sdw_slave *slave, + struct sdw_stream_config *stream_config) +{ + struct sdw_slave_runtime *s_rt; + + s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL); + if (!s_rt) + return NULL; + + INIT_LIST_HEAD(&s_rt->port_list); + s_rt->ch_count = stream_config->ch_count; + s_rt->direction = stream_config->direction; + s_rt->slave = slave; + + return s_rt; +} + static struct sdw_master_runtime *sdw_master_rt_find(struct sdw_bus *bus, struct sdw_stream_runtime *stream) @@ -1118,32 +1144,6 @@ static int sdw_master_rt_config(struct sdw_master_runtime *m_rt, return 0; } -/** - * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. - * - * @slave: Slave handle - * @stream_config: Stream configuration - * - * This function is to be called with bus_lock held. - */ -static struct sdw_slave_runtime -*sdw_alloc_slave_rt(struct sdw_slave *slave, - struct sdw_stream_config *stream_config) -{ - struct sdw_slave_runtime *s_rt; - - s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL); - if (!s_rt) - return NULL; - - INIT_LIST_HEAD(&s_rt->port_list); - s_rt->ch_count = stream_config->ch_count; - s_rt->direction = stream_config->direction; - s_rt->slave = slave; - - return s_rt; -} - /** * sdw_release_slave_stream() - Free Slave(s) runtime handle * -- cgit v1.2.3 From edd5cf99a715afbf394118c366ed0c1427918eff Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:08 +0800 Subject: soundwire: stream: split sdw_alloc_slave_rt() in alloc and config Split the two parts so that we can do multiple configurations during ALSA/ASoC hw_params stage. Also follow existing convention sdw__ used at lower level. No functionality change here. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-13-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index eef2e5fd245e..b7ccfa5a9cfc 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1055,16 +1055,14 @@ struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) EXPORT_SYMBOL(sdw_alloc_stream); /** - * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. + * sdw_slave_rt_alloc() - Allocate a Slave runtime handle. * * @slave: Slave handle - * @stream_config: Stream configuration * * This function is to be called with bus_lock held. */ static struct sdw_slave_runtime -*sdw_alloc_slave_rt(struct sdw_slave *slave, - struct sdw_stream_config *stream_config) +*sdw_slave_rt_alloc(struct sdw_slave *slave) { struct sdw_slave_runtime *s_rt; @@ -1073,13 +1071,28 @@ static struct sdw_slave_runtime return NULL; INIT_LIST_HEAD(&s_rt->port_list); - s_rt->ch_count = stream_config->ch_count; - s_rt->direction = stream_config->direction; s_rt->slave = slave; return s_rt; } +/** + * sdw_slave_rt_config() - Configure a Slave runtime handle. + * + * @s_rt: Slave runtime handle + * @stream_config: Stream configuration + * + * This function is to be called with bus_lock held. + */ +static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt, + struct sdw_stream_config *stream_config) +{ + s_rt->ch_count = stream_config->ch_count; + s_rt->direction = stream_config->direction; + + return 0; +} + static struct sdw_master_runtime *sdw_master_rt_find(struct sdw_bus *bus, struct sdw_stream_runtime *stream) @@ -1423,16 +1436,18 @@ int sdw_stream_add_slave(struct sdw_slave *slave, goto stream_error; skip_alloc_master_rt: - s_rt = sdw_alloc_slave_rt(slave, stream_config); + s_rt = sdw_slave_rt_alloc(slave); if (!s_rt) { - dev_err(&slave->dev, - "Slave runtime config failed for stream:%s\n", - stream->name); + dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); ret = -ENOMEM; goto stream_error; } list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); + ret = sdw_slave_rt_config(s_rt, stream_config); + if (ret) + goto stream_error; + ret = sdw_config_stream(&slave->dev, stream, stream_config, true); if (ret) goto stream_error; -- cgit v1.2.3 From 7a908906d0c115dc23e9f678e2f276c2c7532000 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:09 +0800 Subject: soundwire: stream: group sdw_stream_ functions Group all exported functions prior to split of add in alloc/config stages necessary for support of multiple calls to hw_params() by ALSA/ASoC core. Pure code move, no functionality change. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-14-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 670 ++++++++++++++++++++++----------------------- 1 file changed, 335 insertions(+), 335 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b7ccfa5a9cfc..f939121c4b41 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1015,45 +1015,6 @@ static int sdw_master_port_config(struct sdw_master_runtime *m_rt, return 0; } -/** - * sdw_release_stream() - Free the assigned stream runtime - * - * @stream: SoundWire stream runtime - * - * sdw_release_stream should be called only once per stream - */ -void sdw_release_stream(struct sdw_stream_runtime *stream) -{ - kfree(stream); -} -EXPORT_SYMBOL(sdw_release_stream); - -/** - * sdw_alloc_stream() - Allocate and return stream runtime - * - * @stream_name: SoundWire stream name - * - * Allocates a SoundWire stream runtime instance. - * sdw_alloc_stream should be called only once per stream. Typically - * invoked from ALSA/ASoC machine/platform driver. - */ -struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) -{ - struct sdw_stream_runtime *stream; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - 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); - /** * sdw_slave_rt_alloc() - Allocate a Slave runtime handle. * @@ -1210,62 +1171,6 @@ static void sdw_release_master_stream(struct sdw_master_runtime *m_rt, kfree(m_rt); } -/** - * sdw_stream_remove_master() - Remove master from sdw_stream - * - * @bus: SDW Bus instance - * @stream: SoundWire stream - * - * This removes and frees port_rt and master_rt from a 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); - - list_for_each_entry_safe(m_rt, _m_rt, - &stream->master_list, stream_node) { - if (m_rt->bus != bus) - continue; - - sdw_master_port_free(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); - - return 0; -} -EXPORT_SYMBOL(sdw_stream_remove_master); - -/** - * sdw_stream_remove_slave() - Remove slave from sdw_stream - * - * @slave: SDW Slave instance - * @stream: SoundWire stream - * - * This removes and frees port_rt and slave_rt from a stream - */ -int sdw_stream_remove_slave(struct sdw_slave *slave, - struct sdw_stream_runtime *stream) -{ - mutex_lock(&slave->bus->bus_lock); - - sdw_slave_port_free(slave, stream); - sdw_release_slave_stream(slave, stream); - - mutex_unlock(&slave->bus->bus_lock); - - return 0; -} -EXPORT_SYMBOL(sdw_stream_remove_slave); - /** * sdw_config_stream() - Configure the allocated stream * @@ -1313,269 +1218,100 @@ static int sdw_config_stream(struct device *dev, } /** - * sdw_stream_add_master() - Allocate and add master runtime to a stream + * sdw_get_slave_dpn_prop() - Get Slave port capabilities * - * @bus: SDW Bus instance - * @stream_config: Stream configuration for audio stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * @stream: SoundWire stream + * @slave: Slave handle + * @direction: Data direction. + * @port_num: Port number */ -int sdw_stream_add_master(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) +struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, + enum sdw_data_direction direction, + unsigned int port_num) { - struct sdw_master_runtime *m_rt; - int ret; - - mutex_lock(&bus->bus_lock); + struct sdw_dpn_prop *dpn_prop; + u8 num_ports; + int i; - /* - * 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\n", bus->link_id); - ret = -EINVAL; - goto unlock; + if (direction == SDW_DATA_DIR_TX) { + num_ports = hweight32(slave->prop.source_ports); + dpn_prop = slave->prop.src_dpn_prop; + } else { + num_ports = hweight32(slave->prop.sink_ports); + dpn_prop = slave->prop.sink_dpn_prop; } - /* - * check if Master is already allocated (e.g. as a result of Slave adding - * it first), if so skip allocation and go to configuration - */ - m_rt = sdw_master_rt_find(bus, stream); - if (m_rt) - goto skip_alloc_master_rt; - - m_rt = sdw_master_rt_alloc(bus, stream); - if (!m_rt) { - dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name); - ret = -ENOMEM; - goto unlock; + for (i = 0; i < num_ports; i++) { + if (dpn_prop[i].num == port_num) + return &dpn_prop[i]; } - ret = sdw_master_rt_config(m_rt, stream_config); - if (ret < 0) - goto unlock; - -skip_alloc_master_rt: - ret = sdw_config_stream(bus->dev, stream, stream_config, false); - if (ret) - goto stream_error; - - ret = sdw_master_port_alloc(m_rt, num_ports); - if (ret) - goto stream_error; - - ret = sdw_master_port_config(m_rt, port_config); - if (ret) - goto stream_error; + return NULL; +} - stream->m_rt_count++; +/** + * 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; + struct sdw_bus *bus; - goto unlock; + /* Iterate for all Master(s) in Master list */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; -stream_error: - sdw_release_master_stream(m_rt, stream); -unlock: - mutex_unlock(&bus->bus_lock); - return ret; + mutex_lock(&bus->bus_lock); + } } -EXPORT_SYMBOL(sdw_stream_add_master); /** - * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream + * sdw_release_bus_lock: Release bus lock for all Master runtime(s) * - * @slave: SDW Slave instance - * @stream_config: Stream configuration for audio stream * @stream: SoundWire stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * - * It is expected that Slave is added before adding Master - * to the 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. */ -int sdw_stream_add_slave(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) +static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt; + struct sdw_bus *bus; + + /* 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, + bool update_params) { - struct sdw_slave_runtime *s_rt; struct sdw_master_runtime *m_rt; + struct sdw_bus *bus = NULL; + struct sdw_master_prop *prop; + struct sdw_bus_params params; int ret; - mutex_lock(&slave->bus->bus_lock); + /* 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)); - /* - * check if Master is already allocated, if so skip allocation - * and go to configuration - */ - m_rt = sdw_master_rt_find(slave->bus, stream); - if (m_rt) - goto skip_alloc_master_rt; - - /* - * If this API is invoked by Slave first then m_rt is not valid. - * So, allocate m_rt and add Slave to it. - */ - m_rt = sdw_master_rt_alloc(slave->bus, stream); - if (!m_rt) { - dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name); - ret = -ENOMEM; - goto error; - } - ret = sdw_master_rt_config(m_rt, stream_config); - if (ret < 0) - goto stream_error; - -skip_alloc_master_rt: - s_rt = sdw_slave_rt_alloc(slave); - if (!s_rt) { - dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); - ret = -ENOMEM; - goto stream_error; - } - list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); - - ret = sdw_slave_rt_config(s_rt, stream_config); - if (ret) - goto stream_error; - - ret = sdw_config_stream(&slave->dev, stream, stream_config, true); - if (ret) - goto stream_error; - - ret = sdw_slave_port_alloc(slave, s_rt, num_ports); - if (ret) - goto stream_error; - - ret = sdw_slave_port_config(slave, s_rt, port_config); - if (ret) - goto stream_error; - - /* - * Change stream state to CONFIGURED on first Slave add. - * Bus is not aware of number of Slave(s) in a stream at this - * point so cannot depend on all Slave(s) to be added in order to - * change stream state to CONFIGURED. - */ - stream->state = SDW_STREAM_CONFIGURED; - goto error; - -stream_error: - /* - * we hit error so cleanup the stream, release all Slave(s) and - * Master runtime - */ - sdw_release_master_stream(m_rt, stream); -error: - mutex_unlock(&slave->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_stream_add_slave); - -/** - * sdw_get_slave_dpn_prop() - Get Slave port capabilities - * - * @slave: Slave handle - * @direction: Data direction. - * @port_num: Port number - */ -struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, - enum sdw_data_direction direction, - unsigned int port_num) -{ - struct sdw_dpn_prop *dpn_prop; - u8 num_ports; - int i; - - if (direction == SDW_DATA_DIR_TX) { - num_ports = hweight32(slave->prop.source_ports); - dpn_prop = slave->prop.src_dpn_prop; - } else { - num_ports = hweight32(slave->prop.sink_ports); - dpn_prop = slave->prop.sink_dpn_prop; - } - - for (i = 0; i < num_ports; i++) { - if (dpn_prop[i].num == port_num) - return &dpn_prop[i]; - } - - 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; - struct sdw_bus *bus; - - /* 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; - struct sdw_bus *bus; - - /* 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, - bool update_params) -{ - struct sdw_master_runtime *m_rt; - struct sdw_bus *bus = NULL; - struct sdw_master_prop *prop; - struct sdw_bus_params params; - int ret; - - /* 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_clk_freq % stream->params.rate) != 0) { - dev_err(bus->dev, "Async mode not supported\n"); - return -EINVAL; - } + /* TODO: Support Asynchronous mode */ + if ((prop->max_clk_freq % stream->params.rate) != 0) { + dev_err(bus->dev, "Async mode not supported\n"); + return -EINVAL; + } if (!update_params) goto program_params; @@ -1939,6 +1675,32 @@ static int set_stream(struct snd_pcm_substream *substream, return ret; } +/** + * sdw_alloc_stream() - Allocate and return stream runtime + * + * @stream_name: SoundWire stream name + * + * Allocates a SoundWire stream runtime instance. + * sdw_alloc_stream should be called only once per stream. Typically + * invoked from ALSA/ASoC machine/platform driver. + */ +struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name) +{ + struct sdw_stream_runtime *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + 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); + /** * sdw_startup_stream() - Startup SoundWire stream * @@ -2015,3 +1777,241 @@ void sdw_shutdown_stream(void *sdw_substream) set_stream(substream, NULL); } EXPORT_SYMBOL(sdw_shutdown_stream); + +/** + * sdw_release_stream() - Free the assigned stream runtime + * + * @stream: SoundWire stream runtime + * + * sdw_release_stream should be called only once per stream + */ +void sdw_release_stream(struct sdw_stream_runtime *stream) +{ + kfree(stream); +} +EXPORT_SYMBOL(sdw_release_stream); + +/** + * sdw_stream_add_master() - Allocate and add master runtime to a stream + * + * @bus: SDW Bus instance + * @stream_config: Stream configuration for audio stream + * @port_config: Port configuration for audio stream + * @num_ports: Number of ports + * @stream: SoundWire stream + */ +int sdw_stream_add_master(struct sdw_bus *bus, + struct sdw_stream_config *stream_config, + struct sdw_port_config *port_config, + unsigned int num_ports, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt; + int ret; + + 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\n", bus->link_id); + ret = -EINVAL; + goto unlock; + } + + /* + * check if Master is already allocated (e.g. as a result of Slave adding + * it first), if so skip allocation and go to configuration + */ + m_rt = sdw_master_rt_find(bus, stream); + if (m_rt) + goto skip_alloc_master_rt; + + m_rt = sdw_master_rt_alloc(bus, stream); + if (!m_rt) { + dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name); + ret = -ENOMEM; + goto unlock; + } + + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret < 0) + goto unlock; + +skip_alloc_master_rt: + ret = sdw_config_stream(bus->dev, stream, stream_config, false); + if (ret) + goto stream_error; + + ret = sdw_master_port_alloc(m_rt, num_ports); + if (ret) + goto stream_error; + + ret = sdw_master_port_config(m_rt, port_config); + if (ret) + goto stream_error; + + stream->m_rt_count++; + + goto unlock; + +stream_error: + sdw_release_master_stream(m_rt, stream); +unlock: + mutex_unlock(&bus->bus_lock); + return ret; +} +EXPORT_SYMBOL(sdw_stream_add_master); + +/** + * sdw_stream_remove_master() - Remove master from sdw_stream + * + * @bus: SDW Bus instance + * @stream: SoundWire stream + * + * This removes and frees port_rt and master_rt from a 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); + + list_for_each_entry_safe(m_rt, _m_rt, + &stream->master_list, stream_node) { + if (m_rt->bus != bus) + continue; + + sdw_master_port_free(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); + + return 0; +} +EXPORT_SYMBOL(sdw_stream_remove_master); + +/** + * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream + * + * @slave: SDW Slave instance + * @stream_config: Stream configuration for audio stream + * @stream: SoundWire stream + * @port_config: Port configuration for audio stream + * @num_ports: Number of ports + * + * It is expected that Slave is added before adding Master + * to the Stream. + * + */ +int sdw_stream_add_slave(struct sdw_slave *slave, + struct sdw_stream_config *stream_config, + struct sdw_port_config *port_config, + unsigned int num_ports, + struct sdw_stream_runtime *stream) +{ + struct sdw_slave_runtime *s_rt; + struct sdw_master_runtime *m_rt; + int ret; + + mutex_lock(&slave->bus->bus_lock); + + /* + * check if Master is already allocated, if so skip allocation + * and go to configuration + */ + m_rt = sdw_master_rt_find(slave->bus, stream); + if (m_rt) + goto skip_alloc_master_rt; + + /* + * If this API is invoked by Slave first then m_rt is not valid. + * So, allocate m_rt and add Slave to it. + */ + m_rt = sdw_master_rt_alloc(slave->bus, stream); + if (!m_rt) { + dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name); + ret = -ENOMEM; + goto error; + } + ret = sdw_master_rt_config(m_rt, stream_config); + if (ret < 0) + goto stream_error; + +skip_alloc_master_rt: + s_rt = sdw_slave_rt_alloc(slave); + if (!s_rt) { + dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); + ret = -ENOMEM; + goto stream_error; + } + list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); + + ret = sdw_slave_rt_config(s_rt, stream_config); + if (ret) + goto stream_error; + + ret = sdw_config_stream(&slave->dev, stream, stream_config, true); + if (ret) + goto stream_error; + + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); + if (ret) + goto stream_error; + + ret = sdw_slave_port_config(slave, s_rt, port_config); + if (ret) + goto stream_error; + + /* + * Change stream state to CONFIGURED on first Slave add. + * Bus is not aware of number of Slave(s) in a stream at this + * point so cannot depend on all Slave(s) to be added in order to + * change stream state to CONFIGURED. + */ + stream->state = SDW_STREAM_CONFIGURED; + goto error; + +stream_error: + /* + * we hit error so cleanup the stream, release all Slave(s) and + * Master runtime + */ + sdw_release_master_stream(m_rt, stream); +error: + mutex_unlock(&slave->bus->bus_lock); + return ret; +} +EXPORT_SYMBOL(sdw_stream_add_slave); + +/** + * sdw_stream_remove_slave() - Remove slave from sdw_stream + * + * @slave: SDW Slave instance + * @stream: SoundWire stream + * + * This removes and frees port_rt and slave_rt from a stream + */ +int sdw_stream_remove_slave(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + mutex_lock(&slave->bus->bus_lock); + + sdw_slave_port_free(slave, stream); + sdw_release_slave_stream(slave, stream); + + mutex_unlock(&slave->bus->bus_lock); + + return 0; +} +EXPORT_SYMBOL(sdw_stream_remove_slave); -- cgit v1.2.3 From 00ce0d2afe5a8a1a7553bfcfe0e005e54f4d2003 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:10 +0800 Subject: soundwire: stream: rename and move master/slave_rt_free routines The naming is rather inconsistent, use the sdw__ convention, and move the free routine after alloc/config. No functionality change beyond rename/move. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-15-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 72 +++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index f939121c4b41..0f54574abb61 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1054,6 +1054,33 @@ static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt, return 0; } +/** + * sdw_slave_rt_free() - Free Slave(s) runtime handle + * + * @slave: Slave handle. + * @stream: Stream runtime handle. + * + * This function is to be called with bus_lock held. + */ +static void sdw_slave_rt_free(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + struct sdw_slave_runtime *s_rt, *_s_rt; + struct sdw_master_runtime *m_rt; + + 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; + } + } + } +} + static struct sdw_master_runtime *sdw_master_rt_find(struct sdw_bus *bus, struct sdw_stream_runtime *stream) @@ -1119,51 +1146,24 @@ static int sdw_master_rt_config(struct sdw_master_runtime *m_rt, } /** - * sdw_release_slave_stream() - Free Slave(s) runtime handle - * - * @slave: Slave handle. - * @stream: Stream runtime handle. - * - * This function is to be called with bus_lock held. - */ -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; - - 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; - } - } - } -} - -/** - * sdw_release_master_stream() - Free Master runtime handle + * sdw_master_rt_free() - Free Master runtime handle * * @m_rt: Master runtime node * @stream: Stream runtime handle. * * This function is to be called with bus_lock held * It frees the Master runtime handle and associated Slave(s) runtime - * handle. If this is called first then sdw_release_slave_stream() will have + * handle. If this is called first then sdw_slave_rt_free() will have * no effect as Slave(s) runtime handle would already be freed up. */ -static void sdw_release_master_stream(struct sdw_master_runtime *m_rt, - struct sdw_stream_runtime *stream) +static void sdw_master_rt_free(struct sdw_master_runtime *m_rt, + struct sdw_stream_runtime *stream) { 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) { sdw_slave_port_free(s_rt->slave, stream); - sdw_release_slave_stream(s_rt->slave, stream); + sdw_slave_rt_free(s_rt->slave, stream); } list_del(&m_rt->stream_node); @@ -1860,7 +1860,7 @@ skip_alloc_master_rt: goto unlock; stream_error: - sdw_release_master_stream(m_rt, stream); + sdw_master_rt_free(m_rt, stream); unlock: mutex_unlock(&bus->bus_lock); return ret; @@ -1888,7 +1888,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus, continue; sdw_master_port_free(m_rt); - sdw_release_master_stream(m_rt, stream); + sdw_master_rt_free(m_rt, stream); stream->m_rt_count--; } @@ -1987,7 +1987,7 @@ stream_error: * we hit error so cleanup the stream, release all Slave(s) and * Master runtime */ - sdw_release_master_stream(m_rt, stream); + sdw_master_rt_free(m_rt, stream); error: mutex_unlock(&slave->bus->bus_lock); return ret; @@ -2008,7 +2008,7 @@ int sdw_stream_remove_slave(struct sdw_slave *slave, mutex_lock(&slave->bus->bus_lock); sdw_slave_port_free(slave, stream); - sdw_release_slave_stream(slave, stream); + sdw_slave_rt_free(slave, stream); mutex_unlock(&slave->bus->bus_lock); -- cgit v1.2.3 From 42aad41e969988d9058f8751f3fd952c60519308 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:11 +0800 Subject: soundwire: stream: move list addition to sdw_slave_alloc_rt() Simplify sdw_stream_add_slave() by moving the linked list management inside of the sdw_slave_alloc_rt_free() helper, this also makes the alloc/free helpers more symmetrical. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-16-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 0f54574abb61..512e7936d57c 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1019,11 +1019,13 @@ static int sdw_master_port_config(struct sdw_master_runtime *m_rt, * sdw_slave_rt_alloc() - Allocate a Slave runtime handle. * * @slave: Slave handle + * @m_rt: Master runtime handle * * This function is to be called with bus_lock held. */ static struct sdw_slave_runtime -*sdw_slave_rt_alloc(struct sdw_slave *slave) +*sdw_slave_rt_alloc(struct sdw_slave *slave, + struct sdw_master_runtime *m_rt) { struct sdw_slave_runtime *s_rt; @@ -1034,6 +1036,8 @@ static struct sdw_slave_runtime INIT_LIST_HEAD(&s_rt->port_list); s_rt->slave = slave; + list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); + return s_rt; } @@ -1949,13 +1953,12 @@ int sdw_stream_add_slave(struct sdw_slave *slave, goto stream_error; skip_alloc_master_rt: - s_rt = sdw_slave_rt_alloc(slave); + s_rt = sdw_slave_rt_alloc(slave, m_rt); if (!s_rt) { dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); ret = -ENOMEM; goto stream_error; } - list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); ret = sdw_slave_rt_config(s_rt, stream_config); if (ret) -- cgit v1.2.3 From ac3bc88cc56e2712555adc86ee5b2f3a861ffb1e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:12 +0800 Subject: soundwire: stream: separate alloc and config within sdw_stream_add_xxx() Separate alloc and config parts so that follow-up patches can allow for multiple calls to sdw_stream_add_slave/master. This is a feature from the ALSA/ASoC frameworks which is not supported today. This is an invasive patch which modifies the error handling flow, with cleanups only done when an allocation fails. Configuration failures only return an error code. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-17-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 80 +++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 32 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 512e7936d57c..03cfac0129af 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1811,6 +1811,7 @@ int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; + bool alloc_master_rt = true; int ret; mutex_lock(&bus->bus_lock); @@ -1832,8 +1833,10 @@ int sdw_stream_add_master(struct sdw_bus *bus, * it first), if so skip allocation and go to configuration */ m_rt = sdw_master_rt_find(bus, stream); - if (m_rt) + if (m_rt) { + alloc_master_rt = false; goto skip_alloc_master_rt; + } m_rt = sdw_master_rt_alloc(bus, stream); if (!m_rt) { @@ -1841,30 +1844,32 @@ int sdw_stream_add_master(struct sdw_bus *bus, ret = -ENOMEM; goto unlock; } +skip_alloc_master_rt: + + ret = sdw_master_port_alloc(m_rt, num_ports); + if (ret) + goto alloc_error; + + stream->m_rt_count++; ret = sdw_master_rt_config(m_rt, stream_config); if (ret < 0) goto unlock; -skip_alloc_master_rt: ret = sdw_config_stream(bus->dev, stream, stream_config, false); if (ret) - goto stream_error; - - ret = sdw_master_port_alloc(m_rt, num_ports); - if (ret) - goto stream_error; + goto unlock; ret = sdw_master_port_config(m_rt, port_config); - if (ret) - goto stream_error; - - stream->m_rt_count++; goto unlock; -stream_error: - sdw_master_rt_free(m_rt, stream); +alloc_error: + /* + * we only cleanup what was allocated in this routine + */ + if (alloc_master_rt) + sdw_master_rt_free(m_rt, stream); unlock: mutex_unlock(&bus->bus_lock); return ret; @@ -1926,6 +1931,9 @@ int sdw_stream_add_slave(struct sdw_slave *slave, { struct sdw_slave_runtime *s_rt; struct sdw_master_runtime *m_rt; + bool alloc_master_rt = true; + bool alloc_slave_rt = true; + int ret; mutex_lock(&slave->bus->bus_lock); @@ -1935,8 +1943,10 @@ int sdw_stream_add_slave(struct sdw_slave *slave, * and go to configuration */ m_rt = sdw_master_rt_find(slave->bus, stream); - if (m_rt) + if (m_rt) { + alloc_master_rt = false; goto skip_alloc_master_rt; + } /* * If this API is invoked by Slave first then m_rt is not valid. @@ -1946,35 +1956,37 @@ int sdw_stream_add_slave(struct sdw_slave *slave, if (!m_rt) { dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name); ret = -ENOMEM; - goto error; + goto unlock; } - ret = sdw_master_rt_config(m_rt, stream_config); - if (ret < 0) - goto stream_error; skip_alloc_master_rt: s_rt = sdw_slave_rt_alloc(slave, m_rt); if (!s_rt) { dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); + alloc_slave_rt = false; ret = -ENOMEM; - goto stream_error; + goto alloc_error; } - ret = sdw_slave_rt_config(s_rt, stream_config); + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); if (ret) - goto stream_error; + goto alloc_error; - ret = sdw_config_stream(&slave->dev, stream, stream_config, true); + ret = sdw_master_rt_config(m_rt, stream_config); if (ret) - goto stream_error; + goto unlock; - ret = sdw_slave_port_alloc(slave, s_rt, num_ports); + ret = sdw_slave_rt_config(s_rt, stream_config); if (ret) - goto stream_error; + goto unlock; + + ret = sdw_config_stream(&slave->dev, stream, stream_config, true); + if (ret) + goto unlock; ret = sdw_slave_port_config(slave, s_rt, port_config); if (ret) - goto stream_error; + goto unlock; /* * Change stream state to CONFIGURED on first Slave add. @@ -1983,15 +1995,19 @@ skip_alloc_master_rt: * change stream state to CONFIGURED. */ stream->state = SDW_STREAM_CONFIGURED; - goto error; + goto unlock; -stream_error: +alloc_error: /* - * we hit error so cleanup the stream, release all Slave(s) and - * Master runtime + * we only cleanup what was allocated in this routine. The 'else if' + * is intentional, the 'master_rt_free' will call sdw_slave_rt_free() + * internally. */ - sdw_master_rt_free(m_rt, stream); -error: + if (alloc_master_rt) + sdw_master_rt_free(m_rt, stream); + else if (alloc_slave_rt) + sdw_slave_rt_free(slave, stream); +unlock: mutex_unlock(&slave->bus->bus_lock); return ret; } -- cgit v1.2.3 From 5e1df5431f5b68f178c016e7015fb244e9510370 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:13 +0800 Subject: soundwire: stream: introduce sdw_slave_rt_find() helper Before we split the alloc and config steps, we need a helper to find the Slave runtime for a stream. The helper is based on the search loop in sdw_slave_rt_free(), which can now be simplified. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-18-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 03cfac0129af..a52a9ab0eea1 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1058,6 +1058,23 @@ static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt, return 0; } +static struct sdw_slave_runtime *sdw_slave_rt_find(struct sdw_slave *slave, + struct sdw_stream_runtime *stream) +{ + struct sdw_slave_runtime *s_rt, *_s_rt; + struct sdw_master_runtime *m_rt; + + 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) + return s_rt; + } + } + return NULL; +} + /** * sdw_slave_rt_free() - Free Slave(s) runtime handle * @@ -1069,19 +1086,12 @@ static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt, static void sdw_slave_rt_free(struct sdw_slave *slave, struct sdw_stream_runtime *stream) { - struct sdw_slave_runtime *s_rt, *_s_rt; - struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; - 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; - } - } + s_rt = sdw_slave_rt_find(slave, stream); + if (s_rt) { + list_del(&s_rt->m_rt_node); + kfree(s_rt); } } -- cgit v1.2.3 From f3016b891c8c6f11434eab5b92a0bbc21c1dfb6e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:14 +0800 Subject: soundwire: stream: sdw_stream_add_ functions can be called multiple times The sdw_stream_add_slave/master() functions are called from the .hw_params stage. We need to make sure the functions can be called multiple times. In this version, we assume that only 'audio' parameters provide in the hw_params() can change. If the number of ports could change dynamically depending on the stream configuration (number of channels, etc), we would need to free-up all the stream resources and reallocate them. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-19-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a52a9ab0eea1..ccf3c99dd579 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -898,6 +898,11 @@ static void sdw_port_free(struct sdw_port_runtime *p_rt) kfree(p_rt); } +static bool sdw_slave_port_allocated(struct sdw_slave_runtime *s_rt) +{ + return !list_empty(&s_rt->port_list); +} + static void sdw_slave_port_free(struct sdw_slave *slave, struct sdw_stream_runtime *stream) { @@ -972,6 +977,11 @@ static int sdw_slave_port_config(struct sdw_slave *slave, return 0; } +static bool sdw_master_port_allocated(struct sdw_master_runtime *m_rt) +{ + return !list_empty(&m_rt->port_list); +} + static void sdw_master_port_free(struct sdw_master_runtime *m_rt) { struct sdw_port_runtime *p_rt, *_p_rt; @@ -1856,12 +1866,17 @@ int sdw_stream_add_master(struct sdw_bus *bus, } skip_alloc_master_rt: + if (sdw_master_port_allocated(m_rt)) + goto skip_alloc_master_port; + ret = sdw_master_port_alloc(m_rt, num_ports); if (ret) goto alloc_error; stream->m_rt_count++; +skip_alloc_master_port: + ret = sdw_master_rt_config(m_rt, stream_config); if (ret < 0) goto unlock; @@ -1970,6 +1985,10 @@ int sdw_stream_add_slave(struct sdw_slave *slave, } skip_alloc_master_rt: + s_rt = sdw_slave_rt_find(slave, stream); + if (s_rt) + goto skip_alloc_slave_rt; + s_rt = sdw_slave_rt_alloc(slave, m_rt); if (!s_rt) { dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); @@ -1978,10 +1997,15 @@ skip_alloc_master_rt: goto alloc_error; } +skip_alloc_slave_rt: + if (sdw_slave_port_allocated(s_rt)) + goto skip_port_alloc; + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); if (ret) goto alloc_error; +skip_port_alloc: ret = sdw_master_rt_config(m_rt, stream_config); if (ret) goto unlock; -- cgit v1.2.3 From 63fadaa2c7190ad6fc546a8b95e044bd75422dec Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jan 2022 09:17:15 +0800 Subject: soundwire: stream: make enable/disable/deprepare idempotent The stream management currently flags an 'inconsistent state' error when a change is requested multiple times. This was added on purpose to identify programming mistakes. In hindsight, there was no real reason to fail if the logic at the ASoC-DPCM level invokes the same callback multiple times. It's perfectly acceptable to just return and not flag an error when there is nothing to do. The main concern with the state management is to trap errors such as trying to enable a stream that was not prepared first. This patch suggests allowing the stream functions to be idempotent, i.e. they can be called multiple times. Note that the prepare case was already handling multiple calls, this was added in commit c32464c9393d ("soundwire: stream: only prepare stream when it is configured.") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20220126011715.28204-20-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index ccf3c99dd579..f273459b2023 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1505,6 +1505,11 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_ENABLED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", @@ -1588,6 +1593,11 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DISABLED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_ENABLED) { pr_err("%s: %s: inconsistent state state %d\n", __func__, stream->name, stream->state); @@ -1663,6 +1673,11 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_DEPREPARED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { pr_err("%s: %s: inconsistent state state %d\n", -- cgit v1.2.3 From 74e79da9fd46a59788ad58982e63fe9dbc496cb1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 28 Feb 2022 17:25:26 +0000 Subject: soundwire: qcom: add runtime pm support Add support to runtime PM using SoundWire clock stop Mode0 on supported controller instances and soft reset on instances that do not support clock stop. Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220228172528.3489-2-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 54813417ef8e..810232686196 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,9 @@ #include #include "bus.h" +#define SWRM_COMP_SW_RESET 0x008 +#define SWRM_COMP_STATUS 0x014 +#define SWRM_FRM_GEN_ENABLED BIT(0) #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) @@ -29,6 +33,7 @@ #define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15) #define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) +#define SWRM_COMP_MASTER_ID 0x104 #define SWRM_INTERRUPT_STATUS 0x200 #define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) #define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0) @@ -111,6 +116,13 @@ #define SWR_MAX_CMD_ID 14 #define MAX_FIFO_RD_RETRY 3 #define SWR_OVERFLOW_RETRY_COUNT 30 +#define SWRM_LINK_STATUS_RETRY_CNT 100 + +enum { + MASTER_ID_WSA = 1, + MASTER_ID_RX, + MASTER_ID_TX +}; struct qcom_swrm_port_config { u8 si; @@ -159,6 +171,7 @@ struct qcom_swrm_ctrl { u32 slave_status; u32 wr_fifo_depth; u32 rd_fifo_depth; + bool clock_stop_not_supported; }; struct qcom_swrm_data { @@ -497,6 +510,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) u32 i; int devnum; int ret = IRQ_HANDLED; + clk_prepare_enable(swrm->hclk); swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); intr_sts_masked = intr_sts & swrm->intr_mask; @@ -604,6 +618,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) intr_sts_masked = intr_sts & swrm->intr_mask; } while (intr_sts_masked); + clk_disable_unprepare(swrm->hclk); return ret; } @@ -1017,6 +1032,15 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret, i; + ret = pm_runtime_get_sync(ctrl->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(ctrl->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(ctrl->dev); + return ret; + } + sruntime = sdw_alloc_stream(dai->name); if (!sruntime) return -ENOMEM; @@ -1044,6 +1068,9 @@ static void qcom_swrm_shutdown(struct snd_pcm_substream *substream, sdw_release_stream(ctrl->sruntime[dai->id]); ctrl->sruntime[dai->id] = NULL; + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + } static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = { @@ -1197,12 +1224,23 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) static int swrm_reg_show(struct seq_file *s_file, void *data) { struct qcom_swrm_ctrl *swrm = s_file->private; - int reg, reg_val; + int reg, reg_val, ret; + + ret = pm_runtime_get_sync(swrm->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(swrm->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(swrm->dev); + } for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) { swrm->reg_read(swrm, reg, ®_val); seq_printf(s_file, "0x%.3x: 0x%.2x\n", reg, reg_val); } + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + return 0; } @@ -1267,6 +1305,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) ctrl->bus.ops = &qcom_swrm_ops; ctrl->bus.port_ops = &qcom_swrm_port_ops; ctrl->bus.compute_params = &qcom_swrm_compute_params; + ctrl->bus.clk_stop_timeout = 300; ret = qcom_swrm_get_port_config(ctrl); if (ret) @@ -1319,6 +1358,21 @@ static int qcom_swrm_probe(struct platform_device *pdev) (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, ctrl->version & 0xffff); + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* Clk stop is not supported on WSA Soundwire masters */ + if (ctrl->version <= 0x01030000) { + ctrl->clock_stop_not_supported = true; + } else { + ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val); + if (val == MASTER_ID_WSA) + ctrl->clock_stop_not_supported = true; + } + #ifdef CONFIG_DEBUG_FS ctrl->debugfs = debugfs_create_dir("qualcomm-sdw", ctrl->bus.debugfs); debugfs_create_file("qualcomm-registers", 0400, ctrl->debugfs, ctrl, @@ -1345,6 +1399,105 @@ static int qcom_swrm_remove(struct platform_device *pdev) return 0; } +static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm) +{ + int retry = SWRM_LINK_STATUS_RETRY_CNT; + int comp_sts; + + do { + swrm->reg_read(swrm, SWRM_COMP_STATUS, &comp_sts); + + if (comp_sts & SWRM_FRM_GEN_ENABLED) + return true; + + usleep_range(500, 510); + } while (retry--); + + dev_err(swrm->dev, "%s: link status not %s\n", __func__, + comp_sts && SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); + + return false; +} + +static int swrm_runtime_resume(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + int ret; + + clk_prepare_enable(ctrl->hclk); + + if (ctrl->clock_stop_not_supported) { + reinit_completion(&ctrl->enumeration); + ctrl->reg_write(ctrl, SWRM_COMP_SW_RESET, 0x01); + usleep_range(100, 105); + + qcom_swrm_init(ctrl); + + usleep_range(100, 105); + if (!swrm_wait_for_frame_gen_enabled(ctrl)) + dev_err(ctrl->dev, "link failed to connect\n"); + + /* wait for hw enumeration to complete */ + wait_for_completion_timeout(&ctrl->enumeration, + msecs_to_jiffies(TIMEOUT_MS)); + qcom_swrm_get_device_status(ctrl); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); + } else { + ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, + SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET); + + ctrl->intr_mask |= SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + + usleep_range(100, 105); + if (!swrm_wait_for_frame_gen_enabled(ctrl)) + dev_err(ctrl->dev, "link failed to connect\n"); + + ret = sdw_bus_exit_clk_stop(&ctrl->bus); + if (ret < 0) + dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret); + } + + return 0; +} + +static int __maybe_unused swrm_runtime_suspend(struct device *dev) +{ + struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); + int ret; + + if (!ctrl->clock_stop_not_supported) { + /* Mask bus clash interrupt */ + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + /* Prepare slaves for clock stop */ + ret = sdw_bus_prep_clk_stop(&ctrl->bus); + if (ret < 0 && ret != -ENODATA) { + dev_err(dev, "prepare clock stop failed %d", ret); + return ret; + } + + ret = sdw_bus_clk_stop(&ctrl->bus); + if (ret < 0 && ret != -ENODATA) { + dev_err(dev, "bus clock stop failed %d", ret); + return ret; + } + } + + clk_disable_unprepare(ctrl->hclk); + + usleep_range(300, 305); + + return 0; +} + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL) +}; + static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data }, { .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data }, @@ -1359,6 +1512,7 @@ static struct platform_driver qcom_swrm_driver = { .driver = { .name = "qcom-soundwire", .of_match_table = qcom_swrm_of_match, + .pm = &swrm_dev_pm_ops, } }; module_platform_driver(qcom_swrm_driver); -- cgit v1.2.3 From 04d46a7b38375aef945b663a3957f85025f74934 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 28 Feb 2022 17:25:28 +0000 Subject: soundwire: qcom: add in-band wake up interrupt support Some of the Qualcomm SoundWire Controller instances like the ones that are connected to RX path along with Headset connections support Waking up Controller from Low power clock stop state using SoundWire In-band interrupt. SoundWire Slave on the bus would initiate this by pulling the data line high, while the clock is stopped. Add support to this wake up interrupt. Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220228172528.3489-4-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 810232686196..e5c75305a90a 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,7 @@ struct qcom_swrm_ctrl { u8 rd_cmd_id; int irq; unsigned int version; + int wake_irq; int num_din_ports; int num_dout_ports; int cols_index; @@ -503,6 +505,30 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus) return 0; } +static irqreturn_t qcom_swrm_wake_irq_handler(int irq, void *dev_id) +{ + struct qcom_swrm_ctrl *swrm = dev_id; + int ret; + + ret = pm_runtime_get_sync(swrm->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(swrm->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(swrm->dev); + } + + if (swrm->wake_irq > 0) { + if (!irqd_irq_disabled(irq_get_irq_data(swrm->wake_irq))) + disable_irq_nosync(swrm->wake_irq); + } + + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + + return IRQ_HANDLED; +} + static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) { struct qcom_swrm_ctrl *swrm = dev_id; @@ -1340,6 +1366,18 @@ static int qcom_swrm_probe(struct platform_device *pdev) goto err_clk; } + ctrl->wake_irq = of_irq_get(dev->of_node, 1); + if (ctrl->wake_irq > 0) { + ret = devm_request_threaded_irq(dev, ctrl->wake_irq, NULL, + qcom_swrm_wake_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "swr_wake_irq", ctrl); + if (ret) { + dev_err(dev, "Failed to request soundwire wake irq\n"); + goto err_init; + } + } + ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode); if (ret) { dev_err(dev, "Failed to register Soundwire controller (%d)\n", @@ -1424,6 +1462,11 @@ static int swrm_runtime_resume(struct device *dev) struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); int ret; + if (ctrl->wake_irq > 0) { + if (!irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) + disable_irq_nosync(ctrl->wake_irq); + } + clk_prepare_enable(ctrl->hclk); if (ctrl->clock_stop_not_supported) { @@ -1491,6 +1534,11 @@ static int __maybe_unused swrm_runtime_suspend(struct device *dev) usleep_range(300, 305); + if (ctrl->wake_irq > 0) { + if (irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) + enable_irq(ctrl->wake_irq); + } + return 0; } -- cgit v1.2.3 From 35732a0694cd2e8642d90f9b49a99742faadd869 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Wed, 2 Mar 2022 18:13:01 +0530 Subject: soundwire: qcom: constify static struct qcom_swrm_data global variables The qcom_swrm_data structures is only required for setting soundwire params, so make the qcom_swrm_data structure const to allow the compiler to put it in read-only memory and avoid unintentional modifications. Signed-off-by: Srinivasa Rao Mandadapu Co-developed-by: Venkata Prasad Potturu Signed-off-by: Venkata Prasad Potturu Link: https://lore.kernel.org/r/1646224982-3361-3-git-send-email-quic_srivasam@quicinc.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index e5c75305a90a..f613a41c278a 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -181,12 +181,12 @@ struct qcom_swrm_data { u32 default_rows; }; -static struct qcom_swrm_data swrm_v1_3_data = { +static const struct qcom_swrm_data swrm_v1_3_data = { .default_rows = 48, .default_cols = 16, }; -static struct qcom_swrm_data swrm_v1_5_data = { +static const struct qcom_swrm_data swrm_v1_5_data = { .default_rows = 50, .default_cols = 16, }; -- cgit v1.2.3 From 266fa94673d325f489f74d0a9a12024a7f1ee49c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 3 Mar 2022 11:03:21 +0000 Subject: soundwire: qcom: use __maybe_unused for swrm_runtime_resume() swrm_runtime_resume() would not be defined when CONFIG_PM=n This causes below build failure drivers/soundwire/qcom.c:1460:12: error: 'swrm_runtime_resume' defined but not used [-Werror=unused-function] Mark the resume swrm_runtime_resume() with __maybe_unused attribute. Reported-by: Geert Uytterhoeven Reported-by: Stephen Rothwell Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220303110321.23666-1-srinivas.kandagatla@linaro.org [redo commit title and log] Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soundwire') diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index f613a41c278a..da1ad7ebb1aa 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1457,7 +1457,7 @@ static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm) return false; } -static int swrm_runtime_resume(struct device *dev) +static int __maybe_unused swrm_runtime_resume(struct device *dev) { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); int ret; -- cgit v1.2.3