十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3506 DSM音频子卡调试全攻略:从设备树到SAI3实战

RK3506 DSM音频子卡调试全攻略:从设备树到SAI3实战 瑞芯微RK3506这颗芯片最近在音频类项目里出现得越来越频繁尤其是做多路音频采集、DSMDigital Signal Processing音频子卡扩展这类场景。但凡是真正上手过RK3506音频子系统的人大概率都经历过同一个过程硬件焊好了设备树也改了内核也编了结果声卡就是不出来或者出来了但录音全是噪声。这篇内容就是把我自己在RK3506上折腾DSM音频子卡的完整过程拆开讲从硬件改接到设备树配置再到内核选项和SAI3控制器的调试把中间踩过的坑和验证方法都摊开说清楚。适合正在用RK3506做音频开发、或者准备上DSM子卡但还没跑通的工程师参考也适合刚接触瑞芯微音频子系统、想搞清楚SAI控制器和CODEC之间到底怎么串起来的人。1. 先搞清楚RK3506的音频链路到底长什么样1.1 从SoC到DSM子卡的信号走向RK3506的音频输出不是直接怼到CODEC上的中间要经过SAISerial Audio Interface控制器。这颗芯片内部有多个SAI控制器SAI3是DSM子卡场景里最常被分配到的那个。整条链路大致是SoC内部的I2S/TDM数据流从SAI3出来经过引脚复用IOMUX映射到物理引脚再通过板级走线到DSM子卡上的CODEC或DSP芯片最后CODEC把数字信号转成模拟信号输出或者反过来把模拟输入转成数字信号回传给SoC。这个链路里任何一个环节出问题声卡都不会正常工作。我见过太多人一上来就改设备树结果发现引脚复用根本没配对SAI3的引脚还挂在GPIO模式上那设备树写得再漂亮也没用。所以第一步永远是确认硬件链路SAI3的四个关键信号——BCLK位时钟、LRCLK帧时钟、SDI数据输入、SDO数据输出——到底接到了DSM子卡的哪些引脚上MCLK主时钟有没有接CODEC的I2C控制总线挂在哪一路。1.2 DSM子卡和普通CODEC子卡的区别DSM子卡和普通的音频CODEC子卡有个本质区别DSM通常带自己的DSP或者至少是更复杂的音频处理单元它可能要求SoC以TDM模式发送多路音频数据而不是标准的立体声I2S。这意味着SAI3的配置不能照搬那些单路CODEC的模板得根据DSM子卡的数据手册来定帧格式、时隙数和采样率。我手上这块DSM子卡用的是TDM8模式8个时隙每个时隙32位采样率48kHz。算一下BCLK的频率8时隙 × 32位 × 48kHz 12.288MHz。这个频率必须和DSM子卡要求的MCLK或者BCLK严格匹配差一点都会导致数据错位。很多人在这一步翻车就是因为直接抄了一个I2S的配置BCLK频率对不上录出来的数据全是乱的。1.3 为什么SAI3在RK3506上容易出问题SAI3在RK3506的引脚复用表里不是默认分配给音频功能的需要手动在IOMUX里切换。而且SAI3的寄存器基地址和SAI0/SAI1不一样内核里如果用的是老版本的驱动可能压根没把SAI3的节点使能。另外RK3506的时钟树里SAI3的时钟源选择也比较绕得确认它挂的是哪个PLL分频系数是多少。我实际调试的时候发现RK3506的TRM里关于SAI3的时钟描述和实际寄存器行为有细微出入导致按文档算出来的分频值跑起来时钟不对。后来是用示波器直接量BCLK引脚才定位到的。所以这一块不能全信文档得实测。2. 硬件改接那些文档不会告诉你的细节2.1 引脚复用配置的实操步骤RK3506的引脚复用是通过IOMUX控制器配置的但不是在设备树里写个pinctrl就完事。有些引脚在上电默认状态下是被其他功能占用的比如调试串口或者JTAG。如果你要用的SAI3引脚和调试口冲突得先在U-Boot阶段就把复用改过来否则内核启动到一半串口就没了你连日志都看不到。我的做法是在U-Boot的board_init里加一段IOMUX配置把SAI3的四个引脚先切到音频功能同时保留调试串口的引脚不动。具体寄存器地址得查RK3506的IOMUX基地址加上偏移。这里有个坑RK3506的IOMUX寄存器是32位宽的但每个引脚占4个bit写的时候要读-改-写不能直接覆盖整个寄存器否则会把同一寄存器里其他引脚的配置冲掉。// U-Boot阶段配置SAI3引脚复用示例 #define IOMUX_BASE 0xFF000000 #define SAI3_PIN_REG (IOMUX_BASE 0x0044) void sai3_pinmux_init(void) { uint32_t val readl(SAI3_PIN_REG); // 清除SAI3相关引脚的bit位 val ~(0xF 8 | 0xF 12 | 0xF 16 | 0xF 20); // 设置为SAI3功能具体功能号查TRM val | (0x2 8 | 0x2 12 | 0x2 16 | 0x2 20); writel(val, SAI3_PIN_REG); }这段代码里的功能号0x2只是示例实际值必须查RK3506的TRM引脚复用表。写错功能号的话引脚会切到一个完全无关的功能上表现就是声卡没反应但也不报错特别难查。2.2 MCLK和BCLK的走线注意事项DSM子卡通常需要一个MCLK主时钟频率一般是采样率的256倍或512倍。48kHz采样率下256倍就是12.288MHz512倍是24.576MHz。RK3506的SAI3可以输出MCLK但前提是时钟树配置正确而且引脚复用里MCLK功能得使能。我在PCB改接的时候犯过一个错MCLK的走线太长而且和BCLK平行走了很长一段结果串扰严重DSM子卡锁不住时钟。后来把MCLK改走内层并且和BCLK保持至少3倍线宽的间距问题才解决。如果你是在现有板子上飞线尽量用屏蔽线而且MCLK的线越短越好。另外BCLK和LRCLK的相位关系也要注意。有些DSM子卡要求LRCLK在BCLK的下降沿变化有些要求上升沿。这个在SAI3的寄存器里可以配但硬件上如果走线延迟差太多配置也救不回来。我一般会在PCB上让BCLK和LRCLK等长走线误差控制在5mil以内。2.3 电源和地的处理DSM子卡上的CODEC对电源噪声很敏感尤其是模拟部分的AVDD。RK3506开发板上的3.3V数字电源直接给CODEC的AVDD供电的话底噪会很明显。我的做法是给DSM子卡单独一路LDO或者至少加一个π型滤波磁珠电容。地方面数字地和模拟地要在CODEC下面单点连接不能大面积铺在一起。实测下来电源处理前后录音的底噪能差10dB以上。这个不是玄学是实实在在的能量差异。如果你发现录音有“嘶嘶”声先别急着改软件拿示波器量一下AVDD上的纹波大概率能发现问题。3. 设备树配置从零写一个能用的SAI3节点3.1 SAI3节点的完整写法RK3506的设备树里SAI3节点默认可能是disabled状态而且里面的属性不一定对。我一般是从头写一个而不是改现有的。下面是一个能跑通的SAI3节点示例针对TDM8模式、48kHz、32位采样sai3 { status okay; #sound-dai-cells 0; compatible rockchip,rk3506-sai; reg 0x0 0xff5a0000 0x0 0x1000; interrupts GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH; clocks cru MCLK_SAI3, cru HCLK_SAI3; clock-names mclk, hclk; dmas dmac1 12, dmac1 13; dma-names tx, rx; pinctrl-names default; pinctrl-0 sai3m0_pins; rockchip,tdm-tx-slots 8; rockchip,tdm-rx-slots 8; rockchip,tdm-slot-width 32; rockchip,bclk-fs 256; rockchip,playback-only; // 或者 rockchip,capture-only; };这里有几个关键点rockchip,bclk-fs这个属性决定BCLK和采样率的倍数关系。TDM8模式下BCLK 采样率 × 时隙数 × 时隙宽度 48000 × 8 × 32 12.288MHz而bclk-fs BCLK / 采样率 256。这个值必须和DSM子卡的要求一致写错了要么没声音要么全是噪声。rockchip,tdm-slot-width也要注意有些DSM子卡要求16位时隙有些要求32位。这个和CODEC的配置必须匹配否则数据对齐会错位。3.2 pinctrl配置的坑pinctrl-0引用的sai3m0_pins是在pinctrl节点里定义的。RK3506的pinctrl定义在rk3506-pinctrl.dtsi里但默认可能没有SAI3的完整定义。我遇到过的情况是dtsi里只有SAI3的MCLK和BCLK引脚定义没有SDI和SDO。这时候得自己补sai3m0_pins: sai3m0-pins { rockchip,pins 3 RK_PA0 2 pcfg_pull_none, // SAI3_SCLK 3 RK_PA1 2 pcfg_pull_none, // SAI3_LRCK 3 RK_PA2 2 pcfg_pull_none, // SAI3_SDI 3 RK_PA3 2 pcfg_pull_none, // SAI3_SDO 3 RK_PA4 2 pcfg_pull_none; // SAI3_MCLK };这里的2是功能号必须和TRM里SAI3的功能号一致。pcfg_pull_none表示不上下拉音频信号一般不需要上下拉加了反而可能影响信号质量。但MCLK如果走线长可以考虑加下拉防止悬空时引入噪声。3.3 simple-audio-card的绑定SAI3节点写好后还需要一个machine驱动把SAI3和CODEC绑在一起。RK3506上一般用simple-audio-carddsm_sound: dsm-sound { compatible simple-audio-card; simple-audio-card,name rockchip,dsm-sound; simple-audio-card,format dsp_a; simple-audio-card,bitclock-master master; simple-audio-card,frame-master master; simple-audio-card,mclk-fs 256; simple-audio-card,cpu { sound-dai sai3; }; simple-audio-card,codec { sound-dai dsm_codec; system-clock-frequency 12288000; }; };simple-audio-card,format这里写的是dsp_a对应TDM模式的一种帧格式。如果你的DSM子卡用的是标准I2S就改成i2s。这个格式必须和CODEC驱动里的配置一致否则数据会错位。bitclock-master和frame-master指定谁做主时钟。一般让SoC做master也就是SAI3输出BCLK和LRCLK。如果DSM子卡要求自己做master那就得改成dsm_codec同时SAI3要配成slave模式。4. 内核配置那些默认不开但必须开的选项4.1 SAI驱动和DMA的依赖关系RK3506的内核里SAI驱动依赖DMA引擎。如果DMA驱动没使能SAI会probe失败但报错信息很隐晦可能只提示“dma request failed”。所以内核配置里必须确保CONFIG_SND_SOC_ROCKCHIP_SAIy CONFIG_SND_SOC_ROCKCHIP_I2S_TDMy CONFIG_DMADEVICESy CONFIG_RK_DMACy CONFIG_SND_SOC_SIMPLE_CARDy CONFIG_SND_SIMPLE_CARD_UTILSy其中CONFIG_SND_SOC_ROCKCHIP_I2S_TDM是TDM模式的驱动如果只开ROCKCHIP_SAI可能只支持标准I2S。这个在menuconfig里藏得比较深在Device Drivers - Sound card support - Advanced Linux Sound Architecture - ALSA for SoC audio support - Rockchip I2S/TDM Device Driver下面。4.2 CODEC驱动的选择DSM子卡上的CODEC如果是通用型号比如TI的PCM系列或者ADI的ADAU系列内核里可能有现成驱动。但如果是定制型号就得自己写一个简单的CODEC驱动。我一般先用simple-audio-card的codec节点配一个dummy驱动确认SAI3本身能出时钟和数据再换真正的CODEC驱动。dummy CODEC驱动的写法很简单注册一个snd_soc_component_driverdai_ops里只实现hw_params和set_fmt其他返回0就行。这样能快速验证SAI3的时钟和数据通路是否正常。4.3 时钟配置的验证方法内核起来后第一件事是检查时钟。用cat /sys/kernel/debug/clk/clk_summary | grep sai3看SAI3的时钟频率。如果MCLK不是12.288MHz或者24.576MHz说明时钟树配置有问题。RK3506的SAI3时钟源一般是GPLL或者CPLL分频系数在clk-rk3506.c里定义。如果默认分频出不来想要的频率可能需要在设备树里指定assigned-clocks和assigned-clock-rates。我遇到过一种情况时钟频率是对的但clk_summary里显示的状态是disabled。这是因为SAI3的时钟在probe时没有使能得在驱动里加clk_prepare_enable。如果用的是内核自带的SAI驱动一般不会有这个问题但如果是自己改的驱动容易漏掉。5. 调试实录声卡不出来怎么一步步排查5.1 从dmesg里找线索声卡注册失败时dmesg | grep -i sai和dmesg | grep -i sound是最先要看的。常见的错误有错误信息可能原因排查方向sai3: probe failed时钟或DMA未就绪检查clk_summary和dma节点no sound card foundsimple-audio-card未匹配检查compatible和dai-linkdai format not supported格式配置不匹配检查format和CODEC驱动bclk not accurate时钟分频错误量BCLK引脚频率我实际遇到最多的是no sound card found原因通常是simple-audio-card的cpu或codec节点引用错了或者CODEC驱动没注册成功。用cat /proc/asound/cards看有没有声卡没有的话就顺着dmesg往下查。5.2 用示波器确认时钟和数据软件查完没头绪的时候示波器是最直接的。先量MCLK应该是12.288MHz或24.576MHz的方波。再量BCLKTDM8模式下应该是12.288MHz。然后量LRCLK应该是48kHz。如果LRCLK频率不对说明帧配置错了如果BCLK频率不对说明bclk-fs或者时隙配置错了。数据线SDI/SDO用示波器看的话能看到数据在BCLK边沿变化。如果数据线一直是高电平或者低电平说明SAI3没在发送数据可能是DMA没启动或者playback/capture方向配反了。5.3 录音噪声的几种典型原因声卡出来了但录音全是噪声这个问题比声卡不出来更烦人。我总结了几种典型情况第一种是时钟抖动大。用示波器看MCLK的抖动如果峰峰值超过100psCODEC可能锁不住。解决办法是检查时钟源尽量用低抖动的PLL或者在PCB上给MCLK加一个时钟缓冲器。第二种是数据格式不匹配。比如SAI3配的是TDM8但CODEC配的是I2S数据错位后就是噪声。这种噪声通常有规律听起来像周期性的“咔咔”声。解决办法是统一格式SAI3和CODEC的驱动里format必须一致。第三种是电源噪声。前面说过AVDD纹波大的话底噪会很明显。这种噪声是持续的“嘶嘶”声和音频内容无关。解决办法是加强电源滤波。第四种是地环路。如果DSM子卡和主板之间的地线走线不合理会形成地环路引入工频干扰。这种噪声是50Hz或100Hz的“嗡嗡”声。解决办法是单点接地或者用隔离器。5.4 一个真实的排查案例有一次我调一块DSM子卡声卡能出来但录音只有右声道有数据左声道全是0。查了半天设备树发现simple-audio-card的dai-link里只配了一个cpu和一个codec但DSM子卡是双声道的需要两个dai-link或者一个dai-link里配多通道。后来改成TDM模式8个时隙里前两个时隙分别对应左右声道问题才解决。这个案例说明DSM子卡的通道映射不一定和标准I2S一样。有些DSM子卡会把多路音频映射到TDM的不同时隙上设备树里得用rockchip,tdm-tx-slots和rockchip,tdm-rx-slots来指定时隙数CODEC驱动里也要相应地解析时隙数据。6. 几个能省你几天时间的实操心得6.1 先验证SAI3本身再接CODEC我的习惯是先用dummy CODEC把SAI3跑通确认时钟、DMA、数据通路都没问题再接真正的CODEC。这样能把问题范围缩小到SAI3或者CODEC而不是一锅粥。dummy CODEC的驱动代码不超过50行但能省下大量排查时间。具体做法是写一个最简单的platform driver注册一个CODECdai_ops里只返回0。然后在设备树里把simple-audio-card的codec指向这个dummy。如果这样能出声卡说明SAI3侧没问题问题在CODEC侧。6.2 设备树改动后一定要重新编译dtb很多人改了设备树但忘了重新编译dtb或者编译了但没更新到启动分区。RK3506的启动流程里dtb是单独分区的得用make dtbs编译然后烧录到对应分区。我一般会在U-Boot里用fdt print确认当前用的dtb是不是最新的。另外RK3506的dtb文件名可能和dts里的compatible不匹配导致加载了错误的dtb。这个在/proc/device-tree/compatible里能看到实际加载的compatible字符串和dts里对一下就知道有没有加载错。6.3 内核日志等级调到最高调试音频的时候把内核日志等级调到8能看到SAI驱动和CODEC驱动里所有的dev_dbg输出。这些输出里往往藏着关键信息比如时钟频率计算过程、DMA描述符地址、格式协商结果。默认的日志等级是4很多有用的信息都被过滤掉了。调等级的方法是在U-Boot的bootargs里加loglevel8或者运行时用echo 8 /proc/sys/kernel/printk。不过要注意日志太多可能会刷屏建议配合dmesg -w实时看或者重定向到文件再grep。6.4 用arecord和aplay做快速验证声卡出来之后用arecord -D hw:0,0 -f S32_LE -r 48000 -c 2 -d 5 test.wav录5秒然后用aplay放出来。如果录音有噪声先用arecord录纯静音看噪声水平。如果静音录音的底噪就很大说明是硬件或时钟问题如果静音录音干净但放音乐有噪声说明是数据格式或通道映射问题。arecord的-f参数指定采样格式TDM模式下一般用S32_LE。如果CODEC是16位的用S16_LE。格式不对的话arecord会报错或者录出来全是噪声。6.5 关于SAI3的时钟分频计算RK3506的SAI3时钟分频寄存器里分频系数是整数但实际需要的频率不一定能整除。比如GPLL是1188MHz要得到12.288MHz分频系数是1188/12.28896.68不是整数。这时候得换时钟源或者用小数分频如果支持。我一般会选一个能整除的PLL频率比如CPLL输出到491.52MHz分频40次就是12.288MHz。这个计算过程在clk-rk3506.c里能看到但文档里不一定写清楚。如果发现时钟频率总是差一点先算一下分频系数是不是整数不是的话就换时钟源。7. 从DSM子卡延伸到多路音频采集的扩展思路DSM子卡跑通之后如果要做多路音频采集比如8路或16路思路其实是一样的只是TDM的时隙数要增加。RK3506的SAI3最多支持16个时隙每个时隙32位的话BCLK频率会到24.576MHz16×32×48k。这个频率对PCB走线和CODEC的要求都更高MCLK最好用24.576MHz和BCLK保持整数倍关系。多路采集的时候DMA的配置也要注意。RK3506的DMA控制器每个通道有最大传输长度限制16路音频的数据量很大可能需要多个DMA描述符循环。内核里的SAI驱动一般会处理这个但如果自己改驱动得确认DMA描述符链的长度够用。另外多路音频的同步是个问题。TDM模式下所有时隙共享同一个LRCLK所以天然是同步的。但如果用多个SAI控制器分别接不同的CODEC就得考虑时钟同步一般让一个SAI做master其他做slave共享MCLK和LRCLK。我在实际项目里还遇到过DSM子卡的固件需要单独配置的情况。有些DSM子卡上的DSP需要先通过I2C加载固件才能工作这个在Linux驱动里一般用request_firmware实现。固件文件放在/lib/firmware下面驱动probe的时候加载。如果固件加载失败CODEC不会报错但就是不出声。这个坑我踩过一次查了两天才发现是固件文件没打包进根文件系统。最后说一个关于内核版本的选择。RK3506的SAI驱动在Linux 5.10和6.1上的行为有差异6.1的驱动对TDM模式的支持更好但有些老版本的CODEC驱动在6.1上编译不过。我一般会选一个和SDK匹配的内核版本不要盲目追新。如果SDK里带的是5.10就先用5.10跑通再考虑升级。升级的时候重点看SAI和DMA驱动的变更这两个地方最容易出兼容性问题。
返回列表