TDM mode on the I2S port

Hello!

So it gets a bit confusing between HW and SW, because HW refers to the I2S as 0 and SW uses 1. So what you want is …

rt565x_dai_link: nvidia,dai-link-1 {
link-name = "rt565x-playback";
cpu-dai = <&tegra_i2s1>;
codec-dai = <&spdif_dit0>;
cpu-dai-name = "I2S1";
codec-dai-name = "dit-hifi";
format = "dsp_a"
bitclock-slave;
frame-slave;
bitclock-noninversion;
frame-noninversion;
bit-format = "s24_le"
bclk_ratio = <0>;
srate = <48000>;
num-channel = <8>
ignore_suspend;
name-prefix = "x";
status = "okay";
};

Note that the above is from the codec’s perspective so for Tegra to be the bclk/fsync master, it needs to be slave above.

This will be automatically generated based upon sample-rate and mclk ratio. The default ratio used by the Tegra drivers is 256 and so yes for 48kHz it will generate a 12.228MHz MCLK.

This is the ratio between the bit clock and data. If you want a single bit clock per data bit, then set to 0 or 1. A ratio of 0 or 1 are equivalent and means that there is one bit clock per data bit. Hence, I left as 0 above.

Regards,
Jon

Hi Jon,
Apologies, I now have hardware to test the 8 channel TDM mode however, I am not getting audio out. When i run an “arecord” command in userspace, I can see the the Audio ADC signals with SCLK to FS ratio of 256, SCLK at 12.288Mhz and corresponding data for 8 audio channels using a logic analyzer.
The TX2 as the master generates SCLK and FS correctly. The CS5368 loads the data on SDIN to the TX2 which I have verified.

Setup:
Jetpack 4.2, Rel32.1 for TX2. I use a Cirrus Logic CS5368 in TDM mode.I have used I2S0 on J21 on the TX2 Devlopment kit. I configured the dtb as you suggested(with a few modifications) for TDM mode(DSP_A),8 Channels, 48Khz rate etc as shown in the attached image. I then re-flashed my TX2.

I used the command “arecord -D hw:tegrasndt186ref,0 -t wav -r 48000 -f S32_LE -c 8 -d 30 wavfilename.wav”

Questions:

  1. Do I have to configure any other TX2 parameter e.g(ADMAIFx etc) before running the "arecord" command to record the audio?
  2. The CS5368 has channel audio on 24 MSB's out of the 32bits per channel in TDM mode. Am I correct to have set the dtb to "S32_LE"? How are the lower unused 8bit channel data ignored in the userspace? When I set the dtb to S24_LE, I get 196 SCLK to FS ratio which is wrong.
  3. Is there an Nvidia dtb document that explains the parameters? I am unsure if I have set the link-name, codec-dai-name in the dtb file correctly?

Regards
Bade

Hi Jon,
I have been reading up several threads in an attempt to get the TDM mode working. I also observed my FS siggnal had glitches. I have not configured the J21 pinouts to I2S using the pinmux template.

  1. Is this required, because I can see the TX2 driving the the I2S1 signals using a logic analyzer? I tried configuring using the pinmux template. I am unsure which option to select: I2S or unused DAP1.

  2. I found a thread which you had commented on to setup TDMhttps://devtalk.nvidia.com/default/topic/1031552/jetson-tx2/i2s-tdm-adx-demux/2#reply. You showed a configuration for TDM. I have also used the Nvidia documentation for ASoC products under section ADX to adapt your userspace recommendation in the referenced thread. However I get errors. Please can you confirm I am doing the right thing? See code:

amixer -c <tegrasndt186ref> cset name="ADX1 Mux" I2S1 
amixer -c <tegrasndt186ref> cset name="ADX1 Output1 Channels" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Output2 Channels" 1 
amixer -c <tegrasndt186ref> cset name="ADX1 Output3 Channels" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Output4 Channels" 1 
amixer -c <tegrasndt186ref> cset name="ADX1 Output5 Channels" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Output6 Channels" 1 
amixer -c <tegrasndt186ref> cset name="ADX1 Output7 Channels" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Output8 Channels" 1 

amixer -c <tegrasndt186ref> cset name="ADX1 Input Channels" 8

amixer -c <tegrasndt186ref> cset name="ADMAIF1 Mux" ADX1-1
amixer -c <tegrasndt186ref> cset name="ADMAIF2 Mux" ADX1-2
amixer -c <tegrasndt186ref> cset name="ADMAIF3 Mux" ADX1-3
amixer -c <tegrasndt186ref> cset name="ADMAIF4 Mux" ADX1-4
amixer -c <tegrasndt186ref> cset name="ADMAIF5 Mux" ADX1-5
amixer -c <tegrasndt186ref> cset name="ADMAIF6 Mux" ADX1-6
amixer -c <tegrasndt186ref> cset name="ADMAIF7 Mux" ADX1-7
amixer -c <tegrasndt186ref> cset name="ADMAIF8 Mux" ADX1-8

amixer -c <tegrasndt186ref> cset name="I2S1 Channels" 8


amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 0" 0
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 1" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 2" 0
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 3" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 4" 0
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 5" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 6" 0
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 7" 1




nvidia,adx-slot-size = <32>;
nvidia,adx-slot-map = <
TDM_SLOT_MAP(0, 1, 0)
TDM_SLOT_MAP(0, 1, 1)
TDM_SLOT_MAP(0, 1, 2)
TDM_SLOT_MAP(0, 1, 3)
TDM_SLOT_MAP(1, 1, 0)
TDM_SLOT_MAP(1, 1, 1)
TDM_SLOT_MAP(1, 1, 2)
TDM_SLOT_MAP(1, 1, 3)
TDM_SLOT_MAP(2, 1, 0)
TDM_SLOT_MAP(2, 1, 1)
TDM_SLOT_MAP(2, 1, 2)
TDM_SLOT_MAP(2, 1, 3)
TDM_SLOT_MAP(3, 1, 0)
TDM_SLOT_MAP(3, 1, 1)
TDM_SLOT_MAP(3, 1, 2)
TDM_SLOT_MAP(3, 1, 3)
TDM_SLOT_MAP(4, 1, 0)
TDM_SLOT_MAP(4, 1, 1)
TDM_SLOT_MAP(4, 1, 2)
TDM_SLOT_MAP(4, 1, 3)
TDM_SLOT_MAP(5, 1, 0)
TDM_SLOT_MAP(5, 1, 1)
TDM_SLOT_MAP(5, 1, 2)
TDM_SLOT_MAP(5, 1, 3)
TDM_SLOT_MAP(6, 1, 0)
TDM_SLOT_MAP(6, 1, 1)
TDM_SLOT_MAP(6, 1, 2)
TDM_SLOT_MAP(6, 1, 3)
TDM_SLOT_MAP(7, 1, 0)
TDM_SLOT_MAP(7, 1, 1)
TDM_SLOT_MAP(7, 1, 2)
TDM_SLOT_MAP(7, 1, 3)>;

Best regards
Bade

Hi Bade,

  1. It should not be necessary, but you can check that I2S1 is mapped to ADMAIF1 by executing the following and verifying that 'Item0:' shows 'I2S1'.
    $ amixer -c tegrasndt186ref sget "ADMAIF1 Mux"
    
  2. Yes that is correct to use S32_LE
  3. The existing documentation does not explain these parameters unfortunately, but we are working to improve this and we will ensure that these are documented in the future. However, looking at the image you shared the settings look fine

Given that the you see the clock and frame-sync but it is not capturing the data, can you send me the output from the following command?

$ sudo grep dap1 /sys/kernel/debug/tegra_pinctrl_reg

Thanks
Jon

Hi Bade,

What do these glitches look like?

It should be ‘I2S’ and not ‘unused DAP1’ In my previous response, I mentioned that if you can dump the pinctrl registers I can check the configuration of the pins to see if there is anything that needs to be configured.

The ADX is only needed for demultiplexing an TDM stream into multiple streams. For example, you could use the ADX to demultiplex your 8 inputs channels into 4 stereo streams. However, for capturing TDM data into a single file with 8 channels you do not need to use the ADX. Even if you do wish to use the ADX, I would recommend that you do not use it, until you have basic TDM capture working.

Regards,
Jon

Hi Jon,
Thanks for your ADX explanation. I will look to to get the stream working before splitting to 4 stereo outputs.
I typed the two commands as requested. Please see outputs:

m@TX2:~$ amixer -c tegrasndt186ref sget "ADMAIF1 Mux"
Simple mixer control 'ADMAIF1 Mux',0
  Capabilities: enum
  Items: 'None' 'ADMAIF1' 'ADMAIF2' 'ADMAIF3' 'ADMAIF4' 'ADMAIF5' 'ADMAIF6' 'ADMAIF7' 'ADMAIF8' 'ADM'
  Item0: 'I2S1'
m@TX2:~$ sudo grep dap1 /sys/kernel/debug/tegra_pinctrl_reg
Bank: 0 Reg: 0x02431028 Val: 0x00000400 -> dap1_fs_pj3
Bank: 0 Reg: 0x02431030 Val: 0x00000458 -> dap1_din_pj2
Bank: 0 Reg: 0x02431038 Val: 0x00000400 -> dap1_dout_pj1
Bank: 0 Reg: 0x02431040 Val: 0x00000400 -> dap1_sclk_pj0

I have also attached oscilloscope capture of the glitch on the FS signal. The image is TDM 8 channel with channel 1 active and all others muted.

Best regards


Hi Jon,
I have fixed the glitch(a bus contention issue between the CS5368 and TX2) and I seem to only get audio on 2 out of 8 channels.
I use the following command to record audio on all 8 channels. I have use -v “verborse” to show the settings of the recording.

m@TX2:~$ arecord -v -D hw:tegrasndt186ref,0 -t wav -r 48000 -f S32_LE -c 8 -d 5 cap.wav

Recording WAVE 'cap.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Channels 8
Hardware PCM card 1 'tegra-snd-t186ref-mobile-rt565x' device 0 subdevice 0
Its setup is:
  stream       : CAPTURE
  access       : RW_INTERLEAVED
  format       : S32_LE
  subformat    : STD
  channels     : 8
  rate         : 48000
  exact rate   : 48000 (48000/1)
  msbits       : 32
  buffer_size  : 1024
  period_size  : 256
  period_time  : 5333
  tstamp_mode  : NONE
  tstamp_type  : MONOTONIC
  period_step  : 1
  avail_min    : 256
  period_event : 0
  start_threshold  : 1
  stop_threshold   : 1024
  silence_threshold: 0
  silence_size : 0
  boundary     : 4611686018427387904
  appl_ptr     : 0
  hw_ptr       : 0

The Problem:
I only get ouputs on channels 1 and 4 when the wav file is opened in Audacity. I also notice that Channel 1 and 4 are in stereo mode and their signals are replicated for the other 6 channels in the audio data stream. Its as if the TX2 is reading channels 1 and 4 as Left and Right in I2S mode and replicating it 4 times to make the 8 channel data in the wav file.

Please see the image attached showing channel 4(audio sine wave) and channel 1(no audio) replicated four times in the 8 channel TDM data stream.

Any thoughts or suggestion on how I can configure the TDM stream in the TX2? The clock and frame sync generated by the TX2 is correct but the issue seems to be how the TX2 is handling the TDM 8 channel stream.

Thanks
Bade

Thanks.
8 Channel I2S TDM mode is working now on TX2 Dev-kit J21. It was a issue with the pinmux I2S settings.

Procedure(refer to all comments in this thread):

  1. Modify the pinmux for J21 pins for I2S mode, and run the pinmux script
  2. Modify the dtb as detailed in comment 41 to setup TDM/dsp mode as required.
  3. Reflash TX2
  4. Run are “arecord” as shown in comment #47

I’d be happy if anyone can suggest how to use ADX to split the TDM stream.

Thanks
Bade

Hello Bade,

Thanks for the update. That’s great news. Please refer the ADX example in the L4T documentation for details on how to use …

https://docs.nvidia.com/jetson/l4t/index.html#page/Tegra%2520Linux%2520Driver%2520Package%2520Development%2520Guide%2Fasoc_driver.html%23wwpID0E0GC0HA

Regards,
Jon

Dear Jon,
I have gone through the link. I am a bit confused as the link uses an I2S stereo.

My use case: I have eight 32bit channels in a single TDM input on I2S1. I want to De-Mux to 4 output streams(i.e. 2 channels per stream) on TxCIF0 - TxCIF3.

Please can you kindly verify that the configuration I had earlier modified in comment #43 meets my use case configuration?

Is this configuration all done in userspace?

Best regards

Hello Bade,

Can you try the following …

amixer -c <tegrasndt186ref> cset name="ADX1 Mux" I2S1 
amixer -c <tegrasndt186ref> cset name="ADX1 Output1 Channels" 2
amixer -c <tegrasndt186ref> cset name="ADX1 Output2 Channels" 2 
amixer -c <tegrasndt186ref> cset name="ADX1 Output3 Channels" 2
amixer -c <tegrasndt186ref> cset name="ADX1 Output4 Channels" 2 
amixer -c <tegrasndt186ref> cset name="ADX1 Input Channels" 8
amixer -c <tegrasndt186ref> cset name="ADMAIF1 Mux" ADX1-1
amixer -c <tegrasndt186ref> cset name="ADMAIF2 Mux" ADX1-2
amixer -c <tegrasndt186ref> cset name="ADMAIF3 Mux" ADX1-3
amixer -c <tegrasndt186ref> cset name="ADMAIF4 Mux" ADX1-4
amixer -c <tegrasndt186ref> cset name="I2S1 Channels" 8
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 0" 0
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 1" 1
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 2" 2
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 3" 3
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 4" 4
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 5" 5
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 6" 6
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 7" 7
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 8" 64
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 9" 65
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 10" 66
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 11" 67
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 12" 68
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 13" 69
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 14" 70
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 15" 71
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 16" 128
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 17" 129
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 18" 130
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 19" 131
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 20" 132
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 21" 133
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 22" 134
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 23" 135
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 24" 192
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 25" 193
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 26" 194
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 27" 195
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 28" 196
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 29" 197
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 30" 198
amixer -c <tegrasndt186ref> cset name="ADX1 Byte Map 31" 199

Yes for L4T release 32.2 all the configuration is done via userspace.

Regards
Jon

Hi Jon,
Apologies for the delayed reply, I was away. Thanks so much :) I tried the above code and it works like charm from userspace!

PROCEDURE:

  • I configured as shown in #51 for 4 streams(2ch)
  • I recorded 4 stereo streams:
    arecord -D hw:tegrasndt186ref,0 -t wav -f S32_LE -c 2 -r 48000 -d 15 wav-file-1.wav &
    arecord -D hw:tegrasndt186ref,1 -t wav -f S32_LE -c 2 -r 48000 -d 15 wav-file-2.wav &
    arecord -D hw:tegrasndt186ref,2 -t wav -f S32_LE -c 2 -r 48000 -d 15 wav-file-3.wav &
    arecord -D hw:tegrasndt186ref,3 -t wav -f S32_LE -c 2 -r 48000 -d 15 wav-file-4.wav &
    

However, I have a few questions to better understand the configuration(#51) and hopefully it helps someone else.

  1. Since I use S32_LE, I understand up until line 19. A 32bit word requires 4 byte mappings per channel. However, I am trying to figure out why the ADX1 byte mapping jumps to 64,128 and then 192?
  2. How do I revert this configuration back to the previous single TDM stream consisting of all 8 channels from userspace? I want to be able to dynamically switch from 1 stream(8ch) to 4 streams(2ch)

Best regards
Bade

Hi Bade,

Sorry for the delay, I have been out of the office.

  1. If you refer to the ADX documentation [0], you will see that the value for each byte in the byte RAM is encoded with three values which are …
  • Output stream (bits [7:6])
  • Output Stream Channel (bits [5:2])
  • Output Stream Byte (bits [1:0])

Hence for each output stream, in your case there is 4, we increment the ‘output stream’ value and hence, it jumps from 0 to 64, to 128, to 192.

  1. The simplest way to revert the configuration is to bypass the ADX completely and simply route the audio from the I2S interface directly to the ADMAIF.
amixer -c <tegrasndt186ref> cset name="ADX1 Mux" None 
amixer -c <tegrasndt186ref> cset name="ADMAIF1 Mux" I2S1
amixer -c <tegrasndt186ref> cset name="ADMAIF2 Mux" None
amixer -c <tegrasndt186ref> cset name="ADMAIF3 Mux" None
amixer -c <tegrasndt186ref> cset name="ADMAIF4 Mux" None

Regards,
Jon

[0] https://docs.nvidia.com/jetson/l4t/index.html#page/Tegra%2520Linux%2520Driver%2520Package%2520Development%2520Guide%2Fasoc_driver.html%23wwpID0E0GC0HA