| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- // SPDX-License-Identifier: GPL-2.0
- //
- // test-component.c -- Test Audio Component driver
- //
- // Copyright (C) 2020 Renesas Electronics Corporation
- // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- #include <linux/slab.h>
- #include <linux/of.h>
- #include <linux/of_graph.h>
- #include <linux/module.h>
- #include <linux/workqueue.h>
- #include <sound/pcm.h>
- #include <sound/soc.h>
- #define TEST_NAME_LEN 32
- struct test_dai_name {
- char name[TEST_NAME_LEN];
- char name_playback[TEST_NAME_LEN];
- char name_capture[TEST_NAME_LEN];
- };
- struct test_priv {
- struct device *dev;
- struct snd_pcm_substream *substream;
- struct delayed_work dwork;
- struct snd_soc_component_driver *component_driver;
- struct snd_soc_dai_driver *dai_driver;
- struct test_dai_name *name;
- };
- struct test_adata {
- u32 is_cpu:1;
- u32 cmp_v:1;
- u32 dai_v:1;
- };
- #define mile_stone(d) dev_info((d)->dev, "%s() : %s", __func__, (d)->driver->name)
- #define mile_stone_x(dev) dev_info(dev, "%s()", __func__)
- static int test_dai_set_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
- unsigned int freq_in, unsigned int freq_out)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
- {
- unsigned int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- unsigned int clock = fmt & SND_SOC_DAIFMT_CLOCK_MASK;
- unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
- unsigned int master = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
- char *str;
- dev_info(dai->dev, "name : %s", dai->name);
- str = "unknown";
- switch (format) {
- case SND_SOC_DAIFMT_I2S:
- str = "i2s";
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- str = "right_j";
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- str = "left_j";
- break;
- case SND_SOC_DAIFMT_DSP_A:
- str = "dsp_a";
- break;
- case SND_SOC_DAIFMT_DSP_B:
- str = "dsp_b";
- break;
- case SND_SOC_DAIFMT_AC97:
- str = "ac97";
- break;
- case SND_SOC_DAIFMT_PDM:
- str = "pdm";
- break;
- }
- dev_info(dai->dev, "format : %s", str);
- if (clock == SND_SOC_DAIFMT_CONT)
- str = "continuous";
- else
- str = "gated";
- dev_info(dai->dev, "clock : %s", str);
- str = "unknown";
- switch (master) {
- case SND_SOC_DAIFMT_BP_FP:
- str = "clk provider, frame provider";
- break;
- case SND_SOC_DAIFMT_BC_FP:
- str = "clk consumer, frame provider";
- break;
- case SND_SOC_DAIFMT_BP_FC:
- str = "clk provider, frame consumer";
- break;
- case SND_SOC_DAIFMT_BC_FC:
- str = "clk consumer, frame consumer";
- break;
- }
- dev_info(dai->dev, "clock : codec is %s", str);
- str = "unknown";
- switch (inv) {
- case SND_SOC_DAIFMT_NB_NF:
- str = "normal bit, normal frame";
- break;
- case SND_SOC_DAIFMT_NB_IF:
- str = "normal bit, invert frame";
- break;
- case SND_SOC_DAIFMT_IB_NF:
- str = "invert bit, normal frame";
- break;
- case SND_SOC_DAIFMT_IB_IF:
- str = "invert bit, invert frame";
- break;
- }
- dev_info(dai->dev, "signal : %s", str);
- return 0;
- }
- static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
- {
- mile_stone(dai);
- return 0;
- }
- static void test_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
- {
- mile_stone(dai);
- }
- static int test_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
- {
- mile_stone(dai);
- return 0;
- }
- static int test_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
- {
- mile_stone(dai);
- return 0;
- }
- static const u64 test_dai_formats =
- /*
- * Select below from Sound Card, not auto
- * SND_SOC_POSSIBLE_DAIFMT_BP_FP
- * SND_SOC_POSSIBLE_DAIFMT_BC_FP
- * SND_SOC_POSSIBLE_DAIFMT_BP_FC
- * SND_SOC_POSSIBLE_DAIFMT_BC_FC
- */
- SND_SOC_POSSIBLE_DAIFMT_I2S |
- SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
- SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
- SND_SOC_POSSIBLE_DAIFMT_DSP_A |
- SND_SOC_POSSIBLE_DAIFMT_DSP_B |
- SND_SOC_POSSIBLE_DAIFMT_AC97 |
- SND_SOC_POSSIBLE_DAIFMT_PDM |
- SND_SOC_POSSIBLE_DAIFMT_NB_NF |
- SND_SOC_POSSIBLE_DAIFMT_NB_IF |
- SND_SOC_POSSIBLE_DAIFMT_IB_NF |
- SND_SOC_POSSIBLE_DAIFMT_IB_IF;
- static const struct snd_soc_dai_ops test_ops = {
- .set_fmt = test_dai_set_fmt,
- .startup = test_dai_startup,
- .shutdown = test_dai_shutdown,
- .auto_selectable_formats = &test_dai_formats,
- .num_auto_selectable_formats = 1,
- };
- static const struct snd_soc_dai_ops test_verbose_ops = {
- .set_sysclk = test_dai_set_sysclk,
- .set_pll = test_dai_set_pll,
- .set_clkdiv = test_dai_set_clkdiv,
- .set_fmt = test_dai_set_fmt,
- .mute_stream = test_dai_mute_stream,
- .startup = test_dai_startup,
- .shutdown = test_dai_shutdown,
- .hw_params = test_dai_hw_params,
- .hw_free = test_dai_hw_free,
- .trigger = test_dai_trigger,
- .auto_selectable_formats = &test_dai_formats,
- .num_auto_selectable_formats = 1,
- };
- #define STUB_RATES SNDRV_PCM_RATE_8000_384000
- #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_U8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_U16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_U24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE | \
- SNDRV_PCM_FMTBIT_U32_LE)
- static int test_component_probe(struct snd_soc_component *component)
- {
- mile_stone(component);
- return 0;
- }
- static void test_component_remove(struct snd_soc_component *component)
- {
- mile_stone(component);
- }
- static int test_component_suspend(struct snd_soc_component *component)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_resume(struct snd_soc_component *component)
- {
- mile_stone(component);
- return 0;
- }
- #define PREALLOC_BUFFER (32 * 1024)
- static int test_component_pcm_construct(struct snd_soc_component *component,
- struct snd_soc_pcm_runtime *rtd)
- {
- mile_stone(component);
- snd_pcm_set_managed_buffer_all(
- rtd->pcm,
- SNDRV_DMA_TYPE_DEV,
- rtd->card->snd_card->dev,
- PREALLOC_BUFFER, PREALLOC_BUFFER);
- return 0;
- }
- static void test_component_pcm_destruct(struct snd_soc_component *component,
- struct snd_pcm *pcm)
- {
- mile_stone(component);
- }
- static int test_component_set_sysclk(struct snd_soc_component *component,
- int clk_id, int source, unsigned int freq, int dir)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_set_pll(struct snd_soc_component *component, int pll_id,
- int source, unsigned int freq_in, unsigned int freq_out)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_set_jack(struct snd_soc_component *component,
- struct snd_soc_jack *jack, void *data)
- {
- mile_stone(component);
- return 0;
- }
- static void test_component_seq_notifier(struct snd_soc_component *component,
- enum snd_soc_dapm_type type, int subseq)
- {
- mile_stone(component);
- }
- static int test_component_stream_event(struct snd_soc_component *component, int event)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
- {
- mile_stone(component);
- return 0;
- }
- static const struct snd_pcm_hardware test_component_hardware = {
- /* Random values to keep userspace happy when checking constraints */
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID,
- .buffer_bytes_max = 32 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 128,
- .fifo_size = 256,
- };
- static int test_component_open(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- mile_stone(component);
- /* BE's dont need dummy params */
- if (!rtd->dai_link->no_pcm)
- snd_soc_set_runtime_hwparams(substream, &test_component_hardware);
- return 0;
- }
- static int test_component_close(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_ioctl(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_hw_params(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_hw_free(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_prepare(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- mile_stone(component);
- return 0;
- }
- static void test_component_timer_stop(struct test_priv *priv)
- {
- cancel_delayed_work(&priv->dwork);
- }
- static void test_component_timer_start(struct test_priv *priv)
- {
- schedule_delayed_work(&priv->dwork, msecs_to_jiffies(10));
- }
- static void test_component_dwork(struct work_struct *work)
- {
- struct test_priv *priv = container_of(work, struct test_priv, dwork.work);
- if (priv->substream)
- snd_pcm_period_elapsed(priv->substream);
- test_component_timer_start(priv);
- }
- static int test_component_trigger(struct snd_soc_component *component,
- struct snd_pcm_substream *substream, int cmd)
- {
- struct test_priv *priv = dev_get_drvdata(component->dev);
- mile_stone(component);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- test_component_timer_start(priv);
- priv->substream = substream; /* set substream later */
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- priv->substream = NULL;
- test_component_timer_stop(priv);
- }
- return 0;
- }
- static int test_component_sync_stop(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- mile_stone(component);
- return 0;
- }
- static snd_pcm_uframes_t test_component_pointer(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- static int pointer;
- if (!runtime)
- return 0;
- pointer += 10;
- if (pointer > PREALLOC_BUFFER)
- pointer = 0;
- /* mile_stone(component); */
- return bytes_to_frames(runtime, pointer);
- }
- static int test_component_get_time_info(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct timespec64 *system_ts,
- struct timespec64 *audio_ts,
- struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
- struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
- {
- mile_stone(component);
- return 0;
- }
- static int test_component_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
- {
- mile_stone_x(rtd->dev);
- return 0;
- }
- /* CPU */
- static const struct test_adata test_cpu = { .is_cpu = 1, .cmp_v = 0, .dai_v = 0, };
- static const struct test_adata test_cpu_vv = { .is_cpu = 1, .cmp_v = 1, .dai_v = 1, };
- static const struct test_adata test_cpu_nv = { .is_cpu = 1, .cmp_v = 0, .dai_v = 1, };
- static const struct test_adata test_cpu_vn = { .is_cpu = 1, .cmp_v = 1, .dai_v = 0, };
- /* Codec */
- static const struct test_adata test_codec = { .is_cpu = 0, .cmp_v = 0, .dai_v = 0, };
- static const struct test_adata test_codec_vv = { .is_cpu = 0, .cmp_v = 1, .dai_v = 1, };
- static const struct test_adata test_codec_nv = { .is_cpu = 0, .cmp_v = 0, .dai_v = 1, };
- static const struct test_adata test_codec_vn = { .is_cpu = 0, .cmp_v = 1, .dai_v = 0, };
- static const struct of_device_id test_of_match[] = {
- { .compatible = "test-cpu", .data = (void *)&test_cpu, },
- { .compatible = "test-cpu-verbose", .data = (void *)&test_cpu_vv, },
- { .compatible = "test-cpu-verbose-dai", .data = (void *)&test_cpu_nv, },
- { .compatible = "test-cpu-verbose-component", .data = (void *)&test_cpu_vn, },
- { .compatible = "test-codec", .data = (void *)&test_codec, },
- { .compatible = "test-codec-verbose", .data = (void *)&test_codec_vv, },
- { .compatible = "test-codec-verbose-dai", .data = (void *)&test_codec_nv, },
- { .compatible = "test-codec-verbose-component", .data = (void *)&test_codec_vn, },
- {},
- };
- MODULE_DEVICE_TABLE(of, test_of_match);
- static const struct snd_soc_dapm_widget widgets[] = {
- /*
- * FIXME
- *
- * Just IN/OUT is OK for now,
- * but need to be updated ?
- */
- SND_SOC_DAPM_INPUT("IN"),
- SND_SOC_DAPM_OUTPUT("OUT"),
- };
- static int test_driver_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->of_node;
- struct device_node *ep;
- const struct test_adata *adata = of_device_get_match_data(&pdev->dev);
- struct snd_soc_component_driver *cdriv;
- struct snd_soc_dai_driver *ddriv;
- struct test_dai_name *dname;
- struct test_priv *priv;
- int num, ret, i;
- num = of_graph_get_endpoint_count(node);
- if (!num) {
- dev_err(dev, "no port exits\n");
- return -EINVAL;
- }
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- cdriv = devm_kzalloc(dev, sizeof(*cdriv), GFP_KERNEL);
- ddriv = devm_kzalloc(dev, sizeof(*ddriv) * num, GFP_KERNEL);
- dname = devm_kzalloc(dev, sizeof(*dname) * num, GFP_KERNEL);
- if (!priv || !cdriv || !ddriv || !dname || !adata)
- return -EINVAL;
- priv->dev = dev;
- priv->component_driver = cdriv;
- priv->dai_driver = ddriv;
- priv->name = dname;
- INIT_DELAYED_WORK(&priv->dwork, test_component_dwork);
- dev_set_drvdata(dev, priv);
- if (adata->is_cpu) {
- cdriv->name = "test_cpu";
- cdriv->pcm_construct = test_component_pcm_construct;
- cdriv->pointer = test_component_pointer;
- cdriv->trigger = test_component_trigger;
- cdriv->legacy_dai_naming = 1;
- } else {
- cdriv->name = "test_codec";
- cdriv->idle_bias_on = 1;
- cdriv->endianness = 1;
- }
- cdriv->open = test_component_open;
- cdriv->dapm_widgets = widgets;
- cdriv->num_dapm_widgets = ARRAY_SIZE(widgets);
- if (adata->cmp_v) {
- cdriv->probe = test_component_probe;
- cdriv->remove = test_component_remove;
- cdriv->suspend = test_component_suspend;
- cdriv->resume = test_component_resume;
- cdriv->set_sysclk = test_component_set_sysclk;
- cdriv->set_pll = test_component_set_pll;
- cdriv->set_jack = test_component_set_jack;
- cdriv->seq_notifier = test_component_seq_notifier;
- cdriv->stream_event = test_component_stream_event;
- cdriv->set_bias_level = test_component_set_bias_level;
- cdriv->close = test_component_close;
- cdriv->ioctl = test_component_ioctl;
- cdriv->hw_params = test_component_hw_params;
- cdriv->hw_free = test_component_hw_free;
- cdriv->prepare = test_component_prepare;
- cdriv->sync_stop = test_component_sync_stop;
- cdriv->get_time_info = test_component_get_time_info;
- cdriv->be_hw_params_fixup = test_component_be_hw_params_fixup;
- if (adata->is_cpu)
- cdriv->pcm_destruct = test_component_pcm_destruct;
- }
- i = 0;
- for_each_endpoint_of_node(node, ep) {
- snprintf(dname[i].name, TEST_NAME_LEN, "%s.%d", node->name, i);
- ddriv[i].name = dname[i].name;
- snprintf(dname[i].name_playback, TEST_NAME_LEN, "DAI%d Playback", i);
- ddriv[i].playback.stream_name = dname[i].name_playback;
- ddriv[i].playback.channels_min = 1;
- ddriv[i].playback.channels_max = 384;
- ddriv[i].playback.rates = STUB_RATES;
- ddriv[i].playback.formats = STUB_FORMATS;
- snprintf(dname[i].name_capture, TEST_NAME_LEN, "DAI%d Capture", i);
- ddriv[i].capture.stream_name = dname[i].name_capture;
- ddriv[i].capture.channels_min = 1;
- ddriv[i].capture.channels_max = 384;
- ddriv[i].capture.rates = STUB_RATES;
- ddriv[i].capture.formats = STUB_FORMATS;
- if (adata->dai_v)
- ddriv[i].ops = &test_verbose_ops;
- else
- ddriv[i].ops = &test_ops;
- i++;
- }
- ret = devm_snd_soc_register_component(dev, cdriv, ddriv, num);
- if (ret < 0)
- return ret;
- mile_stone_x(dev);
- return 0;
- }
- static void test_driver_remove(struct platform_device *pdev)
- {
- mile_stone_x(&pdev->dev);
- }
- static struct platform_driver test_driver = {
- .driver = {
- .name = "test-component",
- .of_match_table = test_of_match,
- },
- .probe = test_driver_probe,
- .remove = test_driver_remove,
- };
- module_platform_driver(test_driver);
- MODULE_ALIAS("platform:asoc-test-component");
- MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
- MODULE_DESCRIPTION("ASoC Test Component");
- MODULE_LICENSE("GPL v2");
|