Merging device trees for multiple codecs

Hi Jonathan,

Previously, you helped me out getting both the SGTL5000 and TLV320ADC3150 codecs working on the TX2i. Now I want to merge their device trees and routing etc so that I can use them for audio output and input respectively on the same platform. Is this any more complicated than cutting and pasting the entries under the tegra sound node, or are there further considerations? In particular, I’m worried about the routing, which was quite difficult to get working. There is also the clocking, but only the SGTL5000 is using MCLK.

Best Regards,
David

Hi David,

From a DT perspective, tt should just be a matter of merging the relevant DT nodes/properties. So populating the DAI links for the SGTL5000 and TLV320ADC3150 and adding all the necessary routes.

With regard to clocking, this is probably the area that requires a bit of thought. Everytime audio capture is started the tegra_machine_dai_init() is called and this in turn calls tegra_alt_asoc_utils_set_rate() to init the clocks. What we want to avoid is that if one device, SGTL5000 or TLV320ADC3150, is active and then the other is started the clocks get reconfigured. From previous discussions I believe that both the SGTL5000 and TLV320ADC3150 as I2S slaves (ie. Tegra drivers the bit clock and fsync for both devices), correct?

So the first question is, are these ever both active at the same time and if so, could they be operating at different sample rates? I believe that So some possible scenarios …

  1. SGTL5000 and TLV320ADC3150 always operate at the same sample rate
    If they always operate at the same sample rate then the problem is simpler and we just need to check in tegra_machine_dai_init() if audio is active and the sample rate of the 2nd device matches the 1st, and if it is not then we should fail to start the 2nd device. In this case, there should be no harm calling tegra_alt_asoc_utils_set_rate() a 2nd time while audio is active because it will see the the clocks do not need to be reconfigured.

  2. SGTL5000 and TLV320ADC3150 may operate at different rate but have a common base rate
    By base rate I mean that the sample rate is either divisible by 8000 or 11025. The audio PLL in Tegra is configured differently depending on the base rate. So if you need one device to operate at 44.1kHz and the other to operate at 48kHz. However, if they always have a common base rate then the problem is simpler. For example, if the TLV320ADC3150 is operating at 16kHz and the SGTL5000 is operating at 48kHz then the audio PLL is configured the same in this case. However, given that the SGTL5000 is the only device using the AUD_MCLK we will need to modify the
    tegra_alt_asoc_utils_set_rate() to only configure the AUD_MCLK when this device is active.

If there is a case where you need to use different base rates, then it gets a bit more complex and we would need to think about that.

Regards,
Jon

Hi Jonathan,

Sorry to revisit this question, but we’ve made a custom TX2i carrier board with both an SGTL5000 codec and TLV320ADC5140 ADC. The first is connected to I2C1 and I2S1 and is used for mono output only. The second is connected to I2C2 and I2S2 and is used for four-channel recording.

The sound node for the SGTL5000 looks like this:

	tegra_sound: sound {
		compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x";
		nvidia,model = "tegra-snd-t186ref-mobile-rt565x";
		nvidia,num-codec-link = <1>;
        nvidia,num-clk = <8>;
        nvidia,clk-rates = < 270950400  /* PLLA_x11025_RATE */
                             11289600   /* AUD_MCLK_x11025_RATE */
                             45158400   /* PLLA_OUT0_x11025_RATE */
                             45158400   /* AHUB_x11025_RATE */
                             245760000  /* PLLA_x8000_RATE */
                             12288000   /* AUD_MCLK_x8000_RATE */
                             49152000   /* PLLA_OUT0_x8000_RATE */
                             49152000 >;/* AHUB_x8000_RATE */
        clocks = <&tegra_car TEGRA186_CLK_PLLP_OUT0>,
                <&tegra_car TEGRA186_CLK_PLLA>,
                <&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
                <&tegra_car TEGRA186_CLK_AHUB>,
                <&tegra_car TEGRA186_CLK_CLK_M>,
                <&tegra_car TEGRA186_CLK_AUD_MCLK>;
        clock-names = "pll_p_out1", "pll_a", "pll_a_out0", "ahub",
                        "clk_m", "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>;
		resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>;
		reset-names = "extern1_rst";

		status = "okay";
		nvidia,audio-routing =
		    "x MIC_IN",		"x Mic",
		    "x ADC",		        "x Mic Bias",
                "x Headphone",	"x HP_OUT",
		    "x Lineout",	        "x LINE_OUT",
		    "x LINE_IN",	        "x Linein";

		nvidia,xbar = <&tegra_axbar>;
		mclk-fs = <256>;

		rt565x_dai_link: nvidia,dai-link-1 {
			link-name = "sgtl5000-codec";
			cpu-dai = <&tegra_i2s1>;
			codec-dai = <&sgtl5000>;
			cpu-dai-name = "I2S1";
			codec-dai-name = "sgtl5000";
			format = "i2s";
			fsync-width = <15>;
                    bitclock-slave;
                    frame-slave;
                    bitclock-noninversion;
                    frame-noninversion;
			bit-format = "s16_le";
		  	bclk_ratio = <1>;
			srate = <48000>;
			num-channel = <2>;
			ignore_suspend;
			name-prefix = "x";
			status = "okay";
		};

The sound node for the TLV320ADC5140 looks like this:

	tegra_sound: sound {
		compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x";
		nvidia,model = "tegra-snd-t186ref-mobile-rt565x";
		nvidia,num-codec-link = <1>;
        nvidia,num-clk = <8>;
        nvidia,clk-rates = < 270950400  /* PLLA_x11025_RATE */
                             11289600   /* AUD_MCLK_x11025_RATE */
                             45158400   /* PLLA_OUT0_x11025_RATE */
                             45158400   /* AHUB_x11025_RATE */
                             245760000  /* PLLA_x8000_RATE */
                             12288000   /* AUD_MCLK_x8000_RATE */
                             49152000   /* PLLA_OUT0_x8000_RATE */
                             49152000 >;/* AHUB_x8000_RATE */
        clocks = <&tegra_car TEGRA186_CLK_PLLP_OUT0>,
                <&tegra_car TEGRA186_CLK_PLLA>,
                <&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
                <&tegra_car TEGRA186_CLK_AHUB>,
                <&tegra_car TEGRA186_CLK_CLK_M>,
                <&tegra_car TEGRA186_CLK_AUD_MCLK>;
        clock-names = "pll_p_out1", "pll_a", "pll_a_out0", "ahub",
                        "clk_m", "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>;
		resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>;
		reset-names = "extern1_rst";

		status = "okay";
		nvidia,audio-routing =
		    "x MIC1P",		"x Mic",
		    "x MIC2P",		"x Mic",
		    "x MIC3P",		"x Mic",
		    "x MIC4P",		"x Mic",
		    "x Capture",    "x CH1_ADC",
		    "x Capture",    "x CH2_ADC",
		    "x Capture",    "x CH3_ADC",
	            "x Capture",    "x CH4_ADC";

		nvidia,xbar = <&tegra_axbar>;
		mclk-fs = <256>;

		rt565x_dai_link: nvidia,dai-link-1 {
			link-name = "tlv320adcx140-codec";
			cpu-dai = <&tegra_i2s1>;
			codec-dai = <&tlv320adcx140>;
			cpu-dai-name = "I2S1";
			codec-dai-name = "tlv320adcx140-codec";
			format = "dsp_a";
                    bitclock-slave;
                    frame-slave;
                    bitclock-noninversion;
                    frame-noninversion;
			bit-format = "s32_le";
			bclk_ratio = <0>;
			srate = <48000>;
			num-channel = <4>;
			ignore_suspend;
			name-prefix = "x";
			status = "okay";
		};

So, putting them together on one board, should we have:

tegra_sound: sound {
	compatible = "nvidia,tegra-audio-t186ref-mobile-rt565x";
	nvidia,model = "tegra-snd-t186ref-mobile-rt565x";
	nvidia,num-codec-link = <2>;
    nvidia,num-clk = <8>;
    nvidia,clk-rates = < 270950400  /* PLLA_x11025_RATE */
                         11289600   /* AUD_MCLK_x11025_RATE */
                         45158400   /* PLLA_OUT0_x11025_RATE */
                         45158400   /* AHUB_x11025_RATE */
                         245760000  /* PLLA_x8000_RATE */
                         12288000   /* AUD_MCLK_x8000_RATE */
                         49152000   /* PLLA_OUT0_x8000_RATE */
                         49152000 >;/* AHUB_x8000_RATE */
    clocks = <&tegra_car TEGRA186_CLK_PLLP_OUT0>,
            <&tegra_car TEGRA186_CLK_PLLA>,
            <&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
            <&tegra_car TEGRA186_CLK_AHUB>,
            <&tegra_car TEGRA186_CLK_CLK_M>,
            <&tegra_car TEGRA186_CLK_AUD_MCLK>;
    clock-names = "pll_p_out1", "pll_a", "pll_a_out0", "ahub",
                    "clk_m", "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>;
	resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>;
	reset-names = "extern1_rst";

	status = "okay";
	nvidia,audio-routing =
	    "x MIC_IN",		"x Mic",
	    "x ADC",		        "x Mic Bias",
                "x Headphone",	"x HP_OUT",
	    "x Lineout",	        "x LINE_OUT",
	    "x LINE_IN",	        "x Linein",
	    "y MIC1P",		"y Mic",
	    "y MIC2P",		"y Mic",
	    "y MIC3P",		"y Mic",
	    "y MIC4P",		"y Mic",
	    "y Capture",            "y CH1_ADC",
  	    "y Capture",            "y CH2_ADC",
	    "y Capture",            "y CH3_ADC",
	    "y Capture",            "y CH4_ADC";

	nvidia,xbar = <&tegra_axbar>;
	mclk-fs = <256>;

	rt565x_dai_link: nvidia,dai-link-1 {
		link-name = "sgtl5000-codec";
		cpu-dai = <&tegra_i2s1>;
		codec-dai = <&sgtl5000>;
		cpu-dai-name = "I2S1";
		codec-dai-name = "sgtl5000";
		format = "i2s";
		fsync-width = <15>;
                    bitclock-slave;
                    frame-slave;
                    bitclock-noninversion;
                    frame-noninversion;
		bit-format = "s16_le";
		bclk_ratio = <1>;
		srate = <48000>;
		num-channel = <2>;
		ignore_suspend;
		name-prefix = "x";
 			status = "okay";
	};

	nvidia,dai-link-2 {
		link-name = "tlv320adcx140-codec";
		cpu-dai = <&tegra_i2s2>;
		codec-dai = <&tlv320adcx140>;
		cpu-dai-name = "I2S2";
		codec-dai-name = "tlv320adcx140-codec";
		format = "dsp_a";
                    bitclock-slave;
                    frame-slave;
                    bitclock-noninversion;
                    frame-noninversion;
		bit-format = "s32_le";
		bclk_ratio = <0>;
		srate = <48000>;
		num-channel = <4>;
		ignore_suspend;
		name-prefix = "y";
		status = "okay";
	};

I’m a bit unclear on the role of the prefixes x and y (do they need to be different?) and the naming of rt565x_dai_link: nvidia,dai-link-1 vs nvidia,dai-link-2 . Ideally, I want aplay to default to the SGTL5000 and arecord to default to the TLV320ADC5140. Independently, the devices work correctly (thanks to your previous help), but I’m just after some guidance on how to properly combine the sound nodes. Both devices are slaves, but only the SGTL5000 uses MCLK.

Thanks in advance,
David.

Hi David,

Apologies for the delay. I missed this message. What you have looks correct and yes the prefixes should be different. It is important when you have multiple instances of the same codec, for example two SGTL5000 connected to different I2S interfaces, and you need to be able to distinguish one from another. It is also important for the audio machine driver to use different prefixes for the input/output widgets.

Regards,
Jon