2. MTK6737 7.0 Accdet驱动分析总结
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初始化函数中注册了之前提到的两个中断函数,此为中断上半部分:
- pmic_register_interrupt_callback(12, accdet_int_handler );
- 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,主要代码如下
- case PLUG_OUT:
- #ifdef CONFIG_ACCDET_PIN_RECOGNIZATION
- pmic_pwrap_write(ACCDET_DEBOUNCE1, cust_headset_settings->debounce1);
- #endif
- if (current_status == 0) {
- #ifdef CONFIG_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;
- #ifdef CONFIG_HEADSET_SUPPORT_FIVE_POLE
- msleep(20);
- if (pmic_pwrap_read(0x0F46) & 0x01) {
- /*check 5 pole headset*/
- ACCDET_DEBUG("[Accdet]check 5 pole headset: YES\n");
- cable_type = HEADSET_FIVE_POLE;
- }
- #endif
- /*AB=11 debounce=30ms*/
- pmic_pwrap_write(ACCDET_DEBOUNCE3, cust_headset_settings->debounce3 * 30);
- } else {
- ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
- }
- mutex_unlock(&accdet_eint_irq_sync_mutex);
- pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
- /*recover polling set AB 00-01*/
- #ifdef CONFIG_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
- } else if (current_status == 3) {
- ACCDET_DEBUG("[Accdet]PLUG_OUT state not change!\n");
- #ifdef CONFIG_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;
b、HOOK_SWITCH模式,即耳机按键按下的状态,状态从MIC_BIAS----->HOOK_SWITCH,按键松开时HOOK_SWITCH----->MIC_BIAS,主要代码如下:
- case MIC_BIAS:
- /*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(20);
- }
- 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 CONFIG_ACCDET_EINT || defined CONFIG_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 CONFIG_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
- /*solution: reduce hook switch debounce time to 0x400*/
- pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
- } else if (current_status == 3) {
- #ifdef CONFIG_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 CONFIG_ACCDET_EINT || defined CONFIG_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;
代码对应的耳机按键处理流程如下:
三、总结
耳机按键处理主要依靠2个中断EINT+ACCDET,然后按键检测依靠内部ADC检测,上报按键事件时,按下和松开都要通过send_key_event进行上报。