brintos

brintos / linux-shallow public Read only

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