跳到主要内容

Audio

1. 概述

1.1 Rockchip 音频特性

本小节描述 RK 平台的音频特性,如表 1-1 所示: alt text

表 1-1 Rockchip Audio 特性

1.2 DAI

本章节主要描述 RK 平台数字音频接口支持情况。

1.2.1 I2S

RK 平台有两种 I2S 控制器:I2S 和 I2S-TDM。I2S 控制器支持 I2S, PCM 协议;I2S-TDM 控制器支持 I2S, PCM, TDM 协议。

1.2.1.1 I2S 控制器 Features
  • 支持 8 声道 I2S 协议:standard, left justified, right justified
  • 支持 stereo PCM 协议:early, late 1, late 2, late 3
  • 支持 master/slave 模式,模式同时应用于 TX/RX 逻辑
  • 支持 8k ~ 192k 采样率,384k 采样率
  • 支持 16 ~ 32 bits 位宽
  • 支持 MSB/LSB 模式
  • 支持 时钟相位调整
  • 支持 时钟补偿

Note:
384k 采样率需要高精度 clk,可从 MCLK PIN 导入外部高精度 clk 或者采用 slave 模式。
不支持 mono PCM,如有需求,可采用 stereo PCM 传输 mono PCM,有效数据放在 slot 0。
时钟补偿应用于异源系统的时钟补偿,比如 UAC,但有使用条件限制,参考音频时钟补偿章节。

1.2.1.2 I2S-TDM 控制器 Features

除了支持 I2S 控制器的功能之外,I2S-TDM 控制器新增支持如下功能:

  • 支持 8 slots TDM PCM 协议:normal, left shift mode[0~3]
  • 支持 8 slots TDM I2S 协议:normal, left justified, right justified
  • 支持 slot 位宽可配:16 ~ 32 bits
  • 支持 data line 全映射,可重映射 data line 与 path 的关系
  • 支持 TX/RX 异步模式,独立的时钟,可支持独立的采样率,位宽,协议
  • 支持 TX/RX 同步模式,共享时钟
  • 支持 PCM FSYNC 宽度可配:[1~7] sclk cycle 或 one channel block
  • 支持 I2S FSYNC 宽度可配:half frame / whole frame

Note:
slot 仅支持偶数配置。
TDM I2S 长帧模式下,可支持 16 声道传输。 TDM 协议下,仅使用 path-0,所以物理连接的 PIN 与声道不对应时,请通过 data line 重映射到 path-0。

1.2.1.3 RK 系列芯片 I2S 支持列表
ChipI2SI2S-TDM
PX30/RK3326I2S1_2CH, I2S2_2CHI2S0_8CH
RK1808I2S1I2S0
RK3036I2S0
RK3066I2S0, I2S1, I2S2
RK312XI2S_8CH, I2S_2CH
RK3188I2S0
RK3288I2S0
RK322XI2S0, I2S1, I2S2
RK3308I2S_2CH_0, I2S_2CH_1 I2S_8CH_0, I2S_8CH_1, I2S_8CH_2, I2S_8CH_3
RK3328I2S0, I2S1, I2S2
RK3368I2S_8CH, I2S_2CH
RK3399I2S0, I2S1, I2S2
RK3568/RK3566I2S0_8CH, I2S1_8CH, I2S2_2CH, I2S3_2CH
RV1108I2S0, I2S1, I2S2
RV1126/RV1109I2S1_2CH, I2S2_2CHI2S0_8CH

1.2.2 PDM

1.2.2.1 PDM 控制器 Features

RK 平台支持 PDM 协议的数字麦或者 ADC,具体 Features 如下:

  • 支持 master mode only
  • 支持 最大 8 个 MIC 阵列
  • 支持 16 ~ 24 bits 位宽
  • 支持 8 ~ 48k 采样率
  • 支持 声道交换
  • 支持 高通滤波:3.79 Hz, 60 Hz, 243 Hz, 493 Hz
  • 支持 存储对齐:左对齐, 右对齐
  • 支持 data line 全映射
  • 支持 时钟相位调整
  • 支持 时钟补偿

Note: 存储对齐仅对于位宽大于16 bits 的格式,大于16 bits 的格式,数据按 word 存储,默认低位补0 时钟补偿应用于异源系统的时钟补偿,比如 UAC,但有使用条件限制,参考 音频时钟补偿 章节 data line 全映射功能在 RV1126 及后续推出的芯片才支持: RV1126/RV1109, RK3568/RK3566...

1.2.2.2 RK 系列芯片 PDM 支持列表
ChipNameMax ChannelVersion
PX30/RK3326PDM8V2
RK1808PDM8V2
RK2108PDM4V3
RK3308PDM_8CH8V2
RK3328PDM8V1
RK3568/RK3566PDM8V3
RV1126/RV1109PDM8V3

版本描述

VersionDescription
V11. 支持标准模式,对应时钟:2.048/2.822/3.072 MHz。
V21. 新增小数分频。
2. 新增存储对齐模式。
V3BUG FIX:
1. 修正 HPF 使能情况下,输入信号幅度接近 0 db 时,符号位溢出问题。
2. 修正通带内纹波一致性问题,新版本通带纹波小于 0.1 db。
3. 修正混叠问题。
Features:
1. 新增低功耗模式,对应时钟 1.024/1.411/1.536 MHz。
2. 新增高性能模式,对应时钟 4.096/5.644/6.144 MHz。

1.2.3 DCODEC

RK 平台支持数字 CODEC 接口,可对接支持该协议的模拟 CODEC,比如 RK812,组合成完整 CODEC。另外,控制器工作于 DSM 模式时,可驱动外部 PA,满足普通音质产品需求,节省成本。

  • 支持 3 声道 ADC
  • 支持 stereo DAC
  • 支持 异步模式:播放和录音采样率可不同。
  • 支持 同步模式:播放和录音采样率必须相同。
  • 支持 PGA / ALC
  • 支持 DSM 输出,直驱 PA
  • 支持 高通滤波

1.2.4 VAD

语音活性检测 (Voice Activity Detection),VAD 接收来自 DAI 的数据,处理统计分析,达到预设阈值时,触发中断,唤醒系统。简要数据流图如图 1-1 所示:

图 1-1 RK VAD 数据流

RK VAD 数据流

  1. 系统休眠时,DAI -> VAD -> SRAM 保持工作,VAD 持续的接收数据,滤波,噪声自学习,音频数据存储在 SRAM 循环 Buffer 中。
  2. VAD 检测到声音超过预设阈值时,产生中断唤醒 CPU,CPU 关闭 VAD,VAD 关闭时,数据流自动切换 DMAC 通路,即:DAI -> DMAC -> DDR -> CPU 标准录音路径。
  3. CPU 将 SRAM 中的数据无缝填充到录音 Buffer 中,数据处理对用户态透明。
  4. 唤醒后,ASR 程序对录取的数据做分析处理,关键词检测等。

1.2.5 SPDIF

RK 平台支持 SPDIF Transmitter 接口协议,特别的,RK3308 还支持 SPDIF Receiver。

  • 支持 16 ~ 24 bits 位宽
  • 支持 192k 采样率
  • 支持 stereo uncompressed audio,即 LPCM
  • 支持 5.1 / 7.1 compressed audio,比如:DD, DD+
  • 支持 Optical, Coaxial

Note:
192k 采样率输出的时候,需要配套支持 192k 的 Optical 器件。

1.3 Combo DAI

RK 平台支持任意 DAI 的组合使用,重组 DAI 生成 Combo DAI,如图 1-2 所示:

alt text

2. Audio 驱动开发

本章节描述 RK 平台音频支持情况,驱动开发及调试。

2.1 声卡基本构成

本小节介绍声卡的基本构成,如图 2-1 所示:

alt text 图 2-1 ALSA 声卡构成

DAI: Digital Audio Interface.
MACHINE: Link dai and codec to be a new sound card.
DMAENGINE: Transfer data between memory and dai's fifo.

通常情况下,基于发布的 SDK 添加一个声卡,仅需要移植或者编写 codec 驱动即可,个别情况下需要添加 machine 驱动,详见 Machine 驱动开发章节。

2.2 代码清单

代码清单及描述如下所示:

kernel/sound/soc/rockchip$ tree -I "*.o|*.h"
.
├── rk3288_hdmi_analog.c
├── rk3399_gru_sound.c
├── rockchip_audio_pwm.c /* low quality audio out by driving pa */
├── rockchip_cdndp.c
├── rockchip_da7219.c
├── rockchip_hdmi_analog.c /* hdmi and codec share the same i2s */
├── rockchip_hdmi_dp.c /* hdmi and dp share the same i2s */
├── rockchip_i2s.c /* old i2s which support i2s/pcm */
├── rockchip_i2s_tdm.c /* new i2s-tdm which support i2s/pcm/tdm */
├── rockchip_max98090.c
├── rockchip_multicodecs.c /* support multi-streaming */
├── rockchip_multi_dais.c /* support combo-dais */
├── rockchip_multi_dais_pcm.c /* co-work with combo-dais */
├── rockchip_pcm.c /* audio data flow and control */
├── rockchip_pdm.c /* pdm dai driver */
├── rockchip_rt5645.c
├── rockchip_rt5651.c
├── rockchip_spdif.c /* spdif tx dai driver */
├── rockchip_spdifrx.c /* spdif rx dai driver */
├── rockchip_vad.c /* voice activity detection driver */
├── vad_preprocess_arm64.S /* vad preprocess algorithm for arm64 */
├── vad_preprocess_arm.S /* vad preprocess algorithm for arm32 */
└── vad_preprocess_thumb.S /* vad preprocess algorithm for thumb */
kernel/sound/soc/codecs$ tree -P "rk*.c|h*.c|d*.c"
.
├── dmic.c /* driver for dmic, e.g. i2s dmics, pdm dmics */
├── dummy-codec.c /* driver for dmic, direct, none-codec */
├── hdac_hdmi.c
├── hdmi-codec.c /* hdmi codec driver */
├── rk1000_codec.c
├── rk312x_codec.c /* internal codec */
├── rk3228_codec.c /* internal codec */
├── rk3308_codec.c /* internal codec 8ch adc */
├── rk3328_codec.c /* internal codec */
├── rk817_codec.c /* codec in rk817 pmic ic */
└── rk_codec_digital.c /* link with external analog part, e.g. rk812. */

2.3 DAI 驱动开发

发布的 SDK 里 DAI 的驱动都已经完成,开发者只需要根据应用场景配置属性启用相应功能。

2.3.1 I2S

2.3.1.1 协议格式设置

协议格式设置通过 Machine Driver 解析 DTS,然后调用 set_fmt API 设置控制器的协议格式,Simple Card 请参照 Simple Card 章节。

2.3.1.2 master / slave 设置

master / slave 设置通过 Machine Driver 解析 DTS,然后调用 set_fmt API 设置控制器的协议格式,Simple Card 请参照 Simple Card 章节。

2.3.1.3 时钟相位反相设置

时钟相位反相通过 Machine Driver 解析 DTS,然后调用 set_fmt API 设置控制器的协议格式,Simple Card 请参照 Simple Card 章节。

2.3.1.4 bclk-fs 设置
PropertyValueDescription
rockchip,bclk-fsint默认情况下,bclk 为采样率的64倍

示例:bclk 为采样率的 32 倍

&i2s0 {
rockchip,bclk-fs = <32>;
};
2.3.1.5 高精度时钟设置
PropertyValueDescription
rockchip,mclk-calibrateboolean支持高精度时钟,且支持时钟补偿,具体使用参照 音频时钟补偿
2.3.1.6 其他
PropertyValueDescription
rockchip,no-dmaengineboolean不绑定 dmaengine,具体使用参照 Combo DAI 驱动开发
rockchip,playback-onlyboolean只支持播放功能,仅注册 TX DMA
rockchip,capture-onlyboolean只支持录音功能,仅注册 RX DMA

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip-i2s.txt

2.3.2 I2S-TDM

除了支持 I2S 控制器的功能之外,I2S-TDM 控制器新增支持如下功能:

2.3.2.1 TX/RX 时钟共享设置
PropertyValueDescription
rockchip,clk-trcm0TX/RX 逻辑独立,使用各自的时钟,IO 上有两组时钟
rockchip,clk-trcm1TX/RX 逻辑同步,共享 TX 的时钟,IO上只有 TX 的时钟
rockchip,clk-trcm2TX/TX/RX 逻辑同步,共享 RX 的时钟, IO 上只有 RX 的时钟

示例:设置为共享 TX 时钟模式

&i2s0 {
rockchip,clk-trcm = <1>;
};
2.3.2.2 data line 全映射设置

硬件可接任意 data line,配置对应属性修正声道顺序。

PropertyValueDescription
rockchip,i2s-tx-route<int int int int>默认为正序:SDO0 SDO1 SDO2 SDO3
rockchip,i2s-rx-route<int int int int>默认为正序:SDI0 SDI1 SDI2 SDI3

示例:播放声道顺序为 “SDO3 | SDO2 | SDO1 | SDO0" 排列

&i2s0 {
rockchip,i2s-tx-route = <3 2 1 0>;
};
2.3.2.3 TDM 半帧模式设置
PropertyValueDescription
rockchip,tdm-fsync-half-framebooleanTDM I2S 模式下,帧时钟支持半帧和长帧格式,默认长帧

示例:半帧格式

&i2s0 {
rockchip,tdm-fsync-half-frame;
};
2.3.2.4 TDM slot 参数设置

驱动已经实现 set_tdm_slot 接口,Machine Driver 解析 DTS 属性,然后调用 set_tdm_slot API 设置控制器对应参数。Simple Card 请参照 Simple Card 章节。

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.txt

2.3.3 PDM

2.3.3.1 data line 全映射设置

硬件可接任意 data line,配置对应属性修正声道顺序。

PropertyValueDescription
rockchip,path-map<int int int int>默认为正序:SDI0 SDI1 SDI2 SDI3

示例:录音声道顺序为 “SDI3 | SDI2 | SDI1 | SDI0" 排列

&i2s0 {
rockchip,i2s-tx-route = <3 2 1 0>;
};
2.3.3.2 高精度时钟设置
PropertyValueDescription
rockchip,mclk-calibratboolean支持高精度时钟,且支持时钟补偿,具体使用参照 音频时钟补偿
2.3.3.3 其他
PropertyValueDescription
rockchip,no-dmaengineboolean不绑定 dmaengine,具体使用参照 Combo DAI 驱动开发

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,pdm.txt

2.3.4 DCODEC

2.3.4.1 时钟同步模式设置
PropertyValueDescription
rockchip,clk-sync-modeboolean默认为时钟异步模式,即 ADC/DAC 时钟独立

示例:设置为时钟同步模式,共享 ADC 时钟

&dig_acodec {
rockchip,clk-sync-mode;
};
2.3.4.2 DSM 输出模式设置
PropertyValueDescription
rockchip,pwm-output-modeboolean外接 RC,直驱 PA

示例:设置为 DSM PWM 输出模式,IOMUX 切换为 PWM 差分输出

&dig_acodec {
rockchip,pwm-output-mode;
pinctrl-names = "default";
pinctrl-0 = <&audiopwmoutdiff_pins>;
};

alt text

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,codec-digital.txt

2.3.5 VAD

2.3.5.1 音频源设置
PropertyValueDescription
rockchip,audio-srcphandle设置用于语音检测的音频源

用于语音检测的音频源来自于 DAI,不同的芯片支持的音频源不同,如下表所示:

芯片音频源
RK1808I2S0, I2S1, PDM
RK3308I2S_8CH_0, I2S_8CH_1, I2S_8CH_2, I2S_8CH_3, PDM_8CH
RK3568/RK3566I2S1_8CH, I2S2_2CH, I2S3_2CH

示例:RK3308 上 VAD 使用 PDM_8CH 阵列麦 作为音频源

&vad {
rockchip,audio-src = <&pdm_8ch>;
};

2.3.5.1 VAD 配置

PropertyValueDescription
rockchip,det-channelint设置用于检测的声道,默认声道0

2.3.5.2 检测声道设置

示例:RK3308 上 VAD 使用阵列麦2作为检测声道

&vad {
rockchip,det-channel = <2>;
};

2.3.5.3 数据存储模式设置

PropertyValueDescription
rockchip,mode0VAD 检测到语音信号后才开始缓存音频数据
rockchip,mode1VAD 使能后即开始缓存音频数据
rockchip,mode2VAD 不缓存音频数据

示例:RK3308 上 VAD 使用模式1

&vad {
rockchip,mode = <1>;
};

Note:为了确保后级 ASR 应用能得到完整的音频数据,建议使用模式1。缓存的数据存储于 sram 的循环 buffer 中,VAD 唤醒系统后,将 sram 的缓存音频合并入音频流中,该部分数据处理对应用程序透明。

2.3.5.4 缓存大小设置

PropertyValueDescription
rockchip,buffer-time-msint设置 VAD 缓存大小,大小等于:采样率 * 位宽 * 声道 * 时间

示例:缓存设置为100 ms,16k 16bits 8ch 条件下为 204800 bytes

&vad {
rockchip,buffer-time-ms = <100>;
};

Note:缓存变大,会同等增大后级 ASR 应用的 Latency。且系统 sram 有限,谨慎调整。调整策略为 “保证数据完整性”,即缓存的大小能覆盖 “唤醒-> 应用接管” 时间即可。如果该部分时间过长,请优化该部分时间。

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,vad.txt

2.4 Combo DAI 驱动开发

2.4.1 sub dai 设置

PropertyValueDescription
dais<phandle>...sub dai,用于组合成 combo dai。sub dai 需要使用属性
“rockchip,no-dmaengine” 移除关联的 dmaengine,统一由
Combo DAI 框架注册绑定。

示例:将 i2s_8ch_2 和 pdm_8ch 组合成 Combo DAI

&i2s_8ch_2 {
      status = "okay";
      rockchip,no-dmaengine;
};
&pdm_8ch {
      status = "okay";
      rockchip,no-dmaengine;
};
&multi-dais {
      dais = <&i2s_8ch_2>, <&pdm_8ch>;
};

2.4.2 声道映射设置

PropertyValueDescription
capture,channel-mapping<int ...>每个sub dai 用于组合的录音声道数量
playback,channel-mapping<int ...>每个sub dai 用于组合的播放声道数量

示例:声卡立体声播放,8声道录音(6声道麦 + 2声道回采)

&multi-dais {
dais = <&i2s_8ch_2>, <&pdm_8ch>;
capture,channel-mapping = <2 0>;
playback,channel-mapping = <2 6>;
};

2.4.3 master / slave 设置

PropertyValueDescription
bitclock-master<int ...>设置提供位时钟的设备,1表示提供时钟,即 master
frame-master<int ...>设置提供帧时钟的设备,1表示提供时钟,即 master

示例1:i2s_8ch_2 和 pdm_8ch 组合成 8 声道录音(回采 + 麦克阵列),i2s_8ch_2 可配置为 master / slave 模式,该例中作为 master,pdm 只有 master 模式。

&multi-dais {
      dais = <&i2s_8ch_2>, <&pdm_8ch>;
      capture,channel-mapping = <2 6>;
      playback,channel-mapping = <2 0>;
      bitclock-master = <1 1>;
      frame-master = <1 1>;
};

示例2:两个 i2s 组合成 16 ch,一个为 master,一个为 slave,外部硬件连线需要将提供时钟的 dai 连接到各 slave dai。

&multi-dais {
      dais = <&i2s_8ch_0>, <&i2s_8ch_1>;
      capture,channel-mapping = <8 8>;
      playback,channel-mapping = <8 8>;
      bitclock-master = <1 0>;
      frame-master = <1 0>;
};

示例3:两个 i2s 组合成 16 ch,都为 slave 模式,时钟由 codec 端提供。

&multi-dais {
      dais = <&i2s_8ch_0>, <&i2s_8ch_1>;
      capture,channel-mapping = <8 8>;
      playback,channel-mapping = <8 8>;
      bitclock-master = <0 0>;
      frame-master = <0 0>;
};

2.4.4 时钟相位反相设置

PropertyValueDescription
bbitclock-inversion<int ...>1 表示反相 位时钟,即 BCLK
frame-inversion<int ...>1 反相 帧时钟,即 LRCK

示例:将 sub dai 的时钟都取反

&multi-dais {
      dais = <&i2s_8ch_0>, <&i2s_8ch_1>;
      bitclock-inversion = <1 1>;
      frame-inversion = <1 1>;
};

PropertyValueDescription
bitclock-inversionint1 表示反相位时钟,即 BCLK
frame-inversionint1 反相帧时钟,即 LRCK

2.4.5 实例

示例1:RK3308 PDM 阵列麦 + 立体声回采 + 立体声放音 + VAD 语音检测

multi_dais: multi-dais {
      status = "okay";
      compatible = "rockchip,multi-dais";
      dais = <&i2s_8ch_2>, <&pdm_8ch>;
      capture,channel-mapping = <2 6>;
      playback,channel-mapping = <2 0>;
      bitclock-master = <1 1>;
      frame-master = <1 1>;
      bitclock-inversion = <0 0>;
      frame-inversion = <0 0>;
      rockchip,grf = <&grf>;
};
&i2s_8ch_2 {
      status = "okay";
      rockchip,no-dmaengine;
};
&pdm_8ch {
      status = "okay";
      rockchip,no-dmaengine;
};
vad-sound {
      status = "okay";
      compatible = "rockchip,multicodecs-card";
      rockchip,card-name = "rockchip,rk3308-vad";
      rockchip,cpu = <&multi_dais>;
      rockchip,codec = <&acodec>, <&vad>;
};

示例2:RK3308 SoundBar I2S_8CH_0 + I2S_8CH_1 组合 16 声道输出

&i2s_8ch_0 {
      #sound-dai-cells = <0>;
      rockchip,no-dmaengine;
};
&i2s_8ch_1 {
      #sound-dai-cells = <0>;
      rockchip,no-dmaengine;
      pinctrl-names = "default";
      pinctrl-0 = <&i2s_8ch_1_m0_sdo0
                    &i2s_8ch_1_m0_sdo1_sdi3
                    &i2s_8ch_1_m0_sdo2_sdi2
                    &i2s_8ch_1_m0_sdo3_sdi1
                    &i2s_8ch_1_m0_sdi0>;
};
i2s_16ch_dais: i2s-16ch-dais {
      compatible = "rockchip,rk3308-multi-dais", "rockchip,multi-dais";
      dais = <&i2s_8ch_0>, <&i2s_8ch_1>;
      capture,channel-mapping = <8 8>;
      playback,channel-mapping = <8 8>;
      bitclock-master = <1 0>;
      frame-master = <1 0>;
      rockchip,grf = <&grf>;
};

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,multidais.txt

2.5 CODEC 驱动开发

请参考内核音频官方文档:Linux Sound Subsystem Documentation

2.6 Machine 驱动开发

本章节主要结合实例简述声卡添加步骤。

2.6.1 Simple Card

Simple Card 是 ASoC 通用的 machine driver,可支持大部分标准声卡的添加。

2.6.1.1 协议格式设置

PropertyValueDescription
simple-audio-card,formatstring设置协议格式
FormatDescription
i2si2s 标准格式
right_ji2s 右对齐格式
left_ji2s 左对齐格式
dsp_apcm delay 1 bit 格式
dsp_bpcm no delay 格式
pdmpdm 格式

示例:设置为 i2s 左对齐格式

audiopwmout_diff: audiopwmout-diff {
compatible = "simple-audio-card";
simple-audio-card,format = "left_j";
simple-audio-card,cpu {
sound-dai = <&i2s3_2ch>;
};
master: simple-audio-card,codec {
sound-dai = <&dig_acodec>;
};
};

2.6.1.2 mclk-fs 设置

PropertyValueDescription
simple-audio-card,mclk-fsint默认情况下,mclk 为采样率的256倍

示例:mclk 为采样率的512倍

audiopwmout_diff: audiopwmout-diff {
compatible = "simple-audio-card";
simple-audio-card,mclk-fs = <512>;
simple-audio-card,cpu {
sound-dai = <&i2s3_2ch>;
};
master: simple-audio-card,codec {
sound-dai = <&dig_acodec>;
};
};

2.6.1.3 master / slave 设置

PropertyValueDescription
simple-audio-card,bitclock-master
phandle设置提供位时钟的设备,默认情况下 DAI 提供
时钟
simple-audio-card,frame-masterphandle设置提供帧时钟的设备,默认情况下 DAI 提供
时钟

示例:DAI 工作于 slave 模式,CODEC 工作于 master 模式

audiopwmout_diff: audiopwmout-diff {
compatible = "simple-audio-card";
simple-audio-card,bitclock-master = <&master>;
simple-audio-card,frame-master = <&master>;
simple-audio-card,cpu {
sound-dai = <&i2s3_2ch>;
};
master: simple-audio-card,codec {
sound-dai = <&dig_acodec>;
};
};

2.6.1.4 时钟相位反相设置

PropertyValueDescription
simple-audio-card,bitclock-inversionboolean反相 位时钟,即 BCLK
simple-audio-card,frame-inversionboolean 反相 帧时钟,即 LRCK

示例:bclk,lrck 同时反相

audiopwmout_diff: audiopwmout-diff {
compatible = "simple-audio-card";
simple-audio-card,bitclock-inversion;
simple-audio-card,frame-inversion;
simple-audio-card,cpu {
sound-dai = <&i2s3_2ch>;
};
simple-audio-card,codec {
sound-dai = <&dig_acodec>;
};
};

2.6.1.5 TDM slot 参数设置

PropertyValueDescription
dai-tdm-slot-numint设置 slot 的个数
dai-tdm-slot-widthint设置 slot 的位宽

示例:slot 位宽为 32 bits,8个 slot

i2s-dmic-array {
compatible = "simple-audio-card";
simple-audio-card,mclk-fs = <256>;
simple-audio-card,cpu {
sound-dai = <&i2s_8ch_0>;
dai-tdm-slot-num = <8>;
dai-tdm-slot-width = <32>;
};
simple-audio-card,codec {
sound-dai = <&dummy_codec>;
};
};

Note:mclk-fs 等于 [ slot num ] * [ slot-width ],或为其偶数倍。 更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/simple-card.txt

2.6.1.6 声卡添加实例

  1. 移植或者编写 codec 驱动
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/tas571x.c
sound/soc/codecs/tas571x.h
  1. 使能 CONFIG
CONFIG_SND_SIMPLE_CARD
CONFIG_SND_SOC_ROCKCHIP_I2S
CONFIG_SND_SOC_ROCKCHIP_I2S_TDM
CONFIG_SND_SOC_TAS571X
  1. 添加 DTS 声卡节点 根据硬件连接情况,使能对应的 i2s 节点
&i2s_8ch_1 {
status = "okay";
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2s_8ch_1_m0_sclktx
&i2s_8ch_1_m0_lrcktx
&i2s_8ch_1_m0_sdo0
&i2s_8ch_1_m0_mclk>;
};

根据硬件连接情况,在对应 i2c 或者 spi 下添加 codec 节点

&i2c1 {
      clock-frequency = <400000>;
      status = "okay";
      tas5731: tas5731@1a {
              #sound-dai-cells = <0>;
              compatible = "ti,tas5731";
              reg = <0x1a>;
              clocks = <&cru SCLK_I2S1_8CH_TX_OUT>;
              clock-names = "mclk";
              pinctrl-names = "default";
              pinctrl-0 = <&i2s_8ch_1_m0_mclk>;
              pdn-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
              reset-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_LOW>;
      };
};

添加并使能声卡节点

tas5731_sound: tas5731-sound {
      status = "okay";
      compatible = "simple-audio-card";
      simple-audio-card,format = "i2s";
      simple-audio-card,name = "rockchip,tas5731";
      simple-audio-card,mclk-fs = <256>;
      simple-audio-card,cpu {
              sound-dai = <&i2s_8ch_1>;
      };
      simple-audio-card,codec {
              sound-dai = <&tas5731>;
      };
};

确认声卡创建成功

cat /proc/asound/cards
0 [rockchiptas5731]: rockchip_tas5731 - rockchip,tas5731

Note: mclk 遵循 “谁使用谁申请” 原则。当 codec 需要使用外部 mclk 时,驱动需要申请并管理 mclk。本例中 codec mclk 来自于 RK3308 的 SCLK_I2S1_8CH_TX_OUT。

2.6.2 Multi Codecs Machine Driver

当 Simple Card 不满足需求时,则需要编写对应的 Machine Driver。本小节介绍 RK 的 Multi Codecs Machine Driver。应用于 1个 DAI 对应多个 CODEC,或者 Combo DAI 对应多个 CODEC 的场景。 alt text

2.6.2.1 dai / codec 设置

PropertyValueDescription
rockchip,cpuphandle设置 DAI 设备
rockchip,codecphandle...设置关联的 codecs
示例:i2s0_8ch 同时应用于 codec 和 vad
&sound {
compatible = "rockchip,multicodecs-card";
rockchip,cpu = <&i2s0_8ch>;
rockchip,codec = <&codec>, <&vad>;
};

2.6.2.2 协议格式设置

PropertyValueDescription
rockchip,formatstring设置协议格式
FormatDescription
i2s i2s标准格式
right_ji2s 右对齐格式
left_ji2s 左对齐格式
dsp_apcm delay 1 bit 格式
dsp_bpcm no delay 格式
pdmpdm 格式

示例:设置为 i2s 左对齐格式

&sound {
      rockchip,format = "left_j";
};

2.6.2.3 mclk-fs 设置

PropertyValueDescription
rockchip,mclk-fsint默认情况下,mclk 为采样率的256倍

示例:mclk 为采样率的512倍

&sound {
      rockchip,mclk-fs = <512>;
};

2.6.2.4 master / slave 设置

PropertyValueDescription
rockchip,frame-masterphandle设置提供位时钟的设备,默认情况下 DAI 提供时钟
rockchip,bitclock-masterphandle设置提供帧时钟的设备,默认情况下 DAI 提供时钟

示例:DAI 工作于 slave 模式,CODEC 工作于 master 模式

&sound {
compatible = "rockchip,multicodecs-card";
rockchip,frame-master = <&codec>;
rockchip,bitclock-master = <&codec>;
rockchip,cpu = <&i2s0_8ch>;
rockchip,codec = <&codec>, <&vad>;
};

2.6.2.5 时钟相位反相设置

PropertyValueDescription
rockchip,bitclock-inversionboolean反相位时钟,即 BCLK
rockchip,frame-inversionboolean反相帧时钟,即 LRCK

示例:bclk,lrck 同时反相

&sound {
compatible = "rockchip,multicodecs-card";
rockchip,bitclock-inversion;
rockchip,frame-inversion;
rockchip,cpu = <&i2s0_8ch>;
rockchip,codec = <&codec>, <&vad>;
};

2.6.2.6 设置wait-card-locked

PropertyValueDescription
rockchip,wait-card-lockedint等待特定soundcard的ID注册后再进行注册,确保当前
soundcard的ID排在指定ID之后

示例:希望当前bluetooth-sound注册为card1,需要card0注册了之后当前card才注册。

bluetooth_sound: bluetooth-sound {
status = "disabled";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip,rk3308-pcm";
rockchip,mclk-fs = <128>;
rockchip,cpu = <&i2s_2ch_0>;
rockchip,codec = <&dummy_codec>;
rockchip,format = "dsp_b";
rockchip,bitclock-inversion = <0>;
rockchip,wait-card-locked = <0>;
};

2.6.2.7 耳机按键检测/耳机喇叭使能相关配置

PropertyValueDescription
hp-det-gpiophandle耳机检测pin,通过中断来检测耳机拔插状态
spk-con-gpiophandle功放喇叭控制pin
hp-con-gpiophandle耳机控制pin
io-channelsphandleadc检测通道用来区分3/4段耳机,以及耳机按键
poll-intervalintadc轮询时间间隔。默认100,单位是ms
keyup-threshold-microvoltintadc按键电压
play-pause-keyphandle这里定义了播放暂停按键,可以根据需求定义其他按键
rockchip,audio-routingstring声卡的 routing

示例:

gpio1_d5 低电平插入耳机检测,gpio1_d2使能耳机 ,gpio1_d3 高电平使能喇叭。 adc3 来区分3/4段耳机,同时支持耳机线上的播放暂停按键。 Headphone 对应es8388 的 LROUT1,通过Headphone Power 控制gpio1_d2。 Speaker 对应es8388 的 LROUT2,通过Speaker Power 控制gpio1_d3。 LINPUT 对应es8388 的 Main Mic, RINPUT对应Headset Mic

es8388_sound: es8388-sound {
status = "okay";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip-es8388";
hp-det-gpio = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>;
io-channels = <&saradc 3>;
io-channel-names = "adc-detect";
keyup-threshold-microvolt = <1800000>;
poll-interval = <100>;
spk-con-gpio = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
hp-con-gpio = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
rockchip,format = "i2s";
rockchip,mclk-fs = <256>;
rockchip,cpu = <&i2s0_8ch>;
rockchip,codec = <&es8388>;
rockchip,audio-routing =
"Headphone", "LOUT1",
"Headphone", "ROUT1",
"Speaker", "LOUT2",
"Speaker", "ROUT2",
"Headphone", "Headphone Power",
"Headphone", "Headphone Power",
"Speaker", "Speaker Power",
"LINPUT1", "Main Mic",
"LINPUT2", "Main Mic",
"RINPUT1", "Headset Mic",
"RINPUT2", "Headset Mic";
pinctrl-names = "default";
pinctrl-0 = <&hp_det>;
play-pause-key {
label = "playpause";
linux,code = <KEY_PLAYPAUSE>;
press-threshold-microvolt = <2000>;
};
};

更多功能详见内核文档:kernel/Documentation/devicetree/bindings/sound/rockchip,multicodecs.txt

2.6.3 HDMI Audio

RK 4.4内核 HDMI 包含两套框架:RK HDMI 框架,DRM 框架,因此,音频也对应两种实现方式。RK 4.19内核版本开始只使用标准 DRM 框架。

示例1:RK3399 RK HDMI 框架音频配置

dw_hdmi_audio: dw-hdmi-audio {
compatible = "rockchip,dw-hdmi-audio";
#sound-dai-cells = <0>;
};
hdmi_sound: hdmi-sound {
compatible = "simple-audio-card";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <128>;
simple-audio-card,name = "rockchip,hdmi";
simple-audio-card,cpu {
sound-dai = <&i2s2>;
};
simple-audio-card,codec {
sound-dai = <&dw_hdmi_audio>;
};
};

示例2:DRM 框架音频配置

hdmi: hdmi@ff940000 {
compatible = "rockchip,rk3399-dw-hdmi";
……
#address-cells = <1>;
#size-cells = <0>;
#sound-dai-cells = <0>;
};
hdmi_sound: hdmi-sound {
compatible = "simple-audio-card";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <128>;
simple-audio-card,name = "rockchip,hdmi";
simple-audio-card,cpu {
sound-dai = <&i2s2>;
};
simple-audio-card,codec {
sound-dai = <&hdmi>;
};
};

2.7 音频时钟补偿

RK 平台提供音频时钟微调,用于解决异源系统中音频同步问题,比如:UAC,HDMI IN,BT 语音等,调整范围:± 1000 ppm(parts per million)。

  1. 使能 CONFIG

    CONFIG_ROCKCHIP_CLK_COMPENSATION
  2. 添加 PLL 小数模式频率(时钟微调补偿功能仅在PLL 小数模式下有效),遵循“PLL 为 mclk 的偶数倍”原则。分两类芯片方案描述:

    • 音频独立 PLL 的芯片,只需遵循偶数倍原则即可。比如 RK3308 VPLL0 设置为 1179648000 Hz,VPLL1 设置为 903168000 Hz。

    • 音频和其他模块共享 PLL时,产品需要评估时钟微调对共享模块的影响,分如下两点描述: a. ± 1000 ppm 的调整对模块功能是否有影响,比如:以太网就必须要求精确时钟,而视频编码控制器则没有精确要求。 b. 如果条件a满足,则将 PLL 频率调整到合适的分数频率。比如 RV1126上,既要满足500 MHz 左右的视频编码控制器工作,又要兼容支持 48k 采样率的音频播放支持,选择靠近 500 MHz 的分数 PLL 频率即可,如:491520000 MHz。

      示例: RV1126 CPLL 频率修改为小数频率

            diff --git a/drivers/clk/rockchip/clk-rv1126.c b/drivers/clk/rockchip/clk￾rv1126.c
    index 9ba0bff..1b95aed 100644
    --- a/drivers/clk/rockchip/clk-rv1126.c
    +++ b/drivers/clk/rockchip/clk-rv1126.c
    @@ -74,6 +74,8 @@ static struct rockchip_pll_rate_table rv1126_pll_rates[] = {
          RK3036_PLL_RATE(594000000, 1, 99, 4, 1, 1, 0),
          RK3036_PLL_RATE(504000000, 1, 84, 4, 1, 1, 0),
          RK3036_PLL_RATE(500000000, 1, 125, 6, 1, 1, 0),
    +       RK3036_PLL_RATE(496742400, 1, 124, 6, 1, 0, 3113851),
    +       RK3036_PLL_RATE(491520000, 1, 40, 2, 1, 0, 16106127),

    DTS 配置:仅配置 491520000 Hz,支持 8k, 16k, 24k, 48k, 96k, 192k 采样率

                &cru {
          assigned-clocks =
                  <&pmucru CLK_RTC32K>, <&pmucru PLL_GPLL>,
                  <&pmucru PCLK_PDPMU>, <&cru PLL_CPLL>,
                  <&cru PLL_HPLL>, <&cru ARMCLK>,
                  <&cru ACLK_PDBUS>, <&cru HCLK_PDBUS>,
                  <&cru PCLK_PDBUS>, <&cru ACLK_PDPHP>,
                  <&cru HCLK_PDPHP>, <&cru HCLK_PDAUDIO>,
                  <&cru HCLK_PDCORE_NIU>;
          assigned-clock-rates =
                  <32768>, <1188000000>,
                  <100000000>, <491520000>,
                  <1400000000>, <600000000>,
                  <500000000>, <200000000>,
                  <100000000>, <300000000>,
                  <200000000>, <150000000>,
                  <200000000>;
    };
  3. 使能 DAI 的时钟补偿功能,配置 root pll

    • I2S 控制器
        &i2s1_2ch {
          clocks = <&cru MCLK_I2S1>, <&cru HCLK_I2S1>, <&cru PLL_CPLL>;
          clock-names = "i2s_clk", "i2s_hclk", "i2s_clk_root";
          rockchip,mclk-calibrate;
    };
    • I2S-TDM 控制器
            &i2s0_8ch {
          clocks = <&cru MCLK_I2S0_TX>, <&cru MCLK_I2S0_RX>, <&cru HCLK_I2S0>,
                    <&cru MCLK_I2S0_TX_DIV>, <&cru MCLK_I2S0_RX_DIV>,
                    <&cru PLL_CPLL>, <&cru PLL_CPLL>;
          clock-names = "mclk_tx", "mclk_rx", "hclk",
                        "mclk_tx_src", "mclk_rx_src",
                        "mclk_root0", "mclk_root1";
          rockchip,mclk-calibrate;
    };
    • PDM 控制器
                &pdm {
          status = "okay";
          clocks = <&cru MCLK_PDM>, <&cru HCLK_PDM>, <&cru PLL_CPLL>;
          clock-names = "pdm_clk", "pdm_hclk", "pdm_clk_root";
          rockchip,mclk-calibrate;
    };
  4. 验证功能 功能开启后,可通过 amixer 进行时钟微调。

示例:RV1126 CPLL 频率修改为小数频率

# cat /proc/asound/cards
0 [rockchiprk809co]: rockchip_rk809- - rockchip,rk809-codec
                    rockchip,rk809-codec
# aplay -D hw:0,0 --period-size=1024 --buffer-size=4096 -f dat /dev/zero &
Playing raw data '/dev/zero' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
# cat /sys/kernel/debug/clk/clk_summary | egrep "i2s0|cpll"
pll_cpll                         1       1       0   491519999
cpll                           6       11       0   491519999
  mclk_i2s0_rx_div             1       1       0   12288000
    mclk_i2s0_rx_mux           1       1       0   12288000
      mclk_i2s0_rx             1       1       0   12288000
    mclk_i2s0_rx_fracdiv       0       0       0   12288000
  mclk_i2s0_tx_div             1       1       0   12288000
    mclk_i2s0_tx_mux           1       1       0   12288000
      mclk_i2s0_tx             2       2       0   12288000
        mclk_i2s0_tx_out2io     2       2       0   12288000
    mclk_i2s0_tx_fracdiv       0       0       0   12288000
# amixer contents
...
numid=3,iface=PCM,name='PCM Clk Compensation In PPM'
; type=INTEGER,access=rw------,values=1,min=-1000,max=1000,step=1
: values=0
# amixer -- cset numid=3 10
numid=3,iface=PCM,name='PCM Clk Compensation In PPM'
; type=INTEGER,access=rw------,values=1,min=-1000,max=1000,step=1
: values=10
# cat /sys/kernel/debug/clk/clk_summary | egrep "i2s0|cpll"
pll_cpll                         1       1       0   491524892
cpll                           6       11       0   491524892
  mclk_i2s0_rx_div             1       1       0   12288123
    mclk_i2s0_rx_mux           1       1       0   12288123
      mclk_i2s0_rx             1       1       0   12288123
    mclk_i2s0_rx_fracdiv       0       0       0   12288123
  mclk_i2s0_tx_div             1       1       0   12288123
    mclk_i2s0_tx_mux           1       1       0   12288123
      mclk_i2s0_tx             2       2       0   12288123
        mclk_i2s0_tx_out2io     2       2       0   12288123
    mclk_i2s0_tx_fracdiv       0       0       0   12288123
# amixer -- cset numid=3 -10
numid=3,iface=PCM,name='PCM Clk Compensation In PPM'
; type=INTEGER,access=rw------,values=1,min=-1000,max=1000,step=1
: values=-10
pll_cpll                         1       1       0   491515106
cpll                           6       11       0   491515106
  mclk_i2s0_rx_div             1       1       0   12287878
    mclk_i2s0_rx_mux           1       1       0   12287878
      mclk_i2s0_rx             1       1       0   12287878
    mclk_i2s0_rx_fracdiv       0       0       0   12287878
  mclk_i2s0_tx_div             1       1       0   12287878
    mclk_i2s0_tx_mux           1       1       0   12287878
      mclk_i2s0_tx             2       2       0   12287878
        mclk_i2s0_tx_out2io     2       2       0   12287878
    mclk_i2s0_tx_fracdiv       0       0       0   12287878

3. 调试方法

常用调试工具如图 3-1 所示:

alt text

3.1 硬件仪器

3.1.1 万用表

测量硬件供电情况,结合芯片手册确认工作电压正常。RK 端确认 IO domain的电压是否和软件配置一致,CODEC 端确认各路供电是否符合手册要求。

示例:RV1126 EVB,RK817 接在 I2S0 上,CODEC 工作 1.8v;万用表测量硬件电压为 1.8v,如下图所示:

alt text

软件板级 DTS 对应 domain 的配置为 1.8v,如下所示:

&pmu_io_domains {
status = "okay";
...
vccio7-supply = <&vcc_1v8>;
};

注意:如果硬件供电和软件配置不一致,可能会造成功能异常,芯片损坏等风险。

3.1.2 示波器

使用示波器测量硬件信号:时钟和数据。测量时钟频率,幅度,占空比,jitter等是否符合协议规范(比如:当工作电压为3.3v,而实际时钟信号只有2v时,导致采集不到数据,原因可能是被外设拉低,或者对地短路,或者软件电压配置不正确等);抓取时钟和数据信号,结合特定数据pattern,从波形中分析数据(可用逻辑分析仪替代)是否正确;使用触发模式抓取问题现场(比如:断音瞬间 CLK 是否有毛刺或者抖动)。

3.1.3 信号发生器

可生成特定的波形,比如方波,三角波,正弦波。调试 ADC 时,可使用信号发生器生成正弦波,调试 ADC 的不失真电平等。

3.1.4 AP分析仪

CODEC 指标测试分析仪,测试数字接口信号,测量模拟信号指标,信噪比,总谐波失真,动态范围等。

3.2 调试命令

3.2.1 procfs

通过 proc fs 确认声卡注册成功

# cat /proc/asound/cards
0 [rockchiprk809co]: rockchip_rk809- - rockchip,rk809-codec
rockchip,rk809-codec
7 [Loopback ]: Loopback - Loopback
Loopback 1
# ls /dev/snd/
by-path controlC7 pcmC0D0p pcmC7D0p pcmC7D1p
controlC0 pcmC0D0c pcmC7D0c pcmC7D1c timer

3.2.2 clk summary

查询音频时钟,确认时钟设置正确 示例:查询 i2s0 mclk 频率,以及其所在的 pll,结果:mclk 为 12288000 Hz,pll 源为 cpll

# cat /sys/kernel/debug/clk/clk_summary | egrep "i2s0|pll"
pll_cpll 1 1 0 500000000
cpll 5 10 0 500000000
mclk_i2s0_rx_div 0 0 0 500000000
mclk_i2s0_rx_fracdiv 0 0 0 12288000
mclk_i2s0_rx_mux 0 0 0 12288000
mclk_i2s0_rx 0 0 0 12288000
mclk_i2s0_tx_div 1 1 0 500000000
mclk_i2s0_tx_fracdiv 1 1 0 12288000
mclk_i2s0_tx_mux 1 1 0 12288000
mclk_i2s0_tx 1 1 0 12288000
mclk_i2s0_tx_out2io 2 2 0 12288000

3.2.3 寄存器

3.2.3.1 io 命令

通过 io 命令查看修改寄存器(适合 SOC 寄存器查询),配合芯片手册确认配置以及工作状态。

# cat /proc/iomem | grep i2s
ff800000-ff800fff : i2s@ff800000
# io -4 -l 0x40 0xff800000
ff800000: 7200000f 004e000f 10003f3f 00000010
ff800010: 000f0110 01f00000 00000000 00000003
ff800020: 00000000 00000000 00000000 0000001f
ff800030: 00003eff 00003eff 00000303 20150001
3.2.3.2 regmap

通过 regmap 节点查看寄存器(只读)。

# ls /sys/kernel/debug/regmap/
0-0020-rk817-codec
ff800000.i2s
...
# cat /sys/kernel/debug/regmap/0-0020-rk817-codec/registers
12: 03
13: f4
14: 00
15: ff
16: 00
17: 40
18: 48
19: 00
1a: 00
1b: ff
1c: 00
1d: 00
1e: 02
1f: 00
...

注意regmap 基于 cache 机制,如果通过 io 命令直接修改寄存器后,regmap 节点不会体现更新后的寄存器,除非驱动将寄存器类型设置为 volatile 或者将 regmap cache 关闭。

示例:关闭 regmap cache,查询到正确寄存器值

# cd /sys/kernel/debug/regmap/ff800000.i2s/
# cat registers /* dump register by regmap */
00: 7200000f
04: 004e000f
08: 10003f3f
0c: XXXXXXXX
10: 000f0010
14: 01f00000
18: XXXXXXXX
1c: 00000000
20: XXXXXXXX
24: XXXXXXXX
2c: XXXXXXXX
30: 00003eff
34: 00003eff
38: 00000303
# io -4 0xff800000 0x0 /* set 0xff800000 to 0 */
# io -4 -l 0x40 0xff800000 /* dump to check value of 0xff800000, now it's 0 */
ff800000: 00000000 004e000f 10003f3f 00000000
ff800010: 000f0010 01f00000 00000000 00000000
ff800020: 00000000 00000000 00000000 00000000
ff800030: 00003eff 00003eff 00000303 20150001
# cat registers /* dump register by regmap */
00: 7200000f /* it's still old value */
04: 004e000f
08: 10003f3f
0c: XXXXXXXX
10: 000f0010
14: 01f00000
18: XXXXXXXX
1c: 00000000
20: XXXXXXXX
24: XXXXXXXX
2c: XXXXXXXX
30: 00003eff
34: 00003eff
38: 00000303
# echo N > cache_only
[148833.374641] rockchip-i2s-tdm ff800000.i2s: debugfs cache_only=N forced:
syncing cache
# echo Y > cache_bypass
[148834.760274] rockchip-i2s-tdm ff800000.i2s: debugfs cache_bypass=Y forced
# cat registers /* dump again, now it's correct */
00: 00000000
04: 004e000f
08: 10003f3f
0c: 00000000
10: 000f0010
14: 01f00000
18: 00000000
1c: 00000000
20: 00000000
24: 00000000
2c: 00000000
30: 00003eff
34: 00003eff
38: 00000303
3.2.3.3 i2c-tools

通过 i2c tool 查看修改 codec 寄存器(适合 i2c 类型的 codec 设备),配合 codec 手册确认配置以及工作状态。

3.2.3.3.1 i2cdetect

查看 i2c 总线上的设备 示例:查询 i2c0 总线上的设备

Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
      i2cdetect -F I2CBUS
      i2cdetect -l
I2CBUS is an integer or an I2C bus name
If provided, FIRST and LAST limit the probing range

示例:查询 i2c0 总线上的设备

# i2cdetect -y 0
    0 1 2 3 4 5 6 7 8 9 a b c d e f
00:         -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
3.2.3.3.2 i2cdump

dump 设备的所有寄存器

Usage: i2cdump [-f] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
  b (byte, default)
  w (word)
  W (word on even register addresses)
  s (SMBus block)
  i (I2C block)
  c (consecutive byte)
  Append p for SMBus PEC

示例:查询 i2c0 总线下 rk817(设备地址:0x20)的寄存器,其中 0x12 ~ 0x4f 为 codec 寄存器。

# i2cdump -f -y 0 0x20 b
    0 1 2 3 4 5 6 7 8 9 a b c d e f
00: 03 35 18 10 08 17 04 00 00 00 01 01 00 80 02 00
10: 00 00 03 f4 00 ff 00 40 48 00 00 ff 00 00 02 00
20: 00 00 00 00 ff 00 00 1f 00 99 00 00 00 00 00 03
30: 04 03 03 00 a5 02 00 00 11 03 03 00 00 e0 0f 09
40: a5 7f 04 58 2d 0c a5 00 00 00 00 0f 20 00 0f 88
50: 8c 00 01 00 01 b0 44 1e 00 60 16 6a 16 6a ff f8
60: ff f8 00 00 00 00 00 00 00 00 00 16 66 01 d7 00
70: 00 00 00 00 ff ed d6 58 16 6a ff f9 00 00 00 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 60 81 d5 7f fb df ca 81 d2 ff 00 00 00 00 00 00
a0: 00 00 00 00 b8 00 00 00 00 00 00 00 00 00 00 00
b0: 00 0f 0f 0f 0f ad 0e ff ff 00 64 18 18 64 12 18
c0: 64 00 00 64 62 62 64 ff ff ff ff ff 08 08 30 30
d0: 08 08 30 30 30 30 18 14 58 58 6c 6c 6c 6c 0b 33
e0: 00 00 00 55 a2 c8 c5 40 00 ff 22 03 0a 80 94 0b
f0: c0 8c a0 40 18 10 00 86 00 dc 00 ff 00 ff 60 00
3.2.3.3.3 i2cset

修改单个寄存器值

Usage: i2cset [-f] [-y] [-m MASK] [-r] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] 
... [MODE]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
  c (byte, no value)
  b (byte data, default)
  w (word data)
  i (I2C block data)
  s (SMBus block data)
  Append p for SMBus PEC

示例:修改 rk817 的 0x12 寄存器,将值改为0

# i2cset -f -y -r 0 0x20 0x12 0x0 b
Value 0x00 written, readback matched
3.2.3.3.4 i2cget

查询单个寄存器值

Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77)
MODE is one of:
b (read byte data, default)
w (read word data)
c (write byte/read byte)
Append p for SMBus PEC

示例:查询 rk817 的 0x12 寄存器

# i2cget -f -y 0 0x20 0x12 b
0x00

更多功能详见i2c-tools 官方文档:i2c-tools

3.2.4 alsa-utils

RK Linux SDK 标配 alsa-utils 工具

3.2.4.1 aplay

Usage: aplay [OPTION]... [FILE]...
-h, --help help
--version print current version
-l, --list-devices list all soundcards and digital audio devices
-L, --list-pcms list device names
-D, --device=NAME select PCM by name
-q, --quiet quiet mode
-t, --file-type TYPE file type (voc, wav, raw or au)
-c, --channels=# channels
-f, --format=FORMAT sample format (case insensitive)
-r, --rate=# sample rate
-d, --duration=# interrupt after # seconds
-s, --samples=# interrupt after # samples per channel
-M, --mmap mmap stream
-N, --nonblock nonblocking mode
-F, --period-time=# distance between interrupts is # microseconds
-B, --buffer-time=# buffer duration is # microseconds
--period-size=# distance between interrupts is # frames
--buffer-size=# buffer duration is # frames
-A, --avail-min=# min available space for wakeup is # microseconds
-R, --start-delay=# delay for automatic PCM start is # microseconds
--stop-delay=# delay for automatic PCM stop is # microseconds from xrun
-v, --verbose show PCM structure and setup (accumulative)
-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)
-I, --separate-channels one file for each channel
-i, --interactive allow interactive operation from stdin
-m, --chmap=ch1,ch2,.. Give the channel map to override or follow
--disable-resample disable automatic rate resample
--disable-channels disable automatic channel conversions
--disable-format disable automatic format conversions
--disable-softvol disable software volume control (softvol)
--test-position test ring buffer position
--test-coef=# test coefficient for ring buffer position (default 8)
expression for validation is: coef * (buffer_size / 2)
--test-nowait do not wait for ring buffer - eats whole CPU
--max-file-time=# start another output file when the old file has recorded
for this many seconds
--process-id-file write the process ID here
--use-strftime apply the strftime facility to the output file name
--dump-hw-params dump hw_params of the device
--fatal-errors treat all errors as fatal

示例:通过声卡0 播放 48k 采样率 2声道 16 位的静音数据

aplay -D hw:0,0 --period-size=1024 --buffer-size=4096 -r 48000 -c 2 -f s16_le /dev/zero

3.2.4.2 arecord

Usage: arecord [OPTION]... [FILE]...
-h, --help help
--version print current version
-l, --list-devices list all soundcards and digital audio devices
-L, --list-pcms list device names
-D, --device=NAME select PCM by name
-q, --quiet quiet mode
-t, --file-type TYPE file type (voc, wav, raw or au)
-c, --channels=# channels
-f, --format=FORMAT sample format (case insensitive)
-r, --rate=# sample rate
-d, --duration=# interrupt after # seconds
-s, --samples=# interrupt after # samples per channel
-M, --mmap mmap stream
-N, --nonblock nonblocking mode
-F, --period-time=# distance between interrupts is # microseconds
-B, --buffer-time=# buffer duration is # microseconds
--period-size=# distance between interrupts is # frames
--buffer-size=# buffer duration is # frames
-A, --avail-min=# min available space for wakeup is # microseconds
-R, --start-delay=# delay for automatic PCM start is # microseconds
--stop-delay=# delay for automatic PCM stop is # microseconds from xrun
-v, --verbose show PCM structure and setup (accumulative)
-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)
-I, --separate-channels one file for each channel
-i, --interactive allow interactive operation from stdin
-m, --chmap=ch1,ch2,.. Give the channel map to override or follow
--disable-resample disable automatic rate resample
--disable-channels disable automatic channel conversions
--disable-format disable automatic format conversions
--disable-softvol disable software volume control (softvol)
--test-position test ring buffer position
--test-coef=# test coefficient for ring buffer position (default 8)
expression for validation is: coef * (buffer_size / 2)
--test-nowait do not wait for ring buffer - eats whole CPU
--max-file-time=# start another output file when the old file has recorded
for this many seconds
--process-id-file write the process ID here
--use-strftime apply the strftime facility to the output file name
--dump-hw-params dump hw_params of the device
--fatal-errors treat all errors as fatal

示例:通过声卡0 录制 16k 采样率 8声道 32 位 的音频数据

arecord -D hw:0,0 --period-size=1024 --buffer-size=4096 -r 16000 -c 8 -f s32_le r.wav

3.2.4.3 aplay | arecord

两者通过管道可以方便的实现 loopback 功能,方便驱动调试和指标测试。 示例:声卡0 录制 -> 声卡1 播放

arecord -D hw:0,0 --period-size=1024 --buffer-size=4096 -r 48000 -c 2 -f s16_le -t raw | aplay -D hw:1,0 --period-size=1024 --buffer-size=4096 -r 48000 -c 2 -f s16_le -t raw

3.2.4.4 amixer

控制 codec 内部的通路开关,结合 codec 手册的音频拓扑图(比如 图 3-2 RK3308 codec)实现音频流的路由,音量控制等。

alt text 图 3-2 RK3308 codec

Usage: `amixer <options> [command]`

Available options:
- `-h,--help` this help
- `-c,--card N` select the card
- `-D,--device N` select the device, default 'default'
- `-d,--debug` debug mode
- `-n,--nocheck` do not perform range checking
- `-v,--version` print version of this program
- `-q,--quiet` be quiet
- `-i,--inactive` show also inactive controls
- `-a,--abstract L` select abstraction level (none or basic)
- `-s,--stdin` Read and execute commands from stdin sequentially
- `-R,--raw-volume` Use the raw value (default)
- `-M,--mapped-volume` Use the mapped volume

Available commands:
- `scontrols` show all mixer simple controls
- `scontents` show contents of all mixer simple controls (default command)
- `sset sID P` set contents for one mixer simple control
- `sget sID` get contents for one mixer simple control
- `controls` show all controls for given card
- `contents` show contents of all controls for given card
- `cset cID P` set control contents for one control
- `cget cID` get control contents for one control

示例:查询声卡 controls,将播放通路切换到 SPK

# amixer -c 0 contents
numid=1,iface=MIXER,name='Playback Path'
; type=ENUMERATED,access=rw------,values=1,items=11
; Item #0 'OFF'
; Item #1 'RCV'
; Item #2 'SPK'
; Item #3 'HP'
; Item #4 'HP_NO_MIC'
; Item #5 'BT'
; Item #6 'SPK_HP'
; Item #7 'RING_SPK'
; Item #8 'RING_HP'
; Item #9 'RING_HP_NO_MIC'
; Item #10 'RING_SPK_HP'
: values=0
...
# amixer -c 0 cset numid=1 2
numid=1,iface=MIXER,name='Playback Path'
; type=ENUMERATED,access=rw------,values=1,items=11
; Item #0 'OFF'
; Item #1 'RCV'
; Item #2 'SPK'
; Item #3 'HP'
; Item #4 'HP_NO_MIC'
; Item #5 'BT'
; Item #6 'SPK_HP'
; Item #7 'RING_SPK'
; Item #8 'RING_HP'
; Item #9 'RING_HP_NO_MIC'
; Item #10 'RING_SPK_HP'
: values=2

3.2.4.5 alsaloop

支持任意声卡间的路由
支持自适应时钟同步
支持自适应重采样
支持 mixer controls 重定向

Usage: `alsaloop [OPTION]...`

- `-h,--help` help
- `-g,--config` configuration file (one line = one job specified)
- `-d,--daemonize` daemonize the main process and use syslog for errors
- `-P,--pdevice` playback device
- `-C,--cdevice` capture device
- `-X,--pctl` playback ctl device
- `-Y,--cctl` capture ctl device
- `-l,--latency` requested latency in frames
- `-t,--tlatency` requested latency in usec (1/1000000sec)
- `-f,--format` sample format
- `-c,--channels` channels
- `-r,--rate` rate
- `-n,--resample` resample in alsa-lib
- `-A,--samplerate` use converter (0=sincbest,1=sincmedium,2=sincfastest,3=zerohold,4=linear)
- `-B,--buffer` buffer size in frames
- `-E,--period` period size in frames
- `-s,--seconds` duration of loop in seconds
- `-b,--nblock` non-block mode (very early process wakeup)
- `-S,--sync` sync mode(0=none,1=simple,2=captshift,3=playshift,4=samplerate,5=auto)
- `-a,--slave` stream parameters slave mode (0=auto, 1=on, 2=off)
- `-T,--thread` thread number (-1 = create unique)
- `-m,--mixer` redirect mixer, argument is: `SRC_SLAVE_ID(PLAYBACK)[@DST_SLAVE_ID(CAPTURE)]`
- `-O,--ossmixer` rescan and redirect oss mixer, argument is: `ALSA_ID@OSS_ID` (for example: "Master@VOLUME")
- `-e,--effect` apply an effect (bandpass filter sweep)
- `-v,--verbose` verbose mode (more -v means more verbose)
- `-w,--workaround` use workaround (serialopen)
- `-U,--xrun` xrun profiling
- `-W,--wake` process wake timeout in ms
- `-z,--syslog` use syslog for errors

Recognized sample formats are: `S8 U8 S16_LE S16_BE U16_LE U16_BE S24_LE S24_BE U24_LE U24_BE S32_LE S32_BE U32_LE U32_BE FLOAT_LE FLOAT_BE FLOAT64_LE FLOAT64_BE IEC958_SUBFRAME_LE IEC958_SUBFRAME_BE MU_LAW A_LAW IMA_ADPCM MPEG GSM SPECIAL S24_3LE S24_3BE U24_3LE U24_3BE S20_3LE S20_3BE U20_3LE U20_3BE S18_3LE S18_3BE U18_3LE U18_3BE G723_24 G723_24_1B G723_40 G723_40_1B DSD_U8 DSD_U16_LE DSD_U32_LE DSD_U16_BE`

Tip #1 (usable 500ms latency, good CPU usage, superb xrun prevention):
alsaloop -t 500000

Tip #2 (superb 1ms latency, but heavy CPU usage):
alsaloop -t 1000

Sync mode specification for capture to playback stream:
- `0 or none` - do not touch the stream
- `1 or simple` - add or remove samples to keep both streams synchronized
- `2 or captshift` - use driver for the capture device (if supported) to compensate the rate shift
- `3 or playshift` - use driver for the playback device (if supported) to compensate the rate shift
- `4 or samplerate` - use samplerate library to do rate resampling
- `5 or auto` - automatically selects the best method in this order: captshift, playshift, samplerate, simple

示例:声卡0录制的音频通过声卡1播放,同步模式采用 策略1(增加或减少采样点)

alsaloop -C hw:0,0 -P hw:1,0 -t 10000 -A 3 -S 1 -b -v

Note: 如果声卡0和1的时钟源相同(比如,同一个 OSC),那么不存在异步累积误差问题。如果声卡0为 UAC(时钟来源于 PC),声卡1为系统声卡(时钟来源于设备),因为时钟源不同,随着时间的累积,必然出现累积误差,导致断音。此时需要软件补偿(如:增加或减少采样点)或者硬件补偿(如:音频时钟补偿)。

3.2.5 tiny-alsa

RK Android SDK 标配 tiny-alsa 工具

3.2.5.1 tinypcminfo

查询声卡支持的采样率,格式,声道数等。

Usage: tinypcminfo [-D card] [-d device]

示例:

# tinypcminfo -D 0
Info for card 0, device 0:
PCM out:
Access: 0x000009
Format0: 0x000044
Format1: 0x000010
Format Name: S16_LE, S24_LE, S32_LE
Subformat: 0x000001
Rate: min=16000Hz max=48000Hz
Channels: min=2 max=2
Sample bits: min=16 max=32
Period size: min=4 max=65536
Period count: min=2 max=4096
PCM in:
Access: 0x000009
Format0: 0x000044
Format1: 0x000010
Format Name: S16_LE, S24_LE, S32_LE
Subformat: 0x000001
Rate: min=16000Hz max=48000Hz
Channels: min=2 max=2
Sample bits: min=16 max=32
Period size: min=4 max=65536
Period count: min=2 max=4096

3.2.5.2 tinyplay

Usage: `tinyplay file.wav [-D card] [-d device] [-p period_size] [-n n_periods]`

示例:播放1k0音频文件

# tinyplay /sdcard/1k0.wav -D 0 -d 0 -p 1024 -n 3
Playing sample: 2 ch, 44100 hz, 32 bit

3.2.5.3 tinycap

Usage: `tinycap file.wav [-D card] [-d device] [-c channels] [-r rate] [-b bits] [-p period_size] [-n n_periods]`

示例:44.1k采样率录制音频

# tinycap /sdcard/rec.wav -D 0 -d 0 -c 2 -r 44100 -b 16 -p 1024 -n 3

3.2.5.4 tinymix

控制 codec 内部的通路开关,音量控制等。效果等同于 amixer。

Usage: `tinymix [-D card]`

示例:打开 Mono 通路开关

# tinymix
Mixer name: 'rockchip,rt5640-codec'
Number of controls: 123
ctl type num name value
0 BOOL 1 Mono Playback Switch Off
1 INT 2 Mono DAC Playback Volume 175 175
2 BOOL 2 Speaker Channel Switch Off Off
3 INT 2 Speaker Playback Volume 31 31
4 BOOL 2 HP Channel Switch Off Off
...
# tinymix "Mono Playback Switch" 1
Mono Playback Switch: On

3.2.6 xrun profiling

当音频播放 buffer empty 的时候,触发 underrun;当音频录制 buffer full 的时候,触发 overrun;两者统称为 xrun。音频流路径上,所有 buffer 的节点都可能触发 xrun。本小节描述内核 xrun。

3.2.6.1 xrun kmsg

  1. 使能 XRUN CONFIG
     CONFIG_SND_DEBUG
    CONFIG_SND_PCM_XRUN_DEBUG
    CONFIG_SND_VERBOSE_PROCFS
    xrun 调试开关如下表 3-1 所示,如果需要支持所有开关,取和即可。 表 3-1 xrun 调试开关
BitValue Description
1Basic debugging - show xruns in ksyslog interface
2Dump stack - dump stack for basic debugging
4Jiffies check - compare the position with jiffies (a sort of in-kernel monotonic clock), show what's changed when basic debugging is enabled
  1. 使能声卡0播放设备的所有 xrun 调试开关
        # echo 7 > /proc/asound/card0/pcm0p/xrun_debug
  2. 当播放 dma ringbuffer empty 时,类似 log 举例如下:
        asoc-simple-card rk809-sound: XRUN: pcmC0D0p:0
    CPU: 3 PID: 657 Comm: sh Not tainted 4.19.161 #509
    Hardware name: Generic DT based system
    [<b010f47c>] (unwind_backtrace) from [<b010b9a8>] (show_stack+0x10/0x14)
    [<b010b9a8>] (show_stack) from [<b0850b58>] (dump_stack+0x90/0xa4)
    [<b0850b58>] (dump_stack) from [<b06a39c4>] (__snd_pcm_xrun+0xa0/0x114)
    [<b06a39c4>] (__snd_pcm_xrun) from [<b069dac4>] (snd_pcm_stop_xrun+0x64/0x68)
    [<b069dac4>] (snd_pcm_stop_xrun) from [<b0695c4c>]
    (snd_info_text_entry_release+0x34/0x7c)
    [<b0695c4c>] (snd_info_text_entry_release) from [<b02694a4>]
    (close_pdeo+0x54/0x100)
    [<b02694a4>] (close_pdeo) from [<b02695c0>] (proc_reg_release+0x70/0x78)
    [<b02695c0>] (proc_reg_release) from [<b0210ac8>] (__fput+0x88/0x1c4)
    [<b0210ac8>] (__fput) from [<b01412d4>] (task_work_run+0x94/0xb4)
    [<b01412d4>] (task_work_run) from [<b010b48c>] (do_work_pending+0xc8/0xd0)
    [<b010b48c>] (do_work_pending) from [<b0101064>] (slow_work_pending+0xc/0x20)

3.2.6.2 xrun ftrace

除了内核 log 的调试信息,xrun 的 trace event 提供了更详尽的调试信息,比如:hwptr,applptr等。该部分需要使能内核 trace debug。alsa 支持的内核trace event 如表 3-2 所示:

表 3-2 Trace Event Description

EventDescription
snd_pcm:applptr应用指针更新
snd_pcm:hw_ptr_errordma 指针出错
snd_pcm:xrunxrun
snd_pcm:hwptrdma 指针更新
  1. 使能 FTRACE CONFIG
    CONFIG_FUNCTION_TRACER
    CONFIG_FUNCTION_GRAPH_TRACER
    CONFIG_STACK_TRACER
    CONFIG_DYNAMIC_FTRACE
  2. 后台播放音频
        # aplay -D hw:0,0 --period-size=128 --buffer-size=256 -r 48000 -f dat /dev/zero &
  3. 使能音频 trace event,可以分析 buffer 生产者和消费者详细的读写信息,方便定位问题。
# cd /sys/kernel/debug/tracing
# echo "snd_pcm:applptr" >> set_event
# echo "snd_pcm:hwptr" >> set_event
# echo "snd_pcm:xrun" >> set_event
# cat trace | head -20
...
178.109942: applptr: pcmC0D0p/sub0: prev=0, curr=128, avail=128, period=128,
buf=256
178.109981: applptr: pcmC0D0p/sub0: prev=128, curr=256, avail=0, period=128,
buf=256
178.110224: hwptr:   pcmC0D0p/sub0: POS: pos=24, old=0, base=0, period=128,
buf=256
178.110234: applptr: pcmC0D0p/sub0: prev=256, curr=280, avail=0, period=128,
buf=256
178.112441: hwptr:   pcmC0D0p/sub0: IRQ: pos=128, old=24, base=0, period=128,
buf=256
178.112510: applptr: pcmC0D0p/sub0: prev=280, curr=384, avail=0, period=128,
buf=256
178.112553: hwptr:   pcmC0D0p/sub0: POS: pos=136, old=128, base=0, period=128,
buf=256
178.112556: applptr: pcmC0D0p/sub0: prev=384, curr=392, avail=0, period=128,
buf=256
178.115176: hwptr:   pcmC0D0p/sub0: IRQ: pos=0, old=136, base=0, period=128,
buf=256
178.115270: applptr: pcmC0D0p/sub0: prev=392, curr=512, avail=0, period=128,
buf=256
178.115321: hwptr:   pcmC0D0p/sub0: POS: pos=8, old=256, base=256, period=128,
buf=256
178.115327: applptr: pcmC0D0p/sub0: prev=512, curr=520, avail=0, period=128,
buf=256

更多功能详见内核文档:kernel/Documentation/trace/*

3.2.6.3 trace-cmd

trace-cmdftrace 的前端应用,简化了 ftrace 的操作步骤,如下示例:

    # trace-cmd record -e snd_pcm:hwptr -e snd_pcm:applptr -e snd_pcm:xrun
# trace-cmd report

更多功能详见官方文档:trace-cmd

3.2.6.4 KernelShark

KernelSharktrace-cmd 的前端应用,将 trace-cmd 的数据图形化呈现。请参考官方文档:KernelShark

3.3 PC 工具

Audacity 和 Audition 都是 PC 平台音频分析处理工具,可用于分析音频问题,比如:断音,底噪,失真,恒频干扰等。

4. FAQ

4.1 芯片接口支持情况

请参考 Rockchip 音频特性以及芯片手册。

4.2 声卡注册失败

  1. 根据声卡添加实例确认声卡驱动配置编译进系统。
  2. 根据 kmsg 定位原因:DAI,CODEC 或者 Machine,比如如下 log 表示 DAI 未注册上:
    [ 0.584114] rk-multicodecs vad-sound: ASoC: CPU DAI (null) not registered
  3. DAI fail:当 DMA 通道资源不足时,导致 DAI 注册失败,关闭其他使用 DMA 的模块,再次确认。
  4. CODEC fail:使用万用表和示波器测量 CODEC 电压,时钟;配合 i2c-tools 确认 i2c 设备通信是否正常。

4.3 播放无声

  1. 确认音频源为非静音文件。
  2. 使用 aplay 或者 tinyplay 播放,定位问题是发生在用户态还是内核态。
  3. 播放等待10秒以上确认是否为 I/O error 问题。
  4. 使用 amixer 或者 tinymix 检查 CODEC 内部 DAC 通路是否打开,音量是否静音。
  5. 查看寄存器配置,配合芯片手册或者 CODEC 手册确认配置是否正确:IOMUX,DAI,CODEC。
  6. 使用万用表和示波器测量电压,时钟,数据。确认电压,时钟正常,数据线上有波形;测量 CODEC 近端模拟输出信号是否正常,测量 PA 使能 gpio 电平,逐级定位问题点。

4.4 播放失真

  1. 使用 aplay 或者 tinyplay 播放 1k0 音频文件。
  2. 使用示波器测量模拟输出的正弦波是否正常,是否出现削顶失真。
  3. 调整数字或者模拟增益,观察 CODEC 芯片输出端波形,对比指标测试数据是否相符。
  4. 逐级检查引入失真的节点。

4.5 录音无声

  1. CODEC 端通过信号发生器生成 1k0 波形输入。
  2. 使用 arecord 或者 tinycap 录音,定位问题是发生在用户态还是内核态。
  3. 录音等待10秒以上确认是否为 I/O error 问题。
  4. 使用 amixer 或者 tinymix 检查 CODEC 内部 ADC 通路是否打开,音量是否静音。
  5. 查看寄存器配置,配合芯片手册或者 CODEC 手册确认配置是否正确:IOMUX,DAI,CODEC。
  6. 使用万用表和示波器测量电压,时钟,数据。确认电压,时钟正常,数据线上有波形;测量 CODEC 近端模拟输入信号是否正常,逐级定位问题点。

4.6 录音失真

  1. CODEC 端通过信号发生器生成 1k0 波形输入。
  2. 使用 arecord 或者 tinycap 录音,通过 loopback 输出,使用示波器测量;或者通过 PC 工具分析。
  3. 调整数字或者模拟增益,观察 loopback 波形,对比指标测试数据是否相符。
  4. 逐级检查引入失真的节点。

4.7 速率过快或者过慢

  1. 查看 clk summary 确认时钟(MCLK, BCLK, LRCK)是否准确。
  2. 使用示波器确认时钟信号是否准确。
  3. 使用 PC 工具录制输出信号,通过波形或者频谱分析,确认数据是否丢失或者增加。
  4. 使用逻辑分析仪 dump 芯片输出端数据,确认数据是否丢失或者增加。

4.8 规律性断音

规律性的断音问题通常发生于异源系统中,比如:UAC 应用场景,BT 语音应用场景,网络音频推流等。根本原因为时钟是异步的,随着时间的推移出现累积误差。该类问题可通过音频时钟补偿解决。buffer 边界的处理异常也会导致规律性的断音,杂音。可通过 xrun profiling 分析问题。

4.9 杂音

引起杂音的问题很多,如下列举常用定位方法:

  1. 确认时钟信号是否准确,检查 jitter 是否过大,比如,对于 HDA 音频,jitter 小于 0.5 ns。
  2. 确认时钟上是否有毛刺,特别是在边沿有效值判定范围电压内,如果出现毛刺,会被芯片识别为时钟,导致时序出现问题。RK 平台的 IO 具备 Smitter 触发器功能(支持情况请查阅芯片手册,比如 RV1126 上),可以有效滤除毛刺。解决由于硬件随机毛刺导致的杂音问题。
  3. 确认 CODEC 电源和地情况,CODEC 对电源噪声敏感,任何耦合进电源或地的噪声都将导致 CODEC 性能下降,底噪增大,出现杂音。
  4. 硬件采用差分电路抑制共模噪声。
  5. 检查硬件 PCB 布局,排查噪声来源。

4.10 XRUN

内核 xrun 参照 xrun profiling 章节,如下列举其他分析点:

  1. 应用端使用多线程编程,拆分读写线程与音频处理线程,确保与内核的数据交互不被打断。
  2. 系统调度问题,通过 systrace 或者 ftrace 抓取分析。
  3. 存储 IO 阻塞,导致数据读取阻塞,使用 ram 设备文件(比如:/dev/zero)进行播放,问题是否消失。
  4. 时钟是否不准确,导致数据消耗过快,可参照问题 速率过快或者过慢,规律性断音。

4.11 I/O error

播放录音10秒以上无响应时内核 log 打印:playback/capture write error (DMA or IRQ trouble?

  1. 通过寄存器查询 DAI 控制器 FIFO COUNT寄存器,是否已经停止计数。
  2. 通过 procfs 查看 dma 中断是否更新:cat /proc/interrupts | grep dma
  3. 通过寄存器查询 DMA 控制器 channel status 寄存器(偏移地址:0x100)是否异常停止。
  4. DAI 工作于 slave 时,请确保外设时钟在 DAI 的整个生命周期持续提供,如果无法保证,请保持常开。