162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci// Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/device.h>
862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
962306a36Sopenharmony_ci#include <linux/printk.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <sound/soc.h>
1362306a36Sopenharmony_ci#include <sound/jack.h>
1462306a36Sopenharmony_ci#include "wcd-mbhc-v2.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define HS_DETECT_PLUG_TIME_MS		(3 * 1000)
1762306a36Sopenharmony_ci#define MBHC_BUTTON_PRESS_THRESHOLD_MIN	250
1862306a36Sopenharmony_ci#define GND_MIC_SWAP_THRESHOLD		4
1962306a36Sopenharmony_ci#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS	100
2062306a36Sopenharmony_ci#define HPHL_CROSS_CONN_THRESHOLD	100
2162306a36Sopenharmony_ci#define HS_VREF_MIN_VAL			1400
2262306a36Sopenharmony_ci#define FAKE_REM_RETRY_ATTEMPTS		3
2362306a36Sopenharmony_ci#define WCD_MBHC_ADC_HS_THRESHOLD_MV	1700
2462306a36Sopenharmony_ci#define WCD_MBHC_ADC_HPH_THRESHOLD_MV	75
2562306a36Sopenharmony_ci#define WCD_MBHC_ADC_MICBIAS_MV		1800
2662306a36Sopenharmony_ci#define WCD_MBHC_FAKE_INS_RETRY		4
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
2962306a36Sopenharmony_ci			   SND_JACK_MECHANICAL)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
3262306a36Sopenharmony_ci				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
3362306a36Sopenharmony_ci				  SND_JACK_BTN_4 | SND_JACK_BTN_5)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cienum wcd_mbhc_adc_mux_ctl {
3662306a36Sopenharmony_ci	MUX_CTL_AUTO = 0,
3762306a36Sopenharmony_ci	MUX_CTL_IN2P,
3862306a36Sopenharmony_ci	MUX_CTL_IN3P,
3962306a36Sopenharmony_ci	MUX_CTL_IN4P,
4062306a36Sopenharmony_ci	MUX_CTL_HPH_L,
4162306a36Sopenharmony_ci	MUX_CTL_HPH_R,
4262306a36Sopenharmony_ci	MUX_CTL_NONE,
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct wcd_mbhc {
4662306a36Sopenharmony_ci	struct device *dev;
4762306a36Sopenharmony_ci	struct snd_soc_component *component;
4862306a36Sopenharmony_ci	struct snd_soc_jack *jack;
4962306a36Sopenharmony_ci	struct wcd_mbhc_config *cfg;
5062306a36Sopenharmony_ci	const struct wcd_mbhc_cb *mbhc_cb;
5162306a36Sopenharmony_ci	const struct wcd_mbhc_intr *intr_ids;
5262306a36Sopenharmony_ci	struct wcd_mbhc_field *fields;
5362306a36Sopenharmony_ci	/* Delayed work to report long button press */
5462306a36Sopenharmony_ci	struct delayed_work mbhc_btn_dwork;
5562306a36Sopenharmony_ci	/* Work to correct accessory type */
5662306a36Sopenharmony_ci	struct work_struct correct_plug_swch;
5762306a36Sopenharmony_ci	struct mutex lock;
5862306a36Sopenharmony_ci	int buttons_pressed;
5962306a36Sopenharmony_ci	u32 hph_status; /* track headhpone status */
6062306a36Sopenharmony_ci	u8 current_plug;
6162306a36Sopenharmony_ci	bool is_btn_press;
6262306a36Sopenharmony_ci	bool in_swch_irq_handler;
6362306a36Sopenharmony_ci	bool hs_detect_work_stop;
6462306a36Sopenharmony_ci	bool is_hs_recording;
6562306a36Sopenharmony_ci	bool extn_cable_hph_rem;
6662306a36Sopenharmony_ci	bool force_linein;
6762306a36Sopenharmony_ci	bool impedance_detect;
6862306a36Sopenharmony_ci	unsigned long event_state;
6962306a36Sopenharmony_ci	unsigned long jiffies_atreport;
7062306a36Sopenharmony_ci	/* impedance of hphl and hphr */
7162306a36Sopenharmony_ci	uint32_t zl, zr;
7262306a36Sopenharmony_ci	/* Holds type of Headset - Mono/Stereo */
7362306a36Sopenharmony_ci	enum wcd_mbhc_hph_type hph_type;
7462306a36Sopenharmony_ci	/* Holds mbhc detection method - ADC/Legacy */
7562306a36Sopenharmony_ci	int mbhc_detection_logic;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
7962306a36Sopenharmony_ci				       int field, int val)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (!mbhc->fields[field].reg)
8262306a36Sopenharmony_ci		return 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return snd_soc_component_write_field(mbhc->component,
8562306a36Sopenharmony_ci					     mbhc->fields[field].reg,
8662306a36Sopenharmony_ci					     mbhc->fields[field].mask, val);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (!mbhc->fields[field].reg)
9262306a36Sopenharmony_ci		return 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return snd_soc_component_read_field(mbhc->component,
9562306a36Sopenharmony_ci					    mbhc->fields[field].reg,
9662306a36Sopenharmony_ci					    mbhc->fields[field].mask);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct snd_soc_component *component = mbhc->component;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
11162306a36Sopenharmony_ci				   mbhc->cfg->btn_high,
11262306a36Sopenharmony_ci				   mbhc->cfg->num_btn, micbias);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
11662306a36Sopenharmony_ci					  const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/*
12062306a36Sopenharmony_ci	 * Some codecs handle micbias/pullup enablement in codec
12162306a36Sopenharmony_ci	 * drivers itself and micbias is not needed for regular
12262306a36Sopenharmony_ci	 * plug type detection. So if micbias_control callback function
12362306a36Sopenharmony_ci	 * is defined, just return.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micbias_control)
12662306a36Sopenharmony_ci		return;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	switch (cs_mb_en) {
12962306a36Sopenharmony_ci	case WCD_MBHC_EN_CS:
13062306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
13162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
13262306a36Sopenharmony_ci		/* Program Button threshold registers as per CS */
13362306a36Sopenharmony_ci		wcd_program_btn_threshold(mbhc, false);
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	case WCD_MBHC_EN_MB:
13662306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
13762306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
13862306a36Sopenharmony_ci		/* Disable PULL_UP_EN & enable MICBIAS */
13962306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
14062306a36Sopenharmony_ci		/* Program Button threshold registers as per MICBIAS */
14162306a36Sopenharmony_ci		wcd_program_btn_threshold(mbhc, true);
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case WCD_MBHC_EN_PULLUP:
14462306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
14562306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
14662306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
14762306a36Sopenharmony_ci		/* Program Button threshold registers as per MICBIAS */
14862306a36Sopenharmony_ci		wcd_program_btn_threshold(mbhc, true);
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case WCD_MBHC_EN_NONE:
15162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
15262306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
15362306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	default:
15662306a36Sopenharmony_ci		dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciint wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	struct snd_soc_component *component;
16562306a36Sopenharmony_ci	bool micbias2 = false;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!mbhc)
16862306a36Sopenharmony_ci		return 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	component = mbhc->component;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (mbhc->mbhc_cb->micbias_enable_status)
17362306a36Sopenharmony_ci		micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (event) {
17662306a36Sopenharmony_ci	/* MICBIAS usage change */
17762306a36Sopenharmony_ci	case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
17862306a36Sopenharmony_ci		mbhc->is_hs_recording = true;
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	case WCD_EVENT_POST_MICBIAS_2_ON:
18162306a36Sopenharmony_ci		/* Disable current source if micbias2 enabled */
18262306a36Sopenharmony_ci		if (mbhc->mbhc_cb->mbhc_micbias_control) {
18362306a36Sopenharmony_ci			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
18462306a36Sopenharmony_ci				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
18562306a36Sopenharmony_ci		} else {
18662306a36Sopenharmony_ci			mbhc->is_hs_recording = true;
18762306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case WCD_EVENT_PRE_MICBIAS_2_OFF:
19162306a36Sopenharmony_ci		/*
19262306a36Sopenharmony_ci		 * Before MICBIAS_2 is turned off, if FSM is enabled,
19362306a36Sopenharmony_ci		 * make sure current source is enabled so as to detect
19462306a36Sopenharmony_ci		 * button press/release events
19562306a36Sopenharmony_ci		 */
19662306a36Sopenharmony_ci		if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
19762306a36Sopenharmony_ci			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
19862306a36Sopenharmony_ci				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	/* MICBIAS usage change */
20262306a36Sopenharmony_ci	case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
20362306a36Sopenharmony_ci		mbhc->is_hs_recording = false;
20462306a36Sopenharmony_ci		break;
20562306a36Sopenharmony_ci	case WCD_EVENT_POST_MICBIAS_2_OFF:
20662306a36Sopenharmony_ci		if (!mbhc->mbhc_cb->mbhc_micbias_control)
20762306a36Sopenharmony_ci			mbhc->is_hs_recording = false;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		/* Enable PULL UP if PA's are enabled */
21062306a36Sopenharmony_ci		if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
21162306a36Sopenharmony_ci		    (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
21262306a36Sopenharmony_ci			/* enable pullup and cs, disable mb */
21362306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
21462306a36Sopenharmony_ci		else
21562306a36Sopenharmony_ci			/* enable current source and disable mb, pullup*/
21662306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci	case WCD_EVENT_POST_HPHL_PA_OFF:
22062306a36Sopenharmony_ci		clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		/* check if micbias is enabled */
22362306a36Sopenharmony_ci		if (micbias2)
22462306a36Sopenharmony_ci			/* Disable cs, pullup & enable micbias */
22562306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
22662306a36Sopenharmony_ci		else
22762306a36Sopenharmony_ci			/* Disable micbias, pullup & enable cs */
22862306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	case WCD_EVENT_POST_HPHR_PA_OFF:
23162306a36Sopenharmony_ci		clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
23262306a36Sopenharmony_ci		/* check if micbias is enabled */
23362306a36Sopenharmony_ci		if (micbias2)
23462306a36Sopenharmony_ci			/* Disable cs, pullup & enable micbias */
23562306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
23662306a36Sopenharmony_ci		else
23762306a36Sopenharmony_ci			/* Disable micbias, pullup & enable cs */
23862306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case WCD_EVENT_PRE_HPHL_PA_ON:
24162306a36Sopenharmony_ci		set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
24262306a36Sopenharmony_ci		/* check if micbias is enabled */
24362306a36Sopenharmony_ci		if (micbias2)
24462306a36Sopenharmony_ci			/* Disable cs, pullup & enable micbias */
24562306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
24662306a36Sopenharmony_ci		else
24762306a36Sopenharmony_ci			/* Disable micbias, enable pullup & cs */
24862306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	case WCD_EVENT_PRE_HPHR_PA_ON:
25162306a36Sopenharmony_ci		set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
25262306a36Sopenharmony_ci		/* check if micbias is enabled */
25362306a36Sopenharmony_ci		if (micbias2)
25462306a36Sopenharmony_ci			/* Disable cs, pullup & enable micbias */
25562306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
25662306a36Sopenharmony_ci		else
25762306a36Sopenharmony_ci			/* Disable micbias, enable pullup & cs */
25862306a36Sopenharmony_ci			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	default:
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void wcd_micbias_disable(struct wcd_mbhc *mbhc)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct snd_soc_component *component = mbhc->component;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micbias_control)
27762306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
28062306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (mbhc->mbhc_cb->set_micbias_value) {
28362306a36Sopenharmony_ci		mbhc->mbhc_cb->set_micbias_value(component);
28462306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
28962306a36Sopenharmony_ci					 enum snd_jack_types jack_type)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	mbhc->hph_status &= ~jack_type;
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * cancel possibly scheduled btn work and
29462306a36Sopenharmony_ci	 * report release if we reported button press
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
29762306a36Sopenharmony_ci		snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
29862306a36Sopenharmony_ci		mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	wcd_micbias_disable(mbhc);
30262306a36Sopenharmony_ci	mbhc->hph_type = WCD_MBHC_HPH_NONE;
30362306a36Sopenharmony_ci	mbhc->zl = mbhc->zr = 0;
30462306a36Sopenharmony_ci	snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
30562306a36Sopenharmony_ci	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
30662306a36Sopenharmony_ci	mbhc->force_linein = false;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (!mbhc->impedance_detect)
31362306a36Sopenharmony_ci		return;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (mbhc->cfg->linein_th != 0) {
31662306a36Sopenharmony_ci		u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
31762306a36Sopenharmony_ci		/* Set MUX_CTL to AUTO for Z-det */
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
32062306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
32162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
32262306a36Sopenharmony_ci		mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
32362306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
32862306a36Sopenharmony_ci					   enum snd_jack_types jack_type)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	bool is_pa_on;
33162306a36Sopenharmony_ci	/*
33262306a36Sopenharmony_ci	 * Report removal of current jack type.
33362306a36Sopenharmony_ci	 * Headphone to headset shouldn't report headphone
33462306a36Sopenharmony_ci	 * removal.
33562306a36Sopenharmony_ci	 */
33662306a36Sopenharmony_ci	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
33762306a36Sopenharmony_ci	    jack_type == SND_JACK_HEADPHONE)
33862306a36Sopenharmony_ci		mbhc->hph_status &= ~SND_JACK_HEADSET;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Report insertion */
34162306a36Sopenharmony_ci	switch (jack_type) {
34262306a36Sopenharmony_ci	case SND_JACK_HEADPHONE:
34362306a36Sopenharmony_ci		mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	case SND_JACK_HEADSET:
34662306a36Sopenharmony_ci		mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
34762306a36Sopenharmony_ci		mbhc->jiffies_atreport = jiffies;
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	case SND_JACK_LINEOUT:
35062306a36Sopenharmony_ci		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci	default:
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!is_pa_on) {
36062306a36Sopenharmony_ci		wcd_mbhc_compute_impedance(mbhc);
36162306a36Sopenharmony_ci		if ((mbhc->zl > mbhc->cfg->linein_th) &&
36262306a36Sopenharmony_ci		    (mbhc->zr > mbhc->cfg->linein_th) &&
36362306a36Sopenharmony_ci		    (jack_type == SND_JACK_HEADPHONE)) {
36462306a36Sopenharmony_ci			jack_type = SND_JACK_LINEOUT;
36562306a36Sopenharmony_ci			mbhc->force_linein = true;
36662306a36Sopenharmony_ci			mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
36762306a36Sopenharmony_ci			if (mbhc->hph_status) {
36862306a36Sopenharmony_ci				mbhc->hph_status &= ~(SND_JACK_HEADSET |
36962306a36Sopenharmony_ci						      SND_JACK_LINEOUT);
37062306a36Sopenharmony_ci				snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
37162306a36Sopenharmony_ci						    WCD_MBHC_JACK_MASK);
37262306a36Sopenharmony_ci			}
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Do not calculate impedance again for lineout
37762306a36Sopenharmony_ci	 * as during playback pa is on and impedance values
37862306a36Sopenharmony_ci	 * will not be correct resulting in lineout detected
37962306a36Sopenharmony_ci	 * as headphone.
38062306a36Sopenharmony_ci	 */
38162306a36Sopenharmony_ci	if (is_pa_on && mbhc->force_linein) {
38262306a36Sopenharmony_ci		jack_type = SND_JACK_LINEOUT;
38362306a36Sopenharmony_ci		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
38462306a36Sopenharmony_ci		if (mbhc->hph_status) {
38562306a36Sopenharmony_ci			mbhc->hph_status &= ~(SND_JACK_HEADSET |
38662306a36Sopenharmony_ci					      SND_JACK_LINEOUT);
38762306a36Sopenharmony_ci			snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
38862306a36Sopenharmony_ci					    WCD_MBHC_JACK_MASK);
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	mbhc->hph_status |= jack_type;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
39562306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
39862306a36Sopenharmony_ci			    WCD_MBHC_JACK_MASK);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
40262306a36Sopenharmony_ci				 enum snd_jack_types jack_type)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mbhc->lock));
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (!insertion) /* Report removal */
40862306a36Sopenharmony_ci		wcd_mbhc_report_plug_removal(mbhc, jack_type);
40962306a36Sopenharmony_ci	else
41062306a36Sopenharmony_ci		wcd_mbhc_report_plug_insertion(mbhc, jack_type);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
41562306a36Sopenharmony_ci				      struct work_struct *work)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	mbhc->hs_detect_work_stop = true;
41862306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
41962306a36Sopenharmony_ci	cancel_work_sync(work);
42062306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	/* cancel pending button press */
42662306a36Sopenharmony_ci	wcd_cancel_btn_work(mbhc);
42762306a36Sopenharmony_ci	/* cancel correct work function */
42862306a36Sopenharmony_ci	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	wcd_mbhc_cancel_pending_work(mbhc);
43462306a36Sopenharmony_ci	/* Report extension cable */
43562306a36Sopenharmony_ci	wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
43662306a36Sopenharmony_ci	/*
43762306a36Sopenharmony_ci	 * Disable HPHL trigger and MIC Schmitt triggers.
43862306a36Sopenharmony_ci	 * Setup for insertion detection.
43962306a36Sopenharmony_ci	 */
44062306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
44162306a36Sopenharmony_ci	wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
44262306a36Sopenharmony_ci	/* Disable HW FSM */
44362306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
44462306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Set the detection type appropriately */
44762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
44862306a36Sopenharmony_ci	enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
45262306a36Sopenharmony_ci				   enum wcd_mbhc_plug_type plug_type)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	if (mbhc->current_plug == plug_type)
45562306a36Sopenharmony_ci		return;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	switch (plug_type) {
46062306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADPHONE:
46162306a36Sopenharmony_ci		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADSET:
46462306a36Sopenharmony_ci		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HIGH_HPH:
46762306a36Sopenharmony_ci		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_GND_MIC_SWAP:
47062306a36Sopenharmony_ci		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
47162306a36Sopenharmony_ci			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
47262306a36Sopenharmony_ci		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
47362306a36Sopenharmony_ci			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	default:
47662306a36Sopenharmony_ci		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
47762306a36Sopenharmony_ci		     mbhc->current_plug, plug_type);
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
48462306a36Sopenharmony_ci					    struct work_struct *work)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mbhc->lock));
48762306a36Sopenharmony_ci	mbhc->hs_detect_work_stop = false;
48862306a36Sopenharmony_ci	schedule_work(work);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct snd_soc_component *component = mbhc->component;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mbhc->lock));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
49862306a36Sopenharmony_ci		mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micbias_control) {
50362306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
50462306a36Sopenharmony_ci						    MICB_ENABLE);
50562306a36Sopenharmony_ci		wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct snd_soc_component *component;
51262306a36Sopenharmony_ci	enum snd_jack_types jack_type;
51362306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = data;
51462306a36Sopenharmony_ci	bool detection_type;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	component = mbhc->component;
51762306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	mbhc->in_swch_irq_handler = true;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	wcd_mbhc_cancel_pending_work(mbhc);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Set the detection type appropriately */
52662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* Enable micbias ramp */
52962306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
53062306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (detection_type) {
53362306a36Sopenharmony_ci		if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
53462306a36Sopenharmony_ci			goto exit;
53562306a36Sopenharmony_ci		/* Make sure MASTER_BIAS_CTL is enabled */
53662306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_bias(component, true);
53762306a36Sopenharmony_ci		mbhc->is_btn_press = false;
53862306a36Sopenharmony_ci		wcd_mbhc_adc_detect_plug_type(mbhc);
53962306a36Sopenharmony_ci	} else {
54062306a36Sopenharmony_ci		/* Disable HW FSM */
54162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
54262306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
54362306a36Sopenharmony_ci		mbhc->extn_cable_hph_rem = false;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
54662306a36Sopenharmony_ci			goto exit;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		mbhc->is_btn_press = false;
54962306a36Sopenharmony_ci		switch (mbhc->current_plug) {
55062306a36Sopenharmony_ci		case MBHC_PLUG_TYPE_HEADPHONE:
55162306a36Sopenharmony_ci			jack_type = SND_JACK_HEADPHONE;
55262306a36Sopenharmony_ci			break;
55362306a36Sopenharmony_ci		case MBHC_PLUG_TYPE_HEADSET:
55462306a36Sopenharmony_ci			jack_type = SND_JACK_HEADSET;
55562306a36Sopenharmony_ci			break;
55662306a36Sopenharmony_ci		case MBHC_PLUG_TYPE_HIGH_HPH:
55762306a36Sopenharmony_ci			if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
55862306a36Sopenharmony_ci				wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
55962306a36Sopenharmony_ci			jack_type = SND_JACK_LINEOUT;
56062306a36Sopenharmony_ci			break;
56162306a36Sopenharmony_ci		case MBHC_PLUG_TYPE_GND_MIC_SWAP:
56262306a36Sopenharmony_ci			dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
56362306a36Sopenharmony_ci			goto exit;
56462306a36Sopenharmony_ci		default:
56562306a36Sopenharmony_ci			dev_err(mbhc->dev, "Invalid current plug: %d\n",
56662306a36Sopenharmony_ci				mbhc->current_plug);
56762306a36Sopenharmony_ci			goto exit;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
57062306a36Sopenharmony_ci		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
57162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
57262306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
57362306a36Sopenharmony_ci		wcd_mbhc_report_plug(mbhc, 0, jack_type);
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciexit:
57762306a36Sopenharmony_ci	mbhc->in_swch_irq_handler = false;
57862306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
57962306a36Sopenharmony_ci	return IRQ_HANDLED;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	int mask = 0;
58562306a36Sopenharmony_ci	int btn;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	switch (btn) {
59062306a36Sopenharmony_ci	case 0:
59162306a36Sopenharmony_ci		mask = SND_JACK_BTN_0;
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	case 1:
59462306a36Sopenharmony_ci		mask = SND_JACK_BTN_1;
59562306a36Sopenharmony_ci		break;
59662306a36Sopenharmony_ci	case 2:
59762306a36Sopenharmony_ci		mask = SND_JACK_BTN_2;
59862306a36Sopenharmony_ci		break;
59962306a36Sopenharmony_ci	case 3:
60062306a36Sopenharmony_ci		mask = SND_JACK_BTN_3;
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	case 4:
60362306a36Sopenharmony_ci		mask = SND_JACK_BTN_4;
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci	case 5:
60662306a36Sopenharmony_ci		mask = SND_JACK_BTN_5;
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	default:
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	return mask;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic void wcd_btn_long_press_fn(struct work_struct *work)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct delayed_work *dwork = to_delayed_work(work);
61862306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
62162306a36Sopenharmony_ci		snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
62262306a36Sopenharmony_ci				    mbhc->buttons_pressed);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = data;
62862306a36Sopenharmony_ci	int mask;
62962306a36Sopenharmony_ci	unsigned long msec_val;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
63262306a36Sopenharmony_ci	wcd_cancel_btn_work(mbhc);
63362306a36Sopenharmony_ci	mbhc->is_btn_press = true;
63462306a36Sopenharmony_ci	msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Too short, ignore button press */
63762306a36Sopenharmony_ci	if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
63862306a36Sopenharmony_ci		goto done;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* If switch interrupt already kicked in, ignore button press */
64162306a36Sopenharmony_ci	if (mbhc->in_swch_irq_handler)
64262306a36Sopenharmony_ci		goto done;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/* Plug isn't headset, ignore button press */
64562306a36Sopenharmony_ci	if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
64662306a36Sopenharmony_ci		goto done;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	mask = wcd_mbhc_get_button_mask(mbhc);
64962306a36Sopenharmony_ci	mbhc->buttons_pressed |= mask;
65062306a36Sopenharmony_ci	if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
65162306a36Sopenharmony_ci		WARN(1, "Button pressed twice without release event\n");
65262306a36Sopenharmony_cidone:
65362306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
65462306a36Sopenharmony_ci	return IRQ_HANDLED;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = data;
66062306a36Sopenharmony_ci	int ret;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
66362306a36Sopenharmony_ci	if (mbhc->is_btn_press)
66462306a36Sopenharmony_ci		mbhc->is_btn_press = false;
66562306a36Sopenharmony_ci	else /* fake btn press */
66662306a36Sopenharmony_ci		goto exit;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
66962306a36Sopenharmony_ci		goto exit;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	ret = wcd_cancel_btn_work(mbhc);
67262306a36Sopenharmony_ci	if (ret == 0) { /* Reporting long button release event */
67362306a36Sopenharmony_ci		snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
67462306a36Sopenharmony_ci	} else {
67562306a36Sopenharmony_ci		if (!mbhc->in_swch_irq_handler) {
67662306a36Sopenharmony_ci			/* Reporting btn press n Release */
67762306a36Sopenharmony_ci			snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
67862306a36Sopenharmony_ci					    mbhc->buttons_pressed);
67962306a36Sopenharmony_ci			snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
68062306a36Sopenharmony_ci		}
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci	mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
68362306a36Sopenharmony_ciexit:
68462306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return IRQ_HANDLED;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* TODO Find a better way to report this to Userspace */
69362306a36Sopenharmony_ci	dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
69462306a36Sopenharmony_ci		hphr ? "HPHR" : "HPHL");
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
69762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return IRQ_HANDLED;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	return wcd_mbhc_hph_ocp_irq(data, false);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	return wcd_mbhc_hph_ocp_irq(data, true);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct snd_soc_component *component = mbhc->component;
71562306a36Sopenharmony_ci	int ret;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	ret = pm_runtime_get_sync(component->dev);
71862306a36Sopenharmony_ci	if (ret < 0 && ret != -EACCES) {
71962306a36Sopenharmony_ci		dev_err_ratelimited(component->dev,
72062306a36Sopenharmony_ci				    "pm_runtime_get_sync failed in %s, ret %d\n",
72162306a36Sopenharmony_ci				    __func__, ret);
72262306a36Sopenharmony_ci		pm_runtime_put_noidle(component->dev);
72362306a36Sopenharmony_ci		return ret;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/* enable HS detection */
72962306a36Sopenharmony_ci	if (mbhc->mbhc_cb->hph_pull_up_control_v2)
73062306a36Sopenharmony_ci		mbhc->mbhc_cb->hph_pull_up_control_v2(component,
73162306a36Sopenharmony_ci						      HS_PULLUP_I_DEFAULT);
73262306a36Sopenharmony_ci	else if (mbhc->mbhc_cb->hph_pull_up_control)
73362306a36Sopenharmony_ci		mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
73462306a36Sopenharmony_ci	else
73562306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
73862306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
73962306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
74062306a36Sopenharmony_ci	if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
74162306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
74262306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* Insertion debounce set to 96ms */
74762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/* Button Debounce set to 16ms */
75062306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/* enable bias */
75362306a36Sopenharmony_ci	mbhc->mbhc_cb->mbhc_bias(component, true);
75462306a36Sopenharmony_ci	/* enable MBHC clock */
75562306a36Sopenharmony_ci	if (mbhc->mbhc_cb->clk_setup)
75662306a36Sopenharmony_ci		mbhc->mbhc_cb->clk_setup(component, true);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* program HS_VREF value */
75962306a36Sopenharmony_ci	wcd_program_hs_vref(mbhc);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	wcd_program_btn_threshold(mbhc, false);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	pm_runtime_mark_last_busy(component->dev);
76662306a36Sopenharmony_ci	pm_runtime_put_autosuspend(component->dev);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return 0;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	int micbias = 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	if (mbhc->mbhc_cb->get_micbias_val) {
77662306a36Sopenharmony_ci		mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
77762306a36Sopenharmony_ci	} else {
77862306a36Sopenharmony_ci		u8 vout_ctl = 0;
77962306a36Sopenharmony_ci		/* Read MBHC Micbias (Mic Bias2) voltage */
78062306a36Sopenharmony_ci		vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
78162306a36Sopenharmony_ci		/* Formula for getting micbias from vout
78262306a36Sopenharmony_ci		 * micbias = 1.0V + VOUT_CTL * 50mV
78362306a36Sopenharmony_ci		 */
78462306a36Sopenharmony_ci		micbias = 1000 + (vout_ctl * 50);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci	return micbias;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int wcd_get_voltage_from_adc(u8 val, int micbias)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	/* Formula for calculating voltage from ADC
79262306a36Sopenharmony_ci	 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
79362306a36Sopenharmony_ci	 */
79462306a36Sopenharmony_ci	return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	u8 adc_result;
80062306a36Sopenharmony_ci	int output_mv;
80162306a36Sopenharmony_ci	int retry = 3;
80262306a36Sopenharmony_ci	u8 adc_en;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* Pre-requisites for ADC continuous measurement */
80562306a36Sopenharmony_ci	/* Read legacy electircal detection and disable */
80662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
80762306a36Sopenharmony_ci	/* Set ADC to continuous measurement */
80862306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
80962306a36Sopenharmony_ci	/* Read ADC Enable bit to restore after adc measurement */
81062306a36Sopenharmony_ci	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
81162306a36Sopenharmony_ci	/* Disable ADC_ENABLE bit */
81262306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
81362306a36Sopenharmony_ci	/* Disable MBHC FSM */
81462306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
81562306a36Sopenharmony_ci	/* Set the MUX selection to IN2P */
81662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
81762306a36Sopenharmony_ci	/* Enable MBHC FSM */
81862306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
81962306a36Sopenharmony_ci	/* Enable ADC_ENABLE bit */
82062306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	while (retry--) {
82362306a36Sopenharmony_ci		/* wait for 3 msec before reading ADC result */
82462306a36Sopenharmony_ci		usleep_range(3000, 3100);
82562306a36Sopenharmony_ci		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* Restore ADC Enable */
82962306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
83062306a36Sopenharmony_ci	/* Get voltage from ADC result */
83162306a36Sopenharmony_ci	output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return output_mv;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct device *dev = mbhc->dev;
83962306a36Sopenharmony_ci	u8 adc_timeout = 0;
84062306a36Sopenharmony_ci	u8 adc_complete = 0;
84162306a36Sopenharmony_ci	u8 adc_result;
84262306a36Sopenharmony_ci	int retry = 6;
84362306a36Sopenharmony_ci	int ret;
84462306a36Sopenharmony_ci	int output_mv = 0;
84562306a36Sopenharmony_ci	u8 adc_en;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
84862306a36Sopenharmony_ci	/* Read ADC Enable bit to restore after adc measurement */
84962306a36Sopenharmony_ci	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
85062306a36Sopenharmony_ci	/* Trigger ADC one time measurement */
85162306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
85262306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
85362306a36Sopenharmony_ci	/* Set the appropriate MUX selection */
85462306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
85562306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
85662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	while (retry--) {
85962306a36Sopenharmony_ci		/* wait for 600usec to get adc results */
86062306a36Sopenharmony_ci		usleep_range(600, 610);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		/* check for ADC Timeout */
86362306a36Sopenharmony_ci		adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
86462306a36Sopenharmony_ci		if (adc_timeout)
86562306a36Sopenharmony_ci			continue;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		/* Read ADC complete bit */
86862306a36Sopenharmony_ci		adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
86962306a36Sopenharmony_ci		if (!adc_complete)
87062306a36Sopenharmony_ci			continue;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		/* Read ADC result */
87362306a36Sopenharmony_ci		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		/* Get voltage from ADC result */
87662306a36Sopenharmony_ci		output_mv = wcd_get_voltage_from_adc(adc_result,
87762306a36Sopenharmony_ci						wcd_mbhc_get_micbias(mbhc));
87862306a36Sopenharmony_ci		break;
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Restore ADC Enable */
88262306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (retry <= 0) {
88562306a36Sopenharmony_ci		dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
88662306a36Sopenharmony_ci			__func__, adc_complete, adc_timeout);
88762306a36Sopenharmony_ci		ret = -EINVAL;
88862306a36Sopenharmony_ci	} else {
88962306a36Sopenharmony_ci		ret = output_mv;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	return ret;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/* To determine if cross connection occurred */
89662306a36Sopenharmony_cistatic int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	u8 adc_mode, elect_ctl, adc_en, fsm_en;
89962306a36Sopenharmony_ci	int hphl_adc_res, hphr_adc_res;
90062306a36Sopenharmony_ci	bool is_cross_conn = false;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* If PA is enabled, dont check for cross-connection */
90362306a36Sopenharmony_ci	if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
90462306a36Sopenharmony_ci		return -EINVAL;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* Read legacy electircal detection and disable */
90762306a36Sopenharmony_ci	elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
90862306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* Read and set ADC to single measurement */
91162306a36Sopenharmony_ci	adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
91262306a36Sopenharmony_ci	/* Read ADC Enable bit to restore after adc measurement */
91362306a36Sopenharmony_ci	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
91462306a36Sopenharmony_ci	/* Read FSM status */
91562306a36Sopenharmony_ci	fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/* Get adc result for HPH L */
91862306a36Sopenharmony_ci	hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
91962306a36Sopenharmony_ci	if (hphl_adc_res < 0)
92062306a36Sopenharmony_ci		return hphl_adc_res;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* Get adc result for HPH R in mV */
92362306a36Sopenharmony_ci	hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
92462306a36Sopenharmony_ci	if (hphr_adc_res < 0)
92562306a36Sopenharmony_ci		return hphr_adc_res;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
92862306a36Sopenharmony_ci	    hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
92962306a36Sopenharmony_ci		is_cross_conn = true;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
93262306a36Sopenharmony_ci	/* Set the MUX selection to Auto */
93362306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
93462306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
93562306a36Sopenharmony_ci	/* Restore ADC Enable */
93662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
93762306a36Sopenharmony_ci	/* Restore ADC mode */
93862306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
93962306a36Sopenharmony_ci	/* Restore FSM state */
94062306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
94162306a36Sopenharmony_ci	/* Restore electrical detection */
94262306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	return is_cross_conn;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	int hs_threshold, micbias_mv;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	micbias_mv = wcd_mbhc_get_micbias(mbhc);
95262306a36Sopenharmony_ci	if (mbhc->cfg->hs_thr) {
95362306a36Sopenharmony_ci		if (mbhc->cfg->micb_mv == micbias_mv)
95462306a36Sopenharmony_ci			hs_threshold = mbhc->cfg->hs_thr;
95562306a36Sopenharmony_ci		else
95662306a36Sopenharmony_ci			hs_threshold = (mbhc->cfg->hs_thr *
95762306a36Sopenharmony_ci				micbias_mv) / mbhc->cfg->micb_mv;
95862306a36Sopenharmony_ci	} else {
95962306a36Sopenharmony_ci		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
96062306a36Sopenharmony_ci			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci	return hs_threshold;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	int hph_threshold, micbias_mv;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	micbias_mv = wcd_mbhc_get_micbias(mbhc);
97062306a36Sopenharmony_ci	if (mbhc->cfg->hph_thr) {
97162306a36Sopenharmony_ci		if (mbhc->cfg->micb_mv == micbias_mv)
97262306a36Sopenharmony_ci			hph_threshold = mbhc->cfg->hph_thr;
97362306a36Sopenharmony_ci		else
97462306a36Sopenharmony_ci			hph_threshold = (mbhc->cfg->hph_thr *
97562306a36Sopenharmony_ci				micbias_mv) / mbhc->cfg->micb_mv;
97662306a36Sopenharmony_ci	} else {
97762306a36Sopenharmony_ci		hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
97862306a36Sopenharmony_ci			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci	return hph_threshold;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
98462306a36Sopenharmony_ci					   enum wcd_mbhc_plug_type plug_type)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	bool micbias2 = false;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	switch (plug_type) {
98962306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADPHONE:
99062306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
99162306a36Sopenharmony_ci		break;
99262306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADSET:
99362306a36Sopenharmony_ci		if (mbhc->mbhc_cb->micbias_enable_status)
99462306a36Sopenharmony_ci			micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
99562306a36Sopenharmony_ci									MIC_BIAS_2);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		if (!mbhc->is_hs_recording && !micbias2)
99862306a36Sopenharmony_ci			wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	default:
100162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
100262306a36Sopenharmony_ci		break;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	switch (plug_type) {
101062306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADSET:
101162306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADPHONE:
101262306a36Sopenharmony_ci		if (mbhc->mbhc_cb->bcs_enable)
101362306a36Sopenharmony_ci			mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	default:
101662306a36Sopenharmony_ci		break;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	enum wcd_mbhc_plug_type plug_type;
102462306a36Sopenharmony_ci	u32 hph_thr, hs_thr;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
102762306a36Sopenharmony_ci	hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (adc_result < hph_thr)
103062306a36Sopenharmony_ci		plug_type = MBHC_PLUG_TYPE_HEADPHONE;
103162306a36Sopenharmony_ci	else if (adc_result > hs_thr)
103262306a36Sopenharmony_ci		plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
103362306a36Sopenharmony_ci	else
103462306a36Sopenharmony_ci		plug_type = MBHC_PLUG_TYPE_HEADSET;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	return plug_type;
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	int hs_threshold, micbias_mv;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	micbias_mv = wcd_mbhc_get_micbias(mbhc);
104462306a36Sopenharmony_ci	if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
104562306a36Sopenharmony_ci		if (mbhc->cfg->micb_mv == micbias_mv)
104662306a36Sopenharmony_ci			hs_threshold = mbhc->cfg->hs_thr;
104762306a36Sopenharmony_ci		else
104862306a36Sopenharmony_ci			hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
104962306a36Sopenharmony_ci	} else {
105062306a36Sopenharmony_ci		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
105162306a36Sopenharmony_ci							WCD_MBHC_ADC_MICBIAS_MV);
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci	return hs_threshold;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	bool is_spl_hs = false;
105962306a36Sopenharmony_ci	int output_mv, hs_threshold, hph_threshold;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
106262306a36Sopenharmony_ci		return false;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* Bump up MIC_BIAS2 to 2.7V */
106562306a36Sopenharmony_ci	mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
106662306a36Sopenharmony_ci	usleep_range(10000, 10100);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
106962306a36Sopenharmony_ci	hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
107062306a36Sopenharmony_ci	hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (!(output_mv > hs_threshold || output_mv < hph_threshold))
107362306a36Sopenharmony_ci		is_spl_hs = true;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/* Back MIC_BIAS2 to 1.8v if the type is not special headset */
107662306a36Sopenharmony_ci	if (!is_spl_hs) {
107762306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
107862306a36Sopenharmony_ci		/* Add 10ms delay for micbias to settle */
107962306a36Sopenharmony_ci		usleep_range(10000, 10100);
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	return is_spl_hs;
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_cistatic void wcd_correct_swch_plug(struct work_struct *work)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct wcd_mbhc *mbhc;
108862306a36Sopenharmony_ci	struct snd_soc_component *component;
108962306a36Sopenharmony_ci	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
109062306a36Sopenharmony_ci	unsigned long timeout;
109162306a36Sopenharmony_ci	int pt_gnd_mic_swap_cnt = 0;
109262306a36Sopenharmony_ci	int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
109362306a36Sopenharmony_ci	bool is_spl_hs = false;
109462306a36Sopenharmony_ci	bool is_pa_on;
109562306a36Sopenharmony_ci	int ret;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
109862306a36Sopenharmony_ci	component = mbhc->component;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	ret = pm_runtime_get_sync(component->dev);
110162306a36Sopenharmony_ci	if (ret < 0 && ret != -EACCES) {
110262306a36Sopenharmony_ci		dev_err_ratelimited(component->dev,
110362306a36Sopenharmony_ci				    "pm_runtime_get_sync failed in %s, ret %d\n",
110462306a36Sopenharmony_ci				    __func__, ret);
110562306a36Sopenharmony_ci		pm_runtime_put_noidle(component->dev);
110662306a36Sopenharmony_ci		return;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci	micbias_mv = wcd_mbhc_get_micbias(mbhc);
110962306a36Sopenharmony_ci	hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/* Mask ADC COMPLETE interrupt */
111262306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/* Check for cross connection */
111562306a36Sopenharmony_ci	do {
111662306a36Sopenharmony_ci		cross_conn = wcd_check_cross_conn(mbhc);
111762306a36Sopenharmony_ci		try++;
111862306a36Sopenharmony_ci	} while (try < GND_MIC_SWAP_THRESHOLD);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (cross_conn > 0) {
112162306a36Sopenharmony_ci		plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
112262306a36Sopenharmony_ci		dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
112362306a36Sopenharmony_ci			plug_type);
112462306a36Sopenharmony_ci		goto correct_plug_type;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	/* Find plug type */
112862306a36Sopenharmony_ci	output_mv = wcd_measure_adc_continuous(mbhc);
112962306a36Sopenharmony_ci	plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/*
113262306a36Sopenharmony_ci	 * Report plug type if it is either headset or headphone
113362306a36Sopenharmony_ci	 * else start the 3 sec loop
113462306a36Sopenharmony_ci	 */
113562306a36Sopenharmony_ci	switch (plug_type) {
113662306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADPHONE:
113762306a36Sopenharmony_ci		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
113862306a36Sopenharmony_ci		break;
113962306a36Sopenharmony_ci	case MBHC_PLUG_TYPE_HEADSET:
114062306a36Sopenharmony_ci		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
114162306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
114262306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
114362306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
114462306a36Sopenharmony_ci		break;
114562306a36Sopenharmony_ci	default:
114662306a36Sopenharmony_ci		break;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cicorrect_plug_type:
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	/* Disable BCS slow insertion detection */
115262306a36Sopenharmony_ci	wcd_mbhc_bcs_enable(mbhc, plug_type, false);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	while (!time_after(jiffies, timeout)) {
115762306a36Sopenharmony_ci		if (mbhc->hs_detect_work_stop) {
115862306a36Sopenharmony_ci			wcd_micbias_disable(mbhc);
115962306a36Sopenharmony_ci			goto exit;
116062306a36Sopenharmony_ci		}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		msleep(180);
116362306a36Sopenharmony_ci		/*
116462306a36Sopenharmony_ci		 * Use ADC single mode to minimize the chance of missing out
116562306a36Sopenharmony_ci		 * btn press/release for HEADSET type during correct work.
116662306a36Sopenharmony_ci		 */
116762306a36Sopenharmony_ci		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
116862306a36Sopenharmony_ci		plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
116962306a36Sopenharmony_ci		is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		if (output_mv > hs_threshold && !is_spl_hs) {
117262306a36Sopenharmony_ci			is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
117362306a36Sopenharmony_ci			output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci			if (is_spl_hs) {
117662306a36Sopenharmony_ci				hs_threshold *= wcd_mbhc_get_micbias(mbhc);
117762306a36Sopenharmony_ci				hs_threshold /= micbias_mv;
117862306a36Sopenharmony_ci			}
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci		if ((output_mv <= hs_threshold) && !is_pa_on) {
118262306a36Sopenharmony_ci			/* Check for cross connection*/
118362306a36Sopenharmony_ci			cross_conn = wcd_check_cross_conn(mbhc);
118462306a36Sopenharmony_ci			if (cross_conn > 0) { /* cross-connection */
118562306a36Sopenharmony_ci				pt_gnd_mic_swap_cnt++;
118662306a36Sopenharmony_ci				if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
118762306a36Sopenharmony_ci					continue;
118862306a36Sopenharmony_ci				else
118962306a36Sopenharmony_ci					plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
119062306a36Sopenharmony_ci			} else if (!cross_conn) { /* no cross connection */
119162306a36Sopenharmony_ci				pt_gnd_mic_swap_cnt = 0;
119262306a36Sopenharmony_ci				plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
119362306a36Sopenharmony_ci				continue;
119462306a36Sopenharmony_ci			} else /* Error if (cross_conn < 0) */
119562306a36Sopenharmony_ci				continue;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci			if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
119862306a36Sopenharmony_ci				/* US_EU gpio present, flip switch */
119962306a36Sopenharmony_ci				if (mbhc->cfg->swap_gnd_mic) {
120062306a36Sopenharmony_ci					if (mbhc->cfg->swap_gnd_mic(component, true))
120162306a36Sopenharmony_ci						continue;
120262306a36Sopenharmony_ci				}
120362306a36Sopenharmony_ci			}
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		/* cable is extension cable */
120762306a36Sopenharmony_ci		if (output_mv > hs_threshold || mbhc->force_linein)
120862306a36Sopenharmony_ci			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	wcd_mbhc_bcs_enable(mbhc, plug_type, true);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
121462306a36Sopenharmony_ci		if (is_spl_hs)
121562306a36Sopenharmony_ci			plug_type = MBHC_PLUG_TYPE_HEADSET;
121662306a36Sopenharmony_ci		else
121762306a36Sopenharmony_ci			wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
122162306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
122262306a36Sopenharmony_ci	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/*
122562306a36Sopenharmony_ci	 * Set DETECTION_DONE bit for HEADSET
122662306a36Sopenharmony_ci	 * so that btn press/release interrupt can be generated.
122762306a36Sopenharmony_ci	 * For other plug type, clear the bit.
122862306a36Sopenharmony_ci	 */
122962306a36Sopenharmony_ci	if (plug_type == MBHC_PLUG_TYPE_HEADSET)
123062306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
123162306a36Sopenharmony_ci	else
123262306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micbias_control)
123562306a36Sopenharmony_ci		wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciexit:
123862306a36Sopenharmony_ci	if (mbhc->mbhc_cb->mbhc_micbias_control/* &&  !mbhc->micbias_enable*/)
123962306a36Sopenharmony_ci		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/*
124262306a36Sopenharmony_ci	 * If plug type is corrected from special headset to headphone,
124362306a36Sopenharmony_ci	 * clear the micbias enable flag, set micbias back to 1.8V and
124462306a36Sopenharmony_ci	 * disable micbias.
124562306a36Sopenharmony_ci	 */
124662306a36Sopenharmony_ci	if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
124762306a36Sopenharmony_ci		wcd_micbias_disable(mbhc);
124862306a36Sopenharmony_ci		/*
124962306a36Sopenharmony_ci		 * Enable ADC COMPLETE interrupt for HEADPHONE.
125062306a36Sopenharmony_ci		 * Btn release may happen after the correct work, ADC COMPLETE
125162306a36Sopenharmony_ci		 * interrupt needs to be captured to correct plug type.
125262306a36Sopenharmony_ci		 */
125362306a36Sopenharmony_ci		enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
125762306a36Sopenharmony_ci		mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	pm_runtime_mark_last_busy(component->dev);
126062306a36Sopenharmony_ci	pm_runtime_put_autosuspend(component->dev);
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = data;
126662306a36Sopenharmony_ci	unsigned long timeout;
126762306a36Sopenharmony_ci	int adc_threshold, output_mv, retry = 0;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
127062306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
127162306a36Sopenharmony_ci	adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	do {
127462306a36Sopenharmony_ci		retry++;
127562306a36Sopenharmony_ci		/*
127662306a36Sopenharmony_ci		 * read output_mv every 10ms to look for
127762306a36Sopenharmony_ci		 * any change in IN2_P
127862306a36Sopenharmony_ci		 */
127962306a36Sopenharmony_ci		usleep_range(10000, 10100);
128062306a36Sopenharmony_ci		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		/* Check for fake removal */
128362306a36Sopenharmony_ci		if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
128462306a36Sopenharmony_ci			goto exit;
128562306a36Sopenharmony_ci	} while (!time_after(jiffies, timeout));
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/*
128862306a36Sopenharmony_ci	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for
128962306a36Sopenharmony_ci	 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
129062306a36Sopenharmony_ci	 * follows ELEC_REM one when HEADPHONE is removed.
129162306a36Sopenharmony_ci	 */
129262306a36Sopenharmony_ci	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
129362306a36Sopenharmony_ci		mbhc->extn_cable_hph_rem = true;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
129662306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
129762306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
129862306a36Sopenharmony_ci	wcd_mbhc_elec_hs_report_unplug(mbhc);
129962306a36Sopenharmony_ci	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ciexit:
130262306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
130362306a36Sopenharmony_ci	return IRQ_HANDLED;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct wcd_mbhc *mbhc = data;
130962306a36Sopenharmony_ci	u8 clamp_state;
131062306a36Sopenharmony_ci	u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/*
131362306a36Sopenharmony_ci	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
131462306a36Sopenharmony_ci	 * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
131562306a36Sopenharmony_ci	 * when HEADPHONE is removed.
131662306a36Sopenharmony_ci	 */
131762306a36Sopenharmony_ci	if (mbhc->extn_cable_hph_rem == true) {
131862306a36Sopenharmony_ci		mbhc->extn_cable_hph_rem = false;
131962306a36Sopenharmony_ci		return IRQ_HANDLED;
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	do {
132362306a36Sopenharmony_ci		clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
132462306a36Sopenharmony_ci		if (clamp_state)
132562306a36Sopenharmony_ci			return IRQ_HANDLED;
132662306a36Sopenharmony_ci		/*
132762306a36Sopenharmony_ci		 * check clamp for 120ms but at 30ms chunks to leave
132862306a36Sopenharmony_ci		 * room for other interrupts to be processed
132962306a36Sopenharmony_ci		 */
133062306a36Sopenharmony_ci		usleep_range(30000, 30100);
133162306a36Sopenharmony_ci	} while (--clamp_retry);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/*
133462306a36Sopenharmony_ci	 * If current plug is headphone then there is no chance to
133562306a36Sopenharmony_ci	 * get ADC complete interrupt, so connected cable should be
133662306a36Sopenharmony_ci	 * headset not headphone.
133762306a36Sopenharmony_ci	 */
133862306a36Sopenharmony_ci	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
133962306a36Sopenharmony_ci		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
134062306a36Sopenharmony_ci		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
134162306a36Sopenharmony_ci		wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
134262306a36Sopenharmony_ci		return IRQ_HANDLED;
134362306a36Sopenharmony_ci	}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	return IRQ_HANDLED;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ciint wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,	uint32_t *zr)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	*zl = mbhc->zl;
135162306a36Sopenharmony_ci	*zr = mbhc->zr;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (*zl && *zr)
135462306a36Sopenharmony_ci		return 0;
135562306a36Sopenharmony_ci	else
135662306a36Sopenharmony_ci		return -EINVAL;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_get_impedance);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_civoid wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	mbhc->hph_type = hph_type;
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_set_hph_type);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ciint wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	return mbhc->hph_type;
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_get_hph_type);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ciint wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
137362306a36Sopenharmony_ci		   struct snd_soc_jack *jack)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	if (!mbhc || !cfg || !jack)
137662306a36Sopenharmony_ci		return -EINVAL;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	mbhc->cfg = cfg;
137962306a36Sopenharmony_ci	mbhc->jack = jack;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return wcd_mbhc_initialise(mbhc);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_start);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_civoid wcd_mbhc_stop(struct wcd_mbhc *mbhc)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
138862306a36Sopenharmony_ci	mbhc->hph_status = 0;
138962306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
139062306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_stop);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ciint wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
139762306a36Sopenharmony_ci	int ret, i, microvolt;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
140062306a36Sopenharmony_ci		cfg->hphl_swh = false;
140162306a36Sopenharmony_ci	else
140262306a36Sopenharmony_ci		cfg->hphl_swh = true;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
140562306a36Sopenharmony_ci		cfg->gnd_swh = false;
140662306a36Sopenharmony_ci	else
140762306a36Sopenharmony_ci		cfg->gnd_swh = true;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
141062306a36Sopenharmony_ci				   &microvolt);
141162306a36Sopenharmony_ci	if (ret)
141262306a36Sopenharmony_ci		dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
141362306a36Sopenharmony_ci	else
141462306a36Sopenharmony_ci		cfg->hs_thr = microvolt/1000;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
141762306a36Sopenharmony_ci				   &microvolt);
141862306a36Sopenharmony_ci	if (ret)
141962306a36Sopenharmony_ci		dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt	entry\n");
142062306a36Sopenharmony_ci	else
142162306a36Sopenharmony_ci		cfg->hph_thr = microvolt/1000;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	ret = of_property_read_u32_array(np,
142462306a36Sopenharmony_ci					 "qcom,mbhc-buttons-vthreshold-microvolt",
142562306a36Sopenharmony_ci					 &cfg->btn_high[0],
142662306a36Sopenharmony_ci					 WCD_MBHC_DEF_BUTTONS);
142762306a36Sopenharmony_ci	if (ret)
142862306a36Sopenharmony_ci		dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
143162306a36Sopenharmony_ci		if (ret) /* default voltage */
143262306a36Sopenharmony_ci			cfg->btn_high[i] = 500000;
143362306a36Sopenharmony_ci		else
143462306a36Sopenharmony_ci			/* Micro to Milli Volts */
143562306a36Sopenharmony_ci			cfg->btn_high[i] = cfg->btn_high[i]/1000;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return 0;
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistruct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
144362306a36Sopenharmony_ci			       const struct wcd_mbhc_cb *mbhc_cb,
144462306a36Sopenharmony_ci			       const struct wcd_mbhc_intr *intr_ids,
144562306a36Sopenharmony_ci			       struct wcd_mbhc_field *fields,
144662306a36Sopenharmony_ci			       bool impedance_det_en)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct device *dev = component->dev;
144962306a36Sopenharmony_ci	struct wcd_mbhc *mbhc;
145062306a36Sopenharmony_ci	int ret;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
145362306a36Sopenharmony_ci		dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
145462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
145862306a36Sopenharmony_ci	if (!mbhc)
145962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	mbhc->component = component;
146262306a36Sopenharmony_ci	mbhc->dev = dev;
146362306a36Sopenharmony_ci	mbhc->intr_ids = intr_ids;
146462306a36Sopenharmony_ci	mbhc->mbhc_cb = mbhc_cb;
146562306a36Sopenharmony_ci	mbhc->fields = fields;
146662306a36Sopenharmony_ci	mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (mbhc_cb->compute_impedance)
146962306a36Sopenharmony_ci		mbhc->impedance_detect = impedance_det_en;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	mutex_init(&mbhc->lock);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
147862306a36Sopenharmony_ci					wcd_mbhc_mech_plug_detect_irq,
147962306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
148062306a36Sopenharmony_ci					"mbhc sw intr", mbhc);
148162306a36Sopenharmony_ci	if (ret)
148262306a36Sopenharmony_ci		goto err_free_mbhc;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
148562306a36Sopenharmony_ci					wcd_mbhc_btn_press_handler,
148662306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
148762306a36Sopenharmony_ci					"Button Press detect", mbhc);
148862306a36Sopenharmony_ci	if (ret)
148962306a36Sopenharmony_ci		goto err_free_sw_intr;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
149262306a36Sopenharmony_ci					wcd_mbhc_btn_release_handler,
149362306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
149462306a36Sopenharmony_ci					"Button Release detect", mbhc);
149562306a36Sopenharmony_ci	if (ret)
149662306a36Sopenharmony_ci		goto err_free_btn_press_intr;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
149962306a36Sopenharmony_ci					wcd_mbhc_adc_hs_ins_irq,
150062306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
150162306a36Sopenharmony_ci					"Elect Insert", mbhc);
150262306a36Sopenharmony_ci	if (ret)
150362306a36Sopenharmony_ci		goto err_free_btn_release_intr;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
150862306a36Sopenharmony_ci					wcd_mbhc_adc_hs_rem_irq,
150962306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
151062306a36Sopenharmony_ci					"Elect Remove", mbhc);
151162306a36Sopenharmony_ci	if (ret)
151262306a36Sopenharmony_ci		goto err_free_hs_ins_intr;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
151762306a36Sopenharmony_ci					wcd_mbhc_hphl_ocp_irq,
151862306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
151962306a36Sopenharmony_ci					"HPH_L OCP detect", mbhc);
152062306a36Sopenharmony_ci	if (ret)
152162306a36Sopenharmony_ci		goto err_free_hs_rem_intr;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
152462306a36Sopenharmony_ci					wcd_mbhc_hphr_ocp_irq,
152562306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
152662306a36Sopenharmony_ci					"HPH_R OCP detect", mbhc);
152762306a36Sopenharmony_ci	if (ret)
152862306a36Sopenharmony_ci		goto err_free_hph_left_ocp;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	return mbhc;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cierr_free_hph_left_ocp:
153362306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
153462306a36Sopenharmony_cierr_free_hs_rem_intr:
153562306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
153662306a36Sopenharmony_cierr_free_hs_ins_intr:
153762306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
153862306a36Sopenharmony_cierr_free_btn_release_intr:
153962306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
154062306a36Sopenharmony_cierr_free_btn_press_intr:
154162306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
154262306a36Sopenharmony_cierr_free_sw_intr:
154362306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
154462306a36Sopenharmony_cierr_free_mbhc:
154562306a36Sopenharmony_ci	kfree(mbhc);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	return ERR_PTR(ret);
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_init);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_civoid wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
155662306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
155762306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
155862306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
155962306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
156062306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
156162306a36Sopenharmony_ci	free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	mutex_lock(&mbhc->lock);
156462306a36Sopenharmony_ci	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
156562306a36Sopenharmony_ci	mutex_unlock(&mbhc->lock);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	kfree(mbhc);
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ciEXPORT_SYMBOL(wcd_mbhc_deinit);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_cistatic int __init mbhc_init(void)
157262306a36Sopenharmony_ci{
157362306a36Sopenharmony_ci	return 0;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_cistatic void __exit mbhc_exit(void)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cimodule_init(mbhc_init);
158162306a36Sopenharmony_cimodule_exit(mbhc_exit);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ciMODULE_DESCRIPTION("wcd MBHC v2 module");
158462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1585