xref: /openbmc/linux/sound/soc/codecs/wcd-mbhc-v2.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
10e5c9e7fSSrinivas Kandagatla // SPDX-License-Identifier: GPL-2.0-only
20e5c9e7fSSrinivas Kandagatla // Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
30e5c9e7fSSrinivas Kandagatla 
40e5c9e7fSSrinivas Kandagatla #include <linux/module.h>
50e5c9e7fSSrinivas Kandagatla #include <linux/init.h>
60e5c9e7fSSrinivas Kandagatla #include <linux/slab.h>
70e5c9e7fSSrinivas Kandagatla #include <linux/device.h>
8cc4d891fSSrinivas Kandagatla #include <linux/pm_runtime.h>
90e5c9e7fSSrinivas Kandagatla #include <linux/printk.h>
100e5c9e7fSSrinivas Kandagatla #include <linux/delay.h>
110e5c9e7fSSrinivas Kandagatla #include <linux/kernel.h>
120e5c9e7fSSrinivas Kandagatla #include <sound/soc.h>
130e5c9e7fSSrinivas Kandagatla #include <sound/jack.h>
140e5c9e7fSSrinivas Kandagatla #include "wcd-mbhc-v2.h"
150e5c9e7fSSrinivas Kandagatla 
160e5c9e7fSSrinivas Kandagatla #define HS_DETECT_PLUG_TIME_MS		(3 * 1000)
170e5c9e7fSSrinivas Kandagatla #define MBHC_BUTTON_PRESS_THRESHOLD_MIN	250
180e5c9e7fSSrinivas Kandagatla #define GND_MIC_SWAP_THRESHOLD		4
190e5c9e7fSSrinivas Kandagatla #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS	100
200e5c9e7fSSrinivas Kandagatla #define HPHL_CROSS_CONN_THRESHOLD	100
210e5c9e7fSSrinivas Kandagatla #define HS_VREF_MIN_VAL			1400
220e5c9e7fSSrinivas Kandagatla #define FAKE_REM_RETRY_ATTEMPTS		3
230e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_HS_THRESHOLD_MV	1700
240e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_HPH_THRESHOLD_MV	75
250e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_MICBIAS_MV		1800
260e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_FAKE_INS_RETRY		4
270e5c9e7fSSrinivas Kandagatla 
280e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
290e5c9e7fSSrinivas Kandagatla 			   SND_JACK_MECHANICAL)
300e5c9e7fSSrinivas Kandagatla 
310e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
320e5c9e7fSSrinivas Kandagatla 				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
330e5c9e7fSSrinivas Kandagatla 				  SND_JACK_BTN_4 | SND_JACK_BTN_5)
340e5c9e7fSSrinivas Kandagatla 
350e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_adc_mux_ctl {
360e5c9e7fSSrinivas Kandagatla 	MUX_CTL_AUTO = 0,
370e5c9e7fSSrinivas Kandagatla 	MUX_CTL_IN2P,
380e5c9e7fSSrinivas Kandagatla 	MUX_CTL_IN3P,
390e5c9e7fSSrinivas Kandagatla 	MUX_CTL_IN4P,
400e5c9e7fSSrinivas Kandagatla 	MUX_CTL_HPH_L,
410e5c9e7fSSrinivas Kandagatla 	MUX_CTL_HPH_R,
420e5c9e7fSSrinivas Kandagatla 	MUX_CTL_NONE,
430e5c9e7fSSrinivas Kandagatla };
440e5c9e7fSSrinivas Kandagatla 
450e5c9e7fSSrinivas Kandagatla struct wcd_mbhc {
460e5c9e7fSSrinivas Kandagatla 	struct device *dev;
470e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component;
480e5c9e7fSSrinivas Kandagatla 	struct snd_soc_jack *jack;
490e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc_config *cfg;
500e5c9e7fSSrinivas Kandagatla 	const struct wcd_mbhc_cb *mbhc_cb;
510e5c9e7fSSrinivas Kandagatla 	const struct wcd_mbhc_intr *intr_ids;
520e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc_field *fields;
530e5c9e7fSSrinivas Kandagatla 	/* Delayed work to report long button press */
540e5c9e7fSSrinivas Kandagatla 	struct delayed_work mbhc_btn_dwork;
550e5c9e7fSSrinivas Kandagatla 	/* Work to correct accessory type */
560e5c9e7fSSrinivas Kandagatla 	struct work_struct correct_plug_swch;
570e5c9e7fSSrinivas Kandagatla 	struct mutex lock;
580e5c9e7fSSrinivas Kandagatla 	int buttons_pressed;
590e5c9e7fSSrinivas Kandagatla 	u32 hph_status; /* track headhpone status */
600e5c9e7fSSrinivas Kandagatla 	u8 current_plug;
610e5c9e7fSSrinivas Kandagatla 	bool is_btn_press;
620e5c9e7fSSrinivas Kandagatla 	bool in_swch_irq_handler;
630e5c9e7fSSrinivas Kandagatla 	bool hs_detect_work_stop;
640e5c9e7fSSrinivas Kandagatla 	bool is_hs_recording;
650e5c9e7fSSrinivas Kandagatla 	bool extn_cable_hph_rem;
660e5c9e7fSSrinivas Kandagatla 	bool force_linein;
670e5c9e7fSSrinivas Kandagatla 	bool impedance_detect;
680e5c9e7fSSrinivas Kandagatla 	unsigned long event_state;
690e5c9e7fSSrinivas Kandagatla 	unsigned long jiffies_atreport;
700e5c9e7fSSrinivas Kandagatla 	/* impedance of hphl and hphr */
710e5c9e7fSSrinivas Kandagatla 	uint32_t zl, zr;
720e5c9e7fSSrinivas Kandagatla 	/* Holds type of Headset - Mono/Stereo */
730e5c9e7fSSrinivas Kandagatla 	enum wcd_mbhc_hph_type hph_type;
740e5c9e7fSSrinivas Kandagatla 	/* Holds mbhc detection method - ADC/Legacy */
750e5c9e7fSSrinivas Kandagatla 	int mbhc_detection_logic;
760e5c9e7fSSrinivas Kandagatla };
770e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_write_field(const struct wcd_mbhc * mbhc,int field,int val)780e5c9e7fSSrinivas Kandagatla static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
790e5c9e7fSSrinivas Kandagatla 				       int field, int val)
800e5c9e7fSSrinivas Kandagatla {
810e5c9e7fSSrinivas Kandagatla 	if (!mbhc->fields[field].reg)
820e5c9e7fSSrinivas Kandagatla 		return 0;
830e5c9e7fSSrinivas Kandagatla 
840e5c9e7fSSrinivas Kandagatla 	return snd_soc_component_write_field(mbhc->component,
850e5c9e7fSSrinivas Kandagatla 					     mbhc->fields[field].reg,
860e5c9e7fSSrinivas Kandagatla 					     mbhc->fields[field].mask, val);
870e5c9e7fSSrinivas Kandagatla }
880e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_read_field(const struct wcd_mbhc * mbhc,int field)890e5c9e7fSSrinivas Kandagatla static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
900e5c9e7fSSrinivas Kandagatla {
910e5c9e7fSSrinivas Kandagatla 	if (!mbhc->fields[field].reg)
920e5c9e7fSSrinivas Kandagatla 		return 0;
930e5c9e7fSSrinivas Kandagatla 
940e5c9e7fSSrinivas Kandagatla 	return snd_soc_component_read_field(mbhc->component,
950e5c9e7fSSrinivas Kandagatla 					    mbhc->fields[field].reg,
960e5c9e7fSSrinivas Kandagatla 					    mbhc->fields[field].mask);
970e5c9e7fSSrinivas Kandagatla }
980e5c9e7fSSrinivas Kandagatla 
wcd_program_hs_vref(struct wcd_mbhc * mbhc)990e5c9e7fSSrinivas Kandagatla static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
1000e5c9e7fSSrinivas Kandagatla {
1010e5c9e7fSSrinivas Kandagatla 	u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
1020e5c9e7fSSrinivas Kandagatla 
1030e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
1040e5c9e7fSSrinivas Kandagatla }
1050e5c9e7fSSrinivas Kandagatla 
wcd_program_btn_threshold(const struct wcd_mbhc * mbhc,bool micbias)1060e5c9e7fSSrinivas Kandagatla static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
1070e5c9e7fSSrinivas Kandagatla {
1080e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component = mbhc->component;
1090e5c9e7fSSrinivas Kandagatla 
1100e5c9e7fSSrinivas Kandagatla 	mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
1110e5c9e7fSSrinivas Kandagatla 				   mbhc->cfg->btn_high,
1120e5c9e7fSSrinivas Kandagatla 				   mbhc->cfg->num_btn, micbias);
1130e5c9e7fSSrinivas Kandagatla }
1140e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_curr_micbias_control(const struct wcd_mbhc * mbhc,const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)1150e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
1160e5c9e7fSSrinivas Kandagatla 					  const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
1170e5c9e7fSSrinivas Kandagatla {
1180e5c9e7fSSrinivas Kandagatla 
1190e5c9e7fSSrinivas Kandagatla 	/*
1200e5c9e7fSSrinivas Kandagatla 	 * Some codecs handle micbias/pullup enablement in codec
1210e5c9e7fSSrinivas Kandagatla 	 * drivers itself and micbias is not needed for regular
1220e5c9e7fSSrinivas Kandagatla 	 * plug type detection. So if micbias_control callback function
1230e5c9e7fSSrinivas Kandagatla 	 * is defined, just return.
1240e5c9e7fSSrinivas Kandagatla 	 */
1250e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micbias_control)
1260e5c9e7fSSrinivas Kandagatla 		return;
1270e5c9e7fSSrinivas Kandagatla 
1280e5c9e7fSSrinivas Kandagatla 	switch (cs_mb_en) {
1290e5c9e7fSSrinivas Kandagatla 	case WCD_MBHC_EN_CS:
1300e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
1310e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1320e5c9e7fSSrinivas Kandagatla 		/* Program Button threshold registers as per CS */
1330e5c9e7fSSrinivas Kandagatla 		wcd_program_btn_threshold(mbhc, false);
1340e5c9e7fSSrinivas Kandagatla 		break;
1350e5c9e7fSSrinivas Kandagatla 	case WCD_MBHC_EN_MB:
1360e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1370e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1380e5c9e7fSSrinivas Kandagatla 		/* Disable PULL_UP_EN & enable MICBIAS */
1390e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
1400e5c9e7fSSrinivas Kandagatla 		/* Program Button threshold registers as per MICBIAS */
1410e5c9e7fSSrinivas Kandagatla 		wcd_program_btn_threshold(mbhc, true);
1420e5c9e7fSSrinivas Kandagatla 		break;
1430e5c9e7fSSrinivas Kandagatla 	case WCD_MBHC_EN_PULLUP:
1440e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1450e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1460e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
1470e5c9e7fSSrinivas Kandagatla 		/* Program Button threshold registers as per MICBIAS */
1480e5c9e7fSSrinivas Kandagatla 		wcd_program_btn_threshold(mbhc, true);
1490e5c9e7fSSrinivas Kandagatla 		break;
1500e5c9e7fSSrinivas Kandagatla 	case WCD_MBHC_EN_NONE:
1510e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1520e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1530e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
1540e5c9e7fSSrinivas Kandagatla 		break;
1550e5c9e7fSSrinivas Kandagatla 	default:
1560e5c9e7fSSrinivas Kandagatla 		dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
1570e5c9e7fSSrinivas Kandagatla 		break;
1580e5c9e7fSSrinivas Kandagatla 	}
1590e5c9e7fSSrinivas Kandagatla }
1600e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_event_notify(struct wcd_mbhc * mbhc,unsigned long event)1610e5c9e7fSSrinivas Kandagatla int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
1620e5c9e7fSSrinivas Kandagatla {
1630e5c9e7fSSrinivas Kandagatla 
1640e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component;
1650e5c9e7fSSrinivas Kandagatla 	bool micbias2 = false;
1660e5c9e7fSSrinivas Kandagatla 
1670e5c9e7fSSrinivas Kandagatla 	if (!mbhc)
1680e5c9e7fSSrinivas Kandagatla 		return 0;
1690e5c9e7fSSrinivas Kandagatla 
1700e5c9e7fSSrinivas Kandagatla 	component = mbhc->component;
1710e5c9e7fSSrinivas Kandagatla 
1720e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->micbias_enable_status)
1730e5c9e7fSSrinivas Kandagatla 		micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
1740e5c9e7fSSrinivas Kandagatla 
1750e5c9e7fSSrinivas Kandagatla 	switch (event) {
1760e5c9e7fSSrinivas Kandagatla 	/* MICBIAS usage change */
1770e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
1780e5c9e7fSSrinivas Kandagatla 		mbhc->is_hs_recording = true;
1790e5c9e7fSSrinivas Kandagatla 		break;
1800e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_MICBIAS_2_ON:
1810e5c9e7fSSrinivas Kandagatla 		/* Disable current source if micbias2 enabled */
1820e5c9e7fSSrinivas Kandagatla 		if (mbhc->mbhc_cb->mbhc_micbias_control) {
1830e5c9e7fSSrinivas Kandagatla 			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
1840e5c9e7fSSrinivas Kandagatla 				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1850e5c9e7fSSrinivas Kandagatla 		} else {
1860e5c9e7fSSrinivas Kandagatla 			mbhc->is_hs_recording = true;
1870e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
1880e5c9e7fSSrinivas Kandagatla 		}
1890e5c9e7fSSrinivas Kandagatla 		break;
1900e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_PRE_MICBIAS_2_OFF:
1910e5c9e7fSSrinivas Kandagatla 		/*
1920e5c9e7fSSrinivas Kandagatla 		 * Before MICBIAS_2 is turned off, if FSM is enabled,
1930e5c9e7fSSrinivas Kandagatla 		 * make sure current source is enabled so as to detect
1940e5c9e7fSSrinivas Kandagatla 		 * button press/release events
1950e5c9e7fSSrinivas Kandagatla 		 */
1960e5c9e7fSSrinivas Kandagatla 		if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
1970e5c9e7fSSrinivas Kandagatla 			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
1980e5c9e7fSSrinivas Kandagatla 				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1990e5c9e7fSSrinivas Kandagatla 		}
2000e5c9e7fSSrinivas Kandagatla 		break;
2010e5c9e7fSSrinivas Kandagatla 	/* MICBIAS usage change */
2020e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
2030e5c9e7fSSrinivas Kandagatla 		mbhc->is_hs_recording = false;
2040e5c9e7fSSrinivas Kandagatla 		break;
2050e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_MICBIAS_2_OFF:
2060e5c9e7fSSrinivas Kandagatla 		if (!mbhc->mbhc_cb->mbhc_micbias_control)
2070e5c9e7fSSrinivas Kandagatla 			mbhc->is_hs_recording = false;
2080e5c9e7fSSrinivas Kandagatla 
2090e5c9e7fSSrinivas Kandagatla 		/* Enable PULL UP if PA's are enabled */
2100e5c9e7fSSrinivas Kandagatla 		if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
2110e5c9e7fSSrinivas Kandagatla 		    (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
2120e5c9e7fSSrinivas Kandagatla 			/* enable pullup and cs, disable mb */
2130e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2140e5c9e7fSSrinivas Kandagatla 		else
2150e5c9e7fSSrinivas Kandagatla 			/* enable current source and disable mb, pullup*/
2160e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2170e5c9e7fSSrinivas Kandagatla 
2180e5c9e7fSSrinivas Kandagatla 		break;
2190e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_HPHL_PA_OFF:
2200e5c9e7fSSrinivas Kandagatla 		clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
2210e5c9e7fSSrinivas Kandagatla 
2220e5c9e7fSSrinivas Kandagatla 		/* check if micbias is enabled */
2230e5c9e7fSSrinivas Kandagatla 		if (micbias2)
2240e5c9e7fSSrinivas Kandagatla 			/* Disable cs, pullup & enable micbias */
2250e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2260e5c9e7fSSrinivas Kandagatla 		else
2270e5c9e7fSSrinivas Kandagatla 			/* Disable micbias, pullup & enable cs */
2280e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2290e5c9e7fSSrinivas Kandagatla 		break;
2300e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_POST_HPHR_PA_OFF:
2310e5c9e7fSSrinivas Kandagatla 		clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
2320e5c9e7fSSrinivas Kandagatla 		/* check if micbias is enabled */
2330e5c9e7fSSrinivas Kandagatla 		if (micbias2)
2340e5c9e7fSSrinivas Kandagatla 			/* Disable cs, pullup & enable micbias */
2350e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2360e5c9e7fSSrinivas Kandagatla 		else
2370e5c9e7fSSrinivas Kandagatla 			/* Disable micbias, pullup & enable cs */
2380e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2390e5c9e7fSSrinivas Kandagatla 		break;
2400e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_PRE_HPHL_PA_ON:
2410e5c9e7fSSrinivas Kandagatla 		set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
2420e5c9e7fSSrinivas Kandagatla 		/* check if micbias is enabled */
2430e5c9e7fSSrinivas Kandagatla 		if (micbias2)
2440e5c9e7fSSrinivas Kandagatla 			/* Disable cs, pullup & enable micbias */
2450e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2460e5c9e7fSSrinivas Kandagatla 		else
2470e5c9e7fSSrinivas Kandagatla 			/* Disable micbias, enable pullup & cs */
2480e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2490e5c9e7fSSrinivas Kandagatla 		break;
2500e5c9e7fSSrinivas Kandagatla 	case WCD_EVENT_PRE_HPHR_PA_ON:
2510e5c9e7fSSrinivas Kandagatla 		set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
2520e5c9e7fSSrinivas Kandagatla 		/* check if micbias is enabled */
2530e5c9e7fSSrinivas Kandagatla 		if (micbias2)
2540e5c9e7fSSrinivas Kandagatla 			/* Disable cs, pullup & enable micbias */
2550e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2560e5c9e7fSSrinivas Kandagatla 		else
2570e5c9e7fSSrinivas Kandagatla 			/* Disable micbias, enable pullup & cs */
2580e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2590e5c9e7fSSrinivas Kandagatla 		break;
2600e5c9e7fSSrinivas Kandagatla 	default:
2610e5c9e7fSSrinivas Kandagatla 		break;
2620e5c9e7fSSrinivas Kandagatla 	}
2630e5c9e7fSSrinivas Kandagatla 	return 0;
2640e5c9e7fSSrinivas Kandagatla }
2650e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
2660e5c9e7fSSrinivas Kandagatla 
wcd_cancel_btn_work(struct wcd_mbhc * mbhc)2670e5c9e7fSSrinivas Kandagatla static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
2680e5c9e7fSSrinivas Kandagatla {
2690e5c9e7fSSrinivas Kandagatla 	return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
2700e5c9e7fSSrinivas Kandagatla }
2710e5c9e7fSSrinivas Kandagatla 
wcd_micbias_disable(struct wcd_mbhc * mbhc)2720e5c9e7fSSrinivas Kandagatla static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
2730e5c9e7fSSrinivas Kandagatla {
2740e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component = mbhc->component;
2750e5c9e7fSSrinivas Kandagatla 
2760e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micbias_control)
2770e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
2780e5c9e7fSSrinivas Kandagatla 
2790e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
2800e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
2810e5c9e7fSSrinivas Kandagatla 
2820e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->set_micbias_value) {
2830e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->set_micbias_value(component);
2840e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
2850e5c9e7fSSrinivas Kandagatla 	}
2860e5c9e7fSSrinivas Kandagatla }
2870e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_report_plug_removal(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)2880e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
2890e5c9e7fSSrinivas Kandagatla 					 enum snd_jack_types jack_type)
2900e5c9e7fSSrinivas Kandagatla {
2910e5c9e7fSSrinivas Kandagatla 	mbhc->hph_status &= ~jack_type;
2920e5c9e7fSSrinivas Kandagatla 	/*
2930e5c9e7fSSrinivas Kandagatla 	 * cancel possibly scheduled btn work and
2940e5c9e7fSSrinivas Kandagatla 	 * report release if we reported button press
2950e5c9e7fSSrinivas Kandagatla 	 */
2960e5c9e7fSSrinivas Kandagatla 	if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
2970e5c9e7fSSrinivas Kandagatla 		snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
2980e5c9e7fSSrinivas Kandagatla 		mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
2990e5c9e7fSSrinivas Kandagatla 	}
3000e5c9e7fSSrinivas Kandagatla 
3010e5c9e7fSSrinivas Kandagatla 	wcd_micbias_disable(mbhc);
3020e5c9e7fSSrinivas Kandagatla 	mbhc->hph_type = WCD_MBHC_HPH_NONE;
3030e5c9e7fSSrinivas Kandagatla 	mbhc->zl = mbhc->zr = 0;
3040e5c9e7fSSrinivas Kandagatla 	snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
3050e5c9e7fSSrinivas Kandagatla 	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
3060e5c9e7fSSrinivas Kandagatla 	mbhc->force_linein = false;
3070e5c9e7fSSrinivas Kandagatla }
3080e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_compute_impedance(struct wcd_mbhc * mbhc)3090e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
3100e5c9e7fSSrinivas Kandagatla {
3110e5c9e7fSSrinivas Kandagatla 
3120e5c9e7fSSrinivas Kandagatla 	if (!mbhc->impedance_detect)
3130e5c9e7fSSrinivas Kandagatla 		return;
3140e5c9e7fSSrinivas Kandagatla 
3150e5c9e7fSSrinivas Kandagatla 	if (mbhc->cfg->linein_th != 0) {
3160e5c9e7fSSrinivas Kandagatla 		u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
3170e5c9e7fSSrinivas Kandagatla 		/* Set MUX_CTL to AUTO for Z-det */
3180e5c9e7fSSrinivas Kandagatla 
3190e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
3200e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
3210e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
3220e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
3230e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
3240e5c9e7fSSrinivas Kandagatla 	}
3250e5c9e7fSSrinivas Kandagatla }
3260e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_report_plug_insertion(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)3270e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
3280e5c9e7fSSrinivas Kandagatla 					   enum snd_jack_types jack_type)
3290e5c9e7fSSrinivas Kandagatla {
3300e5c9e7fSSrinivas Kandagatla 	bool is_pa_on;
3310e5c9e7fSSrinivas Kandagatla 	/*
3320e5c9e7fSSrinivas Kandagatla 	 * Report removal of current jack type.
3330e5c9e7fSSrinivas Kandagatla 	 * Headphone to headset shouldn't report headphone
3340e5c9e7fSSrinivas Kandagatla 	 * removal.
3350e5c9e7fSSrinivas Kandagatla 	 */
3360e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
3370e5c9e7fSSrinivas Kandagatla 	    jack_type == SND_JACK_HEADPHONE)
3380e5c9e7fSSrinivas Kandagatla 		mbhc->hph_status &= ~SND_JACK_HEADSET;
3390e5c9e7fSSrinivas Kandagatla 
3400e5c9e7fSSrinivas Kandagatla 	/* Report insertion */
3410e5c9e7fSSrinivas Kandagatla 	switch (jack_type) {
3420e5c9e7fSSrinivas Kandagatla 	case SND_JACK_HEADPHONE:
3430e5c9e7fSSrinivas Kandagatla 		mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
3440e5c9e7fSSrinivas Kandagatla 		break;
3450e5c9e7fSSrinivas Kandagatla 	case SND_JACK_HEADSET:
3460e5c9e7fSSrinivas Kandagatla 		mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
3470e5c9e7fSSrinivas Kandagatla 		mbhc->jiffies_atreport = jiffies;
3480e5c9e7fSSrinivas Kandagatla 		break;
3490e5c9e7fSSrinivas Kandagatla 	case SND_JACK_LINEOUT:
3500e5c9e7fSSrinivas Kandagatla 		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3510e5c9e7fSSrinivas Kandagatla 		break;
3520e5c9e7fSSrinivas Kandagatla 	default:
3530e5c9e7fSSrinivas Kandagatla 		break;
3540e5c9e7fSSrinivas Kandagatla 	}
3550e5c9e7fSSrinivas Kandagatla 
3560e5c9e7fSSrinivas Kandagatla 
3570e5c9e7fSSrinivas Kandagatla 	is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
3580e5c9e7fSSrinivas Kandagatla 
3590e5c9e7fSSrinivas Kandagatla 	if (!is_pa_on) {
3600e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_compute_impedance(mbhc);
3610e5c9e7fSSrinivas Kandagatla 		if ((mbhc->zl > mbhc->cfg->linein_th) &&
3620e5c9e7fSSrinivas Kandagatla 		    (mbhc->zr > mbhc->cfg->linein_th) &&
3630e5c9e7fSSrinivas Kandagatla 		    (jack_type == SND_JACK_HEADPHONE)) {
3640e5c9e7fSSrinivas Kandagatla 			jack_type = SND_JACK_LINEOUT;
3650e5c9e7fSSrinivas Kandagatla 			mbhc->force_linein = true;
3660e5c9e7fSSrinivas Kandagatla 			mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3670e5c9e7fSSrinivas Kandagatla 			if (mbhc->hph_status) {
3680e5c9e7fSSrinivas Kandagatla 				mbhc->hph_status &= ~(SND_JACK_HEADSET |
3690e5c9e7fSSrinivas Kandagatla 						      SND_JACK_LINEOUT);
3700e5c9e7fSSrinivas Kandagatla 				snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
3710e5c9e7fSSrinivas Kandagatla 						    WCD_MBHC_JACK_MASK);
3720e5c9e7fSSrinivas Kandagatla 			}
3730e5c9e7fSSrinivas Kandagatla 		}
3740e5c9e7fSSrinivas Kandagatla 	}
3750e5c9e7fSSrinivas Kandagatla 
3760e5c9e7fSSrinivas Kandagatla 	/* Do not calculate impedance again for lineout
3770e5c9e7fSSrinivas Kandagatla 	 * as during playback pa is on and impedance values
3780e5c9e7fSSrinivas Kandagatla 	 * will not be correct resulting in lineout detected
3790e5c9e7fSSrinivas Kandagatla 	 * as headphone.
3800e5c9e7fSSrinivas Kandagatla 	 */
3810e5c9e7fSSrinivas Kandagatla 	if (is_pa_on && mbhc->force_linein) {
3820e5c9e7fSSrinivas Kandagatla 		jack_type = SND_JACK_LINEOUT;
3830e5c9e7fSSrinivas Kandagatla 		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3840e5c9e7fSSrinivas Kandagatla 		if (mbhc->hph_status) {
3850e5c9e7fSSrinivas Kandagatla 			mbhc->hph_status &= ~(SND_JACK_HEADSET |
3860e5c9e7fSSrinivas Kandagatla 					      SND_JACK_LINEOUT);
3870e5c9e7fSSrinivas Kandagatla 			snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
3880e5c9e7fSSrinivas Kandagatla 					    WCD_MBHC_JACK_MASK);
3890e5c9e7fSSrinivas Kandagatla 		}
3900e5c9e7fSSrinivas Kandagatla 	}
3910e5c9e7fSSrinivas Kandagatla 
3920e5c9e7fSSrinivas Kandagatla 	mbhc->hph_status |= jack_type;
3930e5c9e7fSSrinivas Kandagatla 
3940e5c9e7fSSrinivas Kandagatla 	if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
3950e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
3960e5c9e7fSSrinivas Kandagatla 
3970e5c9e7fSSrinivas Kandagatla 	snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
3980e5c9e7fSSrinivas Kandagatla 			    WCD_MBHC_JACK_MASK);
3990e5c9e7fSSrinivas Kandagatla }
4000e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_report_plug(struct wcd_mbhc * mbhc,int insertion,enum snd_jack_types jack_type)4010e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
4020e5c9e7fSSrinivas Kandagatla 				 enum snd_jack_types jack_type)
4030e5c9e7fSSrinivas Kandagatla {
4040e5c9e7fSSrinivas Kandagatla 
4050e5c9e7fSSrinivas Kandagatla 	WARN_ON(!mutex_is_locked(&mbhc->lock));
4060e5c9e7fSSrinivas Kandagatla 
4070e5c9e7fSSrinivas Kandagatla 	if (!insertion) /* Report removal */
4080e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug_removal(mbhc, jack_type);
4090e5c9e7fSSrinivas Kandagatla 	else
4100e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug_insertion(mbhc, jack_type);
4110e5c9e7fSSrinivas Kandagatla 
4120e5c9e7fSSrinivas Kandagatla }
4130e5c9e7fSSrinivas Kandagatla 
wcd_cancel_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)4140e5c9e7fSSrinivas Kandagatla static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
4150e5c9e7fSSrinivas Kandagatla 				      struct work_struct *work)
4160e5c9e7fSSrinivas Kandagatla {
4170e5c9e7fSSrinivas Kandagatla 	mbhc->hs_detect_work_stop = true;
4180e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
4190e5c9e7fSSrinivas Kandagatla 	cancel_work_sync(work);
4200e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
4210e5c9e7fSSrinivas Kandagatla }
4220e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_cancel_pending_work(struct wcd_mbhc * mbhc)4230e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
4240e5c9e7fSSrinivas Kandagatla {
4250e5c9e7fSSrinivas Kandagatla 	/* cancel pending button press */
4260e5c9e7fSSrinivas Kandagatla 	wcd_cancel_btn_work(mbhc);
4270e5c9e7fSSrinivas Kandagatla 	/* cancel correct work function */
4280e5c9e7fSSrinivas Kandagatla 	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
4290e5c9e7fSSrinivas Kandagatla }
4300e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc * mbhc)4310e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
4320e5c9e7fSSrinivas Kandagatla {
4330e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_cancel_pending_work(mbhc);
4340e5c9e7fSSrinivas Kandagatla 	/* Report extension cable */
4350e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
4360e5c9e7fSSrinivas Kandagatla 	/*
4370e5c9e7fSSrinivas Kandagatla 	 * Disable HPHL trigger and MIC Schmitt triggers.
4380e5c9e7fSSrinivas Kandagatla 	 * Setup for insertion detection.
4390e5c9e7fSSrinivas Kandagatla 	 */
4400e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
4410e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
4420e5c9e7fSSrinivas Kandagatla 	/* Disable HW FSM */
4430e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
4440e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
4450e5c9e7fSSrinivas Kandagatla 
4460e5c9e7fSSrinivas Kandagatla 	/* Set the detection type appropriately */
4470e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
4480e5c9e7fSSrinivas Kandagatla 	enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
4490e5c9e7fSSrinivas Kandagatla }
4500e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_find_plug_and_report(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)4510e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
4520e5c9e7fSSrinivas Kandagatla 				   enum wcd_mbhc_plug_type plug_type)
4530e5c9e7fSSrinivas Kandagatla {
4540e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug == plug_type)
4550e5c9e7fSSrinivas Kandagatla 		return;
4560e5c9e7fSSrinivas Kandagatla 
4570e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
4580e5c9e7fSSrinivas Kandagatla 
4590e5c9e7fSSrinivas Kandagatla 	switch (plug_type) {
4600e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADPHONE:
4610e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
4620e5c9e7fSSrinivas Kandagatla 		break;
4630e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADSET:
4640e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
4650e5c9e7fSSrinivas Kandagatla 		break;
4660e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HIGH_HPH:
4670e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
4680e5c9e7fSSrinivas Kandagatla 		break;
4690e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_GND_MIC_SWAP:
4700e5c9e7fSSrinivas Kandagatla 		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
4710e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
4720e5c9e7fSSrinivas Kandagatla 		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
4730e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
4740e5c9e7fSSrinivas Kandagatla 		break;
4750e5c9e7fSSrinivas Kandagatla 	default:
4760e5c9e7fSSrinivas Kandagatla 		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
4770e5c9e7fSSrinivas Kandagatla 		     mbhc->current_plug, plug_type);
4780e5c9e7fSSrinivas Kandagatla 		break;
4790e5c9e7fSSrinivas Kandagatla 	}
4800e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
4810e5c9e7fSSrinivas Kandagatla }
4820e5c9e7fSSrinivas Kandagatla 
wcd_schedule_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)4830e5c9e7fSSrinivas Kandagatla static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
4840e5c9e7fSSrinivas Kandagatla 					    struct work_struct *work)
4850e5c9e7fSSrinivas Kandagatla {
4860e5c9e7fSSrinivas Kandagatla 	WARN_ON(!mutex_is_locked(&mbhc->lock));
4870e5c9e7fSSrinivas Kandagatla 	mbhc->hs_detect_work_stop = false;
4880e5c9e7fSSrinivas Kandagatla 	schedule_work(work);
4890e5c9e7fSSrinivas Kandagatla }
4900e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc * mbhc)4910e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
4920e5c9e7fSSrinivas Kandagatla {
4930e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component = mbhc->component;
4940e5c9e7fSSrinivas Kandagatla 
4950e5c9e7fSSrinivas Kandagatla 	WARN_ON(!mutex_is_locked(&mbhc->lock));
4960e5c9e7fSSrinivas Kandagatla 
4970e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
4980e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
4990e5c9e7fSSrinivas Kandagatla 
5000e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
5010e5c9e7fSSrinivas Kandagatla 
5020e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micbias_control) {
5030e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
5040e5c9e7fSSrinivas Kandagatla 						    MICB_ENABLE);
5050e5c9e7fSSrinivas Kandagatla 		wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
5060e5c9e7fSSrinivas Kandagatla 	}
5070e5c9e7fSSrinivas Kandagatla }
5080e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_mech_plug_detect_irq(int irq,void * data)5090e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
5100e5c9e7fSSrinivas Kandagatla {
5110e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component;
5120e5c9e7fSSrinivas Kandagatla 	enum snd_jack_types jack_type;
5130e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = data;
5140e5c9e7fSSrinivas Kandagatla 	bool detection_type;
5150e5c9e7fSSrinivas Kandagatla 
5160e5c9e7fSSrinivas Kandagatla 	component = mbhc->component;
5170e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
5180e5c9e7fSSrinivas Kandagatla 
5190e5c9e7fSSrinivas Kandagatla 	mbhc->in_swch_irq_handler = true;
5200e5c9e7fSSrinivas Kandagatla 
5210e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_cancel_pending_work(mbhc);
5220e5c9e7fSSrinivas Kandagatla 
5230e5c9e7fSSrinivas Kandagatla 	detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
5240e5c9e7fSSrinivas Kandagatla 
5250e5c9e7fSSrinivas Kandagatla 	/* Set the detection type appropriately */
5260e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
5270e5c9e7fSSrinivas Kandagatla 
5280e5c9e7fSSrinivas Kandagatla 	/* Enable micbias ramp */
5290e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
5300e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
5310e5c9e7fSSrinivas Kandagatla 
5320e5c9e7fSSrinivas Kandagatla 	if (detection_type) {
5330e5c9e7fSSrinivas Kandagatla 		if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
5340e5c9e7fSSrinivas Kandagatla 			goto exit;
5350e5c9e7fSSrinivas Kandagatla 		/* Make sure MASTER_BIAS_CTL is enabled */
5360e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_bias(component, true);
5370e5c9e7fSSrinivas Kandagatla 		mbhc->is_btn_press = false;
5380e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_adc_detect_plug_type(mbhc);
5390e5c9e7fSSrinivas Kandagatla 	} else {
5400e5c9e7fSSrinivas Kandagatla 		/* Disable HW FSM */
5410e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
5420e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
5430e5c9e7fSSrinivas Kandagatla 		mbhc->extn_cable_hph_rem = false;
5440e5c9e7fSSrinivas Kandagatla 
5450e5c9e7fSSrinivas Kandagatla 		if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
5460e5c9e7fSSrinivas Kandagatla 			goto exit;
5470e5c9e7fSSrinivas Kandagatla 
5480e5c9e7fSSrinivas Kandagatla 		mbhc->is_btn_press = false;
5490e5c9e7fSSrinivas Kandagatla 		switch (mbhc->current_plug) {
5500e5c9e7fSSrinivas Kandagatla 		case MBHC_PLUG_TYPE_HEADPHONE:
5510e5c9e7fSSrinivas Kandagatla 			jack_type = SND_JACK_HEADPHONE;
5520e5c9e7fSSrinivas Kandagatla 			break;
5530e5c9e7fSSrinivas Kandagatla 		case MBHC_PLUG_TYPE_HEADSET:
5540e5c9e7fSSrinivas Kandagatla 			jack_type = SND_JACK_HEADSET;
5550e5c9e7fSSrinivas Kandagatla 			break;
5560e5c9e7fSSrinivas Kandagatla 		case MBHC_PLUG_TYPE_HIGH_HPH:
5570e5c9e7fSSrinivas Kandagatla 			if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
5580e5c9e7fSSrinivas Kandagatla 				wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
5590e5c9e7fSSrinivas Kandagatla 			jack_type = SND_JACK_LINEOUT;
5600e5c9e7fSSrinivas Kandagatla 			break;
5610e5c9e7fSSrinivas Kandagatla 		case MBHC_PLUG_TYPE_GND_MIC_SWAP:
5620e5c9e7fSSrinivas Kandagatla 			dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
5630e5c9e7fSSrinivas Kandagatla 			goto exit;
5640e5c9e7fSSrinivas Kandagatla 		default:
5650e5c9e7fSSrinivas Kandagatla 			dev_err(mbhc->dev, "Invalid current plug: %d\n",
5660e5c9e7fSSrinivas Kandagatla 				mbhc->current_plug);
5670e5c9e7fSSrinivas Kandagatla 			goto exit;
5680e5c9e7fSSrinivas Kandagatla 		}
5690e5c9e7fSSrinivas Kandagatla 		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
5700e5c9e7fSSrinivas Kandagatla 		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
5710e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
5720e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
5730e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_report_plug(mbhc, 0, jack_type);
5740e5c9e7fSSrinivas Kandagatla 	}
5750e5c9e7fSSrinivas Kandagatla 
5760e5c9e7fSSrinivas Kandagatla exit:
5770e5c9e7fSSrinivas Kandagatla 	mbhc->in_swch_irq_handler = false;
5780e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
5790e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
5800e5c9e7fSSrinivas Kandagatla }
5810e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_button_mask(struct wcd_mbhc * mbhc)5820e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
5830e5c9e7fSSrinivas Kandagatla {
5840e5c9e7fSSrinivas Kandagatla 	int mask = 0;
5850e5c9e7fSSrinivas Kandagatla 	int btn;
5860e5c9e7fSSrinivas Kandagatla 
5870e5c9e7fSSrinivas Kandagatla 	btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
5880e5c9e7fSSrinivas Kandagatla 
5890e5c9e7fSSrinivas Kandagatla 	switch (btn) {
5900e5c9e7fSSrinivas Kandagatla 	case 0:
5910e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_0;
5920e5c9e7fSSrinivas Kandagatla 		break;
5930e5c9e7fSSrinivas Kandagatla 	case 1:
5940e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_1;
5950e5c9e7fSSrinivas Kandagatla 		break;
5960e5c9e7fSSrinivas Kandagatla 	case 2:
5970e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_2;
5980e5c9e7fSSrinivas Kandagatla 		break;
5990e5c9e7fSSrinivas Kandagatla 	case 3:
6000e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_3;
6010e5c9e7fSSrinivas Kandagatla 		break;
6020e5c9e7fSSrinivas Kandagatla 	case 4:
6030e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_4;
6040e5c9e7fSSrinivas Kandagatla 		break;
6050e5c9e7fSSrinivas Kandagatla 	case 5:
6060e5c9e7fSSrinivas Kandagatla 		mask = SND_JACK_BTN_5;
6070e5c9e7fSSrinivas Kandagatla 		break;
6080e5c9e7fSSrinivas Kandagatla 	default:
6090e5c9e7fSSrinivas Kandagatla 		break;
6100e5c9e7fSSrinivas Kandagatla 	}
6110e5c9e7fSSrinivas Kandagatla 
6120e5c9e7fSSrinivas Kandagatla 	return mask;
6130e5c9e7fSSrinivas Kandagatla }
6140e5c9e7fSSrinivas Kandagatla 
wcd_btn_long_press_fn(struct work_struct * work)6150e5c9e7fSSrinivas Kandagatla static void wcd_btn_long_press_fn(struct work_struct *work)
6160e5c9e7fSSrinivas Kandagatla {
6170e5c9e7fSSrinivas Kandagatla 	struct delayed_work *dwork = to_delayed_work(work);
6180e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
6190e5c9e7fSSrinivas Kandagatla 
6200e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
6210e5c9e7fSSrinivas Kandagatla 		snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
6220e5c9e7fSSrinivas Kandagatla 				    mbhc->buttons_pressed);
6230e5c9e7fSSrinivas Kandagatla }
6240e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_btn_press_handler(int irq,void * data)6250e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
6260e5c9e7fSSrinivas Kandagatla {
6270e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = data;
6280e5c9e7fSSrinivas Kandagatla 	int mask;
6290e5c9e7fSSrinivas Kandagatla 	unsigned long msec_val;
6300e5c9e7fSSrinivas Kandagatla 
6310e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
6320e5c9e7fSSrinivas Kandagatla 	wcd_cancel_btn_work(mbhc);
6330e5c9e7fSSrinivas Kandagatla 	mbhc->is_btn_press = true;
6340e5c9e7fSSrinivas Kandagatla 	msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
6350e5c9e7fSSrinivas Kandagatla 
6360e5c9e7fSSrinivas Kandagatla 	/* Too short, ignore button press */
6370e5c9e7fSSrinivas Kandagatla 	if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
6380e5c9e7fSSrinivas Kandagatla 		goto done;
6390e5c9e7fSSrinivas Kandagatla 
6400e5c9e7fSSrinivas Kandagatla 	/* If switch interrupt already kicked in, ignore button press */
6410e5c9e7fSSrinivas Kandagatla 	if (mbhc->in_swch_irq_handler)
6420e5c9e7fSSrinivas Kandagatla 		goto done;
6430e5c9e7fSSrinivas Kandagatla 
6440e5c9e7fSSrinivas Kandagatla 	/* Plug isn't headset, ignore button press */
6450e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
6460e5c9e7fSSrinivas Kandagatla 		goto done;
6470e5c9e7fSSrinivas Kandagatla 
6480e5c9e7fSSrinivas Kandagatla 	mask = wcd_mbhc_get_button_mask(mbhc);
6490e5c9e7fSSrinivas Kandagatla 	mbhc->buttons_pressed |= mask;
6500e5c9e7fSSrinivas Kandagatla 	if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
6510e5c9e7fSSrinivas Kandagatla 		WARN(1, "Button pressed twice without release event\n");
6520e5c9e7fSSrinivas Kandagatla done:
6530e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
6540e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
6550e5c9e7fSSrinivas Kandagatla }
6560e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_btn_release_handler(int irq,void * data)6570e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
6580e5c9e7fSSrinivas Kandagatla {
6590e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = data;
6600e5c9e7fSSrinivas Kandagatla 	int ret;
6610e5c9e7fSSrinivas Kandagatla 
6620e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
6630e5c9e7fSSrinivas Kandagatla 	if (mbhc->is_btn_press)
6640e5c9e7fSSrinivas Kandagatla 		mbhc->is_btn_press = false;
6650e5c9e7fSSrinivas Kandagatla 	else /* fake btn press */
6660e5c9e7fSSrinivas Kandagatla 		goto exit;
6670e5c9e7fSSrinivas Kandagatla 
6680e5c9e7fSSrinivas Kandagatla 	if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
6690e5c9e7fSSrinivas Kandagatla 		goto exit;
6700e5c9e7fSSrinivas Kandagatla 
6710e5c9e7fSSrinivas Kandagatla 	ret = wcd_cancel_btn_work(mbhc);
6720e5c9e7fSSrinivas Kandagatla 	if (ret == 0) { /* Reporting long button release event */
6730e5c9e7fSSrinivas Kandagatla 		snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
6740e5c9e7fSSrinivas Kandagatla 	} else {
6750e5c9e7fSSrinivas Kandagatla 		if (!mbhc->in_swch_irq_handler) {
6760e5c9e7fSSrinivas Kandagatla 			/* Reporting btn press n Release */
6770e5c9e7fSSrinivas Kandagatla 			snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
6780e5c9e7fSSrinivas Kandagatla 					    mbhc->buttons_pressed);
6790e5c9e7fSSrinivas Kandagatla 			snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
6800e5c9e7fSSrinivas Kandagatla 		}
6810e5c9e7fSSrinivas Kandagatla 	}
6820e5c9e7fSSrinivas Kandagatla 	mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
6830e5c9e7fSSrinivas Kandagatla exit:
6840e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
6850e5c9e7fSSrinivas Kandagatla 
6860e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
6870e5c9e7fSSrinivas Kandagatla }
6880e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_hph_ocp_irq(struct wcd_mbhc * mbhc,bool hphr)6890e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
6900e5c9e7fSSrinivas Kandagatla {
6910e5c9e7fSSrinivas Kandagatla 
6920e5c9e7fSSrinivas Kandagatla 	/* TODO Find a better way to report this to Userspace */
6930e5c9e7fSSrinivas Kandagatla 	dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
6940e5c9e7fSSrinivas Kandagatla 		hphr ? "HPHR" : "HPHL");
6950e5c9e7fSSrinivas Kandagatla 
6960e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
6970e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
6980e5c9e7fSSrinivas Kandagatla 
6990e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
7000e5c9e7fSSrinivas Kandagatla }
7010e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_hphl_ocp_irq(int irq,void * data)7020e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
7030e5c9e7fSSrinivas Kandagatla {
7040e5c9e7fSSrinivas Kandagatla 	return wcd_mbhc_hph_ocp_irq(data, false);
7050e5c9e7fSSrinivas Kandagatla }
7060e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_hphr_ocp_irq(int irq,void * data)7070e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
7080e5c9e7fSSrinivas Kandagatla {
7090e5c9e7fSSrinivas Kandagatla 	return wcd_mbhc_hph_ocp_irq(data, true);
7100e5c9e7fSSrinivas Kandagatla }
7110e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_initialise(struct wcd_mbhc * mbhc)7120e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
7130e5c9e7fSSrinivas Kandagatla {
7140e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component = mbhc->component;
715cc4d891fSSrinivas Kandagatla 	int ret;
716cc4d891fSSrinivas Kandagatla 
717e18f6bcfSKrzysztof Kozlowski 	ret = pm_runtime_get_sync(component->dev);
718cc4d891fSSrinivas Kandagatla 	if (ret < 0 && ret != -EACCES) {
719cc4d891fSSrinivas Kandagatla 		dev_err_ratelimited(component->dev,
720e18f6bcfSKrzysztof Kozlowski 				    "pm_runtime_get_sync failed in %s, ret %d\n",
721cc4d891fSSrinivas Kandagatla 				    __func__, ret);
722e18f6bcfSKrzysztof Kozlowski 		pm_runtime_put_noidle(component->dev);
723cc4d891fSSrinivas Kandagatla 		return ret;
724cc4d891fSSrinivas Kandagatla 	}
7250e5c9e7fSSrinivas Kandagatla 
7260e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
7270e5c9e7fSSrinivas Kandagatla 
7280e5c9e7fSSrinivas Kandagatla 	/* enable HS detection */
7290e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->hph_pull_up_control_v2)
7300e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->hph_pull_up_control_v2(component,
7310e5c9e7fSSrinivas Kandagatla 						      HS_PULLUP_I_DEFAULT);
7320e5c9e7fSSrinivas Kandagatla 	else if (mbhc->mbhc_cb->hph_pull_up_control)
7330e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
7340e5c9e7fSSrinivas Kandagatla 	else
7350e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
7360e5c9e7fSSrinivas Kandagatla 
7370e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
7380e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
7390e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
7400e5c9e7fSSrinivas Kandagatla 	if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
7410e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
7420e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
7430e5c9e7fSSrinivas Kandagatla 
7440e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
7450e5c9e7fSSrinivas Kandagatla 
7460e5c9e7fSSrinivas Kandagatla 	/* Insertion debounce set to 96ms */
7470e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
7480e5c9e7fSSrinivas Kandagatla 
7490e5c9e7fSSrinivas Kandagatla 	/* Button Debounce set to 16ms */
7500e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
7510e5c9e7fSSrinivas Kandagatla 
7520e5c9e7fSSrinivas Kandagatla 	/* enable bias */
7530e5c9e7fSSrinivas Kandagatla 	mbhc->mbhc_cb->mbhc_bias(component, true);
7540e5c9e7fSSrinivas Kandagatla 	/* enable MBHC clock */
7550e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->clk_setup)
7560e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->clk_setup(component, true);
7570e5c9e7fSSrinivas Kandagatla 
7580e5c9e7fSSrinivas Kandagatla 	/* program HS_VREF value */
7590e5c9e7fSSrinivas Kandagatla 	wcd_program_hs_vref(mbhc);
7600e5c9e7fSSrinivas Kandagatla 
7610e5c9e7fSSrinivas Kandagatla 	wcd_program_btn_threshold(mbhc, false);
7620e5c9e7fSSrinivas Kandagatla 
7630e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
7640e5c9e7fSSrinivas Kandagatla 
765cc4d891fSSrinivas Kandagatla 	pm_runtime_mark_last_busy(component->dev);
766cc4d891fSSrinivas Kandagatla 	pm_runtime_put_autosuspend(component->dev);
767cc4d891fSSrinivas Kandagatla 
7680e5c9e7fSSrinivas Kandagatla 	return 0;
7690e5c9e7fSSrinivas Kandagatla }
7700e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_micbias(struct wcd_mbhc * mbhc)7710e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
7720e5c9e7fSSrinivas Kandagatla {
7730e5c9e7fSSrinivas Kandagatla 	int micbias = 0;
7740e5c9e7fSSrinivas Kandagatla 
7750e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->get_micbias_val) {
7760e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
7770e5c9e7fSSrinivas Kandagatla 	} else {
7780e5c9e7fSSrinivas Kandagatla 		u8 vout_ctl = 0;
7790e5c9e7fSSrinivas Kandagatla 		/* Read MBHC Micbias (Mic Bias2) voltage */
7800e5c9e7fSSrinivas Kandagatla 		vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
7810e5c9e7fSSrinivas Kandagatla 		/* Formula for getting micbias from vout
7820e5c9e7fSSrinivas Kandagatla 		 * micbias = 1.0V + VOUT_CTL * 50mV
7830e5c9e7fSSrinivas Kandagatla 		 */
7840e5c9e7fSSrinivas Kandagatla 		micbias = 1000 + (vout_ctl * 50);
7850e5c9e7fSSrinivas Kandagatla 	}
7860e5c9e7fSSrinivas Kandagatla 	return micbias;
7870e5c9e7fSSrinivas Kandagatla }
7880e5c9e7fSSrinivas Kandagatla 
wcd_get_voltage_from_adc(u8 val,int micbias)7890e5c9e7fSSrinivas Kandagatla static int wcd_get_voltage_from_adc(u8 val, int micbias)
7900e5c9e7fSSrinivas Kandagatla {
7910e5c9e7fSSrinivas Kandagatla 	/* Formula for calculating voltage from ADC
7920e5c9e7fSSrinivas Kandagatla 	 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
7930e5c9e7fSSrinivas Kandagatla 	 */
7940e5c9e7fSSrinivas Kandagatla 	return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
7950e5c9e7fSSrinivas Kandagatla }
7960e5c9e7fSSrinivas Kandagatla 
wcd_measure_adc_continuous(struct wcd_mbhc * mbhc)7970e5c9e7fSSrinivas Kandagatla static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
7980e5c9e7fSSrinivas Kandagatla {
7990e5c9e7fSSrinivas Kandagatla 	u8 adc_result;
8000e5c9e7fSSrinivas Kandagatla 	int output_mv;
8010e5c9e7fSSrinivas Kandagatla 	int retry = 3;
8020e5c9e7fSSrinivas Kandagatla 	u8 adc_en;
8030e5c9e7fSSrinivas Kandagatla 
8040e5c9e7fSSrinivas Kandagatla 	/* Pre-requisites for ADC continuous measurement */
8050e5c9e7fSSrinivas Kandagatla 	/* Read legacy electircal detection and disable */
8060e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
8070e5c9e7fSSrinivas Kandagatla 	/* Set ADC to continuous measurement */
8080e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
8090e5c9e7fSSrinivas Kandagatla 	/* Read ADC Enable bit to restore after adc measurement */
8100e5c9e7fSSrinivas Kandagatla 	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
8110e5c9e7fSSrinivas Kandagatla 	/* Disable ADC_ENABLE bit */
8120e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
8130e5c9e7fSSrinivas Kandagatla 	/* Disable MBHC FSM */
8140e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
8150e5c9e7fSSrinivas Kandagatla 	/* Set the MUX selection to IN2P */
8160e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
8170e5c9e7fSSrinivas Kandagatla 	/* Enable MBHC FSM */
8180e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
8190e5c9e7fSSrinivas Kandagatla 	/* Enable ADC_ENABLE bit */
8200e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
8210e5c9e7fSSrinivas Kandagatla 
8220e5c9e7fSSrinivas Kandagatla 	while (retry--) {
8230e5c9e7fSSrinivas Kandagatla 		/* wait for 3 msec before reading ADC result */
8240e5c9e7fSSrinivas Kandagatla 		usleep_range(3000, 3100);
8250e5c9e7fSSrinivas Kandagatla 		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
8260e5c9e7fSSrinivas Kandagatla 	}
8270e5c9e7fSSrinivas Kandagatla 
8280e5c9e7fSSrinivas Kandagatla 	/* Restore ADC Enable */
8290e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
8300e5c9e7fSSrinivas Kandagatla 	/* Get voltage from ADC result */
8310e5c9e7fSSrinivas Kandagatla 	output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
8320e5c9e7fSSrinivas Kandagatla 
8330e5c9e7fSSrinivas Kandagatla 	return output_mv;
8340e5c9e7fSSrinivas Kandagatla }
8350e5c9e7fSSrinivas Kandagatla 
wcd_measure_adc_once(struct wcd_mbhc * mbhc,int mux_ctl)8360e5c9e7fSSrinivas Kandagatla static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
8370e5c9e7fSSrinivas Kandagatla {
8380e5c9e7fSSrinivas Kandagatla 	struct device *dev = mbhc->dev;
8390e5c9e7fSSrinivas Kandagatla 	u8 adc_timeout = 0;
8400e5c9e7fSSrinivas Kandagatla 	u8 adc_complete = 0;
8410e5c9e7fSSrinivas Kandagatla 	u8 adc_result;
8420e5c9e7fSSrinivas Kandagatla 	int retry = 6;
8430e5c9e7fSSrinivas Kandagatla 	int ret;
8440e5c9e7fSSrinivas Kandagatla 	int output_mv = 0;
8450e5c9e7fSSrinivas Kandagatla 	u8 adc_en;
8460e5c9e7fSSrinivas Kandagatla 
8470e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
8480e5c9e7fSSrinivas Kandagatla 	/* Read ADC Enable bit to restore after adc measurement */
8490e5c9e7fSSrinivas Kandagatla 	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
8500e5c9e7fSSrinivas Kandagatla 	/* Trigger ADC one time measurement */
8510e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
8520e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
8530e5c9e7fSSrinivas Kandagatla 	/* Set the appropriate MUX selection */
8540e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
8550e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
8560e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
8570e5c9e7fSSrinivas Kandagatla 
8580e5c9e7fSSrinivas Kandagatla 	while (retry--) {
8590e5c9e7fSSrinivas Kandagatla 		/* wait for 600usec to get adc results */
8600e5c9e7fSSrinivas Kandagatla 		usleep_range(600, 610);
8610e5c9e7fSSrinivas Kandagatla 
8620e5c9e7fSSrinivas Kandagatla 		/* check for ADC Timeout */
8630e5c9e7fSSrinivas Kandagatla 		adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
8640e5c9e7fSSrinivas Kandagatla 		if (adc_timeout)
8650e5c9e7fSSrinivas Kandagatla 			continue;
8660e5c9e7fSSrinivas Kandagatla 
8670e5c9e7fSSrinivas Kandagatla 		/* Read ADC complete bit */
8680e5c9e7fSSrinivas Kandagatla 		adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
8690e5c9e7fSSrinivas Kandagatla 		if (!adc_complete)
8700e5c9e7fSSrinivas Kandagatla 			continue;
8710e5c9e7fSSrinivas Kandagatla 
8720e5c9e7fSSrinivas Kandagatla 		/* Read ADC result */
8730e5c9e7fSSrinivas Kandagatla 		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
8740e5c9e7fSSrinivas Kandagatla 
8750e5c9e7fSSrinivas Kandagatla 		/* Get voltage from ADC result */
8760e5c9e7fSSrinivas Kandagatla 		output_mv = wcd_get_voltage_from_adc(adc_result,
8770e5c9e7fSSrinivas Kandagatla 						wcd_mbhc_get_micbias(mbhc));
8780e5c9e7fSSrinivas Kandagatla 		break;
8790e5c9e7fSSrinivas Kandagatla 	}
8800e5c9e7fSSrinivas Kandagatla 
8810e5c9e7fSSrinivas Kandagatla 	/* Restore ADC Enable */
8820e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
8830e5c9e7fSSrinivas Kandagatla 
8840e5c9e7fSSrinivas Kandagatla 	if (retry <= 0) {
8850e5c9e7fSSrinivas Kandagatla 		dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
8860e5c9e7fSSrinivas Kandagatla 			__func__, adc_complete, adc_timeout);
8870e5c9e7fSSrinivas Kandagatla 		ret = -EINVAL;
8880e5c9e7fSSrinivas Kandagatla 	} else {
8890e5c9e7fSSrinivas Kandagatla 		ret = output_mv;
8900e5c9e7fSSrinivas Kandagatla 	}
8910e5c9e7fSSrinivas Kandagatla 
8920e5c9e7fSSrinivas Kandagatla 	return ret;
8930e5c9e7fSSrinivas Kandagatla }
8940e5c9e7fSSrinivas Kandagatla 
8950e5c9e7fSSrinivas Kandagatla /* To determine if cross connection occurred */
wcd_check_cross_conn(struct wcd_mbhc * mbhc)8960e5c9e7fSSrinivas Kandagatla static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
8970e5c9e7fSSrinivas Kandagatla {
8980e5c9e7fSSrinivas Kandagatla 	u8 adc_mode, elect_ctl, adc_en, fsm_en;
8990e5c9e7fSSrinivas Kandagatla 	int hphl_adc_res, hphr_adc_res;
9000e5c9e7fSSrinivas Kandagatla 	bool is_cross_conn = false;
9010e5c9e7fSSrinivas Kandagatla 
9020e5c9e7fSSrinivas Kandagatla 	/* If PA is enabled, dont check for cross-connection */
9030e5c9e7fSSrinivas Kandagatla 	if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
9040e5c9e7fSSrinivas Kandagatla 		return -EINVAL;
9050e5c9e7fSSrinivas Kandagatla 
9060e5c9e7fSSrinivas Kandagatla 	/* Read legacy electircal detection and disable */
9070e5c9e7fSSrinivas Kandagatla 	elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
9080e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
9090e5c9e7fSSrinivas Kandagatla 
9100e5c9e7fSSrinivas Kandagatla 	/* Read and set ADC to single measurement */
9110e5c9e7fSSrinivas Kandagatla 	adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
9120e5c9e7fSSrinivas Kandagatla 	/* Read ADC Enable bit to restore after adc measurement */
9130e5c9e7fSSrinivas Kandagatla 	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
9140e5c9e7fSSrinivas Kandagatla 	/* Read FSM status */
9150e5c9e7fSSrinivas Kandagatla 	fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
9160e5c9e7fSSrinivas Kandagatla 
9170e5c9e7fSSrinivas Kandagatla 	/* Get adc result for HPH L */
9180e5c9e7fSSrinivas Kandagatla 	hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
9190e5c9e7fSSrinivas Kandagatla 	if (hphl_adc_res < 0)
9200e5c9e7fSSrinivas Kandagatla 		return hphl_adc_res;
9210e5c9e7fSSrinivas Kandagatla 
9220e5c9e7fSSrinivas Kandagatla 	/* Get adc result for HPH R in mV */
9230e5c9e7fSSrinivas Kandagatla 	hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
9240e5c9e7fSSrinivas Kandagatla 	if (hphr_adc_res < 0)
9250e5c9e7fSSrinivas Kandagatla 		return hphr_adc_res;
9260e5c9e7fSSrinivas Kandagatla 
9270e5c9e7fSSrinivas Kandagatla 	if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
9280e5c9e7fSSrinivas Kandagatla 	    hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
9290e5c9e7fSSrinivas Kandagatla 		is_cross_conn = true;
9300e5c9e7fSSrinivas Kandagatla 
9310e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
9320e5c9e7fSSrinivas Kandagatla 	/* Set the MUX selection to Auto */
9330e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
9340e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
9350e5c9e7fSSrinivas Kandagatla 	/* Restore ADC Enable */
9360e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
9370e5c9e7fSSrinivas Kandagatla 	/* Restore ADC mode */
9380e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
9390e5c9e7fSSrinivas Kandagatla 	/* Restore FSM state */
9400e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
9410e5c9e7fSSrinivas Kandagatla 	/* Restore electrical detection */
9420e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
9430e5c9e7fSSrinivas Kandagatla 
9440e5c9e7fSSrinivas Kandagatla 	return is_cross_conn;
9450e5c9e7fSSrinivas Kandagatla }
9460e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc * mbhc)9470e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
9480e5c9e7fSSrinivas Kandagatla {
9490e5c9e7fSSrinivas Kandagatla 	int hs_threshold, micbias_mv;
9500e5c9e7fSSrinivas Kandagatla 
9510e5c9e7fSSrinivas Kandagatla 	micbias_mv = wcd_mbhc_get_micbias(mbhc);
9520e5c9e7fSSrinivas Kandagatla 	if (mbhc->cfg->hs_thr) {
9530e5c9e7fSSrinivas Kandagatla 		if (mbhc->cfg->micb_mv == micbias_mv)
9540e5c9e7fSSrinivas Kandagatla 			hs_threshold = mbhc->cfg->hs_thr;
9550e5c9e7fSSrinivas Kandagatla 		else
9560e5c9e7fSSrinivas Kandagatla 			hs_threshold = (mbhc->cfg->hs_thr *
9570e5c9e7fSSrinivas Kandagatla 				micbias_mv) / mbhc->cfg->micb_mv;
9580e5c9e7fSSrinivas Kandagatla 	} else {
9590e5c9e7fSSrinivas Kandagatla 		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
9600e5c9e7fSSrinivas Kandagatla 			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
9610e5c9e7fSSrinivas Kandagatla 	}
9620e5c9e7fSSrinivas Kandagatla 	return hs_threshold;
9630e5c9e7fSSrinivas Kandagatla }
9640e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc * mbhc)9650e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
9660e5c9e7fSSrinivas Kandagatla {
9670e5c9e7fSSrinivas Kandagatla 	int hph_threshold, micbias_mv;
9680e5c9e7fSSrinivas Kandagatla 
9690e5c9e7fSSrinivas Kandagatla 	micbias_mv = wcd_mbhc_get_micbias(mbhc);
9700e5c9e7fSSrinivas Kandagatla 	if (mbhc->cfg->hph_thr) {
9710e5c9e7fSSrinivas Kandagatla 		if (mbhc->cfg->micb_mv == micbias_mv)
9720e5c9e7fSSrinivas Kandagatla 			hph_threshold = mbhc->cfg->hph_thr;
9730e5c9e7fSSrinivas Kandagatla 		else
9740e5c9e7fSSrinivas Kandagatla 			hph_threshold = (mbhc->cfg->hph_thr *
9750e5c9e7fSSrinivas Kandagatla 				micbias_mv) / mbhc->cfg->micb_mv;
9760e5c9e7fSSrinivas Kandagatla 	} else {
9770e5c9e7fSSrinivas Kandagatla 		hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
9780e5c9e7fSSrinivas Kandagatla 			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
9790e5c9e7fSSrinivas Kandagatla 	}
9800e5c9e7fSSrinivas Kandagatla 	return hph_threshold;
9810e5c9e7fSSrinivas Kandagatla }
9820e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)9830e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
9840e5c9e7fSSrinivas Kandagatla 					   enum wcd_mbhc_plug_type plug_type)
9850e5c9e7fSSrinivas Kandagatla {
9860e5c9e7fSSrinivas Kandagatla 	bool micbias2 = false;
9870e5c9e7fSSrinivas Kandagatla 
9880e5c9e7fSSrinivas Kandagatla 	switch (plug_type) {
9890e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADPHONE:
9900e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
9910e5c9e7fSSrinivas Kandagatla 		break;
9920e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADSET:
9930e5c9e7fSSrinivas Kandagatla 		if (mbhc->mbhc_cb->micbias_enable_status)
9940e5c9e7fSSrinivas Kandagatla 			micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
9950e5c9e7fSSrinivas Kandagatla 									MIC_BIAS_2);
9960e5c9e7fSSrinivas Kandagatla 
9970e5c9e7fSSrinivas Kandagatla 		if (!mbhc->is_hs_recording && !micbias2)
9980e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
9990e5c9e7fSSrinivas Kandagatla 		break;
10000e5c9e7fSSrinivas Kandagatla 	default:
10010e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
10020e5c9e7fSSrinivas Kandagatla 		break;
10030e5c9e7fSSrinivas Kandagatla 
100403c0cbd9SYang Li 	}
10050e5c9e7fSSrinivas Kandagatla }
10060e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_bcs_enable(struct wcd_mbhc * mbhc,int plug_type,bool enable)10070e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
10080e5c9e7fSSrinivas Kandagatla {
10090e5c9e7fSSrinivas Kandagatla 	switch (plug_type) {
10100e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADSET:
10110e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADPHONE:
10120e5c9e7fSSrinivas Kandagatla 		if (mbhc->mbhc_cb->bcs_enable)
10130e5c9e7fSSrinivas Kandagatla 			mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
10140e5c9e7fSSrinivas Kandagatla 		break;
10150e5c9e7fSSrinivas Kandagatla 	default:
10160e5c9e7fSSrinivas Kandagatla 		break;
10170e5c9e7fSSrinivas Kandagatla 	}
10180e5c9e7fSSrinivas Kandagatla }
10190e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_plug_from_adc(struct wcd_mbhc * mbhc,int adc_result)10200e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
10210e5c9e7fSSrinivas Kandagatla 
10220e5c9e7fSSrinivas Kandagatla {
10230e5c9e7fSSrinivas Kandagatla 	enum wcd_mbhc_plug_type plug_type;
10240e5c9e7fSSrinivas Kandagatla 	u32 hph_thr, hs_thr;
10250e5c9e7fSSrinivas Kandagatla 
10260e5c9e7fSSrinivas Kandagatla 	hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
10270e5c9e7fSSrinivas Kandagatla 	hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
10280e5c9e7fSSrinivas Kandagatla 
10290e5c9e7fSSrinivas Kandagatla 	if (adc_result < hph_thr)
10300e5c9e7fSSrinivas Kandagatla 		plug_type = MBHC_PLUG_TYPE_HEADPHONE;
10310e5c9e7fSSrinivas Kandagatla 	else if (adc_result > hs_thr)
10320e5c9e7fSSrinivas Kandagatla 		plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
10330e5c9e7fSSrinivas Kandagatla 	else
10340e5c9e7fSSrinivas Kandagatla 		plug_type = MBHC_PLUG_TYPE_HEADSET;
10350e5c9e7fSSrinivas Kandagatla 
10360e5c9e7fSSrinivas Kandagatla 	return plug_type;
10370e5c9e7fSSrinivas Kandagatla }
10380e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc * mbhc)10393c8a3ad4SSrinivasa Rao Mandadapu static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
10403c8a3ad4SSrinivasa Rao Mandadapu {
10413c8a3ad4SSrinivasa Rao Mandadapu 	int hs_threshold, micbias_mv;
10423c8a3ad4SSrinivasa Rao Mandadapu 
10433c8a3ad4SSrinivasa Rao Mandadapu 	micbias_mv = wcd_mbhc_get_micbias(mbhc);
10443c8a3ad4SSrinivasa Rao Mandadapu 	if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
10453c8a3ad4SSrinivasa Rao Mandadapu 		if (mbhc->cfg->micb_mv == micbias_mv)
10463c8a3ad4SSrinivasa Rao Mandadapu 			hs_threshold = mbhc->cfg->hs_thr;
10473c8a3ad4SSrinivasa Rao Mandadapu 		else
10483c8a3ad4SSrinivasa Rao Mandadapu 			hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
10493c8a3ad4SSrinivasa Rao Mandadapu 	} else {
10503c8a3ad4SSrinivasa Rao Mandadapu 		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
10513c8a3ad4SSrinivasa Rao Mandadapu 							WCD_MBHC_ADC_MICBIAS_MV);
10523c8a3ad4SSrinivasa Rao Mandadapu 	}
10533c8a3ad4SSrinivasa Rao Mandadapu 	return hs_threshold;
10543c8a3ad4SSrinivasa Rao Mandadapu }
10553c8a3ad4SSrinivasa Rao Mandadapu 
wcd_mbhc_check_for_spl_headset(struct wcd_mbhc * mbhc)10563c8a3ad4SSrinivasa Rao Mandadapu static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
10573c8a3ad4SSrinivasa Rao Mandadapu {
10583c8a3ad4SSrinivasa Rao Mandadapu 	bool is_spl_hs = false;
10593c8a3ad4SSrinivasa Rao Mandadapu 	int output_mv, hs_threshold, hph_threshold;
10603c8a3ad4SSrinivasa Rao Mandadapu 
10613c8a3ad4SSrinivasa Rao Mandadapu 	if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
10623c8a3ad4SSrinivasa Rao Mandadapu 		return false;
10633c8a3ad4SSrinivasa Rao Mandadapu 
10643c8a3ad4SSrinivasa Rao Mandadapu 	/* Bump up MIC_BIAS2 to 2.7V */
10653c8a3ad4SSrinivasa Rao Mandadapu 	mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
10663c8a3ad4SSrinivasa Rao Mandadapu 	usleep_range(10000, 10100);
10673c8a3ad4SSrinivasa Rao Mandadapu 
10683c8a3ad4SSrinivasa Rao Mandadapu 	output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
10693c8a3ad4SSrinivasa Rao Mandadapu 	hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
10703c8a3ad4SSrinivasa Rao Mandadapu 	hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
10713c8a3ad4SSrinivasa Rao Mandadapu 
1072b38892b5SSrinivasa Rao Mandadapu 	if (!(output_mv > hs_threshold || output_mv < hph_threshold))
10733c8a3ad4SSrinivasa Rao Mandadapu 		is_spl_hs = true;
10743c8a3ad4SSrinivasa Rao Mandadapu 
10753c8a3ad4SSrinivasa Rao Mandadapu 	/* Back MIC_BIAS2 to 1.8v if the type is not special headset */
10763c8a3ad4SSrinivasa Rao Mandadapu 	if (!is_spl_hs) {
10773c8a3ad4SSrinivasa Rao Mandadapu 		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
10783c8a3ad4SSrinivasa Rao Mandadapu 		/* Add 10ms delay for micbias to settle */
10793c8a3ad4SSrinivasa Rao Mandadapu 		usleep_range(10000, 10100);
10803c8a3ad4SSrinivasa Rao Mandadapu 	}
10813c8a3ad4SSrinivasa Rao Mandadapu 
10823c8a3ad4SSrinivasa Rao Mandadapu 	return is_spl_hs;
10833c8a3ad4SSrinivasa Rao Mandadapu }
10843c8a3ad4SSrinivasa Rao Mandadapu 
wcd_correct_swch_plug(struct work_struct * work)10850e5c9e7fSSrinivas Kandagatla static void wcd_correct_swch_plug(struct work_struct *work)
10860e5c9e7fSSrinivas Kandagatla {
10870e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc;
10880e5c9e7fSSrinivas Kandagatla 	struct snd_soc_component *component;
10890e5c9e7fSSrinivas Kandagatla 	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
10900e5c9e7fSSrinivas Kandagatla 	unsigned long timeout;
10910e5c9e7fSSrinivas Kandagatla 	int pt_gnd_mic_swap_cnt = 0;
10923c8a3ad4SSrinivasa Rao Mandadapu 	int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
10933c8a3ad4SSrinivasa Rao Mandadapu 	bool is_spl_hs = false;
10940e5c9e7fSSrinivas Kandagatla 	bool is_pa_on;
1095cc4d891fSSrinivas Kandagatla 	int ret;
10960e5c9e7fSSrinivas Kandagatla 
10970e5c9e7fSSrinivas Kandagatla 	mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
10980e5c9e7fSSrinivas Kandagatla 	component = mbhc->component;
10990e5c9e7fSSrinivas Kandagatla 
1100e18f6bcfSKrzysztof Kozlowski 	ret = pm_runtime_get_sync(component->dev);
1101cc4d891fSSrinivas Kandagatla 	if (ret < 0 && ret != -EACCES) {
1102cc4d891fSSrinivas Kandagatla 		dev_err_ratelimited(component->dev,
1103e18f6bcfSKrzysztof Kozlowski 				    "pm_runtime_get_sync failed in %s, ret %d\n",
1104cc4d891fSSrinivas Kandagatla 				    __func__, ret);
1105e18f6bcfSKrzysztof Kozlowski 		pm_runtime_put_noidle(component->dev);
1106cc4d891fSSrinivas Kandagatla 		return;
1107cc4d891fSSrinivas Kandagatla 	}
11083c8a3ad4SSrinivasa Rao Mandadapu 	micbias_mv = wcd_mbhc_get_micbias(mbhc);
11090e5c9e7fSSrinivas Kandagatla 	hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
11100e5c9e7fSSrinivas Kandagatla 
11110e5c9e7fSSrinivas Kandagatla 	/* Mask ADC COMPLETE interrupt */
11120e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
11130e5c9e7fSSrinivas Kandagatla 
11140e5c9e7fSSrinivas Kandagatla 	/* Check for cross connection */
11150e5c9e7fSSrinivas Kandagatla 	do {
11160e5c9e7fSSrinivas Kandagatla 		cross_conn = wcd_check_cross_conn(mbhc);
11170e5c9e7fSSrinivas Kandagatla 		try++;
11180e5c9e7fSSrinivas Kandagatla 	} while (try < GND_MIC_SWAP_THRESHOLD);
11190e5c9e7fSSrinivas Kandagatla 
11200e5c9e7fSSrinivas Kandagatla 	if (cross_conn > 0) {
11210e5c9e7fSSrinivas Kandagatla 		plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
11220e5c9e7fSSrinivas Kandagatla 		dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
11230e5c9e7fSSrinivas Kandagatla 			plug_type);
11240e5c9e7fSSrinivas Kandagatla 		goto correct_plug_type;
11250e5c9e7fSSrinivas Kandagatla 	}
11260e5c9e7fSSrinivas Kandagatla 
11270e5c9e7fSSrinivas Kandagatla 	/* Find plug type */
11280e5c9e7fSSrinivas Kandagatla 	output_mv = wcd_measure_adc_continuous(mbhc);
11290e5c9e7fSSrinivas Kandagatla 	plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11300e5c9e7fSSrinivas Kandagatla 
11310e5c9e7fSSrinivas Kandagatla 	/*
11320e5c9e7fSSrinivas Kandagatla 	 * Report plug type if it is either headset or headphone
11330e5c9e7fSSrinivas Kandagatla 	 * else start the 3 sec loop
11340e5c9e7fSSrinivas Kandagatla 	 */
11350e5c9e7fSSrinivas Kandagatla 	switch (plug_type) {
11360e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADPHONE:
11370e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
11380e5c9e7fSSrinivas Kandagatla 		break;
11390e5c9e7fSSrinivas Kandagatla 	case MBHC_PLUG_TYPE_HEADSET:
11400e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
11410e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
11420e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
11430e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
11440e5c9e7fSSrinivas Kandagatla 		break;
11450e5c9e7fSSrinivas Kandagatla 	default:
11460e5c9e7fSSrinivas Kandagatla 		break;
11470e5c9e7fSSrinivas Kandagatla 	}
11480e5c9e7fSSrinivas Kandagatla 
11490e5c9e7fSSrinivas Kandagatla correct_plug_type:
11500e5c9e7fSSrinivas Kandagatla 
11510e5c9e7fSSrinivas Kandagatla 	/* Disable BCS slow insertion detection */
11520e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_bcs_enable(mbhc, plug_type, false);
11530e5c9e7fSSrinivas Kandagatla 
11540e5c9e7fSSrinivas Kandagatla 	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
11550e5c9e7fSSrinivas Kandagatla 
11560e5c9e7fSSrinivas Kandagatla 	while (!time_after(jiffies, timeout)) {
11570e5c9e7fSSrinivas Kandagatla 		if (mbhc->hs_detect_work_stop) {
11580e5c9e7fSSrinivas Kandagatla 			wcd_micbias_disable(mbhc);
11590e5c9e7fSSrinivas Kandagatla 			goto exit;
11600e5c9e7fSSrinivas Kandagatla 		}
11610e5c9e7fSSrinivas Kandagatla 
11620e5c9e7fSSrinivas Kandagatla 		msleep(180);
11630e5c9e7fSSrinivas Kandagatla 		/*
11640e5c9e7fSSrinivas Kandagatla 		 * Use ADC single mode to minimize the chance of missing out
11650e5c9e7fSSrinivas Kandagatla 		 * btn press/release for HEADSET type during correct work.
11660e5c9e7fSSrinivas Kandagatla 		 */
11670e5c9e7fSSrinivas Kandagatla 		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
11680e5c9e7fSSrinivas Kandagatla 		plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11690e5c9e7fSSrinivas Kandagatla 		is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
11700e5c9e7fSSrinivas Kandagatla 
1171b38892b5SSrinivasa Rao Mandadapu 		if (output_mv > hs_threshold && !is_spl_hs) {
11723c8a3ad4SSrinivasa Rao Mandadapu 			is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
11733c8a3ad4SSrinivasa Rao Mandadapu 			output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
11743c8a3ad4SSrinivasa Rao Mandadapu 
11753c8a3ad4SSrinivasa Rao Mandadapu 			if (is_spl_hs) {
1176b38892b5SSrinivasa Rao Mandadapu 				hs_threshold *= wcd_mbhc_get_micbias(mbhc);
1177b38892b5SSrinivasa Rao Mandadapu 				hs_threshold /= micbias_mv;
11783c8a3ad4SSrinivasa Rao Mandadapu 			}
11793c8a3ad4SSrinivasa Rao Mandadapu 		}
11803c8a3ad4SSrinivasa Rao Mandadapu 
11810e5c9e7fSSrinivas Kandagatla 		if ((output_mv <= hs_threshold) && !is_pa_on) {
11820e5c9e7fSSrinivas Kandagatla 			/* Check for cross connection*/
11830e5c9e7fSSrinivas Kandagatla 			cross_conn = wcd_check_cross_conn(mbhc);
11840e5c9e7fSSrinivas Kandagatla 			if (cross_conn > 0) { /* cross-connection */
11850e5c9e7fSSrinivas Kandagatla 				pt_gnd_mic_swap_cnt++;
11860e5c9e7fSSrinivas Kandagatla 				if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
11870e5c9e7fSSrinivas Kandagatla 					continue;
11880e5c9e7fSSrinivas Kandagatla 				else
11890e5c9e7fSSrinivas Kandagatla 					plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
11900e5c9e7fSSrinivas Kandagatla 			} else if (!cross_conn) { /* no cross connection */
11910e5c9e7fSSrinivas Kandagatla 				pt_gnd_mic_swap_cnt = 0;
11920e5c9e7fSSrinivas Kandagatla 				plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11930e5c9e7fSSrinivas Kandagatla 				continue;
119443265ceeSPierre-Louis Bossart 			} else /* Error if (cross_conn < 0) */
11950e5c9e7fSSrinivas Kandagatla 				continue;
11960e5c9e7fSSrinivas Kandagatla 
11970e5c9e7fSSrinivas Kandagatla 			if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
11980e5c9e7fSSrinivas Kandagatla 				/* US_EU gpio present, flip switch */
11990e5c9e7fSSrinivas Kandagatla 				if (mbhc->cfg->swap_gnd_mic) {
12000e5c9e7fSSrinivas Kandagatla 					if (mbhc->cfg->swap_gnd_mic(component, true))
12010e5c9e7fSSrinivas Kandagatla 						continue;
12020e5c9e7fSSrinivas Kandagatla 				}
12030e5c9e7fSSrinivas Kandagatla 			}
12040e5c9e7fSSrinivas Kandagatla 		}
12050e5c9e7fSSrinivas Kandagatla 
12063c8a3ad4SSrinivasa Rao Mandadapu 		/* cable is extension cable */
1207b38892b5SSrinivasa Rao Mandadapu 		if (output_mv > hs_threshold || mbhc->force_linein)
12080e5c9e7fSSrinivas Kandagatla 			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
12090e5c9e7fSSrinivas Kandagatla 	}
12100e5c9e7fSSrinivas Kandagatla 
12110e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_bcs_enable(mbhc, plug_type, true);
12120e5c9e7fSSrinivas Kandagatla 
12133c8a3ad4SSrinivasa Rao Mandadapu 	if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
12143c8a3ad4SSrinivasa Rao Mandadapu 		if (is_spl_hs)
12153c8a3ad4SSrinivasa Rao Mandadapu 			plug_type = MBHC_PLUG_TYPE_HEADSET;
12163c8a3ad4SSrinivasa Rao Mandadapu 		else
12170e5c9e7fSSrinivas Kandagatla 			wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
12183c8a3ad4SSrinivasa Rao Mandadapu 	}
12190e5c9e7fSSrinivas Kandagatla 
12200e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
12210e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
12220e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
12230e5c9e7fSSrinivas Kandagatla 
12240e5c9e7fSSrinivas Kandagatla 	/*
12250e5c9e7fSSrinivas Kandagatla 	 * Set DETECTION_DONE bit for HEADSET
12260e5c9e7fSSrinivas Kandagatla 	 * so that btn press/release interrupt can be generated.
12270e5c9e7fSSrinivas Kandagatla 	 * For other plug type, clear the bit.
12280e5c9e7fSSrinivas Kandagatla 	 */
12290e5c9e7fSSrinivas Kandagatla 	if (plug_type == MBHC_PLUG_TYPE_HEADSET)
12300e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
12310e5c9e7fSSrinivas Kandagatla 	else
12320e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
12330e5c9e7fSSrinivas Kandagatla 
12340e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micbias_control)
12350e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
12360e5c9e7fSSrinivas Kandagatla 
12370e5c9e7fSSrinivas Kandagatla exit:
12380e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->mbhc_micbias_control/* &&  !mbhc->micbias_enable*/)
12390e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
12400e5c9e7fSSrinivas Kandagatla 
12410e5c9e7fSSrinivas Kandagatla 	/*
12420e5c9e7fSSrinivas Kandagatla 	 * If plug type is corrected from special headset to headphone,
12430e5c9e7fSSrinivas Kandagatla 	 * clear the micbias enable flag, set micbias back to 1.8V and
12440e5c9e7fSSrinivas Kandagatla 	 * disable micbias.
12450e5c9e7fSSrinivas Kandagatla 	 */
12460e5c9e7fSSrinivas Kandagatla 	if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
12470e5c9e7fSSrinivas Kandagatla 		wcd_micbias_disable(mbhc);
12480e5c9e7fSSrinivas Kandagatla 		/*
12490e5c9e7fSSrinivas Kandagatla 		 * Enable ADC COMPLETE interrupt for HEADPHONE.
12500e5c9e7fSSrinivas Kandagatla 		 * Btn release may happen after the correct work, ADC COMPLETE
12510e5c9e7fSSrinivas Kandagatla 		 * interrupt needs to be captured to correct plug type.
12520e5c9e7fSSrinivas Kandagatla 		 */
12530e5c9e7fSSrinivas Kandagatla 		enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
12540e5c9e7fSSrinivas Kandagatla 	}
12550e5c9e7fSSrinivas Kandagatla 
12560e5c9e7fSSrinivas Kandagatla 	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
12570e5c9e7fSSrinivas Kandagatla 		mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
1258cc4d891fSSrinivas Kandagatla 
1259cc4d891fSSrinivas Kandagatla 	pm_runtime_mark_last_busy(component->dev);
1260cc4d891fSSrinivas Kandagatla 	pm_runtime_put_autosuspend(component->dev);
12610e5c9e7fSSrinivas Kandagatla }
12620e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_hs_rem_irq(int irq,void * data)12630e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
12640e5c9e7fSSrinivas Kandagatla {
12650e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = data;
12660e5c9e7fSSrinivas Kandagatla 	unsigned long timeout;
12670e5c9e7fSSrinivas Kandagatla 	int adc_threshold, output_mv, retry = 0;
12680e5c9e7fSSrinivas Kandagatla 
12690e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
12700e5c9e7fSSrinivas Kandagatla 	timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
12710e5c9e7fSSrinivas Kandagatla 	adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
12720e5c9e7fSSrinivas Kandagatla 
12730e5c9e7fSSrinivas Kandagatla 	do {
12740e5c9e7fSSrinivas Kandagatla 		retry++;
12750e5c9e7fSSrinivas Kandagatla 		/*
12760e5c9e7fSSrinivas Kandagatla 		 * read output_mv every 10ms to look for
12770e5c9e7fSSrinivas Kandagatla 		 * any change in IN2_P
12780e5c9e7fSSrinivas Kandagatla 		 */
12790e5c9e7fSSrinivas Kandagatla 		usleep_range(10000, 10100);
12800e5c9e7fSSrinivas Kandagatla 		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
12810e5c9e7fSSrinivas Kandagatla 
12820e5c9e7fSSrinivas Kandagatla 		/* Check for fake removal */
12830e5c9e7fSSrinivas Kandagatla 		if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
12840e5c9e7fSSrinivas Kandagatla 			goto exit;
12850e5c9e7fSSrinivas Kandagatla 	} while (!time_after(jiffies, timeout));
12860e5c9e7fSSrinivas Kandagatla 
12870e5c9e7fSSrinivas Kandagatla 	/*
12880e5c9e7fSSrinivas Kandagatla 	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for
12890e5c9e7fSSrinivas Kandagatla 	 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
12900e5c9e7fSSrinivas Kandagatla 	 * follows ELEC_REM one when HEADPHONE is removed.
12910e5c9e7fSSrinivas Kandagatla 	 */
12920e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
12930e5c9e7fSSrinivas Kandagatla 		mbhc->extn_cable_hph_rem = true;
12940e5c9e7fSSrinivas Kandagatla 
12950e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
12960e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
12970e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
12980e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_elec_hs_report_unplug(mbhc);
12990e5c9e7fSSrinivas Kandagatla 	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
13000e5c9e7fSSrinivas Kandagatla 
13010e5c9e7fSSrinivas Kandagatla exit:
13020e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
13030e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
13040e5c9e7fSSrinivas Kandagatla }
13050e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_adc_hs_ins_irq(int irq,void * data)13060e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
13070e5c9e7fSSrinivas Kandagatla {
13080e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc = data;
1309cac24a36SPierre-Louis Bossart 	u8 clamp_state;
13100e5c9e7fSSrinivas Kandagatla 	u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
13110e5c9e7fSSrinivas Kandagatla 
13120e5c9e7fSSrinivas Kandagatla 	/*
13130e5c9e7fSSrinivas Kandagatla 	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
13140e5c9e7fSSrinivas Kandagatla 	 * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
13150e5c9e7fSSrinivas Kandagatla 	 * when HEADPHONE is removed.
13160e5c9e7fSSrinivas Kandagatla 	 */
13170e5c9e7fSSrinivas Kandagatla 	if (mbhc->extn_cable_hph_rem == true) {
13180e5c9e7fSSrinivas Kandagatla 		mbhc->extn_cable_hph_rem = false;
13190e5c9e7fSSrinivas Kandagatla 		return IRQ_HANDLED;
13200e5c9e7fSSrinivas Kandagatla 	}
13210e5c9e7fSSrinivas Kandagatla 
13220e5c9e7fSSrinivas Kandagatla 	do {
13230e5c9e7fSSrinivas Kandagatla 		clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
13240e5c9e7fSSrinivas Kandagatla 		if (clamp_state)
13250e5c9e7fSSrinivas Kandagatla 			return IRQ_HANDLED;
13260e5c9e7fSSrinivas Kandagatla 		/*
13270e5c9e7fSSrinivas Kandagatla 		 * check clamp for 120ms but at 30ms chunks to leave
13280e5c9e7fSSrinivas Kandagatla 		 * room for other interrupts to be processed
13290e5c9e7fSSrinivas Kandagatla 		 */
13300e5c9e7fSSrinivas Kandagatla 		usleep_range(30000, 30100);
13310e5c9e7fSSrinivas Kandagatla 	} while (--clamp_retry);
13320e5c9e7fSSrinivas Kandagatla 
13330e5c9e7fSSrinivas Kandagatla 	/*
13340e5c9e7fSSrinivas Kandagatla 	 * If current plug is headphone then there is no chance to
13350e5c9e7fSSrinivas Kandagatla 	 * get ADC complete interrupt, so connected cable should be
13360e5c9e7fSSrinivas Kandagatla 	 * headset not headphone.
13370e5c9e7fSSrinivas Kandagatla 	 */
13380e5c9e7fSSrinivas Kandagatla 	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
13390e5c9e7fSSrinivas Kandagatla 		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
13400e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
13410e5c9e7fSSrinivas Kandagatla 		wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
13420e5c9e7fSSrinivas Kandagatla 		return IRQ_HANDLED;
13430e5c9e7fSSrinivas Kandagatla 	}
13440e5c9e7fSSrinivas Kandagatla 
13450e5c9e7fSSrinivas Kandagatla 	return IRQ_HANDLED;
13460e5c9e7fSSrinivas Kandagatla }
13470e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_impedance(struct wcd_mbhc * mbhc,uint32_t * zl,uint32_t * zr)13480e5c9e7fSSrinivas Kandagatla int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,	uint32_t *zr)
13490e5c9e7fSSrinivas Kandagatla {
13500e5c9e7fSSrinivas Kandagatla 	*zl = mbhc->zl;
13510e5c9e7fSSrinivas Kandagatla 	*zr = mbhc->zr;
13520e5c9e7fSSrinivas Kandagatla 
13530e5c9e7fSSrinivas Kandagatla 	if (*zl && *zr)
13540e5c9e7fSSrinivas Kandagatla 		return 0;
13550e5c9e7fSSrinivas Kandagatla 	else
13560e5c9e7fSSrinivas Kandagatla 		return -EINVAL;
13570e5c9e7fSSrinivas Kandagatla }
13580e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_get_impedance);
13590e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_set_hph_type(struct wcd_mbhc * mbhc,int hph_type)13600e5c9e7fSSrinivas Kandagatla void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
13610e5c9e7fSSrinivas Kandagatla {
13620e5c9e7fSSrinivas Kandagatla 	mbhc->hph_type = hph_type;
13630e5c9e7fSSrinivas Kandagatla }
13640e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
13650e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_get_hph_type(struct wcd_mbhc * mbhc)13660e5c9e7fSSrinivas Kandagatla int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
13670e5c9e7fSSrinivas Kandagatla {
13680e5c9e7fSSrinivas Kandagatla 	return mbhc->hph_type;
13690e5c9e7fSSrinivas Kandagatla }
13700e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
13710e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_start(struct wcd_mbhc * mbhc,struct wcd_mbhc_config * cfg,struct snd_soc_jack * jack)13720e5c9e7fSSrinivas Kandagatla int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
13730e5c9e7fSSrinivas Kandagatla 		   struct snd_soc_jack *jack)
13740e5c9e7fSSrinivas Kandagatla {
13750e5c9e7fSSrinivas Kandagatla 	if (!mbhc || !cfg || !jack)
13760e5c9e7fSSrinivas Kandagatla 		return -EINVAL;
13770e5c9e7fSSrinivas Kandagatla 
13780e5c9e7fSSrinivas Kandagatla 	mbhc->cfg = cfg;
13790e5c9e7fSSrinivas Kandagatla 	mbhc->jack = jack;
13800e5c9e7fSSrinivas Kandagatla 
13810e5c9e7fSSrinivas Kandagatla 	return wcd_mbhc_initialise(mbhc);
13820e5c9e7fSSrinivas Kandagatla }
13830e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_start);
13840e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_stop(struct wcd_mbhc * mbhc)13850e5c9e7fSSrinivas Kandagatla void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
13860e5c9e7fSSrinivas Kandagatla {
13870e5c9e7fSSrinivas Kandagatla 	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
13880e5c9e7fSSrinivas Kandagatla 	mbhc->hph_status = 0;
13890e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
13900e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
13910e5c9e7fSSrinivas Kandagatla }
13920e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_stop);
13930e5c9e7fSSrinivas Kandagatla 
wcd_dt_parse_mbhc_data(struct device * dev,struct wcd_mbhc_config * cfg)13940e5c9e7fSSrinivas Kandagatla int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
13950e5c9e7fSSrinivas Kandagatla {
13960e5c9e7fSSrinivas Kandagatla 	struct device_node *np = dev->of_node;
13970e5c9e7fSSrinivas Kandagatla 	int ret, i, microvolt;
13980e5c9e7fSSrinivas Kandagatla 
13990e5c9e7fSSrinivas Kandagatla 	if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
14000e5c9e7fSSrinivas Kandagatla 		cfg->hphl_swh = false;
14010e5c9e7fSSrinivas Kandagatla 	else
14020e5c9e7fSSrinivas Kandagatla 		cfg->hphl_swh = true;
14030e5c9e7fSSrinivas Kandagatla 
14040e5c9e7fSSrinivas Kandagatla 	if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
14050e5c9e7fSSrinivas Kandagatla 		cfg->gnd_swh = false;
14060e5c9e7fSSrinivas Kandagatla 	else
14070e5c9e7fSSrinivas Kandagatla 		cfg->gnd_swh = true;
14080e5c9e7fSSrinivas Kandagatla 
14090e5c9e7fSSrinivas Kandagatla 	ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
14100e5c9e7fSSrinivas Kandagatla 				   &microvolt);
14110e5c9e7fSSrinivas Kandagatla 	if (ret)
14120e5c9e7fSSrinivas Kandagatla 		dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
14130e5c9e7fSSrinivas Kandagatla 	else
14140e5c9e7fSSrinivas Kandagatla 		cfg->hs_thr = microvolt/1000;
14150e5c9e7fSSrinivas Kandagatla 
14160e5c9e7fSSrinivas Kandagatla 	ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
14170e5c9e7fSSrinivas Kandagatla 				   &microvolt);
14180e5c9e7fSSrinivas Kandagatla 	if (ret)
14190e5c9e7fSSrinivas Kandagatla 		dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt	entry\n");
14200e5c9e7fSSrinivas Kandagatla 	else
14210e5c9e7fSSrinivas Kandagatla 		cfg->hph_thr = microvolt/1000;
14220e5c9e7fSSrinivas Kandagatla 
14230e5c9e7fSSrinivas Kandagatla 	ret = of_property_read_u32_array(np,
14240e5c9e7fSSrinivas Kandagatla 					 "qcom,mbhc-buttons-vthreshold-microvolt",
14250e5c9e7fSSrinivas Kandagatla 					 &cfg->btn_high[0],
14260e5c9e7fSSrinivas Kandagatla 					 WCD_MBHC_DEF_BUTTONS);
14270e5c9e7fSSrinivas Kandagatla 	if (ret)
14280e5c9e7fSSrinivas Kandagatla 		dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
14290e5c9e7fSSrinivas Kandagatla 
14300e5c9e7fSSrinivas Kandagatla 	for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
14310e5c9e7fSSrinivas Kandagatla 		if (ret) /* default voltage */
14320e5c9e7fSSrinivas Kandagatla 			cfg->btn_high[i] = 500000;
14330e5c9e7fSSrinivas Kandagatla 		else
14340e5c9e7fSSrinivas Kandagatla 			/* Micro to Milli Volts */
14350e5c9e7fSSrinivas Kandagatla 			cfg->btn_high[i] = cfg->btn_high[i]/1000;
14360e5c9e7fSSrinivas Kandagatla 	}
14370e5c9e7fSSrinivas Kandagatla 
14380e5c9e7fSSrinivas Kandagatla 	return 0;
14390e5c9e7fSSrinivas Kandagatla }
14400e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
14410e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_init(struct snd_soc_component * component,const struct wcd_mbhc_cb * mbhc_cb,const struct wcd_mbhc_intr * intr_ids,struct wcd_mbhc_field * fields,bool impedance_det_en)14420e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
14430e5c9e7fSSrinivas Kandagatla 			       const struct wcd_mbhc_cb *mbhc_cb,
14440e5c9e7fSSrinivas Kandagatla 			       const struct wcd_mbhc_intr *intr_ids,
14450e5c9e7fSSrinivas Kandagatla 			       struct wcd_mbhc_field *fields,
14460e5c9e7fSSrinivas Kandagatla 			       bool impedance_det_en)
14470e5c9e7fSSrinivas Kandagatla {
14480e5c9e7fSSrinivas Kandagatla 	struct device *dev = component->dev;
14490e5c9e7fSSrinivas Kandagatla 	struct wcd_mbhc *mbhc;
14500e5c9e7fSSrinivas Kandagatla 	int ret;
14510e5c9e7fSSrinivas Kandagatla 
14520e5c9e7fSSrinivas Kandagatla 	if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
14530e5c9e7fSSrinivas Kandagatla 		dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
14540e5c9e7fSSrinivas Kandagatla 		return ERR_PTR(-EINVAL);
14550e5c9e7fSSrinivas Kandagatla 	}
14560e5c9e7fSSrinivas Kandagatla 
1457*a5475829SJohan Hovold 	mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
14580e5c9e7fSSrinivas Kandagatla 	if (!mbhc)
14590e5c9e7fSSrinivas Kandagatla 		return ERR_PTR(-ENOMEM);
14600e5c9e7fSSrinivas Kandagatla 
14610e5c9e7fSSrinivas Kandagatla 	mbhc->component = component;
14620e5c9e7fSSrinivas Kandagatla 	mbhc->dev = dev;
14630e5c9e7fSSrinivas Kandagatla 	mbhc->intr_ids = intr_ids;
14640e5c9e7fSSrinivas Kandagatla 	mbhc->mbhc_cb = mbhc_cb;
14650e5c9e7fSSrinivas Kandagatla 	mbhc->fields = fields;
14660e5c9e7fSSrinivas Kandagatla 	mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
14670e5c9e7fSSrinivas Kandagatla 
14680e5c9e7fSSrinivas Kandagatla 	if (mbhc_cb->compute_impedance)
14690e5c9e7fSSrinivas Kandagatla 		mbhc->impedance_detect = impedance_det_en;
14700e5c9e7fSSrinivas Kandagatla 
14710e5c9e7fSSrinivas Kandagatla 	INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
14720e5c9e7fSSrinivas Kandagatla 
14730e5c9e7fSSrinivas Kandagatla 	mutex_init(&mbhc->lock);
14740e5c9e7fSSrinivas Kandagatla 
14750e5c9e7fSSrinivas Kandagatla 	INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
14760e5c9e7fSSrinivas Kandagatla 
1477*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
14780e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_mech_plug_detect_irq,
14790e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14800e5c9e7fSSrinivas Kandagatla 					"mbhc sw intr", mbhc);
14810e5c9e7fSSrinivas Kandagatla 	if (ret)
1482*a5475829SJohan Hovold 		goto err_free_mbhc;
14830e5c9e7fSSrinivas Kandagatla 
1484*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
14850e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_btn_press_handler,
14860e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14870e5c9e7fSSrinivas Kandagatla 					"Button Press detect", mbhc);
14880e5c9e7fSSrinivas Kandagatla 	if (ret)
1489*a5475829SJohan Hovold 		goto err_free_sw_intr;
14900e5c9e7fSSrinivas Kandagatla 
1491*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
14920e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_btn_release_handler,
14930e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14940e5c9e7fSSrinivas Kandagatla 					"Button Release detect", mbhc);
14950e5c9e7fSSrinivas Kandagatla 	if (ret)
1496*a5475829SJohan Hovold 		goto err_free_btn_press_intr;
14970e5c9e7fSSrinivas Kandagatla 
1498*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
14990e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_adc_hs_ins_irq,
15000e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15010e5c9e7fSSrinivas Kandagatla 					"Elect Insert", mbhc);
15020e5c9e7fSSrinivas Kandagatla 	if (ret)
1503*a5475829SJohan Hovold 		goto err_free_btn_release_intr;
15040e5c9e7fSSrinivas Kandagatla 
15050e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
15060e5c9e7fSSrinivas Kandagatla 
1507*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
15080e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_adc_hs_rem_irq,
15090e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15100e5c9e7fSSrinivas Kandagatla 					"Elect Remove", mbhc);
15110e5c9e7fSSrinivas Kandagatla 	if (ret)
1512*a5475829SJohan Hovold 		goto err_free_hs_ins_intr;
15130e5c9e7fSSrinivas Kandagatla 
15140e5c9e7fSSrinivas Kandagatla 	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
15150e5c9e7fSSrinivas Kandagatla 
1516*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
15170e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_hphl_ocp_irq,
15180e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15190e5c9e7fSSrinivas Kandagatla 					"HPH_L OCP detect", mbhc);
15200e5c9e7fSSrinivas Kandagatla 	if (ret)
1521*a5475829SJohan Hovold 		goto err_free_hs_rem_intr;
15220e5c9e7fSSrinivas Kandagatla 
1523*a5475829SJohan Hovold 	ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
15240e5c9e7fSSrinivas Kandagatla 					wcd_mbhc_hphr_ocp_irq,
15250e5c9e7fSSrinivas Kandagatla 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15260e5c9e7fSSrinivas Kandagatla 					"HPH_R OCP detect", mbhc);
15270e5c9e7fSSrinivas Kandagatla 	if (ret)
1528*a5475829SJohan Hovold 		goto err_free_hph_left_ocp;
15290e5c9e7fSSrinivas Kandagatla 
15300e5c9e7fSSrinivas Kandagatla 	return mbhc;
1531*a5475829SJohan Hovold 
1532*a5475829SJohan Hovold err_free_hph_left_ocp:
1533*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1534*a5475829SJohan Hovold err_free_hs_rem_intr:
1535*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1536*a5475829SJohan Hovold err_free_hs_ins_intr:
1537*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1538*a5475829SJohan Hovold err_free_btn_release_intr:
1539*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1540*a5475829SJohan Hovold err_free_btn_press_intr:
1541*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1542*a5475829SJohan Hovold err_free_sw_intr:
1543*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1544*a5475829SJohan Hovold err_free_mbhc:
1545*a5475829SJohan Hovold 	kfree(mbhc);
1546*a5475829SJohan Hovold 
15470e5c9e7fSSrinivas Kandagatla 	dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
15480e5c9e7fSSrinivas Kandagatla 
15490e5c9e7fSSrinivas Kandagatla 	return ERR_PTR(ret);
15500e5c9e7fSSrinivas Kandagatla }
15510e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_init);
15520e5c9e7fSSrinivas Kandagatla 
wcd_mbhc_deinit(struct wcd_mbhc * mbhc)15530e5c9e7fSSrinivas Kandagatla void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
15540e5c9e7fSSrinivas Kandagatla {
1555*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
1556*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1557*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1558*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1559*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1560*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1561*a5475829SJohan Hovold 	free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1562*a5475829SJohan Hovold 
15630e5c9e7fSSrinivas Kandagatla 	mutex_lock(&mbhc->lock);
15640e5c9e7fSSrinivas Kandagatla 	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
15650e5c9e7fSSrinivas Kandagatla 	mutex_unlock(&mbhc->lock);
1566*a5475829SJohan Hovold 
1567*a5475829SJohan Hovold 	kfree(mbhc);
15680e5c9e7fSSrinivas Kandagatla }
15690e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_deinit);
15700e5c9e7fSSrinivas Kandagatla 
mbhc_init(void)15710e5c9e7fSSrinivas Kandagatla static int __init mbhc_init(void)
15720e5c9e7fSSrinivas Kandagatla {
15730e5c9e7fSSrinivas Kandagatla 	return 0;
15740e5c9e7fSSrinivas Kandagatla }
15750e5c9e7fSSrinivas Kandagatla 
mbhc_exit(void)15760e5c9e7fSSrinivas Kandagatla static void __exit mbhc_exit(void)
15770e5c9e7fSSrinivas Kandagatla {
15780e5c9e7fSSrinivas Kandagatla }
15790e5c9e7fSSrinivas Kandagatla 
15800e5c9e7fSSrinivas Kandagatla module_init(mbhc_init);
15810e5c9e7fSSrinivas Kandagatla module_exit(mbhc_exit);
15820e5c9e7fSSrinivas Kandagatla 
15830e5c9e7fSSrinivas Kandagatla MODULE_DESCRIPTION("wcd MBHC v2 module");
15840e5c9e7fSSrinivas Kandagatla MODULE_LICENSE("GPL");
1585