Rockchip平台声卡适配完全指南:从拿到CODEC手册到Android出声

电子说

1.4w人已加入

描述

 

 

你刚拿到一块新硬件,上面焊了一颗从来没用过的 CODEC(比如 ES8388RT5645、或者某颗国产芯片)。硬件同事说线都连好了,现在轮到软件来让它出声。

从哪里开始?要改哪些文件?怎么验证?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 IOCODEC 的 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 驱动至少包含:

1DAI 驱动定义—— 声明播放 录制能力:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
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,};

2DAI 操作函数—— 至少实现 hw_paramsset_fmtset_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,};

3DAPM 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" },};

4I2C 驱动注册—— 让内核能通过 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";        #sound-dai-cells = <0>;             /* 固定为 0 */        /* 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>;    #sound-dai-cells = <0>;    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% 的适配问题都不会出现。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分