mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
1cb6cfab09
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user