Trouble with AUD_MCLK

We have an ADAU1361 audio codec connected to I2S2 on the Xavier 40 pin expansion header and I am having trouble getting AUD_MCLK to start up on playback. Our codec works fine on the TX2 using a fixed crystal oscillator, but we decided to try and use AUD_MCLK when we ported our hardware to the Xavier.

I followed the “AUD_MCLK for Codec SYSCLK” section of the developer guide and added the following lines to my dtsi to configure AUD_MCLK for 19.2 MHz:

i2c@31e0000 {
	adau1361_codec: adau1361@38 {
		compatible = "nova,adau1361";
		reg = <0x38>;
		clocks = <&bpmp_clks TEGRA194_CLK_AUD_MCLK>;
		clock-names = "mclk";
	};
};

i2s@2901000 {
	status = "okay";
};

sound {
	assigned-clocks = <&bpmp_clks TEGRA194_CLK_PLLA_OUT0>, <&bpmp_clks TEGRA194_CLK_AUD_MCLK>;
	assigned-clock-parents = <&bpmp_clks TEGRA194_CLK_PLLA>, <&bpmp_clks TEGRA194_CLK_CLK_M>;
	assigned-clock-rates = <0>, <19200000>;

	nvidia,audio-routing =
		"m Headphone", "m LOUT",
		"m Headphone", "m ROUT",
		"m Mic", "m MICBIAS",
		"m LINP", "m Mic",
		"m LINN", "m Mic",
		"m RINP", "m Mic",
		"m RINN", "m Mic";

	nvidia,dai-link-2 {
		link-name = "adau1361-codec";
		cpu-dai = <&tegra_i2s2>;
		codec-dai = <&adau1361_codec>;
		cpu-dai-name = "I2S2";
		codec-dai-name = "adau-hifi";
		format = "i2s";
		bitclock-slave;
		frame-slave;
		bitclock-noninversion;
		frame-noninversion;
		bit-format = "s32_le";
		bclk_ratio = <1>;
		srate = <48000>;
		num-channel = <2>;
		ignore_suspend;
		name-prefix = "m";
		status = "okay";
	};
};

Next I followed the “Update the Machine Driver to Support a Custom Audio Card” section of the guide to modify tegra_machine_driver_mobile.c

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 fbca4699d..a9bd7d584 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
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+ #define DEBUG 1
+
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of_platform.h>
@@ -431,6 +433,16 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime,
                dai_params->formats = formats;
        }
 
+       rtd = snd_soc_get_pcm_runtime(card, "adau1361-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;
+       }
+
        return 0;
 }

 @@ -594,6 +606,27 @@ static int tegra_machine_rt565x_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int tegra_machine_adau136x_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_pcm_stream *dai_params =
+               (struct snd_soc_pcm_stream *)rtd->dai_link->params;
+       int err = 0;
+
+       dev_info(card->dev, "%s: setting sysclk to %u\n",
+               __func__, dai_params->rate_min);
+
+    err = snd_soc_dai_set_sysclk(
+               rtd->codec_dai, 0, dai_params->rate_min, SND_SOC_CLOCK_IN);
+
+    if (err) {
+        dev_err(card->dev, "%s: failed to set dai sysclk!\n", __func__);
+        return err;
+    }
+ 
+    return 0;
+}
+
 static int codec_init(struct tegra_machine *machine)
 {
        struct snd_soc_dai_link *dai_links = machine->asoc->dai_links;
@@ -611,6 +644,8 @@ static int codec_init(struct tegra_machine *machine)
                        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, "adau1361-codec"))
+                       dai_links[i].init = tegra_machine_adau136x_init;
        }
 
        return 0;

I also confirmed that my codec was probing properly:

[    5.689483] adau1761 8-0038: adau1761_i2c_probe
[    5.689696] adau1761 8-0038: adau1761_i2c_probe success
[    5.915406] tegra-asoc: sound: tegra_machine_adau136x_init: setting sysclk to 48000

There is data on my I2S lines but nothing from aud_mclk. The only error I could find in dmesg was the following:

[    3.983514] tegra-asoc: sound: ASoC: CODEC DAI adau-hifi not registered
[    3.983697] tegra-asoc: sound: snd_soc_register_card failed (-517)

I also noticed that aud_mclk is only reporting 9600000 (exactly half of my assigned rate).

$ sudo cat /sys/kernel/debug/clk/aud_mclk/clk_rate 
9600000

How do I get AUD_MCLK to activate at 19.2 MHz when I initiate playback using my audio codec on I2S2?

Hello!

Can you make sure that the ‘mclk-fs’ property is not populated under the sound node? If this property is present, then the Tegra audio machine driver will attempt to scale the aud_mclk.

Also the following does not look correct …

Per your print …

It is telling the codec the SYSCLK is 48kHz and not 19.2MHz.

Regards,
Jon

You’re right, when I decompile the DTB there is a value for mclk-fs

mclk-fs = <0x100>;

I set it to zero explicitly:

@@ -523,6 +523,7 @@
                assigned-clocks = <&bpmp_clks TEGRA194_CLK_PLLA_OUT0>, <&bpmp_clks TEGRA194_CLK_AUD_MCLK>;
                assigned-clock-parents = <&bpmp_clks TEGRA194_CLK_PLLA>, <&bpmp_clks TEGRA194_CLK_CLK_M>;
                assigned-clock-rates = <0>, <19200000>;
+               mclk-fs = <0>;
 
                nvidia,audio-routing =
                        "m Headphone", "m LOUT",

My clock is now properly 19.2 MHz, so that’s one mystery solved

$sudo cat /sys/kernel/debug/clk/aud_mclk/clk_rate
19200000

I also fixed the init function to explicitly set 19.2 MHz. I had tried using dai_params->rate_min after finding some examples in the tegra_maui_alt driver.

static int tegra_machine_adau136x_init(struct snd_soc_pcm_runtime *rtd)
{
    int err = snd_soc_dai_set_sysclk(
		rtd->codec_dai, 0, 19200000, SND_SOC_CLOCK_IN);

    if (err) {
        dev_err(rtd->card->dev, "%s: failed to set dai sysclk!\n", __func__);
        return err;
    }
 
    return 0;
}

I thought it might be a pinmux issue so I tried it with pin7 muxed to both “unused” and “extperiph4_clk” using jetson-io.py. No signal with either one.

I see the problem, the Xavier developer kit does not have it routed to the 40 pin header, it goes to the HD audio header. I misunderstood the table.

Mystery solved for now, thanks!

Hello!

Yes that is a difference on Jetson Xavier. Glad to hear it is working.

Jon

For reference here are the working DTS components for an audio codec receiving mclk from pin 7 of the 40pin-header

Mux the pins to I2S and extperiph4:

#include <dt-bindings/pinctrl/pinctrl-tegra.h>

pinmux@2430000 {
	pinctrl-names = "default";
	pinctrl-0 = <&hdr40_pinmux>;

	hdr40_pinmux: header-40pin-pinmux {
		pin7 {
			nvidia,function = "extperiph4";
			nvidia,pins = "soc_gpio42_pq6";
			nvidia,pull = <TEGRA_PIN_PULL_NONE>;
			nvidia,tristate = <TEGRA_PIN_DISABLE>;
			nvidia,enable-input = <TEGRA_PIN_DISABLE>;
			nvidia,io-high-voltage = <TEGRA_PIN_DISABLE>;
			nvidia,lpdr = <TEGRA_PIN_DISABLE>;
		};

		pin12 {
			nvidia,function = "i2s2";
			nvidia,pins = "dap2_sclk_ph7";
			nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
			nvidia,tristate = <TEGRA_PIN_DISABLE>;
			nvidia,enable-input = <TEGRA_PIN_ENABLE>;
			nvidia,lpdr = <TEGRA_PIN_DISABLE>;
		};

		pin35 {
			nvidia,function = "i2s2";
			nvidia,pins = "dap2_fs_pi2";
			nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
			nvidia,tristate = <TEGRA_PIN_DISABLE>;
			nvidia,enable-input = <TEGRA_PIN_ENABLE>;
			nvidia,lpdr = <TEGRA_PIN_DISABLE>;
		};

		pin38 {
			nvidia,function = "i2s2";
			nvidia,pins = "dap2_din_pi1";
			nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
			nvidia,tristate = <TEGRA_PIN_ENABLE>;
			nvidia,enable-input = <TEGRA_PIN_ENABLE>;
			nvidia,lpdr = <TEGRA_PIN_DISABLE>;
		};

		pin40 {
			nvidia,function = "i2s2";
			nvidia,pins = "dap2_dout_pi0";
			nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
			nvidia,tristate = <TEGRA_PIN_DISABLE>;
			nvidia,enable-input = <TEGRA_PIN_DISABLE>;
			nvidia,lpdr = <TEGRA_PIN_DISABLE>;
		};
	};
};

Configure the codec and dai-link

/* Audio I2C */
i2c@31e0000 {
	adau1361_codec: adau1361@38 {
		compatible = "nova,adau1361";
		reg = <0x38>;
		clocks = <&bpmp_clks TEGRA194_CLK_EXTPERIPH4>;
		clock-names = "mclk";
	};
};

i2s@2901000 {
	status = "okay";
};

sound {
	assigned-clocks = <&bpmp_clks TEGRA194_CLK_PLLA_OUT0>, <&bpmp_clks TEGRA194_CLK_EXTPERIPH4>;
	assigned-clock-parents = <&bpmp_clks TEGRA194_CLK_PLLA>, <&bpmp_clks TEGRA194_CLK_PLLP_OUT0>;
	assigned-clock-rates = <0>, <24000000>;
	mclk-fs = <0>;

	nvidia,audio-routing =
		"m Headphone", "m LOUT",
		"m Headphone", "m ROUT",
		"m Mic", "m MICBIAS",
		"m LINP", "m Mic",
		"m LINN", "m Mic",
		"m RINP", "m Mic",
		"m RINN", "m Mic";

	nvidia,dai-link-2 {
		link-name = "adau1361-codec";
		cpu-dai = <&tegra_i2s2>;
		codec-dai = <&adau1361_codec>;
		cpu-dai-name = "I2S2";
		codec-dai-name = "adau-hifi";
		format = "i2s";
		bitclock-slave;
		frame-slave;
		bitclock-noninversion;
		frame-noninversion;
		bit-format = "s32_le";
		bclk_ratio = <1>;
		srate = <48000>;
		num-channel = <2>;
		ignore_suspend;
		name-prefix = "m";
		status = "okay";
	};
};
2 Likes