2. MTK6737 7.0 Accdet驱动分析总结

来源:互联网 发布:iphone手机定位软件 编辑:程序博客网 时间:2024/06/05 10:54

MTK6737 Accdet驱动分析总结

注:本文多处转载,已难以找到出处

一、相关概念介绍

1、EINT+ACCDET检测中断

        EINT中断:主要用来检测耳机的插入和拔出,即plug in 和plug out

        ACCDET中断:主要用来检测耳机的事件类型,包括PLUG_OUT、PLUG_IN、MIC_BIAS(耳机上的mic)和HOOK_SWITCH(耳机按键)

        如果采用此方式,需要打开宏CONFIG_EINT_INT_IRQ

2、耳机按键的检测

       耳机按键的原理图如下



          PMIC有内部ADC通道可以读取不同的耳机电压,其中不同事件类型对应的电压如下:

        plug out:1.77<=voltage<=1.9

        mic bias:0.5v<=voltage<1.77v

        hook switch:0v<=voltage<=0.5v

        如上三个状态是通过ACCDET_STATE_RG寄存器获取的,通过2个bit A=bit1、B=bit0判断,对应如上的事件如下:

       plug out:1.77<=voltage<=1.9                 A=1,B=1,AB=3,

        mic bias:0.5v<=voltage<1.77v              A=0,B=1,AB=1,

        hook switch:0v<=voltage<=0.5v           A=0,B=0,AB=0,

       其中hook switch包括了up、middle、down三个按键,对应的电压如下;

        0v<=middle<=0.09v<=up<=0.24v<=down<=0.5v


二、耳机插入流程分析

          当插入耳机按键时,首先是触发了EINT中断,然后再触发ACCDET中断,这2个中断都是PMIC函数中注册的,属于PMIC端的中断。

          在probe初始化函数中注册了之前提到的两个中断函数,此为中断上半部分:

[cpp] view plain copy
  1. pmic_register_interrupt_callback(12, accdet_int_handler );  
  2. pmic_register_interrupt_callback(13, accdet_eint_int_handler );  

        然后又初始化了两个工作队列accdet_work和accdet_eint_work,对应如上两个中断的下半部分。


1、总的大体流程如下,具体的耳机事件插拔检测在下一副流程图会介绍,

       

 

2、这里主要介绍两种耳机模式:MIC_BIAS和HOOK SWITCH

      a、MIC_BIAS模式,即插入耳机后的默认模式,状态从PLUG_OUT----->MIC_BIAS,主要代码如下

         

[cpp] view plain copy
  1. case PLUG_OUT:  
  2. #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION  
  3.         pmic_pwrap_write(ACCDET_DEBOUNCE1, cust_headset_settings->debounce1);  
  4. #endif  
  5.         if (current_status == 0) {  
  6. #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION  
  7.             /*micbias always on during detected PIN recognition*/  
  8.             pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);  
  9.             pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_width);  
  10.             ACCDET_DEBUG("[Accdet]PIN recognition micbias always on!\n");  
  11.             ACCDET_DEBUG("[Accdet]before adc read, pin_adc_value = %d mv!\n", pin_adc_value);  
  12.             msleep(500);  
  13.             current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0) >> 6);    /*A=bit1; B=bit0*/  
  14.             if (current_status == 0 && show_icon_delay != 0) {  
  15.                 /*accdet_auxadc_switch(1);switch on when need to use auxadc read voltage*/  
  16.                 pin_adc_value = Accdet_PMIC_IMM_GetOneChannelValue(1);  
  17.                 ACCDET_DEBUG("[Accdet]pin_adc_value = %d mv!\n", pin_adc_value);  
  18.                 /*accdet_auxadc_switch(0);*/  
  19.                 if (180 > pin_adc_value && pin_adc_value > 90) {  /*90mv   ilegal headset*/  
  20.                     /*mt_set_gpio_out(GPIO_CAMERA_2_CMRST_PIN, GPIO_OUT_ONE);*/  
  21.                     /*ACCDET_DEBUG("[Accdet]PIN recognition change GPIO_OUT!\n");*/  
  22.                     mutex_lock(&accdet_eint_irq_sync_mutex);  
  23.                     if (1 == eint_accdet_sync_flag) {  
  24.                         cable_type = HEADSET_NO_MIC;  
  25.                         accdet_status = HOOK_SWITCH;  
  26.                         cable_pin_recognition = 1;  
  27.                         ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n",  
  28.                                  cable_pin_recognition);  
  29.                     } else {  
  30.                         ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  31.                     }  
  32.                     mutex_unlock(&accdet_eint_irq_sync_mutex);  
  33.                 } else {  
  34.                     mutex_lock(&accdet_eint_irq_sync_mutex);  
  35.                     if (1 == eint_accdet_sync_flag) {  
  36.                         cable_type = HEADSET_NO_MIC;  
  37.                         accdet_status = HOOK_SWITCH;  
  38.                     } else {  
  39.                         ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  40.                     }  
  41.                     mutex_unlock(&accdet_eint_irq_sync_mutex);  
  42.                 }  
  43.             }  
  44. #else  
  45.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  46.             if (1 == eint_accdet_sync_flag) {  
  47.                 cable_type = HEADSET_NO_MIC;  
  48.                 accdet_status = HOOK_SWITCH;  
  49.             } else {  
  50.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  51.             }  
  52.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  53. #endif  
  54.         } else if (current_status == 1) {  
  55.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  56.             if (1 == eint_accdet_sync_flag) {  
  57.                 accdet_status = MIC_BIAS;  
  58.                 cable_type = HEADSET_MIC;  
  59. #ifdef CONFIG_HEADSET_SUPPORT_FIVE_POLE  
  60.                 msleep(20);  
  61.                 if (pmic_pwrap_read(0x0F46) & 0x01) {  
  62.                     /*check 5 pole headset*/  
  63.                     ACCDET_DEBUG("[Accdet]check 5 pole headset: YES\n");  
  64.                     cable_type = HEADSET_FIVE_POLE;  
  65.                 }  
  66. #endif  
  67.                 /*AB=11 debounce=30ms*/  
  68.                 pmic_pwrap_write(ACCDET_DEBOUNCE3, cust_headset_settings->debounce3 * 30);  
  69.             } else {  
  70.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  71.             }  
  72.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  73.             pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);  
  74.             /*recover polling set AB 00-01*/  
  75. #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION  
  76.             pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));  
  77.             pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));  
  78. #endif  
  79.         } else if (current_status == 3) {  
  80.             ACCDET_DEBUG("[Accdet]PLUG_OUT state not change!\n");  
  81. #ifdef CONFIG_ACCDET_EINT  
  82.             ACCDET_DEBUG("[Accdet] do not send plug out event in plug out\n");  
  83. #else  
  84.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  85.             if (1 == eint_accdet_sync_flag) {  
  86.                 accdet_status = PLUG_OUT;  
  87.                 cable_type = NO_DEVICE;  
  88.             } else {  
  89.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  90.             }  
  91.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  92. #endif  
  93.         } else {  
  94.             ACCDET_DEBUG("[Accdet]PLUG_OUT can't change to this state!\n");  
  95.         }  
  96.         break;  

        b、HOOK_SWITCH模式,即耳机按键按下的状态,状态从MIC_BIAS----->HOOK_SWITCH,按键松开时HOOK_SWITCH----->MIC_BIAS,主要代码如下:
[cpp] view plain copy
  1. case MIC_BIAS:  
  2.         /*solution: resume hook switch debounce time*/  
  3.         pmic_pwrap_write(ACCDET_DEBOUNCE0, cust_headset_settings->debounce0);  
  4.   
  5.         if (current_status == 0) {  
  6.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  7.             if (1 == eint_accdet_sync_flag) {  
  8.                 while ((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT)  
  9.                     && (wait_clear_irq_times < 3)) {  
  10.                     ACCDET_DEBUG("[Accdet]check_cable_type: MIC BIAS clear IRQ on-going1....\n");  
  11.                     wait_clear_irq_times++;  
  12.                     msleep(20);  
  13.                 }  
  14.                 irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);  
  15.                 irq_temp = irq_temp & (~IRQ_CLR_BIT);  
  16.                 pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);  
  17.                 IRQ_CLR_FLAG = true;  
  18.                 accdet_status = HOOK_SWITCH;  
  19.             } else {  
  20.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  21.             }  
  22.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  23.             button_status = 1;  
  24.             if (button_status) {  
  25.                 mutex_lock(&accdet_eint_irq_sync_mutex);  
  26.                 if (1 == eint_accdet_sync_flag)  
  27.                     multi_key_detection(current_status);  
  28.                 else  
  29.                     ACCDET_DEBUG("[Accdet] multi_key_detection: Headset has plugged out\n");  
  30.                 mutex_unlock(&accdet_eint_irq_sync_mutex);  
  31.                 /*accdet_auxadc_switch(0);*/  
  32.                 /*recover  pwm frequency and duty*/  
  33.                 pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));  
  34.                 pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));  
  35.             }  
  36.         } else if (current_status == 1) {  
  37.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  38.             if (1 == eint_accdet_sync_flag) {  
  39.                 accdet_status = MIC_BIAS;  
  40.                 cable_type = HEADSET_MIC;  
  41.                 ACCDET_DEBUG("[Accdet]MIC_BIAS state not change!\n");  
  42.             } else {  
  43.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  44.             }  
  45.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  46.         } else if (current_status == 3) {  
  47. #if defined CONFIG_ACCDET_EINT || defined CONFIG_ACCDET_EINT_IRQ  
  48.             ACCDET_DEBUG("[Accdet]do not send plug ou in micbiast\n");  
  49.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  50.             if (1 == eint_accdet_sync_flag)  
  51.                 accdet_status = PLUG_OUT;  
  52.             else  
  53.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  54.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  55. #else  
  56.             mutex_lock(&accdet_eint_irq_sync_mutex);  
  57.             if (1 == eint_accdet_sync_flag) {  
  58.                 accdet_status = PLUG_OUT;  
  59.                 cable_type = NO_DEVICE;  
  60.             } else {  
  61.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");  
  62.             }  
  63.             mutex_unlock(&accdet_eint_irq_sync_mutex);  
  64. #endif  
  65.         } else {  
  66.             ACCDET_DEBUG("[Accdet]MIC_BIAS can't change to this state!\n");  
  67.         }  
  68.         break;  
  69. case HOOK_SWITCH: //按键松开后  
  70.         if (current_status == 0) {   
  71.             mutex_lock(&accdet_eint_irq_sync_mutex);   
  72.             if (1 == eint_accdet_sync_flag) {   
  73.                 /*for avoid 01->00 framework of Headset will report press key info for Audio*/   
  74.                 /*cable_type = HEADSET_NO_MIC;*/   
  75.                 /*accdet_status = HOOK_SWITCH;*/   
  76.                 ACCDET_DEBUG("[Accdet]HOOK_SWITCH state not change!\n");   
  77.             } else {   
  78.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");   
  79.             }   
  80.             mutex_unlock(&accdet_eint_irq_sync_mutex);   
  81.         } else if (current_status == 1) {   
  82.             mutex_lock(&accdet_eint_irq_sync_mutex);   
  83.             if (1 == eint_accdet_sync_flag) {   
  84.                 multi_key_detection(current_status);   
  85.                 accdet_status = MIC_BIAS;   
  86.                 cable_type = HEADSET_MIC;   
  87.             } else {   
  88.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");   
  89.             }   
  90.             mutex_unlock(&accdet_eint_irq_sync_mutex);   
  91.             /*accdet_auxadc_switch(0);*/   
  92. #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION   
  93.             cable_pin_recognition = 0;   
  94.             ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);   
  95.             pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));   
  96.             pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));   
  97. #endif   
  98.             /*solution: reduce hook switch debounce time to 0x400*/   
  99.             pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);   
  100.         } else if (current_status == 3) {   
  101.    
  102. #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION   
  103.             cable_pin_recognition = 0;   
  104.             ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);   
  105.             mutex_lock(&accdet_eint_irq_sync_mutex);   
  106.             if (1 == eint_accdet_sync_flag)   
  107.                 accdet_status = PLUG_OUT;   
  108.             else   
  109.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");   
  110.             mutex_unlock(&accdet_eint_irq_sync_mutex);   
  111. #endif   
  112. #if defined CONFIG_ACCDET_EINT || defined CONFIG_ACCDET_EINT_IRQ   
  113.             ACCDET_DEBUG("[Accdet] do not send plug out event in hook switch\n");   
  114.             mutex_lock(&accdet_eint_irq_sync_mutex);   
  115.             if (1 == eint_accdet_sync_flag)   
  116.                 accdet_status = PLUG_OUT;   
  117.             else   
  118.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");   
  119.             mutex_unlock(&accdet_eint_irq_sync_mutex);   
  120. #else   
  121.             mutex_lock(&accdet_eint_irq_sync_mutex);   
  122.             if (1 == eint_accdet_sync_flag) {   
  123.                 accdet_status = PLUG_OUT;   
  124.                 cable_type = NO_DEVICE;   
  125.             } else {   
  126.                 ACCDET_DEBUG("[Accdet] Headset has plugged out\n");   
  127.             }   
  128.             mutex_unlock(&accdet_eint_irq_sync_mutex);   
  129. #endif   
  130.         } else {   
  131.             ACCDET_DEBUG("[Accdet]HOOK_SWITCH can't change to this state!\n");   
  132.         }   
  133.         break;   

      代码对应的耳机按键处理流程如下:


三、总结

            耳机按键处理主要依靠2个中断EINT+ACCDET,然后按键检测依靠内部ADC检测,上报按键事件时,按下和松开都要通过send_key_event进行上报