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 */
snd_soc_dai_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)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 */
snd_soc_dai_set_clkdiv(struct snd_soc_dai * dai,int div_id,int div)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 */
snd_soc_dai_set_pll(struct snd_soc_dai * dai,int pll_id,int source,unsigned int freq_in,unsigned int freq_out)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 */
snd_soc_dai_set_bclk_ratio(struct snd_soc_dai * dai,unsigned int ratio)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 */
snd_soc_dai_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)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 */
snd_soc_xlate_tdm_slot_mask(unsigned int slots,unsigned int * tx_mask,unsigned int * rx_mask)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 */
snd_soc_dai_set_tdm_slot(struct snd_soc_dai * dai,unsigned int tx_mask,unsigned int rx_mask,int slots,int slot_width)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 */
snd_soc_dai_set_channel_map(struct snd_soc_dai * dai,unsigned int tx_num,unsigned int * tx_slot,unsigned int rx_num,unsigned int * rx_slot)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 */
snd_soc_dai_get_channel_map(struct snd_soc_dai * dai,unsigned int * tx_num,unsigned int * tx_slot,unsigned int * rx_num,unsigned int * rx_slot)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 */
snd_soc_dai_set_tristate(struct snd_soc_dai * dai,int tristate)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 */
snd_soc_dai_digital_mute(struct snd_soc_dai * dai,int mute,int direction)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
snd_soc_dai_hw_params(struct snd_soc_dai * dai,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)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
snd_soc_dai_hw_free(struct snd_soc_dai * dai,struct snd_pcm_substream * substream)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
snd_soc_dai_startup(struct snd_soc_dai * dai,struct snd_pcm_substream * substream)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
snd_soc_dai_shutdown(struct snd_soc_dai * dai,struct snd_pcm_substream * substream)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
snd_soc_dai_prepare(struct snd_soc_dai * dai,struct snd_pcm_substream * substream)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
snd_soc_dai_trigger(struct snd_soc_dai * dai,struct snd_pcm_substream * substream,int cmd)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
snd_soc_dai_bespoke_trigger(struct snd_soc_dai * dai,struct snd_pcm_substream * substream,int cmd)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
snd_soc_dai_delay(struct snd_soc_dai * dai,struct snd_pcm_substream * substream)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
snd_soc_dai_suspend(struct snd_soc_dai * dai)357 void snd_soc_dai_suspend(struct snd_soc_dai *dai)
358 {
359 if (dai->driver->suspend)
360 dai->driver->suspend(dai);
361 }
362
snd_soc_dai_resume(struct snd_soc_dai * dai)363 void snd_soc_dai_resume(struct snd_soc_dai *dai)
364 {
365 if (dai->driver->resume)
366 dai->driver->resume(dai);
367 }
368
snd_soc_dai_probe(struct snd_soc_dai * dai)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
snd_soc_dai_remove(struct snd_soc_dai * dai)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
snd_soc_dai_compress_new(struct snd_soc_dai * dai,struct snd_soc_pcm_runtime * rtd,int num)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 */
snd_soc_dai_stream_valid(struct snd_soc_dai * dai,int dir)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