Alsa里面恶心的DAPM

相关文章

音频系统,Alsa 里面的buff 是怎么计算的?

为什么需要超过48k的采样音频?

我在MTK平台下调试音频ALSA

音频几个重要的参数

openwrt 音频开发

(干货)Ai音箱和Linux音频驱动小谈

Android 音频数据流分析之程序员干架产品经理


正文

做音频的很多初学者对这个概念真的非常懵,我也是非常懵,所以写个文章概述一下,以后遇到问题的时候可以回来看看。

DAPM 从字面上看是和电源相关的,但是我们实际代码里面有非常多的结构体和函数是跟这个又没有多少关系,动态调整电源是最终的目的,那些使用的结构体,路由,小控件,都是用来协助完成这个事情的。

#我们用到的一些结构体和函数

这是小控件的结构体,也可以理解成一个开关,是一个通路里面的一个节点。

struct snd_soc_dapm_widget //一个整理数组
struct snd_kcontrol_new //一个小控件

下面这个是路由的结构体,路由一般在machine里面和codec里面存在,就是确定音频通路设置。

struct snd_soc_dapm_route

路由设置的是 sink sourcecontrol

sink 可以理解成输出,source 是数据源头,control 就是输出是打到哪个通路的开关的。这里有必要说一下,我们正常的source 一般理解是在左边,但是这里source 在右边。

下面是设置小控件的一些宏,不同类型的控件节点需要用到不同的宏

SND_SOC_DAPM_INPUT
SND_SOC_DAPM_SUPPLY_S
SND_SOC_DAPM_SWITCH

#实例

#硬件连接图

#实现开关选项control

static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {
SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),
SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),
SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),
SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),
SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
};

#实现widget,可以理解把contrl整合到 snd_soc_dapm_widget  结构体里面

static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
/* Output */

SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
     wm8900_loutmix_controls,
     ARRAY_SIZE(wm8900_loutmix_controls)),

};

#实现路由通路配置

static const struct snd_soc_dapm_route audio_map[] = {

{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},

};

可以观察下 Left Input Mixer Switch 这个字符串就用来制定control 打向了哪个开关,跟 wm8900_loutmix_controls 里面的值对应。

合起来就是通过Left Input Mixer Switch 开关把数据源 Left Input Mixer连接到 Left Output Mixer 输输出口。

#将上面设置的widget、route和kcontrols串联起来

static int wm8900_add_widgets(struct snd_soc_codec *codec)
{
 snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets,
      ARRAY_SIZE(wm8900_dapm_widgets));
 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 snd_soc_dapm_new_widgets(codec);
 return 0;
} 

大概就这样能够把目标、源、控制开关串联起来。我在看MTK内核代码的时候,里面的实现跟这个有些不一样,主要是新加了一个结构体,但是本质还是串联这些东西。

MTK平台差异部分

##Kcontrol

static const struct snd_kcontrol_new mt8167_afe_o13_mix[] = {
 SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_GAIN1_CONN2, 4, 1, 0),
 SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_GAIN1_CONN2, 9, 1, 0),
};

##widget

static const struct snd_soc_dapm_widget mt8167_afe_pcm_widgets[] = {
 SND_SOC_DAPM_MIXER("O13", SND_SOC_NOPM, 0, 0,
      mt8167_afe_o13_mix, ARRAY_SIZE(mt8167_afe_o13_mix)),
};

##route

static const struct snd_soc_dapm_route mt8167_afe_pcm_routes[] = {
 {"O13", "I15 Switch", "I15"},
};

##串联注册

static const struct snd_soc_component_driver mt8167_afe_pcm_dai_component = {
 .name = "mtk-afe-pcm-dai",
 .dapm_widgets = mt8167_afe_pcm_widgets,
 .num_dapm_widgets = ARRAY_SIZE(mt8167_afe_pcm_widgets),
 .dapm_routes = mt8167_afe_pcm_routes,
 .num_dapm_routes = ARRAY_SIZE(mt8167_afe_pcm_routes),
};
ret = snd_soc_register_component(&pdev->dev,
      &mt8167_afe_pcm_dai_component,
      mt8167_afe_pcm_dais,
      ARRAY_SIZE(mt8167_afe_pcm_dais));

实在话,看英语原文感觉还是很舒服的。

英语原文翻译如下:

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 */

  推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

猜你喜欢

转载自blog.csdn.net/weiqifa0/article/details/108459314