linux驅動由淺入深系列:ALSA框架詳解 音訊子系統之二
本文以高通平臺為例,介紹一下android下的音訊結構。android使用的是tinyALSA作為音訊系統,使用方法和基本框架與linux中常用的ALSA音訊子系統是一致的。
ALSA音訊框架
ALSA(Advanced Linux Sound Architecture)是一個開源專案(http://www.alsa-project.org/) ,在Kernel 2.6正式版本被引入,它提供了一整套音訊解決方案,涵蓋了使用者空間和核心空間對音訊裝置的操作介面。
在傳統的Linux平臺,使用者空間可以呼叫alsa-lib提供的應用來呼叫核心空間的alsa-soc介面, 實現對底層音訊裝置的控制。
android平臺的音訊框架如下:
Android平臺音訊系統使用的是tinyALSA,主要的改變在於使用者空間的音訊介面不依賴alsa-lib庫, 改用精簡的libtinyalsa庫,但是在核心中仍然使用ALSA框架的驅動框架。android中HAL層相關模組使用如下圖所示:
音效卡的主要功能:
1,播放聲音(playback)
2,錄音(capture)
3,聲音控制(control)
音訊裝置節點
音訊裝置節點位於/dev/snd目錄下,分為三類:
1,形如 pcmC0D0p 以‘p’結尾的表示播放節點,playback
2,形如 pcmC0D0c 以‘c’結尾的表示錄音節點,capture
3,形如controlC0 以“control”開頭的表示控制節點,控制音量等
其中pcmC0D0p與pcmC0D0c組成一個pcm裝置,其中C0代表0號音效卡Card,D0代表0號裝置device。一個音效卡可以有多個裝置,每個裝置代表該音效卡的一個音訊通路。
生成pcmC0D0p的程式碼分析如下,分析一下各個pcm節點的含義,如pcmC0D1c:
在kernel/sound/core/pcm.c中組織了pcmC0D0p字串,其中D0的數字0來自於pcm->device,pcm由引數傳遞而來。
以高通msm8952平臺為例:
D後面的數字為msm8952_dai結構體成員的個數
/kernel/sound/soc/msm/msm8952.c
android平臺HAL層音訊框架
音訊資料流框圖如下
HAL層音訊框架介面
1) audio.primary.msm8952.so
該檔案是高通平臺音訊HAL層程式碼庫,負責與framework交換音訊資料和控制引數。
程式碼路徑: hardware/qcom/audio/hal
LOCAL_SRC_FILES := \
audio_hw.c \
voice.c \
platform_info.c \
$(AUDIO_PLATFORM)/platform.c
2) libtinyalsa.so
該檔案是tinyALSA的應用庫,由HAL層呼叫,負責對接kernel的ALSA驅動介面。
程式碼路徑: external/tinyalsa
LOCAL_SRC_FILES:= mixer.c pcm.c
3) libaudioroute.so
該檔案用於解析使用者空間的音訊通道配置檔案(mixer_path_XXX.xml),由ALSA來配置音訊通道。
程式碼路徑: system/media/audio_route
LOCAL_SRC_FILES:= audio_route.c
kernel層ASoC框架
在核心中, ALSA依賴ASoC(ALSA System on Chip)驅動模型。ASoC是嵌入式系統使用的音訊框架,它從硬體架構的角度來將功能相對獨立的硬體單元細分出來,在驅動裝置上分為machine、 platform/CPU和CODEC三個模組。
可以這麼理解:一套嵌入式硬體平臺(Machine)包含了平臺AP(Platform)和音訊CODEC晶片(Codec),對應ASoC的三個裝置驅動。這三個裝置分別註冊各自功能的dev裝置,但都是以核心platform裝置模型來建立。 ASoC主要程式碼位於kernel/sound/soc下。 下面分別來介紹一下:
MAchine裝置驅動
Machine裝置可以看成是一塊嵌入式主機板(Board) 或者一塊音效卡。machine裝置驅動是ASoC驅動框架的入口, 主要功能是負責platform/cpu和codec之間的連線和控制,或者響應獨立於Platform功能和Codec功能之外的特殊音訊事件,如平臺GPIO控制外接功放等,這些屬於machine本身的特定操作程式碼,都會放到machine驅動裡。Machine裝置驅動的主要功能是定義各種DAI(Digital Audio Interface) links,它的作用是把platform/cpu和codec裝置驅動連線起來,形成完整的音訊通路。
在核心裝置樹中,平臺會定義一個音效卡裝置,如MSM8976平臺在msm8976.dtsi中定義了一個名為“msm8976-tasha-snd-card” 的音效卡,它就是ASoC框架裡的machine裝置。 machine裝置的初始化是整個ASoC驅動的入口。 machine裝置的probe()會呼叫snd_soc_register_card()去註冊音效卡,然後在snd_soc_instantiate_card()裡實例化音效卡裝置的時候,呼叫Platform和Codec裝置各自的probe(),完成這兩個裝置的初始化。如果沒有錯誤,那麼音效卡會註冊成功,我們在/dev/snd下可以看到多個音訊裝置。
高通平臺的machine裝置驅動放在如下路徑:
kernel/sound/soc/msm/<Chipset>
msm8976.dtsi定義如下:
msm9952-slimbus.c中程式碼中是machine的probe:
platform裝置驅動
Platform裝置可看作是平臺AP(SoC主控,或CPU)。 它負責提供嵌入式平臺端的音訊功能, 如播放、錄音、 Voice通話等。Platform裝置驅動主要有兩個作用:
(1) transfer:負責平臺AP端的audio/voice資料流(stream)和DSP之間的傳輸;
(2) routing:將stream資料流按照特定的路線對應到其他音訊模組中。
ASoC會註冊多個Platform裝置來負責不同功能的音訊模組:
(1) msm-pcm-dsp: 負責audio回放/錄音
程式碼路徑: kernel/sound/msm/msm-pcm-q6.c
(2) msm-pcm-voice: 負責voice通話
程式碼路徑: kernel/sound/msm/msm-pcm-voice-v2.c
(3) msm-voip-dsp: 負責VoIP通話
程式碼路徑: kernel/sound/msm/msm-pcm-voip-v2.c
(4) msm-compress-dsp: 負責壓縮格式的audio播放
程式碼路徑: kernel/sound/msm/msm-compressed-q6-v2.c
(5) msm-pcm-routing:負責stream資料流的路線指定
程式碼路徑: kernel/sound/msm/msm-pcm-routing-v2.c
msm-pcm-routing-v2.dtsi定義如下:
在msm-pcm-routing-v2.c裡定義了很多kcontrol、 widget和route。
(1) struct snd_kcontrol_new
kcontrol代表一個控制單元, 描述了控制元件自身的屬性和功能。 如mixer多路混合器、 mux多路選擇器、 gain增益設定、 mute開關等。
(2) struct snd_soc_dapm_widget
widget是kcontrol的封裝,能夠用path來連線多widget,形成一條音訊路徑。
(3) struct snd_soc_dapm_route
route用來描述相互連線的widget。
CPU裝置驅動
高通平臺將CPU裝置獨立於Platform裝置,其實他們是緊密結合的關係, CPU裝置驅動定義了平臺能夠支援的各種stream。
高通平臺用單獨的檔案來定義CPU裝置。
(1)在msm-dai-fe.c裡定義了FE CPU DAI
(2)在msm-dai-q6-v2.c裡定義了BE CPU DAI
CODEC裝置驅動
對於一塊嵌入式裝置的主機板來說,一般會整合一顆音訊CODEC晶片。 ASoC架構下的CODEC裝置功能和物理CODEC對應, 其在machine的控制下和platform裝置連線,負責音訊的實際處理,如聲音播放(D/A)、聲音採集(A/D) 和各種音訊control控制元件的設定。平臺一般會整合一個CODEC單元,也會有新增外部獨立CODEC晶片,已達到更好的音質。如Wolfson的WM8998晶片,它是一顆獨立的CODEC,基於I2S介面從平臺獲取音訊資料,在其內部經過DAC輸出到耳機或speaker。高通有自己的外接CODEC晶片,如WCD9326/9335等,和平臺AP的音訊資料介面叫Slimbus,其實是和I2S複用的GPIO口。
CODEC晶片可能需要I2C或SPI控制。
COCEC裝置支援耳機插拔及按鍵檢測功能。
CODEC裝置驅動中定義了大量的mixer、 mux和各種開關的kcontrol,以及DAPM使用的widget和route。
kernel/sound/soc/codec/wcd9335.c
例項:開啟MIC錄音
CODEC裝置驅動中定義了大量的mixer、 mux和各種開關的kcontrol,以及DAPM使用的widget和route。
音訊模組除錯
對於使用平臺內建CODEC或者高通外部CODEC的常規專案,把audio功能調通所需要修改的內容不多,如果是新增額外的SmartPA、 CODEC或者DAC晶片,那麼會比較麻煩。我們在專案中可能會有以下工作:
(1)主、副mic除錯
部分高通平臺的原始程式碼使用的是DMIC,而我們專案通常使用AMIC,這個需要修改DTSI的音效卡節點中“qcom,audio-routing” 值。
部分平臺程式碼的machine driver中缺少副mic的control,需要手動新增。
工模中需要對副mic單獨測試,但是高通預設沒有這樣的usecase來單獨使用mic,我們需要在audio hal層修改input裝置的選擇。
(2)耳機按鍵
在machine driver裡有一個耳機按鍵閥值的陣列,我們需要根據高通文件計算出不同按鍵阻抗的閥值,填進陣列。
(3)普通音訊PA
現在多數專案都會新增speaker PA,如果是單個gpio口控制開關的普通的PA,則需要在machine driver裡新增控制邏輯。
(4)第三方CODEC除錯
第三方CODEC通過I2S介面和AP傳輸音訊資料,然後控制speaker或headphone發聲,一般需要I2C或者SPI來控制晶片。我們需要把I2C或SPI調通,獲得CODEC晶片的暫存器資料;調通平臺端的master I2S輸出;綜合除錯。
(5) SmartPA除錯
SmartPA包含了CODEC和PA,晶片暫存器配置上比CODEC簡單,但是硬體介面仍然會是I2S和I2C,我們要做的事情和第三方CODEC差不多。
(6)第三方音效移植
從第三方獲取移植包,匯入即可。依據音效演算法執行的位置,我們需要配置平臺端的音樂播放模式。如果演算法跑在AP側,我們需要關閉平臺端offload播放模式,仍採用傳統的deep_buffer模式。
(7)音訊loopback測試
高通平臺在audioFT裡提供了loopback模式功能,我們需要針對不同的專案,修改ftm_loopback_config配置檔案,滿足工廠的測試要求。
(8)更新音訊引數
除錯中遇到的問題
在實際專案中, audio的問題種類會很多。 Audio涉及Android幾乎所有的層,遇到聲音問題時我們需要明確界定問題所處的位置。
(1) 無聲問題
一個新專案前期如果遇到無聲問題,首先檢查音效卡驅動是否掛載成功。如果音效卡沒掛上,那麼檢查驅動;如果音效卡OK,那麼用QXDM抓取音訊資料檢查DSP是否無聲。如果DSP無聲,需要往framework上面dump資料找原因;如果DSP正常,一般問題出在kernel裡,我們先試一下耳機是否有聲音。如果耳機有聲音speaker無聲音,那就重點檢查speaker這塊,是否新加的PA有問題;如果耳機無聲音,那麼dump一下CODEC的暫存器,或者檢查mixer_paths是否配置錯誤。等等。。
(2)播放雜音問題
雜音可能有多種原因。驅動裡PA的開關時序不合適會造成POP音;上層framework效能問題會造成音訊流斷續,也會導致POP音;音訊引數沒調好也會導致音質差,等等。。
(3)其他問題
除錯工具
(1) QXDM + QCAT
QXDM工具用來抓取DSP部分的log,包含音訊資料,然後用QCAT解析log,能夠還原出DSP內部各個節點的音訊資料。我們藉此可以判斷聲音從AP進入DSP和從DSP出來前是否異常。
(2)使用QACT確認音訊引數和呼叫的input/output裝置的Device
(3)開啟logcat和kmsg的各種log
對於上層,在音訊相關檔案的開頭開啟#define LOG_NDEBUG 0
對於kernel,如果核心的dynamic_debug可用,我們可以直接開啟相關檔案的除錯資訊;如果不可用,那需要在檔案的開頭新增#define DEBUG
(4)使用tinymix確認音訊控制元件開關
在shell裡可以使用tinymix把ASoC所有的control匯出來,我們可以檢查開關異常的control。