跳到主要内容

Audio

概述

音频部分处理音频的输入输出,即录音和音频播放。不同的音频通路支持不同的音频处理能力,MTT E300 平台主要有如下几种音频通路类型:

  • Peri I2S + Audio codec
  • Display I2S + Display DPTX
  • USB 音频
  • 蓝牙音频
  • 其它

这一章说明在没有 Audio DSP 介入的情况下,Peri I2S + Audio codec 和 Display I2S + Display DPTX 相关的内容。Peri I2S + Audio codec 音频通路在硬件上,结构通常如下图:

E300 033 图示

Peri I2S + audio codec 音频通路的中心是外设 audio codec,它集成模数转换器 (ADC) 和数模转换器 (DAC),负责完成物理世界中的模拟信号和 SoC 可以处理的数字信号之间的转换,对于音频播放,它将音频采样转为电压以用于模拟输出,对于录音,它将来自模拟输入的电压信号转为一系列音频采样。Audio codec 通过连接器外接扬声器、耳机、麦克风和数字麦克风等器件,并根据需要驱动这些器件与外部物理世界交互。


功能描述

MTT E300 平台 Linux 系统音频部分软硬件技术栈如下图所示:

E300 035 图示

Linux 内核音频 ALSA 子系统整体结构如下图:

E300 036 图示


音频通路

MTT E300 平台 audio codec 与 SoC 之间的连接包括几个部分:

  • I2S:数字音频接口 (DAI),SoC 的多个 GPIO 口复用为 I2S 功能。 I2S 负责在 SoC 和 audio codec 之间传输音频数据,对于播放,I2S 将音频采样数据从 SoC 的 DDR 搬到 audio codec。对于录音,I2S 将音频采样数据从 audio codec 搬到 SoC 的 DDR
  • I2C(可选):SoC 的多个 GPIO 口复用为 I2C 总线功能。 SoC 通过 I2C 总线配置 audio codec,如 audio codec 的内部通路,音量等。是否需要通过 I2C 总线来配置,因具体的 audio codec 而异,一是对于一些简单的 audio codec,完全不需要配置接口,二是 audio codec 不一定通过 I2C 来配置,也可能通过 SPI 之类的总线来传递控制命令。
  • INT(可选):SoC 的单个 GPIO 口复用为中断接口。 对于支持接入耳机的 audio codec,通过这个中断上报耳机插拔事件。
  • REFCLK(可选):也称为 MCLK,或 audio codec 的系统时钟。 audio codec 需要它来工作。SoC 的单个 GPIO 口复用为 MCLK 时钟输出。有些 audio codec 也可以使用 I2S 这样的数字音频接口的 BCLK 作为它们的时钟,无需 SoC 提供专门的 MCLK。通常 audio codec 预期 MCLKBCLK 的倍数,通常为采样率的倍数。
  • 供电(可选):SoC 的多个 GPIO 口用来控制为 audio codec 供电。 根据硬件设计来决定是否有需要。具体 audio codec 支持的功能及特性,因板子设计的不同而不同。关于 Peri-I2S,MTT E300 平台共集成 8 个 Peri-I2S 接口,支持对每个 I2S 接口进行独立控制,其中 I2S0I2S1 可以配置为联合模式,支持在 TDM 模式下传输 8 个声道的 RX 或者 8 个声道的 TX。

Peri-I2S

MTT E300 平台的 Peri-I2S 支持如下特性:

  • 支持 I2S2~7 2 声道 TX 和 2 声道 RX 数据传输;
  • 支持 I2S0~1TDM 模式和 I2S 模式下 4 声道 TX 和 4 声道 RX 数据传输;
  • 支持 I2S0~1TDM 模式下 8 声道 TX 和 8 声道 RX 数据传输;
  • 支持单个 I2S 配置为主模式(Master)和从模式(Slave);
  • 支持左对齐和右对齐,PCM 短帧和长帧模式;
  • 支持 16kHz、44.1kHz、48kHz、88.2kHz、96kHz、176.4kHz 和 192kHz 采样频率;
  • 支持设备端 16bits、24bits 和 32bits 采样深度(注:12bit、20bit 不支持);
  • 支持 I2S 传输时钟 SCLK 固定为 32bit 数据传输;
  • 支持 TDM 模式的固定 32bit 传输;
  • 支持 I2S0~1 在联合模式下共 8 声道 TX 和 8 声道 RX 数据同步传输;
  • 支持通过 DMA 读写 RX 和 TX 数据;
  • 8 个 I2S 共用一个 AXI 接口的 DMA 控制器。

MTT E300 平台 Peri-I2S 的一些限制:

  • 特定 I2S 的 TX 和 RX 共用时钟,TX 和 RX 同时工作时,TX 和 RX 的参数配置必须完全一致,包括采样率、通道数和采样深度等;
  • I2S 不支持实时获取 DMA 数据传输过程中的读写指针,无法支持应用层基于定时器的数据传输调度,只能支持中断驱动的数据传输;
  • DMA 描述符 FIFO 无法复位。

Display I2S + Display DPTX

Display I2S + Display DPTX 音频通路只支持音频播放,不支持录音,在硬件上,结构如下图:

E300 034 图示

Display I2S + Display DPTX 音频通路类似于 Peri-I2S+极简 audio codec 的情况,Display DPTX 扮演 audio codec 的角色,但无需太多专门的控制,如 I2C,也无需 SoC 处理供电、提供参考时钟,或处理耳机插拔事件中断。MTT E300 平台中,Display I2S 和 Peri-I2S 是不同的 IP,MTT E300 平台共集成 6 个 Display I2S 接口,它们支持的特性如下:

  • 采样深度灵活配置,范围从 16bits 至 32bits。
  • 支持的采样率范围从 8KHz ~ 192KHz。

各个 Display I2S 接口和各个 Display DPTX 之间的对应关系如下:

Display I2SDisplay DPTX
I2S0 和 I2S1Display DPTX0
I2S2Display DPTX1
I2S3Display DPTX2
I2S4 和 I2S5Display DPTX3

驱动程序提供对硬件能力的子集的支持。

MTT E300 Linux 内核音频设备驱动程序基于 ASoC 框架实现,涉及到的内核文件主要有如下这些:

文件路径说明
sound/soc/m1000/m1000*i2s*_.c
sound/soc/m1000/m1000*i2s*_.h
sound/soc/m1000/m1000*snd_irq.c
sound/soc/m1000/m1000_snd_irq.h
sound/soc/m1000/m1000_snd_pcm.c
sound/soc/m1000/m1000_snd_pcm.h
sound/soc/m1000/m1000_snd_regs.c
sound/soc/m1000/m1000_snd_regs.h
Peri I2S 驱动程序实现
sound/soc/m1000/m1000_disp_i2s*_.c
sound/soc/m1000/m1000*disp_i2s*_.h
Display I2S 驱动程序实现
sound/soc/m1000/m1000_snd_card_dummy_peri_drv.cDummy machine 驱动程序,供实现特定板子的 machine 驱动程序时参考
sound/soc/m1000/m1000_snd_card_dptx_drv.cDisplay I2S + Display DPTX 音频通路 machine 驱动程序
sound/soc/m1000/m1000_snd_card_rt5672_drv.c基于 Realtek ALC5672 audio codec 的 machine 驱动程序,供参考
sound/soc/codecs/*内核主线代码中的 codec 驱动程序

Peri I2S 和 Display I2S 驱动程序实现为 ASoC 的 Platform 类驱动程序,codec 驱动程序实现为 ASoC 的 Codec 类驱动程序,声卡实现为 ASoC 的 Machine 类驱动程序。


API 说明

音频设备驱动程序通过 ALSA API 暴露给用户空间程序使用。


约束说明

Peri I2S + audio codec 音频通路:

  • 播放和录音同时工作时,播放和录音的参数必须完全一致;
  • 无法支持基于定时器的数据传输调度。

Display I2S + Display DPTX 音频通路的正常工作依赖于 Display DPTX 的正常工作。


配置流程

步骤 1:配置 Display I2S + Display DPTX

要使用 Display I2S + Display DPTX 音频通路,需要打开对应的内核配置开关,将相关驱动编译到内核中或编译为模块。相关配置在内核配置菜单 menuconfig 中的位置如下图:

E300 037 图示

位置为 Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> ASoC support for MooreThreads -> ASoC support for MooreThreads boards using a DPTX sound。Display I2S + Display DPTX 音频通路相关的内核配置有多个,开启 ASoC support for MooreThreads boards using a DPTX sound 时,相关的配置项也会被自动打开,如 sound/soc/m1000/Kconfig

config SND_SOC_M1000_DPTX_SOUND
tristate "ASoC support for MooreThreads boards using a DPTX sound"
depends on HAVE_CLK && M1000_DPTX
select SND_SOC_MTHREADS_DISP_I2S
help
Say Y or M here if you want to add support for SoC audio on MooreThreads
M1000 boards using the DPTX sound.

SND_SOC_MTHREADS_DISP_I2S 配置会被自动打开。


步骤 2:配置 Peri I2S + Audio codec

要使用 Peri I2S + Audio codec 音频通路,需要打开对应的内核配置开关,将相关驱动编译到内核中或编译为模块。新的板子,需要根据所选的 audio codec 添加新的声卡配置项,声卡用到的 I2S 和 audio codec 等模块的内核配置开关,通常由声卡配置项开关时自动选择。Peri I2S + Audio codec 音频通路的内核配置开关示例如基于 Realtek ALC5672 的声卡配置开关,它在内 kernel 配置菜单 menuconfig 中的位置如下图:

E300 038 图示

位置为 Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> ASoC support for MooreThreads -> ASoC support for MooreThreads boards using a ALC5672 codec。Peri I2S + Audio codec 音频通路相关的内核配置有多个,开启 ASoC support for MooreThreads boards using a ALC5672 codec 时,相关的配置项也会被自动打开,如 sound/soc/m1000/Kconfig

config SND_SOC_M1000_RT5672
tristate "ASoC support for MooreThreads boards using a ALC5672 codec"
depends on I2C && GPIOLIB && HAVE_CLK
select SND_SOC_MTHREADS_I2S
select SND_SOC_RT5670
select SND_SOC_ACPI if ACPI
help
Say Y or M here if you want to add support for SoC audio on MooreThreads
M1000 boards using the ALC5672 codec.

SND_SOC_MTHREADS_I2SSND_SOC_RT5670SND_SOC_ACPI 配置会被自动打开。


步骤 3:配置用户空间声卡

用户空间对于声卡的配置,可通过 alsa-ucm-conf 配置文件进行。alsa-ucm-conf 位于配置文件位于系统的 /usr/share/alsa/ucm2 目录下。


开发参考

Display I2S + Display DPTX 音频通路通常不需要专门针对板子做额外的开发。Peri I2S + Audio codec 音频通路,对于特定板子,需要如下的额外开发或配置:

  • 配置 Peri I2S 的板级资源,如 GPIO 口等;
  • 集成 Audio codec 的 Linux 内核设备驱动程序;
  • 在设备树文件中为 Audio codec 设备添加设备树节点,配置它用到的各项资源,具体方法因 Audio codec 的选择而异;
  • 编写 machine (声卡) 驱动程序,可以参考 sound/soc/m1000/m1000_snd_card_dummy_peri_drv.csound/soc/m1000/m1000_snd_card_rt5672_drv.c 等文件;
  • 配置声卡设备树节点。

调试方法说明

音频调试可分为如下几个层次:

  • 音频硬件,分析与硬件有关的问题,方法包括查看硬件设备的寄存器,查看相关的资源的状态,通过示波器量信号等;

  • 硬件音频通路和声卡,分析硬件音频通路的配置及声卡有关的问题,方法主要是通过 alsa-utils 包提供的工具来分析;

  • 系统级音频通路,与系统的音频服务器有关的各种问题,方法主要是通过音频服务器提供的工具查看音频服务器的状态等。

regmap 是一个用于统一访问硬件寄存器的框架,它简化了对寄存器映射(Register Map)的操作,并提供了缓存、访问接口抽象、并发控制等机制,可以处理只读、只写、易失的寄存器。通过它可以访问 I2CSPIMMIO 的设备寄存器。使用 regmap 机制访问寄存器的驱动程序,在用户空间,可以通过 /sys/kernel/debug/regmap 目录下的文件查看这些寄存器的状态。MTT E300 平台的 Peri I2S 和 Display I2S Linux 内核驱动程序实现中,使用 regmap 机制访问寄存器。查看 I2S 设备寄存器的方法如:

sudo su
# cd /sys/kernel/debug/regmap/MI2S0001\:05/
# cat registers
000: 00000000
004: 00000000
008: 00000000
......

查看时钟的状态,如:

sudo su
# cd /sys/kernel/debug/clk/peri_i2s5_sclk
# cat clk_enable_count
1
# cat clk_rate
12288000

通过 devmem 命令查看 Pinctrl 的状态,如:

sudo busybox devmem 0x702310
0x00000040
sudo busybox devmem 0x702314
0x00000040
sudo busybox devmem 0x702318
0x00000040
sudo busybox devmem 0x70231c
0x00000064

通过 i2c-tools 包的工具读取和写入通过 I2C 控制的 audio codec 的寄存器,具体方法可参考 I2C 章节的说明。查看已经注册的 ASoC components 和 dais,如:

sudo su
# cd /sys/kernel/debug/asoc
# cat components
MI2S0001:05
i2c-10EC5672:00
m1000-dptx-dev.3
m1000-dptx-dev.1
m1000-dptx-dev.0
snd-soc-dummy
# cat dais
m1000-cpu-dai.5
rt5670-aif1
rt5670-aif2
m1000-dptx-dev.3
m1000-dptx-dev.1

通过 tinymix 命令查看声卡的 mixer control 配置,如:

tinymix contents
Number of controls: 111
ctl type num name value
0 INT 2 HP Playback Volume35, 35 (range 0->39)
1 INT 2 OUT Playback Volume 31, 31 (range 0->39)
2 BOOL 2 DAC2 Playback Switch On, On
. . . . . .

通过 tinymix 命令查看特定声卡的 mixer control 配置,如:

tinymix --card 0 contents
Number of controls: 111
ctl type num name value
0 INT 2 HP Playback Volume35, 35 (range 0->39)
1 INT 2 OUT Playback Volume 31, 31 (range 0->39)
2 BOOL 2 DAC2 Playback Switch On, On
3 BOOL 2 DAC1 Playback Switch On, On
. . . . . .
26 BOOL 1 RECMIXR BST2 Switch Off

通过 tinymix 命令设置声卡的 mixer control 配置,如:

tinymix set 1 35, 35
tinymix set 26 1
tinymix --card 0 set 26 0

通过 aplay 命令查看系统中的 ALSA 播放声卡和数字音频设备,如:

aplay -l
**** PLAYBACK 硬體裝置清單 ****
card 0: mtaport5672 [mt-apo-rt5672], device 0: RT5672 PCM rt5670-aif1-0 []
子设备: 1/1
子设备 #0: subdevice #0

通过 aplay 命令查看系统中的 ALSA 播放设备,如:

aplay -L
null
Discard all samples (playback) or generate zero samples (capture)
default
Playback/recording through the PulseAudio sound server
samplerate
Rate Converter Plugin Using Samplerate Library
speexrate
Rate Converter Plugin Using Speex Resampler
jack
JACK Audio Connection Kit
oss
Open Sound System
pipewire
PipeWire Sound Server
pulse
PulseAudio Sound Server
upmix
Plugin for channel upmix (4,6,8)
vdownmix
Plugin for channel downmix (stereo) with a simple spacialization
hw:CARD=mtaport5672,DEV=0
mt-apo-rt5672,
Direct hardware device without any conversions
plughw:CARD=mtaport5672,DEV=0
mt-apo-rt5672,
Hardware device with all software conversions
sysdefault:CARD=mtaport5672
mt-apo-rt5672,
Default Audio Device
dmix:CARD=mtaport5672,DEV=0
mt-apo-rt5672,
Direct sample mixing device
usbstream:CARD=mtaport5672
mt-apo-rt5672
USB Stream Output

通过 aplay 命令播放 *.wav 音频文件,如:

aplay test.wav

通过 aplay 命令指定设备播放 *.wav 音频文件,如:

aplay --device=hw:CARD=mtaport5672,DEV=0 test.wav

通过 arecord 命令查看系统中的 ALSA 录音声卡和数字音频设备,如:

arecord -l
**** CAPTURE 硬體裝置清單 ****
card 0: mtaport5672 [mt-apo-rt5672], device 0: RT5672 PCM rt5670-aif1-0 []
子设备: 1/1
子设备 #0: subdevice #0

同样,通过 arecord -L 命令可以查看系统中的所有 ALSA 录音设备。通过 arecord 命令录制音频,如:

arecord recording_48k_8ch_16bits_001.wav -r 48000 -c 8 -f S16_LE

通过 arecord 命令指定录音设备录制音频,如:

arecord --device=hw:CARD=mtaport5672,DEV=0 recording_48k_8ch_16bits_001.wav -r 48000 -c 8 -f S16_LE

系统级音频通路,与系统的音频服务器有关的各种问题,可通过音频服务提供的工具来分析,如 PulseAudiopaplayparecordpactl。通过 pactl 命令查看 PulseAudio 的各种对象,如客户端、加载的模块、source 设备和 sink 设备等:

pactl list
模块 #1
名称:libpipewire-module-rt
参数:{
nice.level = -11
#rt.prio = 88
#rt.time.soft = -1
#rt.time.hard = -1
}
使用计数:n/a
属性
module.name = "libpipewire-module-rt"
object.id = "1"
object.serial = "1"
module.author = "Wim Taymans <wim.taymans@gmail.com>"
module.description = "Use realtime thread scheduling, falling back to RTKit"
module.usage = "[nice.level=<priority: default 20(don't change)>] [rt.prio=
<priority: default 88>] [rt.time.soft=<in usec: default -1] [rt.time.hard=<in usec: default -1] "
module.version = "0.3.48"
nice.level = "-11"
模块 #2
名称:libpipewire-module-protocol-native
参数:
使用计数:n/a
属性
module.name = "libpipewire-module-protocol-native"
object.id = "2"
object.serial = "2"
module.author = "Wim Taymans <wim.taymans@gmail.com>"
module.description = "Native protocol using unix sockets"
module.version = "0.3.48"

通过 paplay 由系统音频服务器播放 *.wav 音频文件,如:

paplay test.wav

系统音频服务器走默认音频输出设备播放音频。

通过 parecord 由系统音频服务器录制音频数据,如:

parecord --format=s16le --channels=4 --rate=48000 --file-format=wav test_48k_4ch.wav

查看 I2S 传输的中断计数,如:

E300 039 图示

Audacity 工具可用来查看原始音频数据或 *.wav 音频文件的音频信号,可以通过 sudo apt-get install audacity 指令进行安装,如:

E300 040 图示

上图分别以波形和频谱图展示同一个文件中不同声道的音频信号。

对于用户空间应用程序的问题,如 PulseAudioPipeWire 等,GDB 无疑是一个方便的调试工具。


常见问题

Q:系统设置的声音面板中看不到音频设备,可能的原因是什么?问题如何排查?

A: 可能的原因主要包括声卡加载失败,或系统音频服务器配置音频设备失败。

  • 如果声卡加载失败,在系统的 /dev/snd 目录下将不会创建对应的音频设备文件。排查方法如下:

    • 检查 machine 驱动程序源码,确认是否正确引用各个音频设备(如 I2S 和 audio codec);
    • 检查设备树文件中是否正确配置并启用了声卡设备树节点;
    • 检查 machine 驱动中引用的各个音频设备的 ASoC components 和 DAIs 是否被正确注册;
    • 检查设备树中声卡使用的 I2S 和 audio codec 是否正确配置;
    • 检查 audio codec 是否已正确上电,可通过读取其寄存器判断。
  • 如果声卡已成功加载但面板中仍无设备,可能是系统音频服务器配置失败。原因包括:

    • 系统音频服务器(如 PulseAudioPipeWire)可能默认使用立体声或单声道配置,音频设备不支持;
    • 可通过 dmesg 查看相关日志信息;
    • 如果系统音频服务器支持 ALSA UCM,可通过编辑其配置文件指定音频设备的参数;
    • 在音频服务器配置文件中指定访问参数,也可能解决问题。

Q:播放和录音无法同时工作可能是什么原因?如何处理?

A: MTT E300 平台的 Peri-I2S 播放和录音共用时钟,需确保两者的音频参数完全一致(包括采样率、位深度和通道数)才能同时工作。


Q:播放音频文件时没有声音可能是什么原因?如何处理?

A: 可能原因如下:

  • I2S 的 GPIO 配置不正确;
  • 音频通路的硬件配置不正确;
  • 音频播放过程中出错,导致音频流中断。

排查建议:

  • 检查设备树文件中 I2S 的 GPIO 配置;
  • 读取 GPIO 寄存器,确认引脚状态;
  • 仔细检查音频通路(包括 audio codec 配置),可使用 tinymix 测试;
  • 查看 I2S 中断计数,判断其工作状态。

Q:更新了 alsa-ucm-conf 包之后,如何使新的配置生效?

A: 可以通过以下两种方式使新配置生效:

  • 方式一: 重启系统(推荐,简单直接);
  • 方式二: 重启音频服务器(无需重启系统)。

不同音频服务器的重启命令如下:

PulseAudio:

systemctl --user stop pulseaudio.socket
systemctl --user stop pulseaudio.service
systemctl --user start pulseaudio.socket
systemctl --user start pulseaudio.service

PipeWire + pipewire-media-session:

systemctl --user stop pipewire.service pipewire.socket pipewire-media-session.service pipewire-pulse.service pipewire-pulse.socket

systemctl --user start pipewire.service pipewire.socket pipewire-media-session.service pipewire-pulse.service pipewire-pulse.socket

PipeWire + wireplumber:

systemctl --user stop pipewire.service
pipewire.socket
wireplumber.service
pipewire-pulse.service
pipewire-pulse.socket

systemctl --user start pipewire.service
pipewire.socket
wireplumber.service
pipewire-pulse.service
pipewire-pulse.socket