soundwire updates for 7.4

- CLOCK_STOP_MODE1 support in bus and Intel driver
  - Dmi quirks for disabling ghost on Lenovo Yoga 7 Pro, Lenovo Yoga Slim 7,
    Lenovo Pro 9, Lenovo Yoga Slim 7i 14IPH11.
  - Few more codecs as wake capable, tac5xx2 family, rt718, rt766/rt767
  - Qualcomm Shikra SoC support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmrh7aoACgkQfBQHDyUj
 g0fPsg/7BtywN0/gGM9iv/mpx/XqD0G3pYHtEgvxW0byjKkWkH8IWQKeOyeU2QVu
 67SeqNOZyU5CLCQWU3qeGqQhpjWizq6VB2JMyh/trPi+rNwkZunMP8QnwDccB15y
 JpdnAGPs1l1VQJFv6VjkNVXQhQUK7RZrzDxwiXfNeT4jcbuhc9ei3OupZLYQrt2y
 mEhY21khEidt+/3z3n9dkcNRuCkRhIj47USkv5XG2Hh4zKK4XevohbwvaIW2etaH
 k+2x+zuKmuufDFYPtZdw/2v6E78yw3dy2Zqm5kQfqT4W73p2ImI9COSDsi9WhY7K
 DkAEhgtdzd3BDlSpbpy9hgEiavT9bcavDFNt1wAYu54idh0ImbbldweOqQDU5sjt
 Tb4FUxksTFC/Mx0vOiuuMhH2uGKugW/cxk8fPyTotUGmvoIUJAtNfLIMAbz7PwNm
 cfhES2GJ9qYCgjJqgzRP1+4C08Bj1cmQ3jpUyp3pyYqqYSxeWC217geJHD/Jslzy
 CDxshVQ/4buT58I1gy3alaJK2ci9+/CgsEUX/N0QT+G2+nVB2W1DQtdWFzK+0cHv
 AV+qMaMiCjwOYn5rwWwNvJR6bjtfKDzETwmgtZnbNKeoZ7C6mTcgv/tF+razL4jE
 18e6miqVKvC4GTi594zWHaVBJ6+E2E/x2eVL/bQQtzUSejmOHUQ=
 =V73K
 -----END PGP SIGNATURE-----

Merge tag 'soundwire-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire

Pull soundwire updates from Vinod Koul:

 - CLOCK_STOP_MODE1 support in bus and Intel driver

 - DMI quirks for disabling ghost on Lenovo Yoga 7 Pro, Lenovo Yoga
   Slim 7, Lenovo Pro 9, Lenovo Yoga Slim 7i 14IPH11.

 - Few more codecs as wake capable, tac5xx2 family, rt718, rt766/rt767

 - Qualcomm Shikra SoC support

* tag 'soundwire-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: intel_auxdevice: add rt766/rt767 codec to wake_capable_list
  soundwire: dmi-quirks: Disable ghost rt711 on Lenovo Yoga Slim 7
  soundwire: dmi-quirks: Disable ghost Realtek on Lenovo Yoga 7 Pro
  soundwire: intel_auxdevice: add rt718 codec to wake_capable_list
  soundwire: dmi-quirks: drop the ghost RT1320 on the Surface Pro 11 (Intel)
  soundwire: qcom: add EE-aware register layout for Shikra
  dt-bindings: soundwire: qcom: add qcom,shikra-soundwire compatible
  soundwire: Intel: stop sdw clock in system suspend
  soundwire: Intel: add wake_enable parameter to stop_bus ops
  soundwire: bus: add CLOCK_STOP_MODE1 support back
  soundwire: dmi-quirks: Disable ghost Realtek on Lenovo Pro 9
  soundwire: qcom: cache SCP_ADDRPAGE1/2 to preserve MBQ atomicity
  soundwire: bus: iterate DT compatibles for class-ID match
  soundwire: fix repeated words in log messages
  soundwire: intel_auxdevice: add tac5xx2 family to wake_capable_list
  soundwire: bus_type: Use devres to free slave IDA
  soundwire: dmi-quirks: add Lenovo Yoga Slim 7i 14IPH11
  soundwire: intel: initialise quirk_mask before reading it
This commit is contained in:
Linus Torvalds 2026-10-28 07:18:59 -10:00
commit 1cb6cfab09
12 changed files with 255 additions and 74 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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 */
{
@ -221,6 +249,20 @@ 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"),
DMI_MATCH(DMI_PRODUCT_NAME, "83QM"),
},
.driver_data = (void *)ghost_realtek,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@ -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,
},
{}
};

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */},
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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);