Linux 中DMA 的使用方法:
- dma_request_channel 申请一个chan
- tx = dma_prep_xxxx 准备,tx结构体
- tx->callback = tx_callback; 设置回调
- dma_cookie = dmaengine_submit(tx); 提交
- dma_async_issue_pending(chan); 启动硬件开始传输
注:dmaengine_slave_config 接口有时也要调用
下面看一下DMS 在ASOC 代码中的使用。
从I2S 驱动看起,因为ASOC 中主要是用到 I2S 接口来传输数据,DMA 是内存与 I2S 交换数据的工具。
static int sun4i_i2s_probe(struct platform_device *pdev)
{
...
/* 设置物理地址*/
i2s->playback_dma_data.addr = res->start +
i2s->variant->reg_offset_txdata;
i2s->playback_dma_data.maxburst = 8;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
i2s->capture_dma_data.maxburst = 8;
/*注册PCM*/
ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
goto err_suspend;
}
}
带着问题看代码,既然是用到DMA 那第一步肯定是要 dma_request_channel ,
问题1:ASOC 中何时调用了 dma_request_channel ?
snd_dmaengine_pcm_register
dmaengine_pcm_request_chan_of
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
i++) {
...
chan = dma_request_slave_channel_reason(dev, name); // 这里面调用了dma_request_chan
...
pcm->chan[i] = chan;
...
}
#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name)
struct dma_chan *dma_request_chan(struct device *dev, const char *name)
{
struct dma_device *d, *_d;
struct dma_chan *chan = NULL;
/* If device-tree is present get slave info from here */
if (dev->of_node)
chan = of_dma_request_slave_channel(dev->of_node, name); //这里往下就稍复杂了,先不跟吧,总之chan 在这就request 到手了
if (chan) {
/* Valid channel found or requester need to be deferred */
if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
return chan;
}
...
问题2:dma_prep_xxxx 什么地方调用?
问题3:dmaengine_submit 什么地方调用?
static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
{
...
if (!substream->runtime->no_period_wakeup)
flags |= DMA_PREP_INTERRUPT;
prtd->pos = 0;
desc = dmaengine_prep_dma_cyclic(chan,
substream->runtime->dma_addr,
snd_pcm_lib_buffer_bytes(substream),
snd_pcm_lib_period_bytes(substream), direction, flags);
desc->callback = dmaengine_pcm_dma_complete;
desc->callback_param = substream;
prtd->cookie = dmaengine_submit(desc);
return 0;
}
问题4:dma_async_issue_pending 在哪调用?
int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
...
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
ret = dmaengine_pcm_prepare_and_submit(substream);
if (ret)
return ret;
dma_async_issue_pending(prtd->dma_chan); //在这里调用
break;
...
}
ASOC 框架虽然复杂,但DMA 的用法还是遵循基本法则的。