From a5fef9baa87f8be359a4b3ed11829ebca82e56ad Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 5 Dec 2024 11:33:15 +0000 Subject: [PATCH 01/16] soundwire: bus: Move irq mapping cleanup into devres Currently the IRQ mapping is disposed off in sdw_drv_remove(), however if the SoundWire device uses devres this can run before the actual device clean up, potentially clearing the mapping whilst it is still in use. Make this devres safe by also moving the sdw_irq_dispose_mapping into devres. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20241205113315.2266313-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 3 --- drivers/soundwire/irq.c | 12 ++++++++---- drivers/soundwire/irq.h | 5 ----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 77dc094075e13..e98d5db81b1ce 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -167,9 +167,6 @@ static int sdw_drv_remove(struct device *dev) slave->probed = false; - if (slave->prop.use_domain_irq) - sdw_irq_dispose_mapping(slave); - mutex_unlock(&slave->sdw_dev_lock); if (drv->remove) diff --git a/drivers/soundwire/irq.c b/drivers/soundwire/irq.c index 0c08cebb1235c..c237e6d0766b3 100644 --- a/drivers/soundwire/irq.c +++ b/drivers/soundwire/irq.c @@ -46,14 +46,18 @@ void sdw_irq_delete(struct sdw_bus *bus) irq_domain_remove(bus->domain); } +static void sdw_irq_dispose_mapping(void *data) +{ + struct sdw_slave *slave = data; + + irq_dispose_mapping(irq_find_mapping(slave->bus->domain, slave->dev_num)); +} + void sdw_irq_create_mapping(struct sdw_slave *slave) { slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num); if (!slave->irq) dev_warn(&slave->dev, "Failed to map IRQ\n"); -} -void sdw_irq_dispose_mapping(struct sdw_slave *slave) -{ - irq_dispose_mapping(irq_find_mapping(slave->bus->domain, slave->dev_num)); + devm_add_action_or_reset(&slave->dev, sdw_irq_dispose_mapping, slave); } diff --git a/drivers/soundwire/irq.h b/drivers/soundwire/irq.h index 58a58046d92b8..86e2318409dab 100644 --- a/drivers/soundwire/irq.h +++ b/drivers/soundwire/irq.h @@ -16,7 +16,6 @@ int sdw_irq_create(struct sdw_bus *bus, struct fwnode_handle *fwnode); void sdw_irq_delete(struct sdw_bus *bus); void sdw_irq_create_mapping(struct sdw_slave *slave); -void sdw_irq_dispose_mapping(struct sdw_slave *slave); #else /* CONFIG_IRQ_DOMAIN */ @@ -34,10 +33,6 @@ static inline void sdw_irq_create_mapping(struct sdw_slave *slave) { } -static inline void sdw_irq_dispose_mapping(struct sdw_slave *slave) -{ -} - #endif /* CONFIG_IRQ_DOMAIN */ #endif /* __SDW_IRQ_H */ From 6bba2d3f744c4383b22aa79471d7136cfdce7d0c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:42 +0800 Subject: [PATCH 02/16] soundwire: add lane field in sdw_port_runtime Currently, lane_ctrl is always 0. Add a lane field in sdw_port_runtime to indicate the data lane of the data port. They are 0 by default. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20241218080155.102405-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/amd_manager.c | 2 +- drivers/soundwire/bus.h | 2 ++ drivers/soundwire/generic_bandwidth_allocation.c | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 5a4bfaef65fb2..f47d4cd656ae7 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -410,7 +410,7 @@ static int amd_sdw_compute_params(struct sdw_bus *bus) sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, hstart, hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index fda6b24ac2da9..ff03b97f1d8b4 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -90,6 +90,7 @@ int sdw_find_col_index(int col); * @transport_params: Transport parameters * @port_params: Port parameters * @port_node: List node for Master or Slave port_list + * @lane: Which lane is used * * SoundWire spec has no mention of ports for Master interface but the * concept is logically extended. @@ -100,6 +101,7 @@ struct sdw_port_runtime { struct sdw_transport_params transport_params; struct sdw_port_params port_params; struct list_head port_node; + unsigned int lane; }; /** diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index b9316207c3ab2..abf9b85daa52d 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -56,7 +56,7 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, sample_int, port_bo, port_bo >> 8, t_data->hstart, t_data->hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, @@ -109,7 +109,7 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, *port_bo, (*port_bo) >> 8, hstart, hstop, - SDW_BLK_PKG_PER_PORT, 0x0); + SDW_BLK_PKG_PER_PORT, p_rt->lane); sdw_fill_port_params(&p_rt->port_params, p_rt->num, bps, From 7533d0df69452c3e7b69c727c1e8e1a7e1afc83c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:43 +0800 Subject: [PATCH 03/16] soundwire: mipi_disco: read lane mapping properties from ACPI The DisCo for SoundWire 2.0 added support for the 'mipi-sdw-lane--mapping' property. Co-developed-by: Chao Song Signed-off-by: Chao Song Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20241218080155.102405-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/mipi_disco.c | 40 +++++++++++++++++++++++++++++++++- include/linux/soundwire/sdw.h | 5 +++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index 9d59f486edbe0..65afb28ef8fab 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -366,6 +366,44 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, return 0; } +/* + * In MIPI DisCo spec for SoundWire, lane mapping for a slave device is done with + * mipi-sdw-lane-x-mapping properties, where x is 1..7, and the values for those + * properties are mipi-sdw-manager-lane-x or mipi-sdw-peripheral-link-y, where x + * is an integer between 1 to 7 if the lane is connected to a manager lane, y is a + * character between A to E if the lane is connected to another peripheral lane. + */ +int sdw_slave_read_lane_mapping(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + struct device *dev = &slave->dev; + char prop_name[30]; + const char *prop_val; + size_t len; + int ret, i; + u8 lane; + + for (i = 0; i < SDW_MAX_LANES; i++) { + snprintf(prop_name, sizeof(prop_name), "mipi-sdw-lane-%d-mapping", i); + ret = device_property_read_string(dev, prop_name, &prop_val); + if (ret) + continue; + + len = strlen(prop_val); + if (len < 1) + return -EINVAL; + + /* The last character is enough to identify the connection */ + ret = kstrtou8(&prop_val[len - 1], 10, &lane); + if (ret) + return ret; + if (in_range(lane, 1, SDW_MAX_LANES - 1)) + prop->lane_maps[i] = lane; + } + return 0; +} +EXPORT_SYMBOL(sdw_slave_read_lane_mapping); + /** * sdw_slave_read_prop() - Read Slave properties * @slave: SDW Slave @@ -486,6 +524,6 @@ int sdw_slave_read_prop(struct sdw_slave *slave) sdw_slave_read_dpn(slave, prop->sink_dpn_prop, nval, prop->sink_ports, "sink"); - return 0; + return sdw_slave_read_lane_mapping(slave); } EXPORT_SYMBOL(sdw_slave_read_prop); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index bd9836690da62..bb4e33a4db170 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -54,6 +54,8 @@ struct sdw_slave; #define SDW_MAX_PORTS 15 #define SDW_VALID_PORT_RANGE(n) ((n) < SDW_MAX_PORTS && (n) >= 1) +#define SDW_MAX_LANES 8 + enum { SDW_PORT_DIRN_SINK = 0, SDW_PORT_DIRN_SOURCE, @@ -356,6 +358,7 @@ struct sdw_dpn_prop { * and masks are supported * @commit_register_supported: is PCP_Commit register supported * @scp_int1_mask: SCP_INT1_MASK desired settings + * @lane_maps: Lane mapping for the slave, only valid if lane_control_support is set * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. @@ -385,6 +388,7 @@ struct sdw_slave_prop { u32 sdca_interrupt_register_list; u8 commit_register_supported; u8 scp_int1_mask; + u8 lane_maps[SDW_MAX_LANES]; bool clock_reg_supported; bool use_domain_irq; }; @@ -450,6 +454,7 @@ struct sdw_master_prop { int sdw_master_read_prop(struct sdw_bus *bus); int sdw_slave_read_prop(struct sdw_slave *slave); +int sdw_slave_read_lane_mapping(struct sdw_slave *slave); /* * SDW Slave Structures and APIs From b6a2e1be7d9303d07eff72a13132a37e035fbcfa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:44 +0800 Subject: [PATCH 04/16] soundwire: add lane_used_bandwidth in struct sdw_bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support multi-lane, we need to know how much bandwidth is used on each lane. And to use the lane that has enough bandwidth. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20241218080155.102405-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index bb4e33a4db170..ae38ac848d383 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -893,6 +893,7 @@ struct sdw_master_ops { * @multi_link: Store bus property that indicates if multi links * are supported. This flag is populated by drivers after reading * appropriate firmware (ACPI/DT). + * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane */ struct sdw_bus { struct device *dev; @@ -924,6 +925,7 @@ struct sdw_bus { struct dentry *debugfs; #endif bool multi_link; + unsigned int lane_used_bandwidth[SDW_MAX_LANES]; }; int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, From f1b3dba6905a9afc49996b039042c411aa98fd52 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 18 Dec 2024 16:01:45 +0800 Subject: [PATCH 05/16] soundwire: stream: set DEPREPARED state earlier The existing logic is problematic in that we deprepare all the ports, but still take into account the stream for bit allocation by just walking through the bus->m_rt list. This patch sets the state earlier, so that such DEPREPARED streams can be skipped in the bandwidth allocation (to be implemented in a follow-up patch). Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 7aa4900dcf317..795017c8081a9 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1643,8 +1643,15 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; struct sdw_bus *bus; + int state = stream->state; int ret = 0; + /* + * first mark the state as DEPREPARED so that it is not taken into account + * for bit allocation + */ + stream->state = SDW_STREAM_DEPREPARED; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; /* De-prepare port(s) */ @@ -1652,6 +1659,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) if (ret < 0) { dev_err(bus->dev, "De-prepare port(s) failed: %d\n", ret); + stream->state = state; return ret; } @@ -1665,6 +1673,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) if (ret < 0) { dev_err(bus->dev, "Compute params failed: %d\n", ret); + stream->state = state; return ret; } } @@ -1673,11 +1682,11 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) ret = sdw_program_params(bus, false); if (ret < 0) { dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret); + stream->state = state; return ret; } } - stream->state = SDW_STREAM_DEPREPARED; return do_bank_switch(stream); } From fdd1faeeec14291f9e542afa5ccec2c5a0930f5b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 18 Dec 2024 16:01:46 +0800 Subject: [PATCH 06/16] soundwire: generic_bandwidth_allocation: skip DEPREPARED streams We should not blindly walk through all the m_rt list, since it will have the side effect of accounting for deprepared streams. This behavior is the result of the split implementation where the dailink hw_free() handles the stream state change and the bit allocation, and the dai hw_free() modifies the m_rt list. The bit allocation ends-up using m_rt entries in zombie state, no longer relevant but still used. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index abf9b85daa52d..2950a3d002ce2 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -238,6 +238,9 @@ static int sdw_get_group_count(struct sdw_bus *bus, return -ENOMEM; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream->state == SDW_STREAM_DEPREPARED) + continue; + rate = m_rt->stream->params.rate; if (m_rt == list_first_entry(&bus->m_rt_list, struct sdw_master_runtime, From 8f4e3343eda8cdedaf711bf3d8ef2d6ed571f420 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:47 +0800 Subject: [PATCH 07/16] Soundwire: add sdw_slave_get_scale_index helper Currently, we only set peripheral frequency when the peripheral is initialized. However, curr_dr_freq may change to get required bandwidth. For example, curr_dr_freq may increase from 4.8MHz to 9.6MHz when the 4th stream is opened. Add a helper to get the scale index so that we can get the scale index and program it. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-7-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 55 ++++++++++++++++++++++------------- include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index d1dc62c34f1cf..215630d602adf 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1276,23 +1276,12 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave, return ret; } -static int sdw_slave_set_frequency(struct sdw_slave *slave) +int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base) { u32 mclk_freq = slave->bus->prop.mclk_freq; u32 curr_freq = slave->bus->params.curr_dr_freq >> 1; unsigned int scale; u8 scale_index; - u8 base; - int ret; - - /* - * frequency base and scale registers are required for SDCA - * devices. They may also be used for 1.2+/non-SDCA devices. - * Driver can set the property, we will need a DisCo property - * to discover this case from platform firmware. - */ - if (!slave->id.class_id && !slave->prop.clock_reg_supported) - return 0; if (!mclk_freq) { dev_err(&slave->dev, @@ -1311,19 +1300,19 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) */ if (!(19200000 % mclk_freq)) { mclk_freq = 19200000; - base = SDW_SCP_BASE_CLOCK_19200000_HZ; + *base = SDW_SCP_BASE_CLOCK_19200000_HZ; } else if (!(22579200 % mclk_freq)) { mclk_freq = 22579200; - base = SDW_SCP_BASE_CLOCK_22579200_HZ; + *base = SDW_SCP_BASE_CLOCK_22579200_HZ; } else if (!(24576000 % mclk_freq)) { mclk_freq = 24576000; - base = SDW_SCP_BASE_CLOCK_24576000_HZ; + *base = SDW_SCP_BASE_CLOCK_24576000_HZ; } else if (!(32000000 % mclk_freq)) { mclk_freq = 32000000; - base = SDW_SCP_BASE_CLOCK_32000000_HZ; + *base = SDW_SCP_BASE_CLOCK_32000000_HZ; } else if (!(96000000 % mclk_freq)) { mclk_freq = 24000000; - base = SDW_SCP_BASE_CLOCK_24000000_HZ; + *base = SDW_SCP_BASE_CLOCK_24000000_HZ; } else { dev_err(&slave->dev, "Unsupported clock base, mclk %d\n", @@ -1354,6 +1343,34 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) } scale_index++; + dev_dbg(&slave->dev, + "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n", + *base, scale_index, mclk_freq, curr_freq); + + return scale_index; +} +EXPORT_SYMBOL(sdw_slave_get_scale_index); + +static int sdw_slave_set_frequency(struct sdw_slave *slave) +{ + int scale_index; + u8 base; + int ret; + + /* + * frequency base and scale registers are required for SDCA + * devices. They may also be used for 1.2+/non-SDCA devices. + * Driver can set the property directly, for now there's no + * DisCo property to discover support for the scaling registers + * from platform firmware. + */ + if (!slave->id.class_id && !slave->prop.clock_reg_supported) + return 0; + + scale_index = sdw_slave_get_scale_index(slave, &base); + if (scale_index < 0) + return scale_index; + ret = sdw_write_no_pm(slave, SDW_SCP_BUS_CLOCK_BASE, base); if (ret < 0) { dev_err(&slave->dev, @@ -1373,10 +1390,6 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave) dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE_B1 write failed:%d\n", ret); - dev_dbg(&slave->dev, - "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n", - base, scale_index, mclk_freq, curr_freq); - return ret; } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index ae38ac848d383..05a85e2bd96d0 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1052,6 +1052,8 @@ int sdw_stream_add_slave(struct sdw_slave *slave, int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream); +int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base); + /* messaging and data APIs */ int sdw_read(struct sdw_slave *slave, u32 addr); int sdw_write(struct sdw_slave *slave, u32 addr, u8 value); From 645291cfe5e52cce9571d73542476bec1d79ce26 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:48 +0800 Subject: [PATCH 08/16] Soundwire: stream: program BUSCLOCK_SCALE We need to program bus clock scale to adjust the bus clock if current bus clock doesn't fit the bandwidth. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-8-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 10 ++++++++++ drivers/soundwire/stream.c | 36 +++++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 1 + 3 files changed, 47 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 215630d602adf..9b295fc9acd53 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -813,6 +813,16 @@ void sdw_extract_slave_id(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_extract_slave_id); +bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave) +{ + /* + * Dynamic scaling is a defined by SDCA. However, some devices expose the class ID but + * can't support dynamic scaling. We might need a quirk to handle such devices. + */ + return slave->id.class_id; +} +EXPORT_SYMBOL(is_clock_scaling_supported_by_slave); + static int sdw_program_device_num(struct sdw_bus *bus, bool *programmed) { u8 buf[SDW_NUM_DEV_ID_REGISTERS] = {0}; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 795017c8081a9..bf04b941012c5 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -629,8 +629,44 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) static int sdw_program_params(struct sdw_bus *bus, bool prepare) { struct sdw_master_runtime *m_rt; + struct sdw_slave *slave; int ret = 0; + u32 addr1; + + /* Check if all Peripherals comply with SDCA */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num_sticky) + continue; + if (!is_clock_scaling_supported_by_slave(slave)) { + dev_dbg(&slave->dev, "The Peripheral doesn't comply with SDCA\n"); + goto manager_runtime; + } + } + + if (bus->params.next_bank) + addr1 = SDW_SCP_BUSCLOCK_SCALE_B1; + else + addr1 = SDW_SCP_BUSCLOCK_SCALE_B0; + + /* Program SDW_SCP_BUSCLOCK_SCALE if all Peripherals comply with SDCA */ + list_for_each_entry(slave, &bus->slaves, node) { + int scale_index; + u8 base; + + if (!slave->dev_num_sticky) + continue; + scale_index = sdw_slave_get_scale_index(slave, &base); + if (scale_index < 0) + return scale_index; + + ret = sdw_write_no_pm(slave, addr1, scale_index); + if (ret < 0) { + dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n"); + return ret; + } + } +manager_runtime: list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { /* diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 05a85e2bd96d0..fc0a203c3ae0e 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1041,6 +1041,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus); int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id); void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id); +bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave); #if IS_ENABLED(CONFIG_SOUNDWIRE) From 3ddd303f4725b66d418998ca21cddbe9f31bb8aa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:49 +0800 Subject: [PATCH 09/16] Soundwire: generic_bandwidth_allocation: set frame shape on fly We need to recalculate frame shape when sdw bus clock is changed. And need to make sure all Peripherals connected to the Manager support dynamic clock change. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-9-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- .../soundwire/generic_bandwidth_allocation.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 2950a3d002ce2..d847413141d39 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -327,6 +327,19 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) return -EINVAL; } +static bool is_clock_scaling_supported(struct sdw_bus *bus) +{ + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) + if (!is_clock_scaling_supported_by_slave(s_rt->slave)) + return false; + + return true; +} + /** * sdw_compute_bus_params: Compute bus parameters * @@ -352,6 +365,10 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) clk_buf = NULL; } + /* If dynamic scaling is not supported, don't try higher freq */ + if (!is_clock_scaling_supported(bus)) + clk_values = 1; + for (i = 0; i < clk_values; i++) { if (!clk_buf) curr_dr_freq = bus->params.max_dr_freq; @@ -378,6 +395,12 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) return -EINVAL; } + if (!mstr_prop->default_frame_rate || !mstr_prop->default_row) + return -EINVAL; + + mstr_prop->default_col = curr_dr_freq / mstr_prop->default_frame_rate / + mstr_prop->default_row; + ret = sdw_select_row_col(bus, curr_dr_freq); if (ret < 0) { dev_err(bus->dev, "%s: could not find frame configuration for bus dr_freq %d\n", From cf44ae3d3282572480d760199cc8d6c7c61006cf Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:50 +0800 Subject: [PATCH 10/16] soundwire: generic_bandwidth_allocation: correct clk_freq check in sdw_select_row_col The bits in Column 0 of Rows 0 to 47 are for control word and cannot be used for audio. In practice, entire Column 0 is skipped. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-10-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index d847413141d39..5c4dac36ad1a7 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -302,7 +302,6 @@ static int sdw_compute_port_params(struct sdw_bus *bus) static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) { struct sdw_master_prop *prop = &bus->prop; - int frame_int, frame_freq; int r, c; for (c = 0; c < SDW_FRAME_COLS; c++) { @@ -311,11 +310,8 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) sdw_cols[c] != prop->default_col) continue; - frame_int = sdw_rows[r] * sdw_cols[c]; - frame_freq = clk_freq / frame_int; - - if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < - bus->params.bandwidth) + if (clk_freq * (sdw_cols[c] - 1) < + bus->params.bandwidth * sdw_cols[c]) continue; bus->params.row = sdw_rows[r]; From 4a7927d54d2c819029e70f6efa768085b90bfc34 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:51 +0800 Subject: [PATCH 11/16] soundwire: generic_bandwidth_allocation: check required freq accurately Currently, we check curr_dr_freq roughly by "if (curr_dr_freq <= bus->params.bandwidth)" in sdw_compute_bus_params() and check it accurately in sdw_select_row_col(). It works if we only support one freq. But, we need to check it accurately in sdw_select_row_col() to give it a chance to use a higher freq or use multi-lane. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-11-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 5c4dac36ad1a7..d2632af9c8af6 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -373,7 +373,8 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) (bus->params.max_dr_freq >> clk_buf[i]) : clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; - if (curr_dr_freq <= bus->params.bandwidth) + if (curr_dr_freq * (mstr_prop->default_col - 1) < + bus->params.bandwidth * mstr_prop->default_col) continue; break; From 7a30292ffa2a048271755fdf9364e11343315271 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:52 +0800 Subject: [PATCH 12/16] soundwire: generic_bandwidth_allocation: select data lane If a peripheral supports multi-lane, we can use data lane x to extend the bandwidth. The patch suggests to select data lane x where x > 0 when bandwidth is not enough on data lane 0. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-12-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- .../soundwire/generic_bandwidth_allocation.c | 133 +++++++++++++++++- drivers/soundwire/stream.c | 20 ++- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index d2632af9c8af6..39b4d25ab19e3 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -336,6 +336,82 @@ static bool is_clock_scaling_supported(struct sdw_bus *bus) return true; } +/** + * is_lane_connected_to_all_peripherals: Check if the given manager lane connects to all peripherals + * So that all peripherals can use the manager lane. + * + * @m_rt: Manager runtime + * @lane: Lane number + */ +static bool is_lane_connected_to_all_peripherals(struct sdw_master_runtime *m_rt, unsigned int lane) +{ + struct sdw_slave_prop *slave_prop; + struct sdw_slave_runtime *s_rt; + int i; + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + slave_prop = &s_rt->slave->prop; + for (i = 1; i < SDW_MAX_LANES; i++) { + if (slave_prop->lane_maps[i] == lane) { + dev_dbg(&s_rt->slave->dev, + "M lane %d is connected to P lane %d\n", + lane, i); + break; + } + } + if (i == SDW_MAX_LANES) { + dev_dbg(&s_rt->slave->dev, "M lane %d is not connected\n", lane); + return false; + } + } + return true; +} + +static int get_manager_lane(struct sdw_bus *bus, struct sdw_master_runtime *m_rt, + struct sdw_slave_runtime *s_rt, unsigned int curr_dr_freq) +{ + struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; + struct sdw_port_runtime *m_p_rt; + unsigned int required_bandwidth; + int m_lane; + int l; + + for (l = 1; l < SDW_MAX_LANES; l++) { + if (!slave_prop->lane_maps[l]) + continue; + + required_bandwidth = 0; + list_for_each_entry(m_p_rt, &m_rt->port_list, port_node) { + required_bandwidth += m_rt->stream->params.rate * + hweight32(m_p_rt->ch_mask) * + m_rt->stream->params.bps; + } + if (required_bandwidth <= + curr_dr_freq - bus->lane_used_bandwidth[l]) { + /* Check if m_lane is connected to all Peripherals */ + if (!is_lane_connected_to_all_peripherals(m_rt, + slave_prop->lane_maps[l])) { + dev_dbg(bus->dev, + "Not all Peripherals are connected to M lane %d\n", + slave_prop->lane_maps[l]); + continue; + } + m_lane = slave_prop->lane_maps[l]; + dev_dbg(&s_rt->slave->dev, "M lane %d is used\n", m_lane); + bus->lane_used_bandwidth[l] += required_bandwidth; + /* + * Use non-zero manager lane, subtract the lane 0 + * bandwidth that is already calculated + */ + bus->params.bandwidth -= required_bandwidth; + return m_lane; + } + } + + /* No available multi lane found, only lane 0 can be used */ + return 0; +} + /** * sdw_compute_bus_params: Compute bus parameters * @@ -343,10 +419,16 @@ static bool is_clock_scaling_supported(struct sdw_bus *bus) */ static int sdw_compute_bus_params(struct sdw_bus *bus) { - unsigned int curr_dr_freq = 0; struct sdw_master_prop *mstr_prop = &bus->prop; - int i, clk_values, ret; + struct sdw_slave_prop *slave_prop; + struct sdw_port_runtime *m_p_rt; + struct sdw_port_runtime *s_p_rt; + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + unsigned int curr_dr_freq = 0; + int i, l, clk_values, ret; bool is_gear = false; + int m_lane = 0; u32 *clk_buf; if (mstr_prop->num_clk_gears) { @@ -373,11 +455,26 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) (bus->params.max_dr_freq >> clk_buf[i]) : clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; - if (curr_dr_freq * (mstr_prop->default_col - 1) < + if (curr_dr_freq * (mstr_prop->default_col - 1) >= bus->params.bandwidth * mstr_prop->default_col) - continue; + break; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + /* + * Get the first s_rt that will be used to find the available lane that + * can be used. No need to check all Peripherals because we can't use + * multi-lane if we can't find any available lane for the first Peripheral. + */ + s_rt = list_first_entry(&m_rt->slave_rt_list, + struct sdw_slave_runtime, m_rt_node); - break; + /* + * Find the available Manager lane that connected to the first Peripheral. + */ + m_lane = get_manager_lane(bus, m_rt, s_rt, curr_dr_freq); + if (m_lane > 0) + goto out; + } /* * TODO: Check all the Slave(s) port(s) audio modes and find @@ -391,6 +488,32 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) __func__, bus->params.bandwidth); return -EINVAL; } +out: + /* multilane can be used */ + if (m_lane > 0) { + /* Set Peripheral lanes */ + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + slave_prop = &s_rt->slave->prop; + for (l = 1; l < SDW_MAX_LANES; l++) { + if (slave_prop->lane_maps[l] == m_lane) { + list_for_each_entry(s_p_rt, &s_rt->port_list, port_node) { + s_p_rt->lane = l; + dev_dbg(&s_rt->slave->dev, + "Set P lane %d for port %d\n", + l, s_p_rt->num); + } + break; + } + } + } + /* + * Set Manager lanes. Configure the last m_rt in bus->m_rt_list only since + * we don't want to touch other m_rts that are already working. + */ + list_for_each_entry(m_p_rt, &m_rt->port_list, port_node) { + m_p_rt->lane = m_lane; + } + } if (!mstr_prop->default_frame_rate || !mstr_prop->default_row) return -EINVAL; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index bf04b941012c5..892409cdb88c0 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1678,6 +1678,9 @@ EXPORT_SYMBOL(sdw_disable_stream); static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; + unsigned int multi_lane_bandwidth; + unsigned int bandwidth; struct sdw_bus *bus; int state = stream->state; int ret = 0; @@ -1699,9 +1702,22 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) return ret; } + multi_lane_bandwidth = 0; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + if (!p_rt->lane) + continue; + + bandwidth = m_rt->stream->params.rate * hweight32(p_rt->ch_mask) * + m_rt->stream->params.bps; + multi_lane_bandwidth += bandwidth; + bus->lane_used_bandwidth[p_rt->lane] -= bandwidth; + if (!bus->lane_used_bandwidth[p_rt->lane]) + p_rt->lane = 0; + } /* TODO: Update this during Device-Device support */ - bus->params.bandwidth -= m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; + bandwidth = m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps; + bus->params.bandwidth -= bandwidth - multi_lane_bandwidth; /* Compute params */ if (bus->compute_params) { From 366fd59fb894e3d3605de9eeb856e92221ff9d48 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:53 +0800 Subject: [PATCH 13/16] soundwire: generic_bandwidth_allocation: add lane in sdw_group_params All active streams with the same parameters are grouped together and the params are stored in the sdw_group struct. We compute the required bandwidth for each group. However, each lane has individual bandwidth. Therefore, we should separate different lanes in different params groups. Add lane variable to separate params groups. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20241218080155.102405-13-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.h | 1 + .../soundwire/generic_bandwidth_allocation.c | 122 +++++++++++++----- 2 files changed, 90 insertions(+), 33 deletions(-) diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index ff03b97f1d8b4..fc990171b3f75 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -151,6 +151,7 @@ struct sdw_transport_data { int hstop; int block_offset; int sub_block_offset; + unsigned int lane; }; struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 39b4d25ab19e3..faf04d82ba0a1 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -18,6 +18,7 @@ struct sdw_group_params { unsigned int rate; + unsigned int lane; int full_bw; int payload_bw; int hwidth; @@ -27,6 +28,7 @@ struct sdw_group { unsigned int count; unsigned int max_size; unsigned int *rates; + unsigned int *lanes; }; void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, @@ -48,6 +50,9 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, slave_total_ch = 0; list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + if (p_rt->lane != t_data->lane) + continue; + ch = hweight32(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, @@ -105,6 +110,8 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, t_data.hstart = hstart; list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + if (p_rt->lane != params->lane) + continue; sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, @@ -131,6 +138,7 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, (*port_bo) += bps * ch; } + t_data.lane = params->lane; sdw_compute_slave_ports(m_rt, &t_data); } @@ -138,69 +146,93 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, struct sdw_group_params *params, int count) { struct sdw_master_runtime *m_rt; - int hstop = bus->params.col - 1; - int port_bo, i; + int port_bo, i, l; + int hstop; /* Run loop for all groups to compute transport parameters */ - for (i = 0; i < count; i++) { - port_bo = 1; + for (l = 0; l < SDW_MAX_LANES; l++) { + if (l > 0 && !bus->lane_used_bandwidth[l]) + continue; + /* reset hstop for each lane */ + hstop = bus->params.col - 1; + for (i = 0; i < count; i++) { + if (params[i].lane != l) + continue; + port_bo = 1; - list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { - sdw_compute_master_ports(m_rt, ¶ms[i], &port_bo, hstop); - } + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + sdw_compute_master_ports(m_rt, ¶ms[i], &port_bo, hstop); + } - hstop = hstop - params[i].hwidth; + hstop = hstop - params[i].hwidth; + } } } static int sdw_compute_group_params(struct sdw_bus *bus, struct sdw_group_params *params, - int *rates, int count) + struct sdw_group *group) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; int sel_col = bus->params.col; unsigned int rate, bps, ch; - int i, column_needed = 0; + int i, l, column_needed; /* Calculate bandwidth per group */ - for (i = 0; i < count; i++) { - params[i].rate = rates[i]; + for (i = 0; i < group->count; i++) { + params[i].rate = group->rates[i]; + params[i].lane = group->lanes[i]; params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; } list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { - rate = m_rt->stream->params.rate; - bps = m_rt->stream->params.bps; - ch = m_rt->ch_count; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = hweight32(p_rt->ch_mask); - for (i = 0; i < count; i++) { - if (rate == params[i].rate) - params[i].payload_bw += bps * ch; + for (i = 0; i < group->count; i++) { + if (rate == params[i].rate && p_rt->lane == params[i].lane) + params[i].payload_bw += bps * ch; + } } } - for (i = 0; i < count; i++) { - params[i].hwidth = (sel_col * - params[i].payload_bw + params[i].full_bw - 1) / - params[i].full_bw; + for (l = 0; l < SDW_MAX_LANES; l++) { + if (l > 0 && !bus->lane_used_bandwidth[l]) + continue; + /* reset column_needed for each lane */ + column_needed = 0; + for (i = 0; i < group->count; i++) { + if (params[i].lane != l) + continue; - column_needed += params[i].hwidth; + params[i].hwidth = (sel_col * params[i].payload_bw + + params[i].full_bw - 1) / params[i].full_bw; + + column_needed += params[i].hwidth; + /* There is no control column for lane 1 and above */ + if (column_needed > sel_col) + return -EINVAL; + /* Column 0 is control column on lane 0 */ + if (params[i].lane == 0 && column_needed > sel_col - 1) + return -EINVAL; + } } - if (column_needed > sel_col - 1) - return -EINVAL; return 0; } static int sdw_add_element_group_count(struct sdw_group *group, - unsigned int rate) + unsigned int rate, unsigned int lane) { int num = group->count; int i; for (i = 0; i <= num; i++) { - if (rate == group->rates[i]) + if (rate == group->rates[i] && lane == group->lanes[i]) break; if (i != num) @@ -208,6 +240,7 @@ static int sdw_add_element_group_count(struct sdw_group *group, if (group->count >= group->max_size) { unsigned int *rates; + unsigned int *lanes; group->max_size += 1; rates = krealloc(group->rates, @@ -215,10 +248,20 @@ static int sdw_add_element_group_count(struct sdw_group *group, GFP_KERNEL); if (!rates) return -ENOMEM; + group->rates = rates; + + lanes = krealloc(group->lanes, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!lanes) + return -ENOMEM; + + group->lanes = lanes; } - group->rates[group->count++] = rate; + group->rates[group->count] = rate; + group->lanes[group->count++] = lane; } return 0; @@ -228,6 +271,7 @@ static int sdw_get_group_count(struct sdw_bus *bus, struct sdw_group *group) { struct sdw_master_runtime *m_rt; + struct sdw_port_runtime *p_rt; unsigned int rate; int ret = 0; @@ -237,6 +281,13 @@ static int sdw_get_group_count(struct sdw_bus *bus, if (!group->rates) return -ENOMEM; + group->lanes = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); + if (!group->lanes) { + kfree(group->rates); + group->rates = NULL; + return -ENOMEM; + } + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { if (m_rt->stream->state == SDW_STREAM_DEPREPARED) continue; @@ -246,11 +297,16 @@ static int sdw_get_group_count(struct sdw_bus *bus, struct sdw_master_runtime, bus_node)) { group->rates[group->count++] = rate; - - } else { - ret = sdw_add_element_group_count(group, rate); + } + /* + * Different ports could use different lane, add group element + * even if m_rt is the first entry + */ + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + ret = sdw_add_element_group_count(group, rate, p_rt->lane); if (ret < 0) { kfree(group->rates); + kfree(group->lanes); return ret; } } @@ -284,8 +340,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) } /* Compute transport parameters for grouped streams */ - ret = sdw_compute_group_params(bus, params, - &group.rates[0], group.count); + ret = sdw_compute_group_params(bus, params, &group); if (ret < 0) goto free_params; @@ -295,6 +350,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) kfree(params); out: kfree(group.rates); + kfree(group.lanes); return ret; } From 168cdf9cdef232225f6b6c617fd347b4d1c4a7d7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:54 +0800 Subject: [PATCH 14/16] SoundWire: pass stream to compute_params() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stream parameter will be used in the follow up commit. No function change. Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20241218080155.102405-14-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/amd_manager.c | 2 +- .../soundwire/generic_bandwidth_allocation.c | 11 +- drivers/soundwire/qcom.c | 2 +- drivers/soundwire/stream.c | 4 +- include/linux/soundwire/sdw.h | 148 +++++++++--------- 5 files changed, 85 insertions(+), 82 deletions(-) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index f47d4cd656ae7..0ee792176f229 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -384,7 +384,7 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) return slave_stat; } -static int amd_sdw_compute_params(struct sdw_bus *bus) +static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index faf04d82ba0a1..062e7488b2263 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -170,6 +170,7 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, } static int sdw_compute_group_params(struct sdw_bus *bus, + struct sdw_stream_runtime *stream, struct sdw_group_params *params, struct sdw_group *group) { @@ -319,8 +320,9 @@ static int sdw_get_group_count(struct sdw_bus *bus, * sdw_compute_port_params: Compute transport and port parameters * * @bus: SDW Bus instance + * @stream: Soundwire stream */ -static int sdw_compute_port_params(struct sdw_bus *bus) +static int sdw_compute_port_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_group_params *params = NULL; struct sdw_group group; @@ -340,7 +342,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) } /* Compute transport parameters for grouped streams */ - ret = sdw_compute_group_params(bus, params, &group); + ret = sdw_compute_group_params(bus, stream, params, &group); if (ret < 0) goto free_params; @@ -592,8 +594,9 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) * sdw_compute_params: Compute bus, transport and port parameters * * @bus: SDW Bus instance + * @stream: Soundwire stream */ -int sdw_compute_params(struct sdw_bus *bus) +int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { int ret; @@ -603,7 +606,7 @@ int sdw_compute_params(struct sdw_bus *bus) return ret; /* Compute transport and port params */ - ret = sdw_compute_port_params(bus); + ret = sdw_compute_port_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute transport params failed: %d\n", ret); return ret; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 2b403b14066c1..fd98f05bf56d1 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1072,7 +1072,7 @@ static const struct sdw_master_ops qcom_swrm_ops = { .pre_bank_switch = qcom_swrm_pre_bank_switch, }; -static int qcom_swrm_compute_params(struct sdw_bus *bus) +static int qcom_swrm_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus); struct sdw_master_runtime *m_rt; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 892409cdb88c0..e9df503332bbd 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1419,7 +1419,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, /* Compute params */ if (bus->compute_params) { - ret = bus->compute_params(bus); + ret = bus->compute_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute params failed: %d\n", ret); @@ -1721,7 +1721,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) /* Compute params */ if (bus->compute_params) { - ret = bus->compute_params(bus); + ret = bus->compute_params(bus, stream); if (ret < 0) { dev_err(bus->dev, "Compute params failed: %d\n", ret); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index fc0a203c3ae0e..2d6c303177925 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -855,79 +855,6 @@ struct sdw_master_ops { int dev_num); }; -/** - * struct sdw_bus - SoundWire bus - * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. - * @md: Master device - * @bus_lock_key: bus lock key associated to @bus_lock - * @bus_lock: bus lock - * @slaves: list of Slaves on this bus - * @msg_lock_key: message lock key associated to @msg_lock - * @msg_lock: message lock - * @m_rt_list: List of Master instance of all stream(s) running on Bus. This - * is used to compute and program bus bandwidth, clock, frame shape, - * transport and port parameters - * @defer_msg: Defer message - * @params: Current bus parameters - * @stream_refcount: number of streams currently using this bus - * @ops: Master callback ops - * @port_ops: Master port callback ops - * @prop: Master properties - * @vendor_specific_prop: pointer to non-standard properties - * @hw_sync_min_links: Number of links used by a stream above which - * hardware-based synchronization is required. This value is only - * meaningful if multi_link is set. If set to 1, hardware-based - * synchronization will be used even if a stream only uses a single - * SoundWire segment. - * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. - * @link_id: Link id number, can be 0 to N, unique for each Controller - * @id: bus system-wide unique id - * @compute_params: points to Bus resource management implementation - * @assigned: Bitmap for Slave device numbers. - * Bit set implies used number, bit clear implies unused number. - * @clk_stop_timeout: Clock stop timeout computed - * @bank_switch_timeout: Bank switch timeout computed - * @domain: IRQ domain - * @irq_chip: IRQ chip - * @debugfs: Bus debugfs (optional) - * @multi_link: Store bus property that indicates if multi links - * are supported. This flag is populated by drivers after reading - * appropriate firmware (ACPI/DT). - * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane - */ -struct sdw_bus { - struct device *dev; - struct sdw_master_device *md; - struct lock_class_key bus_lock_key; - struct mutex bus_lock; - struct list_head slaves; - struct lock_class_key msg_lock_key; - struct mutex msg_lock; - struct list_head m_rt_list; - struct sdw_defer defer_msg; - struct sdw_bus_params params; - int stream_refcount; - const struct sdw_master_ops *ops; - const struct sdw_master_port_ops *port_ops; - struct sdw_master_prop prop; - void *vendor_specific_prop; - int hw_sync_min_links; - int controller_id; - unsigned int link_id; - int id; - int (*compute_params)(struct sdw_bus *bus); - DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); - unsigned int clk_stop_timeout; - u32 bank_switch_timeout; - struct irq_chip irq_chip; - struct irq_domain *domain; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs; -#endif - bool multi_link; - unsigned int lane_used_bandwidth[SDW_MAX_LANES]; -}; - int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, struct fwnode_handle *fwnode); void sdw_bus_master_delete(struct sdw_bus *bus); @@ -1017,10 +944,83 @@ struct sdw_stream_runtime { struct list_head master_list; }; +/** + * struct sdw_bus - SoundWire bus + * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. + * @md: Master device + * @bus_lock_key: bus lock key associated to @bus_lock + * @bus_lock: bus lock + * @slaves: list of Slaves on this bus + * @msg_lock_key: message lock key associated to @msg_lock + * @msg_lock: message lock + * @m_rt_list: List of Master instance of all stream(s) running on Bus. This + * is used to compute and program bus bandwidth, clock, frame shape, + * transport and port parameters + * @defer_msg: Defer message + * @params: Current bus parameters + * @stream_refcount: number of streams currently using this bus + * @ops: Master callback ops + * @port_ops: Master port callback ops + * @prop: Master properties + * @vendor_specific_prop: pointer to non-standard properties + * @hw_sync_min_links: Number of links used by a stream above which + * hardware-based synchronization is required. This value is only + * meaningful if multi_link is set. If set to 1, hardware-based + * synchronization will be used even if a stream only uses a single + * SoundWire segment. + * @controller_id: system-unique controller ID. If set to -1, the bus @id will be used. + * @link_id: Link id number, can be 0 to N, unique for each Controller + * @id: bus system-wide unique id + * @compute_params: points to Bus resource management implementation + * @assigned: Bitmap for Slave device numbers. + * Bit set implies used number, bit clear implies unused number. + * @clk_stop_timeout: Clock stop timeout computed + * @bank_switch_timeout: Bank switch timeout computed + * @domain: IRQ domain + * @irq_chip: IRQ chip + * @debugfs: Bus debugfs (optional) + * @multi_link: Store bus property that indicates if multi links + * are supported. This flag is populated by drivers after reading + * appropriate firmware (ACPI/DT). + * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane + */ +struct sdw_bus { + struct device *dev; + struct sdw_master_device *md; + struct lock_class_key bus_lock_key; + struct mutex bus_lock; + struct list_head slaves; + struct lock_class_key msg_lock_key; + struct mutex msg_lock; + struct list_head m_rt_list; + struct sdw_defer defer_msg; + struct sdw_bus_params params; + int stream_refcount; + const struct sdw_master_ops *ops; + const struct sdw_master_port_ops *port_ops; + struct sdw_master_prop prop; + void *vendor_specific_prop; + int hw_sync_min_links; + int controller_id; + unsigned int link_id; + int id; + int (*compute_params)(struct sdw_bus *bus, struct sdw_stream_runtime *stream); + DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); + unsigned int clk_stop_timeout; + u32 bank_switch_timeout; + struct irq_chip irq_chip; + struct irq_domain *domain; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif + bool multi_link; + unsigned int lane_used_bandwidth[SDW_MAX_LANES]; +}; + struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name); void sdw_release_stream(struct sdw_stream_runtime *stream); -int sdw_compute_params(struct sdw_bus *bus); +int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream); int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, From 25befdf32aa407ac21d8b2aa1257ff666fb783b2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 18 Dec 2024 16:01:55 +0800 Subject: [PATCH 15/16] soundwire: generic_bandwidth_allocation: count the bandwidth of active streams only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sdw_compute_group_params() should only count payload bandwidth of the active streams which is in the ENABLED and DISABLED state in the bus. And add the payload bandwidth of the stream that calls sdw_compute_group_params() in sdw_prepare_stream(). Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20241218080155.102405-15-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 062e7488b2263..59965f43c2fb0 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -188,6 +188,19 @@ static int sdw_compute_group_params(struct sdw_bus *bus, } list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream == stream) { + /* Only runtime during prepare should be added */ + if (stream->state != SDW_STREAM_CONFIGURED) + continue; + } else { + /* + * Include runtimes with running (ENABLED state) and paused (DISABLED state) + * streams + */ + if (m_rt->stream->state != SDW_STREAM_ENABLED && + m_rt->stream->state != SDW_STREAM_DISABLED) + continue; + } list_for_each_entry(p_rt, &m_rt->port_list, port_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; From 74148bb59e2064b87eb1715d9eb109adccb75316 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 3 Dec 2024 14:51:44 +0530 Subject: [PATCH 16/16] soundwire: amd: clear wake enable register for power off mode As per design for power off mode, clear the wake enable register during resume sequence. Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20241203092144.4096986-1-Vijendar.Mukunda@amd.com Signed-off-by: Vinod Koul --- drivers/soundwire/amd_manager.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 0ee792176f229..71b3cf2df89d4 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -1190,6 +1190,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev) if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) { return amd_sdw_clock_stop_exit(amd_manager); } else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) { + writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance)); val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); if (val) { val |= AMD_SDW_CLK_RESUME_REQ;