blob: 5b5b979cd1f35f536da14267a45abca1e7883512 [file] [log] [blame]
Kuninori Morimoto06f6e1d2019-07-22 10:32:12 +09001// 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 */
21int 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}
30EXPORT_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 */
42int 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}
50EXPORT_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 */
62int 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}
72EXPORT_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 */
81int 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}
88EXPORT_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 */
97int 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}
103EXPORT_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 */
113static 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 */
152int 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}
171EXPORT_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 */
185int 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}
195EXPORT_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 */
207int 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}
217EXPORT_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 */
226int 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}
233EXPORT_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 */
243int 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}
254EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
Kuninori Morimotoaa6166c2019-07-22 10:33:04 +0900255
256int 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}
Kuninori Morimoto846faae2019-07-22 10:33:19 +0900285
286void 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}
Kuninori Morimoto5a52a042019-07-22 10:33:32 +0900292
293int 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}
Kuninori Morimoto330fcb52019-07-22 10:33:39 +0900303
304void 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}
Kuninori Morimoto4beb8e12019-07-22 10:33:45 +0900310
311int 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}
Kuninori Morimoto95aef352019-07-22 10:33:51 +0900321
322int 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}
Kuninori Morimoto5c0769af2019-07-22 10:33:56 +0900333
334int 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}
Kuninori Morimoto1dea80d2019-07-22 10:34:09 +0900345
346snd_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}