Basic I2S (IN and OUT) setup on CM3 and/or IO Board

On Raspberry Pi, I use a very basic overlay to tell the SoC to act as I2S Slave and to utilize the various GPIO pins in I2S mode, expose them to the OS as a “dummy” sound card, etc. I control my ADC chip via GPIO manually, setting the I2S sample rate which also drives the sample rate of my DAC. I don’t need a “sound card” driver in that sense.

On the CM3 IO board, I’m looking at the pinout: https://wiki.radxa.com/Rock3/CM3/IO/GPIO

Question 1) Master or Slave. This table lists various I2S pins with corresponding “Function” column. I suspect that I use the CM3 in “slave” mode by using it’s various clock “RX” pins: I2S1_LRCK_RX_M1, I2S1_SCLK_RX.

Question 2) Forgive my ignorance, but don’t I need to know how to get each pin into it’s correct “Function” using a device tree overlay? I think this is obvious to most of you but I have no idea where the reference information is that tells me what to do or even how to do it. For instance, I need to also use I2S1_SDI0_M1 and I2S1_SDO0_M1.

On a raspberry pi, all I had to do was put dtparam=i2s=on in config.txt. Thus, I was never forced to learn about what that was doing; how it was instructing the SoC chip underneath.

I’m sorry if this is really basic. If anyone can simply point me to how I can change pin “Function” in a dts file I can take it from there. I’ve yet to find this information, though. Even on the Radxa Zero, in order to truly set up I2S you’d need access to the full data sheet. This information is simply not available. This, thread after thread people are going to be coming on here confused, right? Depending only on those who have access to the full, data sheets? Maybe I’m missing something.

Spent the day reading about this and I’ve compiled a dtbo but can’t seem to even load it. Neither the official debian or ubuntu have /boot/uEnv.txt

Yes /boot is mounted. Any ideas? Anyone?

I also had a problem with the missing file /boot/uEnv.txt. I decided to use the older Debian Buster image instead of Debian Bullseye. This file is available in Buster.

1 Like

Just sticking more notes here while I have time…

I2S1 Pinctrl definitions: rk3568-pinctrl.dtsi#L1008

rk3566-radxa-rock-3-compute-module.dtsi

rk3566-radxa-cm3-io.dts:

  • disable &rk817_sound
  • overlay &i2s1_8ch pinctrl-0 to use desired stuff here:

CM3 Pinout for reference: https://wiki.radxa.com/Rock3/CM/CM3/pinout

Optional…CM3 IO Board for list of exposed I2S1 pins on IO Board 40 pin header:
https://wiki.radxa.com/Rock3/CM3/IO/GPIO

(Note that on the CM3 IO board pin 24 Function3 I believe is mislabeled I2S1_SCLK_RX when it should be I2S1_SCLK_RX_M1 in case you’re using the SoC in I2s slave mode (…_RX and not …_TX on the SCLK and LRCK)?

I finally got the overlay applied and alsa recognizes my generic sound card on both input and output. Keep in mind, I am using the CM3 IO board so I specifically wanted I2S exposed on the 40-pin header.

One problem I had was simply getting my image to allow me to overlay the device tree at all. Thank you to @RadxaYuntian for solving this problem. The solution there was to overlay using /boot/extlinux/extlinux.conf directly. Then it was just a matter of breaking my overlays into individual one-dtbo-per-fragment in order to debug. I was able to quickly isolate the problem fragement.

I will post a finished version combined once I finish checking the I2S output/input.

The first dts simply overwrites pincntrl on &i2s1_8ch so that I can use LRCK and SCLK as receivers (aka I2S slave mode). In addition, I’m controlling the “M1” pins, rather than “M0”. Source:

/dts-v1/;
/plugin/;
 
/ {
	compatible = "rockchip,rk3566";

	fragment@0 {
        target = <&i2s1_8ch>;
        __overlay__ {
			// compatible = "rockchip,rk3568-i2s-tdm";
			

			pinctrl-0 = <&i2s1m1_sclkrx &i2s1m1_lrckrx
				     &i2s1m1_sdi0 &i2s1m1_sdo0>;

			#sound-dai-cells = <0>;
			
			status = "okay";
        };
    };

	fragment@1 {
        target-path = "/";
        __overlay__ {
            codec_out: spdif-transmitter {
                #address-cells = <0>;
                #size-cells = <0>;
                #sound-dai-cells = <0>;
                /* 
                    "linux,spdif-dit" is used in generic I2S(transmitter) driver.                     
                    You can see details "linux,spdif-dit" by bellow command
                    modinfo snd_soc_spdif_tx
                */
                compatible = "linux,spdif-dit";
                status = "okay";
            };
            codec_in: spdif-receiver {
                #address-cells = <0>;
                #size-cells = <0>;
                #sound-dai-cells = <0>;
                /* 
                    "linux,spdif-dir" is used in generic I2S(receiver) driver.                     
                    You can see details "linux,spdif-dir" by bellow command
                    modinfo snd_soc_spdif_rx
                */
                compatible = "linux,spdif-dir";
                status = "okay";
            };
        };
    };

};

In the second overlay, I’m implementing my dummy sound card:

/dts-v1/;
/plugin/;
 
/ {
	compatible = "rockchip,rk3566";

	fragment@0 {
        target-path = "/";
        __overlay__ {

        	my_sound {
	            compatible = "simple-audio-card";
	            simple-audio-card,name = "GenericStereoAudioCodec";
	            status="okay";

	            capture_link: simple-audio-card,dai-link@0 {
	                format = "i2s";

	                // Set RasPi to I2S slave
	                bitclock-master = <&r_codec_dai>;
	                frame-master = <&r_codec_dai>;

	                r_cpu_dai: cpu {
	                    sound-dai = <&i2s1_8ch>;

	                // TDM slot configuration for stereo
	                    dai-tdm-slot-num = <2>;
	                    dai-tdm-slot-width = <32>;
	                };

	                r_codec_dai: codec {
	                    sound-dai = <&codec_in>;
	                };
	            };

	            playback_link: simple-audio-card,dai-link@1 {
	                format = "i2s";

	                // Set RasPi to I2S slave
	                bitclock-master = <&p_codec_dai>;
	                frame-master = <&p_codec_dai>;

	                p_cpu_dai: cpu {
	                    sound-dai = <&i2s1_8ch>;

	                // TDM slot configuration for stereo
	                    dai-tdm-slot-num = <2>;
	                    dai-tdm-slot-width = <32>;
	                };

	                p_codec_dai: codec {
	                    sound-dai = <&codec_out>;
	                };
	            };
	        };
        };
    };

};

I repeat: This hasn’t been tested but I figured I’d share my progress here. At least aplay -L shows my device! On to the scope…

Did you manage with the overlays to play and record audio?

I also want to use i2s (in our case pcm: dsp_b) and configure the codec outside the kernel.
The Radxa has to be master, and the codec slave. Our codec is not able to be master.

I also made 2 overlays, one to disable “rk817_sound”, and one to create an audio device.

aplay -l shows a playback device, and a arecord -l shows the record device.
It is possible to play audio, the radxa produces the clock and frame sync, the audio is clear.

I see on the sope that audio is also sent by the codec, but recording is not possible.
If I make a recording when nothing is playing, the recording is absolutely silent. If I play this recorded audio, no data comes out at all: the “i2s1m1_sdo0” pin stays low.
During recording, the radxa does create the clock and frame sync, there is also pcm audio on the scope.

If I make a recording while something is playing, the recording is a copy of the playback.

To test the codec I connected it with a other pcm device, this works fine in both directions. So i’m sure my hardware is ok.

My current overlay is using “rockchip,dummy-codec”, but when I used the “spdif-dit” & “spdif-dir” I had the same issue.

Overlay to disable rk-817_sound:

    /dts-v1/;
    /plugin/;
    / {
    	compatible = "rockchip,rk3566";

    	fragment@0{
    		target = <&rk817_sound>;
    		__overlay__{
    				status = "disabled";
    		};
    	};

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

Second overlay to create my soundcard “is2masterdummy”:

/dts-v1/; 
/plugin/;

/ {
    compatible = "rockchip,rk3566";

    fragment@0 {
        target-path = "/";
        __overlay__ {
			my_soundcard {
				compatible = "simple-audio-card";
				simple-audio-card,name = "i2smasterdummy";
				simple-audio-card,format = "dsp_b";
				status="okay";

				simple-audio-card,cpu {
					sound-dai = <&i2s1_8ch>;
					//dai-tdm-slot-num = <2>;
					//dai-tdm-slot-width = <32>;
				};
				simple-audio-card,codec {
					sound-dai = <&codec_dummy>;
				};

			};
                
			codec_dummy: codec_dummy {
				compatible = "rockchip,dummy-codec";
				#sound-dai-cells = <0>;
				status = "okay";
            };

        };
    };

    fragment@1 {
        target = <&i2s1_8ch>;
        __overlay__ {
            #sound-dai-cells = <0>;
            rockchip,clk-trcm = <1>;
            pinctrl-0 = <&i2s1m1_sclktx &i2s1m1_lrcktx &i2s1m1_sdo0 &i2s1m1_sdi0>;
            status = "okay";
        };
    };
};

Hey thanks this is the closest I’ve gotten! I only had a small amount of time to work on this today.

/dts-v1/; 
/plugin/;

/ {
    compatible = "rockchip,rk3566";
    
    fragment@0{
      target = <&rk817_sound>;
      __overlay__{
          status = "disabled";
      };
    };

    fragment@1 {
        target = <&i2s1_8ch>;
        __overlay__ {
            #sound-dai-cells = <0>;
            //rockchip,clk-trcm = <1>;
            pinctrl-0 = <&i2s1m1_sclkrx &i2s1m1_lrckrx &i2s1m1_sclktx &i2s1m1_lrcktx &i2s1m1_sdo0 &i2s1m1_sdi0>;
            status = "okay";
        };
    };


    fragment@2 {
        target-path = "/";
        __overlay__ {
          codec_test: codec_test {
	      compatible = "rockchip,dummy-codec";
              #sound-dai-cells = <0>;
              status="okay";
          };
          
          my_soundcard {
            compatible = "simple-audio-card";
            simple-audio-card,name = "GenericAudioCodec";
            simple-audio-card,format = "i2s";
            simple-audio-card,frame-master = <&codec_test>;
            simple-audio-card,bitclock-master = <&codec_test>;
            status="okay";

            simple-audio-card,cpu {
              sound-dai = <&i2s1_8ch>;
              //dai-tdm-slot-num = <2>;
              //dai-tdm-slot-width = <32>;
            };
            
            simple-audio-card,codec {
              sound-dai = <&codec_test>;
            };

          };
         
        };
    };
    
};

I’m finally showing signs of life on i2s1m1_sdo0 !

aplay is also acting as expected, now. Just need to figure out what’s wrong with my i2s stuff. My DAC isn’t outputting anything and Nicolas F. who is a maintainer on this which is not in radxa kernel mentioned some interesting things that might be useful for you including:

"trcm-sync-*x-only may come into play if you want both directions to only use one of the two lrck/bclk lines to sync frames to. "

Another expert on rockchip I2S is @flatmax and I’m trying his build root tomorrow hopefully!

One idea is to confirm that your I2S LR clock and bit clock line is wired in as expected, then set the “rockchip,clk-trcm” appropriately in the device tree.

Check out my setup for the dummy soundcard (this repo branch) which uses the rock3a (i2s bus 3).

It uses the rock3a as bus master - so that would have to change.

It sets up the dts like so (in this patch) :

+	sound {
+		status = "okay";
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "test";
+		simple-audio-card,format="i2s";
+
+		simple-audio-card,bitclock-master = <&cpu_dai>;
+		simple-audio-card,frame-master = <&cpu_dai>;
+		simple-audio-card,mclk-fs = <256>;
+
+		cpu_dai: simple-audio-card,cpu {
+			sound-dai = <&i2s3_2ch>;
+		};
+
+		codec_dai: simple-audio-card,codec {
+			sound-dai = <&i2s3_out>;
+		};
+
+	};
+
+	i2s3_out: i2s3-out {
+		#sound-dai-cells = <0>;
+		compatible = "flatmax,bare";
+		status = "okay";
+	};
+
+
+	i2s3_2ch {
+		status = "okay";
+	};
+
1 Like

I’ve created and tested a branch on the buidlroot.rockchip repo which generates I2S audio on the cm3 boards using the i2s1 bus.