当前位置:首页 > s3c2440 uda1341驱动分析
1, 驱动架构:
驱动分两个层次,上层是平台设备驱动,底层是audio驱动与mixer驱动。 (1)标准的平台设备驱动结构,probe与remove两个函数。 probe:
获得平台资源->申请内存区域->io内存重映射->获得并使能时钟->设置gpio口->初始化iis总线-> 初始化uda1341->audio dma初始化->注册dsp和mixer->释放内存区域。 代码及注释:
static int s3c2410iis_probe(struct platform_device *pdev) { struct resource *res; unsigned long flags; int ret;
DPRINTK(\//获得平台设备资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) {
printk(KERN_INFO PFX \emory region resouce\\n\return -ENOENT; }
//申请可用内存
res = request_mem_region(res->start, RESSIZE(res), pdev->name); if(res == 0){
printk(KERN_INFO PFX \emory region.\\n\return -ENOENT; }
//io内存重映射
iis_base = ioremap(res->start, RESSIZE(res)); if(iis_base == 0){
printk(KERN_INFO PFX \ap() io memory region.\\n\ -EINVAL; goto free_mem_region; }
//获得时钟资源
iis_clock = clk_get(&pdev->dev, \if (iis_clock == NULL) {
printk(KERN_INFO PFX \ source\\n\return -ENOENT; }
/*********************modify by lfc************************/ clk_enable(iis_clock);//使能时钟
/***********************end add*************************/ //禁用本地中断,gpio口设置,恢复中断 local_irq_save(flags);
/* GPB 4: L3CLOCK, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP); s3c2410_gpio_pullup(S3C2410_GPB4,1);
/* GPB 3: L3DATA, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP); /* GPB 2: L3MODE, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP); s3c2410_gpio_pullup(S3C2410_GPB2,1); /* GPE 3: I2SSDI */
s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI); s3c2410_gpio_pullup(S3C2410_GPE3,1); /* GPE 0: I2SLRCK */
s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK); s3c2410_gpio_pullup(S3C2410_GPE0,1); /* GPE 1: I2SSCLK */
s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK); s3c2410_gpio_pullup(S3C2410_GPE1,1); /* GPE 2: CDCLK */
s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK); s3c2410_gpio_pullup(S3C2410_GPE2,1); /* GPE 4: I2SSDO */
s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO); s3c2410_gpio_pullup(S3C2410_GPE4,1); local_irq_restore(flags);
init_s3c2410_iis_bus();//初始化iis init_uda1341();//初始化uda1341 //初始化dma ch1 ch2
output_stream.dma_ch = DMA_CH2;
if (!audio_init_dma(&output_stream, \audio_clear_dma(&output_stream,&s3c2410iis_dma_out); printk( KERN_WARNING AUDIO_NAME_VERBOSE \return -EBUSY; }
input_stream.dma_ch = DMA_CH1;
if (!audio_init_dma(&input_stream, \audio_clear_dma(&input_stream,&s3c2410iis_dma_in); printk( KERN_WARNING AUDIO_NAME_VERBOSE \return -EBUSY; }
//注册dsp及mixer
audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1); printk(AUDIO_NAME_VERBOSE \//释放内存区域 free_mem_region:
release_mem_region(res->start, RESSIZE(res)); return 0; } remove:
禁用时钟->取消dsp mixer注册->清除dma
static int s3c2410iis_remove(struct platform_device *dev) { DPRINTK(\ove\\n\if (iis_clock != NULL){ clk_disable(iis_clock); clk_put(iis_clock); iis_clock = NULL; }
unregister_sound_dsp(audio_dev_dsp); unregister_sound_mixer(audio_dev_mixer);
audio_clear_dma(&output_stream,&s3c2410iis_dma_out);
audio_clear_dma(&input_stream,&s3c2410iis_dma_in); /* input */ printk(AUDIO_NAME_VERBOSE \return 0; }
uda1341的初始化:
设置gpio口->uda1341复位->uda1341设置 static void init_uda1341(void) {
/* GPB 4: L3CLOCK */ /* GPB 3: L3DATA */ /* GPB 2: L3MODE */ unsigned long flags;
DPRINTK(\
uda1341_volume = 62 - ((DEF_VOLUME * 61) / 100); uda1341_boost = 0;
// uda_sampling = DATA2_DEEMP_NONE; // uda_sampling &= ~(DATA2_MUTE); local_irq_save(flags);
s3c2410_gpio_setpin(S3C2410_GPB2,1);//L3MODE=1 s3c2410_gpio_setpin(S3C2410_GPB4,1);//L3CLOCK=1 local_irq_restore(flags);
uda1341_l3_address(UDA1341_REG_STATUS);
uda1341_l3_data(0x40 | STAT0_SC_384FS | STAT0_IF_MSB|STAT0_DC_FILTER); // reset uda1341
uda1341_l3_data(STAT1 | STAT1_ADC_ON | STAT1_DAC_ON); uda1341_l3_address(UDA1341_REG_DATA0);
// uda1341_l3_data(DATA0 |DATA0_VOLUME(0x0)); // maximum volume
uda1341_l3_data(DATA0 | DATA0_VOLUME(uda1341_volume));//lfc
uda1341_l3_data(DATA1 |DATA1_BASS(uda1341_boost)| DATA1_TREBLE(0)); uda1341_l3_data((DATA2 |DATA2_DEEMP_NONE) &~(DATA2_MUTE)); uda1341_l3_data(EXTADDR(EXT2));
uda1341_l3_data(EXTDATA(EXT2_MIC_GAIN(0x6)) | EXT2_MIXMODE_CH1);//input channel 1 select(input channel 2 off) }
(2)底层的audio和mixer其实就是字符设备,完成file_operations结构体后在上面说的probe函数中注册 audio驱动: fops结构体
static struct file_operations smdk2410_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 }; write:
判断打开标志是否可写->判断BUFFER内存空间是否可用->判断阻塞方式还是非阻塞方式->循环写入内存块,并将写好的内存块加入dma队列->返回传输字节数
static ssize_t smdk2410_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) {
const char *buffer0 = buffer;
audio_stream_t *s = &output_stream; int chunksize, ret = 0;
DPRINTK(\%d\\n\switch (file->f_flags & O_ACCMODE) { case O_WRONLY: case O_RDWR: break; default:
DPRINTK(\return -EPERM; }
if (!s->buffers && audio_setup_buf(s)){ DPRINTK(\return -ENOMEM; }
count &= ~0x03;
共分享92篇相关文档