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 GND_MIC_USBC_SWAP_THRESHOLD	2
20#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS	100
21#define HPHL_CROSS_CONN_THRESHOLD	100
22#define HS_VREF_MIN_VAL			1400
23#define FAKE_REM_RETRY_ATTEMPTS		3
24#define WCD_MBHC_ADC_HS_THRESHOLD_MV	1700
25#define WCD_MBHC_ADC_HPH_THRESHOLD_MV	75
26#define WCD_MBHC_ADC_MICBIAS_MV		1800
27#define WCD_MBHC_FAKE_INS_RETRY		4
28
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	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 codec
125	 * drivers itself and micbias is not needed for regular
126	 * plug type detection. So if micbias_control callback function
127	 * 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 detect
198		 * button press/release events
199		 */
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		else
219			/* 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		else
231			/* 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		else
241			/* 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		else
251			/* 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		else
261			/* 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 and
298	 * report release if we reported button press
299	 */
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 headphone
338	 * 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 lineout
381	 * as during playback pa is on and impedance values
382	 * will not be correct resulting in lineout detected
383	 * 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	else
414		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	else
776		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	else
788		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	else
802		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	else
808		/* 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 vout
845		 * micbias = 1.0V + VOUT_CTL * 50mV
846		 */
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 ADC
855	 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
856	 */
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		else
1019			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		else
1037			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	else
1097		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		else
1111			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 headphone
1196	 * else start the 3 sec loop
1197	 */
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 out
1228		 * 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				else
1252					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		else
1280			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 HEADSET
1289	 * 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	else
1295		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 and
1307	 * 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 COMPLETE
1314		 * 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 for
1340		 * any change in IN2_P
1341		 */
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 for
1352	 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
1353	 * 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 one
1378	 * 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 leave
1391		 * room for other interrupts to be processed
1392		 */
1393		usleep_range(30000, 30100);
1394	} while (--clamp_retry);
1395
1396	/*
1397	 * If current plug is headphone then there is no chance to
1398	 * get ADC complete interrupt, so connected cable should be
1399	 * 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	else
1419		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	else
1465		cfg->hphl_swh = true;
1466
1467	if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
1468		cfg->gnd_swh = false;
1469	else
1470		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	else
1477		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	else
1484		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		else
1497			/* 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			       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