【milkv】max98357a驱动添加speaker

news2024/12/23 6:40:09

文章目录

  • 一、电路
    • 1.1 duo音频接口
    • 1.2 I2S2连接
  • 二、I2S2介绍
    • 2.1 参考cv182x的dts实现
    • 2.2 参考cv1835_fpga
    • 2.3 cv180x
    • 2.4 改动——保留i2s
    • 2.5 I2S小结
  • 三、参考资料
    • 3.1 文章
    • 3.2 手册
  • 四、驱动路径
  • 五、添加codec驱动——max98357a
    • 5.1 config
    • 5.2 dtsi
    • 5.3 dai_driver
  • 六、查看platform/cpu
  • 七、添加machin驱动
    • 7.1 仿照182x,183x
    • 7.2 重新定义dai_link
    • 7.3 添加驱动
    • 7.4 添加Kconfig
    • 7.5 添加makefile
    • 7.6 打开config
  • 八、问题
    • 8.1 /proc/asound目录没有生成
    • 8.2 使用audio_debug读取adc、i2s0/2,状态未打开
    • 8.3 mclk
  • 九、结果
    • 9.1 编译
    • 9.2 设备查看
  • 小结

一、电路

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.1 文章

https://blog.csdn.net/pingis58/article/details/109235204
https://github.com/beagleboard/bb.org-overlays/issues/39

https://cloud.tencent.com/developer/article/2225253

3.2 手册

在这里插入图片描述
数据手册
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驱动——max98357a

5.1 config

如上章介绍的,codec路径下的配置,所以加载codec驱动需要配置config。

CONFIG_SND_SOC_MAX98357A=y

5.2 dtsi

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

5.3 dai_driver

这里的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,
};

六、查看platform/cpu

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

七、添加machin驱动

7.1 仿照182x,183x

这里定义的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

7.2 重新定义dai_link

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

7.3 添加驱动

添加驱动代码
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");

7.4 添加Kconfig

config SND_SOC_CV1835_MAX98357A
	tristate "Support for the max98357a card"
	help
	  max98357a codec enable.

7.5 添加makefile

obj-$(CONFIG_SND_SOC_CV1835_MAX98357A) += cv1800_max98357a.o

7.6 打开config

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

八、问题

8.1 /proc/asound目录没有生成

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

8.2 使用audio_debug读取adc、i2s0/2,状态未打开

查看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中增加处理。

8.3 mclk

可能不需要添加,这个看芯片是否支持

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

九、结果

9.1 编译

  CC      sound/soc/codecs/max98357a.o
  CC      sound/soc/cvitek/cv1835_max98357a.o

9.2 设备查看

如图,声卡加载成功。
在这里插入图片描述

小结

本章介绍如何添加max98357a的驱动并成功加载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1061126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VUE3照本宣科——内置指令与自定义指令及插槽

VUE3照本宣科——内置指令与自定义指令及插槽 前言一、内置指令1.v-text2.v-html3.v-show4.v-if5.v-else6.v-else-if7.v-for8.v-on9.v-bind10.v-model11.v-slot12.v-pre13.v-once14.v-memo15.v-cloak 二、自定义指令三、插槽1.v-slot2.useSlots3.defineSlots() 前言 &#x1f…

Chrome之解决DevTools: Failed to load data:No resource with given identifier found

问题 使用DevTools抓包时候, 有些跨域请求无法在加载出来, 提示 Failed to load data:No resource with given identifier found 解决办法 换其他浏览器 下断点 打开DevTools, 选择源代码/来源/Sources,找到事件监听器断点/Event Listener Breakpoints, 找到加载/Load下面…

C++树详解

树 树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。n0时称为空树。在任意一颗非空树中&#xff1a;①有且仅有一个特定的称为根&#xff08;Root&#xff09;的结点&#xff1b;②当n>1时&#xff0c;其余结点可分为m&#xff08…

十天学完基础数据结构-第七天(图(Graph))

图的基本概念 图是一种数据结构&#xff0c;用于表示对象之间的关系。它由两个基本组件构成&#xff1a; 顶点&#xff08;Vertex&#xff09;&#xff1a;也被称为节点&#xff0c;代表图中的对象或实体。 边&#xff08;Edge&#xff09;&#xff1a;连接两个顶点的线&…

【setxattr+userfaultfd】SECCON2020-kstack

这个题主要还是练习 userfaultfd 的利用。说实话&#xff0c;userfaultfd 的利用还是挺多的&#xff0c;虽然在新的内核版本已经做了相关保护。 老规矩&#xff0c;看下启动脚本 #!/bin/sh qemu-system-x86_64 \-m 128M \-kernel ./bzImage \-initrd ./rootfs.cpio \-append …

第三章、运输层

文章目录 3.1 概述和运输层服务3.1.1 运输层和网络层的关系3.1.2 因特网运输层概述 3.2 多路复用与多路分解3.3 无连接运输&#xff1a;UDP3.4 可靠数据传输原理3.4.1构造可靠数据传输协议rdt1.0rdt2.xrdt3.0 3.4.2 流水线可靠数据传输协议3.4.3 回退N步3.4.4选择重传 3.5 面向…

x64内核实验1-调试环境配置

x64内核实验1-调试环境配置 这是一套x64内核实验的课程&#xff0c;我之前学习32位内核的时候就是在网上找的各种教程当学完32位很久之后发现在网上的64位内核相关的完整教程真的很少&#xff0c;所以就想着不如自己写一点方便对内核有兴趣的人能更好的入门&#xff0c;首先声…

哈希原理和解决哈希冲突方法

第一 哈希介绍 哈希和红黑树是我早期就听过的名词&#xff0c;却一直没见到真面目&#xff0c;实现哈希后才发现原来我早就使用过了哈希。看下面例题。 用map和set都可以轻松解决&#xff0c;但是在我写这题时我还不会用map和set&#xff0c;我用了另一种方法。看下面代码。先定…

【Java】微服务——Nacos配置管理(统一配置管理热更新配置共享Nacos集群搭建)

目录 1.统一配置管理1.1.在nacos中添加配置文件1.2.从微服务拉取配置1.3总结 2.配置热更新2.1.方式一2.2.方式二2.3总结 3.配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&#xff09;运行两个UserApplication&#xff0c;使用不同的pr…

Office Tool Plus下载与神龙版官网下载

文章目录 一、Office Tool Plus下载二、神龙下载 Office Tool Plus简称OTP&#xff0c;是一款专业的Office官方镜像下载器&#xff0c;可以下载安装Word、Excel、Visio等。神龙用于快速激活使用。 注意&#xff1a;Win7系统必须要SP1或更高版本才能使用&#xff0c;Office Tool…

intel 一些偏门汇编指令总结

intel 汇编手册下载链接&#xff1a;https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html LDS指令&#xff1a; 手册中可以找到 位于 3-588 根据手册内容猜测&#xff1a;lds r16 m16:16 的作用&#xff0c;是把位于 [m16:16] 内存地址的数…

JMeter的详细使用及相关问题

一、中文乱码问题 如果出现乱码&#xff0c;需要修改编码集&#xff0c;&#xff08;版本问题有的不需要修改&#xff0c;就不用管&#xff09; 修改后保存重启就好了。 JMeter5.5版本的按照如下修改&#xff1a; 二、JMeter的启动 ①建议直接用ApacheJMeter.jar双击启动…

Zabbix4自定义脚本监控MySQL数据库

一、MySQL数据库配置 1.1 创建Mysql数据库用户 [rootmysql ~]# mysql -uroot -p create user zabbix127.0.0.1 identified by 123456; flush privileges; 1.2 添加用户密码到mysql client的配置文件中 [rootmysql ~]# vim /etc/my.cnf.d/client.cnf [client] host127.0.0.1 u…

CSDN博主粉丝数突破10万:坚持分享的力量与收获

今天&#xff0c;我在CSDN上看到了一位好友的统计数据&#xff0c;他统计了CSDN上所有粉丝数量排名靠前的博主的排名。虽然这个统计可能存在一些误差&#xff0c;但大体上应该是准确的。我惊讶地发现&#xff0c;截止到2023年10月4日&#xff0c;我的粉丝数量已经达到了101,376…

QScrollArea样式

QScrollBar垂直滚动条分为sub-line、add-line、add-page、sub-page、up-arrow、down-arrow和handle几个部分。 QScrollBar水平滚动条分为sub-line、add-line、add-page、sub-page、left-arrow、right-arrow和handle几个部分。 部件如下图所示&#xff1a; /* 整个滚动…

Pikachu靶场——文件包含漏洞(File Inclusion)

文章目录 1. File Inclusion1.2 File Inclusion(local)1.2.1 源代码分析1.2.2 漏洞防御 1.3 File Inclusion(remote)1.3.1 源代码分析1.3.2 漏洞防御 1.4 文件包含漏洞防御 1. File Inclusion 还可以参考我的另一篇文章&#xff1a;文件包含漏洞及漏洞复现。 File Inclusion(…

商业智能系统的主要功能包括数据仓库、数据ETL、数据统计输出、分析功能

ETL服务内容包含&#xff1a; 数据迁移数据合并数据同步数据交换数据联邦数据仓库

plt 画图不显示label

没写 plt.legend() 这个 ! # 效果模拟-------------- import matplotlib.pyplot as plt import matplotlib as mpl # matplotlib其实是不支持显示中文的 显示中文需要一行代码设置字体 mpl.rcParams[font.family] = STKAITI # STKAITI——字体 plt.rcParams[axes.unicode_m…

亲,您的假期余额已经严重不足了......

引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 转眼八天长假已经接近尾声了&#xff0c;今天来总结一下大家的假期&#xff0c;聊一聊假期关于学习的看法&#xff0c;并预估一下大家节后大家上班时的样子。 1.放假前一天 即将迎来八天…

侯捷 C++ STL标准库和泛型编程 —— 9 STL周围

最后一篇&#xff0c;完结辽&#xff01;&#x1f60b; 9 STL周围 9.1 万用Hash Function Hash Function的常规写法&#xff1a;其中 hash_val 就是万用Hash Function class CustumerHash { public:size_t operator()(const Customer& c) const{ return hash_val(c.fna…