]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/soc/codecs/arizona.c
Merge branch 'for-3.7' into asoc-next
[karo-tx-linux.git] / sound / soc / codecs / arizona.c
index 1cf7a32d1b211e779cb7705746c32b07c11afa82..815e8e37b5894e8e8889712d97f5eb240aebcce9 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;
        }