ALSA中Widget、route、kcontrol 命名规则

 

对于上面的命名规则,我一直很疑惑,那天我仔细的研究了下:

Kcontrol:

对于struct snd_kcontrol_new结构体里面有以下主要成员:

1、  iface:是定义了kcontrol 的类型,有很多的类型通常以SND_CTL_ELEM_IFACE_xxx定义,有muxmixercardmaster等一些类型

2、  aceee:设置访问的权限,也是有宏实现好的,SNDRV_CTL_ELEM_ACESS_READ是值读的,这个时候,在结构体里面的put函数就没有必要实现了,如果是SNDRV_CTL_ELEM_ACESS_WRITE就意味着是只写的,这个时候get函数就没有必要实现了。加入control 的值频繁的变化的话,那么就设置acess的值是VOLATILE。当control 的值处于非激活状态的时候应设置INACTIVE标志

3、  private_value:是包含一个长整型的值,这个值里面很重要,这个里面的值会传递给info()get()put()这些callback function使用,这个private_value里面有很多的成员,下面我会举例说明下

4、  name:这个成员也是非常重要的成员,因为kcontrol的作用是由名称来区分的,对于相同名称的kcontrol 则用index 区分的,对于mixer通路就是通过和route.control_namewidget中找到合适的kcontrol ,对于mux 是根据route.control_namekcontrolprivate_data里面的txt数组里面的名字匹配的

这个private_data有很多的宏可以定义,定义的成员有主要有以下几个:

         4.1:reg_left指的是register的值,这个代表是左声道的register

         4.2:reg_right指的是register的值,这个代表是右声道的register

         4.3:xshift指的控制的位偏移,其实就是的功能的那个位开始,为头

         4.4:max:指的是控制的功能在codec register中时由几位控制的

         4.5:invert:看英文的意思就是反转的意思,o代表是不反转的意思,1代表反转的意思,如果反转的话,那么设置的值越大结构也就越大,如果是不反转的话,那么效果和反转是相反的。

5、  这个name 的定义还有一些标准的规则,”SOURCE,DIRECTION,FUNCTION” 中文的定义是源,方向,功能”SOURCE是定义控制的源,例如PCMMasterDIRECTION

控制的方向如:captureplayback,如果FUNCTION省略的话,那么就代表是双向的。FUNCTION是功能:switchVolumeroute

6、  put:kcontrol 对于很多的switch slider应用广泛,它可以被用户控件进行存取,从而达到读写codec register的作用,当上层需要写codec register的时候,就会最终调用到这个put函数

7、  get:根据名字也可以看出,get是得到数据就从codec中得到数据,就是read ,上层如果要从codec 中读取数据的时候,最终就会调用到这个read 函数

snd_soc_dapm_add_routes

上面的函数会进行route 的添加和path 的创建

Route 的命名规则是:route(sink,control,source),sink 是目的的widget,control control 的名字,这个control其实就是sink里面的control 就是sink 里面的controlsource是源widget,上面route的定义的意思是:通过sink中的kcontrol控制达到source---àsinkpath 的控制

snd_soc_dapm_add_routes函数里面会进行如下动作

1、  会根据route 里面的sink source的名字找到名字相同的widget,然后创建path ,将找到的widget赋值给path

2、  设置widget是否是extern widget,path connect 的状态,主要分为几种类型:static dynamic、两种类型,有的widget开始就是处于connect status,就不需要自己去通过kcontrol 去连接,下面去讲解下muxmixer着两种case

Case mux:

dapm_connect_mux函数

staticint dapm_connect_mux(struct snd_soc_dapm_context *dapm,

    struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,

    struct snd_soc_dapm_path *path, constchar *control_name,

    conststruct snd_kcontrol_new *kcontrol)

{

    struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;

    int i;

 

    for (i = 0; i < e->max; i++) {

        if (!(strcmp(control_name, e->texts[i]))) {

            list_add(&path->list, &dapm->card->paths);

            list_add(&path->list_sink, &dest->sources);

            list_add(&path->list_source, &src->sinks);

            path->name = (char*)e->texts[i];

            dapm_set_path_status(dest, path, 0);

            return 0;

        }

    }

 

    return -ENODEV;

}

看上面的代码,我们就会发现,我们会将route 中的control_name和这个widget.private_data.txt[i]中的名字数组进行对比,如果一样的话,就代表这个widget中的kcontrol 可以控制这个path 的通路的

接下来我们再看两外一个函数:

Case: mixer

dapm_connect_mixer:会创建关于mixer相关的path

/* connect mixer widget to its interconnecting audio paths */

staticint dapm_connect_mixer(struct snd_soc_dapm_context *dapm,

    struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,

    struct snd_soc_dapm_path *path, constchar *control_name)

{

    int i;

 

    /* search for mixer kcontrol */

    for (i = 0; i < dest->num_kcontrols; i++) {

        if (!strcmp(control_name, dest->kcontrols[i].name)) {

            list_add(&path->list, &dapm->card->paths);

            list_add(&path->list_sink, &dest->sources);

            list_add(&path->list_source, &src->sinks);

            path->name = dest->kcontrols[i].name;

            dapm_set_path_status(dest, path, i);

            return 0;

        }

    }

    return -ENODEV;

}

看了上面的代码,你会发现我们会发现我们的widget里面有很多的kcontrol 数组,我们再创建path 的时候,我们会根据route control name mixer widget中的kcontrol 数组里面的kocntrol .name 进行匹配,如果成功的话,那么才会创建这个path

接下来我们再看另外一个函数:

dapm_set_path_status:这个函数会判断我们的创建的path 的通路的状态

/* set up initial codec paths */

staticvoid dapm_set_path_status(struct snd_soc_dapm_widget *w,

    struct snd_soc_dapm_path *p, int i)

{

    switch (w->id) {

    case snd_soc_dapm_switch:

    case snd_soc_dapm_mixer:

    case snd_soc_dapm_mixer_named_ctl: {

        int val;

        struct soc_mixer_control *mc = (struct soc_mixer_control *)

            w->kcontrols[i].private_value;

        unsignedint reg = mc->reg;

        unsignedint shift = mc->shift;

        int max = mc->max;

        unsignedint mask = (1 << fls(max)) - 1;

        unsignedint invert = mc->invert;

 

        val = snd_soc_read(w->codec, reg);

        val = (val >> shift) & mask;

 

        if ((invert && !val) || (!invert && val))

            p->connect = 1;

        else

            p->connect = 0;

    }

    break;

    case snd_soc_dapm_mux: {

        struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;

        int val, item, bitmask;

 

        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)

        ;

        val = snd_soc_read(w->codec, e->reg);

        item = (val >> e->shift_l) & (bitmask - 1);

 

        p->connect = 0;

        for (i = 0; i < e->max; i++) {

            if (!(strcmp(p->name, e->texts[i])) && item == i)

                p->connect = 1;

        }

    }

    break;

    case snd_soc_dapm_virt_mux: {

        struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;

 

        p->connect = 0;

        /* since a virtual mux has no backing registers to

         * decide which path to connect, it will try to match

         * with the first enumeration.  This is to ensure

         * that the default mux choice (the first) will be

         * correctly powered up during initialization.

         */

        if (!strcmp(p->name, e->texts[0]))

            p->connect = 1;

    }

    break;

    case snd_soc_dapm_value_mux: {

        struct soc_enum *e = (struct soc_enum *)

            w->kcontrols[i].private_value;

        int val, item;

 

        val = snd_soc_read(w->codec, e->reg);

        val = (val >> e->shift_l) & e->mask;

        for (item = 0; item < e->max; item++) {

            if (val == e->values[item])

                break;

        }

 

        p->connect = 0;

        for (i = 0; i < e->max; i++) {

            if (!(strcmp(p->name, e->texts[i])) && item == i)

                p->connect = 1;

        }

    }

    break;

    /* does not effect routing - always connected */

    case snd_soc_dapm_pga:

    case snd_soc_dapm_out_drv:

    case snd_soc_dapm_output:

    case snd_soc_dapm_adc:

    case snd_soc_dapm_input:

    case snd_soc_dapm_dac:

    case snd_soc_dapm_micbias:

    case snd_soc_dapm_vmid:

    case snd_soc_dapm_supply:

    case snd_soc_dapm_aif_in:

    case snd_soc_dapm_aif_out:

        p->connect = 1;

    break;

    /* does effect routing - dynamically connected */

    case snd_soc_dapm_hp:

    case snd_soc_dapm_mic:

    case snd_soc_dapm_spk:

    case snd_soc_dapm_line:

    case snd_soc_dapm_pre:

    case snd_soc_dapm_post:

        p->connect = 0;

    break;

    }

}

这个函数,我自我感觉重要性还是很强的,我讲解下两种情况:

1case snd_soc_dapm_switch:

    case snd_soc_dapm_mixer:

    case snd_soc_dapm_mixer_named_ctl:

这个case 我们会根据shiftinvertmaxreg的值,找出控制这个控制这个path kcontrol register的某几位,然后根据这几位的值,判断path 的状态,我们再定义widget的时候,也是需要按照datasheet中的值进行定义widget

2case snd_soc_dapm_mux: {

        struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;

        int val, item, bitmask;

 

        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)

        ;

        val = snd_soc_read(w->codec, e->reg);

        item = (val >> e->shift_l) & (bitmask - 1);

 

        p->connect = 0;

        for (i = 0; i < e->max; i++) {

            if (!(strcmp(p->name, e->texts[i])) && item == i)

                p->connect = 1;

        }

对于上面的case ,我们会会读取出reg的值,将读取出来的值获取到item 的值,我们会将path.name widget.private_data.txt[i]中的name数组进行对比,首先是必须相等,然后就是从reg中的item必须和匹配i的值是相等的,这就要求我们在定义这个txt档的时候,必须和datasheet中定义的顺序是一致的;举例如下:

staticconstchar *linput_mux_text[] = {

    "IN1L", "IN2L", "IN3L"

};

 

看到上面的datasheet中值的顺序吗。这也是按照这种顺序过来的,不然我们的item 的匹配时不会成功的

你可能感兴趣的:(list,struct,function,Path,Codec,playback)