Linux内核4.14版本——alsa框架分析(17)——DAPM(8)——DAPM的使用过程

      该小节我们讲解DAPM的情景分析的构造过程,我们先回顾一下widget上电的过程,如下是一条上电路线:

Linux内核4.14版本——alsa框架分析(17)——DAPM(8)——DAPM的使用过程_第1张图片

 

      从LINPUT1经过两个Mixer到达ADC,从图上可以看出,控制接口共六个部分,只要其中有一个部分没有打开(connect),则该线路的所有开关都不会打开。如果其上所有接口都开看,并且有应用程序使用这个声卡,那么图中的四个widget全部都会打开。这样就是comlete path,即满足3个条件:

1.线路所有涉及的path->connect全部为连接(等于1)
2.有产生数据和处理数据两个端点(图示的LINPUT1为产生数据,ADC为处理数据)
3.有应用程序使用声卡

其上的LINPUT1我们称为input ed,ADC称为output ed,如下面的下路:

Linux内核4.14版本——alsa框架分析(17)——DAPM(8)——DAPM的使用过程_第2张图片

 

即使把开关合上也不是一条comlete path,因为为mixer不是output ed端点。

那么其是如何找出所有的comlete path的呢?其实不需要找,如下面一个widger:

Linux内核4.14版本——alsa框架分析(17)——DAPM(8)——DAPM的使用过程_第3张图片

 

      其左右两边分别调用is_connected_input_ep与is_connected_output_ep两个函数,判断两边是否分别连接了input_ep与output_ep,如果两边都连接,则这个widger位于comlete path上面,即可以上电(有应用程序使用声卡)。

      is_connected_input_ep与is_connected_output_ep函数是在power_check函数中被调用的,下面是widger设置power_check函数的流程,之前的小节已经讲解过:

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_control_unlocked函数:

snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,const struct snd_soc_dapm_widget *widget)
	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;
	case snd_soc_dapm_spk:
	case snd_soc_dapm_hp:
		w->is_ep = SND_SOC_DAPM_EP_SINK;
		w->power_check = dapm_generic_check_power;
		break;
	case snd_soc_dapm_output:
		if (!dapm->card->fully_routed)
			w->is_ep = SND_SOC_DAPM_EP_SINK;
		w->power_check = dapm_generic_check_power;
		break;
	case snd_soc_dapm_vmid:
	case snd_soc_dapm_siggen:
		w->is_ep = SND_SOC_DAPM_EP_SOURCE;
		w->power_check = dapm_always_on_check_power;
		break;
	case snd_soc_dapm_mux:
	case snd_soc_dapm_demux:
	case snd_soc_dapm_switch:
	case snd_soc_dapm_mixer:
	case snd_soc_dapm_mixer_named_ctl:
	case snd_soc_dapm_adc:
	case snd_soc_dapm_aif_out:
	case snd_soc_dapm_dac:
	case snd_soc_dapm_aif_in:
	case snd_soc_dapm_pga:
	case snd_soc_dapm_out_drv:
	case snd_soc_dapm_micbias:
	case snd_soc_dapm_line:
	case snd_soc_dapm_dai_link:
	case snd_soc_dapm_dai_out:
	case snd_soc_dapm_dai_in:
		w->power_check = dapm_generic_check_power;
		break;
	case snd_soc_dapm_supply:
	case snd_soc_dapm_regulator_supply:
	case snd_soc_dapm_clock_supply:
	case snd_soc_dapm_kcontrol:
		w->is_supply = 1;
		w->power_check = dapm_supply_check_power;
		break;
	default:
		w->power_check = dapm_always_on_check_power;
		break;
	}

       可以知道其会根据w->id的不同,设置不同的w->power_check函数,那么他到底是怎么判断widger两边是否连接input_ep与output_ep的呢?下面我们分析一个例子,即dapm_generic_check_power函数:

/* Generic check to see if a widget should be powered */
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
{
	/*其为一个递归的过程
	首先判断自己是否为input_ep端点,如果不是通过path找到下一个widger,
	继续判断,知道找到末尾,找到则返回1,没有找到返回0*/
	in = is_connected_input_ep(w, NULL);
	/*同上*/
	out = is_connected_output_ep(w, NULL);
	return out != 0 && in != 0;
}

      其原理以在上面进行注释,不再此处重复,对于任意widger,先通过path,找到path中的src widger与 sink widger。利用这种递归的方法。

      那么w->power_check函数时在哪里被调用的呢?我们需要回答以下问题。

1.每个widger->power_check由谁启动
    a. APP使用声卡之前如:aplay/crecord命令之前,无论播放还是录制声音,在这之前我们都需要设置好一条comlete path。
    b. APP使用声卡过程中,axmier可以打开或者关闭某条path,如在录音的时候,是可以更换线路的。其回去设置某个kcontrl,导致重新判断每一个widger
    
2.path->connect合适被设置
    a. 注册route,route转换为path,根据对应kcontrol_new的寄存器值设置(该为初始值)
    b. 后期使用mixer设置:设置kcontrol对应的寄存器,与所在的path的connect。
    c. 会遍历所有的widger,决定是否上电。

上面是我们的提问与总结,下面我们通过源代码去进行验证。

mixer调用过程:
以SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),为例:
snd_soc_dapm_put_volsw
	connect = xxx // 根据传入的val确定

	// 把kcontrol要设置的reg,val写入update
	update.kcontrol = kcontrol;
	update.widget = widget;
	update.reg = reg;
	update.mask = mask;
	update.val = val;
	widget->dapm->update = &update;
	
	soc_dapm_mixer_update_power(widget, kcontrol, connect);
		// 找到path并设置connect
		path->connect = connect;
		
		// 调用此函数逐个widget进行判断、上电/关闭
		dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
	
	widget->dapm->update = NULL;
	
	
aplay调用过程:
播放/录音前都会调用 soc_pcm_prepare
soc_pcm_prepare
    // stream's name = Playback or Capture
	snd_soc_dapm_stream_event(rtd, stream's name, SND_SOC_DAPM_STREAM_START)
		soc_dapm_stream_event
			// 找出每一个widget
			// 如果strstr(w->sname, stream) // w->sname中含有Playback or Capture
			// w->active = 1
			
			dapm_power_widgets

大家都会调用dapm_power_widgets
dapm_power_widgets
	// 对于每一个widget
	// power = w->power_check(w);  // 确定是否要上电
	
	// 放入不同的链表, 以后统一上是或关闭
	if (power)
		dapm_seq_insert(w, &up_list, true);
	else
		dapm_seq_insert(w, &down_list, false);
	
	// 关闭down_list上的所有widget
	dapm_seq_run(dapm, &down_list, event, false);
	
	// 根据dapm->update设置kcontrol, // update来自tinymix的调用
	dapm_widget_update(dapm);
	
	// 打开up_list上的所有widget
	dapm_seq_run(dapm, &up_list, event, true);

// 给链表中的widget上电或关闭
dapm_seq_run
	dapm_seq_run_coalesced
		snd_soc_update_bits

你可能感兴趣的:(Linux,音频子系统,大数据)