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

source code of linux/sound/soc/codecs/wcd-mbhc-v2.c