cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wcd-mbhc-v2.c (43708B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2// Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
      3
      4#include <linux/module.h>
      5#include <linux/init.h>
      6#include <linux/slab.h>
      7#include <linux/device.h>
      8#include <linux/pm_runtime.h>
      9#include <linux/printk.h>
     10#include <linux/delay.h>
     11#include <linux/kernel.h>
     12#include <sound/soc.h>
     13#include <sound/jack.h>
     14#include "wcd-mbhc-v2.h"
     15
     16#define HS_DETECT_PLUG_TIME_MS		(3 * 1000)
     17#define MBHC_BUTTON_PRESS_THRESHOLD_MIN	250
     18#define GND_MIC_SWAP_THRESHOLD		4
     19#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS	100
     20#define HPHL_CROSS_CONN_THRESHOLD	100
     21#define HS_VREF_MIN_VAL			1400
     22#define FAKE_REM_RETRY_ATTEMPTS		3
     23#define WCD_MBHC_ADC_HS_THRESHOLD_MV	1700
     24#define WCD_MBHC_ADC_HPH_THRESHOLD_MV	75
     25#define WCD_MBHC_ADC_MICBIAS_MV		1800
     26#define WCD_MBHC_FAKE_INS_RETRY		4
     27
     28#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
     29			   SND_JACK_MECHANICAL)
     30
     31#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
     32				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
     33				  SND_JACK_BTN_4 | SND_JACK_BTN_5)
     34
     35enum wcd_mbhc_adc_mux_ctl {
     36	MUX_CTL_AUTO = 0,
     37	MUX_CTL_IN2P,
     38	MUX_CTL_IN3P,
     39	MUX_CTL_IN4P,
     40	MUX_CTL_HPH_L,
     41	MUX_CTL_HPH_R,
     42	MUX_CTL_NONE,
     43};
     44
     45struct wcd_mbhc {
     46	struct device *dev;
     47	struct snd_soc_component *component;
     48	struct snd_soc_jack *jack;
     49	struct wcd_mbhc_config *cfg;
     50	const struct wcd_mbhc_cb *mbhc_cb;
     51	const struct wcd_mbhc_intr *intr_ids;
     52	struct wcd_mbhc_field *fields;
     53	/* Delayed work to report long button press */
     54	struct delayed_work mbhc_btn_dwork;
     55	/* Work to correct accessory type */
     56	struct work_struct correct_plug_swch;
     57	struct mutex lock;
     58	int buttons_pressed;
     59	u32 hph_status; /* track headhpone status */
     60	u8 current_plug;
     61	bool is_btn_press;
     62	bool in_swch_irq_handler;
     63	bool hs_detect_work_stop;
     64	bool is_hs_recording;
     65	bool extn_cable_hph_rem;
     66	bool force_linein;
     67	bool impedance_detect;
     68	unsigned long event_state;
     69	unsigned long jiffies_atreport;
     70	/* impedance of hphl and hphr */
     71	uint32_t zl, zr;
     72	/* Holds type of Headset - Mono/Stereo */
     73	enum wcd_mbhc_hph_type hph_type;
     74	/* Holds mbhc detection method - ADC/Legacy */
     75	int mbhc_detection_logic;
     76};
     77
     78static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
     79				       int field, int val)
     80{
     81	if (!mbhc->fields[field].reg)
     82		return 0;
     83
     84	return snd_soc_component_write_field(mbhc->component,
     85					     mbhc->fields[field].reg,
     86					     mbhc->fields[field].mask, val);
     87}
     88
     89static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
     90{
     91	if (!mbhc->fields[field].reg)
     92		return 0;
     93
     94	return snd_soc_component_read_field(mbhc->component,
     95					    mbhc->fields[field].reg,
     96					    mbhc->fields[field].mask);
     97}
     98
     99static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
    100{
    101	u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
    102
    103	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
    104}
    105
    106static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
    107{
    108	struct snd_soc_component *component = mbhc->component;
    109
    110	mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
    111				   mbhc->cfg->btn_high,
    112				   mbhc->cfg->num_btn, micbias);
    113}
    114
    115static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
    116					  const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
    117{
    118
    119	/*
    120	 * Some codecs handle micbias/pullup enablement in codec
    121	 * drivers itself and micbias is not needed for regular
    122	 * plug type detection. So if micbias_control callback function
    123	 * is defined, just return.
    124	 */
    125	if (mbhc->mbhc_cb->mbhc_micbias_control)
    126		return;
    127
    128	switch (cs_mb_en) {
    129	case WCD_MBHC_EN_CS:
    130		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
    131		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
    132		/* Program Button threshold registers as per CS */
    133		wcd_program_btn_threshold(mbhc, false);
    134		break;
    135	case WCD_MBHC_EN_MB:
    136		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
    137		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    138		/* Disable PULL_UP_EN & enable MICBIAS */
    139		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
    140		/* Program Button threshold registers as per MICBIAS */
    141		wcd_program_btn_threshold(mbhc, true);
    142		break;
    143	case WCD_MBHC_EN_PULLUP:
    144		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
    145		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    146		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
    147		/* Program Button threshold registers as per MICBIAS */
    148		wcd_program_btn_threshold(mbhc, true);
    149		break;
    150	case WCD_MBHC_EN_NONE:
    151		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
    152		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    153		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
    154		break;
    155	default:
    156		dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
    157		break;
    158	}
    159}
    160
    161int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
    162{
    163
    164	struct snd_soc_component *component;
    165	bool micbias2 = false;
    166
    167	if (!mbhc)
    168		return 0;
    169
    170	component = mbhc->component;
    171
    172	if (mbhc->mbhc_cb->micbias_enable_status)
    173		micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
    174
    175	switch (event) {
    176	/* MICBIAS usage change */
    177	case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
    178		mbhc->is_hs_recording = true;
    179		break;
    180	case WCD_EVENT_POST_MICBIAS_2_ON:
    181		/* Disable current source if micbias2 enabled */
    182		if (mbhc->mbhc_cb->mbhc_micbias_control) {
    183			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
    184				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
    185		} else {
    186			mbhc->is_hs_recording = true;
    187			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
    188		}
    189		break;
    190	case WCD_EVENT_PRE_MICBIAS_2_OFF:
    191		/*
    192		 * Before MICBIAS_2 is turned off, if FSM is enabled,
    193		 * make sure current source is enabled so as to detect
    194		 * button press/release events
    195		 */
    196		if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
    197			if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
    198				wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
    199		}
    200		break;
    201	/* MICBIAS usage change */
    202	case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
    203		mbhc->is_hs_recording = false;
    204		break;
    205	case WCD_EVENT_POST_MICBIAS_2_OFF:
    206		if (!mbhc->mbhc_cb->mbhc_micbias_control)
    207			mbhc->is_hs_recording = false;
    208
    209		/* Enable PULL UP if PA's are enabled */
    210		if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
    211		    (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
    212			/* enable pullup and cs, disable mb */
    213			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
    214		else
    215			/* enable current source and disable mb, pullup*/
    216			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
    217
    218		break;
    219	case WCD_EVENT_POST_HPHL_PA_OFF:
    220		clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
    221
    222		/* check if micbias is enabled */
    223		if (micbias2)
    224			/* Disable cs, pullup & enable micbias */
    225			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
    226		else
    227			/* Disable micbias, pullup & enable cs */
    228			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
    229		break;
    230	case WCD_EVENT_POST_HPHR_PA_OFF:
    231		clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
    232		/* check if micbias is enabled */
    233		if (micbias2)
    234			/* Disable cs, pullup & enable micbias */
    235			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
    236		else
    237			/* Disable micbias, pullup & enable cs */
    238			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
    239		break;
    240	case WCD_EVENT_PRE_HPHL_PA_ON:
    241		set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
    242		/* check if micbias is enabled */
    243		if (micbias2)
    244			/* Disable cs, pullup & enable micbias */
    245			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
    246		else
    247			/* Disable micbias, enable pullup & cs */
    248			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
    249		break;
    250	case WCD_EVENT_PRE_HPHR_PA_ON:
    251		set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
    252		/* check if micbias is enabled */
    253		if (micbias2)
    254			/* Disable cs, pullup & enable micbias */
    255			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
    256		else
    257			/* Disable micbias, enable pullup & cs */
    258			wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
    259		break;
    260	default:
    261		break;
    262	}
    263	return 0;
    264}
    265EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
    266
    267static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
    268{
    269	return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
    270}
    271
    272static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
    273{
    274	struct snd_soc_component *component = mbhc->component;
    275
    276	if (mbhc->mbhc_cb->mbhc_micbias_control)
    277		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
    278
    279	if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
    280		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
    281
    282	if (mbhc->mbhc_cb->set_micbias_value) {
    283		mbhc->mbhc_cb->set_micbias_value(component);
    284		wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
    285	}
    286}
    287
    288static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
    289					 enum snd_jack_types jack_type)
    290{
    291	mbhc->hph_status &= ~jack_type;
    292	/*
    293	 * cancel possibly scheduled btn work and
    294	 * report release if we reported button press
    295	 */
    296	if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
    297		snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
    298		mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
    299	}
    300
    301	wcd_micbias_disable(mbhc);
    302	mbhc->hph_type = WCD_MBHC_HPH_NONE;
    303	mbhc->zl = mbhc->zr = 0;
    304	snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
    305	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
    306	mbhc->force_linein = false;
    307}
    308
    309static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
    310{
    311
    312	if (!mbhc->impedance_detect)
    313		return;
    314
    315	if (mbhc->cfg->linein_th != 0) {
    316		u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
    317		/* Set MUX_CTL to AUTO for Z-det */
    318
    319		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    320		wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
    321		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    322		mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
    323		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
    324	}
    325}
    326
    327static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
    328					   enum snd_jack_types jack_type)
    329{
    330	bool is_pa_on;
    331	/*
    332	 * Report removal of current jack type.
    333	 * Headphone to headset shouldn't report headphone
    334	 * removal.
    335	 */
    336	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
    337	    jack_type == SND_JACK_HEADPHONE)
    338		mbhc->hph_status &= ~SND_JACK_HEADSET;
    339
    340	/* Report insertion */
    341	switch (jack_type) {
    342	case SND_JACK_HEADPHONE:
    343		mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
    344		break;
    345	case SND_JACK_HEADSET:
    346		mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
    347		mbhc->jiffies_atreport = jiffies;
    348		break;
    349	case SND_JACK_LINEOUT:
    350		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
    351		break;
    352	default:
    353		break;
    354	}
    355
    356
    357	is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
    358
    359	if (!is_pa_on) {
    360		wcd_mbhc_compute_impedance(mbhc);
    361		if ((mbhc->zl > mbhc->cfg->linein_th) &&
    362		    (mbhc->zr > mbhc->cfg->linein_th) &&
    363		    (jack_type == SND_JACK_HEADPHONE)) {
    364			jack_type = SND_JACK_LINEOUT;
    365			mbhc->force_linein = true;
    366			mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
    367			if (mbhc->hph_status) {
    368				mbhc->hph_status &= ~(SND_JACK_HEADSET |
    369						      SND_JACK_LINEOUT);
    370				snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
    371						    WCD_MBHC_JACK_MASK);
    372			}
    373		}
    374	}
    375
    376	/* Do not calculate impedance again for lineout
    377	 * as during playback pa is on and impedance values
    378	 * will not be correct resulting in lineout detected
    379	 * as headphone.
    380	 */
    381	if (is_pa_on && mbhc->force_linein) {
    382		jack_type = SND_JACK_LINEOUT;
    383		mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
    384		if (mbhc->hph_status) {
    385			mbhc->hph_status &= ~(SND_JACK_HEADSET |
    386					      SND_JACK_LINEOUT);
    387			snd_soc_jack_report(mbhc->jack,	mbhc->hph_status,
    388					    WCD_MBHC_JACK_MASK);
    389		}
    390	}
    391
    392	mbhc->hph_status |= jack_type;
    393
    394	if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
    395		mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
    396
    397	snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
    398			    WCD_MBHC_JACK_MASK);
    399}
    400
    401static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
    402				 enum snd_jack_types jack_type)
    403{
    404
    405	WARN_ON(!mutex_is_locked(&mbhc->lock));
    406
    407	if (!insertion) /* Report removal */
    408		wcd_mbhc_report_plug_removal(mbhc, jack_type);
    409	else
    410		wcd_mbhc_report_plug_insertion(mbhc, jack_type);
    411
    412}
    413
    414static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
    415				      struct work_struct *work)
    416{
    417	mbhc->hs_detect_work_stop = true;
    418	mutex_unlock(&mbhc->lock);
    419	cancel_work_sync(work);
    420	mutex_lock(&mbhc->lock);
    421}
    422
    423static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
    424{
    425	/* cancel pending button press */
    426	wcd_cancel_btn_work(mbhc);
    427	/* cancel correct work function */
    428	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
    429}
    430
    431static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
    432{
    433	wcd_mbhc_cancel_pending_work(mbhc);
    434	/* Report extension cable */
    435	wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
    436	/*
    437	 * Disable HPHL trigger and MIC Schmitt triggers.
    438	 * Setup for insertion detection.
    439	 */
    440	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
    441	wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
    442	/* Disable HW FSM */
    443	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    444	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
    445
    446	/* Set the detection type appropriately */
    447	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
    448	enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
    449}
    450
    451static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
    452				   enum wcd_mbhc_plug_type plug_type)
    453{
    454	if (mbhc->current_plug == plug_type)
    455		return;
    456
    457	mutex_lock(&mbhc->lock);
    458
    459	switch (plug_type) {
    460	case MBHC_PLUG_TYPE_HEADPHONE:
    461		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
    462		break;
    463	case MBHC_PLUG_TYPE_HEADSET:
    464		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
    465		break;
    466	case MBHC_PLUG_TYPE_HIGH_HPH:
    467		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
    468		break;
    469	case MBHC_PLUG_TYPE_GND_MIC_SWAP:
    470		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
    471			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
    472		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
    473			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
    474		break;
    475	default:
    476		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
    477		     mbhc->current_plug, plug_type);
    478		break;
    479	}
    480	mutex_unlock(&mbhc->lock);
    481}
    482
    483static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
    484					    struct work_struct *work)
    485{
    486	WARN_ON(!mutex_is_locked(&mbhc->lock));
    487	mbhc->hs_detect_work_stop = false;
    488	schedule_work(work);
    489}
    490
    491static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
    492{
    493	struct snd_soc_component *component = mbhc->component;
    494
    495	WARN_ON(!mutex_is_locked(&mbhc->lock));
    496
    497	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
    498		mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
    499
    500	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
    501
    502	if (mbhc->mbhc_cb->mbhc_micbias_control) {
    503		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
    504						    MICB_ENABLE);
    505		wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
    506	}
    507}
    508
    509static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
    510{
    511	struct snd_soc_component *component;
    512	enum snd_jack_types jack_type;
    513	struct wcd_mbhc *mbhc = data;
    514	bool detection_type;
    515
    516	component = mbhc->component;
    517	mutex_lock(&mbhc->lock);
    518
    519	mbhc->in_swch_irq_handler = true;
    520
    521	wcd_mbhc_cancel_pending_work(mbhc);
    522
    523	detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
    524
    525	/* Set the detection type appropriately */
    526	wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
    527
    528	/* Enable micbias ramp */
    529	if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
    530		mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
    531
    532	if (detection_type) {
    533		if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
    534			goto exit;
    535		/* Make sure MASTER_BIAS_CTL is enabled */
    536		mbhc->mbhc_cb->mbhc_bias(component, true);
    537		mbhc->is_btn_press = false;
    538		wcd_mbhc_adc_detect_plug_type(mbhc);
    539	} else {
    540		/* Disable HW FSM */
    541		wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    542		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
    543		mbhc->extn_cable_hph_rem = false;
    544
    545		if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
    546			goto exit;
    547
    548		mbhc->is_btn_press = false;
    549		switch (mbhc->current_plug) {
    550		case MBHC_PLUG_TYPE_HEADPHONE:
    551			jack_type = SND_JACK_HEADPHONE;
    552			break;
    553		case MBHC_PLUG_TYPE_HEADSET:
    554			jack_type = SND_JACK_HEADSET;
    555			break;
    556		case MBHC_PLUG_TYPE_HIGH_HPH:
    557			if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
    558				wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
    559			jack_type = SND_JACK_LINEOUT;
    560			break;
    561		case MBHC_PLUG_TYPE_GND_MIC_SWAP:
    562			dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
    563			goto exit;
    564		default:
    565			dev_err(mbhc->dev, "Invalid current plug: %d\n",
    566				mbhc->current_plug);
    567			goto exit;
    568		}
    569		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
    570		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
    571		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
    572		wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
    573		wcd_mbhc_report_plug(mbhc, 0, jack_type);
    574	}
    575
    576exit:
    577	mbhc->in_swch_irq_handler = false;
    578	mutex_unlock(&mbhc->lock);
    579	return IRQ_HANDLED;
    580}
    581
    582static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
    583{
    584	int mask = 0;
    585	int btn;
    586
    587	btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
    588
    589	switch (btn) {
    590	case 0:
    591		mask = SND_JACK_BTN_0;
    592		break;
    593	case 1:
    594		mask = SND_JACK_BTN_1;
    595		break;
    596	case 2:
    597		mask = SND_JACK_BTN_2;
    598		break;
    599	case 3:
    600		mask = SND_JACK_BTN_3;
    601		break;
    602	case 4:
    603		mask = SND_JACK_BTN_4;
    604		break;
    605	case 5:
    606		mask = SND_JACK_BTN_5;
    607		break;
    608	default:
    609		break;
    610	}
    611
    612	return mask;
    613}
    614
    615static void wcd_btn_long_press_fn(struct work_struct *work)
    616{
    617	struct delayed_work *dwork = to_delayed_work(work);
    618	struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
    619
    620	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
    621		snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
    622				    mbhc->buttons_pressed);
    623}
    624
    625static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
    626{
    627	struct wcd_mbhc *mbhc = data;
    628	int mask;
    629	unsigned long msec_val;
    630
    631	mutex_lock(&mbhc->lock);
    632	wcd_cancel_btn_work(mbhc);
    633	mbhc->is_btn_press = true;
    634	msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
    635
    636	/* Too short, ignore button press */
    637	if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
    638		goto done;
    639
    640	/* If switch interrupt already kicked in, ignore button press */
    641	if (mbhc->in_swch_irq_handler)
    642		goto done;
    643
    644	/* Plug isn't headset, ignore button press */
    645	if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
    646		goto done;
    647
    648	mask = wcd_mbhc_get_button_mask(mbhc);
    649	mbhc->buttons_pressed |= mask;
    650	if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
    651		WARN(1, "Button pressed twice without release event\n");
    652done:
    653	mutex_unlock(&mbhc->lock);
    654	return IRQ_HANDLED;
    655}
    656
    657static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
    658{
    659	struct wcd_mbhc *mbhc = data;
    660	int ret;
    661
    662	mutex_lock(&mbhc->lock);
    663	if (mbhc->is_btn_press)
    664		mbhc->is_btn_press = false;
    665	else /* fake btn press */
    666		goto exit;
    667
    668	if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
    669		goto exit;
    670
    671	ret = wcd_cancel_btn_work(mbhc);
    672	if (ret == 0) { /* Reporting long button release event */
    673		snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
    674	} else {
    675		if (!mbhc->in_swch_irq_handler) {
    676			/* Reporting btn press n Release */
    677			snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
    678					    mbhc->buttons_pressed);
    679			snd_soc_jack_report(mbhc->jack,	0, mbhc->buttons_pressed);
    680		}
    681	}
    682	mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
    683exit:
    684	mutex_unlock(&mbhc->lock);
    685
    686	return IRQ_HANDLED;
    687}
    688
    689static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
    690{
    691
    692	/* TODO Find a better way to report this to Userspace */
    693	dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
    694		hphr ? "HPHR" : "HPHL");
    695
    696	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
    697	wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
    698
    699	return IRQ_HANDLED;
    700}
    701
    702static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
    703{
    704	return wcd_mbhc_hph_ocp_irq(data, false);
    705}
    706
    707static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
    708{
    709	return wcd_mbhc_hph_ocp_irq(data, true);
    710}
    711
    712static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
    713{
    714	struct snd_soc_component *component = mbhc->component;
    715	int ret;
    716
    717	ret = pm_runtime_get_sync(component->dev);
    718	if (ret < 0 && ret != -EACCES) {
    719		dev_err_ratelimited(component->dev,
    720				    "pm_runtime_get_sync failed in %s, ret %d\n",
    721				    __func__, ret);
    722		pm_runtime_put_noidle(component->dev);
    723		return ret;
    724	}
    725
    726	mutex_lock(&mbhc->lock);
    727
    728	/* enable HS detection */
    729	if (mbhc->mbhc_cb->hph_pull_up_control_v2)
    730		mbhc->mbhc_cb->hph_pull_up_control_v2(component,
    731						      HS_PULLUP_I_DEFAULT);
    732	else if (mbhc->mbhc_cb->hph_pull_up_control)
    733		mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
    734	else
    735		wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
    736
    737	wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
    738	wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
    739	wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
    740	if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
    741		mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
    742	wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
    743
    744	wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
    745
    746	/* Insertion debounce set to 96ms */
    747	wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
    748
    749	/* Button Debounce set to 16ms */
    750	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
    751
    752	/* enable bias */
    753	mbhc->mbhc_cb->mbhc_bias(component, true);
    754	/* enable MBHC clock */
    755	if (mbhc->mbhc_cb->clk_setup)
    756		mbhc->mbhc_cb->clk_setup(component, true);
    757
    758	/* program HS_VREF value */
    759	wcd_program_hs_vref(mbhc);
    760
    761	wcd_program_btn_threshold(mbhc, false);
    762
    763	mutex_unlock(&mbhc->lock);
    764
    765	pm_runtime_mark_last_busy(component->dev);
    766	pm_runtime_put_autosuspend(component->dev);
    767
    768	return 0;
    769}
    770
    771static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
    772{
    773	int micbias = 0;
    774
    775	if (mbhc->mbhc_cb->get_micbias_val) {
    776		mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
    777	} else {
    778		u8 vout_ctl = 0;
    779		/* Read MBHC Micbias (Mic Bias2) voltage */
    780		vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
    781		/* Formula for getting micbias from vout
    782		 * micbias = 1.0V + VOUT_CTL * 50mV
    783		 */
    784		micbias = 1000 + (vout_ctl * 50);
    785	}
    786	return micbias;
    787}
    788
    789static int wcd_get_voltage_from_adc(u8 val, int micbias)
    790{
    791	/* Formula for calculating voltage from ADC
    792	 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
    793	 */
    794	return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
    795}
    796
    797static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
    798{
    799	u8 adc_result;
    800	int output_mv;
    801	int retry = 3;
    802	u8 adc_en;
    803
    804	/* Pre-requisites for ADC continuous measurement */
    805	/* Read legacy electircal detection and disable */
    806	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
    807	/* Set ADC to continuous measurement */
    808	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
    809	/* Read ADC Enable bit to restore after adc measurement */
    810	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
    811	/* Disable ADC_ENABLE bit */
    812	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
    813	/* Disable MBHC FSM */
    814	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    815	/* Set the MUX selection to IN2P */
    816	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
    817	/* Enable MBHC FSM */
    818	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    819	/* Enable ADC_ENABLE bit */
    820	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
    821
    822	while (retry--) {
    823		/* wait for 3 msec before reading ADC result */
    824		usleep_range(3000, 3100);
    825		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
    826	}
    827
    828	/* Restore ADC Enable */
    829	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
    830	/* Get voltage from ADC result */
    831	output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
    832
    833	return output_mv;
    834}
    835
    836static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
    837{
    838	struct device *dev = mbhc->dev;
    839	u8 adc_timeout = 0;
    840	u8 adc_complete = 0;
    841	u8 adc_result;
    842	int retry = 6;
    843	int ret;
    844	int output_mv = 0;
    845	u8 adc_en;
    846
    847	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
    848	/* Read ADC Enable bit to restore after adc measurement */
    849	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
    850	/* Trigger ADC one time measurement */
    851	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
    852	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    853	/* Set the appropriate MUX selection */
    854	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
    855	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    856	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
    857
    858	while (retry--) {
    859		/* wait for 600usec to get adc results */
    860		usleep_range(600, 610);
    861
    862		/* check for ADC Timeout */
    863		adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
    864		if (adc_timeout)
    865			continue;
    866
    867		/* Read ADC complete bit */
    868		adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
    869		if (!adc_complete)
    870			continue;
    871
    872		/* Read ADC result */
    873		adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
    874
    875		/* Get voltage from ADC result */
    876		output_mv = wcd_get_voltage_from_adc(adc_result,
    877						wcd_mbhc_get_micbias(mbhc));
    878		break;
    879	}
    880
    881	/* Restore ADC Enable */
    882	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
    883
    884	if (retry <= 0) {
    885		dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
    886			__func__, adc_complete, adc_timeout);
    887		ret = -EINVAL;
    888	} else {
    889		ret = output_mv;
    890	}
    891
    892	return ret;
    893}
    894
    895/* To determine if cross connection occurred */
    896static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
    897{
    898	u8 adc_mode, elect_ctl, adc_en, fsm_en;
    899	int hphl_adc_res, hphr_adc_res;
    900	bool is_cross_conn = false;
    901
    902	/* If PA is enabled, dont check for cross-connection */
    903	if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
    904		return -EINVAL;
    905
    906	/* Read legacy electircal detection and disable */
    907	elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
    908	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
    909
    910	/* Read and set ADC to single measurement */
    911	adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
    912	/* Read ADC Enable bit to restore after adc measurement */
    913	adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
    914	/* Read FSM status */
    915	fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
    916
    917	/* Get adc result for HPH L */
    918	hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
    919	if (hphl_adc_res < 0)
    920		return hphl_adc_res;
    921
    922	/* Get adc result for HPH R in mV */
    923	hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
    924	if (hphr_adc_res < 0)
    925		return hphr_adc_res;
    926
    927	if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
    928	    hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
    929		is_cross_conn = true;
    930
    931	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
    932	/* Set the MUX selection to Auto */
    933	wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
    934	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
    935	/* Restore ADC Enable */
    936	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
    937	/* Restore ADC mode */
    938	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
    939	/* Restore FSM state */
    940	wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
    941	/* Restore electrical detection */
    942	wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
    943
    944	return is_cross_conn;
    945}
    946
    947static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
    948{
    949	int hs_threshold, micbias_mv;
    950
    951	micbias_mv = wcd_mbhc_get_micbias(mbhc);
    952	if (mbhc->cfg->hs_thr) {
    953		if (mbhc->cfg->micb_mv == micbias_mv)
    954			hs_threshold = mbhc->cfg->hs_thr;
    955		else
    956			hs_threshold = (mbhc->cfg->hs_thr *
    957				micbias_mv) / mbhc->cfg->micb_mv;
    958	} else {
    959		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
    960			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
    961	}
    962	return hs_threshold;
    963}
    964
    965static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
    966{
    967	int hph_threshold, micbias_mv;
    968
    969	micbias_mv = wcd_mbhc_get_micbias(mbhc);
    970	if (mbhc->cfg->hph_thr) {
    971		if (mbhc->cfg->micb_mv == micbias_mv)
    972			hph_threshold = mbhc->cfg->hph_thr;
    973		else
    974			hph_threshold = (mbhc->cfg->hph_thr *
    975				micbias_mv) / mbhc->cfg->micb_mv;
    976	} else {
    977		hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
    978			micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
    979	}
    980	return hph_threshold;
    981}
    982
    983static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
    984					   enum wcd_mbhc_plug_type plug_type)
    985{
    986	bool micbias2 = false;
    987
    988	switch (plug_type) {
    989	case MBHC_PLUG_TYPE_HEADPHONE:
    990		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
    991		break;
    992	case MBHC_PLUG_TYPE_HEADSET:
    993		if (mbhc->mbhc_cb->micbias_enable_status)
    994			micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
    995									MIC_BIAS_2);
    996
    997		if (!mbhc->is_hs_recording && !micbias2)
    998			wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
    999		break;
   1000	default:
   1001		wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
   1002		break;
   1003
   1004	}
   1005}
   1006
   1007static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
   1008{
   1009	switch (plug_type) {
   1010	case MBHC_PLUG_TYPE_HEADSET:
   1011	case MBHC_PLUG_TYPE_HEADPHONE:
   1012		if (mbhc->mbhc_cb->bcs_enable)
   1013			mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
   1014		break;
   1015	default:
   1016		break;
   1017	}
   1018}
   1019
   1020static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
   1021
   1022{
   1023	enum wcd_mbhc_plug_type plug_type;
   1024	u32 hph_thr, hs_thr;
   1025
   1026	hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
   1027	hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
   1028
   1029	if (adc_result < hph_thr)
   1030		plug_type = MBHC_PLUG_TYPE_HEADPHONE;
   1031	else if (adc_result > hs_thr)
   1032		plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
   1033	else
   1034		plug_type = MBHC_PLUG_TYPE_HEADSET;
   1035
   1036	return plug_type;
   1037}
   1038
   1039static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
   1040{
   1041	int hs_threshold, micbias_mv;
   1042
   1043	micbias_mv = wcd_mbhc_get_micbias(mbhc);
   1044	if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
   1045		if (mbhc->cfg->micb_mv == micbias_mv)
   1046			hs_threshold = mbhc->cfg->hs_thr;
   1047		else
   1048			hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
   1049	} else {
   1050		hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
   1051							WCD_MBHC_ADC_MICBIAS_MV);
   1052	}
   1053	return hs_threshold;
   1054}
   1055
   1056static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
   1057{
   1058	bool is_spl_hs = false;
   1059	int output_mv, hs_threshold, hph_threshold;
   1060
   1061	if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
   1062		return false;
   1063
   1064	/* Bump up MIC_BIAS2 to 2.7V */
   1065	mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
   1066	usleep_range(10000, 10100);
   1067
   1068	output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
   1069	hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
   1070	hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
   1071
   1072	if (!(output_mv > hs_threshold || output_mv < hph_threshold))
   1073		is_spl_hs = true;
   1074
   1075	/* Back MIC_BIAS2 to 1.8v if the type is not special headset */
   1076	if (!is_spl_hs) {
   1077		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
   1078		/* Add 10ms delay for micbias to settle */
   1079		usleep_range(10000, 10100);
   1080	}
   1081
   1082	return is_spl_hs;
   1083}
   1084
   1085static void wcd_correct_swch_plug(struct work_struct *work)
   1086{
   1087	struct wcd_mbhc *mbhc;
   1088	struct snd_soc_component *component;
   1089	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
   1090	unsigned long timeout;
   1091	int pt_gnd_mic_swap_cnt = 0;
   1092	int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
   1093	bool is_spl_hs = false;
   1094	bool is_pa_on;
   1095	int ret;
   1096
   1097	mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
   1098	component = mbhc->component;
   1099
   1100	ret = pm_runtime_get_sync(component->dev);
   1101	if (ret < 0 && ret != -EACCES) {
   1102		dev_err_ratelimited(component->dev,
   1103				    "pm_runtime_get_sync failed in %s, ret %d\n",
   1104				    __func__, ret);
   1105		pm_runtime_put_noidle(component->dev);
   1106		return;
   1107	}
   1108	micbias_mv = wcd_mbhc_get_micbias(mbhc);
   1109	hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
   1110
   1111	/* Mask ADC COMPLETE interrupt */
   1112	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
   1113
   1114	/* Check for cross connection */
   1115	do {
   1116		cross_conn = wcd_check_cross_conn(mbhc);
   1117		try++;
   1118	} while (try < GND_MIC_SWAP_THRESHOLD);
   1119
   1120	if (cross_conn > 0) {
   1121		plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
   1122		dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
   1123			plug_type);
   1124		goto correct_plug_type;
   1125	}
   1126
   1127	/* Find plug type */
   1128	output_mv = wcd_measure_adc_continuous(mbhc);
   1129	plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
   1130
   1131	/*
   1132	 * Report plug type if it is either headset or headphone
   1133	 * else start the 3 sec loop
   1134	 */
   1135	switch (plug_type) {
   1136	case MBHC_PLUG_TYPE_HEADPHONE:
   1137		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
   1138		break;
   1139	case MBHC_PLUG_TYPE_HEADSET:
   1140		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
   1141		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
   1142		wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
   1143		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
   1144		break;
   1145	default:
   1146		break;
   1147	}
   1148
   1149correct_plug_type:
   1150
   1151	/* Disable BCS slow insertion detection */
   1152	wcd_mbhc_bcs_enable(mbhc, plug_type, false);
   1153
   1154	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
   1155
   1156	while (!time_after(jiffies, timeout)) {
   1157		if (mbhc->hs_detect_work_stop) {
   1158			wcd_micbias_disable(mbhc);
   1159			goto exit;
   1160		}
   1161
   1162		msleep(180);
   1163		/*
   1164		 * Use ADC single mode to minimize the chance of missing out
   1165		 * btn press/release for HEADSET type during correct work.
   1166		 */
   1167		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
   1168		plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
   1169		is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
   1170
   1171		if (output_mv > hs_threshold && !is_spl_hs) {
   1172			is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
   1173			output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
   1174
   1175			if (is_spl_hs) {
   1176				hs_threshold *= wcd_mbhc_get_micbias(mbhc);
   1177				hs_threshold /= micbias_mv;
   1178			}
   1179		}
   1180
   1181		if ((output_mv <= hs_threshold) && !is_pa_on) {
   1182			/* Check for cross connection*/
   1183			cross_conn = wcd_check_cross_conn(mbhc);
   1184			if (cross_conn > 0) { /* cross-connection */
   1185				pt_gnd_mic_swap_cnt++;
   1186				if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
   1187					continue;
   1188				else
   1189					plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
   1190			} else if (!cross_conn) { /* no cross connection */
   1191				pt_gnd_mic_swap_cnt = 0;
   1192				plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
   1193				continue;
   1194			} else if (cross_conn < 0) /* Error */
   1195				continue;
   1196
   1197			if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
   1198				/* US_EU gpio present, flip switch */
   1199				if (mbhc->cfg->swap_gnd_mic) {
   1200					if (mbhc->cfg->swap_gnd_mic(component, true))
   1201						continue;
   1202				}
   1203			}
   1204		}
   1205
   1206		/* cable is extension cable */
   1207		if (output_mv > hs_threshold || mbhc->force_linein)
   1208			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
   1209	}
   1210
   1211	wcd_mbhc_bcs_enable(mbhc, plug_type, true);
   1212
   1213	if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
   1214		if (is_spl_hs)
   1215			plug_type = MBHC_PLUG_TYPE_HEADSET;
   1216		else
   1217			wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
   1218	}
   1219
   1220	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
   1221	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
   1222	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
   1223
   1224	/*
   1225	 * Set DETECTION_DONE bit for HEADSET
   1226	 * so that btn press/release interrupt can be generated.
   1227	 * For other plug type, clear the bit.
   1228	 */
   1229	if (plug_type == MBHC_PLUG_TYPE_HEADSET)
   1230		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
   1231	else
   1232		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
   1233
   1234	if (mbhc->mbhc_cb->mbhc_micbias_control)
   1235		wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
   1236
   1237exit:
   1238	if (mbhc->mbhc_cb->mbhc_micbias_control/* &&  !mbhc->micbias_enable*/)
   1239		mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
   1240
   1241	/*
   1242	 * If plug type is corrected from special headset to headphone,
   1243	 * clear the micbias enable flag, set micbias back to 1.8V and
   1244	 * disable micbias.
   1245	 */
   1246	if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
   1247		wcd_micbias_disable(mbhc);
   1248		/*
   1249		 * Enable ADC COMPLETE interrupt for HEADPHONE.
   1250		 * Btn release may happen after the correct work, ADC COMPLETE
   1251		 * interrupt needs to be captured to correct plug type.
   1252		 */
   1253		enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
   1254	}
   1255
   1256	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
   1257		mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
   1258
   1259	pm_runtime_mark_last_busy(component->dev);
   1260	pm_runtime_put_autosuspend(component->dev);
   1261}
   1262
   1263static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
   1264{
   1265	struct wcd_mbhc *mbhc = data;
   1266	unsigned long timeout;
   1267	int adc_threshold, output_mv, retry = 0;
   1268
   1269	mutex_lock(&mbhc->lock);
   1270	timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
   1271	adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
   1272
   1273	do {
   1274		retry++;
   1275		/*
   1276		 * read output_mv every 10ms to look for
   1277		 * any change in IN2_P
   1278		 */
   1279		usleep_range(10000, 10100);
   1280		output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
   1281
   1282		/* Check for fake removal */
   1283		if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
   1284			goto exit;
   1285	} while (!time_after(jiffies, timeout));
   1286
   1287	/*
   1288	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for
   1289	 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
   1290	 * follows ELEC_REM one when HEADPHONE is removed.
   1291	 */
   1292	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
   1293		mbhc->extn_cable_hph_rem = true;
   1294
   1295	wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
   1296	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
   1297	wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
   1298	wcd_mbhc_elec_hs_report_unplug(mbhc);
   1299	wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
   1300
   1301exit:
   1302	mutex_unlock(&mbhc->lock);
   1303	return IRQ_HANDLED;
   1304}
   1305
   1306static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
   1307{
   1308	struct wcd_mbhc *mbhc = data;
   1309	u8 clamp_state = 0;
   1310	u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
   1311
   1312	/*
   1313	 * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
   1314	 * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
   1315	 * when HEADPHONE is removed.
   1316	 */
   1317	if (mbhc->extn_cable_hph_rem == true) {
   1318		mbhc->extn_cable_hph_rem = false;
   1319		return IRQ_HANDLED;
   1320	}
   1321
   1322	do {
   1323		clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
   1324		if (clamp_state)
   1325			return IRQ_HANDLED;
   1326		/*
   1327		 * check clamp for 120ms but at 30ms chunks to leave
   1328		 * room for other interrupts to be processed
   1329		 */
   1330		usleep_range(30000, 30100);
   1331	} while (--clamp_retry);
   1332
   1333	/*
   1334	 * If current plug is headphone then there is no chance to
   1335	 * get ADC complete interrupt, so connected cable should be
   1336	 * headset not headphone.
   1337	 */
   1338	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
   1339		disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
   1340		wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
   1341		wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
   1342		return IRQ_HANDLED;
   1343	}
   1344
   1345	return IRQ_HANDLED;
   1346}
   1347
   1348int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,	uint32_t *zr)
   1349{
   1350	*zl = mbhc->zl;
   1351	*zr = mbhc->zr;
   1352
   1353	if (*zl && *zr)
   1354		return 0;
   1355	else
   1356		return -EINVAL;
   1357}
   1358EXPORT_SYMBOL(wcd_mbhc_get_impedance);
   1359
   1360void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
   1361{
   1362	mbhc->hph_type = hph_type;
   1363}
   1364EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
   1365
   1366int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
   1367{
   1368	return mbhc->hph_type;
   1369}
   1370EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
   1371
   1372int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
   1373		   struct snd_soc_jack *jack)
   1374{
   1375	if (!mbhc || !cfg || !jack)
   1376		return -EINVAL;
   1377
   1378	mbhc->cfg = cfg;
   1379	mbhc->jack = jack;
   1380
   1381	return wcd_mbhc_initialise(mbhc);
   1382}
   1383EXPORT_SYMBOL(wcd_mbhc_start);
   1384
   1385void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
   1386{
   1387	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
   1388	mbhc->hph_status = 0;
   1389	disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
   1390	disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
   1391}
   1392EXPORT_SYMBOL(wcd_mbhc_stop);
   1393
   1394int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
   1395{
   1396	struct device_node *np = dev->of_node;
   1397	int ret, i, microvolt;
   1398
   1399	if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
   1400		cfg->hphl_swh = false;
   1401	else
   1402		cfg->hphl_swh = true;
   1403
   1404	if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
   1405		cfg->gnd_swh = false;
   1406	else
   1407		cfg->gnd_swh = true;
   1408
   1409	ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
   1410				   &microvolt);
   1411	if (ret)
   1412		dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
   1413	else
   1414		cfg->hs_thr = microvolt/1000;
   1415
   1416	ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
   1417				   &microvolt);
   1418	if (ret)
   1419		dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt	entry\n");
   1420	else
   1421		cfg->hph_thr = microvolt/1000;
   1422
   1423	ret = of_property_read_u32_array(np,
   1424					 "qcom,mbhc-buttons-vthreshold-microvolt",
   1425					 &cfg->btn_high[0],
   1426					 WCD_MBHC_DEF_BUTTONS);
   1427	if (ret)
   1428		dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
   1429
   1430	for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
   1431		if (ret) /* default voltage */
   1432			cfg->btn_high[i] = 500000;
   1433		else
   1434			/* Micro to Milli Volts */
   1435			cfg->btn_high[i] = cfg->btn_high[i]/1000;
   1436	}
   1437
   1438	return 0;
   1439}
   1440EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
   1441
   1442struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
   1443			       const struct wcd_mbhc_cb *mbhc_cb,
   1444			       const struct wcd_mbhc_intr *intr_ids,
   1445			       struct wcd_mbhc_field *fields,
   1446			       bool impedance_det_en)
   1447{
   1448	struct device *dev = component->dev;
   1449	struct wcd_mbhc *mbhc;
   1450	int ret;
   1451
   1452	if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
   1453		dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
   1454		return ERR_PTR(-EINVAL);
   1455	}
   1456
   1457	mbhc = devm_kzalloc(dev, sizeof(*mbhc), GFP_KERNEL);
   1458	if (!mbhc)
   1459		return ERR_PTR(-ENOMEM);
   1460
   1461	mbhc->component = component;
   1462	mbhc->dev = dev;
   1463	mbhc->intr_ids = intr_ids;
   1464	mbhc->mbhc_cb = mbhc_cb;
   1465	mbhc->fields = fields;
   1466	mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
   1467
   1468	if (mbhc_cb->compute_impedance)
   1469		mbhc->impedance_detect = impedance_det_en;
   1470
   1471	INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
   1472
   1473	mutex_init(&mbhc->lock);
   1474
   1475	INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
   1476
   1477	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_sw_intr, NULL,
   1478					wcd_mbhc_mech_plug_detect_irq,
   1479					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1480					"mbhc sw intr", mbhc);
   1481	if (ret)
   1482		goto err;
   1483
   1484	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_press_intr, NULL,
   1485					wcd_mbhc_btn_press_handler,
   1486					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1487					"Button Press detect", mbhc);
   1488	if (ret)
   1489		goto err;
   1490
   1491	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_release_intr, NULL,
   1492					wcd_mbhc_btn_release_handler,
   1493					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1494					"Button Release detect", mbhc);
   1495	if (ret)
   1496		goto err;
   1497
   1498	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
   1499					wcd_mbhc_adc_hs_ins_irq,
   1500					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1501					"Elect Insert", mbhc);
   1502	if (ret)
   1503		goto err;
   1504
   1505	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
   1506
   1507	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
   1508					wcd_mbhc_adc_hs_rem_irq,
   1509					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1510					"Elect Remove", mbhc);
   1511	if (ret)
   1512		goto err;
   1513
   1514	disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
   1515
   1516	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_left_ocp, NULL,
   1517					wcd_mbhc_hphl_ocp_irq,
   1518					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1519					"HPH_L OCP detect", mbhc);
   1520	if (ret)
   1521		goto err;
   1522
   1523	ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_right_ocp, NULL,
   1524					wcd_mbhc_hphr_ocp_irq,
   1525					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
   1526					"HPH_R OCP detect", mbhc);
   1527	if (ret)
   1528		goto err;
   1529
   1530	return mbhc;
   1531err:
   1532	dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
   1533
   1534	return ERR_PTR(ret);
   1535}
   1536EXPORT_SYMBOL(wcd_mbhc_init);
   1537
   1538void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
   1539{
   1540	mutex_lock(&mbhc->lock);
   1541	wcd_cancel_hs_detect_plug(mbhc,	&mbhc->correct_plug_swch);
   1542	mutex_unlock(&mbhc->lock);
   1543}
   1544EXPORT_SYMBOL(wcd_mbhc_deinit);
   1545
   1546static int __init mbhc_init(void)
   1547{
   1548	return 0;
   1549}
   1550
   1551static void __exit mbhc_exit(void)
   1552{
   1553}
   1554
   1555module_init(mbhc_init);
   1556module_exit(mbhc_exit);
   1557
   1558MODULE_DESCRIPTION("wcd MBHC v2 module");
   1559MODULE_LICENSE("GPL");