| // 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 = 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, substream, 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, substream, 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, 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); |