1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // |
3 | // Copyright (c) 2018 BayLibre, SAS. |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of_platform.h> |
9 | #include <sound/pcm_params.h> |
10 | #include <sound/soc.h> |
11 | #include <sound/soc-dai.h> |
12 | |
13 | #include "axg-tdm.h" |
14 | |
15 | /* Maximum bit clock frequency according the datasheets */ |
16 | #define MAX_SCLK 100000000 /* Hz */ |
17 | |
18 | enum { |
19 | TDM_IFACE_PAD, |
20 | TDM_IFACE_LOOPBACK, |
21 | }; |
22 | |
23 | static unsigned int axg_tdm_slots_total(u32 *mask) |
24 | { |
25 | unsigned int slots = 0; |
26 | int i; |
27 | |
28 | if (!mask) |
29 | return 0; |
30 | |
31 | /* Count the total number of slots provided by all 4 lanes */ |
32 | for (i = 0; i < AXG_TDM_NUM_LANES; i++) |
33 | slots += hweight32(mask[i]); |
34 | |
35 | return slots; |
36 | } |
37 | |
38 | int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, |
39 | u32 *rx_mask, unsigned int slots, |
40 | unsigned int slot_width) |
41 | { |
42 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
43 | struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai); |
44 | struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai); |
45 | unsigned int tx_slots, rx_slots; |
46 | unsigned int fmt = 0; |
47 | |
48 | tx_slots = axg_tdm_slots_total(mask: tx_mask); |
49 | rx_slots = axg_tdm_slots_total(mask: rx_mask); |
50 | |
51 | /* We should at least have a slot for a valid interface */ |
52 | if (!tx_slots && !rx_slots) { |
53 | dev_err(dai->dev, "interface has no slot\n" ); |
54 | return -EINVAL; |
55 | } |
56 | |
57 | iface->slots = slots; |
58 | |
59 | switch (slot_width) { |
60 | case 0: |
61 | slot_width = 32; |
62 | fallthrough; |
63 | case 32: |
64 | fmt |= SNDRV_PCM_FMTBIT_S32_LE; |
65 | fallthrough; |
66 | case 24: |
67 | fmt |= SNDRV_PCM_FMTBIT_S24_LE; |
68 | fmt |= SNDRV_PCM_FMTBIT_S20_LE; |
69 | fallthrough; |
70 | case 16: |
71 | fmt |= SNDRV_PCM_FMTBIT_S16_LE; |
72 | fallthrough; |
73 | case 8: |
74 | fmt |= SNDRV_PCM_FMTBIT_S8; |
75 | break; |
76 | default: |
77 | dev_err(dai->dev, "unsupported slot width: %d\n" , slot_width); |
78 | return -EINVAL; |
79 | } |
80 | |
81 | iface->slot_width = slot_width; |
82 | |
83 | /* Amend the dai driver and let dpcm merge do its job */ |
84 | if (tx) { |
85 | tx->mask = tx_mask; |
86 | dai->driver->playback.channels_max = tx_slots; |
87 | dai->driver->playback.formats = fmt; |
88 | } |
89 | |
90 | if (rx) { |
91 | rx->mask = rx_mask; |
92 | dai->driver->capture.channels_max = rx_slots; |
93 | dai->driver->capture.formats = fmt; |
94 | } |
95 | |
96 | return 0; |
97 | } |
98 | EXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots); |
99 | |
100 | static int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, |
101 | unsigned int freq, int dir) |
102 | { |
103 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
104 | int ret = -ENOTSUPP; |
105 | |
106 | if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { |
107 | if (!iface->mclk) { |
108 | dev_warn(dai->dev, "master clock not provided\n" ); |
109 | } else { |
110 | ret = clk_set_rate(clk: iface->mclk, rate: freq); |
111 | if (!ret) |
112 | iface->mclk_rate = freq; |
113 | } |
114 | } |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
120 | { |
121 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
122 | |
123 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
124 | case SND_SOC_DAIFMT_BP_FP: |
125 | if (!iface->mclk) { |
126 | dev_err(dai->dev, "cpu clock master: mclk missing\n" ); |
127 | return -ENODEV; |
128 | } |
129 | break; |
130 | |
131 | case SND_SOC_DAIFMT_BC_FC: |
132 | break; |
133 | |
134 | case SND_SOC_DAIFMT_BP_FC: |
135 | case SND_SOC_DAIFMT_BC_FP: |
136 | dev_err(dai->dev, "only BP_FP and BC_FC are supported\n" ); |
137 | fallthrough; |
138 | default: |
139 | return -EINVAL; |
140 | } |
141 | |
142 | iface->fmt = fmt; |
143 | return 0; |
144 | } |
145 | |
146 | static int axg_tdm_iface_startup(struct snd_pcm_substream *substream, |
147 | struct snd_soc_dai *dai) |
148 | { |
149 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
150 | struct axg_tdm_stream *ts = |
151 | snd_soc_dai_get_dma_data(dai, substream); |
152 | int ret; |
153 | |
154 | if (!axg_tdm_slots_total(mask: ts->mask)) { |
155 | dev_err(dai->dev, "interface has not slots\n" ); |
156 | return -EINVAL; |
157 | } |
158 | |
159 | if (snd_soc_component_active(component: dai->component)) { |
160 | /* Apply component wide rate symmetry */ |
161 | ret = snd_pcm_hw_constraint_single(runtime: substream->runtime, |
162 | SNDRV_PCM_HW_PARAM_RATE, |
163 | val: iface->rate); |
164 | |
165 | } else { |
166 | /* Limit rate according to the slot number and width */ |
167 | unsigned int max_rate = |
168 | MAX_SCLK / (iface->slots * iface->slot_width); |
169 | ret = snd_pcm_hw_constraint_minmax(runtime: substream->runtime, |
170 | SNDRV_PCM_HW_PARAM_RATE, |
171 | min: 0, max: max_rate); |
172 | } |
173 | |
174 | if (ret < 0) |
175 | dev_err(dai->dev, "can't set iface rate constraint\n" ); |
176 | else |
177 | ret = 0; |
178 | |
179 | return ret; |
180 | } |
181 | |
182 | static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, |
183 | struct snd_pcm_hw_params *params, |
184 | struct snd_soc_dai *dai) |
185 | { |
186 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
187 | struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); |
188 | unsigned int channels = params_channels(p: params); |
189 | unsigned int width = params_width(p: params); |
190 | |
191 | /* Save rate and sample_bits for component symmetry */ |
192 | iface->rate = params_rate(p: params); |
193 | |
194 | /* Make sure this interface can cope with the stream */ |
195 | if (axg_tdm_slots_total(mask: ts->mask) < channels) { |
196 | dev_err(dai->dev, "not enough slots for channels\n" ); |
197 | return -EINVAL; |
198 | } |
199 | |
200 | if (iface->slot_width < width) { |
201 | dev_err(dai->dev, "incompatible slots width for stream\n" ); |
202 | return -EINVAL; |
203 | } |
204 | |
205 | /* Save the parameter for tdmout/tdmin widgets */ |
206 | ts->physical_width = params_physical_width(p: params); |
207 | ts->width = params_width(p: params); |
208 | ts->channels = params_channels(p: params); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai, |
214 | struct snd_pcm_hw_params *params) |
215 | { |
216 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
217 | unsigned int ratio_num; |
218 | int ret; |
219 | |
220 | ret = clk_set_rate(clk: iface->lrclk, rate: params_rate(p: params)); |
221 | if (ret) { |
222 | dev_err(dai->dev, "setting sample clock failed: %d\n" , ret); |
223 | return ret; |
224 | } |
225 | |
226 | switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
227 | case SND_SOC_DAIFMT_I2S: |
228 | case SND_SOC_DAIFMT_LEFT_J: |
229 | case SND_SOC_DAIFMT_RIGHT_J: |
230 | /* 50% duty cycle ratio */ |
231 | ratio_num = 1; |
232 | break; |
233 | |
234 | case SND_SOC_DAIFMT_DSP_A: |
235 | case SND_SOC_DAIFMT_DSP_B: |
236 | /* |
237 | * A zero duty cycle ratio will result in setting the mininum |
238 | * ratio possible which, for this clock, is 1 cycle of the |
239 | * parent bclk clock high and the rest low, This is exactly |
240 | * what we want here. |
241 | */ |
242 | ratio_num = 0; |
243 | break; |
244 | |
245 | default: |
246 | return -EINVAL; |
247 | } |
248 | |
249 | ret = clk_set_duty_cycle(clk: iface->lrclk, num: ratio_num, den: 2); |
250 | if (ret) { |
251 | dev_err(dai->dev, |
252 | "setting sample clock duty cycle failed: %d\n" , ret); |
253 | return ret; |
254 | } |
255 | |
256 | /* Set sample clock inversion */ |
257 | ret = clk_set_phase(clk: iface->lrclk, |
258 | degrees: axg_tdm_lrclk_invert(fmt: iface->fmt) ? 180 : 0); |
259 | if (ret) { |
260 | dev_err(dai->dev, |
261 | "setting sample clock phase failed: %d\n" , ret); |
262 | return ret; |
263 | } |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, |
269 | struct snd_pcm_hw_params *params) |
270 | { |
271 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
272 | unsigned long srate; |
273 | int ret; |
274 | |
275 | srate = iface->slots * iface->slot_width * params_rate(p: params); |
276 | |
277 | if (!iface->mclk_rate) { |
278 | /* If no specific mclk is requested, default to bit clock * 2 */ |
279 | clk_set_rate(clk: iface->mclk, rate: 2 * srate); |
280 | } else { |
281 | /* Check if we can actually get the bit clock from mclk */ |
282 | if (iface->mclk_rate % srate) { |
283 | dev_err(dai->dev, |
284 | "can't derive sclk %lu from mclk %lu\n" , |
285 | srate, iface->mclk_rate); |
286 | return -EINVAL; |
287 | } |
288 | } |
289 | |
290 | ret = clk_set_rate(clk: iface->sclk, rate: srate); |
291 | if (ret) { |
292 | dev_err(dai->dev, "setting bit clock failed: %d\n" , ret); |
293 | return ret; |
294 | } |
295 | |
296 | /* Set the bit clock inversion */ |
297 | ret = clk_set_phase(clk: iface->sclk, |
298 | degrees: axg_tdm_sclk_invert(fmt: iface->fmt) ? 0 : 180); |
299 | if (ret) { |
300 | dev_err(dai->dev, "setting bit clock phase failed: %d\n" , ret); |
301 | return ret; |
302 | } |
303 | |
304 | return ret; |
305 | } |
306 | |
307 | static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, |
308 | struct snd_pcm_hw_params *params, |
309 | struct snd_soc_dai *dai) |
310 | { |
311 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
312 | int ret; |
313 | |
314 | switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
315 | case SND_SOC_DAIFMT_I2S: |
316 | case SND_SOC_DAIFMT_LEFT_J: |
317 | case SND_SOC_DAIFMT_RIGHT_J: |
318 | if (iface->slots > 2) { |
319 | dev_err(dai->dev, "bad slot number for format: %d\n" , |
320 | iface->slots); |
321 | return -EINVAL; |
322 | } |
323 | break; |
324 | |
325 | case SND_SOC_DAIFMT_DSP_A: |
326 | case SND_SOC_DAIFMT_DSP_B: |
327 | break; |
328 | |
329 | default: |
330 | dev_err(dai->dev, "unsupported dai format\n" ); |
331 | return -EINVAL; |
332 | } |
333 | |
334 | ret = axg_tdm_iface_set_stream(substream, params, dai); |
335 | if (ret) |
336 | return ret; |
337 | |
338 | if ((iface->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == |
339 | SND_SOC_DAIFMT_BP_FP) { |
340 | ret = axg_tdm_iface_set_sclk(dai, params); |
341 | if (ret) |
342 | return ret; |
343 | |
344 | ret = axg_tdm_iface_set_lrclk(dai, params); |
345 | if (ret) |
346 | return ret; |
347 | } |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, |
353 | struct snd_soc_dai *dai) |
354 | { |
355 | struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); |
356 | |
357 | /* Stop all attached formatters */ |
358 | axg_tdm_stream_stop(ts); |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, |
364 | struct snd_soc_dai *dai) |
365 | { |
366 | struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); |
367 | |
368 | /* Force all attached formatters to update */ |
369 | return axg_tdm_stream_reset(ts); |
370 | } |
371 | |
372 | static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) |
373 | { |
374 | int stream; |
375 | |
376 | for_each_pcm_streams(stream) { |
377 | struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream); |
378 | |
379 | if (ts) |
380 | axg_tdm_stream_free(ts); |
381 | } |
382 | |
383 | return 0; |
384 | } |
385 | |
386 | static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) |
387 | { |
388 | struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); |
389 | int stream; |
390 | |
391 | for_each_pcm_streams(stream) { |
392 | struct axg_tdm_stream *ts; |
393 | |
394 | if (!snd_soc_dai_get_widget(dai, stream)) |
395 | continue; |
396 | |
397 | ts = axg_tdm_stream_alloc(iface); |
398 | if (!ts) { |
399 | axg_tdm_iface_remove_dai(dai); |
400 | return -ENOMEM; |
401 | } |
402 | snd_soc_dai_dma_data_set(dai, stream, data: ts); |
403 | } |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | static const struct snd_soc_dai_ops axg_tdm_iface_ops = { |
409 | .probe = axg_tdm_iface_probe_dai, |
410 | .remove = axg_tdm_iface_remove_dai, |
411 | .set_sysclk = axg_tdm_iface_set_sysclk, |
412 | .set_fmt = axg_tdm_iface_set_fmt, |
413 | .startup = axg_tdm_iface_startup, |
414 | .hw_params = axg_tdm_iface_hw_params, |
415 | .prepare = axg_tdm_iface_prepare, |
416 | .hw_free = axg_tdm_iface_hw_free, |
417 | }; |
418 | |
419 | /* TDM Backend DAIs */ |
420 | static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { |
421 | [TDM_IFACE_PAD] = { |
422 | .name = "TDM Pad" , |
423 | .playback = { |
424 | .stream_name = "Playback" , |
425 | .channels_min = 1, |
426 | .channels_max = AXG_TDM_CHANNEL_MAX, |
427 | .rates = AXG_TDM_RATES, |
428 | .formats = AXG_TDM_FORMATS, |
429 | }, |
430 | .capture = { |
431 | .stream_name = "Capture" , |
432 | .channels_min = 1, |
433 | .channels_max = AXG_TDM_CHANNEL_MAX, |
434 | .rates = AXG_TDM_RATES, |
435 | .formats = AXG_TDM_FORMATS, |
436 | }, |
437 | .id = TDM_IFACE_PAD, |
438 | .ops = &axg_tdm_iface_ops, |
439 | }, |
440 | [TDM_IFACE_LOOPBACK] = { |
441 | .name = "TDM Loopback" , |
442 | .capture = { |
443 | .stream_name = "Loopback" , |
444 | .channels_min = 1, |
445 | .channels_max = AXG_TDM_CHANNEL_MAX, |
446 | .rates = AXG_TDM_RATES, |
447 | .formats = AXG_TDM_FORMATS, |
448 | }, |
449 | .id = TDM_IFACE_LOOPBACK, |
450 | .ops = &axg_tdm_iface_ops, |
451 | }, |
452 | }; |
453 | |
454 | static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, |
455 | enum snd_soc_bias_level level) |
456 | { |
457 | struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(c: component); |
458 | enum snd_soc_bias_level now = |
459 | snd_soc_component_get_bias_level(component); |
460 | int ret = 0; |
461 | |
462 | switch (level) { |
463 | case SND_SOC_BIAS_PREPARE: |
464 | if (now == SND_SOC_BIAS_STANDBY) |
465 | ret = clk_prepare_enable(clk: iface->mclk); |
466 | break; |
467 | |
468 | case SND_SOC_BIAS_STANDBY: |
469 | if (now == SND_SOC_BIAS_PREPARE) |
470 | clk_disable_unprepare(clk: iface->mclk); |
471 | break; |
472 | |
473 | case SND_SOC_BIAS_OFF: |
474 | case SND_SOC_BIAS_ON: |
475 | break; |
476 | } |
477 | |
478 | return ret; |
479 | } |
480 | |
481 | static const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = { |
482 | SND_SOC_DAPM_SIGGEN("Playback Signal" ), |
483 | }; |
484 | |
485 | static const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = { |
486 | { "Loopback" , NULL, "Playback Signal" }, |
487 | }; |
488 | |
489 | static const struct snd_soc_component_driver axg_tdm_iface_component_drv = { |
490 | .dapm_widgets = axg_tdm_iface_dapm_widgets, |
491 | .num_dapm_widgets = ARRAY_SIZE(axg_tdm_iface_dapm_widgets), |
492 | .dapm_routes = axg_tdm_iface_dapm_routes, |
493 | .num_dapm_routes = ARRAY_SIZE(axg_tdm_iface_dapm_routes), |
494 | .set_bias_level = axg_tdm_iface_set_bias_level, |
495 | }; |
496 | |
497 | static const struct of_device_id axg_tdm_iface_of_match[] = { |
498 | { .compatible = "amlogic,axg-tdm-iface" , }, |
499 | {} |
500 | }; |
501 | MODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match); |
502 | |
503 | static int axg_tdm_iface_probe(struct platform_device *pdev) |
504 | { |
505 | struct device *dev = &pdev->dev; |
506 | struct snd_soc_dai_driver *dai_drv; |
507 | struct axg_tdm_iface *iface; |
508 | int i; |
509 | |
510 | iface = devm_kzalloc(dev, size: sizeof(*iface), GFP_KERNEL); |
511 | if (!iface) |
512 | return -ENOMEM; |
513 | platform_set_drvdata(pdev, data: iface); |
514 | |
515 | /* |
516 | * Duplicate dai driver: depending on the slot masks configuration |
517 | * We'll change the number of channel provided by DAI stream, so dpcm |
518 | * channel merge can be done properly |
519 | */ |
520 | dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), |
521 | size: sizeof(*dai_drv), GFP_KERNEL); |
522 | if (!dai_drv) |
523 | return -ENOMEM; |
524 | |
525 | for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) |
526 | memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], |
527 | sizeof(*dai_drv)); |
528 | |
529 | /* Bit clock provided on the pad */ |
530 | iface->sclk = devm_clk_get(dev, id: "sclk" ); |
531 | if (IS_ERR(ptr: iface->sclk)) |
532 | return dev_err_probe(dev, err: PTR_ERR(ptr: iface->sclk), fmt: "failed to get sclk\n" ); |
533 | |
534 | /* Sample clock provided on the pad */ |
535 | iface->lrclk = devm_clk_get(dev, id: "lrclk" ); |
536 | if (IS_ERR(ptr: iface->lrclk)) |
537 | return dev_err_probe(dev, err: PTR_ERR(ptr: iface->lrclk), fmt: "failed to get lrclk\n" ); |
538 | |
539 | /* |
540 | * mclk maybe be missing when the cpu dai is in slave mode and |
541 | * the codec does not require it to provide a master clock. |
542 | * At this point, ignore the error if mclk is missing. We'll |
543 | * throw an error if the cpu dai is master and mclk is missing |
544 | */ |
545 | iface->mclk = devm_clk_get_optional(dev, id: "mclk" ); |
546 | if (IS_ERR(ptr: iface->mclk)) |
547 | return dev_err_probe(dev, err: PTR_ERR(ptr: iface->mclk), fmt: "failed to get mclk\n" ); |
548 | |
549 | return devm_snd_soc_register_component(dev, |
550 | component_driver: &axg_tdm_iface_component_drv, dai_drv, |
551 | ARRAY_SIZE(axg_tdm_iface_dai_drv)); |
552 | } |
553 | |
554 | static struct platform_driver axg_tdm_iface_pdrv = { |
555 | .probe = axg_tdm_iface_probe, |
556 | .driver = { |
557 | .name = "axg-tdm-iface" , |
558 | .of_match_table = axg_tdm_iface_of_match, |
559 | }, |
560 | }; |
561 | module_platform_driver(axg_tdm_iface_pdrv); |
562 | |
563 | MODULE_DESCRIPTION("Amlogic AXG TDM interface driver" ); |
564 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>" ); |
565 | MODULE_LICENSE("GPL v2" ); |
566 | |