Merge tag 'clk-for-linus-3.16' of git://git.linaro.org/people/mike.turquette/linux...
[firefly-linux-kernel-4.4.55.git] / sound / soc / generic / simple-card.c
index 383a4a18d51fe4a1c5c7447dbe0955f844c4cfe0..03a7fdcdf114390abe3d694535c8c66f7d77e318 100644 (file)
@@ -24,9 +24,32 @@ struct simple_card_data {
                struct asoc_simple_dai cpu_dai;
                struct asoc_simple_dai codec_dai;
        } *dai_props;
+       unsigned int mclk_fs;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
+static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       unsigned int mclk;
+       int ret = 0;
+
+       if (priv->mclk_fs) {
+               mclk = params_rate(params) * priv->mclk_fs;
+               ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+                                            SND_SOC_CLOCK_IN);
+       }
+
+       return ret;
+}
+
+static struct snd_soc_ops asoc_simple_card_ops = {
+       .hw_params = asoc_simple_card_hw_params,
+};
+
 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
                                       struct asoc_simple_dai *set)
 {
@@ -66,8 +89,7 @@ err:
 
 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct simple_card_data *priv =
-                               snd_soc_card_get_drvdata(rtd->card);
+       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *codec = rtd->codec_dai;
        struct snd_soc_dai *cpu = rtd->cpu_dai;
        struct simple_dai_props *dai_props;
@@ -145,7 +167,8 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
 static int simple_card_dai_link_of(struct device_node *node,
                                   struct device *dev,
                                   struct snd_soc_dai_link *dai_link,
-                                  struct simple_dai_props *dai_props)
+                                  struct simple_dai_props *dai_props,
+                                  bool is_top_level_node)
 {
        struct device_node *np = NULL;
        struct device_node *bitclkmaster = NULL;
@@ -156,7 +179,8 @@ static int simple_card_dai_link_of(struct device_node *node,
        char *prefix = "";
        int ret;
 
-       prefix = "simple-audio-card,";
+       if (is_top_level_node)
+               prefix = "simple-audio-card,";
 
        daifmt = snd_soc_of_parse_daifmt(node, prefix,
                                         &bitclkmaster, &framemaster);
@@ -166,8 +190,7 @@ static int simple_card_dai_link_of(struct device_node *node,
        np = of_get_child_by_name(node, prop);
        if (!np) {
                ret = -EINVAL;
-               dev_err(dev, "%s: Can't find simple-audio-card,cpu DT node\n",
-                       __func__);
+               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
                goto dai_link_of_err;
        }
 
@@ -178,7 +201,7 @@ static int simple_card_dai_link_of(struct device_node *node,
                goto dai_link_of_err;
 
        dai_props->cpu_dai.fmt = daifmt;
-       switch (((np == bitclkmaster)<<4)|(np == framemaster)) {
+       switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
        case 0x11:
                dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
                break;
@@ -198,8 +221,7 @@ static int simple_card_dai_link_of(struct device_node *node,
        np = of_get_child_by_name(node, prop);
        if (!np) {
                ret = -EINVAL;
-               dev_err(dev, "%s: Can't find simple-audio-card,codec DT node\n",
-                       __func__);
+               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
                goto dai_link_of_err;
        }
 
@@ -220,7 +242,7 @@ static int simple_card_dai_link_of(struct device_node *node,
                        (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
        } else {
                dai_props->codec_dai.fmt = daifmt;
-               switch (((np == bitclkmaster)<<4)|(np == framemaster)) {
+               switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
                case 0x11:
                        dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
                        break;
@@ -237,8 +259,8 @@ static int simple_card_dai_link_of(struct device_node *node,
        }
 
        if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
-                       ret = -EINVAL;
-                       goto dai_link_of_err;
+               ret = -EINVAL;
+               goto dai_link_of_err;
        }
 
        /* simple-card assumes platform == cpu */
@@ -252,6 +274,7 @@ static int simple_card_dai_link_of(struct device_node *node,
        sprintf(name, "%s-%s", dai_link->cpu_dai_name,
                                dai_link->codec_dai_name);
        dai_link->name = dai_link->stream_name = name;
+       dai_link->ops = &asoc_simple_card_ops;
 
        dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
        dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
@@ -301,6 +324,10 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                        return ret;
        }
 
+       /* Factor to mclk, used in hw_params() */
+       of_property_read_u32(node, "simple-audio-card,mclk-fs",
+                            &priv->mclk_fs);
+
        dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
                priv->snd_card.name : "");
 
@@ -310,14 +337,15 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                for (i = 0; (np = of_get_next_child(node, np)); i++) {
                        dev_dbg(dev, "\tlink %d:\n", i);
                        ret = simple_card_dai_link_of(np, dev, dai_link + i,
-                                                     dai_props + i);
+                                                     dai_props + i, false);
                        if (ret < 0) {
                                of_node_put(np);
                                return ret;
                        }
                }
        } else {
-               ret = simple_card_dai_link_of(node, dev, dai_link, dai_props);
+               ret = simple_card_dai_link_of(node, dev, dai_link, dai_props,
+                                             true);
                if (ret < 0)
                        return ret;
        }
@@ -419,10 +447,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
                        return -EINVAL;
                }
 
-               if (!cinfo->name        ||
-                   !cinfo->codec_dai.name      ||
-                   !cinfo->codec       ||
-                   !cinfo->platform    ||
+               if (!cinfo->name ||
+                   !cinfo->codec_dai.name ||
+                   !cinfo->codec ||
+                   !cinfo->platform ||
                    !cinfo->cpu_dai.name) {
                        dev_err(dev, "insufficient asoc_simple_card_info settings\n");
                        return -EINVAL;
@@ -466,11 +494,11 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
 
 static struct platform_driver asoc_simple_card = {
        .driver = {
-               .name   = "asoc-simple-card",
+               .name = "asoc-simple-card",
                .owner = THIS_MODULE,
                .of_match_table = asoc_simple_of_match,
        },
-       .probe          = asoc_simple_card_probe,
+       .probe = asoc_simple_card_probe,
 };
 
 module_platform_driver(asoc_simple_card);