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 /* Positive, Zero values are not errors */ 16 if (ret >= 0) 17 return ret; 18 19 /* Negative values might be errors */ 20 switch (ret) { 21 case -EPROBE_DEFER: 22 case -ENOTSUPP: 23 break; 24 default: 25 dev_err(rtd->dev, 26 "ASoC: error at %s on %s: %d\n", 27 func, rtd->dai_link->name, ret); 28 } 29 30 return ret; 31 } 32 33 /* 34 * We might want to check substream by using list. 35 * In such case, we can update these macros. 36 */ 37 #define soc_link_mark_push(rtd, substream, tgt) ((rtd)->mark_##tgt = substream) 38 #define soc_link_mark_pop(rtd, substream, tgt) ((rtd)->mark_##tgt = NULL) 39 #define soc_link_mark_match(rtd, substream, tgt) ((rtd)->mark_##tgt == substream) 40 41 int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd) 42 { 43 int ret = 0; 44 45 if (rtd->dai_link->init) 46 ret = rtd->dai_link->init(rtd); 47 48 return soc_link_ret(rtd, ret); 49 } 50 51 void snd_soc_link_exit(struct snd_soc_pcm_runtime *rtd) 52 { 53 if (rtd->dai_link->exit) 54 rtd->dai_link->exit(rtd); 55 } 56 57 int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 58 struct snd_pcm_hw_params *params) 59 { 60 int ret = 0; 61 62 if (rtd->dai_link->be_hw_params_fixup) 63 ret = rtd->dai_link->be_hw_params_fixup(rtd, params); 64 65 return soc_link_ret(rtd, ret); 66 } 67 68 int snd_soc_link_startup(struct snd_pcm_substream *substream) 69 { 70 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 71 int ret = 0; 72 73 if (rtd->dai_link->ops && 74 rtd->dai_link->ops->startup) 75 ret = rtd->dai_link->ops->startup(substream); 76 77 /* mark substream if succeeded */ 78 if (ret == 0) 79 soc_link_mark_push(rtd, substream, startup); 80 81 return soc_link_ret(rtd, ret); 82 } 83 84 void snd_soc_link_shutdown(struct snd_pcm_substream *substream, 85 int rollback) 86 { 87 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 88 89 if (rollback && !soc_link_mark_match(rtd, substream, startup)) 90 return; 91 92 if (rtd->dai_link->ops && 93 rtd->dai_link->ops->shutdown) 94 rtd->dai_link->ops->shutdown(substream); 95 96 /* remove marked substream */ 97 soc_link_mark_pop(rtd, substream, startup); 98 } 99 100 int snd_soc_link_prepare(struct snd_pcm_substream *substream) 101 { 102 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 103 int ret = 0; 104 105 if (rtd->dai_link->ops && 106 rtd->dai_link->ops->prepare) 107 ret = rtd->dai_link->ops->prepare(substream); 108 109 return soc_link_ret(rtd, ret); 110 } 111 112 int snd_soc_link_hw_params(struct snd_pcm_substream *substream, 113 struct snd_pcm_hw_params *params) 114 { 115 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 116 int ret = 0; 117 118 if (rtd->dai_link->ops && 119 rtd->dai_link->ops->hw_params) 120 ret = rtd->dai_link->ops->hw_params(substream, params); 121 122 /* mark substream if succeeded */ 123 if (ret == 0) 124 soc_link_mark_push(rtd, substream, hw_params); 125 126 return soc_link_ret(rtd, ret); 127 } 128 129 void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback) 130 { 131 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 132 133 if (rollback && !soc_link_mark_match(rtd, substream, hw_params)) 134 return; 135 136 if (rtd->dai_link->ops && 137 rtd->dai_link->ops->hw_free) 138 rtd->dai_link->ops->hw_free(substream); 139 140 /* remove marked substream */ 141 soc_link_mark_pop(rtd, substream, hw_params); 142 } 143 144 static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd) 145 { 146 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 147 int ret = 0; 148 149 if (rtd->dai_link->ops && 150 rtd->dai_link->ops->trigger) 151 ret = rtd->dai_link->ops->trigger(substream, cmd); 152 153 return soc_link_ret(rtd, ret); 154 } 155 156 int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd, 157 int rollback) 158 { 159 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 160 int ret = 0; 161 162 switch (cmd) { 163 case SNDRV_PCM_TRIGGER_START: 164 case SNDRV_PCM_TRIGGER_RESUME: 165 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 166 ret = soc_link_trigger(substream, cmd); 167 if (ret < 0) 168 break; 169 soc_link_mark_push(rtd, substream, trigger); 170 break; 171 case SNDRV_PCM_TRIGGER_STOP: 172 case SNDRV_PCM_TRIGGER_SUSPEND: 173 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 174 if (rollback && !soc_link_mark_match(rtd, substream, trigger)) 175 break; 176 177 ret = soc_link_trigger(substream, cmd); 178 soc_link_mark_pop(rtd, substream, startup); 179 } 180 181 return ret; 182 } 183 184 int snd_soc_link_compr_startup(struct snd_compr_stream *cstream) 185 { 186 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 187 int ret = 0; 188 189 if (rtd->dai_link->compr_ops && 190 rtd->dai_link->compr_ops->startup) 191 ret = rtd->dai_link->compr_ops->startup(cstream); 192 193 if (ret == 0) 194 soc_link_mark_push(rtd, cstream, compr_startup); 195 196 return soc_link_ret(rtd, ret); 197 } 198 EXPORT_SYMBOL_GPL(snd_soc_link_compr_startup); 199 200 void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream, 201 int rollback) 202 { 203 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 204 205 if (rollback && !soc_link_mark_match(rtd, cstream, compr_startup)) 206 return; 207 208 if (rtd->dai_link->compr_ops && 209 rtd->dai_link->compr_ops->shutdown) 210 rtd->dai_link->compr_ops->shutdown(cstream); 211 212 soc_link_mark_pop(rtd, cstream, compr_startup); 213 } 214 EXPORT_SYMBOL_GPL(snd_soc_link_compr_shutdown); 215 216 int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream) 217 { 218 struct snd_soc_pcm_runtime *rtd = cstream->private_data; 219 int ret = 0; 220 221 if (rtd->dai_link->compr_ops && 222 rtd->dai_link->compr_ops->set_params) 223 ret = rtd->dai_link->compr_ops->set_params(cstream); 224 225 return soc_link_ret(rtd, ret); 226 } 227 EXPORT_SYMBOL_GPL(snd_soc_link_compr_set_params); 228