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 | |