上一篇文章中,我們介紹了音頻驅動中對基本控制單元的封裝: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來描述:
- struct snd_soc_dapm_widget {
- enum snd_soc_dapm_type id;
- const char *name; /* widget name */
- ......
- /* dapm control */
- int reg; /* negative reg = no direct dapm */
- unsigned char shift; /* bits to shift */
- unsigned int value; /* widget current value */
- unsigned int mask; /* non-shifted mask */
- ......
- int (*power_check)(struct snd_soc_dapm_widget *w);
- int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
- /* kcontrols that relate to this widget */
- int num_kcontrols;
- const struct snd_kcontrol_new *kcontrol_news;
- struct snd_kcontrol **kcontrols;
- /* widget input and outputs */
- struct list_head sources;
- struct list_head sinks;
- ......
- };
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的種類,可選的種類都定義在一個枚舉中:
- /* dapm widget types */
- 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結構來描述:
- struct snd_soc_dapm_path {
- const char *name;
- /* source (input) and sink (output) widgets */
- struct snd_soc_dapm_widget *source;
- struct snd_soc_dapm_widget *sink;
- struct snd_kcontrol *kcontrol;
- /* status */
- u32 connect:1; /* source and sink widgets are connected */
- u32 walked:1; /* path has been walked */
- u32 walking:1; /* path is in the process of being walked */
- u32 weak:1; /* path ignored for power management */
- int (*connected)(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink);
- struct list_head list_source;
- struct list_head list_sink;
- struct list_head list;
- };
當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結構來描述這樣一個連接關系:
- struct snd_soc_dapm_route {
- const char *sink;
- const char *control;
- const char *source;
- int (*connected)(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink);
- };
sink指向到達端widget的名字字符串,source指向起始端widget的名字字符串,control指向負責控制該連接所對應的kcontrol名字字符串,connected回調則定義了上一節所提到的自定義連接檢查回調函數。該結構的意義很明顯就是:source通過一個kcontrol,和sink連接在一起,現在是否處於連接狀態,請調用connected回調函數檢查。
這里直接使用名字字符串來描述連接關系,所有定義好的route,最后都要注冊到dapm系統中,dapm會根據這些名字找出相應的widget,並動態地生成所需要的snd_soc_dapm_path結構,正確地處理各個鏈表和指針的關系,實現兩個widget之間的連接,具體的連接代碼分析,我們留到以后的章節中討論。