1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ALSA SoC using the QUICC Multichannel Controller (QMC)
4 *
5 * Copyright 2022 CS GROUP France
6 *
7 * Author: Herve Codina <herve.codina@bootlin.com>
8 */
9
10#include <linux/dma-mapping.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_platform.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16#include <soc/fsl/qe/qmc.h>
17#include <sound/pcm_params.h>
18#include <sound/soc.h>
19
20struct qmc_dai {
21 char *name;
22 int id;
23 struct device *dev;
24 struct qmc_chan *qmc_chan;
25 unsigned int nb_tx_ts;
26 unsigned int nb_rx_ts;
27};
28
29struct qmc_audio {
30 struct device *dev;
31 unsigned int num_dais;
32 struct qmc_dai *dais;
33 struct snd_soc_dai_driver *dai_drivers;
34};
35
36struct qmc_dai_prtd {
37 struct qmc_dai *qmc_dai;
38 dma_addr_t dma_buffer_start;
39 dma_addr_t period_ptr_submitted;
40 dma_addr_t period_ptr_ended;
41 dma_addr_t dma_buffer_end;
42 size_t period_size;
43 struct snd_pcm_substream *substream;
44};
45
46static int qmc_audio_pcm_construct(struct snd_soc_component *component,
47 struct snd_soc_pcm_runtime *rtd)
48{
49 struct snd_card *card = rtd->card->snd_card;
50 int ret;
51
52 ret = dma_coerce_mask_and_coherent(dev: card->dev, DMA_BIT_MASK(32));
53 if (ret)
54 return ret;
55
56 snd_pcm_set_managed_buffer_all(pcm: rtd->pcm, SNDRV_DMA_TYPE_DEV, data: card->dev,
57 size: 64*1024, max: 64*1024);
58 return 0;
59}
60
61static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
62 struct snd_pcm_substream *substream,
63 struct snd_pcm_hw_params *params)
64{
65 struct snd_pcm_runtime *runtime = substream->runtime;
66 struct qmc_dai_prtd *prtd = substream->runtime->private_data;
67
68 prtd->dma_buffer_start = runtime->dma_addr;
69 prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(p: params);
70 prtd->period_size = params_period_bytes(p: params);
71 prtd->period_ptr_submitted = prtd->dma_buffer_start;
72 prtd->period_ptr_ended = prtd->dma_buffer_start;
73 prtd->substream = substream;
74
75 return 0;
76}
77
78static void qmc_audio_pcm_write_complete(void *context)
79{
80 struct qmc_dai_prtd *prtd = context;
81 int ret;
82
83 prtd->period_ptr_ended += prtd->period_size;
84 if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
85 prtd->period_ptr_ended = prtd->dma_buffer_start;
86
87 prtd->period_ptr_submitted += prtd->period_size;
88 if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
89 prtd->period_ptr_submitted = prtd->dma_buffer_start;
90
91 ret = qmc_chan_write_submit(chan: prtd->qmc_dai->qmc_chan,
92 addr: prtd->period_ptr_submitted, length: prtd->period_size,
93 complete: qmc_audio_pcm_write_complete, context: prtd);
94 if (ret) {
95 dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
96 ret);
97 }
98
99 snd_pcm_period_elapsed(substream: prtd->substream);
100}
101
102static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
103{
104 struct qmc_dai_prtd *prtd = context;
105 int ret;
106
107 if (length != prtd->period_size) {
108 dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
109 length, prtd->period_size);
110 }
111
112 prtd->period_ptr_ended += prtd->period_size;
113 if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
114 prtd->period_ptr_ended = prtd->dma_buffer_start;
115
116 prtd->period_ptr_submitted += prtd->period_size;
117 if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
118 prtd->period_ptr_submitted = prtd->dma_buffer_start;
119
120 ret = qmc_chan_read_submit(chan: prtd->qmc_dai->qmc_chan,
121 addr: prtd->period_ptr_submitted, length: prtd->period_size,
122 complete: qmc_audio_pcm_read_complete, context: prtd);
123 if (ret) {
124 dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
125 ret);
126 }
127
128 snd_pcm_period_elapsed(substream: prtd->substream);
129}
130
131static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
132 struct snd_pcm_substream *substream, int cmd)
133{
134 struct qmc_dai_prtd *prtd = substream->runtime->private_data;
135 int ret;
136
137 if (!prtd->qmc_dai) {
138 dev_err(component->dev, "qmc_dai is not set\n");
139 return -EINVAL;
140 }
141
142 switch (cmd) {
143 case SNDRV_PCM_TRIGGER_START:
144 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
145 /* Submit first chunk ... */
146 ret = qmc_chan_write_submit(chan: prtd->qmc_dai->qmc_chan,
147 addr: prtd->period_ptr_submitted, length: prtd->period_size,
148 complete: qmc_audio_pcm_write_complete, context: prtd);
149 if (ret) {
150 dev_err(component->dev, "write_submit failed %d\n",
151 ret);
152 return ret;
153 }
154
155 /* ... prepare next one ... */
156 prtd->period_ptr_submitted += prtd->period_size;
157 if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
158 prtd->period_ptr_submitted = prtd->dma_buffer_start;
159
160 /* ... and send it */
161 ret = qmc_chan_write_submit(chan: prtd->qmc_dai->qmc_chan,
162 addr: prtd->period_ptr_submitted, length: prtd->period_size,
163 complete: qmc_audio_pcm_write_complete, context: prtd);
164 if (ret) {
165 dev_err(component->dev, "write_submit failed %d\n",
166 ret);
167 return ret;
168 }
169 } else {
170 /* Submit first chunk ... */
171 ret = qmc_chan_read_submit(chan: prtd->qmc_dai->qmc_chan,
172 addr: prtd->period_ptr_submitted, length: prtd->period_size,
173 complete: qmc_audio_pcm_read_complete, context: prtd);
174 if (ret) {
175 dev_err(component->dev, "read_submit failed %d\n",
176 ret);
177 return ret;
178 }
179
180 /* ... prepare next one ... */
181 prtd->period_ptr_submitted += prtd->period_size;
182 if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
183 prtd->period_ptr_submitted = prtd->dma_buffer_start;
184
185 /* ... and send it */
186 ret = qmc_chan_read_submit(chan: prtd->qmc_dai->qmc_chan,
187 addr: prtd->period_ptr_submitted, length: prtd->period_size,
188 complete: qmc_audio_pcm_read_complete, context: prtd);
189 if (ret) {
190 dev_err(component->dev, "write_submit failed %d\n",
191 ret);
192 return ret;
193 }
194 }
195 break;
196
197 case SNDRV_PCM_TRIGGER_RESUME:
198 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
199 break;
200
201 case SNDRV_PCM_TRIGGER_STOP:
202 case SNDRV_PCM_TRIGGER_SUSPEND:
203 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
204 break;
205
206 default:
207 return -EINVAL;
208 }
209
210 return 0;
211}
212
213static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *component,
214 struct snd_pcm_substream *substream)
215{
216 struct qmc_dai_prtd *prtd = substream->runtime->private_data;
217
218 return bytes_to_frames(runtime: substream->runtime,
219 size: prtd->period_ptr_ended - prtd->dma_buffer_start);
220}
221
222static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
223 const struct of_phandle_args *args,
224 const char **dai_name)
225{
226 struct qmc_audio *qmc_audio = dev_get_drvdata(dev: component->dev);
227 struct snd_soc_dai_driver *dai_driver;
228 int id = args->args[0];
229 int i;
230
231 for (i = 0; i < qmc_audio->num_dais; i++) {
232 dai_driver = qmc_audio->dai_drivers + i;
233 if (dai_driver->id == id) {
234 *dai_name = dai_driver->name;
235 return 0;
236 }
237 }
238
239 return -EINVAL;
240}
241
242static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
243 .info = SNDRV_PCM_INFO_MMAP |
244 SNDRV_PCM_INFO_MMAP_VALID |
245 SNDRV_PCM_INFO_INTERLEAVED |
246 SNDRV_PCM_INFO_PAUSE,
247 .period_bytes_min = 32,
248 .period_bytes_max = 64*1024,
249 .periods_min = 2,
250 .periods_max = 2*1024,
251 .buffer_bytes_max = 64*1024,
252};
253
254static int qmc_audio_pcm_open(struct snd_soc_component *component,
255 struct snd_pcm_substream *substream)
256{
257 struct snd_pcm_runtime *runtime = substream->runtime;
258 struct qmc_dai_prtd *prtd;
259 int ret;
260
261 snd_soc_set_runtime_hwparams(substream, hw: &qmc_audio_pcm_hardware);
262
263 /* ensure that buffer size is a multiple of period size */
264 ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
265 if (ret < 0)
266 return ret;
267
268 prtd = kzalloc(size: sizeof(*prtd), GFP_KERNEL);
269 if (prtd == NULL)
270 return -ENOMEM;
271
272 runtime->private_data = prtd;
273
274 return 0;
275}
276
277static int qmc_audio_pcm_close(struct snd_soc_component *component,
278 struct snd_pcm_substream *substream)
279{
280 struct qmc_dai_prtd *prtd = substream->runtime->private_data;
281
282 kfree(objp: prtd);
283 return 0;
284}
285
286static const struct snd_soc_component_driver qmc_audio_soc_platform = {
287 .open = qmc_audio_pcm_open,
288 .close = qmc_audio_pcm_close,
289 .hw_params = qmc_audio_pcm_hw_params,
290 .trigger = qmc_audio_pcm_trigger,
291 .pointer = qmc_audio_pcm_pointer,
292 .pcm_construct = qmc_audio_pcm_construct,
293 .of_xlate_dai_name = qmc_audio_of_xlate_dai_name,
294};
295
296static unsigned int qmc_dai_get_index(struct snd_soc_dai *dai)
297{
298 struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
299
300 return dai->driver - qmc_audio->dai_drivers;
301}
302
303static struct qmc_dai *qmc_dai_get_data(struct snd_soc_dai *dai)
304{
305 struct qmc_audio *qmc_audio = snd_soc_dai_get_drvdata(dai);
306 unsigned int index;
307
308 index = qmc_dai_get_index(dai);
309 if (index > qmc_audio->num_dais)
310 return NULL;
311
312 return qmc_audio->dais + index;
313}
314
315/*
316 * The constraints for format/channel is to match with the number of 8bit
317 * time-slots available.
318 */
319static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai,
320 struct snd_pcm_hw_params *params,
321 unsigned int nb_ts)
322{
323 struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
324 snd_pcm_format_t format = params_format(p: params);
325 struct snd_interval ch = {0};
326
327 switch (snd_pcm_format_physical_width(format)) {
328 case 8:
329 ch.max = nb_ts;
330 break;
331 case 16:
332 ch.max = nb_ts/2;
333 break;
334 case 32:
335 ch.max = nb_ts/4;
336 break;
337 case 64:
338 ch.max = nb_ts/8;
339 break;
340 default:
341 dev_err(qmc_dai->dev, "format physical width %u not supported\n",
342 snd_pcm_format_physical_width(format));
343 return -EINVAL;
344 }
345
346 ch.min = ch.max ? 1 : 0;
347
348 return snd_interval_refine(i: c, v: &ch);
349}
350
351static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
352 struct snd_pcm_hw_rule *rule)
353{
354 struct qmc_dai *qmc_dai = rule->private;
355
356 return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, nb_ts: qmc_dai->nb_tx_ts);
357}
358
359static int qmc_dai_hw_rule_capture_channels_by_format(
360 struct snd_pcm_hw_params *params,
361 struct snd_pcm_hw_rule *rule)
362{
363 struct qmc_dai *qmc_dai = rule->private;
364
365 return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, nb_ts: qmc_dai->nb_rx_ts);
366}
367
368static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai,
369 struct snd_pcm_hw_params *params,
370 unsigned int nb_ts)
371{
372 struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
373 unsigned int channels = params_channels(p: params);
374 unsigned int slot_width;
375 snd_pcm_format_t format;
376 struct snd_mask f_new;
377
378 if (!channels || channels > nb_ts) {
379 dev_err(qmc_dai->dev, "channels %u not supported\n",
380 nb_ts);
381 return -EINVAL;
382 }
383
384 slot_width = (nb_ts / channels) * 8;
385
386 snd_mask_none(mask: &f_new);
387 pcm_for_each_format(format) {
388 if (snd_mask_test_format(mask: f_old, format)) {
389 if (snd_pcm_format_physical_width(format) <= slot_width)
390 snd_mask_set_format(mask: &f_new, format);
391 }
392 }
393
394 return snd_mask_refine(mask: f_old, v: &f_new);
395}
396
397static int qmc_dai_hw_rule_playback_format_by_channels(
398 struct snd_pcm_hw_params *params,
399 struct snd_pcm_hw_rule *rule)
400{
401 struct qmc_dai *qmc_dai = rule->private;
402
403 return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, nb_ts: qmc_dai->nb_tx_ts);
404}
405
406static int qmc_dai_hw_rule_capture_format_by_channels(
407 struct snd_pcm_hw_params *params,
408 struct snd_pcm_hw_rule *rule)
409{
410 struct qmc_dai *qmc_dai = rule->private;
411
412 return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, nb_ts: qmc_dai->nb_rx_ts);
413}
414
415static int qmc_dai_startup(struct snd_pcm_substream *substream,
416 struct snd_soc_dai *dai)
417{
418 struct qmc_dai_prtd *prtd = substream->runtime->private_data;
419 snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
420 snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
421 struct qmc_dai *qmc_dai;
422 unsigned int frame_bits;
423 int ret;
424
425 qmc_dai = qmc_dai_get_data(dai);
426 if (!qmc_dai) {
427 dev_err(dai->dev, "Invalid dai\n");
428 return -EINVAL;
429 }
430
431 prtd->qmc_dai = qmc_dai;
432
433 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
434 hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
435 hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
436 frame_bits = qmc_dai->nb_rx_ts * 8;
437 } else {
438 hw_rule_channels_by_format = qmc_dai_hw_rule_playback_channels_by_format;
439 hw_rule_format_by_channels = qmc_dai_hw_rule_playback_format_by_channels;
440 frame_bits = qmc_dai->nb_tx_ts * 8;
441 }
442
443 ret = snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, SNDRV_PCM_HW_PARAM_CHANNELS,
444 func: hw_rule_channels_by_format, private: qmc_dai,
445 SNDRV_PCM_HW_PARAM_FORMAT, -1);
446 if (ret) {
447 dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
448 return ret;
449 }
450
451 ret = snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, SNDRV_PCM_HW_PARAM_FORMAT,
452 func: hw_rule_format_by_channels, private: qmc_dai,
453 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
454 if (ret) {
455 dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
456 return ret;
457 }
458
459 ret = snd_pcm_hw_constraint_single(runtime: substream->runtime,
460 SNDRV_PCM_HW_PARAM_FRAME_BITS,
461 val: frame_bits);
462 if (ret < 0) {
463 dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
464 return ret;
465 }
466
467 return 0;
468}
469
470static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
471 struct snd_pcm_hw_params *params,
472 struct snd_soc_dai *dai)
473{
474 struct qmc_chan_param chan_param = {0};
475 struct qmc_dai *qmc_dai;
476 int ret;
477
478 qmc_dai = qmc_dai_get_data(dai);
479 if (!qmc_dai) {
480 dev_err(dai->dev, "Invalid dai\n");
481 return -EINVAL;
482 }
483
484 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
485 chan_param.mode = QMC_TRANSPARENT;
486 chan_param.transp.max_rx_buf_size = params_period_bytes(p: params);
487 ret = qmc_chan_set_param(chan: qmc_dai->qmc_chan, param: &chan_param);
488 if (ret) {
489 dev_err(dai->dev, "set param failed %d\n",
490 ret);
491 return ret;
492 }
493 }
494
495 return 0;
496}
497
498static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
499 struct snd_soc_dai *dai)
500{
501 struct qmc_dai *qmc_dai;
502 int direction;
503 int ret;
504
505 qmc_dai = qmc_dai_get_data(dai);
506 if (!qmc_dai) {
507 dev_err(dai->dev, "Invalid dai\n");
508 return -EINVAL;
509 }
510
511 direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
512 QMC_CHAN_WRITE : QMC_CHAN_READ;
513
514 switch (cmd) {
515 case SNDRV_PCM_TRIGGER_START:
516 case SNDRV_PCM_TRIGGER_RESUME:
517 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
518 ret = qmc_chan_start(chan: qmc_dai->qmc_chan, direction);
519 if (ret)
520 return ret;
521 break;
522
523 case SNDRV_PCM_TRIGGER_STOP:
524 ret = qmc_chan_stop(chan: qmc_dai->qmc_chan, direction);
525 if (ret)
526 return ret;
527 ret = qmc_chan_reset(chan: qmc_dai->qmc_chan, direction);
528 if (ret)
529 return ret;
530 break;
531
532 case SNDRV_PCM_TRIGGER_SUSPEND:
533 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
534 ret = qmc_chan_stop(chan: qmc_dai->qmc_chan, direction);
535 if (ret)
536 return ret;
537 break;
538
539 default:
540 return -EINVAL;
541 }
542
543 return 0;
544}
545
546static const struct snd_soc_dai_ops qmc_dai_ops = {
547 .startup = qmc_dai_startup,
548 .trigger = qmc_dai_trigger,
549 .hw_params = qmc_dai_hw_params,
550};
551
552static u64 qmc_audio_formats(u8 nb_ts)
553{
554 unsigned int format_width;
555 unsigned int chan_width;
556 snd_pcm_format_t format;
557 u64 formats_mask;
558
559 if (!nb_ts)
560 return 0;
561
562 formats_mask = 0;
563 chan_width = nb_ts * 8;
564 pcm_for_each_format(format) {
565 /*
566 * Support format other than little-endian (ie big-endian or
567 * without endianness such as 8bit formats)
568 */
569 if (snd_pcm_format_little_endian(format) == 1)
570 continue;
571
572 /* Support physical width multiple of 8bit */
573 format_width = snd_pcm_format_physical_width(format);
574 if (format_width == 0 || format_width % 8)
575 continue;
576
577 /*
578 * And support physical width that can fit N times in the
579 * channel
580 */
581 if (format_width > chan_width || chan_width % format_width)
582 continue;
583
584 formats_mask |= pcm_format_to_bits(pcm_format: format);
585 }
586 return formats_mask;
587}
588
589static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
590 struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver)
591{
592 struct qmc_chan_info info;
593 u32 val;
594 int ret;
595
596 qmc_dai->dev = qmc_audio->dev;
597
598 ret = of_property_read_u32(np, propname: "reg", out_value: &val);
599 if (ret) {
600 dev_err(qmc_audio->dev, "%pOF: failed to read reg\n", np);
601 return ret;
602 }
603 qmc_dai->id = val;
604
605 qmc_dai->name = devm_kasprintf(dev: qmc_audio->dev, GFP_KERNEL, fmt: "%s.%d",
606 np->parent->name, qmc_dai->id);
607
608 qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(dev: qmc_audio->dev, np,
609 phandle_name: "fsl,qmc-chan");
610 if (IS_ERR(ptr: qmc_dai->qmc_chan)) {
611 ret = PTR_ERR(ptr: qmc_dai->qmc_chan);
612 return dev_err_probe(dev: qmc_audio->dev, err: ret,
613 fmt: "dai %d get QMC channel failed\n", qmc_dai->id);
614 }
615
616 qmc_soc_dai_driver->id = qmc_dai->id;
617 qmc_soc_dai_driver->name = qmc_dai->name;
618
619 ret = qmc_chan_get_info(chan: qmc_dai->qmc_chan, info: &info);
620 if (ret) {
621 dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
622 qmc_dai->id, ret);
623 return ret;
624 }
625 dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
626 qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
627
628 if (info.mode != QMC_TRANSPARENT) {
629 dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
630 qmc_dai->id, info.mode);
631 return -EINVAL;
632 }
633 qmc_dai->nb_tx_ts = info.nb_tx_ts;
634 qmc_dai->nb_rx_ts = info.nb_rx_ts;
635
636 qmc_soc_dai_driver->playback.channels_min = 0;
637 qmc_soc_dai_driver->playback.channels_max = 0;
638 if (qmc_dai->nb_tx_ts) {
639 qmc_soc_dai_driver->playback.channels_min = 1;
640 qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
641 }
642 qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_ts: qmc_dai->nb_tx_ts);
643
644 qmc_soc_dai_driver->capture.channels_min = 0;
645 qmc_soc_dai_driver->capture.channels_max = 0;
646 if (qmc_dai->nb_rx_ts) {
647 qmc_soc_dai_driver->capture.channels_min = 1;
648 qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
649 }
650 qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_ts: qmc_dai->nb_rx_ts);
651
652 qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(rate: info.tx_fs_rate);
653 qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
654 qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
655 qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rate: info.rx_fs_rate);
656 qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
657 qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
658
659 qmc_soc_dai_driver->ops = &qmc_dai_ops;
660
661 return 0;
662}
663
664static int qmc_audio_probe(struct platform_device *pdev)
665{
666 struct device_node *np = pdev->dev.of_node;
667 struct qmc_audio *qmc_audio;
668 struct device_node *child;
669 unsigned int i;
670 int ret;
671
672 qmc_audio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*qmc_audio), GFP_KERNEL);
673 if (!qmc_audio)
674 return -ENOMEM;
675
676 qmc_audio->dev = &pdev->dev;
677
678 qmc_audio->num_dais = of_get_available_child_count(np);
679 if (qmc_audio->num_dais) {
680 qmc_audio->dais = devm_kcalloc(dev: &pdev->dev, n: qmc_audio->num_dais,
681 size: sizeof(*qmc_audio->dais),
682 GFP_KERNEL);
683 if (!qmc_audio->dais)
684 return -ENOMEM;
685
686 qmc_audio->dai_drivers = devm_kcalloc(dev: &pdev->dev, n: qmc_audio->num_dais,
687 size: sizeof(*qmc_audio->dai_drivers),
688 GFP_KERNEL);
689 if (!qmc_audio->dai_drivers)
690 return -ENOMEM;
691 }
692
693 i = 0;
694 for_each_available_child_of_node(np, child) {
695 ret = qmc_audio_dai_parse(qmc_audio, np: child,
696 qmc_dai: qmc_audio->dais + i,
697 qmc_soc_dai_driver: qmc_audio->dai_drivers + i);
698 if (ret) {
699 of_node_put(node: child);
700 return ret;
701 }
702 i++;
703 }
704
705
706 platform_set_drvdata(pdev, data: qmc_audio);
707
708 ret = devm_snd_soc_register_component(dev: qmc_audio->dev,
709 component_driver: &qmc_audio_soc_platform,
710 dai_drv: qmc_audio->dai_drivers,
711 num_dai: qmc_audio->num_dais);
712 if (ret)
713 return ret;
714
715 return 0;
716}
717
718static const struct of_device_id qmc_audio_id_table[] = {
719 { .compatible = "fsl,qmc-audio" },
720 {} /* sentinel */
721};
722MODULE_DEVICE_TABLE(of, qmc_audio_id_table);
723
724static struct platform_driver qmc_audio_driver = {
725 .driver = {
726 .name = "fsl-qmc-audio",
727 .of_match_table = of_match_ptr(qmc_audio_id_table),
728 },
729 .probe = qmc_audio_probe,
730};
731module_platform_driver(qmc_audio_driver);
732
733MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
734MODULE_DESCRIPTION("CPM/QE QMC audio driver");
735MODULE_LICENSE("GPL");
736

source code of linux/sound/soc/fsl/fsl_qmc_audio.c