From 8167d7f674648cfd428ed49773522f9df5c4fdfd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 15 Feb 2026 21:44:18 -0800 Subject: [PATCH 01/10] soundwire: sdw.h: repair names and format of kernel-doc comments Fix all kernel-doc warnings in sdw.h: Warning: include/linux/soundwire/sdw.h:538 cannot understand function prototype: 'enum sdw_reg_bank' Warning: include/linux/soundwire/sdw.h:779 struct member 'port_num' not described in 'sdw_transport_params' Warning: include/linux/soundwire/sdw.h:792 struct member 'port_num' not described in 'sdw_enable_ch' Warning: include/linux/soundwire/sdw.h:892 cannot understand function prototype: 'struct sdw_port_config' Warning: include/linux/soundwire/sdw.h:906 cannot understand function prototype: 'struct sdw_stream_config' Warning: include/linux/soundwire/sdw.h:925 cannot understand function prototype: 'enum sdw_stream_state' Warning: include/linux/soundwire/sdw.h:942 cannot understand function prototype: 'struct sdw_stream_params' Warning: include/linux/soundwire/sdw.h:960 cannot understand function prototype: 'struct sdw_stream_runtime' Warning: include/linux/soundwire/sdw.h:1047 struct member 'bpt_stream_refcount' not described in 'sdw_bus' Signed-off-by: Randy Dunlap Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260216054418.2766846-1-rdunlap@infradead.org Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index f462717acf20..6147eb1fb210 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -532,7 +532,7 @@ struct sdw_slave_intr_status { }; /** - * sdw_reg_bank - SoundWire register banks + * enum sdw_reg_bank - SoundWire register banks * @SDW_BANK0: Soundwire register bank 0 * @SDW_BANK1: Soundwire register bank 1 */ @@ -751,7 +751,7 @@ struct sdw_port_params { * struct sdw_transport_params: Data Port Transport Parameters * * @blk_grp_ctrl_valid: Port implements block group control - * @num: Port number + * @port_num: Port number * @blk_grp_ctrl: Block group control value * @sample_interval: Sample interval * @offset1: Blockoffset of the payload data @@ -782,7 +782,7 @@ struct sdw_transport_params { /** * struct sdw_enable_ch: Enable/disable Data Port channel * - * @num: Port number + * @port_num: Port number * @ch_mask: Active channel mask * @enable: Enable (true) /disable (false) channel */ @@ -885,7 +885,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus); void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay); /** - * sdw_port_config: Master or Slave Port configuration + * struct sdw_port_config: Master or Slave Port configuration * * @num: Port number * @ch_mask: channels mask for port @@ -896,7 +896,7 @@ struct sdw_port_config { }; /** - * sdw_stream_config: Master or Slave stream configuration + * struct sdw_stream_config: Master or Slave stream configuration * * @frame_rate: Audio frame rate of the stream, in Hz * @ch_count: Channel count of the stream @@ -913,7 +913,7 @@ struct sdw_stream_config { }; /** - * sdw_stream_state: Stream states + * enum sdw_stream_state: Stream states * * @SDW_STREAM_ALLOCATED: New stream allocated. * @SDW_STREAM_CONFIGURED: Stream configured @@ -934,7 +934,7 @@ enum sdw_stream_state { }; /** - * sdw_stream_params: Stream parameters + * struct sdw_stream_params: Stream parameters * * @rate: Sampling frequency, in Hz * @ch_count: Number of channels @@ -947,7 +947,7 @@ struct sdw_stream_params { }; /** - * sdw_stream_runtime: Runtime stream parameters + * struct sdw_stream_runtime: Runtime stream parameters * * @name: SoundWire stream name * @params: Stream parameters @@ -983,7 +983,7 @@ struct sdw_stream_runtime { * @defer_msg: Defer message * @params: Current bus parameters * @stream_refcount: number of streams currently using this bus - * @btp_stream_refcount: number of BTP streams currently using this bus (should + * @bpt_stream_refcount: number of BTP streams currently using this bus (should * be zero or one, multiple streams per link is not supported). * @bpt_stream: pointer stored to handle BTP streams. * @ops: Master callback ops From de67b4ea168f01dac24e328aab6be0802a5c96f6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 23 Feb 2026 09:20:48 +0000 Subject: [PATCH 02/10] soundwire: slave: Don't register devices that are disabled in ACPI If a piece of hardware is disabled in ACPI it shouldn't be added to the bus. Add code to handle this similar to other buses like SPI/I2C. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260223092048.3695135-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/slave.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f5a3ca3b9dda..ff763b692078 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -115,6 +115,9 @@ static bool find_slave(struct sdw_bus *bus, u64 addr; int ret; + if (acpi_bus_get_status(adev) || !acpi_dev_ready_for_enumeration(adev)) + return false; + ret = acpi_get_local_u64_address(adev->handle, &addr); if (ret < 0) return false; From 4b8fc2b17272d4379f3c80cee2d6d8b7d998fa8f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 23 Feb 2026 15:02:54 +0000 Subject: [PATCH 03/10] soundwire: intel_auxdevice: Add CS47L47 to wake_capable_list The Cirrus Logic CS47L47 codec can generate Jack events so add it to the wake-capable list. Signed-off-by: Richard Fitzgerald Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260223150256.326143-2-rf@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 8752b0e3ce74..9b92ffdfc73b 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -53,6 +53,7 @@ struct wake_capable_part { static struct wake_capable_part wake_capable_list[] = { {0x01fa, 0x4243}, {0x01fa, 0x4245}, + {0x01fa, 0x4747}, {0x025d, 0x5682}, {0x025d, 0x700}, {0x025d, 0x711}, From 2a267a8410841ba1c71daa41d1fb2cc21ff23e6b Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 26 Feb 2026 12:25:53 +0530 Subject: [PATCH 04/10] soundwire: amd: add clock init control function Add generic SoundWire clock initialization sequence to support different SoundWire bus clock frequencies for ACP6.3/7.0/7.1/7.2 platforms and remove hard coding initializations for 12Mhz bus clock frequency. Signed-off-by: Vijendar Mukunda Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260226065638.1251771-2-Vijendar.Mukunda@amd.com Signed-off-by: Vinod Koul --- drivers/soundwire/amd_manager.c | 43 +++++++++++++++++++++++++++------ drivers/soundwire/amd_manager.h | 4 --- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index b9ccb3343896..61df5cecdccc 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -27,6 +27,36 @@ #define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus) +static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager) +{ + struct sdw_bus *bus = &amd_manager->bus; + struct sdw_master_prop *prop = &bus->prop; + u32 divider; + + dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n", + prop->mclk_freq, prop->max_clk_freq, prop->default_row, + prop->default_col, prop->default_frame_rate); + + if (!prop->default_frame_rate || !prop->default_row) { + dev_err(amd_manager->dev, "Default frame_rate %d or row %d is invalid\n", + prop->default_frame_rate, prop->default_row); + return -EINVAL; + } + + /* Set clock divider */ + divider = (prop->mclk_freq / bus->params.curr_dr_freq); + writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL); + + /* Set frame shape base on the actual bus frequency. */ + prop->default_col = bus->params.curr_dr_freq / + prop->default_frame_rate / prop->default_row; + amd_manager->cols_index = sdw_find_col_index(prop->default_col); + amd_manager->rows_index = sdw_find_row_index(prop->default_row); + bus->params.col = prop->default_col; + bus->params.row = prop->default_row; + return 0; +} + static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; @@ -960,6 +990,9 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager) prop = &amd_manager->bus.prop; if (!prop->hw_disabled) { + ret = amd_sdw_clk_init_ctrl(amd_manager); + if (ret) + return ret; ret = amd_init_sdw_manager(amd_manager); if (ret) return ret; @@ -984,7 +1017,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct sdw_master_prop *prop; - struct sdw_bus_params *params; struct amd_sdw_manager *amd_manager; int ret; @@ -1048,14 +1080,8 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) return -EINVAL; } - params = &amd_manager->bus.params; - - params->col = AMD_SDW_DEFAULT_COLUMNS; - params->row = AMD_SDW_DEFAULT_ROWS; prop = &amd_manager->bus.prop; - prop->clk_freq = &amd_sdw_freq_tbl[0]; prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ; - prop->max_clk_freq = AMD_SDW_DEFAULT_CLK_FREQ; ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode); if (ret) { @@ -1347,6 +1373,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev) } } sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); + ret = amd_sdw_clk_init_ctrl(amd_manager); + if (ret) + return ret; amd_init_sdw_manager(amd_manager); amd_enable_sdw_interrupts(amd_manager); ret = amd_enable_sdw_manager(amd_manager); diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h index 6cc916b0c820..88cf8a426a0c 100644 --- a/drivers/soundwire/amd_manager.h +++ b/drivers/soundwire/amd_manager.h @@ -203,10 +203,6 @@ #define AMD_SDW_DEVICE_STATE_D3 3 #define ACP_PME_EN 0x0001400 -static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = { - AMD_SDW_DEFAULT_CLK_FREQ, -}; - struct sdw_manager_dp_reg { u32 frame_fmt_reg; u32 sample_int_reg; From 27ab4f1e4909a674dfd03058fb9802cae2343a36 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 26 Feb 2026 12:25:54 +0530 Subject: [PATCH 05/10] soundwire: amd: refactor bandwidth calculation logic For current platforms(ACP6.3/ACP7.0/ACP7.1/ACP7.2), AMD SoundWire manager doesn't have banked registers for data port programming on Manager's side. Need to use fixed block offsets, hstart & hstop for manager ports. Earlier amd manager driver has support for 12 MHz as a bus clock frequency where frame rate is 48000 and number of bits is 500, frame shape as 50 x 10 with fixed block offset mapping based on port number. Got a new requirement to support 6 MHz as a bus clock frequency. For 6 MHz bus clock frequency amd manager driver needs to support two different frame shapes i.e number of bits as 250 with frame rate as 48000 and frame shape as 125 x 2 and for the second combination number of bits as 500 where frame rate is 24000 and frame shape is 50 x 10. Few SoundWire peripherals doesn't support 125 x 2 as a frame shape for 6 MHz bus clock frequency. They have explicit requirement for the frame shape. In this scenario, amd manager driver needs to use 50 x 10 as a frame shape where frame rate is 24000. Based on the platform and SoundWire topology for 6Mhz support frame shape will be decided which is part of SoundWire manager DisCo tables. For current platforms, amd manager driver supports only two bus clock frequencies(12 MHz & 6 MHz). Refactor bandwidth logic to support different bus clock frequencies. Signed-off-by: Vijendar Mukunda Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260226065638.1251771-3-Vijendar.Mukunda@amd.com Signed-off-by: Vinod Koul --- drivers/soundwire/amd_manager.c | 57 ++++++++++++++++++++++++++++--- include/linux/soundwire/sdw_amd.h | 4 +++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 61df5cecdccc..a3316efdf8ac 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -467,12 +467,16 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { + struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; struct sdw_port_runtime *p_rt; struct sdw_bus_params *b_params = &bus->params; int port_bo, hstart, hstop, sample_int; - unsigned int rate, bps; + unsigned int rate, bps, channels; + unsigned int stream_slot_size, max_slots; + static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1}; + unsigned int inst_id = amd_manager->instance; port_bo = 0; hstart = 1; @@ -483,11 +487,51 @@ static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; + channels = m_rt->stream->params.ch_count; sample_int = (bus->params.curr_dr_freq / rate); + + /* Compute slots required for this stream dynamically */ + stream_slot_size = bps * channels; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { - port_bo = (p_rt->num * 64) + 1; - dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n", - p_rt->num, hstart, hstop, port_bo); + if (p_rt->num >= amd_manager->max_ports) { + dev_err(bus->dev, "Port %d exceeds max ports %d\n", + p_rt->num, amd_manager->max_ports); + return -EINVAL; + } + + if (!amd_manager->port_offset_map[p_rt->num]) { + /* + * port block offset calculation for 6MHz bus clock frequency with + * different frame sizes 50 x 10 and 125 x 2 + */ + if (bus->params.curr_dr_freq == 12000000) { + max_slots = bus->params.row * (bus->params.col - 1); + if (next_offset[inst_id] + stream_slot_size <= + (max_slots - 1)) { + amd_manager->port_offset_map[p_rt->num] = + next_offset[inst_id]; + next_offset[inst_id] += stream_slot_size; + } else { + dev_err(bus->dev, + "No space for port %d\n", p_rt->num); + return -ENOMEM; + } + } else { + /* + * port block offset calculation for 12MHz bus clock + * frequency + */ + amd_manager->port_offset_map[p_rt->num] = + (p_rt->num * 64) + 1; + } + } + port_bo = amd_manager->port_offset_map[p_rt->num]; + dev_dbg(bus->dev, + "Port=%d hstart=%d hstop=%d port_bo=%d slots=%d max_ports=%d\n", + p_rt->num, hstart, hstop, port_bo, stream_slot_size, + amd_manager->max_ports); + 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, @@ -1079,6 +1123,11 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) default: return -EINVAL; } + amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports; + amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports, + sizeof(int), GFP_KERNEL); + if (!amd_manager->port_offset_map) + return -ENOMEM; prop = &amd_manager->bus.prop; prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ; diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index fe31773d5210..470360a2723c 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -66,8 +66,10 @@ struct sdw_amd_dai_runtime { * @status: peripheral devices status array * @num_din_ports: number of input ports * @num_dout_ports: number of output ports + * @max_ports: total number of input ports and output ports * @cols_index: Column index in frame shape * @rows_index: Rows index in frame shape + * @port_offset_map: dynamic array to map port block offset * @instance: SoundWire manager instance * @quirks: SoundWire manager quirks * @wake_en_mask: wake enable mask per SoundWire manager @@ -92,10 +94,12 @@ struct amd_sdw_manager { int num_din_ports; int num_dout_ports; + int max_ports; int cols_index; int rows_index; + int *port_offset_map; u32 instance; u32 quirks; u32 wake_en_mask; From fee12f3c20dd5902dbd95eb41f80d3fba89336d7 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 27 Feb 2026 11:16:48 +0000 Subject: [PATCH 06/10] soundwire: stream: Poll for DP prepare to avoid interrupt deadlock Replace the wait_for_completion_timeout() in sdw_prep_deprep_slave_ports() with a read_poll_timeout(). The original intent of the wait_for_completion_timeout() was to wait for the port prepare interrupt. But at this time the code is holding the bus_lock, which prevents the interrupt handler from running. Because of this, the port_prep completion will not be signaled and the wait_for_completion_timeout() will always timeout. Rewriting the code to avoid taking the bus_lock carries risks, and needs careful consideration of the consequences. It is safer and simpler to replace the completion with a simple register poll. As the code is holding the bus_lock, it is already blocking other activity so consuming control channel bandwidth for polling isn't really a concern. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260227111648.175548-1-rf@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b6982f8dcf17..4ed8fb7663ad 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ #include #include "bus.h" +#define SDW_PORT_PREP_POLL_USEC 1000 + /* * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 * @@ -443,7 +446,6 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, struct sdw_port_runtime *p_rt, bool prep) { - struct completion *port_ready; struct sdw_dpn_prop *dpn_prop; struct sdw_prepare_ch prep_ch; u32 imp_def_interrupts; @@ -518,14 +520,18 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, return ret; } - /* Wait for completion on port ready */ - port_ready = &s_rt->slave->port_ready[prep_ch.num]; - wait_for_completion_timeout(port_ready, - msecs_to_jiffies(ch_prep_timeout)); + /* + * Poll for NOT_PREPARED==0. Cannot use the interrupt because + * this code holds bus_lock which blocks interrupt handling. + */ + ret = read_poll_timeout(sdw_read_no_pm, val, + (val < 0) || ((val & p_rt->ch_mask) == 0), + SDW_PORT_PREP_POLL_USEC, ch_prep_timeout * USEC_PER_MSEC, + false, s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); + if (ret || (val < 0)) { + if (val < 0) + ret = val; - val = sdw_read_no_pm(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); - if ((val < 0) || (val & p_rt->ch_mask)) { - ret = (val < 0) ? val : -ETIMEDOUT; dev_err(&s_rt->slave->dev, "Chn prep failed for port %d: %d\n", prep_ch.num, ret); return ret; From 2c96956fe764f8224f9ec93b2a9160a578949a7a Mon Sep 17 00:00:00 2001 From: Cole Leavitt Date: Wed, 18 Feb 2026 11:02:10 -0700 Subject: [PATCH 07/10] soundwire: bus: demote UNATTACHED state warnings to dev_dbg() The dev_warn() messages in sdw_handle_slave_status() for UNATTACHED transitions were added in commit d1b328557058 ("soundwire: bus: add dev_warn() messages to track UNATTACHED devices") to debug attachment failures with dynamic debug enabled. These warnings fire during normal operation -- for example when a codec driver triggers a hardware reset after firmware download, causing the device to momentarily go UNATTACHED before re-attaching -- producing misleading noise on every boot. Demote the messages to dev_dbg() so they remain available via dynamic debug for diagnosing real attachment failures without alarming users during expected initialization sequences. Fixes: d1b328557058 ("soundwire: bus: add dev_warn() messages to track UNATTACHED devices") Signed-off-by: Cole Leavitt Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260218180210.9263-1-cole@unwrap.rs Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fb68738dfb9b..fe5316d93fef 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1899,8 +1899,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (status[i] == SDW_SLAVE_UNATTACHED && slave->status != SDW_SLAVE_UNATTACHED) { - dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", - i, slave->status); + dev_dbg(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); /* Ensure driver knows that peripheral unattached */ @@ -1951,8 +1951,8 @@ 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); + dev_dbg(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); break; From b2c9f1d5a7eb50bcdda607afef1378e552bbb490 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 26 Jan 2026 13:40:45 +0800 Subject: [PATCH 08/10] soundwire: Intel: test bus.bpt_stream before assigning it We only allow up to 1 bpt stream running on a SoundWire bus. bus.bpt_stream will be assigned when it is opened and will be set to NULL when it is closed. We do check bus->bpt_stream_refcount if the stream type is SDW_STREAM_BPT in sdw_master_rt_alloc(), but at that moment the bpt stream is allocated and set to bus.bpt_stream. It will lead to the original bus.bpt_stream be changed to the new and not used bpt stream. And it will be released and set to NULL when sdw_slave_bpt_stream_add() return error as it supposed to. Then the original stream will try to use the NULL bus.bpt_stream. Fixes: 4c1ce9f37d8a ("soundwire: intel_ace2x: add BPT send_async/wait callbacks") Reported-by: Simon Trimmer Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260126054045.2504103-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_ace2x.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 7f01e43ae978..20422534baf1 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -82,6 +82,11 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * int len; int i; + if (cdns->bus.bpt_stream) { + dev_err(cdns->dev, "%s: BPT stream already exists\n", __func__); + return -EAGAIN; + } + stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT); if (!stream) return -ENOMEM; From cbfea84f820962c3c5394ff06e7e9344c96bf761 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 10 Mar 2026 11:31:33 +0000 Subject: [PATCH 09/10] soundwire: cadence: Clear message complete before signaling waiting thread Clear the CDNS_MCP_INT_RX_WL interrupt before signaling completion. This is to prevent the potential race where: - The main thread is scheduled immediately the completion is signaled, and starts a new message - The RX_WL IRQ for this new message happens before sdw_cdns_irq() has been re-scheduled. - When sdw_cdns_irq() is re-scheduled it clears the new RX_WL interrupt. MAIN THREAD | IRQ THREAD | _cdns_xfer_msg() | { | write data to FIFO | wait_for_completion_timeout() | | <---- RX_WL IRQ | sdw_cdns_irq() | { | signal completion <== RESCHEDULE <== Handle message completion | } | | Start new message | _cdns_xfer_msg() | { | write data to FIFO | wait_for_completion_timeout() | | <---- RX_WL IRQ ==> RESCHEDULE ==> | // New RX_WL IRQ is cleared before | // it has been handled. | clear CDNS_MCP_INTSTAT | return IRQ_HANDLED; | } Before this change, this error message was sometimes seen on kernels that have large amounts of debugging enabled: SCP Msg trf timed out This error indicates that the completion has not been signalled after 500ms. Signed-off-by: Richard Fitzgerald Fixes: 956baa1992f9 ("soundwire: cdns: Add sdw_master_ops and IO transfer support") Reported-by: Norman Bintang Closes: https://issuetracker.google.com/issues/477099834 Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260310113133.1707288-1-rf@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index f245c3ffb9e9..b8b62735c893 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -933,6 +933,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) cdns_read_response(cdns); + /* + * Clear interrupt before signalling the completion to avoid + * a race between this thread and the main thread starting + * another TX. + */ + cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_RX_WL); + int_status &= ~CDNS_MCP_INT_RX_WL; + if (defer && defer->msg) { cdns_fill_msg_resp(cdns, defer->msg, defer->length, 0); From b8f2d65fec19f3866905ac6ae3deb5c0c9faf162 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 13 Mar 2026 15:54:28 +0000 Subject: [PATCH 10/10] soundwire: intel_auxdevice: Add cs42l49 to wake_capable_list The Cirrus Logic cs42l49 codec can generate Jack events so add it to the wake-capable list. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260313155428.1934196-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 9b92ffdfc73b..913e95207ee1 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -53,6 +53,7 @@ struct wake_capable_part { static struct wake_capable_part wake_capable_list[] = { {0x01fa, 0x4243}, {0x01fa, 0x4245}, + {0x01fa, 0x4249}, {0x01fa, 0x4747}, {0x025d, 0x5682}, {0x025d, 0x700},