linux-alsa詳解11之DAPM詳解4驅動中widget初始化


前幾篇文章我們從dapm的數據結構入手,了解了代表音頻控件的widget,代表連接路徑的route以及用於連接兩個widget的path。之前都是一些概念的講解以及對數據結構中各個字段的說明,從本章開始,我們要從代碼入手,分析dapm的詳細工作原理,主要以下幾個方面:

(1)如何注冊widget;

(2)如何連接兩個widget;

(3)一個widget的狀態裱畫如何傳遞到整個音頻路徑中。

1 dapm context

在討論widget的注冊之前,我們先了解另一個概念:dapm context,直譯過來的意思是dapm上下文,這個好像不好理解,其實我們可以這么理解:dapm把整個音頻系統,按照功能和偏置電壓級別,划分為若干個電源域,每個域包含各自的widget,每個域中的所有widget通常都處於同一個偏置電壓級別上,而一個電源域就是一個dapm context,通常會有以下幾種dapm context:

(1)屬於codec中的widget位於一個dapm context中;

(2)屬於platform的widget位於一個dapm context中;

(3)屬於整個聲卡的widget位於一個dapm context中。

對於音頻系統的硬件來說,通常要提供合適的偏置電壓才能正常地工作,有了dapm context這種組織方式,我們可以方便地對同一組widget進行統一的偏置電壓管理,ASoc用snd_soc_dapm_context結構來表示一個dapm context:

 1 /* DAPM context */
 2 struct snd_soc_dapm_context {
 3     enum snd_soc_bias_level bias_level;
 4     unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
 5     /* Go to BIAS_OFF in suspend if the DAPM context is idle */
 6     unsigned int suspend_bias_off:1;
 7     void (*seq_notifier)(struct snd_soc_dapm_context *,
 8                  enum snd_soc_dapm_type, int);
 9 
10     struct device *dev; /* from parent - for debug */
11     struct snd_soc_component *component; /* parent component */
12     struct snd_soc_card *card; /* parent card */
13 
14     /* used during DAPM updates */
15     enum snd_soc_bias_level target_bias_level;
16     struct list_head list;
17 
18     int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
19     int (*set_bias_level)(struct snd_soc_dapm_context *dapm,
20                   enum snd_soc_bias_level level);
21 
22     struct snd_soc_dapm_wcache path_sink_cache;
23     struct snd_soc_dapm_wcache path_source_cache;
24 
25 #ifdef CONFIG_DEBUG_FS
26     struct dentry *debugfs_dapm;
27 #endif
28 }

 枚舉變量snd_soc_bias_level 

1 enum snd_soc_bias_level {
2     SND_SOC_BIAS_OFF = 0,
3     SND_SOC_BIAS_STANDBY = 1,
4     SND_SOC_BIAS_PREPARE = 2,
5     SND_SOC_BIAS_ON = 3,
6 };

snd_soc_dapm_context被內嵌到代表codec、platform和dai中成員結構體snd_soc_component中 :

struct snd_soc_component {
    ...
    const struct snd_soc_component_driver *driver;
....
/* Don't use these, use snd_soc_component_get_dapm() */ struct snd_soc_dapm_context dapm; const struct snd_kcontrol_new *controls; unsigned int num_controls; const struct snd_soc_dapm_widget *dapm_widgets; unsigned int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; unsigned int num_dapm_routes; struct snd_soc_codec *codec; ... }

結構體struct snd_soc_component_driver

/* component interface */
struct snd_soc_component_driver {
    const char *name;
        ...
    /* Default control and setup, added after probe() is run */
    const struct snd_kcontrol_new *controls;
    unsigned int num_controls;
    const struct snd_soc_dapm_widget *dapm_widgets;
    unsigned int num_dapm_widgets;
    const struct snd_soc_dapm_route *dapm_routes;
    unsigned int num_dapm_routes;

     ...
}

card(machine)結構體中是直接內嵌的snd_soc_dapm_context:

 1 struct snd_soc_card {
 2     ...
 3 
 4     const struct snd_kcontrol_new *controls;
 5     int num_controls;
 6 
 7     /*
 8      * Card-specific routes and widgets.
 9      * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.
10      */
11     const struct snd_soc_dapm_widget *dapm_widgets;
12     int num_dapm_widgets;
13     const struct snd_soc_dapm_route *dapm_routes;
14     int num_dapm_routes;
15     const struct snd_soc_dapm_widget *of_dapm_widgets;
16     int num_of_dapm_widgets;
17     const struct snd_soc_dapm_route *of_dapm_routes;
18     int num_of_dapm_routes;
19     bool fully_routed;
20 
21     struct work_struct deferred_resume_work;
22 
23     /* lists of probed devices belonging to this card */
24     struct list_head codec_dev_list;
25 
26     struct list_head widgets;
27     struct list_head paths;
28     struct list_head dapm_list;
29     struct list_head dapm_dirty;
30 
31     /* attached dynamic objects */
32     struct list_head dobj_list;
33 
34     /* Generic DAPM context for the card */
35     struct snd_soc_dapm_context dapm;
36     struct snd_soc_dapm_stats dapm_stats;
37     struct snd_soc_dapm_update *update;
38 
39         ...
40 }

代表widget結構snd_soc_dapm_widget中,有一個snd_soc_dapm_context結構指針,指向所屬的codec、platform、card、或dai的dapm結構。同時,所有的dapm結構,通過它的list字段,鏈接到代表聲卡的snd_soc_card結構的dapm_list鏈表頭字段。
2 創建和注冊widget

我們已經知道,一個widget用snd_soc_dapm_widget結構體來描述,通常,我們會根據音頻硬件的組成,分別在聲卡的codec驅動、platform驅動和machine驅動中定義一組widget,這些widget用數組進行組織,我們一般會使用dapm框架提供的大量的輔助宏來定義這些widget數組,輔助宏的說明請參考前一偏文章linux-alsa詳解10之DAPM詳解3各種widget定義
2.1 codec中widget的注冊

我們知道,我們會通過ASoc提供的api函數snd_soc_register_codec來注冊一個codec驅動。繼續以linux-alsa詳解6 ASOC-codec /sound/soc/codecs/wm8994.c為例。

當machine中匹配codec之后會調用codec的probe函數,然后會創建和注冊widget:

 1 static int wm8994_codec_probe(struct snd_soc_codec *codec)
 2 {
 3     struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 4     struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
 5      ...
 6      switch (control->type) {
 7     case WM8994:
 8         snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
 9                       ARRAY_SIZE(wm8994_specific_dapm_widgets));
10         if (control->revision < 4) {
11             snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
12                           ARRAY_SIZE(wm8994_lateclk_revd_widgets));
13             snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
14                           ARRAY_SIZE(wm8994_adc_revd_widgets));
15             snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
16                           ARRAY_SIZE(wm8994_dac_revd_widgets));
17         } else {
18             snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
19                           ARRAY_SIZE(wm8994_lateclk_widgets));
20             snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
21                           ARRAY_SIZE(wm8994_adc_widgets));
22             snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
23                           ARRAY_SIZE(wm8994_dac_widgets));
24         }
25         break;
26     ...
27 }

結構體struct snd_soc_dapm_widget的定義初始化如下,其他沒有貼出來,詳見code。

1 static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = {
2 SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux),
3 };

2.2 platform中widget注冊

和codec驅動一樣,不再重復
2.3 card中widget注冊

在card驅動中創建和初始化widget,然后注冊card(machine)時會調用snd_soc_instantiate_card,然后注冊widget:

 1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 2 {
 3   ...
 4   if (card->dapm_widgets)
 5         snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
 6                       card->num_dapm_widgets);
 7 
 8     if (card->of_dapm_widgets)
 9         snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
10                       card->num_of_dapm_widgets);
11   ....
12 }

只要把定義好的widget數組和數量賦值給dapm_widgets指針和num_dapm_widgets即可,注冊聲卡使用的api:snd_soc_register_card(),也會通過snd_soc_dapm_new_controls來完成widget的創建工作。

3 注冊widget的api

以上codec、platform和machine的widget注冊最終都調用了widget注冊的api snd_soc_dapm_new_controls,定義位於:linux-4.9.73\sound\soc\soc-dapm.c

 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_unlocked(dapm, widget);
22         if (IS_ERR(w)) {
23             ret = PTR_ERR(w);
24             /* Do not nag about probe deferrals */
25             if (ret == -EPROBE_DEFER)
26                 break;
27             dev_err(dapm->dev,
28                 "ASoC: Failed to create DAPM control %s (%d)\n",
29                 widget->name, ret);
30             break;
31         }
32         if (!w) {
33             dev_err(dapm->dev,
34                 "ASoC: Failed to create DAPM control %s\n",
35                 widget->name);
36             ret = -ENOMEM;
37             break;
38         }
39         widget++;
40     }
41     mutex_unlock(&dapm->card->dapm_mutex);
42     return ret;
43 }

 注:這個函數只是創建widget的第一步,它為每個widget分配內存,初始化必要的字段,然后把這些widget掛在代表聲卡的snd_soc_card的widgets鏈表字段中。要使widget之間具備連接能力。

該函數只是簡單的一個循環,為傳入的widget模板數組依次調用snd_soc_dapm_new_control_unlocked函數,實際的工作由snd_soc_dapm_new_control_unlocked完成,繼續進入該函數,看看它做了那些工作。

驅動中定義的snd_soc_dapm_widget數組,只是作為一個模板,所以,snd_soc_dapm_new_control所做的第一件事,就是為該widget重新分配內存,並把模板的內容拷貝過來

  1 struct snd_soc_dapm_widget *
  2 snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
  3              const struct snd_soc_dapm_widget *widget)
  4 {
  5     enum snd_soc_dapm_direction dir;
  6     struct snd_soc_dapm_widget *w;
  7     const char *prefix;
  8     int ret;
  9 
 10     if ((w = dapm_cnew_widget(widget)) == NULL)//由dapm_cnew_widget完成內存申請和拷貝模板的動作。接下來,根據widget的類型做不同的處理
 11         return NULL;
 12     /*接下來,根據widget的不同類型做不同的處理*/
 13     switch (w->id) {
 14     case snd_soc_dapm_regulator_supply:
 15         w->regulator = devm_regulator_get(dapm->dev, w->name);//對於snd_soc_dapm_regulator_supply類型的widget,根據widget的名稱獲取對應的regulator結構
 16         if (IS_ERR(w->regulator)) {
 17             ret = PTR_ERR(w->regulator);
 18             if (ret == -EPROBE_DEFER)
 19                 return ERR_PTR(ret);
 20             dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
 21                 w->name, ret);
 22             return NULL;
 23         }
 24 
 25         if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
 26             ret = regulator_allow_bypass(w->regulator, true);
 27             if (ret != 0)
 28                 dev_warn(w->dapm->dev,
 29                      "ASoC: Failed to bypass %s: %d\n",
 30                      w->name, ret);
 31         }
 32         break;
 33     case snd_soc_dapm_clock_supply:
 34 #ifdef CONFIG_CLKDEV_LOOKUP
 35         w->clk = devm_clk_get(dapm->dev, w->name);//對於snd_soc_dapm_clock_supply類型的widget,根據widget的名稱,獲取對應的clock結構
 36         if (IS_ERR(w->clk)) {
 37             ret = PTR_ERR(w->clk);
 38             if (ret == -EPROBE_DEFER)
 39                 return ERR_PTR(ret);
 40             dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
 41                 w->name, ret);
 42             return NULL;
 43         }
 44 #else
 45         return NULL;
 46 #endif
 47         break;
 48     default:
 49         break;
 50     }
 51     /*根據需要,在widget的名稱前加入必要的前綴*/
 52     prefix = soc_dapm_prefix(dapm);
 53     if (prefix)
 54         w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
 55     else
 56         w->name = kstrdup_const(widget->name, GFP_KERNEL);
 57     if (w->name == NULL) {
 58         kfree(w);
 59         return NULL;
 60     }
 61     /*然后,為不同類型的widget設置合適的power_check電源狀態回調函數,widget類型和對應的power_check回調函數設置如下表所示:*/
 62     switch (w->id) {
 63     case snd_soc_dapm_mic:
 64         w->is_ep = SND_SOC_DAPM_EP_SOURCE;
 65         w->power_check = dapm_generic_check_power;
 66         break;
 67     case snd_soc_dapm_input:
 68         if (!dapm->card->fully_routed)
 69             w->is_ep = SND_SOC_DAPM_EP_SOURCE;
 70         w->power_check = dapm_generic_check_power;
 71         break;
 72     case snd_soc_dapm_spk:
 73     case snd_soc_dapm_hp:
 74         w->is_ep = SND_SOC_DAPM_EP_SINK;
 75         w->power_check = dapm_generic_check_power;
 76         break;
 77     case snd_soc_dapm_output:
 78         if (!dapm->card->fully_routed)
 79             w->is_ep = SND_SOC_DAPM_EP_SINK;
 80         w->power_check = dapm_generic_check_power;
 81         break;
 82     case snd_soc_dapm_vmid:
 83     case snd_soc_dapm_siggen:
 84         w->is_ep = SND_SOC_DAPM_EP_SOURCE;
 85         w->power_check = dapm_always_on_check_power;
 86         break;
 87     case snd_soc_dapm_sink:
 88         w->is_ep = SND_SOC_DAPM_EP_SINK;
 89         w->power_check = dapm_always_on_check_power;
 90         break;
 91 
 92     case snd_soc_dapm_mux:
 93     case snd_soc_dapm_demux:
 94     case snd_soc_dapm_switch:
 95     case snd_soc_dapm_mixer:
 96     case snd_soc_dapm_mixer_named_ctl:
 97     case snd_soc_dapm_adc:
 98     case snd_soc_dapm_aif_out:
 99     case snd_soc_dapm_dac:
100     case snd_soc_dapm_aif_in:
101     case snd_soc_dapm_pga:
102     case snd_soc_dapm_out_drv:
103     case snd_soc_dapm_micbias:
104     case snd_soc_dapm_line:
105     case snd_soc_dapm_dai_link:
106     case snd_soc_dapm_dai_out:
107     case snd_soc_dapm_dai_in:
108         w->power_check = dapm_generic_check_power;
109         break;
110     case snd_soc_dapm_supply:
111     case snd_soc_dapm_regulator_supply:
112     case snd_soc_dapm_clock_supply:
113     case snd_soc_dapm_kcontrol:
114         w->is_supply = 1;
115         w->power_check = dapm_supply_check_power;
116         break;
117     default:
118         w->power_check = dapm_always_on_check_power;
119         break;
120     }
121     /*初始化widget中的鏈表*/
122     w->dapm = dapm;
123     INIT_LIST_HEAD(&w->list);//用於鏈接到聲卡的widgets鏈表
124     INIT_LIST_HEAD(&w->dirty);//用於鏈接到聲卡的dapm_dirty鏈表
125     list_add_tail(&w->list, &dapm->card->widgets);//將此widget加入所屬的聲卡結構體中
126 
127     snd_soc_dapm_for_each_direction(dir) {
128         INIT_LIST_HEAD(&w->edges[dir]);
129         w->endpoints[dir] = -1;
130     }
131 
132     /* machine layer sets up unconnected pins and insertions */
133     w->connected = 1;//最后,把widget設置為connect狀態,contect字段代表的意義如下詳述
134     return w;
135 }

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

connected字段代表着引腳的連接狀態,目前,只有以下這些widget使用connected字段:

1 snd_soc_dapm_output
2 snd_soc_dapm_input
3 snd_soc_dapm_hp
4 snd_soc_dapm_spk
5 snd_soc_dapm_line
6 snd_soc_dapm_vmid
7 snd_soc_dapm_mic
8 snd_soc_dapm_siggen

驅動程序可以使用以下這些api來設置引腳的連接狀態:

1 snd_soc_dapm_enable_pin
2 snd_soc_dapm_force_enable_pin
3 snd_soc_dapm_disable_pin
4 snd_soc_dapm_nc_pin

到此,widget已經被正確地創建並初始化,而且被掛在聲卡的widgets鏈表中,以后我們就可以通過聲卡的widgets鏈表來遍歷所有的widget,再次強調一下snd_soc_dapm_new_controls函數所完成的主要功能:

(1)為widget分配內存,並拷貝參數中傳入的在驅動中定義好的模板

(2)設置power_check回調函數,當音頻路徑發生變化時,power_check回調會被調用,用於檢查該widget的電源狀態是否需要更新。

(3)把widget掛在聲卡的widgets鏈表中

3 dai widget

上面幾節的內容介紹了codec、platform以及machine級別的widget的注冊方法,在dapm框架中,還有另外一種widget,它代表了一個dai(數字音頻接口),dai按所在的位置,又分為cpu dai和codec dai。在硬件上,通常一個cpu dai會連接一個codec dai,而在machine驅動中,我們要在snd_soc_card結構中指定一個叫做snd_soc_dai_link的結構,該結構定義了聲卡使用哪一個cpu dai和codec dai進行連接。在Asoc中,一個dai用snd_soc_dai結構來表述,其中component成員有幾個字段和dapm框架有關:

 1 struct snd_soc_dai {
 2     const char *name;
 3     ...
 4 
 5     struct snd_soc_dapm_widget *playback_widget;
 6     struct snd_soc_dapm_widget *capture_widget;
 7 
 8     ...
 9 
10     /* parent platform/codec */
11     struct snd_soc_codec *codec;
12     struct snd_soc_component *component;
13 
14     ...
15 }
 1 struct snd_soc_component {
 2     const char *name;
 3     ...
 4 
 5     /* Don't use these, use snd_soc_component_get_dapm() */
 6     struct snd_soc_dapm_context dapm;
 7 
 8     const struct snd_kcontrol_new *controls;
 9     unsigned int num_controls;
10     const struct snd_soc_dapm_widget *dapm_widgets;
11     unsigned int num_dapm_widgets;
12     const struct snd_soc_dapm_route *dapm_routes;
13     unsigned int num_dapm_routes;
14     struct snd_soc_codec *codec;
15 
16     ...
17 }

dai由codec驅動和平台代碼中的I2S或pcm接口驅動注冊,machine驅動負責找到snd_soc_dai_link中指定的一對cpu/codec dai,並把它們進行綁定。不管是cpu dai還是codec dai,通常會同時傳輸播放和錄音的音頻流的能力,所以我們可以看到,snd_soc_dai中有兩個widget指針,分別代表播放流和錄音流。這兩個dai widget是何時創建的呢?下面我們逐一進行分析。

3.1 codec dai widget

首先,codec驅動在注冊codec時,會傳入該codec所支持的dai個數和記錄dai信息的snd_soc_dai_driver結構指針。使得ASoc把codec的dai注冊到系統中,並把這些dai都掛在全局鏈表變量dai_list中。然后在聲卡初始化函數snd_soc_instantiate_card中,會通過全局鏈表變量dai_list查找所有屬於該codec的dai,codec被machine驅動匹配后,soc_probe_codec函數會被調用,這個時候回去調用snd_soc_dapm_new_dai_widgets函數來生成該dai的播放流widget和錄音流widget:

 1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 2 {
 3     ...
 4 
 5     /* probe all components used by DAI links on this card */
 6     for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 7             order++) {
 8         list_for_each_entry(rtd, &card->rtd_list, list) {
 9             ret = soc_probe_link_components(card, rtd, order);
10             if (ret < 0) {
11                 dev_err(card->dev,
12                     "ASoC: failed to instantiate card %d\n",
13                     ret);
14                 goto probe_dai_err;
15             }
16         }
17     }
18 
19     ...
20 }

函數soc_probe_link_component,根據codec中的component找到codec的probe函數

 1 static int soc_probe_link_components(struct snd_soc_card *card,
 2             struct snd_soc_pcm_runtime *rtd,
 3                      int order)
 4 {
 5     ...
 6 
 7     /* probe the CODEC-side components */
 8     for (i = 0; i < rtd->num_codecs; i++) {
 9         component = rtd->codec_dais[i]->component;
10         if (component->driver->probe_order == order) {
11             ret = soc_probe_component(card, component);
12             if (ret < 0)
13                 return ret;
14         }
15     }
16 
17     ...
18 }

 調用codec的probe,soc_probe_component(card, component),調用snd_soc_dapm_new_dai_widgets函數來生成該dai的播放流widget和錄音流widget:

 1 static int soc_probe_component(struct snd_soc_card *card,
 2     struct snd_soc_component *component)
 3 {
 4     ...
 5     list_for_each_entry(dai, &component->dai_list, list) {
 6         ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
 7         if (ret != 0) {
 8             dev_err(component->dev,
 9                 "Failed to create DAI widgets %d\n", ret);
10             goto err_probe;
11         }
12     }
13 
14     ...
15 }

3.1.2 函數snd_soc_dapm_new_dai_widgets

 1 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 2                  struct snd_soc_dai *dai)
 3 {
 4     struct snd_soc_dapm_widget template;
 5     struct snd_soc_dapm_widget *w;
 6 
 7     WARN_ON(dapm->dev != dai->dev);
 8 
 9     memset(&template, 0, sizeof(template));
10     template.reg = SND_SOC_NOPM;
11     /*創建播放 dai widget*/
12     if (dai->driver->playback.stream_name) {
13         template.id = snd_soc_dapm_dai_in;
14         template.name = dai->driver->playback.stream_name;
15         template.sname = dai->driver->playback.stream_name;
16 
17         dev_dbg(dai->dev, "ASoC: adding %s widget\n",
18             template.name);
19 
20         w = snd_soc_dapm_new_control_unlocked(dapm, &template);
21         if (IS_ERR(w)) {
22             int ret = PTR_ERR(w);
23 
24             /* Do not nag about probe deferrals */
25             if (ret != -EPROBE_DEFER)
26                 dev_err(dapm->dev,
27                 "ASoC: Failed to create %s widget (%d)\n",
28                 dai->driver->playback.stream_name, ret);
29             return ret;
30         }
31         if (!w) {
32             dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
33                 dai->driver->playback.stream_name);
34             return -ENOMEM;
35         }
36 
37         w->priv = dai;
38         dai->playback_widget = w;
39     }
40     /*創建錄音的dai widget*/
41     if (dai->driver->capture.stream_name) {
42         template.id = snd_soc_dapm_dai_out;
43         template.name = dai->driver->capture.stream_name;
44         template.sname = dai->driver->capture.stream_name;
45 
46         dev_dbg(dai->dev, "ASoC: adding %s widget\n",
47             template.name);
48 
49         w = snd_soc_dapm_new_control_unlocked(dapm, &template);
50         if (IS_ERR(w)) {
51             int ret = PTR_ERR(w);
52 
53             /* Do not nag about probe deferrals */
54             if (ret != -EPROBE_DEFER)
55                 dev_err(dapm->dev,
56                 "ASoC: Failed to create %s widget (%d)\n",
57                 dai->driver->playback.stream_name, ret);
58             return ret;
59         }
60         if (!w) {
61             dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
62                 dai->driver->capture.stream_name);
63             return -ENOMEM;
64         }
65 
66         w->priv = dai;
67         dai->capture_widget = w;
68     }
69 
70     return 0;
71 }

 分別為Playback和Capture創建了一個widget,widget的priv字段指向了該dai,這樣通過widget就可以找到相應的dai,並且widget的名字就是snd_soc_dai_driver結構的stream_name。

3.2 cpu dai widget

和codec dai widget一樣,cpu dai widget也發生在machine驅動匹配上相應的platform驅動之后,platform的probe函數會被調用, soc_probe_component(card, component),調用snd_soc_dapm_new_dai_widgets函數來生成該dai的播放流widget和錄音流widget,同codec dai widget。

dai widget是一條完整dapm音頻路徑的重要元素,沒有她,我們無法完成dapm的動態電源管理工作,因為它是音頻流和其他widget的紐帶。

4 端點widget

一條完整的dapm音頻路徑,必然有起點和終點,我們把位於這些起點和終點的widget稱之為端點widget。以下這些類型的widget可以成為端點widget:

codec的輸入輸出引腳:

1 snd_soc_dapm_output
2 snd_soc_dapm_input

外接的音頻設備:

1 snd_soc_dapm_hp
2 snd_soc_dapm_spk
3 snd_soc_dapm_line

音頻流 stream domain:

1 snd_soc_dapm_adc
2 snd_soc_dapm_dac
3 snd_soc_dapm_aif_out
4 snd_soc_dapm_aif_in
5 snd_soc_dapm_dai_out
6 snd_soc_dapm_dai_in

電源、時鍾和其他:

1 snd_soc_dapm_supply
2 snd_soc_dapm_regulator_supply
3 snd_soc_dapm_clock_supply
4 snd_soc_dapm_kcontrol

 當聲卡上的其中一個widget的狀態發生改變時,從這個widget開始,dapm框架會向前和向后遍歷路徑上的所有widget,判斷每個widget的狀態是否需要跟着變更,到達這些端點widget就會認為它是一條完整音頻路徑的開始和結束,從而結束一次掃描動作。

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


免責聲明!

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



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