概述
此博客为本人基于前辈的总结和理解,不断更,若有纰漏,还请各位不吝啬赐教。膜拜~顺求一锅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
辅助定义宏
//前为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回调函数
/**
* 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 源码
/**
* 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里
static 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;
}
/**
* 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
/**
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. 概念2.区别3.文献所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复