TDM mode on the I2S port

I am new to the Jetson board and on a steep learning curve.

My project requirements are to input 8 channels of TDM formatted audio to the TX2 board for processing, e.g. sound beamforming and classification. As per discussion topics and sparse documentation I understand that the I2S port can be configured for TDM Mode input.

Question:

  1. Is there a step by step guide available to help me achieve the TDM mode setup?
  2. Is there any code available which can help me validate the TDM input?

Thank you for your assistance!
Jim

Additional Information:
Ubuntu 16.4
L4T Version: R28, Revision 2.1

Hi Jim,

Yes the I2S port supports the dsp-a/b TDM modes. Please note that we support upto a max bit clock of 24.576MHz and so for 8 16-bit channels this would have a max sample rate of 192kHz or for 8 32-bit channels this would have a max sample rate of 96kHz.

To answer your questions specifically …

  1. Unfortunately, there is not and this is something that we need to improve. However, it should not be too difficult. What we need to do is …

i). Edit the device-tree source file for TX2 and configure the appropriate I2S port for dsp-a/b mode. For example, if you are planning to use the I2S interface available on the 40-pin header of the board in dsp-a mode you would make the following change …

diff --git a/kernel-dts/t18x-common-platforms/tegra186-quill-common.dtsi b/kernel-dts/t18x-common-platforms/tegra186-quill-common.dtsi
index 46de427f71f8..23c091e7afdb 100644
--- a/kernel-dts/t18x-common-platforms/tegra186-quill-common.dtsi
+++ b/kernel-dts/t18x-common-platforms/tegra186-quill-common.dtsi
@@ -842,7 +842,7 @@
                        codec-dai = <&spdif_dit0>;
                        cpu-dai-name = "I2S1";
                        codec-dai-name = "dit-hifi";
-                       format = "i2s";
+                       format = "dsp_a";
                        bitclock-slave;
                        frame-slave;
                        bitclock-noninversion;

ii). Configure the I2S frame-sync width as required for the appropriate I2S interface. Typically for dsp-a/b mode the frame sync width is a single bit-clock. So to set the frame sync width to a single bit block for I2S1, the following change would be made.

diff --git a/kernel-dts/tegra186-soc/tegra186-soc-base.dtsi b/kernel-dts/tegra186-soc/tegra186-soc-base.dtsi
index 5534343e96d5..6319f9ef37ad 100644
--- a/kernel-dts/tegra186-soc/tegra186-soc-base.dtsi
+++ b/kernel-dts/tegra186-soc/tegra186-soc-base.dtsi
@@ -1715,7 +1715,7 @@
                        pinctrl-names = "dap_active", "dap_inactive";
                        pinctrl-0 = <>;
                        pinctrl-1 = <>;
-                       fsync-width = <31>;
+                       fsync-width = <0>;
                        status = "disabled";
                };

iii). Recompile device-tree and re-flash the board. See section ‘Kernel Customization’ in the L4T development guide.

iv). When the board has booted, check that the device-tree has been updated correctly by checking the procfs entry for the dai-link that was change in step ‘i’ above. For example for nvidia,dai-link-1, you can check the following to see if it has been updated …

cat /proc/device-tree/sound/nvidia,dai-link-1/format
  1. To verify that audio is working fine (and what I do for quick testing) is to internally loopback back and audio file via the I2S interface by doing the following …
# Enable internal loopback
amixer -c tegrasndt186ref cset name="I2S1 Loopback" on

# Map the I2S playback interface to a DMA interface
amixer -c tegrasndt186ref cset name="I2S1 Mux" "ADMAIF1"

# Map the I2S capture interface to a DMA interface
amixer -c tegrasndt186ref cset name="ADMAIF2 Mux" "I2S1"

# Start I2S capture (for 30 secs)
arecord -D hw:tegrasndt186ref,1 -c 8 -r 48000 -f S16_LE -d 30 cap.wav & 

# Start I2S capture (for 30 secs)
aplay -D hw:tegrasndt186ref,0 -c 8 -r 48000 -f S16_LE <sample.wav>

Alternatively, you can also use ‘speaker-test’ to play tones on each channel and capture the tones. So instead of using aplay above use the following 1kHz tone …

speaker-test -D hw:tegrasndt186ref,0 -c 8 -r 48000 -F S16_LE -t sine -f 1000 -l 1

Finally, please make sure that you are using the latest L4T release for TX2 (r28.2.1) because there were several audio related fixes in this release.

Hope this helps.

Regards,
Jon

I see that you have r28.2.1 and so that is great. Jon

Hello jonathanh,

I appreciate your assistance and I will start the process you described.
Starting with step 1.

i). Edit the device-tree source file for TX2 and configure the appropriate I2S port for dsp-a/b mode. For example, if you are planning to use the I2S interface available on the 40-pin header of the board in dsp-a mode you would make the following change …

My first question is how to edit the device-tree source file? Is there documentation?

Thanks
Jim

Hello Jonathanh,

I found this link which describes how to update the device tree, can you verify that it is correct?

https://devtalk.nvidia.com/default/topic/1023007/jetson-tx2/how-to-use-uart0-as-normal-uart-port-on-r28-1-/post/5206170/#5206170

Post #12

Thanks
Jim

#12 was from an earlier release. Mostly #12 is correct, but there is now a device tree signing which makes installing a bit more complicated (if the dtb is not correctly signed, then the dd step will put a new dtb in which the system won’t accept).

Here is a general blurb on device tree operations, and although it was for a TX1 (file names will probably differ in some cases since they are a combination of base board name and carrier board name…or a subset thereof), it was for R28.2 and the R28.2 generic device tree information is the same as that of a TX2. You may find references to a patch to flash.sh, but R28.2.1 has this in place already.
https://devtalk.nvidia.com/default/topic/1037131/jetson-tx1/does-anybody-know-how-to-enable-and-program-an-spi-protocol-on-the-tx1-/post/5269528/#5269528

Generally speaking, there is only one device tree. When looking at the files of the kernel source each file is just some subset. There is a “make dtbs” kernel target for building device trees, but beware there are also setup steps. It is usually much easier to just extract a device tree from the running Jetson as a whole. In the above URL you can search for this which explains:

dtc -I fs -O dts -o extracted.dts /proc/device-tree

It is always worth saving a backup of this extracted device tree when you have a running system. You might want it for later reference or comparison to other device trees even if you never flash a device tree.

In cases where you see dd being used you must also go through steps to sign first. If you were to flash just the dtb on a TX2 (and let flash.sh do the signing) it would go like this if you have edited the existing dtb file from the driver package and replaced the one normally used for flash:

sudo ./flash.sh -r -k kernel-dtb jetson-tx2 mmcblk0p1

Depending on board you might find the right file to reverse compile, edit, and then recompile and put in place is this one:

/where/ever/it/is/Linux_for_Tegra/kernel/dtb/tegra186-quill-p3310-1000-c03-00-base.dtb

(note there is also an “a03” variant of the “c03” file…I believe this is for older revisions)

Note that in theory (there may be exceptions) the file I just named is the one which actually created the current existing device tree (it is a fusion of pieces of device tree combined during a kernel build “make dtbs” where the config matches what your installed R28.2.1 TX2 is using). Restated, flashing with this dtb file is most likely the same as doing nothing on a TX2 with R28.2.1. An equivalent command to the first one flashing the kernel-dtb would specifically name that file:

sudo ./flash.sh -r -d ./kernel/dtb/tegra186-quill-p3310-1000-c03-00-base.dtb jetson-tx2 mmcblk0p1

Reverse compiling tegra186-quill-p3310-1000-c03-00-base.dtb, editing this, and then recompiling is probably the route I would choose for your case (your device tree is not customized).

A word of caution: The “-r” option says to “reuse” the rootfs’s system.img. In some cases the result is that you just flash the dtb and the rootfs is never touched. In other cases, this results in not generating a new rootfs, but still flashing based on what is already in “bootloader/system.img”. You are advised to back up any rootfs before you experiment (I think the above will leave your rootfs alone, but I won’t guarantee it). If you are interested in cloning, then see this:
http://elinux.org/Jetson/TX2_Cloning
https://devtalk.nvidia.com/default/topic/1000105/?comment=5111893

As mentioned earlier, there was a patch, but this applied to R28.1. Your R28.2.1 does not need any patch to clone.

To emphasize something from cloning: Both a “backup.img” and “backup.img.raw” are generated. You can’t do anything with “backup.img” other than restore with it. I delete my “backup.img” and save “backup.img.raw”. This latter file is very large, but can be loopback mounted, examined, edited, and used for flash (a longer flash versus backup.img since it is larger).

If you ever want to log an operation from flash.sh or any command line tools of the “Linux_for_Tegra/” directory (which is what the driver package is), then just append this to the command (and I suggest logging any flash for help if something goes wrong):

2>&1 | tee log.txt

So for example:

sudo ./flash.sh -r -k kernel-dtb jetson-tx2 mmcblk0p1 2>&1 | tee log.txt

Hi Jim,

If you refer to the ‘Kernel Customization’ chapter in the L4T documentation, the first step refers to obtaining the kernel sources. So really you first need to obtain the kernel sources, and then just use your favourite text editor to edit the ‘tegra186-quill-common.dtsi’ file.

Personally, I re-compile the device-tree blob from source versus extracting the source from the blob on the target. The bootloader modifies the device-tree image on boot and so what is extracted is a bit different from the original blob (although it should probably not matter). However, I also prefer to keep track of my changes I have made to device-tree with git. Therefore, if you move to a newer release of L4T it maybe easier to keep track of your changes.

Regards,
Jon

Thank you both.

Question:
Can all of this be done on the Target Machine (T2 board) or does it need to be done on a remote Host pc?

All of this includes: download source, edit source, re-compile device-tree, etc…
Thanks

I always do all of this on the remote host. It will be much quicker, plus all the sources take up quite a bit of space.

Regards,
Jon

Hello Jon,
my target machine is Ubuntu 16.4, L4T Version: R28, Revision 2.1 therefore which of the compressed source file(s) do I need to download? Maybe only one file: L4T TX2 Sources ?

Also, I downloaded this file and extracted the kernel_src.tbz2 file but I cannot find the tegra186-quill-common.dtsi file.

Hi Jim,

That’s the one. Should be under …

hardware/nvidia/platform/t18x/common/kernel-dts/t18x-common-platforms/

Regards,
Jon

By the way, I just checked the kernel_src.tbz2 the following archive and found it there …

https://developer.nvidia.com/embedded/dlc/sources-r2821

Regards,
Jon

Hello Jon, please verify the steps below:

  1. Source downloaded and extracted onto host computer into folder I created: home/source/public_release/kernel_src

  2. Edit the file:
    kernel_src/hardware/nvidia/platform/t18x/common/kernel-dts/t18x-common-platforms/tegra186-quill-common.dtsi

Edit only the “format” field in the Block:

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";
			<b>format = "i2s";</b>
			bitclock-slave;
			frame-slave;
			bitclock-noninversion;
			frame-noninversion;
			bit-format = "s16_le";
			bclk_ratio = <0>;
			srate = <48000>;
			num-channel = <2>;
			ignore_suspend;
			name-prefix = "x";
			status = "okay";
		};

Change:
format = “i2s”; to
format = “dsp_a”;

? what about num-channel = <2>; should it not be changed to the number of channels on the TDM input?

  1. Save the file changes
  2. Edit the file:
    kernel_src/hardware/nvidia/soc/t18x/kernel-dts/tegra186-soc/tegra186-soc-base.dtsi

Edit only the “fsync-width” field in the Block:

tegra_i2s1: i2s@2901000 {
			compatible = "nvidia,tegra210-i2s";
			reg = <0x2901000 0x100>;
			nvidia,ahub-i2s-id = <0>;
			clocks = <&tegra_car TEGRA186_CLK_I2S1>,
				<&tegra_car TEGRA186_CLK_PLL_A_OUT0>,
				<&tegra_car TEGRA186_CLK_I2S1_SYNC_INPUT>,
				<&tegra_car TEGRA186_CLK_SYNC_I2S1>;
			clock-names = "i2s1", "pll_a_out0", "ext_audio_sync",
						"audio_sync";
			pinctrl-names = "dap_active", "dap_inactive";
			pinctrl-0 = <>;
			pinctrl-1 = <>;
			<b>fsync-width = <31>;</b>
			status = "disabled";
		};

Change:
fsync-width = “31”; to
fsync-width = “0”;
4. Save the file changes

Is this correct so far?

Thanks

Hi Jim,

So far so good. With regard to the ‘num-channels’ you can update but it is not necesssary because the number of channels pass via userspace (ie. arecord) will override the default. Though it could be considered good practice to have the defaults match your actual use-case.

Regards
Jon

Hello Jon,

Finally getting back to try to complete this task.

I have questions about the compiling part of this process. I edited and save the two files as described, downloaded the utilities:

$ sudo apt install build-essential bc

Set the shell variable with the command:

$ TEGRA_KERNEL_OUT=<outdir>

with being a folder created under my Home folder.

I am at the 3rd step of Building the NVIDIA kernel where we create the .config file:

$ cd <kernel_source>
$ mkdir -p $TEGRA_KERNEL_OUT
$ make ARCH=arm64 O=$TEGRA_KERNEL_OUT <defconfig>

My questions:

  1. Is <kernel_sources> the “public_release” folder, which contains both the extracted “hardware” and “kernel” folders along with a number of non-extracted folders?
  2. I am not sure how to define , maybe “tegra_defconfig”? And what do I do with this file?

Thank you
Jim

The driver package for your release (see “head -n 1 /etc/nv_tegra_release” to see which L4T version) has a script called “source_sync.sh”. If you’ve flashed with JetPack, then the driver package would have been automatically downloaded. This is what produces the directory “Linux_for_Tegra/” on the PC host. That script can be copied to a Jetson if desired and run there, but this is how you’d download kernel source including the other associated out-of-tree directories. Example for R28.2.1 kernel download:

./source_sync.sh -k tegra-l4t-r28.2.1

Within this the “source/kernel/kernel-4.4/” is the kernel source where you’d issue any “make” commands from. Your “$TEGRA_KERNEL_OUT” is where any “.config” file would go (and “make tegra18_defconfig” with that set produces a new “.config” there).

I highly recommend you save a copy of your running system’s “/proc/config.gz” to use as a starting configuration. The defconfig files are not always what you expect. If you were to use the defconfig, then the syntax is that the TX2 has the Tegra series “t18x”, and that the specific SoC used on the TX2 is the “t186”. So the TX2 is the “tegra18_defconfig”. But save yourself some effort and always keep a protected copy of the original running system’s config for future reference. If this file has “CONFIG_LOCALVERSION” edited to match the running system (usually CONFIG_LOCALVERSION="-tegra"), then you have an exact match until you edit to add/remove a feature. Having this file named as “.config” in the build tree output location takes the place of “make tegra18_defconfig”.

Hi Jim,

  1. Should be the ‘kernel’ directory.
  2. Yes per the feedback in comment #18, this will be ‘tegra18_defconfig’ for TX2. This file simply contains the kernel configuration and the make command is simply setting the default kernel configuration for building the kernel.

Regards,
Jon

Hello,

I issued the command:

$ make ARCH=arm64 O=$TEGRA_KERNEL_OUT tegra18_defconfig

and the following was returned:

make: *** No rule to make target 'tegra18_defconfig'.  Stop.

Thanks
Jim