ASoC: improve the DMI long card code in asoc-core
[sfrench/cifs-2.6.git] / sound / soc / soc-core.c
index e91325b688f2b039f1a4190a53724bcf10244521..a1f4d64a0a18da7287c0b55224471f0defd326e9 100644 (file)
@@ -356,14 +356,13 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
 }
 EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup);
 
-struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
-                                                  const char *driver_name)
+static struct snd_soc_component
+*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name)
 {
        struct snd_soc_component *component;
        struct snd_soc_component *found_component;
 
        found_component = NULL;
-       mutex_lock(&client_mutex);
        for_each_component(component) {
                if ((dev == component->dev) &&
                    (!driver_name ||
@@ -373,10 +372,21 @@ struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
                        break;
                }
        }
-       mutex_unlock(&client_mutex);
 
        return found_component;
 }
+
+struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
+                                                  const char *driver_name)
+{
+       struct snd_soc_component *component;
+
+       mutex_lock(&client_mutex);
+       component = snd_soc_lookup_component_nolocked(dev, driver_name);
+       mutex_unlock(&client_mutex);
+
+       return component;
+}
 EXPORT_SYMBOL_GPL(snd_soc_lookup_component);
 
 struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
@@ -409,6 +419,9 @@ static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
 
        list_del(&rtd->list);
 
+       flush_delayed_work(&rtd->delayed_work);
+       snd_soc_pcm_component_free(rtd);
+
        /*
         * we don't need to call kfree() for rtd->dev
         * see
@@ -943,19 +956,6 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_find_dai_link);
 
-static bool soc_is_dai_link_bound(struct snd_soc_card *card,
-               struct snd_soc_dai_link *dai_link)
-{
-       struct snd_soc_pcm_runtime *rtd;
-
-       for_each_card_rtds(card, rtd) {
-               if (rtd->dai_link == dai_link)
-                       return true;
-       }
-
-       return false;
-}
-
 static int soc_dai_link_sanity_check(struct snd_soc_card *card,
                                     struct snd_soc_dai_link *link)
 {
@@ -1052,35 +1052,69 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
        return 0;
 }
 
-static void soc_unbind_dai_link(struct snd_soc_card *card,
-                               struct snd_soc_dai_link *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_pcm_runtime *rtd;
 
+       lockdep_assert_held(&client_mutex);
+
+       /*
+        * Notify the machine driver for extra destruction
+        */
+       if (card->remove_dai_link)
+               card->remove_dai_link(card, dai_link);
+
+       list_del(&dai_link->list);
+
        rtd = snd_soc_get_pcm_runtime(card, dai_link->name);
        if (rtd)
                soc_free_pcm_runtime(rtd);
 }
+EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
 
-static int soc_bind_dai_link(struct snd_soc_card *card,
-       struct snd_soc_dai_link *dai_link)
+/**
+ * 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)
 {
        struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_dai_link_component *codec, *platform;
        struct snd_soc_component *component;
        int i, ret;
 
+       lockdep_assert_held(&client_mutex);
+
+       /*
+        * Notify the machine driver for extra initialization
+        */
+       if (card->add_dai_link)
+               card->add_dai_link(card, dai_link);
+
        if (dai_link->ignore)
                return 0;
 
        dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
 
-       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;
-       }
-
        ret = soc_dai_link_sanity_check(card, dai_link);
        if (ret < 0)
                return ret;
@@ -1124,12 +1158,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
                }
        }
 
+       /* see for_each_card_links */
+       list_add_tail(&dai_link->list, &card->dai_link_list);
+
        return 0;
 
 _err_defer:
        soc_free_pcm_runtime(rtd);
        return -EPROBE_DEFER;
 }
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
 
 static void soc_set_of_name_prefix(struct snd_soc_component *component)
 {
@@ -1166,8 +1204,16 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
        soc_set_of_name_prefix(component);
 }
 
-static void soc_cleanup_component(struct snd_soc_component *component)
+static void soc_remove_component(struct snd_soc_component *component,
+                                int probed)
 {
+
+       if (!component->card)
+               return;
+
+       if (probed)
+               snd_soc_component_remove(component);
+
        /* For framework level robustness */
        snd_soc_component_set_jack(component, NULL, NULL);
 
@@ -1178,22 +1224,13 @@ static void soc_cleanup_component(struct snd_soc_component *component)
        snd_soc_component_module_put_when_remove(component);
 }
 
-static void soc_remove_component(struct snd_soc_component *component)
-{
-       if (!component->card)
-               return;
-
-       snd_soc_component_remove(component);
-
-       soc_cleanup_component(component);
-}
-
 static int soc_probe_component(struct snd_soc_card *card,
                               struct snd_soc_component *component)
 {
        struct snd_soc_dapm_context *dapm =
                snd_soc_component_get_dapm(component);
        struct snd_soc_dai *dai;
+       int probed = 0;
        int ret;
 
        if (!strcmp(component->name, "snd-soc-dummy"))
@@ -1249,6 +1286,7 @@ static int soc_probe_component(struct snd_soc_card *card,
             dapm->bias_level != SND_SOC_BIAS_OFF,
             "codec %s can not start from non-off bias with idle_bias_off==1\n",
             component->name);
+       probed = 1;
 
        /* machine specific init */
        if (component->init) {
@@ -1277,7 +1315,7 @@ static int soc_probe_component(struct snd_soc_card *card,
 
 err_probe:
        if (ret < 0)
-               soc_cleanup_component(component);
+               soc_remove_component(component, probed);
 
        return ret;
 }
@@ -1379,7 +1417,7 @@ static void soc_remove_link_components(struct snd_soc_card *card)
                                if (component->driver->remove_order != order)
                                        continue;
 
-                               soc_remove_component(component);
+                               soc_remove_component(component, 1);
                        }
                }
        }
@@ -1420,82 +1458,6 @@ void snd_soc_disconnect_sync(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync);
 
-/**
- * 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)
-{
-       int ret;
-
-       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);
-
-       ret = soc_bind_dai_link(card, dai_link);
-       if (ret < 0)
-               return ret;
-
-       /* see for_each_card_links */
-       list_add_tail(&dai_link->list, &card->dai_link_list);
-
-       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)
-{
-       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_del(&dai_link->list);
-
-       soc_unbind_dai_link(card, dai_link);
-}
-EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
-
 static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
                                struct snd_soc_pcm_runtime *rtd)
 {
@@ -1620,21 +1582,18 @@ static int soc_bind_aux_dev(struct snd_soc_card *card)
 
 static int soc_probe_aux_devices(struct snd_soc_card *card)
 {
-       struct snd_soc_component *comp;
+       struct snd_soc_component *component;
        int order;
        int ret;
 
        for_each_comp_order(order) {
-               for_each_card_auxs(card, comp) {
-                       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;
-                               }
-                       }
+               for_each_card_auxs(card, component) {
+                       if (component->driver->probe_order != order)
+                               continue;
+
+                       ret = soc_probe_component(card, component);
+                       if (ret < 0)
+                               return ret;
                }
        }
 
@@ -1649,7 +1608,7 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
        for_each_comp_order(order) {
                for_each_card_auxs_safe(card, comp, _comp) {
                        if (comp->driver->remove_order == order)
-                               soc_remove_component(comp);
+                               soc_remove_component(comp, 1);
                }
        }
 }
@@ -1759,6 +1718,23 @@ static int is_dmi_valid(const char *field)
        return 1;
 }
 
+/*
+ * Append a string to card->dmi_longname with character cleanups.
+ */
+static void append_dmi_string(struct snd_soc_card *card, const char *str)
+{
+       char *dst = card->dmi_longname;
+       size_t dst_len = sizeof(card->dmi_longname);
+       size_t len;
+
+       len = strlen(dst);
+       snprintf(dst + len, dst_len - len, "-%s", str);
+
+       len++;  /* skip the separator "-" */
+       if (len < dst_len)
+               cleanup_dmi_name(dst + len);
+}
+
 /**
  * snd_soc_set_dmi_name() - Register DMI names to card
  * @card: The card to register DMI names
@@ -1793,61 +1769,36 @@ static int is_dmi_valid(const char *field)
 int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
 {
        const char *vendor, *product, *product_version, *board;
-       size_t longname_buf_size = sizeof(card->snd_card->longname);
-       size_t len;
 
        if (card->long_name)
                return 0; /* long name already set by driver or from DMI */
 
-       /* make up dmi long name as: vendor.product.version.board */
+       /* make up dmi long name as: vendor-product-version-board */
        vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
        if (!vendor || !is_dmi_valid(vendor)) {
                dev_warn(card->dev, "ASoC: no DMI vendor name!\n");
                return 0;
        }
 
-       snprintf(card->dmi_longname, sizeof(card->snd_card->longname),
-                        "%s", vendor);
+       snprintf(card->dmi_longname, sizeof(card->dmi_longname), "%s", vendor);
        cleanup_dmi_name(card->dmi_longname);
 
        product = dmi_get_system_info(DMI_PRODUCT_NAME);
        if (product && is_dmi_valid(product)) {
-               len = strlen(card->dmi_longname);
-               snprintf(card->dmi_longname + len,
-                        longname_buf_size - len,
-                        "-%s", product);
-
-               len++;  /* skip the separator "-" */
-               if (len < longname_buf_size)
-                       cleanup_dmi_name(card->dmi_longname + len);
+               append_dmi_string(card, product);
 
                /*
                 * some vendors like Lenovo may only put a self-explanatory
                 * name in the product version field
                 */
                product_version = dmi_get_system_info(DMI_PRODUCT_VERSION);
-               if (product_version && is_dmi_valid(product_version)) {
-                       len = strlen(card->dmi_longname);
-                       snprintf(card->dmi_longname + len,
-                                longname_buf_size - len,
-                                "-%s", product_version);
-
-                       len++;
-                       if (len < longname_buf_size)
-                               cleanup_dmi_name(card->dmi_longname + len);
-               }
+               if (product_version && is_dmi_valid(product_version))
+                       append_dmi_string(card, product_version);
        }
 
        board = dmi_get_system_info(DMI_BOARD_NAME);
        if (board && is_dmi_valid(board)) {
-               len = strlen(card->dmi_longname);
-               snprintf(card->dmi_longname + len,
-                        longname_buf_size - len,
-                        "-%s", board);
-
-               len++;
-               if (len < longname_buf_size)
-                       cleanup_dmi_name(card->dmi_longname + len);
+               append_dmi_string(card, board);
        } else if (!product) {
                /* fall back to using legacy name */
                dev_warn(card->dev, "ASoC: no DMI board/product name!\n");
@@ -1855,16 +1806,8 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
        }
 
        /* Add flavour to dmi long name */
-       if (flavour) {
-               len = strlen(card->dmi_longname);
-               snprintf(card->dmi_longname + len,
-                        longname_buf_size - len,
-                        "-%s", flavour);
-
-               len++;
-               if (len < longname_buf_size)
-                       cleanup_dmi_name(card->dmi_longname + len);
-       }
+       if (flavour)
+               append_dmi_string(card, flavour);
 
        /* set the card long name */
        card->long_name = card->dmi_longname;
@@ -1984,15 +1927,15 @@ static void __soc_setup_card_name(char *name, int len,
        }
 }
 
-static void soc_cleanup_card_resources(struct snd_soc_card *card)
+static void soc_cleanup_card_resources(struct snd_soc_card *card,
+                                      int card_probed)
 {
        struct snd_soc_dai_link *link, *_link;
 
-       /* free the ALSA card at first; this syncs with pending operations */
-       if (card->snd_card) {
-               snd_card_free(card->snd_card);
-               card->snd_card = NULL;
-       }
+       if (card->snd_card)
+               snd_card_disconnect_sync(card->snd_card);
+
+       snd_soc_dapm_shutdown(card);
 
        /* remove and free each DAI */
        soc_remove_link_dais(card);
@@ -2009,15 +1952,37 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card)
        soc_cleanup_card_debugfs(card);
 
        /* remove the card */
-       if (card->remove)
+       if (card_probed && card->remove)
                card->remove(card);
+
+       if (card->snd_card) {
+               snd_card_free(card->snd_card);
+               card->snd_card = NULL;
+       }
+}
+
+static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
+{
+       if (card->instantiated) {
+               int card_probed = 1;
+
+               card->instantiated = false;
+               snd_soc_flush_all_delayed_work(card);
+
+               soc_cleanup_card_resources(card, card_probed);
+               if (!unregister)
+                       list_add(&card->list, &unbind_card_list);
+       } else {
+               if (unregister)
+                       list_del(&card->list);
+       }
 }
 
-static int snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_bind_card(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_dai_link *dai_link;
-       int ret, i;
+       int ret, i, card_probed = 0;
 
        mutex_lock(&client_mutex);
        mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
@@ -2069,6 +2034,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                ret = card->probe(card);
                if (ret < 0)
                        goto probe_end;
+               card_probed = 1;
        }
 
        /* probe all components used by DAI links on this card */
@@ -2081,8 +2047,11 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
 
        /* probe auxiliary components */
        ret = soc_probe_aux_devices(card);
-       if (ret < 0)
+       if (ret < 0) {
+               dev_err(card->dev,
+                       "ASoC: failed to probe aux component %d\n", ret);
                goto probe_end;
+       }
 
        /* probe all DAI links on this card */
        ret = soc_probe_link_dais(card);
@@ -2123,6 +2092,19 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        soc_setup_card_name(card->snd_card->driver,
                            card->driver_name, card->name, 1);
 
+       if (card->components) {
+               /* the current implementation of snd_component_add() accepts */
+               /* multiple components in the string separated by space, */
+               /* but the string collision (identical string) check might */
+               /* not work correctly */
+               ret = snd_component_add(card->snd_card, card->components);
+               if (ret < 0) {
+                       dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n",
+                               card->name, ret);
+                       goto probe_end;
+               }
+       }
+
        if (card->late_probe) {
                ret = card->late_probe(card);
                if (ret < 0) {
@@ -2131,6 +2113,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                        goto probe_end;
                }
        }
+       card_probed = 1;
 
        snd_soc_dapm_new_widgets(card);
 
@@ -2145,9 +2128,22 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
 
+       /* deactivate pins to sleep state */
+       for_each_card_rtds(card, rtd) {
+               struct snd_soc_dai *dai;
+
+               for_each_rtd_codec_dai(rtd, i, dai) {
+                       if (!dai->active)
+                               pinctrl_pm_select_sleep_state(dai->dev);
+               }
+
+               if (!rtd->cpu_dai->active)
+                       pinctrl_pm_select_sleep_state(rtd->cpu_dai->dev);
+       }
+
 probe_end:
        if (ret < 0)
-               soc_cleanup_card_resources(card);
+               soc_cleanup_card_resources(card, card_probed);
 
        mutex_unlock(&card->mutex);
        mutex_unlock(&client_mutex);
@@ -2377,33 +2373,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
 
-static int snd_soc_bind_card(struct snd_soc_card *card)
-{
-       struct snd_soc_pcm_runtime *rtd;
-       int ret;
-
-       ret = snd_soc_instantiate_card(card);
-       if (ret != 0)
-               return ret;
-
-       /* deactivate pins to sleep state */
-       for_each_card_rtds(card, rtd) {
-               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-               struct snd_soc_dai *codec_dai;
-               int j;
-
-               for_each_rtd_codec_dai(rtd, j, codec_dai) {
-                       if (!codec_dai->active)
-                               pinctrl_pm_select_sleep_state(codec_dai->dev);
-               }
-
-               if (!cpu_dai->active)
-                       pinctrl_pm_select_sleep_state(cpu_dai->dev);
-       }
-
-       return ret;
-}
-
 /**
  * snd_soc_register_card - Register a card with the ASoC core
  *
@@ -2438,22 +2407,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_card);
 
-static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
-{
-       if (card->instantiated) {
-               card->instantiated = false;
-               snd_soc_dapm_shutdown(card);
-               snd_soc_flush_all_delayed_work(card);
-
-               soc_cleanup_card_resources(card);
-               if (!unregister)
-                       list_add(&card->list, &unbind_card_list);
-       } else {
-               if (unregister)
-                       list_del(&card->list);
-       }
-}
-
 /**
  * snd_soc_unregister_card - Unregister a card with the ASoC core
  *
@@ -2532,32 +2485,34 @@ static inline char *fmt_multiple_name(struct device *dev,
        return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL);
 }
 
-/**
- * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
- *
- * @component: The component for which the DAIs should be unregistered
- */
-static void snd_soc_unregister_dais(struct snd_soc_component *component)
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
 {
-       struct snd_soc_dai *dai, *_dai;
-
-       for_each_component_dais_safe(component, dai, _dai) {
-               dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n",
-                       dai->name);
-               list_del(&dai->list);
-       }
+       dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
+       list_del(&dai->list);
 }
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
 
-/* 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)
+/**
+ * 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.
+ */
+struct snd_soc_dai *snd_soc_register_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));
 
+       lockdep_assert_held(&client_mutex);
+
        dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
        if (dai == NULL)
                return NULL;
@@ -2597,6 +2552,19 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
        return dai;
 }
 
+/**
+ * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
+ *
+ * @component: The component for which the DAIs should be unregistered
+ */
+static void snd_soc_unregister_dais(struct snd_soc_component *component)
+{
+       struct snd_soc_dai *dai, *_dai;
+
+       for_each_component_dais_safe(component, dai, _dai)
+               snd_soc_unregister_dai(dai);
+}
+
 /**
  * snd_soc_register_dais - Register a DAI with the ASoC core
  *
@@ -2608,16 +2576,12 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
                                 struct snd_soc_dai_driver *dai_drv,
                                 size_t count)
 {
-       struct device *dev = component->dev;
        struct snd_soc_dai *dai;
        unsigned int i;
        int ret;
 
-       dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count);
-
        for (i = 0; i < count; i++) {
-
-               dai = soc_add_dai(component, dai_drv + i, count == 1 &&
+               dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
                                  !component->driver->non_legacy_dai_naming);
                if (dai == NULL) {
                        ret = -ENOMEM;
@@ -2633,49 +2597,6 @@ err:
        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;
-
-       if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) {
-               dev_err(component->dev, "Invalid dai type %d\n",
-                       dai_drv->dobj.type);
-               return -EINVAL;
-       }
-
-       lockdep_assert_held(&client_mutex);
-       dai = soc_add_dai(component, dai_drv, false);
-       if (!dai)
-               return -ENOMEM;
-
-       /*
-        * 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 int snd_soc_component_initialize(struct snd_soc_component *component,
        const struct snd_soc_component_driver *driver, struct device *dev)
 {
@@ -2746,34 +2667,6 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
 
 #endif
 
-static void snd_soc_component_add(struct snd_soc_component *component)
-{
-       mutex_lock(&client_mutex);
-
-       if (!component->driver->write && !component->driver->read) {
-               if (!component->regmap)
-                       component->regmap = dev_get_regmap(component->dev,
-                                                          NULL);
-               if (component->regmap)
-                       snd_soc_component_setup_regmap(component);
-       }
-
-       /* see for_each_component */
-       list_add(&component->list, &component_list);
-
-       mutex_unlock(&client_mutex);
-}
-
-static void snd_soc_component_del(struct snd_soc_component *component)
-{
-       struct snd_soc_card *card = component->card;
-
-       if (card)
-               snd_soc_unbind_card(card, false);
-
-       list_del(&component->list);
-}
-
 #define ENDIANNESS_MAP(name) \
        (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE)
 static u64 endianness_format_map[] = {
@@ -2820,8 +2713,14 @@ static void snd_soc_try_rebind_card(void)
 
 static void snd_soc_del_component_unlocked(struct snd_soc_component *component)
 {
+       struct snd_soc_card *card = component->card;
+
        snd_soc_unregister_dais(component);
-       snd_soc_component_del(component);
+
+       if (card)
+               snd_soc_unbind_card(card, false);
+
+       list_del(&component->list);
 }
 
 int snd_soc_add_component(struct device *dev,
@@ -2833,6 +2732,8 @@ int snd_soc_add_component(struct device *dev,
        int ret;
        int i;
 
+       mutex_lock(&client_mutex);
+
        ret = snd_soc_component_initialize(component, component_driver, dev);
        if (ret)
                goto err_free;
@@ -2850,14 +2751,26 @@ int snd_soc_add_component(struct device *dev,
                goto err_cleanup;
        }
 
-       snd_soc_component_add(component);
-       snd_soc_try_rebind_card();
+       if (!component->driver->write && !component->driver->read) {
+               if (!component->regmap)
+                       component->regmap = dev_get_regmap(component->dev,
+                                                          NULL);
+               if (component->regmap)
+                       snd_soc_component_setup_regmap(component);
+       }
 
-       return 0;
+       /* see for_each_component */
+       list_add(&component->list, &component_list);
 
 err_cleanup:
-       snd_soc_del_component_unlocked(component);
+       if (ret < 0)
+               snd_soc_del_component_unlocked(component);
 err_free:
+       mutex_unlock(&client_mutex);
+
+       if (ret == 0)
+               snd_soc_try_rebind_card();
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_component);
@@ -2884,29 +2797,19 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component);
  *
  * @dev: The device to unregister
  */
-static int __snd_soc_unregister_component(struct device *dev)
+void snd_soc_unregister_component(struct device *dev)
 {
        struct snd_soc_component *component;
-       int found = 0;
 
        mutex_lock(&client_mutex);
-       for_each_component(component) {
-               if (dev != component->dev)
-                       continue;
+       while (1) {
+               component = snd_soc_lookup_component_nolocked(dev, NULL);
+               if (!component)
+                       break;
 
                snd_soc_del_component_unlocked(component);
-               found = 1;
-               break;
        }
        mutex_unlock(&client_mutex);
-
-       return found;
-}
-
-void snd_soc_unregister_component(struct device *dev)
-{
-       while (__snd_soc_unregister_component(dev))
-               ;
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_component);