diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-01 15:36:50 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-01 15:36:50 +0100 |
| commit | 2c3bc062e5d5aa318e481d8e39728bdf98c5e11a (patch) | |
| tree | 1445d13123606bc35588d872fdb46c2213aa0d72 | |
| parent | b6c791125125999b0344437e7f99377c04aa26e4 (diff) | |
| parent | 90b63b309fd6c0f192337659e73e3047a67df99f (diff) | |
| download | linux-next-2c3bc062e5d5aa318e481d8e39728bdf98c5e11a.tar.gz linux-next-2c3bc062e5d5aa318e481d8e39728bdf98c5e11a.zip | |
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire.git
| -rw-r--r-- | drivers/soundwire/bus.c | 54 | ||||
| -rw-r--r-- | drivers/soundwire/bus_type.c | 16 | ||||
| -rw-r--r-- | drivers/soundwire/dmi-quirks.c | 14 | ||||
| -rw-r--r-- | drivers/soundwire/intel.h | 6 | ||||
| -rw-r--r-- | drivers/soundwire/intel_auxdevice.c | 11 | ||||
| -rw-r--r-- | drivers/soundwire/intel_bus_common.c | 8 | ||||
| -rw-r--r-- | drivers/soundwire/qcom.c | 38 | ||||
| -rw-r--r-- | drivers/soundwire/slave.c | 28 | ||||
| -rw-r--r-- | drivers/soundwire/stream.c | 8 | ||||
| -rw-r--r-- | include/linux/soundwire/sdw.h | 4 | ||||
| -rw-r--r-- | include/linux/soundwire/sdw_intel.h | 2 |
11 files changed, 129 insertions, 60 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/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) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 62fa64b22412..197b75406892 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -224,6 +224,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { { .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"), DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), }, .driver_data = (void *)ghost_realtek, @@ -235,6 +242,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, + }, {} }; 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 a8407560bf4f..901a71262094 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) @@ -159,7 +161,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), @@ -670,7 +672,8 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_intel_stop_bus(sdw, 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; @@ -696,14 +699,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/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; 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; - - ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version, - &id.mfg_id, &id.part_id, &id.class_id); + /* + * 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; + } + } - 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; } 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; 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; 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); |
