zoukankan      html  css  js  c++  java
  • 创建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链表中

  • 相关阅读:
    C++ string 类详解
    C语言 -- 字符串详解
    基本数据结构 -- 链表的遍历、查找、插入和删除
    Shell 基础 -- 总结几种括号、引号的用法
    用 C 语言描述几种排序算法
    Win10 + vs2017 编译并配置tesseract4.1.0
    前端如何引入vConsole
    php设计模式-数据对象映射模式
    PHP设计模式-策略模式
    PHP设计模式-适配器模式
  • 原文地址:https://www.cnblogs.com/-glb/p/14411348.html
Copyright © 2011-2022 走看看