From ee8a8cfe4e8d3c67456e554c988017c252710a25 Mon Sep 17 00:00:00 2001 From: Boris Faure Date: Thu, 20 Aug 2026 21:22:57 +0000 Subject: [PATCH 01/18] soundwire: intel: initialise quirk_mask before reading it sdw_master_read_intel_prop() declares quirk_mask without an initialiser and ignores the return value of fwnode_property_read_u32(). When a BIOS does not describe "intel-quirk-mask" for a link, the property read leaves the variable untouched and stack garbage decides whether SDW_INTEL_QUIRK_MASK_BUS_DISABLE is honoured, so the link may be marked hw_disabled at random. Initialise it to 0, matching is_link_enabled() in sound/hda/core/intel-sdw-acpi.c. Found by code inspection, not observed at run time. Fixes: 395713d8ca43 ("soundwire: intel: handle disabled links") Signed-off-by: Boris Faure Link: https://patch.msgid.link/20260820212220.226912-1-boris@fau.re Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index a8407560bf4f..9b1465af2645 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -159,7 +159,7 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) struct sdw_intel_prop *intel_prop; struct fwnode_handle *link; char name[32]; - u32 quirk_mask; + u32 quirk_mask = 0; /* Find master handle */ snprintf(name, sizeof(name), From c3133906d5bda343bd5c1d8ad63074a14506ad72 Mon Sep 17 00:00:00 2001 From: Sataporn Saijai Date: Wed, 19 Aug 2026 01:20:20 +0700 Subject: [PATCH 02/18] soundwire: dmi-quirks: add Lenovo Yoga Slim 7i 14IPH11 The Lenovo Yoga Slim 7i 14IPH11 (DMI product 83QM) declares an RT722 on link 3 that is not actually present and never attaches, leaving the machine with no audio. Since the address is already handled by the ghost_realtek table, only the DMI match is needed. Signed-off-by: Sataporn Saijai Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260818182020.143096-1-excursively@gmail.com Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 768255dd12db..e49325331b3d 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -214,6 +214,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)ghost_realtek, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QM"), + }, + .driver_data = (void *)ghost_realtek, + }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), From bb22b81339cb97d2eb7ec666bf03f985fe64d6cb Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 31 Aug 2026 12:29:39 +0100 Subject: [PATCH 03/18] soundwire: bus_type: Use devres to free slave IDA Create a devres cleanup action to call ida_free(). This ensures that it is freed after anything that is cleaned up by devres and might have been using the allocated ID. It also avoids mixing devres and manual cleanup during sdw_bus_probe(). Signed-off-by: Richard Fitzgerald Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260831112939.126708-1-rf@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index d61a97c5b41e..fea15107cd9b 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -71,6 +71,13 @@ int sdw_slave_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } +static void sdw_slave_ida_free(void *data) +{ + struct sdw_slave *slave = data; + + ida_free(&slave->bus->slave_ida, slave->index); +} + static int sdw_bus_probe(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -104,15 +111,16 @@ static int sdw_bus_probe(struct device *dev) return ret; } slave->index = ret; + ret = devm_add_action_or_reset(dev, sdw_slave_ida_free, slave); + if (ret) + return ret; /* Create IRQ mapping now so the driver can get it in probe() */ sdw_irq_create_mapping(slave); ret = drv->probe(slave, id); - if (ret) { - ida_free(&slave->bus->slave_ida, slave->index); + if (ret) return ret; - } mutex_lock(&slave->sdw_dev_lock); @@ -170,8 +178,6 @@ static void sdw_bus_remove(struct device *dev) if (drv->remove) drv->remove(slave); - - ida_free(&slave->bus->slave_ida, slave->index); } static void sdw_bus_shutdown(struct device *dev) From 1d4d3198ccce18ec1d58ac41c47e7b2aabba2e94 Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Tue, 1 Sep 2026 09:28:12 +0800 Subject: [PATCH 04/18] soundwire: intel_auxdevice: add tac5xx2 family to wake_capable_list Add tac5572, tac5682 to the wake_capable_list as it can generate jack events whilst the bus is stopped. Signed-off-by: Niranjan H Y Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260901012812.231057-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 9b1465af2645..ece5fba7c6b7 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -74,6 +74,8 @@ static struct wake_capable_part wake_capable_list[] = { {0x025d, 0x721}, {0x025d, 0x722}, {0x04b3, 0x9356}, + {0x0102, 0x5572}, + {0x0102, 0x5682}, }; static bool is_wake_capable(struct sdw_slave *slave) From 1f26ff6aab686c5da7ef749bd14049d1eb3029e4 Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Mon, 7 Sep 2026 11:59:30 +0530 Subject: [PATCH 05/18] soundwire: fix repeated words in log messages Drop the words written twice, reported by checkpatch.pl as a possible repeated word. Only the message text changes, no code changes. Assisted-by: Cursor:claude-opus-5 Signed-off-by: Hemanth Selam Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260907062930.18120-1-hemanth.selam@gmail.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index f106a877d7b2..0b00fcdcab2d 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1571,7 +1571,7 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) if (stream->state != SDW_STREAM_CONFIGURED && stream->state != SDW_STREAM_DEPREPARED && stream->state != SDW_STREAM_DISABLED) { - pr_err("%s: %s: inconsistent state state %d\n", + pr_err("%s: %s: inconsistent state %d\n", __func__, stream->name, stream->state); ret = -EINVAL; goto state_err; @@ -1655,7 +1655,7 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { - pr_err("%s: %s: inconsistent state state %d\n", + pr_err("%s: %s: inconsistent state %d\n", __func__, stream->name, stream->state); ret = -EINVAL; goto state_err; @@ -1742,7 +1742,7 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) } if (stream->state != SDW_STREAM_ENABLED) { - pr_err("%s: %s: inconsistent state state %d\n", + pr_err("%s: %s: inconsistent state %d\n", __func__, stream->name, stream->state); ret = -EINVAL; goto state_err; @@ -1848,7 +1848,7 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) if (stream->state != SDW_STREAM_PREPARED && stream->state != SDW_STREAM_DISABLED) { - pr_err("%s: %s: inconsistent state state %d\n", + pr_err("%s: %s: inconsistent state %d\n", __func__, stream->name, stream->state); ret = -EINVAL; goto state_err; From 659d6f148a88467817e9531a6c793ae022661bab Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 7 Sep 2026 10:14:21 +0100 Subject: [PATCH 06/18] soundwire: bus: iterate DT compatibles for class-ID match sdw_of_find_slaves() reads only the first "compatible" string on a SoundWire slave DT node and rejects the node if that string does not parse as sdwVMMMPPPPXX. This prevents the standard DT convention of placing a vendor-variant compatible ahead of the SoundWire class-ID compatible: compatible = "vendor,partname", "sdwVMMMPPPPXX"; Iterate every string in the compatible property and accept the first one that parses as a SoundWire class ID. The vendor-variant string remains visible to driver probe callbacks via device_is_compatible() for mode/variant disambiguation. Signed-off-by: Srinivas Kandagatla Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260907091421.837917-1-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/slave.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index e0c49cbcb1ba..a7d2938b031d 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -243,17 +243,27 @@ int sdw_of_find_slaves(struct sdw_bus *bus) const char *compat = NULL; struct sdw_slave_id id; const __be32 *addr; + struct property *prop; + bool matched = false; - ret = of_property_read_string(node, "compatible", &compat); - if (ret) - continue; + /* + * Iterate the compatible list so DT nodes can carry a + * vendor-variant compatible ahead of the SoundWire class-ID + * compatible (e.g. "vendor,part", "sdwVMMMPPPPXX"). + */ + of_property_for_each_string(node, "compatible", prop, compat) { + ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", + &sdw_version, &id.mfg_id, &id.part_id, + &id.class_id); + if (ret == 4) { + matched = true; + break; + } + } - ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version, - &id.mfg_id, &id.part_id, &id.class_id); - - if (ret != 4) { - dev_err(dev, "Invalid compatible string found %s\n", - compat); + if (!matched) { + dev_err(dev, "%pOFn: no SoundWire class-ID compatible\n", + node); continue; } From eb9ea1a0fa28abe7a511cb67363a0cc835a02f04 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 2 Sep 2026 12:28:38 +0100 Subject: [PATCH 07/18] soundwire: qcom: cache SCP_ADDRPAGE1/2 to preserve MBQ atomicity qcom_swrm_xfer_msg() reprograms SCP_ADDRPAGE1/2 before every paged transfer, even when the paging window hasn't changed. Besides the wasted bus transactions, this breaks the atomicity that SoundWire Classic Multi-Byte Quantity (MBQ) writes rely on: the kernel's MBQ regmap emits an MBQ pair as two sdw_write_no_pm() calls to bit-13 aliases sharing the same page, and the intervening PAGE writes invalidate the MBQ high-byte staging on Peripherals that observe them. On WCD9378 FU42 Q7.8 Channel Volume this drops the MSB byte silently. Per SDCA v1.1 sections 12.2.5 and 12.2.8.2.1, MBQ pairs are expected to be atomic on the wire. Cache the last-programmed page values per Slave, skip the FIFO write when they match, and invalidate on re-attach since SCP_ADDRPAGE1/2 reset to defaults on re-enumeration. Signed-off-by: Srinivas Kandagatla Cc: Faiz Nabi Kuchay Link: https://patch.msgid.link/20260902112838.1369446-1-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 55678a30cd4a..35ffffd541bd 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -221,6 +221,9 @@ struct qcom_swrm_ctrl { u32 slave_status; u32 wr_fifo_depth; bool clock_stop_not_supported; + /* Per-Slave SCP_ADDRPAGE1/2 shadow; -1 = unknown. */ + s16 page1_cache[SDW_MAX_DEVICES + 1]; + s16 page2_cache[SDW_MAX_DEVICES + 1]; }; struct qcom_swrm_data { @@ -630,6 +633,10 @@ static void qcom_swrm_set_slave_dev_num(struct sdw_bus *bus, mutex_lock(&bus->bus_lock); set_bit(devnum, bus->assigned); mutex_unlock(&bus->bus_lock); + + /* Re-attach resets SCP_ADDRPAGE1/2 to defaults; invalidate. */ + ctrl->page1_cache[devnum] = -1; + ctrl->page2_cache[devnum] = -1; } } @@ -976,17 +983,25 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, int ret, i, len; if (msg->page) { - ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page1, - msg->dev_num, - SDW_SCP_ADDRPAGE1); - if (ret) - return ret; + if (ctrl->page1_cache[msg->dev_num] != msg->addr_page1) { + ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page1, + msg->dev_num, + SDW_SCP_ADDRPAGE1); + if (ret) + return ret; - ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page2, - msg->dev_num, - SDW_SCP_ADDRPAGE2); - if (ret) - return ret; + ctrl->page1_cache[msg->dev_num] = msg->addr_page1; + } + + if (ctrl->page2_cache[msg->dev_num] != msg->addr_page2) { + ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page2, + msg->dev_num, + SDW_SCP_ADDRPAGE2); + if (ret) + return ret; + + ctrl->page2_cache[msg->dev_num] = msg->addr_page2; + } } if (msg->flags == SDW_MSG_FLAG_READ) { @@ -1561,6 +1576,9 @@ static int qcom_swrm_probe(struct platform_device *pdev) if (!ctrl) return -ENOMEM; + memset(ctrl->page1_cache, 0xff, sizeof(ctrl->page1_cache)); + memset(ctrl->page2_cache, 0xff, sizeof(ctrl->page2_cache)); + data = of_device_get_match_data(dev); ctrl->max_reg = data->max_reg; ctrl->reg_layout = data->reg_layout; From a6a867552a36e647cbd35aceae24754c9bcd3661 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 3 Sep 2026 11:27:43 +0100 Subject: [PATCH 08/18] soundwire: dmi-quirks: Disable ghost Realtek on Lenovo Pro 9 The Lenovo Pro 9 also has a Realtek device in the ACPI that doesn't exist in the physical hardware. This confuses the machine driver into attempting to create DAI links for the device. Add a quirk to remove this device. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260903102743.3782541-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index e49325331b3d..72e59b50b685 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -235,6 +235,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)ghost_realtek, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83U9"), + }, + .driver_data = (void *)ghost_realtek, + }, {} }; From 267665f8a3d7a17c5012f85b068d4e11f83432c1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 1 Sep 2026 11:03:09 +0800 Subject: [PATCH 09/18] soundwire: bus: add CLOCK_STOP_MODE1 support back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLOCK_STOP_MODE1 is used when the Peripheral might have entered a deeper power-saving mode that does not retain state while the Clock is stopped. It is useful when the device is more power consumption sensitive. Add it back to allow the Peripheral use CLOCK_STOP_MODE1. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260901030311.232823-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 54 ++++++++++++++++++++++------------- include/linux/soundwire/sdw.h | 4 +++ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 12087cbd87b7..aeaae5a57c89 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -958,8 +958,31 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&bus->bus_lock); } +static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) +{ + enum sdw_clk_stop_mode mode; + + mode = slave->prop.clk_stop_mode1 ? SDW_CLK_STOP_MODE1 : SDW_CLK_STOP_MODE0; + + mutex_lock(&slave->sdw_dev_lock); + + /* + * Query for clock stop mode if Slave implements + * ops->get_clk_stop_mode, else read from property. + */ + if (slave->probed) { + struct sdw_driver *drv = drv_to_sdw_driver(slave->dev.driver); + + if (drv->ops && drv->ops->get_clk_stop_mode) + mode = drv->ops->get_clk_stop_mode(slave); + } + + mutex_unlock(&slave->sdw_dev_lock); + + return mode; +} + static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, - enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) { int ret = 0; @@ -971,7 +994,7 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); if (drv->ops && drv->ops->clk_stop) - ret = drv->ops->clk_stop(slave, mode, type); + ret = drv->ops->clk_stop(slave, slave->clk_stop_mode, type); } mutex_unlock(&slave->sdw_dev_lock); @@ -980,7 +1003,6 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, } static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, - enum sdw_clk_stop_mode mode, bool prepare) { bool wake_en; @@ -992,7 +1014,7 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, if (prepare) { val = SDW_SCP_SYSTEMCTRL_CLK_STP_PREP; - if (mode == SDW_CLK_STOP_MODE1) + if (slave->clk_stop_mode == SDW_CLK_STOP_MODE1) val |= SDW_SCP_SYSTEMCTRL_CLK_STP_MODE1; if (wake_en) @@ -1080,9 +1102,9 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - ret = sdw_slave_clk_stop_callback(slave, - SDW_CLK_STOP_MODE0, - SDW_CLK_PRE_PREPARE); + slave->clk_stop_mode = sdw_get_clk_stop_mode(slave); + + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_PRE_PREPARE); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop pre-prepare cb failed:%d\n", ret); return ret; @@ -1092,9 +1114,7 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; - ret = sdw_slave_clk_stop_prepare(slave, - SDW_CLK_STOP_MODE0, - true); + ret = sdw_slave_clk_stop_prepare(slave, true); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop prepare failed:%d\n", ret); return ret; @@ -1132,9 +1152,7 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - ret = sdw_slave_clk_stop_callback(slave, - SDW_CLK_STOP_MODE0, - SDW_CLK_POST_PREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_POST_PREPARE); if (ret < 0 && ret != -ENODATA) { dev_err(&slave->dev, "clock stop post-prepare cb failed:%d\n", ret); @@ -1206,8 +1224,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, - SDW_CLK_PRE_DEPREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_PRE_DEPREPARE); if (ret < 0) dev_warn(&slave->dev, "clock stop pre-deprepare cb failed:%d\n", ret); @@ -1215,9 +1232,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; - ret = sdw_slave_clk_stop_prepare(slave, SDW_CLK_STOP_MODE0, - false); - + ret = sdw_slave_clk_stop_prepare(slave, false); if (ret < 0) dev_warn(&slave->dev, "clock stop deprepare failed:%d\n", ret); } @@ -1245,8 +1260,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, - SDW_CLK_POST_DEPREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_POST_DEPREPARE); if (ret < 0) dev_warn(&slave->dev, "clock stop post-deprepare cb failed:%d\n", ret); } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 707ca6bd1e23..f710e5932b4b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -618,6 +618,7 @@ struct sdw_bus_params { * @update_status: Update Slave status * @bus_config: Update the bus config for Slave * @port_prep: Prepare the port with parameters + * @get_clk_stop_mode: Get the clock stop mode of the Slave * @clk_stop: handle imp-def sequences before and after prepare and de-prepare */ struct sdw_slave_ops { @@ -631,6 +632,7 @@ struct sdw_slave_ops { int (*port_prep)(struct sdw_slave *slave, struct sdw_prepare_ch *prepare_ch, enum sdw_port_prep_ops pre_ops); + enum sdw_clk_stop_mode (*get_clk_stop_mode)(struct sdw_slave *slave); int (*clk_stop)(struct sdw_slave *slave, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type); @@ -649,6 +651,7 @@ struct sdw_slave_ops { * @node: node for bus list * @port_ready: Port ready completion flag for each Slave port * @m_port_map: static Master port map for each Slave port + * @clk_stop_mode: The clock stop mode of the Slave * @dev_num: Current Device Number, values can be 0 or dev_num_sticky * @dev_num_sticky: one-time static Device Number assigned by Bus * @probed: boolean tracking driver state @@ -683,6 +686,7 @@ struct sdw_slave { struct list_head node; struct completion port_ready[SDW_MAX_PORTS]; unsigned int m_port_map[SDW_MAX_PORTS]; + enum sdw_clk_stop_mode clk_stop_mode; u16 dev_num; u16 dev_num_sticky; bool probed; From 8c1e69e322a9757f596242bdd004e76e30181869 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 1 Sep 2026 11:03:10 +0800 Subject: [PATCH 10/18] soundwire: Intel: add wake_enable parameter to stop_bus ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we assume the bus is wakeable when the bus clock stops. But in some case like system suspend, we want to stop the bus but keep the bus unwakeable. No function change in this commit. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260901030311.232823-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.h | 6 +++--- drivers/soundwire/intel_auxdevice.c | 6 +++--- drivers/soundwire/intel_bus_common.c | 8 ++++---- include/linux/soundwire/sdw_intel.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 86abc465260f..7a2e7e73ad63 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -188,10 +188,10 @@ static inline int sdw_intel_start_bus_after_clock_stop(struct sdw_intel *sdw) return -ENOTSUPP; } -static inline int sdw_intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) +static inline int sdw_intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable) { if (SDW_INTEL_CHECK_OPS(sdw, stop_bus)) - return SDW_INTEL_OPS(sdw, stop_bus)(sdw, clock_stop); + return SDW_INTEL_OPS(sdw, stop_bus)(sdw, clock_stop, wake_enable); return -ENOTSUPP; } @@ -261,7 +261,7 @@ int intel_start_bus(struct sdw_intel *sdw); int intel_start_bus_after_reset(struct sdw_intel *sdw); void intel_check_clock_stop(struct sdw_intel *sdw); int intel_start_bus_after_clock_stop(struct sdw_intel *sdw); -int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop); +int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable); /* common bank switch routines */ int intel_pre_bank_switch(struct sdw_intel *sdw); diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index ece5fba7c6b7..170f9f92f11b 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -672,7 +672,7 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_intel_stop_bus(sdw, false); + ret = sdw_intel_stop_bus(sdw, false, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret; @@ -698,14 +698,14 @@ static int __maybe_unused intel_suspend_runtime(struct device *dev) clock_stop_quirks = sdw->link_res->clock_stop_quirks; if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { - ret = sdw_intel_stop_bus(sdw, false); + ret = sdw_intel_stop_bus(sdw, false, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus during teardown: %d\n", __func__, ret); return ret; } } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) { - ret = sdw_intel_stop_bus(sdw, true); + ret = sdw_intel_stop_bus(sdw, true, true); if (ret < 0) { dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n", __func__, ret); diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c index ad1f8ebdbfc9..fa35eb2fc763 100644 --- a/drivers/soundwire/intel_bus_common.c +++ b/drivers/soundwire/intel_bus_common.c @@ -202,11 +202,11 @@ int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) return 0; } -int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) +int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable) { struct device *dev = sdw->cdns.dev; struct sdw_cdns *cdns = &sdw->cdns; - bool wake_enable = false; + bool en = false; int ret; cancel_delayed_work_sync(&cdns->attach_dwork); @@ -216,7 +216,7 @@ int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) if (ret < 0) dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret); else - wake_enable = true; + en = wake_enable; } ret = sdw_cdns_enable_interrupt(cdns, false); @@ -231,7 +231,7 @@ int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) return ret; } - sdw_intel_shim_wake(sdw, wake_enable); + sdw_intel_shim_wake(sdw, en); return 0; } diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 9c9435009537..9710f2dc04e2 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -424,7 +424,7 @@ struct sdw_intel_hw_ops { int (*start_bus)(struct sdw_intel *sdw); int (*start_bus_after_reset)(struct sdw_intel *sdw); int (*start_bus_after_clock_stop)(struct sdw_intel *sdw); - int (*stop_bus)(struct sdw_intel *sdw, bool clock_stop); + int (*stop_bus)(struct sdw_intel *sdw, bool clock_stop, bool wake_enable); int (*link_power_up)(struct sdw_intel *sdw); int (*link_power_down)(struct sdw_intel *sdw); From 90b63b309fd6c0f192337659e73e3047a67df99f Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 1 Sep 2026 11:03:11 +0800 Subject: [PATCH 11/18] soundwire: Intel: stop sdw clock in system suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perform clock stop with proper mode so that the Peripherals can enter the deserved power state. And keep the bus unwakeable because there is no need to wake up the bus in system suspend. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260901030311.232823-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 170f9f92f11b..901a71262094 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -672,7 +672,8 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_intel_stop_bus(sdw, false, false); + /* Perform clock stop with proper mode and keep the bus unwakeable in system suspend. */ + ret = sdw_intel_stop_bus(sdw, true, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret; From a936b634dd62a414a3da6517fac11f02117ffd7e Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 10 Sep 2026 11:50:14 +0530 Subject: [PATCH 12/18] dt-bindings: soundwire: qcom: add qcom,shikra-soundwire compatible Add qcom,shikra-soundwire as a standalone compatible for the Shikra SoC SoundWire controller. On Shikra SoC, the SoundWire controller is assigned to an Execution Environment (EE), which determines which banked register window must be used for interrupt, FIFO, and status register access on SoundWire v2.0 and later hardware. Add a dedicated qcom,shikra-soundwire compatible so the driver can select the correct register layout for Shikra at probe time. Signed-off-by: Mohammad Rafi Shaik Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260910-shikra_soundwire_support-v2-1-f98c9a6ea1e1@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml index 8e6973fa229c..81532cf69dd0 100644 --- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml +++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml @@ -17,6 +17,7 @@ properties: compatible: oneOf: - enum: + - qcom,shikra-soundwire - qcom,soundwire-v1.3.0 - qcom,soundwire-v1.5.0 - qcom,soundwire-v1.5.1 From b27be5a85ed3c11b51de189218d7a78b0972af4d Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 10 Sep 2026 11:50:15 +0530 Subject: [PATCH 13/18] soundwire: qcom: add EE-aware register layout for Shikra On SoundWire v2.0 and later hardware, FIFO, and status registers are banked per Execution Environment (EE). The existing driver assumes the SoundWire controller is assigned to EE1, which is true for all currently supported Qualcomm SoCs. Shikra is an exception and assigns the controller to EE0. As a result, the relevant register windows are located at different offsets and cannot be accessed using the default EE1 register layout. Add an ee field to the SoC match data and introduce a dedicated qcom,shikra-soundwire compatible with ee = 0. The driver uses this information at probe time to select the correct register layout, while all existing platforms continue to use the default EE1 configuration. Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20260910-shikra_soundwire_support-v2-2-f98c9a6ea1e1@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 85 +++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 35ffffd541bd..6e0c5fb10efa 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -25,7 +25,9 @@ #define SWRM_COMP_SW_RESET 0x008 #define SWRM_COMP_STATUS 0x014 #define SWRM_LINK_MANAGER_EE 0x018 -#define SWRM_EE_CPU 1 +#define SWRM_EE_CPU0 0 +#define SWRM_EE_CPU1 1 +#define SWRM_EE_CPU SWRM_EE_CPU1 #define SWRM_FRM_GEN_ENABLED BIT(0) #define SWRM_VERSION_1_3_0 0x01030000 #define SWRM_VERSION_1_5_1 0x01050001 @@ -118,6 +120,7 @@ #define SWRM_V2_0_CLK_CTRL 0x5060 #define SWRM_V2_0_CLK_CTRL_CLK_START BIT(0) #define SWRM_V2_0_LINK_STATUS 0x5064 +#define SWRM_V2_REG_EE_STRIDE 0x1000 #define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 #define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 @@ -201,6 +204,7 @@ struct qcom_swrm_ctrl { struct mutex port_lock; struct clk *hclk; int irq; + u32 ee; unsigned int version; int wake_irq; int num_din_ports; @@ -224,6 +228,7 @@ struct qcom_swrm_ctrl { /* Per-Slave SCP_ADDRPAGE1/2 shadow; -1 = unknown. */ s16 page1_cache[SDW_MAX_DEVICES + 1]; s16 page2_cache[SDW_MAX_DEVICES + 1]; + unsigned int reg_layout_local[SWRM_OFFSET_DP_SAMPLECTRL2_BANK + 1]; }; struct qcom_swrm_data { @@ -232,6 +237,7 @@ struct qcom_swrm_data { bool sw_clk_gate_required; u32 max_reg; const unsigned int *reg_layout; + u32 ee; }; static const unsigned int swrm_v1_3_reg_layout[] = { @@ -258,6 +264,7 @@ static const struct qcom_swrm_data swrm_v1_3_data = { .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, + .ee = SWRM_EE_CPU, }; static const struct qcom_swrm_data swrm_v1_5_data = { @@ -265,6 +272,7 @@ static const struct qcom_swrm_data swrm_v1_5_data = { .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, + .ee = SWRM_EE_CPU, }; static const struct qcom_swrm_data swrm_v1_6_data = { @@ -273,6 +281,7 @@ static const struct qcom_swrm_data swrm_v1_6_data = { .sw_clk_gate_required = true, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, + .ee = SWRM_EE_CPU, }; static const unsigned int swrm_v2_0_reg_layout[] = { @@ -300,6 +309,7 @@ static const struct qcom_swrm_data swrm_v2_0_data = { .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v2_0_reg_layout, + .ee = SWRM_EE_CPU, }; static const unsigned int swrm_v3_0_reg_layout[] = { @@ -327,9 +337,51 @@ static const struct qcom_swrm_data swrm_v3_0_data = { .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v3_0_reg_layout, + .ee = SWRM_EE_CPU, +}; + +static const struct qcom_swrm_data swrm_shikra_data = { + .default_rows = 50, + .default_cols = 16, + .sw_clk_gate_required = true, + .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v3_0_reg_layout, + .ee = SWRM_EE_CPU0, }; #define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) +static void qcom_swrm_set_ee_register_layout(struct qcom_swrm_ctrl *ctrl, + const struct qcom_swrm_data *data) +{ + int ee_offset; + + memcpy(ctrl->reg_layout_local, data->reg_layout, + sizeof(ctrl->reg_layout_local)); + ctrl->reg_layout = ctrl->reg_layout_local; + + if (ctrl->version < SWRM_VERSION_2_0_0) + return; + + /* + * The register layout constants are defined for EE1 (the default for + * most Qualcomm SoCs). For SoCs where the SoundWire master is assigned + * to EE0, the interrupt, FIFO and status register windows are shifted + * by one EE stride (0x1000) relative to the EE1 base addresses. + */ + ee_offset = ((int)ctrl->ee - SWRM_EE_CPU) * SWRM_V2_REG_EE_STRIDE; + if (!ee_offset) + return; + + ctrl->reg_layout_local[SWRM_REG_FRAME_GEN_ENABLED] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_INTERRUPT_STATUS] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_INTERRUPT_CLEAR] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_INTERRUPT_CPU_EN] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_CMD_FIFO_WR_CMD] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_CMD_FIFO_RD_CMD] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_CMD_FIFO_STATUS] += ee_offset; + ctrl->reg_layout_local[SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] += ee_offset; +} + static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val) { @@ -910,12 +962,13 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val); if (ctrl->version == SWRM_VERSION_1_7_0) { - ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, ctrl->ee); ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, - SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU); + SWRM_MCP_BUS_CLK_START << ctrl->ee); } else if (ctrl->version >= SWRM_VERSION_2_0_0) { - ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); - ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL, + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, ctrl->ee); + ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL + + ((int)ctrl->ee - SWRM_EE_CPU) * SWRM_V2_REG_EE_STRIDE, SWRM_V2_0_CLK_CTRL_CLK_START); } else { ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); @@ -941,11 +994,9 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR], 0xFFFFFFFF); - /* enable CPU IRQs */ - if (ctrl->mmio) { - ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], - SWRM_INTERRUPT_STATUS_RMSK); - } + /* enable CPU IRQs for the selected EE window */ + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + SWRM_INTERRUPT_STATUS_RMSK); /* Set IRQ to PULSE */ ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, @@ -1580,6 +1631,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) memset(ctrl->page2_cache, 0xff, sizeof(ctrl->page2_cache)); data = of_device_get_match_data(dev); + ctrl->ee = data->ee; ctrl->max_reg = data->max_reg; ctrl->reg_layout = data->reg_layout; ctrl->rows_index = sdw_find_row_index(data->default_rows); @@ -1659,6 +1711,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) prop->default_row = data->default_rows; ctrl->reg_read(ctrl, SWRM_COMP_HW_VERSION, &ctrl->version); + qcom_swrm_set_ee_register_layout(ctrl, data); ret = devm_request_threaded_irq(dev, ctrl->irq, NULL, qcom_swrm_irq_handler, @@ -1769,16 +1822,19 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev) reset_control_reset(ctrl->audio_cgcr); if (ctrl->version == SWRM_VERSION_1_7_0) { - ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, ctrl->ee); ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, - SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU); + SWRM_MCP_BUS_CLK_START << ctrl->ee); } else if (ctrl->version >= SWRM_VERSION_2_0_0) { - ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); - ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL, + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, ctrl->ee); + ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL + + ((int)ctrl->ee - SWRM_EE_CPU) * + SWRM_V2_REG_EE_STRIDE, SWRM_V2_0_CLK_CTRL_CLK_START); } else { ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); } + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR], SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET); @@ -1854,6 +1910,7 @@ static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.7.0", .data = &swrm_v1_5_data }, { .compatible = "qcom,soundwire-v2.0.0", .data = &swrm_v2_0_data }, { .compatible = "qcom,soundwire-v3.1.0", .data = &swrm_v3_0_data }, + { .compatible = "qcom,shikra-soundwire", .data = &swrm_shikra_data }, {/* sentinel */}, }; From d32e04c052342d7d1cc03f6ba4cbd135cd0112bc Mon Sep 17 00:00:00 2001 From: Sergey Lebedev Date: Sun, 13 Sep 2026 21:08:55 +0000 Subject: [PATCH 14/18] soundwire: dmi-quirks: drop the ghost RT1320 on the Surface Pro 11 (Intel) The Microsoft Surface Pro 11 for Business (Intel, Lunar Lake) carries a single physical RT1320 amplifier on link 0 and describes it twice: SWRA _ADR 0x000030025D132000 SDCA class 0 SWRB _ADR 0x000030025D132001 SDCA class 1 Identical apart from the class id: same link, same manufacturer, part and version, same unique id 0. The part reports class 1, so only SWRB enumerates. SWRA never attaches on any boot or firmware version tested, including the November 2025 bundle. The ghost is not harmless. It consumes an amplifier index in find_acpi_adr_device(), so the real part is named "rt1320-2" and the stock sof-soundwire UCM profile enables switches on a device that is not there. Its endpoints also reach create_sdw_dailink(), which builds DAI link names from link id and function type alone; the ghost declares the same SmartMic function as the real part, so the card fails to register at all: sysfs: cannot create duplicate filename '.../sof_sdw/SDW0-Capture-SmartMic' kobject_add_internal failed for SDW0-Capture-SmartMic with -EEXIST sof_sdw sof_sdw: probe with driver sof_sdw failed with error -12 Remap its _ADR to zero so sdw_acpi_find_slaves() never creates the peripheral, which is how ghost devices are handled already - see ghost_realtek and global_ghost_adr in this file. Both failures then disappear, because neither path is reached. Matched on DMI_PRODUCT_SKU rather than the product name. A later batch of the same model could carry a different RT1320 version, which would change the _ADR; keying on the SKU keeps this remap to the hardware it was verified on, and follows dell_sku_0A3E above. Link: https://lore.kernel.org/linux-sound/20260830151516.44629-1-lsa.uz@pm.me/ Suggested-by: Pierre-Louis Bossart Suggested-by: Bard Liao Signed-off-by: Sergey Lebedev Reviewed-by: Pierre-Louis Bossart Tested-by: German Pablo Lindo Link: https://patch.msgid.link/20260913210849.6446-1-lsa.uz@pm.me Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 72e59b50b685..f9183e0e63bf 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -111,6 +111,25 @@ static const struct adr_remap ghost_realtek[] = { {} }; +/* + * The Microsoft Surface Pro 11 (Intel) describes its single physical RT1320 + * amplifier twice on link 0, as two _ADR entries differing only in SDCA class + * id and sharing the same unique id. Only the class 1 entry enumerates; the + * class 0 entry is a ghost. + * + * Matched on the product SKU rather than the product name: a later batch of the + * same model could carry a different RT1320 version, which would change the + * _ADR. Keying on the SKU keeps this remap to the hardware it was verified on. + */ +static const struct adr_remap microsoft_sp11_intel[] = { + /* ghost rt1320 on link0 */ + { + 0x000030025d132000ull, + 0x0000000000000000ull + }, + {} +}; + static const struct dmi_system_id adr_remap_quirk_table[] = { /* TGL devices */ { @@ -242,6 +261,15 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)ghost_realtek, }, + /* LNL devices */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, + "Surface_Pro_11th_Edition_With_Intel_For_Business_2103") + }, + .driver_data = (void *)microsoft_sp11_intel, + }, {} }; From fbc3ac576a33a6ad430366dea85585780612de79 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 24 Sep 2026 13:20:27 +0800 Subject: [PATCH 15/18] soundwire: intel_auxdevice: add rt718 codec to wake_capable_list rt718 has wake capability. Add it to the wake_capable_list. Signed-off-by: Oder Chiou Link: https://patch.msgid.link/20260924-rt718fix-v1-1-d92674696e73@realtek.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 901a71262094..b61ce8f4f4ca 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -71,6 +71,7 @@ static struct wake_capable_part wake_capable_list[] = { {0x025d, 0x715}, {0x025d, 0x716}, {0x025d, 0x717}, + {0x025d, 0x718}, {0x025d, 0x721}, {0x025d, 0x722}, {0x04b3, 0x9356}, From 21f4552fbaafa75628ed7bf7693303920da0545c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 28 Sep 2026 18:56:39 +0800 Subject: [PATCH 16/18] soundwire: dmi-quirks: Disable ghost Realtek on Lenovo Yoga 7 Pro Another Lenovo module that has a Realtek device in the ACPI that doesn't exist in the physical hardware. Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260928105640.3014318-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index f9183e0e63bf..d25a86c097a1 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -233,6 +233,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)ghost_realtek, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83SN"), + }, + .driver_data = (void *)ghost_realtek, + }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), From e0cb731652a1bebf962086f17d1b7c9d060cb7d6 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 28 Sep 2026 18:56:40 +0800 Subject: [PATCH 17/18] soundwire: dmi-quirks: Disable ghost rt711 on Lenovo Yoga Slim 7 The device reports both cs42l43 and rt711. But the rt711 codec does not physically exist. Adding a quirk to disable the ghost device. Signed-off-by: Bard Liao Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260928105640.3014318-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index d25a86c097a1..e6ae75bcf185 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -130,6 +130,15 @@ static const struct adr_remap microsoft_sp11_intel[] = { {} }; +static const struct adr_remap ghost_rt711[] = { + /* rt711 on link0 */ + { + 0x000030025d071101ull, + 0x0000000000000000ull + }, + {} +}; + static const struct dmi_system_id adr_remap_quirk_table[] = { /* TGL devices */ { @@ -277,6 +286,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)microsoft_sp11_intel, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83R0"), + }, + .driver_data = (void *)ghost_rt711, + }, {} }; From 2e9eff8847a46530ab937696f340b9d5bea2e4ef Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 29 Sep 2026 16:24:06 +0800 Subject: [PATCH 18/18] soundwire: intel_auxdevice: add rt766/rt767 codec to wake_capable_list rt766/rt767 have wake capability. Add it to the wake_capable_list. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260929-rt766-wake-v3-1-09c0b82c72a6@realtek.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index b61ce8f4f4ca..5983fbc5725a 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -74,6 +74,8 @@ static struct wake_capable_part wake_capable_list[] = { {0x025d, 0x718}, {0x025d, 0x721}, {0x025d, 0x722}, + {0x025d, 0x766}, + {0x025d, 0x767}, {0x04b3, 0x9356}, {0x0102, 0x5572}, {0x0102, 0x5682},