Bootstrap

Linux 音频驱动实验

音频是我们最常用到的功能,音频也是 linux 和安卓的重点应用场合。I.MX6ULL 带有 SAI接口,正点原子的 I.MX6ULL ALPHA 开发板通过此接口外接了一个 WM8960 音频 DAC 芯片,本章我们就来学习一下如何使能 WM8960 驱动,并且通过 WM8960 芯片来完成音乐播放与录音。


音频接口简介

为何需要音频编解码芯片?

处理器要想“听到”外界的声音必须要把外界的声音转化为自己能够理解的“语言”,处理器能理解的就是 0 1,也就是二进制数据。所以我们需要先把外界的声音转换为处理器能理解的 0 1,在信号处理领域,外界的声音是模拟信号,处理器能理解的是数字信号,因此这里就涉及到一个模拟信号转换为数字信号的过程,而完成这个功能的就是 ADC 芯片。

同理,如果处理器要向外界传达自己的“心声”,也就是放音,那么就涉及到将处理器能理解的 0 1 转化为外界能理解的连续变化的声音,这个过程就是将数字信号转化为模拟信号,而完成这个功能的是 DAC 芯片。

现在我们知道了,处理器如果既想“听到”外界的声音,又想向外界传达自己的“心声”,那么就需要同时用到 DAC ADC 这两款芯片。那是不是买两颗 DAC ADC 芯片就行了呢?

答案肯定是可以的,但是音频不单单是能出声、能听到就行。我们往往需要听到的声音动听、录进去的语音贴近真实、可以调节音效、对声音能够进行一些处理(需要 DSP 单元)、拥有统一的标准接口,方便开发等等。将这些针对声音的各种要求全部叠加到 DAC ADC 芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,英文名字就是 Audio CODEC,所以我们在手机或者电脑的介绍中看到“CODEC”这个词语,一般说的都是音频编解码。

既然音频 CODEC 的本质是 ADC DAC,那么采样率和采样位数就是衡量一款音频CODEC 最重要的指标。比如常见音频采样率有 8K44.1K48K192K 甚至 384K 768K,采样位数常见的有 8 位、16 位、24 位、32 位。采样率和采样位数越高,那么音频 CODEC 越能真实的还原声音,也就是大家说的 HIFI。因此大家会看到高端的音频播放器都会有很高的采样率和采样位数,同样的价格也会越高。当然了,实际的效果还与其他部分有关,采样率和采样位数只是其中重要的指标之一。


WM8960 简介

前面我们已经分析了为何需要音频编解码芯片,那是因为专用的音频编解码芯片提供了很多针对音频的特性。我们就以正点原子 ALPHA 开发板所使用的 WM8960 这颗芯片为例,来看一下专用的音频编解码芯片都有哪些特性。

WM8960 是一颗由 wolfson(欧胜)公司出品的音频编解码芯片,是一颗低功耗、高质量的立体声音频 CODEC。集成 D 类喇叭功放,每个通道可以驱动一个 1W 喇叭(8Ω)。内部集成 3 个立体声输入源,可以灵活配置,拥有一路完整的麦克风接口。WM8960 内部 ADC DAC 都为24 位,WM8960 主要特性如下所示:

①、DAC SNR(信噪比)98dB3.3V48KHz THD(谐波失真)-84dB

②、ADC SNR(信噪比)94dB3.3V48KHz THD(谐波失真)-82dB

③、3D 增强。

④、立体声 D 类功放,可以直接外接喇叭,8Ω负载下每通道 1W

⑤、集成耳机接口。

⑥、集成麦克风接口。

⑦、采样率支持 8K11.025K12K16K22.05K24K32K44.1K 48K

……

WM8960 整体框图如图 65.1.2.1 所示:

依次来看一下图 65.1.2.1 中这四部分接口都是什么功能:

①、此部分是 WM8960 提供的输入接口,作为立体声音频输入源,一共提供了三路,分别为 LINPUT1/RINPUT1LINPUT2/RINPUT2LINPUT3/RINPUT3。麦克风或线路输入就连接到此接口上,这部分是需要硬件工程师重点关心的,因为音频选择从哪一路进入需要在画 PCB 的时候就应该定好。

②、此部分是 WM8960 的输出接口,比如输出给耳机或喇叭,SPK_LP/SPK_LN 用于连接左声道的喇叭,支持 1W 8Ω喇叭。SPK_RP/SPK_RN 用于连接右声道的喇叭,同样支持 1W的 8Ω喇叭,最后就是 HP_L/HP_R,用于连接耳机。

③、此部分是数字音频接口,用于和主控制器连接,有 5 根线,用于主控制器和 WM8960之间进行数据“沟通”。主控制器向 WM8960 DAC 发送的数据,WM8960 ADC 向主控制传递的数据都是通过此音频接口来完成的。这个接口非常重要,是我们驱动开发人员重点关注的,此接口支持 I2S 格式。此接口 5 根线的作用如下:

ADCDATADC 数据输出引脚,采集到的音频数据转换为数字信号以后通过此引脚传输给主控制器。

ADCLRCADC 数据对齐时钟,也就是帧时钟(LRCK),用于切换左右声道数据,此信号的频率就是采样率。此引脚可以配置为 GPIO 功能,配置为 GPIO 以后 ADC 就会使用 DACLRC引脚作为帧时钟。

DACDATDAC 数据输入引脚,主控器通过此引脚将数字信号输入给 WM8960 DAC

DACLRCDAC 数据对齐时钟,功能和 ADCLRC 一样,都是帧时钟(LRCK),用于切换左右声道数据,此信号的频率等于采样率。

BCLK位时钟,用于同步。

MCLK主时钟,WM8960 工作的时候还需要一路主时钟,此时钟由 I.MX6ULL 提供,MCLK 频率等于采样率的 256 384 倍,因此大家在 WM8960 的数据手册里面常看到MCLK=256fs 或 MCLK=384fs

④、此部分为控制接口,是一个标准的 I2C 接口,WM8960 要想工作必须对其进行配置,这个 I2C 接口就是用于配置 WM8960 的。


I2S 总线接口

I2S(Inter-IC Sound)总线有时候也写作 IISI2S 是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和 I2CSPI 这些常见的通信协议一样,I2S 总线用于主控制器和音频 CODEC 芯片之间传输音频数据。因此,要想使用 I2S 协议,主控制器和音频 CODEC 都得支持 I2S 协议,I.MX6ULL SAI 外设就支持 I2S 协议,WM8960 同样也支持 I2S,所以本章实验就是使用 I2S 协议来完成的。I2S 接口需要 3 根信号线(如果需要实现收和发,那么就要 4根信号线,收和发分别使用一根信号线)

SCK串行时钟信号,也叫做位时钟(BCLK),音频数据的每一位数据都对应一个 SCK,立体声都是双声道的,因此 SCK=2×采样率×采样位数。比如采样率为 44.1KHz16 位的立体声音频,那么 SCK=2×44100×16=1411200Hz=1.4112MHz

WS字段(声道)选择信号,也叫做 LRCK,也叫做帧时钟,用于切换左右声道数据,WS 为“1”表示正在传输左声道的数据,WS 为“0”表示正在传输右声道的数据。WS 的频率等于采样率,比如采样率为 44.1KHz 的音频,WS=44.1KHz

SD串行数据信号,也就是我们实际的音频数据,如果要同时实现放音和录音,那么就需要 2 根数据线,比如 WM8960 ADCDAT DACDAT,就是分别用于录音和放音。不管音频数据是多少位的,数据的最高位都是最先传输的。数据的最高位总是出现在一帧开始后(LRCK变化)的第 2 SCK 脉冲处。

另外,有时候为了使音频 CODEC 芯片与主控制器之间能够更好的同步,会引入另外一个叫做 MCLK 的信号,也叫做主时钟或系统时钟,一般是采样率的 256 倍或 384 倍。

65.1.3.1 就是一帧立体声音频时序图:

65.1.3.2 就是笔者采用逻辑分析仪抓取到的一帧真实的音频时序图:

图 65.1.3.2 中通道 0 LRCK 时钟,通道 1 BCLK,通道 2 DACDATA,通道 3 是MCLK。随着技术的发展,在统一的 I2S 接口下,出现了不同的数据格式,根据 DATA 数据相对于 LRCK SCLK 位置的不同,出现了 LeftJustified(左对齐)RightJustified(右对齐)两种格式,这两种格式的时序图如图 65.1.3.3 所示:


I.MX6ULL SAI 简介

音频 CODEC 支持 I2S 协议,那么主控制器也必须支持 I2S 协议,大家如果学过STM32F4/F7/H7 的话应该知道 SAI 接口,因为在 STM32 中就是通过 SAI 接口来连接音频CODEC。I.MX6ULL 也提供了一个叫做 SAI 的外设,全称为 SynchronousAudio Interface,翻译过来就是同步音频接口。

I.MX6ULL SAI 是一个全双工、支持帧同步的串行接口,支持 I2SAC97TDM 和音频DSP,SAI 主要特性如下:

①、帧最大为 32 个字。

②、字大小可选择 8bit 32bit

③、每个接收和发送通道拥有 32×32bit FIFO

④、FIFO 错误以后支持平滑重启。

I.MX6ULL SAI 框图如图 65.1.4.1 所示:

图 65.1.4.1 中右侧“SAI_TX”和“SAI_RX”开头的就是 SAI 外设提供给外部连接音频CODEC 的信号线,具体连接方法查看 65.2 小节的原理图。


硬件原理图分析

V2.4 版本以前底板音频原理图如图 65.2.1 所示:

V2.4 及后面版本的底板音频原理图如图 65.2.2 所示:

65.2.1 65.2.2 中唯一的区别就是 WM8960 所连接的 I2C 接口,一个是 I2C2,一个是I2C1,其他都是一模一样的。

我们重点关注两个接口,SAI I2C,我们依次来看一下这两个接口:

①、SAI 接口一共用到了 6 根数据线,这 6 根数据线用于 I.MX6ULL WM8960 之间的音频数据收发。

②、WM8960 在使用的时候需要进行配置,配置接口为 I2CV2.4 以前版本连接到了I.MX6ULL 的 I2C2 上,V2.4 及以后版本连接到了 I2C1


音频驱动使能

NXP 官方已经写好了 WM8960 驱动,因此我们直接配置内核使能 WM8960 驱动即可,按照如下所示步骤使能 WM8960 驱动。

修改设备树

前面分析原理图的时候已经说过了,WM8960 I.MX6ULL 之间有两个通信接口:I2C 和 SAI,因此设备树中会涉及到 I2C SAI 两个设备节点。其中 I2C 用于配置 WM8960SAI 接口用于音频数据传输,我们依次来配置一下这两个接口。

1wm8960 i2c 接口设备树

首先配置一下 I2C 接口,根据原理图我们知道 V2.4 以前版本底板 WM8960 连接到了I.MX6ULL I2C2 接口上,因此在设备树中的“i2c2”节点下需要添加 wm8960 信息。V2.4及以后版本 WM8960 连接到了 I2C1 上,因此需要在设备树中的“i2c1”节点下需要添加 wm8960信息。

如果去添加肯定是要看设备树的绑定手册,打开 Documentation/devicetree/bindings/sound/wm8960.txt,此文件仅仅用于描述如何在 I2C 节点下添加 WM8960 相关信息,此文档适用于所有的主控,不局限于 I.MX6ULL

2 个必要的属性:

compatible:兼容属性,属性值要设置为“wlf,wm8960”。所以大家在 linux 内核里面全局搜索“wlf,wm8960”的话就会找到WM8960I2C驱动文件,此文件为sound/soc/codecs/wm8960.c

reg设置 WM8960 I2C 地址,在正点原子的 ALPHA 开发板中 WM8960 I2C 地址为 0X1A。

还要几个其他的可选属性:

wlf,shared-lrclk这是一个 bool 类型的属性,如果添加了此属性,WM8960 R24 寄存器的 LRCM (bit2)就会置 1。当 LRCM 1 的时候只有当 ADC DAC 全部关闭以后 ADCLRC和 DACLRC 时钟才会关闭。

wlf,capless这也是一个 bool 类型的属性,如果添加了此属性,OUT3 引脚将会使能,并且为了响应耳机插入响应事件,HP_L HP_R 这两个引脚都会关闭。

绑定文档给出的参考节点内容如下所示:

根据 wm8960.txt 这份绑定文档我们就可以在任意一个主控的 I2C 节点下添加 wm8960 相关信息了,NXP 官方 I.MX6ULL EVK 开发板使用的也是 WM8960,因此在设备树中添加设备节点这些工作 NXP 已经帮我们做了。打开 imx6ull-alientek-emmc.dts,根据自己所使用的底板版本号,找到名为“i2c2”或“i2c1”的节点,将如下 wm8960 节点信息添加到对应的 i2c 节点下:

可以看出,示例代码 65.3.1.2 中的内容基本和 wm8960.txt 这个绑定文档中的示例内容一致,只是多了第 4 和第 5 这两行,这两行用于描述时钟相关信息。第 4 行指定时钟源为 SAI2,第 5行指定时钟的名字为“mclk”。前面我们说过,为了更好的同步,一般都会额外提供一条 MCLK时钟。

V2.4 以前版本的底板添加到 I2C2 节点下,如图 65.3.1.1 所示:

V2.4 及以后版本的底板添加到 I2C1 节点下,如图 65.3.1.2 所示:

至此,关于 wm8960 I2C 配置接口设备树就已经添加好了。

I.MX6ULL SAI 音频接口设备树

接下来就是 I.MX6ULL SAI 音频接口设备树相关内容的修改了,同样的,先查阅一下相应的绑定文档:Documentation/devicetree/bindings/sound/fsl-sai.txt。和我们前面讲过的 IIC 接口、 ECSPI 等接口一样,在 imx6ull.dtsi 文件中会有关于 SAI 相关接口的描述,这部分是 NXP 原厂编写的,我们不需要做任何修改,SAI2 的设备子节点内容如下所示:

直接搜索 compatible 属性中的两个兼容值,那么你就会找到 I.MX6ULL SAI 接口驱动文件,路径为 sound/soc/fsl/fsl_sai.c,此驱动文件不需要我们去研究,除非你在 NXP 上班,而你的工作恰好是给 NXP I.MX 系列芯片编写 SAI 驱动的。从第 13 行可以看出,SAI2 默认是关闭的,因此我们需要将其打开,也就是设置 status 属性的值为“okay”,这个工作肯定是在具体板子对应的.dts 文件中完成的,其实就是向 sai2 节点里面追加或者修改一些属性值。打开 imx6ull-alientek-emmc.dts 文件,找到如下内容:

示例代码 65.3.1.3 中的内容是 NXP 针对自己的 I.MX6ULL EVK 开发板而添加的,主要是对 sai2 节点做了三个方面的修改:SAI2 接口的 pinctrl、相应的时钟、修改 status 为“okay”。我们重点来看一下 pinctrl 的设置,因为关系到 SAI2 接口的 IO 设置,从 pinctrl-0 属性可以看出这里一共有两组 IOpinctrl_sai2 pinctrl_sai2_hp_det_b,这两组 IO 内容如下:

pinctrl_sai2 描述的是 SAI2 接口的 IO 配置,这个要根据自己板子的实际硬件情况修改,正点原子的 ALPHA 开发板上 SAI2 所使用的 IO NXP EVK 开发板一样,因此这里不需要做任何修改。pinctrl_sai2_hp_det_b 描述的是耳机插入检测引脚,wm8960 支持耳机插入检测,这样当耳机插入以后就会通过耳机播放音乐,当耳机拔出来以后就会通过喇叭播放音乐。

对于正点原子的 ALPHA 开发板,SAI 部分的设备树信息不需要做任何修改,直接使用 NXP官方写好的即可。

I.MX6ULL sound 节点

最后我们需要在根节点“/”下创建一个名为“sound”的子节点,笔者并没有在 linux 内核中找到此节点的绑定信息。只有一份在 I.MX 系列芯片中使用 WM8962 芯片的 sound 节点绑定文档,路径为:Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt。虽然不是 wm8960的绑定文档,但是我们也可以参考 imx-audio-wm8962.txtNXP 官方已经针对 EVK 开发板编写了 sound 节点,我们可以在此基础上针对我们所使用的平台来修改出对应的 sound 节点,修改完成以后的 sound 节点内容如下所示:

简单看一下 sound 节点中几个重要的属性:

compatible非常重要,用于匹配相应的驱动文件,有两个属性值,在整个 linux 内核源码中搜索这两个属性值即可找到对应的驱动文件,这里找到的驱动文件为:sound/soc/fsl/imx-wm8960.c。

model最终用户看到的此声卡名字,这里设置为“wm8960-audio”。

cpu-daiCPU DAI(Digital Audio Interface)句柄,这里是 sai2 这个节点。

audio-codec:音频解码芯片句柄,也就是 WM8960 芯片,这里为“codec”这个节点。

asrc-controllerasrc 控制器,asrc 全称为 Asynchronous Sample Rate Converters,翻译过来 就是异步采样频率转化器。

hp-det耳机插入检测引脚设置,第一个参数为检测引脚,3 表示 JD3 为检测引脚。第二个参数设置检测电平,设置为 0 的时候,hp 检测到高电平表示耳机插入;设置为 1 的时候,hp 检测到高电平表示是喇叭,也就是耳机拔出了。

audio-routing:音频器件一系列的连接设置,每个条目都是一对字符串,第一个字符串是连接的 sink,第二个是连接的 source()


使能内核的 WM8960 驱动

设备树配置完成以后就可以使能内核自带的 WM8960 驱动了,直接通过图形化界面配置即可,输入如下命令打开 linux 内核的图形化配置界面:

make menuconfig

1、取消 ALSA 模拟 OSS API

首先取消 ALSA 模拟 OSS,进入如下路径:

结果如图 65.5.2.1 所示:

2、使能 I.MX6ULL WM8960 驱动

接下来使能 WM8960 驱动,进入如下路径:

结果如图 65.5.2.2 所示:

驱动使能以后重新编译 linux 内核,编译完成以后使用新的 zImage .dtb 文件启动,如果设备树和驱动都使能的话系统启动过程中就会如图 65.5.2.4 所示的 log 信息:

系统最终启动以后会打印出 ALSA 设备列表,现在的音频 CODEC 驱动基本都是 ALSA 架构的,本章的 WM8960 驱动也是根据 ALSA 架构编写的。因此在 ALSA 设备列表中就会找到“wm8960-audio”这个声卡,如图 65.5.2.5 所示:

进入系统以后查看一下/dev/snd 目录,看看有没有如图 65.5.2.6 所示文件:

65.5.2.6中的这些文件就是ALSA音频驱动框架对应的设备文件,这些文件的作用如下:

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-lib alsa-utils 这两个东西。

具体参考正点原子的应用开发手册或者驱动开发手册。

;