
目录
-
- [一、I2S 音频架构](#一、I2S 音频架构)
-
- [1.1 RV1126 I2S 控制器](#1.1 RV1126 I2S 控制器)
- [1.2 音频系统架构](#1.2 音频系统架构)
- 二、设备树配置
-
- [2.1 I2S 控制器节点](#2.1 I2S 控制器节点)
- [2.2 ES8311 Codec 节点](#2.2 ES8311 Codec 节点)
- [2.3 Sound Card 节点](#2.3 Sound Card 节点)
- [三、I2S 驱动配置](#三、I2S 驱动配置)
-
- [3.1 I2S Platform 驱动](#3.1 I2S Platform 驱动)
- [3.2 I2S DMA 配置](#3.2 I2S DMA 配置)
- [四、ES8311 Codec 驱动](#四、ES8311 Codec 驱动)
-
- [4.1 Codec 初始化](#4.1 Codec 初始化)
- [4.2 音频路由配置](#4.2 音频路由配置)
- [五、ALSA 配置](#五、ALSA 配置)
-
- [5.1 ALSA 配置文件](#5.1 ALSA 配置文件)
- [5.2 测试音频](#5.2 测试音频)
- 六、常见问题排查
-
- [6.1 无音频输出](#6.1 无音频输出)
- [6.2 音频噪音](#6.2 音频噪音)
- [6.3 采样率不支持](#6.3 采样率不支持)
- 七、性能优化
-
- [7.1 DMA 传输优化](#7.1 DMA 传输优化)
- [7.2 音频延迟优化](#7.2 音频延迟优化)
- 八、参考资料
RV1126 支持多种音频接口,包括 I2S(Integrated Inter-IC Sound)、PCM、TDM 等。ES8311 是 Everest 推出的低功耗立体声编解码器,广泛应用于嵌入式音频场景。
本文以 RV1126 连接 ES8311 编解码器为例,详细讲解 I2S 音频驱动开发、设备树配置、ALSA 配置等。
一、I2S 音频架构
1.1 RV1126 I2S 控制器
| 控制器 | 通道数 | 采样率 | 格式 |
|---|---|---|---|
| I2S0 | 2/4/6/8 | 8kHz-192kHz | I2S/PCM/TDM |
| I2S1 | 2 | 8kHz-96kHz | I2S/PCM/TDM |
1.2 音频系统架构
┌─────────────────────────────────────┐
│ Application (ALSA Lib) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ ALSA Core (ASoC Framework) │
│ ┌──────────────────────────────┐ │
│ │ CPU DAI (Platform Driver) │ │
│ │ - I2S Controller │ │
│ │ - DMA Engine │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Codec DAI (Codec Driver) │ │
│ │ - ES8311 Control │ │
│ │ - ADC/DAC │ │
│ └──────────────────────────────┘ │
└──────────────┬──────────────────────┘
│
┌───────▼───────┐
│ I2S Bus │
└───────┬───────┘
│
┌───────▼───────┐
│ ES8311 │
│ (ADC/DAC) │
└───────┬───────┘
│
┌───────▼───────┐
│ Speaker/MIC │
└───────────────┘
二、设备树配置
2.1 I2S 控制器节点
dts
// arch/arm64/boot/dts/rockchip/rv1126.dtsi
&i2s0_2ch {
status = "okay";
// I2S 配置
rockchip,clk-trcm = <1>; // TRCM (Transmit Clock Master)
#sound-dai-cells = <0>;
// DMA 配置
dmas = <&dmac0 3>, <&dmac0 4>;
dma-names = "tx", "rx";
// 时钟配置
clocks = <&cru SCLK_I2S0_2CH_SRC>, <&cru HCLK_I2S0_2CH>;
clock-names = "mclk", "hclk";
// 引脚配置
pinctrl-names = "default";
pinctrl-0 = <&i2s0_2ch_mclk
&i2s0_2ch_sclktx
&i2s0_2ch_sclkrx
&i2s0_2ch_lrcktx
&i2s0_2ch_lrckrx
&i2s0_2ch_sdi0
&i2s0_2ch_sdo0>;
};
2.2 ES8311 Codec 节点
dts
// rv1126-es8311.dts
&i2c0 {
status = "okay";
// ES8311 Codec
es8311: es8311@11 {
compatible = "everest,es8311";
reg = <0x11>; // I2C 地址
#sound-dai-cells = <0>;
// 电源配置
AVDD-supply = <&vcc_3v3>; // 模拟电源 3.3V
DVDD-supply = <&vcc_1v8>; // 数字电源 1.8V
CPVDD-supply = <&vcc_1v8>; // 电荷泵电源 1.8V
// GPIO 控制
hp-det-gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_LOW>; // 耳机检测
hp-amp-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_HIGH>; // 耳放控制
// 时钟配置
clocks = <&cru MCLK_I2S0_2CH>;
clock-names = "mclk";
assigned-clocks = <&cru MCLK_I2S0_2CH>;
assigned-clock-rates = <12288000>; // 12.288MHz
status = "okay";
};
};
2.3 Sound Card 节点
dts
// rv1126-sound.dts
&sound {
status = "okay";
compatible = "simple-audio-card";
simple-audio-card,name = "RV1126-ES8311";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <256>; // MCLK = 256 * SampleRate
simple-audio-card,widgets =
"Microphone",
"Headphone";
simple-audio-card,routing =
"Headphone" "HP",
"Mic Jack" "Mic";
// CPU DAI
simple-audio-card,cpu {
sound-dai = <&i2s0_2ch>;
};
// Codec DAI
simple-audio-card,codec {
sound-dai = <&es8311>;
};
};
三、I2S 驱动配置
3.1 I2S Platform 驱动
c
// sound/soc/rockchip/rockchip_i2s.c
static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int val = 0;
unsigned int mclk_rate, div_bclk, div_lrck;
// 获取采样率
mclk_rate = clk_get_rate(i2s->mclk);
// 计算 BCLK(Bit Clock)
// BCLK = SampleRate * Channels * BitDepth
div_bclk = mclk_rate / params_rate(params) /
params_channels(params) /
snd_pcm_format_width(params_format(params));
// 计算 LRCK(LR Clock)
div_lrck = div_bclk / 2;
// 配置分频器
writel(CLR_I2S_CLK_DIV_BCLK(div_bclk - 1),
i2s->regs + I2S_CLK_DIV);
writel(CLR_I2S_CLK_DIV_LRCK(div_lrck - 1),
i2s->regs + I2S_CLK_DIV);
// 配置格式
if (params_channels(params) == 2)
val |= I2S_TXCR_VDW(16); // 16-bit 数据宽度
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
val |= I2S_TXCR_VDW(16);
writel(val, i2s->regs + I2S_TXCR);
dev_info(i2s->dev, "I2S hw_params: rate=%d, channels=%d, format=%d\n",
params_rate(params), params_channels(params),
params_format(params));
return 0;
}
3.2 I2S DMA 配置
c
static int rockchip_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct rk_i2s_dev *i2s = dev_get_drvdata(component->dev);
// 设置缓冲区大小
snd_soc_set_runtime_hwparams(substream, &rk_pcm_hardware);
// 设置约束
runtime->hw.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE;
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE;
runtime->hw.rates = SNDRV_PCM_RATE_8000_192000;
runtime->hw.channels_min = 1;
runtime->hw.channels_max = 8;
return 0;
}
四、ES8311 Codec 驱动
4.1 Codec 初始化
c
// sound/soc/codecs/es8311.c
static int es8311_codec_probe(struct snd_soc_component *component)
{
struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
int ret;
// 读取芯片 ID
ret = regmap_read(es8311->regmap, ES8311_CHIP_ID_REG, &val);
if (ret || val != ES8311_CHIP_ID) {
dev_err(component->dev, "Failed to read CHIP_ID: 0x%02x\n", val);
return ret;
}
// 重置 Codec
regmap_write(es8311->regmap, ES8311_RESET_REG, 0x3F);
usleep_range(10000, 20000);
// 配置 ADC
regmap_write(es8311->regmap, ES8311_ADC_PGA_L, 0x28); // PGA 增益
regmap_write(es8311->regmap, ES8311_ADC_PGA_R, 0x28);
// 配置 DAC
regmap_write(es8311->regmap, ES8311_DAC_DSM1, 0x00); // 禁用 DSM
// 配置混音器
regmap_write(es8311->regmap, ES8311_SYS_VMID, 0x02); // VMID 使能
usleep_range(100000, 150000); // 100-150ms
dev_info(component->dev, "ES8311 codec initialized\n");
return 0;
}
4.2 音频路由配置
c
static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = {
// 输入
SND_SOC_DAPM_MIC("Mic Jack", NULL),
// 输出
SND_SOC_DAPM_HP("Headphone", NULL),
// Codec 内部
SND_SOC_DAPM_SUPPLY("MICBIAS", ES8311_PWR_MICBIAS, 0, 0, NULL, 0),
SND_SOC_DAPM_ADC("ADC", "Capture", ES8311_PWR_ADC, 0, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", ES8311_PWR_DAC, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPAMP", ES8311_PWR_HP, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route es8311_dapm_routes[] = {
// 输入路由
{"Mic Capture", NULL, "Mic Jack"},
{"Mic Capture", NULL, "MICBIAS"},
{"ADC", NULL, "Mic Capture"},
// 输出路由
{"Headphone", NULL, "HPAMP"},
{"HPAMP", NULL, "DAC"},
{"DAC", NULL, "Playback"},
};
五、ALSA 配置
5.1 ALSA 配置文件
bash
# /etc/asound.conf
pcm.!default {
type hw
card 0
device 0
}
ctl.!default {
type hw
card 0
}
# 音量控制
pcm.softvol {
type softvol
slave.pcm "dmix"
}
# 录音配置
pcm.capture {
type plug
slave {
pcm "hw:0,0"
format S16_LE
rate 44100
channels 2
}
}
5.2 测试音频
bash
# 播放测试
aplay /usr/share/sounds/alsa/Front_Center.wav
# 录音测试
arecord -f cd -t wav -d 10 test.wav
# 查看音量
amixer sget Master
amixer sset Master 80%
# 查看音频设备
aplay -l
arecord -l
六、常见问题排查
6.1 无音频输出
症状:aplay 播放文件无声音。
排查步骤:
- 检查 I2S 配置
bash
# 查看 I2S 控制器状态
cat /sys/class/sound/card0/id
# 应该输出:RV1126-ES8311
- 检查 Codec 寄存器
bash
# 使用 i2c-tools 读取 Codec 寄存器
i2cdump -y 1 0x11 0x20 # 读取前 32 个寄存器
解决方案:
dts
// 确保 I2S 时钟配置正确
&i2s0_2ch {
rockchip,clk-trcm = <1>; // 必须设置为 1(TX Master)
assigned-clocks = <&cru MCLK_I2S0_2CH>;
assigned-clock-rates = <12288000>; // 12.288MHz
};
6.2 音频噪音
症状:音频输出有明显的底噪或爆音。
排查步骤:
- 检查电源
bash
# 检查 AVDD 电压
cat /sys/class/regulator/vcc_3v3/microvolts
# 应该输出:3300000 (3.3V)
- 检查 MCLK 频率
bash
# 检查 MCLK 频率
cat /sys/kernel/debug/clk/mclk_i2s0_2ch/clk_rate
# 应该输出:12288000 (12.288MHz)
解决方案:
c
// 增加 VMID 启动延迟
static int es8311_codec_probe(struct snd_soc_component *component)
{
// VMID 启动
regmap_write(es8311->regmap, ES8311_SYS_VMID, 0x02);
usleep_range(300000, 400000); // 300-400ms(从 100ms 增加)
}
6.3 采样率不支持
症状:aplay 播放 48000Hz 音频失败。
排查步骤:
bash
# 查看支持的采样率
cat /proc/asound/card0/pcm0p/sub0/hw_params
解决方案:
c
// 确保 Codec 驱动支持 48000Hz
static struct snd_soc_dai_driver es8311_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000, // 确保支持 48000Hz
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
};
七、性能优化
7.1 DMA 传输优化
c
// 使用更大的 DMA 缓冲区
#define DMA_BUFFER_SIZE (64 * 1024) // 64KB
static int rockchip_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
// 设置缓冲区大小
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
return 0;
}
7.2 音频延迟优化
bash
# 减小 period 大小(降低延迟)
aplay -D plughw:0,0 -F 200000 -B 200000 test.wav
# -F: period size (us)
# -B: buffer size (us)
八、参考资料
- I2S 协议规范:https://www.nxp.com/docs/en/data-sheet/I2S.pdf
- ES8311 数据手册:https://www.everest-semi.com
- ALSA 文档:https://www.alsa-project.org