MAX98357a Verdin overlay

Hello,

After enabling audio on our project we decided we don’t want to use NAU8822 audio IC, but MAX98357 IC. After searching online for an MAX overlay I’ve came across this topic
and I’ve created my overlay:

/dts-v1/;
/plugin/;

//#include <imx8mm-pinfunc.h>
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>

/ {
    compatible = "toradex,verdin-imx8mm";

    fragment@0
    {
        target-path = "/";
        __overlay__{
            codec_max: max98357a@0
            {
                compatible = "maxim,max98357a";
                #sound-dai-cells = <0>;
                status = "okay";
            };
        };
    };

    fragment@1
    {
        target = <&nau8822_1a>;
        __overlay__{
            status = "disabled";
        };
    };

    fragment@2
    {
        target = <&sound_card>;
        __overlay__{
            compatible = "simple-audio-card";
            simple-audio-card,bitclock-master = <&dailink_master_cpu>;
            simple-audio-card,format = "i2s";
            simple-audio-card,frame-master = <&dailink_master_cpu>;
            simple-audio-card,name = "max98357a";

            dailink_master: simple-audio-card,codec {
                sound-dai = <&codec_ma>;
                clocks = <&clk IMX8MM_CLK_SAI2_ROOT>;
            };

            dailink_master_cpu: simple-audio-card,cpu {
                sound-dai = <&sai2>;
            };
        };
    };
};

I’ve compiled overlay and max98357 module using torizoncore-builder and copied them to my Verdin. After reboot, new node max98357a is created in /proc/device-tree/, however no max98357 module is applied and after manually inserting module:

cd /lib/modules/*version*/extra/
sudo insmod max98357a.ko

dmesg outputs this:

Can someone point out what am I doing wrong ?

Greetings @spasoye,

I’m not entirely sure if your overlay is correct, but the first issue I noticed is the driver for this audio IC. On TorizonCore the kenrel config option for this driver is not enabled by default:

zcat /proc/config.gz | grep MAX98357
# CONFIG_SND_SOC_MAX98357A is not set

If it was then the driver should have been invoked automatically by you defining this peripheral in the device tree. So ignoring your overlay, this is the first issue.

I can request to the TorizonCore team to enable this kernel config option by default in our kernel. Does that sound okay for you?

And just to be clear I’d request to be enabling the kernel option CONFIG_SND_SOC_MAX98357A. Unless there’s another kernel config option for this IC, but I didn’t see any more appropriate options.

Best Regards,
Jeremias

Hi @spasoye , I used to enable MAX98357A on another SoM Colibri iMX6ULL. Here is the device tree for reference.

codec_ext: max98357a@0 {
    compatible = "maxim,max98357a";
    #sound-dai-cells = <0>;
};


sound {
    compatible = "simple-audio-card";
    status = "okay";
    simple-audio-card,name = "max98357a";

    simple-audio-card,format = "i2s";
    simple-audio-card,bitclock-master = <&dailink_master_cpu>;
    simple-audio-card,frame-master = <&dailink_master_cpu>;

    simple-audio-card,codec {
        sound-dai = <&codec_ext>;
    };

    dailink_master_cpu: simple-audio-card,cpu {
        sound-dai = <&sai2>;
    };

}

&sai2 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_sai2>;

    assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
              <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
    assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
    assigned-clock-rates = <2>, <196608000>;

    fsl,sai-asynchronous;
    /*fsl,sai-mclk-direction-output;*/
    status = "okay";
};

pinctrl_sai2: sai2grp {
        fsl,pins = <
            MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK    0x1F089
            MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC    0x17088
            MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA    0x11088
            /*MX6UL_PAD_JTAG_TMS__SAI2_MCLK        0x17088*/
            MX6UL_PAD_JTAG_TMS__CCM_CLKO1    0x17088
        >;
    };
};


Hey @jeremias.tx, so I suppose that copying max98357.ko to /lib/modules/…/extra won’t fix this issue ?

so I suppose that copying max98357.ko to /lib/modules/…/extra won’t fix this issue ?

You can certainly try this method if you want. If done right it should work, you can see we use that directory ourselves:

ls /lib/modules/5.4.161-5.6.0+git.0f0011824921/extra/
galcore.ko

But then the effort of integrating this driver into the kernel will be on your side going forward.

If we enable the module by default in our kernel then it will just be there going forward. Ultimately the choice is up to you how you want to approach this. Just let me know if you want me to make the request to the team to enable this by default.

Best Regards,
Jeremias

Hello, I was assigned to another project and only got to work on this for the last couple of days.
I’ve found few posts on forum from people that had similar problems.

https://community.toradex.com/t/max98357-on-imx8/
https://community.toradex.com/t/imx7-max98357a-device-tree-settings/

I’ve managed to make some progress with my overlay and I’ve added max98357a module to a new TCB image:

/dts-v1/;
/plugin/;

#include <imx8mm-pinfunc.h>
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>

/ {
    compatible = "toradex,verdin-imx8mm";

    fragment@0
    {
        // MADA FAKA: target-path != target
        target-path = "/";
        __overlay__{
            codec_max: max98357a@0
            {
                compatible = "maxim,max98357a";
                #sound-dai-cells = <0>;
                status = "okay";
            };
        };
    };

    fragment@1
    {
        target = <&nau8822_1a>;
        __overlay__{
            status = "disabled";
        };
    };

    fragment@2
    {
        target = <&sound_card>;
        __overlay__{
            compatible = "simple-audio-card";
            pinctrl-names = "default";
            simple-audio-card,name = "max98357a";
            simple-audio-card,test = "jebenica";
            simple-audio-card,format = "i2s";
            
            simple-audio-card,widgets = "Speaker", "Speakers";
		    simple-audio-card,routing = "Speakers", "Speaker";

            simple-audio-card,bitclock-master = <&dailink_master_cpu>;
            simple-audio-card,frame-master = <&dailink_master_cpu>;

            dailink_master: simple-audio-card,codec {
                sound-dai = <&codec_max>;
                clocks = <&clk IMX8MM_CLK_SAI2>;
            };

            dailink_master_cpu: simple-audio-card,cpu {
                sound-dai = <&sai2>;
            };
        };
    };

    fragment@3
    {
        target = <&pinctrl_sai2>;
        __overlay__{
            fsl,pins = <
                MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC		0x1d6	/* SODIMM 32 */
                MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK		0x1d6	/* SODIMM 30 */
                MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0		0x96	/* SODIMM 34 */
		>;
        };
    };

    fragment@4
    {
        target = <&sai2>;
        __overlay__{
            #saund-dai-cells = <0>;
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_sai2>;
            
            fsl,sai-asynchronous;
            fsl,sai-mclk-direction-output;

            clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";

            assigned-clocks = <&clk IMX8MM_CLK_SAI2>;
            assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
            assigend-clock-rates = <24576000>;
            
            status = "okay";
        };
    };
};

After the boot MAX98357a module and overlay are loaded, speaker-test transmits noise and I can hear it on speakers, but when trying to play .wav file dmesg returns:

aplay rajan.wav

[Wed Jun 29 14:57:09 2022] fsl-sai 30020000.sai: failed to derive required Tx rate: 1411200
[Wed Jun 29 14:57:09 2022] fsl-sai 30020000.sai: ASoC: can't set 30020000.sai hw params: -22

I converted .wav to 48 kHz rate and when I play it, MAX98357a aoutputs some strange noise, similar to noise generated by speaker-test.
I think the problem lays in clock configuration but I dont know what to do about it, can somebody help me ?

I have this amplifier working with my iMX8mp verdin

	codec_ext: max98357a@0 {
		compatible = "maxim,max98357a";
		#sound-dai-cells = <0>;
		sdmode-gpios = <&gpio3 0 GPIO_ACTIVE_HIGH>;
	};

	sound_ext {
		compatible = "simple-audio-card";
		simple-audio-card,name = "max98357a";
		simple-audio-card,format = "i2s";
		simple-audio-card,bitclock-master = <&dailink_master1>;
		simple-audio-card,frame-master = <&dailink_master1>;


		dailink_master1: simple-audio-card,cpu {
			sound-dai = <&sai1>;
		    dai-tdm-slot-num = <2>;
		    dai-tdm-slot-width = <16>;
		};

		simple-audio-card,codec {
			sound-dai = <&codec_ext>;
		};


	};

and down further I have this for SAI1

/* VERDIN I2S_1 */
&sai1 {
	#sound-dai-cells = <0>;
	assigned-clocks = <&clk IMX8MP_CLK_SAI1>;
	assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL2_OUT>;
	assigned-clock-rates = <11289600>;												/*works for 44.1kHz sample rate */
	clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_SAI1_IPG>, <&clk IMX8MP_CLK_DUMMY>,
		 <&audiomix_clk IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1>, <&clk IMX8MP_CLK_DUMMY>,
		 <&clk IMX8MP_CLK_DUMMY>;
	clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";
	fsl,sai-mclk-direction-output;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sai1>;
};

I had to make one other important change to this file sound/soc/fsl/fsl_sai.c

comment out this clock speed check around line 555

		/*
		 * Drop the source that can not be
		 * divided into the required rate.
		 */
		//if (ret != 0 && clk_rate / ret < 1000)
			//continue;

Thank you for sharing your solution @eoin_oc1.

@spasoye Could you try what was suggested and see if it works for your system as well. Since the 8m-mini and 8m-plus are quite similiar I imagine the solutions will be as well.

Best Regards,
Jeremias

Hey @jeremias.tx, @eoin_oc1 the problem was in my module. Yesterday I tried to change the module I was using with another (same module) and it seems like my module was faulty.

I used the same overlay and successfully played 48 kHz converted .wav file, but when playing 44.1 kHz file dmesg returned the same Tx rate derivation fail message and aplay returned error.
I suppose that this can be solved by modifying fsl_sai.c? Is there a way to modify this file using tcb builder tool ?

max98357_overlay.dts (2.4 KB)

The final driver looks at how it can derive the bit clock rate based on the clock speed the I2S peripheral has and the bit rate of the file, if after choosing the standard dividers available in the I2S peripheral that it cannot get close enough to the files bit rate, it will fail.

11289600 = 44100 * 256

I think the peripheral can only do 2^x divides on the incoming clock rate

By commenting out the part I did you ignore this check, but of course your sound will be a bit off spectraly

I suppose that this can be solved by modifying fsl_sai.c? Is there a way to modify this file using tcb builder tool ?

Since the patch here is modifying an existing file in Linux kernel source, the answer is no. Tcb is not for patching and re-compiling the kernel. For such things you’ll probably need to use Yocto if you want to patch the kernel and produce an image with a patched kernel.

Best Regards,
Jeremias