]> git.ipfire.org Git - thirdparty/linux.git/blob - sound/soc/soc-dai.c
Merge branch 'nvme-5.7' of git://git.infradead.org/nvme into block-5.7
[thirdparty/linux.git] / sound / soc / soc-dai.c
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->started[substream->stream] &&
299 dai->driver->ops->startup)
300 ret = dai->driver->ops->startup(substream, dai);
301
302 if (ret == 0)
303 dai->started[substream->stream] = 1;
304
305 return ret;
306 }
307
308 void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
309 struct snd_pcm_substream *substream)
310 {
311 if (dai->started[substream->stream] &&
312 dai->driver->ops->shutdown)
313 dai->driver->ops->shutdown(substream, dai);
314
315 dai->started[substream->stream] = 0;
316 }
317
318 int snd_soc_dai_prepare(struct snd_soc_dai *dai,
319 struct snd_pcm_substream *substream)
320 {
321 int ret = 0;
322
323 if (dai->driver->ops->prepare)
324 ret = dai->driver->ops->prepare(substream, dai);
325
326 return ret;
327 }
328
329 int snd_soc_dai_trigger(struct snd_soc_dai *dai,
330 struct snd_pcm_substream *substream,
331 int cmd)
332 {
333 int ret = 0;
334
335 if (dai->driver->ops->trigger)
336 ret = dai->driver->ops->trigger(substream, cmd, dai);
337
338 return ret;
339 }
340
341 int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai,
342 struct snd_pcm_substream *substream,
343 int cmd)
344 {
345 int ret = 0;
346
347 if (dai->driver->ops->bespoke_trigger)
348 ret = dai->driver->ops->bespoke_trigger(substream, cmd, dai);
349
350 return ret;
351 }
352
353 snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
354 struct snd_pcm_substream *substream)
355 {
356 int delay = 0;
357
358 if (dai->driver->ops->delay)
359 delay = dai->driver->ops->delay(substream, dai);
360
361 return delay;
362 }
363
364 int snd_soc_dai_probe(struct snd_soc_dai *dai)
365 {
366 if (dai->driver->probe)
367 return dai->driver->probe(dai);
368 return 0;
369 }
370
371 int snd_soc_dai_remove(struct snd_soc_dai *dai)
372 {
373 if (dai->driver->remove)
374 return dai->driver->remove(dai);
375 return 0;
376 }
377
378 int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
379 struct snd_soc_pcm_runtime *rtd, int num)
380 {
381 if (dai->driver->compress_new)
382 return dai->driver->compress_new(rtd, num);
383 return -ENOTSUPP;
384 }
385
386 /*
387 * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
388 *
389 * Returns true if the DAI supports the indicated stream type.
390 */
391 bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir)
392 {
393 struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir);
394
395 /* If the codec specifies any channels at all, it supports the stream */
396 return stream->channels_min;
397 }