电子说
|
你刚拿到一块新硬件,上面焊了一颗从来没用过的 CODEC(比如 ES8388、RT5645、或者某颗国产芯片)。硬件同事说线都连好了,现在轮到软件来让它出声。 从哪里开始?要改哪些文件?怎么验证?Android 层还要做什么? 这篇文章给你一个完整的五步适配流程,以及每一步的详细操作和检查清单。跟着走,基本不会遗漏关键环节。 |
一、全局视角:五步流程图
① 硬件设计├── 确定 DAI 接口类型(I2S/PDM/SPDIF)├── 确定 CODEC 连接方式(I2C/SPI)└── 确认电源、GPIO、时钟布线│▼② 驱动准备├── 查内核是否已有驱动├── 没有的话编写 CODEC 驱动└── 确认 DAI 驱动已使能│▼③ DTS 配置├── 配置 CODEC 节点(I2C 地址、时钟、GPIO)├── 配置声卡节点(Simple Card / Multi Codecs)└── 配置 pinctrl(引脚复用)│▼④ 验证测试├── 检查声卡注册├── 测试播放/录制└── 调试问题│▼⑤ Android 适配(可选)├── 配置 Audio HAL 路由├── 配置 audio_policy XML└── 测试 Android 音频功能
下面每一步详细展开。
二、第一步:硬件设计—— 先确认这些再动笔
2.1 确定 DAI 接口
根据 CODEC datasheet 确定它支持什么接口:
|
CODEC 接口 |
SoC 侧对接 |
需要的信号线 |
|
I2S |
I2S/SAI |
BCLK, LRCK, SDI, SDO, MCLK |
|
PDM |
PDM |
PDM_CLK, PDM_DATA |
|
SPDIF |
SPDIF TX/RX |
SPDIF 信号线 |
关键:MCLK 一定要连上,大部分 CODEC 需要 MCLK 作为内部 PLL 参考时钟。
2.2 确定 I2C 地址
外部 CODEC 通常通过 I2C 配置寄存器:
需要确认:├── CODEC 的 7 位 I2C 地址(如 0x11)├── 接在哪条 I2C 总线上(如 i2c0)└── 总线上是否需要上拉电阻(通常 4.7kΩ)
I2C 地址一般通过 CODEC 的 ADDR 引脚电平决定,硬件设计时就要确定,别等软件调试时才发现地址对不上。
2.3 电源和 GPIO
确认 CODEC 需要的电源:
├── AVDD(模拟电源,通常是 3.3V 或 1.8V)├── DVDD(数字电源)├── IOVDD(I/O 电源,决定 I2C 和 DAI 的电平)├── RESET(复位 GPIO,低电平有效还是高电平有效?)└── PDN(掉电控制 GPIO,可选)
特别注意:IOVDD 的电平要和 SoC 的 I2C/DAI 电平匹配。如果 SoC 是 3.3V IO,CODEC 的 IOVDD 也要供 3.3V,否则可能通信失败。
三、第二步:驱动准备—— 别重复造轮子
3.1 先搜一下内核里有没有现成驱动
# 搜索 CODEC 驱动find kernel/sound/soc/codecs/ -name "*.c" | xargs grep -l "es8388|rt5645|tas5731"# 检查 Kconfiggrep -i "ES8388|RT5645" kernel/sound/soc/codecs/Kconfig
如果内核里已经有驱动,确认以下配置开启:
# 检查 defconfiggrep -E 'SND_SOC_ES8388|SND_SOC_RT5645' kernel/arch/arm64/configs/rockchip_defconfig
3.2 如果内核没有驱动,需要写哪些代码?
一个最小的 CODEC 驱动至少包含:
(1)DAI 驱动定义—— 声明播放 / 录制能力:
static const struct snd_soc_dai_driver my_codec_dai = {.name = "my-codec-hifi",.playback = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,},.capture = {.stream_name = "Capture",.channels_min = 1,.channels_max = 2,.rates = SNDRV_PCM_RATE_8000_48000,.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,},.ops = &my_codec_dai_ops,};
(2)DAI 操作函数—— 至少实现 hw_params、set_fmt、set_sysclk:
static const struct snd_soc_dai_ops my_codec_dai_ops = {.hw_params = my_codec_hw_params,.set_fmt = my_codec_set_fmt,.set_sysclk = my_codec_set_sysclk,};
(3)DAPM Widgets 和路由—— 定义芯片内部的音频通路:
static const struct snd_soc_dapm_widget my_codec_dapm_widgets[] = {SND_SOC_DAPM_DAC("DAC", "Playback", REG, BIT, 0),SND_SOC_DAPM_ADC("ADC", "Capture", REG, BIT, 0),SND_SOC_DAPM_OUTPUT("HP"),SND_SOC_DAPM_INPUT("MIC"),};static const struct snd_soc_dapm_route my_codec_dapm_routes[] = {{ "HP", NULL, "DAC" },{ "ADC", NULL, "MIC" },};
(4)I2C 驱动注册—— 让内核能通过 I2C 找到这颗芯片:
static const struct i2c_device_id my_codec_i2c_id[] = {{ "my-codec", 0 },{}};static struct i2c_driver my_codec_i2c_driver = {.driver = { .name = "my-codec" },.probe = my_codec_i2c_probe,.id_table = my_codec_i2c_id,};module_i2c_driver(my_codec_i2c_driver);
3.3 确认 DAI 驱动已使能
zcat /proc/config.gz | grep -E 'SND_SOC_ROCKCHIP_I2S|SND_SOC_ROCKCHIP_SAI'
如果没有开启,在rockchip_defconfig 里加上对应的配置项,重新编译内核。
四、第三步:DTS 配置 —— 最核心的一步
4.1 配置 CODEC 节点
&i2c0 {status = "okay";es8388: es8388@11 {compatible = "everest,es8388";reg = <0x11>; /* I2C 地址 */clocks = <&cru I2S_MCLKOUT>; /* MCLK 时钟源 */clock-names = "mclk";/* GPIO 配置(可选) */reset-gpios = <&gpio3 RK_PA2 GPIO_ACTIVE_LOW>;/* 电源配置(可选) */AVDD-supply = <&vcc_audio>;DVDD-supply = <&vcc_audio>;};};
字段含义:
|
属性 |
含义 |
|
compatible |
匹配 CODEC 驱动的字符串,必须和驱动里的一致 |
|
reg |
I2C 7 位地址 |
|
clocks |
MCLK 时钟源 |
|
#sound-dai-cells |
固定<0> |
|
reset-gpios |
复位引脚(可选) |
|
*-supply |
电源 regulator(可选) |
4.2 配置声卡节点(Simple Card)
/ {sound {compatible = "simple-audio-card";simple-audio-card,name = "rockchip-es8388";simple-audio-card,format = "i2s";simple-audio-card,mclk-fs = <256>;/* CPU 为时钟主方 */simple-audio-card,bitclock-master = <&cpu_dai>;simple-audio-card,frame-master = <&cpu_dai>;/* DAPM 路由 */simple-audio-card,audio-routing ="Headphone", "HPOL","Headphone", "HPOR","Main Mic", "MICBIAS";simple-audio-card,cpu {sound-dai = <&i2s0>;};simple-audio-card,codec {sound-dai = <&es8388>;};};};
4.3 配置 I2S/SAI 控制器
&i2s0 {status = "okay";rockchip,grf = <&grf>;pinctrl-names = "default";pinctrl-0 = <&i2s0_sclk&i2s0_lrck&i2s0_sdi0&i2s0_sdo0&i2s0_mclk>;};
pinctrl 必须包含所有用到的引脚,漏一个就会导致部分信号没有输出。
4.4 TDM 模式配置(如需要)
simple-audio-card,cpu {sound-dai = <&i2s0>;dai-tdm-slot-num = <8>; /* 8 个 Slot */dai-tdm-slot-width = <32>; /* 每个 Slot 32 bit */};
4.5 PDM 麦克风配置(无需外部 CODEC)
&pdm_8ch {status = "okay";rockchip,path-map = <3 2 1 0>; /* SDI 路径映射 */pinctrl-names = "default";pinctrl-0 = <&pdm_8ch_clk &pdm_8ch_data0 ...>;};
五、第四步:验证测试—— 一步步确认
5.1 检查声卡注册
# 重启后检查cat /proc/asound/cards# 应该看到 "rockchip-es8388" 声卡# 检查内核日志dmesg | grep -E 'simple-card|snd_soc|es8388'
5.2 测试播放
# Linuxaplay -D hw:0,0 -r 48000 -c 2 -f S16_LE /usr/share/sounds/alsa/Front_Center.wav# Androidtinyplay /sdcard/test.wav -D 0 -d 0
5.3 测试录制
# Linuxarecord -D hw:0,0 -r 16000 -c 2 -f S16_LE -d 5 test.wav# Androidtinycap /sdcard/record.wav -D 0 -d 0 -c 2 -r 16000 -b 16
5.4 控件操作测试
# 列出所有控件amixer -c 0 contents# 设置播放路径amixer -c 0 sset 'Playback Path' SPK# 设置音量amixer -c 0 sset 'DAC Volume' 175
六、第五步:Android 适配 —— 容易被忽视的最后一步
6.1 配置 Audio HAL 路由
在hardware/rockchip/audio/tinyalsa_hal/codec_config/ 下创建路由配置:
// es8388_config.hstatic struct route_info es8388_routes[] = {// 扬声器播放{.card_name = "rockchip-es8388",.output_type = AUDIO_DEVICE_OUT_SPEAKER,.controls = {{"DAC Volume", 175},{"Playback Path", "SPK"},},},// 耳机播放{.card_name = "rockchip-es8388",.output_type = AUDIO_DEVICE_OUT_WIRED_HEADPHONE,.controls = {{"DAC Volume", 175},{"Playback Path", "HP"},},},// 麦克风录制{.card_name = "rockchip-es8388",.input_type = AUDIO_DEVICE_IN_BUILTIN_MIC,.controls = {{"ADC Volume", 175},{"Capture Path", "Main Mic"},},},};
6.2 配置 audio_policy
在device/rockchip/common/audio_policy_volumes_drc.xml 中配置:
<audioPolicyConfiguration><modules><module name="primary" halVersion="2.0"><attachedDevices><item>Speakeritem><item>Built-In Micitem>attachedDevices><defaultOutputDevice>SpeakerdefaultOutputDevice>module>modules>audioPolicyConfiguration>
6.3 声卡名称匹配 —— 最容易踩的坑
DTS 里的声卡名称:rockchip-es8388
Audio HAL 里匹配的名称:rockchipes8388c
看到了吗?不是简单的复制粘贴!Android Audio HAL 里的声卡名称有一套转换规则:
|
DTS 名称 |
HAL 名称 |
|
rockchip-es8388 |
rockchipes8388c |
|
rockchip-hdmi |
rockchiphdmic |
|
realtek,rt5651-codec |
realtekrt5651co |
转换规则:去掉连字符和逗号,保留字母和数字,末尾可能补一个字母。
如果 HAL 里的名称和 DTS 不匹配,Android 根本找不到你的声卡,播放会走默认路由或者完全无声。
七、完整检查清单
在提交适配代码之前,逐项确认:
硬件检查
□I2S/PDM 信号线连接正确
□MCLK 已连接到 CODEC
□I2C 总线已连接,上拉电阻正确
□CODEC 电源(AVDD/DVDD/IOVDD)已正确连接
□RESET/PDN GPIO 已正确连接
软件检查
□内核配置中使能了 DAI 驱动和 CODEC 驱动
□DTS 中 CODEC 节点的 compatible/reg/clocks 正确
□DTS 中声卡节点的 sound-dai phandle 正确
□DTS 中 pinctrl 配置包含所有引脚
□DAPM 路由定义正确
功能检查
□声卡在/proc/asound/cards 中可见
□/dev/snd/ 下有对应的设备节点
□aplay/tinyplay 播放正常
□arecord/tinycap 录制正常
□amixer/tinymix 控件操作正常
□耳机插入检测正常(如适用)
□ADC 按键正常(如适用)
Android 检查
□Audio HAL 路由配置正确
□audio_policy XML 配置正确
□声卡名称匹配正确(注意转换规则!)
□Media 播放正常
□通话录音正常(如适用)
八、总结
Rockchip 平台声卡适配的核心步骤:
1.硬件设计:确定 DAI 类型、I2C 连接、电源 GPIO
2.驱动准备:先搜内核是否已有驱动,没有再写
3.DTS 配置:CODEC 节点 + 声卡节点 + pinctrl,三步缺一不可
4.验证测试:从声卡注册到播放录制,逐层确认
5.Android 适配:HAL 路由 + audio_policy + 声卡名称匹配
最关键的三件事:
•compatible 字符串前后一致(DTS 驱动)
•sound-dai phandle 指向正确
•Android HAL 里的声卡名称按规则转换
把这三件事做对,80% 的适配问题都不会出现。
全部0条评论
快来发表一下你的评论吧 !