Android音頻(3)——ALSA聲卡驅動——DAPM


一、DAPM簡介

  DAPM是Dynamic Audio Power Management的縮寫,直譯過來就是動態音頻電源管理的意思,DAPM是為了使基於linux的移動設備上的音頻子系統,在任何時候都工作在最小功耗狀態下。DAPM對用戶空間的應用程序來說是透明的,所有與電源相關的開關都在ASoc core中完成。DAPM根據當前激活的音頻流(playback/capture)和聲卡中的mixer等的配置來決定那些音頻控件的電源開關被打開或關閉。


二、DAPM的抽象

1. kcontrol
  通常,一個kcontrol代表着一個mixer(混音器),或者是一個mux(多路開關),又或者是一個音量控制器等等。定義一個kcontrol主要就是定義一個snd_kcontrol_new結構.對於每個控件,都需要定義一個和他對應的snd_kcontrol_new結構,這些snd_kcontrol_new結構會在聲卡的初始化階段,通過snd_soc_add_codec_controls()注冊到系統中,用戶空間就可以通過amixer或alsamixer(Linux ala-slib)或tinymix(Android tinyAlsa)等工具查看和設定這些控件的狀態。

定義控件使用的宏舉例:
SOC_SINGLE 簡單型的控件
SOC_SINGLE_TLV 是SOC_SINGLE的一種擴展,主要用於定義那些有增益控制的控件,例如音量控制器,EQ均衡器等等。
SOC_DOUBLE 與SOC_SINGLE相對應,區別是SOC_SINGLE只控制一個變量,而SOC_DOUBLE則可以同時在一個寄存器中控制兩個相似的變量,最常用的就是用於一些立體聲的控件,需要同時對左右聲道進行控制的情況,因為多了一個聲道,參數也就相應地多了一個shift位移值。

SOC_SINGLE_EXT 像這樣的后面帶_EXT的可以指定自己的put和get成員回調函數而不是默認的。

這些宏中使用soc_mixer_control結構來描述mixer控件的寄存器信息,soc_enum結構來描述mux控件的寄存器信息。

用戶空間對kcontrol的修改,最終都會調用到kcontrol的put回調函數。

2. widget

  音頻控件單元抽象為kcontrol,DAPM的基本單元是widget,使用snd_soc_dapm_widget結構表示,它是對kcontrol進一步的封裝,是kcontrol和開關(有的是組件前面的Switch和組件組成一個widget,有的是后面的)的組合體。

  一個widget用snd_soc_dapm_widget結構體來描述,通常,會根據音頻硬件的組成,分別在聲卡的codec驅動、platform驅動和machine驅動中定義一組widget,這些widget用數組進行組織,我們一般會使用dapm框架提供的大量的輔助宏來定義這些widget數組。

  widget把kcontrol和動態電源管理進行了有機的結合,同時還具備音頻路徑的連結功能,一個widget可以與它相鄰的widget有某種動態的連結關系。

  widget所包含的kcontrol與普通的kcontrol有所不同,它們的定義方法與標准的kcontrol也有所不同。普通snd_kcontrol_new結構可以使用宏例如SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0)進行初始化,而widget的snd_kcontrol_new結構使用SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0)初始化,區別是初始化回調函數不同。

SoC動態音頻電源管理,最多可以擁有4個電源域的宏來初始化snd_kcontrol_new結構
(1). Codec domain - VREF,VMID通常在codec的probe/remove時控制。但如果側音不需要上電,可以在stream time設置。
(2). Platform/Machine domain - 物理的輸入和輸出連接是平台/機器和用戶操作特定的,在Machine驅動程序和用戶空間中設置,例如插入HP時的處理。
(3). Path domain - 在用戶更改混音器(mix)和多路復用器(mux)設置時自動設置內部編解碼器路徑混合器。
(4). Stream domain - DAC和ADC。 啟用流playback/capture時啟用。
從soc-dapm.h中宏的reg看出,只有Path domain和Stream domain宏中指定了電源管理寄存器。

  一個電源域對應一個dapm context,使用snd_soc_dapm_context結構表示。屬於codec中的widget位於一個dapm context中;屬於platform的widget位於一個dapm context中;屬於整個聲卡的widget位於一個dapm context中。
  對於音頻系統的硬件來說,通常要提供合適的偏置電壓才能正常地工作,有了dapm context這種組織方式,我們可以方便地對同一組widget進行統一的偏置電壓管理。
  snd_soc_dapm_context被實體內嵌到代表codec(snd_soc_codec )、platform(snd_soc_platform )、card(snd_soc_card )、dai(snd_soc_dai )的結構體中:

定義麥克風,耳機,揚聲器,線路輸入接口這幾種widget,還可以定義一個dapm事件回調函數wevent,因為其是外接設備,提供回調用於事件通知。
/* codec domain */
SND_SOC_DAPM_VMID(wname)

/* platform domain */
SND_SOC_DAPM_INPUT(wname)
SND_SOC_DAPM_OUTPUT(wname)
SND_SOC_DAPM_MIC(wname, wevent)
SND_SOC_DAPM_HP(wname, wevent)
SND_SOC_DAPM_SPK(wname, wevent)
SND_SOC_DAPM_LINE(wname, wevent)

  如果需要自定義這些widget的dapm事件處理回調函數,也可以使用下面這些帶“_E”后綴的版本,eg: SND_SOC_DAPM_PGA_E,這類的宏可以指定event_flag用於表示對哪些事件感興趣。

  為了防止pop-pop聲,需要用戶程序關注各個widget上電和下電的順序。各個widget的上下電次序表示在兩個全局數組dapm_up_seq/dapm_down_seq 中。消pop音延時的時長導出到debugfs了,文件名為dapm_pop_time


dai widget應該是用於抽象Soc的dai接口到path中:
  dai widget又分為cpu dai widget和codec dai widget,它們在machine驅動分別匹配上相應的codec和platform后,由soc_probe_platform和soc_probe_codec這兩個函數通過調用dapm的api函數snd_soc_dapm_new_dai_widgets()來創建的,通常會為playback和capture各自創建一個dai widget,他們的類型分別是:
  snd_soc_dapm_dai_in 對應playback dai
  snd_soc_dapm_dai_out 對應capture dai
另外,dai widget的名字是使用stream name來命名的,他通常來自snd_soc_dai_driver中的stream_name字段。dai widget的sname字段也使用同樣的名字。

dai widget和stream widget是通過stream name進行匹配的

用struct snd_soc_pcm_stream表示,Codec驅動中的struct snd_soc_dai_driver結構中動態指定playback/capture兩個stream widget。

  把dai widget和stream widget連接在一起,就是為了能把ASoc中的pcm處理部分和dapm進行關聯,pcm的處理過程中,會通過發出stream event來通知dapm系統,重新掃描並調整音頻路徑上各個widget的電源狀態。

  因為dai widget和codec上的stream widget是相連的,所以,dai widget的激活狀態改變,會沿着音頻路徑傳遞到路徑上的所有widget,等dapm_power_widgets返回后,如果發出的是SND_SOC_DAPM_STREAM_START事件,路徑上的所有widget會處於上電狀態,保證音頻數據流的順利播放,如果發出的是SND_SOC_DAPM_STREAM_STOP事件,路徑上的所有widget會處於下電狀態,保證最小的功耗水平。

 

3. route

route用於指定兩個widget之間的連接關系,使用snd_soc_dapm_route結構表示,通常,所有的路徑信息會用一個snd_soc_dapm_route結構數組來定義。和widget一樣,路徑信息也分別存在與codec驅動,machine驅動和platform驅動中。

snd_soc_dapm_route在定義的時候若是兩個widget之間是直連的,沒有經過開關Switch,那么kcontrol字符串就初始化為NULL。

 

4. path

route注冊后被解析成path,使用snd_soc_dapm_path結構表示。kcontrol和widget都可以通過靜態定義。但是path只能通過route解析而來。

一個path表示一段連接,至少包含:source widget,跳線path,sink widget。在DAPM中,用snd_soc_dapm_route結構來描述這樣一個連接關系。


5. complete path

  dapm要給一個widget上電的其中一個前提條件是:這個widget位於一條完整的音頻路徑上,而一條完整的音頻路徑的兩頭,必須是輸入/輸出端,或者是一個外部音頻設備,又或者是一個處於激活狀態的音頻流widget。分別用is_connected_output_ep()和is_connected_input_ep()得到該widget是否有同時連接到一個輸入端和一個輸出端,如果是,返回1來表示該widget需要上電。

在掃描dapm_dirty鏈表時,dapm使用兩個鏈表來分別保存需要上電和需要下電的widget,掃描完畢后按照上電次序,統一對各個widget上電。
up_list 保存需要上電的widget
down_list 保存需要下電的widget。

 

三、參考

ALSA聲卡驅動中的DAPM詳解之一:kcontrol
ALSA聲卡驅動中的DAPM詳解之二:widget-具備路徑和電源管理信息的kcontrol
ALSA聲卡驅動中的DAPM詳解之三:如何定義各種widget
ALSA聲卡驅動中的DAPM詳解之四:在驅動程序中初始化並注冊widget和route
ALSA聲卡驅動中的DAPM詳解之五:建立widget之間的連接關系
ALSA聲卡驅動中的DAPM詳解之六:精髓所在,牽一發而動全身
ALSA聲卡驅動中的DAPM詳解之七:dapm事件機制(dapm event)

 


免責聲明!

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



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