2017-02-23 3 views
2

Zynq PL의 맞춤 I2S 컨트롤러로 PCM 스트림을 전송하기 위해 Zynq-7000 기반 플랫폼에서 DMA 엔진을 사용하려고합니다. 내 I2S 컨트롤러는 외부 앰프와 인터페이스합니다. AXI-DMA 컨트롤러를 통해 DMA를 사용하고 싶습니다. 현재 내 데이터 경로입니다 :자일링스의 Zynq 기반 플랫폼에서 AXI-DMA IP를 사용하는 PCM DMA 엔진

axi_dma_path 저는 Zynq PS에서 Linux 4.10 커널을 사용하고 있습니다. 리눅스 ASoC 서브 시스템을 사용하여 pcm 스트림을 생성하고 외부 오디오 앰프를 제어합니다. 512MB의 DDR RAM이 Zynq에 연결되어 있습니다. 이 RAM의 섹션을 사용하여 내 DMA 엔진을 실행하고 싶습니다. 내 I2S 컨트롤러는 AXI-Lite 컨트롤 인터페이스에서 실행되며 오디오 스트리밍을 위해 AXI4-Stream 인터페이스를 사용합니다. 이 IP는 테스트를 거쳤으며 이러한 인터페이스와 잘 작동한다고 가정 할 수 있습니다.

과거에는 Zynq PS에서 PL330을 사용하여 DMA 엔진을 구동했습니다. 필자의 I2S 컨트롤러에는 FIFO가 내장되어있어 AXI-Lite 레지스터 공간이므로 모든 DMA 전송은 AXI-Lite 인터페이스를 통과했습니다.

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>; 
}; 

새로운 설정

: 나는 단순히 그래서 같은이 FIFO 주소로 DMA 엔진을 지적

/* 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>; 
}; 

물론, 몇 가지 세부 사항이 왼쪽으로되어 있지만, 이러한 관련 비트입니다.

이제 PL330 대신 AXI-DMA IP를 사용하여이 드라이버를 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의 모든 5백12메가바이트에 대한 액세스 권한을 가지고 있지만, 커널 내 DMA 전송을위한 메모리를 할당 어디 모르겠어요.

답변

0

DMA 버퍼로 사용될 메모리를 할당하려면 Linux 커널 드라이버가 필요합니다. 큰 연속 버퍼가 필요한 경우 Linux 커널에서 CMA를 활성화해야합니다. 드라이버에서 kmalloc을 사용하여 메모리를 할당 할 수 있습니다.

커널 드라이버에 대한 참조로 udmabuf (https://github.com/ikwzm/udmabuf)를 사용하는 것이 좋습니다.

udmabuf는/sys/class/udmabuf의 장치로 나타나며 DMA 버퍼의 실제 주소는 사용자 공간에서 읽을 수 있습니다. 이 주소를 데이터를 전송할 대상 영역으로 AXI DMA 버퍼에 전달하십시오. Zynq에 리눅스에서 DMA에

상세 정보 : 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

에 대한 예를 들어 리눅스 드라이버를 찾을 수 있습니다