]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: tlv320aic3x: restrict CLKDIV bypass Q values in dual-rate mode
authorSen Wang <sen@ti.com>
Tue, 16 Jun 2026 23:33:22 +0000 (18:33 -0500)
committerMark Brown <broonie@kernel.org>
Wed, 17 Jun 2026 13:56:31 +0000 (14:56 +0100)
The datasheet documents that when the PLL is disabled and dual-rate mode
is enabled, only Q values {4, 8, 9, 12, 16} are valid for the CLKDIV
bypass path; all other Q values produce invalid bitclock output.

The existing loop iterates Q from 2 to 17 without this restriction,
causing silent audio failure when an out-of-spec Q is picked.

Restrict the Q search to the allowed set in dual-rate mode.

Fixes: 4f9c16ccfa26 ("[ALSA] soc - tlv320aic3x - revisit clock setup")
Suggested-by: Mir Jeffres <m-jeffres@ti.com>
Signed-off-by: Sen Wang <sen@ti.com>
Link: https://patch.msgid.link/20260616233322.873081-1-sen@ti.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/tlv320aic3x.c

index b12c1952823ba59c364a29d37d3603937bdb69bd..b38393a8130ff8519d9e639a6e8925ecc1fd4da5 100644 (file)
@@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *params,
                           struct snd_soc_dai *dai)
 {
+       static const u8 dual_rate_q[] = {4, 8, 9, 12, 16};
        struct snd_soc_component *component = dai->component;
        struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
        int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
        u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
        u16 d, pll_d = 1;
+       bool dual_rate;
        int clk;
        int width = aic3x->slot_width;
 
@@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 
        /* Fsref can be 44100 or 48000 */
        fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+       dual_rate = params_rate(params) >= 64000;
 
        /* Try to find a value for Q which allows us to bypass the PLL and
         * generate CODEC_CLK directly. */
-       for (pll_q = 2; pll_q < 18; pll_q++)
-               if (aic3x->sysclk / (128 * pll_q) == fsref) {
-                       bypass_pll = 1;
-                       break;
+       if (dual_rate) {
+               for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) {
+                       pll_q = dual_rate_q[i];
+                       if (aic3x->sysclk / (128 * pll_q) == fsref) {
+                               bypass_pll = 1;
+                               break;
+                       }
                }
+       } else {
+               for (pll_q = 2; pll_q < 18; pll_q++)
+                       if (aic3x->sysclk / (128 * pll_q) == fsref) {
+                               bypass_pll = 1;
+                               break;
+                       }
+       }
 
        if (bypass_pll) {
                pll_q &= 0xf;
@@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
         * right DAC to right channel input */
        data = (LDAC2LCH | RDAC2RCH);
        data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
-       if (params_rate(params) >= 64000)
+       if (dual_rate)
                data |= DUAL_RATE_MODE;
        snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
 
        /* codec sample rate select */
        data = (fsref * 20) / params_rate(params);
-       if (params_rate(params) < 64000)
+       if (!dual_rate)
                data /= 2;
        data /= 5;
        data -= 2;