How to port OAK8x module to Jetson Nano

I want to use OAK8x module on the 40-pin header of Jetson Nano. The OAK8X module is an 8-port FXO using the SPI/I2S bus architecture.

As far as I know , I need to modify the DAHDI dirver. In the pidma.c file, how do I modify the start_i2s_tx(), setup_i2s(), stop_i2s() functions, and which I2S registers do I need to set for Jetson Nano.

oak8x/software/drivers/dahdi/oak8x/pidma.c
oak8x/software/drivers/dahdi/oak8x/pitdm.c

OAK8X module specifications

Manual of install OAK8X module

oak8x driver

Hi will_lin,

I had a look at DAHDI drivers you referenced. In the current shape, these drivers seem to be tightly coupled with Rasperry Pi HW. However it looks like you would need following operations to get OAK8X module working.

  1. SPI based control interface. This I would assume is required to get OAK8X module up and running. I did not find SPI device details in " Manual of install OAK8X module". I am thinking the details are hardcoded in the driver itself.
  2. The I2S configuration from SoC side. I guess you are also expecting to configure FXS/FXO lines on OAK8x side.

The driver architecture is bit different on Jetson [0]. There are separate drivers for DMA and I2S unlike the references you have pointed above. But before proceeding I would like to understand bit more about your requirement and have couple of questions.

  1. Is your application uses (or can use) ALSA/ASoC? or you are looking for a standalone driver module?
  2. What are the I2S configuration (like sample rate, channels, bits per sample) you would need? Do you need static I2S configurations or it depends on the application at runtime?

Thanks,
Sameer.

[0] https://docs.nvidia.com/jetson/l4t/index.html#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/asoc_driver.19.2.html#wwpID0E0FGB0HA

Hi Sameer:

Thanks for your reply.

  1. Asterisk is the application layer, Asterisk uses DAHDI to read/write SPI/PCM data. The official DAHDI only supports PCI interface, but what we need is SPI/PCM interface.
    The oak8x source code use the official Dahdi-2.11.1 and add pitdm.c pidma.c to support SPI/PCM interface 8 port Si3050, so we want to add nano_tdm.c nano_dma.c for Jetson Nano to support 8 port Si3050.
    If ALSA/ASoC can do it, we can try too. Our goal is how to read/write SPI/PCM data from DAHDI driver on Jetson Nano.
  2. 8000 sample rate, 8 channels, 16 bits

Actually, I can read/write the GPIO/SPI data by DAHDI on Jetson Nano now, but PCM not yet, I think the key point is how to read/write PCM data by DAHDI on Jetson Nano PCM DTX/DRX pin.

This is Si3050 Spec, page 48 is the multiple port Si3050 schematic.

The pitdm.c wctdm_init_proslic( ) is the Si3050 initialization.

Hi will_lin,

I had a look at Si3050 Spec. It appears to me that Jetson Nano I2S interface needs to be configured in FSYNC master mode to exchange PCM data (corresponding lines PCLK, FSYNC, DTX and DRX on Si3050).

Currently Jetson Nano uses ASoC drivers to setup DMA and I2S interfaces that would be required to read/write PCM data. I think this would be the simplest option to try first. For understanding you can refer to the “Audio Setup and Development” section in the BSP doc and you can specifically refer below for a quick glance.

  • ADMAIF in Platform driver section (PCM driver interface)
    source code: kernel/nvidia/sound/soc/tegra-alt/tegra210_admaif_alt.c
  • I2S in Codec driver section (Applying necessary configurations for I2S)
    source code: kernel/nvidia/sound/soc/tegra-alt/tegra210_i2s_alt.c
  • I2S mode setting
  • Usage and examples

The Jetson Nano I2S interface on 40-pin header by default is connected dummy codec endpoint. Generally depending on the audio cards that are connected to 40-pin header, specific ASoC codec driver (if available) can be plugged in. In your case I assume there is no ASoC codec driver available for Si3050. But that should not stop you from using ASoC framework. Let Jetson Nano I2S drive dummy codec endpoint. What you would need to ensure is, Si3050 is initialized properly and ready for use.

Check if following steps/commands work for you on the Jetson Nano.

  1. Update DT with following
    diff --git a/tegra210-porg-p3448-common.dtsi b/tegra210-porg-p3448-common.dtsi
    index 2a7d844…689437a 100644
    — a/tegra210-porg-p3448-common.dtsi
    +++ b/tegra210-porg-p3448-common.dtsi
    @@ -414,7 +414,8 @@
    codec-dai = <&spdif_dit0>;
    cpu-dai-name = “I2S4”;
    codec-dai-name = “dit-hifi”;
    - format = “i2s”;
    + format = “dsp_a”;
    + fsync-width = <0>;
    bitclock-slave;
    frame-slave;
    bitclock-noninversion;

  2. Ensure if pinmux for I2S on 40-pin header is configured properly

  3. amixer -c tegrasndt210ref cset name=“I2S4 Mux” “ADMAIF1” (for sending data to Si3050)

  4. amixer -c tegrasndt210ref cset name=“ADMAIF1 Mux” “I2S4” (for receiving data from Si3050)

  5. Initialize Si3050

  6. Data send/receive commands
    aplay -D hw:tegrasndt210ref,0 <send.wav>
    arecord -Dhw:tegrasndt210ref,0 -c 8 -r 8000 -f S16_LE -d 15 <receive.wav>

In case you face any difficulties please refer to “Troubleshooting” section or reach out to us.

Thanks,
Sameer.

Hi Sameer:

Yes, this way is no problem, we have already implemented it before. Si3050 can be initialized correctly and work normally.

But I don’t know how to integrate it to DAHDI? Do you have any suggestions?

Thanks,
Will

Hi Will,

If I understand this correctly you have following driver files for configuration and data transfers.
oak8x/software/drivers/dahdi/oak8x/pidma.c
oak8x/software/drivers/dahdi/oak8x/pitdm.c
And apart from this there are other DAHDI drivers which support the HW you have. So you have Asterisk application layer which relies on the DAHDI drivers for telephonic application.

  1. I am not sure if Asterisk application can make use of ASoC. May be Asterisk support portal should be able to comment if some one has tried something similar (I think it will be good to check this). If it is possible, then your DAHDI driver can just be another linux driver to configure your HW and you can make use of ASoC to configure Jetson Nano and control data.

  2. If this is not possible, then it may not leave much choice other than combining all functionalities in one driver, like what you have for RPi and for this you have to refer to multiple drivers on Jetson Nano. This may be the last option when [1] does not work.

Thanks,
Sameer.

Hi Sameer,

As mentioned earlier, Si3050 can be initialized correctly and work normally. It uses the test tool (proslic_tool) provided by the Si3050 agent manufacturer. It uses spidev0.0 and ALSA to control SPI and send PCM, not use DAHI driver.

But now, we need multiple ports Si3050, dahdi.ko implements the ring detecting, onhook, offhook, and other control events. So we need DAHDI to implement it. The figure 2 is what we want to do.

I modified __spi_read() and __spi_write() functions on pidtdm.c pidma.c with read/write spidev0.0 method, then I insmod dahdi.ko, pitdm.ko pidma.,ko on Nano, it can read the Si3030 ID, but PCM read/write is not implement yet.

Asterisk supports chan_dahdi and chan_alsa module.

  1. chan_alsa uses for headphones and microphones.
    Asterisk config alsa.conf - VoIP-Info
  2. chan_dahdi uses for Digium card.
    Analog Phones

If I use chan_alsa, I need to implement multi-port Si3050 configurations.
If I use chan_dahdi, I need to modify pitdm.c pidma.c for Nano.

When [1] does not work, which drivers should we refer to?

This is Asterisk + DAHDI + PCI interface + TDM800P/TC400M telephony cards.

This is what we want to do. Asterisk + DAHDI + SPI/PCM interface + Si3050

Thanks,
Will

Hi Sameer:

We have been studying ALSA, DAHDI, Asterisk for three months, but we still don’t know how to implement this requirement.

In the RPi platform, they removed interrupts 25, 26 and DMA 9, 10 in the bcm283x.dtsi file, and added the tdm bus definition after the i2s section.

https://github.com/lixinswitchpi/oak8x

                dma: dma@7e007000 {
                        compatible = "brcm,bcm2835-dma";
                        reg = <0x7e007000 0xf00>;
                        interrupts = <1 16>,
                                     <1 17>,
                                     <1 18>,
                                     <1 19>,
                                     <1 20>,
                                     <1 21>,
                                     <1 22>,
                                     <1 23>,
                                     <1 24>,
                                     <1 25>, //remove
                                     <1 26>, //remove
                                     /* dma channel 11-14 share one irq */
                      
			  interrupt-names = "dma0",
                                          "dma1",
                                          "dma2",
                                          "dma3",
                                          "dma4",
                                          "dma5",
                                          "dma6",
                                          "dma7",
                                          "dma8",
                                          "dma9",  //remove
                                          "dma10", //remove
                                          "dma11",
                                          "dma12",
                                          "dma13",
                i2s: i2s@7e203000 {
                        compatible = "brcm,bcm2835-i2s";
                        reg = <0x7e203000 0x24>;
                        clocks = <&clocks BCM2835_CLOCK_PCM>;

                        dmas = <&dma 2>,
                               <&dma 3>;
                        dma-names = "tx", "rx";
                        status = "disabled";
                };

                // new section to add for oak8x:
                tdm: tdm@7e203000 {
                        compatible = "brcm,pi-tdm";
                        reg = <0x7e203000 0x20>,
                              <0x7e101098 0x02>;

                        dmas = <&dma 2>,
                               <&dma 3>;
                        dma-names = "tx", "rx";
                        interrupts = <1 25>, <1 26>;
                        interrupt-names = "dma9", "dma10";
                        status = "okay";
                };

How do I add tdm bus to Jetson Nano platform?

Thanks,
Will

Hi Will,

Sorry for the delayed reply.

I can point relevant drivers on Jetson Nano for you. You can write a custom driver based on these references and integrate with DAHDI drivers the way you are doing for RPi.

DMA driver: kernel/kernel-4.9/drivers/dma/tegra210-adma.c
PCM driver: kernel/nvidia/sound/soc/tegra-alt/utils/tegra_pcm_alt.c
ADMAIF driver: kernel/nvidia/sound/soc/tegra-alt/tegra210_admaif_alt.c
I2S driver: kernel/nvidia/sound/soc/tegra-alt/tegra210_i2s_alt.c

You can refer to relevant device tree nodes. Finally you could define a single TDM device in DT and combine different SW pieces from above in one driver.

But I still think it would be great if chan_alsa can be used for your application. It will be preferable for us to provide a SW support.

Thanks,
Sameer.

Hi Sameer:

Two questions. Thanks.

  1. There is a PCIe interface on Jetson Nano. If we choose a DAHDI card with PCIe interface, we can directly use DAHDI driver without modifying DMA, PCM, ADMAIF, I2S driver, right?

  2. If we do not choose the DAHDI card with PCIe interface, we must customize the driver. Could you provide a rough sample code? Also, do you have the AHUB datasheet? I need to know which registers need to be set.

Thanks,
Will

Hi Will,

  1. Yes, if you could use PCIe interface for your DAHDI card you won’t require the DMA and ASoC driver references which I mentioned earlier.

  2. I forgot to mention AHUB driver in my earlier references. Please refer to “kernel/nvidia/sound/soc/tegra-alt/tegra210_xbar_alt.c”. This driver programs switches for required audio path. It uses user space controls to enable specific path.
    You can also refer to TRM document to get details about the specific registers, https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual
    Honestly speaking, I don’t want to entertain writing customized drivers and prefer using current design as much as possible.

Thanks,
Sameer.

Hi Sameer,
After our internal discussion, we prefer to use SPI/PCM interface, and we are still studying how to implement it, if any question we will let you know.

Thanks,
Will

Hi Sameer,
I try to use devmem2 tools to control the SPI register, but the system halt, these are my test step.
How do I correctly use the devmem2 tool to control the SPI register on Jetson Nano?

I add the printk in tegra_spi_readl() and tegra_spi_writel() on spi-tegra114.c

static inline u32 tegra_spi_readl(struct tegra_spi_data *tspi,
		unsigned long reg)
{
    printk("tegra_spi_readl(), reg:0x%lx\n", reg);

	return readl(tspi->base + reg);
}

static inline void tegra_spi_writel(struct tegra_spi_data *tspi,
		u32 val, unsigned long reg)
{
	/* Read back register to make sure that register writes completed */
	if ((reg == SPI_COMMAND1) && (val & SPI_PIO))
		readl(tspi->base + SPI_COMMAND1);

    printk("tegra_spi_writel(), reg:0x%lx, val:0x%x\n", reg, val);

	writel(val, tspi->base + reg);
}

I send one byte using spidev_test tool.

root@pesi-desktop:/home/pesi/t4_module_spi_slave_test# ./spidev_test -D /dev/spidev0.0 -s 1000000 -H -O -p "\x55"
spi mode: 0x3
bits per word: 8
max speed: 1000000 Hz (1000 KHz)
RX | 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __  | .

The spi-tegra114.c debug log are as follows:

Mar  5 17:03:09 pesi-desktop kernel: [  173.863406] tegra_spi_readl(), reg:0x18c
Mar  5 17:03:09 pesi-desktop kernel: [  173.863453] tegra_spi_writel(), reg:0x18c, val:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.863465] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.863851] tegra_spi_readl(), reg:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.865424] tegra_spi_readl(), reg:0x18c
Mar  5 17:03:09 pesi-desktop kernel: [  173.865436] tegra_spi_writel(), reg:0x18c, val:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.865444] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.865493] tegra_spi_readl(), reg:0x18c
Mar  5 17:03:09 pesi-desktop kernel: [  173.865502] tegra_spi_writel(), reg:0x18c, val:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.865510] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.869857] tegra_spi_readl(), reg:0x14
Mar  5 17:03:09 pesi-desktop kernel: [  173.869878] tegra_spi_readl(), reg:0x10
Mar  5 17:03:09 pesi-desktop kernel: [  173.869896] tegra_spi_writel(), reg:0x10, val:0xff0000
Mar  5 17:03:09 pesi-desktop kernel: [  173.869912] tegra_spi_writel(), reg:0x0, val:0x73e01807
Mar  5 17:03:09 pesi-desktop kernel: [  173.869922] tegra_spi_writel(), reg:0x108, val:0x55
Mar  5 17:03:09 pesi-desktop kernel: [  173.869929] tegra_spi_writel(), reg:0x24, val:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.869959] tegra_spi_writel(), reg:0x0, val:0xf3e01807
Mar  5 17:03:09 pesi-desktop kernel: [  173.870020] tegra_spi_readl(), reg:0x14
Mar  5 17:03:09 pesi-desktop kernel: [  173.870047] tegra_spi_readl(), reg:0x10
Mar  5 17:03:09 pesi-desktop kernel: [  173.870070] tegra_spi_writel(), reg:0x10, val:0x40ff0001
Mar  5 17:03:09 pesi-desktop kernel: [  173.870187] tegra_spi_readl(), reg:0x188
Mar  5 17:03:09 pesi-desktop kernel: [  173.870271] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.870280] tegra_spi_readl(), reg:0x0
Mar  5 17:03:09 pesi-desktop kernel: [  173.870311] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.870318] tegra_spi_writel(), reg:0x0, val:0x73d08000
Mar  5 17:03:09 pesi-desktop kernel: [  173.870519] tegra_spi_readl(), reg:0x0

Run “devmem2 0x7000d58c” to read SPI interrupt mask register (0x18c), the system do not response and halt.

root@pesi-desktop:/home/pesi/spitest# cat /proc/iomem | grep spi
7000d400-7000d5ff : /spi@7000d400
7000d600-7000d7ff : /spi@7000d600

root@pesi-desktop:/home/pesi/spitest# devmem2 0x7000d400
~~ No any response ~~ 

Thanks,
Will Lin

Hi Will,

For some reason I was NOT notified of your query and sorry for the delayed reply.
I am no SPI driver/tool expert. But I have a comment on devmem2.

I think SPI device you are trying to program is runtime suspended. That means it won’t have clock or power. That is probably the reason why you are seeing system halt.

You might be seeing following output.

cat /sys/devices/platform/*.spi/power/runtime_status
suspended
suspended

You can check if following helps. Below keeps SPI device runtime resumed always and I believe you should be able to use devmem2 tool to program specific registers.

diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index e382a88..bd9b446 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1927,7 +1927,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
        tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1);
        tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2);
        tspi->command2_reg = tegra_spi_readl(tspi, SPI_COMMAND2);
-       pm_runtime_put(&pdev->dev);
+       //pm_runtime_put(&pdev->dev);
        ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
                                   tegra_spi_isr_thread, IRQF_ONESHOT,
                                   dev_name(&pdev->dev), tspi);

Thanks,
Sameer.

Hi Sameer:

I remark the pm_runtime_put() function, I can get the “SPI Command1 Register(0x7000d400)” data, but the “SPI Interrupt Mask Register(0x7000d58c)” still fails, show “Bus error (core dumped)”

root@pesi-desktop:/home/pesi# cat /sys/devices/7000d400.spi/power/runtime_status
active
root@pesi-desktop:/home/pesi# devmem2 0x7000d400
/dev/mem opened.
Memory mapped at address 0x7fad770000.
Value at address 0x7000D400 (0x7fad770400): 0x73D08000
root@pesi-desktop:/home/pesi# devmem2 0x7000d58c
/dev/mem opened.
Memory mapped at address 0x7fac146000.
Bus error (core dumped)

Thanks,
Will Lin

Hi Will,

Can you please check if pointers provided in following post help?

If above does not help, I will check if this can be re-directed to a SPI expert to get more direct answers.

Thanks,
Sameer.

Hi Sameer,

In the post, Bibek say “make sure that controllers power and clock is always on”.
I have always turned on the power, but how do I always turn on the clock?

echo on > /sys/devices/7000d400.spi/power/control

Thanks,
Will

Hi Will,

Did you try dumping registers after you ran below command?
echo on > /sys/devices/7000d400.spi/power/control

I checked on my device and can dump the registers. No hang. Please note that clock is turned ON.

root@tegra-ubuntu:/home/ubuntu# cat /sys/kernel/debug/clk/sbc1/clk_state
0
loot@tegra-ubuntu:/home/ubuntu# echo on > /sys/devices/7000d400.spi/power/control
root@tegra-ubuntu:/home/ubuntu# cat /sys/kernel/debug/clk/sbc1/clk_state
1
root@tegra-ubuntu:/home/ubuntu# ./devmem2 0x7000d400
/dev/mem opened.
Memory mapped at address 0x7f89c89000.
Value at address 0x7000D400 (0x7f89c89400): 0x47D08000
root@tegra-ubuntu:/home/ubuntu#
root@tegra-ubuntu:/home/ubuntu#
root@tegra-ubuntu:/home/ubuntu# ./devmem2 0x7000d58c
/dev/mem opened.
Memory mapped at address 0x7f92af4000.
Value at address 0x7000D58C (0x7f92af458c): 0x00000000
root@tegra-ubuntu:/home/ubuntu#
root@tegra-ubuntu:/home/ubuntu#

Thanks,
Sameer.

Hi Sammer,

Yes, I try it, but my device shows “Bus error (core dumped)”.
I will try another device, thanks for your help.

root@pesi-desktop:/home/pesi# cat /sys/kernel/debug/clk/sbc1/clk_state
0
root@pesi-desktop:/home/pesi# echo on > /sys/devices/7000d400.spi/power/control
root@pesi-desktop:/home/pesi# cat /sys/kernel/debug/clk/sbc1/clk_state
1
root@pesi-desktop:/home/pesi# devmem2 0x7000d400
/dev/mem opened.
Memory mapped at address 0x7fab7a4000.
Value at address 0x7000D400 (0x7fab7a4400): 0x73D08000
root@pesi-desktop:/home/pesi# devmem2 0x7000d58c
/dev/mem opened.
Memory mapped at address 0x7f97474000.
Bus error (core dumped)

Thanks,
WIll

Hi Sammer,

I read the register 0x7000d58c by byte or halfword, it fine. But I read the 0x7000d58c by word, it core dumped. Why?

root@pesi-desktop:/home/pesi# devmem2 0x7000d58c b
/dev/mem opened.
Memory mapped at address 0x7fa8d08000.
Value at address 0x7000D58C (0x7fa8d0858c): 0x0

root@pesi-desktop:/home/pesi# devmem2 0x7000d58c h
/dev/mem opened.
Memory mapped at address 0x7f92996000.
Value at address 0x7000D58C (0x7f9299658c): 0x0

root@pesi-desktop:/home/pesi# devmem2 0x7000d58c w
/dev/mem opened.
Memory mapped at address 0x7f8382b000.
Bus error (core dumped)
root@pesi-desktop:/home/pesi# devmem2 0x7000d414 b
/dev/mem opened.
Memory mapped at address 0x7f874a3000.
Value at address 0x7000D414 (0x7f874a3414): 0x5

root@pesi-desktop:/home/pesi# devmem2 0x7000d414 h
/dev/mem opened.
Memory mapped at address 0x7fb5c91000.
Value at address 0x7000D414 (0x7fb5c91414): 0x5

root@pesi-desktop:/home/pesi# devmem2 0x7000d414 w
/dev/mem opened.
Memory mapped at address 0x7f90cee000.
Bus error (core dumped)

Thnaks,
WIll