【转载】【非常好】耳机中断驱动检测代码流程.c

/*

【背景知识】:
	国标耳机插头信号定义:				<<<【左声道】【右声道】【麦克】【地】
	美标耳机插入信号定义:				<<<【左声道】【右声道】【地】  【麦克】	
	
	【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.09owner = 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)))) {
				}











你可能感兴趣的:(mtk源码分析_accdet)