当前位置:首页 > > 充电吧
[导读]4.              通常的使用流程的分析通常使用alsalib来播放声音包括以下几个步骤:1,   open,这个和oss相同,对应于alsa就是snd_pcm_open;2,   par

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,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 */

所以结果就相当于:

 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,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:网络模式;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<

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判断是否联通输出,对于pgais_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



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭