DAPM之浅析(一)

此博客为本人基于前辈的总结和理解,不断更,若有纰漏,还请各位不吝啬赐教。膜拜~顺求一锅Android 音频子系统爱好者同行。

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 supported\n",
			 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: %d\n",
				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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值