第一篇:海思HI35XX之---音频模块使用总结
海思HI35XX之---音频模块使用总结
AUDIO 模块包括音频输入、音频输出、音频编码、音频解码四个子模块。音频输入和
输出模块通过对 Hi35xx 芯片 SIO 接口的控制实现音频输入输出功能。音频编码和解码模块提供对 G711、G726、ADPCM 格式的音频编解码功能,并支持录制和播放 LPCM格式的原始音频文件。
音频输入输出接口 SIO(Sonic Input/Output),用于和 Audio Codec 对接,完成声音的录制和播放。
对每个 SIO 接口的音频输入和音频输出功能,软件分别用 AI 和 AO 两个模块来管理,称之为 AI 设备和 AO 设备,并按照 SIO 序号为其编号。例如与 SIO0 接口对应的软件设备分别为 AiDev0 和 AoDev0。
HI3518录音和播放原理:
录音:原始音频信号以模拟信号的形式给出后,通过 Audio Codec,按一定采样率和采样精度转换为数字信号。Audio Codec 以 I2S 时序或 PCM 时序的方式,将数字信号传输给SIO 接口,SIO 支持多路复用的接收模式。Hi35xx 芯片利用 DMAC 将 SIO 接口中的音频数据保存到内存中,完成录音操作。
播放:Hi35xx 芯片利用 DMAC 将内存中的数据传输到 SIO 接口。SIO 接口通过 I2S 时序或 PCM 时序向 Audio CODEC 发送数据。Audio Codec 完成数字信号到模拟信号的转换过程,并输出模拟信号。
Hi35xx音频部分的编码类型 G711、G726、ADPCM_DVI4 与 ADPCM_ORG_DVI4是使用硬编码,ADPCM_IMA 是使用 CPU 软件解码,其中
Hi3518/Hi3516C 芯片没有硬件编码模块,所有的编码方式都使用软件编码;而所有的解码功能基于独立封装的海思音频编解码库,核心解码器工作在用户态,使用 CPU 软件解码。SDK 支持通过 SYS模块的绑定接口,将一个 AI 通道绑定到 AENC 通道,实现录音编码功能;也可以将一个 ADEC 通道绑定到 AO 通道,实现解码播放功能。
使用海思语音编解码库进行 G711、G726、ADPCM 格式的编码,编码后的码流遵循以下表格中描述的帧结构,即在每帧码流数据的净荷数据之前填充有 4 个字节的帧头;使用语音编解码库进行以上格式的解码时,需要读取相应的帧头信息。
这4个字节的帧头内容即为如下数组中的值:
static char aryHeard[4] = {0,1,160,0};//hisi audio header
利用ACODEC库进行音频解码播放时,每发送一包音频数据到解码通道前,都必须先把这个数组中的内容组合到包的头部位置,否则解码出错。
G711、G726、ADPCM编码协议的采样率均为8KHz。
其中,Hi3518/Hi3516C 使用内部 audio codec。Hi3518A/Hi3516C 支持双声道,左右声道输入,左右声道输出。Hi3518C 只支持单声道,左声道输入、左声道输出。
音频 AI 和 AO 支持的最大通道数为 16 通道(其中 Hi3518/Hi3516C 芯片受内置 codec 限制,只支持 2 通道),且配置 AI 和 AO 设备时需要将通道配置为偶数。
Hi3518/Hi3516C 只支持 16bit 位宽。
Hi3518A/Hi3518C/Hi3516C 提供一个内置的 Audio Codec,并在芯片内部对接到 SIO0接口,即 SIO0 接口只能通过内置的 Audio Codec 完成声音的播放及录制。因为 AudioCodec 不能发送同步时钟,所以 SIO0 接口只能配置为 I2S 时序的主模式(MASTER)。用户需要正确配置 SIO0 和 Audio Codec 对接时序才可接收或发送音频数据。
Audio Codec 分为模拟部分和数字部分。模拟部分可以通过模拟混音(MICPGA)选择由麦克风输入(MICIN)或线性输入(LINEIN),模拟混音支持增益调节。数字部分有 ADC 和 DAC,完成模拟信号和数字信号之间的转换,并且可分别调节音量。用户在进行音量调节时,可综合模拟部分和数字部分的音量调节,建议优先调节模拟部分音量。
Audio Codec 支持去加重滤波、pop 音抑制和高通滤波,并默认开启这些功能。
Audio Codec 的用户态接口以 ioctl 形式体现,其形式如下:
int ioctl(int fd,unsigned long cmd,……);
该函数是 Linux 标准接口,具备可变参数特性。但在 Audio Codec 中,实际只需要 3 个参数。因此,其语法形式等同于:
int ioctl(int fd,unsigned long cmd,CMD_DATA_TYPE *cmddata);
其中,CMD_DATA_TYPE 随参数 cmd 的变化而变化。
综上所述,HI3518C音频子系统初始化时应该作以下软件配置:
音频编码录制流程:
1、音频输入属性(见AIO_ATTR_S结构体);
2、配置音频编码、解码模块(ACODEC);
3、设置AI设备属性;启用AI设备;启用AI通道;(启用AI噪声抑制、启用AI重采样,此两项可选。);
4、根据音频编码协议创建音频编码通道;
5、绑定音频编码通道到音频输入通道;
6、HI_MPI_AENC_GetFd(AENC_CHN AeChn)获取音频编码通道的Fd;
7、HI_MPI_AENC_GetStream从编码通道获取编码之后的音频数据;
8、用户保存或者转发此数据。
音频解码播放流程:
1、音频输出属性(见AIO_ATTR_S结构体)初始化;
2、配置音频编码、解码模块(ACODEC);
3、根据音频编码协议创建音频解码通道;
4、设置AO设备属性;启用AO设备;启用 AO通道(启用AO重采样,此项可选);
5、绑定音频输出通道到音频解码通道;
6、向每包待发送的音频数据头添加4字节的海思音频协议头;
7、HI_MPI_ADEC_SendStream向音频解码通道发送组合之后的音频数据包;
8、播放声音。
Hi35xx SIO 支持扩展的多路接收的 I2S 及 PCM 接口时序,对接 CODEC 的时序模式选择、同步时钟、采样位宽等配置必须与 Hi35xx SIO 的配置保持一致,否则可能采集不到正确的数据。上面代码中AUDIO_POINT_NUM = 320,则通过调用口HI_MPI_AENC_GetStream从音频编码通道获取到的每包音频数据大小都为324字节(320字节的净荷数据+4字节海思音频数据头),使用ACODEC进行解码播放时,每次调用HI_MPI_ADEC_SendStream将音频数据发往音频解码通道时,数据长度也必须是324字节(320字节的净荷数据+4字节海思音频数据头)。
在音频初始化配置完毕后,需要首先对ACODEC模块进行配置,在配置ACODEC模块时,注意:
1、要把MICIN静音(MUTE)功能关闭。
2、输入设备选用LINEIN。
此处我有一点疑惑,设备的输入明明接的是MIC,我一开始也是未加思索就选择MICIN来进行配置ACODEC的,但是音频编码流程完毕后获取到的音频数据播放时只有“沙沙沙”的背景音,听不到话筒端的说话声音,纠结了两天,其它各项参数确定没有问题了,尝试着把MICIN改为LINEIN,声音OK,无语。。。
目前听到的采集到的声音和解码播放的声音都很清晰,只是有点小,后续再尝试通过ioctl及相应的ACODEC模块给出的各种增益调节命令来调整音频监听与对讲的音量大小。。。
第二篇:alsa音频总结
Linux音频驱动总结
参考文章:http://blog.csdn.net/droidphone/
http://blog.chinaunix.net/uid/22917448.html
分析只列出部分重要代码,具体请参考linux3.0内核代码。
Alsa架构整体来说十分复杂,但对于驱动移植来说我们仅仅只需要关心ASOC就足够了。在学习asoc之前我们先了解一些专业术语:
ASoC currently supports the three main Digital Audio Interfaces(DAI)found on SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.ASoC现在支持如今的SoC控制器和便携音频解码器上的三个主要数字音频接口,即AC97,I2S,PCM(与pcm音频格式注意区分,前者是一种音频接口,后者是一种输入声卡的音频格式)。
AC97 AC97 ====
AC97 is a five wire interface commonly found on many PC sound cards.It is now also popular in many portable devices.This DAI has a reset line and time multiplexes its data on its SDATA_OUT(playback)and SDATA_IN(capture)lines.The bit clock(BCLK)is always driven by the CODEC(usually 12.288MHz)and the frame(FRAME)(usually 48kHz)is always driven by the controller.Each AC97 frame is 21uS long and is divided into 13 time slots.AC97是一种个人电脑声卡上常见的五线接口。现在在很多便携设备中也很流行。这个数字音频接口有一个复位线,分时在SDATA_OUT(回放)和SDATA_IN(捕获)线上传送数据。位时钟常由解码器驱动(通常是12.288MHz).帧时钟(通常48kHz)总是由控制器驱动。每个AC97帧21uS,并分为13个时间槽。
I2S I2S ===
I2S is a common 4 wire DAI used in HiFi, STB and portable devices.The Tx and Rx lines are used for audio transmission, whilst the bit clock(BCLK)and left/right clock(LRC)synchronise the link.I2S is flexible in that either the controller or CODEC can drive(master)the BCLK and LRC clock lines.Bit clock usually varies depending on the sample rate and the master system clock(SYSCLK).LRCLK is the same as the sample rate.A few devices support separate ADC and DAC LRCLKs, this allows for simultaneous capture and playback at different sample rates.I2S是一个4线数字音频接口,常用于HiFi,STB便携设备。Tx 和Rx信号线用于音频传输。而位时钟和左右时钟(LRC)用于同步链接。I2S具有灵活性,因为控制器和解码器都可以控制位时钟和左右时钟。位时钟因采样率和主系统时钟而有不同。LRCLK与采样率相同。少数设备支持独立的ADC和DAC的LRCLK。这使在不同采样率情况下同步捕获和回放成为可能。
I2S has several different operating modes:-I2S有几个不同的操作模式:
o I2SMSB is transmitted on transition of LRC.左对齐模式:MSB在LRC传送时传送。
o Right JustifiedMSB is transmitted on falling edge of first BCLK after FRAME/SYNC.模式A-MSB在FRAME/SYNC后第一个BCLK的下降沿传送。
o Mode Busing I2C, 3 Wire(SPI)or both APIs 3)Mixers and audio controls 4)Codec audio operations 1)解码器数字音频接口和PCM配置。
2)解码器控制IO-使用I2C,3总线(SPI)或两个都有。3)混音器和音频控制。4)解码器音频操作。
Optionally, codec drivers can also provide:-解码器驱动可以选择性提供:
5)DAPM description.6)DAPM event handler.7)DAC Digital mute control.5)动态音频电源管理描述。6)动态音频电源管理事件控制。7)数模转换数字消音控制。SoC DAI Drivers 板级DAI驱动 ===============
Each SoC DAI driver must provide the following features:-每个SoC DAI驱动都必须提供如下性能:
1)Digital audio interface(DAI)description 1)数字音频接口描述
2)Digital audio interface configuration 2)数字音频接口配置 3)PCM's description 3)PCM描述
4)SYSCLK configuration 4)系统时钟配置
5)Suspend and resume(optional)5)挂起和恢复(可选的)
以上由君子翻译,本人实在没办法比他描述的更好了,所以把重要的部分提取出来直接copy。在这里对君子表示由衷感谢,赋上君子注。君子注:
您现在所阅读的,是君子阅读Linux音频SoC驱动时,写下的文档译文。
君子写些译文,一方面是作为自己的笔记,帮助记忆,另一方面也希望能对他人有所帮助。如果您能于君子的译文中有所收获,则吾心甚慰
现在我们开始分析ASOC:
ASoC被分为Machine、Platform和Codec三大部分。其中的Machine驱动负责Platform和Codec之间的耦合和设备或板子特定的代码。
看起来挺复杂,其实需要我们做的事情并不多,大部分内核已经完成。下面我们分析哪些是我们需要自己做的:
codec驱动:负责音频解码。这部分代码完全无平台无关,设备原厂提供,我们只需要把它加进内核编译就好了。platform驱动:与处理器芯片相关,这部分代码在该芯片商用之前方案产商提供的demo板已完全确定了,也就是说我们只需要使用就可以了。
machine驱动:好了,到了最关键的地方了,machine驱动是耦合platform和codec驱动,同时与上层交互的代码。由于上层是标准的alsa架构,所以下层接口肯定要做了统一,所以我很负责的告诉你,这部分由machine本身的platform驱动和platform设备组成(请跟asoc的platform驱动区别),platform驱动内核帮我们完成了,所以你无须过多的关心你的驱动怎么跟上层alsa怎么衍接的问题,我们只需要注册一个machine的platform设备以及完成platform和codec耦合就ok
asoc的关系图如下:(以下适应于linux3.0。linux2.6会有所不同)
上图把asoc架构显示的淋漓尽致,如果你分析了asoc你就会发现上图描述的结构以及函数真的一个都跑不了。
Machie:
Machine platform device:(~/sound/soc/samsung/smdk_wm8994.c)
1.smdk_snd_device = platform_device_alloc(“soc-audio”,-1);2.if(!smdk_snd_device)3.return-ENOMEM;4.5.platform_set_drvdata(smdk_snd_device, &smdk);6.7.ret = platform_device_add(smdk_snd_device);
1.static struct snd_soc_dai_link smdk_dai[] = { 2.{ /* Primary DAI i/f */
3..name = “WM8994 AIF1”, 4..stream_name = “Pri_Dai”, 5..cpu_dai_name = “samsung-i2s.0”, 6..codec_dai_name = “wm8994-aif1”, 7..platform_name = “samsung-audio”, 8..codec_name = “wm8994-codec”, 9..init = smdk_wm8994_init_paiftx, 10..ops = &smdk_ops, 11.}, { /* Sec_Fifo Playback i/f */
12..name = “Sec_FIFO TX”, 13..stream_name = “Sec_Dai”, 14..cpu_dai_name = “samsung-i2s.4”, 15..codec_dai_name = “wm8994-aif1”, 16..platform_name = “samsung-audio”, 17..codec_name = “wm8994-codec”, 18..ops = &smdk_ops, 19.}, 20.};21.22.static struct snd_soc_card smdk = { 23..name = “SMDK-I2S”, 24..owner = THIS_MODULE, 25..dai_link = smdk_dai,26..num_links = ARRAY_SIZE(smdk_dai), 27.};
通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:
snd_soc_dai_link(实例:smdk_dai[])snd_soc_ops(实例:smdk_ops)
snd_soc_dai_link看名字就知道,很明显它是起耦合链接作用的。它指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai。
snd_soc_ops连接Platform和Codec的dai_link对应的ops操作函数,本例就是smdk_ops,它只实现了hw_params函数:smdk_hw_params。
到此为止,最主要的部分machine的平台设备注册我们完成了。
下面我们关注machine平台驱动部分(这部分内核不需要我们实现,但我们需要知道它是怎么工作的)
ASoC的platform_driver在以下文件中定义:sound/soc/soc-core.c。还是先从模块的入口看起:
[cpp] view plaincopy 1.static int __init snd_soc_init(void)2.{ 3.......4.return platform_driver_register(&soc_driver);5.}
soc_driver的定义如下:
[cpp] view plaincopy
1./* ASoC platform driver */
2.static struct platform_driver soc_driver = { 3..driver = {
4..name = “soc-audio”, //确保你注册machine平台设备和它保持一致 5..owner = THIS_MODULE, 6..pm = &soc_pm_ops, 7.},8..probe = soc_probe, 9..remove = soc_remove, 10.};
初始化入口soc_probe()
soc_probe函数本身很简单,它先从platform_device参数中取出snd_soc_card,然后调用snd_soc_register_card,通过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中实现,下面就看看snd_soc_instantiate_card做了些什么: 该函数首先利用card->instantiated来判断该卡是否已经实例化,如果已经实例化则直接返回,否则遍历每一对dai_link,进行codec、platform、dai的绑定工作,下只是代码的部分选节,详细的代码请直接参考完整的代码树。static int soc_probe(struct platform_device *pdev){
struct snd_soc_card *card = platform_get_drvdata(pdev);//别忘记了machine的platform_set_drvdata //取出snd_soc_card
.............ret = snd_soc_register_card(card);//注册............}
下面我们看snd_soc_register_card()函数:
int snd_soc_register_card(struct snd_soc_card *card){
。。。。
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime)*
(card->num_links + card->num_aux_devs),GFP_KERNEL);//为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应一个snd_soc_pcm_runtime数组单元。。。。
for(i = 0;i < card->num_links;i++)
card->rtd[i].dai_link = &card->dai_link[i];//把snd_soc_card中的dai_link复制到相应的snd_soc_pcm_runtime。。。。
snd_soc_instantiate_cards();//将调用snd_soc_instantiate_card()//最为重要 }
下面我们分析snd_soc_instantiate_card()函数:
static void snd_soc_instantiate_card(struct snd_soc_card *card){
。。。。
if(card->instantiated){ //判断该卡是否已经实例化,如果是就返回
mutex_unlock(&card->mutex);
return;
}
/* bind DAIs */
for(i = 0;i < card->num_links;i++)//否则遍例每一对dai_link,进行codec,flatrom,dai的绑定工作
soc_bind_dai_link(card, i);/* ******************************************************************************************************************************************************************
ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
*****************************************************************************************************************************************************************/
/* bind completed ? */
if(card->num_rtd!= card->num_links){
mutex_unlock(&card->mutex);
return;
}
。。。。。
/* card bind complete so register a sound card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card)。。。。。
card->snd_card->dev = card->dev;
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);//初始化codec缓存,创建声卡实例
。。。。。。。//下面是最重要的probe匹配工作
if(card->probe){
ret = card->probe(card);
if(ret < 0)
goto card_probe_error;
}
。。。。。
ret = soc_probe_dai_link(card, i, order);//主要的耦合链接工作在此函数完成。。。。。
ret = soc_probe_aux_dev(card, i)。。。。。
if(card->late_probe){//最后的声卡初始化工作,ret = card->late_probe(card);
}
。。。。。
ret = snd_card_register(card->snd_card);//然后调用标准的alsa驱动的声卡函数进行声卡注册
。。。。。
}
到这里声卡已经注册好了,声卡可以正常工作了,我们再分析最后一个函数,也就是它们是怎么匹配的 soc_probe_dai_link():
static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order){
。。。。。
/* probe the cpu_dai */
if(!cpu_dai->probed &&
cpu_dai->driver->probe_order == order){
if(!try_module_get(cpu_dai->dev->driver->owner))
return-ENODEV;
if(cpu_dai->driver->probe){
ret = cpu_dai->driver->probe(cpu_dai);
if(ret < 0){
printk(KERN_ERR “asoc: failed to probe CPU DAI %sn”, cpu_dai->name);
module_put(cpu_dai->dev->driver->owner);
return ret;
}
}
cpu_dai->probed = 1;
/* mark cpu_dai as probed and add to card dai list */
list_add(&cpu_dai->card_list, &card->dai_dev_list);
}
/* probe the CODEC */
if(!codec->probed &&
codec->driver->probe_order == order){
ret = soc_probe_codec(card, codec);
if(ret < 0)
return ret;
}
/* probe the platform */
if(!platform->probed &&
platform->driver->probe_order == order){
ret = soc_probe_platform(card, platform);
if(ret < 0)
return ret;
}
/* probe the CODEC DAI */
if(!codec_dai->probed && codec_dai->driver->probe_order == order){
if(codec_dai->driver->probe){
ret = codec_dai->driver->probe(codec_dai);
if(ret < 0){
printk(KERN_ERR “asoc: failed to probe CODEC DAI %sn”, codec_dai->name);
return ret;
}
}
/* mark codec_dai as probed and add to card dai list */
codec_dai->probed = 1;
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
/* complete DAI probe during last probe */
if(order!= SND_SOC_COMP_ORDER_LAST)return 0;
。。。。。
/* create the pcm */
ret = soc_new_pcm(rtd, num);//如果上面都匹配成功将创建标准的alsa的pcm逻辑设备
。。。。。
}
好了,到此为止我们最主要的部分machine部分分析完成了。接着是codec驱动部分:
Codec简介
在移动设备中,Codec的作用可以归结为4种,分别是:
对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号
对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号
对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的
对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等
ASoC对Codec的这些功能都定义好了一些列相应的接口,以方便地对Codec进行控制。ASoC对Codec驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个Codec的代码不经修改即可用在不同的平台上。以下的讨论基于wolfson的Codec芯片WM8994,kernel的版本3.3.x。
描述Codec的最主要的几个数据结构分别是:snd_soc_codec,snd_soc_codec_driver,snd_soc_dai,snd_soc_dai_driver,其中的snd_soc_dai和snd_soc_dai_driver在ASoC的Platform驱动中也会使用到,Platform和Codec的DAI通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。
Codec的注册
因为Codec驱动的代码要做到平台无关性,要使得Machine驱动能够使用该Codec,Codec驱动的首要任务就是确定snd_soc_codec和snd_soc_dai的实例,并把它们注册到系统中,注册后的codec和dai才能为Machine驱动所用。以WM8994为例,对应的代码位置:/sound/soc/codecs/wm8994.c,模块的入口函数注册了一个platform driver:
[html] view plaincopy
1.static struct platform_driver wm8994_codec_driver = { 2..driver = { 3..name = “wm8994-codec”, //注意machine device里面和这里保持一致 4..owner = THIS_MODULE, 5.}, 6..probe = wm8994_probe, 7..remove = __devexit_p(wm8994_remove), 8.};9.10.module_platform_driver(wm8994_codec_driver);有platform driver,必定会有相应的platform device,platform device其实在我们之前讲过的machine device注册时已经引入了。
[html] view plaincopy
1.static int __devinit wm8994_probe(struct platform_device *pdev)2.{
3.return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994, 4.wm8994_dai, ARRAY_SIZE(wm8994_dai));5.}
其中,soc_codec_dev_wm8994和wm8994_dai的定义如下(代码中定义了3个dai,这里只列出第一个):
[html] view plaincopy
1.static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { 2..probe = wm8994_codec_probe, 3..remove = wm8994_codec_remove, 4..suspend = wm8994_suspend, 5..resume = wm8994_resume,6..set_bias_level = wm8994_set_bias_level, 7..reg_cache_size = WM8994_MAX_REGISTER, 8..volatile_register = wm8994_soc_volatile, 9.};
[html] view plaincopy
1.static struct snd_soc_dai_driver wm8994_dai[] = { 2.{
3..name = “wm8994-aif1”, 4..id = 1, 5..playback = {
6..stream_name = “AIF1 Playback”, 7..channels_min = 1, 8..channels_max = 2, 9..rates = WM8994_RATES, 10..formats = WM8994_FORMATS, 11.},12..capture = {
13..stream_name = “AIF1 Capture”, 14..channels_min = 1, 15..channels_max = 2, 16..rates = WM8994_RATES, 17..formats = WM8994_FORMATS, 18.},19..ops = &wm8994_aif1_dai_ops, 20.}, 21.......22.}
可见,Codec驱动的第一个步骤就是定义snd_soc_codec_driver和snd_soc_dai_driver的实例,然后调用snd_soc_register_codec函数对Codec进行注册。
snd_soc_register_codec()函数是machine driver提供的,只要注册成功后codec提供的操作函数就能正常提供给machine driver使用了。int snd_soc_register_codec(struct device *dev,const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai){ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)。。。。。。
/* create CODEC component name */ codec->name = fmt_single_name(dev, &codec->id);/*Machine驱动定义的snd_soc_dai_link中会指定每个link的codec和dai的名字,进行匹配绑定时就是通过和这里的名字比较,从而找到该Codec的 */
// 然后初始化它的各个字段,多数字段的值来自上面定义的snd_soc_codec_driver的实例soc_codec_dev_wm8994: codec->write = codec_drv->write;codec->read = codec_drv->read;codec->volatile_register = codec_drv->volatile_register;codec->readable_register = codec_drv->readable_register;codec->writable_register = codec_drv->writable_register;codec->dapm.bias_level = SND_SOC_BIAS_OFF;codec->dapm.dev = dev;codec->dapm.codec = codec;codec->dapm.seq_notifier = codec_drv->seq_notifier;codec->dev = dev;codec->driver = codec_drv;codec->num_dai = num_dai;mutex_init(&codec->mutex);
/* allocate CODEC register cache */ if(codec_drv->reg_cache_size && codec_drv->reg_word_size){ reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;codec->reg_size = reg_size;/* it is necessary to make a copy of the default register cache
* because in the case of using a compression type that requires
* the default register cache to be marked as __devinitconst the
* kernel might have freed the array by the time we initialize
* the cache.*/ if(codec_drv->reg_cache_default){ codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,reg_size, GFP_KERNEL);if(!codec->reg_def_copy){ ret =-ENOMEM;goto fail;} } }
。。。。。。/* register any DAIs */ if(num_dai){ ret = snd_soc_register_dais(dev, dai_drv, num_dai);//通过snd_soc_register_dais函数对本Codec的dai进行注册 if(ret < 0)goto fail;}
mutex_lock(&client_mutex);list_add(&codec->list, &codec_list);/*最后,它把codec实例链接到全局链表codec_list中,并且调用snd_soc_instantiate_cards是函数触发Machine驱动进行一次匹配绑定操作 */
snd_soc_instantiate_cards();mutex_unlock(&client_mutex)。。。。。}
好了,在这里我们的codec驱动也分析完了,其实这部分都是与平台无关代码,一般也不需要改动,这部分我们从设备原厂拿到代码后丢上去就可以了,只是我们在写machine device的时候要注意和这里的名字匹配。接下来是asoc的platform驱动:
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进行交互。
snd_soc_platform_driver的注册
通常,ASoC把snd_soc_platform_driver注册为一个系统的platform_driver,不要被这两个想像的术语所迷惑,前者只是针对ASoC子系统的,后者是来自Linux的设备驱动模型。我们要做的就是:
定义一个snd_soc_platform_driver结构的实例;
在platform_driver的probe回调中利用ASoC的API:snd_soc_register_platform()注册上面定义的实例;
实现snd_soc_platform_driver中的各个回调函数;
以kernel3.3中的/sound/soc/samsung/dma.c为例:
[cpp] view plaincopy
1.static struct snd_soc_platform_driver samsung_asoc_platform = { 2..ops = &dma_ops, 3..pcm_new = dma_new, 4..pcm_free = dma_free_dma_buffers, 5.};6.7.static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)8.{ 9.return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);10.} 11.12.static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)13.{ 14.snd_soc_unregister_platform(&pdev->dev);15.return 0;16.} 17.18.static struct platform_driver asoc_dma_driver = { 19..driver = { 20..name = “samsung-audio”, 21..owner = THIS_MODULE, 22.}, 23.24..probe = samsung_asoc_platform_probe, 25..remove = __devexit_p(samsung_asoc_platform_remove), 26.};27.28.module_platform_driver(asoc_dma_driver);snd_soc_register_platform()该函数用于注册一个snd_soc_platform,只有注册以后,它才可以被Machine驱动使用。它的代码已经清晰地表达了它的实现过程:
为snd_soc_platform实例申请内存;
从platform_device中获得它的名字,用于Machine驱动的匹配工作; 初始化snd_soc_platform的字段;
把snd_soc_platform实例连接到全局链表platform_list中;
调用snd_soc_instantiate_cards,触发声卡的machine、platform、codec、dai等的匹配工作;
cpu的snd_soc_dai driver驱动的注册
dai驱动通常对应cpu的一个或几个I2S/PCM接口,与snd_soc_platform一样,dai驱动也是实现为一个platform driver,实现一个dai驱动大致可以分为以下几个步骤:
定义一个snd_soc_dai_driver结构的实例;
在对应的platform_driver中的probe回调中通过API:snd_soc_register_dai或者snd_soc_register_dais,注册snd_soc_dai实例;
实现snd_soc_dai_driver结构中的probe、suspend等回调;
实现snd_soc_dai_driver结构中的snd_soc_dai_ops字段中的回调函数;
snd_soc_register_dai 这个函数在上一篇介绍codec驱动的博文中已有介绍
具体不再分析,这个驱动也不需要用户做任务修改,所以只要知道它的作用就已经够了。就像电话机一样,我们只要知道电话怎么打就够了,至于它怎么连接我们并不太需要关心。
第三篇:心得体会袁思海
视导员培训的启示
汇川区娄山关红军小学:袁思海
2018年9月28日至9月29日,我有幸参加了汇川区中小学责任督学、视导员专业化发展研修班学习,在这两天的学习期间,聆听了李国德、梁祝、周学文几位专家的讲座,真正懂得了督导对学校的重要性。督导,顾名思义即监督、指导。教育督导的“督”具有监督、督促、检查和评估的职能;“导”具有指导、帮助、服务和咨询的职能。虽然为期很短,但受益非浅。我领悟到作为一名新时期的校长应有的新理念、新思想,如何配合督学工作,让学校的管理上台阶,是我需要思考的问题。
一、思想上要转变。
中小学校实行责任督学挂牌督导制度,是国家教育治理和教育督导改革的重大制度创新,是对督学责任区建设的深化和完善。责任督学挂牌亮相,依法依规入校督导,及时发现解决教育教学实际问题,促进学校全面实施素质教育,得到教育行政部门、学校、家长及社会的广泛认可,为提高教育质量、促进教育公平、办人民满意的教育发挥了重要作用,教育现状,需要责任区督学工作的创新。听了李国德督学的讲座,让我深深明白,我原来的想法是错误的,我原来曾想,这些督学一天没事,就到学校来指手画脚,作为一间村级小学,本身人手就不够,你们还来添乱,我还真有抵触的情绪,但是现在说真的,没有了,我茅塞顿开,这些督学都是一线退下来的,在学校的方方面面,都是高手和行家,他一眼看下来,就知道学校的管理漏洞,就知道问题出在哪里。他们是来帮助学校发展的,是来给学校解决问题的,随着挂牌督学创新区的成立,我想这仅仅是个好的开始。
二、行动上加紧。
每位督学是深入学校教育教学管理最直接的监督员,责任区督学挂牌督导是各地人民政府教育督导部门,对区域内每一所学校设置责任督学。在一定程度上增强了教育督导工作的开放性和透明度。根据工作需要,我今后一定会组织本校的一些老教师、教研组长和中层干部和督学一起对学校工作进行内部督导。开展校内教育督导工作,推动学校工作向前发展,使学校工作目标更明确,实施时更有计划性,使学校工作走上正规化、法制化的道路。同时有利于各部门创造性地工作,并能及时总结经验,在工作中加以推广和应用。
通过对教育教学活动全过程进行监督、检查与评价,掌握情况,经验,发现问题,及时进行分析,从而不断优化教育教学过程,提高教学管理水平和教学质量,逐步形成了一套利于学校发展、推动学校素质教育实施的教育督导评估模式与机制,取得较为理想的成效。
三、认真自查,及时改进
通过自查自纠,及时调整方法,让学校的工作持续健康发展,认真听取督学的建议,邀请督学一起给学校把脉问诊,把学校的工作推上一个台阶。
第四篇:7《海思》教案
7、《海思》教案
教学目标 :
1.理清文章结构,学会用自己的话概括文章的主要内容。
2.理解作者由海而产生的独特的联想和深邃的思考。
3.体会文中富有哲理的语言。
4.探究作者思想的广度和深度。
教学重难点 :
重点:学会编写阅读提要。品读语言,有感情地朗读课文。
难点:作者思维的广度和深度。
课时安排 :
两课时
第一课时
教学过程:
一、预习:
①.读课文,表段序,勾划出文中出现的生字词。(注意读音及写法)②.读单元提示,明确学习内容和学习目标。③.完成学案“基础训练”部分。给下列加点字注音:
Wanyan
qì
pú
piaomiao
muyu
juanlian 蜿 蜒
休憩
返璞归真
缥缈
沐浴
眷恋 2.作者简介:
梁衡,1946年出生,1968年毕业于中人民大学。山西霍州人。当代作家。曾长期任新闻记者,历任新闻出版署副署长。现任人民日报副总编辑、中国人民大学新闻学院博士生导师、中国作家协会全委会委员、中国记者协会全委会常务理事、人教版中小学教材总顾问。是著名的新闻理论家、散文家、科普作家和政论家。曾荣获全国青年文学奖、赵树理文学奖、全国优秀科普作品奖和中宣部“五个一”工程奖等多种荣誉称号。
二、问题导学:
1.思考:课文那几个段落集中描写了大海的壮丽图景?那几个段落集中描写了作者对大海的联想与思考?文章中哪些句段完成了内容的转换? ①第三段 ②第四段最后一句
2.作者眼前的海是怎样的?让作者产生了哪些联想与思考?作者心中的海是怎样的?
作者眼前的海是壮丽的大海(汹涌澎湃 波澜起伏
海天一色);作者心中的大海的形象——海的博大精深、海的包容一切、海给人类心灵的慰藉。3.作者围绕海所做的思考是为了表达什么感情?
引导学生看议论抒情句,让学生全面把握作者爱海的博大精深,爱海的包容一切,海给人类心灵的慰藉。
4.由此引导学生理清文章脉络,把文章分成三个部分,全班各自据此编写阅读提要,并分小组交流,然后再进行全班交流。
完成能力训练一
第一部分:写大海的美丽图景。
或
写景
眼前之海 第二部分:写大海引起的联想
或
联想
联想之海 第三部分:写面对大海的沉思
或
沉思
沉思之海
三、自学探究
1.学情预设:学生对“心中的海”理解可能有点困难,另外课文的概括是重点,教师重点点拨。
2.个人能力不能完成的问题,小组合作完成。
四、展示点评
1.展示方式:个别回答,小组代表上台演板。2.点评方式:学生纠错,教师结合学法点评。
五、拓展延伸
面对壮丽的大海,你会做怎样的联想与思考,而作者又做了怎么样的联想与思考?
他为什么会有这种思考? ①联想奇特
②大散文观”
第二课时
教学过程:
一、复习
1.作者眼前的海是什么样子的?面对这样的海他做了怎么样的联想与思考?他为什
么会有这种思考?
2.回顾在学上篇课文中学到的品析语言表达效果的方法:
品析语言我们可以从运用的方式、具体阐述、阐述的特点内容欧、情感意图这四步来组织语言。
二、问题导学
1.研读第二自然段:作者眼前之海是神么样子的呢?给人什么感受?作者是从哪几个方面着手描写的呢?我们可以从哪几个方面去品读呢?你准备怎么样去
朗读它呢?
学生完成能力训练二 小组合作、交流。
方式:回忆《说几句爱海的孩子气的话》中介绍的方法: 四步法:运用的方式
具体阐述
特点 内容
情感意图 示例:、“极目望去„„棉朵”
——运用了排比,比喻的修辞手法。把远处的浪花比作大军与棉朵,从声势、颜色的角度,生动形象的写出了海的博大壮阔。2.学生读:学生按刚才的品析去试着设计朗读。
3.学生小组合作活动结尾“海啊,你在我的心里”还表达了作者什么样的感情? 含蓄地表达了作者对大海深沉的爱。
三、自学探究
1.学情预设:语言品析是重点,估计部分同学回答会不规范,不全面,教师重点点拨。
2.朗读设计一定发挥全体同学的积极性。
四、展示点评
1.展示方式:个别回答,小组代表上台演板。2.点评方式:学生纠错,教师结合学法点评。
五、拓展延伸
1.《海思》是一篇写景抒情类的散文,课文中所体现的作者充满智慧的思考令人叹服。同学们写作文若果单纯写景,会不会是一篇好作文? 一篇好作文,除了写景,还必须写出作者的思考。联想的流畅,思考的有深度,才能算是一篇好作文。2.借助具体物象,培养学生的想像力。
①金钱确实可以买到许多东西,但它不是万能的,如金钱能买床铺,不能买甜蜜的梦;
能买书,不能买到知识„„请你联想它的作用和局限,越多越好。
②有位作家写了一首诗《0的断想》:
0是谦虚者的起点,骄傲者的终点;0的负担最轻,但任务最重;0是一面镜子,让你重新认识自己;
0是一只救生圈,让弱者随波逐流;
请你运用发散思维续写下去。
创新提示:
①能买药物,不能买健康;能买娱乐,不能买幸福;能买房屋,不 能买家庭;能买选票,不能买人心„„
②0是一块空地,可种五谷;是烟圈,虚度年华;是铁环,组成坚韧;是战鼓,激人奋进„„
第五篇:海之韵诵读社团总结
赣榆区华杰双语学校小学部2014——2015学第一学期
海之韵诵读社团工作总结
刚接手社团的我是迷茫的:没有教材,学生来自各个学段,所有的一切仿佛都是处于混乱之中,没办法,只能靠我这张嘴了。于是在开课之前,我就去找张晓老师讨教方法。她非常慷慨地给我讲了怎样开展诵读社团。在前人的经验教导和我逐步的摸索下,我也慢慢累积了经验。细细地回想了与孩子们相处得时光,觉得有收获也有遗憾。
我收获了以下几个方面: 一. 全员巩固了系统的理论知识
作为一名语文教师,我知道诵读是一项技能,不过这对于我好像是与生俱来的能力。在我的印象中,谁都应该会诵读,这个还用教吗?想象总是比现实美好。第一节课,为了能够掌握这个社团的基本状况,我给社团成员5分钟时间,让他们准备一篇文章上台进行诵读表演。结果让我目瞪口呆呀!朗读没感情,节奏杂乱,唱读,拖读,各种各样问题。当让也有几个资质不错的成员,声音如百灵般的动听。于是我决定从头教起,首先我准备系统的诵读知识,每节课讲一个知识点,并伴随相应的练习训练。一学期下来,学生明白了原来诵读还有许多理论要学习,而且也能掌握这些理论,并将这些理论运用于实践。在教授的过程中我也重新温习了这些理论知识。大家都在共同的进步。
二.收获了师生情
刚开始带这个社团的时候,虽然已经将社团的规章制度宣读给孩子们听,但他们所表现的遵守能力不同。有一天,当我刚踏进社团专用教室时,有一个小女孩跑过来跟我说反映我的社团成员翻别人桌洞,胡乱拿别人东西。当时我就心酸了,那种心情就好像一个母亲听着别人说自家孩子的不是。我当时站在门口理了理失望的思绪,并决定利用上课的时间重点强调一下,发生过的事情不能挽回,但是不能让同样的过错再次发生。于是我在班里进行了一番说教,并进行了相应的措施。可能他们感受到了我的关怀,每次见到我就会跟在我的旁边,感觉我又收获了一份满满的感情。
有收获的同时也会有些遗憾:虽然每个人都有进步,但是进步幅度不大,他们还要在这条路上走很长时间。这学期的社团纪律稍有松懈,为社团教室的主人们带来了不便。
我希望下学期的社团成员们都不断进步,不断努力。
刘昶
2014年1月8日