File Index
Files and directories
Sign in
FILE

test-component.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/generic/test-component.c C
  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_set_tdm_slot(struct snd_soc_dai *dai,
144				 unsigned int tx_mask, unsigned int rx_mask,
145				 int slots, int slot_width)
146{
147	dev_info(dai->dev, "set tdm slot: tx_mask=0x%08X, rx_mask=0x%08X, slots=%d, slot_width=%d\n",
148		 tx_mask, rx_mask, slots, slot_width);
149	return 0;
150}
151
152static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
153{
154	mile_stone(dai);
155
156	return 0;
157}
158
159static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
160{
161	mile_stone(dai);
162
163	return 0;
164}
165
166static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
167{
168	mile_stone(dai);
169}
170
171static int test_dai_hw_params(struct snd_pcm_substream *substream,
172			      struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
173{
174	mile_stone(dai);
175
176	return 0;
177}
178
179static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
180{
181	mile_stone(dai);
182
183	return 0;
184}
185
186static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
187{
188	mile_stone(dai);
189
190	return 0;
191}
192
193static const u64 test_dai_formats =
194	/*
195	 * Select below from Sound Card, not auto
196	 *	SND_SOC_POSSIBLE_DAIFMT_BP_FP
197	 *	SND_SOC_POSSIBLE_DAIFMT_BC_FP
198	 *	SND_SOC_POSSIBLE_DAIFMT_BP_FC
199	 *	SND_SOC_POSSIBLE_DAIFMT_BC_FC
200	 */
201	SND_SOC_POSSIBLE_DAIFMT_I2S	|
202	SND_SOC_POSSIBLE_DAIFMT_RIGHT_J	|
203	SND_SOC_POSSIBLE_DAIFMT_LEFT_J	|
204	SND_SOC_POSSIBLE_DAIFMT_DSP_A	|
205	SND_SOC_POSSIBLE_DAIFMT_DSP_B	|
206	SND_SOC_POSSIBLE_DAIFMT_AC97	|
207	SND_SOC_POSSIBLE_DAIFMT_PDM	|
208	SND_SOC_POSSIBLE_DAIFMT_NB_NF	|
209	SND_SOC_POSSIBLE_DAIFMT_NB_IF	|
210	SND_SOC_POSSIBLE_DAIFMT_IB_NF	|
211	SND_SOC_POSSIBLE_DAIFMT_IB_IF;
212
213static const struct snd_soc_dai_ops test_ops = {
214	.set_fmt		= test_dai_set_fmt,
215	.set_tdm_slot		= test_dai_set_tdm_slot,
216	.startup		= test_dai_startup,
217	.shutdown		= test_dai_shutdown,
218	.auto_selectable_formats	= &test_dai_formats,
219	.num_auto_selectable_formats	= 1,
220};
221
222static const struct snd_soc_dai_ops test_verbose_ops = {
223	.set_sysclk		= test_dai_set_sysclk,
224	.set_pll		= test_dai_set_pll,
225	.set_clkdiv		= test_dai_set_clkdiv,
226	.set_fmt		= test_dai_set_fmt,
227	.set_tdm_slot		= test_dai_set_tdm_slot,
228	.mute_stream		= test_dai_mute_stream,
229	.startup		= test_dai_startup,
230	.shutdown		= test_dai_shutdown,
231	.hw_params		= test_dai_hw_params,
232	.hw_free		= test_dai_hw_free,
233	.trigger		= test_dai_trigger,
234	.auto_selectable_formats	= &test_dai_formats,
235	.num_auto_selectable_formats	= 1,
236};
237
238#define STUB_RATES	SNDRV_PCM_RATE_CONTINUOUS
239#define STUB_FORMATS	(SNDRV_PCM_FMTBIT_S8		| \
240			 SNDRV_PCM_FMTBIT_U8		| \
241			 SNDRV_PCM_FMTBIT_S16_LE	| \
242			 SNDRV_PCM_FMTBIT_U16_LE	| \
243			 SNDRV_PCM_FMTBIT_S24_LE	| \
244			 SNDRV_PCM_FMTBIT_S24_3LE	| \
245			 SNDRV_PCM_FMTBIT_U24_LE	| \
246			 SNDRV_PCM_FMTBIT_S32_LE	| \
247			 SNDRV_PCM_FMTBIT_U32_LE)
248
249static int test_component_probe(struct snd_soc_component *component)
250{
251	mile_stone(component);
252
253	return 0;
254}
255
256static void test_component_remove(struct snd_soc_component *component)
257{
258	mile_stone(component);
259}
260
261static int test_component_suspend(struct snd_soc_component *component)
262{
263	mile_stone(component);
264
265	return 0;
266}
267
268static int test_component_resume(struct snd_soc_component *component)
269{
270	mile_stone(component);
271
272	return 0;
273}
274
275#define PREALLOC_BUFFER		(32 * 1024)
276static int test_component_pcm_construct(struct snd_soc_component *component,
277					struct snd_soc_pcm_runtime *rtd)
278{
279	mile_stone(component);
280
281	snd_pcm_set_managed_buffer_all(
282		rtd->pcm,
283		SNDRV_DMA_TYPE_DEV,
284		rtd->card->snd_card->dev,
285		PREALLOC_BUFFER, PREALLOC_BUFFER);
286
287	return 0;
288}
289
290static void test_component_pcm_destruct(struct snd_soc_component *component,
291					struct snd_pcm *pcm)
292{
293	mile_stone(component);
294}
295
296static int test_component_set_sysclk(struct snd_soc_component *component,
297				     int clk_id, int source, unsigned int freq, int dir)
298{
299	mile_stone(component);
300
301	return 0;
302}
303
304static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
305				  int source, unsigned int freq_in, unsigned int freq_out)
306{
307	mile_stone(component);
308
309	return 0;
310}
311
312static int test_component_set_jack(struct snd_soc_component *component,
313				   struct snd_soc_jack *jack,  void *data)
314{
315	mile_stone(component);
316
317	return 0;
318}
319
320static void test_component_seq_notifier(struct snd_soc_component *component,
321					enum snd_soc_dapm_type type, int subseq)
322{
323	mile_stone(component);
324}
325
326static int test_component_stream_event(struct snd_soc_component *component, int event)
327{
328	mile_stone(component);
329
330	return 0;
331}
332
333static int test_component_set_bias_level(struct snd_soc_component *component,
334					 enum snd_soc_bias_level level)
335{
336	mile_stone(component);
337
338	return 0;
339}
340
341static const struct snd_pcm_hardware test_component_hardware = {
342	/* Random values to keep userspace happy when checking constraints */
343	.info			= SNDRV_PCM_INFO_INTERLEAVED	|
344				  SNDRV_PCM_INFO_MMAP		|
345				  SNDRV_PCM_INFO_MMAP_VALID,
346	.buffer_bytes_max	= 32 * 1024,
347	.period_bytes_min	= 32,
348	.period_bytes_max	= 8192,
349	.periods_min		= 1,
350	.periods_max		= 128,
351	.fifo_size		= 256,
352};
353
354static int test_component_open(struct snd_soc_component *component,
355			       struct snd_pcm_substream *substream)
356{
357	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
358
359	mile_stone(component);
360
361	/* BE's dont need dummy params */
362	if (!rtd->dai_link->no_pcm)
363		snd_soc_set_runtime_hwparams(substream, &test_component_hardware);
364
365	return 0;
366}
367
368static int test_component_close(struct snd_soc_component *component,
369				struct snd_pcm_substream *substream)
370{
371	mile_stone(component);
372
373	return 0;
374}
375
376static int test_component_ioctl(struct snd_soc_component *component,
377				struct snd_pcm_substream *substream,
378				unsigned int cmd, void *arg)
379{
380	mile_stone(component);
381
382	return 0;
383}
384
385static int test_component_hw_params(struct snd_soc_component *component,
386				    struct snd_pcm_substream *substream,
387				    struct snd_pcm_hw_params *params)
388{
389	mile_stone(component);
390
391	return 0;
392}
393
394static int test_component_hw_free(struct snd_soc_component *component,
395				  struct snd_pcm_substream *substream)
396{
397	mile_stone(component);
398
399	return 0;
400}
401
402static int test_component_prepare(struct snd_soc_component *component,
403				  struct snd_pcm_substream *substream)
404{
405	mile_stone(component);
406
407	return 0;
408}
409
410static void test_component_timer_stop(struct test_priv *priv)
411{
412	cancel_delayed_work(&priv->dwork);
413}
414
415static void test_component_timer_start(struct test_priv *priv)
416{
417	schedule_delayed_work(&priv->dwork, msecs_to_jiffies(10));
418}
419
420static void test_component_dwork(struct work_struct *work)
421{
422	struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
423
424	if (priv->substream)
425		snd_pcm_period_elapsed(priv->substream);
426
427	test_component_timer_start(priv);
428}
429
430static int test_component_trigger(struct snd_soc_component *component,
431				  struct snd_pcm_substream *substream, int cmd)
432{
433	struct test_priv *priv = dev_get_drvdata(component->dev);
434
435	mile_stone(component);
436
437	switch (cmd) {
438	case SNDRV_PCM_TRIGGER_START:
439		test_component_timer_start(priv);
440		priv->substream = substream; /* set substream later */
441		break;
442	case SNDRV_PCM_TRIGGER_STOP:
443		priv->substream = NULL;
444		test_component_timer_stop(priv);
445	}
446
447	return 0;
448}
449
450static int test_component_sync_stop(struct snd_soc_component *component,
451				    struct snd_pcm_substream *substream)
452{
453	mile_stone(component);
454
455	return 0;
456}
457
458static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
459						struct snd_pcm_substream *substream)
460{
461	struct snd_pcm_runtime *runtime = substream->runtime;
462	static int pointer;
463
464	if (!runtime)
465		return 0;
466
467	pointer += 10;
468	if (pointer > PREALLOC_BUFFER)
469		pointer = 0;
470
471	/* mile_stone(component); */
472
473	return bytes_to_frames(runtime, pointer);
474}
475
476static int test_component_get_time_info(struct snd_soc_component *component,
477					struct snd_pcm_substream *substream,
478					struct timespec64 *system_ts,
479					struct timespec64 *audio_ts,
480					struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
481					struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
482{
483	mile_stone(component);
484
485	return 0;
486}
487
488static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
489					     struct snd_pcm_hw_params *params)
490{
491	mile_stone_x(rtd->dev);
492
493	return 0;
494}
495
496/* CPU */
497static const struct test_adata test_cpu		= { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
498static const struct test_adata test_cpu_vv	= { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
499static const struct test_adata test_cpu_nv	= { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
500static const struct test_adata test_cpu_vn	= { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
501/* Codec */
502static const struct test_adata test_codec	= { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
503static const struct test_adata test_codec_vv	= { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
504static const struct test_adata test_codec_nv	= { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
505static const struct test_adata test_codec_vn	= { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
506
507static const struct of_device_id test_of_match[] = {
508	{ .compatible = "test-cpu",			.data = (void *)&test_cpu,    },
509	{ .compatible = "test-cpu-verbose",		.data = (void *)&test_cpu_vv, },
510	{ .compatible = "test-cpu-verbose-dai",		.data = (void *)&test_cpu_nv, },
511	{ .compatible = "test-cpu-verbose-component",	.data = (void *)&test_cpu_vn, },
512	{ .compatible = "test-codec",			.data = (void *)&test_codec,    },
513	{ .compatible = "test-codec-verbose",		.data = (void *)&test_codec_vv, },
514	{ .compatible = "test-codec-verbose-dai",	.data = (void *)&test_codec_nv, },
515	{ .compatible = "test-codec-verbose-component",	.data = (void *)&test_codec_vn, },
516	{},
517};
518MODULE_DEVICE_TABLE(of, test_of_match);
519
520static const struct snd_soc_dapm_widget widgets[] = {
521	/*
522	 * FIXME
523	 *
524	 * Just IN/OUT is OK for now,
525	 * but need to be updated ?
526	 */
527	SND_SOC_DAPM_INPUT("IN"),
528	SND_SOC_DAPM_OUTPUT("OUT"),
529};
530
531static int test_driver_probe(struct platform_device *pdev)
532{
533	struct device *dev = &pdev->dev;
534	struct device_node *node = dev->of_node;
535	const struct test_adata *adata = of_device_get_match_data(&pdev->dev);
536	struct snd_soc_component_driver *cdriv;
537	struct snd_soc_dai_driver *ddriv;
538	struct test_dai_name *dname;
539	struct test_priv *priv;
540	int num, ret, i;
541
542	num = of_graph_get_endpoint_count(node);
543	if (!num) {
544		dev_err(dev, "no port exits\n");
545		return -EINVAL;
546	}
547
548	priv	= devm_kzalloc(dev, sizeof(*priv),		GFP_KERNEL);
549	cdriv	= devm_kzalloc(dev, sizeof(*cdriv),		GFP_KERNEL);
550	ddriv	= devm_kcalloc(dev, num, sizeof(*ddriv), 	GFP_KERNEL);
551	dname	= devm_kcalloc(dev, num, sizeof(*dname), 	GFP_KERNEL);
552	if (!priv || !cdriv || !ddriv || !dname || !adata)
553		return -EINVAL;
554
555	priv->dev		= dev;
556	priv->component_driver	= cdriv;
557	priv->dai_driver	= ddriv;
558	priv->name		= dname;
559
560	INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
561	dev_set_drvdata(dev, priv);
562
563	if (adata->is_cpu) {
564		cdriv->name			= "test_cpu";
565		cdriv->pcm_construct		= test_component_pcm_construct;
566		cdriv->pointer			= test_component_pointer;
567		cdriv->trigger			= test_component_trigger;
568		cdriv->legacy_dai_naming	= 1;
569	} else {
570		cdriv->name			= "test_codec";
571		cdriv->idle_bias_on		= 1;
572		cdriv->endianness		= 1;
573	}
574
575	cdriv->open		= test_component_open;
576	cdriv->dapm_widgets	= widgets;
577	cdriv->num_dapm_widgets	= ARRAY_SIZE(widgets);
578
579	if (adata->cmp_v) {
580		cdriv->probe			= test_component_probe;
581		cdriv->remove			= test_component_remove;
582		cdriv->suspend			= test_component_suspend;
583		cdriv->resume			= test_component_resume;
584		cdriv->set_sysclk		= test_component_set_sysclk;
585		cdriv->set_pll			= test_component_set_pll;
586		cdriv->set_jack			= test_component_set_jack;
587		cdriv->seq_notifier		= test_component_seq_notifier;
588		cdriv->stream_event		= test_component_stream_event;
589		cdriv->set_bias_level		= test_component_set_bias_level;
590		cdriv->close			= test_component_close;
591		cdriv->ioctl			= test_component_ioctl;
592		cdriv->hw_params		= test_component_hw_params;
593		cdriv->hw_free			= test_component_hw_free;
594		cdriv->prepare			= test_component_prepare;
595		cdriv->sync_stop		= test_component_sync_stop;
596		cdriv->get_time_info		= test_component_get_time_info;
597		cdriv->be_hw_params_fixup	= test_component_be_hw_params_fixup;
598
599		if (adata->is_cpu)
600			cdriv->pcm_destruct	= test_component_pcm_destruct;
601	}
602
603	i = 0;
604	for_each_of_graph_port(node, port) {
605		snprintf(dname[i].name, TEST_NAME_LEN, "%s.%d", node->name, i);
606		ddriv[i].name = dname[i].name;
607
608		snprintf(dname[i].name_playback, TEST_NAME_LEN, "DAI%d Playback", i);
609		ddriv[i].playback.stream_name	= dname[i].name_playback;
610		ddriv[i].playback.channels_min	= 1;
611		ddriv[i].playback.channels_max	= 384;
612		ddriv[i].playback.rates		= STUB_RATES;
613		ddriv[i].playback.formats	= STUB_FORMATS;
614
615		snprintf(dname[i].name_capture, TEST_NAME_LEN, "DAI%d Capture", i);
616		ddriv[i].capture.stream_name	= dname[i].name_capture;
617		ddriv[i].capture.channels_min	= 1;
618		ddriv[i].capture.channels_max	= 384;
619		ddriv[i].capture.rates		= STUB_RATES;
620		ddriv[i].capture.formats	= STUB_FORMATS;
621
622		if (adata->dai_v)
623			ddriv[i].ops = &test_verbose_ops;
624		else
625			ddriv[i].ops = &test_ops;
626
627		i++;
628	}
629
630	ret = devm_snd_soc_register_component(dev, cdriv, ddriv, num);
631	if (ret < 0)
632		return ret;
633
634	mile_stone_x(dev);
635
636	return 0;
637}
638
639static void test_driver_remove(struct platform_device *pdev)
640{
641	mile_stone_x(&pdev->dev);
642}
643
644static struct platform_driver test_driver = {
645	.driver = {
646		.name = "test-component",
647		.of_match_table = test_of_match,
648	},
649	.probe  = test_driver_probe,
650	.remove = test_driver_remove,
651};
652module_platform_driver(test_driver);
653
654MODULE_ALIAS("platform:asoc-test-component");
655MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
656MODULE_DESCRIPTION("ASoC Test Component");
657MODULE_LICENSE("GPL v2");
~/ Read-only file preview.