armlinux学习笔记IIS音频驱动程序分析1.docx
- 文档编号:5930231
- 上传时间:2023-01-02
- 格式:DOCX
- 页数:14
- 大小:24.52KB
armlinux学习笔记IIS音频驱动程序分析1.docx
《armlinux学习笔记IIS音频驱动程序分析1.docx》由会员分享,可在线阅读,更多相关《armlinux学习笔记IIS音频驱动程序分析1.docx(14页珍藏版)》请在冰豆网上搜索。
armlinux学习笔记IIS音频驱动程序分析1
8月6日
armlinux学习笔记--IIS音频驱动程序分析
(1)
//*******************************************************
//* 2007.7.5
//*******************************************************
Linux下的IIS音频驱动程序主要都在/kernel/drivers/sound/s3c2410-uda1341.c文件中。
在音频驱动程序中有2个比较重要的结构体:
typedefstruct{
intsize; /*buffersize*/
char*start; /*pointtoactualbuffer*/(内存虚拟地址起始地址)
dma_addr_tdma_addr; /*physicalbufferaddress*/(内存物理地址起始地址)
structsemaphoresem; /*downbeforetouchingthebuffer*/
intmaster; /*ownerforbufferallocation,containsizewhentrue*/(内存大小)
}audio_buf_t;
typedefstruct{
audio_buf_t*buffers; /*pointertoaudiobufferstructures*/
audio_buf_t*buf; /*currentbufferusedbyread/write*/
u_intbuf_idx; /*indexforthepointerabove*/
u_intfragsize; /*fragmenti.e.buffersize*/(音频缓冲区片大小)
u_intnbfrags; /*nbroffragments*/(音频缓冲区片数量)
dmach_tdma_ch; /*DMAchannel(channel2foraudio)*/
}audio_stream_t;
这是一个管理多缓冲区的结构体,结构体audio_stream_t为音频流数据组成了一个环形缓冲区。
(audio_buf_t*buffers同触摸屏驱动中structTS_DEV结构中的TS_RETbuf[MAX_TS_BUF]意义一样,都为环形缓冲区)用audio_buf_t来管理一段内存,在用audio_stream_t来管理N个audio_buf_t。
音频驱动的file_operations结构定义如下:
staticstructfile_operationssmdk2410_audio_fops={
llseek:
smdk2410_audio_llseek,
write:
smdk2410_audio_write,
read:
smdk2410_audio_read,
poll:
smdk2410_audio_poll,
ioctl:
smdk2410_audio_ioctl,
open:
smdk2410_audio_open,
release:
smdk2410_audio_release
};
staticstructfile_operationssmdk2410_mixer_fops={
ioctl:
smdk2410_mixer_ioctl,
open:
smdk2410_mixer_open,
release:
smdk2410_mixer_release
};
这里定义了两种类型设备的file_operations结构,前者是DSP设备,后者是混频器设备。
------------------------------------------------------------------------
和往常一样,先来看一下加载驱动模块时的初始化函数:
int__inits3c2410_uda1341_init(void)
该函数首先会初始化I/O和UDA1341芯片,然后申请2个DMA通道用于音频传输。
local_irq_save(flags);
调用该宏函数来保存IRQ中断使能状态,并禁止IRQ中断。
在/kernel/include/asm-arm/system.h文件中:
/*Forspinlocksetc*/
#definelocal_irq_save(x) __save_flags_cli(x)
#definelocal_irq_restore(x) __restore_flags(x)
在/kernel/include/asm-arm/proc-armo/system.h文件中:
/*
*Savethecurrentinterruptenablestate&disableIRQs
*/
#define__save_flags_cli(x) \
do{ \
unsignedlongtemp; \
__asm____volatile__( \
" mov %0,pc @save_flags_cli\n" \
" orr %1,%0,#0x08000000\n" \
" and %0,%0,#0x0c000000\n" \
" teqp %1,#0\n" \
:
"=r"(x),"=r"(temp) \
:
\
:
"memory"); \
}while(0)
最后用ARM汇编指令实现了保存IRQ和FIQ的中断使能状态,并禁止IRQ中断。
/*
*restoresavedIRQ&FIQstate
*/
#define__restore_flags(x) \
do{ \
unsignedlongtemp; \
__asm____volatile__( \
" mov %0,pc @restore_flags\n" \
" bic %0,%0,#0x0c000000\n" \
" orr %0,%0,%1\n" \
" teqp %0,#0\n" \
:
"=&r"(temp) \
:
"r"(x) \
:
"memory"); \
}while(0)
最后用ARM汇编指令实现了恢复IRQ和FIQ的中断使能状态。
/*GPB4:
L3CLOCK,OUTPUT*/
set_gpio_ctrl(GPIO_L3CLOCK);
/*GPB3:
L3DATA,OUTPUT*/
set_gpio_ctrl(GPIO_L3DATA);
/*GPB2:
L3MODE,OUTPUT*/
set_gpio_ctrl(GPIO_L3MODE);
/*GPE3:
I2SSDI*/
set_gpio_ctrl(GPIO_E3|GPIO_PULLUP_EN|GPIO_MODE_I2SSDI);
/*GPE0:
I2SLRCK*/
set_gpio_ctrl(GPIO_E0|GPIO_PULLUP_EN|GPIO_MODE_I2SSDI);
/*GPE1:
I2SSCLK*/
set_gpio_ctrl(GPIO_E1|GPIO_PULLUP_EN|GPIO_MODE_I2SSCLK);
/*GPE2:
CDCLK*/
set_gpio_ctrl(GPIO_E2|GPIO_PULLUP_EN|GPIO_MODE_CDCLK);
/*GPE4:
I2SSDO*/
set_gpio_ctrl(GPIO_E4|GPIO_PULLUP_EN|GPIO_MODE_I2SSDO);
接下来马上设置与UDA1341芯片相关GPIO引脚。
这里首先将GPB4,GPB3,GPB2这3个GPIO引脚设置为输出模式,参考原理图后,得知这3个引脚分别连接UDA1341芯片的L3CLOCK,L3DATA,L3MODE这3个引脚,作为这3个信号的输入。
在/kernel/drivers/sound/s3c2410-uda1341.c文件中:
#defineGPIO_L3CLOCK (GPIO_MODE_OUT|GPIO_PULLUP_DIS|GPIO_B4)
#defineGPIO_L3DATA (GPIO_MODE_OUT|GPIO_PULLUP_DIS|GPIO_B3)
#defineGPIO_L3MODE (GPIO_MODE_OUT|GPIO_PULLUP_DIS|GPIO_B2)
然后继续设置与IIS控制器输出信号相关GPIO引脚。
将GPE0~GPE4这5个引脚设置为IIS接口的信号模式。
需要通过配置GPECON寄存器来设定该端口管脚的输出模式,对应位如下:
[9:
8] [7:
6] [5:
4] [3:
2] [1:
0]
GPE4 GPE3 GPE2 GPE1 GPE0
参考S3C2410芯片datasheet的I/O口章节,都要设为10(二进制)。
local_irq_restore(flags);
设置完GPIO口的工作模式,就可以前面已经分析过的local_irq_restore宏函数来恢复IRQ和FIQ的中断使能状态。
init_uda1341();
这里调用了init_uda1341函数来初始化UDA1341芯片,该函数会在后面说明。
output_stream.dma_ch=DMA_CH2;
if(audio_init_dma(&output_stream,"UDA1341out")){
audio_clear_dma(&output_stream);
printk(KERN_WARNINGAUDIO_NAME_VERBOSE
":
unabletogetDMAchannels\n");
return-EBUSY;
}
input_stream.dma_ch=DMA_CH1;
if(audio_init_dma(&input_stream,"UDA1341in")){
audio_clear_dma(&input_stream);
printk(KERN_WARNINGAUDIO_NAME_VERBOSE
":
unabletogetDMAchannels\n");
return-EBUSY;
}
在全局变量中定义了,两个audio_stream_t结构的变量,分别是output_stream和input_stream,一个作为输出音频缓冲区,一个作为输入音频缓冲区。
将输出音频缓冲区的DMA通道设为通道2,输入音频缓冲区的DMA通道设为通道1。
在/kernel/include/asm-arm/arch-s3c2410/dma.h文件中:
#defineDMA_CH0 0
#defineDMA_CH1 1
#defineDMA_CH2 2
#defineDMA_CH3 3
通过查阅S3C2410芯片datasheet中的DMA章节,知道该芯片共有4个DMA通道,DMA控制器的每个通道可以从4个DMA源中选择一个DMA请求源。
其中,通道1具有IIS输入源,而通道2具有IIS输出和输入源。
所以要以全双工模式进行音频数据传输的话,只有将输出音频缓冲区的设为DMA通道2,输入音频缓冲区设为DMA通道1。
接着调用2次audio_init_dma函数来分别对输出和输入音频缓冲区的DMA通道进行初始化设置。
该函数比较简单,定义如下:
staticint__initaudio_init_dma(audio_stream_t*s,char*desc)
{
if(s->dma_ch==DMA_CH2)
returns3c2410_request_dma("I2SSDO",s->dma_ch,audio_dmaout_done_callback,NULL);
elseif(s->dma_ch==DMA_CH1)
returns3c2410_request_dma("I2SSDI",s->dma_ch,NULL,audio_dmain_done_callback);
else
return1;
}
这个函数其实就是对DMA的通道号进行判断,然后调用了s3c2410_request_dma函数来向内核申请一个DMA通道。
在/kernel/arch/arm/mach-s3c2410/dma.c文件中:
ints3c2410_request_dma(constchar*device_id,dmach_tchannel,
dma_callback_twrite_cb,dma_callback_tread_cb)
在该函数中会分配DMA通道,并申请DMA中断,即当DMA传输结束时,会响应中断请求,调用回调函数。
这里的参数中,device_id为设备id号,用字符串来表示;channel为DMA通道号,将前面定义的通道号1,2传入;write_cb和read_cb分别指向DMA发送和读取结束时调用的函数,即DMA传输结束时调用的回调函数。
在该函数中有:
err=request_irq(dma->irq,dma_irq_handler,0*SA_INTERRUPT,
device_id,(void*)dma);
即申请了一个DMA的中断号,中断处理子程序为dma_irq_handler函数,然后:
dma->write.callback=write_cb;
dma->read.callback=read_cb;
将读写DMA中断的两个回调函数指针传入。
在/kernel/arch/arm/mach-s3c2410/dma.c文件中:
staticvoiddma_irq_handler(intirq,void*dev_id,structpt_regs*regs)
{
s3c2410_dma_t*dma=(s3c2410_dma_t*)dev_id;
DPRINTK(__FUNCTION__"\n");
s3c2410_dma_done(dma);
}
在中断处理子程序中,调用了s3c2410_dma_done函数,该函数定义如下:
staticinlinevoids3c2410_dma_done(s3c2410_dma_t*dma)
{
dma_buf_t*buf=dma->curr;
dma_callback_tcallback;
if(buf->write)callback=dma->write.callback;
elsecallback=dma->read.callback;
#ifdefHOOK_LOST_INT
stop_dma_timer();
#endif
DPRINTK("IRQ:
b=%#xst=%ld\n",(int)buf->id,(long)dma->regs->DSTAT);
if(callback)
callback(buf->id,buf->size);
kfree(buf);
dma->active=0;
process_dma(dma);
}
最后在s3c2410_dma_done函数中,通过callback函数指针调用了DMA发送和读取的回调函数。
DMA写入和读取的两个回调函数audio_dmaout_done_callback,audio_dmain_done_callback会在后面说明。
其中DMA写入为音频输出,DMA读取为音频输入。
在调用audio_init_dma函数来对输出和输入音频缓冲区的DMA通道进行初始化设置时,如果返回失败,则会调用audio_clear_dma函数来释放已申请的DMA通道。
在audio_clear_dma函数中直接调用了s3c2410_free_dma函数来进行动作。
在/kernel/arch/arm/mach-s3c2410/dma.c文件中:
voids3c2410_free_dma(dmach_tchannel)
该函数中释放了已申请的DMA通道,并调用了free_irq函数来释放已分配的DMA发送和读取结束的中断号。
audio_dev_dsp=register_sound_dsp(&smdk2410_audio_fops,-1);
audio_dev_mixer=register_sound_mixer(&smdk2410_mixer_fops,-1);
在驱动模块的初始化函数最后调用了register_sound_dsp,和register_sound_mixer两个函数来分别注册驱动设备,前者注册为DSP设备,后者注册为混频器设备。
在/kernel/drivers/sound/sound_core.c文件中:
/**
* register_sound_dsp-registeraDSPdevice
* @fops:
Fileoperationsforthedriver
* @dev:
Unitnumbertoallocate
*
* AllocateaDSPdevice.UnitisthenumberoftheDSPrequested.
* Pass-1torequestthenextfreeDSPunit.Onsuccesstheallocated
* numberisreturned,onfailureanegativeerrorcodeisreturned.
*
* Thisfunctionallocatesboththeaudioanddspdeviceentriestogether
* andwillalwaysallocatethemasamatchingpair-egdsp3/audio3
*/
intregister_sound_dsp(structfile_operations*fops,intdev)
/**
* register_sound_mixer-registeramixerdevice
* @fops:
Fileoperationsforthedriver
* @dev:
Unitnumbertoallocate
*
* Allocateamixerdevice.Unitisthenumberofthemixerrequested.
* Pass-1torequestthenextfreemixerunit.Onsuccesstheallocated
* numberisreturned,onfailureanegativeerrorcodeisreturned.
*/
intregister_sound_mixer(structfile_operations*fops,intdev)
这两个函数的参数一样,fops为传给内核的file_operations结构中的接口函数,dev为分配的设备序号,设为-1表示由内核自动分配一个空闲的序号。
------------------------------------------------------------------------
紧接着就来看一下init_uda1341这个初始化UDA1341芯片的函数:
staticvoidinit_uda1341(void)
uda1341_volume=62-((DEF_VOLUME*61)/100);
uda1341_boost=0;
uda_sampling=DATA2_DEEMP_NONE;
uda_sampling&=~(DATA2_MUTE);
首先上来就是设定几个待会儿配置要用的参数。
参考UDA1341芯片datasheet后,可以知道uda1341_volume参数的含义,62表示音量设置表中有效音量的总档数,61表示音量总共有61档,DEF_VOLUME%表示所要调的音量的百分比大小,这样61*DEF_VOLUME%所得出的就是所要调的音量是音量总档数的第几档,由于音量设置表中列出值的是按衰减量递增的,所以刚才得到的音量档数需要在总档数下衰减多少才能得到呢?
显然只要将音量总档数减去所要调到的音量档数即可,即62-61*DEF_VOLUME%。
local_irq_save(flags);
同先前一样,调用该宏函数来保存IRQ中断使能状态,并禁止IRQ中断。
write_gpio_bit(GPIO_L3MODE,1);
write_gpio_bit(GPIO_L3CLOCK,1);
调用write_gpio_bit宏函数,将GPIO相应的引脚设为高电平或低电平。
这里是把GPIO_L3MODE和GPIO_L3CLOCK这两个引脚设为高电平。
local_irq_restore(flags);
同先前一样,调用该宏函数来恢复IRQ和FIQ的中断使能状态。
//*******************************************************
//* 2007.7.6
//*****************************************************
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- armlinux 学习 笔记 IIS 音频 驱动程序 分析
![提示](https://static.bdocx.com/images/bang_tan.gif)