ASoC: sunxi: i2s: Implement set_sysclk
authorMaxime Ripard <maxime.ripard@free-electrons.com>
Mon, 7 Nov 2016 13:08:19 +0000 (14:08 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 9 Nov 2016 14:01:08 +0000 (14:01 +0000)
In our i2s driver, we were previously trying to guess which oversample the
user wanted to use by looking at the rate and trying to max it.

However, the cards, and especially simple-card with its mclk-fs property
will already provide the expected oversample ratio by using the set_sysclk
callback.

We can thus implement it and remove the logic to deal with the runtime
guess.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun4i-i2s.c

index a7653114e895ac09958f3e9c0de465187dd9bea6..f24d19526603c2d2a31ef9af1cff07ed4dd8d1ca 100644 (file)
@@ -93,6 +93,8 @@ struct sun4i_i2s {
        struct clk      *mod_clk;
        struct regmap   *regmap;
 
+       unsigned int    mclk_freq;
+
        struct snd_dmaengine_dai_dma_data       capture_dma_data;
        struct snd_dmaengine_dai_dma_data       playback_dma_data;
 };
@@ -158,14 +160,24 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
 }
 
 static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 };
+static bool sun4i_i2s_oversample_is_valid(unsigned int oversample)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sun4i_i2s_oversample_rates); i++)
+               if (sun4i_i2s_oversample_rates[i] == oversample)
+                       return true;
+
+       return false;
+}
 
 static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
                                  unsigned int rate,
                                  unsigned int word_size)
 {
-       unsigned int clk_rate;
+       unsigned int oversample_rate, clk_rate;
        int bclk_div, mclk_div;
-       int ret, i;
+       int ret;
 
        switch (rate) {
        case 176400:
@@ -197,21 +209,18 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
        if (ret)
                return ret;
 
-       /* Always favor the highest oversampling rate */
-       for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) {
-               unsigned int oversample_rate = sun4i_i2s_oversample_rates[i];
-
-               bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
-                                                 word_size);
-               mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
-                                                 clk_rate,
-                                                 rate);
+       oversample_rate = i2s->mclk_freq / rate;
+       if (!sun4i_i2s_oversample_is_valid(oversample_rate))
+               return -EINVAL;
 
-               if ((bclk_div >= 0) && (mclk_div >= 0))
-                       break;
-       }
+       bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
+                                         word_size);
+       if (bclk_div < 0)
+               return -EINVAL;
 
-       if ((bclk_div < 0) || (mclk_div < 0))
+       mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
+                                         clk_rate, rate);
+       if (mclk_div < 0)
                return -EINVAL;
 
        regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
@@ -481,9 +490,23 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
        regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0);
 }
 
+static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                               unsigned int freq, int dir)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       if (clk_id != 0)
+               return -EINVAL;
+
+       i2s->mclk_freq = freq;
+
+       return 0;
+}
+
 static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
        .hw_params      = sun4i_i2s_hw_params,
        .set_fmt        = sun4i_i2s_set_fmt,
+       .set_sysclk     = sun4i_i2s_set_sysclk,
        .shutdown       = sun4i_i2s_shutdown,
        .startup        = sun4i_i2s_startup,
        .trigger        = sun4i_i2s_trigger,