ASoC: qcom: AudioReach and WSA88xx fixes

Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> says:

Fixes found while bringing up speaker protection on the Lenovo ThinkPad
T14s Gen 7 (Snapdragon X2 Elite) with WSA8845 speakers.

 - AudioReach: the module cfg blob size was used as an element count,
   so the allocation and copy were four times too large.
 - WSA88xx: speaker temperatures above 45 degC were rejected, runtime
   PM was left enabled on unbind (NULL dereference on wsa884x), and
   the VI sense port carried only half of the V/I pair.

Tool use: an LLM coding assistant (Claude Code with Claude Opus) was
used for some of the patches in this series, to debug the issues during
bring-up and to draft the code and commit messages.

Link: https://patch.msgid.link/20261023171316.819857-1-srinivas.kandagatla@oss.qualcomm.com
This commit is contained in:
Mark Brown 2026-10-26 20:04:23 +00:00
commit e163f6f079
3 changed files with 49 additions and 22 deletions

View File

@ -456,10 +456,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
* outside of valid range.
* outside of the two-point trim range. Do not narrow this to ambient: a
* speaker that has been playing easily runs well above 45 degC.
*/
#define WSA883X_LOW_TEMP_THRESHOLD 5
#define WSA883X_HIGH_TEMP_THRESHOLD 45
#define WSA883X_LOW_TEMP_THRESHOLD WSA883X_T1_TEMP
#define WSA883X_HIGH_TEMP_THRESHOLD WSA883X_T2_TEMP
struct wsa883x_priv {
struct regmap *regmap;
@ -551,7 +552,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = {
.num = WSA883X_PORT_VISENSE + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 1,
.max_ch = 2,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}
@ -572,7 +573,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
},
[WSA883X_PORT_VISENSE] = {
.num = WSA883X_PORT_VISENSE + 1,
.ch_mask = 0x1,
.ch_mask = 0x3,
},
};
@ -1430,7 +1431,7 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
unsigned int dmeas_msb = 0, dmeas_lsb = 0;
int d1, d2, dmeas;
unsigned int mask;
int ret, range;
int ret;
long val;
guard(mutex)(&wsa883x->sp_lock);
@ -1493,8 +1494,8 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
}
val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1));
range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD;
if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) {
if (val >= WSA883X_LOW_TEMP_THRESHOLD &&
val <= WSA883X_HIGH_TEMP_THRESHOLD) {
wsa883x->temperature = val;
*temp = val * 1000;
ret = 0;
@ -1668,7 +1669,9 @@ static int wsa883x_probe(struct sdw_slave *pdev,
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
goto err;
ret = devm_snd_soc_register_component(dev,
&wsa883x_component_drv,

View File

@ -720,10 +720,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
* outside of valid range.
* outside of the two-point trim range. Do not narrow this to ambient: a
* speaker that has been playing easily runs well above 45 degC.
*/
#define WSA884X_LOW_TEMP_THRESHOLD 5
#define WSA884X_HIGH_TEMP_THRESHOLD 45
#define WSA884X_LOW_TEMP_THRESHOLD WSA884X_T1_TEMP
#define WSA884X_HIGH_TEMP_THRESHOLD WSA884X_T2_TEMP
struct wsa884x_priv {
struct regmap *regmap;
@ -858,7 +859,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.num = WSA884X_PORT_VISENSE + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 1,
.max_ch = 2,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
@ -891,7 +892,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
},
[WSA884X_PORT_VISENSE] = {
.num = WSA884X_PORT_VISENSE + 1,
.ch_mask = 0x1,
.ch_mask = 0x3,
},
[WSA884X_PORT_CPS] = {
.num = WSA884X_PORT_CPS + 1,
@ -1929,8 +1930,8 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
val, dmeas, d1, d2);
if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
(val < WSA884X_HIGH_TEMP_THRESHOLD)) {
if (val >= WSA884X_LOW_TEMP_THRESHOLD &&
val <= WSA884X_HIGH_TEMP_THRESHOLD) {
wsa884x->temperature = val;
*temp = val * 1000;
ret = 0;
@ -2124,7 +2125,9 @@ static int wsa884x_probe(struct sdw_slave *pdev,
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
return devm_snd_soc_register_component(dev,
&wsa884x_component_drv,

View File

@ -312,22 +312,37 @@ audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private)
for (sz = 0; sz < le32_to_cpu(private->size); ) {
const struct snd_soc_tplg_vendor_array *mod_array;
u32 array_size;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
array_size = le32_to_cpu(mod_array->size);
if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
struct audioreach_module_priv_data *pdata;
u32 nwords;
pdata = kzalloc_flex(*pdata, data,
le32_to_cpu(mod_array->size));
/* size is the byte count of the __le32 data[] */
nwords = array_size / sizeof(__le32);
/* the blob must lie within the private data */
if (sizeof(*pdata) + (u64)array_size >
le32_to_cpu(private->size) - sz)
return ERR_PTR(-EINVAL);
pdata = kzalloc_flex(*pdata, data, nwords);
if (!pdata)
return ERR_PTR(-ENOMEM);
memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data,
le32_to_cpu(mod_array->size)));
memcpy(pdata, ((u8 *)private->data + sz),
struct_size(pdata, data, nwords));
return pdata;
}
sz = sz + le32_to_cpu(mod_array->size);
/* a zero size would never advance the walk */
if (!array_size)
return ERR_PTR(-EINVAL);
sz = sz + array_size;
}
return NULL;
@ -611,6 +626,12 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
return mod ? PTR_ERR(mod) : -ENODEV;
mod->data = audioreach_get_module_priv_data(&tplg_w->priv);
if (IS_ERR(mod->data)) {
int rc = PTR_ERR(mod->data);
mod->data = NULL;
return rc;
}
dobj = &w->dobj;
dobj->private = mod;