Lazy loaded image
I2S Audio 驱动实战(ASoC 子系统)
Words 3220Read Time 9 min
2026-3-6
本文从零开始,逐步完成一个基于 ASoC 框架的虚拟音频 Codec 驱动,覆盖 Codec 组件注册 → DAI 定义 → Machine Driver 绑定 → DT 配置 → QEMU ALSA 验证 全流程。

1. 前置条件

确保以下环境已就绪(参考同级页面的 QEMU + 交叉编译工具链搭建指南):
交叉编译器 arm-linux-gnueabihf-gcc 可用
已编译 v6.1 内核源码,且开启 CONFIG_SND_SOCCONFIG_SND_SOC_IMX_SAI 支持
QEMU mcimx6ul-evk(Cortex-A7)能正常启动 BusyBox rootfs
内核配置中已启用 CONFIG_SNDCONFIG_SND_PCMCONFIG_SND_SOC
为什么选 ASoC? ASoC(ALSA System on Chip)是 Linux 为嵌入式音频设计的子系统,将音频路径拆分为 Codec、Platform(CPU DAI)、Machine 三层,实现 Codec 驱动与 SoC 平台驱动的解耦复用。

2. ASoC 子系统关键概念(分层视角)

Layer 1 — Codec(音频编解码芯片)

概念
说明
snd_soc_component_driver
描述 Codec 组件,包含 probe/remove 回调、控件定义和电源管理策略
snd_soc_dai_driver
描述 Codec 侧 DAI,定义 playback/capture 支持的格式、采样率、通道数
snd_soc_dai_ops
Codec DAI 操作集:hw_params 配置硬件参数,set_fmt 设置 I2S/TDM 格式
devm_snd_soc_register_component
注册 Codec 组件 + DAI,生命周期由 devm 自动管理

Layer 2 — Platform(SoC CPU DAI + DMA)

概念
说明
snd_soc_component_driver
同样用于描述 Platform 组件(DMA 引擎),提供 PCM 操作回调
snd_soc_dai_driver
描述 CPU 侧 DAI(如 i.MX SAI),定义 SoC 端支持的音频能力
Codec 层与 Platform 层共用 snd_soc_component_driver + snd_soc_dai_driver 结构体,通过不同的驱动实例实现解耦。

Layer 3 — Machine(声卡绑定)

概念
说明
snd_soc_card
Machine Driver 核心结构,绑定 CPU DAI + Codec DAI + 音频路由,对应一块声卡
snd_soc_dai_link
定义一条音频链路:指定 CPU 侧 DAI、Codec 侧 DAI 和 Platform
SND_SOC_DAILINK_DEFS
宏定义 DAI link 的 CPU、Codec、Platform 组件数组
of_parse_phandle
从 DT 获取 CPU DAI / Codec 节点引用,实现 Machine Driver 与具体硬件解耦
devm_snd_soc_register_card
注册声卡,触发所有 DAI link 匹配 → Codec probe → PCM 创建

ASoC 三层架构


3. 驱动源码

3.1 Codec 驱动 — fake_codec.c

3.2 Machine 驱动 — fake_audio_card.c

3.3 关键结构说明

元素
作用
snd_soc_component_driver
描述 Codec 组件,包含 probe 回调和电源管理策略(idle_bias_on 等)
snd_soc_dai_driver
定义 Codec DAI 的 playback/capture 能力(采样率、格式、通道数)
snd_soc_dai_ops
DAI 操作集:hw_params 配置硬件参数,set_fmt 设置 I2S/TDM 格式
SND_SOC_DAILINK_DEFS
宏定义 DAI link 的 CPU、Codec、Platform 组件数组
snd_soc_dai_link
绑定一条音频链路,dai_fmt 指定 I2S 模式 + 极性 + 主从关系
of_parse_phandle
从 DT 获取 CPU DAI / Codec 节点引用,实现 Machine Driver 与具体硬件解耦
devm_snd_soc_register_card
注册声卡,ASoC 核心自动执行 DAI link 匹配 → Codec probe → PCM 创建
ASoC 三层解耦:Codec 驱动只关心音频编解码芯片本身(寄存器、DAI 能力),Platform 驱动只关心 SoC 的 DMA + I2S 控制器,Machine 驱动负责将两者「连线」。更换 SoC 只需换 Platform + Machine,Codec 驱动可直接复用。

4. Makefile

双模块编译obj-m 同时包含 Codec 驱动和 Machine 驱动,make 一次产出 fake_codec.ko + fake_audio_card.ko

5. 编译模块

编译成功后确认产出:
注意 depends: snd-soc-core:ASoC 核心是独立模块,加载驱动前必须确保 snd-soc-core.ko 已加载(或内核内建 CONFIG_SND_SOC=y)。

6. 设备树配置

6.1 I2S + Codec 节点说明

ASoC 驱动需要三个 DT 节点配合:
节点
角色
说明
sai1 / sai2
CPU DAI (Platform)
i.MX6UL SAI 控制器,已存在于基础 DTB 中(sai@2028000
fake_codec@1a
Codec
挂载在 I2C 总线上,提供音频编解码能力
fake-audio-card
Machine
根节点下,通过 phandle 引用 CPU DAI 和 Codec,完成绑定

6.2 需要注入的 DT 节点

6.3 直接修改主 DTB(QEMU 方式)


7. 一键构建脚本 — build.sh

运行方式

8. QEMU 内加载验证

8.1 启动 QEMU — run_qemu.sh

8.2 加载模块

8.3 确认 probe 调用

8.4 查看 ALSA 声卡信息

8.5 自动化验证脚本 — verify_audio.sh

预期输出:
QEMU 音频限制:QEMU mcimx6ul-evk 未完整模拟 SAI DMA 通道,aplay 实际播放可能报错或无声。本实战重点验证 ASoC 框架注册链路(probe → component → card → PCM),实际音频播放在真实硬件上验证。

9. 验证清单

make 编译无 warning,产出 fake_codec.ko + fake_audio_card.ko
modinfo 输出 depends: snd-soc-core
file *.ko 显示 ARM ELF relocatable
设备树中 i2c1 节点下存在 fake_codec@1a 子节点
设备树根节点下存在 fake-audio-card 节点
insmod fake_codec.kodmesg 显示 I2C probe OK, addr=0x1a
insmod fake_audio_card.kodmesg 显示 Fake-Audio-Card registered OK
/proc/asound/cards 显示声卡
/proc/asound/pcm 显示 PCM 设备
verify_audio.sh 全部步骤通过
rmmoddmesg 显示 remove called

10. 常见问题排查

现象
原因
解决方案
insmod: unknown symbol snd_soc_register_component
ASoC 核心未加载
insmod snd-soc-core.ko,或内核配置 CONFIG_SND_SOC=y
Codec probe 成功但 Card 注册失败
DT 中 audio-cpu phandle 指向的 SAI 节点未启用或不存在
确认 SAI 节点 status="okay",检查 &sai2 在 DTB 中是否存在
ASoC: CPU DAI ... not registered
SAI Platform 驱动未加载
确认 CONFIG_SND_SOC_IMX_SAI=y=m 并加载对应 .ko
/proc/asound/cards 为空
声卡注册失败或 /proc 未挂载
检查 dmesg 错误信息;确认 mount -t proc none /proc
aplay: device busy 或 DMA 错误
QEMU 未完整模拟 SAI DMA
正常限制,QEMU 下只验证注册链路。实际播放需真实硬件
Machine driver probe 报 -EPROBE_DEFER
Codec 或 CPU DAI 尚未就绪
确认 Codec 模块先于 Machine 加载;检查加载顺序
insmod: invalid module format
vermagic 不匹配
确保 KERNDIR 指向 QEMU 启动时使用的同一内核树

11. 向真实硬件(WM8960)迁移

完成虚拟驱动验证后,迁移到真实 WM8960 Codec 需替换以下部分:
  1. Codec 驱动替换为内核自带 — 使用 sound/soc/codecs/wm8960.cCONFIG_SND_SOC_WM8960=m
  1. DT compatible 更新
    1. Machine 驱动添加 DAPM 音频路由
      1. 添加时钟配置 — WM8960 需要 MCLK,通过 clk_get + clk_set_rate 配置
      1. 添加电源管理snd_soc_component_driver 中实现 suspend / resume

      12. 下一步扩展方向

      1. DAPM 动态电源管理 — 添加 Widget + Route,实现音频路径按需上下电
      1. ALSA Controls — 添加音量控制、静音开关等 Mixer 控件
      1. Triggered Buffer + DMA — 配合 SAI DMA,实现连续音频流传输
      1. 用户态 ALSA 应用 — 使用 alsa-lib 编写播放/录音程序
      1. 对接 V4L2 音视频同步 — IMU + Camera + Audio 多传感器时间戳同步
      上一篇
      Data Structure and Algorithm
      下一篇
      用面试拷问嵌入式技术栈

      Comments
      Loading...