S3C DMA使用方法,2410-2440 dma介绍

来源:互联网 发布:php date 时区 编辑:程序博客网 时间:2024/04/26 17:21

这里具体DMA CONTROL寄存器(DCON)的配置说明,进而引出DMA的各种工作方式。

Atomic transfer:指的是DMA的单次原子操作,它可以是Unit模式(传输1个data size),也可以是burst模式(传输4个data size),具体对应DCON[28]。

Data Size:指的是单次原子操作的数据位宽,8、16、32,具体对应DCON[21:20]。

Request Source:DMA请求的来源有两种,软件&硬件模块,由DCON[23]控制;当为前者时,由软件对DMASKTRIG寄存器 的位0置位触发一次DMA 操作。当为后者时,具体来源由DCON[26:24]控制,不同硬件模块的某时间触发一次DMA操作,具体要见不同的硬件模 块。
   
DMA service mode:DMA的工作模式有两种,单一服务模式&整体服务模式。前一模式下,一次DMA请求完成一 项原子操作,并且transfer count的值减1。后一模式下,一次DMA请求完成一批原子操作,直到transfer count等于0表示完成 一次整体服务。具体对应DCON[27]。

RELOAD:在reload模式下,当transfer count的值变为零时,将自动加src、dst、TC的值加载到CURR_DST、 CURR_SRC、CURR_TC,并开始一次新的DMA传输。该模式一般和整体服务模式一起使用,也就是说当一次整体服务开始后,src、dst、TC 的值都已经被加载,因此可以更改为下一次

   服务的地址,2410说明文档中建议加入以下语句来判断当前的服务开始,src、dst、TC的值可以被更改了:while((rDSTATn & 0xfffff) == 0) ;

Req&Ack:DMA请求和应答的协议有两种,Demard mode 和 Handshake mode。两者对Request和Ack的时序定义有所不同:在Demard模式下,如果

   DMA完成一次请求如果Request仍然有效,那么DMA就认为这是下一次DMA请求;在Handshake模式下,DMA完成一次请求后等待Request信号无效,然后把ACK也置无效,再等待下一次Request。这个设计外部DMA请求时可能要用到。

传输总长度:DMA一次整体服务传输的总长度为:
    Data Size × Atomic transfer size × TC(字节)。

 

2410的DMA支持四类DMA传输:系统总线到系统总线(ASB/AHB to ASB/AHB),系统总线到外设总线(ASB/AHB to APB),外设总线到系统总线(APB to ASB/AHB),外设总线到外设总线(APB to APB)。

 

2410 DMA 的服务模式:
共有两种服务模式,一种是单一服务模式(single service),另外一种是整体服务模式(whole service)。
在单一服务模式下,不使用传统的DMA计数器,三个DMA状态被顺序执行一次后停止,等待DMA 请求再一次来临后再重新开始另一次循环。
在整体服务模式下,使用传统的DMA 计数器,状态机会停留在状态三,直到DMA计数器的值减为零,再回到状态一,等待下一次DMA请求。

2410 DMA 数据传输模式:
共有两种数据传输模式:
单位数据传输模式:执行一次读操作和一次写操作。
并发数据传输模式:执行四次读操作和四次写操作。

2410 DMA 的基本时序:
nXDREQ请求生效并经过2CLK周期同步后,nXDACK响应并开始生效,但至少还要经过3CLK的周期延迟,DMA控制器才可获得总线的控制权,并开始数据传输。

2410 DMA 的两种协议模式:
请求模式:If XnXDREQ remains asserted, the next transfer starts immediately. Otherwise it waits for XnXDREQ to be asserted.
握手模式:If XnXDREQ is deasserted, DMA deasserts XnXDACK in 2cycles. Otherwise it waits until XnXDREQ is deasserted.

2410 DMA REQ与ACK 协议类型:
共有三种协议类型:
单一服务请求:

单一服务握手:
整体服务握手:

根据上面所说的服务模式和协议模式,很容易推知这三种协议的时序分别是什么。

 

//=================================================

以下分配了四个DMA通道:

s3c2410_dma_t dma_chan[MAX_S3C2410_DMA_CHANNELS];

每个DMA通道维护着一个多缓冲区组成的单链表等待队列,执行DMA操作时先更新DMA通道控制寄存器内容,再依次摘取当前缓冲区投入使用,缓冲区头指针顺次前移;需要插入新的缓冲区时,可从headtail插入;

A详细分析了数据结构关系:


  1. DMA驱动主要函数功能分析(linux/arch/arm/mach-s3c2410/dma.c

写一个DMA驱动的主要工作包括:DMA通道申请、DMA中断申请、控制寄存器设置、挂入DMA等待队列、清除DMA中断、释放DMA通道。Dma.c中对这些工作作了很好的实现,以下具体分析关键函数:

  • int s3c2410_request_dma(const char *device_id, dmach_t channel,

dma_callback_t write_cb, dma_callback_t read_cb) (s3c2410_dma_queue_buffer);

函数描述:申请某通道的DMA资源,填充s3c2410_dma_t 数据结构的内容,申请DMA中断。

输入参数:device_id DMA 设备名;channel 通道号;

write_cb DMA写操作完成的回调函数;read_cb DMA读操作完成的回调函数

输出参数:若channel通道已使用,出错返回;否则,返回0

  • int s3c2410_dma_queue_buffer(dmach_t channel, void *buf_id,

dma_addr_t data, int size, int write) (s3c2410_dma_stop);

函数描述:这是DMA操作最关键的函数,它完成了一系列动作:分配并初始化一个DMA内核缓冲区控制结构,并将它插入DMA等待队列,设置DMA控制寄存器内容,等待DMA操作触发

输入参数: channel 通道号;buf_id,缓冲区标识

dma_addr_t data DMA数据缓冲区起始物理地址;size DMA数据缓冲区大小;write 是写还是读操作

输出参数:操作成功,返回0;否则,返回错误号

  • int s3c2410_dma_stop(dmach_t channel)

函数描述:停止DMA操作。

  • int s3c2410_dma_flush_all(dmach_t channel)

函数描述:释放DMA通道所申请的所有内存资源

  • void s3c2410_free_dma(dmach_t channel)

函数描述:释放DMA通道

因为各函数功能强大,一个完整的DMA驱动程序中一般只需调用以上3个函数即可。可在驱动初始化中调用s3c2410_request_dma,开始DMA传输前调用s3c2410_dma_queue_buffer,释放驱动模块时调用s3c2410_free_dma

  1. 具体的DMA实例分析

Linux下的IIS音频驱动主要都在/kernel/drivers/sound/s3c2410-uda1341.c文件中。它定义了2个重要的数据结构audio_bufer_t, 管理audio缓冲区的数据结构;audio_stream_t 管理多缓冲区的数据结构,它为音频流数据组成了一个环形缓冲区。

我们先看一下加载驱动模块时的初始化函数:int __init s3c2410_uda1341_init(void),该函数先初始化IOUDA341芯片,然后语句s3c2410_request_dma("I2SSDO", s->dma_ch, audio_dmaout_done_callback, NULL);申请了一个DMA通道用于输出音频数据;

smdk2410_audio_write是音频驱动最关键的函数,它从用户进程中拷贝音频数据流至DMA内核缓冲区,然后适用DMA通道2把音频数据发送出去,从而输出声音。我们可以在smdk2410_audio_write 中发现语句s3c2410_dma_queue_buffer(s->dma_ch, (void *) b,b->dma_addr, b->size, DMA_BUF_WR);就是它为DMA写操作作好了一切准备,当I2SSDO中断到来,DMA2通道执行一次写操作(从DMA缓冲写往IO地址0x55000010)。

smdk2410_audio_release函数中先后调用了s3c2410_dma_flush_alls3c2410_free_dma释放DMA2占用的内存资源、和释放DMA2通道.

//=========================================

 

前天完成了UDA1341的驱动,程序是网上下载的,仅仅小改一下而已,昨晚上
分析了其中的DMA部分,有些小小的感想,如果不正确希望读者能够告诉我。谢谢。
写的很乱,没有整理。
    DMA的基础就不介绍了,可以参考http://blog.chinaunix.net/u1/58640/showart_483567.html
和规格书。我主要根据audio的使用,介绍一下DMA的使用。
    1.首先对DMA进行初始化。
    如对outstream的初始化
    channel = 2;
    source = S3C2410_DMASRC_MEM;//源地址为Memory
    hwcfg = 3;                   
    devaddr = 0x55000010;
    dcon = 0xa0800000;
    flags = S3C2410_DMAF_AUTOSTART;
    
/* s3c2410_dma_devconfig
 *
 * configure the dma source/destination hardware type and address
 * 配置DMA的源和目的的硬件类型和地址
 * source:    S3C2410_DMASRC_HW: source is hardware 源是硬件
 *            S3C2410_DMASRC_MEM: source is memory  源是内存
 *
 * hwcfg:     the value for xxxSTCn register,
 *            bit 0: 0=increment pointer, 1=leave pointer
 *            bit 1: 0=soucre is AHB, 1=soucre is APB
 *
 * devaddr:   physical address of the source
*/
因为我们的DMA 源是内存,目的是硬件IIS的固定地址,所以hwcfg应该
设定为APB  FIX_ADDR 即:3
int s3c2410_dma_devconfig(int channel,
              s3c2410_dmasrc_t source,
              int hwcfg,
              unsigned long devaddr)
{
    s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];

    check_channel(channel);

    pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx/n",
         __FUNCTION__, (int)source, hwcfg, devaddr);

    chan->source = source;
    chan->dev_addr = devaddr;

    switch (source) {
    case S3C2410_DMASRC_HW:
        /* source is hardware */
        pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d/n",
             __FUNCTION__, devaddr, hwcfg);
        dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);
        dma_wrreg(chan, S3C2410_DMA_DISRC,  devaddr);
        dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));

        chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
        return 0;

    case S3C2410_DMASRC_MEM:
        /* source is memory */
        pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d/n",
              __FUNCTION__, devaddr, hwcfg);
        dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));
        dma_wrreg(chan, S3C2410_DMA_DIDST,  devaddr);
        dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);

        chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
        return 0;
    }

    printk(KERN_ERR "dma%d: invalid source type (%d)/n", channel, source);
    return -EINVAL;
}

/* s3c2410_dma_config
 *
 * xfersize:     size of unit in bytes (1,2,4)
 * dcon:         base value of the DCONx register
*/
    dcon = 0xa0800000;
    因为IIS是16bit的,所以unit为2.
    s3c2410_dma_config(channel, 2, dcon);
//设定DMA传输结束后的回调函数
    s3c2410_dma_set_buffdone_fn(channel, audio_dmaout_done_callback);
    //设定S3C2410_DMAF_AUTOSTART
    s3c2410_dma_setflags(channel, flags);
    //申请DMA通道
    ret = s3c2410_dma_request(s->dma_ch, &s3c2410iis_dma_out, NULL);
    
    以上是初始化部分,之后通过dmabuf = dma_alloc_coherent(NULL, dmasize, &dmaphys, GFP_KERNEL);
来申请缓冲,dmabuf为虚拟地址,dmaphys是物理地址,虚拟地址用来让驱动写buf用,dmaphys用来传给dma。
dma关心的是实际的物理地址。
    然后通过s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, b->size);来启动dma传输,
这里面的参数就是dmaphys,它为实际地址。
   如果dma传输结束,就会调用:
static void audio_dmaout_done_callback(s3c2410_dma_chan_t *ch, void *buf, int size,
s3c2410_dma_buffresult_t result)
{
    audio_buf_t *b = (audio_buf_t *) buf;
    DPRINTK("audio_dmaout_done_callback/n");
    up(&b->sem);
    wake_up(&b->sem.wait);
}
    来启动下一个buf的传输。

原创粉丝点击