创建widget之snd_soc_dapm_new_controls函数浅析

 

 还是以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
snd_soc_dapm_virt_mux
snd_soc_dapm_value_mux

dapm_generic_check_power

snd_soc_dapm_adc:
snd_soc_dapm_aif_out:

dapm_adc_check_power

snd_soc_dapm_dac
snd_soc_dapm_aif_in

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:
snd_soc_dapm_regulator_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链表中

原文地址:https://www.cnblogs.com/-glb/p/14411348.html