Commit | Line | Data |
---|---|---|
06f6e1d4 KM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // soc-dai.c | |
4 | // | |
5 | // Copyright (C) 2019 Renesas Electronics Corp. | |
6 | // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
7 | // | |
8 | ||
9 | #include <sound/soc.h> | |
10 | #include <sound/soc-dai.h> | |
0cbbf8a0 | 11 | #include <sound/soc-link.h> |
06f6e1d4 | 12 | |
aa7b8230 KM |
13 | #define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret) |
14 | static inline int _soc_dai_ret(struct snd_soc_dai *dai, | |
15 | const char *func, int ret) | |
16 | { | |
28ff437a PLB |
17 | /* Positive, Zero values are not errors */ |
18 | if (ret >= 0) | |
19 | return ret; | |
20 | ||
21 | /* Negative values might be errors */ | |
aa7b8230 KM |
22 | switch (ret) { |
23 | case -EPROBE_DEFER: | |
24 | case -ENOTSUPP: | |
aa7b8230 KM |
25 | break; |
26 | default: | |
27 | dev_err(dai->dev, | |
28 | "ASoC: error at %s on %s: %d\n", | |
29 | func, dai->name, ret); | |
30 | } | |
31 | ||
32 | return ret; | |
33 | } | |
34 | ||
00a0b46c KM |
35 | /* |
36 | * We might want to check substream by using list. | |
37 | * In such case, we can update these macros. | |
38 | */ | |
39 | #define soc_dai_mark_push(dai, substream, tgt) ((dai)->mark_##tgt = substream) | |
40 | #define soc_dai_mark_pop(dai, substream, tgt) ((dai)->mark_##tgt = NULL) | |
41 | #define soc_dai_mark_match(dai, substream, tgt) ((dai)->mark_##tgt == substream) | |
42 | ||
06f6e1d4 KM |
43 | /** |
44 | * snd_soc_dai_set_sysclk - configure DAI system or master clock. | |
45 | * @dai: DAI | |
46 | * @clk_id: DAI specific clock ID | |
47 | * @freq: new clock frequency in Hz | |
48 | * @dir: new clock direction - input/output. | |
49 | * | |
50 | * Configures the DAI master (MCLK) or system (SYSCLK) clocking. | |
51 | */ | |
52 | int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, | |
53 | unsigned int freq, int dir) | |
54 | { | |
aa7b8230 KM |
55 | int ret; |
56 | ||
479914ed KM |
57 | if (dai->driver->ops && |
58 | dai->driver->ops->set_sysclk) | |
aa7b8230 KM |
59 | ret = dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); |
60 | else | |
61 | ret = snd_soc_component_set_sysclk(dai->component, clk_id, 0, | |
62 | freq, dir); | |
06f6e1d4 | 63 | |
aa7b8230 | 64 | return soc_dai_ret(dai, ret); |
06f6e1d4 KM |
65 | } |
66 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); | |
67 | ||
68 | /** | |
69 | * snd_soc_dai_set_clkdiv - configure DAI clock dividers. | |
70 | * @dai: DAI | |
71 | * @div_id: DAI specific clock divider ID | |
72 | * @div: new clock divisor. | |
73 | * | |
74 | * Configures the clock dividers. This is used to derive the best DAI bit and | |
75 | * frame clocks from the system or master clock. It's best to set the DAI bit | |
76 | * and frame clocks as low as possible to save system power. | |
77 | */ | |
78 | int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, | |
79 | int div_id, int div) | |
80 | { | |
aa7b8230 KM |
81 | int ret = -EINVAL; |
82 | ||
479914ed KM |
83 | if (dai->driver->ops && |
84 | dai->driver->ops->set_clkdiv) | |
aa7b8230 KM |
85 | ret = dai->driver->ops->set_clkdiv(dai, div_id, div); |
86 | ||
87 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
88 | } |
89 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); | |
90 | ||
91 | /** | |
92 | * snd_soc_dai_set_pll - configure DAI PLL. | |
93 | * @dai: DAI | |
94 | * @pll_id: DAI specific PLL ID | |
95 | * @source: DAI specific source for the PLL | |
96 | * @freq_in: PLL input clock frequency in Hz | |
97 | * @freq_out: requested PLL output clock frequency in Hz | |
98 | * | |
99 | * Configures and enables PLL to generate output clock based on input clock. | |
100 | */ | |
101 | int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, | |
102 | unsigned int freq_in, unsigned int freq_out) | |
103 | { | |
aa7b8230 KM |
104 | int ret; |
105 | ||
479914ed KM |
106 | if (dai->driver->ops && |
107 | dai->driver->ops->set_pll) | |
aa7b8230 KM |
108 | ret = dai->driver->ops->set_pll(dai, pll_id, source, |
109 | freq_in, freq_out); | |
110 | else | |
111 | ret = snd_soc_component_set_pll(dai->component, pll_id, source, | |
112 | freq_in, freq_out); | |
06f6e1d4 | 113 | |
aa7b8230 | 114 | return soc_dai_ret(dai, ret); |
06f6e1d4 KM |
115 | } |
116 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); | |
117 | ||
118 | /** | |
119 | * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. | |
120 | * @dai: DAI | |
121 | * @ratio: Ratio of BCLK to Sample rate. | |
122 | * | |
123 | * Configures the DAI for a preset BCLK to sample rate ratio. | |
124 | */ | |
125 | int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) | |
126 | { | |
aa7b8230 KM |
127 | int ret = -EINVAL; |
128 | ||
479914ed KM |
129 | if (dai->driver->ops && |
130 | dai->driver->ops->set_bclk_ratio) | |
aa7b8230 KM |
131 | ret = dai->driver->ops->set_bclk_ratio(dai, ratio); |
132 | ||
133 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
134 | } |
135 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); | |
136 | ||
137 | /** | |
138 | * snd_soc_dai_set_fmt - configure DAI hardware audio format. | |
139 | * @dai: DAI | |
140 | * @fmt: SND_SOC_DAIFMT_* format value. | |
141 | * | |
142 | * Configures the DAI hardware format and clocking. | |
143 | */ | |
144 | int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
145 | { | |
aa7b8230 KM |
146 | int ret = -ENOTSUPP; |
147 | ||
479914ed KM |
148 | if (dai->driver->ops && |
149 | dai->driver->ops->set_fmt) | |
aa7b8230 KM |
150 | ret = dai->driver->ops->set_fmt(dai, fmt); |
151 | ||
152 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
153 | } |
154 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); | |
155 | ||
156 | /** | |
157 | * snd_soc_xlate_tdm_slot - generate tx/rx slot mask. | |
158 | * @slots: Number of slots in use. | |
159 | * @tx_mask: bitmask representing active TX slots. | |
160 | * @rx_mask: bitmask representing active RX slots. | |
161 | * | |
162 | * Generates the TDM tx and rx slot default masks for DAI. | |
163 | */ | |
164 | static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, | |
165 | unsigned int *tx_mask, | |
166 | unsigned int *rx_mask) | |
167 | { | |
168 | if (*tx_mask || *rx_mask) | |
169 | return 0; | |
170 | ||
171 | if (!slots) | |
172 | return -EINVAL; | |
173 | ||
174 | *tx_mask = (1 << slots) - 1; | |
175 | *rx_mask = (1 << slots) - 1; | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | /** | |
181 | * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation | |
182 | * @dai: The DAI to configure | |
183 | * @tx_mask: bitmask representing active TX slots. | |
184 | * @rx_mask: bitmask representing active RX slots. | |
185 | * @slots: Number of slots in use. | |
186 | * @slot_width: Width in bits for each slot. | |
187 | * | |
188 | * This function configures the specified DAI for TDM operation. @slot contains | |
189 | * the total number of slots of the TDM stream and @slot_with the width of each | |
190 | * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the | |
191 | * active slots of the TDM stream for the specified DAI, i.e. which slots the | |
192 | * DAI should write to or read from. If a bit is set the corresponding slot is | |
193 | * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to | |
194 | * the first slot, bit 1 to the second slot and so on. The first active slot | |
195 | * maps to the first channel of the DAI, the second active slot to the second | |
196 | * channel and so on. | |
197 | * | |
198 | * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, | |
199 | * @rx_mask and @slot_width will be ignored. | |
200 | * | |
201 | * Returns 0 on success, a negative error code otherwise. | |
202 | */ | |
203 | int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, | |
204 | unsigned int tx_mask, unsigned int rx_mask, | |
205 | int slots, int slot_width) | |
206 | { | |
aa7b8230 KM |
207 | int ret = -ENOTSUPP; |
208 | ||
479914ed KM |
209 | if (dai->driver->ops && |
210 | dai->driver->ops->xlate_tdm_slot_mask) | |
06f6e1d4 KM |
211 | dai->driver->ops->xlate_tdm_slot_mask(slots, |
212 | &tx_mask, &rx_mask); | |
213 | else | |
214 | snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); | |
215 | ||
216 | dai->tx_mask = tx_mask; | |
217 | dai->rx_mask = rx_mask; | |
218 | ||
479914ed KM |
219 | if (dai->driver->ops && |
220 | dai->driver->ops->set_tdm_slot) | |
aa7b8230 | 221 | ret = dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, |
06f6e1d4 | 222 | slots, slot_width); |
aa7b8230 | 223 | return soc_dai_ret(dai, ret); |
06f6e1d4 KM |
224 | } |
225 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); | |
226 | ||
227 | /** | |
228 | * snd_soc_dai_set_channel_map - configure DAI audio channel map | |
229 | * @dai: DAI | |
230 | * @tx_num: how many TX channels | |
231 | * @tx_slot: pointer to an array which imply the TX slot number channel | |
232 | * 0~num-1 uses | |
233 | * @rx_num: how many RX channels | |
234 | * @rx_slot: pointer to an array which imply the RX slot number channel | |
235 | * 0~num-1 uses | |
236 | * | |
237 | * configure the relationship between channel number and TDM slot number. | |
238 | */ | |
239 | int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, | |
240 | unsigned int tx_num, unsigned int *tx_slot, | |
241 | unsigned int rx_num, unsigned int *rx_slot) | |
242 | { | |
aa7b8230 KM |
243 | int ret = -ENOTSUPP; |
244 | ||
479914ed KM |
245 | if (dai->driver->ops && |
246 | dai->driver->ops->set_channel_map) | |
aa7b8230 KM |
247 | ret = dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, |
248 | rx_num, rx_slot); | |
249 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
250 | } |
251 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); | |
252 | ||
253 | /** | |
254 | * snd_soc_dai_get_channel_map - Get DAI audio channel map | |
255 | * @dai: DAI | |
256 | * @tx_num: how many TX channels | |
257 | * @tx_slot: pointer to an array which imply the TX slot number channel | |
258 | * 0~num-1 uses | |
259 | * @rx_num: how many RX channels | |
260 | * @rx_slot: pointer to an array which imply the RX slot number channel | |
261 | * 0~num-1 uses | |
262 | */ | |
263 | int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, | |
264 | unsigned int *tx_num, unsigned int *tx_slot, | |
265 | unsigned int *rx_num, unsigned int *rx_slot) | |
266 | { | |
aa7b8230 KM |
267 | int ret = -ENOTSUPP; |
268 | ||
479914ed KM |
269 | if (dai->driver->ops && |
270 | dai->driver->ops->get_channel_map) | |
aa7b8230 KM |
271 | ret = dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, |
272 | rx_num, rx_slot); | |
273 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
274 | } |
275 | EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); | |
276 | ||
277 | /** | |
278 | * snd_soc_dai_set_tristate - configure DAI system or master clock. | |
279 | * @dai: DAI | |
280 | * @tristate: tristate enable | |
281 | * | |
282 | * Tristates the DAI so that others can use it. | |
283 | */ | |
284 | int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) | |
285 | { | |
aa7b8230 KM |
286 | int ret = -EINVAL; |
287 | ||
479914ed KM |
288 | if (dai->driver->ops && |
289 | dai->driver->ops->set_tristate) | |
aa7b8230 KM |
290 | ret = dai->driver->ops->set_tristate(dai, tristate); |
291 | ||
292 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
293 | } |
294 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); | |
295 | ||
296 | /** | |
297 | * snd_soc_dai_digital_mute - configure DAI system or master clock. | |
298 | * @dai: DAI | |
299 | * @mute: mute enable | |
300 | * @direction: stream to mute | |
301 | * | |
302 | * Mutes the DAI DAC. | |
303 | */ | |
304 | int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, | |
305 | int direction) | |
306 | { | |
aa7b8230 KM |
307 | int ret = -ENOTSUPP; |
308 | ||
350d9935 KM |
309 | /* |
310 | * ignore if direction was CAPTURE | |
311 | * and it had .no_capture_mute flag | |
312 | */ | |
479914ed | 313 | if (dai->driver->ops && |
350d9935 KM |
314 | dai->driver->ops->mute_stream && |
315 | (direction == SNDRV_PCM_STREAM_PLAYBACK || | |
316 | !dai->driver->ops->no_capture_mute)) | |
aa7b8230 | 317 | ret = dai->driver->ops->mute_stream(dai, mute, direction); |
aa7b8230 KM |
318 | |
319 | return soc_dai_ret(dai, ret); | |
06f6e1d4 KM |
320 | } |
321 | EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); | |
aa6166c2 KM |
322 | |
323 | int snd_soc_dai_hw_params(struct snd_soc_dai *dai, | |
324 | struct snd_pcm_substream *substream, | |
325 | struct snd_pcm_hw_params *params) | |
326 | { | |
0ceef681 | 327 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
aa7b8230 | 328 | int ret = 0; |
aa6166c2 KM |
329 | |
330 | /* perform any topology hw_params fixups before DAI */ | |
0cbbf8a0 KM |
331 | ret = snd_soc_link_be_hw_params_fixup(rtd, params); |
332 | if (ret < 0) | |
333 | goto end; | |
aa6166c2 | 334 | |
479914ed KM |
335 | if (dai->driver->ops && |
336 | dai->driver->ops->hw_params) | |
aa6166c2 | 337 | ret = dai->driver->ops->hw_params(substream, params, dai); |
c304c9ac KM |
338 | |
339 | /* mark substream if succeeded */ | |
340 | if (ret == 0) | |
341 | soc_dai_mark_push(dai, substream, hw_params); | |
aa7b8230 KM |
342 | end: |
343 | return soc_dai_ret(dai, ret); | |
aa6166c2 | 344 | } |
846faaed KM |
345 | |
346 | void snd_soc_dai_hw_free(struct snd_soc_dai *dai, | |
c304c9ac KM |
347 | struct snd_pcm_substream *substream, |
348 | int rollback) | |
846faaed | 349 | { |
c304c9ac KM |
350 | if (rollback && !soc_dai_mark_match(dai, substream, hw_params)) |
351 | return; | |
352 | ||
479914ed KM |
353 | if (dai->driver->ops && |
354 | dai->driver->ops->hw_free) | |
846faaed | 355 | dai->driver->ops->hw_free(substream, dai); |
c304c9ac KM |
356 | |
357 | /* remove marked substream */ | |
358 | soc_dai_mark_pop(dai, substream, hw_params); | |
846faaed | 359 | } |
5a52a045 KM |
360 | |
361 | int snd_soc_dai_startup(struct snd_soc_dai *dai, | |
362 | struct snd_pcm_substream *substream) | |
363 | { | |
364 | int ret = 0; | |
365 | ||
479914ed KM |
366 | if (dai->driver->ops && |
367 | dai->driver->ops->startup) | |
5a52a045 KM |
368 | ret = dai->driver->ops->startup(substream, dai); |
369 | ||
00a0b46c KM |
370 | /* mark substream if succeeded */ |
371 | if (ret == 0) | |
372 | soc_dai_mark_push(dai, substream, startup); | |
373 | ||
aa7b8230 | 374 | return soc_dai_ret(dai, ret); |
5a52a045 | 375 | } |
330fcb51 KM |
376 | |
377 | void snd_soc_dai_shutdown(struct snd_soc_dai *dai, | |
00a0b46c KM |
378 | struct snd_pcm_substream *substream, |
379 | int rollback) | |
330fcb51 | 380 | { |
00a0b46c KM |
381 | if (rollback && !soc_dai_mark_match(dai, substream, startup)) |
382 | return; | |
383 | ||
479914ed KM |
384 | if (dai->driver->ops && |
385 | dai->driver->ops->shutdown) | |
330fcb51 | 386 | dai->driver->ops->shutdown(substream, dai); |
00a0b46c KM |
387 | |
388 | /* remove marked substream */ | |
389 | soc_dai_mark_pop(dai, substream, startup); | |
330fcb51 | 390 | } |
4beb8e10 | 391 | |
1dea80d4 KM |
392 | snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, |
393 | struct snd_pcm_substream *substream) | |
394 | { | |
395 | int delay = 0; | |
396 | ||
479914ed KM |
397 | if (dai->driver->ops && |
398 | dai->driver->ops->delay) | |
1dea80d4 KM |
399 | delay = dai->driver->ops->delay(substream, dai); |
400 | ||
401 | return delay; | |
402 | } | |
e0f22622 | 403 | |
b423c420 KM |
404 | int snd_soc_dai_compress_new(struct snd_soc_dai *dai, |
405 | struct snd_soc_pcm_runtime *rtd, int num) | |
406 | { | |
aa7b8230 | 407 | int ret = -ENOTSUPP; |
b423c420 | 408 | if (dai->driver->compress_new) |
aa7b8230 KM |
409 | ret = dai->driver->compress_new(rtd, num); |
410 | return soc_dai_ret(dai, ret); | |
b423c420 | 411 | } |
467fece8 KM |
412 | |
413 | /* | |
414 | * snd_soc_dai_stream_valid() - check if a DAI supports the given stream | |
415 | * | |
416 | * Returns true if the DAI supports the indicated stream type. | |
417 | */ | |
418 | bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) | |
419 | { | |
acf253c1 | 420 | struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); |
467fece8 KM |
421 | |
422 | /* If the codec specifies any channels at all, it supports the stream */ | |
423 | return stream->channels_min; | |
424 | } | |
0b73ba55 | 425 | |
25612477 PLB |
426 | /* |
427 | * snd_soc_dai_link_set_capabilities() - set dai_link properties based on its DAIs | |
428 | */ | |
429 | void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link *dai_link) | |
430 | { | |
431 | struct snd_soc_dai_link_component *cpu; | |
432 | struct snd_soc_dai_link_component *codec; | |
433 | struct snd_soc_dai *dai; | |
434 | bool supported[SNDRV_PCM_STREAM_LAST + 1]; | |
4f872154 PLB |
435 | bool supported_cpu; |
436 | bool supported_codec; | |
25612477 PLB |
437 | int direction; |
438 | int i; | |
439 | ||
440 | for_each_pcm_streams(direction) { | |
4f872154 PLB |
441 | supported_cpu = false; |
442 | supported_codec = false; | |
25612477 PLB |
443 | |
444 | for_each_link_cpus(dai_link, i, cpu) { | |
c1c277b2 | 445 | dai = snd_soc_find_dai_with_mutex(cpu); |
4f872154 PLB |
446 | if (dai && snd_soc_dai_stream_valid(dai, direction)) { |
447 | supported_cpu = true; | |
25612477 PLB |
448 | break; |
449 | } | |
450 | } | |
25612477 | 451 | for_each_link_codecs(dai_link, i, codec) { |
c1c277b2 | 452 | dai = snd_soc_find_dai_with_mutex(codec); |
4f872154 PLB |
453 | if (dai && snd_soc_dai_stream_valid(dai, direction)) { |
454 | supported_codec = true; | |
25612477 PLB |
455 | break; |
456 | } | |
457 | } | |
4f872154 | 458 | supported[direction] = supported_cpu && supported_codec; |
25612477 PLB |
459 | } |
460 | ||
461 | dai_link->dpcm_playback = supported[SNDRV_PCM_STREAM_PLAYBACK]; | |
462 | dai_link->dpcm_capture = supported[SNDRV_PCM_STREAM_CAPTURE]; | |
463 | } | |
464 | EXPORT_SYMBOL_GPL(snd_soc_dai_link_set_capabilities); | |
465 | ||
dc829106 KM |
466 | void snd_soc_dai_action(struct snd_soc_dai *dai, |
467 | int stream, int action) | |
468 | { | |
5552f8d7 | 469 | /* see snd_soc_dai_stream_active() */ |
dc829106 | 470 | dai->stream_active[stream] += action; |
0812a08a | 471 | |
488b2ca5 | 472 | /* see snd_soc_component_active() */ |
dc829106 KM |
473 | dai->component->active += action; |
474 | } | |
475 | EXPORT_SYMBOL_GPL(snd_soc_dai_action); | |
476 | ||
efffd9b3 KM |
477 | int snd_soc_dai_active(struct snd_soc_dai *dai) |
478 | { | |
479 | int stream, active; | |
480 | ||
481 | active = 0; | |
482 | for_each_pcm_streams(stream) | |
483 | active += dai->stream_active[stream]; | |
484 | ||
485 | return active; | |
486 | } | |
487 | EXPORT_SYMBOL_GPL(snd_soc_dai_active); | |
488 | ||
51801aea KM |
489 | int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order) |
490 | { | |
491 | struct snd_soc_dai *dai; | |
492 | int i; | |
493 | ||
494 | for_each_rtd_dais(rtd, i, dai) { | |
495 | if (dai->driver->probe_order != order) | |
496 | continue; | |
497 | ||
498 | if (dai->driver->probe) { | |
499 | int ret = dai->driver->probe(dai); | |
500 | ||
501 | if (ret < 0) | |
502 | return soc_dai_ret(dai, ret); | |
503 | } | |
504 | ||
505 | dai->probed = 1; | |
506 | } | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
7eaa313b KM |
511 | int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order) |
512 | { | |
513 | struct snd_soc_dai *dai; | |
514 | int i, r, ret = 0; | |
515 | ||
516 | for_each_rtd_dais(rtd, i, dai) { | |
517 | if (dai->driver->remove_order != order) | |
518 | continue; | |
519 | ||
520 | if (dai->probed && | |
521 | dai->driver->remove) { | |
522 | r = dai->driver->remove(dai); | |
523 | if (r < 0) | |
524 | ret = r; /* use last error */ | |
525 | } | |
526 | ||
527 | dai->probed = 0; | |
528 | } | |
529 | ||
530 | return ret; | |
531 | } | |
532 | ||
0b73ba55 KM |
533 | int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) |
534 | { | |
535 | struct snd_soc_dai *dai; | |
536 | int i, ret = 0; | |
537 | ||
538 | for_each_rtd_dais(rtd, i, dai) { | |
539 | if (dai->driver->pcm_new) { | |
540 | ret = dai->driver->pcm_new(rtd, dai); | |
541 | if (ret < 0) | |
542 | return soc_dai_ret(dai, ret); | |
543 | } | |
544 | } | |
545 | ||
546 | return 0; | |
547 | } | |
d108c7fd KM |
548 | |
549 | int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) | |
550 | { | |
0ceef681 | 551 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
d108c7fd KM |
552 | struct snd_soc_dai *dai; |
553 | int i, ret; | |
554 | ||
555 | for_each_rtd_dais(rtd, i, dai) { | |
556 | if (dai->driver->ops && | |
557 | dai->driver->ops->prepare) { | |
558 | ret = dai->driver->ops->prepare(substream, dai); | |
559 | if (ret < 0) | |
560 | return soc_dai_ret(dai, ret); | |
561 | } | |
562 | } | |
563 | ||
564 | return 0; | |
565 | } | |
42f2472d | 566 | |
6374f493 KM |
567 | static int soc_dai_trigger(struct snd_soc_dai *dai, |
568 | struct snd_pcm_substream *substream, int cmd) | |
569 | { | |
570 | int ret = 0; | |
571 | ||
572 | if (dai->driver->ops && | |
573 | dai->driver->ops->trigger) | |
574 | ret = dai->driver->ops->trigger(substream, cmd, dai); | |
575 | ||
576 | return soc_dai_ret(dai, ret); | |
577 | } | |
578 | ||
42f2472d | 579 | int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, |
6374f493 | 580 | int cmd, int rollback) |
42f2472d | 581 | { |
0ceef681 | 582 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
42f2472d | 583 | struct snd_soc_dai *dai; |
6374f493 | 584 | int i, r, ret = 0; |
42f2472d | 585 | |
6374f493 KM |
586 | switch (cmd) { |
587 | case SNDRV_PCM_TRIGGER_START: | |
588 | case SNDRV_PCM_TRIGGER_RESUME: | |
589 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
590 | for_each_rtd_dais(rtd, i, dai) { | |
591 | ret = soc_dai_trigger(dai, substream, cmd); | |
42f2472d | 592 | if (ret < 0) |
6374f493 KM |
593 | break; |
594 | soc_dai_mark_push(dai, substream, trigger); | |
595 | } | |
596 | break; | |
597 | case SNDRV_PCM_TRIGGER_STOP: | |
598 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
599 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
600 | for_each_rtd_dais(rtd, i, dai) { | |
601 | if (rollback && !soc_dai_mark_match(dai, substream, trigger)) | |
602 | continue; | |
603 | ||
604 | r = soc_dai_trigger(dai, substream, cmd); | |
605 | if (r < 0) | |
606 | ret = r; /* use last ret */ | |
607 | soc_dai_mark_pop(dai, substream, trigger); | |
42f2472d KM |
608 | } |
609 | } | |
610 | ||
6374f493 | 611 | return ret; |
42f2472d | 612 | } |
30819358 KM |
613 | |
614 | int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, | |
615 | int cmd) | |
616 | { | |
0ceef681 | 617 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
30819358 KM |
618 | struct snd_soc_dai *dai; |
619 | int i, ret; | |
620 | ||
621 | for_each_rtd_dais(rtd, i, dai) { | |
622 | if (dai->driver->ops && | |
623 | dai->driver->ops->bespoke_trigger) { | |
624 | ret = dai->driver->ops->bespoke_trigger(substream, | |
625 | cmd, dai); | |
626 | if (ret < 0) | |
627 | return soc_dai_ret(dai, ret); | |
628 | } | |
629 | } | |
630 | ||
631 | return 0; | |
632 | } | |
b5ae4cce KM |
633 | |
634 | int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, | |
635 | struct snd_compr_stream *cstream) | |
636 | { | |
637 | int ret = 0; | |
638 | ||
639 | if (dai->driver->cops && | |
640 | dai->driver->cops->startup) | |
641 | ret = dai->driver->cops->startup(cstream, dai); | |
642 | ||
1e6a93cf KM |
643 | /* mark cstream if succeeded */ |
644 | if (ret == 0) | |
645 | soc_dai_mark_push(dai, cstream, compr_startup); | |
646 | ||
b5ae4cce KM |
647 | return soc_dai_ret(dai, ret); |
648 | } | |
649 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup); | |
2b25f81d KM |
650 | |
651 | void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai, | |
1e6a93cf KM |
652 | struct snd_compr_stream *cstream, |
653 | int rollback) | |
2b25f81d | 654 | { |
1e6a93cf KM |
655 | if (rollback && !soc_dai_mark_match(dai, cstream, compr_startup)) |
656 | return; | |
657 | ||
2b25f81d KM |
658 | if (dai->driver->cops && |
659 | dai->driver->cops->shutdown) | |
660 | dai->driver->cops->shutdown(cstream, dai); | |
1e6a93cf KM |
661 | |
662 | /* remove marked cstream */ | |
663 | soc_dai_mark_pop(dai, cstream, compr_startup); | |
2b25f81d KM |
664 | } |
665 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown); | |
eb08411b KM |
666 | |
667 | int snd_soc_dai_compr_trigger(struct snd_soc_dai *dai, | |
668 | struct snd_compr_stream *cstream, int cmd) | |
669 | { | |
670 | int ret = 0; | |
671 | ||
672 | if (dai->driver->cops && | |
673 | dai->driver->cops->trigger) | |
674 | ret = dai->driver->cops->trigger(cstream, cmd, dai); | |
675 | ||
676 | return soc_dai_ret(dai, ret); | |
677 | } | |
678 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_trigger); | |
8dfedafb KM |
679 | |
680 | int snd_soc_dai_compr_set_params(struct snd_soc_dai *dai, | |
681 | struct snd_compr_stream *cstream, | |
682 | struct snd_compr_params *params) | |
683 | { | |
684 | int ret = 0; | |
685 | ||
686 | if (dai->driver->cops && | |
687 | dai->driver->cops->set_params) | |
688 | ret = dai->driver->cops->set_params(cstream, params, dai); | |
689 | ||
690 | return soc_dai_ret(dai, ret); | |
691 | } | |
692 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_params); | |
adbef543 KM |
693 | |
694 | int snd_soc_dai_compr_get_params(struct snd_soc_dai *dai, | |
695 | struct snd_compr_stream *cstream, | |
696 | struct snd_codec *params) | |
697 | { | |
698 | int ret = 0; | |
699 | ||
700 | if (dai->driver->cops && | |
701 | dai->driver->cops->get_params) | |
702 | ret = dai->driver->cops->get_params(cstream, params, dai); | |
703 | ||
704 | return soc_dai_ret(dai, ret); | |
705 | } | |
706 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_params); | |
53294353 KM |
707 | |
708 | int snd_soc_dai_compr_ack(struct snd_soc_dai *dai, | |
709 | struct snd_compr_stream *cstream, | |
710 | size_t bytes) | |
711 | { | |
712 | int ret = 0; | |
713 | ||
714 | if (dai->driver->cops && | |
715 | dai->driver->cops->ack) | |
716 | ret = dai->driver->cops->ack(cstream, bytes, dai); | |
717 | ||
718 | return soc_dai_ret(dai, ret); | |
719 | } | |
720 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_ack); | |
ed38cc59 KM |
721 | |
722 | int snd_soc_dai_compr_pointer(struct snd_soc_dai *dai, | |
723 | struct snd_compr_stream *cstream, | |
724 | struct snd_compr_tstamp *tstamp) | |
725 | { | |
726 | int ret = 0; | |
727 | ||
728 | if (dai->driver->cops && | |
729 | dai->driver->cops->pointer) | |
730 | ret = dai->driver->cops->pointer(cstream, tstamp, dai); | |
731 | ||
732 | return soc_dai_ret(dai, ret); | |
733 | } | |
734 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_pointer); | |
88b3a7df KM |
735 | |
736 | int snd_soc_dai_compr_set_metadata(struct snd_soc_dai *dai, | |
737 | struct snd_compr_stream *cstream, | |
738 | struct snd_compr_metadata *metadata) | |
739 | { | |
740 | int ret = 0; | |
741 | ||
742 | if (dai->driver->cops && | |
743 | dai->driver->cops->set_metadata) | |
744 | ret = dai->driver->cops->set_metadata(cstream, metadata, dai); | |
745 | ||
746 | return soc_dai_ret(dai, ret); | |
747 | } | |
748 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_metadata); | |
94d72819 KM |
749 | |
750 | int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, | |
751 | struct snd_compr_stream *cstream, | |
752 | struct snd_compr_metadata *metadata) | |
753 | { | |
754 | int ret = 0; | |
755 | ||
756 | if (dai->driver->cops && | |
757 | dai->driver->cops->get_metadata) | |
758 | ret = dai->driver->cops->get_metadata(cstream, metadata, dai); | |
759 | ||
760 | return soc_dai_ret(dai, ret); | |
761 | } | |
762 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata); |