linux/sound/soc/soc-link.c
Pierre-Louis Bossart 21a00fb337
ASoC: soc-link: introduce exit() callback
Some machine drivers allocate or request resources with
snd_soc_link_init() phase of the card probe. These resources need to
be properly released when removing a card, and this patch suggests a
dual exit() callback.

The exit() is invoked in soc_remove_pcm_runtime(), which is not
completely symmetric with the init() invoked in soc_init_pcm_runtime().

Alternate solutions were considered, e.g. adding a .remove() callback
for the platform driver, but that's not symmetrical at all and would
be difficult to handle if there are more than one dailink implementing
an .init(). We looked also into using .remove_dai_link() callback, but
that would also be imbalanced.

Note that because of the error handling in snd_soc_bind_card(), which
jumps to probe_end, there is no way to guarantee the exit() is invoked
with resources allocated in the init(). Prior to releasing those
resources, implementations of the exit() callback shall check the
resources are valid.

Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Curtis Malainey <curtis@malainey.com>
Link: https://lore.kernel.org/r/20200622154241.29053-2-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2020-06-23 12:25:37 +01:00

162 lines
3.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// soc-link.c
//
// Copyright (C) 2019 Renesas Electronics Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
#include <sound/soc.h>
#include <sound/soc-link.h>
#define soc_link_ret(rtd, ret) _soc_link_ret(rtd, __func__, ret)
static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd,
const char *func, int ret)
{
/* Positive, Zero values are not errors */
if (ret >= 0)
return ret;
/* Negative values might be errors */
switch (ret) {
case -EPROBE_DEFER:
case -ENOTSUPP:
break;
default:
dev_err(rtd->dev,
"ASoC: error at %s on %s: %d\n",
func, rtd->dai_link->name, ret);
}
return ret;
}
int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd)
{
int ret = 0;
if (rtd->dai_link->init)
ret = rtd->dai_link->init(rtd);
return soc_link_ret(rtd, ret);
}
void snd_soc_link_exit(struct snd_soc_pcm_runtime *rtd)
{
if (rtd->dai_link->exit)
rtd->dai_link->exit(rtd);
}
int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
int ret = 0;
if (rtd->dai_link->be_hw_params_fixup)
ret = rtd->dai_link->be_hw_params_fixup(rtd, params);
return soc_link_ret(rtd, ret);
}
int snd_soc_link_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->startup)
ret = rtd->dai_link->ops->startup(substream);
return soc_link_ret(rtd, ret);
}
void snd_soc_link_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
}
int snd_soc_link_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->prepare)
ret = rtd->dai_link->ops->prepare(substream);
return soc_link_ret(rtd, ret);
}
int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->hw_params)
ret = rtd->dai_link->ops->hw_params(substream, params);
return soc_link_ret(rtd, ret);
}
void snd_soc_link_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
}
int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret = 0;
if (rtd->dai_link->ops &&
rtd->dai_link->ops->trigger)
ret = rtd->dai_link->ops->trigger(substream, cmd);
return soc_link_ret(rtd, ret);
}
int snd_soc_link_compr_startup(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
int ret = 0;
if (rtd->dai_link->compr_ops &&
rtd->dai_link->compr_ops->startup)
ret = rtd->dai_link->compr_ops->startup(cstream);
return soc_link_ret(rtd, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_startup);
void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
if (rtd->dai_link->compr_ops &&
rtd->dai_link->compr_ops->shutdown)
rtd->dai_link->compr_ops->shutdown(cstream);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_shutdown);
int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
int ret = 0;
if (rtd->dai_link->compr_ops &&
rtd->dai_link->compr_ops->set_params)
ret = rtd->dai_link->compr_ops->set_params(cstream);
return soc_link_ret(rtd, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_link_compr_set_params);