音频电源动态管理--概述

2019-07-13 22:24发布

DAPM--Dynamic Audio Power Management,对应结构体是snd_soc_dapm_widget和snd_soc_dapm_route,对应的操作函数是snd_soc_dapm_new_controls()、snd_soc_dapm_add_routes()和snd_soc_dapm_new_widgets()。在我看来,DAPM是音频驱动初接触者的噩梦。从何处来,到何处去?它字面上的意义是音频电源动态管理,但是往往困惑于它是怎么被触发的?而最郁闷的是:这方面的资料是最少的,我涉猎多日,却战果寥寥,看来看去还是内核文档dapm.txt最有参考性。先把这个文档贴上:   Dynamic Audio Power Management for Portable Devices
=================================================== 1. Description
============== 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. There are 4 power domains within DAPM    1. Codec domain - VREF, VMID (core codec and audio power)
      Usually controlled at codec probe/remove and suspend/resume, although
      can be set at stream time if power is not needed for sidetone, etc.    2. Platform/Machine domain - physically connected inputs and outputs
      Is platform/machine and user action specific, is configured by the
      machine driver and responds to asynchronous events e.g when HP
      are inserted    3. Path domain - audio susbsystem signal paths
      Automatically set when mixer and mux settings are changed by the user.
      e.g. alsamixer, amixer.    4. Stream domain - DACs and ADCs.
      Enabled and disabled when stream playback/capture is started and
      stopped respectively. e.g. aplay, arecord. All DAPM power switching decisions are made automatically by consulting an audio
routing map of the whole machine. This map is specific to each machine and
consists of the interconnections between every audio component (including
internal codec components). All audio components that effect power are called
widgets hereafter.
2. DAPM Widgets
=============== Audio DAPM widgets fall into a number of types:-  o Mixer      - Mixes several analog signals into a single analog signal.
 o Mux        - An analog switch that outputs only one of many inputs.
 o PGA        - A programmable gain amplifier or attenuation widget.
 o ADC        - Analog to Digital Converter
 o DAC        - Digital to Analog Converter
 o Switch     - An analog switch
 o Input      - A codec input pin
 o Output     - A codec output pin
 o Headphone  - Headphone (and optional Jack)
 o Mic        - Mic (and optional Jack)
 o Line       - Line Input/Output (and optional Jack)
 o Speaker    - Speaker
 o Supply     - Power or clock supply widget used by other widgets.
 o Pre        - Special PRE widget (exec before all others)
 o Post       - Special POST widget (exec after all others) (Widgets are defined in include/sound/soc-dapm.h) Widgets are usually added in the codec driver and the machine driver. There are
convenience macros defined in soc-dapm.h that can be used to quickly build a
list of widgets of the codecs and machines DAPM widgets. Most widgets have a name, register, shift and invert. Some widgets have extra
parameters for stream name and kcontrols.
2.1 Stream Domain Widgets
------------------------- Stream Widgets relate to the stream power domain and only consist of ADCs
(analog to digital converters) and DACs (digital to analog converters). Stream widgets have the following format:- SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert), NOTE: the stream name must match the corresponding stream name in your codec
snd_soc_codec_dai. e.g. stream widgets for HiFi playback and capture SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
2.2 Path Domain Widgets
----------------------- Path domain widgets have a ability to control or affect the audio signal or
audio paths within the audio subsystem. They have the following form:- SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls) Any widget kcontrols can be set using the controls and num_controls members. e.g. Mixer widget (the kcontrols are declared first) /* Output Mixer */
static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
}; SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
 ARRAY_SIZE(wm8731_output_mixer_controls)), If you dont want the mixer elements prefixed with the name of the mixer widget,
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
as for SND_SOC_DAPM_MIXER. 2.3 Platform/Machine domain Widgets
----------------------------------- Machine widgets are different from codec widgets in that they don't have a
codec register bit associated with them. A machine widget is assigned to each
machine audio component (non codec) that can be independently powered. e.g.  o Speaker Amp
 o Microphone Bias
 o Jack connectors A machine widget can have an optional call back. e.g. Jack connector widget for an external Mic that enables Mic Bias
when the Mic is inserted:- static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
{
 gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
 return 0;
} SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
2.4 Codec Domain
---------------- The codec power domain has no widgets and is handled by the codecs DAPM event
handler. This handler is called when the codec powerstate is changed wrt to any
stream event or by kernel PM events.
2.5 Virtual Widgets
------------------- Sometimes widgets exist in the codec or machine audio map that don't have any
corresponding soft power control. In this case it is necessary to create
a virtual widget - a widget with no control bits e.g. SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0), This can be used to merge to signal paths together in software. After all the widgets have been defined, they can then be added to the DAPM
subsystem individually with a call to snd_soc_dapm_new_control().
3. Codec Widget Interconnections
================================ Widgets are connected to each other within the codec 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 (and schematic of the machine audio
system), as it requires joining widgets together via their audio signal paths. e.g., from the WM8731 output mixer (wm8731.c) The WM8731 output mixer has 3 inputs (sources)  1. Line Bypass Input
 2. DAC (HiFi playback)
 3. Mic Sidetone Input Each input in this example has a kcontrol associated with it (defined in example
above) and is connected to the output mixer via it's kcontrol name. We can now
connect the destination widget (wrt audio signal) with it's source widgets.  /* output mixer */
 {"Output Mixer", "Line Bypass Switch", "Line Input"},
 {"Output Mixer", "HiFi Playback Switch", "DAC"},
 {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, So we have :-  Destination Widget  <=== Path Name <=== Source Widget Or:-  Sink, Path, Source Or :-  "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch". When there is no path name connecting widgets (e.g. a direct connection) we
pass NULL for the path name. Interconnections are created with a call to:- snd_soc_dapm_connect_input(codec, sink, path, source); Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and
interconnections have been registered with the core. This causes the core to
scan the codec and machine so that the internal DAPM state matches the
physical state of the machine.
3.1 Machine Widget Interconnections
-----------------------------------
Machine widget interconnections are created in the same way as codec ones and
directly connect the codec pins to machine level widgets. e.g. connects the speaker out codec pins to the internal speaker.  /* ext speaker connected to codec pins LOUT2, ROUT2  */
 {"Ext Spk", NULL , "ROUT2"},
 {"Ext Spk", NULL , "LOUT2"}, This allows the DAPM to power on and off pins that are connected (and in use)
and pins that are NC respectively.
4 Endpoint Widgets
===================
An endpoint is a start or end point (widget) of an audio signal within the
machine and includes the codec. e.g.  o Headphone Jack
 o Internal Speaker
 o Internal Mic
 o Mic Jack
 o Codec Pins When a codec pin is NC it can be marked as not used with a call to snd_soc_dapm_set_endpoint(codec, "Widget Name", 0); The last argument is 0 for inactive and 1 for active. This way the pin and its
input widget will never be powered up and consume power. This also applies to machine widgets. e.g. if a headphone is connected to a
jack then the jack can be marked active. If the headphone is removed, then
the headphone jack can be marked inactive.
5 DAPM Widget Events
==================== Some widgets can register their interest with the DAPM core in PM events.
e.g. A Speaker with an amplifier registers a widget so the amplifier can be
powered only when the spk is in use. /* 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); Please see soc-dapm.h for all other widgets that support events.
5.1 Event types
--------------- The following event types are supported by event widgets. /* dapm event types */
#define SND_SOC_DAPM_PRE_PMU 0x1  /* before widget power up */
#define SND_SOC_DAPM_POST_PMU 0x2  /* after widget power up */
#define SND_SOC_DAPM_PRE_PMD 0x4  /* before widget power down */
#define SND_SOC_DAPM_POST_PMD 0x8  /* after widget power down */
#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */

参考文章链接 http://blog.chinaunix.net/uid-9185047-id-3228644.html
直接来自内核的Documentationsoundalsasoc中dapm.txt 1.描述 音频电源动态管理(DAPM)的目的是为了在音频子系统中允许便携式的linux设备一直使用最小的电量。它独立于其它内核电源管理,因此可以很方便的和其它电源管理系统共存。 DAPM对于所有用户空间的程序也是完全透明的,因为所有的电源开关都是由ASoC core来切换的。用户空间的程序 不需要改变代码和重新编译 。DAPM使得电源开关的切换基于设备的任何的音频流(采集或回放)活动和音频混音器设置之上。 DAPM贯穿于整个machine。它覆盖整个音频子系统的电源控制,包括内部的codec电源模块和machine等级的电源系统。 DAPM中有4个电源相关的域: 1.Codec域--VREF(参考电压),VMID(core codec和音频电源) 通常在codec probe/remove和suspend/resume时被控制,因此可以在侧音不需要电源时在stream time被设置。 2.Platform/Machine 域  --   是platform/machine和user action相关的输入输出物理连接,由machine driver配置并且响应一不事件。比如HP插入时 3.Path 域 --- 音频子系统的信号路径 当mixer和mux设置被用户改变时自动设置,比如alsamixer,amixer 4.Stream 域 -- DACs和ADCs 当回放和采集流开始或者结束时被相应的使能和禁止,比如aplay,arecord 所有DAPM电源开关的策略由整个machine的一个音频线路图自动控制。这个线路图是和每一个machine相关的,并且由所有音频部件(包括内部的codec部件)的互联系统构成。以后,所有影响电源的音频部件被叫做widget
2.DAPM 部件 DAPM音频部件分为一系列的类型: Mixer--将一些模拟信号混合为一个单一的模拟信号 Mux--一个控制多路输入但只有一个输出的模拟开关 PGA--一个可编程的增益放大器或者衰减部件 ADC--模拟信号到数字信号转换器 DAC--数字信号到模拟信号转换器 Switch--一个模拟开关 Input--codec的一个输出接口 Output--codec的一个输入接口 Headphone--耳机(和可选的Jack) Mic--Mic(和可选的Jack) Line--线路输入/输出(和可选的Jack) Speaker--扬声器 Supply--被其它部件使用的电源和时钟供应部件 Pre--特殊的前置部件(在所有其它部件前执行) Post--特殊的后置部件(在所有其它部件后执行) (Widgets 定义在include/sound/soc-dapm.h) widgets经常在codec的driver和machine的driver中被添加.在soc-dapm.h中有一些很方便的宏可以用来快速的构建codecs和machines 的一系列的DAPM widgets。 大多数的widgets有一个name,register,shift,和invert。一些widgets有一些用于stream名字和kcontrols的多余的参数。 2.1 Stream Domain Widgets ------------------------------------- Stream Widgets 和stream power domain相关并且包含ADCs和DACs。 Stream Widgets有如下的格式: SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
注意:stream的名字必须和相应的你snd_soc_codec_dai中codec的stream name 一致 例如。用于HiFi回放和采集的stream widegets SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1), SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),

2.2 Path Domain Widgets --------------------------------- Path domain wideget 有控制和影响音频子系统中音频信号或音频路径的能力。他们有如下的格式: SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
所有widget kcontrols可以用controls和num_controls 成员设置。 比如.mixer widget(首先定义kcontrols) /*Output Mixer*/ static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {  SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),   SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),   SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),  }; SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,ARRAY_SIZE(wm8731_output_mixer_controls)), 如果你不想以mixer widget的name作为mixer元素的名字前缀,你可以使用SND_SOC_DAPM_MIXER_NAMED_CTL宏代替,参数和使用SND_SOC_DAPM_MIXER一样 2.3 Platform/Machine domain Widgets -------------------------------------------------- Machine widgets和codec widgets不同的是,它们没有codec 寄存器位。一个machine widget和每一个可以被独立的供电的machine音频控件相关(非codec),例如  --扬声器放大器  --Microphone Bias  --Jack 连接器 一个machine widget可以有一个可选的回调函数。 例如,对于一个外部的Mic,Jack connector widget在mic插入的时候使能Mic Bias static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)  gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));  return 0;  }
SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
2.4 Codec Domain ------------------------ codec power domain 没有widgets,它被codec DAPM时间处理函数处理。这个处理函数在codec 的powerstate被任何stream event或内核PM 事件改变时调用。 2.5 Virtual Widgets ------------------------ 一些widgets存在于没有任何相应软件电源控制的codec或者machine音频位图。这种情况下需要创建一个虚拟的widget---一个没有控制bits的widget。例如 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
这可以被用于在软件上合并signal paths在一起。 在所有的widget被定义之后,他们可以通过调用snd_soc_dapm_new_control()被独立的添加到DAPM子系统。
3.Codec Widget Interconnections(相互连接) ================================= Widgets在codec和machine上通过audio paths相互连接。每一个连接必须被定义以便在widgets间创建一个所有 音频路径的map 建立codec或者machine音频系统的是很简单的,这是因为它需要通过他们的音频信号路径 把widget组合在一起
e.g., from the WM8731 output mixer (wm8731.c) 
WM8731输出mixer有3个输入(源)
 1. Line Bypass Input--------------------线路旁路输入
 2. DAC (HiFi playback)-----------------DAC(HiFi 回放)
 3. Mic Sidetone Input-------------------Mic 侧音输入
这个例子中每一个输入都有一个与之相关的kcontrol(在上面定义)并且通过kcontrol name和输出mixer相连。我们现在可以和用它们的源widgets和destination widget(wrt 音频信号)来连接 /* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
因此我们有: Destination Widget  <=== Path Name <=== Source Widget
或者: Sink, Path, Source
或者: "Output Mixer" 通过"HiFi Playback Switch"和"DAC"连接
当没有path name和widgets(比如一个直接的连接)相连接时,我们使用NULL作为path name 通过下列函数来创建连接 snd_soc_dapm_connect_input(codec, sink, path, source);
最后,当所有的widgets和连接已经呗注册进内核时我们应当调用snd_soc_dapm_new_widgets(codec) 这会使得内核遍历codec和machine,因此内部的DAPM状态会和物理的machine状态匹配
3.1 Machine Widget Interconnections
-------------------------------------------------
machine widget的连接创建方式和codec一样,并且直接将codec的pins连接到machine level 的 widgets 例如。连接speaker 输出编码pins到内部的speaker  /*ext speaker 连接到codec pins LOUT2,ROUT2*/  {"Ext Spk", NULL , "ROUT2"},
{"Ext Spk", NULL , "LOUT2"},
 这样会让DAPM为已经连接的pin和独立NC的pin上电和关闭
4 Endpoint Widgets  ===================
一个endpoint是machine内部一个音频信号的开始或者结束点(widget)并且包含codec。例如   o Headphone Jack
 o Internal Speaker
 o Internal Mic
 o Mic Jack
 o Codec Pins
当一个codec pin 为NC时,它可以通过如下的函数来标记为没有使用 snd_soc_dapm_set_endpoint(codec, "Widget Name", 0); 最后一个参数是0时为inactive,是1时为active.这样这个pin和它的输入widget不会上电 也不会耗费电量
5 DAPM Widget Events  ====================
一些widgets可以在PM事件的DAPM core中注册他们的偏好。 比如.一个有放大器的Speaker注册一个widget这样amplifier可以在speaker使用 的时候才上电
/*打开和关闭amplifier 使用如下的函数*/ 
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;
 }
/*相应的machine dapm widgets */ 
static const struct snd_soc_dapm_widget wm8731_dapm_widgets =  SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);  请在soc-dapm.h中查看支持events的所有widgets
5.1 Event types
---------------------
evevt widgets支持如下的event 类型
/* dapm event types */   #define SND_SOC_DAPM_PRE_PMU        0x1 /* before widget power up */  #define SND_SOC_DAPM_POST_PMU0x2 /* after widget power up */  #define SND_SOC_DAPM_PRE_PMD        0x4 /* before widget power down */   #define SND_SOC_DAPM_POST_PMD0x8 /* after widget power down */   #define SND_SOC_DAPM_PRE_REG   0x10 /* before audio path setup */    #define SND_SOC_DAPM_POST_REG0x20 /* after audio path setup */ 
参考  http://blog.csdn.net/sepnic/article/details/6331884
http://blog.csdn.net/bingqingsuimeng/article/details/8365628