2017-02-23 535 views
2

我想在基于Zynq-7000的平台上使用DMA引擎将PCM流传输到Zynq PL中的自定义I2S控制器。我的I2S控制器连接到外部放大器。我想通过AXI-DMA控制器使用DMA。这是目前我的数据通路:在Xilinx Zynq平台上使用AXI-DMA IP的PCM DMA引擎

axi_dma_path 我在Zynq PS上使用Linux 4.10内核。我使用Linux ASoC子系统生成pcm流并控制我的外部音频放大器。我有512MB的DDR RAM连接到Zynq。我想用这个RAM的一部分来运行我的DMA引擎。我的I2S控制器运行AXI-Lite控制接口,并使用AXI4-Stream接口进行音频流传输。这个IP已经过测试,并且可以假定这些接口可以很好地工作。

在过去,我使用Zynq PS中的PL330来驱动DMA引擎。我的I2S控制器曾经有一个内置于AXI-Lite寄存器空间的FIFO,因此所有DMA传输都通过AXI-Lite接口。我只是指出了DMA引擎,该FIFO地址,像这样:

struct axi_i2s { 
    struct snd_dmaengine_dai_dma_data playback_dma_data; 
    struct snd_dmaengine_dai_dma_data capture_dma_data; 
}; 

static int axi_i2s_dai_probe(struct snd_soc_dai *dai) 
{ 
    struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai); 

    snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, 
     &i2s->capture_dma_data); 

    return 0; 
} 

static struct snd_soc_dai_driver axi_i2s_dai = { 
    .probe = axi_i2s_dai_probe, 
    .playback = { 
     .channels_min = 1, 
     .channels_max = 8, 
     .rates = I2S_RATES, 
     .formats = SNDRV_PCM_FMTBIT_S16_LE | 
       SNDRV_PCM_FMTBIT_S24_LE | 
       SNDRV_PCM_FMTBIT_S32_LE, 
    }, 
}; 

static int axi_i2s_probe(struct platform_device *pdev) 
{ 
    axi_i2s *i2s; 

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); 
    if (!i2s) 
     return -ENOMEM; 

    platform_set_drvdata(pdev, i2s); 

    i2s->playback_dma_data.addr = I2S_BASE_ADDRESS + TX_FIFO_OFFSET; 
    i2s->playback_dma_data.addr_width = 4; 
    i2s->playback_dma_data.maxburst = 1; 

    i2s->capture_dma_data.addr = I2S_BASE_ADDRESS + RX_FIFO_OFFSET; 
    i2s->capture_dma_data.addr_width = 4; 
    i2s->capture_dma_data.maxburst = 1; 

    devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 

    return 0; 
} 

的DeviceTree:

dmac_s: [email protected] { 
    compatible = "arm,pl330", "arm,primecell"; 
    reg = <0xf8003000 0x1000>; 
    interrupt-parent = <&intc>; 
    interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", 
     "dma4", "dma5", "dma6", "dma7"; 
    interrupts = <0 13 4>, 
     <0 14 4>, <0 15 4>, 
     <0 16 4>, <0 17 4>, 
     <0 40 4>, <0 41 4>, 
     <0 42 4>, <0 43 4>; 
    #dma-cells = <1>; 
    #dma-channels = <8>; 
    #dma-requests = <4>; 
    clocks = <&clkc 27>; 
    clock-names = "apb_pclk"; 
}; 

[email protected] { 
    #sound-dai-cells = <1>; 
    compatible = "my,driver"; 
    reg = <0x43C00000 0x10000>; 
    clocks = <&clkc 15>; 
    clock-names = "axi"; 
    dmas = <&dmac_s 0>, <&dmac_s 1>; 
    dma-names = "tx", "rx"; 
    xlnx,dma-type = <0x1>; 
}; 

新成立:

/* AXI DMA */ 
axi_dma_0: [email protected] { 
    compatible = "xlnx,axi-dma-1.00.a"; 
    #dma-cells = <1>; 
    reg = < 0x40400000 0x10000 >; 
    xlnx,addrwidth = <0x20>; 
    clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>; 
    clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk"; 
    interrupt-parent = <&intc>; 
    interrupts = < 0 33 4 0 34 4>; 
    dma-ranges = <0x00000000 0x00000000 0x20000000>; 
    //xlnx,include-sg ; 
    [email protected] { 
     compatible = "xlnx,axi-dma-mm2s-channel"; 
     dma-channels = <0x1>; 
     interrupts = < 0 33 4 >; 
     xlnx,datawidth = <0x20>; 
     xlnx,device-id = <0x0>; 
     //xlnx,include-dre ; 
    } ; 
    [email protected] { 
     compatible = "xlnx,axi-dma-s2mm-channel"; 
     dma-channels = <0x1>; 
     interrupts = < 0 34 4 >; 
     xlnx,datawidth = <0x20>; 
     xlnx,device-id = <0x0>; 
     //xlnx,include-dre ; 
    } ; 
}; 

/* New stream version */ 
[email protected] { 
    #sound-dai-cells = <1>; 
    compatible = "my,driver"; 
    reg = <0x43C10000 0x10000>; 
    clocks = <&clkc 15>; 
    clock-names = "axi"; 
    dmas = <&axi_dma_0 0 
     &axi_dma_0 1>; 
    dma-names = "axidma0", "axidma1"; 
    xlnx,dma-type = <0x1>; 
}; 

显然,一些细节被省略了,但这些是相关的位。

现在,我不能完全弄清楚如何使用AXI-DMA IP而不是PL330将此驱动程序更改为DMA。由于DMA传输将在没有FIFO的不同存储器区域完成,我如何设置snd_dmaengine_dai_dma_data结构来写入AXI-DMA存储器?具体而言,本节:

i2s->playback_dma_data.addr = I2S_BASE_ADDRESS + TX_FIFO_OFFSET; 
i2s->playback_dma_data.addr_width = 4; 
i2s->playback_dma_data.maxburst = 1; 

i2s->capture_dma_data.addr = I2S_BASE_ADDRESS + RX_FIFO_OFFSET; 
i2s->capture_dma_data.addr_width = 4; 
i2s->capture_dma_data.maxburst = 1; 

的AXI-DMA IP访问我的DDR 512MB的全部,但我不知道在哪里,内核会为我的DMA传送分配内存。

回答

0

您将需要一个Linux内核驱动程序来分配将用作DMA缓冲区的内存。如果你需要一个大的连续缓冲区,你需要在你的linux内核中启用CMA。在您的驱动程序中,您可以使用kmalloc分配内存。

作为内核驱动程序的参考,我会建议使用udmabuf(https://github.com/ikwzm/udmabuf)。

udmabuf显示为/ sys/class/udmabuf中的设备,并且可以在用户空间中读取DMA缓冲区的物理地址。将此地址传递给您的AXI DMA缓冲区作为将数据推送到的目标区域。在linux上的DMA

更多信息上ZYNQ:https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10658/1/drivers-session4-dma-4public.pdf

编辑: 你可以找到AXI DMA这里 https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10658/2/axidma.c.golden

为例linux驱动