TK1 maximum I2S bit clock frequency

Hi,

I’m running a K1 SoC in slave mode TDM I2S, requiring a bus bit clock speed in excess of 25 MHz for an 8ch/32bit/96k audio stream.

I’m wondering, is there an upper bound on the frequency of the bit clock? I am seeing corrupted data reading from the bus, and am considering that the internal sync clock of 24 MHz may not be fast enough to allow the data to be clocked in correctly. The kernel seems to have a maximum of 24 MHz for many of the I2S peripheral clocks, as far as i can tell.

I see indication in the TRM that SPI slave mode requires an internal clock of at least 1.5 times an external salve clock, but i don’t see anything similar for the I2S blocks.

Thanks,

Ed

Hi Ed,

Please see section “4.5.1 I2S, PCM and TDM” of the Tegra K1 data sheet [0], it states …

“The I2S and PCM (master and slave modes) interfaces support clock rates up to 24.5760MHz.”

This should be sufficient for your use-case of 8ch/32bit @ 96kHz. By the way, you said in excess of 25MHz, did you mean 24MHz?

Regards,
Jon

[0] http://developer.nvidia.com/embedded/dlc/tegra-k1-datasheet

Hi Ed,

With regard to the kernel limiting the clock to 24MHz, I am not sure why this is. However, I recently fixed an issue on Tegra X1 that had the same problem [0]. Unfortunately, I don’t know why Tegra TK1 and earlier were limited to 24MHz and if there is a reason for this and so although this does conflict with our documentation, I cannot guarantee this works (as clearly it has not been tested). However, Tegra X1, Tegra X2 and beyond do support 24.576MHz and so migrating to these newer devices could be a consideration.

Regards,
Jon

[0] nv-tegra.nvidia Code Review - linux-4.4.git/commit

Hi Jon,

Thanks for the pointers, i’ve changed the clocks to 24.576MHz, essentially as follows for the 3.10 NVIDIA L4T kernel, resulting in the initialisation and maximum frequencies being updated:

diff --git a/arch/arm/mach-tegra/tegra12_clocks.c b/arch/arm/mach-tegra/tegra12_clocks.c
index 664d41c01278..5dbecc0a583f 100644
--- a/arch/arm/mach-tegra/tegra12_clocks.c
+++ b/arch/arm/mach-tegra/tegra12_clocks.c
@@ -7085,8 +7085,8 @@ static struct clk tegra_pciex_clk = {
                        .dev_id    = #_dev ,            \
                        .con_id    = "ext_audio_sync",  \
                },                                      \
-               .rate      = 24000000,                  \
-               .max_rate  = 24000000,                  \
+               .rate      = 24576000,                  \
+               .max_rate  = 24576000,                  \
                .ops       = &tegra_sync_source_ops     \
        }
@@ -7137,7 +7137,7 @@ static struct clk_mux_sel mux_audio_sync_clk[] =
                },                                      \
                .inputs    = mux_audio_sync_clk,        \
                .reg       = 0x4A0 + (_index) * 4,      \
-               .max_rate  = 24000000,                  \
+               .max_rate  = 24576000,                  \
                .ops       = &tegra_audio_sync_clk_ops  \
        }
 static struct clk tegra_clk_audio_list[] = {
@@ -7157,7 +7157,7 @@ static struct clk tegra_clk_audio_list[] = {
                        .con_id    = "audio_sync_2x"            \
                },                                              \
                .flags     = PERIPH_NO_RESET,                   \
-               .max_rate  = 48000000,                          \
+               .max_rate  = 49152000,                          \
                .ops       = &tegra_clk_double_ops,             \
                .reg       = 0x49C,                             \
                .reg_shift = 24 + (_index),                     \

(…25MHz was a typo in the previous post, indeed)

This seems to have cleared up a problem with the data not keeping in sync with the frame sync, so that looks a lot better. I can’t say it works 100% yet, because i haven’t managed to get verifiable data from I2S via ALSA, likely for other reasons. Although, with a logic analyser, the data on the bus seems correct, as far as i can tell.

To put it another way, I can’t tell whether the data is in fact being clocked in correctly by the I2S block at 25.576 MHz, but the data it’s sending looks ok.

Thanks,
Ed

As it turns out, the data corruption i’m seeing on the I2S at 24.576 MHz bit clock is due to the tegra sampling at half the bit clock rate, once the sync clock has been set to 24.576 MHz.

Might increasing the 2x clock to double the sync clock rate improve matters here?

Thanks.

Hi Ed,

I am surprised it would be half and no I would not think it is necessary to increase the clock any more.

Are you using DSP-A or DSP-B frame format? Looking at the 3.10 I2S driver I am not sure if it is configuring the I2S port correctly (however, that may not explain the 1/2 sampling you are seeing).

Are you able to dump the I2S registers via the debugfs? You should see a entry for each I2S interface under /sys/kernel/debug/regmap/ and if you ‘cat’ the ‘registers’ file for the I2S interface you are using (after you have started the capture) you should be able to view their configuration.

Regards
Jon

Hi Jon,

Currently using SND_SOC_DAIFMT_DSP_B. This could be changed, though.

These are the registers with both I2S i/f’s running:

/sys/kernel/debug/regmap/tegra30-i2s.1
00: 80001207
04: 000000ff
08: 00000000
0c: 1f000000
10: 00000007
14: 00777704
18: 00777700
1c: 8002c060
20: 00008000
24: 00000000
28: 00000000
2c: 00000000
30: 00000000
34: 0000002e
38: 0000f9e6
3c: 000020ca
40: 00007147
44: 0000f17e
48: 000001e0
4c: 00000117
50: 0000f26b
54: 00004c07
64: 00ff00ff
/sys/kernel/debug/regmap/tegra30-i2s.3
00: 80001207
04: 000000ff
08: 00000000
0c: 1f000000
10: 00000007
14: 00777704
18: 00001100
1c: 80009002
20: 00008000
24: 00000000
28: 00000000
2c: 00000000
30: 00000000
34: 0000002e
38: 0000f9e6
3c: 000020ca
40: 00007147
44: 0000f17e
48: 000001e0
4c: 00000117
50: 0000f26b
54: 00004c07
64: 000000ff

What we are seeing, is that if you send fixed values to the bus, and read them back via a hardware loopback, you get results such as:

Tx 0x1 => Rx 0x00000000

	00000001
	00000000

Tx 0x2 => Rx 0x00000000

	00000010
	00000000

Tx 0x3 => Rx 0x00000001

	00000011
	00000001

Tx 0x4 => Rx 0x00000000

	00000100
	00000000

Tx 0xE => Rx 0x00000006

	00001110
	00000110

Tx 0xF => Rx 0x00000007

	00001111
	00000111

…plus, fairly repeatably, pulses longer than or equal to 2 clocks being clocked in, but shorter than that, not.

Thanks,
Ed

Hi Ed,

Looking at the I2S registers, I see that both I2S interfaces have the master bit set in the I2S_CTRL_0 register (bit 10 in register at offset 0x00). Furthermore, both have bit 31 set in the same register implying that both have TX enabled and not RX enabled which seems odd.

Looking back at the 3.10 kernel it appears that the I2S master configuration is configured by the platform data ‘ardbeg_audio_pdata_rt5639’ structure. Have you made some changes to this to set the channels format, etc? If so you need to ensure that one of the I2S interfaces is a slave so they are both not trying to drive the bit clock and frame sync.

With regard to DSP-B mode, I can see that the frame-sync width is 32 bit clocks and not 1 which for loopback maybe ok, but eventually needs to be corrected.

Regards,
Jon

Hi Jon,

Thanks for looking into this.

It turns out the issue wasn’t due to the Tegra I2S interface at all, but a subtle timing issue with how the data was being clocked onto the bus.

For testing, the loopback is a direct wire loopback from each interface’s TX to its RX, within an FPGA (with some nanosecond scale delay). By instead accurately clocking the data out from the FPGA with BCLK, we’re now seeing pristine data writing to, and reading from the bus. So the half sample rate was a red herring.

Both interfaces should be slave to the master FPGA, and both are now reliably reading and writing data, so i’m not sure what effect those register settings are having. I will have a closer look and see if the setup needs refining.

Nice spot re. the FSYNC width, but since we’re defining both ends of the communication, I believe this is adequate for our needs, since we’re really using “I8S”.

Thanks,
Ed

Hi Ed,

Thanks for the update. I did not realise that there was an FPGA in the path. I have tested external digital loopback from one I2S to another on another Tegra platform and so had assumed that this is what you were doing to ensure data integrity.

Regards,
Jon