Alsa驅動分析(轉)


目錄

1.    Abstract

2.    Introduction

3.    音頻驅動框架介紹

3.1      音頻設備的注冊

3.2     音頻驅動的注冊

3.2.1       Probe函數的調用

3.2.2       Soc_probe函數

4.    通常的使用流程的分析

4.1.1       open過程介紹

4.1.2       snd_pcm_hw_params流程分析

4.1.3       prepare流程分析

4.1.4       write的流程

4.1.5       使用流程的總結t

5.    Amixer調用的相關邏輯

5.1.1       Amixer調用的上層邏輯

5.1.2       Amixer的內核流程

6.    總結

7.    未討論

1.               Abstract

主要是講2.6.21內核里面的alsa驅動的架構,以及在我們的平台上需要注意的東西。.

2.               Introduction

分成幾個部分:

驅動整體框架,一個簡單的播放流程介紹,以及我們的平台需要注意的地方;

3.               音頻驅動框架介紹

3.1               音頻設備的注冊

Alsa驅動分析(轉) - wilson - Wilsons blog

這就是設備的注冊了,設備本身非常簡單,復雜的是這個設備的drvdata,drvdata里面包含了三部分,關於machine的,關於platform的,關於codec的,從大體上說machine主要是關於cpu這邊的也可以說是關於ssp本身設置的,而platform是關於平台級別的,就是說這個平台本身實現相關的,而codec就是與我們所用的音頻codec相關的;基本上這里就可以看出整個音頻驅動的架構特點,就是從alsa層進入——>內核alsa層接口->core層,這里再調用上面說的三個方面的函數來處理,先是cpu級別的,再是platform的,再是codec級別的,這幾層做完了,工作也就做得差不多了,后面會詳細講講,當然這個執行順序不是固定的(不知道是不是marvel寫代碼不專業導致的),但多半都包括了這三部分的工作;

3.2            音頻驅動的注冊

3.2.1          Probe函數的調用      

                  Alsa驅動分析(轉) - wilson - Wilsons blog

                   前面講了設備的注冊,里面的設備的名字就是”soc-audio”,而這里的driver的注冊時名字也是 soc-audio”,對於platform的設備匹配的原則是根據名字的,所以將會匹配成功,成功后就會執行audio驅動提供的probe函數soc_probe;

3.2.2          Soc_probe函數

這個函數本身架構很簡單,和前面說的邏輯一樣,先調用了cpu級別的probe,再是codec級別的,最后是platform的(這里三個的順序就不一樣),但是因為cpu級別的和platform級別的都為空,最后都調用了codec級別的probe函數,也就是micco_soc_probe,這個函數基本上就完成了所有應該完成的音頻驅動的初始化了;簡單的划分,分成兩部分,對上和對下:對上主要是注冊設備節點,以及這些設備節點對應的流的創建;對下主要是讀寫函數的設置,codec本身的dai設置,初始化寄存器的設置,最重要的就是后面的control的創建和門的創建了,如下圖所示:

Alsa驅動分析(轉) - wilson - Wilsons blog這里面的第一部分就是負責初始化的;

 

第二部分就是創建卡和流,對於alsa驅動來說,是先分成卡0,卡1…,然后對於每一個卡的每一個cpu支持的dai(digit audio interface)也就是pcm接口 或者i2S接口等都要建立對應的流,一個dai有可能包含兩個流,一個是錄的一個是play的,但在我們的平台上對於i2S的dai是沒有錄音功能的,所以我們的平台只有一個卡,三個流,pcm的錄和play,i2S的play;流的創建還是更多的考慮為上層服務的,它所提供的接口都是soc層的,這里非常重要的地方在於驅動的一個典型做法那就是如何把關鍵的內核數據結構和export到外部的/dev下的設備節點實現關聯,比如:

 

關鍵數據結構struct snd_pcm,是根據cpu所固有的dai創建的,而對於每一個struct  snd_pcm又可能用到兩個substream(它們實現具體的流的播放等),它們之間的鏈接是通過它的內部數據成員struct snd_pcm_str streams[2];來連接的,而這個snd_pcm類型的指針是在函數snd_device_new里面通過device_data放到設備里面的,這個設備會在snd_device_register_all

的時候注冊到/dev下面,並且調用dev_set_drvdata(preg->dev, private_data);來把這個指針放到設備的私有數據里面;而在需要使用的時候通過snd_pcm_playback_open里面的snd_lookup_minor_data函數取得其私有數據並返回的,這樣就實現了設備節點和對應的驅動的數據結構的關聯,這是一種非常普遍的做法;有了這個數據結構它就可以根據一定的原則取得對應於這個需求的substream,於是一切的操作都可以交給這個substream

 

第三部分就是control的創建,這個函數比較簡單,就是把表micco_snd_controls里面已經定義好的controls模板創建controls,然后加入到card的controls列表中去;本身功能很清晰,但是對於我們平台來說,需要非常小心,因為這里決定了各個controls的序號,而這個序號是audio_controller訪問硬件的索引,所以千萬要小心盡量要維持目前的controls的序號,如果要額外添加新的controls一定要記得要放在micco_add_widgets后面來做,這樣可以做到兼容,否則audio_controller的工作量就大了

 

第四部分就是門的創建了,這個函數也是很清楚,就是把codec對應的門都加入到codec->dapm_widgets列表中去(這里的門的概念可以簡單的理解為水管與水管之間的連接的地方,聲音數據像水一樣從水管里面流出來,源頭可以是CPU了,也可以是modem,然后通過不同的門,流向不同的地方,比如speaker,比如藍牙耳機等等),然后根據micco_audio_map把所有可能連在一起的門連接起來,這個表micco_audio_map的意思是{目的名字,控制點名字,源頭名字},然后函數snd_soc_dapm_connect_input會根據這些名字去查表codec->dapm_widgets(先前已經把所有的門都加入了)找到它們再根據不同的類型做不同的連接,比如是mux之間的連接,mux和pga之間的連接等等,注意這里的連接其實只不過是說找到連接的可能性,它對於不同的門,找到其可能的source和sink,加入到對應的列表中去,具體細節如下:

首先,掃描整個codec所擁有的所有的門,如果它的名字和傳入的sink的名字相同,則認為它就是這個路徑的sink,如果它的名字和傳入的source名字相同,則認為它是這個路徑的source,如果源頭或者sink沒有找到都返回錯誤;然后分配一個struct snd_soc_dapm_path,這個數據結構的主要成分包括名字,source門,sink門,這條路徑的control,這個源頭和sink是否已經連接,是否已經走過(用在后面),這個數據結構會被掛在三個鏈表里面,一個是source的就是這個門會在很多的路徑中,把它在這個路徑中做sourcepath都連在一起,一個是sink的就是把這個門在所有這些由它做sinkpath都連接在一起,一個是把所有的路徑都需要連接在一起的這個是通過codecdapm_paths來訪問的;

list_add(&path->list, &codec->dapm_paths);

list_add(&path->list_sink, &wsink->sources);

list_add(&path->list_source, &wsource->sinks);

需要注意的時候,這里把路徑的list_sink加入到了wsink門的sources列表里面,而把路徑的list_source加入到wsourcesinks列表里面,所以當訪問的時候從wsink門的sources出發就可以找到連接這個門作為sink的所有的路徑,而從wsourcesinks列表出發就可以找到所有以這個門作為source的路徑;

第三步就是為這個數據結構賦值:sourcesink,初始化三個鏈表;第四步:如果control為空則把這個路徑加入到相應的三個鏈表中去,並且路徑設為已經連接,並返回;第五步:否則,根據sink的類型,如果是adcdacinputoutputmicbiasvmidprepost,則把路徑加入到三個鏈表,設置已經連接的標志;如果是snd_soc_dapm_mux則調用dapm_connect_mux來處理;如果是mixerswitch則調用dapm_connect_mixer來處理,如果是hpmiclinespk,則把path加入到三個鏈表中去,但是設置成為連接的狀態

大約就是這樣的了。

也許您要問,為什么要這么做呢?

這個,我也有想過,甚至我認為在門比較少的時候,確實沒什么必要,但是這么做的好處在於當要播放音樂的時候,它可以實現自動的尋找路徑並且自動poweron那些門,不需要上層做任何的控制,因為它真的到達目的地的所有的路徑,這樣它可以自己選擇走哪條路;如果不這么連接起來的話,就需要提供給上層連接的接口,完全由上層來決定該連接哪些門,也必須由上層來負責poweronoff這些門;

第五部分就是注冊了,這里就是向/dev注冊設備節點,因為這些設備節點會由alsa層來訪問的,這些設備節點和驅動的連接我前面已經說了,主要是提供了對上的alsa接口,給alsa層調用。  

4.               通常的使用流程的分析

通常使用alsalib來播放聲音包括以下幾個步驟:

1,   open,這個和oss相同,對應於alsa就是snd_pcm_open;

2,   param設置,這個就是snd_pcm_hw_params;

3,   上層的alsa在設置param的成功以后或者出錯的時候恢復都需要調用snd_pcm_prepare;

4,   write函數;

現在一個個的來看;

4.1.1          open過程介紹

如下圖所示:Alsa驅動分析(轉) - wilson - Wilsons blog

         就是我先前說的分成三部分,先是cpu級別的,包括clock的enabe,中斷的申請,空間的申請;

         然后就是平台級別的包括DMA所需要的空間的分配等;

         不過這里codec級別的沒有提供相關的函數,由machine提供了一些函數主要是設置channel,格式,頻率范圍等等;

4.1.2          snd_pcm_hw_params流程分析

Alsa驅動分析(轉) - wilson - Wilsons blog

         流程就是這樣,至於里面做的具體的事情,我覺得只需要對照spec看看就知道了,具體的寄存器設置下面有一點講解,主要是格式的設置(采樣率的設置會留到prepare的時候),至於中斷上來的時候那個更新hw_ptr函數很有用,這樣上層就可以知道數據到底寫了多少或者說還有多少空間可以寫;

 

4.1.3          prepare流程分析

 

alsa層調用snd_pcm_prepare的時候會觸發驅動對應的prepare的函數執行,如下:

Alsa驅動分析(轉) - wilson - Wilsons blog可以看出基本上還是分為了三段,一段是cpu級別的,主要是對於ssp port的設置,具體設置如下:

對於voice通道,littleton_micco_voice_prepare的設置:

the sscr0 0xc0163f,sscr1 0xf01dc0,sspsp 0x800085

其中對於pcmssp地址是:

#define SSCR0_P4        __REG(0x41A00000)  /* SSP Port 4 Control Register 0 */

#define SSCR1_P4        __REG(0x41A00004)  /* SSP Port 4 Control Register 1 */

#define SSPSP_P4        __REG(0x41A0002C)  /* SSP Port 4 Programmable Serial Protocol */

所以結果就相當於:

 SSCR0_P4 0x41A000000xc0163f ——00000000110000000001011000111111

對於這個地址高8位為0

31 MOD-0:普通模式;30ACS-0:時鍾選擇是由NCSECS位絕決定,看后面;

29FPCKE-0FIFO packing mode disabled28()-0reserved

2752MM-013mbps模式;2624FRDC-0:每幀的時隙數

23TIM->1:表示禁止傳輸fifo underrun中斷;22RIM-1:表示禁止接收fifo overrun中斷

21NCS->0:表示時鍾選擇由ECS決定;20EDSS-0:表示前面填充DSS來達到8-16

198SCR-0x16:決定串口bit率,=sspx clock/(scr+1)???;7SSE-0:表示disable port

6ECS-0:表示片內的時鍾用來計算serial clock rate54FRF-0b11:表示psp模式用來模擬I2S協議

30DSS-0b1111:表示16bit數據(EDSS0

 

SSCR1_P4 0x41A00004:  0xf01dc0——00000000111100000001110111000000

對於這個地址高8位也為0

31TTELP-0:表示最后一個bit傳輸(LSB)傳完后有半個時鍾處於高阻(三態)狀態;

30(TTE)-0:表示傳輸信號不是三態的;29EBCEI-0bit傳輸錯誤不產生中斷

28SCFR-0:表示SSPSCLK的時鍾信號需要連續的工作,主模式ignore27ECRA-0:表示禁止其它ssp向它發起始終請求

26ECRB-0:表示同2725SCLKDIR-0:表示主模式,SSP端口,驅動SSPSCLK

24SFRMDIR-0:表示主模式,SSP端口,驅動SSPSFRM信號;

23RWOT-1:表示只接收不傳輸???;22TRAIL-1:表示trailing bytesdma burst來處理;

21TSRE-1:表示傳輸DMA sevice request enabled20RSRE-1:表示接收DMA service request允許

19TINTE-0:表示接收超時中斷disable18PINTE-0:表示外設trail byte 中斷disable

17:保留;16IFS->0:表示幀的極性由PSP的極性位決定;

15STRF-0:表示傳輸FIFO(讀,寫)由SSDR_X來決定;14EFWR-0:表示FIFO讀寫特別函數disable

1310RFT-0b0111:表示到達什么級別rxfifo斷言中斷;96TFT-0111:表示TXFIFO斷言中斷級別

5:保留;4SPH-0:表示在每一個幀開始之前要等一個時鍾,結束后要等0.5個時鍾;

3SPO-0:表示SSPSCLKinactive的時候是低電平;2LBM-0:表示非循環模式

1RIE-0:表示RXFIFO門檻到達的中斷disable0RIE)->0:表示接收FIFO門檻到達中斷disable

 

 

SSPSP_P4 0x41A0002C: 0x800085-00000000100000000000000010000101

這個地址的高8位為0

31reverved3028EDMYSTOP-0extended dummy stop

2726EDMYSTART-0extended dummy start25FSRT-0:下一幀的開始由前面的擴展STOP決定;

2423DMYSTOP-0b01:表示最后一bit傳輸完畢后保持activeclock1clock的延遲;22:保留

2116SFRMWDTH-0:表示最小位幀寬度;159SFRMDLY-0serial frame dealy

87DMYSTRT-0b01:表示1clock的延遲在開始的時候;64STRTDLY-0start delay

3ETDS-0:表示結束時的傳輸狀態為low2SFRMP-1serial frame的極性;

10SCMODE-0b01data driven 上升沿,數據采樣下降沿,idle狀態,低;

 

對於littleton_micco_hifi_prepare的設置:

 The sscr0 e1c0003f,sscr1 701dc0,sspsp 40200004,sstsa 3,ssrsa 3,ssacd 60,ssacdd 6590040

其中對於I2sspp地址是:

#define SSCR0_P3  __REG(0x41900000)  /* SSP Port 3 Control Register 0 */

#define SSCR1_P3  __REG(0x41900004)  /* SSP Port 3 Control Register 1 */

#define SSPSP_P3  __REG(0x4190002C)  /* SSP Port 3 Programmable Serial Protocol */

#define SSTSA_P3  __REG(0x41900030)  /* SSP Port 3 Tx Timeslot Active */

#define SSRSA_P3  __REG(0x41900034)  /* SSP Port 3 Rx Timeslot Active */

#define SSACD_P3 __REG(0x4190003C)  /* SSP Port 3 Audio Clock Divider */

#define     SSACDD_P3   __REG(0x41900040)  /* SSP Port 3 Audio Clock Dither Divider Register */

 

SSCR0_P3==__REG(0x41900000):e1c0003f——11100001110000000000000000111111

31 MOD-1:網絡模式;30ACS-1:時鍾選擇是audio clockaudio clock divider決定,由ssacd寄存器決定;

29FPCKE-1FIFO packing mode enabled28()-0reserved

2752MM-013mbps模式;2624FRDC-1:每幀的時隙數

23TIM->1:表示禁止傳輸fifo underrun中斷;22RIM-1:表示禁止接收fifo overrun中斷

21NCS->0:這里ignore,由ACS決定了(為1);20EDSS-0:表示前面填充DSS來達到8-16

198SCR-0:ACS那里決定;7SSE-0:表示disable port,工作時應為1

6ECS-0:表示片內的時鍾用來計算serial clock rate54FRF-0b11:表示psp模式用來模擬I2S協議

30DSS-0b1111:表示16bit數據(EDSS0

 

SSCR1_P3==__REG(0x41900004):701dc0——00000000011100000001110111000000

31TTELP-0:表示最后一個bit傳輸(LSB)傳完后有半個時鍾處於高阻(三態)狀態;

30(TTE)-0:表示傳輸信號不是三態的;29EBCEI-0bit傳輸錯誤不產生中斷

28SCFR-0:表示SSPSCLK的時鍾信號需要連續的工作,主模式ignore27ECRA-0:表示禁止其它ssp向它發起始終請求

26ECRB-0:表示同2725SCLKDIR-0:表示主模式,SSP端口,驅動SSPSCLK

24SFRMDIR-0:表示主模式,SSP端口,驅動SSPSFRM信號;

23RWOT-0:接收和傳輸都可以;22TRAIL-1:表示trailing bytesdma burst來處理;

21TSRE-1:表示傳輸DMA sevice request enabled20RSRE-1:表示接收DMA service request允許

19TINTE-0:表示接收超時中斷disable18PINTE-0:表示外設trail byte 中斷disable

17:保留;16IFS->0:表示幀的極性由PSP的極性位決定;

15STRF-0:表示傳輸FIFO(讀,寫)由SSDR_X來決定;14EFWR-0:表示FIFO讀寫特別函數disable

1310RFT-0b0111:表示到達什么級別rxfifo斷言中斷;96TFT-0111:表示TXFIFO斷言中斷級別

5:保留;4SPH-0:表示在每一個幀開始之前要等一個時鍾,結束后要等0.5個時鍾;

3SPO-0:表示SSPSCLKinactive的時候是低電平;2LBM-0:表示非循環模式

1RIE-0:表示RXFIFO門檻到達的中斷disable0RIE)->0:表示接收FIFO門檻到達中斷disable

 

SSPSP_P3==__REG(0x4190002C):40200004——01000000001000000000000000000100

31reverved3028EDMYSTOP-4extended dummy stop

2726EDMYSTART-0extended dummy start25FSRT-0:下一幀的開始由前面的擴展STOP決定;

2423DMYSTOP-0b00:表示最后一bit傳輸完畢后保持activeclock數的延遲;22:保留

2116SFRMWDTH-0b20:表示最小位幀寬度;159SFRMDLY-0serial frame dealy

87DMYSTRT-0b00:表示0clock的延遲在開始的時候;64STRTDLY-0start delay

3ETDS-0:表示結束時的傳輸狀態為low2SFRMP-1serial frame的極性;

10SCMODE-0b00data driven 下降沿,數據采樣上升沿,idle狀態,低;

 

SSTSA_P3==__REG(0x41900030):3——0011

318->0:reserved;7:0TTSA->0b0011:表示在那個time slot里面是傳輸數據的0,不傳輸,1傳輸;

SSRSA_P3==__REG(0x41900034):3——0011

31reserved70RTSA-0:表示在那個slot里面接收數據,0,不接受,接收;

SSACD_P3==__REG(0x4190003C):60——01100000

318reserved7SCDX8-0sysclk/4產生內部audio clock1sysclk/8產生audio clock

64ACPS-0b110:PLL輸出時鍾由Audio clock dither Divider register value決定;

3SCDB-0:如果SCDX80scdx8決定,為1,則sysclk不分頻;

20ACDS-0:表示clock divider select /1

SSACDD_P3==__REG(0x41900040):6590040——00000110010110010000000001000000

31reserved3016NUM-1625;除數(0x659

1512reserved110DEN-64:被除數

比如我們的板子上是這樣計算這些值的:

比如,在我們的機子上的一個實例是這樣的,

那么第一步取得采樣率:48K,它也就是Sync clock

第二步球的bit率:48X64=3.072M

第三步求的sysclk:這個根據scdx8決定是X4還是X8,在我們的例子中是4,所以:3.072X4=12.288

第四步求得我們要的dither divider y,公式為:

624*(y)/2=12.288

算出y=0.039384615384615384615384615384615

所以查可能的分子和分母表,得出,分子是:

64,分母是1625

如下圖所示:

Alsa驅動分析(轉) - wilson - Wilsons blog

當然更加詳細的請參閱spec

 

第二段是平台級別的,主要是對於DMA的初始化;

第三段是codec級別的,這里主要是對codec本身的設置,通過i2c接口對codec的寄存器操作,比如采樣率等等;

最后面還有一個poweron的函數,這個函數前面有提到,但是沒有詳細分析,這里分析一下:

首先根據事件類型,決定是關閉門序列,還是啟動門序列,我只分析啟動過程;

得到啟動序列,就開始遍歷整個序列,對於這個序列的每一個類型,查找所有的門的序列,直到有一個門的類型和當前啟動序列的類型相同,然后再根據不同的類型做不同的檢查和power

1,如果是snd_soc_dapm_vmid則繼續,不做任何處理;

2

A)如果是snd_soc_dapm_adc,並且其active1,這個active在上一步已經分析過了,必須要包含這個流的名字的sname的門才會被激活,假設我們現在討論的是用pcm通道播放聲音,那么流的名字就是“Voice Playback”,所以,將dac3active被設成1,這樣就避免了power on dac1dac2,和adc了。如果這兩個條件都滿足,那么必然是“Voice Capture”了,因為只有這時候,我們才會用到adc,現在看看,如果用了adc將會啟動什么,於是調用函數is_connected_input_ep,這是一個通用遞歸函數,從名字上來說就是看是否是已經連接了輸入的門,我們只考慮adc的情況,其余的情況待會再討論,對於adc,在is_connected_input_ep函數里面,是通過遍歷所有以這個門作為sinksource門(list_for_each_entry(path, &widget->sources, list_sink),可以看到,這里的最后一個參數是list_sink,而第二個參數卻是widget->sources,這個原因我在門連接分析頁里面已經分析過了,總之sources就表示這個門的sources列表,而sinks就是這個門的sink列表),通過遞歸調用is_connected_input_ep來查看這些source門是否其中有一個是連通的,返回的是所有是否連通的和(聯通為1,否則為0),所以返回的結果可能是大於1的數,表示不只一個源是聯通的。

B)如果這個函數返回為真則表示此adc是聯通的,於是調用dapm_update_bits來處理,這個函數過對mux(它的reg<0)inputoutputmichplinespk,不做任何處理就返回了;過了這一關,開始查是否menrevert為真,如果為真,則把power取反,原來為真現在變假,於是調用snd_soc_readmicco_soc_read)開始讀這個寄存器的值(注意,這里讀的值是可能和物理上的這個寄存器的值不一樣的,這里讀的值是cache里面的值),讀出來后強制把1<<shift后的位置為1,比較新舊值是否有變化,如果有,則調用snd_soc_write(codec, widget->reg, new)把值寫到cache里面(實際負責寫的是micco_soc_write,而且,它對於0x70+0x15以下包含0x15的值是直接寫到寄存器的地址的,否則只是些到數組cache里面去)好對於adc的情況我們就分析完了。

3

A)如果此類型是snd_soc_dapm_dac並且active1,則調用is_connected_output_ep來取得是否要power,下面來看看函數is_connected_output_ep,這也是一個通用的判斷是否有連接到輸出的遞歸函數,我們只分析dac的情況,list_for_each_entry(path, &widget->sinks, list_source),上面已經講過,這里實際上查的是這個門的所有的sink列表,通過遞歸調用is_connected_output_ep來看是否它的sink是聯通的,只要有一條路是聯通的,則power為真。

B)返回后調用dapm_update_bits來處理,上面已經分析過了,這個函數就是判斷是否需要設置此門的寄存器的1<<shift位。

4,如果此類型是snd_soc_dapm_pga,則調用is_connected_input_ep來判斷是否聯通輸入,再調用is_connected_output_ep判斷是否聯通輸出,對於pga is_connected_input_ep函數的處理和adc是一樣的,對於is_connected_output_epdac的處理是一樣的,接着調用dapm_set_pga,根據power的值決定是mute pga還是啟用pga,但是就我打印的結果來看,基本上這個函數所起的作用為0,因為對於pga的門似乎都沒有設置相應的control,最后調用dapm_update_bits,設置power位。

5,對於other widget,這里在我們的平台上多半是指mux,首先調用is_connected_input_ep判斷是否連接輸入,再調用is_connected_output_ep判斷是否有輸出,調用dapm_update_bits位設置power 位,最后調用w->eventdo_post_event)來進行后期處理,這里主要就是對mux進行寄存器設置,因為mux的寄存器的地址都是大於0x70+0x15的,所以它們的地址需要轉化,這個函數就是根據mux的類型,訪問不同的寄存器。

 

基本上prepare后,一切就都就緒了,只等一個trigger;而trigger的執行會在上層的alsalib調用write的函數觸發;

4.1.4          write的流程

 

Alsa驅動分析(轉) - wilson - Wilsons blog

用戶層的write到內核里面都是通過ioctl來做的,這里面會觸發trigger函數的執行,等trigger執行完以后,才會真正調用函數把用戶層的東西copy到dma分配的空間;

這里面基本上只是畫了最簡單的邏輯,其實里面非常的復雜特別是函snd_pcm_lib_write1,這里面有同步的操作,也就是要等到有空余的空間的時候才允許寫,否則就要等待,喚醒是通過函數snd_pcm_update_hw_ptr_post來做的,這個函數會在DMA傳輸完一幀的中斷到來的時候被調用,用來更新緩沖區指針;

 

其中trigger的邏輯如下:

Alsa驅動分析(轉) - wilson - Wilsons blog

 

簡單的說就是啟動DMA,enable ssp口;

4.1.5          使用流程的總結t

         簡單總結一下,用戶的使用流程;

         A,調用snd_pcm_open打開設備節點對應的pcm流的substream也就是錄音或者play;

B,調用snd_pcm_hw_params設置硬件參數,包括格式,通道,采樣率,DMA空間的分配,中斷的申請等等,這里面會調用prepare函數使硬件准備好硬件,包括codec的寄存器設置,各種路徑的建立,門的power on等;

C,調用write函數實現把數據寫到設備里面去,這里會觸發trigger函數也就是DMA的啟動,SSP端口的啟動等。

5.               Amixer調用的相關邏輯

我們的audio controller所調用的驅動的接口都是amixer 的cset,cget,所以有必要分析一下它的邏輯:

5.1.1          Amixer調用的上層邏輯

Alsa驅動分析(轉) - wilson - Wilsons blog

    也就是說通過/dev下面的設備節點調用相應的ioctl,然后進入到內核的范圍;

5.1.2          Amixer的內核流程     

Alsa驅動分析(轉) - wilson - Wilsons blog

這里只分析了控制函數為snd_soc_dapm_put_enum_double的處理邏輯,其它的都類似,而具體的應該是哪個處理函數來處理是在control的new的時候就已經確立了的,對於我們的平台其實在表micco_dapm_widgets建立的時候就已經確立了;

為了方便后來者的調試,我這里把各個numid的對應的控制函數都列出來了,如下:

numid=112snd_soc_put_volsw

numid=1320snd_soc_dapm_put_enum_double

 

 

.

6.               總結

Alsa驅動的架構主要是分成對上為alsalib提供接口,對下實現硬件的管理,對上的內容基本都是在sound/core目錄里面的文件來完成,而設計硬件的操作分成兩部分一部分相關與cpu這邊的是由sound/soc/pxa目錄里面的文件來完成的,另外一部分設計codec是由sound/soc/codec來完成的,這部分主要就是對codec這邊的寄存器的設置;簡單示意如下:

Alsa驅動分析(轉) - wilson - Wilsons blog

它復雜的地方在於用戶態的alsa lib。

7.               未討論

還有一些地方沒有討論到,比如timer,不過留到以后補充吧

備注:

              內核版本:2.6.21(+marvel patch)

                硬件平台:pxa310+9034codec


免責聲明!

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



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