summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-01 15:36:50 +0100
committerMark Brown <broonie@kernel.org>2026-10-01 15:36:50 +0100
commit2c3bc062e5d5aa318e481d8e39728bdf98c5e11a (patch)
tree1445d13123606bc35588d872fdb46c2213aa0d72
parentb6c791125125999b0344437e7f99377c04aa26e4 (diff)
parent90b63b309fd6c0f192337659e73e3047a67df99f (diff)
downloadlinux-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.c54
-rw-r--r--drivers/soundwire/bus_type.c16
-rw-r--r--drivers/soundwire/dmi-quirks.c14
-rw-r--r--drivers/soundwire/intel.h6
-rw-r--r--drivers/soundwire/intel_auxdevice.c11
-rw-r--r--drivers/soundwire/intel_bus_common.c8
-rw-r--r--drivers/soundwire/qcom.c38
-rw-r--r--drivers/soundwire/slave.c28
-rw-r--r--drivers/soundwire/stream.c8
-rw-r--r--include/linux/soundwire/sdw.h4
-rw-r--r--include/linux/soundwire/sdw_intel.h2
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);