diff --git a/sound/soc/tegra-alt/Makefile b/sound/soc/tegra-alt/Makefile index 144bd0c1..d8b28a1b 100644 --- a/sound/soc/tegra-alt/Makefile +++ b/sound/soc/tegra-alt/Makefile @@ -14,6 +14,7 @@ CFLAGS_tegra_pcm_alt.o += -Wno-shift-count-overflow snd-soc-tegra-alt-utils-objs := utils/tegra_asoc_utils_alt.o \ utils/tegra_pcm_alt.o \ utils/tegra_asoc_machine_alt.o \ + utils/tegra_asoc_dt_parser.o \ utils/tegra_isomgr_bw_alt.o \ utils/ahub_unit_fpga_clock.o \ utils/tegra210_xbar_utils_alt.o diff --git a/sound/soc/tegra-alt/include/tegra_asoc_machine_alt.h b/sound/soc/tegra-alt/include/tegra_asoc_machine_alt.h index f32384d2..4e4749e3 100644 --- a/sound/soc/tegra-alt/include/tegra_asoc_machine_alt.h +++ b/sound/soc/tegra-alt/include/tegra_asoc_machine_alt.h @@ -21,27 +21,6 @@ #include "tegra_asoc_utils_alt.h" -/* used for soc specific data */ -struct tegra_machine_soc_data { - unsigned int num_xbar_dai_links, - /* dai link indexes */ - admaif_dai_link_start, - admaif_dai_link_end, - adsp_pcm_dai_link_start, - adsp_pcm_dai_link_end, - adsp_compr_dai_link_start, - adsp_compr_dai_link_end, - sfc_dai_link; - - bool is_asrc_available, - write_idle_bias_off_state; - - struct snd_soc_codec_conf *ahub_confs; - struct snd_soc_dai_link *ahub_links; - unsigned int num_ahub_links; - unsigned int num_ahub_confs; -}; - /* * struct tegra_asoc - ASoC topology of dai links and codec confs * @codec_confs: Configuration of codecs from xbar and devicetree @@ -63,7 +42,6 @@ struct tegra_asoc { /* machine structure which holds sound card */ struct tegra_machine { struct tegra_asoc_audio_clock_info audio_clock; - struct tegra_machine_soc_data *soc_data; struct tegra_asoc *asoc; unsigned int num_codec_links; int rate_via_kcontrol; @@ -487,17 +465,20 @@ unsigned int tegra_machine_get_tx_mask_t18x( struct snd_soc_pcm_runtime *rtd); void tegra_machine_remove_adsp_links_t18x(void); -int tegra_machine_add_i2s_codec_controls(struct snd_soc_card *card, - unsigned int num_dai_links); +int tegra_machine_add_i2s_codec_controls(struct snd_soc_card *card); int tegra_machine_add_codec_jack_control(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack); -/* new helper functions for populating sound card DAI links and codec confs */ -int tegra_asoc_populate_dai_links(struct platform_device *pdev); -int tegra_asoc_populate_codec_confs(struct platform_device *pdev); void release_asoc_phandles(struct tegra_machine *machine); +/* + * new helper functions for parsing all DAI links from DT. + * Representation of XBAR and codec links would be similar. + */ +int parse_card_info(struct snd_soc_card *card, struct snd_soc_ops *pcm_ops, + struct snd_soc_compr_ops *compr_ops); + /* for legacy machine driver support */ static inline int tegra_machine_get_bclk_ratio(struct snd_soc_pcm_runtime *rtd, unsigned int *ratio) { diff --git a/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c b/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c index b4ad60d5..4d820c3c 100644 --- a/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c +++ b/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c @@ -35,55 +35,6 @@ #define DRV_NAME "tegra-asoc:" -#define PARAMS(sformat, channels) \ - { \ - .formats = sformat, \ - .rate_min = 48000, \ - .rate_max = 48000, \ - .channels_min = channels, \ - .channels_max = channels, \ - } - -/* t210 soc data */ -static const struct tegra_machine_soc_data soc_data_tegra210 = { - .admaif_dai_link_start = TEGRA210_DAI_LINK_ADMAIF1, - .admaif_dai_link_end = TEGRA210_DAI_LINK_ADMAIF10, -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) - .adsp_pcm_dai_link_start = TEGRA210_DAI_LINK_ADSP_PCM1, - .adsp_pcm_dai_link_end = TEGRA210_DAI_LINK_ADSP_PCM2, - .adsp_compr_dai_link_start = TEGRA210_DAI_LINK_ADSP_COMPR1, - .adsp_compr_dai_link_end = TEGRA210_DAI_LINK_ADSP_COMPR2, -#endif - .sfc_dai_link = TEGRA210_DAI_LINK_SFC1_RX, - - .write_idle_bias_off_state = false, - - .ahub_links = tegra210_xbar_dai_links, - .num_ahub_links = TEGRA210_XBAR_DAI_LINKS, - .ahub_confs = tegra210_xbar_codec_conf, - .num_ahub_confs = TEGRA210_XBAR_CODEC_CONF, -}; - -/* t186 soc data */ -static const struct tegra_machine_soc_data soc_data_tegra186 = { - .admaif_dai_link_start = TEGRA186_DAI_LINK_ADMAIF1, - .admaif_dai_link_end = TEGRA186_DAI_LINK_ADMAIF10, -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) - .adsp_pcm_dai_link_start = TEGRA186_DAI_LINK_ADSP_PCM1, - .adsp_pcm_dai_link_end = TEGRA186_DAI_LINK_ADSP_PCM2, - .adsp_compr_dai_link_start = TEGRA186_DAI_LINK_ADSP_COMPR1, - .adsp_compr_dai_link_end = TEGRA186_DAI_LINK_ADSP_COMPR2, -#endif - .sfc_dai_link = TEGRA186_DAI_LINK_SFC1_RX, - - .write_idle_bias_off_state = true, - - .ahub_links = tegra186_xbar_dai_links, - .num_ahub_links = TEGRA186_XBAR_DAI_LINKS, - .ahub_confs = tegra186_xbar_codec_conf, - .num_ahub_confs = TEGRA186_XBAR_CODEC_CONF, -}; - static const char * const tegra_machine_srate_text[] = { "None", "8kHz", @@ -147,15 +98,6 @@ static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = { SND_SOC_DAPM_MIC("y Mic", NULL), }; -static struct snd_soc_pcm_stream tegra_machine_asrc_link_params[] = { - PARAMS(SNDRV_PCM_FMTBIT_S32_LE, 8), - PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), - PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), - PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), - PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), - PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), -}; - static int tegra_machine_codec_get_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -202,80 +144,24 @@ static int tegra_machine_codec_put_format(struct snd_kcontrol *kcontrol, return 0; } -static int tegra_machine_set_params(struct snd_soc_card *card, - struct tegra_machine *machine, - unsigned int rate, - unsigned int channels, - u64 formats) -{ - unsigned int mask = (1 << channels) - 1; - struct snd_soc_pcm_runtime *rtd; - int idx = 0, err = 0; - u64 format_k; - - int num_of_dai_links = machine->soc_data->num_ahub_links + - machine->num_codec_links; - - format_k = (machine->fmt_via_kcontrol == 2) ? - (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; - - /* update dai link hw_params */ - list_for_each_entry(rtd, &card->rtd_list, list) { - if (rtd->dai_link->params) { - struct snd_soc_pcm_stream *dai_params; - - dai_params = - (struct snd_soc_pcm_stream *) - rtd->dai_link->params; - - dai_params->rate_min = rate; - dai_params->channels_min = channels; - dai_params->formats = format_k; - - if ((idx >= machine->soc_data->num_ahub_links) - && (idx < num_of_dai_links)) { - unsigned int fmt; - - /* TODO: why below overrite is needed */ - dai_params->formats = formats; - - fmt = rtd->dai_link->dai_fmt; - fmt &= SND_SOC_DAIFMT_FORMAT_MASK; - - /* set TDM slot mask */ - if (fmt == SND_SOC_DAIFMT_DSP_A || - fmt == SND_SOC_DAIFMT_DSP_B) { - err = snd_soc_dai_set_tdm_slot( - rtd->cpu_dai, mask, - mask, 0, 0); - if (err < 0) { - dev_err(card->dev, - "%s cpu DAI slot mask not set\n", - rtd->cpu_dai->name); - return err; - } - } - } - } - idx++; - } - return 0; -} - static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, unsigned int rate, unsigned int channels, u64 formats) { + unsigned int mask = (1 << channels) - 1; struct snd_soc_card *card = runtime->card; struct tegra_machine *machine = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_stream *dai_params; unsigned int aud_mclk, srate; + u64 format_k, fmt; int err; struct snd_soc_pcm_runtime *rtd; srate = (machine->rate_via_kcontrol) ? tegra_machine_srate_values[machine->rate_via_kcontrol] : rate; + format_k = (machine->fmt_via_kcontrol == 2) ? + (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock, srate, 0, 0); if (err < 0) { @@ -288,19 +174,32 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, pr_debug("pll_a_out0 = %u Hz, aud_mclk = %u Hz, sample rate = %u Hz\n", machine->audio_clock.set_pll_out_rate, aud_mclk, srate); - err = tegra_machine_set_params(card, machine, rate, channels, formats); - if (err < 0) - return err; + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!rtd->dai_link->params) + continue; + dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params; + dai_params->rate_min = srate; + dai_params->channels_min = channels; + dai_params->formats = format_k; + + fmt = rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK; + /* set TDM slot mask */ + if (fmt == SND_SOC_DAIFMT_DSP_A || + fmt == SND_SOC_DAIFMT_DSP_B) { + err = snd_soc_dai_set_tdm_slot( + rtd->cpu_dai, mask, + mask, 0, 0); + if (err < 0) { + dev_err(card->dev, + "%s cpu DAI slot mask not set\n", + rtd->cpu_dai->name); + return err; + } + } + } rtd = snd_soc_get_pcm_runtime(card, "rt565x-playback"); if (rtd) { - dai_params = - (struct snd_soc_pcm_stream *)rtd->dai_link->params; - - dai_params->rate_min = srate; - dai_params->formats = (machine->fmt_via_kcontrol == 2) ? - (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; - err = snd_soc_dai_set_sysclk(rtd->codec_dai, RT5659_SCLK_S_MCLK, aud_mclk, SND_SOC_CLOCK_IN); if (err < 0) { @@ -312,13 +211,7 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, rtd = snd_soc_get_pcm_runtime(card, "rt565x-codec-sysclk-bclk1"); if (rtd) { unsigned int bclk_rate; - - dai_params = - (struct snd_soc_pcm_stream *)rtd->dai_link->params; - - dai_params->rate_min = srate; - dai_params->formats = (machine->fmt_via_kcontrol == 2) ? - (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; + dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params; switch (dai_params->formats) { case SNDRV_PCM_FMTBIT_S8: @@ -357,9 +250,6 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, rtd = snd_soc_get_pcm_runtime(card, "dspk-playback-r"); if (rtd) { - dai_params = - (struct snd_soc_pcm_stream *)rtd->dai_link->params; - if (!strcmp(rtd->codec_dai->name, "tas2552-amplifier")) { err = snd_soc_dai_set_sysclk(rtd->codec_dai, TAS2552_PDM_CLK_IVCLKIN, aud_mclk, @@ -373,9 +263,6 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, rtd = snd_soc_get_pcm_runtime(card, "dspk-playback-l"); if (rtd) { - dai_params = - (struct snd_soc_pcm_stream *)rtd->dai_link->params; - if (!strcmp(rtd->codec_dai->name, "tas2552-amplifier")) { err = snd_soc_dai_set_sysclk(rtd->codec_dai, TAS2552_PDM_CLK_IVCLKIN, aud_mclk, @@ -387,16 +274,6 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, } } - rtd = snd_soc_get_pcm_runtime(card, "fe-pi-audio-z-v2"); - if (rtd) { - dai_params = - (struct snd_soc_pcm_stream *)rtd->dai_link->params; - - dai_params->rate_min = srate; - dai_params->channels_min = channels; - dai_params->formats = formats; - } - return 0; } @@ -449,7 +326,6 @@ static int tegra_machine_suspend_pre(struct snd_soc_card *card) return 0; } -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) static int tegra_machine_compr_startup(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -501,7 +377,6 @@ static int tegra_machine_compr_set_params(struct snd_compr_stream *cstream) return 0; } -#endif static int tegra_machine_fepi_init(struct snd_soc_pcm_runtime *rtd) { @@ -588,66 +463,19 @@ static struct snd_soc_ops tegra_machine_pcm_ops = { .shutdown = tegra_machine_pcm_shutdown, }; -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) static struct snd_soc_compr_ops tegra_machine_compr_ops = { .set_params = tegra_machine_compr_set_params, .startup = tegra_machine_compr_startup, .shutdown = tegra_machine_compr_shutdown, }; -#endif -static void set_dai_ops(struct tegra_machine *machine) +static int add_dai_links(struct snd_soc_card *card) { - int i; - - /* set ADMAIF dai_ops */ - for (i = machine->soc_data->admaif_dai_link_start; - i <= machine->soc_data->admaif_dai_link_end; i++) - machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops; -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) - /* set ADSP PCM/COMPR */ - for (i = machine->soc_data->adsp_pcm_dai_link_start; - i <= machine->soc_data->adsp_pcm_dai_link_end; i++) - machine->asoc->dai_links[i].ops = &tegra_machine_pcm_ops; - /* set ADSP COMPR */ - for (i = machine->soc_data->adsp_compr_dai_link_start; - i <= machine->soc_data->adsp_compr_dai_link_end; i++) - machine->asoc->dai_links[i].compr_ops = - &tegra_machine_compr_ops; -#endif -#if IS_ENABLED(CONFIG_SND_SOC_TEGRA186_ASRC_ALT) - if (!(of_machine_is_compatible("nvidia,tegra210") || - of_machine_is_compatible("nvidia,tegra210b01"))) { - /* set ASRC params. The default is 2 channels */ - for (i = 0; i < 6; i++) { - int tx = TEGRA186_DAI_LINK_ASRC1_TX1 + i; - int rx = TEGRA186_DAI_LINK_ASRC1_RX1 + i; - - machine->asoc->dai_links[tx].params = - &tegra_machine_asrc_link_params[i]; - machine->asoc->dai_links[rx].params = - &tegra_machine_asrc_link_params[i]; - } - } -#endif -} - -static int add_dai_links(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); struct tegra_machine *machine = snd_soc_card_get_drvdata(card); int ret; - machine->asoc = devm_kzalloc(&pdev->dev, sizeof(*machine->asoc), - GFP_KERNEL); - if (!machine->asoc) - return -ENOMEM; - - ret = tegra_asoc_populate_dai_links(pdev); - if (ret < 0) - return ret; - - ret = tegra_asoc_populate_codec_confs(pdev); + ret = parse_card_info(card, &tegra_machine_pcm_ops, + &tegra_machine_compr_ops); if (ret < 0) return ret; @@ -655,8 +483,6 @@ static int add_dai_links(struct platform_device *pdev) if (ret < 0) return ret; - set_dai_ops(machine); - return 0; } @@ -679,79 +505,40 @@ static struct snd_soc_card snd_soc_tegra_card = { /* structure to match device tree node */ static const struct of_device_id tegra_machine_of_match[] = { - { .compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x", - .data = &soc_data_tegra186 }, - { .compatible = "nvidia,tegra-audio-t210ref-mobile-rt565x", - .data = &soc_data_tegra210 }, - { .compatible = "nvidia,tegra-audio-mystique", - .data = &soc_data_tegra186 }, + { .compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x" }, + { .compatible = "nvidia,tegra-audio-t210ref-mobile-rt565x" }, {}, }; static int tegra_machine_driver_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &snd_soc_tegra_card; struct tegra_machine *machine; int ret = 0; - const struct of_device_id *match; - char *prop; - - card->dev = &pdev->dev; - /* parse card name first to log errors with proper device name */ - ret = snd_soc_of_parse_card_name(card, "nvidia,model"); - if (ret) - return ret; - - match = of_match_device(tegra_machine_of_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Error: No device match found\n"); - return -ENODEV; - } - - if (!np) { - dev_err(&pdev->dev, "No DT node for tegra machine driver"); - return -ENODEV; - } machine = devm_kzalloc(&pdev->dev, sizeof(*machine), GFP_KERNEL); if (!machine) return -ENOMEM; - machine->soc_data = (struct tegra_machine_soc_data *)match->data; - if (!machine->soc_data) - return -EINVAL; + machine->asoc = devm_kzalloc(&pdev->dev, sizeof(*machine->asoc), + GFP_KERNEL); + if (!machine->asoc) + return -ENOMEM; + card->dev = &pdev->dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); - if (machine->soc_data->write_idle_bias_off_state) - card->dapm.idle_bias_off = true; - - /* - * Below property of routing map is required only when there are DAPM input/output - * widgets available for external codec, which require them to be connected to machine - * source/sink DAPM widgets. - */ - prop = "nvidia,audio-routing"; - if (of_property_read_bool(np, prop)) { - ret = snd_soc_of_parse_audio_routing(card, prop); - if (ret < 0) - return ret; - } + card->dapm.idle_bias_off = true; memset(&machine->audio_clock, 0, sizeof(machine->audio_clock)); - if (of_property_read_u32(np, "mclk-fs", - &machine->audio_clock.mclk_scale) < 0) - dev_dbg(&pdev->dev, "Missing property mclk-fs\n"); - - ret = add_dai_links(pdev); - if (ret < 0) - goto cleanup_asoc; - ret = tegra_alt_asoc_utils_init(&machine->audio_clock, &pdev->dev, card); + if (ret < 0) + return ret; + + ret = add_dai_links(card); if (ret < 0) goto cleanup_asoc; @@ -762,9 +549,7 @@ static int tegra_machine_driver_probe(struct platform_device *pdev) goto cleanup_asoc; } - tegra_machine_add_i2s_codec_controls(card, - machine->soc_data->num_ahub_links + - machine->num_codec_links); + tegra_machine_add_i2s_codec_controls(card); return 0; cleanup_asoc: diff --git a/sound/soc/tegra-alt/utils/tegra_asoc_dt_parser.c b/sound/soc/tegra-alt/utils/tegra_asoc_dt_parser.c new file mode 100644 index 00000000..d64f2aff --- /dev/null +++ b/sound/soc/tegra-alt/utils/tegra_asoc_dt_parser.c @@ -0,0 +1,455 @@ +/* + * tegra_asoc_dt_parser.c - Tegra DAI links parser + * + * Copyright (c) 2019 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "tegra_asoc_machine_alt.h" + +#define PREFIX "nvidia-audio-card," +#define CELL "#sound-dai-cells" +#define DAI "sound-dai" + +/* DT also uses similar values to specify link type */ +enum dai_link_type { + PCM_LINK, + COMPR_LINK, + C2C_LINK, +}; + +struct snd_soc_pcm_stream link_params = { + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 16, +}; + +/* find if DAI link or its cpu/codec DAI nodes are disabled */ +static bool of_dai_link_is_available(struct device_node *link_node) +{ + struct device_node *child, *dai_node; + + if (!of_device_is_available(link_node)) + return false; + + for_each_child_of_node(link_node, child) { + /* check for "cpu" and "codec" nodes only */ + if (of_node_cmp(child->name, "cpu") && + of_node_cmp(child->name, "codec")) + continue; + + /* + * Skip a codec subnode if DAI property is missing. For a + * link with multiple codecs, at least one codec needs to + * have DAI property (which is ensured while counting the + * number of links that DT exposes). Other codec subnodes + * can be empty and populated in override file. + */ + if (!of_property_read_bool(child, DAI) && + !of_node_cmp(child->name, "codec")) + continue; + + dai_node = of_parse_phandle(child, DAI, 0); + if (!dai_node) { + of_node_put(child); + return false; + } + + if (!of_device_is_available(dai_node)) { + of_node_put(dai_node); + of_node_put(child); + return false; + } + + of_node_put(dai_node); + } + + return true; +} + +/* find number of child nodes with given name and containing DAI property */ +static int of_get_child_count_with_name(struct device_node *node, + const char *name) +{ + struct device_node *child; + int num = 0; + + for_each_child_of_node(node, child) + if (!of_node_cmp(child->name, name) && + of_property_read_bool(child, DAI)) + num++; + + return num; +} + +static int get_num_dai_links(struct platform_device *pdev, + unsigned int *num_links) +{ + struct device_node *top = pdev->dev.of_node; + struct device_node *link_node, *codec; + unsigned int link_count = 0, num_codecs; + + link_node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!link_node) { + dev_err(&pdev->dev, "no dai links found\n"); + return -ENOENT; + } + + do { + if (!of_dai_link_is_available(link_node)) { + link_node = of_get_next_child(top, link_node); + continue; + } + + /* + * depending on the number of codec subnodes, DAI link + * count is incremented. DT can have one DAI link entry + * with multiple codec nodes(for ex: DSPK), driver can + * create multiple links out of it. + */ + num_codecs = of_get_child_count_with_name(link_node, + "codec"); + if (!num_codecs) { + of_node_put(link_node); + dev_err(&pdev->dev, + "no codec subnode or sound-dai property\n"); + return -EINVAL; + } + + for_each_child_of_node(link_node, codec) { + if (of_node_cmp(codec->name, "codec")) + continue; + + if (of_property_read_bool(codec, DAI)) + link_count++; + } + + link_node = of_get_next_child(top, link_node); + } while (link_node); + + *num_links = link_count; + + return 0; +} + +static int get_num_codec_confs(struct platform_device *pdev, int *num_confs) +{ + struct device_node *top = pdev->dev.of_node; + struct device_node *link_node, *codec; + unsigned int conf_count = 0, num_codecs; + + link_node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!link_node) { + dev_err(&pdev->dev, "no dai links found\n"); + return -EINVAL; + } + + do { + if (!of_dai_link_is_available(link_node)) { + link_node = of_get_next_child(top, link_node); + continue; + } + + num_codecs = of_get_child_count_with_name(link_node, + "codec"); + if (!num_codecs) { + of_node_put(link_node); + dev_err(&pdev->dev, "missing codec subnode\n"); + return -EINVAL; + } + + for_each_child_of_node(link_node, codec) { + if (of_node_cmp(codec->name, "codec")) + continue; + + if (of_property_read_bool(codec, "prefix")) + conf_count++; + } + + link_node = of_get_next_child(top, link_node); + } while (link_node); + + *num_confs = conf_count; + + return 0; +} + +static void parse_mclk_fs(struct snd_soc_card *card) +{ + struct tegra_machine *machine = snd_soc_card_get_drvdata(card); + struct platform_device *pdev = to_platform_device(card->dev); + + if (of_property_read_u32(pdev->dev.of_node, PREFIX "mclk-fs", + &machine->audio_clock.mclk_scale)) + dev_dbg(&pdev->dev, "'%smclk-fs' property is missing\n", + PREFIX); +} + +static int parse_dt_codec_confs(struct snd_soc_card *card) +{ + struct platform_device *pdev = to_platform_device(card->dev); + struct tegra_machine *machine = snd_soc_card_get_drvdata(card); + struct device_node *top = pdev->dev.of_node; + struct device_node *link_node; + struct snd_soc_codec_conf *codec_confs; + unsigned int num_confs, i = 0; + int err; + + err = get_num_codec_confs(pdev, &machine->asoc->num_confs); + if (err < 0) + return err; + + num_confs = machine->asoc->num_confs; + if (!num_confs) + return 0; + + machine->asoc->codec_confs = devm_kcalloc(&pdev->dev, + num_confs, + sizeof(*codec_confs), + GFP_KERNEL); + if (!machine->asoc->codec_confs) + return -ENOMEM; + codec_confs = machine->asoc->codec_confs; + + link_node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!link_node) { + dev_err(&pdev->dev, "DAI links not found in DT\n"); + return -ENOENT; + } + + do { + struct of_phandle_args args; + struct device_node *codec; + + if (!of_dai_link_is_available(link_node)) { + link_node = of_get_next_child(top, link_node); + continue; + } + + for_each_child_of_node(link_node, codec) { + if (of_node_cmp(codec->name, "codec")) + continue; + + if (!of_property_read_bool(codec, "prefix")) + continue; + + err = of_parse_phandle_with_args(codec, DAI, CELL, 0, + &args); + if (err < 0) { + of_node_put(codec); + of_node_put(link_node); + return err; + } + + codec_confs[i].of_node = args.np; + codec_confs[i].dev_name = NULL; + of_property_read_string(codec, "prefix", + &codec_confs[i].name_prefix); + + i++; + } + + link_node = of_get_next_child(top, link_node); + } while (link_node); + + card->num_configs = num_confs; + card->codec_conf = codec_confs; + + return 0; +} + +static int parse_dt_dai_links(struct snd_soc_card *card, + struct snd_soc_ops *pcm_ops, + struct snd_soc_compr_ops *compr_ops) +{ + struct platform_device *pdev = to_platform_device(card->dev); + struct tegra_machine *machine = snd_soc_card_get_drvdata(card); + struct device_node *top = pdev->dev.of_node; + struct device_node *link_node; + struct snd_soc_dai_link *dai_links; + unsigned int num_links, link_count = 0; + int ret; + + ret = get_num_dai_links(pdev, &machine->asoc->num_links); + if (ret < 0) + return ret; + + num_links = machine->asoc->num_links; + if (!num_links) + return -EINVAL; + + dai_links = devm_kcalloc(&pdev->dev, num_links, sizeof(*dai_links), + GFP_KERNEL); + if (!dai_links) + return -ENOMEM; + machine->asoc->dai_links = dai_links; + + link_node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!link_node) { + dev_err(&pdev->dev, "DAI links not found in DT\n"); + return -ENOENT; + } + + do { + struct device_node *codec = NULL, *cpu = NULL; + struct snd_soc_dai_link *dai_link; + int link_type = 0, codec_count = 0; + + if (!of_dai_link_is_available(link_node)) { + link_node = of_get_next_child(top, link_node); + continue; + } + + dev_dbg(&pdev->dev, "parsing (%pOF)\n", link_node); + + cpu = of_get_child_by_name(link_node, "cpu"); + if (!cpu) { + dev_err(&pdev->dev, "cpu subnode is missing"); + ret = -ENOENT; + goto cleanup; + } + + for_each_child_of_node(link_node, codec) { + /* loop over codecs only */ + if (of_node_cmp(codec->name, "codec")) + continue; + + if (!of_property_read_bool(codec, DAI)) { + dev_dbg(&pdev->dev, + "sound-dai prop missing for (%pOF)\n", + codec); + codec_count++; + continue; + } + + dai_link = &dai_links[link_count]; + + /* parse CPU DAI */ + ret = asoc_simple_card_parse_cpu(cpu, dai_link, DAI, + CELL, NULL); + if (ret < 0) + goto cleanup; + + /* parse CODEC DAI */ + ret = asoc_simple_card_parse_codec(codec, dai_link, + DAI, CELL); + if (ret < 0) + goto cleanup; + + /* set DAI link name */ + if (of_property_read_string_index(link_node, + "link-name", + codec_count, + &dai_link->name)) { + ret = asoc_simple_card_set_dailink_name( + &pdev->dev, dai_link, "%s-%d", + "tegra-dlink", link_count); + if (ret < 0) + goto cleanup; + } + + asoc_simple_card_parse_daifmt(&pdev->dev, link_node, + codec, NULL, + &dai_link->dai_fmt); + + of_property_read_u32(link_node, "link-type", + &link_type); + switch (link_type) { + case PCM_LINK: + dai_link->ops = pcm_ops; + asoc_simple_card_canonicalize_dailink(dai_link); + dai_link->ignore_pmdown_time = 1; + break; + case COMPR_LINK: + dai_link->compr_ops = compr_ops; + asoc_simple_card_canonicalize_dailink(dai_link); + dai_link->ignore_pmdown_time = 1; + break; + case C2C_LINK: + dai_link->params = &link_params; + break; + default: + dev_err(&pdev->dev, "DAI link type invalid\n"); + ret = -EINVAL; + goto cleanup; + } + + link_count++; + codec_count++; + } +cleanup: + of_node_put(cpu); + if (ret < 0) { + of_node_put(codec); + of_node_put(link_node); + return ret; + } + + link_node = of_get_next_child(top, link_node); + } while (link_node); + + card->num_links = num_links; + card->dai_link = dai_links; + + return 0; +} + +int parse_card_info(struct snd_soc_card *card, struct snd_soc_ops *pcm_ops, + struct snd_soc_compr_ops *compr_ops) +{ + struct device_node *node = card->dev->of_node; + int ret; + + ret = asoc_simple_card_parse_card_name(card, PREFIX); + if (ret < 0) + return ret; + + /* + * Below property of routing map is required only when there + * are DAPM input/output widgets available for external codec, + * which require them to be connected to machine source/sink + * DAPM widgets. + */ + if (of_property_read_bool(node, PREFIX "routing")) { + ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing"); + if (ret < 0) + return ret; + } + + parse_mclk_fs(card); + + ret = parse_dt_dai_links(card, pcm_ops, compr_ops); + if (ret < 0) + return ret; + + ret = parse_dt_codec_confs(card); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(parse_card_info); + +MODULE_DESCRIPTION("Tegra ASoC Machine driver DT parser code"); +MODULE_AUTHOR("Sameer Pujar "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/tegra-alt/utils/tegra_asoc_machine_alt.c b/sound/soc/tegra-alt/utils/tegra_asoc_machine_alt.c index 8a0229bd..142fc8aa 100644 --- a/sound/soc/tegra-alt/utils/tegra_asoc_machine_alt.c +++ b/sound/soc/tegra-alt/utils/tegra_asoc_machine_alt.c @@ -3792,8 +3792,7 @@ static int tegra_machine_add_master_mode_ctl(struct snd_soc_card *card, return tegra_machine_add_ctl(card, &knew, data, name); } -int tegra_machine_add_i2s_codec_controls(struct snd_soc_card *card, - unsigned int num_dai_links) +int tegra_machine_add_i2s_codec_controls(struct snd_soc_card *card) { struct tegra_machine_control_data *data; struct snd_soc_pcm_runtime *rtd; @@ -3919,279 +3918,6 @@ void release_asoc_phandles(struct tegra_machine *machine) } EXPORT_SYMBOL_GPL(release_asoc_phandles); -int tegra_asoc_populate_dai_links(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node, *subnp; - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct tegra_machine *machine = snd_soc_card_get_drvdata(card); - struct snd_soc_dai_link *dai_links, *ahub_links; - struct snd_soc_pcm_stream *params; - char dai_link_name[MAX_STR_SIZE], *str; - unsigned int num_codec_links, num_ahub_links, num_links, - link_count = 0, i, j, *rx_slot, *tx_slot; - int ret; - - num_ahub_links = machine->soc_data->num_ahub_links; - ahub_links = machine->soc_data->ahub_links; - - if (!np || !num_ahub_links || !ahub_links) - return -EINVAL; - - /* read number of codec links exposed via DT */ - ret = of_property_read_u32(np, "nvidia,num-codec-link", - &machine->num_codec_links); - if (ret < 0) { - dev_err(&pdev->dev, - "Property 'nvidia,num-codec-link' missing\n"); - return ret; - } - num_codec_links = machine->num_codec_links; - - /* - * each codec link specified in device tree will result into one DAP - * and one CIF link. For example, i2s dai link will look like below. - * ahub <----CIF----> i2s <----DAP----> codec - */ - num_links = num_ahub_links + (num_codec_links << 1); - - machine->asoc->num_links = num_links; - machine->asoc->dai_links = devm_kzalloc(&pdev->dev, - sizeof(*dai_links) * num_links, - GFP_KERNEL); - dai_links = machine->asoc->dai_links; - if (!dai_links) - return -ENOMEM; - - machine->asoc->rx_slot = devm_kzalloc(&pdev->dev, - sizeof(*rx_slot) * num_links, - GFP_KERNEL); - rx_slot = machine->asoc->rx_slot; - if (!rx_slot) - return -ENOMEM; - - machine->asoc->tx_slot = devm_kzalloc(&pdev->dev, - sizeof(*tx_slot) * num_links, - GFP_KERNEL); - tx_slot = machine->asoc->tx_slot; - if (!tx_slot) - return -ENOMEM; - - /* populate now ahub links */ - memcpy(dai_links, ahub_links, num_ahub_links * sizeof(*dai_links)); - - /* populate now CIF and DAP links from device tree */ - for (i = num_ahub_links, j = num_ahub_links + num_codec_links; - i < num_ahub_links + num_codec_links; i++, j++) { - memset((void *)dai_link_name, '\0', MAX_STR_SIZE); - sprintf(dai_link_name, "nvidia,dai-link-%d", ++link_count); - subnp = of_get_child_by_name(np, dai_link_name); - if (!subnp) - return -ENOENT; - - /* DAP DAI link configuration */ - dai_links[i].stream_name = "Playback"; - dai_links[i].codec_of_node = of_parse_phandle(subnp, - "codec-dai", 0); - if (!dai_links[i].codec_of_node) { - dev_err(&pdev->dev, - "property 'codec-dai' is missing\n"); - ret = -ENOENT; - break; - } - - dai_links[i].cpu_of_node = of_parse_phandle(subnp, "cpu-dai", - 0); - if (!dai_links[i].cpu_of_node) { - dev_err(&pdev->dev, "property 'cpu-dai' is missing\n"); - ret = -ENOENT; - break; - } - - of_property_read_string(subnp, "link-name", &dai_links[i].name); - - /* - * special case for DSPK - * Two mono codecs can be connected to the controller - * DAP2 is required for DAPM path completion - * TODO revisit this when ASoC has multi-codec support - */ - if (!strcmp(dai_links[i].name, "dspk-playback-r")) - dai_links[i].cpu_dai_name = "DAP2"; - else - dai_links[i].cpu_dai_name = "DAP"; - dai_links[i].dai_fmt = snd_soc_of_parse_daifmt(subnp, NULL, - NULL, NULL); - - params = devm_kzalloc(&pdev->dev, sizeof(*params), GFP_KERNEL); - if (!params) { - ret = -ENOMEM; - break; - } - - ret = of_property_read_string(subnp, "bit-format", - (const char **)&str); - if (ret < 0) { - dev_err(&pdev->dev, "Property 'bit-format' missing\n"); - break; - } - - ret = tegra_machine_get_format(¶ms->formats, str); - if (ret < 0) { - dev_err(&pdev->dev, "Wrong codec format\n"); - break; - } - - ret = of_property_read_u32(subnp, "srate", ¶ms->rate_min); - if (ret < 0) { - dev_err(&pdev->dev, "Property 'srate' missing\n"); - break; - } - params->rate_max = params->rate_min; - - ret = of_property_read_u32(subnp, "num-channel", - ¶ms->channels_min); - if (ret < 0) { - dev_err(&pdev->dev, "Property 'num-channel' missing\n"); - break; - } - params->channels_max = params->channels_min; - - dai_links[i].params = params; - ret = of_property_read_string(subnp, "codec-dai-name", - &dai_links[i].codec_dai_name); - if (ret < 0) { - dev_err(&pdev->dev, - "property 'codec-dai-name' is missing\n"); - break; - } - - of_property_read_u32(subnp, "rx-mask", (u32 *)&rx_slot[i]); - of_property_read_u32(subnp, "tx-mask", (u32 *)&tx_slot[i]); - - /* - * CIF DAI link configuration - * CIF link is towards XBAR, hence xbar node is cpu_of_node - * and codec_of_node is same as DAP's cpu_of_node. - */ - dai_links[j].codec_of_node = of_parse_phandle(subnp, "cpu-dai", - 0); - dai_links[j].cpu_of_node = of_parse_phandle(np, "nvidia,xbar", - 0); - if (!dai_links[j].cpu_of_node) { - dev_err(&pdev->dev, - "property 'nvidia,xbar' is missing\n"); - ret = -ENOENT; - break; - } - - /* - * special case for DSPK - * Two mono codecs can be connected to the controller - * CIF2 is required for DAPM path completion - * TODO revist this when ASoC has multi-codec support - */ - if (!strcmp(dai_links[i].name, "dspk-playback-r")) - dai_links[j].codec_dai_name = "CIF2"; - else - dai_links[j].codec_dai_name = "CIF"; - - ret = of_property_read_string(subnp, "cpu-dai-name", - &dai_links[j].cpu_dai_name); - if (ret < 0) { - dev_err(&pdev->dev, - "property 'cpu-dai-name' is missing\n"); - break; - } - - str = devm_kzalloc(&pdev->dev, - sizeof(dai_links[j].cpu_dai_name) + - 1 + sizeof(dai_links[j].codec_dai_name), - GFP_KERNEL); - str = strcat(str, dai_links[j].cpu_dai_name); - str = strcat(str, " "); - str = strcat(str, dai_links[j].codec_dai_name); - - dai_links[j].name = dai_links[j].stream_name = str; - dai_links[j].params = dai_links[i].params; - - of_node_put(subnp); - } - - /* - * release subnp here. DAI links and codec conf release will be - * taken care during error exit of machine driver probe() - */ - if (ret < 0) { - of_node_put(subnp); - return ret; - } - - card->num_links = num_links; - card->dai_link = dai_links; - - return 0; -} -EXPORT_SYMBOL_GPL(tegra_asoc_populate_dai_links); - -int tegra_asoc_populate_codec_confs(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct tegra_machine *machine = snd_soc_card_get_drvdata(card); - unsigned int num_codec_confs, num_ahub_confs, num_confs, i; - struct snd_soc_codec_conf *codec_confs, *ahub_confs; - char dai_link_name[MAX_STR_SIZE]; - struct device_node *of_node; - struct device_node *np = pdev->dev.of_node, *subnp; - - ahub_confs = machine->soc_data->ahub_confs; - num_ahub_confs = machine->soc_data->num_ahub_confs; - num_codec_confs = machine->num_codec_links; - if (!ahub_confs || !num_codec_confs || !num_ahub_confs) - return -EINVAL; - num_confs = num_codec_confs + num_ahub_confs; - machine->asoc->num_confs = num_confs; - - machine->asoc->codec_confs = - devm_kzalloc(&pdev->dev, sizeof(*codec_confs) * num_confs, - GFP_KERNEL); - codec_confs = machine->asoc->codec_confs; - if (!codec_confs) - return -ENOMEM; - - /* add codec confs from ahub */ - memcpy(codec_confs, ahub_confs, num_ahub_confs * sizeof(*codec_confs)); - - /* append codec confs from device tree */ - for (i = 0; i < num_codec_confs; i++) { - memset((void *)dai_link_name, '\0', MAX_STR_SIZE); - sprintf(dai_link_name, "nvidia,dai-link-%d", i+1); - subnp = of_get_child_by_name(np, dai_link_name); - if (!subnp) - return -ENOENT; - - of_node = of_parse_phandle(subnp, "codec-dai", 0); - if (!of_node) { - dev_err(&pdev->dev, - "property 'codec-dai' is missing\n"); - of_node_put(subnp); - return -ENOENT; - } - - codec_confs[i + num_ahub_confs].dev_name = NULL; - codec_confs[i + num_ahub_confs].of_node = of_node; - of_property_read_string(subnp, "name-prefix", - &codec_confs[i + num_ahub_confs].name_prefix); - - of_node_put(subnp); - } - - card->num_configs = num_confs; - card->codec_conf = codec_confs; - - return 0; -} -EXPORT_SYMBOL_GPL(tegra_asoc_populate_codec_confs); - MODULE_AUTHOR("Arun Shamanna Lakshmi "); MODULE_AUTHOR("Junghyun Kim "); MODULE_LICENSE("GPL");