Microphone array using I2S on TX2

Hello,
I was wondering if there is a way to read all eight I2S channels simultaneously in a sample-synchronized manner. I see that you can open each I2S pair as its own audio channel (https://devtalk.nvidia.com/default/topic/1032970/jetson-tx2/i2s-mic-with-tx2/) but is it possible to combine these channels together? We are trying to make a microphone array.

Hello!

The Jetson TX2 has 6 I2S interfaces, however, only 5 of which are accessible from the Jetson TX2 carrier board. So at most there are 5 available for use. Each I2S interface can support upto 16 channels when configured for TDM. So if you have an audio codec that can output the audio from all microphones in a single TDM stream then you could use a single I2S interface (depending on how many channels you need). One example would be this 4 channel mic array …

http://wiki.seeedstudio.com/ReSpeaker_4_Mic_Array_for_Raspberry_Pi/

Alternatively, if you were using multiple I2S interfaces, then it could be possible to use the Audio Multiplexer (AMX) in the Tegra Audio Processing Engine to multiplex the individual I2S streams into a single stream. The AMX can multiplex upto 4 input streams into a single output stream.

I am not sure exactly how many microphones you need to support so if you can clarify that will help. Also I am not sure if there was something you saw there are 8 I2S interfaces, but as mentioned above there are only 5 available for use.

Regards,
Jon

Hi Jon, thanks for your response. What I meant by 8 I2S channels is 8 individual audio channels, two per I2S bus. We were planning on using eight I2S microphones which should fit on four I2S interfaces.
The AMX sounds promising, although I’m unclear whether that means it can do four channels or four I2S streams (8 channels)?

Is there documentation somewhere on how to use the AMX and/or how to set the I2S interfaces to TDM mode? Are kernel-level changes required for the latter? I tried looking in the device tree but it didn’t seem obvious where/if I had to change things.

Thanks,
Eric

Hi Eric,

Yes that could work if you have two channels per I2S, so 8 in total. The Tegra X2 Technical Reference Manual states …

"30.1.2.6 Audio Multiplexer Block (AMX)

The Audio Multiplexer Block (AMX) can multiplex up to 4 input streams of up to 16 channels each and generate an output stream of up to 16 channels."

So each of the 4 I2S ports could be an input to one instance of the AMX. Unfortunately, the documentation for the AMX is sparse and this is something that we are working to improve. If you refer to the older rel28.1 documentation there are some details on the AMX under the ‘System Configuration --> Tegra ASoC Driver’ chapter. If you search for AMX you will see a few references. I have helped others use the ADX [0] (does the opposite of the AMX) and so it should work.

Regards,
Jon

[0] https://devtalk.nvidia.com/default/topic/1031552/i2s-tdm-adx-demux/

Hi Eric,

One thing that I should point out for using the AMX, is that there are userspace mixer controls for configuring it. What this means is that we should be able to configure the AMX from the linux command line and avoid having to match driver and device-tree changes to use it (as done in the ADX example).

I will try to find some time to put together an example for how to use it.

Regards,
Jon

Hi Jon,
Thanks for your help. It is not working yet but I think I’m having good progress. Do you have any examples that you can share pertaining to how to set up the AMX/ADMAIF to work?

I am starting by trying two channels.
Here is what I have tried:

amixer -c 1 cset name="AMX1-1 Mux" "I2S1"

amixer -c 1 cset name="ADMAIF1 Mux" "AMX1"

amixer -c 1 cset name="AMX1 Input1 Channels" 2

amixer -c 1 cset name="I2S1 Channels" 2

amixer -c 1 cset name="I2S1 input bit format" '32'

amixer -c 1 cset name="ADMAIF1 Channels" 2

I’m trying to record audio using Audacity on hw:1,0. When I probe the microphone I can see the bitclock/LR clock running when I record in audacity so I think I am getting the right I2S bus. I also see the microphone outputting data during this time, so the microphone is definitely working. However I cannot record any sound using Audacity or arecord. Are there any other settings I have to set?

Also, I cannot find rel 28.1 of the TRM, only the latest 28.2 which does not seem to have the examples you refer to. Could you copy and paste those examples here?

Thanks,
Eric

Hi Eric,

Apologies for the delay. Yes unfortunately, the public documentation for the AMX is incomplete. However, I should be able to help you get this working.

The AMX (and ADX for that matter) have a 32-bit by 16 entry RAM which needs to be programmed to map the AMX inputs to the the AMX output. Please note that each AMX input can receive a maximum of 16 words of 32-bits and the AMX output can output a maximum of 16 words of 32-bits. Each byte in the RAM is used to describe how each byte in the input frames map to the output frame. Each byte in the RAM is programmed as follows …

bits[7:6] --> AMX input (0-3)
bits[5:2] --> word number of the word in the input frame (0-15).
bits[1:0] --> byte number in the input frame word (0-3)

So as an example, if you just wanted to pass 2 channels of 32-bits from the I2S through the AMX to the ADMAIF, you would program the ‘AMX Byte Map’ as follows …

amixer cset name="AMX1-1 Mux" "I2S1"
amixer cset name="ADMAIF1 Mux" "AMX1"
amixer cset name="AMX1 Input1 Channels" 2
amixer cset name="AMX1 Output Channels" 2
amixer cset name="I2S1 Channels" 2
amixer cset name='AMX1 Byte Map 0' 0
amixer cset name='AMX1 Byte Map 1' 1
amixer cset name='AMX1 Byte Map 2' 2
amixer cset name='AMX1 Byte Map 3' 3
amixer cset name='AMX1 Byte Map 4' 4
amixer cset name='AMX1 Byte Map 5' 5
amixer cset name='AMX1 Byte Map 6' 6
amixer cset name='AMX1 Byte Map 7' 7

Note in the above example, the AMX input bits[7:6] = 0 for all bytes (for AMX1), the word number (bits[5:2]) is 0 for channel 0 and 1 for channel 1 and the byte number has a 1:1 mapping with the input frame (ie 0, 1, 2, 3).

If you wanted to mux two I2S streams into a single stream then you would …

amixer cset name="AMX1-1 Mux" "I2S1"
amixer cset name="AMX1-2 Mux" "I2S2"
amixer cset name="ADMAIF1 Mux" "AMX1"
amixer cset name="AMX1 Input1 Channels" 2
amixer cset name="AMX1 Input2 Channels" 2
amixer cset name="AMX1 Output Channels" 4
amixer cset name="I2S1 Channels" 2
amixer cset name="I2S2 Channels" 2
amixer cset name='AMX1 Byte Map 0' 0
amixer cset name='AMX1 Byte Map 1' 1
amixer cset name='AMX1 Byte Map 2' 2
amixer cset name='AMX1 Byte Map 3' 3
amixer cset name='AMX1 Byte Map 4' 4
amixer cset name='AMX1 Byte Map 5' 5
amixer cset name='AMX1 Byte Map 6' 6
amixer cset name='AMX1 Byte Map 7' 7
amixer cset name='AMX1 Byte Map 8' 64
amixer cset name='AMX1 Byte Map 9' 65
amixer cset name='AMX1 Byte Map 10' 66
amixer cset name='AMX1 Byte Map 11' 67
amixer cset name='AMX1 Byte Map 12' 68
amixer cset name='AMX1 Byte Map 13' 69
amixer cset name='AMX1 Byte Map 14' 70
amixer cset name='AMX1 Byte Map 15' 71

Another example I have tested, for playback, where I want to map 2 16-bit mono streams onto a stereo 16-bit output is configured as follows …

amixer cset name="AMX1-1 Mux" ADMAIF1
amixer cset name="AMX1-2 Mux" ADMAIF2
amixer cset name="I2S1 Mux" AMX1
amixer cset name='AMX1 Input1 Channels' 1
amixer cset name='AMX1 Input2 Channels' 1
amixer cset name='AMX1 Output Channels' 2
amixer cset name='AMX1 Byte Map 0' 0
amixer cset name='AMX1 Byte Map 1' 1
amixer cset name="AMX1 Byte Map 4" 64
amixer cset name="AMX1 Byte Map 5" 65

Let me know if this helps.

Regards
Jon

Hi Jon,
Thanks for your response! I managed to get it working through experimentation and error. This is what ended up working for me (psuedocode):

"I2S1 Mux": "None"
"AMX1-1 Mux": "None"
"I2S1 input bit format": "0"
"I2S1 codec bit format": "32"
"I2S1 Sample Rate": 44100
"AMX1 Input1 Channels": 2
"AMX1 Output Channels": 2
"ADMAIF1 Channels": 2

for idx in {0..7}:
"AMX1 Byte Map $idx": $idx
endfor

"AMX1-1 Mux": "I2S1"
"ADMAIF1 Mux": "AMX1"

Great! Thanks for the feedback! Jon