ASoC: ak4642: enable to use MCKO as fixed rate output pin on DT
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 10 Sep 2015 06:40:19 +0000 (06:40 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 14 Sep 2015 18:41:38 +0000 (19:41 +0100)
ak4642 chip can output clock via MCKO pin as audio reference clock.
This patch supports it on DT

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/sound/ak4642.txt
sound/soc/codecs/ak4642.c

index 623d4e70ae11d9ef5ee6231c09f0e8293fb32680..340784db6808809eecf4ae0e310023550713e2c6 100644 (file)
@@ -7,7 +7,14 @@ Required properties:
   - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
   - reg : The chip select number on the I2C bus
 
-Example:
+Optional properties:
+
+  - #clock-cells :             common clock binding; shall be set to 0
+  - clocks :                   common clock binding; MCKI clock
+  - clock-frequency :          common clock binding; frequency of MCKO
+  - clock-output-names :       common clock binding; MCKO clock name
+
+Example 1:
 
 &i2c {
        ak4648: ak4648@0x12 {
@@ -15,3 +22,16 @@ Example:
                reg = <0x12>;
        };
 };
+
+Example 2:
+
+&i2c {
+       ak4643: codec@12 {
+               compatible = "asahi-kasei,ak4643";
+               reg = <0x12>;
+               #clock-cells = <0>;
+               clocks = <&audio_clock>;
+               clock-frequency = <12288000>;
+               clock-output-names = "ak4643_mcko";
+       };
+};
index 9af06d30386b38d6230ef53e27c5e782b2e81880..b5c4981c9f4c3a1a9aacf4c22066d0045f2a7d74 100644 (file)
@@ -23,6 +23,8 @@
  * AK4648 is tested.
  */
 
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #define I2S            (3 << 0)
 
 /* MD_CTL2 */
-#define FS0            (1 << 0)
-#define FS1            (1 << 1)
-#define FS2            (1 << 2)
-#define FS3            (1 << 5)
-#define FS_MASK                (FS0 | FS1 | FS2 | FS3)
+#define FS(val)                (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PS(val)                ((val & 0x3) << 6)
 
 /* MD_CTL3 */
 #define BST1           (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
 
 struct ak4642_priv {
        const struct ak4642_drvdata *drvdata;
+       struct clk *mcko;
 };
 
 /*
@@ -430,56 +430,55 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+                          u32 frequency)
+{
+       u32 fs_list[] = {
+               [0] = 8000,
+               [1] = 12000,
+               [2] = 16000,
+               [3] = 24000,
+               [4] = 7350,
+               [5] = 11025,
+               [6] = 14700,
+               [7] = 22050,
+               [10] = 32000,
+               [11] = 48000,
+               [14] = 29400,
+               [15] = 44100,
+       };
+       u32 ps_list[] = {
+               [0] = 256,
+               [1] = 128,
+               [2] = 64,
+               [3] = 32
+       };
+       int ps, fs;
+
+       for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+               for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+                       if (frequency == ps_list[ps] * fs_list[fs]) {
+                               snd_soc_write(codec, MD_CTL2, PS(ps) | FS(fs));
+                               return 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
-       u8 rate;
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+       u32 rate = clk_get_rate(priv->mcko);
 
-       switch (params_rate(params)) {
-       case 7350:
-               rate = FS2;
-               break;
-       case 8000:
-               rate = 0;
-               break;
-       case 11025:
-               rate = FS2 | FS0;
-               break;
-       case 12000:
-               rate = FS0;
-               break;
-       case 14700:
-               rate = FS2 | FS1;
-               break;
-       case 16000:
-               rate = FS1;
-               break;
-       case 22050:
-               rate = FS2 | FS1 | FS0;
-               break;
-       case 24000:
-               rate = FS1 | FS0;
-               break;
-       case 29400:
-               rate = FS3 | FS2 | FS1;
-               break;
-       case 32000:
-               rate = FS3 | FS1;
-               break;
-       case 44100:
-               rate = FS3 | FS2 | FS1 | FS0;
-               break;
-       case 48000:
-               rate = FS3 | FS1 | FS0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+       if (!rate)
+               rate = params_rate(params) * 256;
 
-       return 0;
+       return ak4642_set_mcko(codec, rate);
 }
 
 static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +531,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (priv->mcko)
+               ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+       return 0;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+       .probe                  = ak4642_probe,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
@@ -580,6 +590,35 @@ static const struct ak4642_drvdata ak4648_drvdata = {
        .extended_frequencies = 1,
 };
 
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk *clk;
+       const char *clk_name = np->name;
+       const char *parent_clk_name = NULL;
+       u32 rate;
+
+       if (of_property_read_u32(np, "clock-frequency", &rate))
+               return NULL;
+
+       if (of_property_read_bool(np, "clocks"))
+               parent_clk_name = of_clk_get_parent_name(np, 0);
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+                                     (parent_clk_name) ? 0 : CLK_IS_ROOT,
+                                     rate);
+       if (!IS_ERR(clk))
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+       return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
 static const struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
@@ -589,10 +628,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        const struct ak4642_drvdata *drvdata = NULL;
        struct regmap *regmap;
        struct ak4642_priv *priv;
+       struct clk *mcko = NULL;
 
        if (np) {
                const struct of_device_id *of_id;
 
+               mcko = ak4642_of_parse_mcko(dev);
+               if (IS_ERR(mcko))
+                       mcko = NULL;
+
                of_id = of_match_device(ak4642_of_match, dev);
                if (of_id)
                        drvdata = of_id->data;
@@ -610,6 +654,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
                return -ENOMEM;
 
        priv->drvdata = drvdata;
+       priv->mcko = mcko;
 
        i2c_set_clientdata(i2c, priv);