Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40598a72 authored by Phani Kumar Uppalapati's avatar Phani Kumar Uppalapati
Browse files

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: default avatarPhani Kumar Uppalapati <phaniu@codeaurora.org>
parent 63558768
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -256,6 +256,12 @@ static struct wcd_mbhc_register
			  0, 0, 0, 0),
	WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
			  MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x20, 5, 0),
	WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
			  0, 0, 0, 0),
	WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
			  0, 0, 0, 0),
	WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
			  0, 0, 0, 0),
};

struct msm8x16_wcd_spmi {
+146 −20
Original line number Diff line number Diff line
@@ -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 | \
@@ -50,6 +52,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,
@@ -569,7 +572,7 @@ 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,
						mbhc->codec, MIC_BIAS_2,
						MICB_DISABLE);
			if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
				mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
@@ -602,7 +605,7 @@ 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,
						mbhc->codec, MIC_BIAS_2,
						MICB_DISABLE);
				if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
					mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
@@ -634,6 +637,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);
		}

@@ -649,8 +653,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 &&
@@ -690,9 +696,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);

@@ -717,11 +812,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 */
@@ -808,7 +910,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
@@ -856,16 +959,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;
@@ -901,6 +1006,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;
@@ -1132,8 +1238,10 @@ correct_plug_type:
				 * and if there is not button press without
				 * release
				 */
				if (mbhc->current_plug !=
						MBHC_PLUG_TYPE_HEADSET &&
				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__);
@@ -1152,8 +1260,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;
	}

@@ -1168,6 +1278,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);
@@ -1182,7 +1296,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,
@@ -1223,7 +1337,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);

@@ -1350,6 +1465,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 */
@@ -1734,7 +1860,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;

	}
+8 −1
Original line number Diff line number Diff line
@@ -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 *);
+16 −2
Original line number Diff line number Diff line
@@ -614,6 +614,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 = {
@@ -1357,7 +1368,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;

@@ -1368,7 +1379,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
@@ -12029,6 +12040,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);