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