1// SPDX-License-Identifier: GPL-2.0
2//
3// test-component.c -- Test Audio Component driver
4//
5// Copyright (C) 2020 Renesas Electronics Corporation
6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
7
8#include <linux/slab.h>
9#include <linux/of.h>
10#include <linux/of_graph.h>
11#include <linux/module.h>
12#include <linux/workqueue.h>
13#include <sound/pcm.h>
14#include <sound/soc.h>
15
16#define TEST_NAME_LEN 32
17struct test_dai_name {
18 char name[TEST_NAME_LEN];
19 char name_playback[TEST_NAME_LEN];
20 char name_capture[TEST_NAME_LEN];
21};
22
23struct test_priv {
24 struct device *dev;
25 struct snd_pcm_substream *substream;
26 struct delayed_work dwork;
27 struct snd_soc_component_driver *component_driver;
28 struct snd_soc_dai_driver *dai_driver;
29 struct test_dai_name *name;
30};
31
32struct test_adata {
33 u32 is_cpu:1;
34 u32 cmp_v:1;
35 u32 dai_v:1;
36};
37
38#define mile_stone(d) dev_info((d)->dev, "%s() : %s", __func__, (d)->driver->name)
39#define mile_stone_x(dev) dev_info(dev, "%s()", __func__)
40
41static int test_dai_set_sysclk(struct snd_soc_dai *dai,
42 int clk_id, unsigned int freq, int dir)
43{
44 mile_stone(dai);
45
46 return 0;
47}
48
49static int test_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
50 unsigned int freq_in, unsigned int freq_out)
51{
52 mile_stone(dai);
53
54 return 0;
55}
56
57static int test_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
58{
59 mile_stone(dai);
60
61 return 0;
62}
63
64static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
65{
66 unsigned int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
67 unsigned int clock = fmt & SND_SOC_DAIFMT_CLOCK_MASK;
68 unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
69 unsigned int master = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
70 char *str;
71
72 dev_info(dai->dev, "name : %s", dai->name);
73
74 str = "unknown";
75 switch (format) {
76 case SND_SOC_DAIFMT_I2S:
77 str = "i2s";
78 break;
79 case SND_SOC_DAIFMT_RIGHT_J:
80 str = "right_j";
81 break;
82 case SND_SOC_DAIFMT_LEFT_J:
83 str = "left_j";
84 break;
85 case SND_SOC_DAIFMT_DSP_A:
86 str = "dsp_a";
87 break;
88 case SND_SOC_DAIFMT_DSP_B:
89 str = "dsp_b";
90 break;
91 case SND_SOC_DAIFMT_AC97:
92 str = "ac97";
93 break;
94 case SND_SOC_DAIFMT_PDM:
95 str = "pdm";
96 break;
97 }
98 dev_info(dai->dev, "format : %s", str);
99
100 if (clock == SND_SOC_DAIFMT_CONT)
101 str = "continuous";
102 else
103 str = "gated";
104 dev_info(dai->dev, "clock : %s", str);
105
106 str = "unknown";
107 switch (master) {
108 case SND_SOC_DAIFMT_BP_FP:
109 str = "clk provider, frame provider";
110 break;
111 case SND_SOC_DAIFMT_BC_FP:
112 str = "clk consumer, frame provider";
113 break;
114 case SND_SOC_DAIFMT_BP_FC:
115 str = "clk provider, frame consumer";
116 break;
117 case SND_SOC_DAIFMT_BC_FC:
118 str = "clk consumer, frame consumer";
119 break;
120 }
121 dev_info(dai->dev, "clock : codec is %s", str);
122
123 str = "unknown";
124 switch (inv) {
125 case SND_SOC_DAIFMT_NB_NF:
126 str = "normal bit, normal frame";
127 break;
128 case SND_SOC_DAIFMT_NB_IF:
129 str = "normal bit, invert frame";
130 break;
131 case SND_SOC_DAIFMT_IB_NF:
132 str = "invert bit, normal frame";
133 break;
134 case SND_SOC_DAIFMT_IB_IF:
135 str = "invert bit, invert frame";
136 break;
137 }
138 dev_info(dai->dev, "signal : %s", str);
139
140 return 0;
141}
142
143static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
144{
145 mile_stone(dai);
146
147 return 0;
148}
149
150static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
151{
152 mile_stone(dai);
153
154 return 0;
155}
156
157static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
158{
159 mile_stone(dai);
160}
161
162static int test_dai_hw_params(struct snd_pcm_substream *substream,
163 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
164{
165 mile_stone(dai);
166
167 return 0;
168}
169
170static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
171{
172 mile_stone(dai);
173
174 return 0;
175}
176
177static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
178{
179 mile_stone(dai);
180
181 return 0;
182}
183
184static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream,
185 int cmd, struct snd_soc_dai *dai)
186{
187 mile_stone(dai);
188
189 return 0;
190}
191
192static u64 test_dai_formats =
193 /*
194 * Select below from Sound Card, not auto
195 * SND_SOC_POSSIBLE_DAIFMT_BP_FP
196 * SND_SOC_POSSIBLE_DAIFMT_BC_FP
197 * SND_SOC_POSSIBLE_DAIFMT_BP_FC
198 * SND_SOC_POSSIBLE_DAIFMT_BC_FC
199 */
200 SND_SOC_POSSIBLE_DAIFMT_I2S |
201 SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
202 SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
203 SND_SOC_POSSIBLE_DAIFMT_DSP_A |
204 SND_SOC_POSSIBLE_DAIFMT_DSP_B |
205 SND_SOC_POSSIBLE_DAIFMT_AC97 |
206 SND_SOC_POSSIBLE_DAIFMT_PDM |
207 SND_SOC_POSSIBLE_DAIFMT_NB_NF |
208 SND_SOC_POSSIBLE_DAIFMT_NB_IF |
209 SND_SOC_POSSIBLE_DAIFMT_IB_NF |
210 SND_SOC_POSSIBLE_DAIFMT_IB_IF;
211
212static const struct snd_soc_dai_ops test_ops = {
213 .set_fmt = test_dai_set_fmt,
214 .startup = test_dai_startup,
215 .shutdown = test_dai_shutdown,
216 .auto_selectable_formats = &test_dai_formats,
217 .num_auto_selectable_formats = 1,
218};
219
220static const struct snd_soc_dai_ops test_verbose_ops = {
221 .set_sysclk = test_dai_set_sysclk,
222 .set_pll = test_dai_set_pll,
223 .set_clkdiv = test_dai_set_clkdiv,
224 .set_fmt = test_dai_set_fmt,
225 .mute_stream = test_dai_mute_stream,
226 .startup = test_dai_startup,
227 .shutdown = test_dai_shutdown,
228 .hw_params = test_dai_hw_params,
229 .hw_free = test_dai_hw_free,
230 .trigger = test_dai_trigger,
231 .bespoke_trigger = test_dai_bespoke_trigger,
232 .auto_selectable_formats = &test_dai_formats,
233 .num_auto_selectable_formats = 1,
234};
235
236#define STUB_RATES SNDRV_PCM_RATE_8000_384000
237#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
238 SNDRV_PCM_FMTBIT_U8 | \
239 SNDRV_PCM_FMTBIT_S16_LE | \
240 SNDRV_PCM_FMTBIT_U16_LE | \
241 SNDRV_PCM_FMTBIT_S24_LE | \
242 SNDRV_PCM_FMTBIT_S24_3LE | \
243 SNDRV_PCM_FMTBIT_U24_LE | \
244 SNDRV_PCM_FMTBIT_S32_LE | \
245 SNDRV_PCM_FMTBIT_U32_LE)
246
247static int test_component_probe(struct snd_soc_component *component)
248{
249 mile_stone(component);
250
251 return 0;
252}
253
254static void test_component_remove(struct snd_soc_component *component)
255{
256 mile_stone(component);
257}
258
259static int test_component_suspend(struct snd_soc_component *component)
260{
261 mile_stone(component);
262
263 return 0;
264}
265
266static int test_component_resume(struct snd_soc_component *component)
267{
268 mile_stone(component);
269
270 return 0;
271}
272
273#define PREALLOC_BUFFER (32 * 1024)
274static int test_component_pcm_construct(struct snd_soc_component *component,
275 struct snd_soc_pcm_runtime *rtd)
276{
277 mile_stone(component);
278
279 snd_pcm_set_managed_buffer_all(
280 pcm: rtd->pcm,
281 SNDRV_DMA_TYPE_DEV,
282 data: rtd->card->snd_card->dev,
283 PREALLOC_BUFFER, PREALLOC_BUFFER);
284
285 return 0;
286}
287
288static void test_component_pcm_destruct(struct snd_soc_component *component,
289 struct snd_pcm *pcm)
290{
291 mile_stone(component);
292}
293
294static int test_component_set_sysclk(struct snd_soc_component *component,
295 int clk_id, int source, unsigned int freq, int dir)
296{
297 mile_stone(component);
298
299 return 0;
300}
301
302static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
303 int source, unsigned int freq_in, unsigned int freq_out)
304{
305 mile_stone(component);
306
307 return 0;
308}
309
310static int test_component_set_jack(struct snd_soc_component *component,
311 struct snd_soc_jack *jack, void *data)
312{
313 mile_stone(component);
314
315 return 0;
316}
317
318static void test_component_seq_notifier(struct snd_soc_component *component,
319 enum snd_soc_dapm_type type, int subseq)
320{
321 mile_stone(component);
322}
323
324static int test_component_stream_event(struct snd_soc_component *component, int event)
325{
326 mile_stone(component);
327
328 return 0;
329}
330
331static int test_component_set_bias_level(struct snd_soc_component *component,
332 enum snd_soc_bias_level level)
333{
334 mile_stone(component);
335
336 return 0;
337}
338
339static const struct snd_pcm_hardware test_component_hardware = {
340 /* Random values to keep userspace happy when checking constraints */
341 .info = SNDRV_PCM_INFO_INTERLEAVED |
342 SNDRV_PCM_INFO_MMAP |
343 SNDRV_PCM_INFO_MMAP_VALID,
344 .buffer_bytes_max = 32 * 1024,
345 .period_bytes_min = 32,
346 .period_bytes_max = 8192,
347 .periods_min = 1,
348 .periods_max = 128,
349 .fifo_size = 256,
350};
351
352static int test_component_open(struct snd_soc_component *component,
353 struct snd_pcm_substream *substream)
354{
355 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
356
357 mile_stone(component);
358
359 /* BE's dont need dummy params */
360 if (!rtd->dai_link->no_pcm)
361 snd_soc_set_runtime_hwparams(substream, hw: &test_component_hardware);
362
363 return 0;
364}
365
366static int test_component_close(struct snd_soc_component *component,
367 struct snd_pcm_substream *substream)
368{
369 mile_stone(component);
370
371 return 0;
372}
373
374static int test_component_ioctl(struct snd_soc_component *component,
375 struct snd_pcm_substream *substream,
376 unsigned int cmd, void *arg)
377{
378 mile_stone(component);
379
380 return 0;
381}
382
383static int test_component_hw_params(struct snd_soc_component *component,
384 struct snd_pcm_substream *substream,
385 struct snd_pcm_hw_params *params)
386{
387 mile_stone(component);
388
389 return 0;
390}
391
392static int test_component_hw_free(struct snd_soc_component *component,
393 struct snd_pcm_substream *substream)
394{
395 mile_stone(component);
396
397 return 0;
398}
399
400static int test_component_prepare(struct snd_soc_component *component,
401 struct snd_pcm_substream *substream)
402{
403 mile_stone(component);
404
405 return 0;
406}
407
408static void test_component_timer_stop(struct test_priv *priv)
409{
410 cancel_delayed_work(dwork: &priv->dwork);
411}
412
413static void test_component_timer_start(struct test_priv *priv)
414{
415 schedule_delayed_work(dwork: &priv->dwork, delay: msecs_to_jiffies(m: 10));
416}
417
418static void test_component_dwork(struct work_struct *work)
419{
420 struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
421
422 if (priv->substream)
423 snd_pcm_period_elapsed(substream: priv->substream);
424
425 test_component_timer_start(priv);
426}
427
428static int test_component_trigger(struct snd_soc_component *component,
429 struct snd_pcm_substream *substream, int cmd)
430{
431 struct test_priv *priv = dev_get_drvdata(dev: component->dev);
432
433 mile_stone(component);
434
435 switch (cmd) {
436 case SNDRV_PCM_TRIGGER_START:
437 test_component_timer_start(priv);
438 priv->substream = substream; /* set substream later */
439 break;
440 case SNDRV_PCM_TRIGGER_STOP:
441 priv->substream = NULL;
442 test_component_timer_stop(priv);
443 }
444
445 return 0;
446}
447
448static int test_component_sync_stop(struct snd_soc_component *component,
449 struct snd_pcm_substream *substream)
450{
451 mile_stone(component);
452
453 return 0;
454}
455
456static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
457 struct snd_pcm_substream *substream)
458{
459 struct snd_pcm_runtime *runtime = substream->runtime;
460 static int pointer;
461
462 if (!runtime)
463 return 0;
464
465 pointer += 10;
466 if (pointer > PREALLOC_BUFFER)
467 pointer = 0;
468
469 /* mile_stone(component); */
470
471 return bytes_to_frames(runtime, size: pointer);
472}
473
474static int test_component_get_time_info(struct snd_soc_component *component,
475 struct snd_pcm_substream *substream,
476 struct timespec64 *system_ts,
477 struct timespec64 *audio_ts,
478 struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
479 struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
480{
481 mile_stone(component);
482
483 return 0;
484}
485
486static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
487 struct snd_pcm_hw_params *params)
488{
489 mile_stone_x(rtd->dev);
490
491 return 0;
492}
493
494/* CPU */
495static const struct test_adata test_cpu = { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
496static const struct test_adata test_cpu_vv = { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
497static const struct test_adata test_cpu_nv = { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
498static const struct test_adata test_cpu_vn = { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
499/* Codec */
500static const struct test_adata test_codec = { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
501static const struct test_adata test_codec_vv = { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
502static const struct test_adata test_codec_nv = { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
503static const struct test_adata test_codec_vn = { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
504
505static const struct of_device_id test_of_match[] = {
506 { .compatible = "test-cpu", .data = (void *)&test_cpu, },
507 { .compatible = "test-cpu-verbose", .data = (void *)&test_cpu_vv, },
508 { .compatible = "test-cpu-verbose-dai", .data = (void *)&test_cpu_nv, },
509 { .compatible = "test-cpu-verbose-component", .data = (void *)&test_cpu_vn, },
510 { .compatible = "test-codec", .data = (void *)&test_codec, },
511 { .compatible = "test-codec-verbose", .data = (void *)&test_codec_vv, },
512 { .compatible = "test-codec-verbose-dai", .data = (void *)&test_codec_nv, },
513 { .compatible = "test-codec-verbose-component", .data = (void *)&test_codec_vn, },
514 {},
515};
516MODULE_DEVICE_TABLE(of, test_of_match);
517
518static const struct snd_soc_dapm_widget widgets[] = {
519 /*
520 * FIXME
521 *
522 * Just IN/OUT is OK for now,
523 * but need to be updated ?
524 */
525 SND_SOC_DAPM_INPUT("IN"),
526 SND_SOC_DAPM_OUTPUT("OUT"),
527};
528
529static int test_driver_probe(struct platform_device *pdev)
530{
531 struct device *dev = &pdev->dev;
532 struct device_node *node = dev->of_node;
533 struct device_node *ep;
534 const struct test_adata *adata = of_device_get_match_data(dev: &pdev->dev);
535 struct snd_soc_component_driver *cdriv;
536 struct snd_soc_dai_driver *ddriv;
537 struct test_dai_name *dname;
538 struct test_priv *priv;
539 int num, ret, i;
540
541 num = of_graph_get_endpoint_count(np: node);
542 if (!num) {
543 dev_err(dev, "no port exits\n");
544 return -EINVAL;
545 }
546
547 priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL);
548 cdriv = devm_kzalloc(dev, size: sizeof(*cdriv), GFP_KERNEL);
549 ddriv = devm_kzalloc(dev, size: sizeof(*ddriv) * num, GFP_KERNEL);
550 dname = devm_kzalloc(dev, size: sizeof(*dname) * num, GFP_KERNEL);
551 if (!priv || !cdriv || !ddriv || !dname || !adata)
552 return -EINVAL;
553
554 priv->dev = dev;
555 priv->component_driver = cdriv;
556 priv->dai_driver = ddriv;
557 priv->name = dname;
558
559 INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
560 dev_set_drvdata(dev, data: priv);
561
562 if (adata->is_cpu) {
563 cdriv->name = "test_cpu";
564 cdriv->pcm_construct = test_component_pcm_construct;
565 cdriv->pointer = test_component_pointer;
566 cdriv->trigger = test_component_trigger;
567 cdriv->legacy_dai_naming = 1;
568 } else {
569 cdriv->name = "test_codec";
570 cdriv->idle_bias_on = 1;
571 cdriv->endianness = 1;
572 }
573
574 cdriv->open = test_component_open;
575 cdriv->dapm_widgets = widgets;
576 cdriv->num_dapm_widgets = ARRAY_SIZE(widgets);
577
578 if (adata->cmp_v) {
579 cdriv->probe = test_component_probe;
580 cdriv->remove = test_component_remove;
581 cdriv->suspend = test_component_suspend;
582 cdriv->resume = test_component_resume;
583 cdriv->set_sysclk = test_component_set_sysclk;
584 cdriv->set_pll = test_component_set_pll;
585 cdriv->set_jack = test_component_set_jack;
586 cdriv->seq_notifier = test_component_seq_notifier;
587 cdriv->stream_event = test_component_stream_event;
588 cdriv->set_bias_level = test_component_set_bias_level;
589 cdriv->close = test_component_close;
590 cdriv->ioctl = test_component_ioctl;
591 cdriv->hw_params = test_component_hw_params;
592 cdriv->hw_free = test_component_hw_free;
593 cdriv->prepare = test_component_prepare;
594 cdriv->sync_stop = test_component_sync_stop;
595 cdriv->get_time_info = test_component_get_time_info;
596 cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup;
597
598 if (adata->is_cpu)
599 cdriv->pcm_destruct = test_component_pcm_destruct;
600 }
601
602 i = 0;
603 for_each_endpoint_of_node(node, ep) {
604 snprintf(buf: dname[i].name, TEST_NAME_LEN, fmt: "%s.%d", node->name, i);
605 ddriv[i].name = dname[i].name;
606
607 snprintf(buf: dname[i].name_playback, TEST_NAME_LEN, fmt: "DAI%d Playback", i);
608 ddriv[i].playback.stream_name = dname[i].name_playback;
609 ddriv[i].playback.channels_min = 1;
610 ddriv[i].playback.channels_max = 384;
611 ddriv[i].playback.rates = STUB_RATES;
612 ddriv[i].playback.formats = STUB_FORMATS;
613
614 snprintf(buf: dname[i].name_capture, TEST_NAME_LEN, fmt: "DAI%d Capture", i);
615 ddriv[i].capture.stream_name = dname[i].name_capture;
616 ddriv[i].capture.channels_min = 1;
617 ddriv[i].capture.channels_max = 384;
618 ddriv[i].capture.rates = STUB_RATES;
619 ddriv[i].capture.formats = STUB_FORMATS;
620
621 if (adata->dai_v)
622 ddriv[i].ops = &test_verbose_ops;
623 else
624 ddriv[i].ops = &test_ops;
625
626 i++;
627 }
628
629 ret = devm_snd_soc_register_component(dev, component_driver: cdriv, dai_drv: ddriv, num_dai: num);
630 if (ret < 0)
631 return ret;
632
633 mile_stone_x(dev);
634
635 return 0;
636}
637
638static void test_driver_remove(struct platform_device *pdev)
639{
640 mile_stone_x(&pdev->dev);
641}
642
643static struct platform_driver test_driver = {
644 .driver = {
645 .name = "test-component",
646 .of_match_table = test_of_match,
647 },
648 .probe = test_driver_probe,
649 .remove_new = test_driver_remove,
650};
651module_platform_driver(test_driver);
652
653MODULE_ALIAS("platform:asoc-test-component");
654MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
655MODULE_DESCRIPTION("ASoC Test Component");
656MODULE_LICENSE("GPL v2");
657

source code of linux/sound/soc/generic/test-component.c