对于上面的命名规则,我一直很疑惑,那天我仔细的研究了下:
Kcontrol:
对于struct snd_kcontrol_new结构体里面有以下主要成员:
1、 iface:是定义了kcontrol 的类型,有很多的类型通常以SND_CTL_ELEM_IFACE_xxx定义,有mux、mixer、card、master等一些类型
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_name从widget中找到合适的kcontrol 的,对于mux 是根据route.control_name和kcontrol中private_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是定义控制的源,例如PCM、Master。DIRECTION是
控制的方向如:capture、playback,如果FUNCTION省略的话,那么就代表是双向的。FUNCTION是功能:switch、Volume、route等
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 里面的control,source是源widget,上面route的定义的意思是:通过sink中的kcontrol控制达到source---àsink的path 的控制
在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 去连接,下面去讲解下mux和mixer着两种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;
}
}
这个函数,我自我感觉重要性还是很强的,我讲解下两种情况:
(1)case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
这个case 我们会根据shift,invert,max,reg的值,找出控制这个控制这个path 的kcontrol register的某几位,然后根据这几位的值,判断path 的状态,我们再定义widget的时候,也是需要按照datasheet中的值进行定义widget的
(2)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;
}
对于上面的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 的匹配时不会成功的