linux-alsa詳解13之DAPM詳解6音頻路徑route


1 音頻路徑的注冊

系統中注冊的各種widget需要互相連接在一起才能協調工作,連接關系通過snd_soc_dapm_route結構來定義,關於如何用snd_soc_dapm_route結構來定義路徑信息,參考linux-alsa詳解10之DAPM詳解3各種widget定義。通常,所有的路徑信息會用一個snd_soc_dapm_route結構數組來定義。和widget一樣,路徑信息也分別存在與codec驅動,machine驅動和platform驅動中,通過兩步來注冊音頻路徑信息:

(1)通過snd_soc_codec_driver/snd_soc_platform_driver/snd_soc_card結構中component成員的dapm_routes和num_dapm_routes字段初始化;
(2)在codec、platform的的probe回調中主動注冊音頻路徑,machine驅動中則在函數snd_soc_instantiate_card回調函數來注冊音頻路徑;
兩種方法最終都是通過調用snd_soc_dapm_add_routes函數來完成音頻路徑的注冊工作的。

1.1 codec、platform注冊音頻路徑

和widget注冊一樣,snd_soc_instantiate_card中,machine匹配對應的codec、platform后,分別調用codec和platform probe函數,probe函數中調用snd_soc_dapm_add_routes函數來完成音頻路徑的注冊:

 1 static int soc_probe_component(struct snd_soc_card *card,
 2     struct snd_soc_component *component)
 3 {
 4     ...
 5 
 6     if (component->controls)
 7         snd_soc_add_component_controls(component, component->controls,
 8                      component->num_controls);
 9     if (component->dapm_routes)
10         snd_soc_dapm_add_routes(dapm, component->dapm_routes,
11                     component->num_dapm_routes);
12 
13     list_add(&dapm->list, &card->dapm_list);
14 
15     ...
16 }

1.2 machine注冊音頻路徑

snd_soc_instantiate_card中調用函數snd_soc_dapm_add_routes注冊音頻路徑:

 1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 2 {
 3     ...
 4 
 5     snd_soc_dapm_link_dai_widgets(card);
 6     snd_soc_dapm_connect_dai_link_widgets(card);
 7 
 8     if (card->controls)
 9         snd_soc_add_card_controls(card, card->controls, card->num_controls);
10 
11     if (card->dapm_routes)
12         snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
13                     card->num_dapm_routes);
14 
15     if (card->of_dapm_routes)
16         snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
17                     card->num_of_dapm_routes);
18 
19     ...
20 }

2 注冊音頻路徑函數

如果widget之間沒有連接關系,dapm就無法實現動態的電源管理工作,正是widget之間有了連結關系,這些連接關系形成了一條所謂的完成的音頻路徑,dapm可以順着這條路徑,統一控制路徑上所有widget的電源狀態,前面我們已經知道,widget之間是使用snd_soc_path結構進行連接的,驅動要做的是定義一個snd_soc_route結構數組,該數組的每個條目描述了目的widget的和源widget的名稱,以及控制這個連接的kcontrol的名稱,最終,驅動程序使用api函數snd_soc_dapm_add_routes來注冊這些連接信息,接下來我們就是要分析該函數的具體實現方式:

 1 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 2                 const struct snd_soc_dapm_route *route, int num)
 3 {
 4     int i, r, ret = 0;
 5 
 6     mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 7     for (i = 0; i < num; i++) {
 8         r = snd_soc_dapm_add_route(dapm, route);
 9         if (r < 0) {
10             dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n",
11                 route->source,
12                 route->control ? route->control : "direct",
13                 route->sink);
14             ret = r;
15         }
16         route++;
17     }
18     mutex_unlock(&dapm->card->dapm_mutex);
19 
20     return ret;
21 }

該函數只是一個循環,依次對參數傳入的數組調用snd_soc_dapm_add_route,主要的工作由snd_soc_dapm_add_route完成。

2.1 函數snd_soc_dapm_add_route

 1 static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 2                   const struct snd_soc_dapm_route *route)
 3 {
 4     struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
 5     struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
 6     const char *sink;
 7     const char *source;
 8     char prefixed_sink[80];
 9     char prefixed_source[80];
10     const char *prefix;
11     int ret;
12     /*名稱前綴的處理部分*/
13     prefix = soc_dapm_prefix(dapm);
14     if (prefix) {
15         snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
16              prefix, route->sink);
17         sink = prefixed_sink;
18         snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
19              prefix, route->source);
20         source = prefixed_source;
21     } else {
22         sink = route->sink;
23         source = route->source;
24     }
25 
26     wsource = dapm_wcache_lookup(&dapm->path_source_cache, source);
27     wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink);
28 
29     if (wsink && wsource)
30         goto skip_search;
31 
32     /*
33      * find src and dest widgets over all widgets but favor a widget from
34      * current DAPM context
35      */用widget的名字來比較,遍歷聲卡的widgets鏈表,找出源widget和目的widget的指針
36     list_for_each_entry(w, &dapm->card->widgets, list) {
37         if (!wsink && !(strcmp(w->name, sink))) {
38             wtsink = w;//目的widget指針
39             if (w->dapm == dapm) {
40                 wsink = w;
41                 if (wsource)
42                     break;
43             }
44             continue;
45         }
46         if (!wsource && !(strcmp(w->name, source))) {
47             wtsource = w;//源widget指針
48             if (w->dapm == dapm) {
49                 wsource = w;
50                 if (wsink)
51                     break;
52             }
53         }
54     }
55     /* use widget from another DAPM context if not found from this */如果在本dapm context中沒有找到,則使用別的dapm context中找到的widget
56     if (!wsink)
57         wsink = wtsink;
58     if (!wsource)
59         wsource = wtsource;
60 
61     if (wsource == NULL) {
62         dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
63             route->source);
64         return -ENODEV;
65     }
66     if (wsink == NULL) {
67         dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
68             route->sink);
69         return -ENODEV;
70     }
71 
72 skip_search:
73     dapm_wcache_update(&dapm->path_sink_cache, wsink);
74     dapm_wcache_update(&dapm->path_source_cache, wsource);
75 
76     ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,//增加一條連接信息
77         route->connected);
78     if (ret)
79         goto err;
80 
81     return 0;
82 err:
83     dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n",
84          source, route->control, sink);
85     return ret;
86 }

2.2 函數snd_soc_dapm_add_path

snd_soc_dapm_add_path函數是整個調用鏈條中的關鍵:

  1 static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
  2     struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
  3     const char *control,
  4     int (*connected)(struct snd_soc_dapm_widget *source,
  5              struct snd_soc_dapm_widget *sink))
  6 {
  7     struct snd_soc_dapm_widget *widgets[2];
  8     enum snd_soc_dapm_direction dir;
  9     struct snd_soc_dapm_path *path;
 10     int ret;
 11 
 12     if (wsink->is_supply && !wsource->is_supply) {
 13         dev_err(dapm->dev,
 14             "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
 15             wsource->name, wsink->name);
 16         return -EINVAL;
 17     }
 18 
 19     if (connected && !wsource->is_supply) {
 20         dev_err(dapm->dev,
 21             "connected() callback only supported for supply widgets (%s -> %s)\n",
 22             wsource->name, wsink->name);
 23         return -EINVAL;
 24     }
 25 
 26     if (wsource->is_supply && control) {
 27         dev_err(dapm->dev,
 28             "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
 29             wsource->name, control, wsink->name);
 30         return -EINVAL;
 31     }
 32 
 33     ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
 34     if (ret)
 35         return ret;
 36     /*為這個連接分配了一個snd_soc_path結構*/
 37     path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
 38     if (!path)
 39         return -ENOMEM;
 40     /*path的source和sink字段分別指向源widget和目的widget*/
 41     path->node[SND_SOC_DAPM_DIR_IN] = wsource;
 42     path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
 43     widgets[SND_SOC_DAPM_DIR_IN] = wsource;
 44     widgets[SND_SOC_DAPM_DIR_OUT] = wsink;
 45     /*connected字段保存connected回調函數*/
 46     path->connected = connected;
 47     INIT_LIST_HEAD(&path->list);//初始化幾個snd_soc_path結構中的鏈表
 48     INIT_LIST_HEAD(&path->list_kcontrol);
 49 
 50     if (wsource->is_supply || wsink->is_supply)
 51         path->is_supply = 1;
 52     /*增加了連結關系,所以把源widget和目的widget加入到dapm_dirty鏈表中。如果沒有kcontrol來控制該連接關系,則這是一個靜態連接,直接用path把它們連接在一起*/
 53     /* connect static paths */
 54     if (control == NULL) {
 55         path->connect = 1;
 56     } else {
 57         switch (wsource->id) {
 58         case snd_soc_dapm_demux:
 59             ret = dapm_connect_mux(dapm, path, control, wsource);
 60             if (ret)
 61                 goto err;
 62             break;
 63         default:
 64             break;
 65         }
 66         /*按照目的widget來判斷,如果屬於以上這些類型,直接把它們連接在一起即可。目的widget如果是mixer和mux類型,分別用dapm_connect_mixer和dapm_connect_mux函數完成連接工作*/
 67         switch (wsink->id) {
 68         case snd_soc_dapm_mux:
 69             ret = dapm_connect_mux(dapm, path, control, wsink);
 70             if (ret != 0)
 71                 goto err;
 72             break;
 73         case snd_soc_dapm_switch:
 74         case snd_soc_dapm_mixer:
 75         case snd_soc_dapm_mixer_named_ctl:
 76             ret = dapm_connect_mixer(dapm, path, control);
 77             if (ret != 0)
 78                 goto err;
 79             break;
 80         default:
 81             break;
 82         }
 83     }
 84 
 85     list_add(&path->list, &dapm->card->paths);
 86     snd_soc_dapm_for_each_direction(dir)
 87         list_add(&path->list_node[dir], &widgets[dir]->edges[dir]);
 88 
 89     snd_soc_dapm_for_each_direction(dir) {
 90         dapm_update_widget_flags(widgets[dir]);
 91         dapm_mark_dirty(widgets[dir], "Route added");
 92     }
 93 
 94     if (dapm->card->instantiated && path->connect)
 95         dapm_path_invalidate(path);
 96 
 97     return 0;
 98 err:
 99     kfree(path);
100     return ret;
101 }

2.3 函數dapm_connect_mixer

連接一個目的widget為mixer類型的所有輸入端

 1 /* connect mixer widget to its interconnecting audio paths */
 2 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
 3     struct snd_soc_dapm_path *path, const char *control_name)
 4 {
 5     int i;
 6 
 7     /* search for mixer kcontrol */
 8     for (i = 0; i < path->sink->num_kcontrols; i++) {
 9         if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
10             path->name = path->sink->kcontrol_news[i].name;
11             dapm_set_mixer_path_status(path, i);
12             return 0;
13         }
14     }
15     return -ENODEV;
16 }

用需要用來連接的kcontrol的名字,和目的widget中的kcontrol模板數組中的名字相比較,找出該kcontrol在widget中的編號,path的名字設置為該kcontrol的名字,然后用dapm_set_path_status函數來初始化該輸入端的連接狀態。連接兩個widget的鏈表操作和其他widget是一樣的。

2.3.1 函數dapm_set_mixer_path_status

根據傳入widget中的kcontrol編號,讀取實際寄存器的值,根據寄存器的值來初始化這個path是否處於連接狀態。

 1 /* set up initial codec paths */
 2 static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 3 {
 4     struct soc_mixer_control *mc = (struct soc_mixer_control *)
 5         p->sink->kcontrol_news[i].private_value;
 6     unsigned int reg = mc->reg;
 7     unsigned int shift = mc->shift;
 8     unsigned int max = mc->max;
 9     unsigned int mask = (1 << fls(max)) - 1;
10     unsigned int invert = mc->invert;
11     unsigned int val;
12 
13     if (reg != SND_SOC_NOPM) {
14         soc_dapm_read(p->sink->dapm, reg, &val);
15         val = (val >> shift) & mask;
16         if (invert)
17             val = max - val;
18         p->connect = !!val;
19     } else {
20         p->connect = 0;
21     }
22 }

2.4 函數dapm_connect_mux 

用該函數連接一個目的widget是mux類型的所有輸入端。

 1 /* connect mux widget to its interconnecting audio paths */
 2 static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 3     struct snd_soc_dapm_path *path, const char *control_name,
 4     struct snd_soc_dapm_widget *w)
 5 {
 6     const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
 7     struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 8     unsigned int val, item;
 9     int i;
10 
11     if (e->reg != SND_SOC_NOPM) {
12         soc_dapm_read(dapm, e->reg, &val);
13         val = (val >> e->shift_l) & e->mask;
14         item = snd_soc_enum_val_to_item(e, val);
15     } else {
16         /* since a virtual mux has no backing registers to
17          * decide which path to connect, it will try to match
18          * with the first enumeration.  This is to ensure
19          * that the default mux choice (the first) will be
20          * correctly powered up during initialization.
21          */
22         item = 0;
23     }
24 
25     for (i = 0; i < e->items; i++) {
26         if (!(strcmp(control_name, e->texts[i]))) {
27             path->name = e->texts[i];
28             if (i == item)
29                 path->connect = 1;
30             else
31                 path->connect = 0;
32             return 0;
33         }
34     }
35 
36     return -ENODEV;
37 }

和mixer類型一樣用名字進行匹配,只不過mux類型的kcontrol只需一個,所以要通過private_value字段所指向的soc_enum結構找出匹配的輸入腳編號。初始化該輸入端的連接狀態。

3 總結

當widget之間通過path進行連接之后,他們之間的關系就如下圖所示:

到這里為止,我們為聲卡創建並初始化好了所需的widget,各個widget也通過path連接在了一起,接下來,dapm等待用戶的指令,一旦某個dapm kcontrol被用戶空間改變,利用這些連接關系,dapm會重新創建音頻路徑,脫離音頻路徑的widget會被下電,加入音頻路徑的widget會被上電,所有的上下電動作都會自動完成,用戶空間的應用程序無需關注這些變化,它只管按需要改變某個dapm kcontrol即可。

參考博文:https://blog.csdn.net/DroidPhone/java/article/details/14052861


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM