Zynq PL의 맞춤 I2S 컨트롤러로 PCM 스트림을 전송하기 위해 Zynq-7000 기반 플랫폼에서 DMA 엔진을 사용하려고합니다. 내 I2S 컨트롤러는 외부 앰프와 인터페이스합니다. AXI-DMA 컨트롤러를 통해 DMA를 사용하고 싶습니다. 현재 내 데이터 경로입니다 :자일링스의 Zynq 기반 플랫폼에서 AXI-DMA IP를 사용하는 PCM DMA 엔진
저는 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 전송을위한 메모리를 할당 어디 모르겠어요.