1 // SPDX-License-Identifier: GPL-2.0
5 // Copyright (C) 2019 Renesas Electronics Corp.
6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
10 #include <sound/soc-dai.h>
11 #include <sound/soc-link.h>
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
)
17 /* Positive, Zero values are not errors */
21 /* Negative values might be errors */
28 "ASoC: error at %s on %s: %d\n",
29 func
, dai
->name
, ret
);
36 * We might want to check substream by using list.
37 * In such case, we can update these macros.
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)
44 * snd_soc_dai_set_sysclk - configure DAI system or master clock.
46 * @clk_id: DAI specific clock ID
47 * @freq: new clock frequency in Hz
48 * @dir: new clock direction - input/output.
50 * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
52 int snd_soc_dai_set_sysclk(struct snd_soc_dai
*dai
, int clk_id
,
53 unsigned int freq
, int dir
)
57 if (dai
->driver
->ops
&&
58 dai
->driver
->ops
->set_sysclk
)
59 ret
= dai
->driver
->ops
->set_sysclk(dai
, clk_id
, freq
, dir
);
61 ret
= snd_soc_component_set_sysclk(dai
->component
, clk_id
, 0,
64 return soc_dai_ret(dai
, ret
);
66 EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk
);
69 * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
71 * @div_id: DAI specific clock divider ID
72 * @div: new clock divisor.
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.
78 int snd_soc_dai_set_clkdiv(struct snd_soc_dai
*dai
,
83 if (dai
->driver
->ops
&&
84 dai
->driver
->ops
->set_clkdiv
)
85 ret
= dai
->driver
->ops
->set_clkdiv(dai
, div_id
, div
);
87 return soc_dai_ret(dai
, ret
);
89 EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv
);
92 * snd_soc_dai_set_pll - configure DAI PLL.
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
99 * Configures and enables PLL to generate output clock based on input clock.
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
)
106 if (dai
->driver
->ops
&&
107 dai
->driver
->ops
->set_pll
)
108 ret
= dai
->driver
->ops
->set_pll(dai
, pll_id
, source
,
111 ret
= snd_soc_component_set_pll(dai
->component
, pll_id
, source
,
114 return soc_dai_ret(dai
, ret
);
116 EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll
);
119 * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio.
121 * @ratio: Ratio of BCLK to Sample rate.
123 * Configures the DAI for a preset BCLK to sample rate ratio.
125 int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai
*dai
, unsigned int ratio
)
129 if (dai
->driver
->ops
&&
130 dai
->driver
->ops
->set_bclk_ratio
)
131 ret
= dai
->driver
->ops
->set_bclk_ratio(dai
, ratio
);
133 return soc_dai_ret(dai
, ret
);
135 EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio
);
138 * snd_soc_dai_set_fmt - configure DAI hardware audio format.
140 * @fmt: SND_SOC_DAIFMT_* format value.
142 * Configures the DAI hardware format and clocking.
144 int snd_soc_dai_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
148 if (dai
->driver
->ops
&&
149 dai
->driver
->ops
->set_fmt
)
150 ret
= dai
->driver
->ops
->set_fmt(dai
, fmt
);
152 return soc_dai_ret(dai
, ret
);
154 EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt
);
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.
162 * Generates the TDM tx and rx slot default masks for DAI.
164 static int snd_soc_xlate_tdm_slot_mask(unsigned int slots
,
165 unsigned int *tx_mask
,
166 unsigned int *rx_mask
)
168 if (*tx_mask
|| *rx_mask
)
174 *tx_mask
= (1 << slots
) - 1;
175 *rx_mask
= (1 << slots
) - 1;
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.
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
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.
201 * Returns 0 on success, a negative error code otherwise.
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
)
209 if (dai
->driver
->ops
&&
210 dai
->driver
->ops
->xlate_tdm_slot_mask
)
211 dai
->driver
->ops
->xlate_tdm_slot_mask(slots
,
214 snd_soc_xlate_tdm_slot_mask(slots
, &tx_mask
, &rx_mask
);
216 dai
->tx_mask
= tx_mask
;
217 dai
->rx_mask
= rx_mask
;
219 if (dai
->driver
->ops
&&
220 dai
->driver
->ops
->set_tdm_slot
)
221 ret
= dai
->driver
->ops
->set_tdm_slot(dai
, tx_mask
, rx_mask
,
223 return soc_dai_ret(dai
, ret
);
225 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot
);
228 * snd_soc_dai_set_channel_map - configure DAI audio channel map
230 * @tx_num: how many TX channels
231 * @tx_slot: pointer to an array which imply the TX slot number channel
233 * @rx_num: how many RX channels
234 * @rx_slot: pointer to an array which imply the RX slot number channel
237 * configure the relationship between channel number and TDM slot number.
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
)
245 if (dai
->driver
->ops
&&
246 dai
->driver
->ops
->set_channel_map
)
247 ret
= dai
->driver
->ops
->set_channel_map(dai
, tx_num
, tx_slot
,
249 return soc_dai_ret(dai
, ret
);
251 EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map
);
254 * snd_soc_dai_get_channel_map - Get DAI audio channel map
256 * @tx_num: how many TX channels
257 * @tx_slot: pointer to an array which imply the TX slot number channel
259 * @rx_num: how many RX channels
260 * @rx_slot: pointer to an array which imply the RX slot number channel
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
)
269 if (dai
->driver
->ops
&&
270 dai
->driver
->ops
->get_channel_map
)
271 ret
= dai
->driver
->ops
->get_channel_map(dai
, tx_num
, tx_slot
,
273 return soc_dai_ret(dai
, ret
);
275 EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map
);
278 * snd_soc_dai_set_tristate - configure DAI system or master clock.
280 * @tristate: tristate enable
282 * Tristates the DAI so that others can use it.
284 int snd_soc_dai_set_tristate(struct snd_soc_dai
*dai
, int tristate
)
288 if (dai
->driver
->ops
&&
289 dai
->driver
->ops
->set_tristate
)
290 ret
= dai
->driver
->ops
->set_tristate(dai
, tristate
);
292 return soc_dai_ret(dai
, ret
);
294 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate
);
297 * snd_soc_dai_digital_mute - configure DAI system or master clock.
300 * @direction: stream to mute
304 int snd_soc_dai_digital_mute(struct snd_soc_dai
*dai
, int mute
,
310 * ignore if direction was CAPTURE
311 * and it had .no_capture_mute flag
313 if (dai
->driver
->ops
&&
314 dai
->driver
->ops
->mute_stream
&&
315 (direction
== SNDRV_PCM_STREAM_PLAYBACK
||
316 !dai
->driver
->ops
->no_capture_mute
))
317 ret
= dai
->driver
->ops
->mute_stream(dai
, mute
, direction
);
319 return soc_dai_ret(dai
, ret
);
321 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute
);
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
)
327 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
330 /* perform any topology hw_params fixups before DAI */
331 ret
= snd_soc_link_be_hw_params_fixup(rtd
, params
);
335 if (dai
->driver
->ops
&&
336 dai
->driver
->ops
->hw_params
)
337 ret
= dai
->driver
->ops
->hw_params(substream
, params
, dai
);
339 /* mark substream if succeeded */
341 soc_dai_mark_push(dai
, substream
, hw_params
);
343 return soc_dai_ret(dai
, ret
);
346 void snd_soc_dai_hw_free(struct snd_soc_dai
*dai
,
347 struct snd_pcm_substream
*substream
,
350 if (rollback
&& !soc_dai_mark_match(dai
, substream
, hw_params
))
353 if (dai
->driver
->ops
&&
354 dai
->driver
->ops
->hw_free
)
355 dai
->driver
->ops
->hw_free(substream
, dai
);
357 /* remove marked substream */
358 soc_dai_mark_pop(dai
, substream
, hw_params
);
361 int snd_soc_dai_startup(struct snd_soc_dai
*dai
,
362 struct snd_pcm_substream
*substream
)
366 if (dai
->driver
->ops
&&
367 dai
->driver
->ops
->startup
)
368 ret
= dai
->driver
->ops
->startup(substream
, dai
);
370 /* mark substream if succeeded */
372 soc_dai_mark_push(dai
, substream
, startup
);
374 return soc_dai_ret(dai
, ret
);
377 void snd_soc_dai_shutdown(struct snd_soc_dai
*dai
,
378 struct snd_pcm_substream
*substream
,
381 if (rollback
&& !soc_dai_mark_match(dai
, substream
, startup
))
384 if (dai
->driver
->ops
&&
385 dai
->driver
->ops
->shutdown
)
386 dai
->driver
->ops
->shutdown(substream
, dai
);
388 /* remove marked substream */
389 soc_dai_mark_pop(dai
, substream
, startup
);
392 snd_pcm_sframes_t
snd_soc_dai_delay(struct snd_soc_dai
*dai
,
393 struct snd_pcm_substream
*substream
)
397 if (dai
->driver
->ops
&&
398 dai
->driver
->ops
->delay
)
399 delay
= dai
->driver
->ops
->delay(substream
, dai
);
404 int snd_soc_dai_compress_new(struct snd_soc_dai
*dai
,
405 struct snd_soc_pcm_runtime
*rtd
, int num
)
408 if (dai
->driver
->compress_new
)
409 ret
= dai
->driver
->compress_new(rtd
, num
);
410 return soc_dai_ret(dai
, ret
);
414 * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
416 * Returns true if the DAI supports the indicated stream type.
418 bool snd_soc_dai_stream_valid(struct snd_soc_dai
*dai
, int dir
)
420 struct snd_soc_pcm_stream
*stream
= snd_soc_dai_get_pcm_stream(dai
, dir
);
422 /* If the codec specifies any channels at all, it supports the stream */
423 return stream
->channels_min
;
427 * snd_soc_dai_link_set_capabilities() - set dai_link properties based on its DAIs
429 void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link
*dai_link
)
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];
436 bool supported_codec
;
440 for_each_pcm_streams(direction
) {
441 supported_cpu
= false;
442 supported_codec
= false;
444 for_each_link_cpus(dai_link
, i
, cpu
) {
445 dai
= snd_soc_find_dai_with_mutex(cpu
);
446 if (dai
&& snd_soc_dai_stream_valid(dai
, direction
)) {
447 supported_cpu
= true;
451 for_each_link_codecs(dai_link
, i
, codec
) {
452 dai
= snd_soc_find_dai_with_mutex(codec
);
453 if (dai
&& snd_soc_dai_stream_valid(dai
, direction
)) {
454 supported_codec
= true;
458 supported
[direction
] = supported_cpu
&& supported_codec
;
461 dai_link
->dpcm_playback
= supported
[SNDRV_PCM_STREAM_PLAYBACK
];
462 dai_link
->dpcm_capture
= supported
[SNDRV_PCM_STREAM_CAPTURE
];
464 EXPORT_SYMBOL_GPL(snd_soc_dai_link_set_capabilities
);
466 void snd_soc_dai_action(struct snd_soc_dai
*dai
,
467 int stream
, int action
)
469 /* see snd_soc_dai_stream_active() */
470 dai
->stream_active
[stream
] += action
;
472 /* see snd_soc_component_active() */
473 dai
->component
->active
+= action
;
475 EXPORT_SYMBOL_GPL(snd_soc_dai_action
);
477 int snd_soc_dai_active(struct snd_soc_dai
*dai
)
482 for_each_pcm_streams(stream
)
483 active
+= dai
->stream_active
[stream
];
487 EXPORT_SYMBOL_GPL(snd_soc_dai_active
);
489 int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime
*rtd
, int order
)
491 struct snd_soc_dai
*dai
;
494 for_each_rtd_dais(rtd
, i
, dai
) {
495 if (dai
->driver
->probe_order
!= order
)
498 if (dai
->driver
->probe
) {
499 int ret
= dai
->driver
->probe(dai
);
502 return soc_dai_ret(dai
, ret
);
511 int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime
*rtd
, int order
)
513 struct snd_soc_dai
*dai
;
516 for_each_rtd_dais(rtd
, i
, dai
) {
517 if (dai
->driver
->remove_order
!= order
)
521 dai
->driver
->remove
) {
522 r
= dai
->driver
->remove(dai
);
524 ret
= r
; /* use last error */
533 int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime
*rtd
)
535 struct snd_soc_dai
*dai
;
538 for_each_rtd_dais(rtd
, i
, dai
) {
539 if (dai
->driver
->pcm_new
) {
540 ret
= dai
->driver
->pcm_new(rtd
, dai
);
542 return soc_dai_ret(dai
, ret
);
549 int snd_soc_pcm_dai_prepare(struct snd_pcm_substream
*substream
)
551 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
552 struct snd_soc_dai
*dai
;
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
);
560 return soc_dai_ret(dai
, ret
);
567 static int soc_dai_trigger(struct snd_soc_dai
*dai
,
568 struct snd_pcm_substream
*substream
, int cmd
)
572 if (dai
->driver
->ops
&&
573 dai
->driver
->ops
->trigger
)
574 ret
= dai
->driver
->ops
->trigger(substream
, cmd
, dai
);
576 return soc_dai_ret(dai
, ret
);
579 int snd_soc_pcm_dai_trigger(struct snd_pcm_substream
*substream
,
580 int cmd
, int rollback
)
582 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
583 struct snd_soc_dai
*dai
;
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
);
594 soc_dai_mark_push(dai
, substream
, trigger
);
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
))
604 r
= soc_dai_trigger(dai
, substream
, cmd
);
606 ret
= r
; /* use last ret */
607 soc_dai_mark_pop(dai
, substream
, trigger
);
614 int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream
*substream
,
617 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
618 struct snd_soc_dai
*dai
;
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
,
627 return soc_dai_ret(dai
, ret
);
634 int snd_soc_dai_compr_startup(struct snd_soc_dai
*dai
,
635 struct snd_compr_stream
*cstream
)
639 if (dai
->driver
->cops
&&
640 dai
->driver
->cops
->startup
)
641 ret
= dai
->driver
->cops
->startup(cstream
, dai
);
643 /* mark cstream if succeeded */
645 soc_dai_mark_push(dai
, cstream
, compr_startup
);
647 return soc_dai_ret(dai
, ret
);
649 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup
);
651 void snd_soc_dai_compr_shutdown(struct snd_soc_dai
*dai
,
652 struct snd_compr_stream
*cstream
,
655 if (rollback
&& !soc_dai_mark_match(dai
, cstream
, compr_startup
))
658 if (dai
->driver
->cops
&&
659 dai
->driver
->cops
->shutdown
)
660 dai
->driver
->cops
->shutdown(cstream
, dai
);
662 /* remove marked cstream */
663 soc_dai_mark_pop(dai
, cstream
, compr_startup
);
665 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown
);
667 int snd_soc_dai_compr_trigger(struct snd_soc_dai
*dai
,
668 struct snd_compr_stream
*cstream
, int cmd
)
672 if (dai
->driver
->cops
&&
673 dai
->driver
->cops
->trigger
)
674 ret
= dai
->driver
->cops
->trigger(cstream
, cmd
, dai
);
676 return soc_dai_ret(dai
, ret
);
678 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_trigger
);
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
)
686 if (dai
->driver
->cops
&&
687 dai
->driver
->cops
->set_params
)
688 ret
= dai
->driver
->cops
->set_params(cstream
, params
, dai
);
690 return soc_dai_ret(dai
, ret
);
692 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_params
);
694 int snd_soc_dai_compr_get_params(struct snd_soc_dai
*dai
,
695 struct snd_compr_stream
*cstream
,
696 struct snd_codec
*params
)
700 if (dai
->driver
->cops
&&
701 dai
->driver
->cops
->get_params
)
702 ret
= dai
->driver
->cops
->get_params(cstream
, params
, dai
);
704 return soc_dai_ret(dai
, ret
);
706 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_params
);
708 int snd_soc_dai_compr_ack(struct snd_soc_dai
*dai
,
709 struct snd_compr_stream
*cstream
,
714 if (dai
->driver
->cops
&&
715 dai
->driver
->cops
->ack
)
716 ret
= dai
->driver
->cops
->ack(cstream
, bytes
, dai
);
718 return soc_dai_ret(dai
, ret
);
720 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_ack
);
722 int snd_soc_dai_compr_pointer(struct snd_soc_dai
*dai
,
723 struct snd_compr_stream
*cstream
,
724 struct snd_compr_tstamp
*tstamp
)
728 if (dai
->driver
->cops
&&
729 dai
->driver
->cops
->pointer
)
730 ret
= dai
->driver
->cops
->pointer(cstream
, tstamp
, dai
);
732 return soc_dai_ret(dai
, ret
);
734 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_pointer
);
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
)
742 if (dai
->driver
->cops
&&
743 dai
->driver
->cops
->set_metadata
)
744 ret
= dai
->driver
->cops
->set_metadata(cstream
, metadata
, dai
);
746 return soc_dai_ret(dai
, ret
);
748 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_metadata
);
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
)
756 if (dai
->driver
->cops
&&
757 dai
->driver
->cops
->get_metadata
)
758 ret
= dai
->driver
->cops
->get_metadata(cstream
, metadata
, dai
);
760 return soc_dai_ret(dai
, ret
);
762 EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata
);