ALSA声卡驱动的DAPM(二)-建立过程

在上一篇文章中,我们重点介绍了widget、path、route之间的关系及其widget的注册;

http://www.cnblogs.com/linhaostudy/p/8509899.html

在最后一章中,我们已经简单介绍了snd_soc_dapm_new_controls函数用来创建widget。

实际上,这个函数只是创建widget的第一步,它为每一个widget分配内存,初始化;

要使widget之间具备连接能力,我们还需要第二个函数snd_soc_dapm_new_widgets:这个函数会根据widget的信息,创建widget所需要的dapm kcontrol,这些dapm kcontol的状态变化,代表着音频路径的变化,从而影响着各个widget的电源状态。看到函数的名称可能会迷惑一下,实际上,snd_soc_dapm_new_controls的作用更多地是创建widget,而snd_soc_dapm_new_widget的作用则更多地是创建widget所包含的kcontrol,所以在我看来,这两个函数名称应该换过来叫更好!下面我们分别介绍一下这两个函数是如何工作的。

一、创建widget:snd_soc_dapm_new_controls:

snd_soc_dapm_new_controls函数完成widget的创建工作,并把这些创建好的widget注册在声卡的widgets链表中,我们看看他的定义:

 1 /**
 2  * snd_soc_dapm_new_controls - create new dapm controls
 3  * @dapm: DAPM context
 4  * @widget: widget array
 5  * @num: number of widgets
 6  *
 7  * Creates new DAPM controls based upon the templates.
 8  *
 9  * Returns 0 for success else error.
10  */
11 int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
12     const struct snd_soc_dapm_widget *widget,
13     int num)
14 {
15     struct snd_soc_dapm_widget *w;
16     int i;
17     int ret = 0;
18
19     mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
20     for (i = 0; i < num; i++) {
21         w = snd_soc_dapm_new_control(dapm, widget);
22         if (!w) {
23             dev_err(dapm->dev,
24                 "ASoC: Failed to create DAPM control %s\n",
25                 widget->name);
26             ret = -ENOMEM;
27             break;
28         }
29         widget++;
30     }
31     mutex_unlock(&dapm->card->dapm_mutex);
32     return ret;
33 }

该函数只是简单的一个循环,为传入的widget模板数组依次调用snd_soc_dapm_new_control函数,实际的工作由snd_soc_dapm_new_control完成,继续进入该函数,看看它做了那些工作。

我们之前已经说过,驱动中定义的snd_soc_dapm_widget数组,只是作为一个模板,所以,snd_soc_dapm_new_control所做的第一件事,就是为该widget重新分配内存,并把模板的内容拷贝过来:

1 static struct snd_soc_dapm_widget *
2 snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
3                          const struct snd_soc_dapm_widget *widget)
4 {
5         struct snd_soc_dapm_widget *w;
6         int ret;
7
8         if ((w = dapm_cnew_widget(widget)) == NULL)
9                 return NULL;

由dapm_cnew_widget完成内存申请和拷贝模板的动作。接下来,根据widget的类型做不同的处理:

 1         switch (w->id) {
 2         case snd_soc_dapm_regulator_supply:
 3                 w->regulator = devm_regulator_get(dapm->dev, w->name);
 4                 ......
 5
 6                 if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
 7                         ret = regulator_allow_bypass(w->regulator, true);
 8                         ......
 9                 }
10                 break;
11         case snd_soc_dapm_clock_supply:
12 #ifdef CONFIG_CLKDEV_LOOKUP
13                 w->clk = devm_clk_get(dapm->dev, w->name);
14                 ......
15 #else
16                 return NULL;
17 #endif
18                 break;
19         default:
20                 break;
21         }

对于snd_soc_dapm_regulator_supply类型的widget,根据widget的名称获取对应的regulator结构,对于snd_soc_dapm_clock_supply类型的widget,根据widget的名称,获取对应的clock结构。接下来,根据需要,在widget的名称前加入必要的前缀:

if (dapm->codec && dapm->codec->name_prefix)
        w->name = kasprintf(GFP_KERNEL, "%s %s",
                dapm->codec->name_prefix, widget->name);
else
        w->name = kasprintf(GFP_KERNEL, "%s", widget->name); 

然后,为不同类型的widget设置合适的power_check电源状态回调函数,widget类型和对应的power_check回调函数设置如下表所示:

widget的power_check回调函数
widget类型 power_check回调函数
mixer类:
snd_soc_dapm_switch
snd_soc_dapm_mixer
snd_soc_dapm_mixer_named_ctl
dapm_generic_check_power
mux类:
snd_soc_dapm_mux
snd_soc_dapm_mux
snd_soc_dapm_mux
dapm_generic_check_power
snd_soc_dapm_dai_out dapm_adc_check_power
snd_soc_dapm_dai_in dapm_dac_check_power
端点类:
snd_soc_dapm_adc
snd_soc_dapm_aif_out
snd_soc_dapm_dac
snd_soc_dapm_aif_in
snd_soc_dapm_pga
snd_soc_dapm_out_drv
snd_soc_dapm_input
snd_soc_dapm_output
snd_soc_dapm_micbias
snd_soc_dapm_spk
snd_soc_dapm_hp
snd_soc_dapm_mic
snd_soc_dapm_line
snd_soc_dapm_dai_link
dapm_generic_check_power
电源/时钟/影子widget:
snd_soc_dapm_supply
snd_soc_dapm_regulator_supply
snd_soc_dapm_clock_supply
snd_soc_dapm_kcontrol
dapm_supply_check_power
其它类型 dapm_always_on_check_power

当音频路径发生变化时,power_check回调会被调用,用于检查该widget的电源状态是否需要更新。power_check设置完成后,需要设置widget所属的codec、platform和context,几个用于音频路径的链表也需要初始化,然后,把该widget加入到声卡的widgets链表中:

1         w->dapm = dapm;
2         w->codec = dapm->codec;
3         w->platform = dapm->platform;
4         INIT_LIST_HEAD(&w->sources);
5         INIT_LIST_HEAD(&w->sinks);
6         INIT_LIST_HEAD(&w->list);
7         INIT_LIST_HEAD(&w->dirty);
8         list_add(&w->list, &dapm->card->widgets);

几个链表的作用如下:

  • sources    用于链接所有连接到该widget输入端的snd_soc_path结构
  • sinks    用于链接所有连接到该widget输出端的snd_soc_path结构
  • list    用于链接到声卡的widgets链表
  • dirty    用于链接到声卡的dapm_dirty链表

最后,把widget设置为connect状态:

1         /* machine layer set ups unconnected pins and insertions */
2         w->connected = 1;
3         return w;
4 }

connected字段代表着引脚的连接状态,目前,只有以下这些widget使用connected字段:

  • snd_soc_dapm_output
  • snd_soc_dapm_input
  • snd_soc_dapm_hp
  • snd_soc_dapm_spk
  • snd_soc_dapm_line
  • snd_soc_dapm_vmid
  • snd_soc_dapm_mic
  • snd_soc_dapm_siggen

驱动程序可以使用以下这些api来设置引脚的连接状态:

  • snd_soc_dapm_enable_pin
  • snd_soc_dapm_force_enable_pin
  • snd_soc_dapm_disable_pin
  • snd_soc_dapm_nc_pin

到此,widget已经被正确地创建并初始化,而且被挂在声卡的widgets链表中,以后我们就可以通过声卡的widgets链表来遍历所有的widget,再次强调一下snd_soc_dapm_new_controls函数所完成的主要功能:

  • 为widget分配内存,并拷贝参数中传入的在驱动中定义好的模板
  • 设置power_check回调函数
  • 把widget挂在声卡的widgets链表中

二、为widget建立dapm kcontrol

定义一个widget,我们需要指定两个很重要的内容:一个是用于控制widget的电源状态的reg/shift等寄存器信息,另一个是用于控制音频路径切换的dapm kcontrol信息,这些dapm kcontrol有它们自己的reg/shift寄存器信息用于切换widget的路径连接方式。

 1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 2 {
 3         ......
 4         /* card bind complete so register a sound card */
 5         ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 6                         card->owner, 0, &card->snd_card);
 7         ......
 8
 9         card->dapm.bias_level = SND_SOC_BIAS_OFF;
10         card->dapm.dev = card->dev;
11         card->dapm.card = card;
12         list_add(&card->dapm.list, &card->dapm_list);
13
14 #ifdef CONFIG_DEBUG_FS
15         snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
16 #endif
17         ......
18         if (card->dapm_widgets)    /* 创建machine级别的widget  */
19                 snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
20                                           card->num_dapm_widgets);
21         ......
22         snd_soc_dapm_link_dai_widgets(card);  /*  连接dai widget  */
23
24         if (card->controls)    /*  建立machine级别的普通kcontrol控件  */
25                 snd_soc_add_card_controls(card, card->controls, card->num_controls);
26
27         if (card->dapm_routes)    /*  注册machine级别的路径连接信息  */
28                 snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
29                                         card->num_dapm_routes);
30         ......
31
32         if (card->fully_routed)    /*  如果该标志被置位,自动把codec中没有路径连接信息的引脚设置为无用widget  */
33                 list_for_each_entry(codec, &card->codec_dev_list, card_list)
34                         snd_soc_dapm_auto_nc_codec_pins(codec);
35
36         snd_soc_dapm_new_widgets(card);    /*初始化widget包含的dapm kcontrol、电源状态和连接状态*/
37
38         ret = snd_card_register(card->snd_card);
39         ......
40         card->instantiated = 1;
41         snd_soc_dapm_sync(&card->dapm);
42         ......
43         return 0;
44 } 

正如我添加的注释中所示,在完成machine级别的widget和route处理之后,调用的snd_soc_dapm_new_widgets函数,来为所有已经注册的widget初始化他们所包含的dapm kcontrol,并初始化widget的电源状态和路径连接状态。下面我们看看snd_soc_dapm_new_widgets函数的工作过程。

2.1 snd_soc_dapm_new_widgets函数:

 1 int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
 2 {
 3         ......
 4         list_for_each_entry(w, &card->widgets, list)
 5         {
 6                 if (w->new)
 7                         continue;
 8
 9                 if (w->num_kcontrols) {
10                         w->kcontrols = kzalloc(w->num_kcontrols *
11                                                 sizeof(struct snd_kcontrol *),
12                                                 GFP_KERNEL);
13                         ......
14                 }

接着,对几种能影响音频路径的widget,创建并初始化它们所包含的dapm kcontrol:

 1                 switch(w->id) {
 2                 case snd_soc_dapm_switch:
 3                 case snd_soc_dapm_mixer:
 4                 case snd_soc_dapm_mixer_named_ctl:
 5                         dapm_new_mixer(w);
 6                         break;
 7                 case snd_soc_dapm_mux:
 8                 case snd_soc_dapm_virt_mux:
 9                 case snd_soc_dapm_value_mux:
10                         dapm_new_mux(w);
11                         break;
12                 case snd_soc_dapm_pga:
13                 case snd_soc_dapm_out_drv:
14                         dapm_new_pga(w);
15                         break;
16                 default:
17                         break;
18                 }

需要用到的创建函数分别是:

  • dapm_new_mixer()    对于mixer类型,用该函数创建dapm kcontrol;
  • dapm_new_mux()   对于mux类型,用该函数创建dapm kcontrol;
  • dapm_new_pga()   对于pga类型,用该函数创建dapm kcontrol;

接着,设置new字段,表明该widget已经初始化完成,我们还要把该widget加入到声卡的dapm_dirty链表中,表明该widget的状态发生了变化,稍后在合适的时刻,dapm框架会扫描dapm_dirty链表,统一处理所有已经变化的widget。为什么要统一处理?因为dapm要控制各种widget的上下电顺序,同时也是为了减少寄存器的读写次数(多个widget可能使用同一个寄存器):

1                 w->new = 1;
2
3                 dapm_mark_dirty(w, "new widget");
4                 dapm_debugfs_add_widget(w);
5         }

最后,通过dapm_power_widgets函数,统一处理所有位于dapm_dirty链表上的widget的状态改变:

1 dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
2 ......
3 return 0;  

三、为widget建立连接关系

如果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                 ......
10                 route++;
11         }
12         mutex_unlock(&dapm->card->dapm_mutex);
13
14         return ret;
15 }  

该函数只是一个循环,依次对参数传入的数组调用snd_soc_dapm_add_route,主要的工作由snd_soc_dapm_add_route完成。我们进入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         ......
 9         list_for_each_entry(w, &dapm->card->widgets, list) {
10                 if (!wsink && !(strcmp(w->name, sink))) {
11                         wtsink = w;
12                         if (w->dapm == dapm)
13                                 wsink = w;
14                         continue;
15                 }
16                 if (!wsource && !(strcmp(w->name, source))) {
17                         wtsource = w;
18                         if (w->dapm == dapm)
19                                 wsource = w;
20                 }
21         }

上面的代码我再次省略了关于名称前缀的处理部分。我们可以看到,用widget的名字来比较,遍历声卡的widgets链表,找出源widget和目的widget的指针,这段代码虽然正确,但我总感觉少了一个判断退出循环的条件,如果链表的开头就找到了两个widget,还是要遍历整个链表才结束循环,好浪费时间。

下面,如果在本dapm context中没有找到,则使用别的dapm context中找到的widget:

1         if (!wsink)
2                 wsink = wtsink;
3         if (!wsource)
4                 wsource = wtsource;

最后,使用来增加一条连接信息:

1         ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
2                 route->connected);
3         ......
4
5         return 0;
6 }

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_path *path;
 8         int ret;
 9
10         path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
11         if (!path)
12                 return -ENOMEM;
13
14         path->source = wsource;
15         path->sink = wsink;
16         path->connected = connected;
17         INIT_LIST_HEAD(&path->list);
18         INIT_LIST_HEAD(&path->list_kcontrol);
19         INIT_LIST_HEAD(&path->list_source);
20         INIT_LIST_HEAD(&path->list_sink);

最后,使用来增加一条连接信息:

1 ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
2                 route->connected);
3         ......
4
5         return 0;
6 }  

snd_soc_dapm_add_path函数是整个调用链条中的关键,我们来分析一下:(注意linux3.10.28代码没有相应的snd_soc_dapm_add_path函数,在linux3.12才有设计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_path *path;
 8         int ret;
 9
10         path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
11         if (!path)
12                 return -ENOMEM;
13
14         path->source = wsource;
15         path->sink = wsink;
16         path->connected = connected;
17         INIT_LIST_HEAD(&path->list);
18         INIT_LIST_HEAD(&path->list_kcontrol);
19         INIT_LIST_HEAD(&path->list_source);
20         INIT_LIST_HEAD(&path->list_sink);  

函数的一开始,首先为这个连接分配了一个snd_soc_path结构,path的source和sink字段分别指向源widget和目的widget,connected字段保存connected回调函数,初始化几个snd_soc_path结构中的几个链表。

 1 /* check for external widgets */
 2         if (wsink->id == snd_soc_dapm_input) {
 3                 if (wsource->id == snd_soc_dapm_micbias ||
 4                         wsource->id == snd_soc_dapm_mic ||
 5                         wsource->id == snd_soc_dapm_line ||
 6                         wsource->id == snd_soc_dapm_output)
 7                         wsink->ext = 1;
 8         }
 9         if (wsource->id == snd_soc_dapm_output) {
10                 if (wsink->id == snd_soc_dapm_spk ||
11                         wsink->id == snd_soc_dapm_hp ||
12                         wsink->id == snd_soc_dapm_line ||
13                         wsink->id == snd_soc_dapm_input)
14                         wsource->ext = 1;
15         }  

这段代码用于判断是否有外部连接关系,如果有,置位widget的ext字段。判断方法从代码中可以方便地看出:

  • 目的widget是一个输入脚,如果源widget是mic、line、micbias或output,则认为目的widget具有外部连接关系。
  • 源widget是一个输出脚,如果目的widget是spk、hp、line或input,则认为源widget具有外部连接关系。
 1 dapm_mark_dirty(wsource, "Route added");
 2 dapm_mark_dirty(wsink, "Route added");
 3
 4 /* connect static paths */
 5 if (control == NULL) {
 6         list_add(&path->list, &dapm->card->paths);
 7         list_add(&path->list_sink, &wsink->sources);
 8         list_add(&path->list_source, &wsource->sinks);
 9         path->connect = 1;
10         return 0;
11 }  

因为增加了连结关系,所以把源widget和目的widget加入到dapm_dirty链表中。如果没有kcontrol来控制该连接关系,则这是一个静态连接,直接用path把它们连接在一起。在接着往下看:

 1         /* connect dynamic paths */
 2         switch (wsink->id) {
 3         case snd_soc_dapm_adc:
 4         case snd_soc_dapm_dac:
 5         case snd_soc_dapm_pga:
 6         case snd_soc_dapm_out_drv:
 7         case snd_soc_dapm_input:
 8         case snd_soc_dapm_output:
 9         case snd_soc_dapm_siggen:
10         case snd_soc_dapm_micbias:
11         case snd_soc_dapm_vmid:
12         case snd_soc_dapm_pre:
13         case snd_soc_dapm_post:
14         case snd_soc_dapm_supply:
15         case snd_soc_dapm_regulator_supply:
16         case snd_soc_dapm_clock_supply:
17         case snd_soc_dapm_aif_in:
18         case snd_soc_dapm_aif_out:
19         case snd_soc_dapm_dai_in:
20         case snd_soc_dapm_dai_out:
21         case snd_soc_dapm_dai_link:
22         case snd_soc_dapm_kcontrol:
23                 list_add(&path->list, &dapm->card->paths);
24                 list_add(&path->list_sink, &wsink->sources);
25                 list_add(&path->list_source, &wsource->sinks);
26                 path->connect = 1;
27                 return 0;

按照目的widget来判断,如果属于以上这些类型,直接把它们连接在一起即可,这段感觉有点多余,因为通常以上这些类型的widget本来也没有kcontrol,直接用上一段代码就可以了,也许是dapm的作者们想着以后可能会有所扩展吧。

 1 case snd_soc_dapm_mux:
 2 case snd_soc_dapm_virt_mux:
 3 case snd_soc_dapm_value_mux:
 4         ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
 5                 &wsink->kcontrol_news[0]);
 6         if (ret != 0)
 7                 goto err;
 8         break;
 9 case snd_soc_dapm_switch:
10 case snd_soc_dapm_mixer:
11 case snd_soc_dapm_mixer_named_ctl:
12         ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
13         if (ret != 0)
14                 goto err;
15         break;  

当widget之间通过path进行连接之后,他们之间的关系就如下图所示:

到这里为止,我们为声卡创建并初始化好了所需的widget,各个widget也通过path连接在了一起,接下来,dapm等待用户的指令,一旦某个dapm kcontrol被用户空间改变,利用这些连接关系,dapm会重新创建音频路径,脱离音频路径的widget会被下电,加入音频路径的widget会被上电,所有的上下电动作都会自动完成,用户空间的应用程序无需关注这些变化,它只管按需要改变某个dapm kcontrol即可。

原文地址:https://www.cnblogs.com/linhaostudy/p/8513276.html

时间: 2024-08-25 08:50:30

ALSA声卡驱动的DAPM(二)-建立过程的相关文章

PCM data flow - part 6: 声卡和PCM设备的建立过程

前面几章分析了Codec.Platform.Machine驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知.接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备. PCM逻辑设备,我们又习惯称之为PCM中间层或pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝:往下是触发codec.platform.machine的操作函数,实现音频数据在dma_buffer<-> cpu

Linux ALSA声卡驱动之二:声卡的创建

1. struct snd_card 1.1. snd_card是什么 snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体.正因为如此,本节中,我们也从 struct cnd_card开始吧. 1.2. snd_card的定义 snd_card的定义位于改头文件中:include/sound/core.h [c-sharp] vi

Linux ALSA声卡驱动之三:PCM设备的创建

声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! 1. PCM是什么 PCM是英文Pulse-code modulation的缩写,中文译名是脉冲编码调制.我们知道在现实生活中,人耳听到的声音是模拟信号,PCM就是要把声音从模拟转换成数字信号的一种技术,他的原理简单地说就是利用一个固定的频率对模拟信号进行采样,采样后的信号在波形上看就像一串连续的幅值不一的脉冲,把这些脉冲的幅值按一定的精度进行量化,这些量化后的数值被连续地输出.传输.处

Linux ALSA声卡驱动之四:Control设备的创建

声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! Control接口 Control接口主要让用户空间的应用程序(alsa-lib)可以访问和控制音频codec芯片中的多路开关,滑动控件等.对于Mixer(混音)来说,Control接口显得尤为重要,从ALSA 0.9.x版本开始,所有的mixer工作都是通过control接口的API来实现的. ALSA已经为AC97定义了完整的控制接口模型,如果你的Codec芯片只支持AC97接口,你

Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)

转自http://blog.csdn.net/droidphone/article/details/7165482 1.  ASoC的由来 ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系.在ASoc出现之前,内核对于SoC中的音频已经有部分的支持,不过会有一些局限性: Codec驱动与SoC CPU的底层耦合过于紧密,这种不理想会导致代码的重复,例如,仅是wm8731的驱动,当时Linux中

ALSA声卡驱动的DAPM-基于高通平台

最近使用tinymix 调试相应的音频通道,但是一直不知道音频通道的原理是什么.所以百度了一下,百度结果是与DPAM有关. 一.DAPM简介: DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下.DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成.用户空间的应用程序无需对代码做出修改,也无需重新编译,DAP

Linux ALSA声卡驱动之一:ALSA架构简介【转】

本文转载自:http://blog.csdn.net/droidphone/article/details/6271122 声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! 一.  概述 ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org

ALSA 声卡 驱动 linux 4.1.36 中变化

linux 4.1.36 中变化ret = request_irq(IRQ_DMA2, s3c2440_dma2_irq, IRQF_DISABLED, "myalsa for playback", substream); IRQF_DISABLED 换为 IRQF_TRIGGER_NONE snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);换为static const struct snd_soc_compon

AM335x(TQ335x)学习笔记——WM8960声卡驱动移植

经过一段时间的调试,终于调好了TQ335x的声卡驱动.TQ335x采用的Codec是WM8960,本文来总结下WM8960驱动在AM335x平台上的移植方法.Linux声卡驱动架构有OSS和ALSA两种架构,目前最常用的架构是ALSA,本文也使用ALSA架构对WM8960驱动进行移植. ASoC是对ALSA驱动架构的进一步封装.ASoC将ALSA驱动中的各模块抽象为三部分:Platform.Codec和Machine.Platform主要是平台硬件驱动,包括SoC的IIS模块.DMA等,在本文中