TLV320ADCx140 audio adc with TX2

Hi there,

We are currently developing a custom carrier board for the TX2 which uses the new TLV320ADCx140 quad-channel audio ADC in slave mode. One of the requirements to initialize the chip is to toggle its shutdown/reset pin, so I’ve dedicated GPIO19_AUD_RST, otherwise known as gpio_aud1_pj6 to this function (please correct me if I’m wrong).

This was setup via the pinmux spreadsheet as an output, after which sudo grep aud1 /sys/kernel/debug/tegra_pinctrl_reg gives:

Bank: 0 Reg: 0x02431010 Val: 0x00000000 -> gpio_aud1_pj6

Which I think means it has correctly been set as an output.

I am using the following code to register the reset gpio in the device tree, but so far I have not been successful in toggling it on system boot:

         tlv320adcx140: tlv320adcx140@4c {
                compatible = "ti,tlv320adc5140";
                reg = <0x4c>;
                clocks = <&tegra_car TEGRA186_CLK_AUD_MCLK>;
                reset-gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(J,6) GPIO_ACTIVE_HIGH>;
                ti,mic-bias-source = <6>;
                status = "okay";
        };

The i2c_probe function for the tlv320adcx140 looks like this:

static int adcx140_i2c_probe(struct i2c_client *i2c,
			     const struct i2c_device_id *id)
{
	struct adcx140_priv *adcx140;
	int ret;

	adcx140 = devm_kzalloc(&i2c->dev, sizeof(*adcx140), GFP_KERNEL);
	if (!adcx140)
		return -ENOMEM;

	adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
						      "reset", GPIOD_OUT_LOW);
	if (IS_ERR(adcx140->gpio_reset))
		dev_info(&i2c->dev, "Reset GPIO not defined\n");

	adcx140->supply_areg = devm_regulator_get_optional(adcx140->dev,
							   "areg");
	if (IS_ERR(adcx140->supply_areg)) {
		if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER)
			return -EPROBE_DEFER;
		else
			adcx140->supply_areg = NULL;
	} else {
		ret = regulator_enable(adcx140->supply_areg);
		if (ret) {
			dev_err(adcx140->dev, "Failed to enable areg\n");
			return ret;
		}
	}

	adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap);
	if (IS_ERR(adcx140->regmap)) {
		ret = PTR_ERR(adcx140->regmap);
		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
			ret);
		return ret;
	}
	adcx140->dev = &i2c->dev;
	i2c_set_clientdata(i2c, adcx140);

	return snd_soc_register_codec(&i2c->dev,
					       &soc_codec_driver_adcx140,
					       adcx140_dai_driver, 1);
}

It appears now that the IS_ERR() macro is not catching a null pointer returned by devm_gpiod_get_optional(), with the appearance that everything is fine, but the GPIO is not properly registered meaning that the reset does not operate. Without a functioning reset, I cannot bring the device out of shutdown mode, which means that subsequent initialization via i2c fails. I’d appreciate some help in identifying what I’m doing wrong.

Cheers,

David.

OK - on close inspection of the adcx140_i2c_probe() function in the TI driver, it would appear that the wrong pointer arguments have been passed, so the reset GPIO can never be captured correctly. The diff of my new driver file v the original is as follows:

diff --git a/tlv320adcx140_orig.c b/tlv320adcx140.c
index 8bab398..6da5b4a 100644
--- a/tlv320adcx140_orig.c
+++ b/tlv320adcx140.c
@@ -24,7 +24,7 @@
 #include "tlv320adcx140.h"
 
 struct adcx140_priv {
-	struct snd_soc_component *component;
+	struct snd_soc_codec *codec;
 	struct regulator *supply_areg;
 	struct gpio_desc *gpio_reset;
 	struct regmap *regmap;
@@ -550,12 +550,20 @@ static int adcx140_reset(struct adcx140_priv *adcx140)
 {
 	int ret = 0;
 
+	dev_info(adcx140->dev, "Attempting to reset tlv320adcx140...\n");
+
 	if (adcx140->gpio_reset) {
+
+		dev_info(adcx140->dev, "Attempting HW reset...\n");    
+
 		gpiod_direction_output(adcx140->gpio_reset, 0);
 		/* 8.4.1: wait for hw shutdown (25ms) + >= 1ms */
 		usleep_range(30000, 100000);
 		gpiod_direction_output(adcx140->gpio_reset, 1);
 	} else {
+
+		dev_info(adcx140->dev, "Attempting SW reset...\n");    
+
 		ret = regmap_write(adcx140->regmap, ADCX140_SW_RESET,
 		          ADCX140_RESET);
 	}
@@ -563,14 +571,14 @@ static int adcx140_reset(struct adcx140_priv *adcx140)
 	/* 8.4.2: wait >= 10 ms after entering sleep mode. */
 	usleep_range(10000, 100000);
 
-	return 0;
+	return ret;
 }
 
 static int adcx140_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params,
 			     struct snd_soc_dai *dai)
 {
-	struct snd_soc_component *component = dai->component;
+	struct snd_soc_codec *codec = dai->codec;
 	u8 data = 0;
 
 	switch (params_width(params)) {
@@ -587,12 +595,12 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream,
 		data = ADCX140_32_BIT_WORD;
 		break;
 	default:
-		dev_err(component->dev, "%s: Unsupported width %d\n",
+		dev_err(codec->dev, "%s: Unsupported width %d\n",
 			__func__, params_width(params));
 		return -EINVAL;
 	}
 
-	snd_soc_component_update_bits(component, ADCX140_ASI_CFG0,
+	snd_soc_update_bits(codec, ADCX140_ASI_CFG0,
 			    ADCX140_WORD_LEN_MSK, data);
 
 	return 0;
@@ -601,8 +609,8 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream,
 static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
 			       unsigned int fmt)
 {
-	struct snd_soc_component *component = codec_dai->component;
-	struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct adcx140_priv *adcx140 = snd_soc_codec_get_drvdata(codec);
 	u8 iface_reg1 = 0;
 	u8 iface_reg2 = 0;
 
@@ -616,7 +624,7 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	case SND_SOC_DAIFMT_CBS_CFM:
 	case SND_SOC_DAIFMT_CBM_CFS:
 	default:
-		dev_err(component->dev, "Invalid DAI master/slave interface\n");
+		dev_err(codec->dev, "Invalid DAI master/slave interface\n");
 		return -EINVAL;
 	}
 
@@ -634,7 +642,7 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	case SND_SOC_DAIFMT_NB_NF:
 		break;
 	default:
-		dev_err(component->dev, "Invalid DAI clock signal polarity\n");
+		dev_err(codec->dev, "Invalid DAI clock signal polarity\n");
 		return -EINVAL;
 	}
 
@@ -650,18 +658,18 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	case SND_SOC_DAIFMT_DSP_B:
 		break;
 	default:
-		dev_err(component->dev, "Invalid DAI interface format\n");
+		dev_err(codec->dev, "Invalid DAI interface format\n");
 		return -EINVAL;
 	}
 
 	adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 
-	snd_soc_component_update_bits(component, ADCX140_ASI_CFG0,
+	snd_soc_update_bits(codec, ADCX140_ASI_CFG0,
 				      ADCX140_FSYNCINV_BIT |
 				      ADCX140_BCLKINV_BIT |
 				      ADCX140_ASI_FORMAT_MSK,
 				      iface_reg1);
-	snd_soc_component_update_bits(component, ADCX140_MST_CFG0,
+	snd_soc_update_bits(codec, ADCX140_MST_CFG0,
 				      ADCX140_BCLK_FSYNC_MASTER, iface_reg2);
 
 	return 0;
@@ -671,19 +679,19 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
 				  unsigned int tx_mask, unsigned int rx_mask,
 				  int slots, int slot_width)
 {
-	struct snd_soc_component *component = codec_dai->component;
-	struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct adcx140_priv *adcx140 = snd_soc_codec_get_drvdata(codec);
 	unsigned int lsb;
 
 	if (tx_mask != rx_mask) {
-		dev_err(component->dev, "tx and rx masks must be symmetric\n");
+		dev_err(codec->dev, "tx and rx masks must be symmetric\n");
 		return -EINVAL;
 	}
 
 	/* TDM based on DSP mode requires slots to be adjacent */
 	lsb = __ffs(tx_mask);
 	if ((lsb + 1) != __fls(tx_mask)) {
-		dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
+		dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
 		return -EINVAL;
 	}
 
@@ -694,7 +702,7 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
 	case 32:
 		break;
 	default:
-		dev_err(component->dev, "Unsupported slot width %d\n", slot_width);
+		dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
 		return -EINVAL;
 	}
 
@@ -707,8 +715,8 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
 static int adcx140_prepare(struct snd_pcm_substream *substream,
 			 struct snd_soc_dai *dai)
 {
-	struct snd_soc_component *component = dai->component;
-	struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+	struct snd_soc_codec *codec = dai->codec;
+	struct adcx140_priv *adcx140 = snd_soc_codec_get_drvdata(codec);
 	int offset = 0;
 	int width = adcx140->slot_width;
 
@@ -722,7 +730,7 @@ static int adcx140_prepare(struct snd_pcm_substream *substream,
 		offset += adcx140->tdm_delay * width;
 
 	/* Configure data offset */
-	snd_soc_component_update_bits(component, ADCX140_ASI_CFG1,
+	snd_soc_update_bits(codec, ADCX140_ASI_CFG1,
 				      ADCX140_TX_OFFSET_MASK, offset);
 
 	return 0;
@@ -735,14 +743,16 @@ static const struct snd_soc_dai_ops adcx140_dai_ops = {
 	.set_tdm_slot	= adcx140_set_dai_tdm_slot,
 };
 
-static int adcx140_codec_probe(struct snd_soc_component *component)
+static int adcx140_codec_probe(struct snd_soc_codec *codec)
 {
-	struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+	struct adcx140_priv *adcx140 = snd_soc_codec_get_drvdata(codec);
 	int sleep_cfg_val = ADCX140_WAKE_DEV;
 	u8 bias_source;
 	u8 vref_source;
 	int ret;
 
+	dev_info(adcx140->dev, "Entered adcx140_codec_probe\n");
+
 	ret = device_property_read_u8(adcx140->dev, "ti,mic-bias-source",
 				      &bias_source);
 	if (ret)
@@ -792,10 +802,10 @@ out:
 	return ret;
 }
 
-static int adcx140_set_bias_level(struct snd_soc_component *component,
+static int adcx140_set_bias_level(struct snd_soc_codec *codec,
 				  enum snd_soc_bias_level level)
 {
-	struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+	struct adcx140_priv *adcx140 = snd_soc_codec_get_drvdata(codec);
 	int pwr_cfg = 0;
 
 	switch (level) {
@@ -813,20 +823,22 @@ static int adcx140_set_bias_level(struct snd_soc_component *component,
 	return regmap_write(adcx140->regmap, ADCX140_PWR_CFG, pwr_cfg);
 }
 
-static const struct snd_soc_component_driver soc_codec_driver_adcx140 = {
-	.probe			= adcx140_codec_probe,
-	.set_bias_level		= adcx140_set_bias_level,
-	.controls		= adcx140_snd_controls,
-	.num_controls		= ARRAY_SIZE(adcx140_snd_controls),
-	.dapm_widgets		= adcx140_dapm_widgets,
-	.num_dapm_widgets	= ARRAY_SIZE(adcx140_dapm_widgets),
-	.dapm_routes		= adcx140_audio_map,
-	.num_dapm_routes	= ARRAY_SIZE(adcx140_audio_map),
-	.suspend_bias_off	= 1,
-	.idle_bias_on		= 0,
-	.use_pmdown_time	= 1,
-	.endianness		= 1,
-	.non_legacy_dai_naming	= 1,
+static const struct snd_soc_codec_driver soc_codec_driver_adcx140 = {
+	.probe			    = adcx140_codec_probe,
+	.set_bias_level     = adcx140_set_bias_level,
+	.suspend_bias_off	= true,
+	.idle_bias_off		= true,
+	.ignore_pmdown_time	= false,
+/*	.endianness		    = true,*/
+/*	.non_legacy_dai_naming	= true,*/
+	.component_driver = {
+		.controls		    = adcx140_snd_controls,
+		.num_controls		= ARRAY_SIZE(adcx140_snd_controls),
+		.dapm_widgets		= adcx140_dapm_widgets,
+		.num_dapm_widgets	= ARRAY_SIZE(adcx140_dapm_widgets),
+		.dapm_routes		= adcx140_audio_map,
+		.num_dapm_routes	= ARRAY_SIZE(adcx140_audio_map),
+	},
 };
 
 static struct snd_soc_dai_driver adcx140_dai_driver[] = {
@@ -836,8 +848,8 @@ static struct snd_soc_dai_driver adcx140_dai_driver[] = {
 			.stream_name	 = "Capture",
 			.channels_min	 = 2,
 			.channels_max	 = ADCX140_MAX_CHANNELS,
-			.rates		 = ADCX140_RATES,
-			.formats	 = ADCX140_FORMATS,
+			.rates		     = ADCX140_RATES,
+			.formats	     = ADCX140_FORMATS,
 		},
 		.ops = &adcx140_dai_ops,
 		.symmetric_rates = 1,
@@ -862,22 +874,28 @@ static int adcx140_i2c_probe(struct i2c_client *i2c,
 	if (!adcx140)
 		return -ENOMEM;
 
-	adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
+	adcx140->gpio_reset = devm_gpiod_get_optional(&i2c->dev,
 						      "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(adcx140->gpio_reset))
-		dev_info(&i2c->dev, "Reset GPIO not defined\n");
 
-	adcx140->supply_areg = devm_regulator_get_optional(adcx140->dev,
+	if (adcx140->gpio_reset)
+		dev_info(&i2c->dev, "Got reset GPIO %d OK\n", 
+			desc_to_gpio(adcx140->gpio_reset));
+	else
+		dev_err(&i2c->dev, "Reset GPIO not defined!\n");
+
+	adcx140->supply_areg = devm_regulator_get_optional(&i2c->dev,
 							   "areg");
+
 	if (IS_ERR(adcx140->supply_areg)) {
-		if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER)
+		if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER) {
+			dev_err(&i2c->dev, "Probe deferred...\n");
 			return -EPROBE_DEFER;
-		else
+		} else
 			adcx140->supply_areg = NULL;
 	} else {
 		ret = regulator_enable(adcx140->supply_areg);
 		if (ret) {
-			dev_err(adcx140->dev, "Failed to enable areg\n");
+			dev_err(&i2c->dev, "Failed to enable areg\n");
 			return ret;
 		}
 	}
@@ -889,12 +907,19 @@ static int adcx140_i2c_probe(struct i2c_client *i2c,
 			ret);
 		return ret;
 	}
+
 	adcx140->dev = &i2c->dev;
 	i2c_set_clientdata(i2c, adcx140);
 
-	return devm_snd_soc_register_component(&i2c->dev,
+	dev_info(adcx140->dev, "Calling snd_soc_register_codec():\n");
+
+	ret = snd_soc_register_codec(&i2c->dev,
 					       &soc_codec_driver_adcx140,
 					       adcx140_dai_driver, 1);
+
+	dev_info(adcx140->dev, "Return value = %d\n", ret);
+
+	return ret;
 }
 
 static const struct i2c_device_id adcx140_i2c_id[] = {
@@ -917,4 +942,4 @@ module_i2c_driver(adcx140_i2c_driver);
 
 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
 MODULE_DESCRIPTION("ASoC TLV320ADCX140 CODEC Driver");
-MODULE_LICENSE("GPL v2");
\ No newline at end of file
+MODULE_LICENSE("GPL v2");

Having fixed this, I am now on to the next problem, which is a failure to register the DAI:

tegra-asoc: sound: ASoC: CODEC DAI tlv320adcx140 not registered.

I am wondering if this could be caused by incorrect routing???

What I have is below, which is a total guess and bound to be wrong:

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

/*
		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>;
		resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>;
		reset-names = "extern1_rst";

		status = "okay";
		nvidia,audio-routing =
		    "x CH1_ADC",		"x Mic",
			"x CH2_ADC",		"x Mic",
		    "x CH3_ADC",		"x Mic",
			"x CH4_ADC",		"x Mic";

		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";
			format = "dsp-a";
			fsync-width = <15>;
            bitclock-slave;
            frame-slave;
            bitclock-noninversion;
            frame-noninversion;
			bit-format = "s16_le";
			bclk_ratio = <1>;
			srate = <48000>;
			num-channel = <4>;
			ignore_suspend;
			name-prefix = "x";
			status = "okay";
		};

Hello!

From looking at the tlv320adcx140 driver [0], I believe that the ‘codec-dai-name’ should be ‘tlv320adcx140-codec’ [0]. If the codec has been proded and registered correctly, then not finding the codec DAI is most likely a naming issue.

Regards,
Jon

[0] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/codecs/tlv320adcx140.c#n832

Thanks Jon,
I have been staring at the sound node for days now and didn’t notice that issue. So I think now the driver is registering, but it is producing the following errors:

tlv320adcx140 1-004c: Invalid DAI interface format
tlv320adcx140 1-004c: ASoC: Failed to set DAI format -22

This looks like a problem I might bug TI about, (unless you have some suggestions) but in the meantime, having seen the driver code, would you hazard a guess at what routing I need to get set up basic four channels of input? I guess I will be using dsp-a format?

Cheers and thanks again for your help so far.

David.

Hi David,

The format passed to the codec’s adcx140_set_dai_fmt() function should come from the DAI link node that you have specificed in device-tree. Looking at the driver the valid formats are I2S, LEFT_JUST, DSPA or DSPB [0] (assuming that we are looking at the same driver version). In your nvidia,dai-link-1 node you have the format set to ‘dsp-a’ which should be fine.

So the question is, have you modified the Tegra audio machine driver to apply the format correctly for your ‘tlv320adcx140-codec’? If you are not sure what I mean, then in the tegra_machine_dai_init() function [1], you should have something like …

         rtd = snd_soc_get_pcm_runtime(card, "tlv320adcx140-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;
         }

Note that the ‘tlv320adcx140-codec’ name shown above should match the ‘link-name’ property in the nvidia,dai-link-1 node.

Regards,
Jon

[0] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/codecs/tlv320adcx140.c#n641
[1] https://nv-tegra.nvidia.com/gitweb/?p=linux-nvidia.git;a=blob;f=sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c;h=fbca4699d353de7e7eb2b8984c07efab954e5b15;hb=refs/tags/tegra-l4t-r32.4.2#l284

Hi David,

Is the error happening on boot or during playback? The above change needs to be made, but that should only be applicable for playback. The DAI format should be set on boot and thinking about it some more the above should not be necessary to set the initial format. Maybe check to see what ‘fmt’ is set to when the adcx140_set_dai_fmt() is called.

Regards,
Jon

Hi Jon,

To answer your first question, I have modified my machine driver pretty much exactly has you have indicated, although I was also wondering if I should be setting the sysclk frequency there somehow? I may have to ask TI how to do that exactly (unless you have some suggestions). To answer your second question, the error is only occurring on boot, as I have not attempted to record yet (I don’t seem to have any clocks anyway).

The relevant part of dmesg now looks like this (with a lot of my own debugging output):

[    1.747890] Entering tegra_machine_driver_probe()
[    1.747909] Now parsing audio routing...
[    1.747909] Entering add_dai_links()
[    1.748067] Entering codec_init()
[    1.748105] Found match: tlv320adcx140-codec
[    1.748127] Entering set_dai_ops()
[    1.748132] Exiting add_dai_links()
[    1.748621] card->name = tegra-snd-t186ref-mobile-rt565x
[    1.748624] card->long_name = (null)
[    1.748626] card->driver_name = (null)
[    1.812291] tlv320adcx140-codec 1-004c: Entered adcx140_codec_probe
[    1.812302] tlv320adcx140-codec 1-004c: Attempting to reset tlv320adcx140...
[    1.812304] tlv320adcx140-codec 1-004c: Attempting HW reset...
[    1.959678] tegra-pcie 10003000.pcie-controller: link 0 down, retrying
[    2.001036] tegra-asoc: sound: ADMAIF1 <-> ADMAIF1 mapping ok
[    2.001127] tegra-asoc: sound: ADMAIF2 <-> ADMAIF2 mapping ok
[    2.001211] tegra-asoc: sound: ADMAIF3 <-> ADMAIF3 mapping ok
[    2.001297] tegra-asoc: sound: ADMAIF4 <-> ADMAIF4 mapping ok
[    2.001384] tegra-asoc: sound: ADMAIF5 <-> ADMAIF5 mapping ok
[    2.001458] usb 1-2.1: new low-speed USB device number 3 using tegra-xusb
[    2.001521] tegra-asoc: sound: ADMAIF6 <-> ADMAIF6 mapping ok
[    2.001639] tegra-asoc: sound: ADMAIF7 <-> ADMAIF7 mapping ok
[    2.001727] tegra-asoc: sound: ADMAIF8 <-> ADMAIF8 mapping ok
[    2.001813] tegra-asoc: sound: ADMAIF9 <-> ADMAIF9 mapping ok
[    2.001899] tegra-asoc: sound: ADMAIF10 <-> ADMAIF10 mapping ok
[    2.001985] tegra-asoc: sound: ADMAIF11 <-> ADMAIF11 mapping ok
[    2.002070] tegra-asoc: sound: ADMAIF12 <-> ADMAIF12 mapping ok
[    2.002183] tegra-asoc: sound: ADMAIF13 <-> ADMAIF13 mapping ok
[    2.002269] tegra-asoc: sound: ADMAIF14 <-> ADMAIF14 mapping ok
[    2.002355] tegra-asoc: sound: ADMAIF15 <-> ADMAIF15 mapping ok
[    2.002445] tegra-asoc: sound: ADMAIF16 <-> ADMAIF16 mapping ok
[    2.002538] tegra-asoc: sound: ADMAIF17 <-> ADMAIF17 mapping ok
[    2.002644] tegra-asoc: sound: ADMAIF18 <-> ADMAIF18 mapping ok
[    2.002734] tegra-asoc: sound: ADMAIF19 <-> ADMAIF19 mapping ok
[    2.002821] tegra-asoc: sound: ADMAIF20 <-> ADMAIF20 mapping ok
[    2.005802] tegra-asoc: sound: Entered tegra_machine_tlv320adcx140_init
[    2.005810] tlv320adcx140-codec 1-004c: Invalid DAI interface format: 4000
[    2.005815] tlv320adcx140-codec 1-004c: ASoC: Failed to set DAI format: -22

I have printed out the format which is being passed to adcx140_set_dai_fmt() on the second last line. The value appears to be 0x4000, which is very strange because according to soc-dai_8h.h:

#define SND_SOC_DAIFMT_DSP_A 4

So the value is completely out of range (almost as if it has been left-shifted) and would be masked to zero by SND_SOC_DAIFMT_FORMAT_MASK, whose value is 0x000f anyway. Any ideas?

Cheers,

David.

Hi David,

Can you try setting the format to ‘dsp_a’ and not ‘dsp-a’?

			format = "dsp_a";

Regards,
Jon

Hi John.

That did it. The driver now configures without any complaints. When I run arecord test.wav, I see MCLK start up at 12.4MHz, but no LRCLK, no BCLK and no data on DOUT. arecord appears to run until I interrupt it and outputs a short file which is just a header.

So I suppose I need some extra configuration? I’m not entirely clear about the clock setup in the device tree though. I recall when I setup the SGTL5000 in slave mode (as practise for this) I had to use snd_soc_dai_set_sysclk() within snd_soc_get_pcm_runtime() in the machine driver. Is that necessary?

Cheers,

David.

Hi David,

If the MCLK is provided by Tegra and the SGTL5000 is the slave, then you need to add a call to snd_soc_dai_set_sysclk() for the tlv320adcx140-codec in tegra_machine_dai_init() similar to what is shown here for the rt565x codec [0]. Note that you should change the RT5659_SCLK_S_MCLK parameter to be SGTL5000_SYSCLK instead.

Have you configured the pinmux for the I2S pins? By default the I2S1 pins are configured as GPIO. If you dump the following we can tell the default configuration.

$ sudo cat /sys/kernel/debug/tegra_pinctrl_reg | dap1

Regards,
Jon

[0] https://nv-tegra.nvidia.com/gitweb/?p=linux-nvidia.git;a=blob;f=sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c;h=fbca4699d353de7e7eb2b8984c07efab954e5b15;hb=refs/heads/l4t/l4t-r32.4.2#l314

Yes Jon,
I have had the SGTL5000 working for a few weeks now. Pinmux was setup to enable I2S1.

With the TLV320ADC3140, I have similarly enabled I2S1 and AUD_RST. I am seeing MCLK when I run arecord and now I’m looking for the routing/configuration that might enable recording. Any suggestions?

Cheers,
David.

Hi David,

Sorry, too many issues and I am getting confused.

If the tlv320adcx140 is the slave, then you still need to call snd_soc_dai_set_sysclk() for configuring the MCLK as you did for the sgtl5000.

Probably now the audio route/path in the codec needs to be configured. I see you have the ‘x Mic’ connected to the various codec ADCs, in the nvidia,audio-routing, but I think you want to connect the ‘x Mic’ to the SND_SOC_DAPM_INPUT widgets defined for the codec. Then you need to configue the various muxes, enables, etc, in the codec to enable the audio path.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/codecs/tlv320adcx140.c#n326

Regards,
Jon

OK Jon,

So if the SND_SOC_DAPM_INPUT widgets are:

SND_SOC_DAPM_INPUT("MIC1P"),
SND_SOC_DAPM_INPUT("MIC1M"),
SND_SOC_DAPM_INPUT("MIC2P"),
SND_SOC_DAPM_INPUT("MIC2M"),
SND_SOC_DAPM_INPUT("MIC3P"),
SND_SOC_DAPM_INPUT("MIC3M"),
SND_SOC_DAPM_INPUT("MIC4P"),
SND_SOC_DAPM_INPUT("MIC4M"),

should my routing for 4 x single-ended inputs look like:

nvidia,audio-routing =
“x Mic” “x MIC1P”,
“x Mic” “x MIC2P”,
“x Mic” “x MIC3P”,
“x Mic” “x MIC4P”;

I’m afraid my understanding of routing in ALSA is a bit shaky.

Cheers,

David.

Hi David,

Are you only using single-ended inputs for the mics? In other words, just the MICP inputs? The driver shows that each MIC input has a mux mixer control to setup if it is ‘Analog’, Line In’ and in the case of the MICPs ‘Digital’ that need to be configured …

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/codecs/tlv320adcx140.c#n209

The routing is close but you should have it the other way around …

nvidia,audio-routing =
    "x MIC1P”, “x Mic”,
    “x MIC2P”, “x Mic”,
     “x MIC3P”, “x Mic”,
     “x MIC4P”, “x Mic” ;

What we want to see is that when you run your capture command all the various DAPM widgets (Tegra and ADC) turn on. We can see this by tracing the DAPM widget events …

$ echo 0 | sudo tee /sys/kernel/debug/tracing/trace
$ echo 0 | sudo tee /sys/kernel/debug/tracing/events/enable
$ echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on
$ echo 1 | sudo tee /sys/kernel/debug/tracing/events/asoc/snd_soc_dapm_widget_power/enable
$ arecord ...
$ sudo cat /sys/kernel/debug/tracing/trace

If the trace shows very little then this is a sign that something is not configured correct in the ADC.

Regards
Jon

Thanks very much Jon,

My understanding is that 4 x differential or single-ended inputs are supported, or 8 x digital inputs. As I only have single-ended analog inputs, I was assuming that these would be x MIC1-P to x MIC4-P.

I will change my routing and give the trace a try tomorrow and get back to you with the results.

Cheers,

David.

Hi David,

Yes then that would look correct to me for the routing just having those four.

Jon

Hello again Jon,

I quickly went ahead and changed the routing and performed that trace. Unfortunately, it seems to be empty:

tracer: nop

entries-in-buffer/entries-written: 0/0 #P:4# tracer: nop

entries-in-buffer/entries-written: 0/0 #P:4

_-----=> irqs-off

/ _----=> need-resched

| / _—=> hardirq/softirq

|| / _–=> preempt-depth

||| / delay

TASK-PID CPU# |||| TIMESTAMP FUNCTION

| | | |||| | |

Cheers,

David.

Hi Jon,

So the trace was completely blank and I am not seeing any BCLK or LRCLK when I run arecord. I do, however, see MCLK at 12.288 MHz.

As I mentioned before, I currently do not call snd_soc_dai_set_sysclk() anywhere, which makes me slightly surprised to see MCLK at all. Could the absence of the other clocks be caused by this, however?

I think this is close to working now, provided I can restore the clock signals for I2S1, but I note the TI driver has no .set_sysclk member in adcx140_dai_ops [1], so if I do need to set the sysclock, I don’t know how.

I have also had a change in thinking brought about by [1], as the TI driver is apparently written to configure the TLV320ADCx140 in slave mode (which is what I want) with auto-clock configuration, so it’s simplest to disconnect MCLK and let the device generate its own provided I can supply BCLK and LRCLK (FSYNC).

Right now, I need some help with those I2S1 clocks. I am using the same pinmux configuration as for the SGTL5000. sudo grep dap1 /sys/kernel/debug/tegra_pinctrl_reg gives:

Bank: 0 Reg: 0x02431028 Val: 0x00000440 -> dap1_fs_pj3
Bank: 0 Reg: 0x02431030 Val: 0x00000458 -> dap1_din_pj2
Bank: 0 Reg: 0x02431038 Val: 0x00000400 -> dap1_dout_pj1
Bank: 0 Reg: 0x02431040 Val: 0x00000440 -> dap1_sclk_pj0

Cheers,
David.

[1] https://lkml.org/lkml/2020/2/26/413

Hi David,

What exact arecord command are you using?

Have you mapped the I2S1 to one of the ADMAIFx interfaces?

There are some examples for I2S here:
https://docs.nvidia.com/jetson/l4t/index.html#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide%2Fasoc_driver.18.2.html%23wwpID0E0YJ0HA

Typically, you map the I2S1 to say ADMAIF1 …

$ amixer -c tegrasndt186ref sget "ADMAIF1 Mux" I2S1

And then record on ADMAIF1 …

$ arecord -D hw:tegrasndt186ref,0 -c 4 -r 48000 -f S16_LE cap.wav

Regards,
Jon

Hi Jon,

Out of ideas here. After running those exact commands, I’m still only seeing MCLK at 12.288 MHz, but no BCLK or LRCLK. My sound block 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";

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

I should be getting those clocks even if I’ve totally messed up the codec configuration, right?

Cheers,
David.