1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // soc-link.c 4 // 5 // Copyright (C) 2019 Renesas Electronics Corp. 6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 7 // 8 #include <sound/soc.h> 9 #include <sound/soc-link.h> 10 11 #define soc_link_ret(rtd, ret) _soc_link_ret(rtd, __func__, ret) 12 static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd, 13 const char *func, int ret) 14 { 15 return snd_soc_ret(rtd->dev, ret, 16 "at %s() on %s\n", func, rtd->dai_link->name); 17 } 18 19 /* 20 * We might want to check substream by using list. 21 * In such case, we can update these macros. 22 */ 23 #define soc_link_mark_push(rtd, substream, tgt) ((rtd)->mark_##tgt = substream) 24 #define soc_link_mark_pop(rtd, tgt) ((rtd)->mark_##tgt = NULL) 25 #define soc_link_mark_match(rtd, substream, tgt) ((rtd)->mark_##tgt == substream) 26 27 int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd) 28 { 29 int ret = 0; 30 31 if (rtd->dai_link->init) 32 ret = rtd->dai_link->init(rtd); 33 34 return soc_link_ret(rtd, ret); 35 } 36 37 void snd_soc_link_exit(struct snd_soc_pcm_runtime *rtd) 38 { 39 if (rtd->dai_link->exit) 40 rtd->dai_link->exit(rtd); 41 } 42 43 int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 44 struct snd_pcm_hw_params *params) 45 { 46 int ret = 0; 47 48 if (rtd->dai_link->be_hw_params_fixup) 49 ret = rtd->dai_link->be_hw_params_fixup(rtd, params); 50 51 return soc_link_ret(rtd, ret); 52 } 53 54 int snd_soc_link_startup(struct snd_pcm_substream *substream) 55 { 56 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 57 int ret = 0; 58 59 if (rtd->dai_link->ops && 60 rtd->dai_link->ops->startup) 61 ret = rtd->dai_link->ops->startup(substream); 62 63 /* mark substream if succeeded */ 64 if (ret == 0) 65 soc_link_mark_push(rtd, substream, startup); 66 67 return soc_link_ret(rtd, ret); 68 } 69 70 void snd_soc_link_shutdown(struct snd_pcm_substream *substream, 71 int rollback) 72 { 73 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 74 75 if (rollback && !soc_link_mark_match(rtd, substream, startup)) 76 return; 77 78 if (rtd->dai_link->ops && 79 rtd->dai_link->ops->shutdown) 80 rtd->dai_link->ops->shutdown(substream); 81 82 /* remove marked substream */ 83 soc_link_mark_pop(rtd, startup); 84 } 85 86 int snd_soc_link_prepare(struct snd_pcm_substream *substream) 87 { 88 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 89 int ret = 0; 90 91 if (rtd->dai_link->ops && 92 rtd->dai_link->ops->prepare) 93 ret = rtd->dai_link->ops->prepare(substream); 94 95 return soc_link_ret(rtd, ret); 96 } 97 98 int snd_soc_link_hw_params(struct snd_pcm_substream *substream, 99 struct snd_pcm_hw_params *params) 100 { 101 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 102 int ret = 0; 103 104 if (rtd->dai_link->ops && 105 rtd->dai_link->ops->hw_params) 106 ret = rtd->dai_link->ops->hw_params(substream, params); 107 108 /* mark substream if succeeded */ 109 if (ret == 0) 110 soc_link_mark_push(rtd, substream, hw_params); 111 112 return soc_link_ret(rtd, ret); 113 } 114 115 void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback) 116 { 117 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 118 119 if (rollback && !soc_link_mark_match(rtd, substream, hw_params)) 120 return; 121 122 if (rtd->dai_link->ops && 123 rtd->dai_link->ops->hw_free) 124 rtd->dai_link->ops->hw_free(substream); 125 126 /* remove marked substream */ 127 soc_link_mark_pop(rtd, hw_params); 128 } 129 130 static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd) 131 { 132 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 133 int ret = 0; 134 135 if (rtd->dai_link->ops && 136 rtd->dai_link->ops->trigger) 137 ret = rtd->dai_link->ops->trigger(substream, cmd); 138 139 return soc_link_ret(rtd, ret); 140 } 141 142 int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd, 143 int rollback) 144 { 145 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 146 int ret = 0; 147 148 switch (cmd) { 149 case SNDRV_PCM_TRIGGER_START: 150 case SNDRV_PCM_TRIGGER_RESUME: 151 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 152 ret = soc_link_trigger(substream, cmd); 153 if (ret < 0) 154 break; 155 soc_link_mark_push(rtd, substream, trigger); 156 break; 157 case SNDRV_PCM_TRIGGER_STOP: 158 case SNDRV_PCM_TRIGGER_SUSPEND: 159 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 160 if (rollback && !soc_link_mark_match(rtd, substream, trigger)) 161 break; 162 163 ret = soc_link_trigger(substream, cmd); 164 soc_link_mark_pop(rtd, startup); 165 } 166 167 return ret; 168 } 169 170 int snd_soc_link_compr_startup(struct snd_compr_stream *cstream) 171 { 172 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 173 int ret = 0; 174 175 if (rtd->dai_link->compr_ops && 176 rtd->dai_link->compr_ops->startup) 177 ret = rtd->dai_link->compr_ops->startup(cstream); 178 179 if (ret == 0) 180 soc_link_mark_push(rtd, cstream, compr_startup); 181 182 return soc_link_ret(rtd, ret); 183 } 184 EXPORT_SYMBOL_GPL(snd_soc_link_compr_startup); 185 186 void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream, 187 int rollback) 188 { 189 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 190 191 if (rollback && !soc_link_mark_match(rtd, cstream, compr_startup)) 192 return; 193 194 if (rtd->dai_link->compr_ops && 195 rtd->dai_link->compr_ops->shutdown) 196 rtd->dai_link->compr_ops->shutdown(cstream); 197 198 soc_link_mark_pop(rtd, compr_startup); 199 } 200 EXPORT_SYMBOL_GPL(snd_soc_link_compr_shutdown); 201 202 int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream) 203 { 204 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 205 int ret = 0; 206 207 if (rtd->dai_link->compr_ops && 208 rtd->dai_link->compr_ops->set_params) 209 ret = rtd->dai_link->compr_ops->set_params(cstream); 210 211 return soc_link_ret(rtd, ret); 212 } 213 EXPORT_SYMBOL_GPL(snd_soc_link_compr_set_params); 214