ALSA 驅動中dma的配置:
- /*
- * prepare DMA for pcm
- */
- int i2s_dma_prepare(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sep0611_runtime_data *prtd = runtime->private_data;
- struct dma_info *p_dma_info;
- unsigned long i2s_fifo_width = 1; /* 16 bits in default */
- int ret;
-
- /* we need call the i2s_dma_prepare after set hw_params, such as in pcm_prepare */
- if(runtime->format == 0){
- printk("ERR:please ensure the hw_params has been set correctly\n");
- return -EINVAL;
- }
-
- p_dma_info = kmalloc(sizeof(struct dma_info), GFP_KERNEL);
- if(!p_dma_info){
- return -ENOMEM;
- }
-
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- p_dma_info->trans_type = MEM_TO_DEV;
- p_dma_info->src_addr = prtd->dma_buffer;
- p_dma_info->dst_addr = I2S_BASE + I2S_TXDMA;
-
- /*
- * match with tx data of (S16_LE | S20_3LE | S24_3LE | S32_LE)
- */
- if(runtime->format != SNDRV_PCM_FORMAT_S16_LE)
- i2s_fifo_width = 2;
-
- /* CTLx and CFGx */
- p_dma_info->ctrl_hi = DMAC_DONE (0) | DMAC_BLK_TRAN_SZ(prtd->period_len/8);
-
- p_dma_info->ctrl_lo = DMAC_SRC_LLP_EN (1) | DMAC_DST_LLP_EN (0) | DMAC_SRC_MASTER_SEL(0)\
- | DMAC_DST_MASTER_SEL(1)| DMAC_TRAN_TYPE_FLOW_CTL(1)| DMAC_DST_SCAT_EN (0) \
- | DMAC_SRC_GATH_EN (0)| DMAC_SRC_BST_SZ (2)| DMAC_DST_BST_SZ (3) \
- | DMAC_SRC_INCR (0)| DMAC_DST_INCR (3)| DMAC_SRC_TRAN_WIDTH(3)\
- | DMAC_DST_TRAN_WIDTH(i2s_fifo_width) | DMAC_INT_EN(1);
-
- p_dma_info->cfg_hi = DMAC_DST_PER (7) | DMAC_SRC_PER (6) | DMAC_SRC_STAT_UPD_EN(0) \
- | DMAC_DST_STAT_UPD_EN(0) | DMAC_PROT_CTL (1) | DMAC_FIFO_MODE(0) \
- | DMAC_FLOW_CTL_MODE (0);
-
- p_dma_info->cfg_lo = DMAC_AUTO_RELOAD_DST(0) | DMAC_AUTO_RELOAD_SRC(0) | DMAC_MAX_AMBA_BST_LEN(0) \
- | DMAC_SRC_HS_POL (0) | DMAC_DST_HS_POL (0) | DMAC_BUS_LOCK (0) \
- | DMAC_CH_LOCK (0) | DMAC_BUS_LOCK_LEVEL(1)| DMAC_HW_SW_SEL_SRC (1) \
- | DMAC_HW_SW_SEL_DST (0) | DMAC_CH_SUSP (0) | DMAC_CH_PRIOR (0);
- }
- else{
- p_dma_info->trans_type = DEV_TO_MEM;
- p_dma_info->src_addr = I2S_BASE + I2S_RXDMA;
- p_dma_info->dst_addr = prtd->dma_buffer;
-
- /* CTLx and CFGx */
- p_dma_info->ctrl_hi = DMAC_DONE(0) | DMAC_BLK_TRAN_SZ(prtd->period_len/2);
-
- p_dma_info->ctrl_lo = DMAC_SRC_LLP_EN (0)| DMAC_DST_LLP_EN (1) | DMAC_SRC_MASTER_SEL (1) \
- | DMAC_DST_MASTER_SEL(0) | DMAC_TRAN_TYPE_FLOW_CTL(2) | DMAC_DST_SCAT_EN (0) \
- | DMAC_SRC_GATH_EN (0) | DMAC_SRC_BST_SZ (2) | DMAC_DST_BST_SZ (3) \
- | DMAC_SRC_INCR (3) | DMAC_DST_INCR (0) | DMAC_SRC_TRAN_WIDTH (i2s_fifo_width) \
- | DMAC_DST_TRAN_WIDTH(2) | DMAC_INT_EN(1);
-
- p_dma_info->cfg_hi = DMAC_DST_PER(7) | DMAC_SRC_PER(6) | DMAC_SRC_STAT_UPD_EN(0) \
- | DMAC_DST_STAT_UPD_EN(0) | DMAC_PROT_CTL (1) | DMAC_FIFO_MODE (0) \
- | DMAC_FLOW_CTL_MODE (0);
-
- p_dma_info->cfg_lo = DMAC_AUTO_RELOAD_DST(0) | DMAC_AUTO_RELOAD_SRC(0) | DMAC_MAX_AMBA_BST_LEN(0) \
- | DMAC_SRC_HS_POL (0) | DMAC_DST_HS_POL (0) | DMAC_BUS_LOCK (0) \
- | DMAC_CH_LOCK (0) | DMAC_BUS_LOCK_LEVEL (1) | DMAC_HW_SW_SEL_SRC (0) \
- | DMAC_HW_SW_SEL_DST (1) | DMAC_CH_SUSP (0) | DMAC_CH_PRIOR (0);
- }
-
- /* store p_dma_info in prtd */
- prtd->dma_info = p_dma_info;
-
- ret = <SPAN style="BACKGROUND-COLOR: rgb(255,255,255)">set_dma_cll</SPAN>(p_dma_info, prtd->buffer_len, prtd->period_len);
- if(ret < 0){
- return -ENOMEM;
- }
-
- load_first_llp(prtd->dma_info, prtd->dma_params->channel);
-
- return 0;
- }
其中函數set_dma_cll是設置dma鏈的,dma傳輸時用戶空間的buffer大小為64k,分為16個塊(block),每個塊為4k,dma啟用多塊傳輸,當一個塊傳輸完成後,都會會寫剛剛使用的那個LLI中的CTL寄存器中的高32位,將其中的第13位置1,並且會產生塊傳輸完成中斷,中斷處理程序會通知上層,往bufffer裡寫數據。dma鏈如下所示。