ASoC: Implement WM8994 OPCLK support
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 8 Jul 2010 02:25:43 +0000 (11:25 +0900)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 8 Jul 2010 23:50:12 +0000 (08:50 +0900)
The WM8994 can output a clock derived from its internal SYSCLK, called
OPCLK.  The rate can be selected as a sysclk, with a division from the
SYSCLK rate specified (multiplied by 10 since a division of 5.5 is
supported) and the clock can be disabled by specifying a divisor of
zero.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h

index ed8be9db2b0238a4ce70a97c869a6f3f3fe53d23..c41cf47f4009f925e8d96aa80e2be3de5e1340c0 100644 (file)
@@ -2492,6 +2492,7 @@ static const struct snd_kcontrol_new aif3adc_mux =
 static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
 SND_SOC_DAPM_INPUT("DMIC1DAT"),
 SND_SOC_DAPM_INPUT("DMIC2DAT"),
+SND_SOC_DAPM_INPUT("Clock"),
 
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
                    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -2966,11 +2967,14 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
        return 0;
 }
 
+static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
+
 static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+       int i;
 
        switch (dai->id) {
        case 1:
@@ -3008,6 +3012,25 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
                dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
                break;
 
+       case WM8994_SYSCLK_OPCLK:
+               /* Special case - a division (times 10) is given and
+                * no effect on main clocking. 
+                */
+               if (freq) {
+                       for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
+                               if (opclk_divs[i] == freq)
+                                       break;
+                       if (i == ARRAY_SIZE(opclk_divs))
+                               return -EINVAL;
+                       snd_soc_update_bits(codec, WM8994_CLOCKING_2,
+                                           WM8994_OPCLK_DIV_MASK, i);
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+                                           WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
+               } else {
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+                                           WM8994_OPCLK_ENA, 0);
+               }
+
        default:
                return -EINVAL;
        }
index 7072dc539354fbd6d9ac6d5bd5b62251eedd23b2..2e0ca67a8df7ad8f83c83c822190ae09d9395acf 100644 (file)
@@ -20,6 +20,9 @@ extern struct snd_soc_dai wm8994_dai[];
 #define WM8994_SYSCLK_FLL1  3
 #define WM8994_SYSCLK_FLL2  4
 
+/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
+#define WM8994_SYSCLK_OPCLK 5
+
 #define WM8994_FLL1 1
 #define WM8994_FLL2 2