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

這裏具體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通道控制寄存器內容,再依次摘取當前緩衝區投入使用,緩衝區頭指針順次前移;需要插入新的緩衝區時,可從head或tail插入;

圖A詳細分析了數據結構關係:

 


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。

具體的DMA實例分析
Linux下的IIS音頻驅動主要都在/kernel/drivers/sound/s3c2410-uda1341.c文件中。它定義了2個重要的數據結構audio_bufer_t, 管理audio緩衝區的數據結構;audio_stream_t 管理多緩衝區的數據結構,它爲音頻流數據組成了一個環形緩衝區。

我們先看一下加載驅動模塊時的初始化函數:int __init s3c2410_uda1341_init(void),該函數先初始化IO和UDA341芯片,然後語句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_all、s3c2410_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的傳輸。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章