]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/soc/soc-core.c
Merge remote-tracking branch 'wireless-drivers/master'
[karo-tx-linux.git] / sound / soc / soc-core.c
index a1305f827a98f077ac3cdeffed96b2ce73d45c63..790ee2bf1a474b394a286f650b2772c8d3436c05 100644 (file)
@@ -537,26 +537,75 @@ static inline void snd_soc_debugfs_exit(void)
 struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
                const char *dai_link, int stream)
 {
-       int i;
+       struct snd_soc_pcm_runtime *rtd;
 
-       for (i = 0; i < card->num_links; i++) {
-               if (card->rtd[i].dai_link->no_pcm &&
-                       !strcmp(card->rtd[i].dai_link->name, dai_link))
-                       return card->rtd[i].pcm->streams[stream].substream;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->dai_link->no_pcm &&
+                       !strcmp(rtd->dai_link->name, dai_link))
+                       return rtd->pcm->streams[stream].substream;
        }
        dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
 
+static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
+       struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+       struct snd_soc_pcm_runtime *rtd;
+
+       rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
+       if (!rtd)
+               return NULL;
+
+       rtd->card = card;
+       rtd->dai_link = dai_link;
+       rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) *
+                                       dai_link->num_codecs,
+                                       GFP_KERNEL);
+       if (!rtd->codec_dais) {
+               kfree(rtd);
+               return NULL;
+       }
+
+       return rtd;
+}
+
+static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
+{
+       if (rtd && rtd->codec_dais)
+               kfree(rtd->codec_dais);
+       kfree(rtd);
+}
+
+static void soc_add_pcm_runtime(struct snd_soc_card *card,
+               struct snd_soc_pcm_runtime *rtd)
+{
+       list_add_tail(&rtd->list, &card->rtd_list);
+       rtd->num = card->num_rtd;
+       card->num_rtd++;
+}
+
+static void soc_remove_pcm_runtimes(struct snd_soc_card *card)
+{
+       struct snd_soc_pcm_runtime *rtd, *_rtd;
+
+       list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) {
+               list_del(&rtd->list);
+               soc_free_pcm_runtime(rtd);
+       }
+
+       card->num_rtd = 0;
+}
+
 struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
                const char *dai_link)
 {
-       int i;
+       struct snd_soc_pcm_runtime *rtd;
 
-       for (i = 0; i < card->num_links; i++) {
-               if (!strcmp(card->rtd[i].dai_link->name, dai_link))
-                       return &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (!strcmp(rtd->dai_link->name, dai_link))
+                       return rtd;
        }
        dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
        return NULL;
@@ -578,7 +627,8 @@ int snd_soc_suspend(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
        struct snd_soc_codec *codec;
-       int i, j;
+       struct snd_soc_pcm_runtime *rtd;
+       int i;
 
        /* If the card is not initialized yet there is nothing to do */
        if (!card->instantiated)
@@ -595,13 +645,13 @@ int snd_soc_suspend(struct device *dev)
        snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
 
        /* mute any active DACs */
-       for (i = 0; i < card->num_rtd; i++) {
+       list_for_each_entry(rtd, &card->rtd_list, list) {
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
-               for (j = 0; j < card->rtd[i].num_codecs; j++) {
-                       struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       struct snd_soc_dai *dai = rtd->codec_dais[i];
                        struct snd_soc_dai_driver *drv = dai->driver;
 
                        if (drv->ops->digital_mute && dai->playback_active)
@@ -610,20 +660,20 @@ int snd_soc_suspend(struct device *dev)
        }
 
        /* suspend all pcms */
-       for (i = 0; i < card->num_rtd; i++) {
-               if (card->rtd[i].dai_link->ignore_suspend)
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
-               snd_pcm_suspend_all(card->rtd[i].pcm);
+               snd_pcm_suspend_all(rtd->pcm);
        }
 
        if (card->suspend_pre)
                card->suspend_pre(card);
 
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
                if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
@@ -631,19 +681,19 @@ int snd_soc_suspend(struct device *dev)
        }
 
        /* close any waiting streams */
-       for (i = 0; i < card->num_rtd; i++)
-               flush_delayed_work(&card->rtd[i].delayed_work);
+       list_for_each_entry(rtd, &card->rtd_list, list)
+               flush_delayed_work(&rtd->delayed_work);
 
-       for (i = 0; i < card->num_rtd; i++) {
+       list_for_each_entry(rtd, &card->rtd_list, list) {
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
-               snd_soc_dapm_stream_event(&card->rtd[i],
+               snd_soc_dapm_stream_event(rtd,
                                          SNDRV_PCM_STREAM_PLAYBACK,
                                          SND_SOC_DAPM_STREAM_SUSPEND);
 
-               snd_soc_dapm_stream_event(&card->rtd[i],
+               snd_soc_dapm_stream_event(rtd,
                                          SNDRV_PCM_STREAM_CAPTURE,
                                          SND_SOC_DAPM_STREAM_SUSPEND);
        }
@@ -690,10 +740,10 @@ int snd_soc_suspend(struct device *dev)
                }
        }
 
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
                if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
@@ -717,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work)
 {
        struct snd_soc_card *card =
                        container_of(work, struct snd_soc_card, deferred_resume_work);
+       struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_codec *codec;
-       int i, j;
+       int i;
 
        /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
         * so userspace apps are blocked from touching us
@@ -733,10 +784,10 @@ static void soc_resume_deferred(struct work_struct *work)
                card->resume_pre(card);
 
        /* resume control bus DAIs */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
                if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
@@ -751,28 +802,28 @@ static void soc_resume_deferred(struct work_struct *work)
                }
        }
 
-       for (i = 0; i < card->num_rtd; i++) {
+       list_for_each_entry(rtd, &card->rtd_list, list) {
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
-               snd_soc_dapm_stream_event(&card->rtd[i],
+               snd_soc_dapm_stream_event(rtd,
                                          SNDRV_PCM_STREAM_PLAYBACK,
                                          SND_SOC_DAPM_STREAM_RESUME);
 
-               snd_soc_dapm_stream_event(&card->rtd[i],
+               snd_soc_dapm_stream_event(rtd,
                                          SNDRV_PCM_STREAM_CAPTURE,
                                          SND_SOC_DAPM_STREAM_RESUME);
        }
 
        /* unmute any active DACs */
-       for (i = 0; i < card->num_rtd; i++) {
+       list_for_each_entry(rtd, &card->rtd_list, list) {
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
-               for (j = 0; j < card->rtd[i].num_codecs; j++) {
-                       struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       struct snd_soc_dai *dai = rtd->codec_dais[i];
                        struct snd_soc_dai_driver *drv = dai->driver;
 
                        if (drv->ops->digital_mute && dai->playback_active)
@@ -780,10 +831,10 @@ static void soc_resume_deferred(struct work_struct *work)
                }
        }
 
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
-               if (card->rtd[i].dai_link->ignore_suspend)
+               if (rtd->dai_link->ignore_suspend)
                        continue;
 
                if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
@@ -808,15 +859,14 @@ int snd_soc_resume(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
        bool bus_control = false;
-       int i;
+       struct snd_soc_pcm_runtime *rtd;
 
        /* If the card is not initialized yet there is nothing to do */
        if (!card->instantiated)
                return 0;
 
        /* activate pins from sleep state */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list) {
                struct snd_soc_dai **codec_dais = rtd->codec_dais;
                struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
                int j;
@@ -837,8 +887,8 @@ int snd_soc_resume(struct device *dev)
         * 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;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
                bus_control |= cpu_dai->driver->bus_control;
        }
        if (bus_control) {
@@ -910,18 +960,41 @@ static struct snd_soc_dai *snd_soc_find_dai(
        return NULL;
 }
 
-static int soc_bind_dai_link(struct snd_soc_card *card, int num)
+static bool soc_is_dai_link_bound(struct snd_soc_card *card,
+               struct snd_soc_dai_link *dai_link)
 {
-       struct snd_soc_dai_link *dai_link = &card->dai_link[num];
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+       struct snd_soc_pcm_runtime *rtd;
+
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->dai_link == dai_link)
+                       return true;
+       }
+
+       return false;
+}
+
+static int soc_bind_dai_link(struct snd_soc_card *card,
+       struct snd_soc_dai_link *dai_link)
+{
+       struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_dai_link_component *codecs = dai_link->codecs;
        struct snd_soc_dai_link_component cpu_dai_component;
-       struct snd_soc_dai **codec_dais = rtd->codec_dais;
+       struct snd_soc_dai **codec_dais;
        struct snd_soc_platform *platform;
        const char *platform_name;
        int i;
 
-       dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
+       dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
+
+       rtd = soc_new_pcm_runtime(card, dai_link);
+       if (!rtd)
+               return -ENOMEM;
+
+       if (soc_is_dai_link_bound(card, dai_link)) {
+               dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
+                       dai_link->name);
+               return 0;
+       }
 
        cpu_dai_component.name = dai_link->cpu_name;
        cpu_dai_component.of_node = dai_link->cpu_of_node;
@@ -930,18 +1003,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
        if (!rtd->cpu_dai) {
                dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
                        dai_link->cpu_dai_name);
-               return -EPROBE_DEFER;
+               goto _err_defer;
        }
 
        rtd->num_codecs = dai_link->num_codecs;
 
        /* Find CODEC from registered CODECs */
+       codec_dais = rtd->codec_dais;
        for (i = 0; i < rtd->num_codecs; i++) {
                codec_dais[i] = snd_soc_find_dai(&codecs[i]);
                if (!codec_dais[i]) {
                        dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
                                codecs[i].dai_name);
-                       return -EPROBE_DEFER;
+                       goto _err_defer;
                }
        }
 
@@ -973,9 +1047,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
                return -EPROBE_DEFER;
        }
 
-       card->num_rtd++;
-
+       soc_add_pcm_runtime(card, rtd);
        return 0;
+
+_err_defer:
+       soc_free_pcm_runtime(rtd);
+       return  -EPROBE_DEFER;
 }
 
 static void soc_remove_component(struct snd_soc_component *component)
@@ -1014,9 +1091,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order)
        }
 }
 
-static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
+static void soc_remove_link_dais(struct snd_soc_card *card,
+               struct snd_soc_pcm_runtime *rtd, int order)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        int i;
 
        /* unregister the rtd device */
@@ -1032,10 +1109,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
        soc_remove_dai(rtd->cpu_dai, order);
 }
 
-static void soc_remove_link_components(struct snd_soc_card *card, int num,
-                                      int order)
+static void soc_remove_link_components(struct snd_soc_card *card,
+       struct snd_soc_pcm_runtime *rtd, int order)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_component *component;
@@ -1061,23 +1137,200 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
 
 static void soc_remove_dai_links(struct snd_soc_card *card)
 {
-       int dai, order;
+       int order;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_soc_dai_link *link, *_link;
 
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
-               for (dai = 0; dai < card->num_rtd; dai++)
-                       soc_remove_link_dais(card, dai, order);
+               list_for_each_entry(rtd, &card->rtd_list, list)
+                       soc_remove_link_dais(card, rtd, order);
        }
 
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
-               for (dai = 0; dai < card->num_rtd; dai++)
-                       soc_remove_link_components(card, dai, order);
+               list_for_each_entry(rtd, &card->rtd_list, list)
+                       soc_remove_link_components(card, rtd, order);
        }
 
-       card->num_rtd = 0;
+       list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
+               if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
+                       dev_warn(card->dev, "Topology forgot to remove link %s?\n",
+                               link->name);
+
+               list_del(&link->list);
+               card->num_dai_links--;
+       }
 }
 
+static int snd_soc_init_multicodec(struct snd_soc_card *card,
+                                  struct snd_soc_dai_link *dai_link)
+{
+       /* Legacy codec/codec_dai link is a single entry in multicodec */
+       if (dai_link->codec_name || dai_link->codec_of_node ||
+           dai_link->codec_dai_name) {
+               dai_link->num_codecs = 1;
+
+               dai_link->codecs = devm_kzalloc(card->dev,
+                               sizeof(struct snd_soc_dai_link_component),
+                               GFP_KERNEL);
+               if (!dai_link->codecs)
+                       return -ENOMEM;
+
+               dai_link->codecs[0].name = dai_link->codec_name;
+               dai_link->codecs[0].of_node = dai_link->codec_of_node;
+               dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
+       }
+
+       if (!dai_link->codecs) {
+               dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int soc_init_dai_link(struct snd_soc_card *card,
+                                  struct snd_soc_dai_link *link)
+{
+       int i, ret;
+
+       ret = snd_soc_init_multicodec(card, link);
+       if (ret) {
+               dev_err(card->dev, "ASoC: failed to init multicodec\n");
+               return ret;
+       }
+
+       for (i = 0; i < link->num_codecs; i++) {
+               /*
+                * Codec must be specified by 1 of name or OF node,
+                * not both or neither.
+                */
+               if (!!link->codecs[i].name ==
+                   !!link->codecs[i].of_node) {
+                       dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
+               /* Codec DAI name must be specified */
+               if (!link->codecs[i].dai_name) {
+                       dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Platform may be specified by either name or OF node, but
+        * can be left unspecified, and a dummy platform will be used.
+        */
+       if (link->platform_name && link->platform_of_node) {
+               dev_err(card->dev,
+                       "ASoC: Both platform name/of_node are set for %s\n",
+                       link->name);
+               return -EINVAL;
+       }
+
+       /*
+        * CPU device may be specified by either name or OF node, but
+        * can be left unspecified, and will be matched based on DAI
+        * name alone..
+        */
+       if (link->cpu_name && link->cpu_of_node) {
+               dev_err(card->dev,
+                       "ASoC: Neither/both cpu name/of_node are set for %s\n",
+                       link->name);
+               return -EINVAL;
+       }
+       /*
+        * At least one of CPU DAI name or CPU device name/node must be
+        * specified
+        */
+       if (!link->cpu_dai_name &&
+           !(link->cpu_name || link->cpu_of_node)) {
+               dev_err(card->dev,
+                       "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
+                       link->name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * snd_soc_add_dai_link - Add a DAI link dynamically
+ * @card: The ASoC card to which the DAI link is added
+ * @dai_link: The new DAI link to add
+ *
+ * This function adds a DAI link to the ASoC card's link list.
+ *
+ * Note: Topology can use this API to add DAI links when probing the
+ * topology component. And machine drivers can still define static
+ * DAI links in dai_link array.
+ */
+int snd_soc_add_dai_link(struct snd_soc_card *card,
+               struct snd_soc_dai_link *dai_link)
+{
+       if (dai_link->dobj.type
+           && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
+               dev_err(card->dev, "Invalid dai link type %d\n",
+                       dai_link->dobj.type);
+               return -EINVAL;
+       }
+
+       lockdep_assert_held(&client_mutex);
+       /* Notify the machine driver for extra initialization
+        * on the link created by topology.
+        */
+       if (dai_link->dobj.type && card->add_dai_link)
+               card->add_dai_link(card, dai_link);
+
+       list_add_tail(&dai_link->list, &card->dai_link_list);
+       card->num_dai_links++;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
+
+/**
+ * snd_soc_remove_dai_link - Remove a DAI link from the list
+ * @card: The ASoC card that owns the link
+ * @dai_link: The DAI link to remove
+ *
+ * This function removes a DAI link from the ASoC card's link list.
+ *
+ * For DAI links previously added by topology, topology should
+ * remove them by using the dobj embedded in the link.
+ */
+void snd_soc_remove_dai_link(struct snd_soc_card *card,
+                            struct snd_soc_dai_link *dai_link)
+{
+       struct snd_soc_dai_link *link, *_link;
+
+       if (dai_link->dobj.type
+           && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
+               dev_err(card->dev, "Invalid dai link type %d\n",
+                       dai_link->dobj.type);
+               return;
+       }
+
+       lockdep_assert_held(&client_mutex);
+       /* Notify the machine driver for extra destruction
+        * on the link created by topology.
+        */
+       if (dai_link->dobj.type && card->remove_dai_link)
+               card->remove_dai_link(card, dai_link);
+
+       list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
+               if (link == dai_link) {
+                       list_del(&link->list);
+                       card->num_dai_links--;
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
+
 static void soc_set_name_prefix(struct snd_soc_card *card,
                                struct snd_soc_component *component)
 {
@@ -1160,6 +1413,16 @@ static int soc_probe_component(struct snd_soc_card *card,
                        component->name);
        }
 
+       /* machine specific init */
+       if (component->init) {
+               ret = component->init(component);
+               if (ret < 0) {
+                       dev_err(component->dev,
+                               "Failed to do machine specific init %d\n", ret);
+                       goto err_probe;
+               }
+       }
+
        if (component->controls)
                snd_soc_add_component_controls(component, component->controls,
                                     component->num_controls);
@@ -1220,10 +1483,10 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static int soc_probe_link_components(struct snd_soc_card *card, int num,
+static int soc_probe_link_components(struct snd_soc_card *card,
+                       struct snd_soc_pcm_runtime *rtd,
                                     int order)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_component *component;
        int i, ret;
@@ -1283,35 +1546,35 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
 {
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_dapm_widget *play_w, *capture_w;
+       struct snd_soc_dapm_widget *sink, *source;
        int ret;
 
        if (rtd->num_codecs > 1)
                dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
 
        /* link the DAI widgets */
-       play_w = codec_dai->playback_widget;
-       capture_w = cpu_dai->capture_widget;
-       if (play_w && capture_w) {
+       sink = codec_dai->playback_widget;
+       source = cpu_dai->capture_widget;
+       if (sink && source) {
                ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                          dai_link->num_params, capture_w,
-                                          play_w);
+                                          dai_link->num_params,
+                                          source, sink);
                if (ret != 0) {
                        dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
-                               play_w->name, capture_w->name, ret);
+                               sink->name, source->name, ret);
                        return ret;
                }
        }
 
-       play_w = cpu_dai->playback_widget;
-       capture_w = codec_dai->capture_widget;
-       if (play_w && capture_w) {
+       sink = cpu_dai->playback_widget;
+       source = codec_dai->capture_widget;
+       if (sink && source) {
                ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                          dai_link->num_params, capture_w,
-                                          play_w);
+                                          dai_link->num_params,
+                                          source, sink);
                if (ret != 0) {
                        dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
-                               play_w->name, capture_w->name, ret);
+                               sink->name, source->name, ret);
                        return ret;
                }
        }
@@ -1319,15 +1582,15 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
        return 0;
 }
 
-static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
+static int soc_probe_link_dais(struct snd_soc_card *card,
+               struct snd_soc_pcm_runtime *rtd, int order)
 {
-       struct snd_soc_dai_link *dai_link = &card->dai_link[num];
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+       struct snd_soc_dai_link *dai_link = rtd->dai_link;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int i, ret;
 
        dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
-                       card->name, num, order);
+                       card->name, rtd->num, order);
 
        /* set default power off timeout */
        rtd->pmdown_time = pmdown_time;
@@ -1372,7 +1635,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 
        if (cpu_dai->driver->compress_new) {
                /*create compress_device"*/
-               ret = cpu_dai->driver->compress_new(rtd, num);
+               ret = cpu_dai->driver->compress_new(rtd, rtd->num);
                if (ret < 0) {
                        dev_err(card->dev, "ASoC: can't create compress %s\n",
                                         dai_link->stream_name);
@@ -1382,7 +1645,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 
                if (!dai_link->params) {
                        /* create the pcm */
-                       ret = soc_new_pcm(rtd, num);
+                       ret = soc_new_pcm(rtd, rtd->num);
                        if (ret < 0) {
                                dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
                                       dai_link->stream_name, ret);
@@ -1404,65 +1667,81 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 
 static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       const char *name = aux_dev->codec_name;
-
-       rtd->component = soc_find_component(aux_dev->codec_of_node, name);
-       if (!rtd->component) {
-               if (aux_dev->codec_of_node)
-                       name = of_node_full_name(aux_dev->codec_of_node);
-
-               dev_err(card->dev, "ASoC: %s not registered\n", name);
-               return -EPROBE_DEFER;
+       struct snd_soc_component *component;
+       const char *name;
+       struct device_node *codec_of_node;
+
+       if (aux_dev->codec_of_node || aux_dev->codec_name) {
+               /* codecs, usually analog devices */
+               name = aux_dev->codec_name;
+               codec_of_node = aux_dev->codec_of_node;
+               component = soc_find_component(codec_of_node, name);
+               if (!component) {
+                       if (codec_of_node)
+                               name = of_node_full_name(codec_of_node);
+                       goto err_defer;
+               }
+       } else if (aux_dev->name) {
+               /* generic components */
+               name = aux_dev->name;
+               component = soc_find_component(NULL, name);
+               if (!component)
+                       goto err_defer;
+       } else {
+               dev_err(card->dev, "ASoC: Invalid auxiliary device\n");
+               return -EINVAL;
        }
 
-       /*
-        * Some places still reference rtd->codec, so we have to keep that
-        * initialized if the component is a CODEC. Once all those references
-        * have been removed, this code can be removed as well.
-        */
-        rtd->codec = rtd->component->codec;
-
+       component->init = aux_dev->init;
+       list_add(&component->list_aux, &card->aux_comp_list);
        return 0;
+
+err_defer:
+       dev_err(card->dev, "ASoC: %s not registered\n", name);
+       return -EPROBE_DEFER;
 }
 
-static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+static int soc_probe_aux_devices(struct snd_soc_card *card)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
-       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       struct snd_soc_component *comp;
+       int order;
        int ret;
 
-       ret = soc_probe_component(card, rtd->component);
-       if (ret < 0)
-               return ret;
-
-       /* do machine specific initialization */
-       if (aux_dev->init) {
-               ret = aux_dev->init(rtd->component);
-               if (ret < 0) {
-                       dev_err(card->dev, "ASoC: failed to init %s: %d\n",
-                               aux_dev->name, ret);
-                       return ret;
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+               order++) {
+               list_for_each_entry(comp, &card->aux_comp_list, list_aux) {
+                       if (comp->driver->probe_order == order) {
+                               ret = soc_probe_component(card, comp);
+                               if (ret < 0) {
+                                       dev_err(card->dev,
+                                               "ASoC: failed to probe aux component %s %d\n",
+                                               comp->name, ret);
+                                       return ret;
+                               }
+                       }
                }
        }
 
-       return soc_post_component_init(rtd, aux_dev->name);
+       return 0;
 }
 
-static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
+static void soc_remove_aux_devices(struct snd_soc_card *card)
 {
-       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
-       struct snd_soc_component *component = rtd->component;
+       struct snd_soc_component *comp, *_comp;
+       int order;
 
-       /* unregister the rtd device */
-       if (rtd->dev_registered) {
-               device_unregister(rtd->dev);
-               rtd->dev_registered = 0;
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+               order++) {
+               list_for_each_entry_safe(comp, _comp,
+                       &card->aux_comp_list, list_aux) {
+                       if (comp->driver->remove_order == order) {
+                               soc_remove_component(comp);
+                               /* remove it from the card's aux_comp_list */
+                               list_del(&comp->list_aux);
+                       }
+               }
        }
-
-       if (component)
-               soc_remove_component(component);
 }
 
 static int snd_soc_init_codec_cache(struct snd_soc_codec *codec)
@@ -1552,6 +1831,8 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
        struct snd_soc_codec *codec;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_soc_dai_link *dai_link;
        int ret, i, order;
 
        mutex_lock(&client_mutex);
@@ -1559,7 +1840,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
 
        /* bind DAIs */
        for (i = 0; i < card->num_links; i++) {
-               ret = soc_bind_dai_link(card, i);
+               ret = soc_bind_dai_link(card, &card->dai_link[i]);
                if (ret != 0)
                        goto base_error;
        }
@@ -1571,6 +1852,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                        goto base_error;
        }
 
+       /* add predefined DAI links to the list */
+       for (i = 0; i < card->num_links; i++)
+               snd_soc_add_dai_link(card, card->dai_link+i);
+
        /* initialize the register cache for each available codec */
        list_for_each_entry(codec, &codec_list, list) {
                if (codec->cache_init)
@@ -1624,8 +1909,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        /* probe all components used by DAI links on this card */
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
-               for (i = 0; i < card->num_links; i++) {
-                       ret = soc_probe_link_components(card, i, order);
+               list_for_each_entry(rtd, &card->rtd_list, list) {
+                       ret = soc_probe_link_components(card, rtd, order);
                        if (ret < 0) {
                                dev_err(card->dev,
                                        "ASoC: failed to instantiate card %d\n",
@@ -1635,11 +1920,31 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
+       /* probe auxiliary components */
+       ret = soc_probe_aux_devices(card);
+       if (ret < 0)
+               goto probe_dai_err;
+
+       /* Find new DAI links added during probing components and bind them.
+        * Components with topology may bring new DAIs and DAI links.
+        */
+       list_for_each_entry(dai_link, &card->dai_link_list, list) {
+               if (soc_is_dai_link_bound(card, dai_link))
+                       continue;
+
+               ret = soc_init_dai_link(card, dai_link);
+               if (ret)
+                       goto probe_dai_err;
+               ret = soc_bind_dai_link(card, dai_link);
+               if (ret)
+                       goto probe_dai_err;
+       }
+
        /* probe all DAI links on this card */
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
-               for (i = 0; i < card->num_links; i++) {
-                       ret = soc_probe_link_dais(card, i, order);
+               list_for_each_entry(rtd, &card->rtd_list, list) {
+                       ret = soc_probe_link_dais(card, rtd, order);
                        if (ret < 0) {
                                dev_err(card->dev,
                                        "ASoC: failed to instantiate card %d\n",
@@ -1649,16 +1954,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
-       for (i = 0; i < card->num_aux_devs; i++) {
-               ret = soc_probe_aux_dev(card, i);
-               if (ret < 0) {
-                       dev_err(card->dev,
-                               "ASoC: failed to add auxiliary devices %d\n",
-                               ret);
-                       goto probe_aux_dev_err;
-               }
-       }
-
        snd_soc_dapm_link_dai_widgets(card);
        snd_soc_dapm_connect_dai_link_widgets(card);
 
@@ -1718,8 +2013,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        return 0;
 
 probe_aux_dev_err:
-       for (i = 0; i < card->num_aux_devs; i++)
-               soc_remove_aux_dev(card, i);
+       soc_remove_aux_devices(card);
 
 probe_dai_err:
        soc_remove_dai_links(card);
@@ -1733,6 +2027,7 @@ card_probe_error:
        snd_card_free(card->snd_card);
 
 base_error:
+       soc_remove_pcm_runtimes(card);
        mutex_unlock(&card->mutex);
        mutex_unlock(&client_mutex);
 
@@ -1763,20 +2058,18 @@ static int soc_probe(struct platform_device *pdev)
 
 static int soc_cleanup_card_resources(struct snd_soc_card *card)
 {
-       int i;
+       struct snd_soc_pcm_runtime *rtd;
 
        /* make sure any delayed work runs */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list)
                flush_delayed_work(&rtd->delayed_work);
-       }
-
-       /* remove auxiliary devices */
-       for (i = 0; i < card->num_aux_devs; i++)
-               soc_remove_aux_dev(card, i);
 
        /* remove and free each DAI */
        soc_remove_dai_links(card);
+       soc_remove_pcm_runtimes(card);
+
+       /* remove auxiliary devices */
+       soc_remove_aux_devices(card);
 
        soc_cleanup_card_debugfs(card);
 
@@ -1803,29 +2096,26 @@ static int soc_remove(struct platform_device *pdev)
 int snd_soc_poweroff(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
-       int i;
+       struct snd_soc_pcm_runtime *rtd;
 
        if (!card->instantiated)
                return 0;
 
        /* Flush out pmdown_time work - we actually do want to run it
         * now, we're shutting down so no imminent restart. */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list)
                flush_delayed_work(&rtd->delayed_work);
-       }
 
        snd_soc_dapm_shutdown(card);
 
        /* deactivate pins to sleep state */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list) {
                struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-               int j;
+               int i;
 
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
-               for (j = 0; j < rtd->num_codecs; j++) {
-                       struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
                        pinctrl_pm_select_sleep_state(codec_dai->dev);
                }
        }
@@ -2301,33 +2591,6 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
-static int snd_soc_init_multicodec(struct snd_soc_card *card,
-                                  struct snd_soc_dai_link *dai_link)
-{
-       /* Legacy codec/codec_dai link is a single entry in multicodec */
-       if (dai_link->codec_name || dai_link->codec_of_node ||
-           dai_link->codec_dai_name) {
-               dai_link->num_codecs = 1;
-
-               dai_link->codecs = devm_kzalloc(card->dev,
-                               sizeof(struct snd_soc_dai_link_component),
-                               GFP_KERNEL);
-               if (!dai_link->codecs)
-                       return -ENOMEM;
-
-               dai_link->codecs[0].name = dai_link->codec_name;
-               dai_link->codecs[0].of_node = dai_link->codec_of_node;
-               dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
-       }
-
-       if (!dai_link->codecs) {
-               dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 /**
  * snd_soc_register_card - Register a card with the ASoC core
  *
@@ -2336,7 +2599,8 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
  */
 int snd_soc_register_card(struct snd_soc_card *card)
 {
-       int i, j, ret;
+       int i, ret;
+       struct snd_soc_pcm_runtime *rtd;
 
        if (!card->name || !card->dev)
                return -EINVAL;
@@ -2344,63 +2608,11 @@ int snd_soc_register_card(struct snd_soc_card *card)
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai_link *link = &card->dai_link[i];
 
-               ret = snd_soc_init_multicodec(card, link);
+               ret = soc_init_dai_link(card, link);
                if (ret) {
-                       dev_err(card->dev, "ASoC: failed to init multicodec\n");
-                       return ret;
-               }
-
-               for (j = 0; j < link->num_codecs; j++) {
-                       /*
-                        * Codec must be specified by 1 of name or OF node,
-                        * not both or neither.
-                        */
-                       if (!!link->codecs[j].name ==
-                           !!link->codecs[j].of_node) {
-                               dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
-                                       link->name);
-                               return -EINVAL;
-                       }
-                       /* Codec DAI name must be specified */
-                       if (!link->codecs[j].dai_name) {
-                               dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
-                                       link->name);
-                               return -EINVAL;
-                       }
-               }
-
-               /*
-                * Platform may be specified by either name or OF node, but
-                * can be left unspecified, and a dummy platform will be used.
-                */
-               if (link->platform_name && link->platform_of_node) {
-                       dev_err(card->dev,
-                               "ASoC: Both platform name/of_node are set for %s\n",
+                       dev_err(card->dev, "ASoC: failed to init link %s\n",
                                link->name);
-                       return -EINVAL;
-               }
-
-               /*
-                * CPU device may be specified by either name or OF node, but
-                * can be left unspecified, and will be matched based on DAI
-                * name alone..
-                */
-               if (link->cpu_name && link->cpu_of_node) {
-                       dev_err(card->dev,
-                               "ASoC: Neither/both cpu name/of_node are set for %s\n",
-                               link->name);
-                       return -EINVAL;
-               }
-               /*
-                * At least one of CPU DAI name or CPU device name/node must be
-                * specified
-                */
-               if (!link->cpu_dai_name &&
-                   !(link->cpu_name || link->cpu_of_node)) {
-                       dev_err(card->dev,
-                               "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
-                               link->name);
-                       return -EINVAL;
+                       return ret;
                }
        }
 
@@ -2408,28 +2620,11 @@ int snd_soc_register_card(struct snd_soc_card *card)
 
        snd_soc_initialize_card_lists(card);
 
-       card->rtd = devm_kzalloc(card->dev,
-                                sizeof(struct snd_soc_pcm_runtime) *
-                                (card->num_links + card->num_aux_devs),
-                                GFP_KERNEL);
-       if (card->rtd == NULL)
-               return -ENOMEM;
-       card->num_rtd = 0;
-       card->rtd_aux = &card->rtd[card->num_links];
-
-       for (i = 0; i < card->num_links; i++) {
-               card->rtd[i].card = card;
-               card->rtd[i].dai_link = &card->dai_link[i];
-               card->rtd[i].codec_dais = devm_kzalloc(card->dev,
-                                       sizeof(struct snd_soc_dai *) *
-                                       (card->rtd[i].dai_link->num_codecs),
-                                       GFP_KERNEL);
-               if (card->rtd[i].codec_dais == NULL)
-                       return -ENOMEM;
-       }
+       INIT_LIST_HEAD(&card->dai_link_list);
+       card->num_dai_links = 0;
 
-       for (i = 0; i < card->num_aux_devs; i++)
-               card->rtd_aux[i].card = card;
+       INIT_LIST_HEAD(&card->rtd_list);
+       card->num_rtd = 0;
 
        INIT_LIST_HEAD(&card->dapm_dirty);
        INIT_LIST_HEAD(&card->dobj_list);
@@ -2442,8 +2637,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
                return ret;
 
        /* deactivate pins to sleep state */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+       list_for_each_entry(rtd, &card->rtd_list, list)  {
                struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
                int j;
 
@@ -2558,6 +2752,56 @@ static void snd_soc_unregister_dais(struct snd_soc_component *component)
        }
 }
 
+/* Create a DAI and add it to the component's DAI list */
+static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
+       struct snd_soc_dai_driver *dai_drv,
+       bool legacy_dai_naming)
+{
+       struct device *dev = component->dev;
+       struct snd_soc_dai *dai;
+
+       dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev));
+
+       dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
+       if (dai == NULL)
+               return NULL;
+
+       /*
+        * Back in the old days when we still had component-less DAIs,
+        * instead of having a static name, component-less DAIs would
+        * inherit the name of the parent device so it is possible to
+        * register multiple instances of the DAI. We still need to keep
+        * the same naming style even though those DAIs are not
+        * component-less anymore.
+        */
+       if (legacy_dai_naming &&
+          (dai_drv->id == 0 || dai_drv->name == NULL)) {
+               dai->name = fmt_single_name(dev, &dai->id);
+       } else {
+               dai->name = fmt_multiple_name(dev, dai_drv);
+               if (dai_drv->id)
+                       dai->id = dai_drv->id;
+               else
+                       dai->id = component->num_dai;
+       }
+       if (dai->name == NULL) {
+               kfree(dai);
+               return NULL;
+       }
+
+       dai->component = component;
+       dai->dev = dev;
+       dai->driver = dai_drv;
+       if (!dai->driver->ops)
+               dai->driver->ops = &null_dai_ops;
+
+       list_add(&dai->list, &component->dai_list);
+       component->num_dai++;
+
+       dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
+       return dai;
+}
+
 /**
  * snd_soc_register_dais - Register a DAI with the ASoC core
  *
@@ -2579,58 +2823,66 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
        dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
 
        component->dai_drv = dai_drv;
-       component->num_dai = count;
 
        for (i = 0; i < count; i++) {
 
-               dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
+               dai = soc_add_dai(component, dai_drv + i,
+                               count == 1 && legacy_dai_naming);
                if (dai == NULL) {
                        ret = -ENOMEM;
                        goto err;
                }
+       }
 
-               /*
-                * Back in the old days when we still had component-less DAIs,
-                * instead of having a static name, component-less DAIs would
-                * inherit the name of the parent device so it is possible to
-                * register multiple instances of the DAI. We still need to keep
-                * the same naming style even though those DAIs are not
-                * component-less anymore.
-                */
-               if (count == 1 && legacy_dai_naming &&
-                       (dai_drv[i].id == 0 || dai_drv[i].name == NULL)) {
-                       dai->name = fmt_single_name(dev, &dai->id);
-               } else {
-                       dai->name = fmt_multiple_name(dev, &dai_drv[i]);
-                       if (dai_drv[i].id)
-                               dai->id = dai_drv[i].id;
-                       else
-                               dai->id = i;
-               }
-               if (dai->name == NULL) {
-                       kfree(dai);
-                       ret = -ENOMEM;
-                       goto err;
-               }
+       return 0;
 
-               dai->component = component;
-               dai->dev = dev;
-               dai->driver = &dai_drv[i];
-               if (!dai->driver->ops)
-                       dai->driver->ops = &null_dai_ops;
+err:
+       snd_soc_unregister_dais(component);
 
-               list_add(&dai->list, &component->dai_list);
+       return ret;
+}
+
+/**
+ * snd_soc_register_dai - Register a DAI dynamically & create its widgets
+ *
+ * @component: The component the DAIs are registered for
+ * @dai_drv: DAI driver to use for the DAI
+ *
+ * Topology can use this API to register DAIs when probing a component.
+ * These DAIs's widgets will be freed in the card cleanup and the DAIs
+ * will be freed in the component cleanup.
+ */
+int snd_soc_register_dai(struct snd_soc_component *component,
+       struct snd_soc_dai_driver *dai_drv)
+{
+       struct snd_soc_dapm_context *dapm =
+               snd_soc_component_get_dapm(component);
+       struct snd_soc_dai *dai;
+       int ret;
 
-               dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
+       if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) {
+               dev_err(component->dev, "Invalid dai type %d\n",
+                       dai_drv->dobj.type);
+               return -EINVAL;
        }
 
-       return 0;
+       lockdep_assert_held(&client_mutex);
+       dai = soc_add_dai(component, dai_drv, false);
+       if (!dai)
+               return -ENOMEM;
 
-err:
-       snd_soc_unregister_dais(component);
+       /* Create the DAI widgets here. After adding DAIs, topology may
+        * also add routes that need these widgets as source or sink.
+        */
+       ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+       if (ret != 0) {
+               dev_err(component->dev,
+                       "Failed to create DAI widgets %d\n", ret);
+       }
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
 
 static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm,
        enum snd_soc_dapm_type type, int subseq)