ASoC: max98396: Fix TDM mode BSEL settings
authorDaniel Mack <daniel@zonque.org>
Wed, 29 Jun 2022 05:06:30 +0000 (07:06 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 29 Jun 2022 10:58:44 +0000 (11:58 +0100)
In TDM mode, the BSEL register value must be set according to table 5 in the
datasheet. This patch adds a lookup function and uses it in
max98396_dai_tdm_slot().

As the first 3 entries can also be used for non-TDM setups, the code re-uses
the same table for such scenarios.

Signed-off-by: Daniel Mack <daniel@zonque.org>
Link: https://lore.kernel.org/r/20220629050630.2848317-1-daniel@zonque.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/max98396.c
sound/soc/codecs/max98396.h

index f28831f..1b6f053 100644 (file)
@@ -438,47 +438,68 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        return 0;
 }
 
-/* BCLKs per LRCLK */
-static const int bclk_sel_table[] = {
-       32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+#define MAX98396_BSEL_32       0x2
+#define MAX98396_BSEL_48       0x3
+#define MAX98396_BSEL_64       0x4
+#define MAX98396_BSEL_96       0x5
+#define MAX98396_BSEL_128      0x6
+#define MAX98396_BSEL_192      0x7
+#define MAX98396_BSEL_256      0x8
+#define MAX98396_BSEL_384      0x9
+#define MAX98396_BSEL_512      0xa
+#define MAX98396_BSEL_320      0xb
+#define MAX98396_BSEL_250      0xc
+#define MAX98396_BSEL_125      0xd
+
+/* Refer to table 5 in the datasheet */
+static const struct max98396_pcm_config {
+       int in, out, width, bsel, max_sr;
+} max98396_pcm_configs[] = {
+       { .in = 2,  .out = 4,  .width = 16, .bsel = MAX98396_BSEL_32,  .max_sr = 192000 },
+       { .in = 2,  .out = 6,  .width = 24, .bsel = MAX98396_BSEL_48,  .max_sr = 192000 },
+       { .in = 2,  .out = 8,  .width = 32, .bsel = MAX98396_BSEL_64,  .max_sr = 192000 },
+       { .in = 3,  .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+       { .in = 4,  .out = 8,  .width = 16, .bsel = MAX98396_BSEL_64,  .max_sr = 192000 },
+       { .in = 4,  .out = 12, .width = 24, .bsel = MAX98396_BSEL_96,  .max_sr = 192000 },
+       { .in = 4,  .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 192000 },
+       { .in = 5,  .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+       { .in = 7,  .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 192000 },
+       { .in = 2,  .out = 4,  .width = 16, .bsel = MAX98396_BSEL_32,  .max_sr = 96000  },
+       { .in = 2,  .out = 6,  .width = 24, .bsel = MAX98396_BSEL_48,  .max_sr = 96000  },
+       { .in = 2,  .out = 8,  .width = 32, .bsel = MAX98396_BSEL_64,  .max_sr = 96000  },
+       { .in = 3,  .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 96000  },
+       { .in = 4,  .out = 8,  .width = 16, .bsel = MAX98396_BSEL_64,  .max_sr = 96000  },
+       { .in = 4,  .out = 12, .width = 24, .bsel = MAX98396_BSEL_96,  .max_sr = 96000  },
+       { .in = 4,  .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 96000  },
+       { .in = 5,  .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 96000  },
+       { .in = 7,  .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 96000  },
+       { .in = 7,  .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 96000  },
+       { .in = 8,  .out = 16, .width = 16, .bsel = MAX98396_BSEL_128, .max_sr = 96000  },
+       { .in = 8,  .out = 24, .width = 24, .bsel = MAX98396_BSEL_192, .max_sr = 96000  },
+       { .in = 8,  .out = 32, .width = 32, .bsel = MAX98396_BSEL_256, .max_sr = 96000  },
+       { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 96000  },
+       { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 96000  },
+       { .in = 16, .out = 32, .width = 16, .bsel = MAX98396_BSEL_256, .max_sr = 96000  },
+       { .in = 7,  .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 48000  },
+       { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 48000  },
+       { .in = 10, .out = 40, .width = 32, .bsel = MAX98396_BSEL_320, .max_sr = 48000  },
+       { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 48000  },
+       { .in = 16, .out = 48, .width = 24, .bsel = MAX98396_BSEL_384, .max_sr = 48000  },
+       { .in = 16, .out = 64, .width = 32, .bsel = MAX98396_BSEL_512, .max_sr = 48000  },
 };
 
-static int max98396_get_bclk_sel(int bclk)
+static int max98396_pcm_config_index(int in_slots, int out_slots, int width)
 {
        int i;
-       /* match BCLKs per LRCLK */
-       for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
-               if (bclk_sel_table[i] == bclk)
-                       return i + 2;
-       }
-       return 0;
-}
-
-static int max98396_set_clock(struct snd_soc_component *component,
-                             struct snd_pcm_hw_params *params)
-{
-       struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
-       /* BCLK/LRCLK ratio calculation */
-       int blr_clk_ratio = params_channels(params) * max98396->ch_size;
-       int value;
 
-       if (!max98396->tdm_mode) {
-               /* BCLK configuration */
-               value = max98396_get_bclk_sel(blr_clk_ratio);
-               if (!value) {
-                       dev_err(component->dev,
-                               "blr_clk_ratio %d unsupported, format %d\n",
-                               blr_clk_ratio, params_format(params));
-                       return -EINVAL;
-               }
+       for (i = 0; i < ARRAY_SIZE(max98396_pcm_configs); i++) {
+               const struct max98396_pcm_config *c = &max98396_pcm_configs[i];
 
-               regmap_update_bits(max98396->regmap,
-                                  MAX98396_R2042_PCM_CLK_SETUP,
-                                  MAX98396_PCM_CLK_SETUP_BSEL_MASK,
-                                  value);
+               if (in_slots == c->in && out_slots <= c->out && width == c->width)
+                       return i;
        }
 
-       return 0;
+       return -1;
 }
 
 static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
@@ -489,8 +510,7 @@ static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
        struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component);
        unsigned int sampling_rate = 0;
        unsigned int chan_sz = 0;
-       int ret, reg;
-       int status;
+       int ret, reg, status, bsel = 0;
        bool update = false;
 
        /* pcm mode configuration */
@@ -510,8 +530,6 @@ static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
                goto err;
        }
 
-       max98396->ch_size = snd_pcm_format_width(params_format(params));
-
        dev_dbg(component->dev, "format supported %d",
                params_format(params));
 
@@ -559,6 +577,33 @@ static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
                goto err;
        }
 
+       if (max98396->tdm_mode) {
+               if (params_rate(params) > max98396->tdm_max_samplerate) {
+                       dev_err(component->dev, "TDM sample rate %d too high",
+                               params_rate(params));
+                       goto err;
+               }
+       } else {
+               /* BCLK configuration */
+               ret = max98396_pcm_config_index(params_channels(params),
+                                               params_channels(params),
+                                               snd_pcm_format_width(params_format(params)));
+               if (ret < 0) {
+                       dev_err(component->dev,
+                               "no PCM config for %d channels, format %d\n",
+                               params_channels(params), params_format(params));
+                       goto err;
+               }
+
+               bsel = max98396_pcm_configs[ret].bsel;
+
+               if (params_rate(params) > max98396_pcm_configs[ret].max_sr) {
+                       dev_err(component->dev, "sample rate %d too high",
+                               params_rate(params));
+                       goto err;
+               }
+       }
+
        ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status);
        if (ret < 0)
                goto err;
@@ -604,12 +649,16 @@ static int max98396_dai_hw_params(struct snd_pcm_substream *substream,
                                   MAX98396_IVADC_SR_MASK,
                                   sampling_rate << MAX98396_IVADC_SR_SHIFT);
 
-       ret = max98396_set_clock(component, params);
+       if (bsel)
+               regmap_update_bits(max98396->regmap,
+                               MAX98396_R2042_PCM_CLK_SETUP,
+                               MAX98396_PCM_CLK_SETUP_BSEL_MASK,
+                               bsel);
 
        if (status && update)
                max98396_global_enable_onoff(max98396->regmap, true);
 
-       return ret;
+       return 0;
 
 err:
        return -EINVAL;
@@ -634,13 +683,16 @@ static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
                max98396->tdm_mode = true;
 
        /* BCLK configuration */
-       bsel = max98396_get_bclk_sel(slots * slot_width);
-       if (bsel == 0) {
-               dev_err(component->dev, "BCLK %d not supported\n",
-                       slots * slot_width);
+       ret = max98396_pcm_config_index(slots, slots, slot_width);
+       if (ret < 0) {
+               dev_err(component->dev, "no TDM config for %d slots %d bits\n",
+                       slots, slot_width);
                return -EINVAL;
        }
 
+       bsel = max98396_pcm_configs[ret].bsel;
+       max98396->tdm_max_samplerate = max98396_pcm_configs[ret].max_sr;
+
        /* Channel size configuration */
        switch (slot_width) {
        case 16:
index ff330ef..7278c77 100644 (file)
@@ -306,8 +306,8 @@ struct max98396_priv {
        unsigned int spkfb_slot;
        unsigned int bypass_slot;
        bool interleave_mode;
-       unsigned int ch_size;
        bool tdm_mode;
+       int tdm_max_samplerate;
        int device_id;
 };
 #endif