This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Using the McBSP in TDM mode on the AM3517 under Linux

Other Parts Discussed in Thread: AM3517, CODECOMPOSER, DM3730

I was wondering if anyone could point me to documentation, sample code, or existing code in Linux to help me with the following scenario.

I need to configure three of the McBSPs on the AM3517 for TDM functionality using DMA transfers to minimize the load on the CPU. After a specified number of frames are transmitted/received and placed into a buffer, a callback function is called to process this buffer (or load the next buffer in the case of transmitting). This McBSP driver will be called from application code, using a separate thread for each McBSP.

The McBSPs are configured for 14 channels per frame and 32bits per word. So, as you can see this is not your "standard" audio use of the McBSPs.

I have a background using TI DSPs, where I have used the McBSP to do what I have stated. However, I am unsure how to proceed using Linux, specifically with driver and kernel code spread "all" over the place.

Any advice or direction pointing would be greatly appreciated.

  • Jeremiah

    Apologies for not responding sooner.

    I'm not a Linux expert but, if you want, I can provide you with some low level examples that show the setup and configuration of the McBSP with multiple DMA for transmit and receive using multiple buffers for each. 

    In addition, we'll work on getting more information on the Linux aspect.

      Paul

  • Paul,

    I work with Jeremiah in the same office. Any low level examples you can provide would be very useful at this point. I have familiarity with the kernel files that require modifications for the McBSP on our system.

    Thanks,
    Tony

  • Tony

    Attached is the code.

    The zip file contains both a RX DMA example and a TXRX DMA example.

      Paul

  • Paul,

    Thanks for sending the code; I will setup the code in my local kernel and test module to perform the same steps.

    Regards,
    Tony

  • Paul,

    We were able to get the CodeComposer code running last week modifying it for a TDM configuration,. Yesterday we were able to get a prototype running on the Linux side. Thank you very much for your assistance.

    Jeremiah

  • Jeremiah

    Thanks for the feedback, glad I could help.

      Paul

  • Hello,

    where is this zip file?

    I have some problem with the mcbsp dma.

    Thanks

  • Ikudo,

    I'm unsure where the zip file disappeared to. So, I've attached it to this response.

    Thanks,

    Tony

    7624.MCBSP_RX_DMA_1.zip

  • Jeremiah,

    I am interested in using the McBSP ports on a DM3730 under Linux to pull in data from an TI AD1278 ADC. Can you provide some insight or references to how you got your McBSP module working?

    Thanks,

    Alex.

  • I first have a question for you, are you doing one off conversions or continuous conversions? Also, on the AD1278, is your data in TDM mode (assuming this) or each channel on its own DOUT pin? The interesting thing is, I am familiar with the AD1274 as I interfaced it to a TI C6748 DSP.

    Your biggest challenge will be to correctly obtain and link several DMA channels together to continually stream in the data from the AD1278. On the C6748 I did a ping/pong double buffer. While the ping buffer was being filled with incoming data, I was able to process the pong buffer. When the pong buffer was being filled, I was able to process the ping buffer. Depending on your setup, a double buffer may be adequate, or you may need to go to a triple buffer to ensure no sample loss.

    On the C6748, I actually needed three DMA channels configured: Initial, link1 and lilnk2. When it was working, this is how it went:

    initial->link1->link2->link1->link2->link1............

    This could have just been an issue with the C6748, but I thought I should warn you.

    As for the Linux side, the driver I wrote was for the older 2.6.32 kernel. All the omap_ functions are functions provided in the kernel.

    void mcbsp_tdm_configure_chained_rx_channels(struct mcbsp_tdm_dev* device,
                int rx_length_bytes,
                int elem_count_per_frame,
                int elem_per_packet)
    {
        int ic = 0;
        int err_code = 0;
        int rx_lch = 0;
        int num_frames = 0;
    
        struct omap_dma_channel_params rx_params;
        memset(&rx_params, 0, sizeof(rx_params));
    
        //dividing by 4 since we are transmitting 32bit elements
        num_frames = (rx_length_bytes >> 2) / elem_count_per_frame;
        device->rx_buffer_length = rx_length_bytes;
    
        printk(KERN_ALERT "Setting TDM dma rx params");
    
        for (ic = 0; ic < RX_NUM_DMA_BUFFERS; ic++)
        {
            printk(KERN_ALERT "Request DMA channel for McBSP RX buffer %d (TDM mode).\n", ic);
    
            if (omap_request_dma(omap24xx_dma_reqs[device->device][RX_STREAM],
                        "McBSP RX (TDM mode)",
                        mcbsp_tdm_rx_dma_callback,
                        device,
                        &rx_lch))
            {
                printk(KERN_ALERT "Unable to request DMA channel for McBSP RX buffer %d (TDM mode).\n", ic);
                err_code = -EAGAIN;
                break;  // break out of the for loop
            }
    
            device->rx_lch[ic] = rx_lch;
    
            omap_set_dma_transfer_params(rx_lch,
                    OMAP_DMA_DATA_TYPE_S32,
                    elem_count_per_frame, num_frames,
                    OMAP_DMA_SYNC_PACKET,
                    omap24xx_dma_reqs[device->device][RX_STREAM], OMAP_DMA_SRC_SYNC);
    
            omap_set_dma_src_params(rx_lch,
                    0,
                    OMAP_DMA_AMODE_CONSTANT,
                    //OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR,
                    omap34xx_mcbsp_port[device->device][RX_STREAM],
                    0, elem_per_packet);
    
            omap_set_dma_dest_params(rx_lch,
                    0,
                    OMAP_DMA_AMODE_POST_INC,
                    device->physdst[ic],
                    1, 1);
        }
    
        if (err_code == 0)
        {
            // link DMA channels together - assume MAX_NUM_DMA_BUFFERS > 1
            for (ic = 0; ic < (RX_NUM_DMA_BUFFERS - 1); ic++)
            {
                omap_dma_link_lch(device->rx_lch[ic], device->rx_lch[ic+1]);
            }
            // link last channel to first
            omap_dma_link_lch(device->rx_lch[RX_NUM_DMA_BUFFERS-1], device->rx_lch[0]);
        }
    }

    Here is the callback function. It places the entire buffer received into a kernel fifo buffer. This is a specific 2.6.32 kernel function, but I believe there are equivalents in newer kernels.

    static void mcbsp_tdm_rx_dma_callback(int lch, u16 ch_status, void *data)
    {
        struct mcbsp_tdm_dev* device = data;
        unsigned int num_bytes = 0;
        unsigned char* buf_ptr = NULL;
        int ic = 0;
        int found = 0;
    
    //    printk(KERN_ALERT "mcbsp_tdm_rx_dma_callback");
    
        for (ic = 0; ic < RX_NUM_DMA_BUFFERS; ic++)
        {
            if(lch == device->rx_lch[ic])
            {
                found = 1;
                buf_ptr = device->cpu_dst[ic];
                break;
            }
        }
    
        if(found == 0)
        {
            printk(KERN_ALERT "Incorrect DMA logical channel %d for RX -- no data copied from McBSP\n", lch);
        }
    
        if (buf_ptr)
        {
            num_bytes = kfifo_put(device->rx_circular_buffer, buf_ptr, device->rx_buffer_length);
            if (num_bytes != device->rx_buffer_length)
            {
                ///\TODO: need to protect against this in case of lost data packets
    //          printk(KERN_ALERT "Fifo push failed. Pushed %d bytes but only %d pushed on", device->rx_buffer_length, num_bytes);
            }
        }
    }

    I have not done a whole lot with this low level code in over five years, so my memory is a bit rusty. I hope this helps.