08.音频系统:第003课_Linux音频驱动程序:第006节_DAPM的kcontrol注册过程

在上小节我们初步的讲解了DAPM的widget,route,path3个概念,并且猜测了DAPM对他们的使用过程,接下来的几个小节,我们根据代码讲解DAPM的调用过程:
a,dapm的kcontrol注册过程
b,tinymix,tinyplay,tinycap殊途同归,都会调用dapm_power_widgets函数
c,dapm的核心,comlete path
该小节我们先讲解第一点dapm的kcontrol注册过程。

小节回顾

先来回顾一下之前知识,在没有引入DAPM之前:
在这里插入图片描述
在这里插入图片描述
想实现录音功能,需要把上面每个kcontrol对应的寄存器都进行设定,如上图,就有6个寄存器之多。如果每个寄存器都暴露给应用程序,让应用程序进行设定。在引入DAPM之后,我们只需要把图中红色箭头部分打开,那么整个线路都会被打开了。

对于普通的kcontrol,首先由一个snd_kcontrol_new结构体,该结构体包含了info,put,get,私有数据(一般包含寄存器,以及寄存器的位等等)。会使用其构造一个snd_kcontrol,然后通过snd_ctl_add添加到声卡之中,下面是之前小节分析之后的过程:

static const struct snd_kcontrol_new rt5651_snd_controls[] = {}

static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
	.controls = rt5651_snd_controls,
	.num_controls = ARRAY_SIZE(rt5651_snd_controls),
}
snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,rt5651_dai, ARRAY_SIZE(rt5651_dai));
	ret = snd_soc_register_card(card);
		/*在该函数内,会做很多工作,如绑定dai等等*/
		ret = snd_soc_instantiate_card(card);
			snd_soc_add_card_controls(card, card->controls, card->num_controls);
				snd_soc_add_controls(card, soc_card->dev, controls, num_controls,NULL, soc_card);
					const struct snd_kcontrol_new *control = &controls[i];
					snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));
						/*添加到&card->controls*/
						list_add_tail(&kcontrol->list, &card->controls);

可以看到,所谓的添加到声卡之中,实际就是添加到card->controls这个链表之中,以上就是传统的kcontrol注册过程。那么 DAPM的kcontrol是如何注册的呢?

DAPM的kcontrol

上小节我们说过,
在这里插入图片描述
一个widget包括了mixer,以及3对个kcontrol(上图),如果是我们的ALC5651:
在这里插入图片描述
则存在4个kcontrol,下面我们查看源码:

static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
{
	......
	......
}
static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
	.dapm_widgets = rt5651_dapm_widgets,
}
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,rt5651_dai, ARRAY_SIZE(rt5651_dai));
	/*snd_soc_register_codec注册之后,通过machine最终会导致该函数被调用*/
	ret = snd_soc_instantiate_card(card);
		if (card->dapm_widgets)//通过平台
		snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,card->num_dapm_widgets);
		if (card->of_dapm_widgets)//通过设备树
		snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,card->num_of_dapm_widgets);
			snd_soc_dapm_new_control_unlocked(dapm, widget);
				/*添加到声卡card->widgets链表*/
				list_add_tail(&w->list, &dapm->card->widgets);

可以知道其最终会被添加到card->widgets链表之中。

大家肯定有一个疑问,普通的kcontrol和DAPM的kcontrol给应用程序的访问接口都是一样的,那么很显然DAPM的kcontrol(widgets中的kcontrol)最终都会放到card->controls(普通kcontrol)链表之中。

但是上面的分析,其并没有处理widgets中的kcontrol,把其放入到card->controls链表之中,那么肯定还存在其他的操作:

static int snd_soc_instantiate_card(struct snd_soc_card *card)
	static int soc_probe_link_components(struct snd_soc_card *card, int num,int order)
		static int soc_probe_component(struct snd_soc_card *card,struct snd_soc_component *component)
			int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
				snd_soc_dapm_new_control_unlocked(dapm, &template);
					w->power_check = dapm_generic_check_power;
	snd_soc_dapm_new_widgets(card);
		
				

1.soc_probe_link_components函数会对每一个widgets设置他的power_check = dapm_generic_check_power,该函数用来判断是否需要上电(上电条件:1.位于complete path上。2.有APP在使用声卡)
2.对每个mixer:在这里插入图片描述
可以看到通过三个开关,每个开关只要连接了都会进入mixer。我们想象一下三个开关对应的kcontrol我们在什么时候会使用到他,如果他的kcontrol_new位于某个path上,则使用kcontrol_new构造出son_kcontrol放入到card->controls链表中。

接着前面的分析:

snd_soc_dapm_new_widgets(card);
	case snd_soc_dapm_mixer://如果为混合器
		dapm_new_mixer(w);
			/*看一下widget中的kcontrol是否存在于一个path中*/
			snd_soc_dapm_widget_for_each_source_path(w, path) {
				/* mixer/mux paths name must match control name */
				if (path->name != (char *)w->kcontrol_news[i].name)
					continue
				if (!w->kcontrols[i]) {
					ret = dapm_create_or_share_kcontrol(w, i);
						ret = snd_ctl_add(card, kcontrol);
							list_add_tail(&kcontrol->list, &card->controls);

可以看到,其最终添加到了声卡的card->controls链表。注意:他们的名字由mixer的名字加上kcontrol的名字,可以在开发板上使用tinymix命令查看。

我们知道一个音频编码器有多路输入,对于mixer是多选(多路选择一路),但是对于mux不一样,其是单选(一路有多路选择)。打开rt5651.c文件:

static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
	......
	SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,&rt5651_sto1_adc_l2_mux),
	SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0&rt5651_sto1_adc_r2_mux),
	SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,&rt5651_sto1_adc_l1_mux),
	......
}

static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
	......
	{"Stereo1 ADC L1 Mux", "ADC", "ADC L"},
	{"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"},
	......
}

如下图,是一个简单的图像:
在这里插入图片描述
其上这种情况,我们只需要一种寄存器若干位表示他就可以了,可以看出,其选择和之前的mixer相反,为一对对,mixer为多对1。其上这种情况,只需要一个kcontrol即可,mux连接是用值来确定的,如寄存器的值。mixer值通过字符串(name)。一个mux widgets包含了一个mux本身与一个snd_kcontrol_new。snd_kcontrol_new有多个取值,比如lift。right等。
在这里插入图片描述
如上图,两条绿线代表两条路径path1,与path1,那么他们之中的kcontrol为同一个kcontrol。

我们猜测一下,对mux widgets其会怎么做:
1.snd_kcontrol_new会转换为snd_kcontrol放入到链表之中,最后在放入到声卡的card->controls链表。
2.对于path1,path1他们指向同一个kcontrol,并且他们的sink(目的地)相同。
如下,我们任意查看一个:

static const struct snd_kcontrol_new rt5651_inl1_mux =
	SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),

回到之前的

snd_soc_dapm_new_widgets(card);
	case snd_soc_dapm_mux:
		dapm_new_mux(w);
			ret = dapm_create_or_share_kcontrol(w, 0);
				ret = snd_ctl_add(card, kcontrol);
					list_add_tail(&kcontrol->list, &card->controls);

可以看到其分析和之前是类似的。下面是个小中结:DAPM的kcontrol注册过程,

DAPM的kcontrol注册过程

a. 对于普通的snd_kcontrol:
snd_soc_add_controls : snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表

b. 对于DAPM的snd_kcontrol, 分2步:
b.1 snd_soc_dapm_new_controls  // 把widget放入card->widgets链表
b.2 在注册machine驱动时, 导致如下调用: 
    soc_probe_dai_link > soc_post_component_init > snd_soc_dapm_new_widgets

snd_soc_dapm_new_widgets:
   对于每一个widget, 设置它的power_check函数(用来判断该widget是否应该上电)  
   对于mixer widget, 取出其中的snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表
   对于mux widget,它只有一个snd_kcontrol_new, 构造出snd_kcontrol, 放入card->controls链表
  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南才尽,年少无知!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值