blob: d03d8342a8df3f505712ffe3059be648c49651cf [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2015-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-legacy.h"
31#include "wcd-mbhc-v2.h"
32
33static int det_extn_cable_en;
34module_param(det_extn_cable_en, int, 0664);
35MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect");
36
37static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
38{
39 bool anc_mic_found = false;
40 u16 val, hs_comp_res, btn_status = 0;
41 unsigned long retry = 0;
42 int valid_plug_cnt = 0, invalid_plug_cnt = 0;
43 int btn_status_cnt = 0;
44 bool is_check_btn_press = false;
45
46
47 if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 ||
48 mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4)
49 return false;
50
51 if (!mbhc->mbhc_cb->mbhc_micbias_control)
52 return false;
53
54 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val);
55
56 if (val)
57 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
58
59 mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
60 mbhc->mbhc_cfg->anc_micbias,
61 MICB_ENABLE);
62 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2);
63 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1);
64 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
65 /*
66 * wait for button debounce time 20ms. If 4-pole plug is inserted
67 * into 5-pole jack, then there will be a button press interrupt
68 * during anc plug detection. In that case though Hs_comp_res is 0,
69 * it should not be declared as ANC plug type
70 */
71 usleep_range(20000, 20100);
72
73 /*
74 * After enabling FSM, to handle slow insertion scenarios,
75 * check hs_comp_result for few times to see if the IN3 voltage
76 * is below the Vref
77 */
78 do {
79 if (wcd_swch_level_remove(mbhc)) {
80 pr_debug("%s: Switch level is low\n", __func__);
81 goto exit;
82 }
83 pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
84 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
85
86 if (!hs_comp_res) {
87 valid_plug_cnt++;
88 is_check_btn_press = true;
89 } else
90 invalid_plug_cnt++;
91 /* Wait 1ms before taking another reading */
92 usleep_range(1000, 1100);
93
94 WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status);
95 if (btn_status)
96 btn_status_cnt++;
97
98 retry++;
99 } while (retry < ANC_DETECT_RETRY_CNT);
100
101 pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n",
102 __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt);
103
104 /* decision logic */
105 if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press &&
106 (btn_status_cnt == 0))
107 anc_mic_found = true;
108exit:
109 if (!val)
110 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
111
112 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0);
113
114 mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
115 mbhc->mbhc_cfg->anc_micbias,
116 MICB_DISABLE);
117 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0);
118 pr_debug("%s: anc mic %sfound\n", __func__,
119 anc_mic_found ? "" : "not ");
120 return anc_mic_found;
121}
122
123/* To determine if cross connection occurred */
124static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
125{
126 u16 swap_res = 0;
127 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE;
128 s16 reg1 = 0;
129 bool hphl_sch_res = 0, hphr_sch_res = 0;
130
131 if (wcd_swch_level_remove(mbhc)) {
132 pr_debug("%s: Switch level is low\n", __func__);
133 return -EINVAL;
134 }
135
136 /* If PA is enabled, dont check for cross-connection */
137 if (mbhc->mbhc_cb->hph_pa_on_status)
138 if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec))
139 return false;
140
141 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
142 /*
143 * Check if there is any cross connection,
144 * Micbias and schmitt trigger (HPHL-HPHR)
145 * needs to be enabled. For some codecs like wcd9335,
146 * pull-up will already be enabled when this function
147 * is called for cross-connection identification. No
148 * need to enable micbias in that case.
149 */
150 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
151 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2);
152
153 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res);
154 pr_debug("%s: swap_res%x\n", __func__, swap_res);
155
156 /*
157 * Read reg hphl and hphr schmitt result with cross connection
158 * bit. These bits will both be "0" in case of cross connection
159 * otherwise, they stay at 1
160 */
161 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res);
162 WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res);
163 if (!(hphl_sch_res || hphr_sch_res)) {
164 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
165 pr_debug("%s: Cross connection identified\n", __func__);
166 } else {
167 pr_debug("%s: No Cross connection found\n", __func__);
168 }
169
170 /* Disable schmitt trigger and restore micbias */
171 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
172 pr_debug("%s: leave, plug type: %d\n", __func__, plug_type);
173
174 return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
175}
176
177static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
178{
179 struct snd_soc_codec *codec = mbhc->codec;
180 int delay = 0, rc;
181 bool ret = false;
182 u16 hs_comp_res;
183 bool is_spl_hs = false;
184
185 /*
186 * Increase micbias to 2.7V to detect headsets with
187 * threshold on microphone
188 */
189 if (mbhc->mbhc_cb->mbhc_micbias_control &&
190 !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
191 pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n",
192 __func__);
193 return false;
194 } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
195 rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec,
196 MIC_BIAS_2, true);
197 if (rc) {
198 pr_err("%s: Micbias control for thr mic failed, rc: %d\n",
199 __func__, rc);
200 return false;
201 }
202 }
203
204 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
205
206 pr_debug("%s: special headset, start register writes\n", __func__);
207
208 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
209 while (!is_spl_hs) {
210 if (mbhc->hs_detect_work_stop) {
211 pr_debug("%s: stop requested: %d\n", __func__,
212 mbhc->hs_detect_work_stop);
213 break;
214 }
215 delay = delay + 50;
216 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
217 mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
218 MBHC_COMMON_MICB_PRECHARGE,
219 true);
220 mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
221 MBHC_COMMON_MICB_SET_VAL,
222 true);
223 }
224 /* Wait for 50msec for MICBIAS to settle down */
225 msleep(50);
226 if (mbhc->mbhc_cb->set_auto_zeroing)
227 mbhc->mbhc_cb->set_auto_zeroing(codec, true);
228 /* Wait for 50msec for FSM to update result values */
229 msleep(50);
230 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
231 if (!(hs_comp_res)) {
232 pr_debug("%s: Special headset detected in %d msecs\n",
233 __func__, (delay * 2));
234 is_spl_hs = true;
235 }
236 if (delay == SPECIAL_HS_DETECT_TIME_MS) {
237 pr_debug("%s: Spl headset didn't get detect in 4 sec\n",
238 __func__);
239 break;
240 }
241 }
242 if (is_spl_hs) {
243 pr_debug("%s: Headset with threshold found\n", __func__);
244 mbhc->micbias_enable = true;
245 ret = true;
246 }
247 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
248 mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
249 MBHC_COMMON_MICB_PRECHARGE,
250 false);
251 if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
252 mbhc->mbhc_cb->set_micbias_value(codec);
253 if (mbhc->mbhc_cb->set_auto_zeroing)
254 mbhc->mbhc_cb->set_auto_zeroing(codec, false);
255
256 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
257 !mbhc->micbias_enable)
258 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2,
259 false);
260
261 pr_debug("%s: leave, micb_enable: %d\n", __func__,
262 mbhc->micbias_enable);
263 return ret;
264}
265
266static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc,
267 enum wcd_mbhc_plug_type plug_type)
268{
269 bool micbias2;
270
271 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
272 MIC_BIAS_2);
273 switch (plug_type) {
274 case MBHC_PLUG_TYPE_HEADPHONE:
275 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
276 break;
277 case MBHC_PLUG_TYPE_HEADSET:
278 case MBHC_PLUG_TYPE_ANC_HEADPHONE:
279 if (!mbhc->is_hs_recording && !micbias2)
280 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
281 break;
282 default:
283 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
284 break;
285
286 };
287}
288
289static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc,
290 enum wcd_mbhc_plug_type plug_type)
291{
292
293 struct snd_soc_codec *codec = mbhc->codec;
294
295 /*
296 * Do not disable micbias if recording is going on or
297 * headset is inserted on the other side of the extn
298 * cable. If headset has been detected current source
299 * needs to be kept enabled for button detection to work.
300 * If the accessory type is invalid or unsupported, we
301 * dont need to enable either of them.
302 */
303 if (det_extn_cable_en && mbhc->is_extn_cable &&
304 mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb &&
305 mbhc->mbhc_cb->extn_use_mb(codec)) {
306 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE ||
307 plug_type == MBHC_PLUG_TYPE_HEADSET)
308 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
309 } else {
310 if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
311 if (mbhc->is_hs_recording || mbhc->micbias_enable) {
312 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
313 } else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL,
314 &mbhc->event_state)) ||
315 (test_bit(WCD_MBHC_EVENT_PA_HPHR,
316 &mbhc->event_state))) {
317 wcd_enable_curr_micbias(mbhc,
318 WCD_MBHC_EN_PULLUP);
319 } else {
320 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
321 }
322 } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
323 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
324 } else {
325 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
326 }
327 }
328}
329
330static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc,
331 int *spl_hs_cnt)
332{
333 u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0;
334 bool spl_hs = false;
335
336 if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
337 goto done;
338
339 if (!spl_hs_cnt) {
340 pr_err("%s: spl_hs_cnt is NULL\n", __func__);
341 goto done;
342 }
343 /* Read back hs_comp_res @ 1.8v Micbias */
344 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v);
345 if (!hs_comp_res_1_8v) {
346 spl_hs = false;
347 goto done;
348 }
349
350 /* Bump up MB2 to 2.7v */
351 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
352 mbhc->mbhc_cfg->mbhc_micbias, true);
353 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
354 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
355 usleep_range(10000, 10100);
356
357 /* Read back HS_COMP_RESULT */
358 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v);
359 if (!hs_comp_res_2_7v && hs_comp_res_1_8v)
360 spl_hs = true;
361
362 if (spl_hs)
363 *spl_hs_cnt += 1;
364
365 /* MB2 back to 1.8v */
366 if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) {
367 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
368 mbhc->mbhc_cfg->mbhc_micbias, false);
369 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
370 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
371 usleep_range(10000, 10100);
372 }
373
374 if (spl_hs)
375 pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
376
377done:
378 return spl_hs;
379}
380
381/* should be called under interrupt context that hold suspend */
382static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
383 struct work_struct *work)
384{
385 pr_debug("%s: scheduling correct_swch_plug\n", __func__);
386 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
387 mbhc->hs_detect_work_stop = false;
388 mbhc->mbhc_cb->lock_sleep(mbhc, true);
389 schedule_work(work);
390}
391
392/* called under codec_resource_lock acquisition */
393static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
394 struct work_struct *work)
395{
396 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
397 mbhc->hs_detect_work_stop = true;
398 WCD_MBHC_RSC_UNLOCK(mbhc);
399 if (cancel_work_sync(work)) {
400 pr_debug("%s: correct_plug_swch is canceled\n",
401 __func__);
402 mbhc->mbhc_cb->lock_sleep(mbhc, false);
403 }
404 WCD_MBHC_RSC_LOCK(mbhc);
405}
406
407/* called under codec_resource_lock acquisition */
408static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
409{
410 struct snd_soc_codec *codec = mbhc->codec;
411 bool micbias1 = false;
412
413 pr_debug("%s: enter\n", __func__);
414 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
415
416 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
417 mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false);
418
419 if (mbhc->mbhc_cb->micbias_enable_status)
420 micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
421 MIC_BIAS_1);
422
423 if (mbhc->mbhc_cb->set_cap_mode)
424 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
425
426 if (mbhc->mbhc_cb->mbhc_micbias_control)
427 mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
428 MICB_ENABLE);
429 else
430 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
431
432 /* Re-initialize button press completion object */
433 reinit_completion(&mbhc->btn_press_compl);
434 wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
435 pr_debug("%s: leave\n", __func__);
436}
437
438static void wcd_correct_swch_plug(struct work_struct *work)
439{
440 struct wcd_mbhc *mbhc;
441 struct snd_soc_codec *codec;
442 enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
443 unsigned long timeout;
444 u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0;
445 bool wrk_complete = false;
446 int pt_gnd_mic_swap_cnt = 0;
447 int no_gnd_mic_swap_cnt = 0;
448 bool is_pa_on = false, spl_hs = false, spl_hs_reported = false;
449 bool micbias2 = false;
450 bool micbias1 = false;
451 int ret = 0;
452 int rc, spl_hs_count = 0;
453 int cross_conn;
454 int try = 0;
455
456 pr_debug("%s: enter\n", __func__);
457
458 mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
459 codec = mbhc->codec;
460
461 /*
462 * Enable micbias/pullup for detection in correct work.
463 * This work will get scheduled from detect_plug_type which
464 * will already request for pullup/micbias. If the pullup/micbias
465 * is handled with ref-counts by individual codec drivers, there is
466 * no need to enabale micbias/pullup here
467 */
468
469 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
470
471 /* Enable HW FSM */
472 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
473 /*
474 * Check for any button press interrupts before starting 3-sec
475 * loop.
476 */
477 rc = wait_for_completion_timeout(&mbhc->btn_press_compl,
478 msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS));
479
480 WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
481 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
482
483 if (!rc) {
484 pr_debug("%s No btn press interrupt\n", __func__);
485 if (!btn_result && !hs_comp_res)
486 plug_type = MBHC_PLUG_TYPE_HEADSET;
487 else if (!btn_result && hs_comp_res)
488 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
489 else
490 plug_type = MBHC_PLUG_TYPE_INVALID;
491 } else {
492 if (!btn_result && !hs_comp_res)
493 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
494 else
495 plug_type = MBHC_PLUG_TYPE_INVALID;
496 }
497
498 do {
499 cross_conn = wcd_check_cross_conn(mbhc);
500 try++;
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800501 } while (try < mbhc->swap_thr);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530502
503 /*
504 * Check for cross connection 4 times.
505 * Consider the result of the fourth iteration.
506 */
507 if (cross_conn > 0) {
508 pr_debug("%s: cross con found, start polling\n",
509 __func__);
510 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
511 pr_debug("%s: Plug found, plug type is %d\n",
512 __func__, plug_type);
513 goto correct_plug_type;
514 }
515
516 if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
517 plug_type == MBHC_PLUG_TYPE_HEADPHONE) &&
518 (!wcd_swch_level_remove(mbhc))) {
519 WCD_MBHC_RSC_LOCK(mbhc);
520 if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH)
521 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
522 0);
523 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
524 WCD_MBHC_RSC_UNLOCK(mbhc);
525 }
526
527correct_plug_type:
528
529 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
530 while (!time_after(jiffies, timeout)) {
531 if (mbhc->hs_detect_work_stop) {
532 pr_debug("%s: stop requested: %d\n", __func__,
533 mbhc->hs_detect_work_stop);
534 wcd_enable_curr_micbias(mbhc,
535 WCD_MBHC_EN_NONE);
536 if (mbhc->micbias_enable) {
537 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
538 mbhc->codec, MIC_BIAS_2, false);
539 if (mbhc->mbhc_cb->set_micbias_value)
540 mbhc->mbhc_cb->set_micbias_value(
541 mbhc->codec);
542 mbhc->micbias_enable = false;
543 }
544 goto exit;
545 }
546 if (mbhc->btn_press_intr) {
547 wcd_cancel_btn_work(mbhc);
548 mbhc->btn_press_intr = false;
549 }
550 /* Toggle FSM */
551 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
552 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
553
554 /* allow sometime and re-check stop requested again */
555 msleep(20);
556 if (mbhc->hs_detect_work_stop) {
557 pr_debug("%s: stop requested: %d\n", __func__,
558 mbhc->hs_detect_work_stop);
559 wcd_enable_curr_micbias(mbhc,
560 WCD_MBHC_EN_NONE);
561 if (mbhc->micbias_enable) {
562 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
563 mbhc->codec, MIC_BIAS_2, false);
564 if (mbhc->mbhc_cb->set_micbias_value)
565 mbhc->mbhc_cb->set_micbias_value(
566 mbhc->codec);
567 mbhc->micbias_enable = false;
568 }
569 goto exit;
570 }
571 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
572
573 pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res);
574 if (mbhc->mbhc_cb->hph_pa_on_status)
575 is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec);
576
577 /*
578 * instead of hogging system by contineous polling, wait for
579 * sometime and re-check stop request again.
580 */
581 msleep(180);
582 if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) {
583 spl_hs = wcd_mbhc_check_for_spl_headset(mbhc,
584 &spl_hs_count);
585
586 if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
587 hs_comp_res = 0;
588 spl_hs = true;
589 mbhc->micbias_enable = true;
590 }
591 }
592
593 if ((!hs_comp_res) && (!is_pa_on)) {
594 /* Check for cross connection*/
595 ret = wcd_check_cross_conn(mbhc);
596 if (ret < 0) {
597 continue;
598 } else if (ret > 0) {
599 pt_gnd_mic_swap_cnt++;
600 no_gnd_mic_swap_cnt = 0;
601 if (pt_gnd_mic_swap_cnt <
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800602 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530603 continue;
604 } else if (pt_gnd_mic_swap_cnt >
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800605 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530606 /*
607 * This is due to GND/MIC switch didn't
608 * work, Report unsupported plug.
609 */
610 pr_debug("%s: switch didn't work\n",
611 __func__);
612 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
613 goto report;
614 } else {
615 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
616 }
617 } else {
618 no_gnd_mic_swap_cnt++;
619 pt_gnd_mic_swap_cnt = 0;
620 plug_type = MBHC_PLUG_TYPE_HEADSET;
621 if ((no_gnd_mic_swap_cnt <
622 GND_MIC_SWAP_THRESHOLD) &&
623 (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
624 continue;
625 } else {
626 no_gnd_mic_swap_cnt = 0;
627 }
628 }
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800629 if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530630 (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
631 /*
632 * if switch is toggled, check again,
633 * otherwise report unsupported plug
634 */
635 if (mbhc->mbhc_cfg->swap_gnd_mic &&
636 mbhc->mbhc_cfg->swap_gnd_mic(codec,
637 true)) {
638 pr_debug("%s: US_EU gpio present,flip switch\n"
639 , __func__);
640 continue;
641 }
642 }
643 }
644
645 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
646 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
647 if (hs_comp_res && !(hphl_sch || mic_sch)) {
648 pr_debug("%s: cable is extension cable\n", __func__);
649 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
650 wrk_complete = true;
651 } else {
652 pr_debug("%s: cable might be headset: %d\n", __func__,
653 plug_type);
654 if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
655 plug_type = MBHC_PLUG_TYPE_HEADSET;
656 if (!spl_hs_reported &&
657 spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
658 spl_hs_reported = true;
659 WCD_MBHC_RSC_LOCK(mbhc);
660 wcd_mbhc_find_plug_and_report(mbhc,
661 plug_type);
662 WCD_MBHC_RSC_UNLOCK(mbhc);
663 continue;
664 } else if (spl_hs_reported)
665 continue;
666 /*
667 * Report headset only if not already reported
668 * and if there is not button press without
669 * release
670 */
671 if (((mbhc->current_plug !=
672 MBHC_PLUG_TYPE_HEADSET) &&
673 (mbhc->current_plug !=
674 MBHC_PLUG_TYPE_ANC_HEADPHONE)) &&
675 !wcd_swch_level_remove(mbhc) &&
676 !mbhc->btn_press_intr) {
677 pr_debug("%s: cable is %sheadset\n",
678 __func__,
679 ((spl_hs_count ==
680 WCD_MBHC_SPL_HS_CNT) ?
681 "special ":""));
682 goto report;
683 }
684 }
685 wrk_complete = false;
686 }
687 }
688 if (!wrk_complete && mbhc->btn_press_intr) {
689 pr_debug("%s: Can be slow insertion of headphone\n", __func__);
690 wcd_cancel_btn_work(mbhc);
Vatsal Buchad6d62b82017-12-12 14:22:31 +0530691 /* Report as headphone only if previously
692 * not reported as lineout
693 */
694 if (!mbhc->force_linein)
695 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530696 }
697 /*
698 * If plug_tye is headset, we might have already reported either in
699 * detect_plug-type or in above while loop, no need to report again
700 */
701 if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
702 (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) {
703 pr_debug("%s: plug_type:0x%x already reported\n",
704 __func__, mbhc->current_plug);
705 goto enable_supply;
706 }
707
708 if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH &&
709 (!det_extn_cable_en)) {
710 if (wcd_is_special_headset(mbhc)) {
711 pr_debug("%s: Special headset found %d\n",
712 __func__, plug_type);
713 plug_type = MBHC_PLUG_TYPE_HEADSET;
714 goto report;
715 }
716 }
717
718report:
719 if (wcd_swch_level_remove(mbhc)) {
720 pr_debug("%s: Switch level is low\n", __func__);
721 goto exit;
722 }
723 if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) {
724 pr_debug("%s: insertion of headphone with swap\n", __func__);
725 wcd_cancel_btn_work(mbhc);
726 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
727 }
728 pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
729 __func__, plug_type, wrk_complete,
730 mbhc->btn_press_intr);
731 WCD_MBHC_RSC_LOCK(mbhc);
732 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
733 WCD_MBHC_RSC_UNLOCK(mbhc);
734enable_supply:
735 if (mbhc->mbhc_cb->mbhc_micbias_control)
736 wcd_mbhc_update_fsm_source(mbhc, plug_type);
737 else
738 wcd_enable_mbhc_supply(mbhc, plug_type);
739exit:
740 if (mbhc->mbhc_cb->mbhc_micbias_control &&
741 !mbhc->micbias_enable)
742 mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
743 MICB_DISABLE);
744
745 /*
746 * If plug type is corrected from special headset to headphone,
747 * clear the micbias enable flag, set micbias back to 1.8V and
748 * disable micbias.
749 */
750 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
751 mbhc->micbias_enable) {
752 if (mbhc->mbhc_cb->mbhc_micbias_control)
753 mbhc->mbhc_cb->mbhc_micbias_control(
754 codec, MIC_BIAS_2,
755 MICB_DISABLE);
756 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
757 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
758 codec,
759 MIC_BIAS_2, false);
760 if (mbhc->mbhc_cb->set_micbias_value) {
761 mbhc->mbhc_cb->set_micbias_value(codec);
762 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
763 }
764 mbhc->micbias_enable = false;
765 }
766
767 if (mbhc->mbhc_cb->micbias_enable_status) {
768 micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
769 MIC_BIAS_1);
770 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
771 MIC_BIAS_2);
772 }
773
774 if (mbhc->mbhc_cfg->detect_extn_cable &&
775 ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
776 (plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
777 !mbhc->hs_detect_work_stop) {
778 WCD_MBHC_RSC_LOCK(mbhc);
779 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true);
780 WCD_MBHC_RSC_UNLOCK(mbhc);
781 }
782 if (mbhc->mbhc_cb->set_cap_mode)
783 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2);
784
785 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
786 mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true);
787
788 mbhc->mbhc_cb->lock_sleep(mbhc, false);
789 pr_debug("%s: leave\n", __func__);
790}
791
792static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)
793{
794 struct wcd_mbhc *mbhc = data;
795 u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0;
796 static u16 hphl_trigerred;
797 static u16 mic_trigerred;
798 unsigned long timeout;
799 bool removed = true;
800 int retry = 0;
Meng Wang6f901622017-09-19 10:21:57 +0800801 bool hphpa_on = false;
802 u8 moisture_status = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530803
804 pr_debug("%s: enter\n", __func__);
805
806 WCD_MBHC_RSC_LOCK(mbhc);
807
808 timeout = jiffies +
809 msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
810 do {
811 retry++;
812 /*
813 * read the result register every 10ms to look for
814 * any change in HS_COMP_RESULT bit
815 */
816 usleep_range(10000, 10100);
817 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
818 pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n",
819 __func__, hs_comp_result);
820 if ((!hs_comp_result) &&
821 retry > FAKE_REM_RETRY_ATTEMPTS) {
822 removed = false;
823 break;
824 }
825 } while (!time_after(jiffies, timeout));
826
827 if (wcd_swch_level_remove(mbhc)) {
828 pr_debug("%s: Switch level is low ", __func__);
829 goto exit;
830 }
831 pr_debug("%s: headset %s actually removed\n", __func__,
832 removed ? "" : "not ");
833
834 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
835 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
836 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
837
838 if (removed) {
Meng Wang6f901622017-09-19 10:21:57 +0800839 if (mbhc->mbhc_cfg->moisture_en) {
840 if (mbhc->mbhc_cb->hph_pa_on_status)
841 if (
842 mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) {
843 hphpa_on = true;
844 WCD_MBHC_REG_UPDATE_BITS(
845 WCD_MBHC_HPHL_PA_EN, 0);
846 WCD_MBHC_REG_UPDATE_BITS(
847 WCD_MBHC_HPH_PA_EN, 0);
848 }
849
850 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1);
851 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1);
852 /* wait for 50ms to get moisture status */
853 usleep_range(50000, 50100);
854
855 WCD_MBHC_REG_READ(
856 WCD_MBHC_MOISTURE_STATUS, moisture_status);
857 }
858
859 if (mbhc->mbhc_cfg->moisture_en && !moisture_status) {
860 pr_debug("%s: moisture present in jack\n", __func__);
861 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
862 WCD_MBHC_REG_UPDATE_BITS(
863 WCD_MBHC_MECH_DETECTION_TYPE, 1);
864 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
865 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
866 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
867 mbhc->btn_press_intr = false;
868 mbhc->is_btn_press = false;
869 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
870 wcd_mbhc_report_plug(
871 mbhc, 0, SND_JACK_HEADSET);
872 else if (mbhc->current_plug ==
873 MBHC_PLUG_TYPE_HEADPHONE)
874 wcd_mbhc_report_plug(
875 mbhc, 0, SND_JACK_HEADPHONE);
876 else if (mbhc->current_plug ==
877 MBHC_PLUG_TYPE_GND_MIC_SWAP)
878 wcd_mbhc_report_plug(
879 mbhc, 0, SND_JACK_UNSUPPORTED);
880 else if (mbhc->current_plug ==
881 MBHC_PLUG_TYPE_HIGH_HPH)
882 wcd_mbhc_report_plug(
883 mbhc, 0, SND_JACK_LINEOUT);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530884 } else {
Meng Wang6f901622017-09-19 10:21:57 +0800885 if (!(hphl_sch && mic_sch && hs_comp_result)) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530886 /*
887 * extension cable is still plugged in
888 * report it as LINEOUT device
889 */
890 goto report_unplug;
Meng Wang6f901622017-09-19 10:21:57 +0800891 } else {
892 if (!mic_sch) {
893 mic_trigerred++;
894 pr_debug(
895 "%s: Removal MIC trigerred %d\n",
896 __func__, mic_trigerred);
897 }
898 if (!hphl_sch) {
899 hphl_trigerred++;
900 pr_debug(
901 "%s: Removal HPHL trigerred %d\n",
902 __func__, hphl_trigerred);
903 }
904 if (mic_trigerred && hphl_trigerred) {
905 /*
906 * extension cable is still plugged in
907 * report it as LINEOUT device
908 */
909 goto report_unplug;
910 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530911 }
912 }
913 }
914exit:
915 WCD_MBHC_RSC_UNLOCK(mbhc);
916 pr_debug("%s: leave\n", __func__);
917 return IRQ_HANDLED;
918
919report_unplug:
920 wcd_mbhc_elec_hs_report_unplug(mbhc);
Meng Wang6f901622017-09-19 10:21:57 +0800921 if (hphpa_on) {
922 hphpa_on = false;
923 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
924 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1);
925 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530926 hphl_trigerred = 0;
927 mic_trigerred = 0;
928 WCD_MBHC_RSC_UNLOCK(mbhc);
929 pr_debug("%s: leave\n", __func__);
930 return IRQ_HANDLED;
931}
932
933static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data)
934{
935 struct wcd_mbhc *mbhc = data;
936 bool detection_type = 0, hphl_sch = 0, mic_sch = 0;
937 u16 elect_result = 0;
938 static u16 hphl_trigerred;
939 static u16 mic_trigerred;
940
941 pr_debug("%s: enter\n", __func__);
942 if (!mbhc->mbhc_cfg->detect_extn_cable) {
943 pr_debug("%s: Returning as Extension cable feature not enabled\n",
944 __func__);
945 return IRQ_HANDLED;
946 }
947 WCD_MBHC_RSC_LOCK(mbhc);
948
949 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type);
950 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result);
951
952 pr_debug("%s: detection_type %d, elect_result %x\n", __func__,
953 detection_type, elect_result);
954 if (detection_type) {
955 /* check if both Left and MIC Schmitt triggers are triggered */
956 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
957 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
958 if (hphl_sch && mic_sch) {
959 /* Go for plug type determination */
960 pr_debug("%s: Go for plug type determination\n",
961 __func__);
962 goto determine_plug;
963
964 } else {
965 if (mic_sch) {
966 mic_trigerred++;
967 pr_debug("%s: Insertion MIC trigerred %d\n",
968 __func__, mic_trigerred);
969 WCD_MBHC_REG_UPDATE_BITS(
970 WCD_MBHC_ELECT_SCHMT_ISRC,
971 0);
972 msleep(20);
973 WCD_MBHC_REG_UPDATE_BITS(
974 WCD_MBHC_ELECT_SCHMT_ISRC,
975 1);
976 }
977 if (hphl_sch) {
978 hphl_trigerred++;
979 pr_debug("%s: Insertion HPHL trigerred %d\n",
980 __func__, hphl_trigerred);
981 }
982 if (mic_trigerred && hphl_trigerred) {
983 /* Go for plug type determination */
984 pr_debug("%s: Go for plug type determination\n",
985 __func__);
986 goto determine_plug;
987 }
988 }
989 }
990 WCD_MBHC_RSC_UNLOCK(mbhc);
991 pr_debug("%s: leave\n", __func__);
992 return IRQ_HANDLED;
993
994determine_plug:
995 /*
996 * Disable HPHL trigger and MIC Schmitt triggers.
997 * Setup for insertion detection.
998 */
999 pr_debug("%s: Disable insertion interrupt\n", __func__);
1000 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
1001 false);
1002
1003 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
1004 hphl_trigerred = 0;
1005 mic_trigerred = 0;
1006 mbhc->is_extn_cable = true;
1007 mbhc->btn_press_intr = false;
1008 wcd_mbhc_detect_plug_type(mbhc);
1009 WCD_MBHC_RSC_UNLOCK(mbhc);
1010 pr_debug("%s: leave\n", __func__);
1011 return IRQ_HANDLED;
1012}
1013
1014static struct wcd_mbhc_fn mbhc_fn = {
1015 .wcd_mbhc_hs_ins_irq = wcd_mbhc_hs_ins_irq,
1016 .wcd_mbhc_hs_rem_irq = wcd_mbhc_hs_rem_irq,
1017 .wcd_mbhc_detect_plug_type = wcd_mbhc_detect_plug_type,
1018 .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_detect_anc_plug_type,
1019 .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug,
1020};
1021
1022/* Function: wcd_mbhc_legacy_init
1023 * @mbhc: MBHC function pointer
1024 * Description: Initialize MBHC legacy based function pointers to MBHC structure
1025 */
1026void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc)
1027{
1028 if (!mbhc) {
1029 pr_err("%s: mbhc is NULL\n", __func__);
1030 return;
1031 }
1032 mbhc->mbhc_fn = &mbhc_fn;
1033 INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
1034}
1035EXPORT_SYMBOL(wcd_mbhc_legacy_init);