[PATCH 10/20] ASoC: tlv320aic3x: Propagate power restoration errors

From: Pengpeng Hou

Date: Fri Aug 28 2026 - 07:36:32 EST


aic3x_set_power() ignores the model-specific bypassed register sequence
and cache replay, while aic3x_set_bias_level() ignores the helper
result. A failed OFF-to-STANDBY transition can therefore be reported as
successful with power set.

Check both register restore operations, return the device to reset,
cache-only and supplies-off state on failure, and propagate power-on and
power-off results from the DAPM bias callback.

The issue was identified via static analysis and manually reviewed.

Fixes: 2a6fedec195b ("ASoC: tlv320aic3x: Convert to direct regmap API usage")
Fixes: 733a763dd8b3 ("ASoC: tlv320aic3x: Fix class-D initialization for tlv320aic3007")

Assisted-by: LLM
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
sound/soc/codecs/tlv320aic3x.c | 36 +++++++++++++++++++++++++++-------
1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index b38393a8130f..b5ef59169dbe 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1416,13 +1416,19 @@ static int aic3x_set_power(struct snd_soc_component *component, int power)
gpiod_set_value(aic3x->gpio_reset, 0);
}

- if (aic3x->model == AIC3X_MODEL_3007)
- regmap_multi_reg_write_bypassed(aic3x->regmap, aic3007_class_d,
- ARRAY_SIZE(aic3007_class_d));
+ if (aic3x->model == AIC3X_MODEL_3007) {
+ ret = regmap_multi_reg_write_bypassed(aic3x->regmap,
+ aic3007_class_d,
+ ARRAY_SIZE(aic3007_class_d));
+ if (ret)
+ goto err_power_off;
+ }

/* Sync reg_cache with the hardware */
regcache_cache_only(aic3x->regmap, false);
- regcache_sync(aic3x->regmap);
+ ret = regcache_sync(aic3x->regmap);
+ if (ret)
+ goto err_power_off;

/* Rewrite paired PLL D registers in case cached sync skipped
* writing one of them and thus caused other one also not
@@ -1457,6 +1463,17 @@ static int aic3x_set_power(struct snd_soc_component *component, int power)
}
out:
return ret;
+
+err_power_off:
+ if (aic3x->gpio_reset)
+ gpiod_set_value(aic3x->gpio_reset, 1);
+ regcache_cache_only(aic3x->regmap, true);
+ regcache_mark_dirty(aic3x->regmap);
+ aic3x->power = 0;
+ regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+
+ return ret;
}

static int aic3x_set_bias_level(struct snd_soc_component *component,
@@ -1464,6 +1481,7 @@ static int aic3x_set_bias_level(struct snd_soc_component *component,
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
+ int ret;

switch (level) {
case SND_SOC_BIAS_ON:
@@ -1477,8 +1495,12 @@ static int aic3x_set_bias_level(struct snd_soc_component *component,
}
break;
case SND_SOC_BIAS_STANDBY:
- if (!aic3x->power)
- aic3x_set_power(component, 1);
+ if (!aic3x->power) {
+ ret = aic3x_set_power(component, 1);
+ if (ret)
+ return ret;
+ }
+
if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE &&
aic3x->master) {
/* disable pll */
@@ -1488,7 +1510,7 @@ static int aic3x_set_bias_level(struct snd_soc_component *component,
break;
case SND_SOC_BIAS_OFF:
if (aic3x->power)
- aic3x_set_power(component, 0);
+ return aic3x_set_power(component, 0);
break;
}

--
2.43.0