问题:使用飞思卡尔 IMX8m 通过 SAI(5) 数据线发送 16 通道音频 TDM 流。硬件上没有编解码器终止。仅数据以 TDM 形式封装并通过 I2S 数据线发送。有一个 FPGA 接收 Imx8m 数据线另一端的数据并将其解复用到多个音频输出源。
问题:当播放 8 个(4 个立体声源)通道时,TDM 帧似乎工作正常,没有任何问题。但是,当我将 TDM 帧大小增加到 16 通道(8 个立体声源)时,有时甚至在开始播放之前播放就会卡住。它仍然停留在 poll() 函数中并且永远不会返回。该 poll 函数在 /alsa-lib/src/pcm/pcm.c 中的 snd_pcm_write_areas() 顶层调用
这是我在 aplay app/alsa-lib <=> alsa-pcm-snd-driver <==> fsl_sai driver > HW 之间调试期间发现的内容
- 当播放正常时我可以看到snd_pcm_direct.spcm->hw.ptr对于每个 snd_pcm_write_areas 调用,都会增加帧大小*1(或更多)。这个指针似乎及时跟踪应该发送出去的帧数。它是一个无符号 int 值,边界为 0x40000000,一旦到达边界,ptr 就会再次循环。来自 alsa-lib/src/pcm/pcm.c 文件
static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare) { int err; snd_pcm_hwsync(dshare->spcm); dshare->slave_appl_ptr= dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
- 当播放卡在 poll 函数时,我发现该指针从未更新,并且卡在周期大小(帧大小 * n)或帧大小的某个倍数处。
- 我在网上看到了几篇关于播放卡住的 alsa 帖子,但没有解决方案或指导。
- 另请阅读一篇文章,提到这是使用 Dshare/Dmix/Dsnoop 插件时从 4.14 内核开始存在的 alsa bug。
- 我尝试从 1.1.2 升级到 1.1.5 或 1.1.7 版本,但没有成功。显然,使用硬件卡时不会出现此问题,只有使用上述插件时才会出现此问题。
我不确定哪一个会导致另一个,以及哪一个是需要修复的错误。
由于计时器进入不良状态或计时器进入不良状态导致轮询被卡住,轮询功能被卡住。
我们如何创建 TDM 帧的一些细节 我们有一个 16 通道音频(8 个独立立体声对),需要将其作为帧发送到硬件,以便在其中一条 SAI 数据线(4 条数据线)上写出。这 8 个源可以独立播放,并且源将根据 asound.conf 配置文件中指定的通道映射占用 TDM 帧中的时隙。
这德共享插件用于创建将保存帧的共享缓冲区。我在conf文件中创建了8个虚拟源,每个播放进程一个。每个源都可以在单独的 aplay 进程中使用,以播放 8 个不同的源。查看播放所有 8 个源的批处理文件。
aplay -L/l 的输出
null
Discard all samples (playback) or generate zero samples (capture)
src1
TDM 0 channel 0/1 for audio playback
src2
TDM 1 channel 2/3 for audio playback
src3
TDM 2 channel 4/5 for audio playback
src4
TDM 3 channel 6/7 for audio playback
src5
TDM 4 channel 8/9 for audio playback
src6
TDM 5channel 10/11 for audio playback
src7
TDM 6 channel 12/13 for audio playback
src8
TDM 7 channel 14/15 for audio playback
default:CARD=<xxxxx>
Default Audio Device
sysdefault:CARD=<xxxxxxxx>
Default Audio Device
asound.conf 文件
# NOTE: this is for sharing multiple channels on a single (TDM) audio device with multple ALSA clients
# shared buffer for playback
pcm_slave.tdmshare {
pcm "hw:0"
channels 16
rate 48000 # fixed, because all dshare devices must use the same samplerate.
format S24_LE
period_size 512
buffer_size 1024
}
# src1 shared pcm device and corresponding virtual playback device
pcm.src1_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 0
bindings.1 4
}
pcm.src1 {
type plug
slave.pcm "src1_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 0 channel 0/1 for audio playback"
}
}
# src2 shared pcm device and corresponding virtual playback device
pcm.src2_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 8
bindings.1 12
}
pcm.src2 {
type plug
slave.pcm "src2_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 1 channel 2/3 for audio playback"
}
}
# src3 shared pcm device and corresponding virtual playback device
pcm.src3_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 1
bindings.1 5
}
pcm.src3 {
type plug
slave.pcm "src3_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 2 channel 4/5 for audio playback"
}
}
# src4 shared pcm device and corresponding virtual playback device
pcm.src4_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 9
bindings.1 13
}
pcm.src4 {
type plug
slave.pcm "src4_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 3 channel 6/7 for audio playback"
}
}
# src5 shared pcm device and corresponding virtual playback device
pcm.src5_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 2
bindings.1 6
}
pcm.src5 {
type plug
slave.pcm "src5_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 4 channel 8/9 for audio playback"
}
}
# src6 shared pcm device and corresponding virtual playback device
pcm.src6_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 10
bindings.1 14
}
pcm.src6 {
type plug
slave.pcm "src6_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 5channel 10/11 for audio playback"
}
}
# src7 shared pcm device and corresponding virtual playback device
pcm.src7_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 3
bindings.1 7
}
pcm.src7 {
type plug
slave.pcm "src7_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 6 channel 12/13 for audio playback"
}
}
# src8 shared pcm device and corresponding virtual playback device
pcm.src8_dshare {
type dshare
ipc_key 43544553
slave tdmshare
bindings.0 11
bindings.1 15
}
pcm.src8 {
type plug
slave.pcm "src8_dshare"
hint {
show {
@func refer
name defaults.namehint.basic
}
description "TDM 7 channel 14/15 for audio playback"
}
}
用于播放所有 8 个源的批处理文件
aplay -Dplug:src1 /data/PCMCH1-Ch2_1.wav&
aplay -Dplug:src2 /data/PCMCH3-Ch4_1.wav&
aplay -Dplug:src3 /data/PCMCH5-Ch6_1.wav&
aplay -Dplug:src4 /data/PCMCH7-Ch8_1.wav&
aplay -Dplug:src5 /data/PCMCH1-Ch2_1.wav&
aplay -Dplug:src6 /data/PCMCH3-Ch4_1.wav&
aplay -Dplug:src7 /data/PCMCH5-Ch6_1.wav&
aplay -Dplug:src8 /data/PCMCH7-Ch8_1.wav&
请告诉我 asound.conf 文件中是否有可以修改的内容,或者我在 alsa-lib 中播放 16 与 8 通道时遇到的任何限制是否导致了此问题。
我看到的有关此问题的其他链接如下。它们是非常旧的链接,有些人说它已修复,但我不确定它是如何修复的或这些补丁是什么。我们的系统使用的是 alsa 版本 1.1.6 和内核 4.14,并且存在于此版本中。请向我指出任何显示如何解决此问题的补丁集或链接。由于我在这方面的知识有限,我此时在这个问题上陷入了死胡同。
[https://bugzilla.redhat.com/show_bug.cgi?id=534130][1]
[https://www.raspberrypi.org/forums/viewtopic.php?t=64936][2]
[https://sourceforge.net/p/alsa/mailman/message/26464680/][3]
答案1
对内核声音核心 pcm 驱动程序和 pcm 核心 dma 引擎使用的相应 sdma 控制器进行一些调试后,我发现 DMA 控制器 (/drivers/dma/imx-sdma.c) 和 SAI FIFO (/ sound/soc/fsl/fsl_sai.c) 当通道数为 16 且每个通道宽度为 32 位时,就会停止。 asound.conf 文件中指定的 DMA 缓冲区大小也会产生影响。
Sdma 控制器的中断处理程序是增加我在上面的问题中提到的 HW.ptr 的中断处理程序。 SDMA 中断处理程序触发由 alsa 用户空间创建的计时器,用户空间在 poll 函数中等待该计时器(这就是它被卡住的地方)被清除,以便知道要写入多少个缓冲区DMA 内存映射缓冲区。
由于 dma 中断处理程序从未被触发,因此 HW.ptr 从未更新,定时器中断处理程序也从未被触发,这导致用户空间在 poll 函数中等待并且永远不会返回。
这可能不是最终的解决方案,但根据具体情况,解决方案可以是 DMA 控制器固件/脚本内的修复,也可以是连接到 DMA 控制器的外设 FIFO 的运行方式。无论哪种方式,都需要了解给定处理器上的底层硬件 IP 实现。