From 0de94589cdeb1309f8205e27abc7cb1f8d358296 Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Sat, 5 Sep 2026 01:32:24 +0200 Subject: [PATCH] ASoC: wm8962: don't clear a SYSCLK_ENA that DAPM owns in configure_bclk wm8962_configure_bclk() provisionally sets SYSCLK_ENA so that the read-only DSPCLK_DIV field becomes valid, then clears it again whenever the bias level is below SND_SOC_BIAS_ON. It never checks whether the bit was already set. It can be: the "SYSCLK" DAPM supply widget owns that bit, and wm8962_mic_detect() force-enables the pin. On boards that call it (the Tegra machine driver, i.e. Microsoft Surface RT and Surface 2) DAPM sets SYSCLK_ENA once at card init and, because the widget then never changes power state, never writes it again. The first stream's transition to SND_SOC_BIAS_PREPARE runs configure_bclk(), which clears the bit behind DAPM's back. From then on every stream plays with SYSCLK off: the DACs do not run, CP_ENA refuses to latch, hp_event() reports "DC servo timed out", and the codec is silent although every register DAPM believes in looks right. Remember whether SYSCLK_ENA was set on entry and only undo our own provisional enable. Verified on a Microsoft Surface 2 (Tegra 4, WM8962 rev F): with the bit forced on over I2C during a stream the DC servo completes in ~56 ms and audio is heard; with this patch applied the DC servo completes on every stream and there is no more "DC servo timed out". Fixes: 75704ecfbb41 ("ASoC: wm8962: Enable SYSCLK provisonally before fetching generated DSPCLK_DIV") Cc: stable@vger.kernel.org Co-authored-by: Claude Fable 5.1 Signed-off-by: Maxime Douailin --- sound/soc/codecs/wm8962.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 68ea15be73309..43d68f168c405 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2466,6 +2466,7 @@ static void wm8962_configure_bclk(struct snd_soc_component *component) int clocking2 = 0; int clocking4 = 0; int aif2 = 0; + bool sysclk_was_ena; if (!wm8962->sysclk_rate) { dev_dbg(component->dev, "No SYSCLK configured\n"); @@ -2498,8 +2499,19 @@ static void wm8962_configure_bclk(struct snd_soc_component *component) /* DSPCLK_DIV can be only generated correctly after enabling SYSCLK. * So we here provisionally enable it and then disable it afterward * if current bias_level hasn't reached SND_SOC_BIAS_ON. + * + * SYSCLK_ENA is owned by the "SYSCLK" DAPM supply widget, which may + * already have it set even below SND_SOC_BIAS_ON: wm8962_mic_detect() + * force-enables that pin, so on boards using it (the Tegra machine + * driver) the bit is set once at card init and DAPM never writes it + * again. If we unconditionally clear it here, SYSCLK stays off for + * every stream, DAPM still believes it is on, and the codec is + * silent with "DC servo timed out" errors. Only undo what we did. */ - if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) + sysclk_was_ena = snd_soc_component_read(component, WM8962_CLOCKING2) & + WM8962_SYSCLK_ENA; + if (!sysclk_was_ena && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) snd_soc_component_update_bits(component, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA); @@ -2513,7 +2525,8 @@ static void wm8962_configure_bclk(struct snd_soc_component *component) usleep_range(500, 1000); dspclk = snd_soc_component_read(component, WM8962_CLOCKING1); - if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) + if (!sysclk_was_ena && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) snd_soc_component_update_bits(component, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, 0);