1. 音频驱动概述
什么是 ASoC(Advanced Sound on Chip)?
ASoC(高级音频子系统)是 Linux 内核中的一个 音频框架,用于管理 SoC(System on Chip)中的音频设备。其核心目标是 模块化设计,将 控制器(SAI/SSI)、音频处理(ASRC)、音频编解码器(Codec) 分离,使驱动更加灵活。
ASoC 主要组成部分
- Machine Driver(机型驱动)
- 负责 管理 SoC DAI(SAI/SSI)和 Codec DAI(如 WM8960)的连接关系。
- 主要作用:解析 设备树,配置 DAI 格式、时钟、音频路由。
- Platform Driver(平台驱动)
- 负责 底层音频控制器(如 SAI、ESAI、I2S) 的寄存器控制。
- Codec Driver(编解码器驱动)
- 负责 具体音频芯片(WM8960、SGTL5000) 的寄存器控制,通常通过 I2C/SPI 进行通讯。
什么是 DAI(Digital Audio Interface)?
DAI(数字音频接口)是 SoC 端 SAI 控制器 和 Codec 端数据接口 之间的桥梁,用于音频数据的传输。
在 i.MX8MP + WM8960 方案中:
- SoC 端:
sai3
(CPU DAI) - Codec 端:
wm8960-hifi
(Codec DAI)
2. WM8960 音频驱动架构
设备树节点解析
sound-wm8960 {
compatible = "fsl,imx-audio-wm8960";
model = "wm8960-audio";
audio-cpu = <&sai3>; // SoC 端 DAI
audio-codec = <&codec>; // Codec 端 DAI
audio-asrc = <&easrc>;
};
&i2c3 {
codec: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI3_MCLK1>;
clock-names = "mclk";
};
};
关键解析
audio-cpu = <&sai3>;
👉 绑定 SoC 侧的 SAI 控制器。audio-codec = <&codec>;
👉 绑定 WM8960 编解码器,通过 I2C 配置。clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI3_MCLK1>;
👉 配置 WM8960 的主时钟 MCLK。
3. Machine Driver 详解
Machine Driver 负责将 SoC DAI(sai3)和 Codec DAI(wm8960-hifi)正确连接,主要工作包括:
- 解析设备树,获取
sai3
和wm8960
设备节点。 - 配置 DAI,如 时钟、数据格式、TDM。
- 注册
snd_soc_card
,完成 SoC 和 Codec 的绑定。
核心代码解析
static int fsl_asoc_card_probe(struct platform_device *pdev)
{
struct device_node *cpu_np, *codec_np;
struct fsl_asoc_card_priv *priv;
struct platform_device *cpu_pdev;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
cpu_np = of_parse_phandle(np, "audio-cpu", 0);
codec_np = of_parse_phandle(np, "audio-codec", 0);
priv->dai_link[0].cpus->of_node = cpu_np;
priv->dai_link[0].codecs->of_node = codec_np;
priv->dai_link[0].platforms->of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
return devm_snd_soc_register_card(&pdev->dev, &priv->card);
}
核心作用
- 解析 设备树,找到
sai3
和wm8960
。 - 通过
snd_soc_dai_link
绑定 SoC 端和 Codec 端。 - 调用
snd_soc_register_card()
完成注册。
4. DAI 机制与 hw_params()
Machine Driver 只负责 注册 DAI,但音频流启动时,还需要 hw_params()
来配置时钟和数据格式。
hw_params()
作用
snd_soc_dai_set_sysclk()
👉 设置 MCLK。snd_soc_dai_set_fmt()
👉 设置 I2S 格式。snd_soc_dai_set_tdm_slot()
👉 设置 时分复用(TDM)。
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0),
priv->cpu_priv.sysclk_id,
priv->cpu_priv.sysclk_freq,
SND_SOC_CLOCK_IN);
snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), priv->dai_fmt);
return 0;
}
音频流启动流程
aplay
触发hw_params()
hw_params()
配置 MCLK、I2S格式、TDMsnd_pcm_ops->trigger()
启动音频流
5. 总结
- Machine Driver 解析设备树,绑定 SoC DAI 和 Codec DAI。
- 设备树
sound-wm8960
指定sai3
作为 SoC 端,wm8960@1a
作为 Codec 端。 - DAI(Digital Audio Interface)是 SoC 和 Codec 的桥梁,用于 音频数据传输。
hw_params()
负责配置 DAI 的时钟、数据格式。- 最终,
snd_soc_card
绑定 SoC 和 Codec,实现音频驱动。
本博文详细解析了 WM8960 在 i.MX8MP 平台上的音频驱动实现,希望能帮助开发者深入理解 ASoC 框架与 DAI 机制! 🎯