diff options
| -rw-r--r-- | Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml | 1 | ||||
| -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 | 65 | ||||
| -rw-r--r-- | drivers/soundwire/intel.h | 6 | ||||
| -rw-r--r-- | drivers/soundwire/intel_auxdevice.c | 14 | ||||
| -rw-r--r-- | drivers/soundwire/intel_bus_common.c | 8 | ||||
| -rw-r--r-- | drivers/soundwire/qcom.c | 123 | ||||
| -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 |
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); |
