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
36 enum 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
46 struct wcd_mbhc {
47 struct device *dev;
48 struct snd_soc_component *component;
49 struct snd_soc_jack *jack;
50 struct wcd_mbhc_config *cfg;
51 const struct wcd_mbhc_cb *mbhc_cb;
52 const struct wcd_mbhc_intr *intr_ids;
53 const struct wcd_mbhc_field *fields;
54 /* Delayed work to report long button press */
55 struct delayed_work mbhc_btn_dwork;
56 /* Work to handle plug report */
57 struct work_struct mbhc_plug_detect_work;
58 /* Work to correct accessory type */
59 struct work_struct correct_plug_swch;
60 struct mutex lock;
61 int buttons_pressed;
62 u32 hph_status; /* track headhpone status */
63 u8 current_plug;
64 unsigned int swap_thr;
65 bool is_btn_press;
66 bool in_swch_irq_handler;
67 bool hs_detect_work_stop;
68 bool is_hs_recording;
69 bool extn_cable_hph_rem;
70 bool force_linein;
71 bool impedance_detect;
72 unsigned long event_state;
73 unsigned long jiffies_atreport;
74 /* impedance of hphl and hphr */
75 uint32_t zl, zr;
76 /* Holds type of Headset - Mono/Stereo */
77 enum wcd_mbhc_hph_type hph_type;
78 /* Holds mbhc detection method - ADC/Legacy */
79 int mbhc_detection_logic;
80 };
81
wcd_mbhc_write_field(const struct wcd_mbhc * mbhc,int field,int val)82 static 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
wcd_mbhc_read_field(const struct wcd_mbhc * mbhc,int field)93 static 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
wcd_program_hs_vref(struct wcd_mbhc * mbhc)103 static 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
wcd_program_btn_threshold(const struct wcd_mbhc * mbhc,bool micbias)110 static 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
wcd_mbhc_curr_micbias_control(const struct wcd_mbhc * mbhc,const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)119 static 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
wcd_mbhc_event_notify(struct wcd_mbhc * mbhc,unsigned long event)165 int 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 }
269 EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
270
wcd_cancel_btn_work(struct wcd_mbhc * mbhc)271 static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
272 {
273 return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
274 }
275
wcd_micbias_disable(struct wcd_mbhc * mbhc)276 static 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
wcd_mbhc_report_plug_removal(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)292 static 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
wcd_mbhc_compute_impedance(struct wcd_mbhc * mbhc)313 static 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
wcd_mbhc_report_plug_insertion(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)331 static 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
wcd_mbhc_report_plug(struct wcd_mbhc * mbhc,int insertion,enum snd_jack_types jack_type)405 static 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
wcd_cancel_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)418 static 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
wcd_mbhc_cancel_pending_work(struct wcd_mbhc * mbhc)427 static 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
wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc * mbhc)435 static 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
wcd_mbhc_find_plug_and_report(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)455 static 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
wcd_schedule_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)487 static 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
wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc * mbhc)495 static 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
mbhc_plug_detect_fn(struct work_struct * work)513 static 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
579 exit:
580 mbhc->in_swch_irq_handler = false;
581 mutex_unlock(&mbhc->lock);
582 }
583
wcd_mbhc_mech_plug_detect_irq(int irq,void * data)584 static 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
wcd_mbhc_typec_report_unplug(struct wcd_mbhc * mbhc)594 int 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 }
610 EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);
611
wcd_mbhc_typec_report_plug(struct wcd_mbhc * mbhc)612 int 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 }
625 EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);
626
wcd_mbhc_get_button_mask(struct wcd_mbhc * mbhc)627 static 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
wcd_btn_long_press_fn(struct work_struct * work)660 static 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
wcd_mbhc_btn_press_handler(int irq,void * data)670 static 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");
697 done:
698 mutex_unlock(&mbhc->lock);
699 return IRQ_HANDLED;
700 }
701
wcd_mbhc_btn_release_handler(int irq,void * data)702 static 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;
728 exit:
729 mutex_unlock(&mbhc->lock);
730
731 return IRQ_HANDLED;
732 }
733
wcd_mbhc_hph_ocp_irq(struct wcd_mbhc * mbhc,bool hphr)734 static 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
wcd_mbhc_hphl_ocp_irq(int irq,void * data)747 static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
748 {
749 return wcd_mbhc_hph_ocp_irq(data, false);
750 }
751
wcd_mbhc_hphr_ocp_irq(int irq,void * data)752 static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
753 {
754 return wcd_mbhc_hph_ocp_irq(data, true);
755 }
756
wcd_mbhc_initialise(struct wcd_mbhc * mbhc)757 static 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_put_autosuspend(component->dev);
829
830 return 0;
831 }
832
wcd_mbhc_get_micbias(struct wcd_mbhc * mbhc)833 static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
834 {
835 int micbias = 0;
836
837 if (mbhc->mbhc_cb->get_micbias_val) {
838 mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
839 } else {
840 u8 vout_ctl = 0;
841 /* Read MBHC Micbias (Mic Bias2) voltage */
842 vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
843 /* Formula for getting micbias from vout
844 * micbias = 1.0V + VOUT_CTL * 50mV
845 */
846 micbias = 1000 + (vout_ctl * 50);
847 }
848 return micbias;
849 }
850
wcd_get_voltage_from_adc(u8 val,int micbias)851 static int wcd_get_voltage_from_adc(u8 val, int micbias)
852 {
853 /* Formula for calculating voltage from ADC
854 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
855 */
856 return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
857 }
858
wcd_measure_adc_continuous(struct wcd_mbhc * mbhc)859 static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
860 {
861 u8 adc_result;
862 int output_mv;
863 int retry = 3;
864 u8 adc_en;
865
866 /* Pre-requisites for ADC continuous measurement */
867 /* Read legacy electircal detection and disable */
868 wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
869 /* Set ADC to continuous measurement */
870 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
871 /* Read ADC Enable bit to restore after adc measurement */
872 adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
873 /* Disable ADC_ENABLE bit */
874 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
875 /* Disable MBHC FSM */
876 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
877 /* Set the MUX selection to IN2P */
878 wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
879 /* Enable MBHC FSM */
880 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
881 /* Enable ADC_ENABLE bit */
882 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
883
884 while (retry--) {
885 /* wait for 3 msec before reading ADC result */
886 usleep_range(3000, 3100);
887 adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
888 }
889
890 /* Restore ADC Enable */
891 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
892 /* Get voltage from ADC result */
893 output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
894
895 return output_mv;
896 }
897
wcd_measure_adc_once(struct wcd_mbhc * mbhc,int mux_ctl)898 static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
899 {
900 struct device *dev = mbhc->dev;
901 u8 adc_timeout = 0;
902 u8 adc_complete = 0;
903 u8 adc_result;
904 int retry = 6;
905 int ret;
906 int output_mv = 0;
907 u8 adc_en;
908
909 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
910 /* Read ADC Enable bit to restore after adc measurement */
911 adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
912 /* Trigger ADC one time measurement */
913 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
914 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
915 /* Set the appropriate MUX selection */
916 wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
917 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
918 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
919
920 while (retry--) {
921 /* wait for 600usec to get adc results */
922 usleep_range(600, 610);
923
924 /* check for ADC Timeout */
925 adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
926 if (adc_timeout)
927 continue;
928
929 /* Read ADC complete bit */
930 adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
931 if (!adc_complete)
932 continue;
933
934 /* Read ADC result */
935 adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
936
937 /* Get voltage from ADC result */
938 output_mv = wcd_get_voltage_from_adc(adc_result,
939 wcd_mbhc_get_micbias(mbhc));
940 break;
941 }
942
943 /* Restore ADC Enable */
944 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
945
946 if (retry <= 0) {
947 dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
948 __func__, adc_complete, adc_timeout);
949 ret = -EINVAL;
950 } else {
951 ret = output_mv;
952 }
953
954 return ret;
955 }
956
957 /* To determine if cross connection occurred */
wcd_check_cross_conn(struct wcd_mbhc * mbhc)958 static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
959 {
960 u8 adc_mode, elect_ctl, adc_en, fsm_en;
961 int hphl_adc_res, hphr_adc_res;
962 bool is_cross_conn = false;
963
964 /* If PA is enabled, dont check for cross-connection */
965 if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
966 return -EINVAL;
967
968 /* Read legacy electircal detection and disable */
969 elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
970 wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
971
972 /* Read and set ADC to single measurement */
973 adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
974 /* Read ADC Enable bit to restore after adc measurement */
975 adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
976 /* Read FSM status */
977 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
978
979 /* Get adc result for HPH L */
980 hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
981 if (hphl_adc_res < 0)
982 return hphl_adc_res;
983
984 /* Get adc result for HPH R in mV */
985 hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
986 if (hphr_adc_res < 0)
987 return hphr_adc_res;
988
989 if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
990 hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
991 is_cross_conn = true;
992
993 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
994 /* Set the MUX selection to Auto */
995 wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
996 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
997 /* Restore ADC Enable */
998 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
999 /* Restore ADC mode */
1000 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
1001 /* Restore FSM state */
1002 wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
1003 /* Restore electrical detection */
1004 wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
1005
1006 return is_cross_conn;
1007 }
1008
wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc * mbhc)1009 static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
1010 {
1011 int hs_threshold, micbias_mv;
1012
1013 micbias_mv = wcd_mbhc_get_micbias(mbhc);
1014 if (mbhc->cfg->hs_thr) {
1015 if (mbhc->cfg->micb_mv == micbias_mv)
1016 hs_threshold = mbhc->cfg->hs_thr;
1017 else
1018 hs_threshold = (mbhc->cfg->hs_thr *
1019 micbias_mv) / mbhc->cfg->micb_mv;
1020 } else {
1021 hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
1022 micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
1023 }
1024 return hs_threshold;
1025 }
1026
wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc * mbhc)1027 static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
1028 {
1029 int hph_threshold, micbias_mv;
1030
1031 micbias_mv = wcd_mbhc_get_micbias(mbhc);
1032 if (mbhc->cfg->hph_thr) {
1033 if (mbhc->cfg->micb_mv == micbias_mv)
1034 hph_threshold = mbhc->cfg->hph_thr;
1035 else
1036 hph_threshold = (mbhc->cfg->hph_thr *
1037 micbias_mv) / mbhc->cfg->micb_mv;
1038 } else {
1039 hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
1040 micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
1041 }
1042 return hph_threshold;
1043 }
1044
wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)1045 static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
1046 enum wcd_mbhc_plug_type plug_type)
1047 {
1048 bool micbias2 = false;
1049
1050 switch (plug_type) {
1051 case MBHC_PLUG_TYPE_HEADPHONE:
1052 wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1053 break;
1054 case MBHC_PLUG_TYPE_HEADSET:
1055 if (mbhc->mbhc_cb->micbias_enable_status)
1056 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
1057 MIC_BIAS_2);
1058
1059 if (!mbhc->is_hs_recording && !micbias2)
1060 wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1061 break;
1062 default:
1063 wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1064 break;
1065
1066 }
1067 }
1068
wcd_mbhc_bcs_enable(struct wcd_mbhc * mbhc,int plug_type,bool enable)1069 static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
1070 {
1071 switch (plug_type) {
1072 case MBHC_PLUG_TYPE_HEADSET:
1073 case MBHC_PLUG_TYPE_HEADPHONE:
1074 if (mbhc->mbhc_cb->bcs_enable)
1075 mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
1076 break;
1077 default:
1078 break;
1079 }
1080 }
1081
wcd_mbhc_get_plug_from_adc(struct wcd_mbhc * mbhc,int adc_result)1082 static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
1083
1084 {
1085 enum wcd_mbhc_plug_type plug_type;
1086 u32 hph_thr, hs_thr;
1087
1088 hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
1089 hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
1090
1091 if (adc_result < hph_thr)
1092 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
1093 else if (adc_result > hs_thr)
1094 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
1095 else
1096 plug_type = MBHC_PLUG_TYPE_HEADSET;
1097
1098 return plug_type;
1099 }
1100
wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc * mbhc)1101 static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
1102 {
1103 int hs_threshold, micbias_mv;
1104
1105 micbias_mv = wcd_mbhc_get_micbias(mbhc);
1106 if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
1107 if (mbhc->cfg->micb_mv == micbias_mv)
1108 hs_threshold = mbhc->cfg->hs_thr;
1109 else
1110 hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
1111 } else {
1112 hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
1113 WCD_MBHC_ADC_MICBIAS_MV);
1114 }
1115 return hs_threshold;
1116 }
1117
wcd_mbhc_check_for_spl_headset(struct wcd_mbhc * mbhc)1118 static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
1119 {
1120 bool is_spl_hs = false;
1121 int output_mv, hs_threshold, hph_threshold;
1122
1123 if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
1124 return false;
1125
1126 /* Bump up MIC_BIAS2 to 2.7V */
1127 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
1128 usleep_range(10000, 10100);
1129
1130 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
1131 hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
1132 hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
1133
1134 if (!(output_mv > hs_threshold || output_mv < hph_threshold))
1135 is_spl_hs = true;
1136
1137 /* Back MIC_BIAS2 to 1.8v if the type is not special headset */
1138 if (!is_spl_hs) {
1139 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
1140 /* Add 10ms delay for micbias to settle */
1141 usleep_range(10000, 10100);
1142 }
1143
1144 return is_spl_hs;
1145 }
1146
wcd_correct_swch_plug(struct work_struct * work)1147 static void wcd_correct_swch_plug(struct work_struct *work)
1148 {
1149 struct wcd_mbhc *mbhc;
1150 struct snd_soc_component *component;
1151 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
1152 unsigned long timeout;
1153 int pt_gnd_mic_swap_cnt = 0;
1154 int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
1155 bool is_spl_hs = false;
1156 bool is_pa_on;
1157 int ret;
1158
1159 mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
1160 component = mbhc->component;
1161
1162 ret = pm_runtime_get_sync(component->dev);
1163 if (ret < 0 && ret != -EACCES) {
1164 dev_err_ratelimited(component->dev,
1165 "pm_runtime_get_sync failed in %s, ret %d\n",
1166 __func__, ret);
1167 pm_runtime_put_noidle(component->dev);
1168 return;
1169 }
1170 micbias_mv = wcd_mbhc_get_micbias(mbhc);
1171 hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
1172
1173 /* Mask ADC COMPLETE interrupt */
1174 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
1175
1176 /* Check for cross connection */
1177 do {
1178 cross_conn = wcd_check_cross_conn(mbhc);
1179 try++;
1180 } while (try < mbhc->swap_thr);
1181
1182 if (cross_conn > 0) {
1183 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
1184 dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
1185 plug_type);
1186 goto correct_plug_type;
1187 }
1188
1189 /* Find plug type */
1190 output_mv = wcd_measure_adc_continuous(mbhc);
1191 plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
1192
1193 /*
1194 * Report plug type if it is either headset or headphone
1195 * else start the 3 sec loop
1196 */
1197 switch (plug_type) {
1198 case MBHC_PLUG_TYPE_HEADPHONE:
1199 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
1200 break;
1201 case MBHC_PLUG_TYPE_HEADSET:
1202 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
1203 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
1204 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
1205 wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
1206 break;
1207 default:
1208 break;
1209 }
1210
1211 correct_plug_type:
1212
1213 /* Disable BCS slow insertion detection */
1214 wcd_mbhc_bcs_enable(mbhc, plug_type, false);
1215
1216 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
1217
1218 while (!time_after(jiffies, timeout)) {
1219 if (mbhc->hs_detect_work_stop) {
1220 wcd_micbias_disable(mbhc);
1221 goto exit;
1222 }
1223
1224 msleep(180);
1225 /*
1226 * Use ADC single mode to minimize the chance of missing out
1227 * btn press/release for HEADSET type during correct work.
1228 */
1229 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
1230 plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
1231 is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
1232
1233 if (output_mv > hs_threshold && !is_spl_hs) {
1234 is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
1235 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
1236
1237 if (is_spl_hs) {
1238 hs_threshold *= wcd_mbhc_get_micbias(mbhc);
1239 hs_threshold /= micbias_mv;
1240 }
1241 }
1242
1243 if ((output_mv <= hs_threshold) && !is_pa_on) {
1244 /* Check for cross connection*/
1245 cross_conn = wcd_check_cross_conn(mbhc);
1246 if (cross_conn > 0) { /* cross-connection */
1247 pt_gnd_mic_swap_cnt++;
1248 if (pt_gnd_mic_swap_cnt < mbhc->swap_thr)
1249 continue;
1250 else
1251 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
1252 } else if (!cross_conn) { /* no cross connection */
1253 pt_gnd_mic_swap_cnt = 0;
1254 plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
1255 continue;
1256 } else /* Error if (cross_conn < 0) */
1257 continue;
1258
1259 if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
1260 /* US_EU gpio present, flip switch */
1261 if (mbhc->cfg->swap_gnd_mic) {
1262 if (mbhc->cfg->swap_gnd_mic(component))
1263 continue;
1264 }
1265 }
1266 }
1267
1268 /* cable is extension cable */
1269 if (output_mv > hs_threshold || mbhc->force_linein)
1270 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
1271 }
1272
1273 wcd_mbhc_bcs_enable(mbhc, plug_type, true);
1274
1275 if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
1276 if (is_spl_hs)
1277 plug_type = MBHC_PLUG_TYPE_HEADSET;
1278 else
1279 wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
1280 }
1281
1282 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
1283 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
1284 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
1285
1286 /*
1287 * Set DETECTION_DONE bit for HEADSET
1288 * so that btn press/release interrupt can be generated.
1289 * For other plug type, clear the bit.
1290 */
1291 if (plug_type == MBHC_PLUG_TYPE_HEADSET)
1292 wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
1293 else
1294 wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
1295
1296 if (mbhc->mbhc_cb->mbhc_micbias_control)
1297 wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
1298
1299 exit:
1300 if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/)
1301 mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
1302
1303 /*
1304 * If plug type is corrected from special headset to headphone,
1305 * clear the micbias enable flag, set micbias back to 1.8V and
1306 * disable micbias.
1307 */
1308 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
1309 wcd_micbias_disable(mbhc);
1310 /*
1311 * Enable ADC COMPLETE interrupt for HEADPHONE.
1312 * Btn release may happen after the correct work, ADC COMPLETE
1313 * interrupt needs to be captured to correct plug type.
1314 */
1315 enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
1316 }
1317
1318 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
1319 mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
1320
1321 pm_runtime_put_autosuspend(component->dev);
1322 }
1323
wcd_mbhc_adc_hs_rem_irq(int irq,void * data)1324 static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
1325 {
1326 struct wcd_mbhc *mbhc = data;
1327 unsigned long timeout;
1328 int adc_threshold, output_mv, retry = 0;
1329
1330 mutex_lock(&mbhc->lock);
1331 timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
1332 adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
1333
1334 do {
1335 retry++;
1336 /*
1337 * read output_mv every 10ms to look for
1338 * any change in IN2_P
1339 */
1340 usleep_range(10000, 10100);
1341 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
1342
1343 /* Check for fake removal */
1344 if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
1345 goto exit;
1346 } while (!time_after(jiffies, timeout));
1347
1348 /*
1349 * ADC COMPLETE and ELEC_REM interrupts are both enabled for
1350 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
1351 * follows ELEC_REM one when HEADPHONE is removed.
1352 */
1353 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
1354 mbhc->extn_cable_hph_rem = true;
1355
1356 wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
1357 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
1358 wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
1359 wcd_mbhc_elec_hs_report_unplug(mbhc);
1360 wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1361
1362 exit:
1363 mutex_unlock(&mbhc->lock);
1364 return IRQ_HANDLED;
1365 }
1366
wcd_mbhc_adc_hs_ins_irq(int irq,void * data)1367 static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
1368 {
1369 struct wcd_mbhc *mbhc = data;
1370 u8 clamp_state;
1371 u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
1372
1373 /*
1374 * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
1375 * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
1376 * when HEADPHONE is removed.
1377 */
1378 if (mbhc->extn_cable_hph_rem == true) {
1379 mbhc->extn_cable_hph_rem = false;
1380 return IRQ_HANDLED;
1381 }
1382
1383 do {
1384 clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
1385 if (clamp_state)
1386 return IRQ_HANDLED;
1387 /*
1388 * check clamp for 120ms but at 30ms chunks to leave
1389 * room for other interrupts to be processed
1390 */
1391 usleep_range(30000, 30100);
1392 } while (--clamp_retry);
1393
1394 /*
1395 * If current plug is headphone then there is no chance to
1396 * get ADC complete interrupt, so connected cable should be
1397 * headset not headphone.
1398 */
1399 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
1400 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
1401 wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
1402 wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
1403 return IRQ_HANDLED;
1404 }
1405
1406 return IRQ_HANDLED;
1407 }
1408
wcd_mbhc_get_impedance(struct wcd_mbhc * mbhc,uint32_t * zl,uint32_t * zr)1409 int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr)
1410 {
1411 *zl = mbhc->zl;
1412 *zr = mbhc->zr;
1413
1414 if (*zl && *zr)
1415 return 0;
1416 else
1417 return -EINVAL;
1418 }
1419 EXPORT_SYMBOL(wcd_mbhc_get_impedance);
1420
wcd_mbhc_set_hph_type(struct wcd_mbhc * mbhc,int hph_type)1421 void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
1422 {
1423 mbhc->hph_type = hph_type;
1424 }
1425 EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
1426
wcd_mbhc_get_hph_type(struct wcd_mbhc * mbhc)1427 int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
1428 {
1429 return mbhc->hph_type;
1430 }
1431 EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
1432
wcd_mbhc_start(struct wcd_mbhc * mbhc,struct wcd_mbhc_config * cfg,struct snd_soc_jack * jack)1433 int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
1434 struct snd_soc_jack *jack)
1435 {
1436 if (!mbhc || !cfg || !jack)
1437 return -EINVAL;
1438
1439 mbhc->cfg = cfg;
1440 mbhc->jack = jack;
1441
1442 return wcd_mbhc_initialise(mbhc);
1443 }
1444 EXPORT_SYMBOL(wcd_mbhc_start);
1445
wcd_mbhc_stop(struct wcd_mbhc * mbhc)1446 void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
1447 {
1448 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
1449 mbhc->hph_status = 0;
1450 disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
1451 disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
1452 }
1453 EXPORT_SYMBOL(wcd_mbhc_stop);
1454
wcd_dt_parse_mbhc_data(struct device * dev,struct wcd_mbhc_config * cfg)1455 int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
1456 {
1457 struct device_node *np = dev->of_node;
1458 int ret, i, microvolt;
1459
1460 if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
1461 cfg->hphl_swh = false;
1462 else
1463 cfg->hphl_swh = true;
1464
1465 if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
1466 cfg->gnd_swh = false;
1467 else
1468 cfg->gnd_swh = true;
1469
1470 ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
1471 µvolt);
1472 if (ret)
1473 dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
1474 else
1475 cfg->hs_thr = microvolt/1000;
1476
1477 ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
1478 µvolt);
1479 if (ret)
1480 dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n");
1481 else
1482 cfg->hph_thr = microvolt/1000;
1483
1484 ret = of_property_read_u32_array(np,
1485 "qcom,mbhc-buttons-vthreshold-microvolt",
1486 &cfg->btn_high[0],
1487 WCD_MBHC_DEF_BUTTONS);
1488 if (ret)
1489 dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
1490
1491 for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
1492 if (ret) /* default voltage */
1493 cfg->btn_high[i] = 500000;
1494 else
1495 /* Micro to Milli Volts */
1496 cfg->btn_high[i] = cfg->btn_high[i]/1000;
1497 }
1498
1499 return 0;
1500 }
1501 EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
1502
wcd_mbhc_init(struct snd_soc_component * component,const struct wcd_mbhc_cb * mbhc_cb,const struct wcd_mbhc_intr * intr_ids,const struct wcd_mbhc_field * fields,bool impedance_det_en)1503 struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
1504 const struct wcd_mbhc_cb *mbhc_cb,
1505 const struct wcd_mbhc_intr *intr_ids,
1506 const struct wcd_mbhc_field *fields,
1507 bool impedance_det_en)
1508 {
1509 struct device *dev = component->dev;
1510 struct wcd_mbhc *mbhc;
1511 int ret;
1512
1513 if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
1514 dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
1515 return ERR_PTR(-EINVAL);
1516 }
1517
1518 mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
1519 if (!mbhc)
1520 return ERR_PTR(-ENOMEM);
1521
1522 mbhc->component = component;
1523 mbhc->dev = dev;
1524 mbhc->intr_ids = intr_ids;
1525 mbhc->mbhc_cb = mbhc_cb;
1526 mbhc->fields = fields;
1527 mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
1528
1529 if (mbhc_cb->compute_impedance)
1530 mbhc->impedance_detect = impedance_det_en;
1531
1532 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
1533
1534 mutex_init(&mbhc->lock);
1535
1536 INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
1537 INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn);
1538
1539 ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
1540 wcd_mbhc_mech_plug_detect_irq,
1541 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1542 "mbhc sw intr", mbhc);
1543 if (ret)
1544 goto err_free_mbhc;
1545
1546 ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
1547 wcd_mbhc_btn_press_handler,
1548 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1549 "Button Press detect", mbhc);
1550 if (ret)
1551 goto err_free_sw_intr;
1552
1553 ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
1554 wcd_mbhc_btn_release_handler,
1555 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1556 "Button Release detect", mbhc);
1557 if (ret)
1558 goto err_free_btn_press_intr;
1559
1560 ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
1561 wcd_mbhc_adc_hs_ins_irq,
1562 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1563 "Elect Insert", mbhc);
1564 if (ret)
1565 goto err_free_btn_release_intr;
1566
1567 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
1568
1569 ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
1570 wcd_mbhc_adc_hs_rem_irq,
1571 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1572 "Elect Remove", mbhc);
1573 if (ret)
1574 goto err_free_hs_ins_intr;
1575
1576 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
1577
1578 ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
1579 wcd_mbhc_hphl_ocp_irq,
1580 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1581 "HPH_L OCP detect", mbhc);
1582 if (ret)
1583 goto err_free_hs_rem_intr;
1584
1585 ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
1586 wcd_mbhc_hphr_ocp_irq,
1587 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
1588 "HPH_R OCP detect", mbhc);
1589 if (ret)
1590 goto err_free_hph_left_ocp;
1591
1592 return mbhc;
1593
1594 err_free_hph_left_ocp:
1595 free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1596 err_free_hs_rem_intr:
1597 free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1598 err_free_hs_ins_intr:
1599 free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1600 err_free_btn_release_intr:
1601 free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1602 err_free_btn_press_intr:
1603 free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1604 err_free_sw_intr:
1605 free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1606 err_free_mbhc:
1607 kfree(mbhc);
1608
1609 dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
1610
1611 return ERR_PTR(ret);
1612 }
1613 EXPORT_SYMBOL(wcd_mbhc_init);
1614
wcd_mbhc_deinit(struct wcd_mbhc * mbhc)1615 void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
1616 {
1617 free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
1618 free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1619 free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1620 free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1621 free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1622 free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1623 free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1624
1625 mutex_lock(&mbhc->lock);
1626 wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
1627 cancel_work_sync(&mbhc->mbhc_plug_detect_work);
1628 mutex_unlock(&mbhc->lock);
1629
1630 kfree(mbhc);
1631 }
1632 EXPORT_SYMBOL(wcd_mbhc_deinit);
1633
mbhc_init(void)1634 static int __init mbhc_init(void)
1635 {
1636 return 0;
1637 }
1638
mbhc_exit(void)1639 static void __exit mbhc_exit(void)
1640 {
1641 }
1642
1643 module_init(mbhc_init);
1644 module_exit(mbhc_exit);
1645
1646 MODULE_DESCRIPTION("wcd MBHC v2 module");
1647 MODULE_LICENSE("GPL");
1648