Linux-音频驱动

序言

移植了 mplayer 播放器,播放视频时发现没有声音,原来是音频驱动还没移植。100ask 开发板使用到的音频芯片为 WM8690,Linux 内核已经有该驱动文件了,我们仅仅只需要使能驱动和修改设备树。

音频这一部分对于目前的自己有点困难了,网上齐全的资料也较少,这里最后在开发板上使用 aplay 不能正常播放音频文件,但是使用 mplayer 可以正常播放视频带有音频也能正常单独播放音频,倒也是解决了目前的需求,后续有能力再回头看能不能解决 aplay 的问题。

环境

硬件环境

  • 开发板型号100ask_imx6ull_pro 开发板
  • 处理器类型:NXP IMX6ULL
  • **处理器架构:**恩单核 Cortex-A7
  • **处理器主频:**800MHZ
  • 内存容量:512 MB DDR3
  • 存储介质:4GB eMMC
  • 本次测试的驱动:音频芯片 WM8690

软件环境

  • 宿主机
    • 宿主机操作系统:Ubuntu 18.04
    • 交叉编译器:100ask 提供的工具链 arm-buildroot-linux-gnueabihf- 支持的最低内核版本:4.9.0
  • 开发板
  • 音频相关软件
    • ALSA 库版本:1.0.29 版本
    • ALSA-Utils 版本:1.0.29 版本
    • 音频播放器:mplayer 1.1 版本

音频接口简介

移植音频驱动

只需要修改设备树和使能内核音频驱动即可。

修改设备树

I2C 接口

i2c2 接口添加子节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";

codec: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
clocks = <&clks IMX6UL_CLK_SAI2>;
clock-names = "mclk";
wlf,shared-lrclk;
};
};

SAI 接口

sai2 节点添加一些属性:

1
2
3
4
5
6
7
8
9
10
11
12
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2
&pinctrl_sai2_hp_det_b>;

assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>,
<&clks IMX6UL_CLK_SAI2>;
assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <12288000>;

status = "okay";
};

sai2 接口的 IO 配置:pinctrl_sai2pinctrl_sai2_hp_det_b

iomuxc 节点下的 imx6u-evk 子节点添加:

1
2
3
4
5
6
7
8
9
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
>;
};

iomuxc_snvs 节点添加:

1
2
3
4
5
pinctrl_sai2_hp_det_b: sai2_hp_det_grp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059
>;
};
  • pinctrl_sai2_hp_det_b 描述的是耳机插入检测引脚,wm8960 支持耳机插入检测,当耳机插入就通过耳机输出音频,无耳机时通过喇叭播放音乐。

PS:100ask 开发板添加该引脚会报错,查看设备树也没有其他节点用到该引脚,具体原因不知,于是把该节点移除可正常工作,不影响后续操作。

sound 节点

根节点 \ 下添加节点 sound

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
sound {
compatible = "fsl,imx6ul-evk-wm8960",
"fsl,imx-audio-wm8960";
model = "wm8960-audio";
cpu-dai = <&sai2>;
audio-codec = <&codec>;
asrc-controller = <&asrc>;
codec-master;
gpr = <&gpr 4 0x100000 0x100000>;
/*
* hp-det = <hp-det-pin hp-det-polarity>;
* hp-det-pin: JD1 JD2 or JD3
* hp-det-polarity = 0: hp detect high for headphone
* hp-det-polarity = 1: hp detect high for speaker
*/
hp-det = <3 0>;
// hp-det-gpios = <&gpio5 4 0>;
// mic-det-gpios = <&gpio5 4 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";
status = "okay";
};

内核使能驱动

设备树配置完成以后就可以使能内核自带的 WM8960 驱动,一般是默认打开的这里检查一下,打开 linux 内核的图形化配置界面:

1
make menuconfig

确保这两项 OSS Mixer APIOSS PCM (digital audio) API 不选择。

1
2
3
4
5
-> Device Drivers
-> Sound card support (SOUND [=y])
-> Advanced Linux Sound Architecture (SND [=y])
-> <> OSS Mixer API //不选择
-> <> OSS PCM (digital audio) API //不选择

image-20240718211018169

使能 WM8690 驱动:

1
2
3
4
5
6
7
-> 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) module support //选中
-> <*> SoC Audio support for i.MX boards with wm8960 //选中

image-20240719165711761

编译使用新的内核启动开发板启动过程提示:在 ALSA 设备列表中就会找到 wm8960-audio 这个声卡,

1
2
ALSA device list:
#0: wm8960-audio

查看一下 /dev/snd 目录:

1
2
[@obito:/]# ls /dev/snd/
controlC0 pcmC0D0c pcmC0D0p pcmC0D1c pcmC0D1p timer
  • controlC0:用于声卡控制,C0 表示声卡 0。
  • pcmC0D0c 和 pcmC0D1c:用于录音的 pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“c”是 capture 的缩写,表示录音。
  • pcmC0D0p 和 pcmC0D1p:用于播放的pcm 设备,其中的“COD0”和“C0D1”分别表示声卡 0 中的设备 0 和设备 1,最后面的“p”是 playback 的缩写,表示放音。
  • timer:定时器。

交叉编译 alsa

这个库交叉编译 mplayer 时已经交叉编译好了,所以这里直接交叉编译 alsa-utils。

交叉编译 alsa-utils

由于开发板上的声卡配置默认是关闭的,所以需要使用 alsa-utils 工具集来设置声卡配置。

alsa-utils 是用来操作 alsa 框架音频的工具集,因此得先交叉编译 alsa 库。

下载地址:下载版本最好与 alsa 库版本相同,否则可能提示版本不符合的错误

https://github.com/alsa-project/alsa-utils/tree/v1.0.29

解压完文件结构如下:

1
2
3
4
5
6
router2@ubuntu:~/third_lib/alsa-utils-1.2.2$ ls
acinclude.m4 alsamixer axfer gitcompile Makefile.am TODO
alsaconf alsaucm bat iecset po topology
alsactl amidi ChangeLog include README.md utils
alsa-info amixer configure.ac INSTALL seq
alsaloop aplay COPYING m4 speaker-test

执行 aclocal 命令,提示 warning: macro 'AM_PATH_ALSA' not found in library,这是因为没把交叉编译 alsa 生成的 m4 文件放到系统目录。为了不与宿主机 x86 结构相混乱,添加 -I 选项添加额外的搜索目录

1
2
3
4
5
6
7
8
9
router2@ubuntu:~/third_lib/alsa-utils-1.2.2$ aclocal 
configure.ac:22: warning: macro 'AM_PATH_ALSA' not found in library
router2@ubuntu:~/third_lib/alsa-utils-1.2.2$ ls
acinclude.m4 alsamixer axfer iecset README.md
aclocal.m4 alsaucm bat include seq
alsaconf amidi ChangeLog INSTALL speaker-test
alsactl amixer configure.ac m4 TODO
alsa-info aplay COPYING Makefile.am topology
alsaloop autom4te.cache gitcompile po utils

添加 aclocal 搜索路径,前面交叉编译好的 alsa 库中的 .m4 文件

1
aclocal -I /home/router2/third_lib/tmp/arm-libalsa-1.0.29/share/aclocal

这个目录是交叉编译 alsa 库生成的,里面有 alsa.m4 文件:

1
2
router2@ubuntu:~/third_lib/tmp/arm-libalsa-1.0.29$ ls share/aclocal/
alsa.m4

gettextize 初始化 GNU gettext 支持,用于多语言支持:

1
gettextize 

autoheader 生成 config.h.in 文件

1
autoheader

报错:

1
2
3
4
5
configure.ac:356: error: `po/Makefile.in' is already registered with AC_CONFIG_FILES.
../../lib/autoconf/status.m4:288: AC_CONFIG_FILES is expanded from...
configure.ac:356: the top level
autom4te: /usr/bin/m4 failed with exit status: 1
autoheader: '/usr/bin/autom4te' failed with exit status: 1

查看 configure.ac 文件发现 AC_OUTPUT 出现了两次 po/Makefile.in ,删除其中一个即可。这一个错误找了将近 2 个小时,搜索网上的资料也都未找到合适的解决方法。

image-20240719003003493

删除后再次执行 autoheader 成功,执行 autoconf 生成 configure 文件

1
autoconf

执行 automake 命令生成 Makefile.in 文件

1
automake --foreign --copy --add-missing

之后就可以执行 configure 脚本文件生成 Makefile 文件了:

1
./configure --host=arm-buildroot-linux-gnueabihf --prefix=/home/router2/third_lib/tmp/alsa-utils --with-alsa-inc-prefix=/home/router2/third_lib/tmp/arm-libalsa-1.0.29/include/ --with-alsaprefix=/home/router2/third_lib/tmp/arm-libalsa-1.0.29/lib/ --disable-alsamixer --disable-xmlto

最后执行 make 和 make install 也没出现什么问题了,make install 需要 sudo 加权限否则会报错。

1
2
make
sudo make install

移植在开发板测试时出现以下错误:

1
2
3
ALSA lib conf.c:3512:(snd_config_hook_load) cannot stat file/directory /home/router2/third_lib/tmp/arm-libalsa-1.0.29/share/alsa/cards/aliases.conf
ALSA lib pcm.c:2267:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.default
aplay: main:722: audio open error: No such file or directory

后查资料发现是由于前面交叉编译 alsa 时未正常设置配置文件出现的错误,因此需要重新交叉编译 alsa 配置时加上 --with-configdir=/usr/share/arm-alsa ,配置文件也放在开发板的此路径即可。

声卡设置与测试

alsa-utils 自带了 amixer 这个声卡设置工具,amixer 软件命令分为两组,scontrols、scontents、sset 和 sget 为一组。controls、contents、cset 和 cget 为另一组,带 s 的是简化版。

简单使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 查看amixer 帮助信息
amixer --help

// 查看所有设置项
amixer scontrols

// 查看所有设置项
amixer controls

// 查看设置值
amixer scontents

// 设置声卡
amixer sset 设置项目 设置值
amixer cset 设置项目 设置值

// 获取声卡设置值
amixer sget 设置项目
amixer cget 设置项目

音乐播放测试

由于 100ask 开发板没有板载喇叭但有预留接口,所以不能直接直接外放,需要插入有线耳机或者插上喇叭接口

设置声卡,打开耳机和喇叭,并且设置其音量,

1
2
3
4
amixer sset Headphone 100,100
amixer sset Speaker 120,120
amixer sset 'Right Output Mixer PCM' on
amixer sset 'Left Output Mixer PCM' on

使用 aplay 软件播放 wav 格式音乐

1
aplay test.wav

提示错误:

1
aplay: set_params:1305: Can't use period equal to buffer size (0 == 0)

根据下方博客提供方法重新编译 alsa-lib,仍无法解决:

播放时alsa出错Can’t use period equal to buffer size (0 == 0)_signed 16 bit little endian, rate 44100 hz, stereo-CSDN博客

问题待解决。

设置声卡配置文件:

使用正点原子提供的声卡配置文件:写入脚本文件执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/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 AC(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

使用 arecord 录制音频

1
arecord -f cd -d 10 record.wav

刷屏提示错误:

1
2
3
4
5
6
7
8
9
10
11
[@obito:/music]# arecord -f cd -d 10 record.wav
Recording WAVE 'record.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
overrun!!! (at least 3.360 ms long)
overrun!!! (at least 3.064 ms long)
overrun!!! (at least 2.956 ms long)
overrun!!! (at least 3.157 ms long)
overrun!!! (at least 2.959 ms long)
overrun!!! (at least 3.087 ms long)
overrun!!! (at least 2.956 ms long)
overrun!!! (at least 2.950 ms long)
overrun!!! (at least 3.026 ms long)

问题待解决。

再次测试 mplayer

使用 mplayer 能够正常播放音频和视频,视频输出画面和音频都正常。

保存声卡配置文件

使用 alsa-utils 工具集中的 alsactl 工具保存声卡配置,默认保存在 /var/lib/alsa 目录下,先创建该目录:

1
mkdir /var/lib/alsa -p

保存声卡配置提示错误:

1
2
[@obito:/music]# alsactl -f /var/lib/alsa/asound.state store
alsactl: state_lock:125: file /var/lib/alsa/asound.state lock error: No such file or directory

查询资料得知是:制作根文件系统的时候没有 /var/lock 的目录,alsactl 默认会在 /var/lock 目录操作,所以就会失败。

alsactl: state_lock:125: file /var/lib/alsa/asound.state lock error: No such file or directory_asound.state.lock-CSDN博客

新建目录重新保存成功:

1
2
mkdir -p /var/lock
alsactl -f /var/lib/alsa/asound.state store

以后就可以使用以下命令进行恢复声卡配置了:

1
alsactl -f /var/lib/alsa/asound.state restore

为了开机自动设置声卡,将其写入 /etc/init.d/rcS 文件中:

1
2
3
4
if [ -f "/var/lib/alsa/asound.state" ]; then
echo "ALSA: Restoring mixer setting......"
/sbin/alsactl -f /var/lib/alsa/asound.state restore &
fi

先判断文件是否存在,再进行恢复。

总结

移植 WM8690 驱动成功,aplay 和 arecord 问题待解决,mplayer 能够正常使用。