Linux soc声卡构架分析

Linux soc声卡构架分析(DMA从豆丁上看到的,分析soc声卡初始化过程的。分析的很详细



S3C2440为例进行分析,对应的文件linux-2.6.32.2/sound/soc/s3c24xx/s3c24xx_uda134x.c

其中module_init入口内容为:

359行是一个平台驱动的注册函数,注册的驱动是s3c24xx_uda134x_driver。内容如下:

348 static structplatform_driver s3c24xx_uda134x_driver = {

由上面的name="s3c24xx_uda134x"可知,这个驱动对应的平台设备早在系统启动时在dev_init中注册进来了,所以接下来的事情就是直接调用probe方法。

305-318行就是设置udal34x要用的gpio引脚的功能。

320-334行是融入soc-core的关键,320行申请的platform_devsoc-coredevice结构。对应驱动的名称也应该是"soc-audio"330行是问题的关键,s3c24xx_uda134x_snd_device注册并与soc-core驱动进行匹配。相关的内容在linux/sound/soc/soc-core.c文件中。

Soc-core的入口是module_initsnd_soc_init)。

2557行是soc-core驱动注册的核心,soc_driver的内容如下:

Soc_drivers3c24xx_uda134x_snd_device的名称匹配,满足mach的基本条件。后面会调用soc_driverprobe方法进行驱动的进一步枚举。

928行是获取平台驱动数据,这里就是前面设置的:

platform_set_drvdata(s3c24xx_uda134x_snd_device,&s3c24xx_uda134x_snd_devdata);

s3c24xx_uda134x_snd_devdata的数据类型为structsnd_soc_device

Structsnd_soc_device描述了一个soc子系统中的soc设备其成员如下:

首先主要关注structsnd_soc_card *card;这个数据结构,后面的内容就是这张声卡实例化。

接下来进入到snd_soc_register_card(card);函数,这事soc_probe的核心。

函数首先初始化一个list头,并将当前card加入到card_list全局的一张soc声卡链表中。然后就调用snd_soc_instantiate_cards();来具体实例化一张声卡。

974行遍历card_list然后调用snd_soc_instantiate_card实例化。

850行如果声卡已经是实例化了的就直接返回。

854行遍历platform_list链表,这个platform_list是个全局变量初始化是通过linux/sound/soc/s3c24xx/s3c24xx-pcm.c中的初始化完成的,内容如下:

snd_soc_register_platform目标是注册一个soc_platform结构到platform_list链表中供后文使用。这里对应得平台是s3c24xx_soc_platform,如下:

snd_soc_register_platform完成的工作主要是将s3c24xx_soc_platform加入到platform_list,同时重新扫描card链,看是否有卡兼容当前soc_platform。具体代码如下:

2440行重新回到了snd_soc_instantiate_cards()中,解释清楚platform_list以后继续会爱到snd_soc_instantiate_cards函数。

855行匹配两个平台是否一致。一路走来

card->platform实际上就是s3c24xx_uda134x_snd_devdata中的snd_soc_s3c24xx_uda134x指向的platform

从上面的结构不难看出card->platform=&s3c24xx_soc_platform,与之前注册的一至,found=1,跳出搜索,反之如果没有找到匹配的目标,表示platform还尚未注册进来,一直等到platform注册再次调用instantial­_card来实现平台的匹配。

找完platform,接下来进入数字音频接口的匹配也就是daidigitalaudio interface),整个dai匹配的过程与platform的匹配过程相似的,dai_list注册的内容如下:

snd_soc_register_dai注册数字接口的内容如下:

2354-2355行是如果dai没有注册操作方法,那么就使用默认的dai设置方法。这里注册的dai接口内容如下:

snd_soc_register_platform一样,snd_soc_register_dai同样调用了snd_soc_instantiate_cards()来重新实例化card

另外需要注意的是structsnd_soc_card结构中的structsnd_soc_dai_link实际上关联链接的是cpu音频接口与codec编解码接口。内容如下:

很明显structsnd_soc_dai *codec_dai;structsnd_soc_dai *cpu_dai;分别指向了不同的音频接口,这里card->dai_link[i].cpu_dai=&s3c24xx_i2s_dai指的是cpui2s接口,与刚注册的相同。所以接口匹配,注意snd_soc_instantiate_card879行是关于AC97声卡的配置,这里略过。

883-886行是关于数字编解码接口的相关信息,card->dai_link[i].codec_dai->ops内容如下:

894-907行是不支持AC97标准的的codec相关的处理,这里还是使用了dai_list链表。Codec的注册在/sound/soc/codecs/uda134x.c中的module_init(uda134x_init);

codec_dai的内容如下:

911行开始,是真正instantiating的过程,下面一一分析:

914行如果cardprobe方法,则直接调用。这里没有提供也就跳过了。

920-927行是cpu_dai接口的枚举过程,对应的函数在linux/sound/soc/s3c24xx/s3c24xx-i2s.c中的s2c24xx_i2s_probe,内容如下:

这个函数主要是针对s3c2440I2S内部寄存器的配置。

394行重映射内部寄存器,

398-404行是设置系统时钟。

407-412行是设置I2SGPIO功能,使之支持I2S

413行使能I2S功能

415行函数实现如下:

函数是对I2S内部的控制寄存器的设置。

回到snd_soc_instantiate_card函数,接下来是codec_devprobe方法的调用,codec_dev是相对cpu_dev的软解码设备,这个probe方法后面分析,接下来把snd_soc_instantiate_card分析完。函数的最后就是platformprobe方法的调用,最后是初始化一个延时等待队列。一切工作完成以后card->instantiated= 1;完成卡的instantial工作。

目光转向codec_devprobe方法

代码位于linux/sound/soc/codecs/uda134x.c

在进入代码分析之前,首先关注一下函数的参数,这里的pdev实际上就是指向S3C24xx-uda134x.c中的&s3c24xx_uda134x_snd_device。当然471socdev也就是指向staticstruct snd_soc_device s3c24xx_uda134x_snd_devdata = {

.card = &snd_soc_s3c24xx_uda134x,

.codec_dev = &soc_codec_dev_uda134x,

.codec_data = &s3c24xx_uda134x,

};

这里474codec_setup_data也就是指向s3c24xx_uda134x,其实质是

L3pinuda134x控制数据接口,后面用到了再详细分析。

499-503行是为soc_codec分配空间。

510kmemdupkmalloc的一个变种,他不仅实现内存的分配,同时能为其赋值,这里uda134x_reg是内部寄存器缓存的情况,最终复制到reg_codec中。

524525行是读写uda134x寄存器的方法,但是read只是对寄存器缓存变量的读取,具体的函数实现后面谈到的时候在详细分析。

537uda134x_reset的过程如下:

148行正是前面所说的codec->read。内容如下:

从代码中我们可以看到,读数据的过程实际上就是从之前uda134x_reg变量中读取的数据,从注释上也不难看出,codec不支持内部寄存器数据的读取。

回到uda134x_resetuda134x_write也正是前面codec->write提供的方法。在分析具体内容之前不妨从整体上观察一下函数所做的操作,reset首先置位状态寄存器0的第六位,然后延时一段时间后清零该位,完成复位过程。uda134x_write写硬件的过程如下:

109行是将改变值写入reg_cache也就是前面读函数缓存的变量。

138行是L3引脚想uda134x写入数据的过程,内容如下:

很明显这里是对GPIO口按位操作的过程,实际在硬件连接的过程中L3引脚包含clkdatamode引脚。structl3_pins *adap的内容前面已经贴出来过,这里以setclk为例看看是如何接口的,实际就是对GPIO的操作。

最终s3c24xx_uda134x_l3_pins关联到了

回到uda134x_probe函数中….

540snd_soc_new_pcms是重头戏,注册了一个新的基于pcm的声卡。重新回到了soc_core.c文件。

1431snd_card_create注册一张声卡,很容易发现,名字中少了soc的字符。这里调用的是sound/core中的函数。

160行分配一个snd_card结构,紧接着的一大段代码都是在为card寻找自己的坐标,这里重点关注220snd_ctl_createsnd_ctl_create中定义了snddevice的操作方法,如下:

同时会调用snd_device_newSnd_device_new会重新分配一个snd_device结构,并初始化后链接到snd_card的链表中来。

回到snd_card_create中来225snd_info_card_createprocfs等相关的处理。接着回到snd_soc_new_pcms

1446soc_new_pcm是建立cpucodecdai接口的核心。内容如下:

1094-1097行分别代表了设备具有的播放和录音功能。

1099snd_pcm_new函数的内容如下:

在分析函数之前首先来关注一下structsnd_pcm结构,

其中structsnd_pcm_str streams[2]分别用于playbackcapture,具体后面再说。

721行利用函数snd_pcm_new_stream新建一个playback流。

672 atomic_set(&substream->mmap_count,0);

这个函数主要是为structsnd_pcm_strstreams[]建立substream,其中pstr->substream_count记录了substream的数量,pstr->substream建立了一条substream的链表,使得支持playback的数据流链接到链表中来。632,658行等主要为proc文件夹服务的信息。

回到snd_pcm_new函数的731snd_device_new,这个函数之前已经见到过了,只是当时新建的snd_device是属于controldevice,而这里建立的是pcmdevice,当然不同的device所使用的操作方法是不一样的。

回到soc_new_pcm函数中

1110-1116行就是为这个soc_pcm_ops赋值了,所赋值的内容如下:

1118-1122行就是为刚才初始化的substream制定相应的操作方法,也就是使之指向s3c24xx_pcm_ops

1124行这是新建pcm数据流过程中初始化硬件设置的部分,关联DMA相关的一些类初始化,这个将在后面与硬件构架相关的内容一起分析,这里贴出关联的数据结构。

new相对应的是pcm_free函数。这个工作以后程序重新回到snd_soc_new_pcms,函数中1445行的for(i = 0; i < card->num_links; i++)循环为soc_card所有的连接建立pcm流。接着就返回到了

546-560行是关于card控制信息的注册,pcm是音频数据流的通道,对于像音量等等信息就是由controls来管理了,具体的内容相对比较简单不在详细分析。

最后的最后567snd_soc_init_card是设备注册过程的大结局,内容如下:

重点关注1498snd_register_card,最关键的是其中调用了snd_device_register_all,内容如下:

192行会遍历card中注册的devices最终通过调用其注册函数,在这里主要有两个设备注册进来了,分别是devicecontrolpcmdevice。首先来看device_control注册是利用snd_ctl_create函数实现的,注册的ops内容如下:

由此可见snd_ctl_dev_register函数会被调用。

这里主要是关注snd_register_device函数,内容如下:

实际调用snd_register_device_for_dev

280行之前就是为这个device找到一个从设备号,然后281行就调用device_create来创建设备节点。完成control_device的初始化工作。

与之类似的pcmdevice则是通过

中的snd_pcm_dev_register来创建设备节点。

989行的snd_register_device_for_dev就完成了pcmdevice的设备创建工作,这里所用的classsound_class,其注册的地方在/linux/sound/sound-core.c中这是声卡初始化的入口module_init(init_soundcore);


47行是sound_class的注册,同时43行的init_oss_soundcore注册了字符型设备,内容如下:

660register_chrdev注册字符型设备。这就是整个声卡设备初始化的过程,接下来就是他具体的使用了。

原文地址:https://www.cnblogs.com/leino11121/p/2381894.html