diff options
| -rw-r--r-- | sound/soc/codecs/wcd-mbhc-v2.c | 166 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd-mbhc-v2.h | 9 | ||||
| -rwxr-xr-x | sound/soc/codecs/wcd9335.c | 18 |
3 files changed, 170 insertions, 23 deletions
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 445b7e8a2345..ade9c704ea48 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -32,7 +32,9 @@ #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ - SND_JACK_UNSUPPORTED | SND_JACK_MECHANICAL) + SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \ + SND_JACK_UNSUPPORTED) + #define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ SND_JACK_BTN_4 | SND_JACK_BTN_5 ) @@ -49,6 +51,7 @@ #define MAX_IMPED 60000 #define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50 +#define ANC_DETECT_RETRY_CNT 7 static int det_extn_cable_en; module_param(det_extn_cable_en, int, @@ -568,8 +571,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, if (mbhc->micbias_enable) { if (mbhc->mbhc_cb->mbhc_micbias_control) mbhc->mbhc_cb->mbhc_micbias_control( - mbhc->codec, - MICB_DISABLE); + mbhc->codec, MIC_BIAS_2, + MICB_DISABLE); if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( mbhc->codec, @@ -601,12 +604,12 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, if (mbhc->micbias_enable) { if (mbhc->mbhc_cb->mbhc_micbias_control) mbhc->mbhc_cb->mbhc_micbias_control( - mbhc->codec, - MICB_DISABLE); + mbhc->codec, MIC_BIAS_2, + MICB_DISABLE); if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( - mbhc->codec, - MIC_BIAS_2, false); + mbhc->codec, + MIC_BIAS_2, false); mbhc->micbias_enable = false; } mbhc->hph_type = WCD_MBHC_HPH_NONE; @@ -633,6 +636,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, } mbhc->hph_status &= ~(SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_ANC_HEADPHONE | SND_JACK_UNSUPPORTED); } @@ -648,8 +652,10 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, else if (jack_type == SND_JACK_HEADSET) { mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET; mbhc->jiffies_atreport = jiffies; - } else if (jack_type == SND_JACK_LINEOUT) + } else if (jack_type == SND_JACK_LINEOUT) { mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + } else if (jack_type == SND_JACK_ANC_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE; if (mbhc->impedance_detect && mbhc->mbhc_cb->compute_impedance && @@ -689,9 +695,98 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); } +static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 val, hs_comp_res, btn_status = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int btn_status_cnt = 0; + bool is_check_btn_press = false; + + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val); + + if (val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check hs_comp_result for few times to see if the IN3 voltage + * is below the Vref + */ + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!hs_comp_res) { + valid_plug_cnt++; + is_check_btn_press = true; + } else + invalid_plug_cnt++; + /* Wait 1ms before taking another reading */ + usleep_range(1000, 1100); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status); + if (btn_status) + btn_status_cnt++; + + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n", + __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt); + + /* decision logic */ + if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press && + (btn_status_cnt == 0)) + anc_mic_found = true; +exit: + if (!val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + return anc_mic_found; +} + static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, enum wcd_mbhc_plug_type plug_type) { + bool anc_mic_found; + enum snd_jack_types jack_type; + pr_debug("%s: enter current_plug(%d) new_plug(%d)\n", __func__, mbhc->current_plug, plug_type); @@ -716,11 +811,18 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED); } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->mbhc_cfg->enable_anc_mic_detect) + anc_mic_found = wcd_mbhc_detect_anc_plug_type(mbhc); + + jack_type = SND_JACK_HEADSET; + if (anc_mic_found) + jack_type = SND_JACK_ANC_HEADPHONE; + /* * If Headphone was reported previously, this will * only report the mic line */ - wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET); + wcd_mbhc_report_plug(mbhc, 1, jack_type); } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { if (mbhc->mbhc_cfg->detect_extn_cable) { /* High impedance device found. Report as LINEOUT */ @@ -807,7 +909,8 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) struct snd_soc_codec *codec = mbhc->codec; int delay = 0, rc; bool ret = false; - bool hs_comp_res; + u16 hs_comp_res; + bool is_spl_hs = false; /* * Increase micbias to 2.7V to detect headsets with @@ -855,16 +958,18 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) /* Wait for 50msec for FSM to update result values */ msleep(50); WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); - if (!(hs_comp_res)) + if (!(hs_comp_res)) { pr_debug("%s: Special headset detected in %d msecs\n", __func__, (delay * 2)); + is_spl_hs = true; + } if (delay == SPECIAL_HS_DETECT_TIME_MS) { pr_debug("%s: Spl headset didnt get detect in 4 sec\n", __func__); break; } } - if (!(hs_comp_res)) { + if (is_spl_hs) { pr_debug("%s: Headset with threshold found\n", __func__); mbhc->micbias_enable = true; ret = true; @@ -900,6 +1005,7 @@ static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc, WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); break; case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: if (!mbhc->is_hs_recording && !micbias2) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); break; @@ -1131,9 +1237,11 @@ correct_plug_type: * and if there is not button press without * release */ - if (mbhc->current_plug != - MBHC_PLUG_TYPE_HEADSET && - !mbhc->btn_press_intr) { + if (((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE)) && + !mbhc->btn_press_intr) { pr_debug("%s: cable is headset\n", __func__); goto report; @@ -1151,8 +1259,10 @@ correct_plug_type: * If plug_tye is headset, we might have already reported either in * detect_plug-type or in above while loop, no need to report again */ - if (!wrk_complete && plug_type == MBHC_PLUG_TYPE_HEADSET) { - pr_debug("%s: Headset already reported\n", __func__); + if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); goto enable_supply; } @@ -1167,6 +1277,10 @@ correct_plug_type: } report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } 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); @@ -1181,7 +1295,7 @@ enable_supply: exit: if (mbhc->mbhc_cb->mbhc_micbias_control && !mbhc->micbias_enable) - mbhc->mbhc_cb->mbhc_micbias_control(codec, + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, MICB_DISABLE); if (mbhc->mbhc_cb->micbias_enable_status) { micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, @@ -1222,7 +1336,8 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); if (mbhc->mbhc_cb->mbhc_micbias_control) - mbhc->mbhc_cb->mbhc_micbias_control(codec, MICB_ENABLE); + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); else wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); @@ -1349,6 +1464,17 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT); + } else if (mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { + mbhc->mbhc_cb->irq_control(codec, + mbhc->intr_ids->mbhc_hs_rem_intr, + false); + mbhc->mbhc_cb->irq_control(codec, + mbhc->intr_ids->mbhc_hs_ins_intr, + false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE); } } else if (!detection_type) { /* Disable external voltage source to micbias if present */ @@ -1727,7 +1853,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) * headset not headphone. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { - wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET); + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); goto exit; } diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index f6cc238ec6e5..bc48cbff4576 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -64,6 +64,9 @@ enum wcd_mbhc_register_function { WCD_MBHC_SWCH_LEVEL_REMOVE, WCD_MBHC_MOISTURE_VREF, WCD_MBHC_PULLDOWN_CTRL, + WCD_MBHC_ANC_DET_EN, + WCD_MBHC_FSM_STATUS, + WCD_MBHC_MUX_CTL, WCD_MBHC_REG_FUNC_MAX, }; @@ -74,6 +77,7 @@ enum wcd_mbhc_plug_type { MBHC_PLUG_TYPE_HEADPHONE, MBHC_PLUG_TYPE_HIGH_HPH, MBHC_PLUG_TYPE_GND_MIC_SWAP, + MBHC_PLUG_TYPE_ANC_HEADPHONE, }; enum pa_dac_ack_flags { @@ -249,6 +253,9 @@ struct wcd_mbhc_config { int key_code[WCD_MBHC_KEYCODE_NUM]; uint32_t linein_th; struct wcd_mbhc_moisture_cfg moist_cfg; + int mbhc_micbias; + int anc_micbias; + bool enable_anc_mic_detect; }; struct wcd_mbhc_intr { @@ -349,7 +356,7 @@ struct wcd_mbhc_cb { int num_btn, bool); void (*hph_pull_up_control)(struct snd_soc_codec *, enum mbhc_hs_pullup_iref); - int (*mbhc_micbias_control)(struct snd_soc_codec *, int req); + int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req); void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool); void (*skip_imped_detect)(struct snd_soc_codec *); bool (*extn_use_mb)(struct snd_soc_codec *); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f4992acb5468..fb1e939459db 100755 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -621,6 +621,17 @@ static struct wcd_mbhc_register 0, 0, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0), + /* + * MBHC FSM status register is only available in Tasha 2.0. + * So, init with 0 later once the version is known, then values + * will be updated. + */ + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD9335_MBHC_CTL_2, 0x70, 4, 0), }; static const struct wcd_mbhc_intr intr_ids = { @@ -1367,7 +1378,7 @@ static int tasha_micbias_control(struct snd_soc_codec *codec, } static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec, - int req) + int micb_num, int req) { int ret; @@ -1378,7 +1389,7 @@ static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec, if (req == MICB_ENABLE) tasha_cdc_mclk_enable(codec, true, false); - ret = tasha_micbias_control(codec, MIC_BIAS_2, req, false); + ret = tasha_micbias_control(codec, micb_num, req, false); /* * Release vote for mclk while requesting for @@ -12303,6 +12314,9 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) 0x0C; wcd_mbhc_registers[WCD_MBHC_MOISTURE_VREF].offset = 2; + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg = + WCD9335_MBHC_FSM_STATUS; + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01; } ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); |
