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

Commit 797519ff authored by Yeleswarapu Nagaradhesh's avatar Yeleswarapu Nagaradhesh Committed by Laxminath Kasam
Browse files

ASoC: wcd: Add polling for proper headset detection



Add polling to detect headset properly.
This fixes slow insertion of headset detected as Linout cable.
Fixes issues relating to slow insertion like headset fail to
detect after insertion and removal slowly 2-3 times.

CRs-Fixed: 653693
Change-Id: I80940ff0c903b78aec53a928081a1c6a1ea5276f
Signed-off-by: default avatarYeleswarapu Nagaradhesh <nagaradh@codeaurora.org>
parent 38be2e8f
Loading
Loading
Loading
Loading
+139 −31
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@
				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
				  SND_JACK_BTN_4)
#define OCP_ATTEMPT 1
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250

#define WCD_MBHC_RSC_LOCK(mbhc)			\
{							\
@@ -89,6 +91,34 @@ static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc)
	}
}

/* should be called under interrupt context that hold suspend */
static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
					    struct work_struct *work)
{
	pr_debug("%s: scheduling correct_swch_plug\n", __func__);
	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
	mbhc->hs_detect_work_stop = false;
	wcd9xxx_spmi_lock_sleep();
	schedule_work(work);
}

/* called under codec_resource_lock acquisition */
static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
					 struct work_struct *work)
{
	pr_debug("%s: Canceling correct_plug_swch\n", __func__);
	mbhc->hs_detect_work_stop = true;
	WCD_MBHC_RSC_UNLOCK(mbhc);
	if (cancel_work_sync(work)) {
		pr_debug("%s: correct_plug_swch is canceled\n",
			 __func__);
		wcd9xxx_spmi_unlock_sleep();
	}
	WCD_MBHC_RSC_LOCK(mbhc);
}



static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
				struct snd_soc_jack *jack, int status, int mask)
{
@@ -427,8 +457,10 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
			mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
		else if (jack_type == SND_JACK_UNSUPPORTED)
			mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
		else if (jack_type == SND_JACK_HEADSET)
		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)
			mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;

@@ -498,6 +530,58 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
	pr_debug("%s: leave\n", __func__);
}

static void wcd_correct_swch_plug(struct work_struct *work)
{
	struct wcd_mbhc *mbhc;
	struct snd_soc_codec *codec;
	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
	unsigned long timeout;
	u16 result2;
	bool wrk_complete = false;

	pr_debug("%s: enter\n", __func__);

	mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
	codec = mbhc->codec;

	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
	while (!time_after(jiffies, timeout)) {
		if (mbhc->hs_detect_work_stop) {
			pr_debug("%s: stop requested\n", __func__);
			return;
		}

		/*
		 * allow sometime and re-check stop requested again.
		 * this is as recommeneded by HW design team after
		 * multiple test iterations with trial error basis.
		 */
		msleep(200);
		if (mbhc->hs_detect_work_stop) {
			pr_debug("%s: stop requested\n", __func__);
			return;
		}
		result2 = snd_soc_read(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
		pr_debug("%s: result2 = %x\n", __func__, result2);
		if (result2 == 1) {
			pr_debug("%s: cable is extension cable\n", __func__);
			wrk_complete = true;
		} else {
			pr_debug("%s: cable is headset\n", __func__);
			plug_type = MBHC_PLUG_TYPE_HEADSET;
			wcd_mbhc_find_plug_and_report(mbhc, plug_type);
			return;
		}
	}
	if (wrk_complete == true)
		plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
	else
		plug_type = MBHC_PLUG_TYPE_INVALID;
	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
	pr_debug("%s: leave\n", __func__);
}

/* called under codec_resource_lock acquisition */
static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
{
@@ -509,6 +593,11 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)

	pr_debug("%s: enter\n", __func__);
	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);

	/* Enable micbias */
	snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x80);
	/*
	 * Wait for 50msec for FSM to complete its task.
	 * wakeup if btn pres intr occurs
@@ -530,34 +619,39 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
		 * Micbias and schmitt trigger (HPHL-HPHR)
		 * needs to be enabled.
		 */
		snd_soc_update_bits(codec,
				MSM8X16_WCD_A_ANALOG_MICB_2_EN,
				0x80, 0x80);
		pr_debug("%s: result1 %x, result2 %x\n",
				__func__, result1, result2);
		if (!(result2 & 0x01)) {
			snd_soc_update_bits(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
				0x6, 0x2);
		/* read reg MBHC_RESULT_2 value with cross connection bit */
			/*
			 * read reg MBHC_RESULT_2 value with cross
			 * connection bit
			 */
			swap_res = snd_soc_read(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
			pr_debug("%s: swap_res %x\n", __func__, swap_res);

			if (!result1 && !(swap_res & 0x04)) {
			plug_type = PLUG_TYPE_GND_MIC_SWAP;
			pr_debug("%s: Cross connection identified", __func__);
				plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
				pr_debug("%s: Cross connection identified\n",
						__func__);
				goto eu_us_switch;
			} else {
			pr_debug("%s: No Cross connection found", __func__);
				pr_debug("%s: No Cross connection found\n",
						__func__);
			}

			/* Disable micbias and schmitt trigger */
			snd_soc_update_bits(codec,
				MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
				0x6, 0x0);
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x00);
		if (!result1 && !result2)
		}
		if (!result1 && !(result2 & 0x01))
			plug_type = MBHC_PLUG_TYPE_HEADSET;
		else if (!result1 && (result2 & 0x01))
			plug_type = PLUG_TYPE_HIGH_HPH;
			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
		else {
			plug_type = MBHC_PLUG_TYPE_INVALID;
			goto exit;
@@ -578,20 +672,23 @@ eu_us_switch:
			pr_debug("%s: US_EU gpio present, flip switch\n",
				__func__);
			if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
				plug_type = PLUG_TYPE_HEADSET;
				plug_type = MBHC_PLUG_TYPE_HEADSET;
		}
		/* Disable micbias and schmitt trigger */
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
			0x6, 0x0);
	}
	snd_soc_update_bits(codec,
		MSM8X16_WCD_A_ANALOG_MICB_2_EN,
		0x80, 0x00);
	}

	pr_debug("%s: Valid plug found, plug type is %d\n",
			 __func__, plug_type);
	if (plug_type != MBHC_PLUG_TYPE_HIGH_HPH &&
			plug_type != MBHC_PLUG_TYPE_INVALID)
		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
	else
		wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
exit:
	pr_debug("%s: leave\n", __func__);
}
@@ -613,6 +710,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
	/* Set the detection type appropriately */
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_1,
			0x20, (!detection_type << 5));
	wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);

	if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) && detection_type) {
		/* Enable Tx2 RBias */
@@ -686,7 +784,6 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
		wcd_mbhc_swch_irq_handler(mbhc);
		wcd9xxx_spmi_unlock_sleep();
	}

	pr_debug("%s: leave %d\n", __func__, r);
	return r;
}
@@ -768,12 +865,21 @@ irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
	struct snd_soc_codec *codec = mbhc->codec;
	u16 result1;
	int mask;
	unsigned long msec_val;

	pr_debug("%s: enter\n", __func__);
	WCD_MBHC_RSC_LOCK(mbhc);
	/* send event to sw intr handler*/
	mbhc->is_btn_press = true;
	wake_up_interruptible(&mbhc->wait_btn_press);

	msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
	pr_debug("%s: msec_val = %ld\n", __func__, msec_val);
	if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) {
		pr_debug("%s: Too short, ignore button press\n", __func__);
		goto done;
	}

	/* If switch interrupt already kicked in, ignore button press */
	if (mbhc->in_swch_irq_handler) {
		pr_debug("%s: Swtich level changed, ignore button press\n",
@@ -790,7 +896,7 @@ irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
	result1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
	mask = wcd_mbhc_get_button_mask(result1);
	mbhc->buttons_pressed |= mask;
	pr_debug("%s: Reporting btn press\n", __func__);
	pr_debug("%s: Reporting btn press result1 =%d\n", __func__, result1);
	wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
			mbhc->buttons_pressed, mbhc->buttons_pressed);
done:
@@ -921,6 +1027,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
			0x08, 0x08);
	/* Program Button threshold registers */
	wcd_program_btn_threshold(mbhc);

	INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
	/* enable the WCD MBHC IRQ's */
	wcd9xxx_spmi_enable_irq(mbhc->intr_ids->mbhc_sw_intr);
	wcd9xxx_spmi_enable_irq(mbhc->intr_ids->mbhc_btn_press_intr);
+5 −0
Original line number Diff line number Diff line
@@ -76,11 +76,13 @@ struct wcd_mbhc {
	bool in_swch_irq_handler;
	bool hphl_swh; /*track HPHL switch NC / NO */
	bool gnd_swh; /*track GND switch NC / NO */
	bool hs_detect_work_stop;

	struct snd_soc_codec *codec;

	/* track PA/DAC state to sync with userspace */
	unsigned long hph_pa_dac_state;
	unsigned long jiffies_atreport;

	/* impedance of hphl and hphr */
	uint32_t zl, zr;
@@ -92,6 +94,9 @@ struct wcd_mbhc {

	/* Holds codec specific interrupt mapping */
	const struct wcd_mbhc_intr *intr_ids;

	/* Work to correct accessory type */
	struct work_struct correct_plug_swch;
};

#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \