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> |
11 | #include <sound/soc-link.h> |
12 | |
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 | { |
17 | /* Positive, Zero values are not errors */ |
18 | if (ret >= 0) |
19 | return ret; |
20 | |
21 | /* Negative values might be errors */ |
22 | switch (ret) { |
23 | case -EPROBE_DEFER: |
24 | case -ENOTSUPP: |
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 | |
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 | |
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 | { |
55 | int ret; |
56 | |
57 | if (dai->driver->ops && |
58 | dai->driver->ops->set_sysclk) |
59 | ret = dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); |
60 | else |
61 | ret = snd_soc_component_set_sysclk(component: dai->component, clk_id, source: 0, |
62 | freq, dir); |
63 | |
64 | return soc_dai_ret(dai, ret); |
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 | { |
81 | int ret = -EINVAL; |
82 | |
83 | if (dai->driver->ops && |
84 | dai->driver->ops->set_clkdiv) |
85 | ret = dai->driver->ops->set_clkdiv(dai, div_id, div); |
86 | |
87 | return soc_dai_ret(dai, ret); |
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 | { |
104 | int ret; |
105 | |
106 | if (dai->driver->ops && |
107 | dai->driver->ops->set_pll) |
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(component: dai->component, pll_id, source, |
112 | freq_in, freq_out); |
113 | |
114 | return soc_dai_ret(dai, ret); |
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 | { |
127 | int ret = -ENOTSUPP; |
128 | |
129 | if (dai->driver->ops && |
130 | dai->driver->ops->set_bclk_ratio) |
131 | ret = dai->driver->ops->set_bclk_ratio(dai, ratio); |
132 | |
133 | return soc_dai_ret(dai, ret); |
134 | } |
135 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); |
136 | |
137 | int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd) |
138 | { |
139 | struct snd_soc_dai *dai; |
140 | int i, max = 0; |
141 | |
142 | /* |
143 | * return max num if *ALL* DAIs have .auto_selectable_formats |
144 | */ |
145 | for_each_rtd_dais(rtd, i, dai) { |
146 | if (dai->driver->ops && |
147 | dai->driver->ops->num_auto_selectable_formats) |
148 | max = max(max, dai->driver->ops->num_auto_selectable_formats); |
149 | else |
150 | return 0; |
151 | } |
152 | |
153 | return max; |
154 | } |
155 | |
156 | /** |
157 | * snd_soc_dai_get_fmt - get supported audio format. |
158 | * @dai: DAI |
159 | * @priority: priority level of supported audio format. |
160 | * |
161 | * This should return only formats implemented with high |
162 | * quality by the DAI so that the core can configure a |
163 | * format which will work well with other devices. |
164 | * For example devices which don't support both edges of the |
165 | * LRCLK signal in I2S style formats should only list DSP |
166 | * modes. This will mean that sometimes fewer formats |
167 | * are reported here than are supported by set_fmt(). |
168 | */ |
169 | u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority) |
170 | { |
171 | const struct snd_soc_dai_ops *ops = dai->driver->ops; |
172 | u64 fmt = 0; |
173 | int i, max = 0, until = priority; |
174 | |
175 | /* |
176 | * Collect auto_selectable_formats until priority |
177 | * |
178 | * ex) |
179 | * auto_selectable_formats[] = { A, B, C }; |
180 | * (A, B, C = SND_SOC_POSSIBLE_DAIFMT_xxx) |
181 | * |
182 | * priority = 1 : A |
183 | * priority = 2 : A | B |
184 | * priority = 3 : A | B | C |
185 | * priority = 4 : A | B | C |
186 | * ... |
187 | */ |
188 | if (ops) |
189 | max = ops->num_auto_selectable_formats; |
190 | |
191 | if (max < until) |
192 | until = max; |
193 | |
194 | for (i = 0; i < until; i++) |
195 | fmt |= ops->auto_selectable_formats[i]; |
196 | |
197 | return fmt; |
198 | } |
199 | |
200 | /** |
201 | * snd_soc_dai_set_fmt - configure DAI hardware audio format. |
202 | * @dai: DAI |
203 | * @fmt: SND_SOC_DAIFMT_* format value. |
204 | * |
205 | * Configures the DAI hardware format and clocking. |
206 | */ |
207 | int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
208 | { |
209 | int ret = -ENOTSUPP; |
210 | |
211 | if (dai->driver->ops && dai->driver->ops->set_fmt) |
212 | ret = dai->driver->ops->set_fmt(dai, fmt); |
213 | |
214 | return soc_dai_ret(dai, ret); |
215 | } |
216 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); |
217 | |
218 | /** |
219 | * snd_soc_xlate_tdm_slot_mask - generate tx/rx slot mask. |
220 | * @slots: Number of slots in use. |
221 | * @tx_mask: bitmask representing active TX slots. |
222 | * @rx_mask: bitmask representing active RX slots. |
223 | * |
224 | * Generates the TDM tx and rx slot default masks for DAI. |
225 | */ |
226 | static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, |
227 | unsigned int *tx_mask, |
228 | unsigned int *rx_mask) |
229 | { |
230 | if (*tx_mask || *rx_mask) |
231 | return 0; |
232 | |
233 | if (!slots) |
234 | return -EINVAL; |
235 | |
236 | *tx_mask = (1 << slots) - 1; |
237 | *rx_mask = (1 << slots) - 1; |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | /** |
243 | * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation |
244 | * @dai: The DAI to configure |
245 | * @tx_mask: bitmask representing active TX slots. |
246 | * @rx_mask: bitmask representing active RX slots. |
247 | * @slots: Number of slots in use. |
248 | * @slot_width: Width in bits for each slot. |
249 | * |
250 | * This function configures the specified DAI for TDM operation. @slot contains |
251 | * the total number of slots of the TDM stream and @slot_with the width of each |
252 | * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the |
253 | * active slots of the TDM stream for the specified DAI, i.e. which slots the |
254 | * DAI should write to or read from. If a bit is set the corresponding slot is |
255 | * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to |
256 | * the first slot, bit 1 to the second slot and so on. The first active slot |
257 | * maps to the first channel of the DAI, the second active slot to the second |
258 | * channel and so on. |
259 | * |
260 | * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, |
261 | * @rx_mask and @slot_width will be ignored. |
262 | * |
263 | * Returns 0 on success, a negative error code otherwise. |
264 | */ |
265 | int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, |
266 | unsigned int tx_mask, unsigned int rx_mask, |
267 | int slots, int slot_width) |
268 | { |
269 | int ret = -ENOTSUPP; |
270 | int stream; |
271 | unsigned int *tdm_mask[] = { |
272 | &tx_mask, |
273 | &rx_mask, |
274 | }; |
275 | |
276 | if (dai->driver->ops && |
277 | dai->driver->ops->xlate_tdm_slot_mask) |
278 | dai->driver->ops->xlate_tdm_slot_mask(slots, |
279 | &tx_mask, &rx_mask); |
280 | else |
281 | snd_soc_xlate_tdm_slot_mask(slots, tx_mask: &tx_mask, rx_mask: &rx_mask); |
282 | |
283 | for_each_pcm_streams(stream) |
284 | snd_soc_dai_tdm_mask_set(dai, stream, tdm_mask: *tdm_mask[stream]); |
285 | |
286 | if (dai->driver->ops && |
287 | dai->driver->ops->set_tdm_slot) |
288 | ret = dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, |
289 | slots, slot_width); |
290 | return soc_dai_ret(dai, ret); |
291 | } |
292 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); |
293 | |
294 | /** |
295 | * snd_soc_dai_set_channel_map - configure DAI audio channel map |
296 | * @dai: DAI |
297 | * @tx_num: how many TX channels |
298 | * @tx_slot: pointer to an array which imply the TX slot number channel |
299 | * 0~num-1 uses |
300 | * @rx_num: how many RX channels |
301 | * @rx_slot: pointer to an array which imply the RX slot number channel |
302 | * 0~num-1 uses |
303 | * |
304 | * configure the relationship between channel number and TDM slot number. |
305 | */ |
306 | int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, |
307 | unsigned int tx_num, unsigned int *tx_slot, |
308 | unsigned int rx_num, unsigned int *rx_slot) |
309 | { |
310 | int ret = -ENOTSUPP; |
311 | |
312 | if (dai->driver->ops && |
313 | dai->driver->ops->set_channel_map) |
314 | ret = dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, |
315 | rx_num, rx_slot); |
316 | return soc_dai_ret(dai, ret); |
317 | } |
318 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); |
319 | |
320 | /** |
321 | * snd_soc_dai_get_channel_map - Get DAI audio channel map |
322 | * @dai: DAI |
323 | * @tx_num: how many TX channels |
324 | * @tx_slot: pointer to an array which imply the TX slot number channel |
325 | * 0~num-1 uses |
326 | * @rx_num: how many RX channels |
327 | * @rx_slot: pointer to an array which imply the RX slot number channel |
328 | * 0~num-1 uses |
329 | */ |
330 | int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, |
331 | unsigned int *tx_num, unsigned int *tx_slot, |
332 | unsigned int *rx_num, unsigned int *rx_slot) |
333 | { |
334 | int ret = -ENOTSUPP; |
335 | |
336 | if (dai->driver->ops && |
337 | dai->driver->ops->get_channel_map) |
338 | ret = dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, |
339 | rx_num, rx_slot); |
340 | return soc_dai_ret(dai, ret); |
341 | } |
342 | EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); |
343 | |
344 | /** |
345 | * snd_soc_dai_set_tristate - configure DAI system or master clock. |
346 | * @dai: DAI |
347 | * @tristate: tristate enable |
348 | * |
349 | * Tristates the DAI so that others can use it. |
350 | */ |
351 | int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) |
352 | { |
353 | int ret = -EINVAL; |
354 | |
355 | if (dai->driver->ops && |
356 | dai->driver->ops->set_tristate) |
357 | ret = dai->driver->ops->set_tristate(dai, tristate); |
358 | |
359 | return soc_dai_ret(dai, ret); |
360 | } |
361 | EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); |
362 | |
363 | /** |
364 | * snd_soc_dai_digital_mute - configure DAI system or master clock. |
365 | * @dai: DAI |
366 | * @mute: mute enable |
367 | * @direction: stream to mute |
368 | * |
369 | * Mutes the DAI DAC. |
370 | */ |
371 | int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, |
372 | int direction) |
373 | { |
374 | int ret = -ENOTSUPP; |
375 | |
376 | /* |
377 | * ignore if direction was CAPTURE |
378 | * and it had .no_capture_mute flag |
379 | */ |
380 | if (dai->driver->ops && |
381 | dai->driver->ops->mute_stream && |
382 | (direction == SNDRV_PCM_STREAM_PLAYBACK || |
383 | !dai->driver->ops->no_capture_mute)) |
384 | ret = dai->driver->ops->mute_stream(dai, mute, direction); |
385 | |
386 | return soc_dai_ret(dai, ret); |
387 | } |
388 | EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); |
389 | |
390 | int snd_soc_dai_hw_params(struct snd_soc_dai *dai, |
391 | struct snd_pcm_substream *substream, |
392 | struct snd_pcm_hw_params *params) |
393 | { |
394 | int ret = 0; |
395 | |
396 | if (dai->driver->ops && |
397 | dai->driver->ops->hw_params) |
398 | ret = dai->driver->ops->hw_params(substream, params, dai); |
399 | |
400 | /* mark substream if succeeded */ |
401 | if (ret == 0) |
402 | soc_dai_mark_push(dai, substream, hw_params); |
403 | |
404 | return soc_dai_ret(dai, ret); |
405 | } |
406 | |
407 | void snd_soc_dai_hw_free(struct snd_soc_dai *dai, |
408 | struct snd_pcm_substream *substream, |
409 | int rollback) |
410 | { |
411 | if (rollback && !soc_dai_mark_match(dai, substream, hw_params)) |
412 | return; |
413 | |
414 | if (dai->driver->ops && |
415 | dai->driver->ops->hw_free) |
416 | dai->driver->ops->hw_free(substream, dai); |
417 | |
418 | /* remove marked substream */ |
419 | soc_dai_mark_pop(dai, substream, hw_params); |
420 | } |
421 | |
422 | int snd_soc_dai_startup(struct snd_soc_dai *dai, |
423 | struct snd_pcm_substream *substream) |
424 | { |
425 | int ret = 0; |
426 | |
427 | if (!snd_soc_dai_stream_valid(dai, stream: substream->stream)) |
428 | return 0; |
429 | |
430 | if (dai->driver->ops && |
431 | dai->driver->ops->startup) |
432 | ret = dai->driver->ops->startup(substream, dai); |
433 | |
434 | /* mark substream if succeeded */ |
435 | if (ret == 0) |
436 | soc_dai_mark_push(dai, substream, startup); |
437 | |
438 | return soc_dai_ret(dai, ret); |
439 | } |
440 | |
441 | void snd_soc_dai_shutdown(struct snd_soc_dai *dai, |
442 | struct snd_pcm_substream *substream, |
443 | int rollback) |
444 | { |
445 | if (!snd_soc_dai_stream_valid(dai, stream: substream->stream)) |
446 | return; |
447 | |
448 | if (rollback && !soc_dai_mark_match(dai, substream, startup)) |
449 | return; |
450 | |
451 | if (dai->driver->ops && |
452 | dai->driver->ops->shutdown) |
453 | dai->driver->ops->shutdown(substream, dai); |
454 | |
455 | /* remove marked substream */ |
456 | soc_dai_mark_pop(dai, substream, startup); |
457 | } |
458 | |
459 | int snd_soc_dai_compress_new(struct snd_soc_dai *dai, |
460 | struct snd_soc_pcm_runtime *rtd, int num) |
461 | { |
462 | int ret = -ENOTSUPP; |
463 | if (dai->driver->ops && |
464 | dai->driver->ops->compress_new) |
465 | ret = dai->driver->ops->compress_new(rtd, num); |
466 | return soc_dai_ret(dai, ret); |
467 | } |
468 | |
469 | /* |
470 | * snd_soc_dai_stream_valid() - check if a DAI supports the given stream |
471 | * |
472 | * Returns true if the DAI supports the indicated stream type. |
473 | */ |
474 | bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) |
475 | { |
476 | struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, stream: dir); |
477 | |
478 | /* If the codec specifies any channels at all, it supports the stream */ |
479 | return stream->channels_min; |
480 | } |
481 | |
482 | /* |
483 | * snd_soc_dai_link_set_capabilities() - set dai_link properties based on its DAIs |
484 | */ |
485 | void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link *dai_link) |
486 | { |
487 | bool supported[SNDRV_PCM_STREAM_LAST + 1]; |
488 | int direction; |
489 | |
490 | for_each_pcm_streams(direction) { |
491 | struct snd_soc_dai_link_component *cpu; |
492 | struct snd_soc_dai_link_component *codec; |
493 | struct snd_soc_dai *dai; |
494 | bool supported_cpu = false; |
495 | bool supported_codec = false; |
496 | int i; |
497 | |
498 | for_each_link_cpus(dai_link, i, cpu) { |
499 | dai = snd_soc_find_dai_with_mutex(dlc: cpu); |
500 | if (dai && snd_soc_dai_stream_valid(dai, dir: direction)) { |
501 | supported_cpu = true; |
502 | break; |
503 | } |
504 | } |
505 | for_each_link_codecs(dai_link, i, codec) { |
506 | dai = snd_soc_find_dai_with_mutex(dlc: codec); |
507 | if (dai && snd_soc_dai_stream_valid(dai, dir: direction)) { |
508 | supported_codec = true; |
509 | break; |
510 | } |
511 | } |
512 | supported[direction] = supported_cpu && supported_codec; |
513 | } |
514 | |
515 | dai_link->dpcm_playback = supported[SNDRV_PCM_STREAM_PLAYBACK]; |
516 | dai_link->dpcm_capture = supported[SNDRV_PCM_STREAM_CAPTURE]; |
517 | } |
518 | EXPORT_SYMBOL_GPL(snd_soc_dai_link_set_capabilities); |
519 | |
520 | void snd_soc_dai_action(struct snd_soc_dai *dai, |
521 | int stream, int action) |
522 | { |
523 | /* see snd_soc_dai_stream_active() */ |
524 | dai->stream[stream].active += action; |
525 | |
526 | /* see snd_soc_component_active() */ |
527 | dai->component->active += action; |
528 | } |
529 | EXPORT_SYMBOL_GPL(snd_soc_dai_action); |
530 | |
531 | int snd_soc_dai_active(struct snd_soc_dai *dai) |
532 | { |
533 | int stream, active; |
534 | |
535 | active = 0; |
536 | for_each_pcm_streams(stream) |
537 | active += dai->stream[stream].active; |
538 | |
539 | return active; |
540 | } |
541 | EXPORT_SYMBOL_GPL(snd_soc_dai_active); |
542 | |
543 | int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order) |
544 | { |
545 | struct snd_soc_dai *dai; |
546 | int i; |
547 | |
548 | for_each_rtd_dais(rtd, i, dai) { |
549 | if (dai->probed) |
550 | continue; |
551 | |
552 | if (dai->driver->ops) { |
553 | if (dai->driver->ops->probe_order != order) |
554 | continue; |
555 | |
556 | if (dai->driver->ops->probe) { |
557 | int ret = dai->driver->ops->probe(dai); |
558 | |
559 | if (ret < 0) |
560 | return soc_dai_ret(dai, ret); |
561 | } |
562 | } |
563 | dai->probed = 1; |
564 | } |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order) |
570 | { |
571 | struct snd_soc_dai *dai; |
572 | int i, r, ret = 0; |
573 | |
574 | for_each_rtd_dais(rtd, i, dai) { |
575 | if (!dai->probed) |
576 | continue; |
577 | |
578 | if (dai->driver->ops) { |
579 | if (dai->driver->ops->remove_order != order) |
580 | continue; |
581 | |
582 | if (dai->driver->ops->remove) { |
583 | r = dai->driver->ops->remove(dai); |
584 | if (r < 0) |
585 | ret = r; /* use last error */ |
586 | } |
587 | } |
588 | dai->probed = 0; |
589 | } |
590 | |
591 | return ret; |
592 | } |
593 | |
594 | int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) |
595 | { |
596 | struct snd_soc_dai *dai; |
597 | int i; |
598 | |
599 | for_each_rtd_dais(rtd, i, dai) { |
600 | if (dai->driver->ops && |
601 | dai->driver->ops->pcm_new) { |
602 | int ret = dai->driver->ops->pcm_new(rtd, dai); |
603 | if (ret < 0) |
604 | return soc_dai_ret(dai, ret); |
605 | } |
606 | } |
607 | |
608 | return 0; |
609 | } |
610 | |
611 | int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) |
612 | { |
613 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
614 | struct snd_soc_dai *dai; |
615 | int i, ret; |
616 | |
617 | for_each_rtd_dais(rtd, i, dai) { |
618 | if (!snd_soc_dai_stream_valid(dai, dir: substream->stream)) |
619 | continue; |
620 | if (dai->driver->ops && |
621 | dai->driver->ops->prepare) { |
622 | ret = dai->driver->ops->prepare(substream, dai); |
623 | if (ret < 0) |
624 | return soc_dai_ret(dai, ret); |
625 | } |
626 | } |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | static int soc_dai_trigger(struct snd_soc_dai *dai, |
632 | struct snd_pcm_substream *substream, int cmd) |
633 | { |
634 | int ret = 0; |
635 | |
636 | if (!snd_soc_dai_stream_valid(dai, dir: substream->stream)) |
637 | return 0; |
638 | |
639 | if (dai->driver->ops && |
640 | dai->driver->ops->trigger) |
641 | ret = dai->driver->ops->trigger(substream, cmd, dai); |
642 | |
643 | return soc_dai_ret(dai, ret); |
644 | } |
645 | |
646 | int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, |
647 | int cmd, int rollback) |
648 | { |
649 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
650 | struct snd_soc_dai *dai; |
651 | int i, r, ret = 0; |
652 | |
653 | switch (cmd) { |
654 | case SNDRV_PCM_TRIGGER_START: |
655 | case SNDRV_PCM_TRIGGER_RESUME: |
656 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
657 | for_each_rtd_dais(rtd, i, dai) { |
658 | ret = soc_dai_trigger(dai, substream, cmd); |
659 | if (ret < 0) |
660 | break; |
661 | |
662 | if (dai->driver->ops && dai->driver->ops->mute_unmute_on_trigger) |
663 | snd_soc_dai_digital_mute(dai, 0, substream->stream); |
664 | |
665 | soc_dai_mark_push(dai, substream, trigger); |
666 | } |
667 | break; |
668 | case SNDRV_PCM_TRIGGER_STOP: |
669 | case SNDRV_PCM_TRIGGER_SUSPEND: |
670 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
671 | for_each_rtd_dais(rtd, i, dai) { |
672 | if (rollback && !soc_dai_mark_match(dai, substream, trigger)) |
673 | continue; |
674 | |
675 | if (dai->driver->ops && dai->driver->ops->mute_unmute_on_trigger) |
676 | snd_soc_dai_digital_mute(dai, 1, substream->stream); |
677 | |
678 | r = soc_dai_trigger(dai, substream, cmd); |
679 | if (r < 0) |
680 | ret = r; /* use last ret */ |
681 | soc_dai_mark_pop(dai, substream, trigger); |
682 | } |
683 | } |
684 | |
685 | return ret; |
686 | } |
687 | |
688 | int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, |
689 | int cmd) |
690 | { |
691 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
692 | struct snd_soc_dai *dai; |
693 | int i, ret; |
694 | |
695 | for_each_rtd_dais(rtd, i, dai) { |
696 | if (dai->driver->ops && |
697 | dai->driver->ops->bespoke_trigger) { |
698 | ret = dai->driver->ops->bespoke_trigger(substream, |
699 | cmd, dai); |
700 | if (ret < 0) |
701 | return soc_dai_ret(dai, ret); |
702 | } |
703 | } |
704 | |
705 | return 0; |
706 | } |
707 | |
708 | void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, |
709 | snd_pcm_sframes_t *cpu_delay, |
710 | snd_pcm_sframes_t *codec_delay) |
711 | { |
712 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
713 | struct snd_soc_dai *dai; |
714 | int i; |
715 | |
716 | /* |
717 | * We're looking for the delay through the full audio path so it needs to |
718 | * be the maximum of the DAIs doing transmit and the maximum of the DAIs |
719 | * doing receive (ie, all CPUs and all CODECs) rather than just the maximum |
720 | * of all DAIs. |
721 | */ |
722 | |
723 | /* for CPU */ |
724 | for_each_rtd_cpu_dais(rtd, i, dai) |
725 | if (dai->driver->ops && |
726 | dai->driver->ops->delay) |
727 | *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai)); |
728 | |
729 | /* for Codec */ |
730 | for_each_rtd_codec_dais(rtd, i, dai) |
731 | if (dai->driver->ops && |
732 | dai->driver->ops->delay) |
733 | *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai)); |
734 | } |
735 | |
736 | int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, |
737 | struct snd_compr_stream *cstream) |
738 | { |
739 | int ret = 0; |
740 | |
741 | if (dai->driver->cops && |
742 | dai->driver->cops->startup) |
743 | ret = dai->driver->cops->startup(cstream, dai); |
744 | |
745 | /* mark cstream if succeeded */ |
746 | if (ret == 0) |
747 | soc_dai_mark_push(dai, cstream, compr_startup); |
748 | |
749 | return soc_dai_ret(dai, ret); |
750 | } |
751 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup); |
752 | |
753 | void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai, |
754 | struct snd_compr_stream *cstream, |
755 | int rollback) |
756 | { |
757 | if (rollback && !soc_dai_mark_match(dai, cstream, compr_startup)) |
758 | return; |
759 | |
760 | if (dai->driver->cops && |
761 | dai->driver->cops->shutdown) |
762 | dai->driver->cops->shutdown(cstream, dai); |
763 | |
764 | /* remove marked cstream */ |
765 | soc_dai_mark_pop(dai, cstream, compr_startup); |
766 | } |
767 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown); |
768 | |
769 | int snd_soc_dai_compr_trigger(struct snd_soc_dai *dai, |
770 | struct snd_compr_stream *cstream, int cmd) |
771 | { |
772 | int ret = 0; |
773 | |
774 | if (dai->driver->cops && |
775 | dai->driver->cops->trigger) |
776 | ret = dai->driver->cops->trigger(cstream, cmd, dai); |
777 | |
778 | return soc_dai_ret(dai, ret); |
779 | } |
780 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_trigger); |
781 | |
782 | int snd_soc_dai_compr_set_params(struct snd_soc_dai *dai, |
783 | struct snd_compr_stream *cstream, |
784 | struct snd_compr_params *params) |
785 | { |
786 | int ret = 0; |
787 | |
788 | if (dai->driver->cops && |
789 | dai->driver->cops->set_params) |
790 | ret = dai->driver->cops->set_params(cstream, params, dai); |
791 | |
792 | return soc_dai_ret(dai, ret); |
793 | } |
794 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_params); |
795 | |
796 | int snd_soc_dai_compr_get_params(struct snd_soc_dai *dai, |
797 | struct snd_compr_stream *cstream, |
798 | struct snd_codec *params) |
799 | { |
800 | int ret = 0; |
801 | |
802 | if (dai->driver->cops && |
803 | dai->driver->cops->get_params) |
804 | ret = dai->driver->cops->get_params(cstream, params, dai); |
805 | |
806 | return soc_dai_ret(dai, ret); |
807 | } |
808 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_params); |
809 | |
810 | int snd_soc_dai_compr_ack(struct snd_soc_dai *dai, |
811 | struct snd_compr_stream *cstream, |
812 | size_t bytes) |
813 | { |
814 | int ret = 0; |
815 | |
816 | if (dai->driver->cops && |
817 | dai->driver->cops->ack) |
818 | ret = dai->driver->cops->ack(cstream, bytes, dai); |
819 | |
820 | return soc_dai_ret(dai, ret); |
821 | } |
822 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_ack); |
823 | |
824 | int snd_soc_dai_compr_pointer(struct snd_soc_dai *dai, |
825 | struct snd_compr_stream *cstream, |
826 | struct snd_compr_tstamp *tstamp) |
827 | { |
828 | int ret = 0; |
829 | |
830 | if (dai->driver->cops && |
831 | dai->driver->cops->pointer) |
832 | ret = dai->driver->cops->pointer(cstream, tstamp, dai); |
833 | |
834 | return soc_dai_ret(dai, ret); |
835 | } |
836 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_pointer); |
837 | |
838 | int snd_soc_dai_compr_set_metadata(struct snd_soc_dai *dai, |
839 | struct snd_compr_stream *cstream, |
840 | struct snd_compr_metadata *metadata) |
841 | { |
842 | int ret = 0; |
843 | |
844 | if (dai->driver->cops && |
845 | dai->driver->cops->set_metadata) |
846 | ret = dai->driver->cops->set_metadata(cstream, metadata, dai); |
847 | |
848 | return soc_dai_ret(dai, ret); |
849 | } |
850 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_metadata); |
851 | |
852 | int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, |
853 | struct snd_compr_stream *cstream, |
854 | struct snd_compr_metadata *metadata) |
855 | { |
856 | int ret = 0; |
857 | |
858 | if (dai->driver->cops && |
859 | dai->driver->cops->get_metadata) |
860 | ret = dai->driver->cops->get_metadata(cstream, metadata, dai); |
861 | |
862 | return soc_dai_ret(dai, ret); |
863 | } |
864 | EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata); |
865 | |