资讯动态

从 aplay 到 Speaker:一条完整的 ASoC 播放链路到底发生了什么?(八)

发布时间:2026/9/5 8:39:19 来源:尧图企业网站定制
目录一、先从最简单的场景开始二、第一站aplay三、WAV 文件和 PCM 到底是什么关系四、第二站ALSA PCM五、这里千万不要把 ALSA 和 ASoC 混为一谈六、第三站ASoC七、第四站DAI Link八、第五站hw_params()九、为什么 hw_params() 失败以后就播放不了了十、第六站set_fmt()十一、为什么 I2S 配错会出现“有数据但没声音”十二、第七站Clock十三、音频为什么这么喜欢“时钟”十四、第八站DMA十五、DMA 为什么需要 Buffer十六、这里出现一个很重要的概念Period十七、第九站CPU DAI十八、第十站I2S / TDM十九、I2S 到底传了什么二十、第十一站Codec DAI二十一、DAC 做什么二十二、第十二站DAPM二十三、DAPM 就像“自动开关电工”二十四、Mixer 也可能让你“有数据但没声音”二十五、第十三站PGA 和 Volume二十六、第十四站功放 PA二十七、终于到了 Speaker二十八、那么 aplay 成功到底说明了什么二十九、没有声音时应该怎么查1. 先确认声卡存在2. 确认 PCM 能否打开3. 看 Sample Rate4. 看 DAI Format5. 看 Clock6. 看 DMA7. 看 DAPM8. 看 Mixer9. 看 Codec Register三十、为什么我们要花这么大篇幅理解这条链三十一、把整个播放过程压缩成一张图三十二、到这里传统 ASoC 基础基本闭环三十三、下一步为什么 Qualcomm Audio 又突然复杂起来了前面七章我们一直在认识 ASoC 的“零件”ALSA ASoC CPU DAI Codec DAI Machine Driver Device Tree DAPM零件认识得差不多了现在终于可以把它们装起来。这一章我们不再纠结某个结构体有多少成员而是回答一个非常实际的问题我执行aplay test.wav后到底是谁在干什么最终我们要把这条链路跑通aplay ↓ ALSA ↓ ASoC ↓ Machine Driver ↓ DAI Link ↓ CPU DAI ↓ DMA ↓ I2S / TDM ↓ Codec DAI ↓ DAPM ↓ DAC ↓ Speaker如果这一章真正理解了后面再看 Qualcomm Audio就不会再觉得那些模块像一锅煮熟的面条。一、先从最简单的场景开始假设我们现在有一台非常朴素的设备SoC │ CPU DAI │ I2S │ ↓ Codec │ DAC │ ↓ Speaker用户执行aplay test.wav我们的目标就是test.wav ↓ PCM 数据 ↓ SoC ↓ I2S ↓ Codec ↓ 模拟信号 ↓ Speaker注意一个非常重要的概念aplay并不是直接操作 Codec。它也不知道你的 Codec 是什么型号。甚至它根本不关心你后面是WM8960 ES8316 NAU8822 某个 Qualcomm Codecaplay只关心一件事“给我一个可以播放 PCM 数据的 ALSA PCM 设备。”至于后面怎么把数据送到 Speaker是内核音频框架的事情。二、第一站aplay先看用户空间。执行aplay test.wavaplay首先要打开一个 PCM 播放设备。可以粗略理解成aplay │ ↓ 打开 PCM │ ↓ 设置参数 │ ↓ 写入 PCM 数据 │ ↓ 开始播放典型的操作包括snd_pcm_open(); snd_pcm_hw_params(); snd_pcm_writei(); snd_pcm_prepare(); snd_pcm_start();不同版本和实现细节会有所区别这里只关注整体流程。例如test.wav是Sample Rate : 48000 Hz Channels : 2 Format : S16_LE那么应用就会告诉 ALSA我要 48 kHz 2 Channel 16 bit三、WAV 文件和 PCM 到底是什么关系这里顺便解决一个小白特别容易混淆的问题。WAV 是一种文件格式。里面通常包含文件头 PCM 音频数据例如test.wav ┌──────────────────┐ │ WAV Header │ ├──────────────────┤ │ PCM Data │ │ PCM Data │ │ PCM Data │ │ ... │ └──────────────────┘aplay读取 WAV 文件后真正送入 ALSA 的核心数据是PCM samples比如100 230 -120 500 ...当然真实数据会按照采样格式组织。所以最终WAV 文件 ↓ 解析 ↓ PCM 数据 ↓ ALSA四、第二站ALSA PCM进入内核以后首先会经过 ALSA PCM 子系统。这里的 PCM 可以理解成Linux 对音频数据流的一套标准接口。应用看到的是/dev/snd/...以及PCM Playback PCM Capture例如aplay -l可能看到card 0: MySoundCard device 0: My PCM这时候我们可以认为ALSA ↓ 发现了一张 Sound Card ↓ 发现了一个 PCM Playback Device五、这里千万不要把 ALSA 和 ASoC 混为一谈这是初学者非常容易搞混的地方。可以简单理解用户空间 ↓ ALSA API ↓ ---------------- ALSA Core ---------------- ↓ ASoC ↓ CPU / Codec / MachineALSA 是整个 Linux 音频框架的一部分。ASoC 是 Linux 针对 SoC 音频硬件的一套框架。所以ASoC ⊂ ALSA用这个思路理解会简单很多。六、第三站ASoC现在问题来了ALSA 收到 PCM 数据之后怎么知道应该送到哪个硬件这时候 ASoC 开始工作。ASoC 早已经通过前面几章介绍的东西建立好了硬件关系Sound Card │ ↓ DAI Link │ ┌──┴──┐ ↓ ↓ CPU Codec DAI DAI也就是说aplay ↓ ALSA ↓ 找到 PCM ↓ ASoC ↓ 找到对应的 DAI Link于是音频数据有了下一站。七、第四站DAI Link前面第七章我们专门讲过snd_soc_dai_link现在终于派上用场了。假设CPU DAI cpu-i2s0 Codec DAI codec-hifiMachine Driver 建立cpu-i2s0 │ │ DAI Link ↓ codec-hifi所以当用户播放aplay test.wavASoC 知道这个 PCM ↓ 应该走这个 DAI Link ↓ CPU DAI Codec DAI这就是 Machine Driver 前面辛辛苦苦“牵线搭桥”的意义。八、第五站hw_params()真正开始播放之前有一个非常重要的步骤hw_params()它负责告诉硬件这次播放到底使用什么参数。例如48 kHz 2 Channel 16 bit这时候链路上的各个组件需要根据这些参数进行配置。可以粗略理解成ALSA ↓ ASoC ↓ DAI Link ↓ CPU DAI ↓ Codec DAI大家开始讨论“这次是 48k 还是 44.1k”“几个声道”“数据是 16bit 还是 24bit”“接口是 I2S 还是其他格式”这就是hw_params()这类回调发挥作用的地方。九、为什么hw_params()失败以后就播放不了了例如应用想播放192 kHz但是 Codec 根本不支持。那么在参数配置阶段就可能失败。日志中可能看到hw_params failed或者Invalid argument最终aplay ↓ 失败所以播放失败不一定是 DMA 的问题也不一定是 Codec 坏了。可能在很早的hw_params()阶段就已经失败了。十、第六站set_fmt()接下来还有一个非常重要的概念set_fmt()它负责配置数字音频接口的格式。例如I2S Left Justified DSP_A DSP_B以及时钟主从关系CPU Master Codec Slave或者CPU Slave Codec Master还可能涉及BCLK LRCLK等极性和时序配置。十一、为什么 I2S 配错会出现“有数据但没声音”比如 CPU 认为I2SCodec 却按照DSP_A来解析。那么可能出现CPU101010101... Codec数据确实从 CPU 出来了。时钟甚至也有。但是 Codec 根本没按照正确方式理解这些数据。结果软件播放成功 硬件完全听不懂 用户没声音这类问题在 Audio 调试中非常经典。十二、第七站Clock音频系统非常依赖时钟。常见的就有MCLK BCLK LRCLK简单理解MCLK ↓ Codec / Audio Clock BCLK ↓ Bit Clock LRCLK ↓ Left / Right Channel Clock例如48 kHz意味着LRCLK通常与 48 kHz 的采样节奏相关。而BCLK则与采样率 × 声道数 × 每个 sample 的 bit 数等因素相关。具体硬件还可能存在额外时钟关系所以这里不要死记一个公式就认为所有平台都完全一样。十三、音频为什么这么喜欢“时钟”因为音频是一个非常讲究节奏的东西。你不能今天送 48000 个 sample 明天送 100 个 后天突然送 2 亿个否则声音就开始“哒哒哒——呜——咔——”所以 Audio Hardware 非常依赖稳定的Clock后面你进入 Qualcomm Audio会发现Clock PLL MCLK BCLK LRCLK Sample Rate这些词出现频率非常高。十四、第八站DMA参数都配置好以后真正的 PCM 数据要开始搬运。问题来了假设48 kHz 2 Channel 16 bit音频数据会源源不断产生。如果每次数据都让 CPU读取 ↓ 复制 ↓ 发送 ↓ 等待那 CPU 会非常忙。所以一般会使用DMA也就是让 DMA 控制器帮 CPU 搬数据。简单画CPU │ 配置 DMA │ ↓ ┌───────────┐ │ DMA │ └───────────┘ │ │ ↓ ↓ PCM Buffer Audio HWCPU 负责安排工作。DMA 负责大量重复搬运。十五、DMA 为什么需要 Buffer因为音频数据是连续流。不能sample ↓ 硬件 ↓ sample ↓ 硬件每一个 sample 都让 CPU 参与一次。通常会准备一块PCM Buffer例如┌───────────────────────────┐ │ PCM PCM PCM PCM PCM PCM │ │ PCM PCM PCM PCM PCM PCM │ └───────────────────────────┘ ↑ DMADMA 不断从 Buffer 中读取数据。Buffer 消耗到一定程度后DMA ↓ 产生中断 ↓ 软件继续准备数据然后继续播放。十六、这里出现一个很重要的概念PeriodALSA PCM Buffer 通常还会进一步划分为Buffer ├── Period ├── Period ├── Period └── Period例如Buffer Size 4096 frames Period Size 1024 frames可以粗略理解整个 Buffer ┌──────┬──────┬──────┬──────┐ │ 1024 │ 1024 │ 1024 │ 1024 │ └──────┴──────┴──────┴──────┘DMA 每处理完一个 Period可能产生一次中断。所以你以后看到buffer_size period_size period_bytes不要害怕。它们都是在描述“音频数据准备多少、多久通知软件一次。”十七、第九站CPU DAI现在PCM Buffer ↓ DMA ↓ CPU DAICPU DAI 是 SoC 侧的数字音频接口。它可能对应I2S TDM或者平台上的其他音频接口。CPU DAI 主要负责采样参数 接口格式 时钟 数据传输等相关配置和操作。十八、第十站I2S / TDM假设硬件采用 I2SCPU DAI │ │ I2S ↓ Codec DAI这里传输的是数字音频数据注意这里还是 0 和 1。Speaker 现在还没有真正收到模拟声音。十九、I2S 到底传了什么初学阶段可以简单理解为三类信号BCLK LRCLK DATA例如BCLK ───────────────────── LRCLK ────────┐ ┌───── │ │ └──────┘ DATA 101010101010101010...其中DATA传输音频数据。BCLK提供 bit 级别的时钟。LRCLK帮助区分左右声道。具体接口时序会根据协议模式不同而变化。二十、第十一站Codec DAI数据到达 CodecI2S ↓ Codec DAICodec DAI 收到的是数字音频数据。然后继续往 Codec 内部走Codec DAI ↓ DAC二十一、DAC 做什么DACDigital-to-Analog Converter就是数字转模拟。之前的数据101010101010...还是数字。经过 DACDigital ↓ DAC ↓ Analog才变成模拟音频信号。当然真实 Codec 内部远比这复杂但入门阶段抓住这个核心即可。二十二、第十二站DAPM这里又轮到我们第六章学过的DAPM出场了。Codec 内部可能有DAC Mixer PGA Speaker Output例如DAC ↓ Mixer ↓ PGA ↓ SPKOUTDAPM 会根据当前使用的音频路径判断哪些 Widget 需要工作。例如播放 SpeakerDAC ↓ Mixer ↓ PGA ↓ Speaker那么这条路径需要被打开。二十三、DAPM 就像“自动开关电工”可以把 DAPM 想象成一个非常勤快的电工。当你播放Speaker它发现DAC ↓ Mixer ↓ PGA ↓ Speaker需要工作。于是DAC ON Mixer ON PGA ON而ADC Mic如果完全没有使用就不需要跟着一起上电。这就是 DAPM 的价值之一只让真正需要的音频路径工作。二十四、Mixer 也可能让你“有数据但没声音”比如路径DAC ↓ Mixer ↓ Speaker但是 MixerDAC → Speaker OFF那么DMA我在搬数据啊 CPU DAI我在发数据啊 Codec DAI我收到数据了啊最后Speaker我不知道你们在忙什么。于是没有声音。所以 Audio 调试时Mixer Control 是非常重要的。二十五、第十三站PGA 和 VolumeCodec 内部可能还有PGA以及Volume Gain Mute例如DAC ↓ Mixer ↓ PGA ↓ Speaker如果Volume 0或者Mute ON结果依然可能是播放正常 但是没声音所以“数据流通了”不等于“人耳能听到”。二十六、第十四站功放 PA现实中的手机/开发板可能还不止 Codec。例如Codec ↓ External PA ↓ SpeakerPAPower Amplifier负责把音频信号进一步放大。于是完整链路可能变成CPU ↓ I2S ↓ Codec ↓ DAC ↓ Mixer ↓ PGA ↓ PA ↓ Speaker如果 PA 没打开前面全部正常 ↓ 最后 ↓ 没声音这种情况也非常常见。二十七、终于到了 Speaker现在终于可以把整个过程画出来用户空间 │ ↓ aplay │ ↓ ALSA PCM API │ ↓ ALSA │ ↓ ASoC │ ↓ Sound Card │ ↓ DAI Link / \ ↓ ↓ CPU DAI Codec DAI │ │ │ ↓ │ DAC │ ↓ │ Mixer │ ↓ │ PGA │ ↓ └──I2S──→ PA ↓ Speaker这就是一条非常典型的播放链路。二十八、那么aplay成功到底说明了什么这是非常值得注意的问题。假设aplay test.wav没有报错。只能说明至少软件播放流程中的某些环节已经成功。不能直接证明Speaker 一定有声音。因为后面还有很多可能出问题的地方Clock DMA I2S Codec DAPM Mixer Volume Mute PA Speaker所以aplay 成功和Speaker 有声音不是完全等价的。二十九、没有声音时应该怎么查不要一上来“Codec 坏了”这属于典型的“拍脑袋调音频”。正确方式应该沿着链路一层一层排查。1. 先确认声卡存在aplay -l如果连声卡都没有Machine Driver Device Tree CPU DAI Codec Driver DAI Link优先检查这些地方。2. 确认 PCM 能否打开例如aplay -D hw:0,0 test.wav如果这里直接失败Invalid argument重点看hw_params3. 看 Sample Rate例如48 kHz确认CPU DAI Codec DAI是否支持。4. 看 DAI Format确认I2S DSP_A DSP_B是否匹配。5. 看 Clock检查MCLK BCLK LRCLK是否正常。6. 看 DMA确认DMA Buffer Period有没有正常工作。7. 看 DAPM确认DAC Mixer PGA Speaker相关路径有没有真正打开。8. 看 Mixer例如amixer -c 0检查Mute Volume Switch9. 看 Codec Register如果前面的都没问题再去看Codec Register确认Power DAC Mixer Volume Mute等配置是否正确。三十、为什么我们要花这么大篇幅理解这条链因为以后你看日志时脑子里应该自动有一条路线。比如看到hw_params failed你就知道PCM 参数配置阶段看到set_fmt failed你就知道DAI 格式/时序相关看到DMA timeout就应该想到DMA / Buffer / Hardware看到DAPM就想到音频路径 / Power / Widget看到Codec就想到寄存器 / DAC / Mixer / Volume这就是我们学习 ASoC 的目的。不是为了背struct snd_soc_dai_link里面到底有多少成员。而是为了建立看到一个问题就知道它大概率在哪一层。三十一、把整个播放过程压缩成一张图最后再看一次。┌──────────────────────┐ │ Application │ │ aplay │ └──────────┬───────────┘ │ ↓ ┌──────────────────────┐ │ ALSA PCM │ └──────────┬───────────┘ │ ↓ ┌──────────────────────┐ │ ASoC │ │ Sound Card │ └──────────┬───────────┘ │ ↓ ┌──────────────────────┐ │ DAI Link │ └───────┬───────┬──────┘ │ │ ↓ ↓ CPU DAI Codec DAI │ │ │ ↓ │ DAC │ ↓ │ Mixer │ ↓ │ PGA │ ↓ └──→ PA ↓ Speaker数据传输则大致是PCM Buffer ↓ DMA ↓ CPU DAI ↓ I2S/TDM ↓ Codec DAI ↓ DAC ↓ Analog ↓ Speaker而控制这条链路的则是Device Tree ↓ Machine Driver ↓ Sound Card ↓ DAI LinkCodec 内部的路径管理则主要交给DAPM这样一来前面七章的知识终于全部串起来了。三十二、到这里传统 ASoC 基础基本闭环现在我们已经知道ALSA解决用户空间和 PCM 接口。ASoC解决 SoC 音频设备的组织。Machine Driver负责组装整台设备。Device Tree描述硬件和连接关系。DAI Link连接 CPU DAI 和 Codec DAI。CPU DAI负责 SoC 侧数字音频接口。Codec DAI负责 Codec 侧数字音频接口。DAPM负责 Codec 内部音频路径和动态电源管理。最后DAC ↓ PA ↓ Speaker把数字世界里的 PCM 数据变成我们真正能听到的声音。三十三、下一步为什么 Qualcomm Audio 又突然复杂起来了到这里你可能会产生一个疑问既然 ASoC 已经能完成 CPU → Codec → Speaker为什么 Qualcomm Audio 还要搞那么多东西比如以后你会看到Audio HAL PAL DSP ADSP AFE ASM ADM GPR APR COPP Copp MI2S TDM第一眼看起来像“Linux Audio DLC地狱难度版”其实它们是在解决更复杂的问题。因为现代 Qualcomm 平台的音频通常已经不是CPU ↓ I2S ↓ Codec这么简单。更典型的思路可能是Application ↓ Audio Framework ↓ Audio HAL ↓ DSP ↓ AFE / ASM / ADM ↓ Audio Interface ↓ Codec ↓ Speaker也就是说真正复杂的地方从“CPU 怎么连接 Codec”开始逐渐转向“音频数据到底应该交给谁处理”。这也是下一阶段最值得学习的内容。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价