alsa驅(qū)動分析(2.6.21內(nèi)核)之二
4.??????????????通常的使用流程的分析
通常使用alsalib來播放聲音包括以下幾個步驟:
1,?? open,這個和oss相同,對應于alsa就是snd_pcm_open;
2,?? param設置,這個就是snd_pcm_hw_params;
3,?? 上層的alsa在設置param的成功以后或者出錯的時候恢復都需要調(diào)用snd_pcm_prepare;
4,?? write函數(shù);
現(xiàn)在一個個的來看;
4.1.1?????????open過程介紹
如下圖所示:
???????? 就是我先前說的分成三部分,先是cpu級別的,包括clock的enabe,中斷的申請,空間的申請;
???????? 然后就是平臺級別的包括DMA所需要的空間的分配等;
???????? 不過這里codec級別的沒有提供相關的函數(shù),由machine提供了一些函數(shù)主要是設置channel,格式,頻率范圍等等;
4.1.2?????????snd_pcm_hw_params流程分析
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
???????? 流程就是這樣,至于里面做的具體的事情,我覺得只需要對照spec看看就知道了,具體的寄存器設置下面有一點講解,主要是格式的設置(采樣率的設置會留到prepare的時候),至于中斷上來的時候那個更新hw_ptr函數(shù)很有用,這樣上層就可以知道數(shù)據(jù)到底寫了多少或者說還有多少空間可以寫;
?
4.1.3?????????prepare流程分析
?
當alsa層調(diào)用snd_pcm_prepare的時候會觸發(fā)驅(qū)動對應的prepare的函數(shù)執(zhí)行,如下:
可以看出基本上還是分為了三段,一段是cpu級別的,主要是對于ssp port的設置,具體設置如下:
對于voice通道,littleton_micco_voice_prepare的設置:
the sscr0 0xc0163f,sscr10xf01dc0,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 */
所以結(jié)果就相當于:
?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:每幀的時隙數(shù)
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:表示片內(nèi)的時鐘用來計算serial clock rate;5:4(FRF)-》0b11:表示psp模式用來模擬I2S協(xié)議
3:0(DSS)-》0b1111:表示16bit數(shù)據(jù)(EDSS為0)
?
SSCR1_P4 0x41A00004:? 0xf01dc0——》0000,0000,1111,0000,0001,1101,1100,0000
對于這個地址高8位也為0,
31(TTELP)-》0:表示最后一個bit傳輸(LSB)傳完后有半個時鐘處于高阻(三態(tài))狀態(tài);
30(TTE)-》0:表示傳輸信號不是三態(tài)的;29(EBCEI)-》0:bit傳輸錯誤不產(chǎn)生中斷
28(SCFR)-》0:表示SSPSCLK的時鐘信號需要連續(xù)的工作,主模式ignore;27(ECRA)-》0:表示禁止其它ssp向它發(fā)起始終請求
26(ECRB)-》0:表示同27;25(SCLKDIR)-》0:表示主模式,SSP端口,驅(qū)動SSPSCLK;
24(SFRMDIR)-》0:表示主模式,SSP端口,驅(qū)動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讀寫特別函數(shù)disable
13:10(RFT)-》0b0111:表示到達什么級別rxfifo斷言中斷;9:6(TFT)-》0111:表示TXFIFO斷言中斷級別
5:保留;4(SPH)-》0:表示在每一個幀開始之前要等一個時鐘,結(jié)束后要等0.5個時鐘;
3(SPO)-》0:表示SSPSCLK在inactive的時候是低電平;2(LBM)-》0:表示非循環(huán)模式
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數(shù)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:表示結(jié)束時的傳輸狀態(tài)為low;2(SFRMP)-》1:serial frame的極性;
1:0(SCMODE)-》0b01:data driven 上升沿,數(shù)據(jù)采樣下降沿,idle狀態(tài),低;
?
對于littleton_micco_hifi_prepare的設置:
?The sscr0 e1c0003f,sscr1701dc0,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 DitherDivider Register */
?
SSCR0_P3==__REG(0x41900000):e1c0003f——》1110,0001,1100,0000,0000,0000,0011,1111
31 (MOD)-》1:網(wǎng)絡模式;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:每幀的時隙數(shù)
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:表示片內(nèi)的時鐘用來計算serial clock rate;5:4(FRF)-》0b11:表示psp模式用來模擬I2S協(xié)議
3:0(DSS)-》0b1111:表示16bit數(shù)據(jù)(EDSS為0)
?
SSCR1_P3==__REG(0x41900004):701dc0——》0000,0000,0111,0000,0001,1101,1100,0000
31(TTELP)-》0:表示最后一個bit傳輸(LSB)傳完后有半個時鐘處于高阻(三態(tài))狀態(tài);
30(TTE)-》0:表示傳輸信號不是三態(tài)的;29(EBCEI)-》0:bit傳輸錯誤不產(chǎn)生中斷
28(SCFR)-》0:表示SSPSCLK的時鐘信號需要連續(xù)的工作,主模式ignore;27(ECRA)-》0:表示禁止其它ssp向它發(fā)起始終請求
26(ECRB)-》0:表示同27;25(SCLKDIR)-》0:表示主模式,SSP端口,驅(qū)動SSPSCLK;
24(SFRMDIR)-》0:表示主模式,SSP端口,驅(qū)動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讀寫特別函數(shù)disable
13:10(RFT)-》0b0111:表示到達什么級別rxfifo斷言中斷;9:6(TFT)-》0111:表示TXFIFO斷言中斷級別
5:保留;4(SPH)-》0:表示在每一個幀開始之前要等一個時鐘,結(jié)束后要等0.5個時鐘;
3(SPO)-》0:表示SSPSCLK在inactive的時候是低電平;2(LBM)-》0:表示非循環(huán)模式
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數(shù)的延遲;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:表示結(jié)束時的傳輸狀態(tài)為low;2(SFRMP)-》1:serial frame的極性;
1:0(SCMODE)-》0b00:data driven 下降沿,數(shù)據(jù)采樣上升沿,idle狀態(tài),低;
?
SSTSA_P3==__REG(0x41900030):3——》0011
31:8->0:reserved;7:0(TTSA)->0b0011:表示在那個time slot里面是傳輸數(shù)據(jù)的0,不傳輸,1傳輸;
SSRSA_P3==__REG(0x41900034):3——》0011
31:8 :reserved;7:0(RTSA)-》0:表示在那個slot里面接收數(shù)據(jù),0,不接受,1 接收;
SSACD_P3==__REG(0x4190003C):60——》0110,0000
31:8:reserved;7(SCDX8)-》0:sysclk/4產(chǎn)生內(nèi)部audio clock,1,sysclk/8產(chǎn)生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;除數(shù)(0x659)
15:12:reserved;11:0(DEN)-》64:被除數(shù)
比如我們的板子上是這樣計算這些值的:
比如,在我們的機子上的一個實例是這樣的,
那么第一步取得采樣率:48K,它也就是Sync clock;
第二步球的bit率:48X64=3.072M
第三步求的sysclk:這個根據(jù)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的函數(shù),這個函數(shù)前面有提到,但是沒有詳細分析,這里分析一下:
首先根據(jù)事件類型,決定是關閉門序列,還是啟動門序列,我只分析啟動過程;
得到啟動序列,就開始遍歷整個序列,對于這個序列的每一個類型,查找所有的門的序列,直到有一個門的類型和當前啟動序列的類型相同,然后再根據(jù)不同的類型做不同的檢查和power:
1,如果是snd_soc_dapm_vmid則繼續(xù),不做任何處理;
2,
A)如果是snd_soc_dapm_adc,并且其active為1,這個active在上一步已經(jīng)分析過了,必須要包含這個流的名字的sname的門才會被激活,假設我們現(xiàn)在討論的是用pcm通道播放聲音,那么流的名字就是“Voice Playback”,所以,將置有dac3的active被設成1,這樣就避免了power on dac1,dac2,和adc了。如果這兩個條件都滿足,那么必然是“Voice Capture”了,因為只有這時候,我們才會用到adc,現(xiàn)在看看,如果用了adc將會啟動什么,于是調(diào)用函數(shù)is_connected_input_ep,這是一個通用遞歸函數(shù),從名字上來說就是看是否是已經(jīng)連接了輸入的門,我們只考慮adc的情況,其余的情況待會再討論,對于adc,在is_connected_input_ep函數(shù)里面,是通過遍歷所有以這個門作為sink的source門(list_for_each_entry(path, &widget->sources,list_sink),可以看到,這里的最后一個參數(shù)是list_sink,而第二個參數(shù)卻是widget->sources,這個原因我在“門連接分析”頁里面已經(jīng)分析過了,總之sources就表示這個門的sources列表,而sinks就是這個門的sink列表),通過遞歸調(diào)用is_connected_input_ep來查看這些source門是否其中有一個是連通的,返回的是所有是否連通的和(聯(lián)通為1,否則為0),所以返回的結(jié)果可能是大于1的數(shù),表示不只一個源是聯(lián)通的。
B)如果這個函數(shù)返回為真則表示此adc是聯(lián)通的,于是調(diào)用dapm_update_bits來處理,這個函數(shù)過對mux(它的reg<0),input,output,mic,hp,line,spk,不做任何處理就返回了;過了這一關,開始查是否men的revert為真,如果為真,則把power取反,原來為真現(xiàn)在變假,于是調(diào)用snd_soc_read(micco_soc_read)開始讀這個寄存器的值(注意,這里讀的值是可能和物理上的這個寄存器的值不一樣的,這里讀的值是cache里面的值),讀出來后強制把1<
3,
A)如果此類型是snd_soc_dapm_dac并且active為1,則調(diào)用is_connected_output_ep來取得是否要power,下面來看看函數(shù)is_connected_output_ep,這也是一個通用的判斷是否有連接到輸出的遞歸函數(shù),我們只分析dac的情況,list_for_each_entry(path, &widget->sinks,list_source),上面已經(jīng)講過,這里實際上查的是這個門的所有的sink列表,通過遞歸調(diào)用is_connected_output_ep來看是否它的sink是聯(lián)通的,只要有一條路是聯(lián)通的,則power為真。
B)返回后調(diào)用dapm_update_bits來處理,上面已經(jīng)分析過了,這個函數(shù)就是判斷是否需要設置此門的寄存器的1<<shift位。
4,如果此類型是snd_soc_dapm_pga,則調(diào)用is_connected_input_ep來判斷是否聯(lián)通輸入,再調(diào)用is_connected_output_ep判斷是否聯(lián)通輸出,對于pgais_connected_input_ep函數(shù)的處理和adc是一樣的,對于is_connected_output_ep和dac的處理是一樣的,接著調(diào)用dapm_set_pga,根據(jù)power的值決定是mute pga還是啟用pga,但是就我打印的結(jié)果來看,基本上這個函數(shù)所起的作用為0,因為對于pga的門似乎都沒有設置相應的control,最后調(diào)用dapm_update_bits,設置power 位。
5,對于other widget,這里在我們的平臺上多半是指mux,首先調(diào)用is_connected_input_ep判斷是否連接輸入,再調(diào)用is_connected_output_ep判斷是否有輸出,調(diào)用dapm_update_bits位設置power 位,最后調(diào)用w->event(do_post_event)來進行后期處理,這里主要就是對mux進行寄存器設置,因為mux的寄存器的地址都是大于0x70+0x15的,所以它們的地址需要轉(zhuǎn)化,這個函數(shù)就是根據(jù)mux的類型,訪問不同的寄存器。
?
基本上prepare后,一切就都就緒了,只等一個trigger;而trigger的執(zhí)行會在上層的alsalib調(diào)用write的函數(shù)觸發(fā);
?
?
4.1.4?????????write的流程
?
用戶層的write到內(nèi)核里面都是通過ioctl來做的,這里面會觸發(fā)trigger函數(shù)的執(zhí)行,等trigger執(zhí)行完以后,才會真正調(diào)用函數(shù)把用戶層的東西copy到dma分配的空間;
這里面基本上只是畫了最簡單的邏輯,其實里面非常的復雜特別是函數(shù)snd_pcm_lib_write1,這里面有同步的操作,也就是要等到有空余的空間的時候才允許寫,否則就要等待,喚醒是通過函數(shù)snd_pcm_update_hw_ptr_post來做的,這個函數(shù)會在DMA傳輸完一幀的中斷到來的時候被調(diào)用,用來更新緩沖區(qū)指針;
?
其中trigger的邏輯如下:
?
簡單的說就是啟動DMA,enable ssp口;
?
?
?
?
?
?
4.1.5?????????使用流程的總結(jié)t
???????? 簡單總結(jié)一下,用戶的使用流程;
???????? A,調(diào)用snd_pcm_open打開設備節(jié)點對應的pcm流的substream也就是錄音或者play;
B,調(diào)用snd_pcm_hw_params設置硬件參數(shù),包括格式,通道,采樣率,DMA空間的分配,中斷的申請等等,這里面會調(diào)用prepare函數(shù)使硬件準備好硬件,包括codec的寄存器設置,各種路徑的建立,門的power on等;
C,調(diào)用write函數(shù)實現(xiàn)把數(shù)據(jù)寫到設備里面去,這里會觸發(fā)trigger函數(shù)也就是DMA的啟動,SSP端口的啟動等。
??????
?
5.??????????????Amixer調(diào)用的相關邏輯
我們的audio controller所調(diào)用的驅(qū)動的接口都是amixer的cset,cget,所以有必要分析一下它的邏輯:
5.1.1?????????Amixer調(diào)用的上層邏輯
??? 也就是說通過/dev下面的設備節(jié)點調(diào)用相應的ioctl,然后進入到內(nèi)核的范圍;
5.1.2?????????Amixer的內(nèi)核流程?????
這里只分析了控制函數(shù)為snd_soc_dapm_put_enum_double的處理邏輯,其它的都類似,而具體的應該是哪個處理函數(shù)來處理是在control的new的時候就已經(jīng)確立了的,對于我們的平臺其實在表micco_dapm_widgets建立的時候就已經(jīng)確立了;
為了方便后來者的調(diào)試,我這里把各個numid的對應的控制函數(shù)都列出來了,如下:
numid=1到12:snd_soc_put_volsw;
numid=13到20:snd_soc_dapm_put_enum_double
?
?
.
6.??????????????總結(jié)
Alsa驅(qū)動的架構(gòu)主要是分成對上為alsalib提供接口,對下實現(xiàn)硬件的管理,對上的內(nèi)容基本都是在sound/core目錄里面的文件來完成,而設計硬件的操作分成兩部分一部分相關與cpu這邊的是由sound/soc/pxa目錄里面的文件來完成的,另外一部分設計codec是由sound/soc/codec來完成的,這部分主要就是對codec這邊的寄存器的設置;簡單示意如下:
?
它復雜的地方在于用戶態(tài)的alsa lib。
?
?
?
?
7.??????????????未討論
還有一些地方?jīng)]有討論到,比如timer,不過留到以后補充吧;
?
?
?
?
?
備注:
????????????? 內(nèi)核版本:2.6.21(+marvel patch)
?????? ? ? ? ?? 硬件平臺:pxa310+9034codec;
?作者:wylhistory