From d293abc0c8fbb7b1610b9f7497323028b06cd5f8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:53:38 +0900 Subject: ASoC: test-component: add Test Component for Sound debug/test We already have dummy-codec, dummy-platform. But its issues are 1) we don't have dummy-cpu, 2) we can't select it via DeviceTree 3) It do nothing Sometimes we want to have Dummy Sound Component for debugging, for testing, for learning Framework behavior, etc, etc... This patch adds Test-Component driver for it. User can select CPU Component by using "test-cpu" compatible, and can select Codec Component by using "test-codec" compatible. It doesn't support Platform so far, but is easy to add. We can verbose print to know its progress if user selected xxx-verbose compatible driver. for example, test-cpu : silent Component, silent DAI test-cpu-verbose-component : verbose Component, silent DAI test-cpu-verbose-dai : silent Component, verbose DAI test-cpu-verbose : verbose Component, verbose DAI Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/877dein8rx.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/Kconfig | 6 + sound/soc/generic/Makefile | 2 + sound/soc/generic/test-component.c | 659 +++++++++++++++++++++++++++++++++++++ 3 files changed, 667 insertions(+) create mode 100644 sound/soc/generic/test-component.c (limited to 'sound') diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index 4cafcf0e2bbf..bb734780669e 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -17,3 +17,9 @@ config SND_AUDIO_GRAPH_CARD This option enables generic simple sound card support with OF-graph DT bindings. It also support DPCM of multi CPU single Codec ststem. + +config SND_TEST_COMPONENT + tristate "ASoC Test component sound support" + depends on OF + help + This option enables test component sound driver support. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 21c29e5e0671..988bfd45d2e2 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -2,7 +2,9 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o +snd-soc-test-component-objs := test-component.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o +obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c new file mode 100644 index 000000000000..85385a771d80 --- /dev/null +++ b/sound/soc/generic/test-component.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// test-component.c -- Test Audio Component driver +// +// Copyright (C) 2020 Renesas Electronics Corporation +// Kuninori Morimoto + +#include +#include +#include +#include +#include +#include +#include + +#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_MASTER_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_CBP_CFP: + str = "clk provider, frame provider"; + break; + case SND_SOC_DAIFMT_CBC_CFP: + str = "clk consumer, frame provider"; + break; + case SND_SOC_DAIFMT_CBP_CFC: + str = "clk provider, frame consumer"; + break; + case SND_SOC_DAIFMT_CBC_CFC: + 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 int test_dai_bespoke_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + mile_stone(dai); + + return 0; +} + +static u64 test_dai_formats = + /* + * Select below from Sound Card, not auto + * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP + * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP + * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC + * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC + */ + 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, + .bespoke_trigger = test_dai_bespoke_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 = asoc_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 of_device_id *of_id = of_match_device(test_of_match, &pdev->dev); + const struct test_adata *adata = of_id->data; + 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) + 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; + } else { + cdriv->name = "test_codec"; + cdriv->idle_bias_on = 1; + cdriv->endianness = 1; + cdriv->non_legacy_dai_naming = 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 int test_driver_remove(struct platform_device *pdev) +{ + mile_stone_x(&pdev->dev); + + return 0; +} + +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 "); +MODULE_DESCRIPTION("ASoC Test Component"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 92939252458fa279d0013e5dc545a98a4ca4064a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:54:01 +0900 Subject: ASoC: simple-card-utils: add asoc_graph_is_ports0() audio-graph-card2 will support DPCM/Multi/Codec2Codec, and these will use almost same DT settings which uses ports0 and ports1. This patch adds asoc_graph_is_ports0() which checks port is under port0 or not. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/875yu2n8ra.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 1 + sound/soc/generic/simple-card-utils.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'sound') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 6b780346eaa7..03d07541d958 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -180,6 +180,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, int asoc_simple_remove(struct platform_device *pdev); int asoc_graph_card_probe(struct snd_soc_card *card); +int asoc_graph_is_ports0(struct device_node *port); #ifdef DEBUG static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 10c63b73900c..fd893d20ccc7 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -759,6 +759,34 @@ int asoc_graph_card_probe(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(asoc_graph_card_probe); +int asoc_graph_is_ports0(struct device_node *np) +{ + struct device_node *port, *ports, *ports0, *top; + int ret; + + /* np is "endpoint" or "port" */ + if (of_node_name_eq(np, "endpoint")) { + port = of_get_parent(np); + } else { + port = np; + of_node_get(port); + } + + ports = of_get_parent(port); + top = of_get_parent(ports); + ports0 = of_get_child_by_name(top, "ports"); + + ret = ports0 == ports; + + of_node_put(port); + of_node_put(ports); + of_node_put(ports0); + of_node_put(top); + + return ret; +} +EXPORT_SYMBOL_GPL(asoc_graph_is_ports0); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto "); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); -- cgit v1.2.3 From 52a18c291470e66a27f415b8c99136f25f55092e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:54:13 +0900 Subject: ASoC: simple-card-utils: add codec2codec support codec2codec needs snd_soc_pcm_stream settings. This patch adds it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/874k9mn8qy.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 +++ sound/soc/generic/simple-card-utils.c | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 03d07541d958..df430f1c2a10 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -42,6 +42,7 @@ struct prop_nums { int cpus; int codecs; int platforms; + int c2c; }; struct asoc_simple_priv { @@ -54,6 +55,7 @@ struct asoc_simple_priv { struct snd_soc_dai_link_component *platforms; struct asoc_simple_data adata; struct snd_soc_codec_conf *codec_conf; + struct snd_soc_pcm_stream *c2c_conf; struct prop_nums num; unsigned int mclk_fs; } *dai_props; @@ -64,6 +66,7 @@ struct asoc_simple_priv { struct snd_soc_dai_link_component *dlcs; struct snd_soc_dai_link_component dummy; struct snd_soc_codec_conf *codec_conf; + struct snd_soc_pcm_stream *c2c_conf; struct gpio_desc *pa_gpio; const struct snd_soc_ops *ops; unsigned int dpcm_selectable:1; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index fd893d20ccc7..39ba70088127 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -619,7 +619,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *cconf = NULL; - int i, dai_num = 0, dlc_num = 0, cnf_num = 0; + struct snd_soc_pcm_stream *c2c_conf = NULL; + int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0; dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); @@ -638,6 +639,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, if (!li->num[i].cpus) cnf_num += li->num[i].codecs; + + c2c_num += li->num[i].c2c; } dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL); @@ -651,6 +654,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, return -ENOMEM; } + if (c2c_num) { + c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL); + if (!c2c_conf) + return -ENOMEM; + } + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", li->link, dai_num, cnf_num); @@ -664,6 +673,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, priv->dais = dais; priv->dlcs = dlcs; priv->codec_conf = cconf; + priv->c2c_conf = c2c_conf; card->dai_link = priv->dai_link; card->num_links = li->link; @@ -681,6 +691,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dlcs += li->num[i].cpus; dais += li->num[i].cpus; + + if (li->num[i].c2c) { + /* Codec2Codec */ + dai_props[i].c2c_conf = c2c_conf; + c2c_conf += li->num[i].c2c; + } } else { /* DPCM Be's CPU = dummy */ dai_props[i].cpus = -- cgit v1.2.3 From 6e5f68fe3f2d35046856572fa037a5149d55a070 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:54:46 +0900 Subject: ASoC: add Audio Graph Card2 driver We already have audio-graph-card which is Of-graph base of general sound card driver. It is supporting DPCM connection, but was forcibly expanded. Thus, it is very difficult to add new features on it, for example Multi CPU/Codec support, Codec2Codec support, etc. This patch adds more flexible new Audio Graph Card2 driver for it. audio-graph-card and audio-graph-card2 are similar, but don't have full compatibility. Audio Graph Card2 supports very generic connection, but some users want to have its own settings, for example PLL settings, etc. For such case, it has customizing support. In users own driver, it can use Audio Graph Card2 parsing by using audio_graph2_parse_of(), and doing its own customizing. Because Audio Graph Card2 is still under experimental stage, it will indicate such warning when probing, and the DT syntax might be changed. Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/871r8u4s6q.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/87a6mhwyqn.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/87tuitusy4.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/87a6jn56x0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/8735p6n8q1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 15 + sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card2.c | 670 ++++++++++++++++++++++++++++++++++ 4 files changed, 695 insertions(+) create mode 100644 sound/soc/generic/audio-graph-card2.c (limited to 'sound') diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index 6f10bfb0d5ee..497d59585b2d 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -9,6 +9,21 @@ #include +typedef int (*GRAPH2_CUSTOM)(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li); + +struct graph2_custom_hooks { + int (*hook_pre)(struct asoc_simple_priv *priv); + int (*hook_post)(struct asoc_simple_priv *priv); + GRAPH2_CUSTOM custom_normal; +}; + int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); +int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, + struct graph2_custom_hooks *hooks); + +int audio_graph2_link_normal(struct asoc_simple_priv *priv, + struct device_node *lnk, struct link_info *li); #endif /* __GRAPH_CARD_H */ diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index bb734780669e..829ca2b0bf76 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -18,6 +18,14 @@ config SND_AUDIO_GRAPH_CARD with OF-graph DT bindings. It also support DPCM of multi CPU single Codec ststem. +config SND_AUDIO_GRAPH_CARD2 + tristate "ASoC Audio Graph sound card2 support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple sound card2 support + with OF-graph DT bindings. + config SND_TEST_COMPONENT tristate "ASoC Test component sound support" depends on OF diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 988bfd45d2e2..b480f47a330d 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -2,9 +2,11 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o +snd-soc-audio-graph-card2-objs := audio-graph-card2.o snd-soc-test-component-objs := test-component.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c new file mode 100644 index 000000000000..4ab726891e24 --- /dev/null +++ b/sound/soc/generic/audio-graph-card2.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC Audio Graph Card2 support +// +// Copyright (C) 2020 Renesas Electronics Corp. +// Copyright (C) 2020 Kuninori Morimoto +// +// based on ${LINUX}/sound/soc/generic/audio-graph-card.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************ + daifmt + ************************************ + ports { + format = "left_j"; + port@0 { + bitclock-master; + sample0: endpoint@0 { + frame-master; + }; + sample1: endpoint@1 { + format = "i2s"; + }; + }; + ... + }; + + You can set daifmt at ports/port/endpoint. + It uses *latest* format, and *share* master settings. + In above case, + sample0: left_j, bitclock-master, frame-master + sample1: i2s, bitclock-master + + If there was no settings, *Codec* will be + bitclock/frame provider as default. + see + graph_parse_daifmt(). + + ************************************ + Normal Audio-Graph + ************************************ + + CPU <---> Codec + + sound { + compatible = "audio-graph-card2"; + links = <&cpu>; + }; + + CPU { + cpu: port { + bitclock-master; + frame-master; + cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; }; + }; + + Codec { + port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; }; + }; + +*/ + +enum graph_type { + GRAPH_NORMAL, +}; + +#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") + +static enum graph_type graph_get_type(struct asoc_simple_priv *priv, + struct device_node *lnk) +{ + enum graph_type type = GRAPH_NORMAL; + +#ifdef DEBUG + { + struct device *dev = simple_priv_to_dev(priv); + const char *str = "Normal"; + + dev_dbg(dev, "%pOF (%s)", lnk, str); + } +#endif + return type; +} + +static const struct snd_soc_ops graph_ops = { + .startup = asoc_simple_startup, + .shutdown = asoc_simple_shutdown, + .hw_params = asoc_simple_hw_params, +}; + +static int graph_get_dai_id(struct device_node *ep) +{ + struct device_node *node; + struct device_node *endpoint; + struct of_endpoint info; + int i, id; + const u32 *reg; + int ret; + + /* use driver specified DAI ID if exist */ + ret = snd_soc_get_dai_id(ep); + if (ret != -ENOTSUPP) + return ret; + + /* use endpoint/port reg if exist */ + ret = of_graph_parse_endpoint(ep, &info); + if (ret == 0) { + /* + * Because it will count port/endpoint if it doesn't have "reg". + * But, we can't judge whether it has "no reg", or "reg = <0>" + * only of_graph_parse_endpoint(). + * We need to check "reg" property + */ + if (of_get_property(ep, "reg", NULL)) + return info.id; + + node = of_get_parent(ep); + reg = of_get_property(node, "reg", NULL); + of_node_put(node); + if (reg) + return info.port; + } + node = of_graph_get_port_parent(ep); + + /* + * Non HDMI sound case, counting port/endpoint on its DT + * is enough. Let's count it. + */ + i = 0; + id = -1; + for_each_endpoint_of_node(node, endpoint) { + if (endpoint == ep) + id = i; + i++; + } + + of_node_put(node); + + if (id < 0) + return -ENODEV; + + return id; +} + +static int asoc_simple_parse_dai(struct device_node *ep, + struct snd_soc_dai_link_component *dlc, + int *is_single_link) +{ + struct device_node *node; + struct of_phandle_args args; + int ret; + + if (!ep) + return 0; + + node = of_graph_get_port_parent(ep); + + /* Get dai->name */ + args.np = node; + args.args[0] = graph_get_dai_id(ep); + args.args_count = (of_graph_get_endpoint_count(node) > 1); + + /* + * FIXME + * + * Here, dlc->dai_name is pointer to CPU/Codec DAI name. + * If user unbinded CPU or Codec driver, but not for Sound Card, + * dlc->dai_name is keeping unbinded CPU or Codec + * driver's pointer. + * + * If user re-bind CPU or Codec driver again, ALSA SoC will try + * to rebind Card via snd_soc_try_rebind_card(), but because of + * above reason, it might can't bind Sound Card. + * Because Sound Card is pointing to released dai_name pointer. + * + * To avoid this rebind Card issue, + * 1) It needs to alloc memory to keep dai_name eventhough + * CPU or Codec driver was unbinded, or + * 2) user need to rebind Sound Card everytime + * if he unbinded CPU or Codec. + */ + ret = snd_soc_get_dai_name(&args, &dlc->dai_name); + if (ret < 0) + return ret; + + dlc->of_node = node; + + if (is_single_link) + *is_single_link = of_graph_get_endpoint_count(node) == 1; + + return 0; +} + +static void graph_parse_mclk_fs(struct device_node *ep, + struct simple_dai_props *props) +{ + struct device_node *port = of_get_parent(ep); + struct device_node *ports = of_get_parent(port); + + if (of_node_name_eq(ports, "ports")) + of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); + of_property_read_u32(port, "mclk-fs", &props->mclk_fs); + of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); + + of_node_put(port); + of_node_put(ports); +} + +static int __graph_parse_node(struct asoc_simple_priv *priv, + enum graph_type gtype, + struct device_node *ep, + struct link_info *li, + int is_cpu, int idx) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct snd_soc_dai_link_component *dlc; + struct asoc_simple_dai *dai; + int ret, is_single_links = 0; + + if (is_cpu) { + dlc = asoc_link_to_cpu(dai_link, idx); + dai = simple_props_to_dai_cpu(dai_props, idx); + } else { + dlc = asoc_link_to_codec(dai_link, idx); + dai = simple_props_to_dai_codec(dai_props, idx); + } + + graph_parse_mclk_fs(ep, dai_props); + + ret = asoc_simple_parse_dai(ep, dlc, &is_single_links); + if (ret < 0) + return ret; + + ret = asoc_simple_parse_tdm(ep, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_parse_clk(dev, ep, dai, dlc); + if (ret < 0) + return ret; + + /* + * set DAI Name + */ + if (!dai_link->name) { + struct snd_soc_dai_link_component *cpus = dlc; + struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); + + switch (gtype) { + case GRAPH_NORMAL: + /* run is_cpu only. see audio_graph2_link_normal() */ + if (is_cpu) + asoc_simple_set_dailink_name(dev, dai_link, "%s-%s", + cpus->dai_name, codecs->dai_name); + break; + default: + break; + } + } + + if (is_cpu) { + struct snd_soc_dai_link_component *cpus = dlc; + struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx); + + asoc_simple_canonicalize_cpu(cpus, is_single_links); + asoc_simple_canonicalize_platform(platforms, cpus); + } + + return 0; +} + +static int graph_parse_node(struct asoc_simple_priv *priv, + enum graph_type gtype, + struct device_node *port, + struct link_info *li, int is_cpu) +{ + struct device_node *ep = port_to_endpoint(port); + + /* Need Multi support later */ + return __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); +} + +static void graph_parse_daifmt(struct device_node *node, + unsigned int *daifmt, unsigned int *bit_frame) +{ + unsigned int fmt; + + /* + * see also above "daifmt" explanation + * and samples. + */ + + /* + * ports { + * (A) + * port { + * (B) + * endpoint { + * (C) + * }; + * }; + * }; + * }; + */ + + /* + * clock_provider: + * + * It can be judged it is provider + * if (A) or (B) or (C) has bitclock-master / frame-master flag. + * + * use "or" + */ + *bit_frame |= snd_soc_daifmt_parse_clock_provider_as_bitmap(node, NULL); + +#define update_daifmt(name) \ + if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \ + (fmt & SND_SOC_DAIFMT_##name##_MASK)) \ + *daifmt |= fmt & SND_SOC_DAIFMT_##name##_MASK + + /* + * format + * + * This function is called by (C) -> (B) -> (A) order. + * Set if applicable part was not yet set. + */ + fmt = snd_soc_daifmt_parse_format(node, NULL); + update_daifmt(FORMAT); + update_daifmt(CLOCK); + update_daifmt(INV); +} + +static void graph_link_init(struct asoc_simple_priv *priv, + struct device_node *port, + struct link_info *li, + int is_cpu_node) +{ + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct device_node *ep; + struct device_node *ports; + unsigned int daifmt = 0, daiclk = 0; + unsigned int bit_frame = 0; + + /* Need Multi support later */ + ep = port_to_endpoint(port); + ports = of_get_parent(port); + + /* + * ports { + * (A) + * port { + * (B) + * endpoint { + * (C) + * }; + * }; + * }; + * }; + */ + graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */ + graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */ + if (of_node_name_eq(ports, "ports")) + graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ + + /* + * convert bit_frame + * We need to flip clock_provider if it was CPU node, + * because it is Codec base. + */ + daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame); + if (is_cpu_node) + daiclk = snd_soc_daifmt_clock_provider_fliped(daiclk); + + dai_link->dai_fmt = daifmt | daiclk; + dai_link->init = asoc_simple_dai_init; + dai_link->ops = &graph_ops; + if (priv->ops) + dai_link->ops = priv->ops; +} + +int audio_graph2_link_normal(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device_node *cpu_port = lnk; + struct device_node *cpu_ep = port_to_endpoint(cpu_port); + struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); + int ret; + + /* + * call Codec first. + * see + * __graph_parse_node() :: DAI Naming + */ + ret = graph_parse_node(priv, GRAPH_NORMAL, codec_port, li, 0); + if (ret < 0) + goto err; + + /* + * call CPU, and set DAI Name + */ + ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_port, li, 1); + if (ret < 0) + goto err; + + graph_link_init(priv, cpu_port, li, 1); +err: + of_node_put(codec_port); + of_node_put(cpu_ep); + + return ret; +} +EXPORT_SYMBOL_GPL(audio_graph2_link_normal); + +static int graph_link(struct asoc_simple_priv *priv, + struct graph2_custom_hooks *hooks, + enum graph_type gtype, + struct device_node *lnk, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + GRAPH2_CUSTOM func = NULL; + int ret = -EINVAL; + + switch (gtype) { + case GRAPH_NORMAL: + if (hooks && hooks->custom_normal) + func = hooks->custom_normal; + else + func = audio_graph2_link_normal; + break; + default: + break; + } + + if (!func) { + dev_err(dev, "non supported gtype (%d)\n", gtype); + goto err; + } + + ret = func(priv, lnk, li); + if (ret < 0) + goto err; + + li->link++; +err: + return ret; +} + +static int graph_counter(struct device_node *lnk) +{ + /* Need Multi support later */ + return 1; +} + +static int graph_count_normal(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device_node *cpu_port = lnk; + struct device_node *cpu_ep = port_to_endpoint(cpu_port); + struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); + + /* + * CPU { + * => lnk: port { endpoint { .. }; }; + * }; + */ + li->num[li->link].cpus = + li->num[li->link].platforms = graph_counter(cpu_port); + li->num[li->link].codecs = graph_counter(codec_port); + + of_node_put(cpu_ep); + of_node_put(codec_port); + + return 0; +} + +static int graph_count(struct asoc_simple_priv *priv, + struct graph2_custom_hooks *hooks, + enum graph_type gtype, + struct device_node *lnk, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + GRAPH2_CUSTOM func = NULL; + int ret = -EINVAL; + + if (li->link >= SNDRV_MAX_LINKS) { + dev_err(dev, "too many links\n"); + return ret; + } + + switch (gtype) { + case GRAPH_NORMAL: + func = graph_count_normal; + break; + default: + break; + } + + if (!func) { + dev_err(dev, "non supported gtype (%d)\n", gtype); + goto err; + } + + ret = func(priv, lnk, li); + if (ret < 0) + goto err; + + li->link++; +err: + return ret; +} + +static int graph_for_each_link(struct asoc_simple_priv *priv, + struct graph2_custom_hooks *hooks, + struct link_info *li, + int (*func)(struct asoc_simple_priv *priv, + struct graph2_custom_hooks *hooks, + enum graph_type gtype, + struct device_node *lnk, + struct link_info *li)) +{ + struct of_phandle_iterator it; + struct device *dev = simple_priv_to_dev(priv); + struct device_node *node = dev->of_node; + struct device_node *lnk; + enum graph_type gtype; + int rc, ret; + + /* loop for all listed CPU port */ + of_for_each_phandle(&it, rc, node, "links", NULL, 0) { + lnk = it.node; + + gtype = graph_get_type(priv, lnk); + + ret = func(priv, hooks, gtype, lnk, li); + if (ret < 0) + return ret; + } + + return 0; +} + +int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, + struct graph2_custom_hooks *hooks) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct link_info *li; + int ret; + + dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n"); + + li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + if (!li) + return -ENOMEM; + + card->probe = asoc_graph_card_probe; + card->owner = THIS_MODULE; + card->dev = dev; + + if ((hooks) && (hooks)->hook_pre) { + ret = (hooks)->hook_pre(priv); + if (ret < 0) + goto err; + } + + ret = graph_for_each_link(priv, hooks, li, graph_count); + if (!li->link) + ret = -EINVAL; + if (ret < 0) + goto err; + + ret = asoc_simple_init_priv(priv, li); + if (ret < 0) + goto err; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); + goto err; + } + + ret = asoc_simple_parse_widgets(card, NULL); + if (ret < 0) + goto err; + + ret = asoc_simple_parse_routing(card, NULL); + if (ret < 0) + goto err; + + memset(li, 0, sizeof(*li)); + ret = graph_for_each_link(priv, hooks, li, graph_link); + if (ret < 0) + goto err; + + ret = asoc_simple_parse_card_name(card, NULL); + if (ret < 0) + goto err; + + snd_soc_card_set_drvdata(card, priv); + + if ((hooks) && (hooks)->hook_post) { + ret = (hooks)->hook_post(priv); + if (ret < 0) + goto err; + } + + asoc_simple_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +err: + devm_kfree(dev, li); + + if ((ret < 0) && (ret != -EPROBE_DEFER)) + dev_err(dev, "parse error %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(audio_graph2_parse_of); + +static int graph_probe(struct platform_device *pdev) +{ + struct asoc_simple_priv *priv; + struct device *dev = &pdev->dev; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + return audio_graph2_parse_of(priv, dev, NULL); +} + +static const struct of_device_id graph_of_match[] = { + { .compatible = "audio-graph-card2", }, + {}, +}; +MODULE_DEVICE_TABLE(of, graph_of_match); + +static struct platform_driver graph_card = { + .driver = { + .name = "asoc-audio-graph-card2", + .pm = &snd_soc_pm_ops, + .of_match_table = graph_of_match, + }, + .probe = graph_probe, + .remove = asoc_simple_remove, +}; +module_platform_driver(graph_card); + +MODULE_ALIAS("platform:asoc-audio-graph-card2"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph Card2"); +MODULE_AUTHOR("Kuninori Morimoto "); -- cgit v1.2.3 From c8c74939f791ccbbfff988aec5f929374dbef2a6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:55:03 +0900 Subject: ASoC: audio-graph-card2: add Multi CPU/Codec support This patch adds Multi CPU/Codec support to audio-graph-card2. Multi CPU/Codec will have connection part (= X) and CPU/Codec list part (= Y). links indicates connection part of CPU side (= A). +-+ (A) +-+ CPU1 --(Y) | | <-(X)--(X)-> | | (Y)-- Codec1 CPU2 --(Y) | | | | (Y)-- Codec2 +-+ +-+ sound { compatible = "audio-graph-card2"; (A) links = <&mcpu>; multi { ports@0 { (X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; (Y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; (Y) port@1 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; }; ports@1 { (X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; (Y) port@0 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; (Y) port@1 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; }; }; Codec { ports { port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; }; }; Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/20210804171748.GC26252@sirena.org.uk Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/871r4qn8pk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 196 ++++++++++++++++++++++++++++++++-- 1 file changed, 186 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 4ab726891e24..a7819a05e8b2 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -69,18 +69,95 @@ port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; }; }; + ************************************ + Multi-CPU/Codec + ************************************ + +It has connection part (= X) and list part (= y). +links indicates connection part of CPU side (= A). + + +-+ (A) +-+ + CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1 + CPU2 --(y) | | | | (y)-- Codec2 + +-+ +-+ + + sound { + compatible = "audio-graph-card2"; + +(A) links = <&mcpu>; + + multi { + ports@0 { +(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; +(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; +(y) port@1 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; + }; + ports@1 { +(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; +(y) port@0 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; +(y) port@1 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; + }; + }; + }; + + CPU { + ports { + bitclock-master; + frame-master; + port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; + port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; + }; + }; + + Codec { + ports { + port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; + port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; + }; + }; + */ enum graph_type { GRAPH_NORMAL, + + GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ }; +#define GRAPH_NODENAME_MULTI "multi" + #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") +static enum graph_type __graph_get_type(struct device_node *lnk) +{ + struct device_node *np; + + /* + * target { + * ports { + * => lnk: port@0 { ... }; + * port@1 { ... }; + * }; + * }; + */ + np = of_get_parent(lnk); + if (of_node_name_eq(np, "ports")) + np = of_get_parent(np); + + if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) + return GRAPH_MULTI; + + return GRAPH_NORMAL; +} + static enum graph_type graph_get_type(struct asoc_simple_priv *priv, struct device_node *lnk) { - enum graph_type type = GRAPH_NORMAL; + enum graph_type type = __graph_get_type(lnk); + + /* GRAPH_MULTI here means GRAPH_NORMAL */ + if (type == GRAPH_MULTI) + type = GRAPH_NORMAL; #ifdef DEBUG { @@ -93,6 +170,49 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv, return type; } +static int graph_lnk_is_multi(struct device_node *lnk) +{ + return __graph_get_type(lnk) == GRAPH_MULTI; +} + +static struct device_node *graph_get_next_multi_ep(struct device_node **port) +{ + struct device_node *ports = of_get_parent(*port); + struct device_node *ep = NULL; + struct device_node *rep = NULL; + + /* + * multi { + * ports { + * => lnk: port@0 { ... }; + * port@1 { ep { ... = rep0 } }; + * port@2 { ep { ... = rep1 } }; + * ... + * }; + * }; + * + * xxx { + * port@0 { rep0 }; + * port@1 { rep1 }; + * }; + */ + do { + *port = of_get_next_child(ports, *port); + if (!*port) + break; + } while (!of_node_name_eq(*port, "port")); + + if (*port) { + ep = port_to_endpoint(*port); + rep = of_graph_get_remote_endpoint(ep); + } + + of_node_put(ep); + of_node_put(ports); + + return rep; +} + static const struct snd_soc_ops graph_ops = { .startup = asoc_simple_startup, .shutdown = asoc_simple_shutdown, @@ -258,13 +378,21 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, if (!dai_link->name) { struct snd_soc_dai_link_component *cpus = dlc; struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); + char *cpu_multi = ""; + char *codec_multi = ""; + + if (dai_link->num_cpus > 1) + cpu_multi = "_multi"; + if (dai_link->num_codecs > 1) + codec_multi = "_multi"; switch (gtype) { case GRAPH_NORMAL: /* run is_cpu only. see audio_graph2_link_normal() */ if (is_cpu) - asoc_simple_set_dailink_name(dev, dai_link, "%s-%s", - cpus->dai_name, codecs->dai_name); + asoc_simple_set_dailink_name(dev, dai_link, "%s%s-%s%s", + cpus->dai_name, cpu_multi, + codecs->dai_name, codec_multi); break; default: break; @@ -287,10 +415,33 @@ static int graph_parse_node(struct asoc_simple_priv *priv, struct device_node *port, struct link_info *li, int is_cpu) { - struct device_node *ep = port_to_endpoint(port); + struct device_node *ep; + int ret = 0; - /* Need Multi support later */ - return __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); + if (graph_lnk_is_multi(port)) { + int idx; + + of_node_get(port); + + for (idx = 0;; idx++) { + ep = graph_get_next_multi_ep(&port); + if (!ep) + break; + + ret = __graph_parse_node(priv, gtype, ep, + li, is_cpu, idx); + of_node_put(ep); + if (ret < 0) + break; + } + } else { + /* Single CPU / Codec */ + ep = port_to_endpoint(port); + ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); + of_node_put(ep); + } + + return ret; } static void graph_parse_daifmt(struct device_node *node, @@ -354,8 +505,14 @@ static void graph_link_init(struct asoc_simple_priv *priv, unsigned int daifmt = 0, daiclk = 0; unsigned int bit_frame = 0; - /* Need Multi support later */ - ep = port_to_endpoint(port); + if (graph_lnk_is_multi(port)) { + of_node_get(port); + ep = graph_get_next_multi_ep(&port); + port = of_get_parent(ep); + } else { + ep = port_to_endpoint(port); + } + ports = of_get_parent(port); /* @@ -462,8 +619,27 @@ err: static int graph_counter(struct device_node *lnk) { - /* Need Multi support later */ - return 1; + /* + * Multi CPU / Codec + * + * multi { + * ports { + * => lnk: port@0 { ... }; + * port@1 { ... }; + * port@2 { ... }; + * ... + * }; + * }; + * + * ignore first lnk part + */ + if (graph_lnk_is_multi(lnk)) + return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1; + /* + * Single CPU / Codec + */ + else + return 1; } static int graph_count_normal(struct asoc_simple_priv *priv, -- cgit v1.2.3 From f03beb55a831bc7575b3c8882bf8fa6c81198eca Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:55:12 +0900 Subject: ASoC: audio-graph-card2: add DPCM support This patch adds DPCM support to audio-graph-card2. It uses "dpcm" node (= D), needs to have routing (= A), need to indicate both FE/BE at links (= B, C). dpcm ports@0 is for FE (= B), port@1 is for BE (= C). remote-endpoint can use both Single/Multi connection. DSP ************ PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers PCM2 <--> * fe2 be2 * <--> DAI2: MODEM PCM3 <--> * fe3 be3 * <--> DAI3: BT * be4 * <--> DAI4: DMIC * be5 * <--> DAI5: FM ************ sound { compatible = "audio-graph-card2"; // indicate routing (A) routing = "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback", "xxx Playback"; // indicate all Front-End, Back-End in DPCM case (B) links = <&fe0, &fe1, ... (C) &be0, &be1, ... (D) dpcm { // Front-End ports@0 { (B) fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; (B) fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; ... }; // Back-End ports@1 { (C) be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; (C) be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; ... }; }; }; CPU { ports { bitclock-master; frame-master; port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; ... }; }; Codec { ports { port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; ... }; }; Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87zgrelu4v.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 3 + sound/soc/generic/audio-graph-card2.c | 254 ++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) (limited to 'sound') diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index 497d59585b2d..ece78a84391c 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -17,6 +17,7 @@ struct graph2_custom_hooks { int (*hook_pre)(struct asoc_simple_priv *priv); int (*hook_post)(struct asoc_simple_priv *priv); GRAPH2_CUSTOM custom_normal; + GRAPH2_CUSTOM custom_dpcm; }; int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); @@ -25,5 +26,7 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, int audio_graph2_link_normal(struct asoc_simple_priv *priv, struct device_node *lnk, struct link_info *li); +int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, + struct device_node *lnk, struct link_info *li); #endif /* __GRAPH_CARD_H */ diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index a7819a05e8b2..56e9e6c3b86e 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -116,15 +116,77 @@ links indicates connection part of CPU side (= A). }; }; + ************************************ + DPCM + ************************************ + + DSP + ************ + PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset + PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers + PCM2 <--> * fe2 be2 * <--> DAI2: MODEM + PCM3 <--> * fe3 be3 * <--> DAI3: BT + * be4 * <--> DAI4: DMIC + * be5 * <--> DAI5: FM + ************ + + sound { + compatible = "audio-graph-card2"; + + // indicate routing + routing = "xxx Playback", "xxx Playback", + "xxx Playback", "xxx Playback", + "xxx Playback", "xxx Playback"; + + // indicate all Front-End, Back-End + links = <&fe0, &fe1, ..., + &be0, &be1, ...>; + + dpcm { + // Front-End + ports@0 { + fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; + fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; + ... + }; + // Back-End + ports@1 { + be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; + be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; + ... + }; + }; + }; + + CPU { + ports { + bitclock-master; + frame-master; + port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; + port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; + ... + }; + }; + + Codec { + ports { + port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; + port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; + ... + }; + }; + */ enum graph_type { GRAPH_NORMAL, + GRAPH_DPCM, GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ }; #define GRAPH_NODENAME_MULTI "multi" +#define GRAPH_NODENAME_DPCM "dpcm" #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") @@ -147,6 +209,9 @@ static enum graph_type __graph_get_type(struct device_node *lnk) if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) return GRAPH_MULTI; + if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) + return GRAPH_DPCM; + return GRAPH_NORMAL; } @@ -164,6 +229,17 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv, struct device *dev = simple_priv_to_dev(priv); const char *str = "Normal"; + switch (type) { + case GRAPH_DPCM: + if (asoc_graph_is_ports0(lnk)) + str = "DPCM Front-End"; + else + str = "DPCM Back-End"; + break; + default: + break; + } + dev_dbg(dev, "%pOF (%s)", lnk, str); } #endif @@ -322,6 +398,22 @@ static int asoc_simple_parse_dai(struct device_node *ep, return 0; } +static void graph_parse_convert(struct device_node *ep, + struct simple_dai_props *props) +{ + struct device_node *port = of_get_parent(ep); + struct device_node *ports = of_get_parent(port); + struct asoc_simple_data *adata = &props->adata; + + if (of_node_name_eq(ports, "ports")) + asoc_simple_parse_convert(ports, NULL, adata); + asoc_simple_parse_convert(port, NULL, adata); + asoc_simple_parse_convert(ep, NULL, adata); + + of_node_put(port); + of_node_put(ports); +} + static void graph_parse_mclk_fs(struct device_node *ep, struct simple_dai_props *props) { @@ -394,11 +486,37 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, cpus->dai_name, cpu_multi, codecs->dai_name, codec_multi); break; + case GRAPH_DPCM: + if (is_cpu) + asoc_simple_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s", + cpus->of_node, cpus->dai_name, cpu_multi); + else + asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", + codecs->of_node, codecs->dai_name, codec_multi); + break; default: break; } } + /* + * Check "prefix" from top node + * if DPCM-BE case + */ + if (!is_cpu && gtype == GRAPH_DPCM) { + struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); + struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); + struct device_node *rport = of_get_parent(ep); + struct device_node *rports = of_get_parent(rport); + + if (of_node_name_eq(rports, "ports")) + snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); + + of_node_put(rport); + of_node_put(rports); + } + if (is_cpu) { struct snd_soc_dai_link_component *cpus = dlc; struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, idx); @@ -582,6 +700,98 @@ err: } EXPORT_SYMBOL_GPL(audio_graph2_link_normal); +int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device_node *ep = port_to_endpoint(lnk); + struct device_node *rep = of_graph_get_remote_endpoint(ep); + struct device_node *rport = of_graph_get_remote_port(ep); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + int is_cpu = asoc_graph_is_ports0(lnk); + int ret; + + if (is_cpu) { + /* + * dpcm { + * // Front-End + * ports@0 { + * => lnk: port@0 { ep: { ... = rep }; }; + * ... + * }; + * // Back-End + * ports@0 { + * ... + * }; + * }; + * + * CPU { + * rports: ports { + * rport: port@0 { rep: { ... = ep } }; + * } + * } + */ + /* + * setup CPU here, Codec is already set as dummy. + * see + * asoc_simple_init_priv() + */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1); + if (ret) + goto err; + } else { + /* + * dpcm { + * // Front-End + * ports@0 { + * ... + * }; + * // Back-End + * ports@0 { + * => lnk: port@0 { ep: { ... = rep; }; }; + * ... + * }; + * }; + * + * Codec { + * rports: ports { + * rport: port@0 { rep: { ... = ep; }; }; + * } + * } + */ + /* + * setup Codec here, CPU is already set as dummy. + * see + * asoc_simple_init_priv() + */ + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; + + ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0); + if (ret < 0) + goto err; + } + + graph_parse_convert(rep, dai_props); + + snd_soc_dai_link_set_capabilities(dai_link); + + graph_link_init(priv, rport, li, is_cpu); +err: + of_node_put(ep); + of_node_put(rep); + of_node_put(rport); + + return ret; +} +EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); + static int graph_link(struct asoc_simple_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, @@ -599,6 +809,12 @@ static int graph_link(struct asoc_simple_priv *priv, else func = audio_graph2_link_normal; break; + case GRAPH_DPCM: + if (hooks && hooks->custom_dpcm) + func = hooks->custom_dpcm; + else + func = audio_graph2_link_dpcm; + break; default: break; } @@ -665,6 +881,41 @@ static int graph_count_normal(struct asoc_simple_priv *priv, return 0; } +static int graph_count_dpcm(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device_node *ep = port_to_endpoint(lnk); + struct device_node *rport = of_graph_get_remote_port(ep); + + /* + * dpcm { + * // Front-End + * ports@0 { + * => lnk: port@0 { endpoint { ... }; }; + * ... + * }; + * // Back-End + * ports@1 { + * => lnk: port@0 { endpoint { ... }; }; + * ... + * }; + * }; + */ + + if (asoc_graph_is_ports0(lnk)) { + li->num[li->link].cpus = graph_counter(rport); /* FE */ + li->num[li->link].platforms = graph_counter(rport); + } else { + li->num[li->link].codecs = graph_counter(rport); /* BE */ + } + + of_node_put(ep); + of_node_put(rport); + + return 0; +} + static int graph_count(struct asoc_simple_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, @@ -684,6 +935,9 @@ static int graph_count(struct asoc_simple_priv *priv, case GRAPH_NORMAL: func = graph_count_normal; break; + case GRAPH_DPCM: + func = graph_count_dpcm; + break; default: break; } -- cgit v1.2.3 From c3a15c92a67b701751c2680fa894d832570f7e7b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:55:33 +0900 Subject: ASoC: audio-graph-card2: add Codec2Codec support This patch adds Codec2Codec support to audio-graph-card2. It can use Codec2Codec but very simple case only for now. It doesn't have "SWITCH" control yet, thus it start automatically when it was probed, and can't stop, so far. Thus it needs to be updated around widgets/routing handling, and you need to understand that it is under experimental. Codec has SND_SOC_DAPM_INPUT() (= IN) / SND_SOC_DAPM_OUTPUT(= OUT) widgets in below case. It is assuming 2channel, S32_LE format for now. It needs to be updated, too. It needs "codec2codec" node (= B), needs to have routing (= A), need to indicate CPU side at links (= X). ports@0 is for CPU side (= X), port@1 is Codec side (= Y). It needs to have "rate" (= C) +--+ | |<-- Codec0 <-- IN | |--> Codec1 --> OUT +--+ sound { compatible = "audio-graph-card2"; (A) routing = "OUT" ,"DAI1 Playback", "DAI0 Capture", "IN"; (X) links = <&c2c>; (B) codec2codec { ports { (C) rate = <48000>; (X) c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; (Y) port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; }; }; Codec { ports { port@0 { bitclock-master; frame-master; codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; }; }; Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87y26ylu4a.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 3 + sound/soc/generic/audio-graph-card2.c | 181 ++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) (limited to 'sound') diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index ece78a84391c..4c8b94c77b8e 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -18,6 +18,7 @@ struct graph2_custom_hooks { int (*hook_post)(struct asoc_simple_priv *priv); GRAPH2_CUSTOM custom_normal; GRAPH2_CUSTOM custom_dpcm; + GRAPH2_CUSTOM custom_c2c; }; int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); @@ -28,5 +29,7 @@ int audio_graph2_link_normal(struct asoc_simple_priv *priv, struct device_node *lnk, struct link_info *li); int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, struct device_node *lnk, struct link_info *li); +int audio_graph2_link_c2c(struct asoc_simple_priv *priv, + struct device_node *lnk, struct link_info *li); #endif /* __GRAPH_CARD_H */ diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 56e9e6c3b86e..b6049bcfb771 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -176,17 +176,54 @@ links indicates connection part of CPU side (= A). }; }; + ************************************ + Codec to Codec + ************************************ + + +--+ + | |<-- Codec0 <- IN + | |--> Codec1 -> OUT + +--+ + + sound { + compatible = "audio-graph-card2"; + + routing = "OUT" ,"DAI1 Playback", + "DAI0 Capture", "IN"; + + links = <&c2c>; + + codec2codec { + ports { + rate = <48000>; + c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; + port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; + }; + }; + + Codec { + ports { + port@0 { + bitclock-master; + frame-master; + codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; + port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; + }; + }; + */ enum graph_type { GRAPH_NORMAL, GRAPH_DPCM, + GRAPH_C2C, GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ }; #define GRAPH_NODENAME_MULTI "multi" #define GRAPH_NODENAME_DPCM "dpcm" +#define GRAPH_NODENAME_C2C "codec2codec" #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") @@ -212,6 +249,9 @@ static enum graph_type __graph_get_type(struct device_node *lnk) if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) return GRAPH_DPCM; + if (of_node_name_eq(np, GRAPH_NODENAME_C2C)) + return GRAPH_C2C; + return GRAPH_NORMAL; } @@ -236,6 +276,9 @@ static enum graph_type graph_get_type(struct asoc_simple_priv *priv, else str = "DPCM Back-End"; break; + case GRAPH_C2C: + str = "Codec2Codec"; + break; default: break; } @@ -494,6 +537,13 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", codecs->of_node, codecs->dai_name, codec_multi); break; + case GRAPH_C2C: + /* run is_cpu only. see audio_graph2_link_c2c() */ + if (is_cpu) + asoc_simple_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s", + cpus->dai_name, cpu_multi, + codecs->dai_name, codec_multi); + break; default: break; } @@ -792,6 +842,91 @@ err: } EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); +int audio_graph2_link_c2c(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct snd_soc_pcm_stream *c2c_conf = dai_props->c2c_conf; + struct device_node *port0, *port1, *ports; + struct device_node *codec0_port, *codec1_port; + struct device_node *ep0, *ep1; + u32 val; + int ret = -EINVAL; + + /* + * codec2codec { + * ports { + * rate = <48000>; + * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; }; + * port@1 { c2c1_ep: { ... = codec1_ep; }; }; + * }; + * }; + * + * Codec { + * ports { + * port@0 { codec0_ep: ... }; }; + * port@1 { codec1_ep: ... }; }; + * }; + * }; + */ + of_node_get(lnk); + port0 = lnk; + ports = of_get_parent(port0); + port1 = of_get_next_child(ports, lnk); + + if (!of_get_property(ports, "rate", &val)) { + struct device *dev = simple_priv_to_dev(priv); + + dev_err(dev, "Codec2Codec needs rate settings\n"); + goto err1; + } + + c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */ + c2c_conf->rate_min = + c2c_conf->rate_max = val; + c2c_conf->channels_min = + c2c_conf->channels_max = 2; /* update ME */ + dai_link->params = c2c_conf; + + ep0 = port_to_endpoint(port0); + ep1 = port_to_endpoint(port1); + + codec0_port = of_graph_get_remote_port(ep0); + codec1_port = of_graph_get_remote_port(ep1); + + /* + * call Codec first. + * see + * __graph_parse_node() :: DAI Naming + */ + ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0); + if (ret < 0) + goto err2; + + /* + * call CPU, and set DAI Name + */ + ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1); + if (ret < 0) + goto err2; + + graph_link_init(priv, codec0_port, li, 1); +err2: + of_node_put(ep0); + of_node_put(ep1); + of_node_put(codec0_port); + of_node_put(codec1_port); +err1: + of_node_put(ports); + of_node_put(port0); + of_node_put(port1); + + return ret; +} +EXPORT_SYMBOL_GPL(audio_graph2_link_c2c); + static int graph_link(struct asoc_simple_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, @@ -815,6 +950,12 @@ static int graph_link(struct asoc_simple_priv *priv, else func = audio_graph2_link_dpcm; break; + case GRAPH_C2C: + if (hooks && hooks->custom_c2c) + func = hooks->custom_c2c; + else + func = audio_graph2_link_c2c; + break; default: break; } @@ -916,6 +1057,43 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, return 0; } +static int graph_count_c2c(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device_node *ports = of_get_parent(lnk); + struct device_node *port0 = lnk; + struct device_node *port1 = of_get_next_child(ports, lnk); + struct device_node *ep0 = port_to_endpoint(port0); + struct device_node *ep1 = port_to_endpoint(port1); + struct device_node *codec0 = of_graph_get_remote_port(ep0); + struct device_node *codec1 = of_graph_get_remote_port(ep1); + + of_node_get(lnk); + + /* + * codec2codec { + * ports { + * => lnk: port@0 { endpoint { ... }; }; + * port@1 { endpoint { ... }; }; + * }; + * }; + */ + li->num[li->link].cpus = + li->num[li->link].platforms = graph_counter(codec0); + li->num[li->link].codecs = graph_counter(codec1); + li->num[li->link].c2c = 1; + + of_node_put(ports); + of_node_put(port1); + of_node_put(ep0); + of_node_put(ep1); + of_node_put(codec0); + of_node_put(codec1); + + return 0; +} + static int graph_count(struct asoc_simple_priv *priv, struct graph2_custom_hooks *hooks, enum graph_type gtype, @@ -938,6 +1116,9 @@ static int graph_count(struct asoc_simple_priv *priv, case GRAPH_DPCM: func = graph_count_dpcm; break; + case GRAPH_C2C: + func = graph_count_c2c; + break; default: break; } -- cgit v1.2.3 From 95373f36b9b810aa5461e3a864d7a3ad05b30b91 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:07 +0900 Subject: ASoC: add Audio Graph Card2 Custom Sample audio-graph-card2 has customizing support. This means user can re-use audio-graph-card2 DT parsing, and possible to expand to own special handling. This patch adds Audio Graph Card2 Customize Sample Driver. It can re-use audio-graph-card2 parsing by calling audio_graph2_parse_of(...), and user can expand each functions by using hooks. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87v922lu3c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/Kconfig | 6 + sound/soc/generic/Makefile | 2 + .../soc/generic/audio-graph-card2-custom-sample.c | 183 +++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 sound/soc/generic/audio-graph-card2-custom-sample.c (limited to 'sound') diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index 829ca2b0bf76..b6df4e26bc4a 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -26,6 +26,12 @@ config SND_AUDIO_GRAPH_CARD2 This option enables generic simple sound card2 support with OF-graph DT bindings. +config SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE + tristate "ASoC Audio Graph Card2 base custom sample support" + depends on SND_AUDIO_GRAPH_CARD2 + help + This option enables Audio Graph Card2 base custom sample + config SND_TEST_COMPONENT tristate "ASoC Test component sound support" depends on OF diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index b480f47a330d..084862156506 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -3,10 +3,12 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o snd-soc-audio-graph-card2-objs := audio-graph-card2.o +snd-soc-audio-graph-card2-custom-sample-objs := audio-graph-card2-custom-sample.o snd-soc-test-component-objs := test-component.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE) += snd-soc-audio-graph-card2-custom-sample.o obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.c b/sound/soc/generic/audio-graph-card2-custom-sample.c new file mode 100644 index 000000000000..4a2c743e286c --- /dev/null +++ b/sound/soc/generic/audio-graph-card2-custom-sample.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// audio-graph-card2-custom-sample.c +// +// Copyright (C) 2020 Renesas Electronics Corp. +// Copyright (C) 2020 Kuninori Morimoto +// +#include +#include +#include +#include + +/* + * Custom driver can have own priv + * which includes asoc_simple_priv. + */ +struct custom_priv { + struct asoc_simple_priv simple_priv; + + /* custom driver's own params */ + int custom_params; +}; + +/* You can get custom_priv from simple_priv */ +#define simple_to_custom(simple) container_of((simple), struct custom_priv, simple_priv) + +static int custom_card_probe(struct snd_soc_card *card) +{ + struct asoc_simple_priv *simple_priv = snd_soc_card_get_drvdata(card); + struct custom_priv *custom_priv = simple_to_custom(simple_priv); + struct device *dev = simple_priv_to_dev(simple_priv); + + dev_info(dev, "custom probe\n"); + + custom_priv->custom_params = 1; + + /* you can use generic probe function */ + return asoc_graph_card_probe(card); +} + +static int custom_hook_pre(struct asoc_simple_priv *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + + /* You can custom before parsing */ + dev_info(dev, "hook : %s\n", __func__); + + return 0; +} + +static int custom_hook_post(struct asoc_simple_priv *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_card *card; + + /* You can custom after parsing */ + dev_info(dev, "hook : %s\n", __func__); + + /* overwrite .probe sample */ + card = simple_priv_to_card(priv); + card->probe = custom_card_probe; + + return 0; +} + +static int custom_normal(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + + /* + * You can custom Normal parsing + * before/affter audio_graph2_link_normal() + */ + dev_info(dev, "hook : %s\n", __func__); + + return audio_graph2_link_normal(priv, lnk, li); +} + +static int custom_dpcm(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + + /* + * You can custom DPCM parsing + * before/affter audio_graph2_link_dpcm() + */ + dev_info(dev, "hook : %s\n", __func__); + + return audio_graph2_link_dpcm(priv, lnk, li); +} + +static int custom_c2c(struct asoc_simple_priv *priv, + struct device_node *lnk, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + + /* + * You can custom Codec2Codec parsing + * before/affter audio_graph2_link_c2c() + */ + dev_info(dev, "hook : %s\n", __func__); + + return audio_graph2_link_c2c(priv, lnk, li); +} + +/* + * audio-graph-card2 has many hooks for your customizing. + */ +static struct graph2_custom_hooks custom_hooks = { + .hook_pre = custom_hook_pre, + .hook_post = custom_hook_post, + .custom_normal = custom_normal, + .custom_dpcm = custom_dpcm, + .custom_c2c = custom_c2c, +}; + +static int custom_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct device *dev = simple_priv_to_dev(priv); + + dev_info(dev, "custom startup\n"); + + return asoc_simple_startup(substream); +} + +/* You can use custom ops */ +static const struct snd_soc_ops custom_ops = { + .startup = custom_startup, + .shutdown = asoc_simple_shutdown, + .hw_params = asoc_simple_hw_params, +}; + +static int custom_probe(struct platform_device *pdev) +{ + struct custom_priv *custom_priv; + struct asoc_simple_priv *simple_priv; + struct device *dev = &pdev->dev; + int ret; + + custom_priv = devm_kzalloc(dev, sizeof(*custom_priv), GFP_KERNEL); + if (!custom_priv) + return -ENOMEM; + + simple_priv = &custom_priv->simple_priv; + simple_priv->ops = &custom_ops; /* customize dai_link ops */ + + /* use audio-graph-card2 parsing with own custom hooks */ + ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks); + if (ret < 0) + return ret; + + /* customize more if needed */ + + return 0; +} + +static const struct of_device_id custom_of_match[] = { + { .compatible = "audio-graph-card2-custom-sample", }, + {}, +}; +MODULE_DEVICE_TABLE(of, custom_of_match); + +static struct platform_driver custom_card = { + .driver = { + .name = "audio-graph-card2-custom-sample", + .of_match_table = custom_of_match, + }, + .probe = custom_probe, + .remove = asoc_simple_remove, +}; +module_platform_driver(custom_card); + +MODULE_ALIAS("platform:asoc-audio-graph-card2-custom-sample"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph Card2 Custom Sample"); +MODULE_AUTHOR("Kuninori Morimoto "); -- cgit v1.2.3 From c601fdf5c845b5bc416a1215cd22a7a786fcf268 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:14 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add Sample DT for Normal (Single) Audio Graph Card2 settings is a little bit difficult for beginner, and Customizing it also difficult/confusable too. So, this patch adds sample for it. You can easily use it by adding below line on your DT file, and select CONFIGs to your .config. #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi" CONFIG_SND_AUDIO_GRAPH_CARD2 CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE CONFIG_SND_TEST_COMPONENT This patch uses audio-graph-card2 base custom sample driver. You can directly use audio-graph-card2 instead of custom sample driver by modifing compatible. - compatible = "audio-graph-card2-custom-sample"; + compatible = "audio-graph-card2"; Sample custom driver will indicate customized print. It is using Test-Component driver for CPU/Codec. It can indicate more detail print of each behavior if user want to. In such case, you need to update compatible to "xxx-nv" or "xxx-vv". - compatible = "test-cpu"; + compatible = "test-cpu-verbose"; Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87tuhmlu35.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 sound/soc/generic/audio-graph-card2-custom-sample.dtsi (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi new file mode 100644 index 000000000000..c2511da31530 --- /dev/null +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * audio-graph-card2-custom-sample.dtsi + * + * Copyright (C) 2020 Renesas Electronics Corp. + * Copyright (C) 2020 Kuninori Morimoto + * + * This sample indicates how to use audio-graph-card2 and its + * custom driver. "audio-graph-card2-custom-sample" is the custome driver + * which is using audio-graph-card2. + * + * You can easily use this sample by adding below line on your DT file, + * and add new CONFIG to your .config. + * + * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi" + * + * CONFIG_SND_AUDIO_GRAPH_CARD2 + * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE + * CONFIG_SND_TEST_COMPONENT + */ +/ { + /* + * @ : used at links + * + * [Normal] + * cpu0 <-@-----------------> codec0 + */ + audio-graph-card2-custom-sample { + /* + * You can use audio-graph-card2 directly by using + * + * compatible = "audio-graph-card2"; + */ + compatible = "audio-graph-card2-custom-sample"; + + links = <&cpu0 /* normal: cpu side only */ + >; + }; + + test_cpu { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-cpu"; + * + compatible = "test-cpu-verbose"; + */ + compatible = "test-cpu"; + ports { + bitclock-master; + frame-master; + cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; + }; + }; + + test_codec { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-codec"; + * + compatible = "test-codec-verbose"; + */ + compatible = "test-codec"; + ports { + port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; + }; + }; +}; -- cgit v1.2.3 From 5279bd8a842b88b24724dc6364b9850eacb5f490 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:19 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add Sample DT for Normal (Nulti) This patch adds Normal link Multi-CPU/Codec sample to audio-graph-card2-custom-sample.dtsi. +-+ +-+ CPU1 --| | <---> | | -- Codec1 CPU2 --| | | | -- Codec2 +-+ +-+ Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87sfx6lu30.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index c2511da31530..b4f4fa743c2b 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -24,6 +24,12 @@ * * [Normal] * cpu0 <-@-----------------> codec0 + * + * [Multi-CPU/Codec] + * +-+ +-+ + * cpu1 <--| |<-@--------->| |-> codec1 + * cpu2 <--| | | |-> codec2 + * +-+ +-+ */ audio-graph-card2-custom-sample { /* @@ -34,7 +40,21 @@ compatible = "audio-graph-card2-custom-sample"; links = <&cpu0 /* normal: cpu side only */ + &mcpu0 /* multi: cpu side only */ >; + + multi { + ports@0 { + mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; + port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; + port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; + }; + ports@1 { + port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; + port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; + port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; + }; + }; }; test_cpu { @@ -51,6 +71,8 @@ bitclock-master; frame-master; cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; + port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; + port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; }; }; @@ -66,6 +88,8 @@ compatible = "test-codec"; ports { port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; + port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; + port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; }; }; }; -- cgit v1.2.3 From e781759ab87b5b7bc4282faf08352e564c3eaf81 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:23 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add DPCM sample (Single) This patch adds DPCM link Single-CPU/Codec sample to audio-graph-card2-custom-sample.dtsi. This sample is assuming MIXer connection. FE BE **** CPU3 -- * * -- Codec3 CPU4 -- * * **** Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87r1cqlu2w.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index b4f4fa743c2b..b03dbce504cb 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -30,6 +30,13 @@ * cpu1 <--| |<-@--------->| |-> codec1 * cpu2 <--| | | |-> codec2 * +-+ +-+ + * + * [DPCM] + * FE BE + * **** + * cpu3 <-@--* *--@-> codec3 + * cpu4 <-@--* * + * **** */ audio-graph-card2-custom-sample { /* @@ -39,8 +46,16 @@ */ compatible = "audio-graph-card2-custom-sample"; + /* for [DPCM] */ + /* BE FE */ + routing = "TC DAI3 Playback", "DAI3 Playback", + "TC DAI3 Playback", "DAI4 Playback", + "DAI3 Capture", "TC DAI3 Capture", + "DAI4 Capture", "TC DAI3 Capture"; + links = <&cpu0 /* normal: cpu side only */ &mcpu0 /* multi: cpu side only */ + &fe00 &fe01 &be0 /* dpcm: both FE / BE */ >; multi { @@ -55,6 +70,18 @@ port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; }; + + dpcm { + /* FE */ + ports@0 { + fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; }; + fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; }; + }; + /* BE */ + ports@1 { + be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; + }; + }; }; test_cpu { @@ -73,6 +100,8 @@ cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; + port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; + port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; }; }; }; @@ -87,9 +116,16 @@ */ compatible = "test-codec"; ports { + /* + * prefix can be added to *component*, + * see audio-graph-card2::routing + */ + prefix = "TC"; + port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; + port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; }; }; }; }; -- cgit v1.2.3 From cb2d94aa4d51e49f68ea02fe49225948467427bd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:27 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add DPCM sample (Multi) This patch adds DPCM link Multi-CPU/Codec sample to audio-graph-card2-custom-sample.dtsi. This sample is assuming MIXer connection. One note is that Multi-FE is not supported on ASoC FE BE **** +-+ CPU5 -- * * -- | | -- Codec4 CPU6 -- * * | | -- Codec5 **** +-+ Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87pmsalu2s.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index b03dbce504cb..4bc96e0aa447 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -37,6 +37,17 @@ * cpu3 <-@--* *--@-> codec3 * cpu4 <-@--* * * **** + * + * [DPCM-Multi] + * + * --NOTE-- + * Multi-FE is not supported by ASoC. + * + * FE BE + * **** +-+ + * cpu5 <-@--* *--@-> | | -> codec4 + * cpu6 <-@--* * | | -> codec5 + * **** +-+ */ audio-graph-card2-custom-sample { /* @@ -51,11 +62,22 @@ routing = "TC DAI3 Playback", "DAI3 Playback", "TC DAI3 Playback", "DAI4 Playback", "DAI3 Capture", "TC DAI3 Capture", - "DAI4 Capture", "TC DAI3 Capture"; + "DAI4 Capture", "TC DAI3 Capture", + /* for [DPCM-Multi] */ + /* BE FE */ + "TC DAI4 Playback", "DAI5 Playback", + "TC DAI5 Playback", "DAI5 Playback", + "TC DAI4 Playback", "DAI6 Playback", + "TC DAI5 Playback", "DAI6 Playback", + "DAI5 Capture", "TC DAI4 Capture", + "DAI5 Capture", "TC DAI5 Capture", + "DAI6 Capture", "TC DAI4 Capture", + "DAI6 Capture", "TC DAI5 Capture"; links = <&cpu0 /* normal: cpu side only */ &mcpu0 /* multi: cpu side only */ &fe00 &fe01 &be0 /* dpcm: both FE / BE */ + &fe10 &fe11 &be1 /* dpcm-m: both FE / BE */ >; multi { @@ -69,6 +91,11 @@ port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; + ports@2 { + port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; }; + port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; }; + port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; }; + }; }; dpcm { @@ -76,10 +103,13 @@ ports@0 { fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; }; fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; }; + fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; }; + fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; }; }; /* BE */ ports@1 { be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; + be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; }; }; }; @@ -102,6 +132,8 @@ port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; }; + port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; }; + port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; }; }; }; @@ -126,6 +158,8 @@ port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; }; + port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; }; + port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; }; }; }; }; -- cgit v1.2.3 From 349b15ef9d535116ded20fd2ac945afce98b227e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:31 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add Codec2Codec sample (Single) This patch adds Codec2Codec-Single sample to audio-graph-card2-custom-sample.dtsi. Because it can use very basic connection only for now, it can use only - 2channels - S32_LE format Test-Component driver has "IN" and "OUT" widget. Thus the route is +--+ | | <-- Codec6 <-- IN | | --> Codec7 --> OUT +--+ One note here is that it will start works when it boot. In other words we can't stop it so far. We need to update driver for it in the future. ... asoc-audio-graph-card2-custom-sample: test_codec.7 <-> test_codec.6 mapping ok test-component test_codec: test_dai_startup() : test_codec.6 test-component test_codec: test_dai_startup() : test_codec.7 ... Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87o87ulu2o.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 4bc96e0aa447..f416dead9522 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -48,6 +48,11 @@ * cpu5 <-@--* *--@-> | | -> codec4 * cpu6 <-@--* * | | -> codec5 * **** +-+ + * + * [Codec2Codec] + * +-@-> codec6 + * | + * +---> codec7 */ audio-graph-card2-custom-sample { /* @@ -72,12 +77,16 @@ "DAI5 Capture", "TC DAI4 Capture", "DAI5 Capture", "TC DAI5 Capture", "DAI6 Capture", "TC DAI4 Capture", - "DAI6 Capture", "TC DAI5 Capture"; + "DAI6 Capture", "TC DAI5 Capture", + /* for [Codec2Codec] */ + "TC OUT", "TC DAI7 Playback", + "TC DAI6 Capture", "TC IN"; links = <&cpu0 /* normal: cpu side only */ &mcpu0 /* multi: cpu side only */ &fe00 &fe01 &be0 /* dpcm: both FE / BE */ &fe10 &fe11 &be1 /* dpcm-m: both FE / BE */ + &c2c /* c2c: cpu side only */ >; multi { @@ -112,6 +121,14 @@ be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; }; }; + + codec2codec { + ports@0 { + rate = <48000>; + c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; }; + port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; + }; + }; }; test_cpu { @@ -160,6 +177,10 @@ port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; }; port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; }; port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; }; + port@6 { bitclock-master; + frame-master; + codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; + port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; }; }; }; -- cgit v1.2.3 From baa274db99effe4fd85cf7bee70fecc8159be0cb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 12 Oct 2021 13:56:35 +0900 Subject: ASoC: audio-graph-card2-custom-sample.dtsi: add Codec2Codec sample (Multi) This patch adds Codec2Codec-Multi sample to audio-graph-card2-custom-sample.dtsi. Because it can use very basic connection only for now, it can use only - 2channels - S32_LE format Test-Component driver has "IN" and "OUT" widget. Thus the route is +--+ +-+ | | | |- Codec8 <- IN | | <- | |- Codec9 <- IN | | +-+ | | | | +-+ | | -> | |- Codec10 -> OUT | | | |- Codec11 -> OUT +--+ +-+ One note here is that it will start works when it boot. In other words we can't stop it so far. We need to update driver for it in the future. ... asoc-audio-graph-card2-custom-sample: multicodec <-> multicpu mapping ok test-component test_codec: test_dai_startup() : test_codec.9 test-component test_codec: test_dai_startup() : test_codec.8 test-component test_codec: test_dai_startup() : test_codec.11 test-component test_codec: test_dai_startup() : test_codec.10 ... Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87mtnelu2k.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../generic/audio-graph-card2-custom-sample.dtsi | 43 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index f416dead9522..8eee7b821ff7 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -53,6 +53,20 @@ * +-@-> codec6 * | * +---> codec7 + * + * [Codec2Codec-Multi] + * + * --NOTE-- + * Multi connect N:M is not supported by ASoC. + * + * +-+ + * +-@->| |-> codec8 + * | | |-> codec9 + * | +-+ + * | +-+ + * +--->| |-> codec10 + * | |-> codec11 + * +-+ */ audio-graph-card2-custom-sample { /* @@ -80,13 +94,19 @@ "DAI6 Capture", "TC DAI5 Capture", /* for [Codec2Codec] */ "TC OUT", "TC DAI7 Playback", - "TC DAI6 Capture", "TC IN"; + "TC DAI6 Capture", "TC IN", + /* for [Codec2Codec-Multi] */ + "TC OUT", "TC DAI10 Playback", + "TC DAI8 Capture", "TC IN", + "TC OUT", "TC DAI11 Playback", + "TC DAI9 Capture", "TC IN"; links = <&cpu0 /* normal: cpu side only */ &mcpu0 /* multi: cpu side only */ &fe00 &fe01 &be0 /* dpcm: both FE / BE */ &fe10 &fe11 &be1 /* dpcm-m: both FE / BE */ &c2c /* c2c: cpu side only */ + &c2c_m /* c2c: cpu side only */ >; multi { @@ -105,6 +125,16 @@ port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; }; port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; }; }; + ports@3 { + port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; }; + port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; }; + port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; }; + }; + ports@4 { + port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; }; + port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; }; + port@2 { mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; }; + }; }; dpcm { @@ -128,6 +158,11 @@ c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; }; port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; }; + ports@1 { + rate = <48000>; + c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; }; + port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; }; + }; }; }; @@ -181,6 +216,12 @@ frame-master; codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; + port@8 { bitclock-master; + frame-master; + codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; }; + port@9 { codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; }; + port@10 { codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; }; + port@11 { codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; }; }; }; }; -- cgit v1.2.3