blob: 9ebdeca01fffa19e61c47ad6d4d8c66084ef98ca [file] [log] [blame]
Ramprasad Katkamdeec3ba2018-01-25 14:57:20 +05301/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302 *
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);
Ramprasad Katkamdeec3ba2018-01-25 14:57:20 +0530536 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
537 mbhc->micbias_enable) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530538 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
539 mbhc->codec, MIC_BIAS_2, false);
540 if (mbhc->mbhc_cb->set_micbias_value)
541 mbhc->mbhc_cb->set_micbias_value(
542 mbhc->codec);
543 mbhc->micbias_enable = false;
544 }
545 goto exit;
546 }
547 if (mbhc->btn_press_intr) {
548 wcd_cancel_btn_work(mbhc);
549 mbhc->btn_press_intr = false;
550 }
551 /* Toggle FSM */
552 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
553 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
554
555 /* allow sometime and re-check stop requested again */
556 msleep(20);
557 if (mbhc->hs_detect_work_stop) {
558 pr_debug("%s: stop requested: %d\n", __func__,
559 mbhc->hs_detect_work_stop);
560 wcd_enable_curr_micbias(mbhc,
561 WCD_MBHC_EN_NONE);
Ramprasad Katkamdeec3ba2018-01-25 14:57:20 +0530562 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
563 mbhc->micbias_enable) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530564 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
565 mbhc->codec, MIC_BIAS_2, false);
566 if (mbhc->mbhc_cb->set_micbias_value)
567 mbhc->mbhc_cb->set_micbias_value(
568 mbhc->codec);
569 mbhc->micbias_enable = false;
570 }
571 goto exit;
572 }
573 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
574
575 pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res);
576 if (mbhc->mbhc_cb->hph_pa_on_status)
577 is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec);
578
579 /*
580 * instead of hogging system by contineous polling, wait for
581 * sometime and re-check stop request again.
582 */
583 msleep(180);
584 if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) {
585 spl_hs = wcd_mbhc_check_for_spl_headset(mbhc,
586 &spl_hs_count);
587
588 if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
589 hs_comp_res = 0;
590 spl_hs = true;
591 mbhc->micbias_enable = true;
592 }
593 }
594
595 if ((!hs_comp_res) && (!is_pa_on)) {
596 /* Check for cross connection*/
597 ret = wcd_check_cross_conn(mbhc);
598 if (ret < 0) {
599 continue;
600 } else if (ret > 0) {
601 pt_gnd_mic_swap_cnt++;
602 no_gnd_mic_swap_cnt = 0;
603 if (pt_gnd_mic_swap_cnt <
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800604 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530605 continue;
606 } else if (pt_gnd_mic_swap_cnt >
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800607 mbhc->swap_thr) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530608 /*
609 * This is due to GND/MIC switch didn't
610 * work, Report unsupported plug.
611 */
612 pr_debug("%s: switch didn't work\n",
613 __func__);
614 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
615 goto report;
616 } else {
617 plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
618 }
619 } else {
620 no_gnd_mic_swap_cnt++;
621 pt_gnd_mic_swap_cnt = 0;
622 plug_type = MBHC_PLUG_TYPE_HEADSET;
623 if ((no_gnd_mic_swap_cnt <
624 GND_MIC_SWAP_THRESHOLD) &&
625 (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
626 continue;
627 } else {
628 no_gnd_mic_swap_cnt = 0;
629 }
630 }
Karthikeyan Mani5392d802017-11-08 20:32:38 -0800631 if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530632 (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
633 /*
634 * if switch is toggled, check again,
635 * otherwise report unsupported plug
636 */
637 if (mbhc->mbhc_cfg->swap_gnd_mic &&
638 mbhc->mbhc_cfg->swap_gnd_mic(codec,
639 true)) {
640 pr_debug("%s: US_EU gpio present,flip switch\n"
641 , __func__);
642 continue;
643 }
644 }
645 }
646
647 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
648 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
649 if (hs_comp_res && !(hphl_sch || mic_sch)) {
650 pr_debug("%s: cable is extension cable\n", __func__);
651 plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
652 wrk_complete = true;
653 } else {
654 pr_debug("%s: cable might be headset: %d\n", __func__,
655 plug_type);
656 if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
657 plug_type = MBHC_PLUG_TYPE_HEADSET;
658 if (!spl_hs_reported &&
659 spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
660 spl_hs_reported = true;
661 WCD_MBHC_RSC_LOCK(mbhc);
662 wcd_mbhc_find_plug_and_report(mbhc,
663 plug_type);
664 WCD_MBHC_RSC_UNLOCK(mbhc);
665 continue;
666 } else if (spl_hs_reported)
667 continue;
668 /*
669 * Report headset only if not already reported
670 * and if there is not button press without
671 * release
672 */
673 if (((mbhc->current_plug !=
674 MBHC_PLUG_TYPE_HEADSET) &&
675 (mbhc->current_plug !=
676 MBHC_PLUG_TYPE_ANC_HEADPHONE)) &&
677 !wcd_swch_level_remove(mbhc) &&
678 !mbhc->btn_press_intr) {
679 pr_debug("%s: cable is %sheadset\n",
680 __func__,
681 ((spl_hs_count ==
682 WCD_MBHC_SPL_HS_CNT) ?
683 "special ":""));
684 goto report;
685 }
686 }
687 wrk_complete = false;
688 }
689 }
690 if (!wrk_complete && mbhc->btn_press_intr) {
691 pr_debug("%s: Can be slow insertion of headphone\n", __func__);
692 wcd_cancel_btn_work(mbhc);
Vatsal Buchad6d62b82017-12-12 14:22:31 +0530693 /* Report as headphone only if previously
694 * not reported as lineout
695 */
696 if (!mbhc->force_linein)
697 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530698 }
699 /*
700 * If plug_tye is headset, we might have already reported either in
701 * detect_plug-type or in above while loop, no need to report again
702 */
703 if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
704 (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) {
705 pr_debug("%s: plug_type:0x%x already reported\n",
706 __func__, mbhc->current_plug);
707 goto enable_supply;
708 }
709
710 if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH &&
711 (!det_extn_cable_en)) {
712 if (wcd_is_special_headset(mbhc)) {
713 pr_debug("%s: Special headset found %d\n",
714 __func__, plug_type);
715 plug_type = MBHC_PLUG_TYPE_HEADSET;
716 goto report;
717 }
718 }
719
720report:
721 if (wcd_swch_level_remove(mbhc)) {
722 pr_debug("%s: Switch level is low\n", __func__);
723 goto exit;
724 }
725 if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) {
726 pr_debug("%s: insertion of headphone with swap\n", __func__);
727 wcd_cancel_btn_work(mbhc);
728 plug_type = MBHC_PLUG_TYPE_HEADPHONE;
729 }
730 pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
731 __func__, plug_type, wrk_complete,
732 mbhc->btn_press_intr);
733 WCD_MBHC_RSC_LOCK(mbhc);
734 wcd_mbhc_find_plug_and_report(mbhc, plug_type);
735 WCD_MBHC_RSC_UNLOCK(mbhc);
736enable_supply:
737 if (mbhc->mbhc_cb->mbhc_micbias_control)
738 wcd_mbhc_update_fsm_source(mbhc, plug_type);
739 else
740 wcd_enable_mbhc_supply(mbhc, plug_type);
741exit:
742 if (mbhc->mbhc_cb->mbhc_micbias_control &&
743 !mbhc->micbias_enable)
744 mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
745 MICB_DISABLE);
746
747 /*
748 * If plug type is corrected from special headset to headphone,
749 * clear the micbias enable flag, set micbias back to 1.8V and
750 * disable micbias.
751 */
752 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
753 mbhc->micbias_enable) {
754 if (mbhc->mbhc_cb->mbhc_micbias_control)
755 mbhc->mbhc_cb->mbhc_micbias_control(
756 codec, MIC_BIAS_2,
757 MICB_DISABLE);
758 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
759 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
760 codec,
761 MIC_BIAS_2, false);
762 if (mbhc->mbhc_cb->set_micbias_value) {
763 mbhc->mbhc_cb->set_micbias_value(codec);
764 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
765 }
766 mbhc->micbias_enable = false;
767 }
768
769 if (mbhc->mbhc_cb->micbias_enable_status) {
770 micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
771 MIC_BIAS_1);
772 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
773 MIC_BIAS_2);
774 }
775
776 if (mbhc->mbhc_cfg->detect_extn_cable &&
777 ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
778 (plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
779 !mbhc->hs_detect_work_stop) {
780 WCD_MBHC_RSC_LOCK(mbhc);
781 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true);
782 WCD_MBHC_RSC_UNLOCK(mbhc);
783 }
784 if (mbhc->mbhc_cb->set_cap_mode)
785 mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2);
786
787 if (mbhc->mbhc_cb->hph_pull_down_ctrl)
788 mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true);
789
790 mbhc->mbhc_cb->lock_sleep(mbhc, false);
791 pr_debug("%s: leave\n", __func__);
792}
793
794static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)
795{
796 struct wcd_mbhc *mbhc = data;
797 u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0;
798 static u16 hphl_trigerred;
799 static u16 mic_trigerred;
800 unsigned long timeout;
801 bool removed = true;
802 int retry = 0;
Meng Wang6f901622017-09-19 10:21:57 +0800803 bool hphpa_on = false;
804 u8 moisture_status = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530805
806 pr_debug("%s: enter\n", __func__);
807
808 WCD_MBHC_RSC_LOCK(mbhc);
809
810 timeout = jiffies +
811 msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
812 do {
813 retry++;
814 /*
815 * read the result register every 10ms to look for
816 * any change in HS_COMP_RESULT bit
817 */
818 usleep_range(10000, 10100);
819 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
820 pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n",
821 __func__, hs_comp_result);
822 if ((!hs_comp_result) &&
823 retry > FAKE_REM_RETRY_ATTEMPTS) {
824 removed = false;
825 break;
826 }
827 } while (!time_after(jiffies, timeout));
828
829 if (wcd_swch_level_remove(mbhc)) {
830 pr_debug("%s: Switch level is low ", __func__);
831 goto exit;
832 }
833 pr_debug("%s: headset %s actually removed\n", __func__,
834 removed ? "" : "not ");
835
836 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
837 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
838 WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
839
840 if (removed) {
Meng Wang6f901622017-09-19 10:21:57 +0800841 if (mbhc->mbhc_cfg->moisture_en) {
842 if (mbhc->mbhc_cb->hph_pa_on_status)
843 if (
844 mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) {
845 hphpa_on = true;
846 WCD_MBHC_REG_UPDATE_BITS(
847 WCD_MBHC_HPHL_PA_EN, 0);
848 WCD_MBHC_REG_UPDATE_BITS(
849 WCD_MBHC_HPH_PA_EN, 0);
850 }
851
852 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1);
853 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1);
854 /* wait for 50ms to get moisture status */
855 usleep_range(50000, 50100);
856
857 WCD_MBHC_REG_READ(
858 WCD_MBHC_MOISTURE_STATUS, moisture_status);
859 }
860
861 if (mbhc->mbhc_cfg->moisture_en && !moisture_status) {
862 pr_debug("%s: moisture present in jack\n", __func__);
863 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
864 WCD_MBHC_REG_UPDATE_BITS(
865 WCD_MBHC_MECH_DETECTION_TYPE, 1);
866 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
867 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
868 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
869 mbhc->btn_press_intr = false;
870 mbhc->is_btn_press = false;
871 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
872 wcd_mbhc_report_plug(
873 mbhc, 0, SND_JACK_HEADSET);
874 else if (mbhc->current_plug ==
875 MBHC_PLUG_TYPE_HEADPHONE)
876 wcd_mbhc_report_plug(
877 mbhc, 0, SND_JACK_HEADPHONE);
878 else if (mbhc->current_plug ==
879 MBHC_PLUG_TYPE_GND_MIC_SWAP)
880 wcd_mbhc_report_plug(
881 mbhc, 0, SND_JACK_UNSUPPORTED);
882 else if (mbhc->current_plug ==
883 MBHC_PLUG_TYPE_HIGH_HPH)
884 wcd_mbhc_report_plug(
885 mbhc, 0, SND_JACK_LINEOUT);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530886 } else {
Meng Wang6f901622017-09-19 10:21:57 +0800887 if (!(hphl_sch && mic_sch && hs_comp_result)) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530888 /*
889 * extension cable is still plugged in
890 * report it as LINEOUT device
891 */
892 goto report_unplug;
Meng Wang6f901622017-09-19 10:21:57 +0800893 } else {
894 if (!mic_sch) {
895 mic_trigerred++;
896 pr_debug(
897 "%s: Removal MIC trigerred %d\n",
898 __func__, mic_trigerred);
899 }
900 if (!hphl_sch) {
901 hphl_trigerred++;
902 pr_debug(
903 "%s: Removal HPHL trigerred %d\n",
904 __func__, hphl_trigerred);
905 }
906 if (mic_trigerred && hphl_trigerred) {
907 /*
908 * extension cable is still plugged in
909 * report it as LINEOUT device
910 */
911 goto report_unplug;
912 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530913 }
914 }
915 }
916exit:
917 WCD_MBHC_RSC_UNLOCK(mbhc);
918 pr_debug("%s: leave\n", __func__);
919 return IRQ_HANDLED;
920
921report_unplug:
922 wcd_mbhc_elec_hs_report_unplug(mbhc);
Meng Wang6f901622017-09-19 10:21:57 +0800923 if (hphpa_on) {
924 hphpa_on = false;
925 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
926 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1);
927 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530928 hphl_trigerred = 0;
929 mic_trigerred = 0;
930 WCD_MBHC_RSC_UNLOCK(mbhc);
931 pr_debug("%s: leave\n", __func__);
932 return IRQ_HANDLED;
933}
934
935static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data)
936{
937 struct wcd_mbhc *mbhc = data;
938 bool detection_type = 0, hphl_sch = 0, mic_sch = 0;
939 u16 elect_result = 0;
940 static u16 hphl_trigerred;
941 static u16 mic_trigerred;
942
943 pr_debug("%s: enter\n", __func__);
944 if (!mbhc->mbhc_cfg->detect_extn_cable) {
945 pr_debug("%s: Returning as Extension cable feature not enabled\n",
946 __func__);
947 return IRQ_HANDLED;
948 }
949 WCD_MBHC_RSC_LOCK(mbhc);
950
951 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type);
952 WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result);
953
954 pr_debug("%s: detection_type %d, elect_result %x\n", __func__,
955 detection_type, elect_result);
956 if (detection_type) {
957 /* check if both Left and MIC Schmitt triggers are triggered */
958 WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
959 WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
960 if (hphl_sch && mic_sch) {
961 /* Go for plug type determination */
962 pr_debug("%s: Go for plug type determination\n",
963 __func__);
964 goto determine_plug;
965
966 } else {
967 if (mic_sch) {
968 mic_trigerred++;
969 pr_debug("%s: Insertion MIC trigerred %d\n",
970 __func__, mic_trigerred);
971 WCD_MBHC_REG_UPDATE_BITS(
972 WCD_MBHC_ELECT_SCHMT_ISRC,
973 0);
974 msleep(20);
975 WCD_MBHC_REG_UPDATE_BITS(
976 WCD_MBHC_ELECT_SCHMT_ISRC,
977 1);
978 }
979 if (hphl_sch) {
980 hphl_trigerred++;
981 pr_debug("%s: Insertion HPHL trigerred %d\n",
982 __func__, hphl_trigerred);
983 }
984 if (mic_trigerred && hphl_trigerred) {
985 /* Go for plug type determination */
986 pr_debug("%s: Go for plug type determination\n",
987 __func__);
988 goto determine_plug;
989 }
990 }
991 }
992 WCD_MBHC_RSC_UNLOCK(mbhc);
993 pr_debug("%s: leave\n", __func__);
994 return IRQ_HANDLED;
995
996determine_plug:
997 /*
998 * Disable HPHL trigger and MIC Schmitt triggers.
999 * Setup for insertion detection.
1000 */
1001 pr_debug("%s: Disable insertion interrupt\n", __func__);
1002 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
1003 false);
1004
1005 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
1006 hphl_trigerred = 0;
1007 mic_trigerred = 0;
1008 mbhc->is_extn_cable = true;
1009 mbhc->btn_press_intr = false;
1010 wcd_mbhc_detect_plug_type(mbhc);
1011 WCD_MBHC_RSC_UNLOCK(mbhc);
1012 pr_debug("%s: leave\n", __func__);
1013 return IRQ_HANDLED;
1014}
1015
1016static struct wcd_mbhc_fn mbhc_fn = {
1017 .wcd_mbhc_hs_ins_irq = wcd_mbhc_hs_ins_irq,
1018 .wcd_mbhc_hs_rem_irq = wcd_mbhc_hs_rem_irq,
1019 .wcd_mbhc_detect_plug_type = wcd_mbhc_detect_plug_type,
1020 .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_detect_anc_plug_type,
1021 .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug,
1022};
1023
1024/* Function: wcd_mbhc_legacy_init
1025 * @mbhc: MBHC function pointer
1026 * Description: Initialize MBHC legacy based function pointers to MBHC structure
1027 */
1028void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc)
1029{
1030 if (!mbhc) {
1031 pr_err("%s: mbhc is NULL\n", __func__);
1032 return;
1033 }
1034 mbhc->mbhc_fn = &mbhc_fn;
1035 INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
1036}
1037EXPORT_SYMBOL(wcd_mbhc_legacy_init);