Loading sound/soc/codecs/wcd-mbhc-adc.c +101 −26 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) u8 adc_result = 0; int output_mv = 0; int retry = 3; u8 adc_en = 0; pr_debug("%s: enter\n", __func__); Loading @@ -73,6 +74,8 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); /* Set ADC to continuous measurement */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 1); /* Read ADC Enable bit to restore after adc measurement */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); /* Disable ADC_ENABLE bit */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); /* Disable MBHC FSM */ Loading @@ -92,6 +95,8 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); } /* Restore ADC Enable */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); /* Get voltage from ADC result */ output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc)); Loading @@ -106,13 +111,16 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) u8 adc_timeout = 0; u8 adc_complete = 0; u8 adc_result = 0; int retry = 5; int retry = 6; int ret = 0; int output_mv = 0; u8 adc_en = 0; pr_debug("%s: enter\n", __func__); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); /* Read ADC Enable bit to restore after adc measurement */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); /* Trigger ADC one time measurement */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); Loading @@ -122,8 +130,8 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); while (retry--) { /* wait for 3msec to get adc results */ usleep_range(3000, 3100); /* wait for 600usec to get adc results */ usleep_range(600, 610); /* check for ADC Timeout */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_TIMEOUT, adc_timeout); Loading @@ -145,6 +153,9 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) break; } /* Restore ADC Enable */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); if (retry <= 0) { pr_err("%s: adc complete: %d, adc timeout: %d\n", __func__, adc_complete, adc_timeout); Loading Loading @@ -352,8 +363,13 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, /* Bump up MB2 to 2.7V */ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, mbhc->mbhc_cfg->mbhc_micbias, true); usleep_range(10000, 10100); output_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/relesae for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); Loading Loading @@ -521,7 +537,11 @@ static int wcd_mbhc_get_plug_type(struct wcd_mbhc *mbhc) { int result_mv = 0; result_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/release for HEADSET type during correct work. */ result_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); return wcd_mbhc_get_plug_from_adc(result_mv); } Loading Loading @@ -564,7 +584,8 @@ static void wcd_correct_swch_plug(struct work_struct *work) goto correct_plug_type; } /* Find plug type */ plug_type = wcd_mbhc_get_plug_type(mbhc); output_mv = wcd_measure_adc_continuous(mbhc); plug_type = wcd_mbhc_get_plug_from_adc(output_mv); /* * Report plug type if it is either headset or headphone Loading @@ -578,6 +599,17 @@ static void wcd_correct_swch_plug(struct work_struct *work) WCD_MBHC_RSC_UNLOCK(mbhc); } /* * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, * so that btn press/release interrupt can be generated. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET || mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); } correct_plug_type: timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); while (!time_after(jiffies, timeout)) { Loading @@ -587,10 +619,6 @@ static void wcd_correct_swch_plug(struct work_struct *work) wcd_micbias_disable(mbhc); goto exit; } if (mbhc->btn_press_intr) { wcd_cancel_btn_work(mbhc); mbhc->btn_press_intr = false; } /* allow sometime and re-check stop requested again */ msleep(20); Loading @@ -602,7 +630,12 @@ static void wcd_correct_swch_plug(struct work_struct *work) } msleep(180); output_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/release for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); /* * instead of hogging system by contineous polling, wait for * sometime and re-check stop request again. Loading Loading @@ -674,11 +707,10 @@ static void wcd_correct_swch_plug(struct work_struct *work) * and if there is not button press without * release */ if (((mbhc->current_plug != if ((mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) && (mbhc->current_plug != MBHC_PLUG_TYPE_ANC_HEADPHONE)) && !mbhc->btn_press_intr) { MBHC_PLUG_TYPE_ANC_HEADPHONE)) { if (plug_type == MBHC_PLUG_TYPE_HEADSET) pr_debug("%s: cable is %s headset\n", __func__, Loading @@ -691,12 +723,6 @@ static void wcd_correct_swch_plug(struct work_struct *work) } } if (!wrk_complete) { if (mbhc->btn_press_intr) { pr_debug("%s: Can be slow insertion of headphone\n", __func__); wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } /* * If plug_tye is headset, we might have already reported either * in detect_plug-type or in above while loop, no need to report Loading Loading @@ -726,11 +752,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) pr_debug("%s: Switch level is low\n", __func__); goto exit; } if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { pr_debug("%s: insertion of headphone with swap\n", __func__); wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", __func__, plug_type, wrk_complete, mbhc->btn_press_intr); Loading @@ -742,9 +764,16 @@ static void wcd_correct_swch_plug(struct work_struct *work) wcd_mbhc_find_plug_and_report(mbhc, plug_type); WCD_MBHC_RSC_UNLOCK(mbhc); enable_supply: /* * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, * so that btn press/release interrupt can be generated. * For other plug type, clear the bit. */ if (plug_type == MBHC_PLUG_TYPE_HEADSET || plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); else WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); if (mbhc->mbhc_cb->mbhc_micbias_control) wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); Loading @@ -762,6 +791,18 @@ static void wcd_correct_swch_plug(struct work_struct *work) WCD_MBHC_RSC_UNLOCK(mbhc); } /* * Enable ADC COMPLETE interrupt for HEADPHONE. * Btn release may happen after the correct work, ADC COMPLETE * interrupt needs to be captured to correct plug type. */ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { WCD_MBHC_RSC_LOCK(mbhc); wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, true); WCD_MBHC_RSC_UNLOCK(mbhc); } if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); Loading @@ -775,6 +816,13 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) pr_debug("%s: enter\n", __func__); WCD_MBHC_RSC_LOCK(mbhc); /* * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one * when HEADPHONE is removed. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) mbhc->extn_cable_hph_rem = true; WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); Loading @@ -789,12 +837,39 @@ static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; pr_debug("%s: enter\n", __func__); /* * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one * when HEADPHONE is removed. */ if (mbhc->extn_cable_hph_rem == true) { mbhc->extn_cable_hph_rem = false; pr_debug("%s: leave\n", __func__); return IRQ_HANDLED; } WCD_MBHC_RSC_LOCK(mbhc); /* * If current plug is headphone then there is no chance to * get ADC complete interrupt, so connected cable should be * headset not headphone. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); WCD_MBHC_RSC_UNLOCK(mbhc); return IRQ_HANDLED; } if (!mbhc->mbhc_cfg->detect_extn_cable) { pr_debug("%s: Returning as Extension cable feature not enabled\n", __func__); WCD_MBHC_RSC_UNLOCK(mbhc); return IRQ_HANDLED; } WCD_MBHC_RSC_LOCK(mbhc); pr_debug("%s: Disable electrical headset insertion interrupt\n", __func__); wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); Loading sound/soc/codecs/wcd-mbhc-v2.c +16 −1 Original line number Diff line number Diff line Loading @@ -520,6 +520,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, { struct snd_soc_codec *codec = mbhc->codec; bool is_pa_on = false; u8 fsm_en = 0; WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); Loading Loading @@ -646,8 +647,16 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->compute_impedance && (mbhc->mbhc_cfg->linein_th != 0) && (!is_pa_on)) { /* Set MUX_CTL to AUTO for Z-det */ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); mbhc->mbhc_cb->compute_impedance(mbhc, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && mbhc->zl < MAX_IMPED) && (mbhc->zr > mbhc->mbhc_cfg->linein_th && Loading Loading @@ -906,6 +915,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); mbhc->extn_cable_hph_rem = false; wcd_mbhc_report_plug(mbhc, 0, jack_type); } else if (!detection_type) { Loading @@ -915,6 +925,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) /* Disable HW FSM */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); mbhc->extn_cable_hph_rem = false; } mbhc->in_swch_irq_handler = false; Loading Loading @@ -1094,8 +1105,11 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) * If current plug is headphone then there is no chance to * get btn release interrupt, so connected cable should be * headset not headphone. * For ADC MBHC, ADC_COMPLETE interrupt will be generated * in this case. So skip the check here. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { if (!WCD_MBHC_DETECTION && mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); goto exit; Loading Loading @@ -1856,6 +1870,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, mbhc->btn_press_intr = false; mbhc->is_hs_recording = false; mbhc->is_extn_cable = false; mbhc->extn_cable_hph_rem = false; mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->wcd_mbhc_regs = wcd_mbhc_regs; Loading sound/soc/codecs/wcd-mbhc-v2.h +1 −0 Original line number Diff line number Diff line Loading @@ -528,6 +528,7 @@ struct wcd_mbhc { bool is_extn_cable; bool skip_imped_detection; bool is_btn_already_regd; bool extn_cable_hph_rem; struct snd_soc_codec *codec; /* Work to perform MBHC Firmware Read */ Loading Loading
sound/soc/codecs/wcd-mbhc-adc.c +101 −26 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) u8 adc_result = 0; int output_mv = 0; int retry = 3; u8 adc_en = 0; pr_debug("%s: enter\n", __func__); Loading @@ -73,6 +74,8 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); /* Set ADC to continuous measurement */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 1); /* Read ADC Enable bit to restore after adc measurement */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); /* Disable ADC_ENABLE bit */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); /* Disable MBHC FSM */ Loading @@ -92,6 +95,8 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); } /* Restore ADC Enable */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); /* Get voltage from ADC result */ output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc)); Loading @@ -106,13 +111,16 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) u8 adc_timeout = 0; u8 adc_complete = 0; u8 adc_result = 0; int retry = 5; int retry = 6; int ret = 0; int output_mv = 0; u8 adc_en = 0; pr_debug("%s: enter\n", __func__); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); /* Read ADC Enable bit to restore after adc measurement */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); /* Trigger ADC one time measurement */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); Loading @@ -122,8 +130,8 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); while (retry--) { /* wait for 3msec to get adc results */ usleep_range(3000, 3100); /* wait for 600usec to get adc results */ usleep_range(600, 610); /* check for ADC Timeout */ WCD_MBHC_REG_READ(WCD_MBHC_ADC_TIMEOUT, adc_timeout); Loading @@ -145,6 +153,9 @@ static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) break; } /* Restore ADC Enable */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); if (retry <= 0) { pr_err("%s: adc complete: %d, adc timeout: %d\n", __func__, adc_complete, adc_timeout); Loading Loading @@ -352,8 +363,13 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, /* Bump up MB2 to 2.7V */ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, mbhc->mbhc_cfg->mbhc_micbias, true); usleep_range(10000, 10100); output_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/relesae for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); Loading Loading @@ -521,7 +537,11 @@ static int wcd_mbhc_get_plug_type(struct wcd_mbhc *mbhc) { int result_mv = 0; result_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/release for HEADSET type during correct work. */ result_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); return wcd_mbhc_get_plug_from_adc(result_mv); } Loading Loading @@ -564,7 +584,8 @@ static void wcd_correct_swch_plug(struct work_struct *work) goto correct_plug_type; } /* Find plug type */ plug_type = wcd_mbhc_get_plug_type(mbhc); output_mv = wcd_measure_adc_continuous(mbhc); plug_type = wcd_mbhc_get_plug_from_adc(output_mv); /* * Report plug type if it is either headset or headphone Loading @@ -578,6 +599,17 @@ static void wcd_correct_swch_plug(struct work_struct *work) WCD_MBHC_RSC_UNLOCK(mbhc); } /* * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, * so that btn press/release interrupt can be generated. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET || mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); } correct_plug_type: timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); while (!time_after(jiffies, timeout)) { Loading @@ -587,10 +619,6 @@ static void wcd_correct_swch_plug(struct work_struct *work) wcd_micbias_disable(mbhc); goto exit; } if (mbhc->btn_press_intr) { wcd_cancel_btn_work(mbhc); mbhc->btn_press_intr = false; } /* allow sometime and re-check stop requested again */ msleep(20); Loading @@ -602,7 +630,12 @@ static void wcd_correct_swch_plug(struct work_struct *work) } msleep(180); output_mv = wcd_measure_adc_continuous(mbhc); /* * Use ADC single mode to minimize the chance of missing out * btn press/release for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); /* * instead of hogging system by contineous polling, wait for * sometime and re-check stop request again. Loading Loading @@ -674,11 +707,10 @@ static void wcd_correct_swch_plug(struct work_struct *work) * and if there is not button press without * release */ if (((mbhc->current_plug != if ((mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) && (mbhc->current_plug != MBHC_PLUG_TYPE_ANC_HEADPHONE)) && !mbhc->btn_press_intr) { MBHC_PLUG_TYPE_ANC_HEADPHONE)) { if (plug_type == MBHC_PLUG_TYPE_HEADSET) pr_debug("%s: cable is %s headset\n", __func__, Loading @@ -691,12 +723,6 @@ static void wcd_correct_swch_plug(struct work_struct *work) } } if (!wrk_complete) { if (mbhc->btn_press_intr) { pr_debug("%s: Can be slow insertion of headphone\n", __func__); wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } /* * If plug_tye is headset, we might have already reported either * in detect_plug-type or in above while loop, no need to report Loading Loading @@ -726,11 +752,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) pr_debug("%s: Switch level is low\n", __func__); goto exit; } if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { pr_debug("%s: insertion of headphone with swap\n", __func__); wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", __func__, plug_type, wrk_complete, mbhc->btn_press_intr); Loading @@ -742,9 +764,16 @@ static void wcd_correct_swch_plug(struct work_struct *work) wcd_mbhc_find_plug_and_report(mbhc, plug_type); WCD_MBHC_RSC_UNLOCK(mbhc); enable_supply: /* * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, * so that btn press/release interrupt can be generated. * For other plug type, clear the bit. */ if (plug_type == MBHC_PLUG_TYPE_HEADSET || plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); else WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); if (mbhc->mbhc_cb->mbhc_micbias_control) wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); Loading @@ -762,6 +791,18 @@ static void wcd_correct_swch_plug(struct work_struct *work) WCD_MBHC_RSC_UNLOCK(mbhc); } /* * Enable ADC COMPLETE interrupt for HEADPHONE. * Btn release may happen after the correct work, ADC COMPLETE * interrupt needs to be captured to correct plug type. */ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { WCD_MBHC_RSC_LOCK(mbhc); wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, true); WCD_MBHC_RSC_UNLOCK(mbhc); } if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); Loading @@ -775,6 +816,13 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) pr_debug("%s: enter\n", __func__); WCD_MBHC_RSC_LOCK(mbhc); /* * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one * when HEADPHONE is removed. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) mbhc->extn_cable_hph_rem = true; WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); Loading @@ -789,12 +837,39 @@ static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; pr_debug("%s: enter\n", __func__); /* * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one * when HEADPHONE is removed. */ if (mbhc->extn_cable_hph_rem == true) { mbhc->extn_cable_hph_rem = false; pr_debug("%s: leave\n", __func__); return IRQ_HANDLED; } WCD_MBHC_RSC_LOCK(mbhc); /* * If current plug is headphone then there is no chance to * get ADC complete interrupt, so connected cable should be * headset not headphone. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); WCD_MBHC_RSC_UNLOCK(mbhc); return IRQ_HANDLED; } if (!mbhc->mbhc_cfg->detect_extn_cable) { pr_debug("%s: Returning as Extension cable feature not enabled\n", __func__); WCD_MBHC_RSC_UNLOCK(mbhc); return IRQ_HANDLED; } WCD_MBHC_RSC_LOCK(mbhc); pr_debug("%s: Disable electrical headset insertion interrupt\n", __func__); wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); Loading
sound/soc/codecs/wcd-mbhc-v2.c +16 −1 Original line number Diff line number Diff line Loading @@ -520,6 +520,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, { struct snd_soc_codec *codec = mbhc->codec; bool is_pa_on = false; u8 fsm_en = 0; WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); Loading Loading @@ -646,8 +647,16 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->compute_impedance && (mbhc->mbhc_cfg->linein_th != 0) && (!is_pa_on)) { /* Set MUX_CTL to AUTO for Z-det */ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); mbhc->mbhc_cb->compute_impedance(mbhc, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && mbhc->zl < MAX_IMPED) && (mbhc->zr > mbhc->mbhc_cfg->linein_th && Loading Loading @@ -906,6 +915,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); mbhc->extn_cable_hph_rem = false; wcd_mbhc_report_plug(mbhc, 0, jack_type); } else if (!detection_type) { Loading @@ -915,6 +925,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) /* Disable HW FSM */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); mbhc->extn_cable_hph_rem = false; } mbhc->in_swch_irq_handler = false; Loading Loading @@ -1094,8 +1105,11 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) * If current plug is headphone then there is no chance to * get btn release interrupt, so connected cable should be * headset not headphone. * For ADC MBHC, ADC_COMPLETE interrupt will be generated * in this case. So skip the check here. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { if (!WCD_MBHC_DETECTION && mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); goto exit; Loading Loading @@ -1856,6 +1870,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, mbhc->btn_press_intr = false; mbhc->is_hs_recording = false; mbhc->is_extn_cable = false; mbhc->extn_cable_hph_rem = false; mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->wcd_mbhc_regs = wcd_mbhc_regs; Loading
sound/soc/codecs/wcd-mbhc-v2.h +1 −0 Original line number Diff line number Diff line Loading @@ -528,6 +528,7 @@ struct wcd_mbhc { bool is_extn_cable; bool skip_imped_detection; bool is_btn_already_regd; bool extn_cable_hph_rem; struct snd_soc_codec *codec; /* Work to perform MBHC Firmware Read */ Loading