简介:本资源是高通平台音频子系统入门教程,面向刚接触高通音频开发或希望了解其整体架构的工程师。内容从硬件框架、音频接口到软件架构与代码位置均有梳理,并给出Audio Bringup启动流程、PIL镜像加载、声卡注册等关键步骤,同时围绕ACDB校准和调试工具展开介绍,帮助读者建立从启动到校准、再到故障排查的完整认知。压缩包为单个docx文档,约2.64MB,结构为完整章节式手册,适合作为案头参考或培训材料。目前已有7246人学习,说明该资料在实际开发者中有较高的参考和复用价值。
1. 高通 Audio 入门:从开机没声音到把通路调通,你需要的不是玄学而是这张地图
做高通平台 bringup 的第一周,某开发者在模拟项目X的调试现场遇到的现象八成你也见过:烧完整包镜像,系统跑起来了,屏亮了,触摸也能用,唯独扬声器一个字都不出。当时硬件量过 PA 使能脚,电压正常;Audio 服务进程也在跑,logcat 里没有 crash。整整排查一个下午,最后发现问题出在 mixer path 默认路由根本没把扬声器节点打开。高通 Audio 系统之所以难上手,不是因为它用了什么高深算法,而是你的代码要同时穿过 Linux ALSA 框架、tinyalsa 工具链、ADSP 音频固件、音频 DSP 路由这四层,调试工具却只有几个命令和一堆 xml。与其对着报错瞎猜,不如先把这四层的地图画清楚。这篇文章面向刚接手高通平台的驱动工程师或系统软件工程师,把构建、烧录、调试到踩坑的完整路径放平讲透。
2. 高通音频架构:从 ALSA 到 ADSP 的四层调用链
2.1 为什么要先理解分层而不是先看代码
高通平台音频代码量巨大,msm 目录下的文件有数千个,如果直接从某个 codec 驱动开始读,你会淹没在函数指针和 platform 驱动的海洋里。正确做法是先建立调用链的整体图景:应用层通过 AudioFlinger 下发 PCM 流,走 ALSA lib 到达内核的 snd_pcm 驱动;内核侧高通把数据分成两条路,一条是 primary MI2S 这种直接走 AP 内部编解码器,另一条是经过共享内存端口传给 ADSP,由 DSP 完成混音、重采样、回声消除等处理。高通声卡在 proc 节点下通常注册为 snd_soc_msm 或类似的 ASoC 卡,节点路径类似 /dev/snd/pcmC0D0p。从设备节点角度,理解高通问题只需要记住:pcmCxDx 后面的 p 代表 playback,c 代表 capture,数字越大代表越新添加的 PCM 设备。
分层调用链中,mixer 控制接口是最重要的调试入口。tinymix 里看到的每一个控件名字,最终对应到 codec 驱动里 snd_kcontrol_new 的 get/put 回调。举个例子,你写 tinymix "SLIM RX0 MUX" "AIF1_PB",这里的 AIF1_PB 字符串将触发 codec 驱动查找 RX0 所在的音频路由表项,继而配置寄存器。也就是说,高通音频调试几乎都是字符串匹配游戏,而不是寄存器数值游戏。只要理解这条链,排查方向就明确了:app 层报错看 logcat,内核报错看 dmesg,硬件无输出先查 mixer 状态。
2.2 控制路径与数据路径要分开看
高通音频设计里,控制路径和数据路径是解耦的。控制路径通过 ALSA control 接口完成路由配置,数据路径则可能走 slimbus、soundwire 或 I2S。调试时你常做的一件事是:先用 tinyplay 播放 wav,再用 tinymix 抓取当前的寄存器状态。如果播放不报错但物理上没声音,一种常见原因是控制路径配置了错误的 codec 时钟源,导致数据路径上的位时钟没了。这种问题用示波器看 MCLK/BCLK 波形就能立刻定位,比反复改 xml 快得多。
我一般会在第一次拿到板子时做三件事:读取 /proc/asound/cards 确认声卡注册顺序;用 tinyplay 播一个 1kHz 0dB 的正弦波 wav,同时用 tinymix 输出完整 mixer 状态保存为基线文件;再对比出问题时与基线文件的差异。这个习惯在后续排查问题时省下大量时间。值得注意的是,高通每代平台声卡个数不同,有的平台会同时注册多个声卡,分别对应 audio DSP 前端和 SLIMBUS 后端,不要只看声卡 0。
3. 音频通路梳理:用 mixer_paths.xml 找到你的路由节点
3.1 mixer_paths 文件结构与修改套路
高通平台的音频路由由两个文件主导:mixer_paths.xml 和 audio_policy_configuration.xml。前者定义某个场景(如扬声器、耳机、听筒)下需要打开的 ALSA 控件及顺序,后者定义策略,也就是什么音频流优先级最高、什么设备映射到哪个输出。改路由只需要修改前者,而策略文件一般只在增加新输出设备时才动。
mixer_paths.xml 中最核心的标签是 path 和 ctl。一个典型扬声器通路配置如下:
<path name="speaker"> <ctl name="SLIM RX0 MUX" value="AIF1_PB" /> <ctl name="RX0 MIX INP0" value="RX0" /> <ctl name="SLIM RX0 DL_HL" value="1" /> <ctl name="RX0 Digital Volume" value="84" /> <ctl name="SPK PA" value="On" /> </path>这段配置的逻辑是:先从 SLIM 总线接收侧选择数据源 AIF1_PB,再把 RX0 混音器输入设为 RX0 主线,接着打开 RX0 下行链路,设置数字音量,最后才打开 PA 功放。注意这里的 SPK PA 是 codec 内部功放使能控制,不是外部 PA,如果你用的平台外部 PA 由 GPIO 控制,需要额外在 device tree 里配置 gpio 并在这个节点里添加对应的 gpio 控件。
修改路径文件后必须重新编译 vendor 分区并刷入,只 push 修改后的 xml 到 /vendor/etc 目录通常也能生效,但注意权限要设为 644。高通在启动时会解析这个 xml 生成控件映射表,语法错误不会报错,而是直接跳过该节点,所以出现改了没效果的情况第一反应要查 logcat 里是否有 "parse error" 关键字。
3.2 用 tinymix 手工拉通一条通路
在改 xml 之前,建议先用 tinymix 手工走通通路,确认每个控件名在目标平台上真实存在。因为不同平台控件命名差异很大,直接照抄其他平台的 xml 基本会翻车。下面是一组手工排查步骤对应的 shell 脚本:
# 列出当前音频卡及所有控件 tinymix -D 0 # 查看某个控件当前值与可选值 tinymix -D 0 "SLIM RX0 MUX" # 设置输入源 tinymix -D 0 "SLIM RX0 MUX" "AIF1_PB" # 设置音量(十六进制 50 对应约 -6dB) tinymix -D 0 "RX0 Digital Volume" 80 # 打开 PA tinymix -D 0 "SPK PA" "On" # 播放测试音 tinyplay /data/local/tmp/playback.wav这里 -D 参数指定声卡序号,如果系统只有一个声卡可以省略。每次设置完一个控件建议重新读取一次值,确认写入生效。有些控件带音量映射,比如写入 80 但实际寄存器会做范围和步进映射,从返回值中可以看到实际设置值。如果控件写入后读取值没变化,说明该控件处于非活跃状态,或者 codec 在低功耗模式下屏蔽了写入。
播放 wav 文件时建议使用采样率 48kHz、16bit、双声道的标准文件,因为高通平台内部重采样默认关闭,大部分通路直接透传,如果你手头只有 44.1kHz 的文件,打到某些不支持重采样的后端上会出现明显的变调或杂音。后续要验证重采样是否开启,可以对比播放不同采样率文件时的 dmesg 有无 "Invalid rate" 提示。
4. 高通平台调音驱动:从 codec 到 DSP 的参数传递链
4.1 理解 codec 驱动里的 kcontrol 注册机制
高通平台的 codec 驱动通常位于 kernel 的 sound/soc/codecs 目录下,文件命名类似 wcd93xx、wsa88xx。这些驱动内部大量使用下表所示的宏来注册控件,看懂这张表你就理解了为什么 tinymix 里能搜到那些奇怪的控件名。
| 宏名 | 作用 | 典型控件示例 |
|---|---|---|
| SOC_SINGLE | 单值控件 | RX0 Digital Volume |
| SOC_DOUBLE | 双值控件(左右声道) | RX0 Volume |
| SOC_ENUM | 枚举选择控件 | SLIM RX0 MUX |
| SOC_SINGLE_EXT | 外部回调单值控件 | SPK PA |
| SOC_DAPM_ENUM | DAPM 枚举控件 | RX INT0_8 MUX |
DAPM 是高通 codec 驱动里最常见也最容易出问题的机制。DAPM 会自动管理电源域,比如检测到音频流开始播放时自动给对应 DAC 上电。如果你在 mixer_paths.xml 里手动打开了某个通路,但播放结束 DAPM 检测到 idle 会直接断电,下次播放时重新上电瞬间会有 pop 音。这是高通平台的经典痛点,解决方式通常是在 codec 驱动初始化里配置 pop 消除序列,而不是在 xml 侧反复开关时钟。
4.2 采样率、位深与通道数的传递路径
应用层打开 pcm 设备时,需要同时在驱动侧匹配采样参数。高通平台的音频 DSP 接收端路由在运行时通过音频框架下发,但底层 pcm 设备节点的 open 参数由 snd_pcm_hw_params 决定。这里最常见的坑是:上层播放器指定了 S24_LE 格式,而你的 mixer 通路配置里写的是 S16_LE 转换路径,导致底层上报 unsupported format。
一个完整的音频流参数传递链可以写成三步验证脚本:
# 查看 pcm 设备支持的参数范围 tinycap /dev/null -D 0 -d 0 -c 2 -r 48000 -b 16 # 用 pcmplay 指定格式播放 tinyplay /data/local/tmp/test48k16bitstereo.wav -D 0 -d 0 # 实时查看时钟与 bit 流状态 cat /proc/asound/pcm0p/sub0/hw_paramshw_params 节点会显示当前 stream 的 rate、format、channels 等实际协商结果,用来核对应用层请求与驱动最终采用的参数是否一致。如果出现 rate 不匹配但没明显报错,先看处理链里 sample rate converter 有没有注册成功,高通一般通过 conf 文件控制 SRC 的使能状态,不带 SRC 的回放路径对源文件格式要求非常苛刻。
4.3 时钟树配置:MCLK 的来源与分频关系
高通 audio 系统的时钟树复杂在它不仅由 codec 决定,还由主 CPU 侧的 LPA (Low Power Audio) 时钟控制。调试中我会在 dmesg 里搜 "clk" 关键字确认 MCLK 是否稳定输出。典型配置是 MCLK 由 PMIC 的 GPIO 输出 9.6MHz 或 12.288MHz,codec 内部 PLL 再根据采样率做分频倍频。如果你播放 48kHz 文件正常但播放 96kHz 出来声音尖锐,基本可以断定 PLL 分频表缺失,需要在 codec 驱动里补齐 96kHz 对应的倍频系数。
5. 高通 Audio 调试避坑笔记:五条实战踩坑记录
5.1 扬声器无声但耳机有声
现象:插耳机声音完全正常,拔掉耳机后扬声器一点声音也出不来。dmesg 无错误,tinymix 检查扬声器通路各控件均已打开。
原因:DAPM 检测到耳机插入事件后,把音频路由切换到了耳机通路,拔出耳机时没有触发回切扬声器的回调。这种问题多数出在 Headset Detection 的中断处理函数里,耳机插入与拔出状态上报的是同一 GPIO 中断,但没有做防抖和状态校验,导致拔出的状态没被正确识别。
解决:在 codec 驱动的 jack detection 回调里打印当前声卡的路由状态,确认拔出事件到达后是否执行了 route 更新。就实际场景而言,大多不是路由没更新,而是 mixer 通路更新与播放线程产生竞态,拔出瞬间播放线程仍在写数据,混音器切到扬声器后参数不匹配。强制做法是拔插后先 stop 再重开播放线程,或者增加 20ms 延时再恢复通路。
5.2 播放正常但录音全部是静音
现象:扬声器回放正常,但用 tinycap 录音后播放 wav 全是静音。tinymix 里看 ADC 通路控件均已设置。
原因:高通平台录音通路需要同时开启 mic bias 和对应的模拟输入增益。很多时候只配了 RX(回放)通路,忘了配 TX(录音)通路里的 mic bias 控件。还有一个高频翻车点是:多颗麦克风,但设备树里 dmic 引脚映射错误,实际采到的咪头没接对。
解决:优先检查 tinymix 里 "TX0 DMIC MUX" 这类控件的值是否指向空闲的 DMIC 输入,再抓取实时音频数据用音频分析工具看波形底噪。底噪特别干净但无有效信号,说明编解码通道没问题,瓶颈在 mic 供电或偏置电压上。我们会用示波器量 DC 电平确认 mic bias 是否在 1.8V 左右。
5.3 修改 mixer_paths.xml 后没任何效果
现象:在 xml 添加了播放通路节点,重新编译 vendor 刷机后,tinymix 里找不到新添加的控件,播放声音也没变化。
原因:xml 文件语法有一个标签嵌套错误,高通解析器静默失败,整个文件部分节点没被加载。另一个原因是控件名与实际驱动注册名不一致,多了一个空格或者下划线都会导致匹配不到。
解决:先看解析日志。在 init 阶段用 grep 抓 audio 服务日志里的关键字:
adb logcat -s audiopolicy audio_hw_primary | grep -i "parse\|mixer_path"如果看到 "failed to find ctl" 就说明控件名拼写有问题。我会把 tinymix 输出结果重定向到文件,再用 python 脚本做一次控件名交叉核对,比肉眼检查靠谱得多。
5.4 播放 pop 音严重
现象:从暂停恢复播放或切换音源时耳机有明显爆音。
原因:DAC 在上电和掉电瞬间输出未稳定电位的模拟信号,pop 本质上是输出信号瞬态突变。
解决:在播放路径的末尾添加延时节点,即在 DAC 上电与 PA 使能之间加 200ms 左右延时。高通 codec 驱动里资源较多,可以用驱动现有的后置电源序列完成。实践中把 PA 使能延后到 DAC 输出稳定后再开,通常就能压掉。
5.5 不同采样率切换导致播放失败
现象:同一个播放器播放 44.1kHz 文件正常,切到 48kHz 文件后持续杂音,且 logcat 里出现 "unsupported sample rate"。
原因:ALSA 后端配置里没有启用 SRC,且物理音频总线时钟来自 codec 的固定 MCLK,无法实时跟随采样率变化。
解决:在 device tree 上确认硬件是否支持多时钟域独立配置,如果支持,可以给每个采样率段配置独立时钟源。如果硬件固定,只能让音频服务强制重采样后再送到底层。我通常在 hal 层音频配置里加一行 sample_rate 的固定重采样提示,保证上来走同一条通路。
6. 进阶验证:把音频通路 dump 出来对比基线的三个技巧
解决完基础通路后,我希望你养成的习惯是给每一块板子做一套音频通路基线快照。具体做法是:在全新烧录、未做任何修改的状态下,依次执行播放、录音、通话场景,每执行一个场景就把完整 mixer 状态存成一个文件。之后无论谁改了 xml 还是 codec 驱动,通过对比差分就能快速看出哪些控件被改变了。用 shell 一行实现:
for i in 0 1 2 3 4 5 6 7 8 9; do tinymix -D 0 > /data/local/tmp/mixer_state_$i.txt; sleep 1; done每一秒采一次,连续采 10 次,覆盖播放过程不同阶段的控件状态变化。如果某些控件从始至终没变化,说明它们不在该通路控制范围内。再配合 /proc/asound 的 pcm 设备节点状态信息,能定位到具体是哪个后端没有 work。
第二个技巧是抓取 pcm 设备节点的实时状态。播放时读取 /proc/asound/pcm0p/sub0/status 可以看到当前运行状态是否为 RUNNING,如果一直是 PREPARED 说明数据流没真正启动。此时观察音频服务进程的 CPU 占用,占用高但状态不切换大概率是上层 session 建立失败。
第三个技巧是验证回放链路的相位一致性。播放一个左声道 1kHz、右声道静音的正弦波文件,用录音设备抓回信号,用 python 读 wav 检查左右声道能量差,这能间接判定通路里是否存在通道交换或增益失衡。
import wave import numpy as np w = wave.open('/data/local/tmp/capture.wav', 'rb') data = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).reshape(-1, w.getnchannels()) left_rms = np.sqrt((data[:, 0] ** 2).mean()) right_rms = np.sqrt((data[:, 1] ** 2).mean()) print(f"L RMS: {left_rms}, R RMS: {right_rms}")如果左声道理论上有信号而测出来右侧能量更大,先查控件里是否有声道反相映射。当年某项目就是被这种左右反相困了两天,最后发现 mixer 通路里 LEFT/RIGHT 映射反了。高通平台没有全局的调试利器,全靠把控制链路、数据链路、时钟链路的日志与状态点横纵对比。从那以后我每次接新板子都强制走一遍基线快照流程,再开始任何功能开发,这个习惯救了我无数次,希望帮到你。
本文还有配套的精品资源,点击获取