ASoC: core: Remove only the registered component in devm functions
authorMaxime Ripard <maxime@cerno.tech>
Tue, 7 Jul 2020 07:42:37 +0000 (09:42 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 7 Jul 2020 10:55:46 +0000 (11:55 +0100)
The ASoC devm_ functions that register a component
(devm_snd_soc_register_component and devm_snd_dmaengine_pcm_register) will
clean their component by running snd_soc_unregister_component.

snd_soc_unregister_component will then remove all the components for the
device that was used to register the component in the first place.

However, some drivers register several components (such as a DAI and a
dmaengine PCM) on the same device, and if the dmaengine PCM is registered
first, then the DAI will be cleaned up first and
snd_dmaengine_pcm_unregister will be called next.

snd_dmaengine_pcm_unregister will then lookup the dmaengine PCM component
on the device, and if there's one unregister that component and release its
dmaengine channels. That doesn't happen in practice though since the first
call to snd_soc_unregister_component removed all the components, so we
never get the chance to release the dmaengine channels.

In order to fix this, instead of removing all the components for a given
device, we can simply remove the component that was registered in the first
place. We should have the same number of component registration than we
have components, so it should work just fine.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20200707074237.287171-1-maxime@cerno.tech
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/soc.h
sound/soc/soc-core.c
sound/soc/soc-devres.c
sound/soc/soc-generic-dmaengine-pcm.c

index fddab504c227cfcc3cc7ea39abc82294dc70ecde..9ad30135b53729a10f513e315d1317c5f56d6c4f 100644 (file)
@@ -444,6 +444,8 @@ int devm_snd_soc_register_component(struct device *dev,
                         const struct snd_soc_component_driver *component_driver,
                         struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_component(struct device *dev);
+void snd_soc_unregister_component_by_driver(struct device *dev,
+                        const struct snd_soc_component_driver *component_driver);
 struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev,
                                                            const char *driver_name);
 struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
index 0f30f5aabaa87c54478766fd86ba7b88c84e1ec7..2b8abf88ec6032f3a95dad93072ce67e33f2aac9 100644 (file)
@@ -2572,6 +2572,33 @@ int snd_soc_register_component(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_component);
 
+/**
+ * snd_soc_unregister_component_by_driver - Unregister component using a given driver
+ * from the ASoC core
+ *
+ * @dev: The device to unregister
+ * @component_driver: The component driver to unregister
+ */
+void snd_soc_unregister_component_by_driver(struct device *dev,
+                                           const struct snd_soc_component_driver *component_driver)
+{
+       struct snd_soc_component *component;
+
+       if (!component_driver)
+               return;
+
+       mutex_lock(&client_mutex);
+       component = snd_soc_lookup_component_nolocked(dev, component_driver->name);
+       if (!component)
+               goto out;
+
+       snd_soc_del_component_unlocked(component);
+
+out:
+       mutex_unlock(&client_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver);
+
 /**
  * snd_soc_unregister_component - Unregister all related component
  * from the ASoC core
index 11e5d79623707a7b36803323056bc179a09dab5d..4534a1c03e8e5e85f9f5e4e889197edd88652649 100644 (file)
@@ -48,7 +48,9 @@ EXPORT_SYMBOL_GPL(devm_snd_soc_register_dai);
 
 static void devm_component_release(struct device *dev, void *res)
 {
-       snd_soc_unregister_component(*(struct device **)res);
+       const struct snd_soc_component_driver **cmpnt_drv = res;
+
+       snd_soc_unregister_component_by_driver(dev, *cmpnt_drv);
 }
 
 /**
@@ -65,7 +67,7 @@ int devm_snd_soc_register_component(struct device *dev,
                         const struct snd_soc_component_driver *cmpnt_drv,
                         struct snd_soc_dai_driver *dai_drv, int num_dai)
 {
-       struct device **ptr;
+       const struct snd_soc_component_driver **ptr;
        int ret;
 
        ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL);
@@ -74,7 +76,7 @@ int devm_snd_soc_register_component(struct device *dev,
 
        ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);
        if (ret == 0) {
-               *ptr = dev;
+               *ptr = cmpnt_drv;
                devres_add(dev, ptr);
        } else {
                devres_free(ptr);
index 80a4e71f2d95d6254a6e45b8ffe4600c3a7151bb..61844403f1817fd2bdc7c781f0d1c545e5f16f97 100644 (file)
@@ -478,7 +478,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
 
        pcm = soc_component_to_pcm(component);
 
-       snd_soc_unregister_component(dev);
+       snd_soc_unregister_component_by_driver(dev, component->driver);
        dmaengine_pcm_release_chan(pcm);
        kfree(pcm);
 }