summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml1
-rw-r--r--drivers/soundwire/bus.c54
-rw-r--r--drivers/soundwire/bus_type.c16
-rw-r--r--drivers/soundwire/dmi-quirks.c65
-rw-r--r--drivers/soundwire/intel.h6
-rw-r--r--drivers/soundwire/intel_auxdevice.c14
-rw-r--r--drivers/soundwire/intel_bus_common.c8
-rw-r--r--drivers/soundwire/qcom.c123
-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
12 files changed, 255 insertions, 74 deletions
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
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..715626174367 100644
--- a/drivers/soundwire/dmi-quirks.c
+++ b/drivers/soundwire/dmi-quirks.c
@@ -111,6 +111,34 @@ 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 adr_remap ghost_rt711[] = {
+ /* rt711 on link0 */
+ {
+ 0x000030025d071101ull,
+ 0x0000000000000000ull
+ },
+ {}
+};
+
static const struct dmi_system_id adr_remap_quirk_table[] = {
/* TGL devices */
{
@@ -224,6 +252,20 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
{
.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"),
+ 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 +277,29 @@ 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,
+ },
+ /* 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,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83R0"),
+ },
+ .driver_data = (void *)ghost_rt711,
+ },
{}
};
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..5983fbc5725a 100644
--- a/drivers/soundwire/intel_auxdevice.c
+++ b/drivers/soundwire/intel_auxdevice.c
@@ -71,9 +71,14 @@ static struct wake_capable_part wake_capable_list[] = {
{0x025d, 0x715},
{0x025d, 0x716},
{0x025d, 0x717},
+ {0x025d, 0x718},
{0x025d, 0x721},
{0x025d, 0x722},
+ {0x025d, 0x766},
+ {0x025d, 0x767},
{0x04b3, 0x9356},
+ {0x0102, 0x5572},
+ {0x0102, 0x5682},
};
static bool is_wake_capable(struct sdw_slave *slave)
@@ -159,7 +164,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 +675,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 +702,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..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;
@@ -221,6 +225,10 @@ 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];
+ unsigned int reg_layout_local[SWRM_OFFSET_DP_SAMPLECTRL2_BANK + 1];
};
struct qcom_swrm_data {
@@ -229,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[] = {
@@ -255,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 = {
@@ -262,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 = {
@@ -270,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[] = {
@@ -297,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[] = {
@@ -324,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)
{
@@ -630,6 +685,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;
}
}
@@ -903,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);
@@ -934,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,
@@ -976,17 +1034,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,7 +1627,11 @@ 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->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);
@@ -1641,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,
@@ -1751,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);
@@ -1836,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 */},
};
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);