ASoC: max9867: Calculate LRCLK divider
authorLadislav Michl <ladis@linux-mips.org>
Tue, 4 Dec 2018 18:21:04 +0000 (19:21 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 7 Dec 2018 12:20:41 +0000 (12:20 +0000)
Drop "Common NI Values Table" and calculate LRCLK divider, then
add allowed rate constraints based on master clock frequency.

Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/max9867.c
sound/soc/codecs/max9867.h

index c454de4..faec094 100644 (file)
@@ -126,124 +126,106 @@ static const struct snd_soc_dapm_route max9867_audio_map[] = {
        {"LINE_IN", NULL, "Right Line"},
 };
 
-enum rates {
-       pcm_rate_8, pcm_rate_16, pcm_rate_24,
-       pcm_rate_32, pcm_rate_44,
-       pcm_rate_48, max_pcm_rate,
+static const unsigned int max9867_rates_44k1[] = {
+       11025, 22050, 44100,
 };
 
-static const struct ni_div_rates {
-       u32 mclk;
-       u16 ni[max_pcm_rate];
-} ni_div[] = {
-       {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
-       {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
-       {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
-       {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
-       {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
-       {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
-       {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
-       {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
+static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = {
+       .list = max9867_rates_44k1,
+       .count = ARRAY_SIZE(max9867_rates_44k1),
 };
 
-static inline int get_ni_value(int mclk, int rate)
+static const unsigned int max9867_rates_48k[] = {
+       8000, 16000, 32000, 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
+       .list = max9867_rates_48k,
+       .count = ARRAY_SIZE(max9867_rates_48k),
+};
+
+struct max9867_priv {
+       struct regmap *regmap;
+       const struct snd_pcm_hw_constraint_list *constraints;
+       unsigned int sysclk, pclk;
+       bool master, dsp_a;
+};
+
+static int max9867_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
-       int i, ret = 0;
+        struct max9867_priv *max9867 =
+               snd_soc_component_get_drvdata(dai->component);
 
-       /* find the closest rate index*/
-       for (i = 0; i < ARRAY_SIZE(ni_div); i++) {
-               if (ni_div[i].mclk >= mclk)
-                       break;
-       }
-       if (i == ARRAY_SIZE(ni_div))
-               return -EINVAL;
+       if (max9867->constraints)
+               snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, max9867->constraints);
 
-       switch (rate) {
-       case 8000:
-               return ni_div[i].ni[pcm_rate_8];
-       case 16000:
-               return ni_div[i].ni[pcm_rate_16];
-       case 32000:
-               return ni_div[i].ni[pcm_rate_32];
-       case 44100:
-               return ni_div[i].ni[pcm_rate_44];
-       case 48000:
-               return ni_div[i].ni[pcm_rate_48];
-       default:
-               pr_err("%s wrong rate %d\n", __func__, rate);
-               ret = -EINVAL;
-       }
-       return ret;
+       return 0;
 }
 
 static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
                struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
+       int value;
+       unsigned long int rate, ratio;
        struct snd_soc_component *component = dai->component;
        struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
-       unsigned int ni_h, ni_l;
-       int value;
+       unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params),
+                                               max9867->pclk);
 
-       value = get_ni_value(max9867->sysclk, params_rate(params));
-       if (value < 0)
-               return value;
-
-       ni_h = (0xFF00 & value) >> 8;
-       ni_l = 0x00FF & value;
        /* set up the ni value */
        regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
-               MAX9867_NI_HIGH_MASK, ni_h);
+               MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8);
        regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
-               MAX9867_NI_LOW_MASK, ni_l);
-       if (!max9867->master) {
-               /*
-                * digital pll locks on to any externally supplied LRCLK signal
-                * and also enable rapid lock mode.
-                */
-               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
-                       MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
-               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
-                       MAX9867_PLL, MAX9867_PLL);
-       } else {
-               unsigned long int bclk_rate, pclk_bclk_ratio;
-               int bclk_value;
-
-               bclk_rate = params_rate(params) * 2 * params_width(params);
-               pclk_bclk_ratio = max9867->pclk/bclk_rate;
-               switch (params_width(params)) {
-               case 8:
-               case 16:
-                       switch (pclk_bclk_ratio) {
-                       case 2:
-                               bclk_value = MAX9867_IFC1B_PCLK_2;
-                               break;
-                       case 4:
-                               bclk_value = MAX9867_IFC1B_PCLK_4;
-                               break;
+               MAX9867_NI_LOW_MASK, 0x00FF & ni);
+       if (max9867->master) {
+               if (max9867->dsp_a) {
+                       value = MAX9867_IFC1B_48X;
+               } else {
+                       rate = params_rate(params) * 2 * params_width(params);
+                       ratio = max9867->pclk / rate;
+                       switch (params_width(params)) {
                        case 8:
-                               bclk_value = MAX9867_IFC1B_PCLK_8;
-                               break;
                        case 16:
-                               bclk_value = MAX9867_IFC1B_PCLK_16;
+                               switch (ratio) {
+                               case 2:
+                                       value = MAX9867_IFC1B_PCLK_2;
+                                       break;
+                               case 4:
+                                       value = MAX9867_IFC1B_PCLK_4;
+                                       break;
+                               case 8:
+                                       value = MAX9867_IFC1B_PCLK_8;
+                                       break;
+                               case 16:
+                                       value = MAX9867_IFC1B_PCLK_16;
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
+                               break;
+                       case 24:
+                               value = MAX9867_IFC1B_48X;
+                               break;
+                       case 32:
+                               value = MAX9867_IFC1B_64X;
                                break;
                        default:
-                               dev_err(component->dev,
-                                       "unsupported sampling rate\n");
                                return -EINVAL;
                        }
-                       break;
-               case 24:
-                       bclk_value = MAX9867_IFC1B_24BIT;
-                       break;
-               case 32:
-                       bclk_value = MAX9867_IFC1B_32BIT;
-                       break;
-               default:
-                       dev_err(component->dev, "unsupported sampling rate\n");
-                       return -EINVAL;
                }
                regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
-                       MAX9867_IFC1B_BCLK_MASK, bclk_value);
+                       MAX9867_IFC1B_BCLK_MASK, value);
+       } else {
+               /*
+                * digital pll locks on to any externally supplied LRCLK signal
+                * and also enable rapid lock mode.
+                */
+               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+                       MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
+               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+                       MAX9867_PLL, MAX9867_PLL);
        }
        return 0;
 }
@@ -285,8 +267,16 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                        freq);
                return -EINVAL;
        }
-       value = value << MAX9867_PSCLK_SHIFT;
+       if (freq % 48000 == 0)
+               max9867->constraints = &max9867_constraints_48k;
+       else if (freq % 44100 == 0)
+               max9867->constraints = &max9867_constraints_44k1;
+       else
+               dev_warn(component->dev,
+                        "Unable to set exact rate with %uHz clock frequency\n",
+                        freq);
        max9867->sysclk = freq;
+       value = value << MAX9867_PSCLK_SHIFT;
        /* exact integer mode is not supported */
        value &= ~MAX9867_FREQ_MASK;
        regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
@@ -299,16 +289,17 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
 {
        struct snd_soc_component *component = codec_dai->component;
        struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
-       u8 iface1A = 0, iface1B = 0;
+       u8 iface1A, iface1B;
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
-               max9867->master = 1;
-               iface1A |= MAX9867_MASTER;
+               max9867->master = true;
+               iface1A = MAX9867_MASTER;
+               iface1B = MAX9867_IFC1B_48X;
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
-               max9867->master = 0;
-               iface1A &= ~MAX9867_MASTER;
+               max9867->master = false;
+               iface1A = iface1B = 0;
                break;
        default:
                return -EINVAL;
@@ -316,9 +307,11 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
+               max9867->dsp_a = false;
                iface1A |= MAX9867_I2S_DLY;
                break;
        case SND_SOC_DAIFMT_DSP_A:
+               max9867->dsp_a = true;
                iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ;
                break;
        default:
@@ -344,20 +337,18 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
 
        regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
        regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
+
        return 0;
 }
 
 static const struct snd_soc_dai_ops max9867_dai_ops = {
-       .set_fmt = max9867_dai_set_fmt,
        .set_sysclk     = max9867_set_dai_sysclk,
+       .set_fmt        = max9867_dai_set_fmt,
        .digital_mute   = max9867_mute,
-       .hw_params = max9867_dai_hw_params,
+       .startup        = max9867_startup,
+       .hw_params      = max9867_dai_hw_params,
 };
 
-#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
-       SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
-
 static struct snd_soc_dai_driver max9867_dai[] = {
        {
        .name = "max9867-aif1",
@@ -365,15 +356,15 @@ static struct snd_soc_dai_driver max9867_dai[] = {
                .stream_name = "HiFi Playback",
                .channels_min = 2,
                .channels_max = 2,
-               .rates = MAX9867_RATES,
-               .formats = MAX9867_FORMATS,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
        .capture = {
                .stream_name = "HiFi Capture",
                .channels_min = 2,
                .channels_max = 2,
-               .rates = MAX9867_RATES,
-               .formats = MAX9867_FORMATS,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
        .ops = &max9867_dai_ops,
        .symmetric_rates = 1,
index d917085..7f037ab 100644 (file)
 #define MAX9867_PSCLK_10_20  0x1
 #define MAX9867_PSCLK_20_40  0x2
 #define MAX9867_PSCLK_40_60  0x3
-#define MAX9867_AUDIOCLKHIGH 0x06
-#define MAX9867_NI_HIGH_WIDTH 0x7
-#define MAX9867_NI_HIGH_MASK 0x7F
-#define MAX9867_NI_LOW_MASK 0x7F
-#define MAX9867_NI_LOW_SHIFT 0x1
-#define MAX9867_PLL     (1<<7)
-#define MAX9867_AUDIOCLKLOW  0x07
+#define MAX9867_AUDIOCLKHIGH   0x06
+#define MAX9867_NI_HIGH_MASK   0x7F
+#define MAX9867_NI_LOW_MASK    0xFE
+#define MAX9867_PLL            (1<<7)
+#define MAX9867_AUDIOCLKLOW    0x07
 #define MAX9867_RAPID_LOCK   0x01
 #define MAX9867_IFC1A        0x08
 #define MAX9867_MASTER       (1<<7)
 #define MAX9867_BCI_MODE     (1<<5)
 #define MAX9867_IFC1B        0x09
 #define MAX9867_IFC1B_BCLK_MASK 7
-#define MAX9867_IFC1B_32BIT  0x01
-#define MAX9867_IFC1B_24BIT  0x02
-#define MAX9867_IFC1B_PCLK_2 4
-#define MAX9867_IFC1B_PCLK_4 5
-#define MAX9867_IFC1B_PCLK_8 6
-#define MAX9867_IFC1B_PCLK_16 7
+#define MAX9867_IFC1B_64X      0x01
+#define MAX9867_IFC1B_48X      0x02
+#define MAX9867_IFC1B_PCLK_2   0x04
+#define MAX9867_IFC1B_PCLK_4   0x05
+#define MAX9867_IFC1B_PCLK_8   0x06
+#define MAX9867_IFC1B_PCLK_16  0x07
 #define MAX9867_CODECFLTR    0x0a
 #define MAX9867_DACGAIN      0x0b
 #define MAX9867_DACLEVEL     0x0c
 
 #define MAX9867_CACHEREGNUM 10
 
-/* codec private data */
-struct max9867_priv {
-       struct regmap *regmap;
-       unsigned int sysclk;
-       unsigned int pclk;
-       unsigned int master;
-};
 #endif