此博客为本人基于前辈的总结和理解,不断更,若有纰漏,还请各位不吝啬赐教。膜拜~顺求一锅Android 音频子系统爱好者同行。
DAPM
- 1. 概念
- 1.1 初衷
- 1.2 widget
- 2.区别
- kcontrol 与 damp kcontrol
- 辅助定义宏
- 触发条件
- kcontrol 与 ucontrol
- snd_kcontrol 与 snd_kcontrol_new
- 3.文献
1. 概念
1.1 初衷
实现任意时刻音频系统的最小功耗,这就要求实时的检测音频链路,关闭多余的部件,才能保证低功耗,顺带引入底层音频调试工具tinyalsa (alsa架构过于庞大,针对SOC,我们使用tinyalsa架构,降低功耗)所在目录 external/tinyalsa/ 记得编译哦!
针对一系列调试工具的使用,源码分析如何至底层的kcontrol的get,put回调 见 link.
实习三个月来,我接触了linux标准电源管理,以及Android Android在Linux内核原有的睡眠唤醒模块上基础上,主要增加了下面三个机制:
Wake _Lock 唤醒锁机制;
Early _Suspend 预挂起机制;
Late _Resume 迟唤醒机制; 总结;DAPM独立于内核电源管理,与其它电源管理系统模块共存。
具体请参考源码文档 kernel/Document/sound/alsa/soc/
1.2 widget
针对kcontrol还是有以下几点不足引入widget:
只能描述自身,无法描述各个kcontrol之间的连接关系;
没有相应的电源管理机制;
没有相应的时间处理机制来响应播放、停止、上电、下电等音频事件;
为了防止pop-pop声,需要用户程序关注各个kcontrol上电和下电的顺序;
当一个音频路径不再有效时,不能自动关闭该路径上的所有的kcontrol;
widget把kcontrol和动态电源管理进行了有机的结合,同时还具备音频路径的连结功能
2.区别
kcontrol 与 damp kcontrol
辅助定义宏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//前为kcontrol辅助定义宏 #define SOC_SINGLE(xname, reg, shift, max, invert) { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } /* dapm kcontrol types */ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_volsw, .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
可以看出, SOC DAPM SINGLE对应与普通控件的SOC SINGLE, SOC DAPM SINGLE TLV对应SOC SINGLE TLV…相 比普通的kcontrol控件, dapm的kcontrol控件只是把info, get, put回调函数换掉了.dapm kcontrol的put回调函数不仅仅会更新控件本身的状态,他还会把这种变化传递到相邻的dapm kcontrol,相邻的dapm kcontrol又会传递这个变化到他自己相邻的dapmkcontrol,直到音频路径的末端,通过这种机制,只要改变其中一个widget的连接状态,与之相关的所有widget都会被扫描并测试一下自身是否还在有效的音频路径中,从而可以动态地改变自身的电源状态,这就是dapm的精髓所在。
针对get回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50/** * snd_soc_dapm_get_volsw - dapm mixer get callback * @kcontrol: mixer control * @ucontrol: control element information * * Callback to get the value of a dapm mixer control. * * Returns 0 for success. */ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_card *card = dapm->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; unsigned int val; int ret = 0; if (snd_soc_volsw_is_stereo(mc)) dev_warn(dapm->dev, "ASoC: Control '%s' is stereo, which is not supportedn", kcontrol->id.name); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { ret = soc_dapm_read(dapm, reg, &val); val = (val >> shift) & mask; } else { val = dapm_kcontrol_get_value(kcontrol); } mutex_unlock(&card->dapm_mutex); if (ret) return ret; if (invert) ucontrol->value.integer.value[0] = max - val; else ucontrol->value.integer.value[0] = val; return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
触发条件
tinymix 工具调节音频音量大小,或者实现音频路径切换,实质都是操作寄存器,因此针对kcontrol 以及dapm kcontrol 的 触发,存在一定的联系,大致如下:
同:应用层API调用流程
异:实际操作寄存器 使不使能dapm widget(power on/down)的区别
详细的追踪流程见下回分解
kcontrol 与 ucontrol
这两者又有什么区别呢?kernel and use?前者可以认为是声卡控件对应的一个数据结构,那后者呢?
可以肯定与应用层有关,辅助定义宏又提供了操作kcontrol的get,put 回调。在使用tinymix 时也使用到参数,应该是参数的应用层->底层传递,详细的追踪流程见下回分解。
snd_kcontrol 与 snd_kcontrol_new
在这之前,我发现个小问题,在codec驱动中实例化了一个snd_kcontrol_new 数组
并在codec探针函数probe的初始化阶段,调用snd_soc_add_codec_controls绑定,
也可以在实例化codec driver 时对controls字段赋值。
下面深入snd_soc_add_codec_controls 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were * duplicating this code. * * @codec: codec to add controls to * @controls: array of controls to add * @num_controls: number of elements in the array * * Return 0 for success, else error. */ int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, unsigned int num_controls) { return snd_soc_add_component_controls(&codec->component, controls, num_controls); } /** * snd_soc_add_component_controls - Add an array of controls to a component. * * @component: Component to add controls to * @controls: Array of controls to add * @num_controls: Number of elements in the array * * Return: 0 for success, else error. */ int snd_soc_add_component_controls(struct snd_soc_component *component, const struct snd_kcontrol_new *controls, unsigned int num_controls) { struct snd_card *card = component->card->snd_card; return snd_soc_add_controls(card, component->dev, controls, num_controls, component->name_prefix, component); }
在soc-core.c里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20static int snd_soc_add_controls(struct snd_card *card, struct device *dev, const struct snd_kcontrol_new *controls, int num_controls, const char *prefix, void *data) { int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; err = snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix)); if (err < 0) { dev_err(dev, "ASoC: Failed to add %s: %dn", control->name, err); return err; } } return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43/** * snd_soc_cnew - create new control * @_template: control template * @data: control private data * @long_name: control long name * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, void *data, const char *long_name, const char *prefix) { struct snd_kcontrol_new template; struct snd_kcontrol *kcontrol; char *name = NULL; memcpy(&template, _template, sizeof(template)); template.index = 0; if (!long_name) long_name = template.name; if (prefix) { name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); if (!name) return NULL; template.name = name; } else { template.name = long_name; } kcontrol = snd_ctl_new1(&template, data); kfree(name); return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew);
在snd_soc_cnew里,你可以看出实现了snd_kcontrol_new到snd_kcontrol的转化,如下,实例化一个snd_kcontrol 并 kctl ->private_data = private_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49/** 221 * snd_ctl_new1 - create a control instance from the template 222 * @ncontrol: the initialization record 223 * @private_data: the private data to set 224 * 225 * Allocates a new struct snd_kcontrol instance and initialize from the given 226 * template. When the access field of ncontrol is 0, it's assumed as 227 * READWRITE access. When the count field is 0, it's assumes as one. 228 * 229 * Return: The pointer of the newly generated instance, or %NULL on failure. 230 */ 231 struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, 232 void *private_data) 233 { 234 struct snd_kcontrol kctl; 235 unsigned int access; 236 237 if (snd_BUG_ON(!ncontrol || !ncontrol->info)) 238 return NULL; 239 memset(&kctl, 0, sizeof(kctl)); 240 kctl.id.iface = ncontrol->iface; 241 kctl.id.device = ncontrol->device; 242 kctl.id.subdevice = ncontrol->subdevice; 243 if (ncontrol->name) { 244 strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); 245 if (strcmp(ncontrol->name, kctl.id.name) != 0) 246 snd_printk(KERN_WARNING 247 "Control name '%s' truncated to '%s'n", 248 ncontrol->name, kctl.id.name); 249 } 250 kctl.id.index = ncontrol->index; 251 kctl.count = ncontrol->count ? ncontrol->count : 1; 252 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 253 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| 254 SNDRV_CTL_ELEM_ACCESS_VOLATILE| 255 SNDRV_CTL_ELEM_ACCESS_INACTIVE| 256 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| 257 SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| 258 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); 259 kctl.info = ncontrol->info; 260 kctl.get = ncontrol->get; 261 kctl.put = ncontrol->put; 262 kctl.tlv.p = ncontrol->tlv.p; 263 kctl.private_value = ncontrol->private_value; 264 kctl.private_data = private_data; 265 return snd_ctl_new(&kctl, access); 266 }
3.文献
参考资料
https://blog.csdn.net/DroidPhone
最后
以上就是愉快曲奇最近收集整理的关于DAPM之浅析(一)1. 概念2.区别3.文献的全部内容,更多相关DAPM之浅析(一)1.内容请搜索靠谱客的其他文章。
发表评论 取消回复