ASoC: update gfp/slab.h includes
[deliverable/linux.git] / sound / soc / codecs / wm8978.c
index 8dcebaa8604a32c4018c9c0084bbc35b4fb86f83..28bb59ea6ea1597cae32396fd18e65f8bcbe2338 100644 (file)
@@ -58,6 +58,7 @@ struct wm8978_priv {
        unsigned int f_mclk;
        unsigned int f_256fs;
        unsigned int f_opclk;
+       int mclk_idx;
        enum wm8978_sysclk_src sysclk;
        u16 reg_cache[WM8978_CACHEREGNUM];
 };
@@ -210,6 +211,10 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
        /* Speaker */
        SOC_DOUBLE_R("Speaker Switch",
                WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
+
+       /* DAC / ADC oversampling */
+       SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0),
+       SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0),
 };
 
 /* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
@@ -398,6 +403,35 @@ static void pll_factors(struct wm8978_pll_div *pll_div, unsigned int target,
 
        pll_div->k = k;
 }
+
+/* MCLK dividers */
+static const int mclk_numerator[]      = {1, 3, 2, 3, 4, 6, 8, 12};
+static const int mclk_denominator[]    = {1, 2, 1, 1, 1, 1, 1, 1};
+
+/*
+ * find index >= idx, such that, for a given f_out,
+ * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
+ * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be
+ * generalised for f_opclk with suitable coefficient arrays, but currently
+ * the OPCLK divisor is calculated directly, not iteratively.
+ */
+static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
+                           unsigned int *f_pllout)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+               unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] /
+                       mclk_denominator[i];
+               if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) {
+                       *f_pllout = f_pllout_x4 / 4;
+                       return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
 /*
  * Calculate internal frequencies and dividers, according to Figure 40
  * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6
@@ -408,12 +442,16 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
        struct wm8978_pll_div pll_div;
        unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
                f_256fs = wm8978->f_256fs;
-       unsigned int f2, opclk_div;
+       unsigned int f2;
 
        if (!f_mclk)
                return -EINVAL;
 
        if (f_opclk) {
+               unsigned int opclk_div;
+               /* Cannot set up MCLK divider now, do later */
+               wm8978->mclk_idx = -1;
+
                /*
                 * The user needs OPCLK. Choose OPCLKDIV to put
                 * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4.
@@ -440,7 +478,7 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
                wm8978->f_pllout = f_opclk * opclk_div;
        } else if (f_256fs) {
                /*
-                * Not using OPCLK, choose R:
+                * Not using OPCLK, but PLL is used for the codec, choose R:
                 * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12.
                 * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where
                 * prescale = 1, or prescale = 2. Prescale is calculated inside
@@ -449,18 +487,11 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
                 * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK
                 * must be 3.781MHz <= f_MCLK <= 32.768MHz
                 */
-               if (48 * f_256fs < 3 * f_mclk || 4 * f_256fs >= 13 * f_mclk)
-                       return -EINVAL;
+               int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout);
+               if (idx < 0)
+                       return idx;
 
-               /*
-                * MCLKDIV will be selected in .hw_params(), just choose a
-                * suitable f_PLLOUT
-                */
-               if (4 * f_256fs < 3 * f_mclk)
-                       /* Will have to use MCLKDIV */
-                       wm8978->f_pllout = wm8978->f_mclk * 3 / 4;
-               else
-                       wm8978->f_pllout = f_256fs;
+               wm8978->mclk_idx = idx;
 
                /* GPIO1 into default mode as input - before configuring PLL */
                snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
@@ -511,23 +542,22 @@ static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
                wm8978->f_opclk = div;
 
                if (wm8978->f_mclk)
+                       /*
+                        * We know the MCLK frequency, the user has requested
+                        * OPCLK, configure the PLL based on that and start it
+                        * and OPCLK immediately. We will configure PLL to match
+                        * user-requested OPCLK frquency as good as possible.
+                        * In fact, it is likely, that matching the sampling
+                        * rate, when it becomes known, is more important, and
+                        * we will not be reconfiguring PLL then, because we
+                        * must not interrupt OPCLK. But it should be fine,
+                        * because typically the user will request OPCLK to run
+                        * at 256fs or 512fs, and for these cases we will also
+                        * find an exact MCLK divider configuration - it will
+                        * be equal to or double the OPCLK divisor.
+                        */
                        ret = wm8978_configure_pll(codec);
                break;
-       case WM8978_MCLKDIV:
-               if (div & ~0xe0)
-                       return -EINVAL;
-               snd_soc_update_bits(codec, WM8978_CLOCKING, 0xe0, div);
-               break;
-       case WM8978_ADCCLK:
-               if (div & ~8)
-                       return -EINVAL;
-               snd_soc_update_bits(codec, WM8978_ADC_CONTROL, 8, div);
-               break;
-       case WM8978_DACCLK:
-               if (div & ~8)
-                       return -EINVAL;
-               snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 8, div);
-               break;
        case WM8978_BCLKDIV:
                if (div & ~0x1c)
                        return -EINVAL;
@@ -651,10 +681,6 @@ static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        return 0;
 }
 
-/* MCLK dividers */
-static const int mclk_numerator[]      = {1, 3, 2, 3, 4, 6, 8, 12};
-static const int mclk_denominator[]    = {1, 2, 1, 1, 1, 1, 1, 1};
-
 /*
  * Set PCM DAI bit size and sample rate.
  */
@@ -720,9 +746,11 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
        wm8978->f_256fs = params_rate(params) * 256;
 
        if (wm8978->sysclk == WM8978_MCLK) {
+               wm8978->mclk_idx = -1;
                f_sel = wm8978->f_mclk;
        } else {
                if (!wm8978->f_pllout) {
+                       /* We only enter here, if OPCLK is not used */
                        int ret = wm8978_configure_pll(codec);
                        if (ret < 0)
                                return ret;
@@ -730,32 +758,34 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
                f_sel = wm8978->f_pllout;
        }
 
-       /*
-        * In some cases it is possible to reconfigure PLL to a higher frequency
-        * by raising OPCLKDIV, but normally OPCLK is configured to 256 * fs or
-        * 512 * fs, so, we should be fine.
-        */
-       if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
-               return -EINVAL;
+       if (wm8978->mclk_idx < 0) {
+               /* Either MCLK is used directly, or OPCLK is used */
+               if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
+                       return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
-               diff = abs(wm8978->f_256fs * 3 -
-                          f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
+               for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+                       diff = abs(wm8978->f_256fs * 3 -
+                                  f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
 
-               if (diff < diff_best) {
-                       diff_best = diff;
-                       best = i;
-               }
+                       if (diff < diff_best) {
+                               diff_best = diff;
+                               best = i;
+                       }
 
-               if (!diff)
-                       break;
+                       if (!diff)
+                               break;
+               }
+       } else {
+               /* OPCLK not used, codec driven by PLL */
+               best = wm8978->mclk_idx;
+               diff = 0;
        }
 
        if (diff)
-               dev_warn(codec->dev, "Imprecise clock: %u%s\n",
-                        f_sel * mclk_denominator[best] / mclk_numerator[best],
-                        wm8978->sysclk == WM8978_MCLK ?
-                        ", consider using PLL" : "");
+               dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
+                       f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
+                       wm8978->sysclk == WM8978_MCLK ?
+                       ", consider using PLL" : "");
 
        dev_dbg(codec->dev, "%s: fmt %d, rate %u, MCLK divisor #%d\n", __func__,
                params_format(params), params_rate(params), best);
This page took 0.029143 seconds and 5 git commands to generate.