xref: /linux/sound/soc/soc-dai.c (revision 52338415)
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 
12 /**
13  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
14  * @dai: DAI
15  * @clk_id: DAI specific clock ID
16  * @freq: new clock frequency in Hz
17  * @dir: new clock direction - input/output.
18  *
19  * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
20  */
21 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
22 			   unsigned int freq, int dir)
23 {
24 	if (dai->driver->ops->set_sysclk)
25 		return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
26 
27 	return snd_soc_component_set_sysclk(dai->component, clk_id, 0,
28 					    freq, dir);
29 }
30 EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
31 
32 /**
33  * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
34  * @dai: DAI
35  * @div_id: DAI specific clock divider ID
36  * @div: new clock divisor.
37  *
38  * Configures the clock dividers. This is used to derive the best DAI bit and
39  * frame clocks from the system or master clock. It's best to set the DAI bit
40  * and frame clocks as low as possible to save system power.
41  */
42 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
43 			   int div_id, int div)
44 {
45 	if (dai->driver->ops->set_clkdiv)
46 		return dai->driver->ops->set_clkdiv(dai, div_id, div);
47 	else
48 		return -EINVAL;
49 }
50 EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
51 
52 /**
53  * snd_soc_dai_set_pll - configure DAI PLL.
54  * @dai: DAI
55  * @pll_id: DAI specific PLL ID
56  * @source: DAI specific source for the PLL
57  * @freq_in: PLL input clock frequency in Hz
58  * @freq_out: requested PLL output clock frequency in Hz
59  *
60  * Configures and enables PLL to generate output clock based on input clock.
61  */
62 int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
63 			unsigned int freq_in, unsigned int freq_out)
64 {
65 	if (dai->driver->ops->set_pll)
66 		return dai->driver->ops->set_pll(dai, pll_id, source,
67 						 freq_in, freq_out);
68 
69 	return snd_soc_component_set_pll(dai->component, pll_id, source,
70 					 freq_in, freq_out);
71 }
72 EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
73 
74 /**
75  * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio.
76  * @dai: DAI
77  * @ratio: Ratio of BCLK to Sample rate.
78  *
79  * Configures the DAI for a preset BCLK to sample rate ratio.
80  */
81 int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
82 {
83 	if (dai->driver->ops->set_bclk_ratio)
84 		return dai->driver->ops->set_bclk_ratio(dai, ratio);
85 	else
86 		return -EINVAL;
87 }
88 EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio);
89 
90 /**
91  * snd_soc_dai_set_fmt - configure DAI hardware audio format.
92  * @dai: DAI
93  * @fmt: SND_SOC_DAIFMT_* format value.
94  *
95  * Configures the DAI hardware format and clocking.
96  */
97 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
98 {
99 	if (dai->driver->ops->set_fmt == NULL)
100 		return -ENOTSUPP;
101 	return dai->driver->ops->set_fmt(dai, fmt);
102 }
103 EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
104 
105 /**
106  * snd_soc_xlate_tdm_slot - generate tx/rx slot mask.
107  * @slots: Number of slots in use.
108  * @tx_mask: bitmask representing active TX slots.
109  * @rx_mask: bitmask representing active RX slots.
110  *
111  * Generates the TDM tx and rx slot default masks for DAI.
112  */
113 static int snd_soc_xlate_tdm_slot_mask(unsigned int slots,
114 				       unsigned int *tx_mask,
115 				       unsigned int *rx_mask)
116 {
117 	if (*tx_mask || *rx_mask)
118 		return 0;
119 
120 	if (!slots)
121 		return -EINVAL;
122 
123 	*tx_mask = (1 << slots) - 1;
124 	*rx_mask = (1 << slots) - 1;
125 
126 	return 0;
127 }
128 
129 /**
130  * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation
131  * @dai: The DAI to configure
132  * @tx_mask: bitmask representing active TX slots.
133  * @rx_mask: bitmask representing active RX slots.
134  * @slots: Number of slots in use.
135  * @slot_width: Width in bits for each slot.
136  *
137  * This function configures the specified DAI for TDM operation. @slot contains
138  * the total number of slots of the TDM stream and @slot_with the width of each
139  * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the
140  * active slots of the TDM stream for the specified DAI, i.e. which slots the
141  * DAI should write to or read from. If a bit is set the corresponding slot is
142  * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to
143  * the first slot, bit 1 to the second slot and so on. The first active slot
144  * maps to the first channel of the DAI, the second active slot to the second
145  * channel and so on.
146  *
147  * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask,
148  * @rx_mask and @slot_width will be ignored.
149  *
150  * Returns 0 on success, a negative error code otherwise.
151  */
152 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
153 			     unsigned int tx_mask, unsigned int rx_mask,
154 			     int slots, int slot_width)
155 {
156 	if (dai->driver->ops->xlate_tdm_slot_mask)
157 		dai->driver->ops->xlate_tdm_slot_mask(slots,
158 						      &tx_mask, &rx_mask);
159 	else
160 		snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
161 
162 	dai->tx_mask = tx_mask;
163 	dai->rx_mask = rx_mask;
164 
165 	if (dai->driver->ops->set_tdm_slot)
166 		return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
167 						      slots, slot_width);
168 	else
169 		return -ENOTSUPP;
170 }
171 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
172 
173 /**
174  * snd_soc_dai_set_channel_map - configure DAI audio channel map
175  * @dai: DAI
176  * @tx_num: how many TX channels
177  * @tx_slot: pointer to an array which imply the TX slot number channel
178  *           0~num-1 uses
179  * @rx_num: how many RX channels
180  * @rx_slot: pointer to an array which imply the RX slot number channel
181  *           0~num-1 uses
182  *
183  * configure the relationship between channel number and TDM slot number.
184  */
185 int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
186 				unsigned int tx_num, unsigned int *tx_slot,
187 				unsigned int rx_num, unsigned int *rx_slot)
188 {
189 	if (dai->driver->ops->set_channel_map)
190 		return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot,
191 							 rx_num, rx_slot);
192 	else
193 		return -ENOTSUPP;
194 }
195 EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
196 
197 /**
198  * snd_soc_dai_get_channel_map - Get DAI audio channel map
199  * @dai: DAI
200  * @tx_num: how many TX channels
201  * @tx_slot: pointer to an array which imply the TX slot number channel
202  *           0~num-1 uses
203  * @rx_num: how many RX channels
204  * @rx_slot: pointer to an array which imply the RX slot number channel
205  *           0~num-1 uses
206  */
207 int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai,
208 				unsigned int *tx_num, unsigned int *tx_slot,
209 				unsigned int *rx_num, unsigned int *rx_slot)
210 {
211 	if (dai->driver->ops->get_channel_map)
212 		return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot,
213 							 rx_num, rx_slot);
214 	else
215 		return -ENOTSUPP;
216 }
217 EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map);
218 
219 /**
220  * snd_soc_dai_set_tristate - configure DAI system or master clock.
221  * @dai: DAI
222  * @tristate: tristate enable
223  *
224  * Tristates the DAI so that others can use it.
225  */
226 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
227 {
228 	if (dai->driver->ops->set_tristate)
229 		return dai->driver->ops->set_tristate(dai, tristate);
230 	else
231 		return -EINVAL;
232 }
233 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
234 
235 /**
236  * snd_soc_dai_digital_mute - configure DAI system or master clock.
237  * @dai: DAI
238  * @mute: mute enable
239  * @direction: stream to mute
240  *
241  * Mutes the DAI DAC.
242  */
243 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
244 			     int direction)
245 {
246 	if (dai->driver->ops->mute_stream)
247 		return dai->driver->ops->mute_stream(dai, mute, direction);
248 	else if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
249 		 dai->driver->ops->digital_mute)
250 		return dai->driver->ops->digital_mute(dai, mute);
251 	else
252 		return -ENOTSUPP;
253 }
254 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
255 
256 int snd_soc_dai_hw_params(struct snd_soc_dai *dai,
257 			  struct snd_pcm_substream *substream,
258 			  struct snd_pcm_hw_params *params)
259 {
260 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
261 	int ret;
262 
263 	/* perform any topology hw_params fixups before DAI  */
264 	if (rtd->dai_link->be_hw_params_fixup) {
265 		ret = rtd->dai_link->be_hw_params_fixup(rtd, params);
266 		if (ret < 0) {
267 			dev_err(rtd->dev,
268 				"ASoC: hw_params topology fixup failed %d\n",
269 				ret);
270 			return ret;
271 		}
272 	}
273 
274 	if (dai->driver->ops->hw_params) {
275 		ret = dai->driver->ops->hw_params(substream, params, dai);
276 		if (ret < 0) {
277 			dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n",
278 				dai->name, ret);
279 			return ret;
280 		}
281 	}
282 
283 	return 0;
284 }
285 
286 void snd_soc_dai_hw_free(struct snd_soc_dai *dai,
287 			 struct snd_pcm_substream *substream)
288 {
289 	if (dai->driver->ops->hw_free)
290 		dai->driver->ops->hw_free(substream, dai);
291 }
292 
293 int snd_soc_dai_startup(struct snd_soc_dai *dai,
294 			struct snd_pcm_substream *substream)
295 {
296 	int ret = 0;
297 
298 	if (dai->driver->ops->startup)
299 		ret = dai->driver->ops->startup(substream, dai);
300 
301 	return ret;
302 }
303 
304 void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
305 			 struct snd_pcm_substream *substream)
306 {
307 	if (dai->driver->ops->shutdown)
308 		dai->driver->ops->shutdown(substream, dai);
309 }
310 
311 int snd_soc_dai_prepare(struct snd_soc_dai *dai,
312 			struct snd_pcm_substream *substream)
313 {
314 	int ret = 0;
315 
316 	if (dai->driver->ops->prepare)
317 		ret = dai->driver->ops->prepare(substream, dai);
318 
319 	return ret;
320 }
321 
322 int snd_soc_dai_trigger(struct snd_soc_dai *dai,
323 			struct snd_pcm_substream *substream,
324 			int cmd)
325 {
326 	int ret = 0;
327 
328 	if (dai->driver->ops->trigger)
329 		ret = dai->driver->ops->trigger(substream, cmd, dai);
330 
331 	return ret;
332 }
333 
334 int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai,
335 				struct snd_pcm_substream *substream,
336 				int cmd)
337 {
338 	int ret = 0;
339 
340 	if (dai->driver->ops->bespoke_trigger)
341 		ret = dai->driver->ops->bespoke_trigger(substream, cmd, dai);
342 
343 	return ret;
344 }
345 
346 snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
347 				    struct snd_pcm_substream *substream)
348 {
349 	int delay = 0;
350 
351 	if (dai->driver->ops->delay)
352 		delay = dai->driver->ops->delay(substream, dai);
353 
354 	return delay;
355 }
356 
357 void snd_soc_dai_suspend(struct snd_soc_dai *dai)
358 {
359 	if (dai->driver->suspend)
360 		dai->driver->suspend(dai);
361 }
362 
363 void snd_soc_dai_resume(struct snd_soc_dai *dai)
364 {
365 	if (dai->driver->resume)
366 		dai->driver->resume(dai);
367 }
368 
369 int snd_soc_dai_probe(struct snd_soc_dai *dai)
370 {
371 	if (dai->driver->probe)
372 		return dai->driver->probe(dai);
373 	return 0;
374 }
375 
376 int snd_soc_dai_remove(struct snd_soc_dai *dai)
377 {
378 	if (dai->driver->remove)
379 		return dai->driver->remove(dai);
380 	return 0;
381 }
382 
383 int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
384 			     struct snd_soc_pcm_runtime *rtd, int num)
385 {
386 	if (dai->driver->compress_new)
387 		return dai->driver->compress_new(rtd, num);
388 	return -ENOTSUPP;
389 }
390 
391 /*
392  * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
393  *
394  * Returns true if the DAI supports the indicated stream type.
395  */
396 bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir)
397 {
398 	struct snd_soc_pcm_stream *stream;
399 
400 	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
401 		stream = &dai->driver->playback;
402 	else
403 		stream = &dai->driver->capture;
404 
405 	/* If the codec specifies any channels at all, it supports the stream */
406 	return stream->channels_min;
407 }
408