[PATCH] ASoC: sunxi: sun50i-dmic: resume device for control access

From: Jiale Yao

Date: Wed Oct 28 2026 - 01:09:24 EST


The capture volume controls use regmap directly while runtime PM may
have suspended the DMIC and disabled its clocks. Since the regmap has no
cache, reading or writing a control while suspended accesses unpowered
hardware.

Use control callbacks which resume the device around the regmap access
and release the runtime PM reference afterwards.

Fixes: 9a8cadddd930 ("ASoC: sunxi: DMIC: Add controls for adjusting the mic gains")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Jiale Yao <yaojiale02@xxxxxxx>
---
sound/soc/sunxi/sun50i-dmic.c | 59 +++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c
index 5c784b1f6846..a5f70db856b9 100644
--- a/sound/soc/sunxi/sun50i-dmic.c
+++ b/sound/soc/sunxi/sun50i-dmic.c
@@ -286,22 +286,55 @@ MODULE_DEVICE_TABLE(of, sun50i_dmic_of_match);

static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(sun50i_dmic_vol_scale, -12000, 75, 1);

-static const struct snd_kcontrol_new sun50i_dmic_controls[] = {
+static int sun50i_dmic_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ret;

- SOC_DOUBLE_TLV("DMIC Channel 0 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
- SUN50I_DMIC_D0D1_VOL_CTR_0L, SUN50I_DMIC_D0D1_VOL_CTR_0R,
- 0xFF, 0, sun50i_dmic_vol_scale),
- SOC_DOUBLE_TLV("DMIC Channel 1 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
- SUN50I_DMIC_D0D1_VOL_CTR_1L, SUN50I_DMIC_D0D1_VOL_CTR_1R,
- 0xFF, 0, sun50i_dmic_vol_scale),
- SOC_DOUBLE_TLV("DMIC Channel 2 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
- SUN50I_DMIC_D2D3_VOL_CTR_2L, SUN50I_DMIC_D2D3_VOL_CTR_2R,
- 0xFF, 0, sun50i_dmic_vol_scale),
- SOC_DOUBLE_TLV("DMIC Channel 3 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
- SUN50I_DMIC_D2D3_VOL_CTR_3L, SUN50I_DMIC_D2D3_VOL_CTR_3R,
- 0xFF, 0, sun50i_dmic_vol_scale),
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0)
+ return ret;

+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ pm_runtime_put(component->dev);

+ return ret;
+}
+
+static int sun50i_dmic_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ pm_runtime_put(component->dev);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new sun50i_dmic_controls[] = {
+ SOC_DOUBLE_EXT_TLV("DMIC Channel 0 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
+ SUN50I_DMIC_D0D1_VOL_CTR_0L, SUN50I_DMIC_D0D1_VOL_CTR_0R,
+ 0xFF, 0, sun50i_dmic_get_volsw, sun50i_dmic_put_volsw,
+ sun50i_dmic_vol_scale),
+ SOC_DOUBLE_EXT_TLV("DMIC Channel 1 Capture Volume", SUN50I_DMIC_D0D1_VOL_CTR,
+ SUN50I_DMIC_D0D1_VOL_CTR_1L, SUN50I_DMIC_D0D1_VOL_CTR_1R,
+ 0xFF, 0, sun50i_dmic_get_volsw, sun50i_dmic_put_volsw,
+ sun50i_dmic_vol_scale),
+ SOC_DOUBLE_EXT_TLV("DMIC Channel 2 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
+ SUN50I_DMIC_D2D3_VOL_CTR_2L, SUN50I_DMIC_D2D3_VOL_CTR_2R,
+ 0xFF, 0, sun50i_dmic_get_volsw, sun50i_dmic_put_volsw,
+ sun50i_dmic_vol_scale),
+ SOC_DOUBLE_EXT_TLV("DMIC Channel 3 Capture Volume", SUN50I_DMIC_D2D3_VOL_CTR,
+ SUN50I_DMIC_D2D3_VOL_CTR_3L, SUN50I_DMIC_D2D3_VOL_CTR_3R,
+ 0xFF, 0, sun50i_dmic_get_volsw, sun50i_dmic_put_volsw,
+ sun50i_dmic_vol_scale),
};

static const struct snd_soc_component_driver sun50i_dmic_component = {
--
2.34.1