diff options
Diffstat (limited to 'sound/soc/soc-link.c')
-rw-r--r-- | sound/soc/soc-link.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/sound/soc/soc-link.c b/sound/soc/soc-link.c new file mode 100644 index 000000000..619664cc9 --- /dev/null +++ b/sound/soc/soc-link.c @@ -0,0 +1,227 @@ +// 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; +} + +/* + * We might want to check substream by using list. + * In such case, we can update these macros. + */ +#define soc_link_mark_push(rtd, substream, tgt) ((rtd)->mark_##tgt = substream) +#define soc_link_mark_pop(rtd, substream, tgt) ((rtd)->mark_##tgt = NULL) +#define soc_link_mark_match(rtd, substream, tgt) ((rtd)->mark_##tgt == substream) + +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 = asoc_substream_to_rtd(substream); + int ret = 0; + + if (rtd->dai_link->ops && + rtd->dai_link->ops->startup) + ret = rtd->dai_link->ops->startup(substream); + + /* mark substream if succeeded */ + if (ret == 0) + soc_link_mark_push(rtd, substream, startup); + + return soc_link_ret(rtd, ret); +} + +void snd_soc_link_shutdown(struct snd_pcm_substream *substream, + int rollback) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + if (rollback && !soc_link_mark_match(rtd, substream, startup)) + return; + + if (rtd->dai_link->ops && + rtd->dai_link->ops->shutdown) + rtd->dai_link->ops->shutdown(substream); + + /* remove marked substream */ + soc_link_mark_pop(rtd, substream, startup); +} + +int snd_soc_link_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + 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 = asoc_substream_to_rtd(substream); + int ret = 0; + + if (rtd->dai_link->ops && + rtd->dai_link->ops->hw_params) + ret = rtd->dai_link->ops->hw_params(substream, params); + + /* mark substream if succeeded */ + if (ret == 0) + soc_link_mark_push(rtd, substream, hw_params); + + return soc_link_ret(rtd, ret); +} + +void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + if (rollback && !soc_link_mark_match(rtd, substream, hw_params)) + return; + + if (rtd->dai_link->ops && + rtd->dai_link->ops->hw_free) + rtd->dai_link->ops->hw_free(substream); + + /* remove marked substream */ + soc_link_mark_pop(rtd, substream, hw_params); +} + +static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + 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_trigger(struct snd_pcm_substream *substream, int cmd, + int rollback) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = soc_link_trigger(substream, cmd); + if (ret < 0) + break; + soc_link_mark_push(rtd, substream, trigger); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (rollback && !soc_link_mark_match(rtd, substream, trigger)) + break; + + ret = soc_link_trigger(substream, cmd); + soc_link_mark_pop(rtd, substream, startup); + } + + return 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); + + if (ret == 0) + soc_link_mark_push(rtd, cstream, compr_startup); + + 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, + int rollback) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + + if (rollback && !soc_link_mark_match(rtd, cstream, compr_startup)) + return; + + if (rtd->dai_link->compr_ops && + rtd->dai_link->compr_ops->shutdown) + rtd->dai_link->compr_ops->shutdown(cstream); + + soc_link_mark_pop(rtd, cstream, compr_startup); +} +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); |