alsa 驱动介绍
Machine
以装配有CS4270的一款android 智能电视的为例
/sound/soc/samsung/exynos.c
Platform
以Samsung cpu exynos4412为例
/sound/soc/samsung/
Codec
以wolfson的Codec芯片cs4270为例
/sound/soc/codecs/cs4270.c
ALSA 框架介绍
Alsa 太多太杂,很难整理的规整,只能看到哪里写到哪里
ASoC被分为Machine,Platform和Codec三大部件,
Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送
给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。在具体实现上,ASoC有把Platform驱动分为两个部分:snd_soc_platform_driver
和snd_soc_dai_driver。其中,platform_driver负责管理音频数据,把音频数据通过dma或其他操作传送至cpu dai中,dai_driver则主要完成cpu一侧的
dai的参数配置,同时也会通过一定的途径把必要的dma等参数与snd_soc_platform_driver进行交互。
Machine 是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实
现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。
Platform 一般是指某一个SoC平台,比如pxaxxx,s3cxxxx,omapxxx等等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中。实际上,把Platform认为是某个SoC更好理解。
Codec 字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个
输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec通常通过I2C对
内部的寄存器进行控制。
Machine驱动的初始化,codec和dai的注册,都会调用snd_soc_instantiate_cards()进行一次声卡和codec,dai,platform的匹配绑定过程,这里所说的
绑定,正如Machine驱动一文中所描述,就是通过3个全局链表,按名字进行匹配,把匹配的codec,dai和platform实例赋值给声卡每对dai的snd_soc_pcm_runtime变量中。一旦绑定成功,将会使得codec和dai驱动的probe回调被调用
alsa架构的数据交互,是通过对PCM设备的操作来完成的, PCM设备分成playback和capture两个stream, 每个stream底下有N个substream
alsa驱动最底层需要调试的有三块: DMA部分,IIS驱动部分,codec部分
IIS介绍
A)I2S有四根线,
1.串行时钟SCLK,也叫位时钟(BCLK),即对应数字音频的每一位数据,SCLK都有1个脉冲。SCLK的频率=2×采样频率×采样位数。
2. 帧时钟LRCK,(也称WS),用于切换左右声道的数据。LRCK为“1”表示正在传输的是右声道的数据,为“0”则表示正在传输的是左声道的数据。LRCK
的频率等于采样频率。
3.串行数据SDATA,就是用二进制补码表示的音频数据。
4.有时为了使系统间能够更好地同步,还需要另外传输一个信号MCLK,称为主时钟,也叫系统时钟(Sys Clock),是采样频率的256倍或384倍。
B)声音数据DAT一般在CLK的上升沿进行采样,有些DAC也是可以调的。每个声道里面可以容纳的CLK数必须多于数据的位数,多出来的时钟和数据DAC会丢弃不用,比如16bit采样的声音数据当一个声道是32个CLK且left-justify的时候,后面十六个时钟的数据会被DAC丢掉,不影响的。
C)I2S数据的格式分I2S, Left-justify, Right-justify。三种格式的区别在于声音数据与WS的对应关系:
1 . I2S模式DAT的MSB在WS变化后的第二个上升沿开始传输
2. Left-justify模式DAT的MSB在WS变化后的第一个上升沿开始传输
3. Right-justify模式DAT的LSB在WS即将变换到下一声道前的最后一个时钟传输
I2S部分涉及的几个频率:
* 输出采样频率 fs = 44.1KHz. (也有其它fs的音源, 但加了resampler后, 都变成44.1KHz输出了). 这是个关键频率.
* LRCLK, 就等于fs. (L/R声道信号)
* BCLK = 32倍fs = 1411.2KHz = 1.4112MHz. (bit clock). 2声道16bit, 故32倍fs. 若2声道24bit, 则48倍fs.
* MCLK是整个audio模块的工作频率, 通常选fs的256, 384, 512倍. 比如: 256倍fs = 11289.6KHz = 11.2896MHz.
从频率设置来说, MCLK是个主要频率, 它是整个audio模块的工作频率.
那么, 从软件来说要设置两个方面的寄存器: 一是该PLL从晶振频率如何得到PLLout频率(比如P/M/S/k). 二是PLLout如何分频得到audio部分的MCLK.
IIS驱动部分最重要的就是注册以下钩子函数,挂到了alsa驱动上
1 2 3 4 5 6 7 8 9 10 |
|
codec芯片介绍
cs4270的驱动要设置的参数有:
静音,传输模式,比特位长度,时钟主从模式,音量大小
cs4270驱动里面定义了snd_soc_dai_driver结构成员,里面定义了playback和capture两个substream,同事也挂了一个snd_soc_dai_ops结构体,里面全
是操作函数指针。
alsa上面一层层的最终会调用到这些指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
DMA介绍
IIS总线是慢速总线,相对于CPU来说,太慢。所以采用DMA的方式最能节省CPU性能。
PCM playback的时候,DMA目的地址是IIS FIFO寄存器。源地址是存放PCM数据的内存。
DMA的驱动采用了linux pl330的驱动架构,采用中断的方式来触发后续DMA。
IIS中通过DMA的方式写入FIFO寄存器,在DMA的驱动中挂接了一个回调函数audio_buffdone。DMA完成后,回函数调用,刷新alsa的环,便于下一次DMA
DMA的目的地址,就是IIS发送寄存器的地址。源地址,就是申请的DMA buffer,只不过DMAbuffer被映射成了一个环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
|
DMA部分主要通过注册以下钩子函数来挂到alsa驱动里面
1 2 3 4 5 6 7 8 9 10 11 |
|
alsa数据读写简介
播放时,应用程序把音频数据源源不断地写入dma buffer中,然后相应platform的dma操作则不停地从该buffer中取出数据,经dai送往codec中。录音时
则正好相反,codec源源不断地把A/D转换好的音频数据经过dai送入dma buffer中,而应用程序则不断地从该buffer中读走音频数据
以播放(playback)为例,我现在知道至少有3个途径可以完成对dma buffer的写入:
应用程序调用alsa-lib的snd_pcm_writei、snd_pcm_writen函数;
应用程序使用ioctl:SNDRV_PCM_IOCTL_WRITEI_FRAMES或SNDRV_PCM_IOCTL_WRITEN_FRAMES;
应用程序使用alsa-lib的snd_pcm_mmap_begin/snd_pcm_mmap_commit;
以上几种方式最终把数据写入dma buffer中,然后修改runtime->control->appl_ptr的值。
播放过程中,通常会配置成每一个period size生成一个dma中断,中断处理函数最重要的任务就是:
更新dma的硬件的当前位置,该数值通常保存在runtime->private_data中;
调用snd_pcm_period_elapsed函数,该函数会进一步调用snd_pcm_update_hw_ptr0函数更新上述所说的4个缓冲区管理字段,然后唤醒相应的等待进程;
这个中断实际上在DMA驱动内部,给DMA驱动一个回调函数就可以了。就是我们前面说的audio_buffdone
Playback时数据流向
/sound/soc/samsung/里面,写入到DMA源buffer时,
用的是mmap的写入方式,不是采用的snd_pcm_hw_writei
几个关键点:
1,Pos计算方式
dma_pointer里面, res = prtd->dma_pos - prtd->dma_start;
此pos就是在DMA 源buffer中的位置
2,dma的初始化
dma_enqueue里面,把DMA源buffer切成period_size大小,挂到DMA队列里
每次period_size传输完了,就会调用audio_buffdone终端处理函数,更新dma_pos
同时audio_buffdone也会调用snd_pcm_update_hw_ptr0重新计算hw_ptr,从而计算是不是有足够的可用空间,来唤醒等待的poll
从alsalib来看,要先调用snd_pcm_start,触发DMA操作开始
snd_pcm_start---SNDRV_PCM_IOCTL_START---snd_pcm_action_lock_irq--snd_pcm_do_start----dma_trigger
每次写入mmap buffer之前,要先snd_pcm_wait,等待有足够可用的空间.
snd_pcm_wait----snd_pcm_wait_nocheck----poll-----snd_pcm_playback_poll---poll_wait
然后调用snd_pcm_mmap_begin获取mmap 内存
然后写入,然后调用snd_pcm_mmap_commit做一下alsalib和驱动里面的环同步
DMA实际上是在不停的DMA的。有空闲的了,上层就不用wait了,就会写入了
几个典型调用流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
转自:https://www.2cto.com/kf/201606/520305.html