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

Commit d3037dd8 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ASoC: wcd: handle micbias and current source properly"

parents cb5f0d53 ac30ac5d
Loading
Loading
Loading
Loading
+93 −67
Original line number Original line Diff line number Diff line
@@ -73,6 +73,12 @@ MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect");
		  "%s: BCL should have acquired\n", __func__); \
		  "%s: BCL should have acquired\n", __func__); \
}
}


enum wcd_mbhc_cs_mb_en_flag {
	WCD_MBHC_EN_CS = 0,
	WCD_MBHC_EN_MB,
	WCD_MBHC_EN_NONE,
};

static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
				struct snd_soc_jack *jack, int status, int mask)
				struct snd_soc_jack *jack, int status, int mask)
{
{
@@ -152,6 +158,46 @@ static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
	}
	}
}
}


static void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc,
				const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
{
	struct snd_soc_codec *codec = mbhc->codec;

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

	switch (cs_mb_en) {
	case WCD_MBHC_EN_CS:
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x00);
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0xB0);
		break;
	case WCD_MBHC_EN_MB:
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0x80);
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x80);
		break;
	case WCD_MBHC_EN_NONE:
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0x80);
		snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x00);
		break;
	default:
		pr_debug("%s: Invalid parameter", __func__);
		break;
	}

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

static int wcd_event_notify(struct notifier_block *self, unsigned long val,
static int wcd_event_notify(struct notifier_block *self, unsigned long val,
				void *data)
				void *data)
{
{
@@ -184,9 +230,7 @@ static int wcd_event_notify(struct notifier_block *self, unsigned long val,
					0x60, 0x00);
					0x60, 0x00);
		}
		}
		/* Disable current source if micbias enabled */
		/* Disable current source if micbias enabled */
		snd_soc_update_bits(codec,
		wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
				    MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
				    0xB0, 0x80);
		mbhc->is_hs_recording = true;
		mbhc->is_hs_recording = true;
		/* Program Button threshold registers */
		/* Program Button threshold registers */
		wcd_program_btn_threshold(mbhc, true);
		wcd_program_btn_threshold(mbhc, true);
@@ -199,9 +243,7 @@ static int wcd_event_notify(struct notifier_block *self, unsigned long val,
		snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
		snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL,
				0x20);
				0x20);
		/* Enable current source again for polling */
		/* Enable current source again for polling */
		snd_soc_update_bits(codec,
		wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
				    MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
				    0xB0, 0xB0);
		mbhc->is_hs_recording = false;
		mbhc->is_hs_recording = false;
		/* Program Button threshold registers */
		/* Program Button threshold registers */
		wcd_program_btn_threshold(mbhc, false);
		wcd_program_btn_threshold(mbhc, false);
@@ -576,13 +618,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,


				pr_debug("%s: Enable micbias\n", __func__);
				pr_debug("%s: Enable micbias\n", __func__);
				/* Disable current source and enable micbias */
				/* Disable current source and enable micbias */
				snd_soc_update_bits(codec,
				wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
					MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
					0xB0, 0x80);
				snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_MICB_2_EN,
					0x80, 0x80);

				pr_debug("%s: set up elec removal detection\n",
				pr_debug("%s: set up elec removal detection\n",
					  __func__);
					  __func__);
				snd_soc_update_bits(codec,
				snd_soc_update_bits(codec,
@@ -652,14 +688,6 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
		 * only report the mic line
		 * only report the mic line
		 */
		 */
		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
		 if ((!(snd_soc_read(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80))) {
			pr_debug("%s: Enable current source for polling\n",
				__func__);
			snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
					0xB0, 0xB0);
		}
	} else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
	} else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
		if (mbhc->mbhc_cfg->detect_extn_cable) {
		if (mbhc->mbhc_cfg->detect_extn_cable) {
			/* High impedance device found. Report as LINEOUT */
			/* High impedance device found. Report as LINEOUT */
@@ -700,9 +728,8 @@ static bool wcd_check_cross_conn(struct wcd_mbhc *mbhc)
	u16 result1, swap_res;
	u16 result1, swap_res;
	struct snd_soc_codec *codec = mbhc->codec;
	struct snd_soc_codec *codec = mbhc->codec;
	enum wcd_mbhc_plug_type plug_type = mbhc->current_plug;
	enum wcd_mbhc_plug_type plug_type = mbhc->current_plug;
	s16 reg, reg1, reg2;
	s16 reg1, reg2;


	reg = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL);
	reg1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2);
	reg1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2);
	reg2 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN);
	reg2 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN);
	/*
	/*
@@ -712,12 +739,7 @@ static bool wcd_check_cross_conn(struct wcd_mbhc *mbhc)
	 */
	 */
	result1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
	result1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
	/* Make sure micbias is enabled now */
	/* Make sure micbias is enabled now */
	snd_soc_update_bits(codec,
	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x80);
	/* If enabling micbias, turn off current source */
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0x80);
	snd_soc_update_bits(codec,
	snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
			MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
			0x6, 0x4);
			0x6, 0x4);
@@ -735,7 +757,6 @@ static bool wcd_check_cross_conn(struct wcd_mbhc *mbhc)
	/* Disable schmitt trigger and restore micbias */
	/* Disable schmitt trigger and restore micbias */
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, reg2);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, reg2);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2, reg1);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2, reg1);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL, reg);
	pr_debug("%s: leave, plug type: %d\n", __func__,  plug_type);
	pr_debug("%s: leave, plug type: %d\n", __func__,  plug_type);


	return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
	return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
@@ -753,12 +774,8 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
	 * Enable micbias if not already enabled
	 * Enable micbias if not already enabled
	 * and disable current source if using micbias
	 * and disable current source if using micbias
	 */
	 */
	reg = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL);
	reg = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
			0xB0, 0x80);
	/* Enable micbias if not already enabled */
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			    0x80, 0x80);
	pr_debug("%s: special headset, start register writes\n", __func__);
	pr_debug("%s: special headset, start register writes\n", __func__);
	result2 = snd_soc_read(codec,
	result2 = snd_soc_read(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
			MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
@@ -795,8 +812,7 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
		ret = true;
		ret = true;
	}
	}
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x60, 0x00);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_CTL, 0x60, 0x00);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x80, 0x00);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, reg);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL, reg);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL, 0x20);
	snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MICB_1_VAL, 0x20);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x18, 0x00);
	snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN, 0x18, 0x00);


@@ -821,9 +837,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
	codec = mbhc->codec;
	codec = mbhc->codec;


	/* Enable micbias for detection in correct work*/
	/* Enable micbias for detection in correct work*/
	snd_soc_update_bits(codec,
	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x80);
	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
	while (!time_after(jiffies, timeout)) {
	while (!time_after(jiffies, timeout)) {
		if (mbhc->hs_detect_work_stop || wcd_swch_level_remove(mbhc)) {
		if (mbhc->hs_detect_work_stop || wcd_swch_level_remove(mbhc)) {
@@ -921,11 +935,22 @@ static void wcd_correct_swch_plug(struct work_struct *work)
	 */
	 */
	if (!wrk_complete && plug_type == MBHC_PLUG_TYPE_HEADSET) {
	if (!wrk_complete && plug_type == MBHC_PLUG_TYPE_HEADSET) {
		pr_debug("%s: It's neither headset nor headphone\n", __func__);
		pr_debug("%s: It's neither headset nor headphone\n", __func__);
		/* Write back micbias value */
		/*
		if (!mbhc->is_hs_recording)
		 * Do not disable micbias if recording is going on or
			snd_soc_update_bits(codec,
		 * headset is inserted on the other side of the extn
				MSM8X16_WCD_A_ANALOG_MICB_2_EN,
		 * cable. If headset has been detected current source
				0x80, 0x00);
		 * needs to be kept enabled for button detection to work.
		 * If the accessory type is invalid or unsupported, we
		 * dont need to enable either of them.
		 */
		if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
			if (!(mbhc->is_hs_recording || det_extn_cable_en))
				wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
			else
				wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
		} else {
			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
		}
		goto exit;
		goto exit;
	}
	}


@@ -943,11 +968,22 @@ report:
	pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
	pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
			__func__, plug_type, wrk_complete,
			__func__, plug_type, wrk_complete,
			mbhc->btn_press_intr);
			mbhc->btn_press_intr);
	/* Write back micbias value */
	/*
	if (!mbhc->is_hs_recording)
	 * Do not disable micbias if recording is going on or
		snd_soc_update_bits(codec,
	 * headset is inserted on the other side of the extn
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
	 * cable. If headset has been detected current source
			0x80, 0x00);
	 * needs to be kept enabled for button detection to work.
	 * If the accessory type is invalid or unsupported, we
	 * dont need to enable either of them.
	 */
	if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
		if (!(mbhc->is_hs_recording || det_extn_cable_en))
			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
		else
			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
	} else {
		wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
	}
	WCD_MBHC_RSC_LOCK(mbhc);
	WCD_MBHC_RSC_LOCK(mbhc);
	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
	WCD_MBHC_RSC_UNLOCK(mbhc);
	WCD_MBHC_RSC_UNLOCK(mbhc);
@@ -970,15 +1006,7 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
	pr_debug("%s: enter\n", __func__);
	pr_debug("%s: enter\n", __func__);
	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);


	/* Enable micbias */
	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
	snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
			0x80, 0x80);

	/* Disable current source as micbias is enabled */
	snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
			0xB0, 0x80);
	/*
	/*
	 * Wait for 50msec for FSM to complete its task.
	 * Wait for 50msec for FSM to complete its task.
	 * wakeup if btn pres intr occurs
	 * wakeup if btn pres intr occurs
@@ -1323,6 +1351,7 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)


	WCD_MBHC_RSC_LOCK(mbhc);
	WCD_MBHC_RSC_LOCK(mbhc);


	wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
	timeout = jiffies +
	timeout = jiffies +
		  msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
		  msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
	do {
	do {
@@ -1378,13 +1407,10 @@ report_unplug:
	 * Setup for insertion detection.
	 * Setup for insertion detection.
	 */
	 */
	wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
	wcd9xxx_spmi_disable_irq(mbhc->intr_ids->mbhc_hs_rem_intr);
	/* Disable HW FSM and current source */
	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
	/* Disable HW FSM */
	snd_soc_update_bits(codec,
	snd_soc_update_bits(codec,
		MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
		MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
		0xB0, 0x0);
	/* Disable micbias */
	snd_soc_update_bits(codec,
			MSM8X16_WCD_A_ANALOG_MICB_2_EN,
		0x80, 0x00);
		0x80, 0x00);
	snd_soc_update_bits(codec,
	snd_soc_update_bits(codec,
		MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
		MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,