Here’s a complete breakdown of the changes I made to get things working with the TX2-4GB. We ended up switching from the TLV320AIC3120 to the TLV320AIC3100. What I have here should work for both codecs. Hopefully this helps some other newbie to audio codecs figure things out.
In the end, one thing that became particularly helpful for me was
sudo find /sys/kernel/debug/asoc/ -name 'x Playback'
sudo find /sys/kernel/debug/asoc/ -name 'x Playback' -exec cat {} \;
This pointed me to the folder
/sys/kernel/debug/asoc/tegra-snd-t186ref-mobile-rt565x/codec:tlv320aic31xx-codec.1-0018/dapm/
Inspecting the files in this folder will show how the audio is routed. If things don’t work, then start mapping the audio route from the codec input/output back to I2S (x Playback).
My complete devicetree changes are:
File: kernel_src/hardware/nvidia/platform/t18x/common/kernel-dts/t18x-common-platforms/tegra186-qill-common.dtsi
i2c@c240000 {
...
+ tlv320aic31xx: tlv320aic31xx@18 {
+ compatible = "ti,tlv320aic3100";
+ reg = <0x18>;
+
+ gpio-reset = <&tegra_main_gpio TEGRA_MAIN_GPIO(J, 6) GPIO_ACTIVE_LOW>; //GPIO3_PJ.06
+ };
};
...
tegra_sound: sound {
compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x";
nvidia,model = "tegra-snd-t186ref-mobile-rt565x";
nvidia,num-codec-link = <12>;
clocks = <&tegra_car TEGRA186_CLK_PLLA>,
<&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA186_CLK_AHUB>,
<&tegra_car TEGRA186_CLK_AUD_MCLK>;
clock-names = "pll_a", "pll_a_out0", "ahub", "extern1";
assigned-clocks = <&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA186_CLK_AHUB>,
<&tegra_car TEGRA186_CLK_AUD_MCLK>;
assigned-clock-parents = <&tegra_car TEGRA186_CLK_PLLA>,
<&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
- <&tegra_car TEGRA186_CLK_PLL_A_OUT0>;
+ <&tegra_car TEGRA186_CLK_PLLP_OUT0>;
+ assigned-clock-rates = <0>, <0>, <12000000>;
resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>;
reset-names = "extern1_rst";
status = "okay";
nvidia,audio-routing =
- "x Headphone", "x OUT",
- "x IN", "x Mic",
+ "x Headphone", "x HPL",
+ "x Headphone", "x HPR",
+ "x Int Spk", "x SPK",
+ "x MIC1LP", "x Mic",
+ "x MIC1RP", "x Mic",
+ "x MIC1LM", "x Mic",
"y Headphone", "y OUT",
"y IN", "y Mic",
"z Headphone", "z OUT",
"z IN", "z Mic",
"m Headphone", "m OUT",
"m IN", "m Mic",
"n Headphone", "n OUT",
"n IN", "n Mic",
"o Headphone", "o OUT",
"o IN", "o Mic",
"a IN", "a Mic",
"b IN", "b Mic",
"c IN", "c Mic",
"d IN", "d Mic",
"d1 Headphone", "d1 OUT",
"d3 Headphone", "d3 OUT";
nvidia,xbar = <&tegra_axbar>;
- mclk-fs = <256>;
rt565x_dai_link: nvidia,dai-link-1 {
- link-name = "spdif-dit-0";
+ link-name = "tlv320aic31xx-codec";
cpu-dai = <&tegra_i2s1>;
- codec-dai = <&spdif_dit0>;
+ codec-dai = <&tlv320aic31xx>;
cpu-dai-name = "I2S1";
- codec-dai-name = "dit-hifi";
+ codec-dai-name = "tlv320aic31xx-hifi";
format = "i2s";
bit-format = "s16_le";
srate = <48000>;
num-channel = <2>;
ignore_suspend;
name-prefix = "x";
status = "okay";
};
...
};
Codec driver changes for slave mode [0]:
File: kernel_src/kernel/kernel-4.9/sound/soc/codecs/tlv320aic31xx.c
static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 iface_reg1 = 0;
u8 iface_reg2 = 0;
u8 dsp_a_val = 0;
dev_dbg(codec->dev, "## %s: fmt = 0x%x\n", __func__, fmt);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER;
break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ iface_reg1 |= AIC31XX_WCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ iface_reg1 |= AIC31XX_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
default:
dev_alert(codec->dev, "Invalid DAI master/slave interface\n");
return -EINVAL;
}
Codec driver changes to correct audio routing so I2S was connected to the codec [1] (‘x Playback’ DAPM widget):
File: kernel_src/kernel/kernel-4.9/sound/soc/codecs/tlv320aic31xx.c
static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",
SND_SOC_NOPM, 0, 0, &ldac_in_control),
SND_SOC_DAPM_MUX("DAC Right Input",
SND_SOC_NOPM, 0, 0, &rdac_in_control),
/* DACs */
- SND_SOC_DAPM_DAC_E("DAC Left", "Left Playback",
+ SND_SOC_DAPM_DAC_E("DAC Left", "Playback"
AIC31XX_DACSETUP, 7, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_DAC_E("DAC Right", "Right Playback",
+ SND_SOC_DAPM_DAC_E("DAC Right", "Playback",
AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
....
Codec driver changes fro dummy regulator:
File: kernel_src/kernel/kernel-4.9/sound/soc/codecs/tlv320aic31xx.c
-#define AIC31XX_NUM_SUPPLIES 6
+#define AIC31XX_NUM_SUPPLIES 1
static const char * const aic31xx_supply_names[AIC31XX_NUM_SUPPLIES] = {
- "HPVDD",
- "SPRVDD",
- "SPLVDD",
- "AVDD",
- "IOVDD",
- "DVDD",
+ "dummy",
};
Machine driver changes:
File: kernel_src/kernel/nvidia/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c
+#include "tlv320aic31xx.h"
....
static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime,
unsigned int rate, unsigned int channels,
u64 formats)
{
....
+ rtd = snd_soc_get_pcm_runtime(card, "tlv320aic31xx-codec");
+ 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;
+ }
+
rtd = snd_soc_get_pcm_runtime(card, "rt565x-playback");
....
}
....
+static int tegra_machine_tlv320aic31xx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *dev = rtd->card->dev;
+ int err;
+ err = snd_soc_dai_set_sysclk(rtd->codec_dai, AIC31XX_PLL_CLKIN_MCLK, 12000000,
+ SND_SOC_CLOCK_IN);
+ if (err) {
+ dev_err(dev, "failed to set tlv320aic31xx sysclk!\n");
+ return err;
+ }
+
+ return 0;
+}
+
+
static int codec_init(struct tegra_machine *machine)
{
struct snd_soc_dai_link *dai_links = machine->asoc->dai_links;
unsigned int num_links = machine->asoc->num_links, i;
if (!dai_links || !num_links)
return -EINVAL;
for (i = 0; i < num_links; i++) {
if (!dai_links[i].name)
continue;
if (strstr(dai_links[i].name, "rt565x-playback") ||
strstr(dai_links[i].name, "rt565x-codec-sysclk-bclk1"))
dai_links[i].init = tegra_machine_rt565x_init;
else if (strstr(dai_links[i].name, "fe-pi-audio-z-v2"))
dai_links[i].init = tegra_machine_fepi_init;
+ else if (strstr(dai_links[i].name, "tlv320aic31xx-codec"))
+ dai_links[i].init = tegra_machine_tlv320aic31xx_init;
}
return 0;
}
Kernel config changes:
CONFIG_SND_SOC_TLV320AIC31XX=y
CONFIG_REGULATOR_DUMMY=y
Pinmux settings:
Amixer settings (Note: I configured the headphones in Mono output):
amixer -c tegrasndt186ref cset name='x ADC Capture Switch' on
amixer -c tegrasndt186ref cset name='x ADC Capture Volume' 64
amixer -c tegrasndt186ref cset name='x ADC Fine Capture Volume' 4
amixer -c tegrasndt186ref cset name='x DAC Left Input' 3
amixer -c tegrasndt186ref cset name='x DAC Playback Volume' 127
amixer -c tegrasndt186ref cset name='x DAC Right Input' 3
amixer -c tegrasndt186ref cset name='x HP Analog Playback Volume' 127,127
amixer -c tegrasndt186ref cset name='x HP Driver Playback Switch' on,on
amixer -c tegrasndt186ref cset name='x HP Driver Playback Volume' 7,7
amixer -c tegrasndt186ref cset name='x HP Left Switch' on
amixer -c tegrasndt186ref cset name='x HP Right Switch' on
amixer -c tegrasndt186ref cset name='x MIC1LM M-Terminal' 1
amixer -c tegrasndt186ref cset name='x MIC1LM P-Terminal' 1
amixer -c tegrasndt186ref cset name='x MIC1LP P-Terminal' 1
amixer -c tegrasndt186ref cset name='x MIC1RP P-Terminal' 1
amixer -c tegrasndt186ref cset name='x Mic PGA Capture Volume' 80
amixer -c tegrasndt186ref cset name='x Output Left From Left DAC' on
amixer -c tegrasndt186ref cset name='x Output Left From MIC1LP' on
amixer -c tegrasndt186ref cset name='x Output Left From MIC1RP' on
amixer -c tegrasndt186ref cset name='x Output Right From MIC1RP' on
amixer -c tegrasndt186ref cset name='x Output Right From Right DAC' on
amixer -c tegrasndt186ref cset name='x Speaker Analog Playback Volume' 127
amixer -c tegrasndt186ref cset name='x Speaker Driver Playback Switch' on
amixer -c tegrasndt186ref cset name='x Speaker Driver Playback Volume' 3
amixer -c tegrasndt186ref cset name='x Speaker Switch' on
[0] ASoC: tlv320aic31xx: Add CODEC clock slave support · torvalds/linux@77f8b3c · GitHub
[1] Sound card transplantation - #27 by jonathanh