还是以LINPUT1为例,当使用DAPM后,只需要将LINPUT1 Switch和Boost Switch暴露给应用程序就可以了。
对于普通的kcontrol,里面有一个snd_kcontrol_new结构体,里面有info、put、get等函数。将snd_kcontrol_new结构体封装成一个snd_kcontrol结构体,使用函数snd_ctl_add函数添加到声卡中(即放到声卡的controls链表中)。
驱动程序使用以下API函数创建widget
snd_soc_dapm_new_controls
实际上,这个函数只是创建widget的第一步,它为每个子widget分配内存,初始化必要的字段,然后把这些widget挂在代表声卡的snd_soc_card的widget链表字段中。
对于DAPM的kcontrol和普通的kcontrol,应用程序给用户的访问接口都是一样的。最终widget中的kcontrol_new也会转换为snd_kcontrol,放入到声卡的controls链表中。
在codec驱动中,仅仅是将widget放入到了声卡的widgets链表中去。并没有将widget中的kcontrol_new进入转化,转化并放入声卡的controls链表是在函数snd_soc_dapm_new_widgets中完成的。
snd_soc_dapm_new_widgets
这个函数会根据widget的信息,创建widget所需要的dapm kcontrol,这些dapm kcontrol的状态变化,代表着音频路径的变化,从而影响着各个widget的电源状态。
创建widget: snd_soc_dapm_new_controls
snd_soc_dapm_new_controls函数完成widget的创建工作,并把这些创建好的widget注册到声卡的widgets链表中。
/**
* snd_soc_dapm_new_controls - create new dapm controls
* @dapm: DAPM context
* @widget: widget array
* @num: number of widgets
*
* Creates new DAPM controls based upon the templates.
*
* Returns 0 for success else error.
*/
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget,
int num)
{
struct snd_soc_dapm_widget *w;
int i;
for (i = 0; i < num; i++) {
w = snd_soc_dapm_new_control(dapm, widget);
......
widget++;
}
return 0;
}
该函数只是一个简单的一个循环,为传入的widget模板数组依次调用snd_soc_dapm_new_control函数,实际的工作由snd_soc_dapm_new_control完成。
在驱动中定义的snd_soc_dapm_widget数组,只是作为一个模板,所以snd_soc_dapm_new_control所做的第一件事,就是为该widget重新分配内存,并把模板的内容拷贝过来
static struct snd_soc_dapm_widget * snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
size_t name_len;
int ret;
/* create a new dapm widget */
if ((w = dapm_cnew_widget(widget)) == NULL)
return NULL;
由dapm_cnew_widget完成内存申请和拷贝模板的动作。接下来,根据widget的类型做不同的处理:
switch (w->id) {
case snd_soc_dapm_regulator_supply:
w->priv = devm_regulator_get(dapm->dev, w->name);
......
break;
default:
break;
}
对于snd_soc_dapm_regulator_supply类型的widget,根据widget的名称获取对应的regulator结构体。接下来,根据需要,在widget的名称前面加入必要的前缀:
if (dapm->codec && dapm->codec->name_prefix)
snprintf((char *)w->name, name_len, "%s %s",
dapm->codec->name_prefix, widget->name);
else
snprintf((char *)w->name, name_len, "%s", widget->name);
然后为不同类型的widget设置合适的power_check电源状态回调函数,widget类型和对应的power_check回调函数设置如下表所示:
widget的power_check回调函数
widget类型 | power_check回调函数 |
mixer类: snd_soc_dapm_switch snd_soc_dapm_mixer snd_soc_dapm_mixer_named_ctl |
dapm_generic_check_power |
mux类: snd_soc_dapm_mux |
dapm_generic_check_power |
snd_soc_dapm_adc: |
dapm_adc_check_power |
snd_soc_dapm_dac |
dapm_dac_check_power |
端点类: snd_soc_dapm_pga snd_soc_dapm_out_drv snd_soc_dapm_input snd_soc_dapm_output snd_soc_dapm_micbias snd_soc_dapm_spk snd_soc_dapm_hp snd_soc_dapm_mic snd_soc_dapm_line |
dapm_generic_check_power |
snd_soc_dapm_supply: |
dapm_supply_check_power |
snd_soc_dapm_dai | dapm_dai_check_power |
对于其他类型,power_check回调函数为dapm_always_on_check_power
当音频路径发生变化时,power_check回调函数会被调用,用于检查该widget的电源状态是否需要更新。power_check设置完成后,需要设置widget所属的codec、platform以及dapm context,几个用于音频路径的链表也需要初始化,然后把该widget加入到声卡的widgets链表中。
dapm->n_widgets++;
w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets);
几个链表的作用如下:
sources:用于链接所有链接到该widget输入端的snd_soc_path结构
sinks:用于链接所有链接到该widget输出端的snd_soc_path结构
list:用于链接到声卡的widgets链表
dirty:用于链接到声卡的dapm_dirty链表
最后,把widget设置为connect状态:
/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
return w;
connected字段代表着引脚的连接状态,目前,只有以下这些widget使用connected字段:
snd_soc_dapm_output
snd_soc_dapm_input
snd_soc_dapm_hp
snd_soc_dapm_spk
snd_soc_dapm_line
snd_soc_dapm_vmid
snd_soc_dapm_mic
snd_soc_dapm_siggen
驱动程序可以使用以下这些api来设置引脚的连接状态:
snd_soc_dapm_enable_pin
snd_soc_dapm_force_enable_pin
snd_soc_dapm_disable_pin
snd_soc_dapm_nc_pin
到此,widget已经被正确地创建并初始化,而且被挂在声卡的widgets链表中,以后我们就可以通过声卡的widgets链表来遍历所有的widget,再次强调一下snd_soc_dapm_new_controls函数所完成的主要功能:
1)为widget分配内存,并拷贝参数中传入的在驱动中定义好的模板
2)设置power_check回调函数
3)把widget挂在声卡的widgets链表中