1. 程式人生 > >ALSA音效卡驅動中的DAPM詳解之二:widget-具備路徑和電源管理資訊的kcontrol

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來描述:

  1. struct snd_soc_dapm_widget {

  2. enum snd_soc_dapm_type id;

  3. const char *name; /* widget name */

  4. ......

  5. /* dapm control */

  6. int reg; /* negative reg = no direct dapm */

  7. unsigned char shift; /* bits to shift */

  8. unsigned int value; /* widget current value */

  9. unsigned int mask; /* non-shifted mask */

  10. ......

  11. int (*power_check)(struct snd_soc_dapm_widget *w);

  12. int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);

  13. /* kcontrols that relate to this widget */

  14. int num_kcontrols;

  15. const struct snd_kcontrol_new *kcontrol_news;

  16. struct snd_kcontrol **kcontrols;

  17. /* widget input and outputs */

  18. struct list_head sources;

  19. struct list_head sinks;

  20. ......

  21. };

sndsocdapm_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的種類,可選的種類都定義在一個列舉中:

  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結構來描述:

  1. struct snd_soc_dapm_path {

  2. const char *name;

  3. /* source (input) and sink (output) widgets */

  4. struct snd_soc_dapm_widget *source;

  5. struct snd_soc_dapm_widget *sink;

  6. struct snd_kcontrol *kcontrol;

  7. /* status */

  8. u32 connect:1; /* source and sink widgets are connected */

  9. u32 walked:1; /* path has been walked */

  10. u32 walking:1; /* path is in the process of being walked */

  11. u32 weak:1; /* path ignored for power management */

  12. int (*connected)(struct snd_soc_dapm_widget *source,

  13. struct snd_soc_dapm_widget *sink);

  14. struct list_head list_source;

  15. struct list_head list_sink;

  16. struct list_head list;

  17. };

當widget之間發生連線關係時,sndsocdapmpath作為連線者,它的source欄位會指向該連線的起始端widget,而它的sink欄位會指向該連線的到達端widget,還記得前面sndsocdapmwidget結構中的兩個連結串列頭欄位:sources和sinks麼?widget的輸入端和輸出端可能連線著多個path,所有輸入端的sndsocdapmpath結構通過listsink欄位掛在widget的souces連結串列中,同樣,所有輸出端的sndsocdapmpath結構通過listsource欄位掛在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結構來描述這樣一個連線關係:

  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之間的連線,具體的連線程式碼分析,我們留到以後的章節中討論。