1.ALSA介绍
ALSA全称为Advanced Linux Sound Architecture,是Linux系统中用于处理音频的的内核模块,其处理的数据为音频数据格式为PCM。ALSA功能的核心是管理音频PCM数据流的输入输出。ALSA原本是为桌面计算机设计的,没有考虑到嵌入式领域的应用。这导致其在嵌入式领域应用时,有以下的缺点:
- CPU和codec代码之间有强耦合,增加移植的难度以及代码的重复。
- 对用户的音频相关行为没有标准的处理方式。在移动应用场景中,用户的音频相关行为是很频繁的,因此需要特殊的处理机制。
- 在原有的ALSA框架中,未考虑能源的使用效率。但对于嵌入式设备来说(大多数情况下,设备由电池供电),能源的使用效率是关键点,因此需要一个机制来对其进行处理;
为解决以上问题,ALSA中添加了ASoC(全称为ALSA System on Chip)。其设计目的是为了更好地适配不同的嵌入式处理器和音频编解码芯片。ASoC框架具有以下优点:
- 独立的音频编解码芯片(codec)驱动,以减少和CPU代码间的耦合;
- 更为便捷的CPU和codec间的音频数据接口配置。
- Dynamic Audio Power Management(DAPM)模块可以动态调节音频部分的能源消耗。
- 减少音频的“pop and click”问题,同时增加在用户态的控制功能。
为实现以上的功能,ASoC将嵌入式音频系统划分为三种可重用的组件驱动,分别为machine class、platform classy以及codec class。其中platform class和codec class属于独立的,而machine class则是针对具体的开发板的。ASoC中各部分主要代码之间的联系如图所示

2.ASoC相关代码的分布
ALSA的ASoC属于Linux Kernel代码中的一部分,开发和适配ASoC相关的代码主要有以下几个部分
- DeviceTree 用于添加codec控制接口、音频接口、声卡设备信息等;
- KConfig用于管理需要编译的ASoC模块;
- Makefile用于管理代码的编译;
- sound/soc/codec目录中存放相关的音频编解码芯片的codec驱动程序;
- sound/soc/rockchip目录中存放machine和platform驱动;
- sound/soc目录下为ASoC core部分的代码;
2.1 ASoC主要的结构体
阅读ASoC相关代码时从snd_soc_component_driver
、snd_soc_dai_driver
、snd_soc_dai_link
等结构的声明可以快速分辨codec、platform和machine的相关代码。snd_soc_component_driver
、snd_soc_dai_driver
与codec、platform相关,snd_soc_dai_link
与machine相关。
另外Device Tree中的compatible属性与codec、platform、machine使用的硬件相关,从这一属性入手,可以帮助开发者快速定位开发板涉及到的源文件有哪些。以elf2开发板为例,从其定义的sound中的cpu、codec节点可以确定cpu、codec对应的属性节点,并且可以通过sound、cpu、codec中的compatible属性对应的字符串,可以确定codec、platform涉及到的源代码,从而确定编译到内核中的源代码构成。
elf2源代码中与声卡相关的Device Tree属性节点。Device Tree文件位于kernel\arch\arm64\boot\dts\rockchip\
路径下
从相关的设备树文件中提取出ASoC相关的设备节点信息。
machine相关的设备节点位于elf2-3588-common.dtsi
nau8822_sound: nau8822-sound {
status = "okay";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip-nau8822";
hp-det-gpio = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>;
io-channels = <&saradc 3>;
io-channel-names = "adc-detect";
keyup-threshold-microvolt = <1800000>;
poll-interval = <100>;
rockchip,format = "i2s";
rockchip,mclk-fs = <256>;
rockchip,cpu = <&i2s0_8ch>;
rockchip,codec = <&nau8822>;
rockchip,audio-routing =
"Headphone", "RHP",
"Headphone", "LHP",
"Speaker", "LSPK",
"Speaker", "RSPK",
"RMICP", "Main Mic",
"RMICN", "Main Mic",
"RMICP", "Headset Mic",
"RMICN", "Headset Mic",
"LMICP", "Main Mic",
"LMICN", "Main Mic",
"LMICP", "Headset Mic",
"LMICN", "Headset Mic";
pinctrl-names = "default";
pinctrl-0 = <&hp_det>;
play-pause-key {
label = "playpause";
linux,code = <KEY_PLAYPAUSE>;
press-threshold-microvolt = <2000>;
poll-interval = <1000>;
};
};
codec相关的设备节点位于elf2-3588-common.dtsi
&i2c3 {
status = "okay";
nau8822: nau8822[url=home.php?mod=space&uid=105298]@1a[/url] {
status = "okay";
compatible = "nuvoton,nau8822";
reg = <0x1a>;
clocks = <&mclkout_i2s0>;
clock-names = "mclk";
assigned-clocks = <&mclkout_i2s0>;
assigned-clock-rates = <12288000>;
pinctrl-names = "default";
pinctrl-0 = <&i2s0_mclk>;
};
platform相关的设备节点位于rk3588s.dts中
i2s0_8ch: i2s@fe470000 {
compatible = "rockchip,rk3588-i2s-tdm";
reg = <0x0 0xfe470000 0x0 0x1000>;
interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru MCLK_I2S0_8CH_TX>, <&cru MCLK_I2S0_8CH_RX>, <&cru HCLK_I2S0_8CH>;
clock-names = "mclk_tx", "mclk_rx", "hclk";
assigned-clocks = <&cru CLK_I2S0_8CH_TX_SRC>, <&cru CLK_I2S0_8CH_RX_SRC>;
assigned-clock-parents = <&cru PLL_AUPLL>, <&cru PLL_AUPLL>;
dmas = <&dmac0 0>, <&dmac0 1>;
dma-names = "tx", "rx";
power-domains = <&power RK3588_PD_AUDIO>;
resets = <&cru SRST_M_I2S0_8CH_TX>, <&cru SRST_M_I2S0_8CH_RX>;
reset-names = "tx-m", "rx-m";
rockchip,clk-trcm = <1>;
i2s-lrck-gpio = <&gpio1 RK_PC5 GPIO_ACTIVE_HIGH>; /* i2s0_lrck */
pinctrl-names = "default", "idle", "clk";
pinctrl-0 = <&i2s0_sdi0
&i2s0_sdi1
&i2s0_sdi2
&i2s0_sdi3
&i2s0_sdo0
&i2s0_sdo1>;
pinctrl-1 = <&i2s0_idle>;
pinctrl-2 = <&i2s0_lrck
&i2s0_sclk>;
status = "disabled";
};
从上面的各个设备树节点信息中的compatible
属性值,可以确定elf2中ASoC与nau8822音频芯片相关的源代码由codec的nau8822.c和nau8822.h、platform的rockchip_i2s_tdm.c和rockchip_i2s_tdm.h以及machine的rockchip_multicodecs.c组成。
这样由以上三个文件作为起点,理解ASoC中codec、platform和machine是如何创建的,三者如何结合到一起最终创建出声卡设备注册到ALSA Core当中。