君正4760B的linux audio(OSS)驱动分析
来源:互联网 发布:java垃圾回收机制详解 编辑:程序博客网 时间:2024/04/28 01:44
君正4760B的audio(OSS) 驱动分析
君正的audio驱动使用了传统的OSS模式, 真是BT,大家都在用ALSA,他
还用OSS, 在网上详细资料甚少,在这里记录一下自己的过程,希望有人能够用
到。
文章作者: http://blog.csdn.net/dyron, 文章不断完善中....
系统环境:
简介:
君正的codec有两种接法,一种使用内部的codec,另外一种使用外部的
codec, 我们使用的是内部codec, 连接codec的接口有三种, PCM, I2S,
AC-LINK, 我们使用的是I2S.
按照惯例,先统一一下术语:
AIC:
君正的AC’97 and I2S Controller
采样频率:
是指每秒采样的次数。 8k, 44.1k, 48k等等
量化精度:
量化精度是指对采样数据分析的精度,量化精度越高,声音越逼真。
连接方式
OSS的驱动结构
关于OSS架构,不用太多介绍了吧,OSS与ALSA不同,ALSA提供一堆接口与库来供上
层用户访问, OSS主要提供两个基本设备,dsp, mixer, mixer主要用于对codec的控制, dsp
主要用于播放和录音。
下边是OSS的注册流程图:
注册设备:
首先注册设备, arch/mips/mach-jz4760b/boards/m7r_3/m7r_3-audio.c中注册了audio用
到的switch_gpio设备和系统操作的接口, mixer设备等。
#if CONFIG_GPIO_HP_DETECTstruct gpio_switch_platform_data jz_hp_switch_data = {.name= "h2w",.gpio= GPIO_HEAD_DET,.state_on= "1",.state_off= "0",.valid_level = 0,};struct platform_device jz_hp_switch_device = {.name = "switch-gpio",.id = -1,.dev = {.platform_data = &jz_hp_switch_data,},};#endif
注册audio的switch-gpio设备,会在switch-gpio.c中调用,用于耳机插拔的检测,其中
GPIO_HEAD_DET宏定义了用于耳机检测的GPIO引脚。
static struct platform_device jz_dlv_device = {.name = "jz_dlv",.id = -1,.dev = {.platform_data = &jz_dlv_platform_data,},};/*-----------------------*/static int __init m7r_3_dlv_board_init(void){int ret = 0;ret = platform_device_register(&jz_dlv_device);return ret;}static jz_dlv_platform_data_t jz_dlv_platform_data = {.dlv_replay_volume_base = M7R_3_REPLAY_VOLUME_BASE,.dlv_record_volume_base = M7R_3_RECORD_VOLUME_BASE,/*set vaule ROUTE_COUNT will use default route*/.default_replay_route = ROUTE_COUNT,.default_record_route = ROUTE_COUNT,.default_call_record_route = ROUTE_COUNT,.dlv_set_device = m7r_3_dlv_set_device,.dlv_set_gpio_before_set_route = m7r_3_dlv_set_gpio_before_set_route,.dlv_set_gpio_after_set_route = m7r_3_dlv_set_gpio_after_set_route,.dlv_init_part = m7r_3_dlv_init_part,.dlv_reset_part = m7r_3_dlv_reset_part,.dlv_turn_off_part = m7r_3_dlv_turn_off_part,.dlv_shutdown_part = m7r_3_dlv_shutdown_part,.dlv_suspend_part = m7r_3_dlv_suspend_part,.dlv_resume_part = m7r_3_dlv_resume_part,.dlv_anti_pop_part = m7r_3_dlv_anti_pop_part,};
注册jz_dlv设备,主要是为了将jz_dlv_platform_data传给jz_dlv中使用。 在jz_dlv.c中
赋值,如下图所示:
static int jz_dlv_probe(struct platform_device *pdev){dlv_platform_data = pdev->dev.platform_data;return 0;}static struct platform_device jz_snd_device = {.name = "mixer",.id = -1,.dev = {.platform_data = &jz_snd_endpoints,},};/* - Sound device */
注册mixer设备, 在arch/mips/mach-jz4760b/common/platform.c中,主要用于调用到
jz47XX中的probe函数。 Endpoints结构如下:
static struct msm_snd_endpoints jz_snd_endpoints = {.endpoints = snd_endpoints_list,.num = ARRAY_SIZE(snd_endpoints_list),};
注册dsp及mixer。
Audio首先是注册上jzdlv_ioctl, 然后再进入mixer的probe, 注册mixer及dsp设备。
static int __init init_dlv(void){int retval;cpm_start_clock(CGM_AIC);spin_lock_init(&dlv_irq_lock);INIT_WORK(&dlv_irq_work, dlv_irq_work_handler);dlv_work_queue = create_singlethread_workqueue("dlv_irq_wq");if (!dlv_work_queue) {// this can not happen, if happen, we die!BUG();}register_jz_codecs((void *)jzdlv_ioctl);
首先在module_init中通过register_jz_codecs注册jzdlv_ioctl方法,jzdlv_ioctl中全是
对dsp操作的函数。
void register_jz_codecs(void *func){int i;ENTER();for (i = 0; i < NR_I2S; i++) {if (the_codecs[i].codecs_ioctrl == 0) {printk("register codec %x\n",(unsigned int)func);the_codecs[i].id = i;the_codecs[i].codecs_ioctrl = func;init_MUTEX(&(the_codecs[i].i2s_sem));break;}}fLEAVE();}
看上边可知,我们系统中只有一个codec,其实就是将jzdlv_ioctl赋值给the_codec[0],
并将the_codecs[0]的信号量进行初始化。
dlv_reset_part();retval = request_irq(IRQ_AIC, dlv_codec_irq, IRQF_DISABLED, "dlv_codec_irq", NULL);if (retval) {printk("JZ DLV: Could not get AIC CODEC irq %d\n", IRQ_AIC);return retval;}#ifdef CONFIG_HP_SENSE_DETECTretval = platform_driver_register(&jz_hp_switch_driver);if (retval) {printk("JZ HP Switch: Could net register headphone sense switch\n");return retval;}#endifretval = platform_driver_register(&jz_dlv_driver);if (retval) {printk("JZ CODEC: Could net register jz_dlv_driver\n");return retval;}return 0;}
后边申请了AIC的中断,用于处理中断及短路保护处理。 Platform_driver_register注册
了jz_dlv_driver驱动,就将前面所讲的jz_dlv_platform_data 赋值给了dlv_platform_data 。
static struct platform_driver snd_plat_driver = {.probe= init_jz_i2s,.driver= {.name= "mixer",.owner= THIS_MODULE,},.suspend= jz_i2s_suspend,.resume= jz_i2s_resume,.shutdown = jz_i2s_shutdown,};static int __init snd_init(void){return platform_driver_register(&snd_plat_driver);}
注册名为“mixer”的snd_plat_driver,与上文讲到的jz_snd_device匹配, 进入
init_jz_i2s函数。
static int __init init_jz_i2s(struct platform_device *pdev){struct i2s_codec *default_codec = &(the_codecs[0]);int errno;int fragsize;int fragstotal;cpm_start_clock(CGM_AIC);REG_AIC_I2SCR |= AIC_I2SCR_ESCLK;i2s_controller_init();if (default_codec->codecs_ioctrl == NULL) {printk("default_codec: not ready!");return -1;}default_codec->codecs_ioctrl(default_codec, CODEC_INIT, 0);if ((errno = probe_jz_i2s(&the_i2s_controller)) < 0) {return errno;}/* May be external CODEC need it ... * default_codec->codecs_ioctrl(default_codec, CODEC_SET_GPIO_PIN, 0); */attach_jz_i2s(the_i2s_controller);
首先将the_codec[0]赋值给了default_codec,此时the_codec[0]就是刚才注册的dsp设
备操作方法。 由于register_jz_codecs是在module_init中调用的,比probe要早,所以这个
时候一定注册成功了。
接着对AIC 的寄存器进行初始化,设置为I2S模式,内部codec模式等等。
此时调用DSP中的codec_init初始化codec设备。 初始完成codec后,进入probe_jz_i2s
对the_i2s_controller进行内存分配与基本的信息初始化。
接下来进入attach_jz_i2s,进行mixer与dsp设备注册。
static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller){char*name = NULL;intadev = 0; /* No of Audio device. */ENTER();name = controller->name;/* Initialize I2S CODEC and register /dev/mixer. */if (jz_i2s_codec_init(controller) <= 0) {goto mixer_failed;}/* Initialize AIC controller and reset it. */jz_i2s_reinit_hw(controller->i2s_codec,1);adev = register_sound_dsp(&jz_i2s_audio_fops, -1);if (adev < 0) {goto audio_failed;}controller->dev_audio = adev;LEAVE();}/* I2S codec initialisation. */static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller){int i;ENTER();for (i = 0; i < NR_I2S; i++) {the_codecs[i].private_data = controller;if (i2s_probe_codec(&the_codecs[i]) == 0) {break;}if ((the_codecs[i].dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, the_codecs[i].id)) < 0) {printk(KERN_ERR "JZ I2S: couldn't register mixer!\n");break;}}controller->i2s_codec = &the_codecs[0];LEAVE();return i;}
jz_i2s_codec_init中通过register_sound_mixer将jz_i2s_mixer_fops与the_codecs[0]设备
注册在一起。 最后将the_codecs[0]赋值给刚才分配内存的controller->i2s_codec。 这就完成
了mixer设备的注册, 下面看看jz_i2s_mixer_fops的函数。
static struct file_operations jz_i2s_mixer_fops = {owner:THIS_MODULE,ioctl:jz_i2s_ioctl_mixdev,open:jz_i2s_open_mixdev,write:jz_i2s_write_mixdev,};
这里open与write并没有做太多实质的内容, 主要通过ioctl控制mixer设备的声音大
小,route与standby等。
/* Initialize AIC controller and reset it. */jz_i2s_reinit_hw(controller->i2s_codec,1);adev = register_sound_dsp(&jz_i2s_audio_fops, -1);if (adev < 0) {goto audio_failed;}controller->dev_audio = adev;
回到attach_jz_i2s函数, 通过register_sound_dsp注册dsp设备, dsp的操作接口多一
点, 看下面结构体。
/* static struct file_operations jz_i2s_audio_fops */static struct file_operations jz_i2s_audio_fops = {owner:THIS_MODULE,open:jz_audio_open,release:jz_audio_release,write:jz_audio_write,read:jz_audio_read,ioctl:jz_audio_ioctl,mmap: jz_audio_mmap};
这里介绍一下君正dsp的读写操作方式, 有两种, 一种是众所周知的通过read&write
实现的, 另一种是通过mmap接口,mmap出去一段内存,应用程序直接写mmap的内存,
然后通过写入direct_info info 来触发驱动来更新,后者相对操作更快一些。
现在回到init_jz_i2s函数完成播放与录音的DMA内存初始化。
/* Now the command is not supported by DLV CODEC ... * default_codec->codecs_ioctrl(default_codec, CODEC_SET_VOLUME_TABLE, 0); */fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;fragstotal = JZCODEC_RW_BUFFER_TOTAL;audio_init_endpoint(&out_endpoint, fragsize, fragstotal);audio_init_endpoint(&in_endpoint, fragsize, fragstotal);printk("JZ I2S OSS audio driver initialized\n");LEAVE();
至此OSS系统的mixer与dsp都注册完成了,对于各种播放和录音的通路流程在下次再分析 。
ADUIO OSS 的读写buff 分析
- 君正4760B的linux audio(OSS)驱动分析
- 君正4760B的linux audio(OSS)驱动分析
- linux+audio(OSS)驱动的读写buff分析(jz4760B)
- linux+audio(OSS)驱动的读写buff分析(jz4760B)
- linux+audio(OSS)驱动的读写buff分析(jz4760B)
- Linux audio(OSS)子系统分析
- Linux audio(OSS)子系统分析
- OSS驱动
- linux audio(alsa) 驱动注册的简明流程.
- linux audio(alsa) 驱动注册的简明流程.
- android audio/linux alsa音频-应用与驱动的接口
- PCM5242从OSS切换到ALSA问题 【君正JZ4760】
- Linux的声卡驱动中ALSA与OSS的区别和简单流程介
- 基于uda341的OSS声卡驱动
- 音频audio/sound声卡驱动分析
- linux audio device driver 音频设备驱动讲解
- usbtouch的linux驱动分析
- linux驱动的Makefile分析
- wireless流程简明分析
- MaskEdit用法
- J2SE密码输入框
- linux audio(alsa) 驱动注册的简明流程.
- Eratoshenes 筛选素数算法
- 君正4760B的linux audio(OSS)驱动分析
- openfire+spark+smack
- Cloud computing —— 云计算
- 在使用svn的时候禁用svn保存密码功能
- Http请求的一个bug
- telerik mvc treeView crud 示例
- java集合总结
- 一个成功人士总结的六十条
- 【STL】list基础