I2S on GPIO pins?

I figured this but I couldn’t seem to figure out where to put my own compiled overlay files. Maybe just in /boot/overlays? This directory doesn’t exist.

I’d be more than happy to submit/propose wiki edits about this.

This is /boot/config.txt:

# Execute "update_extlinux.sh" after changing file /boot/config.txt.

#
# kernel command line: started with "cmdline:"
#

# For console
cmdline: earlyprintk console=ttyFIQ0,1500000n8 console=tty1 consoleblank=0 loglevel=7

# For rootfs
cmdline: panic=10 rootwait rw init=/sbin/init rootfstype=ext4

# Docker optimizations
cmdline: cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1

cmdline: switolb=1
cmdline: coherent_pool=1m


#
# dt overlay line: started with "dtoverlay=". One dt overlay one line.
#

dtoverlay=rk3568-fiq-debugger-uart2m0
dtoverlay=rk3568-disable-npu

Are you sure you grabbed the right image? That looks like a boot script for a Rockchip-based board. I wouldn’t even expect the Radxa Zero to be able to boot that.

Sorry that’s from the cm3 image but I had the same exact issue with the radxa zero images.

It’s so frustrating…as if the device tree isn’t complicated enough to learn. Just not even being able to use it. Aye… I’m wasting so much time on what should be the trivial part haha.

Edit: I was wrong. My radxa zero does have the /boot/uEnv.txt. Okay let me try my overlay there and I will report back. On the CM3, still no sign of /boot/uEnv.txt and no apparent way to overlay the device tree.

Well, for your CM3, the boot.txt file has the overlay instructions right there at the end. There’s even two already there. So I would put the new overlays wherever rk3568-fiq-debugger-uart2m0.dtbo and rk3568-disable-npu.dtbo are located and then add the additional dtoverlay= lines as appropriate.

sudo find . -name boot.txt

No results. I have no idea what I’m doing. I think I should just stop. I think there’s a minimal requirement of knowledge for using the radxa boards and I just don’t have it; and worse I don’t even know where to get it. Sorry for the trouble but thanks for trying.

Edit (and case in point): Assuming now you meant “config.txt” and I followed those instructions and did exactly what you said and what the instructions said.

find . -name rk3568-fiq-debugger-uart2m0.dtbo
./dtbs/4.19.193-56-rockchip-gf62b47f70096/rockchip/overlay/rk3568-fiq-debugger-uart2m0.dtbo

Great. So I compiled my overlay and put it in dtbs/4.19.193-56-rockchip-gf62b47f70096/rockchip/overlay

Good so far. Continuing with config.txt instructions “Execute “update_extlinux.sh” after changing file /boot/config.txt.” (I found this file in /usr/local/sbin/update_extlinux.sh)

Did this. My dtbo was gone. Reboot and not surprisingly none of my device tree overlay was applied. That was yesterday morning over 24 hours ago…on day 2 of following instructions now.

Here is where I left off trying to apply the “patch” which…is an email? I am so confused. However, I managed to find some tutorials on writing a device tree overlay and have no good grasp on the syntax; not surprisingly I cannot get this one to compile.

I’m not sure why a “patch” is done when all I’ve read everywhere is that overlays are the preferred method of modifying the device tree. That was in my training from 3 days ago. This has a syntax error and I have no idea what that error is but I tried to adapt the “patch” (it’s an email…I mean it’s an email right? Or to a list?)

(Note: if you’re some poor soul who finds this, do not use it. It won’t compile.)

Error:

Error: /home/rock/dts-sources/i2s.dts:36.44-45 syntax error
FATAL ERROR: Unable to parse input tree

My failing to compile device tree based on an email of a patch

/dts-v1/;
/plugin/;
 
/ {
    compatible = "radxa,zero";

    fragment@0 {
        target = <&sound>;
        __overlay__ {

            /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/amlogic/meson-g12a-radxa-zero.dts#n135
            audio-aux-devs = <&tdmout_b>;
            audio-routing = "TDMOUT_B IN 0", "FRDDR_A OUT 1",
                    "TDMOUT_B IN 1", "FRDDR_B OUT 1",
                    "TDMOUT_B IN 2", "FRDDR_C OUT 1",
                    "TDM_B Playback", "TDMOUT_B OUT";
            */
        
            audio-aux-devs = <&tdmout_a>, <&tdmout_b>;
            audio-routing = "TDMOUT_A IN 0", "FRDDR_A OUT 0",
                "TDMOUT_A IN 1", "FRDDR_B OUT 0",
                "TDMOUT_A IN 2", "FRDDR_C OUT 0",
                "TDM_A Playback", "TDMOUT_A OUT",
                "TDMOUT_B IN 0", "FRDDR_A OUT 1",
                "TDMOUT_B IN 1", "FRDDR_B OUT 1",
                "TDMOUT_B IN 2", "FRDDR_C OUT 1",
                "TDM_B Playback", "TDMOUT_B OUT";

            dai-link-5 {
                sound-dai = <&tdmif_a>;
                dai-format = "i2s";
                dai-tdm-slot-tx-mask-0 = <1 1>;
                mclk-fs = <256>;

                codec-0 {
                    sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>;
                };

                /* codec-1 { */
                /*  sound-dai = <&yourawesomecodec HERE>; */
                /* }; */
            };
        };
    };
 
    fragment@1 {
        target-path = "/";
        __overlay__ {
            &tdmif_a {
                status = "okay";
                pinctrl-0 = <&tdm_a_dout0_pins>, <&tdm_a_fs_pins>,
                        <&tdm_a_sclk_pins>, <&mclk0_ao_pins>;
                pinctrl-names = "default";

                assigned-clocks = <&clkc_audio AUD_CLKID_TDM_MCLK_PAD0>,
                          <&clkc_audio AUD_CLKID_TDM_SCLK_PAD0>,
                          <&clkc_audio AUD_CLKID_TDM_LRCLK_PAD0>;
                assigned-clock-parents = <&clkc_audio AUD_CLKID_MST_A_SCLK>,
                          <&clkc_audio AUD_CLKID_MST_A_SCLK>,
                          <&clkc_audio AUD_CLKID_MST_A_LRCLK>;
                assigned-clock-rates = <0>, <0>, <0>;
            };

            &tdmout_a {
               status = "okay";
            };
        };
    };
};

Patches often have headers that look like emails because of how common it is for patches to be circulated via email listservs, especially in kernel development. Since Jerome is one of the kernel maintainers in this area, it makes sense that he would view this as something to include as an addition in meson-g12a-radxa-zero.dts rather than as an overlay. You should be able to apply the patch without modification and then compile a new meson-g12a-radxa-zero.dtbo to replace the existing one, just to see if it works.

Regarding the error you’re getting, it’s telling you that the syntax error is on line 36 of your .dts, 44-45 characters in. That’s the “codec-0” node and it’s choking on TOHDMITX_I2S_IN_A because the device tree compiler doesn’t know what that is or what to do with it. In actuality, it’s a binding that is defined in meson-g12a-tohdmitx.h, but the compiler has no way of knowing that.

meson-g12a-radxa-zero.dts contains a specific include line for this header file which tells the compiler to check there for defined terms like this. Since you’re taking this from a patch, the patch assumes the existing includes will still be there. You can still adapt it into an overlay, but since the compiler doesn’t know what the overlay will be laying over, you need to help the compiler out by putting the necessary includes in the overlay .dts itself.

So in your patch, add #include <dt-bindings/sound/meson-g12a-tohdmitx.h> to the top after the /plugin/; line. That should get you past that error, but you’ll probably have others.

Glancing through the overlay, you can see several other “allcaps” terms that aren’t defined in meson-g12a-tohdmitx.h, so you can bet the compiler is going to choke on those too. Doing a search of the kernel tree, you can see that at least some of them are defined in axg-audio-clkc.h so I’d also add another line at the top for #include <dt-bindings/clock/axg-audio-clkc.h>. If you get other errors, you can use the same process to find and add the missing includes.

Amazing explanation. Thank you!!!

Edit: Crazy question…do you have a preferred IDE that’s dt-bindings aware and knows dts syntax?

Nope. I just use nano. I’ve been learning device tree mostly by trial and error. I also found this article very accessible and helpful.

Okay I got it to compile but never using #include or /include/ more cryptic failure to parse errors on those include lines.

I ended up just putting the values in manually but that’s probably why this still doesn’t work. Here is where I left off. At least it compiles. Put it at end of uEnv.txt overlays= line and in the directory where I’m still not sure it even belongs…

I put it here:

/boot/dtbs/$(uname -r)/amlogic/overlay/i2s.dtbo

Here is my /boot/uEnv.txt

verbosity=7
console=ttyAML0,115200
overlay_prefix=meson
rootfstype=ext4
fdtfile=amlogic/meson-g12a-radxa-zero.dtb
overlays=meson-g12a-uart-ao-a-on-gpioao-0-gpioao-1 i2s
rootuuid=a1d6d1b5-7219-4126-8ecd-e5cf6265cfc6
initrdsize=0xfc6a47
kernelversion=5.10.69-12-amlogic-g98700611d064
initrdimg=initrd.img-5.10.69-12-amlogic-g98700611d064
kernelimg=vmlinuz-5.10.69-12-amlogic-g98700611d064

(Warning to future readers: this does not work)

/dts-v1/;
/plugin/;
 
/ {
    compatible = "radxa,zero";

    fragment@0 {
        target = <&sound>;
        __overlay__ {

            model = "RADXA-ZERO-I2S";

            /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/amlogic/meson-g12a-radxa-zero.dts#n135
            audio-aux-devs = <&tdmout_b>;
            audio-routing = "TDMOUT_B IN 0", "FRDDR_A OUT 1",
                    "TDMOUT_B IN 1", "FRDDR_B OUT 1",
                    "TDMOUT_B IN 2", "FRDDR_C OUT 1",
                    "TDM_B Playback", "TDMOUT_B OUT";
            */
        
            audio-aux-devs = <&tdmout_a>, <&tdmout_b>;
            audio-routing = "TDMOUT_A IN 0", "FRDDR_A OUT 0",
                "TDMOUT_A IN 1", "FRDDR_B OUT 0",
                "TDMOUT_A IN 2", "FRDDR_C OUT 0",
                "TDM_A Playback", "TDMOUT_A OUT",
                "TDMOUT_B IN 0", "FRDDR_A OUT 1",
                "TDMOUT_B IN 1", "FRDDR_B OUT 1",
                "TDMOUT_B IN 2", "FRDDR_C OUT 1",
                "TDM_B Playback", "TDMOUT_B OUT";

            dai-link-5 {
                sound-dai = <&tdmif_a>;
                dai-format = "i2s";
                dai-tdm-slot-tx-mask-0 = <1 1>;
                mclk-fs = <256>;

                codec-0 {
                    sound-dai = <&tohdmitx 0>;
                };

                /* codec-1 { */
                /*  sound-dai = <&yourawesomecodec HERE>; */
                /* }; */
            };
        };
    };
 
    fragment@1 {
        target = <&tdmif_a>;
        __overlay__ {
            status = "okay";
            pinctrl-0 = <&tdm_a_dout0_pins>, <&tdm_a_fs_pins>,
                    <&tdm_a_sclk_pins>, <&mclk0_ao_pins>;
            pinctrl-names = "default";

            assigned-clocks = <&clkc_audio <155>>,
                      <&clkc_audio 160>,
                      <&clkc_audio 157>;
            assigned-clock-parents = <&clkc_audio 79>,
                      <&clkc_audio 79>,
                      <&clkc_audio 86>;
            assigned-clock-rates = <0>, <0>, <0>;
        };

    };

    fragment@2 {
        target = <&tdmout_a>;
        __overlay__ {
           status = "okay";
        };
    };
};

Thanks again for the help. I’m just logging all this here in hopes that it’s every useful to someone.

In what way does it not work? Is it booting?

Boots fine, but I expected to see some sign of life in either aplay -l or by outputting my entire device tree and looking for my overlay changes to be reflected which they aren’t dtc -I fs /sys/firmware/devicetree/base;

I was looking for dai-link-5 in either.

Edit: Beyond that I tried researching ways to see if the overlay failed in some way when the kernel was starting up but I have no idea where to look. Some kind of device tree log.

Tell me about it. I’ve been wishing for something like that too.

If you’re not seeing the new nodes in /sys/firmware/devicetree/base, then it sounds like the overlay didn’t apply for some reason. One way to get to the bottom of it is to watch the serial console over UART. That’ll show you the bootloader output as well as early kernel init, which is where the device tree gets combined and flattened.

Cool, thanks. I ordered the cable and hopefully can get it to work on a mac. I’d really like to get I2S working on this thing as I’m already working with ESS DACs for work. It would be cool to make a Hifi-berry hat but…even higher…fi. Also I can get all the components at JLCPCB now so really the only thing stopping me from manufacturing this is figuring out I2S and being able to provide users with a easily implemented overlay. @radxa please help us :slight_smile:

I’m just assuming I2C will be easier. I hope that’s not a bad assumption. Also just getting Volumio working with the Radxa Zero would be awesome. They’d probably post about it, too.

I2C works. I’m using I2C without issue in my project.

1 Like

Hello. Have you been able to get the i2s DAC to work?

lloyd not sure if you came any further with this, but this is my dts file
As @theophile suggested it has the include of the axg-audio-clck.h

/dts-v1/;
/plugin/;
 
#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
#include <dt-bindings/clock/axg-audio-clkc.h>

/ {

	compatible = "radxa,zero", "amlogic,g12a";
	
    fragment@0 {
        target = <&sound>;
        __overlay__ {
        
            audio-aux-devs = <&tdmout_a>, <&tdmout_b>;
            audio-routing = "TDMOUT_A IN 0", "FRDDR_A OUT 0",
				"TDMOUT_A IN 1", "FRDDR_B OUT 0",
				"TDMOUT_A IN 2", "FRDDR_C OUT 0",
				"TDM_A Playback", "TDMOUT_A OUT",
				"TDMOUT_B IN 0", "FRDDR_A OUT 1",
				"TDMOUT_B IN 1", "FRDDR_B OUT 1",
				"TDMOUT_B IN 2", "FRDDR_C OUT 1",
				"TDM_B Playback", "TDMOUT_B OUT";
				

			dai-link-5 {
				sound-dai = <&tdmif_a>;
				dai-format = "i2s";
				dai-tdm-slot-tx-mask-0 = <1 1>;
				mclk-fs = <256>;

				codec {
					sound-dai = <&tohdmitx TOHDMITX_I2S_IN_B>;
				};
			};
        };
		
    };
    
    fragment@1 {
        target-path = <&tdmif_a>;
        __overlay__ {
			status = "okay";
			pinctrl-0 = <&tdm_a_dout0_pins>, <&tdm_a_fs_pins>,
					<&tdm_a_sclk_pins>, <&mclk0_ao_pins>;
			pinctrl-names = "default";

			assigned-clocks = <&clkc_audio AUD_CLKID_TDM_MCLK_PAD0>,
					  <&clkc_audio AUD_CLKID_TDM_SCLK_PAD0>,
					  <&clkc_audio AUD_CLKID_TDM_LRCLK_PAD0>;
			assigned-clock-parents = <&clkc_audio AUD_CLKID_MST_A_SCLK>,
					  <&clkc_audio AUD_CLKID_MST_A_SCLK>,
					  <&clkc_audio AUD_CLKID_MST_A_LRCLK>;
			assigned-clock-rates = <0>, <0>, <0>;
		};
	};
    
	
	fragment@2 {
        target = <&tdmout_a>;
        __overlay__ {
               status = "okay";			
        };
    };
};

I also tried adding a fragment to set the function of pins as you suggested earlier. Got this off of a standard overlay from Raspberry Pi. Ofc also not sure if this is the right approach, not sure if the syntax is correct either…

	fragment@3 {
        target = <&gpio>;
        __overlay__ {
            pinctrl-names = "default";
            pinctrl-0 = <&i2s_func2pins>;

            i2s_func2pins: i2s_func2pins {
                radxa,pins = <8 9 10 11>;
                radxa,function = <2 1 1 1>;
            };
			
        };
    };

I’ve put it in the rock user space and then ran this to compile from within /usr/src/linux-headers-5.10.69-12-amlogic-g98700611d064:

cpp -nostdinc -I include -undef -x assembler-with-cpp -o ~/radxa.tmp ~/radxa-zero-i2s.dts
dtc -O dtb -b 0 -o ~/radxa-zero-i2s.dtbo ~/radxa.tmp
sudo cp ~/radxa-zero-i2s.dtbo /boot/dtbs/5.10.69-12-amlogic-g98700611d064/amlogic/overlays/

It compiles fine but I also don’t see any change after reboot so it doesn’t actually work.
The overlays entry in my /boot/uEnv.txt looks like this (pretty standard I guess)
overlays=meson-g12a-uart-ao-a-on-gpioao-0-gpioao-1 radxa-zero-i2s
In the above I copy it to the same location as the uart dtb that is already stated in the uEnv.txt file, hoping it would be picked up.

I even tried to prefix it with “meson-” to see if it made a difference (in uEnv.txt there’s “overlay_prefix=meson”, not sure what that means, a filter maybe?).

Bottom line is that the only way I came closer was to patch as suggested by Jerome Brunet from BayLibre and compile the original dts which involved getting some more included files and cheating in one place where I couldn’t find a needed constant.
Now when I use the resulting dtb I see TDMOUT_A entries in Alsamixer which are not there in the original.

@radxa we really need you to jump in here and help us with the last steps to use you product :slight_smile:
Questions:

  1. Are we on the right track with the overlay file?
  2. If not, what’s wrong/missing in order to get I2S to work on the pin header?
  3. Where do we put the overlay files in order for them to be picked up on boot?
  4. What should the entry be in uEnv.txt for this to happen?

@maycides Please check with Feng about his I2S progress. We could add Radxa Zero support to the schedule.

1 Like

We are working on the I2S development of CM3 now. After its completion, Radxa Zero would be support soon.

1 Like

Well, you could be lucky soon.
Any interest in a pre-version? It does not have spdif/i2s support yet, but Radxa promised to work on that (I tried spdif myself, but failed).
Everything else, incl. myVolumio appears to be working, so currently ok with a USB DAC.

Cheers - Gé