mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![Maxime Ripard](/assets/img/avatar_default.png)
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>
161 lines
3.9 KiB
C
161 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
//
|
|
// soc-devres.c -- ALSA SoC Audio Layer devres functions
|
|
//
|
|
// Copyright (C) 2013 Linaro Ltd
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
|
|
static void devm_dai_release(struct device *dev, void *res)
|
|
{
|
|
snd_soc_unregister_dai(*(struct snd_soc_dai **)res);
|
|
}
|
|
|
|
/**
|
|
* devm_snd_soc_register_dai - resource-managed dai registration
|
|
* @dev: Device used to manage component
|
|
* @component: The component the DAIs are registered for
|
|
* @dai_drv: DAI driver to use for the DAI
|
|
* @legacy_dai_naming: if %true, use legacy single-name format;
|
|
* if %false, use multiple-name format;
|
|
*/
|
|
struct snd_soc_dai *devm_snd_soc_register_dai(struct device *dev,
|
|
struct snd_soc_component *component,
|
|
struct snd_soc_dai_driver *dai_drv,
|
|
bool legacy_dai_naming)
|
|
{
|
|
struct snd_soc_dai **ptr;
|
|
struct snd_soc_dai *dai;
|
|
|
|
ptr = devres_alloc(devm_dai_release, sizeof(*ptr), GFP_KERNEL);
|
|
if (!ptr)
|
|
return NULL;
|
|
|
|
dai = snd_soc_register_dai(component, dai_drv, legacy_dai_naming);
|
|
if (dai) {
|
|
*ptr = dai;
|
|
devres_add(dev, ptr);
|
|
} else {
|
|
devres_free(ptr);
|
|
}
|
|
|
|
return dai;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devm_snd_soc_register_dai);
|
|
|
|
static void devm_component_release(struct device *dev, void *res)
|
|
{
|
|
const struct snd_soc_component_driver **cmpnt_drv = res;
|
|
|
|
snd_soc_unregister_component_by_driver(dev, *cmpnt_drv);
|
|
}
|
|
|
|
/**
|
|
* devm_snd_soc_register_component - resource managed component registration
|
|
* @dev: Device used to manage component
|
|
* @cmpnt_drv: Component driver
|
|
* @dai_drv: DAI driver
|
|
* @num_dai: Number of DAIs to register
|
|
*
|
|
* Register a component with automatic unregistration when the device is
|
|
* unregistered.
|
|
*/
|
|
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)
|
|
{
|
|
const struct snd_soc_component_driver **ptr;
|
|
int ret;
|
|
|
|
ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL);
|
|
if (!ptr)
|
|
return -ENOMEM;
|
|
|
|
ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);
|
|
if (ret == 0) {
|
|
*ptr = cmpnt_drv;
|
|
devres_add(dev, ptr);
|
|
} else {
|
|
devres_free(ptr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devm_snd_soc_register_component);
|
|
|
|
static void devm_card_release(struct device *dev, void *res)
|
|
{
|
|
snd_soc_unregister_card(*(struct snd_soc_card **)res);
|
|
}
|
|
|
|
/**
|
|
* devm_snd_soc_register_card - resource managed card registration
|
|
* @dev: Device used to manage card
|
|
* @card: Card to register
|
|
*
|
|
* Register a card with automatic unregistration when the device is
|
|
* unregistered.
|
|
*/
|
|
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
|
|
{
|
|
struct snd_soc_card **ptr;
|
|
int ret;
|
|
|
|
ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL);
|
|
if (!ptr)
|
|
return -ENOMEM;
|
|
|
|
ret = snd_soc_register_card(card);
|
|
if (ret == 0) {
|
|
*ptr = card;
|
|
devres_add(dev, ptr);
|
|
} else {
|
|
devres_free(ptr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card);
|
|
|
|
#ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM
|
|
|
|
static void devm_dmaengine_pcm_release(struct device *dev, void *res)
|
|
{
|
|
snd_dmaengine_pcm_unregister(*(struct device **)res);
|
|
}
|
|
|
|
/**
|
|
* devm_snd_dmaengine_pcm_register - resource managed dmaengine PCM registration
|
|
* @dev: The parent device for the PCM device
|
|
* @config: Platform specific PCM configuration
|
|
* @flags: Platform specific quirks
|
|
*
|
|
* Register a dmaengine based PCM device with automatic unregistration when the
|
|
* device is unregistered.
|
|
*/
|
|
int devm_snd_dmaengine_pcm_register(struct device *dev,
|
|
const struct snd_dmaengine_pcm_config *config, unsigned int flags)
|
|
{
|
|
struct device **ptr;
|
|
int ret;
|
|
|
|
ptr = devres_alloc(devm_dmaengine_pcm_release, sizeof(*ptr), GFP_KERNEL);
|
|
if (!ptr)
|
|
return -ENOMEM;
|
|
|
|
ret = snd_dmaengine_pcm_register(dev, config, flags);
|
|
if (ret == 0) {
|
|
*ptr = dev;
|
|
devres_add(dev, ptr);
|
|
} else {
|
|
devres_free(ptr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devm_snd_dmaengine_pcm_register);
|
|
|
|
#endif
|