Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[cascardo/linux.git] / sound / soc / soc-core.c
index 397d00a..9357210 100644 (file)
@@ -34,9 +34,6 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
-struct snd_ac97_reset_cfg {
-       struct pinctrl *pctl;
-       struct pinctrl_state *pstate_reset;
-       struct pinctrl_state *pstate_warm_reset;
-       struct pinctrl_state *pstate_run;
-       int gpio_sdata;
-       int gpio_sync;
-       int gpio_reset;
-};
-
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -496,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
-       if (codec->ac97->dev.bus)
-               device_unregister(&codec->ac97->dev);
-       return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
-{
-       int err;
-
-       codec->ac97->dev.bus = &ac97_bus_type;
-       codec->ac97->dev.parent = codec->component.card->dev;
-       codec->ac97->dev.release = soc_ac97_device_release;
-
-       dev_set_name(&codec->ac97->dev, "%d-%d:%s",
-                    codec->component.card->snd_card->number, 0,
-                    codec->component.name);
-       err = device_register(&codec->ac97->dev);
-       if (err < 0) {
-               dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
-               codec->ac97->dev.bus = NULL;
-               return err;
-       }
-       return 0;
-}
-#endif
-
 static void codec2codec_close_delayed_work(struct work_struct *work)
 {
        /* Currently nothing to do for c2c links
@@ -593,7 +546,7 @@ int snd_soc_suspend(struct device *dev)
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
        }
 
@@ -667,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
 
                /* deactivate pins to sleep state */
@@ -703,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
        if (card->resume_pre)
                card->resume_pre(card);
 
-       /* resume AC97 DAIs */
+       /* resume control bus DAIs */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
        }
 
@@ -770,7 +723,7 @@ static void soc_resume_deferred(struct work_struct *work)
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
        }
 
@@ -791,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
 int snd_soc_resume(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
-       int i, ac97_control = 0;
+       bool bus_control = false;
+       int i;
 
        /* If the card is not initialized yet there is nothing to do */
        if (!card->instantiated)
@@ -814,17 +768,18 @@ int snd_soc_resume(struct device *dev)
                }
        }
 
-       /* AC97 devices might have other drivers hanging off them so
-        * need to resume immediately.  Other drivers don't have that
-        * problem and may take a substantial amount of time to resume
+       /*
+        * DAIs that also act as the control bus master might have other drivers
+        * hanging off them so need to resume immediately. Other drivers don't
+        * have that problem and may take a substantial amount of time to resume
         * due to I/O costs and anti-pop so handle them out of line.
         */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               ac97_control |= cpu_dai->driver->ac97_control;
+               bus_control |= cpu_dai->driver->bus_control;
        }
-       if (ac97_control) {
-               dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+       if (bus_control) {
+               dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
                soc_resume_deferred(&card->deferred_resume_work);
        } else {
                dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -1387,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                }
        }
 
-       /* add platform data for AC97 devices */
-       for (i = 0; i < rtd->num_codecs; i++) {
-               if (rtd->codec_dais[i]->driver->ac97_control)
-                       snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
-                                              rtd->cpu_dai->ac97_pdata);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
-                                  struct snd_soc_dai *codec_dai)
-{
-       int ret;
-
-       /* Only instantiate AC97 if not already done by the adaptor
-        * for the generic AC97 subsystem.
-        */
-       if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
-               /*
-                * It is possible that the AC97 device is already registered to
-                * the device subsystem. This happens when the device is created
-                * via snd_ac97_mixer(). Currently only SoC codec that does so
-                * is the generic AC97 glue but others migh emerge.
-                *
-                * In those cases we don't try to register the device again.
-                */
-               if (!codec->ac97_created)
-                       return 0;
-
-               ret = soc_ac97_dev_register(codec);
-               if (ret < 0) {
-                       dev_err(codec->dev,
-                               "ASoC: AC97 device register failed: %d\n", ret);
-                       return ret;
-               }
-
-               codec->ac97_registered = 1;
-       }
-       return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
-       if (codec->ac97_registered) {
-               soc_ac97_dev_unregister(codec);
-               codec->ac97_registered = 0;
-       }
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i, ret;
-
-       for (i = 0; i < rtd->num_codecs; i++) {
-               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
-               ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
-               if (ret) {
-                       while (--i >= 0)
-                               soc_unregister_ac97_codec(codec_dai->codec);
-                       return ret;
-               }
-       }
-
        return 0;
 }
 
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i;
-
-       for (i = 0; i < rtd->num_codecs; i++)
-               soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
 static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1758,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                goto probe_aux_dev_err;
        }
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       /* register any AC97 codecs */
-       for (i = 0; i < card->num_rtd; i++) {
-               ret = soc_register_ac97_dai_link(&card->rtd[i]);
-               if (ret < 0) {
-                       dev_err(card->dev,
-                               "ASoC: failed to register AC97: %d\n", ret);
-                       while (--i >= 0)
-                               soc_unregister_ac97_dai_link(&card->rtd[i]);
-                       goto probe_aux_dev_err;
-               }
-       }
-#endif
-
        card->instantiated = 1;
        snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
@@ -1913,216 +1779,6 @@ static struct platform_driver soc_driver = {
        .remove         = soc_remove,
 };
 
-/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-       struct snd_ac97_bus_ops *ops, int num)
-{
-       codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
-       if (codec->ac97 == NULL)
-               return -ENOMEM;
-
-       codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
-       if (codec->ac97->bus == NULL) {
-               kfree(codec->ac97);
-               codec->ac97 = NULL;
-               return -ENOMEM;
-       }
-
-       codec->ac97->bus->ops = ops;
-       codec->ac97->num = num;
-
-       /*
-        * Mark the AC97 device to be created by us. This way we ensure that the
-        * device will be registered with the device subsystem later on.
-        */
-       codec->ac97_created = 1;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
-               struct snd_ac97_reset_cfg *cfg)
-{
-       struct pinctrl *p;
-       struct pinctrl_state *state;
-       int gpio;
-       int ret;
-
-       p = devm_pinctrl_get(dev);
-       if (IS_ERR(p)) {
-               dev_err(dev, "Failed to get pinctrl\n");
-               return PTR_ERR(p);
-       }
-       cfg->pctl = p;
-
-       state = pinctrl_lookup_state(p, "ac97-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-warm-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_warm_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-running");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-running\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_run = state;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sync gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sync gpio\n");
-               return ret;
-       }
-       cfg->gpio_sync = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
-               return ret;
-       }
-       cfg->gpio_sdata = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-reset gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-reset gpio\n");
-               return ret;
-       }
-       cfg->gpio_reset = gpio;
-
-       return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
-       if (ops == soc_ac97_ops)
-               return 0;
-
-       if (soc_ac97_ops && ops)
-               return -EBUSY;
-
-       soc_ac97_ops = ops;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
-               struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct snd_ac97_reset_cfg cfg;
-       int ret;
-
-       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_set_ac97_ops(ops);
-       if (ret)
-               return ret;
-
-       ops->warm_reset = snd_soc_ac97_warm_reset;
-       ops->reset = snd_soc_ac97_reset;
-
-       snd_ac97_rst_cfg = cfg;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       soc_unregister_ac97_codec(codec);
-#endif
-       kfree(codec->ac97->bus);
-       kfree(codec->ac97);
-       codec->ac97 = NULL;
-       codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
 /**
  * snd_soc_cnew - create new control
  * @_template: control template
@@ -2947,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
        return 0;
 }
 
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
 {
-       if (!component->regmap)
-               component->regmap = dev_get_regmap(component->dev, NULL);
-       if (component->regmap) {
-               int val_bytes = regmap_get_val_bytes(component->regmap);
-               /* Errors are legitimate for non-integer byte multiples */
-               if (val_bytes > 0)
-                       component->val_bytes = val_bytes;
-       }
+       int val_bytes = regmap_get_val_bytes(component->regmap);
+
+       /* Errors are legitimate for non-integer byte multiples */
+       if (val_bytes > 0)
+               component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+       struct regmap *regmap)
+{
+       component->regmap = regmap;
+       snd_soc_component_setup_regmap(component);
 }
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+       regmap_exit(component->regmap);
+       component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
 
 static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 {
-       if (!component->write && !component->read)
-               snd_soc_component_init_regmap(component);
+       if (!component->write && !component->read) {
+               if (!component->regmap)
+                       component->regmap = dev_get_regmap(component->dev, NULL);
+               if (component->regmap)
+                       snd_soc_component_setup_regmap(component);
+       }
 
        list_add(&component->list, &component_list);
 }
@@ -3313,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev,
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->component.val_bytes = codec_drv->reg_word_size;
-       mutex_init(&codec->mutex);
 
 #ifdef CONFIG_DEBUG_FS
        codec->component.init_debugfs = soc_init_codec_debugfs;
@@ -3536,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname)
 {
        struct device_node *np = card->dev->of_node;
-       int num_routes;
+       int num_routes, old_routes;
        struct snd_soc_dapm_route *routes;
        int i, ret;
 
@@ -3554,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
-       routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+       old_routes = card->num_dapm_routes;
+       routes = devm_kzalloc(card->dev,
+                             (old_routes + num_routes) * sizeof(*routes),
                              GFP_KERNEL);
        if (!routes) {
                dev_err(card->dev,
@@ -3562,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
+       memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes));
+
        for (i = 0; i < num_routes; i++) {
                ret = of_property_read_string_index(np, propname,
-                       2 * i, &routes[i].sink);
+                       2 * i, &routes[old_routes + i].sink);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -3572,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                        return -EINVAL;
                }
                ret = of_property_read_string_index(np, propname,
-                       (2 * i) + 1, &routes[i].source);
+                       (2 * i) + 1, &routes[old_routes + i].source);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -3581,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                }
        }
 
-       card->num_dapm_routes = num_routes;
+       card->num_dapm_routes += num_routes;
        card->dapm_routes = routes;
 
        return 0;
@@ -3701,36 +3400,30 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
 
-int snd_soc_of_get_dai_name(struct device_node *of_node,
-                           const char **dai_name)
+static int snd_soc_get_dai_name(struct of_phandle_args *args,
+                               const char **dai_name)
 {
        struct snd_soc_component *pos;
-       struct of_phandle_args args;
-       int ret;
-
-       ret = of_parse_phandle_with_args(of_node, "sound-dai",
-                                        "#sound-dai-cells", 0, &args);
-       if (ret)
-               return ret;
-
-       ret = -EPROBE_DEFER;
+       int ret = -EPROBE_DEFER;
 
        mutex_lock(&client_mutex);
        list_for_each_entry(pos, &component_list, list) {
-               if (pos->dev->of_node != args.np)
+               if (pos->dev->of_node != args->np)
                        continue;
 
                if (pos->driver->of_xlate_dai_name) {
-                       ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name);
+                       ret = pos->driver->of_xlate_dai_name(pos,
+                                                            args,
+                                                            dai_name);
                } else {
                        int id = -1;
 
-                       switch (args.args_count) {
+                       switch (args->args_count) {
                        case 0:
                                id = 0; /* same as dai_drv[0] */
                                break;
                        case 1:
-                               id = args.args[0];
+                               id = args->args[0];
                                break;
                        default:
                                /* not supported */
@@ -3752,6 +3445,21 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
                break;
        }
        mutex_unlock(&client_mutex);
+       return ret;
+}
+
+int snd_soc_of_get_dai_name(struct device_node *of_node,
+                           const char **dai_name)
+{
+       struct of_phandle_args args;
+       int ret;
+
+       ret = of_parse_phandle_with_args(of_node, "sound-dai",
+                                        "#sound-dai-cells", 0, &args);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_get_dai_name(&args, dai_name);
 
        of_node_put(args.np);
 
@@ -3759,6 +3467,77 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
 
+/*
+ * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Builds an array of CODEC DAI components from the DAI link property
+ * 'sound-dai'.
+ * The array is set in the DAI link and the number of DAIs is set accordingly.
+ * The device nodes in the array (of_node) must be dereferenced by the caller.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+                                  struct device_node *of_node,
+                                  struct snd_soc_dai_link *dai_link)
+{
+       struct of_phandle_args args;
+       struct snd_soc_dai_link_component *component;
+       char *name;
+       int index, num_codecs, ret;
+
+       /* Count the number of CODECs */
+       name = "sound-dai";
+       num_codecs = of_count_phandle_with_args(of_node, name,
+                                               "#sound-dai-cells");
+       if (num_codecs <= 0) {
+               if (num_codecs == -ENOENT)
+                       dev_err(dev, "No 'sound-dai' property\n");
+               else
+                       dev_err(dev, "Bad phandle in 'sound-dai'\n");
+               return num_codecs;
+       }
+       component = devm_kzalloc(dev,
+                                sizeof *component * num_codecs,
+                                GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+       dai_link->codecs = component;
+       dai_link->num_codecs = num_codecs;
+
+       /* Parse the list */
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               ret = of_parse_phandle_with_args(of_node, name,
+                                                "#sound-dai-cells",
+                                                 index, &args);
+               if (ret)
+                       goto err;
+               component->of_node = args.np;
+               ret = snd_soc_get_dai_name(&args, &component->dai_name);
+               if (ret < 0)
+                       goto err;
+       }
+       return 0;
+err:
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               if (!component->of_node)
+                       break;
+               of_node_put(component->of_node);
+               component->of_node = NULL;
+       }
+       dai_link->codecs = NULL;
+       dai_link->num_codecs = 0;
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS