linux-alsa详解9之DAPM详解2widget基本知识

上节中介绍的音频驱动中对基本控制单元的封装:kcontrol。利用kcontrol,我们可以完成对音频系统中的mixer,mux,音量控制,音效控制,以及各种开关量的控制,通过对各种kcontrol的控制,使得音频硬件能够按照我们预想的结果进行工作。同时我们可以看到,kcontrol还是有以下几点不足:

只能描述自身,无法描述各个kcontrol之间的连接关系;
没有相应的电源管理机制;
没有相应的时间处理机制来响应播放、停止、上电、下电等音频事件;
为了防止pop-pop声,需要用户程序关注各个kcontrol上电和下电的顺序;
当一个音频路径不再有效时,不能自动关闭该路径上的所有的kcontrol。
为此,DAPM框架正是为了要解决以上这些问题而诞生的,DAPM目前已经是ASoc中的重要组成部分,让我们先从DAPM的数据结构开始,了解它的设计思想和工作原理。

1 DAPM的基本单元widget

文章的开头,我们说明了一下目前kcontrol的一些不足,而DAPM框架为了解决这些问题,引入了widget这一概念,所谓widget,具备路径和电源管理的kcontrol,其实可以理解为是kcontrol的进一步升级和封装,她同样是指音频系统中的某个部件,比如mixer,mux,输入输出引脚,电源供应器等等,甚至,我们可以定义虚拟的widget,例如playback stream widget。widget把kcontrol和动态电源管理进行了有机的结合,同时还具备音频路径的连结功能,一个widget可以与它相邻的widget有某种动态的连结关系。在DAPM框架中,widget用结构体snd_soc_dapm_widget来描述:

定义位于:include\sound\soc-dapm.h

 1 /* dapm widget */
 2 struct snd_soc_dapm_widget {
 3     enum snd_soc_dapm_type id;该widget的类型值,比如snd_soc_dapm_output,snd_soc_dapm_mixer等
 4     const char *name;        /* widget name */
 5     const char *sname;    /* stream name */代表该widget所在stream的名字,比如对于snd_soc_dapm_dai_in类型的widget,会使用该字段
 6     struct list_head list;所有注册到系统中的widget都会通过该list,链接到代表声卡的snd_soc_card结构的widgets链表头字段中
 7     struct snd_soc_dapm_context *dapm;snd_soc_dapm_context结构指针,ASoc把系统划分为多个dapm域,每个widget属于某个dapm域,同一个域代表着同样的偏置电压供电策略,
 8     比如,同一个codec中的widget通常位于同一个dapm域,而平台上的widget可能又会位于另外一个platform域中
 9     void *priv;                /* widget specific data */有些widget可能需要一些专有的数据,可以使用该字段来保存,像snd_soc_dapm_dai_in类型的widget,会使用该字段来记住与之相关联的snd_soc_dai结构指针
10     struct regulator *regulator;        /* attached regulator */对于snd_soc_dapm_regulator_supply类型的widget,该字段指向与之相关的regulator结构指针
11     const struct snd_soc_pcm_stream *params; /* params for dai links */
12     unsigned int num_params; /* number of params for dai links */
13     unsigned int params_select; /* currently selected param for dai link */
14     
15     /* dapm control */reg/shift/mask3个字段用来控制该widget的电源状态,分别对应控制信息所在的寄存器地址,位移值和屏蔽值
16     int reg;                /* negative reg = no direct dapm */
17     unsigned char shift;            /* bits to shift */
18     unsigned int mask;            /* non-shifted mask */
19     unsigned int on_val;            /* on state value */电源开启时的值
20     unsigned int off_val;            /* off state value */电源关闭时的值
21     unsigned char power:1;            /* block power status */表示当前widget是否处于上电
22     unsigned char active:1;            /* active stream on DAC, ADC's */表示当前widget是否处于激活状态
23     unsigned char connected:1;        /* connected codec pin */表示当前widget是否处于连接状态
24     unsigned char new:1;            /* cnew complete */我们定义好的widget(snd_soc_dapm_widget结构),在注册到声卡中时需要进行实例化,该字段用来表示该widget是否已经被实例化
25     unsigned char force:1;            /* force state */该位被设置后,将会不管widget当前的状态,强制更新至新的电源状态
26     unsigned char ignore_suspend:1;         /* kept enabled over suspend */
27     unsigned char new_power:1;        /* power from this run */
28     unsigned char power_checked:1;        /* power checked this run */用于检查该widget是否应该上电或下电的回调函数指针
29     unsigned char is_supply:1;        /* Widget is a supply type widget */
30     unsigned char is_ep:2;            /* Widget is a endpoint type widget */
31     int subseq;                /* sort within widget type */
32 
33     int (*power_check)(struct snd_soc_dapm_widget *w);
34 
35     /* external events */
36     unsigned short event_flags;        /* flags to specify event types */
37     int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
38 
39     /* kcontrols that relate to this widget */
40     int num_kcontrols;
41     const struct snd_kcontrol_new *kcontrol_news;
42     struct snd_kcontrol **kcontrols;
43     struct snd_soc_dobj dobj;
44 
45     /* widget input and output edges */
46     struct list_head edges[2];
47 
48     /* used during DAPM updates */
49     struct list_head work_list;
50     struct list_head power_list;
51     struct list_head dirty;
52     int endpoints[2];
53 
54     struct clk *clk;
55 }

2 widget种类

DAPM框架中,把各种不同的widget划分为不同的种类,snd_soc_dapm_widget结构中的id字段用来表示该widget的种类,可选的种类都定义在一个枚举中:

 1 /* dapm widget types */
 2 enum snd_soc_dapm_type {
 3     snd_soc_dapm_input = 0,        /* input pin */该widget对应一个输入引脚
 4     snd_soc_dapm_output,        /* output pin */ 该widget对应一个输出引脚
 5     snd_soc_dapm_mux,            /* selects 1 analog signal from many inputs *该widget对应一个mux控件/
 6     snd_soc_dapm_demux,            /* connects the input to one of multiple outputs */
 7     snd_soc_dapm_mixer,            /* mixes several analog signals together */
 8     snd_soc_dapm_mixer_named_ctl,        /* mixer with named controls */对应一个mixer控件,但是对应的kcontrol的名字不会加入widget的名字作为前缀
 9     snd_soc_dapm_pga,            /* programmable gain/attenuation (volume) */对应一个pga控件(可编程增益控件)
10     snd_soc_dapm_out_drv,            /* output driver */对应一个输出驱动控件
11     snd_soc_dapm_adc,            /* analog to digital converter */对应一个ADC
12     snd_soc_dapm_dac,            /* digital to analog converter */对应一个DAC
13     snd_soc_dapm_micbias,        /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */对应一个麦克风偏置电压控件
14     snd_soc_dapm_mic,            /* microphone */麦克风
15     snd_soc_dapm_hp,            /* headphones */耳机
16     snd_soc_dapm_spk,            /* speaker */扬声器
17     snd_soc_dapm_line,            /* line input/output */线路输入输出
18     snd_soc_dapm_switch,        /* analog switch */模拟开关
19     snd_soc_dapm_vmid,            /* codec bias/vmid - to minimise pops */对应一个codec的vmid偏置电压
20     snd_soc_dapm_pre,            /* machine specific pre widget - exec first */machine级别的专用widget,会先于其它widget执行检查操作
21     snd_soc_dapm_post,            /* machine specific post widget - exec last */machine级别的专用widget,会后于其它widget执行检查操作
22     snd_soc_dapm_supply,        /* power/clock supply */对应一个电源或是时钟源
23     snd_soc_dapm_regulator_supply,    /* external regulator */对应一个外部regulator稳压器
24     snd_soc_dapm_clock_supply,    /* external clock */对应一个外部时钟源
25     snd_soc_dapm_aif_in,        /* audio interface input */对应一个数字音频输入接口,比如I2S接口的输入端
26     snd_soc_dapm_aif_out,        /* audio interface output */对应一个数字音频输出接口,比如I2S接口的输出端
27     snd_soc_dapm_siggen,        /* signal generator */对应一个信号发生器
28     snd_soc_dapm_sink,
29     snd_soc_dapm_dai_in,        /* link to DAI structure */对应一个platform或codec域的输入DAI结构
30     snd_soc_dapm_dai_out,       对应一个platform或codec域的输出DAI结构
31     snd_soc_dapm_dai_link,        /* link between two DAI structures */用于链接一对输入/输出DAI结构
32     snd_soc_dapm_kcontrol,        /* Auto-disabled kcontrol */
33 }

 3 widget之间的链接器path

之前已经提到,一个widget是有输入和输出的,而且widget之间是可以动态地进行连接的,那它们是用什么来连接两个widget的呢?DAPM为我们提出了path这一概念,path相当于电路中的一根跳线,它把一个widget的输出端和另一个widget的输入端连接在一起,path用snd_soc_dapm_path结构来描述:

 1 /* dapm audio path between two widgets */
 2 struct snd_soc_dapm_path {
 3     const char *name;
 4 
 5     /*
 6      * source (input) and sink (output) widgets
 7      * The union is for convience, since it is a lot nicer to type
 8      * p->source, rather than p->node[SND_SOC_DAPM_DIR_IN]
 9      */
10     union {
11         struct {
12             struct snd_soc_dapm_widget *source;
13             struct snd_soc_dapm_widget *sink;
14         };
15         struct snd_soc_dapm_widget *node[2];
16     };
17 
18     /* status */
19     u32 connect:1;    /* source and sink widgets are connected */
20     u32 walking:1;  /* path is in the process of being walked */
21     u32 weak:1;    /* path ignored for power management */
22     u32 is_supply:1;    /* At least one of the connected widgets is a supply */
23 
24     int (*connected)(struct snd_soc_dapm_widget *source,
25              struct snd_soc_dapm_widget *sink);
26 
27     struct list_head list_node[2];
28     struct list_head list_kcontrol;
29     struct list_head list;
30 }

当widget之间发生连接关系时,snd_soc_dapm_path作为连接者,它的source字段会指向该连接的起始端widget,而它的sink字段会指向该连接的到达端widget,还记得前面snd_soc_dapm_widget结构中的两个链表头字段:sources和sinks么?widget的输入端和输出端可能连接着多个path,所有输入端的snd_soc_dapm_path结构通过list_sink字段挂在widget的souces链表中,同样,所有输出端的snd_soc_dapm_path结构通过list_source字段挂在widget的sinks链表中。这里可能大家会被搞得晕呼呼的,一会source,一会sink,不要紧,只要记住,连接的路径是这样的:起始端widget的输出-->path的输入-->path的输出-->到达端widget输入。

 另外,snd_soc_dapm_path结构的list字段用于把所有的path注册到声卡中,其实就是挂在snd_soc_card结构的paths链表头字段中。如果你要自己定义方法来检查path的当前连接状态,你可以提供自己的connected回调函数指针.

connect,walked,walking,weak是几个辅助字段,用于帮助所有path的遍历。

4 widget的连接关系route

通过上一节的内容,我们知道,一个路径的连接至少包含以下几个元素:起始端widget,跳线path,到达端widget,在DAPM中,用snd_soc_dapm_route结构来描述这样一个连接关系:

 1 /*
 2  * DAPM audio route definition.
 3  *
 4  * Defines an audio route originating at source via control and finishing
 5  * at sink.
 6  */
 7 struct snd_soc_dapm_route {
 8     const char *sink;
 9     const char *control;
10     const char *source;
11 
12     /* Note: currently only supported for links where source is a supply */
13     int (*connected)(struct snd_soc_dapm_widget *source,
14              struct snd_soc_dapm_widget *sink);
15 };

sink指向到达端widget的名字字符串,source指向起始端widget的名字字符串,control指向负责控制该连接所对应的kcontrol名字字符串,connected回调则定义了上一节所提到的自定义连接检查回调函数。该结构的意义很明显就是:source通过一个kcontrol,和sink连接在一起,现在是否处于连接状态,请调用connected回调函数检查。

这里直接使用名字字符串来描述连接关系,所有定义好的route,最后都要注册到dapm系统中,dapm会根据这些名字找出相应的widget,并动态地生成所需要的snd_soc_dapm_path结构,正确地处理各个链表和指针的关系,实现两个widget之间的连接。

参考博文:https://blog.csdn.net/DroidPhone/java/article/details/12906139

posted @ 2020-06-20 19:53  Action_er  阅读(1146)  评论(0编辑  收藏  举报