Custom TLV320AIC3104 overlay not loading on BYAI

Hello,

I am integrating a TLV320AIC3104 sound codec (I2S) into a custom BeagleY-AI hat we have built for a computer science embedded systems course I am teaching. I have made a custom device tree overlay but am unable to have Linux successfully detect the TLV320AIC3104 chip.

Problems:

  1. Linux won’t load the tlv320aic3x driver at boot: gives “platform sound1: deferred probe pending”.
  2. Sound does not work (when tested with speaker-test)

What I have succeeded at:

  1. Create a device tree overlay and have it load at startup (included below). It shows up in /proc/device-tree/chosen/overlays/
  2. Compile the TLV320AIC3104 driver (not compiled in default BYAI image):
    CONFIG_SND_SOC_TLV320AIC3X_I2C=m in kernel config, builds snd-soc-tlv320aic3x.ko and snd-soc-tlv320aic3x-i2c.ko. Copied to /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/ and can be manually loaded with insmod.
  3. Once manually loaded, the TLV320AIC3014 device specified by the device tree shows up as a device is aplay -l. See below.

I’m struggling to debug the overlay and sound driver configuration. Some specific issues I would like help with include:

1. Reset Line
Three unexpected behaviours with the reset line:
1. Reset line stays at ~0.8v during boot, even when trying to probe.
2. When I manually load the tlv320aic3x.ko driver (with insmod), it pulls the reset line low and holds it low.
3. gpioinfo shows “active-high”, regardless of if I use GPIO_ACTIVE_LOW or GPIO_ACTIVE_HIGH in my device-tree overlay.

reset-gpios in the device tree overlay (full code below):

&mcu_i2c0 {
	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";

	tlv320aic3104: tlv320aic3104@18 {
		#sound-dai-cells = <0>;
		compatible = "ti,tlv320aic3104";
		reg = <0x18>;                       
		status = "okay";

		reset-gpios = <&main_gpio0 36 GPIO_ACTIVE_LOW>;

		ai3x-micbias-vg = <2>; // set MICBIAS to 2.5v
		AVDD-supply = <&vdd_3v3>;
		IOVDD-supply = <&vdd_3v3>;
		DRVDD-supply = <&vdd_3v3>;
		DVDD-supply = <&vdd_audio_1v8>;
	};
};

GPIO26 is the right pin: when its manually driven low, the codec reads X’s with i2cdump; when manually driven high, it reads seemingly valid values.

main_gpio0 36 seems correct: gpioinfo shows:
line 36: "GPIO26" "tlv320aic3x reset" output active-high [used].

But, watching the physical pin with a multimeter, it has the above three unexpected behaviours.

2. Linux is unable to detect the codec

When configured to load the device-tree overlay in extlinux.conf, it seems to try to find the device, but it fails.

dmesg displays:
[ 26.592305] platform sound1: deferred probe pending

$ cat /sys/kernel/debug/devices_deferred
sound1 asoc-simple-card: parse error

When I manually hold the reset line high (externally wired with a pull-up resistor and changed .dts to not use the reset pin), it is still unable to find the device. Hence, it does not automatically load the tlv320aic3x driver.

I have checked most of the syntax and structure of my overlay against the k3-am625-beaglemod-audio.dts file and looked into as many differences as I could find but am stuck!

Any suggestions would be very helpful!
Brian.

System Information

BeagleY-AI board

$ uname -a
    Linux BeagleBone 6.1.83-ti-arm64-r63 #1bookworm SMP PREEMPT_DYNAMIC Wed Jul 10 23:00:56 UTC 2024 aarch64 GNU/Linux

aplay listing of devices:

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: HDMI [it66122 HDMI], device 0: davinci-mcasp.0-i2s-hifi i2s-hifi-0 [davinci-mcasp.0-i2s-hifi i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Audio [ZenHat Audio], device 0: davinci-mcasp.0-tlv320aic3x-hifi tlv320aic3x-hifi-0 [davinci-mcasp.0-tlv320aic3x-hifi tlv320aic3x-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

Beagle-Version:

$ sudo beagle-version
eeprom:[BYAI202406002197]
model:[BeagleBoard.org_BeagleY-AI]
dogtag:[BeagleBoard.org Debian Bookworm Minimal Image 2024-09-06]
bootloader:[/dev/mmcblk1]:[/boot/firmware/tiboot3.bin]:[U-Boot SPL 2023.04-g93735daa (Aug 29 2024 - 22:05:30 +0000)]
bootloader:[/dev/mmcblk1]:[/boot/firmware/tispl.bin]:[U-Boot SPL 2023.04-g93735daa (Aug 29 2024 - 22:05:30 +0000)]
bootloader:[/dev/mmcblk1]:[/boot/firmware/u-boot.img]:[U-Boot 2023.04-g93735daa (Aug 29 2024 - 22:05:30 +0000)]
UBOOT: Booted Device-Tree:[k3-am67a-beagley-ai.dts]
UBOOT: Loaded Overlay:[k3-am67asbeaglemod-audio.kernel]
kernel:[6.1.83-ti-arm64-r63]
pkg check: to individually upgrade run: [sudo apt install --only-upgrade <pkg>]
pkg:[bb-u-boot-beagley-ai]:[2023.04.20240319.24-09.02.00.009-0~bookworm+20240829]
pkg:[bb-wl18xx-firmware]:[1.20240622.0-0~bookworm+20240622]
pkg:[bb-customizations]:[1.20240627.0-0~bookworm+20240627]
pkg:[bb-usb-gadgets]:[1.20240717.0-0~bookworm+20240717]
groups:[brian : brian adm kmem dialout cdrom floppy audio dip video plugdev users systemd-journal input render netdev i2c bluetooth gpio admin tisdk weston-launch cloud9ide]
cmdline:[console=ttyS2,115200n8 root=/dev/mmcblk1p3 ro rootfstype=ext4 resume=/dev/mmcblk1p2 rootwait net.ifnames=0 quiet]
ti-sci:
optee:
wlcore:
wlcore: Wireless driver version 1.7.0.114
wlcore: Wireless firmware version 1.7.0.130
wlcore: Wireless PHY version 1.2.36.5.22.66
dmesg | grep remote
[    7.095303] k3-dsp-rproc 7e000000.dsp: configured DSP for remoteproc mode
[    7.095554] remoteproc remoteproc0: 7e000000.dsp is available
[    7.096567] k3-dsp-rproc 7e000000.dsp: register pm nitifiers in remoteproc mode
[    7.097715] k3-dsp-rproc 7e200000.dsp: configured DSP for remoteproc mode
[    7.097958] remoteproc remoteproc1: 7e200000.dsp is available
[    7.099965] remoteproc remoteproc1: Direct firmware load for j722s-c71_1-fw failed with error -2
[    7.099965] remoteproc remoteproc0: Direct firmware load for j722s-c71_0-fw failed with error -2
[    7.100001] remoteproc remoteproc1: powering up 7e200000.dsp
[    7.100002] remoteproc remoteproc0: powering up 7e000000.dsp
[    7.100057] remoteproc remoteproc1: Direct firmware load for j722s-c71_1-fw failed with error -2
[    7.100058] remoteproc remoteproc0: Direct firmware load for j722s-c71_0-fw failed with error -2
[    7.100067] remoteproc remoteproc0: request_firmware failed: -2
[    7.100067] remoteproc remoteproc1: request_firmware failed: -2
[    7.100449] k3-dsp-rproc 7e200000.dsp: register pm nitifiers in remoteproc mode
[    7.376611] platform 79000000.r5f: configured R5F for remoteproc mode
[    7.402231] remoteproc remoteproc2: 79000000.r5f is available
[    7.402599] remoteproc remoteproc2: Direct firmware load for j722s-mcu-r5f0_0-fw failed with error -2
[    7.402640] remoteproc remoteproc2: powering up 79000000.r5f
[    7.402696] remoteproc remoteproc2: Direct firmware load for j722s-mcu-r5f0_0-fw failed with error -2
[    7.402708] remoteproc remoteproc2: request_firmware failed: -2
[    7.523563] remoteproc remoteproc3: 78000000.r5f is available
[    7.523700] remoteproc remoteproc3: attaching to 78000000.r5f
[    7.862551] remoteproc remoteproc3: remote processor 78000000.r5f is now attached
[    7.883956] platform 78400000.r5f: configured R5F for remoteproc mode
[    7.886809] remoteproc remoteproc4: 78400000.r5f is available
[    7.887120] remoteproc remoteproc4: Direct firmware load for j722s-main-r5f0_0-fw failed with error -2
[    7.887154] remoteproc remoteproc4: powering up 78400000.r5f
[    7.887274] remoteproc remoteproc4: Direct firmware load for j722s-main-r5f0_0-fw failed with error -2
[    7.887293] remoteproc remoteproc4: request_firmware failed: -2
[   12.232427] Modules linked in: cc33xx mac80211 algif_aead rpmsg_ctrl rpmsg_char virtio_rpmsg_bus rpmsg_ns libarc4 crct10dif_ce snd_soc_simple_card snd_soc_simple_card_utils pvrsrvkm(O) cpufreq_dt cc33xx_sdio cfg80211 pwm_fan e5010_jpeg_enc at24 rti_wdt btti_uart bluetooth ti_k3_r5_remoteproc wave5 snd_soc_davinci_mcasp snd_soc_ti_udma snd_soc_ti_edma videobuf2_dma_contig v4l2_mem2mem snd_soc_hdmi_codec snd_soc_ti_sdma videobuf2_memops videobuf2_v4l2 snd_soc_core videobuf2_common snd_pcm_dmaengine ti_k3_dsp_remoteproc ti_k3_common snd_pcm videodev snd_timer mc snd uio_pdrv_genirq uio loop dm_mod efi_pstore
[   12.233864] Modules linked in: cc33xx mac80211 algif_aead rpmsg_ctrl rpmsg_char virtio_rpmsg_bus rpmsg_ns libarc4 crct10dif_ce snd_soc_simple_card snd_soc_simple_card_utils pvrsrvkm(O) cpufreq_dt cc33xx_sdio cfg80211 pwm_fan e5010_jpeg_enc at24 rti_wdt btti_uart bluetooth ti_k3_r5_remoteproc wave5 snd_soc_davinci_mcasp snd_soc_ti_udma snd_soc_ti_edma videobuf2_dma_contig v4l2_mem2mem snd_soc_hdmi_codec snd_soc_ti_sdma videobuf2_memops videobuf2_v4l2 snd_soc_core videobuf2_common snd_pcm_dmaengine ti_k3_dsp_remoteproc ti_k3_common snd_pcm videodev snd_timer mc snd uio_pdrv_genirq uio loop dm_mod efi_pstore
dmesg | grep pru
dmesg | grep pinctrl-single
[    1.111489] pinctrl-single 4084000.pinctrl: 34 pins, size 136
[    1.112516] pinctrl-single f4000.pinctrl: 171 pins, size 684
dmesg | grep gpio-of-helper
lsusb
Bus 002 Device 002: ID 0451:8140 Texas Instruments, Inc. TUSB8041 4-Port Hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 0451:8142 Texas Instruments, Inc. TUSB8041 4-Port Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
lspci
00:00.0 PCI bridge: Texas Instruments Device b010
END

extlinux.conf

...
label microSD (default)
    kernel /Image
    append console=ttyS2,115200n8 root=/dev/mmcblk1p3 ro rootfstype=ext4 resume=/dev/mmcblk1p2 rootwait net.ifnames=0 quiet
    fdtdir /
    fdt /ti/k3-am67a-beagley-ai.dtb

    fdtoverlays /overlays/k3-am67a-beaglemod-audio.dtbo
    #initrd /initrd.img

Device Tree Overlay

It is annotated with thoughts and questions from my debugging process.
It is built and installed natively on the target using the tree cloned from https://openbeagle.org/beagleboard/BeagleBoard-DeviceTrees.git, on branch v6.1.x-Beagle.

// Audio driver for TLV320AIC3104 for BeagleY-AI
// SPDX-License-Identifier: GPL-2.0
/* 
 * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
 * Modified by Brian Fraser
 */

/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include "ti/k3-pinctrl.h"


/*
 * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
 */
&{/chosen} {
	overlays {
		k3-am67asbeaglemod-audio.kernel = __TIMESTAMP__;
	};
};

&{/} {
	tlv320aic3104_mclk: tlv320aic3104_mclk {
		#clock-cells = <0>;
		compatible = "fixed-clock";
		clock-frequency = <12000000>;
	};

    // Question: Should this be "sound1" or the like to not conflict with the HDMI?
	// (HDMI is "sound0")
	// - When this is just 'sound', then it stops HDMI sound from loading, and 
	//   generates no "deferred" messages when booting.
	// - When this is 'sound1' or 'sound1', it generates a message in dmesg:
	//   [   26.336146] platform sound1: deferred probe pending
	//   ==> Seems 'sound1' is a good answer.
	sound1 {
		compatible = "simple-audio-card";
		simple-audio-card,format = "i2s";
		simple-audio-card,name = "ZenHat Audio";
		simple-audio-card,bitclock-master = <&dailink0_master>;
		simple-audio-card,frame-master = <&dailink0_master>;
		status = "okay";

		simple-audio-card,widgets =
			"Headphone", "Headphone Jack",
			"Line", "Line In";
		simple-audio-card,routing =
			"Headphone Jack",       "HPLOUT",
			"Headphone Jack",       "HPROUT",
			"LINE1L",               "Line In",
			"LINE1R",               "Line In";

		// QUESTION: Should the "dailink0_master:" label be on the first or second struct?
		// Is this defining which chip is in control of the clock? (referenced above)
		// Observation from other .DTS:
		// 		When running in i2s format, the CPU is setup with the label.
		//		  See: src/arm64/overlays/k3-am625-beaglemod-audio.dts
		//		When running in dsp_b format, the codec has the label (ex k3-am62x-sk-common.dtsi)
		dailink0_master: simple-audio-card,cpu {
			sound-dai = <&mcasp0>;
			system-clock-direction-out;
		};
		simple-audio-card,codec {
			sound-dai = <&tlv320aic3104>;
			// This line is not in k3-j722s-beagley-ai-evt, but is for k3-am62x-sk-common
			clocks = <&tlv320aic3104_mclk>;	
		};
	};



	// Added to remove error on load:
	// [   39.214167] tlv320aic3x 1-0018: Invalid supply voltage(s) AVDD: 3300000, DVDD: -22
	// (Unsure if this is the correct value: 1.8v?)
	vdd_audio_1v8: regulator-audio {
		compatible = "regulator-fixed";
		regulator-name = "vdd_audio_1v8";
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <1800000>;
		vin-supply = <&vdd_3v3>;
		regulator-always-on;
		regulator-boot-on;
	};

};

&main_pmx0 {
    // I2S running on MCASP0 (MCASP1 is HDMI audio).
	main_mcasp0_pins_default: main-mcasp0-codec-default-pins {
		pinctrl-single,pins = <
			J722S_IOPAD(0x01a4, PIN_INPUT, 0) /* BCLK on hat-12 (D25) MCASP0_ACLKX */
			J722S_IOPAD(0x01a8, PIN_INPUT, 0) /* WCLK on hat-35 (C26) MCASP0_AFSX */
			J722S_IOPAD(0x01a0, PIN_INPUT, 0) /* DIN  on hat-38 (F24) MCASP0_AXR0  (may be F23, as per beagley_ai-pinmux.dts)*/
			J722S_IOPAD(0x019c, PIN_OUTPUT, 0)/* DOUT on hat-40 (B25) MCASP0_AXR1 */

			/*Question: Is this needed in use reset_gpios below?*/
			J722S_IOPAD(0x0094, PIN_OUTPUT, 7) /* Reset on hat-37 (P26) GPMC0_BE1n.GPIO0_36 */

		>;
	};

	// Question: Is this needed in use reset_gpios below?
	//   Have found it does not make a difference to the reset line.
	//   And I2C chip still fails to detect.
	// mygpio1_pins_default: mygpio1-default-pins {
	// 	pinctrl-single,pins = <
	// 	>;
	// };

};


&mcu_i2c0 {

	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";

	tlv320aic3104: tlv320aic3104@18 {
		#sound-dai-cells = <0>;
		compatible = "ti,tlv320aic3104";
		reg = <0x18>;                       // I2C address 0x18, matches @18 of this section.
		status = "okay";

        // Reset pin (active low) is GPIO26 = "gpiochip1 36" to Linux user space
        // From k3-am67a-beagly-ai.dts:
        // &main_gpio0 {
        //     status = "okay";
        //     pinctrl-names = "default";
        //     gpio-line-names = "", "", "", "", "", /* 0-4 */
        //             "", "", "", "", "", /* 5-9 */
        //             "", "", "", "", "", /* 10-14 */
        //             "", "", "", "", "", /* 15-19 */
        //             "", "", "", "", "", /* 20-24 */
        //             "", "", "", "", "", /* 25-29 */
        //             "", "", "USB_RST", "GPIO27", "", /* 30-34 */
        //             "", "GPIO26", "", "GPIO4", "", /* 35-39 */
        //             "", "GPIO22", "GPIO25", "", ""; /* 40-44 */
        // };
        // Question: Could it be &main_gpio1? &mcu_gpio1?
		// -> Likely not. Above comment seems pretty clear.
        // -> Seems correct, because when driver loader:
		// 		$ gpioset gpiochip1 36=1
		// 		gpioset: error setting the GPIO line values: Device or resource busy		
		//    Whereas `reset-gpios = <&mcu_gpio0 36 GPIO_ACTIVE_LOW>;` same gpioset succeeds.
		//	  Also, when driver manually loaded it forces pin to 0, but holds it there.
		// HOWEVER:
		//    Loading the DTS seems not affect the reset line (watched with a DMM)
		//    Before and after driver manually loaded, read X's from i2cdump, so in reset.
		// Seems to have no effect for ACTIVE_LOW or ACTIVE_HIGH; Either way :
		//		During start up pin seems to be ~0.8v (floating? input?)
		//		When driver is loaded, pin gets pulled low
		//	    Seems not to matter if pin is defined using J722S_IOPAD either.
		//
		// Using GPIO_ACTIVE_LOW gpioinfo shows "active high":
		//      line  36:     "GPIO26" "tlv320aic3x reset" output active-high [used]
		reset-gpios = <&main_gpio0 36 GPIO_ACTIVE_LOW>;  // Suspected best one
		// reset-gpios = <&main_gpio0 36 GPIO_ACTIVE_HIGH>;	// Just testing to see if I can get the pin high.

		ai3x-micbias-vg = <2>; // set MICBIAS to 2.5v
		AVDD-supply = <&vdd_3v3>;
		IOVDD-supply = <&vdd_3v3>;
		DRVDD-supply = <&vdd_3v3>;

		// Issue: If `DVDD-supply = <&vdd_3v3>;`, get error: 
		//	[   66.836344] tlv320aic3x 1-0018: Too high supply voltage(s) AVDD: 3300000, DVDD: 3300000
		// DVDD-supply = <&vdd_3v3>;
		DVDD-supply = <&vdd_audio_1v8>;
	};
};
 
&mcasp0 {
	// This section made on template of the mcasp1 from k3-am67a-beagley-ai.dts
	status = "okay";

	#sound-dai-cells = <0>;
	pinctrl-names = "default";
	pinctrl-0 = <&main_mcasp0_pins_default>;
	auxclk-fs-ratio = <2177>;	// No idea! It's in the mcasp1 definition.
	op-mode = <0>;          /* MCASP_IIS_MODE */
	tdm-slots = <2>;

	serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX */
	       2 1 0 0
	       0 0 0 0
	       0 0 0 0
	       0 0 0 0
	>;

	/* Audio FIFO *threshold for transmit and receive */
	// Question: What values are needed here?
	tx-num-evt = <32>;
	rx-num-evt = <32>;
	// tx-num-evt = <1>;
	// rx-num-evt = <1>;
};


Modinfo on drivers

$ modinfo /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/snd-soc-tlv320aic3x.ko
filename:       /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/snd-soc-tlv320aic3x.ko
license:        GPL
author:         Vladimir Barinov
description:    ASoC TLV320AIC3X codec driver
depends:        snd-soc-core,snd-pcm
intree:         Y
name:           snd_soc_tlv320aic3x
vermagic:       6.1.83 SMP preempt mod_unload modversions aarch64
$ modinfo /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/snd-soc-tlv320aic3x-i2c.ko
filename:       /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/snd-soc-tlv320aic3x-i2c.ko
license:        GPL
author:         Arun KS <arunks@mistralsolutions.com>
description:    ASoC TLV320AIC3x codec driver I2C
alias:          of:N*T*Cti,tlv320aic3106C*
alias:          of:N*T*Cti,tlv320aic3106
alias:          of:N*T*Cti,tlv320aic3104C*
alias:          of:N*T*Cti,tlv320aic3104
alias:          of:N*T*Cti,tlv320aic3007C*
alias:          of:N*T*Cti,tlv320aic3007
alias:          of:N*T*Cti,tlv320aic33C*
alias:          of:N*T*Cti,tlv320aic33
alias:          of:N*T*Cti,tlv320aic3xC*
alias:          of:N*T*Cti,tlv320aic3x
alias:          i2c:tlv320aic3106
alias:          i2c:tlv320aic3104
alias:          i2c:tlv320aic3007
alias:          i2c:tlv320aic33
alias:          i2c:tlv320aic3x
depends:        snd-soc-tlv320aic3x
intree:         Y
name:           snd_soc_tlv320aic3x_i2c
vermagic:       6.1.83 SMP preempt mod_unload modversions aarch64
1 Like

I’ve started inserting printk() statements into the simple-card.c kernel driver (and others) to try and track down what might be causing:

$ cat /sys/kernel/debug/devices_deferred
sound1 asoc-simple-card: parse error

Inside simple-card.c’s asoc_simple_probe(...), the card->name is NULL, which later causes devm_snd_soc_register_card() to return a -EINVAL error, which casuses the parse error.

However, I’m not 100% sure this is the right track, because my custom compiled drivers are not automatically being loaded by my kernel; instead I have to use insmod to force them to load. I’m still looking into that.

(If my custom compiled snd-soc-tlv320aic3x.ko and snd-soc-tlv320aic3x-i2c.ko won’t be loaded by the kernel automatically, that might explain the card->name being NULL as well).

Still looking into it! Any ideas welcome!
Brian.

1 Like

I got it working!

  1. After compiling the .ko driver, I compress it like the rest of the modules (not sure if this is needed):
    $ sudo xz --lzma2=dict=2MiB snd-soc-tlv320aic3x.ko
    $ sudo xz --lzma2=dict=2MiB snd-soc-tlv320aic3x-i2c.ko

  2. Copy both of the compressed files into the correct folder on the BYAI:
    $ cd linux/ # Directory of cloned linux tree
    $ sudo cp ./sound/soc/codecs/snd-soc-tlv320aic3x*.ko.xz /lib/modules/6.1.83-ti-arm64-r63/kernel/sound/soc/codecs/

  3. Run depmod to teach modprobe about the new driver:
    $ cd /lib/modules/6.1.83-ti-arm64-r63/
    $ sudo depmod -a

Now when I boot my board, the custom driver is loaded and nothing is deferred:

$ lsmod | grep tlv320
snd_soc_tlv320aic3x_i2c    16384  1
snd_soc_tlv320aic3x    69632  1 snd_soc_tlv320aic3x_i2c
snd_soc_core          217088  8 snd_soc_davinci_mcasp,snd_soc_ti_sdma,snd_soc_ti_edma,snd_soc_hdmi_codec,snd_soc_tlv320aic3x,snd_soc_ti_udma,snd_soc_simple_card_utils,snd_soc_simple_card
snd_pcm               114688  6 snd_soc_davinci_mcasp,snd_soc_hdmi_codec,snd_soc_tlv320aic3x,snd_soc_simple_card_utils,snd_soc_core,snd_pcm_dmaengine

$ cat /sys/kernel/debug/devices_deferred

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Audio [ZenHat Audio], device 0: davinci-mcasp.0-tlv320aic3x-hifi tlv320aic3x-hifi-0 [davinci-mcasp.0-tlv320aic3x-hifi tlv320aic3x-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: HDMI [it66122 HDMI], device 0: davinci-mcasp.0-i2s-hifi i2s-hifi-0 [davinci-mcasp.0-i2s-hifi i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

It will now play sound!

$ sudo apt install espeak
$ espeak 'All your bits are belong to us.' -w test.wav
$ aplay test.wav 

gpioinfo still shows active-high for the reset pin. But it works!

$ gpioinfo | grep -i GPIO26
	line  36:     "GPIO26" "tlv320aic3x reset" output active-high [used]

Brian.

2 Likes

Congrats on getting it working :grinning:

To answer the lingering questions about the reset gpio behaviour:

At boot, the reset line is unconfigured and floating, which you measured as 0.8V. When the device is probed (loaded with insmod), the driver takes ownership of the line, configures it as an output, and pulls it low to initiate a reset:

Now, you would expect the line to go high shortly after to complete the reset cycle and have the device in a ready state, but it is still held low when the probe function exits. This is because the driver implements DAPM, a feature of the Linux audio subsystem that strives to minimize power consumption from audio hardware. In this case, the driver saves power by holding the device in reset (effectively off). It is only when the device is needed for playback that the audio subsystem calls on the driver to power it on (as soon as you start playing audio from the codec, you will see that the reset line goes high).

In 6.1 and earlier kernels, the tlv320aic3x driver ignores whatever flags you set for reset-gpio in the device tree (it only takes the gpio line handle). This is why the reset line always appears with active-high polarity in the gpioinfo output: active-high is the default polarity for a gpio line, and the driver keeps it that way. Essentially, the polarity of the reset line is hard-coded into the driver, because it already knows it needs to deliver the codec an active-low reset.

The hard-coded polarity can be problematic in cases where some logic inversion takes place between the gpio output and codec’s reset input. So, a later patch (effective in 6.2 and newer kernels) added support for configuring reset line polarity from the device tree.

1 Like

Hello Schwil,

Thank you for the follow-up on this and the very helpful info!

Brian.

1 Like