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