This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

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

  • I spend some time familiarizing myself with the audio driver and device trees in the Linux source code (there is a special version for Raspberry!) I've "kinda" found how the driver talks to the device tree. But not sure. I'll post the relevant section here.

    Meanwhile, here's my schematics - in case you ca see something wrong I missed (very very possible)

  • This is the section in the source code that I'm trying to understand:

    static const struct snd_soc_dapm_route
    common31xx_audio_map[] = {
    	/* DAC Input Routing */
    	{"DAC Left Input", "Left Data", "AIF IN"},
    	{"DAC Left Input", "Right Data", "AIF IN"},
    	{"DAC Left Input", "Mono", "AIF IN"},
    	{"DAC Right Input", "Left Data", "AIF IN"},
    	{"DAC Right Input", "Right Data", "AIF IN"},
    	{"DAC Right Input", "Mono", "AIF IN"},
    	{"DAC Left", NULL, "DAC Left Input"},
    	{"DAC Right", NULL, "DAC Right Input"},
    
    	/* HPL path */
    	{"HP Left", "Switch", "Output Left"},
    	{"HPL Driver", NULL, "HP Left"},
    	{"HPL", NULL, "HPL Driver"},
    
    	/* HPR path */
    	{"HP Right", "Switch", "Output Right"},
    	{"HPR Driver", NULL, "HP Right"},
    	{"HPR", NULL, "HPR Driver"},
    };
    
    static const struct snd_soc_dapm_route
    dac31xx_audio_map[] = {
    	/* Left Output */
    	{"Output Left", "From Left DAC", "DAC Left"},
    	{"Output Left", "From AIN1", "AIN1"},
    	{"Output Left", "From AIN2", "AIN2"},
    
    	/* Right Output */
    	{"Output Right", "From Right DAC", "DAC Right"},
    	{"Output Right", "From AIN2", "AIN2"},
    };
    
    static const struct snd_soc_dapm_route
    aic31xx_audio_map[] = {
    	/* Mic input */
    	{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
    	{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
    	{"MIC1LP P-Terminal", "FFR 40 Ohm", "MIC1LP"},
    	{"MIC1RP P-Terminal", "FFR 10 Ohm", "MIC1RP"},
    	{"MIC1RP P-Terminal", "FFR 20 Ohm", "MIC1RP"},
    	{"MIC1RP P-Terminal", "FFR 40 Ohm", "MIC1RP"},
    	{"MIC1LM P-Terminal", "FFR 10 Ohm", "MIC1LM"},
    	{"MIC1LM P-Terminal", "FFR 20 Ohm", "MIC1LM"},
    	{"MIC1LM P-Terminal", "FFR 40 Ohm", "MIC1LM"},
    
    	{"MIC1LM M-Terminal", "FFR 10 Ohm", "MIC1LM"},
    	{"MIC1LM M-Terminal", "FFR 20 Ohm", "MIC1LM"},
    	{"MIC1LM M-Terminal", "FFR 40 Ohm", "MIC1LM"},
    
    	{"MIC_GAIN_CTL", NULL, "MIC1LP P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1RP P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1LM P-Terminal"},
    	{"MIC_GAIN_CTL", NULL, "MIC1LM M-Terminal"},
    
    	{"ADC", NULL, "MIC_GAIN_CTL"},
    
    	{"AIF OUT", NULL, "ADC"},
    
    	/* Left Output */
    	{"Output Left", "From Left DAC", "DAC Left"},
    	{"Output Left", "From MIC1LP", "MIC1LP"},
    	{"Output Left", "From MIC1RP", "MIC1RP"},
    
    	/* Right Output */
    	{"Output Right", "From Right DAC", "DAC Right"},
    	{"Output Right", "From MIC1RP", "MIC1RP"},
    };
    
    static const struct snd_soc_dapm_route
    aic311x_audio_map[] = {
    	/* SP L path */
    	{"Speaker Left", "Switch", "Output Left"},
    	{"SPL ClassD", NULL, "Speaker Left"},
    	{"SPL", NULL, "SPL ClassD"},
    
    	/* SP R path */
    	{"Speaker Right", "Switch", "Output Right"},
    	{"SPR ClassD", NULL, "Speaker Right"},
    	{"SPR", NULL, "SPR ClassD"},
    };
    
    static const struct snd_soc_dapm_route
    aic310x_audio_map[] = {
    	/* SP L path */
    	{"Speaker", "Switch", "Output Left"},
    	{"SPK ClassD", NULL, "Speaker"},
    	{"SPK", NULL, "SPK ClassD"},
    };
    
    /*
     * Always connected DAPM routes for codec clock master modes.
     * If the codec is the master on the I2S bus, we need to power up components
     * to have valid DAC_CLK.
     *
     * In order to have the I2S clocks on the bus either the DACs/ADC need to be
     * enabled, or the P0/R29/D2 (Keep bclk/wclk in power down) need to be set.
     *
     * Otherwise the codec will not generate clocks on the bus.
     */
    static const struct snd_soc_dapm_route
    common31xx_cm_audio_map[] = {
    	{"HPL", NULL, "AIF IN"},
    	{"HPR", NULL, "AIF IN"},
    
    	{"AIF IN", NULL, "Activate I2S clocks"},
    };
    
    static const struct snd_soc_dapm_route
    aic31xx_cm_audio_map[] = {
    	{"AIF OUT", NULL, "MIC1LP"},
    	{"AIF OUT", NULL, "MIC1RP"},
    	{"AIF OUT", NULL, "MIC1LM"},
    
    	{"AIF OUT", NULL, "Activate I2S clocks"},
    };

  • Hi Jaques,

    I believe dapm_route is how the driver makes connections to the device, where the outputs correspond to "sinks" and inputs as "sources" in Linux dts files, and they are mapped to the corresponding i2c commands in the .h file corresponding to the driver file. I'm not sure why there are 3 paths for one speaker but I think you do need to connect "Output Left" to "Speaker Left", then "Speaker Left" to "SPL ClassD", then "SPL ClassD" to "SPL", and similar for SPR. Maybe adding in one or all of these connections in your dts file will help.

    Best,
    Mir

  • This is from the example EVM and corresponding discussion on this forum: https://forum.beagleboard.org/t/custom-beaglebone-black-audio-cape-with-tlv320aic3110/21319/2

    ti,audio-routing =
    /* Source Sink /
    / output in driver*/
    /* Speaker connections /
    “Headphone Jack”, “HPL”,
    “Headphone Jack”, “HPR”,
    “Speaker”, “SPL”,
    “Speaker”, “SPR”,

  • Thanks. Mir
    Already had those.

    I *JUST NOW* noticed something about pin 11 on the codec. It's dual function
    VOL/MICDET 11 I Volume control or microphone detection

    If it's volume control, then maybe I shouldn't ground it. I'll try to read the PDF more to see what this means. But has anyone wired the pin differently? How do I make it volume control on the RPi? Is it just for headphones?

    (will also try to add pull-up resistors for I2C signals)

  • I actually read more about i2cdetect, i2cdump, i2cget and i2cset. Turns out, once a device driver is loaded, it takes control of the i2c address and you can't really interact with it anymore. Unless you use the dangerous i2c<cmd> -f xxxx to force it.

    I used this and I get:

    i2cdump -y -f 1 0x18
    No size specified (using byte-data access)
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
    00: 00 00 01 56 03 91 04 00 00 00 00 81 81 00 80 80    ..?V???....??.??
    10: 08 00 81 81 80 80 04 00 00 00 01 0c 00 00 81 00    ?.?????...??..?.
    20: 00 00 00 00 80 11 00 00 00 00 00 00 00 00 00 00    ....??..........
    30: 00 00 00 02 32 12 03 02 02 11 10 00 01 04 00 3d    ...?2??????.??.=
    40: 0c 1c 1c 00 6f 38 00 00 00 00 00 ee 10 d8 7e e3    ???.o8.....???~?
    50: 00 00 a0 0e 00 00 00 00 7f 00 00 00 00 00 00 00    ..??....?.......
    60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    70: 00 00 00 00 00 44 00 00 00 00 00 00 00 00 00 00    .....D..........
    80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................


    So now all I have to do is figure out the correct register setting and force them by hand to see if that solves my issue.

    I know I have to fully understand the datasheet now Flushed If anybody know the correct settings for amplifier / speaker output, I'm all ears

  • Hi Jacques,

    Just FYI, Mir is out of office for an extended period. I'll be responding to you through mail, but the thread will stay up in case anybody else has something to add.

    Best regards,
    Jeff McPherson

  • Hi Jeff,

    I was reading the source code for the driver (really trying!) and I noticed this in the frequency divs:


    /* ADC dividers can be disabled by configuring them to 0 */
    static const struct aic31xx_rate_divs aic31xx_divs[] = {
    	/* mclk/p    rate  pll: r  j     d     dosr ndac mdac  aors nadc madc */
    	/* 8k rate */
    	{  512000,   8000,	4, 48,   0,	128,  48,  2,   128,  48,  2},
    	{12000000,   8000,	1, 8, 1920,	128,  48,  2,	128,  48,  2},
    	{12000000,   8000,	1, 8, 1920,	128,  32,  3,	128,  32,  3},
    	{12500000,   8000,	1, 7, 8643,	128,  48,  2,	128,  48,  2},
    	/* 11.025k rate */
    	{  705600,  11025,	3, 48,   0,	128,  24,  3,	128,  24,  3},
    	{12000000,  11025,	1, 7, 5264,	128,  32,  2,	128,  32,  2},
    	{12000000,  11025,	1, 8, 4672,	128,  24,  3,	128,  24,  3},
    	{12500000,  11025,	1, 7, 2253,	128,  32,  2,	128,  32,  2},
    	/* 16k rate */
    	{  512000,  16000,	4, 48,   0,	128,  16,  3,	128,  16,  3},
    	{ 1024000,  16000,	2, 48,   0,	128,  16,  3,	128,  16,  3},
    	{12000000,  16000,	1, 8, 1920,	128,  24,  2,	128,  24,  2},
    	{12000000,  16000,	1, 8, 1920,	128,  16,  3,	128,  16,  3},
    	{12500000,  16000,	1, 7, 8643,	128,  24,  2,	128,  24,  2},
    	/* 22.05k rate */
    	{  705600,  22050,	4, 36,   0,	128,  12,  3,	128,  12,  3},
    	{ 1411200,  22050,	2, 36,   0,	128,  12,  3,	128,  12,  3},
    	{12000000,  22050,	1, 7, 5264,	128,  16,  2,	128,  16,  2},
    	{12000000,  22050,	1, 8, 4672,	128,  12,  3,	128,  12,  3},
    	{12500000,  22050,	1, 7, 2253,	128,  16,  2,	128,  16,  2},
    	/* 32k rate */
    	{ 1024000,  32000,      2, 48,   0,	128,  12,  2,	128,  12,  2},
    	{ 2048000,  32000,      1, 48,   0,	128,  12,  2,	128,  12,  2},
    	{12000000,  32000,	1, 8, 1920,	128,  12,  2,	128,  12,  2},
    	{12000000,  32000,	1, 8, 1920,	128,   8,  3,	128,   8,  3},
    	{12500000,  32000,	1, 7, 8643,	128,  12,  2,	128,  12,  2},
    	/* 44.1k rate */
    	{ 1411200,  44100,	2, 32,   0,	128,   8,  2,	128,   8,  2},
    	{ 2822400,  44100,	1, 32,   0,	128,   8,  2,	128,   8,  2},
    	{12000000,  44100,	1, 7, 5264,	128,   8,  2,	128,   8,  2},
    	{12000000,  44100,	1, 8, 4672,	128,   6,  3,	128,   6,  3},
    	{12500000,  44100,	1, 7, 2253,	128,   8,  2,	128,   8,  2},
    	/* 48k rate */
    	{ 1536000,  48000,	2, 32,   0,	128,   8,  2,	128,   8,  2},
    	{ 3072000,  48000,	1, 32,   0,	128,   8,  2,	128,   8,  2},
    	{12000000,  48000,	1, 8, 1920,	128,   8,  2,	128,   8,  2},
    	{12000000,  48000,	1, 7, 6800,	 96,   5,  4,	 96,   5,  4},
    	{12500000,  48000,	1, 7, 8643,	128,   8,  2,	128,   8,  2},
    	/* 88.2k rate */
    	{ 2822400,  88200,	2, 16,   0,	 64,   8,  2,	 64,   8,  2},
    	{ 5644800,  88200,	1, 16,   0,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  88200,	1, 7, 5264,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  88200,	1, 8, 4672,	 64,   6,  3,	 64,   6,  3},
    	{12500000,  88200,	1, 7, 2253,	 64,   8,  2,	 64,   8,  2},
    	/* 96k rate */
    	{ 3072000,  96000,	2, 16,   0,	 64,   8,  2,	 64,   8,  2},
    	{ 6144000,  96000,	1, 16,   0,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  96000,	1, 8, 1920,	 64,   8,  2,	 64,   8,  2},
    	{12000000,  96000,	1, 7, 6800,	 48,   5,  4,	 48,   5,  4},
    	{12500000,  96000,	1, 7, 8643,	 64,   8,  2,	 64,   8,  2},
    	/* 176.4k rate */
    	{ 5644800, 176400,	2, 8,    0,	 32,   8,  2,	 32,   8,  2},
    	{11289600, 176400,	1, 8,    0,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 176400,	1, 7, 5264,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 176400,	1, 8, 4672,	 32,   6,  3,	 32,   6,  3},
    	{12500000, 176400,	1, 7, 2253,	 32,   8,  2,	 32,   8,  2},
    	/* 192k rate */
    	{ 6144000, 192000,	2, 8,	 0,	 32,   8,  2,	 32,   8,  2},
    	{12288000, 192000,	1, 8,	 0,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 192000,	1, 8, 1920,	 32,   8,  2,	 32,   8,  2},
    	{12000000, 192000,	1, 7, 6800,	 24,   5,  4,	 24,   5,  4},
    	{12500000, 192000,	1, 7, 8643,	 32,   8,  2,	 32,   8,  2},
    };

    I noticed that my exact oscillator frequency (12.288Mhz) *12288000* is not present. In fact, the next one I was going to try (11.2896Mhz) is not present either. Now these frequencies, when divided by 256, would give exactly 48Khz and 41.1Khz.

    So either when I don't need the PLL, I can provide those frequencies, or really 12Mhz exact is what the driver expects.

    === in addition ===

    This section of the code:

    	if (match == -1) {
    		dev_err(component->dev,
    			"%s: Sample rate (%u) and format not supported\n",
    			__func__, params_rate(params));
    		/* See below for details on how to fix this. */
    		return -EINVAL;
    	}
    	if (bclk_score != 0) {
    		dev_warn(component->dev, "Can not produce exact bitclock");
    		/* This is fine if using dsp format, but if using i2s
    		   there may be trouble. To fix the issue edit the
    		   aic31xx_divs table for your mclk and sample
    		   rate. Details can be found from:
    		   https://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf
    		   Section: 5.6 CLOCK Generation and PLL
    		*/
    	}

    seems relevant, as I an getting this error now in dmesg

    tlv320aic31xx-codec 1-0018: aic31xx_setup_pll: Sample rate (44100) and format not supported


    Any thoughts?

  • After carefully reading the source code, I finally found the culprit of my issues. Even if the datasheet suggest I can bypass the PLL if use a proper frequency for MCLK (I chose 12.288Mhz and 11.2896Mhz which divided by 256 gives 48Khz and 41.1Khz), the device driver does not support any arbitrary frequency! They have a bunch of lookup tables for clock rates and playback and if your choice is not present, the driver will fail.

    I modified the source code and added the proper lookups for my custom frequencies

    /* ADC dividers can be disabled by configuring them to 0 */
    static const struct aic31xx_rate_divs aic31xx_divs[] = {
        ...
        /* 11.2896 MHz (11289600 Hz) */
        { 11289600, 44100,  1, 7, 6800, 128,   8,  2,   128,   8,  2},
        /* 12.288 MHz (12288000 Hz) */
        { 12288000, 44100,  1, 7, 5264, 128,   8,  2,   128,   8,  2},
        	/* 11.2896 MHz (11289600 Hz) */
        { 11289600, 48000,  1, 7, 6800, 128,   8,  2,   128,   8,  2},
        /* 12.288 MHz (12288000 Hz) */
        { 12288000, 48000,  1, 8, 1920, 128,   8,  2,   128,   8,  2},
        ...
    };

    After a fun fight with Ubuntu to compile this, lo and behold, everything plays!

    Now for my project which involves an Iot device, I don't want to have to include an unsigned device driver that will need DKMS to install with every kernel update, I will redo my PCB hardware oscillator to use a natively supported frequency. I leave as an exercise to the people at TI to write a function that dynamically creates a lookup entry for a random frequency MCLK and playback combo!

    I'll still place my device tree file here for further reference for anybody else looking for similar info. Please note that the microphone input is not tested (I just got things to work!) and will open another question if I have issues!

    Thanks for all the support.

    /dts-v1/;
    /plugin/;
    
    / {
        compatible = "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835";
        // compatible = "brcm,bcm2835";
    
        fragment@0 {
            target = <&i2s_clk_consumer>;
            __overlay__ {
                status = "okay";
                #sound-dai-cells = <0>;
                brcm,tx-channels = <2>;
                brcm,rx-channels = <1>;
            };
        };
    
        fragment@1 {
            target-path = "/";
            __overlay__ {
                status = "okay";
    
                codec_1v8_reg: codec-1v8-reg {
                    compatible = "regulator-fixed";
                    regulator-name = "tlv320aic3104_1v8";
                    regulator-min-microvolt = <1800000>;
                    regulator-max-microvolt = <1800000>;
                    regulator-always-on;
                    status = "okay";
                };
            };
        };
     
        fragment@2 {
            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-pg,tlv320aic3110", "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;
    
                    reset-gpios = <&gpio 13 1>; // GPIO 13 as active low reset
                    reset-delay-us = <10000>;     /* 10ms delay */
    
                    // Debug properties (must precede subnodes)
                    debug;
                    linux,debug;
    
                };
            };
        };
    
        fragment@3 {
            target = <&sound>;
            __overlay__ {
                compatible = "simple-audio-card";
                i2s-controller = <&i2s_clk_consumer>;
                simple-audio-card,name = "TLV320AIC3110";
                simple-audio-card,format = "i2s";
                simple-audio-card,convert-rate = <48000>;
                simple-audio-card,mclk-fs = <256>;
    
                simple-audio-card,bitclock-master = <&snd_codec>;
                simple-audio-card,frame-master = <&snd_codec>;
    
                simple-audio-card,widgets =
                    "Microphone", "Mic Jack",
                    "Speaker", "External Speaker";
                simple-audio-card,routing =
                    "External Speaker", "SPL",
                    "External Speaker", "SPR",
                    "MIC1LP", "Mic Jack",
                    "MIC1LM", "Mic Jack";
    
                status = "okay";
    
                dailink0_master: simple-audio-card,cpu {
                    sound-dai = <&i2s_clk_consumer>;
                    dai-tdm-slot-num = <2>;
                    dai-tdm-slot-width = <32>;
                };
    
                snd_codec: simple-audio-card,codec {
                    sound-dai = <&tlv320aic3110>;
                    clocks = <&mclk_external>;
                    system-clock-direction-out = "out";
                    system-clock-frequency = <12288000>; //*** added LAX
                    mclk-fs = <256>; //*** added LAX
                };
            };
        };
    
        fragment@4 {
            target-path = "/";
            __overlay__ {
                mclk_external: mclk_external {
                    compatible = "fixed-clock";
                    #clock-cells = <0>;
                    clock-frequency = <12288000>; // 12.288 MHz
                    clock-output-names = "mclk";
                };
            };
        };
    };