Linux内核4.14版本——alsa框架分析(13)——DAPM(4)——widget的注册

目录

1. snd_soc_dapm_new_controls直接注册

2. snd_soc_register_codec间接注册

3. 具体分析几个点

3.2 snd_soc_dapm_new_widgets

3.2.1 mixer

3.2.2 mux


      在前面的文章《如何定义各种widget》介绍了各种widget的定义以及一种注册widget的方法,今天我们着重介绍widget的注册。widget本质上也是kcontrol,只不过添加了电源管理的部分。

1. snd_soc_dapm_new_controls直接注册

我们以sound\soc\codecs\wm8960.c为例。

static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
	......
};
				
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
		  ARRAY_SIZE(wm8960_dapm_widgets));		

snd_soc_dapm_new_controls
    snd_soc_dapm_new_control_unlocked
        list_add_tail(&w->list, &dapm->card->widgets);

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

2. snd_soc_register_codec间接注册

以sound\soc\codecs\es8328.c为例。

static const struct snd_soc_codec_driver es8328_codec_driver = {
	.......
	.component_driver = {
		.controls		= es8328_snd_controls,
		.num_controls		= ARRAY_SIZE(es8328_snd_controls),
		.dapm_widgets		= es8328_dapm_widgets,
		.num_dapm_widgets	= ARRAY_SIZE(es8328_dapm_widgets),
		.dapm_routes		= es8328_dapm_routes,
		.num_dapm_routes	= ARRAY_SIZE(es8328_dapm_routes),
	},
};
 
return snd_soc_register_codec(dev,
		&es8328_codec_driver, &es8328_dai, 1);
snd_soc_register_codec(dev, &es8328_codec_driver, &es8328_dai, 1);
    ret = snd_soc_component_initialize(&codec->component, 
			&codec_drv->component_driver, dev);  ---->  component->driver = driver; 
		ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
			component->dai_drv = dai_drv;
			dai = soc_add_dai(component, dai_drv + i,count == 1 && legacy_dai_naming);
				struct snd_soc_dai *dai;
				dai->component = component;
				dai->driver = dai_drv;
				list_add(&dai->list, &component->dai_list);
    snd_soc_component_add_unlocked(&codec->component);
         list_add(&component->list, &component_list);

      最终es8328_codec_driver中的component_driver被幅值给dai->component的component_driver。这里面既包含kcontrol又包含widget。这个dai->component又最终被什么调用呢。

snd_soc_register_card
	snd_soc_instantiate_card
		struct snd_soc_pcm_runtime *rtd;
			soc_bind_dai_link
				struct snd_soc_pcm_runtime *rtd;
				rtd = soc_new_pcm_runtime(card, dai_link);
				
				snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);
				snd_soc_rtdcom_add(rtd, codec_dais[i]->component);
				snd_soc_rtdcom_add(rtd, component);
				
				soc_add_pcm_runtime(card, rtd);
					list_add_tail(&rtd->list, &card->rtd_list);
		soc_probe_link_components
			soc_probe_component
				snd_soc_dapm_new_controls
					snd_soc_dapm_new_control_unlocked
						list_add_tail(&w->list, &dapm->card->widgets);
				snd_soc_dapm_new_dai_widgets
					snd_soc_dapm_new_control_unlocked
						list_add_tail(&w->list, &dapm->card->widgets);
		soc_probe_link_dais
			snd_soc_dapm_new_pcm
				snd_soc_dapm_new_control_unlocked
					list_add_tail(&w->list, &dapm->card->widgets);

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

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

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

snd_soc_register_card
	snd_soc_instantiate_card
		snd_soc_dapm_new_widgets

3. 具体分析几个点

        soc_probe_link_components
            soc_probe_component
                snd_soc_dapm_new_controls
                    snd_soc_dapm_new_control_unlocked

struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
			 const struct snd_soc_dapm_widget *widget)
{
	......
	if ((w = dapm_cnew_widget(widget)) == NULL)
		return NULL;
	......
	switch (w->id) {
	case snd_soc_dapm_mic:
		w->is_ep = SND_SOC_DAPM_EP_SOURCE;
		w->power_check = dapm_generic_check_power;
		break;
	case snd_soc_dapm_input:
		if (!dapm->card->fully_routed)
			w->is_ep = SND_SOC_DAPM_EP_SOURCE;
		w->power_check = dapm_generic_check_power;
		break;
		.......
	}

	w->dapm = dapm;
	INIT_LIST_HEAD(&w->list);
	INIT_LIST_HEAD(&w->dirty);
	list_add_tail(&w->list, &dapm->card->widgets);

	snd_soc_dapm_for_each_direction(dir) {
		INIT_LIST_HEAD(&w->edges[dir]);
		w->endpoints[dir] = -1;
	}

	/* machine layer sets up unconnected pins and insertions */
	w->connected = 1;
	return w;
}

      soc_probe_link_components函数会对每一个widgets设置他的power_check = dapm_generic_check_power,该函数用来判断是否需要上电(上电条件:1.位于complete path上。2.有APP在使用声卡)。

3.2 snd_soc_dapm_new_widgets

int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
{
	struct snd_soc_dapm_widget *w;
	unsigned int val;

	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);

	list_for_each_entry(w, &card->widgets, list)
	{
		if (w->new)
			continue;

		if (w->num_kcontrols) {
			w->kcontrols = kzalloc(w->num_kcontrols *
						sizeof(struct snd_kcontrol *),
						GFP_KERNEL);
			if (!w->kcontrols) {
				mutex_unlock(&card->dapm_mutex);
				return -ENOMEM;
			}
		}

		switch(w->id) {
		case snd_soc_dapm_switch:
		case snd_soc_dapm_mixer:
		case snd_soc_dapm_mixer_named_ctl:
			dapm_new_mixer(w);
			break;
		case snd_soc_dapm_mux:
		case snd_soc_dapm_demux:
			dapm_new_mux(w);
			break;
		case snd_soc_dapm_pga:
		case snd_soc_dapm_out_drv:
			dapm_new_pga(w);
			break;
		case snd_soc_dapm_dai_link:
			dapm_new_dai_link(w);
			break;
		default:
			break;
		}

		/* Read the initial power state from the device */
		if (w->reg >= 0) {
			soc_dapm_read(w->dapm, w->reg, &val);
			val = val >> w->shift;
			val &= w->mask;
			if (val == w->on_val)
				w->power = 1;
		}

		w->new = 1;

		dapm_mark_dirty(w, "new widget");
		dapm_debugfs_add_widget(w);
	}

	dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
	mutex_unlock(&card->dapm_mutex);
	return 0;
}

3.2.1 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链表。

3.2.2 mux

         其上这种情况,我们只需要一种寄存器若干位表示他就可以了,可以看出,其选择和之前的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链表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值