對於上面的命名規則,我一直很疑惑,那天我仔細的研究了下:
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 的匹配時不會成功的