iMX6 DMA, SSI in TDM mode

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

iMX6 DMA, SSI in TDM mode

8,363 Views
robyf
Contributor IV

Hi All,

I'm writing a TDM driver for linux to connect a SLIC having FXS/FXO port via SSI configured in TDM mode.

The logic working with interrupt triggering RX/TX data available works fine but I would perform all the TDM trasfers from both TX/RX SSI FIFO via SDMA channel.

Is there an example how to do it considering that SSI can trigger SDMA transfers automatically?

Thanks in advance,

Roberto Fichera.

Labels (2)
10 Replies

5,971 Views
rosciio
Contributor I

Hi everyone,

i'm working on that argument. I would like configure the ssi into Network mode. I have done this, so, I modified the register to do it, but now I can't understand how linux show on user space "/proc/asound/card0/pcmX" X times my slot.

any one can explain me this?

thank you very much

Marco From Italy

0 Kudos

5,971 Views
igorpadykov
NXP Employee
NXP Employee

Hi Roberto

SSI with SDMA is supported by Linux, description can be found in

Chapter 4 Smart Direct Memory Access (SDMA) attached Linux Manual.

Best regards

igor

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------

0 Kudos

5,971 Views
robyf
Contributor IV

Hi Igor,

thanks for your answer. However I've already read this document but doesn't add much information to what I know. SPDIF and SSI driver actually add the sound layer API which doesn't really show how the SSI and DMA API can work together. I've set SSI_SIER to both CCSR_SSI_SIER_RDMAE and CCSR_SSI_SIER_TDMAE and both SSI_RFWM0 and SSI_TFWM0 FIFO to the TDM frame size. I know also that everything is set in the DTS

                                ssi1: ssi@02028000 {

                                        compatible = "fsl,imx6sx-ssi", "fsl,imx21-ssi";

                                        reg = <0x02028000 0x4000>;

                                        interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;

                                        clocks = <&clks IMX6SX_CLK_SSI1_IPG>,

                                                 <&clks IMX6SX_CLK_SSI1>;

                                        clock-names = "ipg", "baud";

                                        dmas = <&sdma 37 1 0>, <&sdma 38 1 0>;

                                        dma-names = "rx", "tx";

                                        status = "enabled";

What I don't understand is how can I set the DMA correctly so that SSI can trigger DMA transfers automatically. Would be great an example of code.

Cheers,

Roberto Fichera.

5,971 Views
igorpadykov
NXP Employee
NXP Employee

Hi Roberto

one can look at SDK SDMA examples for uart or cspi,

as they work simlar to SSI

"MX6_PLATFORM_SDK "

https://community.freescale.com/docs/DOC-94139

Best regards

igor

0 Kudos

5,971 Views
robyf
Contributor IV

Hi Igor,

Finally I had some time to get back to this. I was able to make it partially working, the code isn't nothing complex at all and a good starting point is the imx uart within the kernel. However after receiving roughly 4000 frames 2 slots x 8 bits everything stops working. This happen using only one FIFO and setting the related watermark to trigger DMA transfer every frame so 2 slots. I will eventually publish some code tomorrow.

Cheers,

Roberto Fichera

0 Kudos

5,971 Views
robyf
Contributor IV

Hi Igor,

after doing some tests I've found that the SLIC si32178 connected to the SSI in TDM master mode and providing the clocks for both PCLK and FSYNC, seems not running at constant bitrate PCLK@512KHz and FSYNC@8KHz. As starting point I've used the function fsl_ssi_set_bclk() in sound/soc/fsl/fsl_ssi.c.

This function try to solve the equation at pag.4014 of the MX6SXRM manual by only finding the right PM variable roughly fitting the requested bit clock frequency.

Do you know if there is a better function that better try to match as much closer as possible BCLK and FSYNC rates by providing all DIV2, PSR and PM values?

Cheers,

Roberto Fichera.

5,971 Views
robyf
Contributor IV

Hi Igor,

After looking more closely to the PCM control pad configuration I've notice that was wrongly configured to operate with the bit LVE=1 (1.8V) instead of 0 (3.3V).

So now everything works pretty fine from the TDM point of view. There still a problem related to RX SDMA transfers which still suddenly stops after 4000 or chunks

of transfers. The DMA setup looks:

        if (ssi_private->use_dma)

        {

          ssi_private->tx_chan = dma_request_slave_channel_reason(&pdev->dev, "tx");

          if (IS_ERR(ssi_private->tx_chan))

          {

            dev_err(&pdev->dev, "could not get TX DMA\n");

            ssi_private->tx_chan = NULL;

          }

          else

          {

            slave_config.direction = DMA_MEM_TO_DEV;

            slave_config.src_addr = ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);

            slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;

            slave_config.src_maxburst = ssi_private->tdm_slots;

            ret = dmaengine_slave_config(ssi_private->tx_chan, &slave_config);

            if (ret) {

                    dev_err(&pdev->dev, "error in TX DMA configuration.\n");

            }

          }

          ssi_private->rx_chan = dma_request_slave_channel_reason(&pdev->dev, "rx");

          if (IS_ERR(ssi_private->rx_chan))

          {

            printk("could not get RX DMA\n");

            ssi_private->rx_chan = NULL;

          }

          else

          {

            slave_config.direction = DMA_DEV_TO_MEM;

            slave_config.src_addr = ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);

            slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;

            slave_config.src_maxburst = ssi_private->tdm_slots;

            ret = dmaengine_slave_config(ssi_private->rx_chan, &slave_config);

            if (ret) {

                    dev_err(&pdev->dev, "error in RX DMA configuration.\n");

            }

          }

        }

RX DMA looks like below, buffer len is typically tdm_slots * 8 bytes

    struct dma_async_tx_descriptor *desc;

    ssi_private->rx_callback = callback;

    ssi_private->rx_userparam = userparam;

    ssi_private->rx_buffer_len = buffer_len;

    ssi_private->rx_buffer_count = buffer_len;

    ssi_private->rx_buf = dma_alloc_coherent(NULL, buffer_len,

                                    &ssi_private->rx_dmaaddr, GFP_KERNEL);

    if (!ssi_private->rx_buf) {

            printk("cannot alloc RX DMA buffer.\n");

            return -ENOMEM;

    }

    desc = dmaengine_prep_dma_cyclic(ssi_private->rx_chan, ssi_private->rx_dmaaddr,

            buffer_len, ssi_private->tdm_slots,

            DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);

    if (!desc) {

            printk("Prepare for the RX slave dma failed!\n");

            return -EINVAL;

    }

    desc->callback = dma_rx_callback;

    desc->callback_param = ssi_private;

    printk("RX: prepare for the DMA.\n");

    dmaengine_submit(desc);

    dma_async_issue_pending(ssi_private->rx_chan);

    return 0;

Finally the SSI is set to use DMA

        flags = SIER_FLAGS;

        /*

         * if DMA is enabled than allow SSI request for DMA transfers

         * otherwise normal interrupt requests

         */

        if (ssi_private->use_dma)

        {

          flags |= CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_TDMAE;

        }

        else

        {

          flags |= CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_TIE;

        }

        /* Enable the interrupts and DMA requests */

        writel(flags, &ssi->sier);

        /*

         * Set the watermark for transmit FIFI 0 and receive FIFO 0. We

         * don't use FIFO 1.  We program the transmit water to signal a

         * DMA transfer if there are at least tdm_slots elements left

         * in the FIFO.

         *

         * We program the receive FIFO to notify us if at least tdm_slots

         * elements (one frame) have been written to the FIFO.  We could

         * make this value larger (and maybe we should), but this way

         * data will be written to memory as soon as it's available.

         */

        writel(CCSR_SSI_SFCSR_TFWM0(ssi_private->tdm_slots) |

               CCSR_SSI_SFCSR_RFWM0(ssi_private->tdm_slots) |

               CCSR_SSI_SFCSR_TFWM1(ssi_private->tdm_slots) |

               CCSR_SSI_SFCSR_RFWM1(ssi_private->tdm_slots),

               &ssi->sfcsr);

        /* disable FIFO1 */

        write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1, 0);

        write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1, 0);

        /* disable SSI two-channel mode operation */

        write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN, 0);

        /* Set default slot number */

        write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,

            CCSR_SSI_SxCCR_DC(ssi_private->tdm_slots));

        write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,

        /* Set valid word length -- 8 bits, SSI still continue to use 32bits word length in TDM master mode */

        write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK,

            CCSR_SSI_SxCCR_WL(ssi_private->tdm_word_size));

        write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK,

            CCSR_SSI_SxCCR_WL(ssi_private->tdm_word_size));

        /* enable only both RX and TX TDM requested slots */

        writel(~(ssi_private->tdm_slots_enabled), &ssi->stmsk);

        writel(~(ssi_private->tdm_slots_enabled), &ssi->srmsk);

        /* enable the SSI */

        write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);

5,971 Views
robyf
Contributor IV

Hi,

I've basically figured out regarding this problem. It seems that the SDMA for both TX and RX stall for some reasons due to the either SDMA script or SSI not triggering the related dma event associated. It's not clear when this situation will happen currently it's varying between few DMA transfers up to 65000 or so.

To track down the I've changed the imx-sdma kernel code to add some more debugging information when this condition is happening. Basically if the DMA request is associated to the SSI peripheral then I enable the corresponding dma channel in the INTRMASK. Within the SDMA ISR I will check this condition in the EVTERR register and as soon as this happen I print the SSI registers status. This happen exactly once with the following status:

[ 57.426204] SSI Registers:

[ 57.428955] ssi_scr=0x0000109f

[ 57.432117] ssi_sier=0x00500504

[ 57.435361] ssi_stcr=0x000002e8

[ 57.438603] ssi_srcr=0x00000288

[ 57.441845] ssi_stccr=0x00007f01

[ 57.445175] ssi_srccr=0x00007f01

[ 57.448504] ssi_sfcsr=0x00aaf0aa

[ 57.451833] ssi_stmsk=0xfffffffc

[ 57.455164] ssi_srmsk=0xfffffffc

Both TX and RX FIFO watermarks are set to maxburst + 2 = 10 in this run, the SFCSR register reports TX FIFO empty and RX FIFO full and SIER is asserting the related flags associated to this condition.

So why this is happening and the DMA transfer is not triggered? Maybe trying to increase the DMA priority might solve this problem? Or maybe the problem is related to the SDMA script which miss some dma events?

Please give an answer since this problem is blocking me and I cannot proceed further.

Thanks in advance,

Roberto Fichera.

5,971 Views
rosciio
Contributor I

Hi Robert. Have you found a solution about TDM audio with SSI?

I'm tring yet. I have began with the same driver for SGTL5000 and change same configuration.

Marco  

0 Kudos

5,971 Views
abhijeet_ghodga
Contributor III

Hi marco rosciio,

The driver for SGTL5000 that you used is ALSA driver?

If yes, how did you modify the driver to take the standard telephonic voice signal of 8-bit Mu Law.

I have used this driver, and I able to write data, but unable to read data from the SLIC IC Si3228. I think I have to use a buffer, and mask the bits properly from the 32-bit I2S word.

Best,

Abhijeet

0 Kudos