TLV320AIC3110: For the life of me I can't make Audio chip DTBO loaded or ALSA working in Raspberry Pi 5

Part Number: TLV320AIC3110

Tool/software:

I have been trying for the last month to get a HAT I've designed that contains a TI TLV320AIC3110 audio code chip to be recognized at boot or even by using modprobe to load the driver. I can't get ALSA to see the chip. This is on a RPi5

I have really tried to search online for solutions, but nothing seem to work.

I am using UBUNTU 24.04 Server, as I need ubuntu-frame and wpe-mir-kiosk for my dedicated IoT with no HDMI connections. This setup doesn't offer vcdbg and I can't seem to get ANY debug info.

I will post my .dts file here if anybody can point me to what's wrong.


/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2712"; 
    /* "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835"; */

    /* 
     * Fragment@0: Enable the I2S (PCM) interface 
     */
    fragment@0 {
        target = <&i2s>;
        __overlay__ {
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <&tlv320aic3110_pins>;
            
            #sound-dai-cells = <0>;
            brcm,enable-mclk = <1>;           // Enable MCLK output
            brcm,mclk-rate = <12288000>;      // 12.288MHz MCLK rate
            clock-frequency = <12288000>;      // Master clock frequency
            brcm,tx-channels = <2>;           // Stereo output
            brcm,rx-channels = <2>;           // Stereo input
        };
    };

    fragment@1 {
            target-path = "/";
            __overlay__ {
                    codec_1v8_reg: codec-1v8-reg {
                        compatible = "regulator-fixed";
                        regulator-name = "tlv320aic3104_1v8";
                        regulator-min-microvolt = <1800000>;
                        regulator-max-microvolt = <1800000>;
                        regulator-always-on;
                    };
            };
    };

    fragment@2 {
            target = <&gpio>;
            __overlay__ {
                    codec_rst: codec-rst {
                            brcm,pins = <13>;
                            brcm,function = <0>;
                    };
            };
    };

    fragment@3 {
        target = <&i2c1>;
        __overlay__ {
            #address-cells = <1>; /* Single cell for I2C address */
            #size-cells = <0>;    /* No size cells for I2C devices */
            status = "okay";

            tlv320aic3110: tlv320aic3110@18 {
                compatible = "ti,tlv320aic3110", "ti,tlv320aic311x", "ti,tlv320aic3x";
                reg = <0x18>;
                #sound-dai-cells = <0>;
                status = "okay";
                /* DRVDD-supply = <&vdd_3v3_reg>; */
                AVDD-supply = <&vdd_3v3_reg>;
                DVDD-supply = <&codec_1v8_reg>;
                HPVDD-supply = <&vdd_3v3_reg>;
                IOVDD-supply = <&vdd_3v3_reg>;
                SPRVDD-supply = <&vdd_5v0_reg>;
                SPLVDD-supply = <&vdd_5v0_reg>;
                clocks = <&mclk_external>;
                mclk-frequency = <12288000>;
                gpio-controller;
                resets = <&gpio_reset>; /* active-high GPIO4_13 */
                reset-gpios = <&gpio 13 0>; /* GPIO 13 as active high */

                // Debug properties
                debug;
                linux,debug;
            };
        };
    };

    fragment@4 {
        target-path = "/";
        __overlay__ {
            gpio_reset: gpio-reset {
                compatible = "gpio-reset";
                reset-gpios = <&gpio 13 0>; /* GPIO 13 active high */
                #reset-cells = <0>;
                reset-delay-us = <10000>;
                status = "okay";
            };
        };
    };

    fragment@5 {
        target = <&sound>;
        __overlay__ {
            compatible = "simple-audio-card";
            simple-audio-card,name = "TLV320AIC3110";
            simple-audio-card,widgets =
                "Microphone", "Mic Jack",
                "Speaker", "Speaker Left",
                "Speaker", "Speaker Right";
            simple-audio-card,routing =
                "Speaker Left", "SPL",
                "Speaker Right", "SPR",
                "MIC1LP", "Mic Jack",
                "MIC1LM", "Mic Jack";
            status = "okay";

            simple-audio-card,dai-link {
                /* I2S format settings */
                format = "i2s";
                bitclock-master = <&codec_dai>;
                frame-master = <&codec_dai>;
                
                /* DAI link format */
                dai-tdm-slot-num = <2>;      // Stereo
                dai-tdm-slot-width = <32>;   // 32 bits per sample

                cpu_dai: cpu {
                    sound-dai = <&bcm2835_i2s>;
                    dai-tdm-slot-num = <2>;
                    dai-tdm-slot-width = <32>;
                };

                codec_dai: codec {
                    sound-dai = <&tlv320aic3110>;
                    
                    /* Clock settings */
                    system-clock-frequency = <12288000>;  // 12.288MHz MCLK
                    system-clock-direction-out;           // MCLK is input to codec
                    
                    /* Format settings */
                    frame-inversion;                      // Frame sync polarity
                    bitclock-inversion;                   // Clock polarity
                };
            };
        };
    };

    fragment@6 {
        target-path = "/";
        __overlay__ {
            mclk_external: mclk_external {
                compatible = "fixed-clock";
                #clock-cells = <0>;
                clock-frequency = <12288000>; // 12.288 MHz
            };
        };
    };

    fragment@7 {
        target = <&gpio>;
        __overlay__ {
            tlv320aic3110_pins: tlv320aic3110_pins {
                brcm,pins = <18 19 20 21>;     // PCM_CLK, PCM_FS, PCM_DIN, PCM_DOUT
                brcm,function = <4>;           // ALT0 for I2S function
                brcm,pull = <0>;               // No pull-up/down
            };
        };
    };

};

Any help would be appreciated. I can supply the schematics of the PCB also

  • Just to add that I can see the device with i2cdetect -y 1 as 0x18 and can send commands to it

  • I finally succeeded at getting the device-tree overlay loaded and the device driver works with ALSA.

    I can't however figure out the exact commands to have:

    1. The output directed to the amplifier and speakers
    2. Control the volume of the speakers
    3. Hear any output.

    I have a 48Khz file that should play though ALSA and pulseaudio

    Here, for reference, is the best working example I ahve to the device-tree overlay file (.dts) that uses the external oscillator, the 1.8V digital and the correct reset:

    /dts-v1/;
    /plugin/;
    
    / {
        compatible = "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835";
    
        fragment@0 {
            target = <&i2s>;  // For RPi4
            __overlay__ {
                status = "okay";
                #sound-dai-cells = <0>;
                brcm,tx-channels = <2>;
                brcm,rx-channels = <2>;
            };
        };
    
        /* 
         * Fragment@1: Enable the I2S (PCM) interface 
         * Additional for RPi5
         */
        /*
        fragment@1 {
            target = <&i2s_clk_consumer>;
            __overlay__ {
                status = "okay";
                
                #sound-dai-cells = <0>;
                compatible = "snps,designware-i2s";
                clock-names = "i2sclk";
                tx-channels = <2>;
                rx-channels = <2>;
                
                // brcm,enable-mclk = <1>;           // Enable MCLK output
                // brcm,mclk-rate = <12288000>;      // 12.288MHz MCLK rate
                // clock-frequency = <12288000>;      // Master clock frequency
                // brcm,tx-channels = <2>;           // Stereo output
                // brcm,rx-channels = <2>;           // Stereo input
                // brcm,slave = <1>;                 // I2S controller as slave
                
            };
        };
         */
    
        fragment@2 {
                target-path = "/";
                __overlay__ {
                        codec_1v8_reg: codec-1v8-reg {
                            compatible = "regulator-fixed";
                            regulator-name = "tlv320aic3104_1v8";
                            regulator-min-microvolt = <1800000>;
                            regulator-max-microvolt = <1800000>;
                            regulator-always-on;
                        };
                };
        };
     
        fragment@3 {
            target = <&i2c1>;
            __overlay__ {
                #address-cells = <1>; /* Single cell for I2C address */
                #size-cells = <0>;    /* No size cells for I2C devices */
                status = "okay";
    
                tlv320aic3110: tlv320aic3110@18 {
                    compatible = "ti,tlv320aic3110", "ti,tlv320aic311x"; 
                    reg = <0x18>;
                    #sound-dai-cells = <0>;
                    // system-clock-frequency = <12288000>;  // Codec clock config
                    // system-clock-direction-out;           // Codec as master
                    status = "okay";
    
                    HPVDD-supply = <&vdd_3v3_reg>;
                    SPRVDD-supply = <&vdd_5v0_reg>;
                    SPLVDD-supply = <&vdd_5v0_reg>;
                    AVDD-supply = <&vdd_3v3_reg>;
                    IOVDD-supply = <&vdd_3v3_reg>;
                    DVDD-supply = <&codec_1v8_reg>;
    
                    clocks = <&mclk_external>;
                    clock-names = "mclk";
                    // system-clk-frequency = <12288000>;
                    mclk-frequency = <12288000>;
                    // gpio-controller;
    
                    /* PLL configuration for 48kHz with 12.288MHz MCLK */
                    pll-p = <1>;
                    pll-r = <1>;
                    pll-j = <7>;
                    pll-d = <1680>;
    
                    reset-gpios = <&gpio 13 0>; // GPIO 13 as active high 
                    reset-delay-us = <10000>;     /* 10ms delay */
    
                    // Debug properties
                    debug;
                    linux,debug;
                };
            };
        };
        /*
        fragment@4 {
            target-path = "/";
            __overlay__ {
                gpio_reset: gpio-reset {
                    compatible = "gpio-reset";
                    reset-gpios = <&gpio 13 0>; // GPIO 13 active high 
                    #reset-cells = <0>;
                    reset-delay-us = <10000>;
                    status = "okay";
                };
            };
        };
         */
    
        fragment@5 {
            target = <&sound>;
            __overlay__ {
                compatible = "simple-audio-card";
                simple-audio-card,name = "TLV320AIC3110";
                simple-audio-card,format = "i2s";
                // simple-audio-card,bitclock-master = <&dailink0_master>;
                // simple-audio-card,frame-master = <&dailink0_master>;
    
                simple-audio-card,widgets =
                    "Microphone", "Mic Jack",
                    "Speaker", "Speaker Left",
                    "Speaker", "Speaker Right";
                simple-audio-card,routing =
                    "Speaker Left", "SPL",
                    "Speaker Right", "SPR",
                    "MIC1LP", "Mic Jack",
                    "MIC1LM", "Mic Jack";
                status = "okay";
    
                simple-audio-card,cpu {
                    sound-dai = <&i2s>;
                };
    
                dailink0_master: simple-audio-card,codec {
                    sound-dai = <&tlv320aic3110>;
                    clocks = <&mclk_external>;
                };
    
            };
        };
    
        fragment@6 {
            target-path = "/";
            __overlay__ {
                mclk_external: mclk_external {
                    compatible = "fixed-clock";
                    #clock-cells = <0>;
                    clock-frequency = <12288000>; // 12.288 MHz
                    clock-output-names = "mclk";
                };
            };
        };
    
    };
    

    Hope this helps others and someone can help me get the PLL and amplifier working

  • Hi,

    Thanks for attaching your dts file. To get your audio routed to an output, you can use the wording specified in the "snd_soc_dapm_route" structures in the driver for this device (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/codecs/tlv320aic31xx.c). 

    In your dts file, I seeyou have a widgets/routing section, but you need to update these with the driver-specific language. Check out the "common31xx_audio_map" section of the driver code for a starting point there. 

    What are your current commands to play audio through the codec? What happens when you run alsamixer?

    Best,
    Mir

  • Hi Mir,

    I am a strong believer in RTFM also! I was looking at the simple-audio and tlv320aic31xx driver documentation. Lacking experience in device tree, can't figure out the proper syntax or how the device driver interacts with the device tree overlay.

    The source code is rather large and I appreciate you sending me to the appropriate section. I still can't figure out what the exact syntax is to enable D-Class amplifier and send output to it. If you have an example *anywhere* I would appreciate it.

    As to alsamixer, it seems to know much better what are installed and gives me a whole bunch of controls. There's no way to have the right/left speakers get output.

    I have tried 

    aplay /web/config/sounds/news-ting-48k.wav
    speaker-test -t sine -f 800

    I had added a 12.288Mhz Oscillator as MCLK (for 48Khz playback) which turns out to be stupid, as all my sounds are 44.1Khz. I will change to 11.2896Mhz as soon as a validate this design and get some sound out from the amplifiers/speakers.
    BTW, this required me to create /etc/asound.conf (which could go elsewehere as in /etc/alsa/conf.d)

    pcm.!default {
        type plug
        slave {
            pcm "hw:TLV320AIC3110,0"
            rate 48000
        }
    }
    
    ctl.!default {
        type hw
        card "TLV320AIC3110"
    }

    Again, thanks for your help and open to any sample

  • Hi Jacques,

    What does the amixer show its controls are? I found this old e2e thread where they had output from the class D speaker for the AIC3100 with the same driver: https://e2e.ti.com/support/audio-group/audio/f/audio-forum/1209106/tlv320aic3100-class-d-speaker-wave-and-sound-quality

    You may need to set the speaker to "on" as shown in their post. Sorry I am not an expert in writing dts files or Linux audio systems, just here to help nudge our debugging in the right direction. Do you still have questions about the PLL as well? Also, are you sure you are powering the SPLVDD and SPRVDD power supplies? Can you send your schematic?

    Best,
    Mir