来源:转载自https://community.milkv.io/t/i2s-milk-v-duo-speaker-i2s2/666,原作者 LangZhao
前言
本章介绍duo的i2s2接口。
后续章节再介绍如何添加max98357a驱动,使用i2s驱动max98357a播放音频。
一、电路
1.1 duo音频接口
外接的i2s只有1和2,而2被eth复用了,需要移除eth然后再配置i2s2
1.2 I2S2连接
对应电路图中,从上到下
LRCK
BCK
DO
DI
duo可以利用i2s2外接codec+speaker进行播放音频。
二、I2S2介绍
查看cv1800的dtsi配置,可以看到其实是复用的cv1835的i2s驱动。
duo-buildroot-sdk-develop\build\boards\default\dts\cv180x\cv180x_base.dtsi
i2s2: i2s@04120000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04120000 0x0 0x2000>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <2>;
#sound-dai-cells = <0>;
dmas = <&dmac 6 1 1 /* read channel */
&dmac 1 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "txrx";
mclk_out = "false";
};
&i2s2 {
status = "okay";
#sound-dai-cells = <0>;
}
我这里参考2个平台的dts
cv1835:i2s的驱动复用的该平台
cv182x:cv1800和该平台较为接近,而且adc定义有用该名称
2.1 参考cv182x的dts实现
duo-buildroot-sdk-develop\build\boards\default\dts\cv182x\cv182x_asic.dtsi
仅供参考
i2s0: i2s@04100000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04100000 0x0 0x2000>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <0>;
#sound-dai-cells = <0>;
dmas = <&dmac 0 1 1>; /* read channel */
dma-names = "rx";
capability = "rx"; /* I2S0 connect to internal ADC as RX */
mclk_out = "false";
};
i2s1: i2s@04110000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04110000 0x0 0x2000>;
interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <1>;
#sound-dai-cells = <0>;
dmas = <&dmac 2 1 1 /* read channel */
&dmac 3 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "txrx";
mclk_out = "false";
};
i2s2: i2s@04120000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04120000 0x0 0x2000>;
interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <2>;
#sound-dai-cells = <0>;
dmas = <&dmac 6 1 1 /* read channel */
&dmac 1 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "txrx";
mclk_out = "false";
};
i2s3: i2s@04130000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04130000 0x0 0x2000>;
interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <3>;
#sound-dai-cells = <0>;
dmas = <&dmac 7 1 1>; /* write channel */
dma-names = "tx";
capability = "tx"; /* I2S3 connect to internal DAC as TX */
mclk_out = "true";
};
adc: adc@0300A100 {
compatible = "cvitek,cv182xadc";
reg = <0x0 0x0300A100 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
clk_source = <0x04130000>; /* MCLK source is I2S3 */
};
dac: dac@0300A000 {
compatible = "cvitek,cv182xdac";
reg = <0x0 0x0300A000 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
};
pdm: pdm@0x041D0C00 {
compatible = "cvitek,cv1835pdm";
reg = <0x0 0x041D0C00 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
};
sound_adc {
compatible = "cvitek,cv182x-adc";
cvi,model = "CV182X";
cvi,card_name = "cv182x_adc";
};
sound_dac {
compatible = "cvitek,cv182x-dac";
cvi,model = "CV182X";
cvi,card_name = "cv182x_dac";
};
sound_PDM {
compatible = "cvitek,cv182x-pdm";
cvi,model = "CV182X";
cvi,card_name = "cv182x_internal_PDM";
};
sound_ext1 {
compatible = "cvitek,cv1835-adau1372";
cvi,model = "CV1835";
cvi,mode = "I2S";
cvi,fmt = "IBNF";
cvi,card_name = "cvi_sound_card_0";
cvi,slot_no=<2>;
dai@0 {
cvi,dai_name = "cv1835-i2s-1";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4110000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4110000.i2s";
cvi,codec_name = "adau1372.1-003c";
cvi,role = "master";
};
dai@1 {
cvi,dai_name = "cv1835-i2s-2";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4120000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4120000.i2s";
cvi,codec_name = "adau1372.1-003c";
cvi,role = "slave";
};
};
/* sound_ext2 use external codec */
sound_ext2 {
compatible = "cvitek,cv1835-adau1372";
cvi,model = "CV1835";
cvi,mode = "I2S";
cvi,fmt = "IBNF";
cvi,card_name = "cv1835_external_card";
cvi,slot_no=<2>;
dai@0 {
cvi,dai_name = "cv1835-i2s-2";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4120000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4120000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "master";
};
dai@1 {
cvi,dai_name = "cv1835-i2s-3";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4130000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4130000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "slave";
};
};
2.2 参考cv1835_fpga
i2s的定义来看需要开cv1835相关的配置,可以参考这里定义的外接声卡。
可以参考duo-buildroot-sdk-develop\build\boards\cv183x\cv1835_fpga\linux\cv1835_fpga.dts的定义
# audio driver
#CONFIG_SOUND=y
#CONFIG_SND=y
#CONFIG_SND_SOC=y
#CONFIG_SND_CV1835_I2S=y
#CONFIG_CV1835_I2S_SUBSYS=y
# 待添加max的config
#CONFIG_SND_SOC_ADAU1372=y
#CONFIG_SND_SOC_ADAU_UTILS=y
#CONFIG_SND_SOC_ADAU1372_I2C=y
#CONFIG_SND_SOC_CV1835_ADAU1372=y
# CONFIG_SND_SOC_ADAU1372_SPI is not set
#CONFIG_SND_SOC_CV1835_CONCURRENT_I2S=y #!!!!
#CONFIG_SND_SOC_CV1835_USE_AUDIO_PLL=y
#CONFIG_SND_SOC_CV1835_CV1835PDM=y
#CONFIG_SND_SOC_CV1835PDM=y
#CONFIG_SND_SOC_CV1835_CV1835ADC=y
#CONFIG_SND_SOC_CV1835ADC=y
#CONFIG_SND_SOC_CV1835_CV1835DAC=y
#CONFIG_SND_SOC_CV1835DAC=y
i2s_mclk: i2s_mclk {
clock-output-names = "i2s_mclk";
clock-frequency = <24576000>; /* use internal audio PLL */
#clock-cells = <0x0>;
compatible = "fixed-clock";
};
#ifdef CV1835_AUDIO_CODEC_EN
adc: adc@0300A000 {
compatible = "cvitek,cv1835adc";
reg = <0x0 0x0300A000 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
};
dac: dac@0300A400 {
compatible = "cvitek,cv1835dac";
reg = <0x0 0x0300A400 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
};
sound_adc {
compatible = "cvitek,cv1835-adc";
cvi,model = "CV1835";
cvi,card_name = "cvi_adc";
};
sound_dac {
compatible = "cvitek,cv1835-dac";
cvi,model = "CV1835";
cvi,card_name = "cvi_dac";
};
#endif
i2s0: i2s@04100000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04100000 0x0 0x2000>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <0>;
#sound-dai-cells = <0>;
dmas = <&dmac 0 1 1 /* read channel */
&dmac 1 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "rx"; /* I2S0 connect to internal ADC as RX */
};
i2s1: i2s@04110000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04110000 0x0 0x2000>;
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <1>;
#sound-dai-cells = <0>;
dmas = <&dmac 2 1 1 /* read channel */
&dmac 3 1 1>; /* write channel */
dma-names = "rx", "tx";
#ifndef CV1835_CONCURRENT_I2S /* refer to /include/dt-bindings/sound/cv1835-audio.h */
capability = "txrx";
#else
capability = "tx";
#endif
};
#ifdef CV1835_EXT_CARD_1_EN
/* sound_ext1 use external codec */
sound_ext1 {
compatible = "cvitek,cv1835-adau1372";
cvi,model = "CV1835";
cvi,mode = "I2S";
cvi,fmt = "IBNF";
cvi,card_name = "cvi_sound_card_0";
cvi,slot_no=<2>;
dai@0 {
cvi,dai_name = "cv1835-i2s-1";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4110000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4110000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "master";
};
#ifdef CV1835_CONCURRENT_I2S
dai@1 {
cvi,dai_name = "cv1835-i2s-0";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4100000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4100000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "slave";
};
#endif
};
#endif
pdm: pdm@0x041D0C00 {
compatible = "cvitek,cv1835pdm";
reg = <0x0 0x041D0C00 0x0 0x100>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
};
#ifdef CV1835_PDM_EN
/* sound_PDM use PDM to transfer DMIC signal to I2S signal as audio input */
sound_PDM {
compatible = "cvitek,cv1835-pdm";
cvi,model = "CV1835";
cvi,card_name = "cv1835_internal_PDM";
};
#endif
i2s2: i2s@04120000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04120000 0x0 0x2000>;
interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <2>;
#sound-dai-cells = <0>;
dmas = <&dmac 4 1 1 /* read channel */
&dmac 5 1 1>; /* write channel */
dma-names = "rx", "tx";
#ifndef CV1835_CONCURRENT_I2S
capability = "txrx";
#else
capability = "rx";
#endif
};
i2s3: i2s@04130000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04130000 0x0 0x2000>;
interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <3>;
#sound-dai-cells = <0>;
dmas = <&dmac 6 1 1 /* read channel */
&dmac 7 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "tx"; /* I2S3 connect to internal DAC as TX */
};
#ifdef CV1835_EXT_CARD_2_EN
/* sound_ext2 use external codec */
sound_ext2 {
compatible = "cvitek,cv1835-adau1372";
cvi,model = "CV1835";
cvi,mode = "I2S";
cvi,fmt = "IBNF";
cvi,card_name = "cv1835_external_card";
cvi,slot_no=<2>;
dai@0 {
cvi,dai_name = "cv1835-i2s-2";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4120000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4120000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "master";
};
#ifdef CV1835_CONCURRENT_I2S
dai@1 {
cvi,dai_name = "cv1835-i2s-3";
cvi,stream_name = "adau1372-aif";
cvi,cpu_dai_name = "4130000.i2s";
cvi,codec_dai_name = "adau1372-aif";
cvi,platform_name = "4130000.i2s";
cvi,codec_name = "adau1372.0-003c";
cvi,role = "slave";
};
#endif
};
#endif
2.3 cv180x
cv1800b_milkv_duo_sd.dts
- cv180x_base_riscv.dtsi
- cv180x_base.dtsi (定义i2s接口)
- cv180x_asic_qfn.dtsi (移除sound接口)
- cv180x_asic_sd.dtsi
- cv180x_default_memmap.dtsi
平台定义的i2s接口,最终包该定义。
cv180x_base.dtsi
duo-buildroot-sdk-develop\build\boards\default\dts\cv180x
i2s_mclk: i2s_mclk {
clock-output-names = "i2s_mclk";
clock-frequency = <24576000>;
#clock-cells = <0x0>;
compatible = "fixed-clock";
};
i2s_subsys {
compatible = "cvitek,i2s_tdm_subsys";
reg = <0x0 0x04108000 0x0 0x100>;
clocks = <&i2s_mclk>, <&clk CV180X_CLK_A0PLL>,
<&clk CV180X_CLK_SDMA_AUD0>, <&clk CV180X_CLK_SDMA_AUD1>,
<&clk CV180X_CLK_SDMA_AUD2>, <&clk CV180X_CLK_SDMA_AUD3>;
clock-names = "i2sclk", "clk_a0pll",
"clk_sdma_aud0", "clk_sdma_aud1",
"clk_sdma_aud2", "clk_sdma_aud3";
master_base = <0x04110000>; /* I2S1 is master, only useful while using multi I2S IPs work on same IO */
};
i2s0: i2s@04100000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04100000 0x0 0x2000>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <0>;
#sound-dai-cells = <0>;
dmas = <&dmac 0 1 1>; /* read channel */
dma-names = "rx";
capability = "rx"; /* I2S0 connect to internal ADC as RX */
mclk_out = "false";
};
i2s1: i2s@04110000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04110000 0x0 0x2000>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <1>;
#sound-dai-cells = <0>;
dmas = <&dmac 2 1 1 /* read channel */
&dmac 3 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "txrx";
mclk_out = "false";
};
i2s2: i2s@04120000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04120000 0x0 0x2000>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <2>;
#sound-dai-cells = <0>;
dmas = <&dmac 6 1 1 /* read channel */
&dmac 1 1 1>; /* write channel */
dma-names = "rx", "tx";
capability = "txrx";
mclk_out = "false";
};
i2s3: i2s@04130000 {
compatible = "cvitek,cv1835-i2s";
reg = <0x0 0x04130000 0x0 0x2000>;
clocks = <&i2s_mclk 0>;
clock-names = "i2sclk";
dev-id = <3>;
#sound-dai-cells = <0>;
dmas = <&dmac 7 1 1>; /* write channel */
dma-names = "tx";
capability = "tx"; /* I2S3 connect to internal DAC as TX */
mclk_out = "true";
};
cv180x_base_riscv.dtsi
i2s0: i2s@04100000 {
interrupts = <40 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&plic0>;
};
i2s1: i2s@04110000 {
interrupts = <41 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&plic0>;
};
i2s2: i2s@04120000 {
interrupts = <42 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&plic0>;
};
i2s3: i2s@04130000 {
interrupts = <43 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&plic0>;
};
cv180x_asic_qfn.dtsi
/delete-node/ i2s@04110000;
/delete-node/ i2s@04120000;
/delete-node/ sound_ext1;
/delete-node/ sound_ext2;
/delete-node/ sound_PDM;
cv1800移除了i2s1&i2s2,我们需要启动该节点。
PS:可以通过dtsi最终生成的tmp文件查看节点状况
路径:duo_buildroot_sdk\duo-buildroot-sdk\u-boot-2021.10\build\cv1800b_milkv_duo_sd\arch\riscv\dts.cv1800b_milkv_duo_sd.dtb.dts.tmp
2.4 改动——保留i2s
cv180x_asic_qfn.dtsi
/delete-node/ i2s@04110000;
/delete-node/ sound_ext1;
/delete-node/ sound_ext2;
/delete-node/ sound_PDM;
删除i2s2的移除配置。
2.5 I2S小结
为了eth做准备,默认是删除i2s2。
我们需要保留i2s2的配置。
cv1800没有定义i2s的外接声卡,只有内部使用的pdm、adc、dac。
speaker需要自己添加声卡驱动以及声卡节点。
3 小结
本章只是介绍duo的i2s2,由于编辑器篇幅有限,max98357a的添加放在第二帖。
后一章节