ALSA SoC 框架 编解码器和平台类驱动程序
ALSA是什么?
ALSA(advanced linux sound architecture) 高级linux声音架构。但是这个架构主要为计算机设置,没有怎么考虑嵌入式,后来就出现ASoC(ALSA system on chip),为嵌入式处理器和各种编解码器提供更好的ALSA支持。
ASoC将嵌入式音频系统划分为3个可重用的组件驱动程序,即机器类(machine class)、平台类(platform class)和编解码器类(codec class)。其中平台类和编解码器类具有跨平台属性,机器类则是板级的。即平台类和编解码类一个就可以在不同的板子上使用,而机器就是这块板子上配置编解码器和平台的,所以是专属于板子的。
ASoC
在当前的内核实现中,使用一种通用方法——组件(component),来进行平台和编码器的描述。有两个结构体来辅助进行,struct snd_soc_component(即编码器或平台)和struct snd_soc_component_driver(他们各自的音频接口驱动程序)。
数字音频接口(digital audio interface,DAI)是一种总线控制器,可以将音频数据从一端(比如SoC)送到另一端(编解码器)。ASoC支持大多数DAI
一个ASoC分为三个元素(平台、编解码器、机器),每一个元素都有自己的专用驱动程序。
平台(platform): SoC 平台相关的驱动部分,也就是“芯片原厂”提供的、与具体 SoC 平台绑定的音频驱动模块。包含SoC的音频DMA引擎(负责音频数据的内存搬运);控制 数字音频接口(DAI),如 I2S、PCM、AC97 等;配置 SoC 内部的音频相关硬件资源(如 FIFO、时钟、DMA 通道),各个芯片原厂的产品比如stm,qcom,mtk。Platform 类是 ASoC 中“CPU 一侧”的驱动,负责把音频数据从内存搬到 SoC 的音频接口(DAI),再通过 DMA 等方式交给 Codec。平台的驱动程序分为两部分:
CPU DAI: cpu的音频总线控制器,比如I2S、pcm等总线控制器。平台驱动程序定义DAI并将它们注册到ASoC内核中。描述 SoC 内部数字音频接口(I²S、PCM、AC97…)的寄存器配置、时钟、格式、slot、TDM 等。实现
startup / hw_params / set_fmt / trigger等 DAI 操作回调。PCM DMA: pcm驱动程序通过覆盖由struct snd_soc_component_driver结构体公开的指正帮助执行DMA操作。pcm驱动程序与平台无关,仅仅与引擎上游的API交互。然后dma引擎与特定平台的dma驱动程序交互获得正确dma配置。这里分为两部分,DMA引擎和PCM运行库。
DMA 引擎驱动(dmaengine 实现)
• 封装 SoC 的专用音频 DMA 控制器,向内核 dmaengine 子系统注册通道。
• 提供device_prep_slave_sg / device_config / device_tx_status等回调,完成把环形 buffer 搬到 Tx FIFO(或从 Rx FIFO 搬进 buffer)的全部硬件操作。
• 负责 burst、width、FIFO threshold、中断/周期回调等细节。PCM 运行库(snd_soc_platform_driver)
• 把上面两者“粘”在一起:
– 用snd_pcm_ops封装open / close / ioctl / pointer / mmap等 PCM 运行时接口;
– 借助 dmaengine API 为每一次trigger(START)向 DMA 引擎申请描述符并启动传输;
– 通过 DAI 驱动在hw_params里把接口时钟、格式配置下去pcm和dma它负责的是将dma缓冲区中的音频数据传送到总线Tx FIFO。
平台类驱动 = CPU-DAI 驱动 + DMA 引擎驱动 + PCM 运行库
编解码器(codec):功能就是编码和解码,还包括一些回声消除的功能和其他组件。将来自声源的模拟信号转换为处理可以操作的数字信号,或者反过来。会对音频信号进行相应的调整并控制音频信号之间的路径。
机器(machine): 板级表示,链接两个音频接口(cpu_dai 和 codec_dai)。该链接在内核中通过struct snd_soc_dai_link实例抽象出来。配置好链接后,机器驱动将注册一个struct snd_soc_card对象,它是linux对于声卡的抽象。
编解码器
编解码器的驱动程序需要满足一些规范:
• 通过定义DAI 和 PCM 配置来提供与其他模块的接口。
• 提供编解码器控制IO挂钩?什么意思?
• 根据用户空间实用程序的需要,公开其他内核空间(kernel control, kcontrol)以动态控制模块行为。(向上提供接口?)
• 定义DPAM weight,为动态电源切换建立DAPM路由,并提供DAC数字静音控制。
知道了规范,我们来看看编解码器的结构。它的驱动程序根据上面的了解,很明显就是包括了编解码设备(组件)本身和DAI组件,它们在与平台绑定时使用。通过devm_snd_soc_register_component(),驱动程序注册一个struct snd_soc_component_driver对象(实际上是编解码器驱动程序的实例,包含指向编解码器的路由、weight、控件和一组编解码器相关函数回调的指针)以及一个或多个struct snd_soc_dai_driver(编解码器DAI驱动程序的一个实例,可能包含一个音频流。如下就注册了一个组件devm_snd_soc_register_component(dev,&component_driver, &dai, 1))。看下它们的结构体,接下来会跟着这个结构体来了解编解码驱动程序和平台驱动程序。
1 | struct snd_soc_component_driver { |
| 成员 | 作用 |
|---|---|
| name | 组件名称,用于调试和匹配 |
| controls / num_controls | 该组件提供的 ALSA kcontrols(音量、开关、增益等) |
| dapm_widgets / dapm_routes | DAPM(动态音频电源管理)所需的 widget 和路径 |
| probe / remove | 组件被绑定/解绑时的回调 |
| ops | 指向 struct snd_pcm_ops,用于实现 PCM 流操作(open、hw_params、trigger、pointer 等) |
| pcm_construct / pcm_destruct | 可选:手动创建/销毁 PCM 设备 |
| compress_ops | 用于支持压缩音频(如 aptX、AAC) |
| of_xlate_dai_name | 在设备树中解析 DAI 名称 |
| seq_notifier / stream_event | 监听 DAPM 或音频流事件,用于电源管理 |
| probe_order / remove_order | 多组件时的加载顺序控制 |
一个组件驱动由他本身的驱动程序和DAI驱动程序组成,当然也要看struct snd_soc_dai_driver这个结构。这个结构体是干啥的?它是 ASoC(ALSA System on Chip)框架里专门描述 一个数字音频接口(DAI)能力 的结构体。在一条音频链路中,无论是 SoC 内部 I²S/PCM 端口,还是 Codec 的 DAC/ADC 端口,都有一个对应的 snd_soc_dai_driver 实例,用来告诉 ALSA/ASoC:
“我这个接口支持哪些格式、采样率、时钟角色、bclk/lrclk 分频能力、通道数等”。
在驱动中,这个结构体的实例应该和编解码器上的DAI数量相同,它们需要填充和注册(填充在驱动程序里,注册在probe函数中,同时也必须使用devm_snd_soc_register_component()导出(实际上是插入到ASoC全局组件列表里),以便它可以在注册声卡之前由机器驱动程序注册到核心。(各 Component 的 probe 阶段先把自身“挂”到 ASoC 全局表;Machine 驱动在真正注册声卡时再去表里匹配并绑定它们。)
看看结构体:
1 | struct snd_soc_dai_driver { |
我们要理解其他数据结构和struct snd_soc_pcm_stream 和 struct snd_soc_dai_ops,这两个结构体和dai配置息息相关。
DAI操作
上面的dai_driver中包含了两个ops结构体实例抽象。
struct snd_soc_dai_ops中包含了一组回调函数,这些回调函数的作用基本上分为三类,时钟配置回调函数,DAI格式配置回调函数,PCM校正回调函数。
1 | struct snd_soc_dai_ops { |
了解这个结构体能提供什么操作,绑定到driver中,能怎么用。
- 采集和回放的硬件配置
在采集和回放的操作期间,要设置DAI和功能,以便于允许配置底层PCM流。实现这个目标,就要在编解码器和平台驱动程序中为每个操作和每个DAI填充struct snd_soc_pcm_stream的一个实例。(采集和播放操作?怎么填充)
1 | struct snd_soc_pcm_stream { |
控件
上面列出的都是驱动中所需要的结构体,按照组件结构体中的第一个`struct snd_kcontrol_new *controls;` 控件!他的结构体就是struct snd_kcontrol_new,编解码器的驱动程序用控件公开一些可以从用户空间可以修改的编解码器属性。当编解码器初始化的时候,这些音频控件会注册到ALSA核心。
让ai介绍控件类型和如何使用