| // 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) | |
| { | |
| return snd_soc_ret(rtd->dev, ret, | |
| "at %s() on %s\n", func, rtd->dai_link->name); | |
| } | |
| /* | |
| * 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, 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 = snd_soc_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 = snd_soc_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, startup); | |
| } | |
| int snd_soc_link_prepare(struct snd_pcm_substream *substream) | |
| { | |
| struct snd_soc_pcm_runtime *rtd = snd_soc_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 = snd_soc_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 = snd_soc_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, hw_params); | |
| } | |
| static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd) | |
| { | |
| struct snd_soc_pcm_runtime *rtd = snd_soc_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 = snd_soc_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, 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, 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); |