ALSA是Advanced Linux Sound Architecture 的缩写, 是linux声卡的一种驱动框架,类似的还有OSS, 简单的说,声卡就是一块外接硬件,用来播放和录音的设备,将需处理的数据通过iis和soc进行交互,因此,声卡需要2个部分共同作用,即soc侧和codec侧。典型的音频设计是: 一块电路板上一颗CPU的I2S接口外挂一颗Codec芯片, Codec在外接耳机或功放等. 如下图所示

ALSA音频框架理解:前言

 

以播放为例, 在这样一个硬件结构下,这里准确的说是播放音频所需的一条完整的链路, 涉及到几个模块:

DMA : 负责把用户空间的音频数据搬移至I2S的FIFO.

I2S : 负责以某个采样频率、采样深度、通道数发送音频数据, 也叫dai (Digital Audio Interface).

AFIx (Audio Interface): 负责以某个采样频率、采样深度、通道数接收音频数据, 也称作dai

DAC : 并把数据通过DAC转换后送给耳机等播放.

 

播放音频的简单流程:

音频文件数据==>DMA==> I2S (按照某种采样 单双通道等发送)==>AFIx (按照某种采样 单双通道等接受)==>DAC (数模转换)==>音频输出设备

 

那么,要使整个功能有效,即我们需要实现platform和codec的驱动,linux驱动还是采用通用的做法来解耦合,即分别实现platform 配置soc侧的i2s和dma的功能,codec实现codec侧的APF和数据处理功能。但是由于模块化的关系,每个部分的驱动并非一家公司所写,假设每家公司写单独的模块,那么必须有一个能使platform 和codec关联起来的部分,即machine。从v4.18开始,这些模块采用了component系统来管理了,但这里我们还是用以前的方式说明,便于理解。

 

因此声卡驱动部分可以初步划为三大部分:

Platform : 该模块负责DMA的控制和I2S的控制, 由CPU厂商负责编写此部分代码.

Codec : 该模块负责AFIx的控制和DAC部分的控制(也可以说是芯片自身的功能的控制), 由Codec厂商负责编写此部分代码.

Machine : 用于描述一块电路板, 它指明此块电路板上用的是哪个Platform和哪个Codec, 由电路板商负责编写此部分代码.

 

从设计的角度来讲,不同的独立模块需要匹配,最容易的做法就是查询链表,按照属性匹配即可,那么asoc需要维护2个链表来维护Platform 和 Codec ,即 Platform _list和Codec_list,machine驱动负责遍历这两条链表来匹配需要的Platform和Codec 。

因此驱动的编写就变成填充Platform和 Codec 链表结点。

到这里,其实就可以初步的划分出2个模块就可以了,但是linux为了进一步划分,将i2s和AFIx这类负责数据传输的再次抽出为dai,dai就可分为soc-dai和codec-dai,Platform和Codec 主负责CPU配置,内存分配,dai就主负责数据传输和接口控制器配置。

综上:alsa声卡框架就分为5大部分

硬件相关:Platform  Codec  soc-dai  codec-dai

匹配逻辑相关: machine

 

 

从数据结构的角度来说,上面5大部分可用下面结构体来表示:

ü         struct snd_soc_platform : 用于抽象一个platform, 作用是描述一个CPU的DMA设备及操作函数. 系统中可能有多个platforms, 它们都挂载在全局链表头static LIST_HEAD(platform_list)下面, 不同的platform以name区分.

 

ü         struct snd_soc_codec : 用于抽象一颗Codec. 一颗Codec可能会有多个dai接口, 该结构体的作用是描述与具体dai无关的、Codec内部的工作逻辑, 例如控件/微件/音频路由的描述信息、时钟配置、IO 控制等. 系统中可能有多个Codecs, 它们都挂载在全局链表头static LIST_HEAD(codec_list) 下面, 不同的Codec以name区分.

 

ü         struct snd_soc_dai : 用于描述一个dai, 既可以是CPU侧的dai(I2S), 也可以是Codec侧的dai(AFIx). 一颗CPU可能有多个I2S, 一个Codec也可能有多个AFIx, 因此系统中会有很多个dai, 它们都挂载在全局链表头static LIST_HEAD(dai_list)下面, 不同的dai以name区分.

 

ü      struct snd_soc_card:用来描述platform、codec、dai的联系关系。

 

注册到list的函数:

针对platform:使用snd_soc_register_platform挂载一个snd_soc_platform 节点

针对Codec :  使用snd_soc_register_codec挂载一个snd_soc_codec 节点

针对cpu_dai: 使用snd_soc_register_dai挂载一个snd_soc_dai 节点

针对Machine: 使用snd_soc_register_card挂载一个 snd_soc_card节点(声卡节点)

 

假设上面的几大部分的都实现并已通过注册函数挂载到list上,那么,还需要实现一个Machine 的数据结构,来进行上列组件的匹配,linux中使用struct snd_soc_card,结构体dai_link属性即指定了上列组件的关系。dailink可以认为是一条完整数据流的链路,一个声卡中不止有一条链路,可存在多个dailink。

对于一个声卡WM8994原理图:

ALSA音频框架理解:前言

 

 

针对上图我们可知,一张声卡存在多个完整的数据链路,一条链路用一个dailink来描述其所需的硬件资源,那么一个声卡就是多个完整链路描述的集合。

 

例如:

static struct snd_soc_dai_link imx_dai[] = {
{
.name = "HiFi",
.stream_name = "HiFi",
.codec_dai_name = "wm8904",   //指定codec_dai
.codec_name = "wm8904.0-001a", //指定codec
.cpu_dai_name = "imx-ssi.1",  //指定soc-dai
.platform_name = "imx-pcm-audio.1", //指定platform
.init = imx_wm8904_init,
.ops = &imx_hifi_ops,
},

 

那么上面这个结构体,表示了当前一条dalink所用的4大部分资源:

Platform:imx-pcm-audio

Codec:wm8904.0-001a

soc-dai:  imx-ssi

codec-dai:wm8904

网上找的一张图,画得很好,这里直接拿来用用,表示了一个dalink的4大部分。

ALSA音频框架理解:前言

 

最后在调用snd_soc_register_card注册一个声卡,在这个注册函数中,会按照上面各个的

name去在相应的链表中去查找匹配,由于我们前面假设了3大部分的驱动已经添加到list

中,那么就可以实现整个声卡的功能了,上面5大部分的驱动实际上是所需的硬件资源,提

供了硬件操作接口,对于整个系统来讲,如何有效的调用硬件资源而实现具体功能,必然会

有一个核心逻辑框架,以回调的方式来实现整个框架的正常运行,这与同样的tty、spi等

驱动框架思想一致。

总结一下这一篇讲的内容,即将声卡分为5个部分,platfrom、codec、soc-dai、codec-dai、

machine。 分别注册进platfrom_list、codec_list、dai_list,通过machine中定义的

dailink来描述链路的关联,从而通过遍历匹配来获取链路所需要的硬件资源和操作接口,

将这些接口再注册进一个与硬件无关的逻辑的音频音频处理框架中,从而利用回调实现了真

整个音频功能。

下一篇,将分析这5大硬件部分具体实现的部分和可提供的回调接口。

相关文章:

  • 2021-08-18
  • 2021-05-16
  • 2021-11-07
  • 2022-12-23
  • 2021-11-29
  • 2022-12-23
  • 2021-08-03
  • 2022-12-23
猜你喜欢
  • 2021-07-07
  • 2021-04-08
  • 2021-06-21
  • 2021-11-14
  • 2022-12-23
  • 2022-12-23
  • 2021-12-31
相关资源
相似解决方案