ASoC: wcd-mbhc: Add support for 5-pole plug detection

MBHC hardware block on WCD9335 supports 5-pole plug
detection. Add support for 5-pole plug detection in
MBHC driver.

Change-Id: Ia2620b5cc3ef5065f350549d29cde063fdd1bf04
Signed-off-by: Phani Kumar Uppalapati <phaniu@codeaurora.org>
This commit is contained in:
Phani Kumar Uppalapati 2015-06-26 00:00:22 -07:00 committed by David Keitel
parent ed642a40cd
commit f359b9f372
3 changed files with 170 additions and 23 deletions

View file

@ -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;
}

View file

@ -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 *);

View file

@ -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);