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???????????????音頻設備的注冊
這就是設備的注冊了,設備本身非常簡單,復雜的是這個設備的drvdata,drvdata里面包含了三部分,關于machine的,關于platform的,關于codec的,從大體上說machine主要是關于cpu這邊的也可以說是關于ssp本身設置的,而platform是關于平臺級別的,就是說這個平臺本身實現相關的,而codec就是與我們所用的音頻codec相關的;基本上這里就可以看出整個音頻驅動的架構特點,就是從alsa層進入——>內核alsa層接口->core層,這里再調用上面說的三個方面的函數來處理,先是cpu級別的,再是platform的,再是codec級別的,這幾層做完了,工作也就做得差不多了,后面會詳細講講,當然這個執行順序不是固定的(不知道是不是marvel寫代碼不專業導致的),但多半都包括了這三部分的工作;
3.2????????????音頻驅動的注冊
3.2.1??????????Probe函數的調用??????
??????????????????
???????????????????前面講了設備的注冊,里面的設備的名字就是”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驅動來說,是先分成卡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的就是這個門會在很多的路徑中,把它在這個路徑中做source的path都連在一起,一個是sink的就是把這個門在所有這些由它做sink的path都連接在一起,一個是把所有的路徑都需要連接在一起的這個是通過codec的dapm_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加入到wsource的sinks列表里面,所以當訪問的時候從wsink門的sources出發就可以找到連接這個門作為sink的所有的路徑,而從wsource的sinks列表出發就可以找到所有以這個門作為source的路徑;
第三步就是為這個數據結構賦值:source,sink,初始化三個鏈表;第四步:如果control為空則把這個路徑加入到相應的三個鏈表中去,并且路徑設為已經連接,并返回;第五步:否則,根據sink的類型,如果是adc,dac,input,output,micbias,vmid,pre,post,則把路徑加入到三個鏈表,設置已經連接的標志;如果是snd_soc_dapm_mux則調用dapm_connect_mux來處理;如果是mixer和switch則調用dapm_connect_mixer來處理,如果是hp,mic,line,spk,則把path加入到三個鏈表中去,但是設置成為連接的狀態。
大約就是這樣的了。
也許您要問,為什么要這么做呢?
這個,我也有想過,甚至我認為在門比較少的時候,確實沒什么必要,但是這么做的好處在于當要播放音樂的時候,它可以實現自動的尋找路徑并且自動poweron那些門,不需要上層做任何的控制,因為它真的到達目的地的所有的路徑,這樣它可以自己選擇走哪條路;如果不這么連接起來的話,就需要提供給上層連接的接口,完全由上層來決定該連接哪些門,也必須由上層來負責poweron和off這些門;
第五部分就是注冊了,這里就是向 /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過程介紹
如下圖所示:
?????????就是我先前說的分成三部分,先是cpu級別的,包括clock的enabe,中斷的申請,空間的申請;
?????????然后就是平臺級別的包括DMA所需要的空間的分配等;
?????????不過這里codec級別的沒有提供相關的函數,由machine提供了一些函數主要是設置channel,格式,頻率范圍等等;
4.1.2??????????snd_pcm_hw_params流程分析
?????????流程就是這樣,至于里面做的具體的事情,我覺得只需要對照spec看看就知道了,具體的寄存器設置下面有一點講解,主要是格式的設置(采樣率的設置會留到prepare的時候),至于中斷上來的時候那個更新hw_ptr函數很有用,這樣上層就可以知道數據到底寫了多少或者說還有多少空間可以寫;
?
4.1.3??????????prepare流程分析
?
當alsa層調用snd_pcm_prepare的時候會觸發驅動對應的prepare的函數執行,如下:
可以看出基本上還是分為了三段,一段是cpu級別的,主要是對于ssp port的設置,具體設置如下:
對于voice通道,littleton_micco_voice_prepare的設置:
the sscr0 0xc0163f,sscr1 0xf01dc0,sspsp 0x800085
其中對于pcm的ssp地址是:
#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 0x41A00000:0xc0163f ——》0000,0000,1100,0000,0001,0110,0011,1111
對于這個地址高8位為0,
31?(MOD)-》0:普通模式;30(ACS)-》0:時鐘選擇是由NCS和ECS位絕決定,看后面;
29(FPCKE)-》0:FIFO packing mode disabled;28()-》0:reserved
27(52MM)-》0:13mbps模式;26:24(FRDC)-》0:每幀的時隙數
23(TIM)->1:表示禁止傳輸fifo underrun中斷;22(RIM)-》1:表示禁止接收fifo overrun中斷
21(NCS)->0:表示時鐘選擇由ECS決定;20(EDSS)-》0:表示前面填充DSS來達到8-16位
19:8(SCR)-》0x16:決定串口bit率,=sspx clock/(scr+1)???;7(SSE)-》0:表示disable port
6(ECS)-》0:表示片內的時鐘用來計算serial clock rate;5:4(FRF)-》0b11:表示psp模式用來模擬I2S協議
3:0(DSS)-》0b1111:表示16bit數據(EDSS為0)
?
SSCR1_P4 0x41A00004:??0xf01dc0——》0000,0000,1111,0000,0001,1101,1100,0000
對于這個地址高8位也為0,
31(TTELP)-》0:表示最后一個bit傳輸(LSB)傳完后有半個時鐘處于高阻(三態)狀態;
30(TTE)-》0:表示傳輸信號不是三態的;29(EBCEI)-》0:bit傳輸錯誤不產生中斷
28(SCFR)-》0:表示SSPSCLK的時鐘信號需要連續的工作,主模式ignore;27(ECRA)-》0:表示禁止其它ssp向它發起始終請求
26(ECRB)-》0:表示同27;25(SCLKDIR)-》0:表示主模式,SSP端口,驅動SSPSCLK;
24(SFRMDIR)-》0:表示主模式,SSP端口,驅動SSPSFRM信號;
23(RWOT)-》1:表示只接收不傳輸???;22(TRAIL)-》1:表示trailing bytes?由dma burst來處理;
21(TSRE)-》1:表示傳輸DMA sevice request?是enabled;20(RSRE)-》1:表示接收DMA service request允許
19(TINTE)-》0:表示接收超時中斷disable;18(PINTE)-》0:表示外設trail byte?中斷disable;
17:保留;16(IFS)->0:表示幀的極性由PSP的極性位決定;
15(STRF)-》0:表示傳輸FIFO(讀,寫)由SSDR_X來決定;14(EFWR)-》0:表示FIFO讀寫特別函數disable
13:10(RFT)-》0b0111:表示到達什么級別rxfifo斷言中斷;9:6(TFT)-》0111:表示TXFIFO斷言中斷級別
5:保留;4(SPH)-》0:表示在每一個幀開始之前要等一個時鐘,結束后要等0.5個時鐘;
3(SPO)-》0:表示SSPSCLK在inactive的時候是低電平;2(LBM)-》0:表示非循環模式
1(RIE)-》0:表示RXFIFO門檻到達的中斷disable;0(RIE)->0:表示接收FIFO門檻到達中斷disable
?
?
SSPSP_P4 0x41A0002C:??0x800085-》0000,0000,1000,0000,0000,0000,1000,0101
這個地址的高8位為0,
31:reverved;30:28(EDMYSTOP)-》0:extended dummy stop;
27:26(EDMYSTART)-》0:extended dummy start;25(FSRT)-》0:下一幀的開始由前面的擴展STOP決定;
24:23(DMYSTOP)-》0b01:表示最后一bit傳輸完畢后保持active的clock數1clock的延遲;22:保留
21:16(SFRMWDTH)-》0:表示最小位幀寬度;15:9(SFRMDLY)-》0:serial frame dealy
8:7(DMYSTRT)-》0b01:表示1clock的延遲在開始的時候;6:4(STRTDLY)-》0:start delay
3(ETDS)-》0:表示結束時的傳輸狀態為low;2(SFRMP)-》1:serial frame的極性;
1:0(SCMODE)-》0b01:data driven?上升沿,數據采樣下降沿,idle狀態,低;
?
對于littleton_micco_hifi_prepare的設置:
?The sscr0 e1c0003f,sscr1 701dc0,sspsp 40200004,sstsa 3,ssrsa 3,ssacd 60,ssacdd 6590040
其中對于I2s的spp地址是:
#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——》1110,0001,1100,0000,0000,0000,0011,1111
31?(MOD)-》1:網絡模式;30(ACS)-》1:時鐘選擇是audio clock和audio clock divider決定,由ssacd寄存器決定;
29(FPCKE)-》1:FIFO packing mode enabled;28()-》0:reserved
27(52MM)-》0:13mbps模式;26:24(FRDC)-》1:每幀的時隙數
23(TIM)->1:表示禁止傳輸fifo underrun中斷;22(RIM)-》1:表示禁止接收fifo overrun中斷
21(NCS)->0:這里ignore,由ACS決定了(為1);20(EDSS)-》0:表示前面填充DSS來達到8-16位
19:8(SCR)-》0:由ACS那里決定;7(SSE)-》0:表示disable port,工作時應為1
6(ECS)-》0:表示片內的時鐘用來計算serial clock rate;5:4(FRF)-》0b11:表示psp模式用來模擬I2S協議
3:0(DSS)-》0b1111:表示16bit數據(EDSS為0)
?
SSCR1_P3==__REG(0x41900004):701dc0——》0000,0000,0111,0000,0001,1101,1100,0000
31(TTELP)-》0:表示最后一個bit傳輸(LSB)傳完后有半個時鐘處于高阻(三態)狀態;
30(TTE)-》0:表示傳輸信號不是三態的;29(EBCEI)-》0:bit傳輸錯誤不產生中斷
28(SCFR)-》0:表示SSPSCLK的時鐘信號需要連續的工作,主模式ignore;27(ECRA)-》0:表示禁止其它ssp向它發起始終請求
26(ECRB)-》0:表示同27;25(SCLKDIR)-》0:表示主模式,SSP端口,驅動SSPSCLK;
24(SFRMDIR)-》0:表示主模式,SSP端口,驅動SSPSFRM信號;
23(RWOT)-》0:接收和傳輸都可以;22(TRAIL)-》1:表示trailing bytes?由dma burst來處理;
21(TSRE)-》1:表示傳輸DMA sevice request?是enabled;20(RSRE)-》1:表示接收DMA service request允許
19(TINTE)-》0:表示接收超時中斷disable;18(PINTE)-》0:表示外設trail byte?中斷disable;
17:保留;16(IFS)->0:表示幀的極性由PSP的極性位決定;
15(STRF)-》0:表示傳輸FIFO(讀,寫)由SSDR_X來決定;14(EFWR)-》0:表示FIFO讀寫特別函數disable
13:10(RFT)-》0b0111:表示到達什么級別rxfifo斷言中斷;9:6(TFT)-》0111:表示TXFIFO斷言中斷級別
5:保留;4(SPH)-》0:表示在每一個幀開始之前要等一個時鐘,結束后要等0.5個時鐘;
3(SPO)-》0:表示SSPSCLK在inactive的時候是低電平;2(LBM)-》0:表示非循環模式
1(RIE)-》0:表示RXFIFO門檻到達的中斷disable,0(RIE)->0:表示接收FIFO門檻到達中斷disable
?
SSPSP_P3==__REG(0x4190002C):40200004——》0100,0000,0010,0000,0000,0000,0000,0100
31:reverved;30:28(EDMYSTOP)-》4:extended dummy stop;
27:26(EDMYSTART)-》0:extended dummy start;25(FSRT)-》0:下一幀的開始由前面的擴展STOP決定;
24:23(DMYSTOP)-》0b00:表示最后一bit傳輸完畢后保持active的clock數的延遲;22:保留
21:16(SFRMWDTH)-》0b20:表示最小位幀寬度;15:9(SFRMDLY)-》0:serial frame dealy
8:7(DMYSTRT)-》0b00:表示0clock的延遲在開始的時候;6:4(STRTDLY)-》0:start delay
3(ETDS)-》0:表示結束時的傳輸狀態為low;2(SFRMP)-》1:serial frame的極性;
1:0(SCMODE)-》0b00:data driven?下降沿,數據采樣上升沿,idle狀態,低;
?
SSTSA_P3==__REG(0x41900030):3——》0011
31:8->0:reserved;7:0(TTSA)->0b0011:表示在那個time slot里面是傳輸數據的0,不傳輸,1傳輸;
SSRSA_P3==__REG(0x41900034):3——》0011
31:8?:reserved;7:0(RTSA)-》0:表示在那個slot里面接收數據,0,不接受,1?接收;
SSACD_P3==__REG(0x4190003C):60——》0110,0000
31:8:reserved;7(SCDX8)-》0:sysclk/4產生內部audio clock,1,sysclk/8產生audio clock;
6:4(ACPS)-》0b110:PLL輸出時鐘由Audio clock dither Divider register value決定;
3(SCDB)-》0:如果SCDX8為0則scdx8決定,為1,則sysclk不分頻;
2:0(ACDS)-》0:表示clock divider select?為/1;
SSACDD_P3==__REG(0x41900040):6590040——》0000,0110,0101,1001,0000,0000,0100,0000
31:reserved;30:16(NUM)-》1625;除數(0x659)
15:12:reserved;11:0(DEN)-》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
如下圖所示:
當然更加詳細的請參閱spec;
?
第二段是平臺級別的,主要是對于DMA的初始化;
第三段是codec級別的,這里主要是對codec本身的設置,通過i2c接口對codec的寄存器操作,比如采樣率等等;
最后面還有一個poweron的函數,這個函數前面有提到,但是沒有詳細分析,這里分析一下:
首先根據事件類型,決定是關閉門序列,還是啟動門序列,我只分析啟動過程;
得到啟動序列,就開始遍歷整個序列,對于這個序列的每一個類型,查找所有的門的序列,直到有一個門的類型和當前啟動序列的類型相同,然后再根據不同的類型做不同的檢查和power:
1,如果是snd_soc_dapm_vmid則繼續,不做任何處理;
2,
A)如果是snd_soc_dapm_adc,并且其active為1,這個active在上一步已經分析過了,必須要包含這個流的名字的sname的門才會被激活,假設我們現在討論的是用pcm通道播放聲音,那么流的名字就是“Voice Playback”,所以,將置有dac3的active被設成1,這樣就避免了power on dac1,dac2,和adc了。如果這兩個條件都滿足,那么必然是“Voice Capture”了,因為只有這時候,我們才會用到adc,現在看看,如果用了adc將會啟動什么,于是調用函數is_connected_input_ep,這是一個通用遞歸函數,從名字上來說就是看是否是已經連接了輸入的門,我們只考慮adc的情況,其余的情況待會再討論,對于adc,在is_connected_input_ep函數里面,是通過遍歷所有以這個門作為sink的source門(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),input,output,mic,hp,line,spk,不做任何處理就返回了;過了這一關,開始查是否men的revert為真,如果為真,則把power取反,原來為真現在變假,于是調用snd_soc_read(micco_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并且active為1,則調用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_ep和dac的處理是一樣的,接著調用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->event(do_post_event)來進行后期處理,這里主要就是對mux進行寄存器設置,因為mux的寄存器的地址都是大于0x70+0x15的,所以它們的地址需要轉化,這個函數就是根據mux的類型,訪問不同的寄存器。
?
基本上prepare后,一切就都就緒了,只等一個trigger;而trigger的執行會在上層的alsalib調用write的函數觸發;
4.1.4??????????write的流程
?
用戶層的write到內核里面都是通過ioctl來做的,這里面會觸發trigger函數的執行,等trigger執行完以后,才會真正調用函數把用戶層的東西copy到dma分配的空間;
這里面基本上只是畫了最簡單的邏輯,其實里面非常的復雜特別是函數snd_pcm_lib_write1,這里面有同步的操作,也就是要等到有空余的空間的時候才允許寫,否則就要等待,喚醒是通過函數snd_pcm_update_hw_ptr_post來做的,這個函數會在DMA傳輸完一幀的中斷到來的時候被調用,用來更新緩沖區指針;
?
其中trigger的邏輯如下:
?
簡單的說就是啟動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調用的上層邏輯
????也就是說通過/dev下面的設備節點調用相應的ioctl,然后進入到內核的范圍;
5.1.2??????????Amixer的內核流程?????
這里只分析了控制函數為snd_soc_dapm_put_enum_double的處理邏輯,其它的都類似,而具體的應該是哪個處理函數來處理是在control的new的時候就已經確立了的,對于我們的平臺其實在表micco_dapm_widgets建立的時候就已經確立了;
為了方便后來者的調試,我這里把各個numid的對應的控制函數都列出來了,如下:
numid=1到12:snd_soc_put_volsw;
numid=13到20:snd_soc_dapm_put_enum_double
?
?
.
6.???????????????總結
Alsa驅動的架構主要是分成對上為alsalib提供接口,對下實現硬件的管理,對上的內容基本都是在sound/core目錄里面的文件來完成,而設計硬件的操作分成兩部分一部分相關與cpu這邊的是由sound/soc/pxa目錄里面的文件來完成的,另外一部分設計codec是由sound/soc/codec來完成的,這部分主要就是對codec這邊的寄存器的設置;簡單示意如下:
它復雜的地方在于用戶態的alsa lib。
7.???????????????未討論
還有一些地方沒有討論到,比如timer,不過留到以后補充吧
備注:
??????????????內核版本:2.6.21(+marvel patch)
?????? ? ? ? ???硬件平臺:pxa310+9034codec;
?作者:wylhistory
轉載請注明出處!