soundwire updates for 7.2

- Improvements in handling of soundwire groups
  - Additional checks flagged by various tools
  - Intel driver updates for ghost Realtek device handling in firmware and
    adding devices to wake lists
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmo6rhwACgkQfBQHDyUj
 g0dYtBAAiEa7b58duDNxl2lf4YlZBqa/MvMfMWVMbW5ZtYd00aokv2QBIEVubb22
 a5l+7T7X5wHA1meS3mrsYktWInjJTe257b3qpwghfhbBkbLnbMs7esmZES03p9eg
 9lcmznhJnWSFddO8/CTch3EALGjmqN1WnCjwCaqjFXeQc36qxWILbEzShKtu88CE
 IU8MAvMyj4TWtklbZh6Y/VKKc+tcah2KgdHmKhv+z/ypiDUH5k5fHORQOsHo+Hxt
 rukFL4hQvdBA1B7//Q6u6Usvu7yKQQRsYQ6ybZi9ZXa7+GJ5BOznfM5fTVU5mQjS
 +xbPH/CMrdDH/HxdoKns72DQvC2n18tAGobS0UiadpTLSWkZ9PypW3VfYUb5Z+Ur
 iWt+plzYXceM8viq3KcmgV07gzTwLXopBWv0iVDP9fEBzVnpJbX/l5+yUwtfabq0
 IJMzSILlKoqwIJgP69rN3SScTm1G3biB6m+Y3GCzeUBbEEQs4JQB6mjkPVvpP7/I
 qp9pz5mQJwVBoInlKzYWlNmAEjWRqmQUoEYU0g35vI7bcWjtbtemFzuDFXkqZMp9
 wk784sXlXOvUoBudBuJFy2OG/tdELarmVj/Pk7U1XNPAzh39nf9tMfQnj9CzOxVR
 trzkPKoR7m65WbuIFt8nlC81JOSunDRBmXvBSzbXvVvdVqd9Vn0=
 =dF8L
 -----END PGP SIGNATURE-----

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

Pull soundwire updates from Vinod Koul:

 - Improvements in handling of soundwire groups

 - Additional checks flagged by various tools

 - Intel driver updates for ghost Realtek device handling in firmware
   and adding devices to wake lists

* tag 'soundwire-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: dmi-quirks: Disable ghost Realtek devices
  soundwire: only handle alert events when the peripheral is attached
  soundwire: intel_ace2x: release bpt_stream when close it
  soundwire: intel: Move suspend tracking from trigger to pm suspend
  soundwire: intel_auxdevice: Add es9356 to wake_capable_list
  soundwire: use krealloc_array to prevent integer overflow
  soundwire: increase group->max_size after allocation
  soundwire: fix bug in sdw_add_element_group_count found by syzkaller
  soundwire: don't program SDW_SCP_BUSCLOCK_SCALE on a unattached Peripheral
  soundwire: validate DT compatible before parsing it
  soundwire: intel_auxdevice: Add cs42l43b to wake_capable_list
  soundwire: stream: sdw_stream_remove_slave(): Check stream is valid
This commit is contained in:
Linus Torvalds 2026-06-23 13:58:38 -07:00
commit 240303e47f
8 changed files with 116 additions and 70 deletions

View File

@ -1986,6 +1986,10 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
break;
case SDW_SLAVE_ALERT:
if (slave->status != SDW_SLAVE_ATTACHED &&
slave->status != SDW_SLAVE_ALERT)
continue;
ret = sdw_handle_slave_alerts(slave);
if (ret < 0)
dev_err(&slave->dev,

View File

@ -90,6 +90,19 @@ static const struct adr_remap intel_rooks_county[] = {
{}
};
/*
* Many platforms have ghost realtek devices in the ACPI that don't physically
* exist, remove those devices.
*/
static const struct adr_remap ghost_realtek[] = {
/* rt722 on link3 */
{
0x000330025d072201ull,
0x0000000000000000ull
},
{}
};
static const struct dmi_system_id adr_remap_quirk_table[] = {
/* TGL devices */
{
@ -164,6 +177,28 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
},
.driver_data = (void *)hp_omen_16,
},
/* PTL devices */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
DMI_MATCH(DMI_BOARD_NAME, "UX5406AA"),
},
.driver_data = (void *)ghost_realtek,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "83QK"),
},
.driver_data = (void *)ghost_realtek,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "83SF"),
},
.driver_data = (void *)ghost_realtek,
},
{}
};

View File

@ -299,39 +299,35 @@ static int sdw_add_element_group_count(struct sdw_group *group,
int num = group->count;
int i;
for (i = 0; i <= num; i++) {
for (i = 0; i < num; i++) {
if (rate == group->rates[i] && lane == group->lanes[i])
break;
if (i != num)
continue;
if (group->count >= group->max_size) {
unsigned int *rates;
unsigned int *lanes;
group->max_size += 1;
rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!rates)
return -ENOMEM;
group->rates = rates;
lanes = krealloc(group->lanes,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!lanes)
return -ENOMEM;
group->lanes = lanes;
}
group->rates[group->count] = rate;
group->lanes[group->count++] = lane;
return 0;
}
if (group->count >= group->max_size) {
unsigned int *rates;
unsigned int *lanes;
rates = krealloc_array(group->rates, group->max_size + 1,
sizeof(*group->rates), GFP_KERNEL);
if (!rates)
return -ENOMEM;
group->rates = rates;
lanes = krealloc_array(group->lanes, group->max_size + 1,
sizeof(*group->lanes), GFP_KERNEL);
if (!lanes)
return -ENOMEM;
group->lanes = lanes;
group->max_size += 1;
}
group->rates[group->count] = rate;
group->lanes[group->count++] = lane;
return 0;
}

View File

@ -906,19 +906,6 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_SUSPEND:
/*
* The .prepare callback is used to deal with xruns and resume operations.
* In the case of xruns, the DMAs and SHIM registers cannot be touched,
* but for resume operations the DMAs and SHIM registers need to be initialized.
* the .trigger callback is used to track the suspend case only.
*/
dai_runtime->suspended = true;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dai_runtime->paused = true;
break;
@ -955,10 +942,12 @@ static int intel_component_dais_suspend(struct snd_soc_component *component)
struct snd_soc_dai *dai;
/*
* In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
* does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
* Since the component suspend is called last, we can trap this corner case
* and force the DAIs to release their resources.
* Mark all open streams as suspended.
* Open streams at this point can be in SUSPENDED, PAUSED or STOPPED
* state and during prepare the DMAs and SHIM registers need to be
* initialized for them.
* The STOPPED state is a special corner case which can happen if audio
* experiences xrun at suspend time.
*/
for_each_component_dais(component, dai) {
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
@ -966,13 +955,7 @@ static int intel_component_dais_suspend(struct snd_soc_component *component)
dai_runtime = cdns->dai_runtime_array[dai->id];
if (!dai_runtime)
continue;
if (dai_runtime->suspended)
continue;
if (dai_runtime->paused)
if (dai_runtime)
dai_runtime->suspended = true;
}

View File

@ -317,6 +317,7 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
__func__, ret);
sdw_release_stream(cdns->bus.bpt_stream);
cdns->bus.bpt_stream = NULL;
}
@ -894,19 +895,6 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_SUSPEND:
/*
* The .prepare callback is used to deal with xruns and resume operations.
* In the case of xruns, the DMAs and SHIM registers cannot be touched,
* but for resume operations the DMAs and SHIM registers need to be initialized.
* the .trigger callback is used to track the suspend case only.
*/
dai_runtime->suspended = true;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dai_runtime->paused = true;
break;
@ -930,8 +918,34 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
.get_stream = intel_get_sdw_stream,
};
static int intel_component_dais_suspend(struct snd_soc_component *component)
{
struct snd_soc_dai *dai;
/*
* Mark all open streams as suspended.
* Open streams at this point can be in SUSPENDED, PAUSED or STOPPED
* state and during prepare the DMAs and SHIM registers need to be
* initialized for them.
* The STOPPED state is a special corner case which can happen if audio
* experiences xrun at suspend time.
*/
for_each_component_dais(component, dai) {
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
struct sdw_cdns_dai_runtime *dai_runtime;
dai_runtime = cdns->dai_runtime_array[dai->id];
if (dai_runtime)
dai_runtime->suspended = true;
}
return 0;
}
static const struct snd_soc_component_driver dai_component = {
.name = "soundwire",
.suspend = intel_component_dais_suspend,
};
/*

View File

@ -51,6 +51,8 @@ struct wake_capable_part {
};
static struct wake_capable_part wake_capable_list[] = {
{0x01fa, 0x2A30},
{0x01fa, 0x2A3B},
{0x01fa, 0x4243},
{0x01fa, 0x4245},
{0x01fa, 0x4249},
@ -70,6 +72,7 @@ static struct wake_capable_part wake_capable_list[] = {
{0x025d, 0x717},
{0x025d, 0x721},
{0x025d, 0x722},
{0x04b3, 0x9356},
};
static bool is_wake_capable(struct sdw_slave *slave)

View File

@ -244,8 +244,8 @@ int sdw_of_find_slaves(struct sdw_bus *bus)
struct sdw_slave_id id;
const __be32 *addr;
compat = of_get_property(node, "compatible", NULL);
if (!compat)
ret = of_property_read_string(node, "compatible", &compat);
if (ret)
continue;
ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version,

View File

@ -697,6 +697,13 @@ static int sdw_program_params(struct sdw_bus *bus, bool prepare)
if (scale_index < 0)
return scale_index;
/* Skip the unattached Peripherals */
if (!completion_done(&slave->enumeration_complete)) {
dev_warn(&slave->dev,
"Not enumerated, skip programming BUSCLOCK_SCALE\n");
continue;
}
ret = sdw_write_no_pm(slave, addr1, scale_index);
if (ret < 0) {
dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n");
@ -2229,11 +2236,15 @@ EXPORT_SYMBOL(sdw_stream_add_slave);
* @slave: SDW Slave instance
* @stream: SoundWire stream
*
* This removes and frees port_rt and slave_rt from a stream
* This removes and frees port_rt and slave_rt from a stream.
* If stream is NULL or an ERR_PTR, do nothing and return 0.
*/
int sdw_stream_remove_slave(struct sdw_slave *slave,
struct sdw_stream_runtime *stream)
{
if (IS_ERR_OR_NULL(stream))
return 0;
mutex_lock(&slave->bus->bus_lock);
sdw_slave_port_free(slave, stream);