【DAPM杂谈之一】DAPM作用与内核文档解读

本文主要分析DAPM的设计与实现
内核的版本是:linux-5.15.164,下载链接: Linux内核下载
主要讲解有关于DAPM相关的知识,会给出一些例程并分析内核如何去实现的
/*****************************************************************************************************************/
声明: 本博客内容均由 芯心智库-CSDN博客原创,转载or引用请注明出处,谢谢!
创作不易,如果文章对你有帮助,麻烦点赞 收藏支持~感谢
/*****************************************************************************************************************/
目录

零、本文主要内容

一、DAPM的作用

二、内核文档解读

[2.1 Description](#2.1 Description)

[2.2 DAPM Widgets](#2.2 DAPM Widgets)

[2.2.1 Stream Domain Widgets](#2.2.1 Stream Domain Widgets)

[2.2.2 Path Domain Widgets](#2.2.2 Path Domain Widgets)

[2.2.3 Machine domain Widgets](#2.2.3 Machine domain Widgets)

[2.2.4 Codec (BIAS) Domain](#2.2.4 Codec (BIAS) Domain)

[2.2.5 一种虚拟的控制方式--Virtual Widgets](#2.2.5 一种虚拟的控制方式--Virtual Widgets)

[2.3 Codec/DSP Widget Interconnections](#2.3 Codec/DSP Widget Interconnections)

[2.3.1 Machine Widget Interconnections](#2.3.1 Machine Widget Interconnections)

[2.4 Endpoint Widgets](#2.4 Endpoint Widgets)

[2.5 DAPM Widget Events](#2.5 DAPM Widget Events)

三、总结几个重要的知识点


零、本文主要内容

本文主要是介绍一下什么是DAPM,分析其设计出来的目的是什么,并将会解读一下Linux官方对于DAPM是怎么描述的。
为何需要解读Linux文档呢,因为站在巨人的肩膀上才能看得更远,网上大多数资料都是抄袭泛滥,多数都是你引用我,我引用他,他又引用别人...口口相传后实际含义就有了偏差....

一、DAPM的作用

在DAPM的之前,可能需要了解一下kcontrol的概念,你才能知道为何需要推出DAPM了,关于kcontrol可以参考下面的文章(下次有空我也总结一下):
8.声卡驱动05-自己实现alsa驱动-虚拟声卡-kcontrol_8.声卡驱动01-自己实现alsa驱动-虚拟声卡-匹配-CSDN博客
Linux内核4.14版本------alsa框架分析(9)------ASoC(Codec control的创建)_alsa-lib中如何获取设备controls的名字-CSDN博客
Linux系统ALSA DAPM 中的控制(Controls)功能介绍-CSDN博客
我这边总结一下DAPM的作用:

  1. 自动化使能与失能路径:这是DAPM比较kcontrol引入的最大特征。我们知道对于外设,上电和下电的顺序很重要,不恰当的顺序容易引起pop音,如果全部硬件都抽象成kcontrol,那么你就需要手动的安排一下上电或者下电的顺序,但是如果定义成DAPM类型,那么系统会帮你按照一定的顺序进行上下电。
  2. 对用户层透明:DAPM和kcontrol都有的作用,对寄存器等操作进行封装,从而在用户层使用更方便,用户无须了解外设如何控制,只需要执行对应的命令,底层就会配置好外设
  3. 独立性:DAPM和kcontrol都有的作用,其在内核中拥有属于自己的一套电源管理系统,不会与其他电源管理系统冲突
    那么你肯定会有疑问,那既然DAPM比kcontrol好这么多,为什么Linux内核不抛弃widget?我认为的原因是有些控制并不需要用到DAPM的自动化特征,比如声音大小的控制。在这些方面,widget更简单轻便可以实现,而DAPM的最大优势在于音频路由的控制方面。你也可以认为DAPM是kcontrol的加强版本!

二、内核文档解读

官网原链接是: 内核文档,网址为: https://www.kernel.org/doc/html/v5.15/sound/soc/dapm.html
文档主要是关于五个方面进行解读:

  1. Description:基本描述与power domains的划分
  2. DAPM Widgets:DAPM Widgets种类的划分、以及各个domains与DAPM Widgets的关系
  3. Codec/DSP Widget Interconnections:Widget如何进行连接,也就是route的相关内容
  4. Endpoint Widgets:端点widget,介绍端点widget的种类,这个端点widget决定一条route是否完整,route两端都是端点widget才能自动上电,这也是自动化上下电的前提
  5. DAPM Widget Events:个别widget支持对特定事件进行处理
    这时你可以会有疑问,也就是DAPM Widgets和power domains有什么区别?
    其实可以这么理解:
    power domains表示一组相关的Widgets,而DAPM Widgets 则是代表具体音频组件(如 ADC、DAC、Mixer 等)的抽象。 他们是包含关系,一组power domains会包含若干个DAPM Widgets。如何知道每个power domains包括哪些DAPM Widgets,可以参考下面的源码:
cpp 复制代码
/linux-5.15.164/include/sound/soc-dapm.h

那么就详细解读一下内核文档的具体内容吧

2.1 Description

下面这段是Linux关于DAPM的介绍以及DAPM会给设备带来什么好处:

cpp 复制代码
Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices to use the minimum amount of power within the audio subsystem at all times. It is independent of other kernel PM and as such, can easily co-exist with the other PM systems.
DAPM is also completely transparent to all user space applications as all power switching is done within the ASoC core. No code changes or recompiling are required for user space applications. DAPM makes power switching decisions based upon any audio stream (capture/playback) activity and audio mixer settings within the device.
DAPM spans the whole machine. It covers power control within the entire audio subsystem, this includes internal codec power blocks and machine level power systems.

这里讲得特点和第一章节分析DAPM的作用差不多,因为DAPM自动上下电的策略,而不是类似kcontrol那样直接就控制硬件,因此在DAPM第一段话就指出DAPM是可以为设备省电的。
想要真正悟到这句话,还需要结合route和complete的源码来看就会比较直观了(后面文章会细讲),这里你只需要知道, 就算用amixer或者tinymix了DAPM的控件后,设备不是立马上电,需要某些条件满足才能上电。
然后官方文档描述了四大domain:
这里你可以细看官方的文档,我总结一下如下:

cpp 复制代码
Codec Bias domain:管理核心编解码器电源 (VREF/VMID),通常在编解码器挂载或挂载移除时处理。
Platform/Machine domain:控制物理输入输出,比如耳机插拔事件。
Path domain:与音频信号路径相关,用户通过混音器配置(如 alsamixer)触发。
Stream domain:控制音频流的启停。也就是ADC跟DAC,对应录音与播放。例如 aplay、arecord。

按照笔者的理解,这里的划分跟ASOC的三大件--codec、platform、machine的划分有点类似

2.2 DAPM Widgets

内核文档这段解释了各种DAPM类型以及他们的含义,我这边也总结一 下:

|-----------|------------------------------------|------------------------------------|
| 组件 | 描述 | DAPM Widget具体含义 |
| Mixer | 混合多个模拟信号为一个单一模拟信号 | 用于将多个音频信号组合成一个输出信号,通常用于音量控制或音频混音。 |
| Mux | 模拟开关,输出多个输入中的一个 | 允许选择多个输入信号中的一个进行传输,可以用于不同音频源的切换。 |
| PGA | 可编程增益放大器或衰减控件 | 用于调节音频信号的增益或衰减,可以动态调整音量。 |
| ADC | 模拟到数字转换器 | 将模拟信号转换为数字信号,通常用于将模拟音频输入转换为数字格式。 |
| DAC | 数字到模拟转换器 | 将数字信号转换为模拟信号,用于将数字音频输出转换为模拟输出。 |
| Switch | 模拟开关 | 控制音频信号的传输或切换,通常在信号路径中切换不同的信号源。 |
| Input | 编解码器输入引脚 | 音频输入引脚,用于接收外部信号到编解码器。 |
| Output | 编解码器输出引脚 | 音频输出引脚,用于输出编解码器的音频信号。 |
| Headphone | 耳机(和可选插孔) | 用于连接耳机输出或耳机插孔,通常是音频输出的最后一步。 |
| Mic | 麦克风(和可选插孔) | 用于连接麦克风输入或麦克风插孔,捕捉外部声音信号。 |
| Line | 线路输入/输出(和可选插孔) | 用于接收或发送线路音频信号,可以是音频源或音频输出设备。 |
| Speaker | 扬声器 | 用于将音频信号转换为声波输出的硬件设备。 |
| Supply | 其他控件使用的电源或时钟供给控件 | 提供所需电力或时钟信号给其他控件,如DAC、ADC等。 |
| Regulator | 为音频组件提供电力的外部调节器 | 外部电源调节器,提供稳定的电压和电流供应,确保音频组件正常运行。 |
| Clock | 为音频组件提供时钟的外部时钟 | 提供稳定的时钟信号,确保音频数据的同步传输。 |
| AIF IN | 音频接口输入(带TDM槽掩码) | 音频接口输入控件,用于接收通过TDM协议传输的音频数据。 |
| AIF OUT | 音频接口输出(带TDM槽掩码) | 音频接口输出控件,用于发送通过TDM协议传输的音频数据。 |
| Siggen | 信号发生器 | 用于生成特定频率或幅度的测试音频信号,通常用于音频设备的调试。 |
| DAI IN | 数字音频接口输入 | 数字音频输入控件,用于接收数字音频数据。 |
| DAI OUT | 数字音频接口输出 | 数字音频输出控件,用于发送数字音频数据。 |
| DAI Link | 两个DAI结构之间的DAI链路 | 在两个数字音频接口之间建立连接,通常用于实现音频传输。 |
| Pre | 特殊的PRE控件(在其他控件之前执行) | 特殊类型控件,用于在其他控件之前执行初始化或前置处理。 |
| Post | 特殊的POST控件(在其他控件之后执行) | 特殊类型控件,用于在其他控件之后执行后置处理或清理工作。 |
| Buffer | DSP中的组件间音频数据缓冲区 | 用于在DSP内部存储音频数据,在多个组件之间传递音频数据。 |
| Scheduler | DSP内部调度器,负责调度组件/处理流水线工作 | 控制DSP内部任务的执行顺序,确保音频处理任务按顺序执行。 |
| Effect | 执行音频处理效果的控件 | 用于执行音频效果处理,如回声、混响、均衡等。 |
| SRC | DSP或编解码器内的采样率转换器 | 用于转换音频信号的采样率,通常用于不同采样率之间的信号适配。 |
| ASRC | DSP或编解码器内的异步采样率转换器 | 用于异步转换采样率,能够处理源信号和目标信号不同步的情况。 |
| Encoder | 将音频数据从一种格式(通常为PCM)编码为另一种通常为压缩格式的控件 | 对音频数据进行编码处理,将其转换为压缩格式以减小数据大小。 |
| Decoder | 将音频数据从压缩格式解码为未压缩格式(如PCM)的控件 | 对压缩格式的音频数据进行解码处理,恢复为标准未压缩格式(如PCM)。 |

2.2.1 Stream Domain Widgets

这里是音频流相关的输入输出,内核文档指出了四大类型:

  • ADC (Analog-to-Digital Converter):模拟到数字的转换器,用于将模拟音频信号转换为数字信号。
  • DAC (Digital-to-Analog Converter):数字到模拟的转换器,用于将数字音频信号转换为模拟信号。
  • AIF IN:音频接口输入,通常用于数字音频输入(如 TDM、I2S)。
  • AIF OUT:音频接口输出,用于数字音频输出。
    其实就是两组输入输出接口,一组是数字输入输出,一组是模拟输入输出,源码的体现如下:
cpp 复制代码
/linux-5.15.164/include/sound/soc-dapm.h
219行  /* stream domain */

2.2.2 Path Domain Widgets

Path Domain Widgets 负责控制和影响音频信号路径。官方文档没有具体指出这部分包括什么,这里通过下面的源码分析一下:

cpp 复制代码
/linux-5.15.164/include/sound/soc-dapm.h
 82行  /* path domain */

其实也就是五种,

  • 第一种:PGA,也就是增益放大器,通常用于调节信号的增益或衰减,比如:
cpp 复制代码
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert, wcontrols, wncontrols)
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert, wcontrols)
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, wncontrols, wevent, wflags)
#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, wevent, wflags)
  • 第二种:Mixer,也就是混音器,用于将多个音频输入信号混合成一个输出信号,它能够控制每个输入信号的音量或开关状态。比如:
cpp 复制代码
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, wcontrols, wncontrols)
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, wcontrols, wncontrols)
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, wcontrols)
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, wcontrols)
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, wncontrols, wevent, wflags)
  • 第三种,Output Driver,也就是输出驱动,通常用于提供最终输出的信号,它可以驱动扬声器或其他音频输出设备,例如用在DAC后边增强DAC的输出,代码:
cpp 复制代码
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert, wcontrols, wncontrols)
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, wncontrols, wevent, wflags)
  • 第四种,Switch,也就是开关,控制音频路径的开关状态,比如:
cpp 复制代码
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols)
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, wevent, wflags)
  • 第五种,Mux,复用器,用于选择不同的信号源,通常用于多个输入之间的选择。比如:
cpp 复制代码
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols)
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, wevent, wflags)

2.2.3 Machine domain Widgets

这个跟codec domain有点区别,主要体现在 不与特定的 codec 寄存器位关联,更多的是 与硬件接口和物理组件相关。官方指出的有三个类型:

  • Speaker Amp:扬声器放大器
  • Microphone Bias:麦克风偏差
  • Jack connectors:插孔连接器

2.2.4 Codec (BIAS) Domain

codec domain区别于Machine domain,他不直接关联任何硬件 widget,而是通过 codec 的 DAPM 事件处理器来控制。这个事件处理器会在以下两种情况被调用:

  • 当 codec 的电源状态(power state)发生变化时,特别是与音频流(stream)事件相关时。
  • 当内核的电源管理(PM)事件触发时。

2.2.5 一种虚拟的控制方式--Virtual Widgets

Virtual Widgets主要就是一个虚拟的控制,个人感觉是为了代码的可理解性才提出来的,DAPM在有了route的概念后,可以做需要事情了,加上这个Virtual Widgets应该可以做更多的事情(尽管我现在没怎么使用),源码中示例了一个:

cpp 复制代码
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),

SND_SOC_DAPM_NOPM 是一个特殊的标志,表示不进行任何电源管理(No Power Management)。而NULL 和 0表示没有控制项和数量。

2.3 Codec/DSP Widget Interconnections

内核文档这一章节讲得就是DAPM中的一个重要的概念--route了
在内核文档的原文中是这样描述的:

cpp 复制代码
Widgets are connected to each other within the codec, platform and machine by audio paths (called interconnections). 
Each interconnection must be defined in order to create a map of all audio paths between widgets.

This is easiest with a diagram of the codec or DSP (and schematic of the machine audio system), 
as it requires joining widgets together via their audio signal paths.

首先给出了interconnections的定义:

  • Widgets 通过音频路径 连接在一起,这些路径(audio paths)称为互联(Interconnections),它们定义了音频信号如何从一个widgets传递到另一个widgets。
    然后是如何实现上面的概念:
  • with a diagram of the codec or DSP (and schematic of the machine audio system)
    也就是通过图表和示意图来表示,那么是何种图表和示意图呢?官方文档也指出是下面这样:
cpp 复制代码
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},

格式如下:

cpp 复制代码
Destination Widget <=== Path Name <=== Source Widget

如果需要路径控制的话,那么中间的"Path Name"可以写成"NULL"。
然后文档最后给出了连接方式和注册的方式:

  • 连接的方式:snd_soc_dapm_connect_input(codec, sink, path, source);
  • 注册的方式:snd_soc_dapm_new_widgets(codec)

2.3.1 Machine Widget Interconnections

这部分跟上文的内容基本一致。这不详细介绍了。

2.4 Endpoint Widgets

这个也就是一个complete path的重要概念,也就是在音频中,应该根据什么进行判断路径已经完整了(如果路径完整了就会开始按照顺序上电)。
这里判断的依据就是Endpoint Widgets。
只有当一条路径的两头都是Endpoint Widgets的时候,才能视为路径已经完整了,在官方的文档中也给我指出了哪些是Endpoint Widgets:

  • Headphone Jack 耳机插孔
  • Internal Speaker 内置扬声器
  • Internal Mic 内置麦克风
  • Mic Jack 麦克风插孔
  • Codec Pins 编解码器引脚
    官方文档这里给的是概念内容,如果要追踪到代码中,Endpoint Widgets的类型应该是下面的:
cpp 复制代码
/linux-5.15.164/sound/soc/soc-dapm.c
3618  struct snd_soc_dapm_widget *
3619  snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
3620  			 const struct snd_soc_dapm_widget *widget)

关键部分:

  • w->is_ep:通过设置此属性来标识端点类型。端点可以是源(Source)或接收端(Sink)。定义如下:
cpp 复制代码
#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN)
#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT)

总结一下,也就是下面的8种:

|---------------------|------------------|------------------------|-----------------|
| Widget ID | Widget Name | Endpoint Type | Description |
| snd_soc_dapm_mic | Microphone | SND_SOC_DAPM_EP_SOURCE | 输入端点,作为音频信号源。 |
| snd_soc_dapm_input | Input | SND_SOC_DAPM_EP_SOURCE | 输入端点,作为音频信号源。 |
| snd_soc_dapm_spk | Speaker | SND_SOC_DAPM_EP_SINK | 输出端点,作为音频信号接收端。 |
| snd_soc_dapm_hp | Headphone | SND_SOC_DAPM_EP_SINK | 输出端点,作为音频信号接收端。 |
| snd_soc_dapm_output | Output | SND_SOC_DAPM_EP_SINK | 输出端点,作为音频信号接收端。 |
| snd_soc_dapm_vmid | Virtual Midrail | SND_SOC_DAPM_EP_SOURCE | 输入端点,作为音频信号源。 |
| snd_soc_dapm_siggen | Signal Generator | SND_SOC_DAPM_EP_SOURCE | 输入端点,作为音频信号源。 |
| snd_soc_dapm_sink | Sink | SND_SOC_DAPM_EP_SINK | 输出端点,作为音频信号接收端。 |

调用的函数在:
dapm_generic_check_power

  • is_connected_input_ep()
  • is_connected_output_ep()

2.5 DAPM Widget Events

DAPM另外还向我们提供了一些事件,以便我们可以对自己感兴趣的事件可以处理。其实这个很容易理解,就是当系统的状态发生了改变后,DAPM可以根据系统具体改变的内容,进行变化,这里ALSA定义了6种事件,Widget可以根据自己提点去响应这些事件,具体的事件如下:

  • SND_SOC_DAPM_PRE_PMU**(0x1)**: 在 widget 电源打开之前触发。
  • SND_SOC_DAPM_POST_PMU**(0x2)**: 在 widget 电源打开之后触发。
  • SND_SOC_DAPM_PRE_PMD**(0x4)**: 在 widget 电源关闭之前触发。
  • SND_SOC_DAPM_POST_PMD**(0x8)**: 在 widget 电源关闭之后触发。
  • SND_SOC_DAPM_PRE_REG**(0x10)**: 在音频路径设置之前触发。
  • SND_SOC_DAPM_POST_REG**(0x20)**: 在音频路径设置之后触发。
    内核文档还给我们举了个例子:
cpp 复制代码
/* turn speaker amplifier on/off depending on use */
static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
{
      gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
      return 0;
}

/* corgi machine dapm widgets */
static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
      SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
  • event 参数是传递的事件类型,SND_SOC_DAPM_EVENT_ON(event) 是用来判断事件类型是否要求设备打开(或关闭)。
  • gpio_set_value(CORGI_GPIO_APM_ON, ...) 是控制GPIO引脚的函数,开关扬声器功放。CORGI_GPIO_APM_ON 是控制扬声器功放电源的GPIO引脚。

三、总结几个重要的知识点

  • DAPM和kcontrol的关系
    -可以看做DAPM为kcontrol的加强版本,DAPM具备有了自动使能/失能通路的特点
  • DAPM的种类
    -主要是分成四大类:Codec Bias domain、Platform/Machine domain、Path domain、Stream domain。详细内容见本文的第2.2节
  • 如何实现自动使能/失能通路
    -抽象出route、Endpoint Widgets, route表明了每个wridget的连接关系,而Endpoint Widgets是头部或者尾部,当连接关系能满足首尾都是Endpoint Widgets,则视为这个通路已经完整,系统会按照预定的顺序上电
  • DAPM提供哪些扩展功能
    -提供了6个事件类型,每个Widgets可以去注册相应的事件,随后当事件被满足后,将会调用相应的回调函数
相关推荐
x66ccff26 分钟前
️ 如何将 Julia 包切换为本地开发版本?以 Reactant 为例
linux·服务器·julia
计算机小混子41 分钟前
C++实现设计模式---状态模式 (State)
c++·设计模式·状态模式
想做富婆1 小时前
linux: 文本编辑器vim
linux·运维·服务器
别断我一连胜1 小时前
系统看门狗配置--以ubuntu为例
linux·运维·ubuntu
KeyPan1 小时前
【Ubuntu与Linux操作系统:二、图形界面与命令行】
linux·运维·服务器·pytorch·ubuntu·机器学习·计算机视觉
KeyPan1 小时前
【Ubuntu与Linux操作系统:四、文件与目录管理】
linux·运维·服务器·算法·ubuntu
L_09071 小时前
【C】初阶数据结构1 -- 时间复杂度与空间复杂度
c语言·数据结构
昵称难产中2 小时前
OpenStack 网络服务的插件架构
架构·云计算·openstack
誓约酱2 小时前
Linux下ext2文件系统
android·linux·c语言·数据库·c++·后端·ubuntu