blob: 920796f2e39dbdd08a8248dd51ebeba243e81cad [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/slab.h>
15#include <linux/of_gpio.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/list.h>
21#include <linux/bitops.h>
22#include <linux/delay.h>
23#include <linux/pm_runtime.h>
24#include <linux/kernel.h>
25#include <linux/input.h>
26#include <linux/firmware.h>
27#include <linux/completion.h>
28#include <sound/soc.h>
29#include <sound/jack.h>
30#include "wcd-mbhc-adc.h"
31#include "wcd-mbhc-v2.h"
32
33#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700
34#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75
35#define WCD_MBHC_ADC_MICBIAS_MV 1800
36
37static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
38{
39 int micbias = 0;
40 u8 vout_ctl = 0;
41
42 /* Read MBHC Micbias (Mic Bias2) voltage */
43 WCD_MBHC_REG_READ(WCD_MBHC_MICB2_VOUT, vout_ctl);
44
45 /* Formula for getting micbias from vout
46 * micbias = 1.0V + VOUT_CTL * 50mV
47 */
48 micbias = 1000 + (vout_ctl * 50);
49 pr_debug("%s: vout_ctl: %d, micbias: %d\n",
50 __func__, vout_ctl, micbias);
51
52 return micbias;
53}
54
55static int wcd_get_voltage_from_adc(u8 val, int micbias)
56{
57 /* Formula for calculating voltage from ADC
58 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
59 */
60 return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
61}
62
63static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
64{
65 u8 adc_result = 0;
66 int output_mv = 0;
67 int retry = 3;
68 u8 adc_en = 0;
69
70 pr_debug("%s: enter\n", __func__);
71
72 /* Pre-requisites for ADC continuous measurement */
73 /* Read legacy electircal detection and disable */
74 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
75 /* Set ADC to continuous measurement */
76 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 1);
77 /* Read ADC Enable bit to restore after adc measurement */
78 WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en);
79 /* Disable ADC_ENABLE bit */
80 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
81 /* Disable MBHC FSM */
82 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
83 /* Set the MUX selection to IN2P */
84 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
85 /* Enable MBHC FSM */
86 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
87 /* Enable ADC_ENABLE bit */
88 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1);
89
90 while (retry--) {
91 /* wait for 3 msec before reading ADC result */
92 usleep_range(3000, 3100);
93
94 /* Read ADC result */
95 WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result);
96 }
97
98 /* Restore ADC Enable */
99 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en);
100 /* Get voltage from ADC result */
101 output_mv = wcd_get_voltage_from_adc(adc_result,
102 wcd_mbhc_get_micbias(mbhc));
103 pr_debug("%s: adc_result: 0x%x, output_mv: %d\n",
104 __func__, adc_result, output_mv);
105
106 return output_mv;
107}
108
109static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
110{
111 u8 adc_timeout = 0;
112 u8 adc_complete = 0;
113 u8 adc_result = 0;
114 int retry = 6;
115 int ret = 0;
116 int output_mv = 0;
117 u8 adc_en = 0;
118
119 pr_debug("%s: enter\n", __func__);
120
121 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
122 /* Read ADC Enable bit to restore after adc measurement */
123 WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en);
124 /* Trigger ADC one time measurement */
125 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
126 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
127 /* Set the appropriate MUX selection */
128 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, mux_ctl);
129 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
130 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1);
131
132 while (retry--) {
133 /* wait for 600usec to get adc results */
134 usleep_range(600, 610);
135
136 /* check for ADC Timeout */
137 WCD_MBHC_REG_READ(WCD_MBHC_ADC_TIMEOUT, adc_timeout);
138 if (adc_timeout)
139 continue;
140
141 /* Read ADC complete bit */
142 WCD_MBHC_REG_READ(WCD_MBHC_ADC_COMPLETE, adc_complete);
143 if (!adc_complete)
144 continue;
145
146 /* Read ADC result */
147 WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result);
148
149 pr_debug("%s: ADC result: 0x%x\n", __func__, adc_result);
150 /* Get voltage from ADC result */
151 output_mv = wcd_get_voltage_from_adc(adc_result,
152 wcd_mbhc_get_micbias(mbhc));
153 break;
154 }
155
156 /* Restore ADC Enable */
157 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en);
158
159 if (retry <= 0) {
160 pr_err("%s: adc complete: %d, adc timeout: %d\n",
161 __func__, adc_complete, adc_timeout);
162 ret = -EINVAL;
163 } else {
164 pr_debug("%s: adc complete: %d, adc timeout: %d output_mV: %d\n",
165 __func__, adc_complete, adc_timeout, output_mv);
166 ret = output_mv;
167 }
168
169 pr_debug("%s: leave\n", __func__);
170
171 return ret;
172}
173
174static bool wcd_mbhc_adc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
175{
176 bool anc_mic_found = false;
177 u16 fsm_en = 0;
178 u8 det = 0;
179 unsigned long retry = 0;
180 int valid_plug_cnt = 0, invalid_plug_cnt = 0;
181 int ret = 0;
182 u8 elect_ctl = 0;
183 u8 adc_mode = 0;
184 u8 vref = 0;
185 int vref_mv[] = {1650, 1500, 1600, 1700};
186
187 if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 ||
188 mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4)
189 return false;
190
191 if (!mbhc->mbhc_cb->mbhc_micbias_control)
192 return false;
193
194 /* Disable Detection done for ADC operation */
195 WCD_MBHC_REG_READ(WCD_MBHC_DETECTION_DONE, det);
196 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0);
197
198 /* Mask ADC COMPLETE interrupt */
199 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
200
201 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
202 mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
203 mbhc->mbhc_cfg->anc_micbias,
204 MICB_ENABLE);
205
206 /* Read legacy electircal detection and disable */
207 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
208 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
209
210 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1);
211 WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode);
212
213 /*
214 * wait for button debounce time 20ms. If 4-pole plug is inserted
215 * into 5-pole jack, then there will be a button press interrupt
216 * during anc plug detection. In that case though Hs_comp_res is 0,
217 * it should not be declared as ANC plug type
218 */
219 usleep_range(20000, 20100);
220
221 /*
222 * After enabling FSM, to handle slow insertion scenarios,
223 * check IN3 voltage is below the Vref
224 */
225 WCD_MBHC_REG_READ(WCD_MBHC_HS_VREF, vref);
226
227 do {
228 if (wcd_swch_level_remove(mbhc)) {
229 pr_debug("%s: Switch level is low\n", __func__);
230 goto done;
231 }
232 pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
233 ret = wcd_measure_adc_once(mbhc, MUX_CTL_IN3P);
234 /* TODO - check the logic */
235 if (ret && (ret < vref_mv[vref]))
236 valid_plug_cnt++;
237 else
238 invalid_plug_cnt++;
239 retry++;
240 } while (retry < ANC_DETECT_RETRY_CNT);
241
242 pr_debug("%s: valid: %d, invalid: %d\n", __func__, valid_plug_cnt,
243 invalid_plug_cnt);
244
245 /* decision logic */
246 if (valid_plug_cnt > invalid_plug_cnt)
247 anc_mic_found = true;
248done:
249 /* Restore ADC mode */
250 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode);
251 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0);
252 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
253 /* Set the MUX selection to AUTO */
254 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
255 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
256 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en);
257 /* Restore detection done */
258 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, det);
259
260 /* Restore electrical detection */
261 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
262
263 mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
264 mbhc->mbhc_cfg->anc_micbias,
265 MICB_DISABLE);
266 pr_debug("%s: anc mic %sfound\n", __func__,
267 anc_mic_found ? "" : "not ");
268
269 return anc_mic_found;
270}
271
272/* To determine if cross connection occurred */
273static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
274{
275 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE;
276 int hphl_adc_res = 0, hphr_adc_res = 0;
277 u8 fsm_en = 0;
278 int ret = 0;
279 u8 adc_mode = 0;
280 u8 elect_ctl = 0;
281 u8 adc_en = 0;
282
283 pr_debug("%s: enter\n", __func__);
284 /* Check for button press and plug detection */
285 if (wcd_swch_level_remove(mbhc)) {
286 pr_debug("%s: Switch level is low\n", __func__);
287 return -EINVAL;
288 }
289
290 /* If PA is enabled, dont check for cross-connection */
291 if (mbhc->mbhc_cb->hph_pa_on_status)
292 if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec))
293 return -EINVAL;
294
295 /* Read legacy electircal detection and disable */
296 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
297 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
298
299 /* Read and set ADC to single measurement */
300 WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode);
301 /* Read ADC Enable bit to restore after adc measurement */
302 WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en);
303 /* Read FSM status */
304 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
305
306 /* Get adc result for HPH L */
307 hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
308 if (hphl_adc_res < 0) {
309 pr_err("%s: hphl_adc_res adc measurement failed\n", __func__);
310 ret = hphl_adc_res;
311 goto done;
312 }
313
314 /* Get adc result for HPH R in mV */
315 hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
316 if (hphr_adc_res < 0) {
317 pr_err("%s: hphr_adc_res adc measurement failed\n", __func__);
318 ret = hphr_adc_res;
319 goto done;
320 }
321
322 if (hphl_adc_res > 100 && hphr_adc_res > 100) {
323 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
324 pr_debug("%s: Cross connection identified\n", __func__);
325 } else {
326 pr_debug("%s: No Cross connection found\n", __func__);
327 }
328
329done:
330 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
331 /* Set the MUX selection to Auto */
332 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
333 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
334
335 /* Restore ADC Enable */
336 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en);
337
338 /* Restore ADC mode */
339 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode);
340
341 /* Restore FSM state */
342 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en);
343
344 /* Restore electrical detection */
345 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
346
347 pr_debug("%s: leave, plug type: %d\n", __func__, plug_type);
348
349 return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
350}
351
352static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc,
353 int *spl_hs_cnt)
354{
355 bool spl_hs = false;
356 int output_mv = 0;
357 int adc_threshold = 0, adc_hph_threshold = 0;
358
359 pr_debug("%s: enter\n", __func__);
360 if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
361 goto exit;
362
363 /* Bump up MB2 to 2.7V */
364 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
365 mbhc->mbhc_cfg->mbhc_micbias, true);
366 usleep_range(10000, 10100);
367
368 /*
369 * Use ADC single mode to minimize the chance of missing out
370 * btn press/relesae for HEADSET type during correct work.
371 */
372 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
Meng Wang4d6a6be2017-09-15 10:35:44 +0800373 if (mbhc->hs_thr)
374 adc_threshold = mbhc->hs_thr;
375 else
376 adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530377 wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV);
Meng Wang4d6a6be2017-09-15 10:35:44 +0800378
379 if (mbhc->hph_thr)
380 adc_hph_threshold = mbhc->hph_thr;
381 else
382 adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
383 wcd_mbhc_get_micbias(mbhc))/
384 WCD_MBHC_ADC_MICBIAS_MV);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530385
386 if (output_mv > adc_threshold || output_mv < adc_hph_threshold) {
387 spl_hs = false;
388 } else {
389 spl_hs = true;
390 if (spl_hs_cnt)
391 *spl_hs_cnt += 1;
392 }
393
394 /* MB2 back to 1.8v if the type is not special headset */
395 if (spl_hs_cnt && (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT)) {
396 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
397 mbhc->mbhc_cfg->mbhc_micbias, false);
398 /* Add 10ms delay for micbias to settle */
399 usleep_range(10000, 10100);
400 }
401
402 if (spl_hs)
403 pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
404
405exit:
406 pr_debug("%s: leave\n", __func__);
407 return spl_hs;
408}
409
410static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
411{
412 int delay = 0;
413 bool ret = false;
414 bool is_spl_hs = false;
415 int output_mv = 0;
416 int adc_threshold = 0;
417
418 /*
419 * Increase micbias to 2.7V to detect headsets with
420 * threshold on microphone
421 */
422 if (mbhc->mbhc_cb->mbhc_micbias_control &&
423 !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
424 pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n",
425 __func__);
426 return false;
427 } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
428 ret = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
429 MIC_BIAS_2, true);
430 if (ret) {
431 pr_err("%s: mbhc_micb_ctrl_thr_mic failed, ret: %d\n",
432 __func__, ret);
433 return false;
434 }
435 }
Meng Wang4d6a6be2017-09-15 10:35:44 +0800436 if (mbhc->hs_thr)
437 adc_threshold = mbhc->hs_thr;
438 else
439 adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530440 wcd_mbhc_get_micbias(mbhc)) /
441 WCD_MBHC_ADC_MICBIAS_MV);
442
443 while (!is_spl_hs) {
444 if (mbhc->hs_detect_work_stop) {
445 pr_debug("%s: stop requested: %d\n", __func__,
446 mbhc->hs_detect_work_stop);
447 break;
448 }
449 delay += 50;
450 /* Wait for 50ms for FSM to update result */
451 msleep(50);
452 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
453 if (output_mv <= adc_threshold) {
454 pr_debug("%s: Special headset detected in %d msecs\n",
455 __func__, delay);
456 is_spl_hs = true;
457 }
458
459 if (delay == SPECIAL_HS_DETECT_TIME_MS) {
460 pr_debug("%s: Spl headset not found in 2 sec\n",
461 __func__);
462 break;
463 }
464 }
465 if (is_spl_hs) {
466 pr_debug("%s: Headset with threshold found\n", __func__);
467 mbhc->micbias_enable = true;
468 ret = true;
469 }
470 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
471 !mbhc->micbias_enable)
472 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, MIC_BIAS_2,
473 false);
474 pr_debug("%s: leave, micb_enable: %d\n", __func__,
475 mbhc->micbias_enable);
476
477 return ret;
478}
479
480static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
481 enum wcd_mbhc_plug_type plug_type)
482{
483 bool micbias2;
484
485 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
486 MIC_BIAS_2);
487 switch (plug_type) {
488 case MBHC_PLUG_TYPE_HEADPHONE:
489 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
490 break;
491 case MBHC_PLUG_TYPE_HEADSET:
492 case MBHC_PLUG_TYPE_ANC_HEADPHONE:
493 if (!mbhc->is_hs_recording && !micbias2)
494 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
495 break;
496 default:
497 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
498 break;
499
500 };
501}
502
503/* should be called under interrupt context that hold suspend */
504static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
505 struct work_struct *work)
506{
507 pr_debug("%s: scheduling correct_swch_plug\n", __func__);
508 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
509 mbhc->hs_detect_work_stop = false;
510 mbhc->mbhc_cb->lock_sleep(mbhc, true);
511 schedule_work(work);
512}
513
514/* called under codec_resource_lock acquisition */
515static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
516 struct work_struct *work)
517{
518 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
519 mbhc->hs_detect_work_stop = true;
520 WCD_MBHC_RSC_UNLOCK(mbhc);
521 if (cancel_work_sync(work)) {
522 pr_debug("%s: correct_plug_swch is canceled\n",
523 __func__);
524 mbhc->mbhc_cb->lock_sleep(mbhc, false);
525 }
526 WCD_MBHC_RSC_LOCK(mbhc);
527}
528
529/* called under codec_resource_lock acquisition */
530static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
531{
532 struct snd_soc_codec *codec = mbhc->codec;
533
534 pr_debug("%s: enter\n", __func__);
535 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
536
537 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
538 mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false);
539
540 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0);
541
542 if (mbhc->mbhc_cb->mbhc_micbias_control) {
543 mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
544 MICB_ENABLE);
545 } else {
546 pr_err("%s: Mic Bias is not enabled\n", __func__);
547 return;
548 }
549
550 /* Re-initialize button press completion object */
551 reinit_completion(&mbhc->btn_press_compl);
552 wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
553 pr_debug("%s: leave\n", __func__);
554}
555
556static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
557{
558 if (mbhc->micbias_enable) {
559 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
560 mbhc->codec, MIC_BIAS_2, false);
561 if (mbhc->mbhc_cb->set_micbias_value)
562 mbhc->mbhc_cb->set_micbias_value(
563 mbhc->codec);
564 mbhc->micbias_enable = false;
565 }
566}
567
Meng Wang4d6a6be2017-09-15 10:35:44 +0800568static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530569
570{
571 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
Meng Wang4d6a6be2017-09-15 10:35:44 +0800572 u32 hph_thr = 0, hs_thr = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530573
Meng Wang4d6a6be2017-09-15 10:35:44 +0800574 if (mbhc->hs_thr)
575 hs_thr = mbhc->hs_thr;
576 else
577 hs_thr = WCD_MBHC_ADC_HS_THRESHOLD_MV;
578
579 if (mbhc->hph_thr)
580 hph_thr = mbhc->hph_thr;
581 else
582 hph_thr = WCD_MBHC_ADC_HPH_THRESHOLD_MV;
583
584 if (adc_result < hph_thr)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530585 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
Meng Wang4d6a6be2017-09-15 10:35:44 +0800586 else if (adc_result > hs_thr)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530587 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
588 else
589 plug_type = MBHC_PLUG_TYPE_HEADSET;
590 pr_debug("%s: plug type is %d found\n", __func__, plug_type);
591
592 return plug_type;
593}
594
595static void wcd_correct_swch_plug(struct work_struct *work)
596{
597 struct wcd_mbhc *mbhc;
598 struct snd_soc_codec *codec;
599 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
600 unsigned long timeout;
601 bool wrk_complete = false;
602 int pt_gnd_mic_swap_cnt = 0;
603 int no_gnd_mic_swap_cnt = 0;
604 bool is_pa_on = false, spl_hs = false, spl_hs_reported = false;
605 int ret = 0;
606 int spl_hs_count = 0;
607 int output_mv = 0;
608 int cross_conn;
609 int try = 0;
610
611 pr_debug("%s: enter\n", __func__);
612
613 mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
614 codec = mbhc->codec;
615
616 WCD_MBHC_RSC_LOCK(mbhc);
617 /* Mask ADC COMPLETE interrupt */
618 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
619 WCD_MBHC_RSC_UNLOCK(mbhc);
620
621 /* Check for cross connection */
622 do {
623 cross_conn = wcd_check_cross_conn(mbhc);
624 try++;
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800625 } while (try < mbhc->swap_thr);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530626
627 if (cross_conn > 0) {
628 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
629 pr_debug("%s: cross connection found, Plug type %d\n",
630 __func__, plug_type);
631 goto correct_plug_type;
632 }
633 /* Find plug type */
634 output_mv = wcd_measure_adc_continuous(mbhc);
Meng Wang4d6a6be2017-09-15 10:35:44 +0800635 plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530636
637 /*
638 * Report plug type if it is either headset or headphone
639 * else start the 3 sec loop
640 */
641 if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
642 plug_type == MBHC_PLUG_TYPE_HEADPHONE) &&
643 (!wcd_swch_level_remove(mbhc))) {
644 WCD_MBHC_RSC_LOCK(mbhc);
645 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
646 WCD_MBHC_RSC_UNLOCK(mbhc);
647 }
648
649 /*
650 * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE,
651 * so that btn press/release interrupt can be generated.
652 */
653 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET ||
654 mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) {
655 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
656 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
657 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1);
658 }
659
660correct_plug_type:
661 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
662 while (!time_after(jiffies, timeout)) {
663 if (mbhc->hs_detect_work_stop) {
664 pr_debug("%s: stop requested: %d\n", __func__,
665 mbhc->hs_detect_work_stop);
666 wcd_micbias_disable(mbhc);
667 goto exit;
668 }
669
670 /* allow sometime and re-check stop requested again */
671 msleep(20);
672 if (mbhc->hs_detect_work_stop) {
673 pr_debug("%s: stop requested: %d\n", __func__,
674 mbhc->hs_detect_work_stop);
675 wcd_micbias_disable(mbhc);
676 goto exit;
677 }
678
679 msleep(180);
680 /*
681 * Use ADC single mode to minimize the chance of missing out
682 * btn press/release for HEADSET type during correct work.
683 */
684 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
685
686 /*
687 * instead of hogging system by contineous polling, wait for
688 * sometime and re-check stop request again.
689 */
Meng Wang4d6a6be2017-09-15 10:35:44 +0800690 plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530691
692 if ((output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) &&
693 (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) {
694 spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc,
695 &spl_hs_count);
696
697 if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
698 output_mv = WCD_MBHC_ADC_HS_THRESHOLD_MV;
699 spl_hs = true;
700 mbhc->micbias_enable = true;
701 }
702 }
703
704 if (mbhc->mbhc_cb->hph_pa_on_status)
705 is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec);
706
707 if ((output_mv <= WCD_MBHC_ADC_HS_THRESHOLD_MV) &&
708 (!is_pa_on)) {
709 /* Check for cross connection*/
710 ret = wcd_check_cross_conn(mbhc);
711 if (ret < 0)
712 continue;
713 else if (ret > 0) {
714 pt_gnd_mic_swap_cnt++;
715 no_gnd_mic_swap_cnt = 0;
716 if (pt_gnd_mic_swap_cnt <
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800717 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530718 continue;
719 } else if (pt_gnd_mic_swap_cnt >
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800720 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530721 /*
722 * This is due to GND/MIC switch didn't
723 * work, Report unsupported plug.
724 */
725 pr_debug("%s: switch did not work\n",
726 __func__);
727 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
728 goto report;
729 } else {
730 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
731 }
732 } else {
733 no_gnd_mic_swap_cnt++;
734 pt_gnd_mic_swap_cnt = 0;
735 plug_type = wcd_mbhc_get_plug_from_adc(
Meng Wang4d6a6be2017-09-15 10:35:44 +0800736 mbhc, output_mv);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530737 if ((no_gnd_mic_swap_cnt <
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800738 mbhc->swap_thr) &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530739 (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
740 continue;
741 } else {
742 no_gnd_mic_swap_cnt = 0;
743 }
744 }
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800745 if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530746 (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
747 /*
748 * if switch is toggled, check again,
749 * otherwise report unsupported plug
750 */
751 if (mbhc->mbhc_cfg->swap_gnd_mic &&
752 mbhc->mbhc_cfg->swap_gnd_mic(codec,
753 true)) {
754 pr_debug("%s: US_EU gpio present,flip switch\n"
755 , __func__);
756 continue;
757 }
758 }
759 }
760
761 if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) {
762 pr_debug("%s: cable is extension cable\n", __func__);
763 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
764 wrk_complete = true;
765 } else {
766 pr_debug("%s: cable might be headset: %d\n", __func__,
767 plug_type);
768 if (plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP) {
769 plug_type = wcd_mbhc_get_plug_from_adc(
Meng Wang4d6a6be2017-09-15 10:35:44 +0800770 mbhc, output_mv);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530771 if (!spl_hs_reported &&
772 spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
773 spl_hs_reported = true;
774 WCD_MBHC_RSC_LOCK(mbhc);
775 wcd_mbhc_find_plug_and_report(mbhc,
776 plug_type);
777 WCD_MBHC_RSC_UNLOCK(mbhc);
778 continue;
779 } else if (spl_hs_reported)
780 continue;
781 /*
782 * Report headset only if not already reported
783 * and if there is not button press without
784 * release
785 */
786 if ((mbhc->current_plug !=
787 MBHC_PLUG_TYPE_HEADSET) &&
788 (mbhc->current_plug !=
789 MBHC_PLUG_TYPE_ANC_HEADPHONE) &&
790 !wcd_swch_level_remove(mbhc)) {
791 pr_debug("%s: cable is %s headset\n",
792 __func__,
793 ((spl_hs_count ==
794 WCD_MBHC_SPL_HS_CNT) ?
795 "special ":""));
796 goto report;
797 }
798 }
799 wrk_complete = false;
800 }
801 }
802 if (!wrk_complete) {
803 /*
804 * If plug_tye is headset, we might have already reported either
805 * in detect_plug-type or in above while loop, no need to report
806 * again
807 */
808 if ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
809 (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE)) {
810 pr_debug("%s: plug_type:0x%x already reported\n",
811 __func__, mbhc->current_plug);
812 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
813 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
814 goto enable_supply;
815 }
816 }
817 if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
818 if (wcd_is_special_headset(mbhc)) {
819 pr_debug("%s: Special headset found %d\n",
820 __func__, plug_type);
821 plug_type = MBHC_PLUG_TYPE_HEADSET;
822 } else {
823 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 1);
824 }
825 }
826
827report:
828 if (wcd_swch_level_remove(mbhc)) {
829 pr_debug("%s: Switch level is low\n", __func__);
830 goto exit;
831 }
832
833 pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
834 __func__, plug_type, wrk_complete,
835 mbhc->btn_press_intr);
836
837 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
838 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
839
840 WCD_MBHC_RSC_LOCK(mbhc);
841 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
842 WCD_MBHC_RSC_UNLOCK(mbhc);
843enable_supply:
844 /*
845 * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE,
846 * so that btn press/release interrupt can be generated.
847 * For other plug type, clear the bit.
848 */
849 if (plug_type == MBHC_PLUG_TYPE_HEADSET ||
850 plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE)
851 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1);
852 else
853 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0);
854
855 if (mbhc->mbhc_cb->mbhc_micbias_control)
856 wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
857exit:
858 if (mbhc->mbhc_cb->mbhc_micbias_control &&
859 !mbhc->micbias_enable)
860 mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
861 MICB_DISABLE);
862
863 /*
864 * If plug type is corrected from special headset to headphone,
865 * clear the micbias enable flag, set micbias back to 1.8V and
866 * disable micbias.
867 */
868 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
869 mbhc->micbias_enable) {
870 if (mbhc->mbhc_cb->mbhc_micbias_control)
871 mbhc->mbhc_cb->mbhc_micbias_control(
872 codec, MIC_BIAS_2,
873 MICB_DISABLE);
874 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
875 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
876 codec,
877 MIC_BIAS_2, false);
878 if (mbhc->mbhc_cb->set_micbias_value) {
879 mbhc->mbhc_cb->set_micbias_value(codec);
880 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
881 }
882 mbhc->micbias_enable = false;
883 }
884
885 if (mbhc->mbhc_cfg->detect_extn_cable &&
886 ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
887 (plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
888 !mbhc->hs_detect_work_stop) {
889 WCD_MBHC_RSC_LOCK(mbhc);
890 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true);
891 WCD_MBHC_RSC_UNLOCK(mbhc);
892 }
893
894 /*
895 * Enable ADC COMPLETE interrupt for HEADPHONE.
896 * Btn release may happen after the correct work, ADC COMPLETE
897 * interrupt needs to be captured to correct plug type.
898 */
899 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
900 WCD_MBHC_RSC_LOCK(mbhc);
901 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
902 true);
903 WCD_MBHC_RSC_UNLOCK(mbhc);
904 }
905
906 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
907 mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true);
908
909 mbhc->mbhc_cb->lock_sleep(mbhc, false);
910 pr_debug("%s: leave\n", __func__);
911}
912
913static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
914{
915 struct wcd_mbhc *mbhc = data;
916 unsigned long timeout;
917 int adc_threshold, output_mv, retry = 0;
Meng Wang6f901622017-09-19 10:21:57 +0800918 bool hphpa_on = false;
919 u8 moisture_status = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530920
921 pr_debug("%s: enter\n", __func__);
922 WCD_MBHC_RSC_LOCK(mbhc);
923
924 timeout = jiffies +
925 msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
926 adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
927 wcd_mbhc_get_micbias(mbhc)) /
928 WCD_MBHC_ADC_MICBIAS_MV);
929 do {
930 retry++;
931 /*
932 * read output_mv every 10ms to look for
933 * any change in IN2_P
934 */
935 usleep_range(10000, 10100);
936 output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
937
938 pr_debug("%s: Check for fake removal: output_mv %d\n",
939 __func__, output_mv);
940 if ((output_mv <= adc_threshold) &&
941 retry > FAKE_REM_RETRY_ATTEMPTS) {
942 pr_debug("%s: headset is NOT actually removed\n",
943 __func__);
944 goto exit;
945 }
946 } while (!time_after(jiffies, timeout));
947
948 if (wcd_swch_level_remove(mbhc)) {
949 pr_debug("%s: Switch level is low ", __func__);
950 goto exit;
951 }
952
Meng Wang6f901622017-09-19 10:21:57 +0800953 if (mbhc->mbhc_cfg->moisture_en) {
954 if (mbhc->mbhc_cb->hph_pa_on_status)
955 if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) {
956 hphpa_on = true;
957 WCD_MBHC_REG_UPDATE_BITS(
958 WCD_MBHC_HPHL_PA_EN, 0);
959 WCD_MBHC_REG_UPDATE_BITS(
960 WCD_MBHC_HPH_PA_EN, 0);
961 }
962 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1);
963 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1);
964 /* wait for 50ms to get moisture status */
965 usleep_range(50000, 50100);
966
967 WCD_MBHC_REG_READ(WCD_MBHC_MOISTURE_STATUS, moisture_status);
968 }
969
970 if (mbhc->mbhc_cfg->moisture_en && !moisture_status) {
971 pr_debug("%s: moisture present in jack\n", __func__);
972 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
973 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, 1);
974 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
975 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
976 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
977 mbhc->btn_press_intr = false;
978 mbhc->is_btn_press = false;
979 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
980 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
981 else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
982 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
983 else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP)
984 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
985 else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH)
986 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
987 } else {
988 /*
989 * ADC COMPLETE and ELEC_REM interrupts are both enabled for
990 * HEADPHONE, need to reject the ADC COMPLETE interrupt which
991 * follows ELEC_REM one when HEADPHONE is removed.
992 */
993 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
994 mbhc->extn_cable_hph_rem = true;
995 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0);
996 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
997 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
998 wcd_mbhc_elec_hs_report_unplug(mbhc);
999
1000 if (hphpa_on) {
1001 hphpa_on = false;
1002 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
1003 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1);
1004 }
1005 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301006exit:
1007 WCD_MBHC_RSC_UNLOCK(mbhc);
1008 pr_debug("%s: leave\n", __func__);
1009 return IRQ_HANDLED;
1010}
1011
1012static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
1013{
1014 struct wcd_mbhc *mbhc = data;
1015
1016 pr_debug("%s: enter\n", __func__);
1017
1018 /*
1019 * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
1020 * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
1021 * when HEADPHONE is removed.
1022 */
1023 if (mbhc->extn_cable_hph_rem == true) {
1024 mbhc->extn_cable_hph_rem = false;
1025 pr_debug("%s: leave\n", __func__);
1026 return IRQ_HANDLED;
1027 }
1028
1029 WCD_MBHC_RSC_LOCK(mbhc);
1030 /*
1031 * If current plug is headphone then there is no chance to
1032 * get ADC complete interrupt, so connected cable should be
1033 * headset not headphone.
1034 */
1035 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
1036 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
1037 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1);
1038 wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
1039 WCD_MBHC_RSC_UNLOCK(mbhc);
1040 return IRQ_HANDLED;
1041 }
1042
1043 if (!mbhc->mbhc_cfg->detect_extn_cable) {
1044 pr_debug("%s: Returning as Extension cable feature not enabled\n",
1045 __func__);
1046 WCD_MBHC_RSC_UNLOCK(mbhc);
1047 return IRQ_HANDLED;
1048 }
1049
1050 pr_debug("%s: Disable electrical headset insertion interrupt\n",
1051 __func__);
1052 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
1053 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
1054 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0);
1055 mbhc->is_extn_cable = true;
1056 mbhc->btn_press_intr = false;
1057 wcd_mbhc_adc_detect_plug_type(mbhc);
1058 WCD_MBHC_RSC_UNLOCK(mbhc);
1059 pr_debug("%s: leave\n", __func__);
1060 return IRQ_HANDLED;
1061}
1062
1063static struct wcd_mbhc_fn mbhc_fn = {
1064 .wcd_mbhc_hs_ins_irq = wcd_mbhc_adc_hs_ins_irq,
1065 .wcd_mbhc_hs_rem_irq = wcd_mbhc_adc_hs_rem_irq,
1066 .wcd_mbhc_detect_plug_type = wcd_mbhc_adc_detect_plug_type,
1067 .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_adc_detect_anc_plug_type,
1068 .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug,
1069};
1070
1071/* Function: wcd_mbhc_adc_init
1072 * @mbhc: MBHC function pointer
1073 * Description: Initialize MBHC ADC related function pointers to MBHC structure
1074 */
1075void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc)
1076{
1077 if (!mbhc) {
1078 pr_err("%s: mbhc is NULL\n", __func__);
1079 return;
1080 }
1081 mbhc->mbhc_fn = &mbhc_fn;
1082 INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
1083}
1084EXPORT_SYMBOL(wcd_mbhc_adc_init);