ALSA聲卡驅動中的DAPM詳解之二:widget-具備路徑和電源管理信息的kcontrol


上一篇文章中,我們介紹了音頻驅動中對基本控制單元的封裝:kcontrol。利用kcontrol,我們可以完成對音頻系統中的mixer,mux,音量控制,音效控制,以及各種開關量的控制,通過對各種kcontrol的控制,使得音頻硬件能夠按照我們預想的結果進行工作。同時我們可以看到,kcontrol還是有以下幾點不足:

 

  • 只能描述自身,無法描述各個kcontrol之間的連接關系;
  • 沒有相應的電源管理機制;
  • 沒有相應的時間處理機制來響應播放、停止、上電、下電等音頻事件;
  • 為了防止pop-pop聲,需要用戶程序關注各個kcontrol上電和下電的順序;
  • 當一個音頻路徑不再有效時,不能自動關閉該路徑上的所有的kcontrol;

 

/*****************************************************************************************************/
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請注明出處,謝謝!
/*****************************************************************************************************/

為此,DAPM框架正是為了要解決以上這些問題而誕生的,DAPM目前已經是ASoc中的重要組成部分,讓我們先從DAPM的數據結構開始,了解它的設計思想和工作原理。

 

DAPM的基本單元:widget


 

 

文章的開頭,我們說明了一下目前kcontrol的一些不足,而DAPM框架為了解決這些問題,引入了widget這一概念,所謂widget,其實可以理解為是kcontrol的進一步升級和封裝,她同樣是指音頻系統中的某個部件,比如mixer,mux,輸入輸出引腳,電源供應器等等,甚至,我們可以定義虛擬的widget,例如playback stream widget。widget把kcontrol和動態電源管理進行了有機的結合,同時還具備音頻路徑的連結功能,一個widget可以與它相鄰的widget有某種動態的連結關系。在DAPM框架中,widget用結構體snd_soc_dapm_widget來描述:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. struct snd_soc_dapm_widget {  
  2.         enum snd_soc_dapm_type id;  
  3.         const char *name;               /* widget name */  
  4.   
  5.         ......  
  6.         /* dapm control */  
  7.         int reg;                                /* negative reg = no direct dapm */  
  8.         unsigned char shift;                    /* bits to shift */  
  9.         unsigned int value;                             /* widget current value */  
  10.         unsigned int mask;                      /* non-shifted mask */  
  11.         ......  
  12.   
  13.         int (*power_check)(struct snd_soc_dapm_widget *w);  
  14.   
  15.         int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);  
  16.   
  17.         /* kcontrols that relate to this widget */  
  18.         int num_kcontrols;  
  19.         const struct snd_kcontrol_new *kcontrol_news;  
  20.         struct snd_kcontrol **kcontrols;  
  21.   
  22.         /* widget input and outputs */  
  23.         struct list_head sources;  
  24.         struct list_head sinks;  
  25.         ......  
  26. };  

snd_soc_dapm_widget結構比較大,為了簡潔一些,這里我沒有列出該結構體的完整字段,不過不用擔心,下面我會說明每個字段的意義:

 

id    該widget的類型值,比如snd_soc_dapm_output,snd_soc_dapm_mixer等等。

*name    該widget的名字

*sname    代表該widget所在stream的名字,比如對於snd_soc_dapm_dai_in類型的widget,會使用該字段。

*codec *platform    指向該widget所屬的codec和platform。

list    所有注冊到系統中的widget都會通過該list,鏈接到代表聲卡的snd_soc_card結構的widgets鏈表頭字段中。

*dapm    snd_soc_dapm_context結構指針,ASoc把系統划分為多個dapm域,每個widget屬於某個dapm域,同一個域代表着同樣的偏置電壓供電策略,比如,同一個codec中的widget通常位於同一個dapm域,而平台上的widget可能又會位於另外一個platform域中。

*priv    有些widget可能需要一些專有的數據,可以使用該字段來保存,像snd_soc_dapm_dai_in類型的widget,會使用該字段來記住與之相關聯的snd_soc_dai結構指針。

*regulator    對於snd_soc_dapm_regulator_supply類型的widget,該字段指向與之相關的regulator結構指針。

*params    目前對於snd_soc_dapm_dai_link類型的widget,指向該dai的配置信息的snd_soc_pcm_stream結構。

reg shift mask     這3個字段用來控制該widget的電源狀態,分別對應控制信息所在的寄存器地址,位移值和屏蔽值。

value  on_val  off_val    電源狀態的當前只,開啟時和關閉時所對應的值。

power invert    用於指示該widget當前是否處於上電狀態,invert則用於表明power字段是否需要邏輯反轉。

active connected    分別表示該widget是否處於激活狀態和連接狀態,當和相鄰的widget有連接關系時,connected位會被置1,否則置0。

new   我們定義好的widget(snd_soc_dapm_widget結構),在注冊到聲卡中時需要進行實例化,該字段用來表示該widget是否已經被實例化。

ext    表示該widget當前是否有外部連接,比如連接mic,耳機,喇叭等等。

force    該位被設置后,將會不管widget當前的狀態,強制更新至新的電源狀態。

ignore_suspend new_power power_checked    這些電源管理相關的字段。

subseq    該widget目前在上電或下電隊列中的排序編號,為了防止在上下電的過程中出現pop-pop聲,DAPM會給每個widget分配合理的上下電順序。

*power_check    用於檢查該widget是否應該上電或下電的回調函數指針。
event_flags    該字段是一個位或字段,每個位代表該widget會關注某個DAPM事件通知。只有被關注的通知事件會被發送到widget的事件處理回調函數中。

*event    DAPM事件處理回調函數指針。

num_kcontrols *kcontrol_news **kcontrols    這3個字段用來描述與該widget所包含的kcontrol控件,例如一個mixer控件或者是一個mux控件。

 

sources sinks    兩個鏈表字段,兩個widget如果有連接關系,會通過一個snd_soc_dapm_path結構進行連接,sources鏈表用於鏈接所有的輸入path,sinks鏈表用於鏈接所有的輸出path。

power_list    每次更新整個dapm的電源狀態時,會根據一定的算法掃描所有的widget,然后把需要變更電源狀態的widget利用該字段鏈接到一個上電或下電的鏈表中,掃描完畢后,dapm系統會遍歷這兩個鏈表執行相應的上電或下電操作。

dirty    鏈表字段,widget的狀態變更后,dapm系統會利用該字段,把該widget加入到一個dirty鏈表中,稍后會對dirty鏈表進行掃描,以執行整個路徑的更新。

inputs    該widget的所有有效路徑中,連接到輸入端的路徑數量。

outputs    該widget的所有有效路徑中,連接到輸出端的路徑數量。

*clk    對於snd_soc_dapm_clock_supply類型的widget,指向相關聯的clk結構指針。

以上我們對snd_soc_dapm_widget結構的各個字段所代表的意義一一做出了說明,這里只是讓大家現有個概念,至於每個字段的詳細作用,我們會在以后相關的章節中提及。

widget的種類


 

在DAPM框架中,把各種不同的widget划分為不同的種類,snd_soc_dapm_widget結構中的id字段用來表示該widget的種類,可選的種類都定義在一個枚舉中:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. /* dapm widget types */  
  2. enum snd_soc_dapm_type {......}  

下面我們逐個解釋一下這些widget的種類:

 

 

  • snd_soc_dapm_input     該widget對應一個輸入引腳。
  • snd_soc_dapm_output    該widget對應一個輸出引腳。
  • snd_soc_dapm_mux    該widget對應一個mux控件。
  • snd_soc_dapm_virt_mux    該widget對應一個虛擬的mux控件。
  • snd_soc_dapm_value_mux    該widget對應一個value類型的mux控件。
  • snd_soc_dapm_mixer    該widget對應一個mixer控件。
  • snd_soc_dapm_mixer_named_ctl    該widget對應一個mixer控件,但是對應的kcontrol的名字不會加入widget的名字作為前綴。
  • snd_soc_dapm_pga    該widget對應一個pga控件(可編程增益控件)。
  • snd_soc_dapm_out_drv    該widget對應一個輸出驅動控件
  • snd_soc_dapm_adc    該widget對應一個ADC 
  • snd_soc_dapm_dac    該widget對應一個DAC 
  • snd_soc_dapm_micbias    該widget對應一個麥克風偏置電壓控件
  • snd_soc_dapm_mic    該widget對應一個麥克風。
  • snd_soc_dapm_hp    該widget對應一個耳機。
  • snd_soc_dapm_spk    該widget對應一個揚聲器。
  • snd_soc_dapm_line     該widget對應一個線路輸入。
  • snd_soc_dapm_switch       該widget對應一個模擬開關。
  • snd_soc_dapm_vmid      該widget對應一個codec的vmid偏置電壓。
  • snd_soc_dapm_pre      machine級別的專用widget,會先於其它widget執行檢查操作。
  • snd_soc_dapm_post    machine級別的專用widget,會后於其它widget執行檢查操作。
  • snd_soc_dapm_supply           對應一個電源或是時鍾源。
  • snd_soc_dapm_regulator_supply  對應一個外部regulator穩壓器。
  • snd_soc_dapm_clock_supply      對應一個外部時鍾源。
  • snd_soc_dapm_aif_in            對應一個數字音頻輸入接口,比如I2S接口的輸入端。
  • snd_soc_dapm_aif_out          對應一個數字音頻輸出接口,比如I2S接口的輸出端。
  • snd_soc_dapm_siggen            對應一個信號發生器。
  • snd_soc_dapm_dai_in           對應一個platform或codec域的輸入DAI結構。
  • snd_soc_dapm_dai_out        對應一個platform或codec域的輸出DAI結構。
  • snd_soc_dapm_dai_link         用於鏈接一對輸入/輸出DAI結構。

 

widget之間的連接器:path


 

之前已經提到,一個widget是有輸入和輸出的,而且widget之間是可以動態地進行連接的,那它們是用什么來連接兩個widget的呢?DAPM為我們提出了path這一概念,path相當於電路中的一根跳線,它把一個widget的輸出端和另一個widget的輸入端連接在一起,path用snd_soc_dapm_path結構來描述:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. struct snd_soc_dapm_path {  
  2.         const char *name;  
  3.   
  4.         /* source (input) and sink (output) widgets */  
  5.         struct snd_soc_dapm_widget *source;  
  6.         struct snd_soc_dapm_widget *sink;  
  7.         struct snd_kcontrol *kcontrol;  
  8.   
  9.         /* status */  
  10.         u32 connect:1;  /* source and sink widgets are connected */  
  11.         u32 walked:1;   /* path has been walked */  
  12.         u32 walking:1;  /* path is in the process of being walked */  
  13.         u32 weak:1;     /* path ignored for power management */  
  14.   
  15.         int (*connected)(struct snd_soc_dapm_widget *source,  
  16.                          struct snd_soc_dapm_widget *sink);  
  17.   
  18.         struct list_head list_source;  
  19.         struct list_head list_sink;  
  20.         struct list_head list;  
  21. };  

當widget之間發生連接關系時,snd_soc_dapm_path作為連接者,它的source字段會指向該連接的起始端widget,而它的sink字段會指向該連接的到達端widget,還記得前面snd_soc_dapm_widget結構中的兩個鏈表頭字段:sources和sinks么?widget的輸入端和輸出端可能連接着多個path,所有輸入端的snd_soc_dapm_path結構通過list_sink字段掛在widget的souces鏈表中,同樣,所有輸出端的snd_soc_dapm_path結構通過list_source字段掛在widget的sinks鏈表中。這里可能大家會被搞得暈呼呼的,一會source,一會sink,不要緊,只要記住,連接的路徑是這樣的:起始端widget的輸出-->path的輸入-->path的輸出-->到達端widget輸入。

 



                                                                                   圖1    widget通過path進行連接

另外,snd_soc_dapm_path結構的list字段用於把所有的path注冊到聲卡中,其實就是掛在snd_soc_card結構的paths鏈表頭字段中。如果你要自己定義方法來檢查path的當前連接狀態,你可以提供自己的connected回調函數指針。

connect,walked,walking,weak是幾個輔助字段,用於幫助所有path的遍歷。

widget的連接關系:route


通過上一節的內容,我們知道,一個路徑的連接至少包含以下幾個元素:起始端widget,跳線path,到達端widget,在DAPM中,用snd_soc_dapm_route結構來描述這樣一個連接關系:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. struct snd_soc_dapm_route {  
  2.         const char *sink;  
  3.         const char *control;  
  4.         const char *source;  
  5.         int (*connected)(struct snd_soc_dapm_widget *source,  
  6.                          struct snd_soc_dapm_widget *sink);  
  7. };  

sink指向到達端widget的名字字符串,source指向起始端widget的名字字符串,control指向負責控制該連接所對應的kcontrol名字字符串,connected回調則定義了上一節所提到的自定義連接檢查回調函數。該結構的意義很明顯就是:source通過一個kcontrol,和sink連接在一起,現在是否處於連接狀態,請調用connected回調函數檢查。

 

這里直接使用名字字符串來描述連接關系,所有定義好的route,最后都要注冊到dapm系統中,dapm會根據這些名字找出相應的widget,並動態地生成所需要的snd_soc_dapm_path結構,正確地處理各個鏈表和指針的關系,實現兩個widget之間的連接,具體的連接代碼分析,我們留到以后的章節中討論。


免責聲明!

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



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