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");