一、ASoC简介
ASoC(Alsa System on Chaip),也称为移动设备中的ALSA。是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。ASoC不能单独存在,他只是建立在标准ALSA驱动上的一个它必须和标准的ALSA驱动框架相结合才能工作。
二、ASoC硬件架构
嵌入式设备中的音频系统(ASoC)可以被划分为板载硬件(Machine)、Soc(Platform)、Codec三大部分,如下图所示:
Machine:是指某款单板,包含特定的外设,为CPU、Codec提供了一个载体,Machine驱动几乎是不可重用的。
Platform:一般是指某一个SoC平台,可以理解为某款Soc,具有I2S,AC97音频接口等,内部有时钟,DMA单元用于传输音频数据。
Codec:音频编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),一般Soc可通过I2C来控制codec芯片。
三、ASoC软件架构
对应于硬件,软件驱动上也分Machine驱动、Platform驱动、Codec驱动三大部分,其中Platform驱动用于驱动Soc端的音频相关模块,Codec驱动用于驱动音频编解码器,的Machine驱动负责Platform驱动和Codec驱动之间的耦合以及部分和设备或板子特定的代码。
Platform驱动:
只与特定的Soc有关,实现Soc的音频DMA驱动和Soc端的dai接口驱动,注册的所有platform驱动都会挂载在全局platform_list链表上。注册的Soc端的cpu_dai接口驱动挂载在全局dai_list上。只要指定了SoC,就会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做
任何的改动,就可以用在不同的Machine中。
Codec驱动:
只与Codec编解码器驱动有关,与Soc和Machine无关。所有注册的Codec驱动都会挂载在全局codec_list链表上,注册的Codec端的dai接口驱动挂载在全局dai_list上。Codec和Platform一样,要实现为可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec驱动通常通过I2C对编解码芯片进行控制。
Machine驱动:
用于将Platform驱动和Codec驱动关联在一起,通过snd_soc_dai_link中的名字指定使用哪个Platform驱动,使用哪个Soc端的dai接口,使用哪个Codec驱动,使用Codec上的哪个dai接口。同时也做一些特定于单板的操作。只要是与Soc和Codec驱动无关的操作,都应该放在Machine驱动中。Machine驱动中注册的and_codec_card放在全局card_list链表上。ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等。
涉及的三个驱动中涉及的全局链表
//soc-core.c中 struct list_head card_list; /*Machine驱动注册的snd_codec_card放在这个链表上*/ struct list_head dai_list; /*soundsocsamsungI2s.c中使用snd_soc_register_dai注册的cpu_dai会放在这上面,codec中注册的codec端的dai也会挂在这上面*/ struct list_head platform_list; /*注册的Soc的platform驱动在这个链表上*/ struct list_head codec_list; /*音频编解码芯片驱动注册的codec挂载在这个链表上*/
snd_soc_card是整个ASoc的核心结构体。
四、ASoC架构中的Machine驱动
1.核心结构体 snd_soc_dai_link snd_soc_card 2.注册 //设备端: struct snd_soc_dai_link tiny4412_wm8960_dai_link; struct snd_soc_card myalsa_card; tiny4412_wm8960_init platform_set_drvdata(&asoc_dev, &myalsa_card); //驱动端: soc_probe(struct platform_device *pdev) //soc-core.c snd_soc_register_card(card);
Machine驱动负责处理单板特有的一些控件和音频事件。单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。snd_soc_dai_link则负责关联Platform和Codec。
snd_soc_card代表着Machine驱动中要注册的声卡设备,(也就是tiny4412_wm8960.c,它只是平台设备的设备端,在设备端构造snd_soc_card,在驱动端
注册这个card,驱动端是soc-core.c)
snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。当然还要实现连接Platform和Codec的dai_link对应的ops实现。
通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现,包括调用soc_bind_dai_link()来解析struct snd_soc_dai_link来关联各个设备。
Machine驱动的初始化,codec和dai的注册,都会调用snd_soc_instantiate_cards()进行一次声卡和codec,dai,platform的匹配绑定过程,这里所说的绑定,正如Machine驱动一文中所描述,就是通过3个全局链表,按名字进行匹配,把匹配的codec,dai和platform实例赋值给声卡中用于描述每组关联关系的snd_soc_pcm_runtime结构中。一旦通过Machine驱动将Codec、Platform绑定成功,就会依次调用各个组件的probe()回调函数,调用关系依次是:
CPU dai接口的:snd_soc_dai_driver.probe()
Codec驱动的:snd_soc_codec_driver.probe()
Platform驱动的:snd_soc_platform_driver.probe()
Codec dai接口的:snd_soc_dai_driver.probe()
详细调用过程见soc_probe_dai_link()
snd_soc_instantiate_card(struct snd_soc_card *card) //通过snd_soc_dai_link中的name构建关联关系 soc_bind_dai_link(card, i); //对每一条链路调用各个组件的probe() soc_probe_dai_link(struct snd_soc_card *card, int num) //如果codec驱动有多个codec_conf配置,machine驱动可以通过snd_soc_card选择一个 //走上了ALSA的老路了 snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); //若Machine驱动中指定了card的probe()则调用 card->probe(card); snd_soc_add_controls snd_soc_dapm_add_routes snd_card_register
五、ASoC架构中的Codec驱动
1.核心结构体 snd_soc_codec snd_soc_codec_driver snd_soc_dai snd_soc_dai_driver 2.注册 //wm8960.c snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8960, &wm8960_dai, 1);
ASoC中的一个重要设计原则就是要求Codec驱动是平台无关的,它包含了一些音频的控件(Controls),音频接口,DAMP(动态音频电源管理)的定义和某些Codec IO功能。为了保证硬件无关性,任何特定于平台和机器的代码都要移到Platform和Machine驱动中。
所有的Codec驱动都要提供以下特性:
Codec DAI 和 PCM的配置信息;
Codec的IO控制方式(I2C,SPI等);
Mixer和其他的音频控件;
Codec的ALSA音频操作接口;
必要时,也可以提供以下功能:
DAPM描述信息;
DAPM事件处理程序;
DAC数字静音控制
snd_soc_codec/snd_soc_codec_driver代表了Codec驱动(也就是wm8960.c),snd_soc_register_codec()同时注册了codec和codec端的dai。
六、ASoC架构中的Platform驱动
1.核心结构体 snd_soc_platform snd_soc_platform_driver snd_soc_dai snd_soc_dai_driver 2.注册 注册platform驱动: //dma.c snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform); 注册cpu_dai驱动: //i2s.c i2s_alloc_dai(pdev, false); snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
它包含了该SoC平台的音频DMA和音频接口的配置和控制(I2S,PCM,AC97等等);它也不能包含任何与单板相关的代码。
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进行交互。
CPU端的dai驱动通常对应Soc的一个或几个I2S/PCM接口的驱动
七、总结
注意各个驱动的分工,就大致知道每个模块需要提供哪些钩子函数,这钩子函数应该由谁调用。比如Codec应该提供自己的各个控制接口给Machine驱动程序调用,Platform驱动也应该提供自己的驱动接口给Machine驱动调用。
自己编写驱动的时候需要提供Machine驱动的设备端,若Codec驱动还没有,则还需要提供codec的驱动。一般platform驱动是Soc厂商已经提供好的,Codec驱动是音频编解码芯片供应商提供好的。因此,我们的工作就是编写Machine驱动和适配Codec芯片到我们的单板上。
参考:
Linux ALSA声卡驱动之五:移动设备中的ALSA: https://blog.csdn.net/michaelcao1980/article/details/78228630
Linux ALSA声卡驱动之六:ASoC架构中的Machine: https://blog.csdn.net/droidphone/article/details/7231605
Linux ALSA声卡驱动之七:ASoC架构中的Codec: https://blog.csdn.net/DroidPhone/article/details/7283833
Linux ALSA声卡驱动之八:ASoC架构中的Platform: https://blog.csdn.net/droidphone/article/details/7316061