ASoC: arizona: Implement OPCLK support
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 8 Aug 2012 16:52:44 +0000 (17:52 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 9 Aug 2012 18:34:28 +0000 (19:34 +0100)
Arizona devices support two output system clocks. Provide support for
configuring these via set_sysclk(). Once the clock API is more useful
we should migrate over to that.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h

index 5c9cacaf2d525cddabffd0b416c0695dfadaab99..5e96a0a1669cb46c8b8d7a55101be1c3f3ea8146 100644 (file)
@@ -229,6 +229,69 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(arizona_out_ev);
 
+static unsigned int arizona_sysclk_48k_rates[] = {
+       6144000,
+       12288000,
+       22579200,
+       49152000,
+};
+
+static unsigned int arizona_sysclk_44k1_rates[] = {
+       5644800,
+       11289600,
+       24576000,
+       45158400,
+};
+
+static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
+                            unsigned int freq)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg;
+       unsigned int *rates;
+       int ref, div, refclk;
+
+       switch (clk) {
+       case ARIZONA_CLK_OPCLK:
+               reg = ARIZONA_OUTPUT_SYSTEM_CLOCK;
+               refclk = priv->sysclk;
+               break;
+       case ARIZONA_CLK_ASYNC_OPCLK:
+               reg = ARIZONA_OUTPUT_ASYNC_CLOCK;
+               refclk = priv->asyncclk;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (refclk % 8000)
+               rates = arizona_sysclk_44k1_rates;
+       else
+               rates = arizona_sysclk_48k_rates;
+
+       for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
+                    rates[ref] <= refclk; ref++) {
+               div = 1;
+               while (rates[ref] / div >= freq && div < 32) {
+                       if (rates[ref] / div == freq) {
+                               dev_dbg(codec->dev, "Configured %dHz OPCLK\n",
+                                       freq);
+                               snd_soc_update_bits(codec, reg,
+                                                   ARIZONA_OPCLK_DIV_MASK |
+                                                   ARIZONA_OPCLK_SEL_MASK,
+                                                   (div <<
+                                                    ARIZONA_OPCLK_DIV_SHIFT) |
+                                                   ref);
+                               return 0;
+                       }
+                       div++;
+               }
+       }
+
+       dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+       return -EINVAL;
+}
+
 int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
                       int source, unsigned int freq, int dir)
 {
@@ -252,6 +315,9 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
                reg = ARIZONA_ASYNC_CLOCK_1;
                clk = &priv->asyncclk;
                break;
+       case ARIZONA_CLK_OPCLK:
+       case ARIZONA_CLK_ASYNC_OPCLK:
+               return arizona_set_opclk(codec, clk_id, freq);
        default:
                return -EINVAL;
        }
index 59caca8865e8aa355c279dd257cf836d667627aa..eb66b52777c9e82ad478a39bd3bb2936971feaa1 100644 (file)
 
 #include <sound/soc.h>
 
-#define ARIZONA_CLK_SYSCLK   1
-#define ARIZONA_CLK_ASYNCCLK 2
+#define ARIZONA_CLK_SYSCLK         1
+#define ARIZONA_CLK_ASYNCCLK       2
+#define ARIZONA_CLK_OPCLK          3
+#define ARIZONA_CLK_ASYNC_OPCLK    4
 
 #define ARIZONA_CLK_SRC_MCLK1    0x0
 #define ARIZONA_CLK_SRC_MCLK2    0x1