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

Pengpeng Hou <[email protected]>
Newsgroups gmane.linux.sound,gmane.linux.kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.