3. MTK 底层耳机中断上报流程


转载自:http://blog.csdn.net/wangjun7121/article/details/62114085?locationNum=12&fps=1


【背景知识】:

国标耳机插头信号定义:             <<<【左声道】【右声道】【麦克】【地】
美标耳机插入信号定义:             <<<【左声道】【右声道】【地】【麦克】    

 
 
  • 1
  • 2
  • 3

【micbias】: 是用来在耳机有 Mic 的情况下,给 Mic 提供直流偏置,让其正常工作的。他使用 PWM 控制,用于降低麦克的功耗, 在不用麦克的情况下,可以降低轮询检测按键的功耗。
【PMCI ACCDET 模块】:是用来检测耳机类型及按键类型的。

【线控耳机原理图】:这里写图片描述

图片盗自百度,见谅。

解释下大概原理吧,首先这是个美标耳机,从左至右四段依次为
【左声道】【右声道】【地】【麦克】
左、右声道与麦克共地,重点看麦克与地之间,他在地之间并了 4 个元件,CPU 判断哪个按键的原理就是依靠麦克与地之间的按下按键时的不同的电阻值,导致麦克与地之间电压变化:
  MIC: 电阻很高(600Ω至10kΩ)
  接听/播放按键,按下时电阻为 0
  上一曲:按下时电阻为 600
  下一曲:按下时电阻为 220
在麦克线路上,上面会连接到 PMIC 的 Accdet 引脚,以及 micbias 引脚,micbias 会产生一个 PWM 波形,波峰时会进行录音,检查【麦克脚与地】之间的电压,从而判断现在是接上 MIC 还是按下按键了。

【检测电路解释】

MTK 检测耳机有两种方式:

  1. Accdet only
    此方式是让耳机 micbias 常开下,依靠 PMIC 内部中断来检测耳机处于的状态的。但此方式会带来耳机插入瞬间有 pop 杂音的出现。

  2. Accdet+EINT 方式
    此方式在耳机为插入时,micbias 是被 disable 的。利用中断让 AP 检测到有耳机插入后,在中断中打开 micbias,从而达到省功耗和减小杂音的效果的。待插入后,再检测耳机类型,检测走的路线还是通过 PMIC 的 accdet 内部中断。

/*

【背景知识】:
	国标耳机插头信号定义:				<<<【左声道】【右声道】【麦克】【地】
	美标耳机插入信号定义:				<<<【左声道】【右声道】【地】  【麦克】	
	
	【micbias】: 是用来在耳机有 Mic 的情况下,给 Mic 提供直流偏置,让其正常工作的。
				他使用 PWM 控制,用于降低麦克的功耗, 在不用麦克的情况下,可以降低
				轮询检测按键的功耗。
	【PMCI ACCDET 模块】:是用来检测耳机类型及按键类型的。

【耳机附件检测原理】:	Accessory detected<附件检测>
	1.Accdet only
		此方式是让耳机 micbias 常开下,依靠 PMIC 内部中断来检测耳机处于的状态的。但
		此方式会带来耳机插入瞬间有 pop 杂音的出现。

	2.Accdet+EINT 方式
		此方式在耳机为插入时,micbias 是被 disable 的。利用中断让 AP 检测到有耳机插入后,
		在中断中打开 micbias,从而达到省功耗和减小杂音的效果的。
		待插入后,再检测耳机类型,检测走的路线还是通过 PMIC 的 accdet 内部中断。
		耳机检测:
			Plug in: HP_EINT 触发中断 -> 插入检测 -> ACCDET检测耳机类型
			Plug out:HP_EINT 触发中断 -> 拔出检测
		HP_EINT:插入拔出检测
		ACCDET:检测耳机类型和按键
		UP:0.09<V<0.24
		MID: 0<V<0.09
		DOWN: 0.24<V<0.5


		ACCDET 内部有两个比较器,会根据传入的电压判断并产生中断。ACCDET 的输入电压即耳机 MIC PIN 的电压,内部比较器的输
	出分别对应 A/B 两个寄存器。
	ACCDET 内部两个比较器的 Vref 分别是 1.77V 和 0.4V(硬件决定的,不能修改),所以,对应的电压有 3 个范围:
		1.77V-1.9V: 未插入耳机的状态(AB = B11)
		0.4V-1.77V: 插入4段式(有 Mic)耳机时的状态(AB = B01)或者4段式按键松开状态
		0-0.4V : 	插入3段式耳机时的状态,或者4段式按键按下时的状态(AB = B00)		
					按键判断电压:
						【0v <= MD < 0.09V <= UP< 0.24V <= DW < 0.5V】
		耳机的状态会保存到 ACCDET 的寄存器中,当电压在任意 2 个范围间切换时,状态发生变化,ACCDET 产生中断,中断处理中
    读取状态寄存器的值,并根据状态的变化做相应的处理.


【耳机检测中断总结】:
    
    //Accdet_drv.c (mediatek\kernel\drivers\accdet)
    module_init(accdet_mod_init);
            platform_driver_register(&accdet_driver);
                            // static struct platform_driver accdet_driver = {
                            //     .probe      = accdet_probe, 
                            //     //.suspend  = accdet_suspend,
                            //     //.resume       = accdet_resume,
                            //     .remove   = accdet_remove,
                            //     .driver     = {
                            //     .name       = "Accdet_Driver",
                            // #ifdef CONFIG_PM
                            //     .pm         = &accdet_pm_ops,
                            // #endif
                            //     },
    
    //
    // 匹配后执行 
    accdet_probe(struct platform_device *dev)   
            //
            // Accdet.c (mediatek\platform\mt6582\kernel\drivers\accdet)
            mt_accdet_probe();
                    
                    // 0. 设置长按判断的时长
                    struct headset_key_custom* press_key_time = get_headset_key_custom_setting();
                                                                    /
                                                                    // Accdet_custom.c (mediatek\custom\hongyu82_wet_kk\kernel\headset\accdet)
                                                                    return &headset_key_custom_setting;
                                                                                //key press customization: long press time
                                                                                struct headset_key_custom headset_key_custom_setting = {
                                                                                    2000
                                                                                };
                    
                    
                    // 1,注册一个 switch 设备, 
                    accdet_data.name = "h2w";
                    accdet_data.index = 0;
                    accdet_data.state = NO_DEVICE;
                    // 获得耳机的 micbias 的 PWM 波形,这个波形用来采样,可以节约功耗
                    cust_headset_settings = get_cust_headset_settings();
                                                /
                                                //Accdet_custom.c (mediatek\custom\hongyu82_wet_kk\kernel\headset\accdet)
                                                get_cust_headset_settings(void)
                                                        static struct headset_mode_settings cust_headset_settings = {
                                                            0x900, 0x400, 1, 0x3f0, 0x800, 0x800, 0x20
                                                        };
                                                        ///
                                                        // struct headset_mode_settings{
                                                        //     int pwm_width;  // pwm frequence
                                                        //     int pwm_thresh; // pwm duty 
                                                        //     int fall_delay; // falling stable time
                                                        //     int rise_delay; // rising stable time
                                                        //     int debounce0;  // hook switch or double check debounce
                                                        //     int debounce1;  // mic bias debounce
                                                        //     int debounce3;  // plug out debounce
                                                        // };
                    // 注册一个 switch 设备,表示耳机状态
                    ret = switch_dev_register(&accdet_data);

                    /
                    // 2. 创建一个字符设备节点 
                    ret = alloc_chrdev_region(&accdet_devno, 0, 1, ACCDET_DEVNAME);
                    accdet_cdev = cdev_alloc();
                    accdet_cdev->owner = THIS_MODULE;
                    accdet_cdev->ops = accdet_get_fops();
                    ret = cdev_add(accdet_cdev, accdet_devno, 1);
                
                    //
                    // 3. 创建 /sys 目录下的文件节点
                    accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);
                    // if we want auto creat device node, we must call this
                    accdet_nor_device = device_create(accdet_class, NULL, accdet_devno, NULL, ACCDET_DEVNAME);  

                    ///
                    // 4. 创建一个 input 输入设备, 用于上报耳机按键事件,创建一个定时器
                    //      每 6s 自动关闭 micbias,达到省电目的。同时设置 input 输入设备上 
                    //      报的键值类型,并注册输入设备。
                    kpd_accdet_dev = input_allocate_device();
                
                    // 通过定时器每 6s 自动关闭 MICBIAS,达到省电目的,因为中断会唤醒使能
                    // micbias 检测耳机类型与 mic 及按键,如果没有 mic, 且不需要检测按键,则
                    // 可以直接关闭 micbias 偏置
                    init_timer(&micbias_timer);
                    micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
                    micbias_timer.function = &disable_micbias;
                                                disable_micbias(unsigned long a)
                                                        ret = queue_work(accdet_disable_workqueue, &accdet_disable_work);   
                                                                        /
                                                                        // 调用工作队列 
                                                                        disable_micbias_callback//(struct work_struct *work)
                                                                            // 如果插入的耳机没有 mic ,则直接关闭 PMIC 的 ACCDET 模块
                                                                            if(cable_type == HEADSET_NO_MIC)
                                                                                disable_accdet();
                                                                            else if(cable_type == HEADSET_MIC) 
                                                                                // 如果要检测键,则重新设置 pwm 

                    //define multi-key keycode
                    // 设置 input 输入设备上报的键值类型
                    __set_bit(EV_KEY, kpd_accdet_dev->evbit);
                    __set_bit(KEY_CALL, kpd_accdet_dev->keybit);
                    。。。 
                    // 注册 input 输入设备
                    input_register_device(kpd_accdet_dev)

                    
                    // 5. 注册工作队列,用于处理 ACCDET 模块识别按键流程 , 他会根据 PMIC 的状态,
                    //      判断是否有耳机插入,及耳机类型和按键类型,如果有按键,通过 input 上报
                    //      处理完后,同步更新 switch 模块的状态
                    accdet_workqueue = create_singlethread_workqueue("accdet");
                    INIT_WORK(&accdet_work, accdet_work_callback);
                                accdet_work_callback//(struct work_struct *work)
                                        ///
                                        // 通过 PMCI ,来识别插入是否有耳机插入,及耳机类型或按键类型,如果有按键的话,
                                        // 还需要判断长按、短按,然后通过 input 输入设备上报事件类型。
                                        check_cable_type();
                                            
                                            // 获得 PMIC 的内部电压比较器的 AB 状态
                                            //      1.77V-1.9V: 未插入耳机的状态(AB = B11)
                                            //      0.4V-1.77V: 插入4段式(有 Mic)耳机时的状态(AB = B01)或者4段式按键松开状态 
                                            //      0-0.4V :    插入3段式耳机时的状态,或者4段式按键按下时的状态(AB = B00)     
                                            //                  按键判断电压:
                                            //                      【0v <= MD < 0.09V <= UP< 0.24V <= DW < 0.5V】
                                            /
                                            current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //A=bit1; B=bit0

                                            //
                                            // 状态机转换:根据 AB 寄存器的值,来切换状态机,更新耳机状态,检测按键,上报按键
                                            switch(accdet_status)
                                                case PLUG_OUT:
                                                    ///
                                                    // 当前状态 AB = 00 : 插入3段式耳机时的状态,或者4段式按键按下时的状态(AB = B00)
                                                    if(current_status == 0)
                                                        // 如果有使能 ACCDET_PIN_RECOGNIZATION 的话,则一直打开 micbias 检测按键 
                                                        // 否则检测是否有 accdet 中断发生,如果有过,则更新当前耳机状态,耳机没有 mic 
                                                    
                                                    // 当前状态 AB = 01:插入4段式(有 Mic)耳机时的状态(AB = B01)或者4段式按键松开状态 
                                                    else if(current_status == 1)
                                                            // 发生了 accdet 中断,插入的是 4 段是耳机,更新状态
                                                            if(1 == eint_accdet_sync_flag)
                                                                accdet_status = MIC_BIAS;       
                                                                cable_type = HEADSET_MIC;
                                                            // 打开 micbias 偏置,用于检测按键,使能 mic, 具体录不录间,取决于上层
                                                    ///
                                                    // 当前状态 AB = 11: 未插入耳机的状态(AB = B11)
                                                    else if(current_status == 3)
                                                            // 发生了 accdet 中断,耳机状态有变化,更新状态
                                                            if(1 == eint_accdet_sync_flag) 
                                                                accdet_status = PLUG_OUT;       
                                                                cable_type = NO_DEVICE;
                                                            // 如果使用的是中断+Accdet 检测耳机,则关闭 Accdet 模块 
                                                            disable_accdet();

                                                case MIC_BIAS:
                                                    if(current_status == 0) 
                                                        // 首先读取 PMIC 的 Accdet 模块寄存器,判断有没有按键
                                                        // 长按短按事件判断 
                                                        multi_key = multi_key_detection();

                                                        // 判断按键,唤醒线程,上报按键,按键是根据 accdet 电压判断的
                                                        switch (multi_key) 
                                                            case SHORT_UP: 。。。 
                                                            case SHORT_MD: 。。。
                                                            case SHORT_DW: 。。。
                                                                    // 短按
                                                                    notify_sendKeyEvent(int event)
                                                                        wake_up(&send_event_wq);
                                                                            /
                                                                            // 延迟通过 input 输入设备上报事件类型
                                                                            sendKeyEvent(void *unuse)
                                                                                while(1)
                                                                                    wait_event_interruptible(send_event_wq, (atomic_read(&send_event_flag) != 0));
                                                                                    wake_lock_timeout(&accdet_key_lock, 2*HZ);    //set the wake lock.
                                                                                    // 根据不同的键值,通过 input 输入设备上报给系统
                                                                                    input_report_key(kpd_accdet_dev, KEY_ENDCALL, 1);
                                                                                    input_report_key(kpd_accdet_dev, KEY_ENDCALL, 0);
                                                                                    input_sync(kpd_accdet_dev)

                                                            case LONG_UP:  。。。
                                                            case LONG_MD:  。。。
                                                            case LONG_DW:  。。。 
                                                                    // 长按事件立即上报
                                                                    send_key_event(int keycode,int flag)
                                                                        // 1. 打电话状态,上报 KEY_VOLUMEDOWN/KEY_VOLUMEUP
                                                                        // 2. 非打电话状态,上报 KEY_NEXTSONG/KEY_PREVIOUSSONG
                                                            default: 。。。
                                                    else if(current_status == 1) 。。。
                                                    else if(current_status == 3) 。。。

                                                case HOOK_SWITCH:
                                                    if(current_status == 0) 。。。
                                                    else if(current_status == 1) 。。。
                                                    else if(current_status == 3)。。。
                                                case STAND_BY:
                                                    if(current_status == 3)。。。

                                        /
                                        // 切换 switch 模块的状态,如果 PMIC 的 ACCDET 发生中断话
                                        if(1 == eint_accdet_sync_flag)
                                            switch_set_state((struct switch_dev *)&accdet_data, cable_type);

                    ///
                    // 6. 创建一个按键上报线程,用于按键上报
                    init_waitqueue_head(&send_event_wq);
                    //start send key event thread
                    keyEvent_thread = kthread_run(sendKeyEvent, 0, "keyEvent_send");

                    /
                    // 7. 针对第一次启动,初始化 accdet 模块硬件,及注册 ap 的耳机中断函数,在 ap 耳机中断函数中
                    //          0. 变更耳机中断电平,保证插入/拔出都有中断
                    //          1. 如果是耳机插入,则创建一个定时器,用于耳机插入 6s 后,检测耳机是否有 mic 及按键,没
                    //              有则关闭 micbias。
                    //          2. 调用 accdet_eint_work_callback() 函数,用于打开/关闭 accdet 模块。
                    if (g_accdet_first == 1) 
                        ///
                        // 初始化 PMIC 的 accdet 模块硬件,主要通过 pmic_pwrap_write() 写寄存器值
                        accdet_init();   
                        
                        /
                        // 如果使用的是 accdet+中断 检测模式,则创建两个工作队列
                        queue_work(accdet_workqueue, &accdet_work); //schedule a work for the first detection   
                        accdet_eint_workqueue = create_singlethread_workqueue("accdet_eint");
                        //
                        // 插/拔耳机发生中断时,打开/关闭 accdet 模块
                        INIT_WORK(&accdet_eint_work, accdet_eint_work_callback);
                                                            ///
                                                            // 在发生 ap 耳机中断时,调用的回调函数,打开/关闭 accdet 模块
                                                            accdet_eint_work_callback(struct work_struct *work)
                                                                mt_eint_mask(CUST_EINT_ACCDET_NUM);
                                                                if (cur_eint_state == EINT_PIN_PLUG_IN)
                                                                    // 使能 accdet 模块 
                                                                else
                                                                    // 除能 accdet 模块
                                                                mt_eint_unmask(CUST_EINT_ACCDET_NUM);


                        ///
                        // 注册耳机检测的 ap 中断引脚状态及处理函数
                        accdet_setup_eint();
                                // 1. 设置引脚状态
                                mt_set_gpio_mode(GPIO_ACCDET_EINT_PIN, GPIO_ACCDET_EINT_PIN_M_EINT);
                                mt_set_gpio_dir(GPIO_ACCDET_EINT_PIN, GPIO_DIR_IN);
                                mt_set_gpio_pull_enable(GPIO_ACCDET_EINT_PIN, GPIO_PULL_DISABLE); //To disable GPIO PULL.
                                mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_CN);
                                // 2. 注册耳机中断处理函数
                                mt_eint_registration(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_TYPE, accdet_eint_func, 0);
                                                                                //
                                                                                // 耳机 ap 中断处理函数 
                                                                                accdet_eint_func(void)
                                                                                    //
                                                                                    // 1. 如果上次耳机状态是插入,那么这次就是拔出耳机了,
                                                                                    // 则需要变更中断触发电平,保证拔出插入都有中断发生
                                                                                    if(cur_eint_state ==  EINT_PIN_PLUG_IN )
                                                                                        // 变更中断引脚的触发电平,这样保证拔出,插入都能中断
                                                                                        if (CUST_EINT_ACCDET_TYPE == CUST_EINTF_TRIGGER_HIGH)
                                                                                            mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, (1));
                                                                                        else
                                                                                            mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, (0));
                                                                                        
                                                                                        // update the eint status 
                                                                                        cur_eint_state = EINT_PIN_PLUG_OUT;
                                                                                    else
                                                                                        // 同上, 首先变更中断电平 
                                                                                        。。。 
                                                                                        
                                                                                        
                                                                                        // 创建一个定时器,用于检测,在耳机插入后,
                                                                                        // 如果检测到没有 mic 且没有按键,则关闭 micbias 
                                                                                        init_timer(&micbias_timer);
                                                                                        micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
                                                                                        micbias_timer.function = &disable_micbias;
                                                                                        micbias_timer.data = ((unsigned long) 0 );
                                                                                        add_timer(&micbias_timer);

                                                                                    
                                                                                    // 2. 调用 accdet_eint_work_callback() 函数,打开 accdet 模块,用于
                                                                                    //     检测耳机类型,有无 mic, 以及耳机按键事件
                                                                                    ret = queue_work(accdet_eint_workqueue, &accdet_eint_work); 

                                // 3. 打开中断 
                                mt_eint_unmask(CUST_EINT_ACCDET_NUM); 

                        accdet_disable_workqueue = create_singlethread_workqueue("accdet_disable");
                        ///
                        // 创建一个工作队列,用于在没有 mic 且没按键的情况下关闭 micbias
                        INIT_WORK(&accdet_disable_work, disable_micbias_callback);
                                                    //
                                                    // 在上面的定时器函数中调用的,用于在特定情况下关闭 micbias 省电
                                                    disable_micbias_callback//(struct work_struct *work)
                                                        // 如果插入的耳机没有 mic ,则直接关闭 PMIC 的 ACCDET 模块
                                                        if(cable_type == HEADSET_NO_MIC)
                                                            disable_accdet();
                                                        else if(cable_type == HEADSET_MIC) 
                                                            // 如果要检测键,则重新设置 pwm 




*/
// accdet_drv.c
static struct platform_driver accdet_driver = {				| //mt_devs.c 
	.probe = accdet_probe,                                  | struct platform_device accdet_device = {
	/* .suspend      = accdet_suspend, */                   | 	.name	  ="Accdet_Driver",
	/* .resume               = accdet_resume, */            | 	.id		  = -1,
	.remove = accdet_remove,                                | 	//.dev    ={
	.driver = {                                             | 	//.release = accdet_dumy_release,
		   .name = "Accdet_Driver",                         | 	//}
#ifdef CONFIG_PM                                            | };
	.pm         = &accdet_pm_ops,                           |
#endif                                                      |
		   },                                               |
};

module_init(accdet_mod_init);
	accdet_mod_init()
		ret = platform_driver_register(&accdet_driver);

static int accdet_probe(struct platform_device *dev)
{
	int ret = 0;
#if defined(ACCDET_TS3A225E_PIN_SWAP)
	if (ts3a225e_i2c_client == NULL)
    	{
        	ACCDET_DEBUG("[Accdet]ts3a225e_i2c_client is NULL!\n");
        	return -EPROBE_DEFER;
    	}
#endif

#ifdef SW_WORK_AROUND_ACCDET_REMOTE_BUTTON_ISSUE
     //struct task_struct *keyEvent_thread = NULL;
	 //int error=0;
#endif
#if DEBUG_THREAD
		 struct platform_driver accdet_driver_hal = accdet_driver_func();
#endif

	struct headset_key_custom* press_key_time = get_headset_key_custom_setting();

	ACCDET_DEBUG("[Accdet]accdet_probe begin!\n");

	
	//------------------------------------------------------------------
	//   1,注册一个 switch 设备 				below register accdet as switch class
	//------------------------------------------------------------------	
	accdet_data.name = "h2w";
	accdet_data.index = 0;
	accdet_data.state = NO_DEVICE;

	cust_headset_settings = get_cust_headset_settings();
	
	ret = switch_dev_register(&accdet_data);
	if(ret)
	{
		ACCDET_DEBUG("[Accdet]switch_dev_register returned:%d!\n", ret);
		return 1;
	}
		
	//------------------------------------------------------------------
	// 	2,创建一个设备节点 			Create normal device for auido use
	//------------------------------------------------------------------
	ret = alloc_chrdev_region(&accdet_devno, 0, 1, ACCDET_DEVNAME);
	if (ret)
	{
		ACCDET_DEBUG("[Accdet]alloc_chrdev_region: Get Major number error!\n");			
	} 
		
	accdet_cdev = cdev_alloc();
    accdet_cdev->owner = THIS_MODULE;
    accdet_cdev->ops = accdet_get_fops();
    ret = cdev_add(accdet_cdev, accdet_devno, 1);
	if(ret)
	{
		ACCDET_DEBUG("[Accdet]accdet error: cdev_add\n");
	}
	
	accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);

    // if we want auto creat device node, we must call this
	accdet_nor_device = device_create(accdet_class, NULL, accdet_devno, NULL, ACCDET_DEVNAME);  
	
	//------------------------------------------------------------------
	// 	3. 输入设备						Create input device 
	//------------------------------------------------------------------
	kpd_accdet_dev = input_allocate_device();
	if (!kpd_accdet_dev) 
	{
		ACCDET_DEBUG("[Accdet]kpd_accdet_dev : fail!\n");
		return -ENOMEM;
	}
	//INIT the timer to disable micbias.
	// 通过定时器每 6s 关闭 MICBIAS
	init_timer(&micbias_timer);
	micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
	micbias_timer.function = &disable_micbias;
								disable_micbias//(unsigned long a)
								{
									  int ret = 0;
								      ret = queue_work(accdet_disable_workqueue, &accdet_disable_work);	
								      // INIT_WORK(&accdet_disable_work, disable_micbias_callback);
								      											disable_micbias_callback//(struct work_struct *work)
								      											{
								      												
								      											        if(cable_type == HEADSET_NO_MIC) {
								      														#ifdef ACCDET_PIN_RECOGNIZATION
								      														   show_icon_delay = 0;
								      														   cable_pin_recognition = 0;
								      														   ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
								      															pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
								      											    			pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_thresh);
								      														#endif
								      											            // setting pwm idle;
								      											   			pmic_pwrap_write(ACCDET_STATE_SWCTRL, pmic_pwrap_read(ACCDET_STATE_SWCTRL)&~ACCDET_SWCTRL_IDLE_EN);
								      														#ifdef ACCDET_PIN_SWAP
								      													    	//accdet_FSA8049_disable();  //disable GPIOxxx for PIN swap 
								      													    	//ACCDET_DEBUG("[Accdet] FSA8049 disable!\n");
								      														#endif
								      											                disable_accdet();
								      											                			disable_accdet//(void)
								      											                			{
								      											                				int irq_temp = 0;
								      											                				//sync with accdet_irq_handler set clear accdet irq bit to avoid  set clear accdet irq bit after disable accdet
								      											                				//disable accdet irq
								      											                				pmic_pwrap_write(INT_CON_ACCDET_CLR, RG_ACCDET_IRQ_CLR);
								      											                				clear_accdet_interrupt();
								      											                				udelay(200);
								      											                				mutex_lock(&accdet_eint_irq_sync_mutex);
								      											                				while(pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT)
								      											                				{
								      											                					ACCDET_DEBUG("[Accdet]check_cable_type: Clear interrupt on-going....\n");
								      											                					msleep(5);
								      											                				}
								      											                				irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
								      											                				irq_temp = irq_temp & (~IRQ_CLR_BIT);
								      											                				pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
								      											                				//ACCDET_DEBUG("[Accdet]disable_accdet:Clear interrupt:Done[0x%x]!\n", pmic_pwrap_read(ACCDET_IRQ_STS));	
								      											                				mutex_unlock(&accdet_eint_irq_sync_mutex);
								      											                			   // disable ACCDET unit
								      											                			   ACCDET_DEBUG("accdet: disable_accdet\n");
								      											                			   pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
								      											                			   #ifdef ACCDET_EINT
								      											                			   pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0);
								      											                			   pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
								      											                			   //disable clock and Analog control
								      											                			   //mt6331_upmu_set_rg_audmicbias1vref(0x0);
								      											                			   pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET); 
								      											                			   #endif
								      											                			   #ifdef ACCDET_EINT_IRQ
								      											                			   pmic_pwrap_write(ACCDET_STATE_SWCTRL, ACCDET_EINT_PWM_EN);
								      											                			   pmic_pwrap_write(ACCDET_CTRL, pmic_pwrap_read(ACCDET_CTRL)&(~(ACCDET_ENABLE)));
								      											                			   //mt_set_gpio_out(GPIO_CAMERA_2_CMRST_PIN, GPIO_OUT_ZERO);
								      											                			   #endif

								      											                			}
								      											                ACCDET_DEBUG("[Accdet] more than 5s MICBIAS : Disabled\n");
								      											        }
								      												#ifdef ACCDET_PIN_RECOGNIZATION
								      													else if(cable_type == HEADSET_MIC) {       
								      														pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
								      											    		pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_thresh);
								      														ACCDET_DEBUG("[Accdet]pin recog after 5s recover micbias polling!\n");
								      													}
								      												#endif
								      											}
								      if(!ret)
								      {
								  	    ACCDET_DEBUG("[Accdet]disable_micbias:accdet_work return:%d!\n", ret);  		
								      }
								}

	micbias_timer.data = ((unsigned long) 0 );

	//define multi-key keycode
	__set_bit(EV_KEY, kpd_accdet_dev->evbit);
	__set_bit(KEY_CALL, kpd_accdet_dev->keybit);
	__set_bit(KEY_ENDCALL, kpd_accdet_dev->keybit);
    __set_bit(KEY_NEXTSONG, kpd_accdet_dev->keybit);
    __set_bit(KEY_PREVIOUSSONG, kpd_accdet_dev->keybit);
    __set_bit(KEY_PLAYPAUSE, kpd_accdet_dev->keybit);
    __set_bit(KEY_STOPCD, kpd_accdet_dev->keybit);
	__set_bit(KEY_VOLUMEDOWN, kpd_accdet_dev->keybit);
    __set_bit(KEY_VOLUMEUP, kpd_accdet_dev->keybit);
	__set_bit(KEY_VOICECOMMAND, kpd_accdet_dev->keybit);
	
	kpd_accdet_dev->id.bustype = BUS_HOST;
	kpd_accdet_dev->name = "ACCDET";
	if(input_register_device(kpd_accdet_dev))
	{
		ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : fail!\n");
	}else
	{
		ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : success!!\n");
	} 
	//------------------------------------------------------------------
	// 							Create workqueue 
	//------------------------------------------------------------------	
	accdet_workqueue = create_singlethread_workqueue("accdet");
	INIT_WORK(&accdet_work, accdet_work_callback);
									accdet_work_callback//(struct work_struct *work)
									{

									    wake_lock(&accdet_irq_lock);
									    check_cable_type();
									    				// 通过 PMIC 来识别插入
									    				check_cable_type//(void)
									    				{
									    				    int current_status = 0;
									    					int irq_temp = 0; //for clear IRQ_bit
									    					int wait_clear_irq_times = 0;
									    				#ifdef ACCDET_PIN_RECOGNIZATION
									    				    int pin_adc_value = 0;
									    				#define PIN_ADC_CHANNEL 5
									    				#endif
									    				    
									    				    current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //A=bit1; B=bit0
									    				    ACCDET_DEBUG("[Accdet]accdet interrupt happen:[%s]current AB = %d\n", 
									    						accdet_status_string[accdet_status], current_status);
									    					    	
									    				    button_status = 0;
									    				    pre_status = accdet_status;

									    				    //ACCDET_DEBUG("[Accdet]check_cable_type: ACCDET_IRQ_STS = 0x%x\n", pmic_pwrap_read(ACCDET_IRQ_STS));
									    				    IRQ_CLR_FLAG = FALSE;
									    					switch(accdet_status)
									    				    {
									    				        case PLUG_OUT:
									    							  #ifdef ACCDET_PIN_RECOGNIZATION
									    							    pmic_pwrap_write(ACCDET_DEBOUNCE1, cust_headset_settings->debounce1);
									    							  #endif
									    				            if(current_status == 0)
									    				            {
									    								#ifdef ACCDET_PIN_RECOGNIZATION
									    								//micbias always on during detected PIN recognition
									    								pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
									    				    	  		pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_width);
									    						  		ACCDET_DEBUG("[Accdet]PIN recognition micbias always on!\n");
									    								 ACCDET_DEBUG("[Accdet]before adc read, pin_adc_value = %d mv!\n", pin_adc_value);
									    								 msleep(500);
									    								 current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //A=bit1; B=bit0
									    								 if (current_status == 0 && show_icon_delay != 0)
									    								 {
									    									//accdet_auxadc_switch(1);//switch on when need to use auxadc read voltage
									    									pin_adc_value = Accdet_PMIC_IMM_GetOneChannelValue(1);
									    									ACCDET_DEBUG("[Accdet]pin_adc_value = %d mv!\n", pin_adc_value);
									    									//accdet_auxadc_switch(0);			
									    									if (180 > pin_adc_value && pin_adc_value> 90) //90mv   ilegal headset
									    									{
									    				                        //mt_set_gpio_out(GPIO_CAMERA_2_CMRST_PIN, GPIO_OUT_ONE);
									    										//ACCDET_DEBUG("[Accdet]PIN recognition change GPIO_OUT!\n");
									    										mutex_lock(&accdet_eint_irq_sync_mutex);
									    										if(1 == eint_accdet_sync_flag) {
									    										cable_type = HEADSET_NO_MIC;
									    										accdet_status = HOOK_SWITCH;
									    										cable_pin_recognition = 1;
									    										ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
									    										}else {
									    											ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    										}		
									    										mutex_unlock(&accdet_eint_irq_sync_mutex);
									    									}
									    									else
									    									{
									    										mutex_lock(&accdet_eint_irq_sync_mutex);
									    										if(1 == eint_accdet_sync_flag) {
									    											cable_type = HEADSET_NO_MIC;
									    											accdet_status = HOOK_SWITCH;
									    										}else {
									    											ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    										}	
									    										mutex_unlock(&accdet_eint_irq_sync_mutex);
									    									}
									    								 }
									    								 #else
									    								  mutex_lock(&accdet_eint_irq_sync_mutex);
									    								  if(1 == eint_accdet_sync_flag) {
									    									cable_type = HEADSET_NO_MIC;
									    									accdet_status = HOOK_SWITCH;
									    								  }else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    								  }
									    								  mutex_unlock(&accdet_eint_irq_sync_mutex);
									    				           		 #endif
									    				            }
									    							else if(current_status == 1)
									    				            {
									    								mutex_lock(&accdet_eint_irq_sync_mutex);
									    								if(1 == eint_accdet_sync_flag) {
									    									accdet_status = MIC_BIAS;		
									    					         		cable_type = HEADSET_MIC;
									    									pmic_pwrap_write(ACCDET_DEBOUNCE3, cust_headset_settings->debounce3*30);//AB=11 debounce=30ms
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    								}
									    								mutex_unlock(&accdet_eint_irq_sync_mutex);
									    								//ALPS00038030:reduce the time of remote button pressed during incoming call
									    				                //solution: reduce hook switch debounce time to 0x400
									    				                pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
									    							   //recover polling set AB 00-01
									    							   #ifdef ACCDET_PIN_RECOGNIZATION
									    								pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
									    				                pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
									    							   #endif
									    								//#ifdef ACCDET_LOW_POWER
									    								//wake_unlock(&accdet_timer_lock);//add for suspend disable accdet more than 5S
									    								//#endif
									    				            }
									    				            else if(current_status == 3)
									    				            {
									    				                ACCDET_DEBUG("[Accdet]PLUG_OUT state not change!\n");
									    						    	#ifdef ACCDET_EINT
									    						    		ACCDET_DEBUG("[Accdet] do not send plug out event in plug out\n");
									    						    	#else
									    								mutex_lock(&accdet_eint_irq_sync_mutex);
									    								if(1 == eint_accdet_sync_flag) {
									    						    		accdet_status = PLUG_OUT;		
									    					           		cable_type = NO_DEVICE;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    								}
									    								mutex_unlock(&accdet_eint_irq_sync_mutex);
									    						        #endif
									    				            }
									    				            else
									    				            {
									    				                ACCDET_DEBUG("[Accdet]PLUG_OUT can't change to this state!\n"); 
									    				            }
									    				            break;

									    					    case MIC_BIAS:
									    					    //ALPS00038030:reduce the time of remote button pressed during incoming call
									    				            //solution: resume hook switch debounce time
									    				            pmic_pwrap_write(ACCDET_DEBOUNCE0, cust_headset_settings->debounce0);
									    							
									    				            if(current_status == 0)
									    				            {
									    							mutex_lock(&accdet_eint_irq_sync_mutex);
									    							if(1 == eint_accdet_sync_flag) {
									    								while((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && (wait_clear_irq_times<3))
									    					        	{
									    						          ACCDET_DEBUG("[Accdet]check_cable_type: MIC BIAS clear IRQ on-going1....\n");	
									    								  wait_clear_irq_times++;
									    								  msleep(5);
									    					        	}
									    								irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
									    								irq_temp = irq_temp & (~IRQ_CLR_BIT);
									    								pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
									    				            	IRQ_CLR_FLAG = TRUE;
									    						    	accdet_status = HOOK_SWITCH;
									    							}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							}
									    							mutex_unlock(&accdet_eint_irq_sync_mutex);
									    						    button_status = 1;
									    							if(button_status)
									    						    {	
									    							    mutex_lock(&accdet_eint_irq_sync_mutex);
									    								if(1 == eint_accdet_sync_flag) {   
									    									multi_key_detection(current_status);
									    								}else {
									    									ACCDET_DEBUG("[Accdet] multi_key_detection: Headset has plugged out\n");
									    								}
									    								mutex_unlock(&accdet_eint_irq_sync_mutex);
									    								//accdet_auxadc_switch(0);
									    							//recover  pwm frequency and duty
									    				                pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
									    				                pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
									    					     	}
									    					   	  }
									    				          else if(current_status == 1)
									    				          {
									    				          	 mutex_lock(&accdet_eint_irq_sync_mutex);
									    							 if(1 == eint_accdet_sync_flag) {
									    				                accdet_status = MIC_BIAS;		
									    					            cable_type = HEADSET_MIC;
									    				                ACCDET_DEBUG("[Accdet]MIC_BIAS state not change!\n");
									    							 }else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 }
									    							 mutex_unlock(&accdet_eint_irq_sync_mutex);
									    				          }
									    				          else if(current_status == 3)
									    				          {
									    						   #if defined ACCDET_EINT || defined ACCDET_EINT_IRQ
									    						   		ACCDET_DEBUG("[Accdet]do not send plug ou in micbiast\n");
									    						        mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    						   			accdet_status = PLUG_OUT;
									    							 	}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    						   #else
									    						   		mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    						   			accdet_status = PLUG_OUT;		
									    					          		cable_type = NO_DEVICE;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    						   #endif
									    				          }
									    				          else
									    				           {
									    				               ACCDET_DEBUG("[Accdet]MIC_BIAS can't change to this state!\n"); 
									    				           }
									    				          break;

									    					case HOOK_SWITCH:
									    				            if(current_status == 0)
									    				            {
									    								mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    									//for avoid 01->00 framework of Headset will report press key info for Audio
									    									//cable_type = HEADSET_NO_MIC;
									    						        	//accdet_status = HOOK_SWITCH;
									    				                	ACCDET_DEBUG("[Accdet]HOOK_SWITCH state not change!\n");
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    					    	}
									    				            else if(current_status == 1)
									    				            {
									    								mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {			
									    									multi_key_detection(current_status);
									    									accdet_status = MIC_BIAS;		
									    					        		cable_type = HEADSET_MIC;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    								//accdet_auxadc_switch(0);
									    								#ifdef ACCDET_PIN_RECOGNIZATION
									    								cable_pin_recognition = 0;
									    								ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
									    								pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
									    				                pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
									    								#endif
									    						//ALPS00038030:reduce the time of remote button pressed during incoming call
									    				         //solution: reduce hook switch debounce time to 0x400
									    				                pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
									    								//#ifdef ACCDET_LOW_POWER
									    								//wake_unlock(&accdet_timer_lock);//add for suspend disable accdet more than 5S
									    								//#endif
									    				            }
									    				            else if(current_status == 3)
									    				            {
									    				            	
									    				             #ifdef ACCDET_PIN_RECOGNIZATION
									    							 	cable_pin_recognition = 0;
									    							 	ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
									    							    mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    							 	accdet_status = PLUG_OUT;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    							 #endif
									    				             #if defined ACCDET_EINT || defined ACCDET_EINT_IRQ
									    							 	ACCDET_DEBUG("[Accdet] do not send plug out event in hook switch\n"); 
									    							 	mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    							 		accdet_status = PLUG_OUT;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    							 #else
									    							 	mutex_lock(&accdet_eint_irq_sync_mutex);
									    						        if(1 == eint_accdet_sync_flag) {
									    						      		accdet_status = PLUG_OUT;		
									    						      		cable_type = NO_DEVICE;
									    								}else {
									    									ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 	}
									    							 	mutex_unlock(&accdet_eint_irq_sync_mutex);
									    						     #endif
									    				            }
									    				            else
									    				            {
									    				                ACCDET_DEBUG("[Accdet]HOOK_SWITCH can't change to this state!\n"); 
									    				            }
									    				            break;			
									    					case STAND_BY:
									    							if(current_status == 3)
									    							{
									    				                 #if defined ACCDET_EINT || defined ACCDET_EINT_IRQ
									    										ACCDET_DEBUG("[Accdet]accdet do not send plug out event in stand by!\n");
									    						    	 #else
									    								 		mutex_lock(&accdet_eint_irq_sync_mutex);
									    						       			 if(1 == eint_accdet_sync_flag) {
									    											accdet_status = PLUG_OUT;		
									    											cable_type = NO_DEVICE;
									    										 }else {
									    											ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
									    							 			 }
									    							 			mutex_unlock(&accdet_eint_irq_sync_mutex);
									    							    #endif
									    							 }
									    							 else
									    							{
									    									ACCDET_DEBUG("[Accdet]STAND_BY can't change to this state!\n"); 
									    							}
									    							break;
									    							
									    							default:
									    								ACCDET_DEBUG("[Accdet]check_cable_type: accdet current status error!\n");
									    							break;
									    										
									    						}
									    							
									    						if(!IRQ_CLR_FLAG)
									    						{
									    							mutex_lock(&accdet_eint_irq_sync_mutex);
									    							if(1 == eint_accdet_sync_flag) {
									    								while((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && (wait_clear_irq_times<3))
									    								{
									    								  ACCDET_DEBUG("[Accdet]check_cable_type: Clear interrupt on-going2....\n");
									    								  wait_clear_irq_times++;
									    								  msleep(5);
									    								}
									    							}
									    							irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
									    							irq_temp = irq_temp & (~IRQ_CLR_BIT);
									    							pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
									    							mutex_unlock(&accdet_eint_irq_sync_mutex);
									    							IRQ_CLR_FLAG = TRUE;
									    							//ACCDET_DEBUG("[Accdet]check_cable_type:Clear interrupt:Done[0x%x]!\n", pmic_pwrap_read(ACCDET_IRQ_STS));	
									    							
									    						}
									    						else
									    						{
									    							IRQ_CLR_FLAG = FALSE;
									    						}

									    						ACCDET_DEBUG("[Accdet]cable type:[%s], status switch:[%s]->[%s]\n",
									    				        accdet_report_string[cable_type], accdet_status_string[pre_status], 
									    				        accdet_status_string[accdet_status]);
									    				}

									#ifdef ACCDET_PIN_SWAP
									#ifdef ACCDET_PIN_RECOGNIZATION
									        if (cable_pin_recognition == 1)
									        {
									            cable_pin_recognition = 0;
									            accdet_FSA8049_disable();
									            cable_type = HEADSET_NO_MIC;
									            accdet_status = PLUG_OUT;
									        }
									#endif
									#endif	
										mutex_lock(&accdet_eint_irq_sync_mutex);
									    if(1 == eint_accdet_sync_flag) {
											switch_set_state((struct switch_dev *)&accdet_data, cable_type);
									    }else {
											ACCDET_DEBUG("[Accdet] Headset has plugged out don't set accdet state\n");
										}
										mutex_unlock(&accdet_eint_irq_sync_mutex);
										ACCDET_DEBUG( " [accdet] set state in cable_type  status\n");

									    wake_unlock(&accdet_irq_lock);
									}

		
    //------------------------------------------------------------------
	//							wake lock
	//------------------------------------------------------------------
	wake_lock_init(&accdet_suspend_lock, WAKE_LOCK_SUSPEND, "accdet wakelock");
    wake_lock_init(&accdet_irq_lock, WAKE_LOCK_SUSPEND, "accdet irq wakelock");
    wake_lock_init(&accdet_key_lock, WAKE_LOCK_SUSPEND, "accdet key wakelock");
	wake_lock_init(&accdet_timer_lock, WAKE_LOCK_SUSPEND, "accdet timer wakelock");
#if DEBUG_THREAD
 	 if((ret = accdet_create_attr(&accdet_driver_hal.driver))!=0)
	 {
		ACCDET_DEBUG("create attribute err = %d\n", ret);
	
	 }
#endif
	 pmic_register_interrupt_callback(12,accdet_int_handler);					//PMIC ACCDET_EINT 中断处理函数
	 pmic_register_interrupt_callback(13,accdet_eint_int_handler);  			//PMIC ACCDET_NEGV 中断处理函数

	 long_press_time = press_key_time->headset_long_press_time;

	ACCDET_DEBUG("[Accdet]accdet_probe : ACCDET_INIT\n");  
	// 只在第一次运行初始化
	if (g_accdet_first == 1) 
	{	
		long_press_time_ns = (s64)long_press_time * NSEC_PER_MSEC;
		
		eint_accdet_sync_flag = 1;
		#ifdef ACCDET_EINT_IRQ
          accdet_eint_workqueue = create_singlethread_workqueue("accdet_eint");
	      INIT_WORK(&accdet_eint_work, accdet_eint_work_callback);
		  accdet_disable_workqueue = create_singlethread_workqueue("accdet_disable");
	      INIT_WORK(&accdet_disable_work, disable_micbias_callback);
        #endif
	    //Accdet Hardware Init
		accdet_init();   
		accdet_pmic_Read_Efuse_HPOffset();

		//INIT_WORK(&accdet_work, accdet_work_callback);
		queue_work(accdet_workqueue, &accdet_work); //schedule a work for the first detection				
		#ifdef ACCDET_EINT
		  accdet_disable_workqueue = create_singlethread_workqueue("accdet_disable");
	      INIT_WORK(&accdet_disable_work, disable_micbias_callback);
          accdet_eint_workqueue = create_singlethread_workqueue("accdet_eint");
	      INIT_WORK(&accdet_eint_work, accdet_eint_work_callback);

	      // AP 中断设置 
	      accdet_setup_eint();
	      			accdet_setup_eint//(void)
	      			{
	      				int ret;
	      			#ifdef CONFIG_OF
	      				u32 ints[2]={0,0};
	      				struct device_node *node;
	      			#endif
	      				/*configure to GPIO function, external interrupt*/
	      			    ACCDET_DEBUG("[Accdet]accdet_setup_eint\n");
	      				mt_set_gpio_mode(GPIO_ACCDET_EINT_PIN, GPIO_ACCDET_EINT_PIN_M_EINT);
	      			    mt_set_gpio_dir(GPIO_ACCDET_EINT_PIN, GPIO_DIR_IN);
	      			    mt_set_gpio_pull_enable(GPIO_ACCDET_EINT_PIN, GPIO_PULL_DISABLE); //To disable GPIO PULL.

	      			#ifdef CONFIG_OF
	      			    node = of_find_compatible_node(NULL,NULL,"mediatek, ACCDET-eint");
	      				if(node) {
	      			        of_property_read_u32_array(node,"debounce",ints,ARRAY_SIZE(ints));
	      					gpiopin = ints[0];
	      					headsetdebounce = ints[1];
	      					mt_gpio_set_debounce(gpiopin,headsetdebounce);
	      					accdet_irq = irq_of_parse_and_map(node,0);
	      					ret = request_irq(accdet_irq,accdet_eint_func,IRQF_TRIGGER_NONE,"ACCDET-eint",NULL);
	      					if(ret>0){
	      			            ACCDET_DEBUG("[Accdet]EINT IRQ LINE NOT AVAILABLE\n");
	      					}else{
	      						ACCDET_DEBUG("[Accdet]accdet set EINT finished, accdet_irq=%d, headsetdebounce=%d \n", accdet_irq, headsetdebounce);
	      					}
	      				}
	      				else {
	      			        ACCDET_DEBUG("[Accdet]%s can't find compatible node\n", __func__);
	      				}
	      			#else
	      				mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_CN);
	      				mt_eint_registration(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_TYPE, accdet_eint_func, 0);
	      				ACCDET_DEBUG("[Accdet]accdet set EINT finished, accdet_eint_num=%d, accdet_eint_debounce_en=%d, accdet_eint_polarity=%d\n", CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_EN, CUST_EINT_ACCDET_TYPE);
	      				mt_eint_unmask(CUST_EINT_ACCDET_NUM);  
	      			#endif

	      				return 0;
	      			}

        #endif
		g_accdet_first = 0;
	}
	
        ACCDET_DEBUG("[Accdet]accdet_probe done!\n");
//#ifdef ACCDET_PIN_SWAP
	//pmic_pwrap_write(0x0400, 0x1000); 
	//ACCDET_DEBUG("[Accdet]accdet enable VRF28 power!\n");
//#endif
		
	    return 0;
}



{			
		   //accdet.c
	return mt_accdet_probe();
					cust_headset_settings = get_cust_headset_settings();
					
					accdet_data.name = "h2w";
					accdet_data.index = 0;
					accdet_data.state = NO_DEVICE;
					ret = switch_dev_register(&accdet_data);
					
					//------------------------------------------------------------------
					// 							Create normal device for auido use
					//------------------------------------------------------------------
					ret = alloc_chrdev_region(&accdet_devno, 0, 1, ACCDET_DEVNAME);
					if (ret)
					{
						ACCDET_DEBUG("[Accdet]alloc_chrdev_region: Get Major number error!\n");			
					} 
						
					accdet_cdev = cdev_alloc();
				    accdet_cdev->owner = THIS_MODULE;
				    accdet_cdev->ops = accdet_get_fops();
				    ret = cdev_add(accdet_cdev, accdet_devno, 1);
					if(ret)
					{
						ACCDET_DEBUG("[Accdet]accdet error: cdev_add\n");
					}
					
					accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);

				    // if we want auto creat device node, we must call this
					accdet_nor_device = device_create(accdet_class, NULL, accdet_devno, NULL, ACCDET_DEVNAME); 

						//------------------------------------------------------------------
						// 							Create input device 
						//------------------------------------------------------------------
						kpd_accdet_dev = input_allocate_device();
						if (!kpd_accdet_dev) 
						{
							ACCDET_DEBUG("[Accdet]kpd_accdet_dev : fail!\n");
							return -ENOMEM;
						}
						//INIT the timer to disable micbias.
						init_timer(&micbias_timer);
						micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
						micbias_timer.function = &disable_micbias;
						micbias_timer.data = ((unsigned long) 0 );

						//define multi-key keycode
						__set_bit(EV_KEY, kpd_accdet_dev->evbit);
						__set_bit(KEY_CALL, kpd_accdet_dev->keybit);
						__set_bit(KEY_ENDCALL, kpd_accdet_dev->keybit);
					    __set_bit(KEY_NEXTSONG, kpd_accdet_dev->keybit);
					    __set_bit(KEY_PREVIOUSSONG, kpd_accdet_dev->keybit);
					    __set_bit(KEY_PLAYPAUSE, kpd_accdet_dev->keybit);
					    __set_bit(KEY_STOPCD, kpd_accdet_dev->keybit);
						__set_bit(KEY_VOLUMEDOWN, kpd_accdet_dev->keybit);
					    __set_bit(KEY_VOLUMEUP, kpd_accdet_dev->keybit);
						__set_bit(KEY_VOICECOMMAND, kpd_accdet_dev->keybit);
						
						kpd_accdet_dev->id.bustype = BUS_HOST;
						kpd_accdet_dev->name = "ACCDET";
						if(input_register_device(kpd_accdet_dev))
						{
							ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : fail!\n");
						}else
						{
							ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : success!!\n");
						} 




					//INIT the timer to disable micbias.
					// 通过定时器每 6s 关闭 MICBIAS
					init_timer(&micbias_timer);
					micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
					micbias_timer.function = &disable_micbias;
												ret = queue_work(accdet_disable_workqueue, &accdet_disable_work);	
														disable_micbias_callback()
															disable_accdet();
																   //sync with accdet_irq_handler set clear accdet irq bit to avoid  set clear accdet irq bit after disable accdet
																   //disable accdet irq
																   clear_accdet_interrupt();
																   
																   // 关闭 ACCDET
																   #ifdef ACCDET_EINT
																   pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0);
																   // disables PWM of ACCDET comparator
																   pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);	
																   //disable clock and Analog control
																   //mt6331_upmu_set_rg_audmicbias1vref(0x0);
																   pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET); 
																   #endif
					accdet_workqueue = create_singlethread_workqueue("accdet");
					INIT_WORK(&accdet_work, accdet_work_callback);
					
					pmic_register_interrupt_callback(12,accdet_int_handler);			//PMIC ACCDET_EINT 中断处理函数
	 				pmic_register_interrupt_callback(13,accdet_eint_int_handler);  		//PMIC ACCDET_NEGV 中断处理函数
					
					// 只在第一次运行初始化
					if (g_accdet_first == 1) 
					{
						eint_accdet_sync_flag = 1;
						
						// Accdet Hardware Init
						accdet_init();
						    // disable ACCDET unit
							pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
							pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
							pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0x0);
							pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET);
					
						queue_work(accdet_workqueue, &accdet_work); //schedule a work for the first detection
										// 应该只执行一次
										accdet_work_callback()
												check_cable_type();
												
												
												
												
						accdet_disable_workqueue = create_singlethread_workqueue("accdet_disable");
					  	INIT_WORK(&accdet_disable_work, disable_micbias_callback); 	// 跟定时器一样的函数
					  	accdet_eint_workqueue = create_singlethread_workqueue("accdet_eint");
					  	INIT_WORK(&accdet_eint_work, accdet_eint_work_callback);
				  	
				  		// AP 中断设置 
				  		accdet_setup_eint();
					  			/*configure to GPIO function, external interrupt*/
								ACCDET_DEBUG("[Accdet]accdet_setup_eint\n");
								mt_set_gpio_mode(GPIO_ACCDET_EINT_PIN, GPIO_ACCDET_EINT_PIN_M_EINT);
								mt_set_gpio_dir(GPIO_ACCDET_EINT_PIN, GPIO_DIR_IN);
								mt_set_gpio_pull_enable(GPIO_ACCDET_EINT_PIN, GPIO_PULL_DISABLE); //To disable GPIO PULL.
					  	
					  			mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_CN);
					  			// 设置 AP 中断处理函数
								mt_eint_registration(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_TYPE, accdet_eint_func, 0);
											accdet_eint_func()
													if(cur_eint_state ==  EINT_PIN_PLUG_IN ) 
													{
														cur_eint_state = EINT_PIN_PLUG_OUT;
													}
													else
													{
														cur_eint_state = EINT_PIN_PLUG_IN;
														mod_timer(&micbias_timer, jiffies + MICBIAS_DISABLE_TIMER);
													}
													ret = queue_work(accdet_eint_workqueue, &accdet_eint_work);	
																accdet_eint_work_callback()
																		if (cur_eint_state == EINT_PIN_PLUG_IN) 
																		{
																			accdet_init();// do set pwm_idle on in accdet_init
																			//set PWM IDLE  on
																			// 此处使能 MICBIAS ?
																			pmic_pwrap_write(ACCDET_STATE_SWCTRL, (pmic_pwrap_read(ACCDET_STATE_SWCTRL)|ACCDET_SWCTRL_IDLE_EN));
																			//enable ACCDET unit
																			enable_accdet(ACCDET_SWCTRL_EN); 
																		}
																		else
																		{
																			disable_accdet();			   
																			headset_plug_out();
																					accdet_status = PLUG_OUT;
		    																		cable_type = NO_DEVICE;
																		}
																		// 再次使能 AP 中断
																		mt_eint_unmask(CUST_EINT_ACCDET_NUM);
								// 使能 AP 中断
								mt_eint_unmask(CUST_EINT_ACCDET_NUM);  
						g_accdet_first = 0;
					}

				  	
				  	
					
}

// AP 中断使能 MICBIAS 之后,会走 Accdet 检测,PMIC 会产生中断,调用中断函数
accdet_int_handler()
	accdet_irq_handler()
				if((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT)) {
					//clear ACCDET IRQ in accdet register
					clear_accdet_interrupt();
				}
				if (accdet_status == MIC_BIAS){
					//accdet_auxadc_switch(1);
					pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
				 	pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_width));
				}
				accdet_workqueue_func();  
				while(((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && 
					   (accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))) {
				}












  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值