赞
踩
外接的i2s只有1和2,而2被eth复用了,需要移除eth然后再配置i2s2
对应电路图中,从上到下
LRCK
BCK
DO
DI
duo可以利用i2s2外接codec+speaker进行播放音频。
查看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定义有用该名称
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"; }; };
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
cv1800b_milkv_duo_sd.dts
平台定义的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
cv180x_asic_qfn.dtsi
/delete-node/ i2s@04110000;
/delete-node/ sound_ext1;
/delete-node/ sound_ext2;
/delete-node/ sound_PDM;
删除i2s2的移除配置。
为了eth做准备,默认是删除i2s2。
我们需要保留i2s2的配置。
cv1800没有定义i2s的外接声卡,只有内部使用的pdm、adc、dac。
speaker需要自己添加声卡驱动以及声卡节点。
https://blog.csdn.net/pingis58/article/details/109235204
https://github.com/beagleboard/bb.org-overlays/issues/39
https://cloud.tencent.com/developer/article/2225253
数据手册
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX98357A-MAX98357B.pdf
max98357a用i2s接口,我们只要用如下引脚:
LRC:左右声道选择
BCLK:采样信号
DIN:音频数据输入
Vin:电源
GND:接地
gain和sd-mode驱动里也有定义,需要自己增加接口去控制,后续再说。
max98357a驱动:
duo-buildroot-sdk-develop\linux_5.10\sound\soc\codecs\max98357a.c
duo-buildroot-sdk-develop\linux_5.10\sound\soc\codecs\Makefile
snd-soc-max98357a-objs := max98357a.o
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
duo-buildroot-sdk-develop\linux_5.10\sound\soc\Makefile
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
duo-buildroot-sdk-develop\linux_5.10\sound\Makefile
obj-$(CONFIG_SOUND) += soundcore.o
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/
duo-buildroot-sdk-develop\linux_5.10\Makefile
drivers-y := drivers/ sound/
所以需要打开的config如下
CONFIG_SND_SOC=y
CONFIG_SOUND=y
CONFIG_SND=y
如上章介绍的,codec路径下的配置,所以加载codec驱动需要配置config。
CONFIG_SND_SOC_MAX98357A=y
duo-buildroot-sdk-develop\linux_5.10\sound\soc\codecs\max98357a.c
#ifdef CONFIG_OF
static const struct of_device_id max98357a_device_id[] = {
{ .compatible = "maxim,max98357a" },
{ .compatible = "maxim,max98360a" },
{}
};
MODULE_DEVICE_TABLE(of, max98357a_device_id);
#endif
dtsi中需要匹配该节点,增加如下配置,让codec可以正常加载。
/* codec */
max98357a: max98357a {
#sound-dai-cells = <0>;
compatible = "maxim,max98357a";
status = "okay";
/*sdmode-gpios = <&gpio1 14 0>;*/
/* max98357a has gain & sd_mode gpio. but codec driver just has sdmode */
};
这里的dai_driver后续会在machine驱动中使用到,注意name。
static struct snd_soc_dai_driver max98357a_dai_driver = { .name = "HiFi", .playback = { .stream_name = "HiFi Playback", .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 8000, .rate_max = 96000, .channels_min = 1, .channels_max = 2, }, .ops = &max98357a_dai_ops, };
duo-buildroot-sdk-develop\linux_5.10\sound\soc\cvitek\Makefile
使用的是i2s2,注意需要打开i2s2节点。
Makefile
# Cvitek Platform Support
obj-$(CONFIG_SND_CV1835_I2S) += cv1835_i2s.o
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";
};
这里定义的cpu_dai_name,由于audio的dapm接口已经变化了,之前的name方式加载被弃用。
若还用下述配置,则导致编译不过。
/* machine -- sound card*/ #ifdef CV1835_EXT_CARD_3_EN /* sound_ext3 use external codec */ sound_ext3 { compatible = "cvitek,cv1835-max98357a"; cvi,model = "CV1835"; cvi,mode = "I2S"; cvi,fmt = "IBNF"; cvi,card_name = "cv1835_external_card_max98357a"; cvi,slot_no=<2>; dai@0 { cvi,dai_name = "cv1835-i2s-2"; cvi,stream_name = "HiFi"; cvi,cpu_dai_name = "4120000.i2s"; cvi,codec_dai_name = "HiFi"; cvi,platform_name = "4120000.i2s"; cvi,codec_name = "MX98357A:00"; cvi,role = "master"; }; }; #endif
SND_SOC_DAILINK_DEFS(dailink_max98357a, DAILINK_COMP_ARRAY(COMP_CPU("4120000.i2s")), DAILINK_COMP_ARRAY(COMP_CODEC("max98357a", "HiFi")), DAILINK_COMP_ARRAY(COMP_PLATFORM("4120000.i2s"))); static struct snd_soc_dai_link cv1835_max98357a_dai[] = { [DPCM_AUDIO_SPKR] = { .name = "max98357a", .stream_name = "max98357a i2s", //"cv1835-max98357a", .init = cv1835_max98357a_asoc_init, .ops = &cv1835_max98357a_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAILINK_REG(dailink_max98357a), // Interface Abandonment // .cpu_dai_name= "4120000.i2s", // .codec_dai_name = "HiFi", // .codec_name = "maxim,max98357a", // .platform_name= "4120000.i2s", }, };
添加驱动代码
duo-buildroot-sdk-develop\linux_5.10\sound\soc\cvitek\cv1835_max98357a.c
先编译看看能不能成功添加驱动
// SPDX-License-Identifier: GPL-2.0-or-later /* * Machine driver for EVAL-ADAU1372 on CVITEK CV1835 * * Copyright 2019 CVITEK * * Author: EthanChen * */ #include <linux/module.h> #include <linux/device.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <sound/pcm_params.h> #include <linux/io.h> #include <linux/proc_fs.h> // // u16 tdm_slot_no = 2; struct card_private { int tmp; //save sth. // struct snd_soc_jack headset; // struct list_head hdmi_pcm_list; // struct snd_soc_jack hdmi[3]; }; enum { DPCM_AUDIO_SPKR = 0, }; static const struct snd_soc_dapm_widget cv1835_max98357a_dapm_widgets[] = { SND_SOC_DAPM_SPK("SPKR", NULL), // diff "Speaker" in max98357a }; static const struct snd_soc_dapm_route cv1835_max98357a_dapm_routes[] = { {"SPKR", NULL, "Speaker"}, }; static int cv1835_max98357a_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { return 0; } static int cv1835_max98357a_asoc_init(struct snd_soc_pcm_runtime *rtd) { // dual with private data // struct snd_soc_dai *cpu_dai = rtd->cpu_dai; // struct snd_soc_dai *codec_dai = rtd->codec_dai; // u32 ret; // /* Only need to set slot number while mode is TDM/PDM, otherwise default slot number is 2 */ // if (tdm_slot_no != 2) { // ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0F, 0x0F, tdm_slot_no, 32); // if (ret < 0) // return ret; // ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0F, 0x0F, tdm_slot_no, 32); // if (ret < 0) // return ret; // } return 0; } static struct snd_soc_ops cv1835_max98357a_ops = { .hw_params = cv1835_max98357a_hw_params, }; SND_SOC_DAILINK_DEFS(dailink_max98357a, DAILINK_COMP_ARRAY(COMP_CPU("4120000.i2s")), DAILINK_COMP_ARRAY(COMP_CODEC("max98357a", "HiFi")), DAILINK_COMP_ARRAY(COMP_PLATFORM("4120000.i2s"))); static struct snd_soc_dai_link cv1835_max98357a_dai[] = { [DPCM_AUDIO_SPKR] = { .name = "max98357a", .stream_name = "max98357a i2s", //"cv1835-max98357a", .init = cv1835_max98357a_asoc_init, .ops = &cv1835_max98357a_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAILINK_REG(dailink_max98357a), }, }; static struct snd_soc_card cv1835_max98357a = { .name = "cv1835 max98357a", // card name .owner = THIS_MODULE, .dai_link = cv1835_max98357a_dai, .num_links = ARRAY_SIZE(cv1835_max98357a_dai), // control may don't have // .controls = skylake_controls, // .num_controls = ARRAY_SIZE(skylake_controls), .dapm_widgets = cv1835_max98357a_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cv1835_max98357a_dapm_widgets), .dapm_routes = cv1835_max98357a_dapm_routes, .num_dapm_routes = ARRAY_SIZE(cv1835_max98357a_dapm_routes), // .fully_routed = true, // .late_probe = skylake_card_late_probe, }; static const struct of_device_id cvi_audio_match_ids[] = { { .compatible = "cvitek,cv1835-max98357a", //.data = (void *) &cv1835_max98357a_dai, }, {}, }; MODULE_DEVICE_TABLE(of, cvi_audio_match_ids); static int cv1835_max98357a_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &cv1835_max98357a; struct card_private *ctx; // create private data for card int ret; // struct device_node *np = pdev->dev.of_node, *dai; // dev_info("snd card name = %s",card->name); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, ctx); //save card info to snd_card ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); return ret; } return 0; } static struct platform_driver cv1835_max98357a_driver = { .driver = { .name = "cv1835-max98357a", .pm = &snd_soc_pm_ops, .of_match_table = cvi_audio_match_ids, }, .probe = cv1835_max98357a_probe, }; module_platform_driver(cv1835_max98357a_driver); MODULE_AUTHOR("EthanChen"); MODULE_DESCRIPTION("ALSA SoC cv1835 max98357a driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:cv1835-max98357a");
config SND_SOC_CV1835_MAX98357A
tristate "Support for the max98357a card"
help
max98357a codec enable.
obj-$(CONFIG_SND_SOC_CV1835_MAX98357A) += cv1800_max98357a.o
CONFIG_SND_SOC_CV1835_MAX98357A为了加载machine驱动。
CONFIG_SND_SOC_MAX98357A加载codec驱动。
CONFIG_SND_CV1835_I2S加载cpu/platform驱动。
CONFIG_SND_PROC_FS打开proc接口实现,用于/proc/asound目录下的声卡信息。
# sound -- max98357a
CONFIG_SND_SOC_CV1835_MAX98357A=y
CONFIG_SND_SOC_MAX98357A=y
CONFIG_SND_PROC_FS=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_CV1835_I2S=y
CONFIG_CV1835_I2S_SUBSYS=y
CONFIG_SND_SOC_CV1835_CONCURRENT_I2S=y
CONFIG_SND_PROC_FS
查看config最终生成文件
路径:duo-buildroot-sdk\linux_5.10\build\cv1800b_milkv_duo_sd.config
makefile
ifneq ($(CONFIG_SND_PROC_FS),)
snd-y += info.o
snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
endif
asound目录下存放这些内容
info.c
int snd_info_check_reserved_words(const char *str) { static const char * const reserved[] = { "version", "meminfo", "memdebug", "detect", "devices", "oss", "cards", "timers", "synth", "pcm", "seq", NULL }; const char * const *xstr = reserved; while (*xstr) { if (!strcmp(*xstr, str)) return 0; xstr++; } if (!strncmp(str, "card", 4)) return 0; return 1; }
alsa源码
duo-buildroot-sdk-develop\linux_5.10\sound\core\sound.c
查看dts,状态是ok的
查看dts生成文件
路径:duo-buildroot-sdk\u-boot-2021.10\build\cv1800b_milkv_duo_sd\arch\riscv\dts.cv1800b_milkv_duo_sd.dtb.dts.tmp
由于驱动中没有添加该实现,所以显示的状态不对。
需要在dtsi中添加信息,并在code中增加处理。
可能不需要添加,这个看芯片是否支持
duo-buildroot-sdk-develop\linux_5.10\sound\soc\cvitek\cv1835_i2s.c
这里可能需要开config,以及定义dts中mclk_out
device_property_read_string(&pdev->dev, "mclk_out", &mclk_out); if (!strcmp(mclk_out, "true")) dev->mclk_out = true; else dev->mclk_out = false; val = i2s_read_reg(dev->i2s_base, I2S_CLK_CTRL0); val &= ~(AUD_CLK_SOURCE_MASK); val &= ~(BCLK_OUT_FORCE_EN); /* blck_out output after transmission start */ #if defined(CONFIG_SND_SOC_CV1835_USE_AUDIO_PLL) if (dev->mclk_out == true) i2s_write_reg(dev->i2s_base, I2S_CLK_CTRL0, val | AUD_CLK_FROM_PLL | MCLK_OUT_EN | AUD_ENABLE); /* Turn aud_en on due to external codec might need MCLK to do register initialization */ else i2s_write_reg(dev->i2s_base, I2S_CLK_CTRL0, val | AUD_CLK_FROM_PLL); #else i2s_write_reg(dev->i2s_base, I2S_CLK_CTRL0, val | AUD_CLK_FROM_MCLK_IN); #endif
CC sound/soc/codecs/max98357a.o
CC sound/soc/cvitek/cv1835_max98357a.o
如图,声卡加载成功。
本章介绍如何添加max98357a的驱动并成功加载。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。