title: Linux音频驱动开发 tags:
音频系统需要将声音信号转换为数字信号(ADC)或将数字信号转换为声音信号(DAC)。虽然可以使用独立的ADC和DAC芯片,但专业音频CODEC芯片集成了更多功能:
| 功能 | 说明 |
|---|---|
| ADC/DAC | 模数/数模转换 |
| DSP单元 | 音效处理、降噪 |
| 标准接口 | I2S、I2C等统一接口 |
| 功率放大 | 内置耳机/喇叭驱动 |
| 混音器 | 多路音频信号混合 |
音频CODEC主要性能指标:
WM8960是Wolfson公司的低功耗立体声音频CODEC,主要特性:
┌─────────────────────────────────────────────────────────────┐
│ WM8960 主要特性 │
├─────────────────────────────────────────────────────────────┤
│ ADC参数: │
│ - SNR: 94dB (3.3V, 48KHz) │
│ - THD: -82dB │
│ - 24位高精度 │
├─────────────────────────────────────────────────────────────┤
│ DAC参数: │
│ - SNR: 98dB (3.3V, 48KHz) │
│ - THD: -84dB │
│ - 24位高精度 │
├─────────────────────────────────────────────────────────────┤
│ 功能特性: │
│ - 3D增强效果 │
│ - 立体声D类功放(8Ω负载下每通道1W) │
│ - 集成耳机接口 │
│ - 集成麦克风接口 │
│ - 采样率支持8K/11.025K/12K/16K/22.05K/24K/32K/44.1K/48K │
└─────────────────────────────────────────────────────────────┘
WM8960引脚功能:
ES8388是上海屹恒微电子的低功耗立体声CODEC,主要特性:
| 模块 | 特性 |
|---|---|
| ADC | 24位,8K~96K采样率,95dB动态范围 |
| DAC | 24位,8K~96K采样率,96dB动态范围 |
| 功耗 | 播放7mW,录放同步16mW |
| 接口 | I2C/SPI配置,I2S/DSP/PCM音频格式 |
| 驱动 | 40mW耳机驱动,免耦电容输出 |
I2S(Inter-IC Sound)是飞利浦公司提出的数字音频传输总线,需要3根信号线(全双工需要4根):
| 信号线 | 名称 | 功能 |
|---|---|---|
| SCK/BCLK | 位时钟 | 每个数据位对应一个时钟脉冲 |
| WS/LRCK | 帧时钟 | 切换左右声道,频率=采样率 |
| SD/DATA | 串行数据 | 实际音频数据 |
| MCLK | 主时钟 | 系统同步时钟,通常为256fs或384fs |
时钟计算公式:
BCLK = 2 × 采样率 × 采样位数
例如:44.1KHz、16位立体声
BCLK = 2 × 44100 × 16 = 1.4112MHz
I2S时序图:
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
WS/LRCK│ │ │ │ │ │ │ │ │ │
└───┘ └───┘ └───┘ └───┘ └───┘
←─── LEFT ───→←─── RIGHT ───→
┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐
BCLK │└┐│└┐│└┐│└┐│└┐│└┐│└┐│└┐│└┐│└┐│└┐│└┐│
└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘
DATA D15 D14 D13 ... D1 D0 D15 D14 ... D1 D0
←─ LEFT ─→ ←─ RIGHT ─→
I2S数据格式:
SAI(Synchronous Audio Interface)是I.MX6ULL的同步音频接口,主要特性:
| 特性 | 参数 |
|---|---|
| 工作模式 | 全双工、帧同步串行接口 |
| 支持协议 | I2S、AC97、TDM、音频DSP |
| 帧长度 | 最大32个字 |
| 字大小 | 8bit或32bit |
| FIFO深度 | 每个收发通道32×32bit |
| 错误处理 | FIFO错误后支持平滑重启 |
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。原书第六十五章未讲解 ASoC 框架内部结构,本实验直接使能内核自带的 WM8960/ES8388 驱动。
ALSA(Advanced Linux Sound Architecture)是Linux内核的标准音频框架:
graph TB
subgraph "用户空间"
APP[应用程序] --> ALSALIB[alsa-lib]
ALSALIB --> PCM[PCM设备]
ALSALIB --> CTL[Control设备]
end
subgraph "内核空间"
PCM --> SOC[ASoC框架]
CTL --> SOC
subgraph "ASoC三驱动架构"
SOC --> CPU[CPU DAI驱动]
SOC --> CODEC[Codec驱动]
SOC --> MACHINE[Machine驱动]
end
CPU --> SAI[SAI控制器]
CODEC --> WM[WM8960/ES8388]
MACHINE --> GLUE[音频路由 glue]
end
SAI <--> I2S[I2S总线]
I2S <--> WM
ASoC(ALSA System on Chip)将音频驱动分为三个独立组件:
| 组件 | 作用 | 典型文件 |
|---|---|---|
| CPU DAI | SoC音频接口驱动 | fsl_sai.c |
| Codec | 音频编解码器驱动 | wm8960.c、es8388.c |
| Machine | 板级音频路由配置 | imx-audio-wm8960.c |
这种架构的优势:
PCM(Pulse-Code Modulation)设备用于音频数据的播放和录制:
/dev/snd/pcmC0D0p # 声卡0 设备0 播放(playback)
/dev/snd/pcmC0D0c # 声卡0 设备0 录音(capture)
/dev/snd/pcmC0D1p # 声卡0 设备1 播放
Control设备用于配置声卡参数(音量、开关等):
/dev/snd/controlC0 # 声卡0 控制设备
可通过amixer工具操作:
amixer scontrols # 查看所有简单控制项
amixer scontents # 查看控制项当前值
amixer sset 'Headphone' 100 # 设置耳机音量
amixer sget 'Headphone' # 获取耳机音量
| 结构体 | 作用 |
|---|---|
snd_card |
声卡主结构体,包含所有音频设备 |
snd_pcm |
PCM设备结构体 |
snd_pcm_hardware |
PCM硬件参数定义 |
snd_soc_dai_link |
DAI链接配置 |
snd_soc_card |
声卡配置结构体 |
I.MX6ULL使用SAI2连接音频Codec:
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
<&clks IMX6UL_CLK_SAI2>;
assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <11289600>;
status = "okay";
};
说明(原书 65.2/65.3.2):WM8960 所挂 I2C 总线随底板版本不同——V2.4 以前挂 I2C2,V2.4 及以后挂 I2C1;ES8388 始终挂 I2C1。V2.4/V2.8 及以后的
sound节点通过audio-codec = <&codec1 &codec2>同时引用两个 WM8960 子节点。
&i2c1 {
codec1: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>;
clock-names = "mclk";
wlf,shared-lrclk;
};
};
&i2c2 {
codec2: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>;
clock-names = "mclk";
wlf,shared-lrclk;
};
};
&i2c1 {
codec: es8388@11 {
compatible = "alientek,es8388";
reg = <0x11>;
clock-names = "mclk";
clocks = <&clks IMX6UL_CLK_SAI2>;
hp-det-gpio = <&gpio5 4 GPIO_ACTIVE_HIGH>;
AVDD-supply = <®_audio_pwr>;
DVDD-supply = <®_audio_pwr>;
HPVDD-supply = <®_audio_pwr>;
PVDD-supply = <®_audio_pwr>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_es8388_hp_det_b>;
};
};
/ {
sound {
compatible = "fsl,imx6ul-evk-wm8960",
"fsl,imx-audio-wm8960";
model = "wm8960-audio";
cpu-dai = <&sai2>;
audio-codec = <&codec1 &codec2>;
asrc-controller = <&asrc>;
codec-master;
gpr = <&gpr 4 0x100000 0x100000>;
hp-det = <3 0>;
audio-routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
"Ext Spk", "SPK_LP",
"Ext Spk", "SPK_LN",
"Ext Spk", "SPK_RP",
"Ext Spk", "SPK_RN",
"LINPUT2", "Mic Jack",
"LINPUT3", "Mic Jack",
"RINPUT1", "Main MIC",
"RINPUT2", "Main MIC",
"Mic Jack", "MICB",
"Main MIC", "MICB",
"CPU-Playback", "ASRC-Playback",
"Playback", "CPU-Playback",
"ASRC-Capture", "CPU-Capture",
"CPU-Capture", "Capture";
};
};
/ {
regulators {
reg_audio_pwr: regulator-audio-pwr {
compatible = "regulator-fixed";
regulator-name = "audio-pwr";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
enable-active-high;
};
};
sound-es8388 {
compatible = "fsl,imx-audio-es8388";
model = "es8388-audio";
audio-cpu = <&sai2>;
audio-codec = <&codec>;
audio-asrc = <&asrc>;
gpr = <&gpr 4 0x100000 0x100000>;
audio-routing =
"Headphone Jack", "LOUT1",
"Headphone Jack", "ROUT1",
"Ext Spk", "LOUT2",
"Ext Spk", "ROUT2",
"LINPUT1", "Line In Jack",
"RINPUT1", "Line In Jack",
"LINPUT2", "Mic Jack",
"RINPUT2", "Mic Jack",
"Mic Jack", "Mic Bias",
"CPU-Playback", "ASRC-Playback",
"Playback", "CPU-Playback",
"ASRC-Capture", "CPU-Capture",
"CPU-Capture", "Capture";
};
};
&iomuxc {
imx6ul-evk {
pinctrl_sai2: sai2grp {
fsl,pins = <
MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088
MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088
MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088
MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088
MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088
>;
};
};
};
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。原书第六十五章仅介绍“使能内核自带音频驱动”,未涉及自己编写 ASoC/ALSA 驱动所需的注册 API。
// 创建声卡
struct snd_card *snd_card_new(struct device *parent,
const char *id,
struct module *owner,
int extra_size);
// 注册声卡
int snd_card_register(struct snd_card *card);
// 释放声卡
void snd_card_free(struct snd_card *card);
// 创建PCM设备
int snd_pcm_new(struct snd_card *card,
const char *id,
int device,
int playback_count,
int capture_count,
struct snd_pcm **rpcm);
// 设置PCM硬件参数
int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm,
const struct snd_pcm_hardware *hw,
struct device *dev,
size_t size);
// 注册声卡(Machine驱动入口)
int snd_soc_register_card(struct snd_soc_card *card);
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card);
// 注册DAI驱动
int snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv);
// 注册Codec/CPU DAI组件(cmpnt_drv 提供 probe/remove/controls/dapm 等)
int snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai);
int devm_snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai);
关键结构体:
snd_soc_component_driver:组件(Codec/CPU DAI/Platform)通用回调集,含 .controls、.dapm_widgets、.dapm_routes、.probe、.remove 等。snd_soc_dai_ops:DAI 级操作集,核心回调为 .hw_params(配置采样率、位宽、时钟)、.set_fmt(设置 I2S 格式与主从)、.set_sysclk、.trigger、.startup、.shutdown。snd_soc_dai_driver 通过 .ops 指向一个 snd_soc_dai_ops,通过 .playback/.capture 描述 snd_soc_pcm_stream(声道数、采样率、格式)。⚠️ 来源说明:本节大部分内容不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。原书第六十五章只在 65.5.3 小节摘录了
wm8960.c的wm8960_reg_defaults数组用于修改单声道 MIC 录音,并未展开分析imx-audio-wm8960.c/fsl_sai.c的源码。下表中 5.3 的寄存器数组片段对应原书内容,其余为扩展。
Machine驱动是连接CPU DAI和Codec的胶水层:
// imx-audio-wm8960.c 关键结构体
static struct snd_soc_dai_link imx_wm8960_dai = {
.name = "HiFi",
.stream_name = "HiFi",
.cpu_dai_name = "sai2",
.codec_dai_name = "wm8960-hifi",
.platform_name = "fsl-sai",
.codec_name = "1-001a",
.init = imx_wm8960_init,
.ops = &imx_wm8960_hifi_ops,
};
static struct snd_soc_card imx_wm8960_card = {
.name = "imx6ul-evk",
.owner = THIS_MODULE,
.dai_link = &imx_wm8960_dai,
.num_links = 1,
.dapm_widgets = imx_wm8960_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(imx_wm8960_dapm_widgets),
.dapm_routes = imx_wm8960_routes,
.num_dapm_routes = ARRAY_SIZE(imx_wm8960_routes),
};
I.MX6ULL SAI驱动核心结构:
// fsl_sai.c 关键回调
static struct snd_soc_dai_driver fsl_sai_dai = {
.playback = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 32,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 32,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &fsl_sai_pcm_dai_ops,
};
WM8960 Codec驱动关键代码:
// wm8960.c 寄存器默认值(原书示例代码 65.5.3.2,共 R0~R55 计 56 个寄存器)
static const struct reg_default wm8960_reg_defaults[] = {
{ 0x0, 0x00a7 },
// ...
{ 0x16, 0x00c3 },
/*{ 0x17, 0x01c0 },*/ // 原默认值:左右声道各自使用本侧 ADC
{ 0x17, 0x01c4 }, // 修改后:左右声道数据均使用左 ADC
{ 0x18, 0x0000 },
// ...
{ 0x37, 0x00e9 }, // R55 Class-D Control 2
};
// DAI驱动
static struct snd_soc_dai_driver wm8960_dai = {
.name = "wm8960-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,
},
.ops = &wm8960_hifi_dai_ops,
};
// 音频路由定义
static const struct snd_kcontrol_new imx_wm8960_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Ext Spk"),
SOC_DAPM_PIN_SWITCH("Mic Jack"),
};
static const struct snd_soc_dapm_widget imx_wm8960_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", imx_hifi_hp_event),
SND_SOC_DAPM_SPK("Ext Spk", imx_hifi_spk_event),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
static const struct snd_soc_dapm_route imx_wm8960_routes[] = {
{ "Headphone Jack", NULL, "HP_L" },
{ "Headphone Jack", NULL, "HP_R" },
{ "Ext Spk", NULL, "SPK_LP" },
{ "Ext Spk", NULL, "SPK_LN" },
{ "Ext Spk", NULL, "SPK_RP" },
{ "Ext Spk", NULL, "SPK_RN" },
};
# 查看声卡信息
cat /proc/asound/cards
# 查看PCM设备
cat /proc/asound/pcm
# 查看所有控制项
amixer controls
# 查看简单控制项
amixer scontrols
# 查看控制值
amixer scontents
# 设置声卡参数
amixer sset Headphone 100,100
amixer sset Speaker 1 20,120
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
# 播放WAV文件
aplay test.wav
# 播放指定设备
aplay -D plughw:0,0 test.wav
# 查看播放信息
aplay -l
在开发板根文件系统的 /music 目录下创建 mic_in_config.sh(对应原书示例代码 65.5.3.1),完整内容如下:
#!/bin/sh
# 正点原子@ALIENTEK
# 设置捕获的音量
amixer cset name='Capture Volume' 90,90
# PCM
amixer sset 'PCM Playback' on
amixer sset 'Playback' 256
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
# ADC PCM
amixer sset 'ADC PCM' 200
# 耳机/喇叭(扬声器)设置播放音量,直流/交流
# Turn on Headphone
amixer sset 'Headphone Playback ZC' on
# Set the volume of your headphones(98% volume, 127 is the MaxVolume)
amixer sset Headphone 125,125
# Turn on the speaker
amixer sset 'Speaker Playback ZC' on
# Set the volume of your Speaker(98% volume, 127 is the MaxVolume)
amixer sset Speaker 125,125
# Set the volume of your Speaker AC(80% volume, 100 is the MaxVolume)
amixer sset 'Speaker AC' 4
# Set the volume of your Speaker DC(80% volume, 5 is the MaxVolume)
amixer sset 'Speaker DC' 4
# 音频输入,左声道管理
# Turn on Left Input Mixer Boost
amixer sset 'Left Input Mixer Boost' off
amixer sset 'Left Boost Mixer LINPUT1' off
amixer sset 'Left Input Boost Mixer LINPUT1' 0
amixer sset 'Left Boost Mixer LINPUT2' off
amixer sset 'Left Input Boost Mixer LINPUT2' 0
# Turn off Left Boost Mixer LINPUT3
amixer sset 'Left Boost Mixer LINPUT3' off
amixer sset 'Left Input Boost Mixer LINPUT3' 0
# 音频输入,右声道管理
# Turn on Right Input Mixer Boost
amixer sset 'Right Input Mixer Boost' on
amixer sset 'Right Boost Mixer RINPUT1' off
amixer sset 'Right Input Boost Mixer RINPUT2' 0
amixer sset 'Right Boost Mixer RINPUT2' on
amixer sset 'Right Input Boost Mixer RINPUT2' 127
amixer sset 'Right Boost Mixer RINPUT3' off
amixer sset 'Right Input Boost Mixer RINPUT3' 0
chmod 777 mic_in_config.sh # 给予可执行权限
./mic_in_config.sh # 运行
arecord -f cd -d 10 record.wav # 录制10秒CD质量音频
aplay record.wav # 录制完成后播放
说明:-f cd 表示录音质量为 CD 级别(16bit/44.1kHz/立体声),-d 指定录音时间(单位 s)。ALPHA 开发板的 MIC 只接了左声道,因此录出的音频只有左声道有数据。
要让只接左声道的 MIC 录出双声道,可修改 WM8960 的 R23 寄存器(地址 0x17)的 bit3:2,让左右声道 ADC 都使用左 ADC 数据。wm8960_reg_defaults 中 0x17 默认值为 0x01C0,改为 0x01C4 即可(详见第 8 节 Q4)。修改后需重新编译内核并启动。
Line IN 接口不能接话筒,需使用一根 3.5mm 公对公音频线连接手机/电脑与开发板 Line in 接口。
创建 line_in_config.sh(对应原书示例代码 65.5.4.2):
#!/bin/sh
# 正点原子@ALIENTEK
# 设置捕获的音量
amixer cset name='Capture Volume' 100,100
# PCM
amixer sset 'PCM Playback' on
amixer sset 'Playback' 256
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on
# ADC PCM
amixer sset 'ADC PCM' 200
# 耳机/喇叭(扬声器)设置播放音量,直流/交流
amixer sset 'Headphone Playback ZC' on
amixer sset Headphone 125,125
amixer sset 'Speaker Playback ZC' on
amixer sset Speaker 125,125
amixer sset 'Speaker AC' 4
amixer sset 'Speaker DC' 4
# 音频输入,左声道管理
# Turn off Left Input Mixer Boost
amixer sset 'Left Input Mixer Boost' on
# 关闭其他通道输入
amixer sset 'Left Boost Mixer LINPUT1' off
amixer sset 'Left Input Boost Mixer LINPUT1' 0
# 关闭麦克风左声道输入
amixer sset 'Left Boost Mixer LINPUT2' on
amixer sset 'Left Input Boost Mixer LINPUT2' 127
# Line_in 右声道输入关闭
amixer sset 'Left Boost Mixer LINPUT3' off
amixer sset 'Left Input Boost Mixer LINPUT3' 0
# 音频输入,右声道管理
# Turn on Right Input Mixer Boost
amixer sset 'Right Input Mixer Boost' on
amixer sset 'Right Boost Mixer RINPUT1' off
amixer sset 'Right Input Boost Mixer RINPUT1' 0
amixer sset 'Right Boost Mixer RINPUT2' off
amixer sset 'Right Input Boost Mixer RINPUT2' 0
# 要想设置成音频输入,请打开 RINPUT3,看原理图可知
# 其他的声道通过上面的配置可关闭,这样是为了避免干扰,需要的时候就打开
# RINPUT3 打开(关键点)
amixer sset 'Right Boost Mixer RINPUT3' on
amixer sset 'Right Input Boost Mixer RINPUT3' 127
chmod 777 line_in_config.sh
./line_in_config.sh
arecord -f cd -d 10 record.wav
ALPHA 开发板 Line IN 接的是左右双声道,因此录制出来是立体声,不像 MIC 只有左声道。若在 MIC 实验里把 R23 改为 0x01C4,做 Line IN 测试前应先改回 0x01C0(也可不改,不影响测试)。
出厂文件系统已预置音频测试脚本 /home/root/shell/audio/atk-audio.sh,直接在文件系统中执行 ./atk-audio.sh,按提示即可进行板载 MIC 录音测试和 Line IN 录音测试。
# 保存声卡设置
alsactl -f /var/lib/alsa/asound.state store
# 恢复声卡设置
alsactl -f /var/lib/alsa/asound.state restore
# 在/etc/init.d/rcS中添加开机恢复
if [ -f "/var/lib/alsa/asound.state" ]; then
echo "ALSA: Restoring mixer setting......"
/sbin/alsactl -f /var/lib/alsa/asound.state restore &
fi
# 取消OSS API
-> Device Drivers
-> Sound card support (SOUND=y)
-> Advanced Linux Sound Architecture (SND=y)
-> <> OSS Mixer API
-> <> OSS PCM (digital audio) API
# 使能WM8960驱动
-> Device Drivers
-> Sound card support (SOUND=y)
-> Advanced Linux Sound Architecture (SND=y)
-> ALSA for SoC audio support (SND_SOC=y)
-> SoC Audio for Freescale CPUs
-> <*> Asynchronous Sample Rate Converter (ASRC)
-> <*> SoC Audio support for i.MX boards with wm8960
# 使能ES8388驱动(V2.8及以后底板)
SND_SOC_FSL_ASOC_CARD = y # i.MX ASoC card 支持
SND_SOC_ES8388 = y # ES8388 codec 驱动
ES8388 使能后重新编译内核与设备树,启动后 ALSA 设备列表中应出现 es8388-audio(与设备树 sound-es8388 节点 model 一致的声卡名)。
源码下载地址 http://www.alsa-project.org/main/index.php/Main_Page,原书使用 1.2.2 版本(光盘路径:例程源码 → 第三方库源码 → alsa-lib-1.2.2.tar.bz2、alsa-utils-1.2.2.tar.bz2)。
关键:配置文件路径一致性。alsa-lib 编译时会生成含绝对路径的配置文件,因此 Ubuntu 与开发板根文件系统必须创建同名目录:
# Ubuntu
cd /usr/share
sudo mkdir arm-alsa
# 开发板根文件系统
mkdir /usr/share/arm-alsa -p
移植 alsa-lib:
tar -vxjf alsa-lib-1.2.2.tar.bz2
cd alsa-lib-1.2.2/
./configure --host=arm-linux-gnueabihf --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/alsa-lib --with-configdir=/usr/share/arm-alsa
make
sudo make install
若 libatopology.la 编译失败(sudo 切到 root 后找不到交叉编译器),先 sudo -s 切到 root,再 source /etc/profile,然后直接 make install(此时无需 sudo),完成后 su zuozhongkai 回到原用户。
编译结果:alsa-lib/lib 下文件拷贝到开发板 /usr/lib,/usr/share/arm-alsa 下文件拷贝到开发板 /usr/share/arm-alsa:
cd alsa-lib
sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/lib/ -af
cd /usr/share/arm-alsa
sudo cp * /home/zuozhongkai/linux/nfs/rootfs/usr/share/arm-alsa/ -raf
移植 alsa-utils:
tar -vxjf alsa-utils-1.2.2.tar.bz2
cd alsa-utils-1.2.2/
./configure --host=arm-linux-gnueabihf --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/alsa-utils --with-alsa-inc-prefix=/home/zuozhongkai/linux/IMX6ULL/tool/alsa-lib/include/ --with-alsa-prefix=/home/zuozhongkai/linux/IMX6ULL/tool/alsa-lib/lib/ --disable-alsamixer --disable-xmlto
make
sudo make install
说明:
--disable-alsamixer是因为 alsamixer 依赖 ncurses 库且原书未能成功交叉编译,但可用同功能的命令行工具amixer(alsamixer 的图形化版本)。原书另用 buildroot 编译出 alsamixer 再拷贝进根文件系统。
将编译出的 bin、sbin、share 分别拷贝到开发板 /bin、/sbin、/usr/share,并在 /etc/profile 中加入:
export ALSA_CONFIG_PATH=/usr/share/arm-alsa/alsa.conf
mplayer 依赖 zlib 与 alsa-lib。
1)移植 zlib(zlib-1.2.11.tar.gz):
tar -vxzf zlib-1.2.11.tar.gz
cd zlib-1.2.11/
CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld AD=arm-linux-gnueabihf-as ./configure --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/zlib
make
make install
sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/lib/ -rfa
2)移植 mplayer(MPlayer-1.4.tar.gz):
tar -vxzf MPlayer-1.4.tar.gz
cd MPlayer-1.4/
./configure --cc=arm-linux-gnueabihf-gcc --host-cc=gcc --target=arm-linux-gnueabihf --disable-ossaudio --enable-alsa --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/mplayer --extra-cflags="-I /home/zuozhongkai/linux/IMX6ULL/tool/zlib/include -I /home/zuozhongkai/linux/IMX6ULL/tool/alsa-lib/include" --extra-ldflags="-L/home/zuozhongkai/linux/IMX6ULL/tool/zlib/lib -lz -L/home/zuozhongkai/linux/IMX6ULL/tool/alsa-lib/lib -lasound" --enable-fbdev --disable-mencoder
make
编译后编辑 config.mak,把 INSTALLSTRIP = -s 后面的 -s 去掉,否则 make install 会失败。然后:
make install
sudo cp bin/mplayer /home/zuozhongkai/linux/nfs/rootfs/bin/ -f
使用:
mplayer test.flac # 播放音频
mplayer test.avi -fs # 全屏/居中播放视频
播放过程中常用按键:9 增加音量、0 减小音量、左/右键倒退/快进 10 秒、上/下键倒退/快进 1 分钟、空格暂停/播放。I.MX6ULL 性能有限且无硬件视频解码,视频分辨率建议 640×480 左右。
alsamixer 是 amixer 的图形化版本,直接输入 alsamixer 打开。功能键:F1 帮助、F2 系统信息、F3 播放设置、F4 录音设置、F6 选择声卡;左右键选择设置项,上下键调整大小,MM 表示静音,按 M 切换 MM/OO。
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
| 特性 | I.MX6ULL | STM32F4/F7 | RK3568 |
|---|---|---|---|
| 音频接口 | SAI | SAI/I2S | I2S |
| 支持协议 | I2S/AC97/TDM | I2S/TDM/PCM | I2S/PCM |
| FIFO深度 | 32×32bit | 32×16bit | 64×32bit |
| DMA通道 | 独立收发 | 共享 | 独立收发 |
| 主从模式 | 支持 | 支持 | 支持 |
| 常用Codec | WM8960/ES8388 | WM8960/CS43L22 | ES8388/RT5640 |
| Linux支持 | 完善 | 较好 | 完善 |
关键差异:
⚠️ 来源说明:本节面试题不属于《I.MX6U嵌入式Linux驱动开发指南》正文内容,为扩展知识;仅 Q4 涉及的 WM8960 R23 寄存器取值来自原书 65.5.3 小节。
答:ASoC将音频驱动分为CPU DAI、Codec和Machine三个独立组件:
设计优势:
答:I2S需要3-4根信号线:
| 信号 | 功能 | 频率计算 |
|---|---|---|
| BCLK | 位时钟 | = 2 × 采样率 × 采样位数 |
| LRCK | 帧时钟 | = 采样率 |
| DATA | 音频数据 | - |
| MCLK | 主时钟 | = 256×fs 或 384×fs |
示例:44.1KHz/16位立体声
答:
| 参数 | WM8960 | ES8388 |
|---|---|---|
| 厂商 | Wolfson(欧胜) | 上海屹恒微 |
| ADC性能 | 94dB SNR | 95dB SNR |
| DAC性能 | 98dB SNR | 96dB SNR |
| 最大采样率 | 48KHz | 96KHz |
| 功放 | 双路D类1W | 40mW耳机 |
| 特色 | 3D增强 | 超低功耗 |
选择建议:
答:修改WM8960的R23寄存器(地址0x17)的bit3:2:
| bit3:2 | 左声道来源 | 右声道来源 |
|---|---|---|
| 00 | 左ADC | 右ADC(默认) |
| 01 | 左ADC | 左ADC |
| 10 | 右ADC | 右ADC |
| 11 | 右ADC | 左ADC |
修改方法:将wm8960_reg_defaults中0x17寄存器的值从0x01c0改为0x01c4。
答:
PCM设备:
/dev/snd/pcmC0D0p(播放)、/dev/snd/pcmC0D0c(录音)alsa-lib的snd_pcm_writei/snd_pcm_readi进行数据传输Control设备:
/dev/snd/controlC0amixer工具或alsa-lib的snd_ctl_*系列函数两者分工明确:PCM负责数据流,Control负责参数配置。
内容来源: 《I.MX6U嵌入式Linux驱动开发指南》第六十五章 Linux 音频驱动实验