簡體   English   中英

在基於Xilinx Zynq的平台上使用AXI-DMA IP的PCM DMA引擎

[英]PCM DMA Engine Using AXI-DMA IP on Xilinx Zynq Based Platform

我正在嘗試在基於Zynq-7000的平台上使用DMA引擎將PCM流傳輸到Zynq PL中的自定義I2S控制器。 我的I2S控制器連接到外部放大器。 我想通過AXI-DMA控制器使用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: dmac@f8003000 {
    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";
};

axi_i2s@0x43C00000 {
    #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: axidma@40400000 {
    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 ;
    dma-channel@40400000 {
        compatible = "xlnx,axi-dma-mm2s-channel";
        dma-channels = <0x1>;
        interrupts = < 0 33 4 >;
        xlnx,datawidth = <0x20>;
        xlnx,device-id = <0x0>;
        //xlnx,include-dre ;
    } ;
    dma-channel@40400030 {
        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 */
axi_i2s@0x43C10000 {
    #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傳輸分配內存。

您將需要一個Linux內核驅動程序來分配將用作DMA緩沖區的內存。 如果需要大的連續緩沖區,則需要在Linux內核中啟用CMA。 在驅動程序中,您可以使用kmalloc分配內存。

作為內核驅動程序的參考,我建議使用udmabuf( https://github.com/ikwzm/udmabuf )。

udmabuf在/ sys / class / udmabuf中顯示為設備,並且可以在用戶空間中讀取DMA緩沖區的物理地址。 將此地址作為目標區域傳遞到您的AXI DMA緩沖區。

Zynq上有關Linux中DMA的更多信息: https ://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10658/1/drivers-session4-dma-4public.pdf

編輯:您可以在這里找到AXI DMA的示例Linux驅動程序https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10658/2/axidma.c.golden

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM