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