ASoC: tegra-alt: move to DT based DAI links

Currently we are using static DAI links in machine-utils code and there
are some drawbacks associated with it.
 * All DAI links are enabled, even though if a platform does not require.
   For ex: ALSA ring buffers are allocated for all the ADMAIF PCM links.
   Some platforms might want to enable a very few links, which is not
   currently possible with static array in the driver code.
 * No way to disable specific modules from DT. For example if any of the
   module instances are disabled, sound card registration fails.
 * CPU and CODEC names are hard coded in the DAI link arrays. With this
   we need to pass instance-id properties from DT to identify or match
   the correct instance names.
 * One of the other concerns is about latency, where some platform want
   to optimize this by only enabling required links.

Above issues can be addressed with DT based DAI links and going ahead
plan is to use upstream style of DT binding. This patch includes following
changes.
 * Add new helper functions to parse all DAI links from DT. All code
   related to this is added in a new file.
 * Remove older helper functions for parsing. Please note that, not all
   functions can be removed since few machine drivers rely on them.
 * Common params structure is used for all codec-to-codec links. In machine
   driver hw_params() callback all DAI link params are updated. Hence it
   is not necessary to maintain separate structures.
 * By default card->dapm.idle_bias_off is set to true. This means, codec
   suspend() won't be called during STANDBY. The flag is set to true for
   Tegra210 as well, where earlier it was set to false. This further helps
   to remove soc_data structure.
 * Module specific checks are not needed to manage DAI links now.

Bug 200538260

Change-Id: I210b70c23a8878f5b1733dfa9d2010834ae4a85a
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2229358
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Sameer Pujar
2019-09-20 12:34:35 +05:30
parent e8f6236ef5
commit 1f403897f1
5 changed files with 509 additions and 561 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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:

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <sound/simple_card_utils.h>
#include <sound/jack.h>
#include <sound/soc.h>
#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 <spujar@nvidia.com>");
MODULE_LICENSE("GPL v2");

View File

@@ -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(&params->formats, str);
if (ret < 0) {
dev_err(&pdev->dev, "Wrong codec format\n");
break;
}
ret = of_property_read_u32(subnp, "srate", &params->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",
&params->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 <aruns@nvidia.com>");
MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>");
MODULE_LICENSE("GPL");