没有合适的资源?快使用搜索试试~ 我知道了~
首页PCM_data_flow_in_ASoC-Part1
资源详情
资源评论
资源推荐

0. Introduction
0.1. Purpose
分析 ALSA/ASoC 的 pcm_dma 注册过程,当然 codec 及 cpu_dai(I2S/PCM)也会有所提及,但不会太详细;
分析 PCM 数据如何从 userspace 流向 codec;
阐述 pcm_dma 的 ops 函数集的作用,如何启动 dma 搬运 PCM 数据。
在此之后,应该对 PCM 数据流向有较清晰的认知,能在新的平台实现其 pcm_dma 驱动,知道 xrun 的根源所在并进行参数调整,在 cpu performance
和 pcm latency 之间取得平衡。
0.2. Terminology
ALSA - Advanced Linux Sound Architecture
ASoC - ALSA System on Chip,建立在标准 ALSA 驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频 CODEC 的一套软件体系
CODEC - Coder/Decoder,模拟信号通过 A/D 转换成 PCM 信号(录制),PCM 信号通过 D/A 转换成模拟信号(回放)
I2S/PCM/AC97 - CODEC 与 CPU 间音频的通信协议/接口/总线
DAI - Digital Audio Interface,通常是 I2S/PCM/AC97
DAC - Digit to Analog Conversion
ADC - Analog to Digit Conversion
DSP - Digital Signal Processor
Mixer - Mixes several analog signals into a single analog signal
PCM - Pulse Code Modulation,一种音频模拟信号转换成数字信号的技术,区别于 PCM 音频通信协议/接口
Sample Rate - ADC 采样频率,每秒的采样次数,典型值如 44.1Khz/48Khz
Sample Length- 量化位数,比如 24bit,就是将模拟信号按照 2 的 24 次方进行量化
DAPM - Dynamic Audio Power Management,动态音频电源管理
0.3. Copyright
欢迎转载,但请注明作者和来源,如下:
FILE : PCM data flow in ASoC
AUTHOR : Loon Zhong <sepnic@gmail.com>
BLOG : http://blog.csdn.net/sepnic

1. Overview
Kernel - 3.4.5
SoC - Samsung exynos
CODEC - WM8994
Machine - goni_wm8994
Userspace - tinyalsa
ALSA/ASoC 驱动有如下三部分构成:
Platform:通常指某款 SoC 平台,如 exynos、omap、qcom 等等。Platform 又可细分两部分:
1). cpu dai:在嵌入式系统里面通常指 CPU 的 I2S、PCM 总线控制器,负责将音频数据从 I2S tx FIFO 搬运到 CODEC(回
放的情形,录制则方向相反)。cpu_dai 通过 snd_soc_register_dai()来注册。注:DAI 是 Digital Audio Interface 的
缩写,分为 cpu_dai 和 codec_dai,这两者通过 I2S/PCM 总线连接;AIF 是 Audio Interface 的缩写,一般分为 I2S 和 PCM
接口。
2). pcm dma:负责将 dma buffer 中的音频数据搬运到 I2S tx FIFO,这部分的逻辑比较复杂,以下几篇会对它详细阐述。
音频 dma 驱动通过 snd_soc_register_platform()来注册。值得留意的是:某些情形下是不需要 dma 操作的,比如 Modem
和 CODEC 直连,因为 Modem 本身已经把数据送到 PCM FIFO 了,这时只需启动 codec_dai 接收数据即可;该情形下,Machine
驱动 dai_link 中需要指定.platform_name = "snd-soc-dummy", 这是虚拟出来的 platform 驱动,实现见
sound/soc/soc-utils.c。
Codec:对于 Playback 来说,userspace 送过来的 PCM 数据是经过抽样量化出来的数字信号,在 codec 经过 DAC 转换成模
拟信号送到外放耳机输出,这样我们就可以听到声音了。Codec 字面意思是编解码器,但芯片里面的部件很多,常见的有
AIF、DAC、ADC、Mixer、PGA、Line-in、Line-out,有些高端的 codec 芯片还有 EQ、DSP、SRC、DRC、AGC、Echo canceller、
Noise suppression 等功能部件。
Machine:指某一款机器,它把 cpu_dai、codec_dai、modem_dai 各个音频接口通过定义 dai_link 链结起来,然后注册
snd_soc_card。和上面两个不一样,Platform 和 CODEC 驱动一般是可以重用的,而 Machine 有它特定的硬件特性,几乎是
不可重用的。所谓的硬件特性指:DAIs 之间的链结;通过某个 GPIO 打开 Amplifier;通过某个 GPIO 检测耳机插拔;使用
某个时钟如 MCLK/External OSC 作为 I2S、CODEC 模块的基准时钟源等等。
从上面的描述来看,对于回放的情形,PCM 数据流向大致是:
| copy_from_user | | DMA | | I2S | | AIF->DAC->PGA/Mixer |
userspace-------------->dma buffer----->I2S tx FIFO-------->CODEC-------------->SPK/HS/Earp
这系列初步定为如下几个部分:
分析 ALSA/ASoC 的 pcm_dma 注册过程,soc-core 如何找出 dai_link 上链接的 cpu_dai、codec_dai、platform 并逐一完成
各自的 probe。
当 pcm_open 时,如何建立 hw constraints;如何检查 hw params 是否在 hw constraints 约束范围之内;如何回调
platform/codec/machine 的 hw_params()。
当 pcm_write 时,1)如何把 pcm 数据从 userspace 拷贝到 dma_alloc_writecombine()分配出来的 dma buffer 中;2)如
何触发 pcm_dma 的 trigger()回调去启动 DMA,把数据从 dma buffer 搬运到 I2S tx FIFO;3)如何启动 I2S 把数据从 I2S

tx FIFO 搬运到 codec。但在这之前,会先分析 dma ops 函数集的作用及 dma buffer 分配过程,还有 dma 搬运的源地址和
目的地址如何设定,这部分平台相关。
xrun 出现的根源是什么?有些资料这样提及:“xrun 指的是,声卡 period 一到,引发一个中断,告诉 alsa 驱动,要填
入数据,或读走数据,但是问题在于 alsa 的读取和写入操作必须用户调用 writei 和 readi 才会发生的,它不会去缓存数
据;如果上层没有用户调用 writei 和 readi,那么就会产生 overrun(录制时,数据都满了,还没被读走)和 underrun
(需要数据来播放,却不写入数据),统称为 xrun”。这里源码上分析它是如何产生的,这样我们知道需要正确配置好哪
个参数才能更好避免 xrun 或取得更好的表现性能。
最后探讨 Android Audio HAL 上的 deep buffer 和 low latency。
dai_link:machine 驱动中定义的音频数据链路,它指定用到的 cpu_dai、codec_dai、platform 分别是什么。比如对于
goni_wm8994 平台的 media 链路:codec="wm8994-codec"、codec_dai="wm8994-aif1"、cpu_dai="samsung-i2s",
pcm_dma="samsung-audio",这四者就构成了一条音频数据链路。一个系统可能有多个音频数据链路,比如 media 和 voice,因
此可以定义多个 dai_link。如 WM8994 的典型设计,有三个 dai_link,分别是 AP<->AIF1 的"HIFI",BP<->AIF2 的"Voice",
BT<->AIF3(AIF3 并不是真正的 DAI,它需要桥接到 AIF1 或 AIF2 上才能工作)。
代码如下:
static struct snd_soc_dai_link goni_dai[] = {
{
.name = "WM8994",
.stream_name = "WM8994 HiFi",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-audio",
.codec_name = "wm8994-codec.0-001a",
.init = goni_wm8994_init,
.ops = &goni_hifi_ops,

}, {
.name = "WM8994 Voice",
.stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai",
.codec_dai_name = "wm8994-aif2",
.codec_name = "wm8994-codec.0-001a",
.ops = &goni_voice_ops,
},
};
hw constraints:指平台本身的硬件特性限制,如所能支持的通道数/采样率/数据格式、DMA 支持的周期大小(period size)
范围、周期次数(period count)范围等,通过 snd_pcm_hardware 结构直观描述,如下:
static const struct snd_pcm_hardware dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S8,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128*1024,
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = PAGE_SIZE*2,
.periods_min = 2,
.periods_max = 128,
.fifo_size = 32,
};
hw params:用户层设置的硬件参数,如 channels、sample rate、pcm format、period size、period count;这些参数受 hw
constraints 约束。
sw params:用户层设置的软件参数,如 start threshold、stop threshold、silence threshold。

2. ASoC data structure
ASoC:ALSA System on Chip,是建立在标准 ALSA 驱动之上,为了更好支持嵌入式系统和移动设备中的音频 codec 的一套软件
体系,它依赖于标准 ALSA 驱动框架。内核文档 Documentation/alsa/soc/overview.txt 中详细介绍了 ASoC 的设计初衷,这里
不一一引用,简单陈述如下:
独立的 codec 驱动,标准的 ALSA 驱动框架里面 codec 驱动往往与 SoC CPU 耦合过于紧密,不利于在多样化的平台/机器上
移植复用;
方便 codec 与 SoC 通过音频总线 PCM/I2S 建立链接,这通常在 Machine 驱动里面通过 dai_link 定义的 codec_dai_name 和
cpu_dai_name 来匹配绑定 codec_dai 和 cpu_dai;
动态音频电源管理 DAPM,使得 codec 任何时候都工作在最低功耗状态,同时负责音频路由的创建;这部分在我的另一个系
列文章 dapm 里有详细的分析,是 ASoC 的重点和难点;
POPs 和 click 音抑制弱化处理,在 ASoC 中通过正确的音频部件通电断电次序来实现;
Machine 驱动的特定控制,比如耳机、麦克风的插拔检测,外放功放的开关。
在概述中已经介绍了 ASoC 驱动的三大构成:Codec、Platform 和 Machine,下面列举各部分驱动包含的功能特性:
ASoC Codec Driver:
Codec DAI 和 PCM 的配置信息;
Codec 的控制接口,I2C 或者 SPI 等;
Mixer 和其他音频控件;
Codec 的音频操作接口,见 snd_soc_dai_ops 结构体定义;
DAPM 描述信息;
DAPM 事件处理句柄;
DAC 数字静音控制。
ASoC Platform Driver:
包括 Audio DMA 和 SoC DAI Drivers 两部分:
Audio DMA 实现音频 dma 操作函数集,具体见 snd_pcm_ops 结构体定义;
SoC DAI Driver 实现数字音频接口的描述和配置、系统时钟配置、休眠唤醒等。
ASoC Machine Driver:
作为链结 Platform 和 Codec 的载体,它必须定义 dai_link 为音频物理链路选定 Platform 和 Codec;
处理机器特有的音频控件和音频事件,例如回放时打开外放功放。
以下 UML 类图标示着 ASoC 中重要的数据结构以及它们之间的联系,帮助理解整个 ASoC 系统。通过不同颜色标示数据结构对应
的模块,其中 soc-core 和 pcm_native 是核心,platform_drv、codec_drv、cpu_dai_drv、machine_drv 是我们要实现的
alsa-driver,主要是一些结构体实例的创建和回调函数集的实现。
注:对于 Linux 内核来说,整理清楚模块中重要的数据结构,包括主要成员的作用以及各个数据结构之间的联系,那么就等于
把握了该模块的脉络,剩下的只是细节。因此在各模块的分析之前,作者会把模块重要的数据结构先列出来,逐一介绍,然后
才是源码分析。
剩余41页未读,继续阅读











安全验证
文档复制为VIP权益,开通VIP直接复制

评论9