blob: 01544aab9c4304c43c2a34522c18edc2ef04ecdf [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Vatsal Buchae1728c52019-01-17 17:24:55 +05302/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05303 */
4#include <linux/module.h>
5#include <linux/init.h>
6#include <linux/slab.h>
7#include <linux/of_gpio.h>
8#include <linux/platform_device.h>
9#include <linux/device.h>
10#include <linux/printk.h>
11#include <linux/ratelimit.h>
12#include <linux/list.h>
13#include <linux/bitops.h>
14#include <linux/delay.h>
15#include <linux/pm_runtime.h>
16#include <linux/kernel.h>
17#include <linux/input.h>
18#include <linux/firmware.h>
19#include <linux/completion.h>
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -080020#include <linux/soc/qcom/fsa4480-i2c.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053021#include <sound/soc.h>
22#include <sound/jack.h>
Meng Wang11a25cf2018-10-31 14:11:26 +080023#include <asoc/msm-cdc-pinctrl.h>
24#include <asoc/wcdcal-hwdep.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053025#include "wcd-mbhc-legacy.h"
26#include "wcd-mbhc-adc.h"
Meng Wang11a25cf2018-10-31 14:11:26 +080027#include <asoc/wcd-mbhc-v2-api.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053028
29void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
30 struct snd_soc_jack *jack, int status, int mask)
31{
32 snd_soc_jack_report(jack, status, mask);
33}
34EXPORT_SYMBOL(wcd_mbhc_jack_report);
35
36static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status,
37 int irq)
38{
Meng Wang15c825d2018-09-06 10:49:18 +080039 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053040
Meng Wang15c825d2018-09-06 10:49:18 +080041 dev_dbg(component->dev, "%s: clear ocp status %x\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053042 __func__, jack_status);
43
44 if (mbhc->hph_status & jack_status) {
45 mbhc->hph_status &= ~jack_status;
46 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
47 mbhc->hph_status, WCD_MBHC_JACK_MASK);
48 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
49 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
50 /*
51 * reset retry counter as PA is turned off signifying
52 * start of new OCP detection session
53 */
54 if (mbhc->intr_ids->hph_left_ocp)
55 mbhc->hphlocp_cnt = 0;
56 else
57 mbhc->hphrocp_cnt = 0;
Meng Wang15c825d2018-09-06 10:49:18 +080058 mbhc->mbhc_cb->irq_control(component, irq, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053059 }
60}
61
62static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
63{
64 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
65 mbhc->intr_ids->hph_right_ocp);
66}
67
68static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
69{
70 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
71 mbhc->intr_ids->hph_left_ocp);
72}
73
74static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
75{
76 struct wcd_mbhc_plug_type_cfg *plug_type_cfg;
Meng Wang15c825d2018-09-06 10:49:18 +080077 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053078 u32 reg_val;
79
80 plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
81 reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
82
Meng Wang15c825d2018-09-06 10:49:18 +080083 dev_dbg(component->dev, "%s: reg_val = %x\n", __func__, reg_val);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053084 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val);
85}
86
87static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
88{
89 struct wcd_mbhc_btn_detect_cfg *btn_det;
Meng Wang15c825d2018-09-06 10:49:18 +080090 struct snd_soc_component *component = mbhc->component;
91 struct snd_soc_card *card = component->card;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053092 s16 *btn_low, *btn_high;
93
94 if (mbhc->mbhc_cfg->calibration == NULL) {
95 dev_err(card->dev, "%s: calibration data is NULL\n", __func__);
96 return;
97 }
98
99 btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
100 btn_low = btn_det->_v_btn_low;
101 btn_high = ((void *)&btn_det->_v_btn_low) +
102 (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn);
103
Meng Wang15c825d2018-09-06 10:49:18 +0800104 mbhc->mbhc_cb->set_btn_thr(component, btn_low, btn_high,
105 btn_det->num_btn, micbias);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530106}
107
108void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc,
109 const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
110{
111
112 /*
113 * Some codecs handle micbias/pullup enablement in codec
114 * drivers itself and micbias is not needed for regular
115 * plug type detection. So if micbias_control callback function
116 * is defined, just return.
117 */
118 if (mbhc->mbhc_cb->mbhc_micbias_control)
119 return;
120
121 pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en);
122
123 switch (cs_mb_en) {
124 case WCD_MBHC_EN_CS:
125 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
126 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
127 /* Program Button threshold registers as per CS */
128 wcd_program_btn_threshold(mbhc, false);
129 break;
130 case WCD_MBHC_EN_MB:
131 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
132 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
133
134 /* Disable PULL_UP_EN & enable MICBIAS */
135 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2);
136 /* Program Button threshold registers as per MICBIAS */
137 wcd_program_btn_threshold(mbhc, true);
138 break;
139 case WCD_MBHC_EN_PULLUP:
140 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
141 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
142 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1);
143 /* Program Button threshold registers as per MICBIAS */
144 wcd_program_btn_threshold(mbhc, true);
145 break;
146 case WCD_MBHC_EN_NONE:
147 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
148 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
149 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
150 break;
151 default:
152 pr_debug("%s: Invalid parameter", __func__);
153 break;
154 }
155
156 pr_debug("%s: exit\n", __func__);
157}
158EXPORT_SYMBOL(wcd_enable_curr_micbias);
159
160static const char *wcd_mbhc_get_event_string(int event)
161{
162 switch (event) {
163 case WCD_EVENT_PRE_MICBIAS_2_OFF:
164 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF);
165 case WCD_EVENT_POST_MICBIAS_2_OFF:
166 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF);
167 case WCD_EVENT_PRE_MICBIAS_2_ON:
168 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON);
169 case WCD_EVENT_POST_MICBIAS_2_ON:
170 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON);
171 case WCD_EVENT_PRE_HPHL_PA_ON:
172 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON);
173 case WCD_EVENT_POST_HPHL_PA_OFF:
174 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF);
175 case WCD_EVENT_PRE_HPHR_PA_ON:
176 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON);
177 case WCD_EVENT_POST_HPHR_PA_OFF:
178 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF);
179 case WCD_EVENT_PRE_HPHR_PA_OFF:
180 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF);
181 case WCD_EVENT_PRE_HPHL_PA_OFF:
182 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF);
183 case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
184 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
185 case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON:
186 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON);
187 case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
188 return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
189 case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF:
190 return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF);
191 case WCD_EVENT_OCP_OFF:
192 return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF);
193 case WCD_EVENT_OCP_ON:
194 return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON);
195 case WCD_EVENT_INVALID:
196 default:
197 return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID);
198 }
199}
200
201static int wcd_event_notify(struct notifier_block *self, unsigned long val,
202 void *data)
203{
204 struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data;
205 enum wcd_notify_event event = (enum wcd_notify_event)val;
Meng Wang15c825d2018-09-06 10:49:18 +0800206 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530207 bool micbias2 = false;
208 bool micbias1 = false;
209 u8 fsm_en = 0;
210
211 pr_debug("%s: event %s (%d)\n", __func__,
212 wcd_mbhc_get_event_string(event), event);
213 if (mbhc->mbhc_cb->micbias_enable_status) {
214 micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
215 MIC_BIAS_2);
216 micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
217 MIC_BIAS_1);
218 }
219 switch (event) {
220 /* MICBIAS usage change */
221 case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
222 mbhc->is_hs_recording = true;
223 pr_debug("%s: is_capture: %d\n", __func__,
224 mbhc->is_hs_recording);
225 break;
226 case WCD_EVENT_POST_MICBIAS_2_ON:
227 if (!mbhc->micbias_enable)
228 goto out_micb_en;
229 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
Meng Wang15c825d2018-09-06 10:49:18 +0800230 mbhc->mbhc_cb->mbhc_common_micb_ctrl(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530231 MBHC_COMMON_MICB_PRECHARGE,
232 true);
Meng Wang15c825d2018-09-06 10:49:18 +0800233 mbhc->mbhc_cb->mbhc_common_micb_ctrl(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530234 MBHC_COMMON_MICB_SET_VAL,
235 true);
236 /*
237 * Special headset needs MICBIAS as 2.7V so wait for
238 * 50 msec for the MICBIAS to reach 2.7 volts.
239 */
240 msleep(50);
241 }
242 if (mbhc->mbhc_cb->set_auto_zeroing)
Meng Wang15c825d2018-09-06 10:49:18 +0800243 mbhc->mbhc_cb->set_auto_zeroing(component, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530244 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
Meng Wang15c825d2018-09-06 10:49:18 +0800245 mbhc->mbhc_cb->mbhc_common_micb_ctrl(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530246 MBHC_COMMON_MICB_PRECHARGE,
247 false);
248out_micb_en:
249 /* Disable current source if micbias enabled */
250 if (mbhc->mbhc_cb->mbhc_micbias_control) {
251 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
252 if (fsm_en)
253 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
254 0);
255 } else {
256 mbhc->is_hs_recording = true;
257 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
258 }
259 /* configure cap settings properly when micbias is enabled */
260 if (mbhc->mbhc_cb->set_cap_mode)
Meng Wang15c825d2018-09-06 10:49:18 +0800261 mbhc->mbhc_cb->set_cap_mode(component, micbias1, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530262 break;
263 case WCD_EVENT_PRE_MICBIAS_2_OFF:
264 /*
265 * Before MICBIAS_2 is turned off, if FSM is enabled,
266 * make sure current source is enabled so as to detect
267 * button press/release events
268 */
269 if (mbhc->mbhc_cb->mbhc_micbias_control &&
270 !mbhc->micbias_enable) {
271 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
272 if (fsm_en)
273 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
274 3);
275 }
276 break;
277 /* MICBIAS usage change */
278 case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
279 mbhc->is_hs_recording = false;
280 pr_debug("%s: is_capture: %d\n", __func__,
281 mbhc->is_hs_recording);
282 break;
283 case WCD_EVENT_POST_MICBIAS_2_OFF:
284 if (!mbhc->mbhc_cb->mbhc_micbias_control)
285 mbhc->is_hs_recording = false;
286 if (mbhc->micbias_enable) {
287 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
288 break;
289 }
290
291 if (mbhc->mbhc_cb->set_auto_zeroing)
Meng Wang15c825d2018-09-06 10:49:18 +0800292 mbhc->mbhc_cb->set_auto_zeroing(component, false);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530293 if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
Meng Wang15c825d2018-09-06 10:49:18 +0800294 mbhc->mbhc_cb->set_micbias_value(component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530295 /* Enable PULL UP if PA's are enabled */
296 if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
297 (test_bit(WCD_MBHC_EVENT_PA_HPHR,
298 &mbhc->event_state)))
299 /* enable pullup and cs, disable mb */
300 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
301 else
302 /* enable current source and disable mb, pullup*/
303 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
304
305 /* configure cap settings properly when micbias is disabled */
306 if (mbhc->mbhc_cb->set_cap_mode)
Meng Wang15c825d2018-09-06 10:49:18 +0800307 mbhc->mbhc_cb->set_cap_mode(component, micbias1, false);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530308 break;
309 case WCD_EVENT_PRE_HPHL_PA_OFF:
310 mutex_lock(&mbhc->hphl_pa_lock);
311 break;
312 case WCD_EVENT_POST_HPHL_PA_OFF:
313 clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
314 if (mbhc->hph_status & SND_JACK_OC_HPHL)
315 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
316 clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
317 /* check if micbias is enabled */
318 if (micbias2)
319 /* Disable cs, pullup & enable micbias */
320 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
321 else
322 /* Disable micbias, pullup & enable cs */
323 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
324 mutex_unlock(&mbhc->hphl_pa_lock);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530325 clear_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530326 break;
327 case WCD_EVENT_PRE_HPHR_PA_OFF:
328 mutex_lock(&mbhc->hphr_pa_lock);
329 break;
330 case WCD_EVENT_POST_HPHR_PA_OFF:
331 clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
332 if (mbhc->hph_status & SND_JACK_OC_HPHR)
333 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
334 clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
335 /* check if micbias is enabled */
336 if (micbias2)
337 /* Disable cs, pullup & enable micbias */
338 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
339 else
340 /* Disable micbias, pullup & enable cs */
341 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
342 mutex_unlock(&mbhc->hphr_pa_lock);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530343 clear_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530344 break;
345 case WCD_EVENT_PRE_HPHL_PA_ON:
346 set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
347 /* check if micbias is enabled */
348 if (micbias2)
349 /* Disable cs, pullup & enable micbias */
350 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
351 else
352 /* Disable micbias, enable pullup & cs */
353 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
354 break;
355 case WCD_EVENT_PRE_HPHR_PA_ON:
356 set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
357 /* check if micbias is enabled */
358 if (micbias2)
359 /* Disable cs, pullup & enable micbias */
360 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
361 else
362 /* Disable micbias, enable pullup & cs */
363 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
364 break;
365 case WCD_EVENT_OCP_OFF:
Meng Wang15c825d2018-09-06 10:49:18 +0800366 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530367 mbhc->intr_ids->hph_left_ocp,
368 false);
369 break;
370 case WCD_EVENT_OCP_ON:
Meng Wang15c825d2018-09-06 10:49:18 +0800371 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530372 mbhc->intr_ids->hph_left_ocp,
373 true);
374 break;
375 default:
376 break;
377 }
378 return 0;
379}
380
381int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
382{
383 int r;
384
385 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
386 /*
387 * if scheduled mbhc.mbhc_btn_dwork is canceled from here,
388 * we have to unlock from here instead btn_work
389 */
390 if (r)
391 mbhc->mbhc_cb->lock_sleep(mbhc, false);
392 return r;
393}
394EXPORT_SYMBOL(wcd_cancel_btn_work);
395
396bool wcd_swch_level_remove(struct wcd_mbhc *mbhc)
397{
398 u16 result2 = 0;
399
400 WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2);
401 return (result2) ? true : false;
402}
403EXPORT_SYMBOL(wcd_swch_level_remove);
404
405static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
406{
407 bool pa_turned_on = false;
408 u8 wg_time = 0;
409
410 WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
411 wg_time += 1;
412
413 mutex_lock(&mbhc->hphr_pa_lock);
414 if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK,
415 &mbhc->hph_pa_dac_state)) {
416 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
417 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1);
418 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1);
419 pa_turned_on = true;
420 }
421 mutex_unlock(&mbhc->hphr_pa_lock);
422 mutex_lock(&mbhc->hphl_pa_lock);
423 if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK,
424 &mbhc->hph_pa_dac_state)) {
425 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
426 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
427 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1);
428 pa_turned_on = true;
429 }
430 mutex_unlock(&mbhc->hphl_pa_lock);
431
432 if (pa_turned_on) {
433 pr_debug("%s: PA was turned on by MBHC and not by DAPM\n",
434 __func__);
435 usleep_range(wg_time * 1000, wg_time * 1000 + 50);
436 }
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530437
438 if (test_and_clear_bit(WCD_MBHC_ANC0_OFF_ACK,
439 &mbhc->hph_anc_state)) {
440 usleep_range(20000, 20100);
441 pr_debug("%s: HPHL ANC clear flag and enable ANC_EN\n",
442 __func__);
443 if (mbhc->mbhc_cb->update_anc_state)
Meng Wang15c825d2018-09-06 10:49:18 +0800444 mbhc->mbhc_cb->update_anc_state(mbhc->component,
445 true, 0);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530446 }
447
448 if (test_and_clear_bit(WCD_MBHC_ANC1_OFF_ACK,
449 &mbhc->hph_anc_state)) {
450 usleep_range(20000, 20100);
451 pr_debug("%s: HPHR ANC clear flag and enable ANC_EN\n",
452 __func__);
453 if (mbhc->mbhc_cb->update_anc_state)
Meng Wang15c825d2018-09-06 10:49:18 +0800454 mbhc->mbhc_cb->update_anc_state(mbhc->component,
455 true, 1);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530456 }
457
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530458}
459
460static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc)
461{
462 bool hph_pa_on = false;
463
464 WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on);
465
466 return (hph_pa_on) ? true : false;
467}
468
469static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc)
470{
471 u8 wg_time = 0;
472
473 WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
474 wg_time += 1;
475
476 /* If headphone PA is on, check if userspace receives
477 * removal event to sync-up PA's state
478 */
479 if (wcd_mbhc_is_hph_pa_on(mbhc)) {
480 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
481 set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
482 set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
483 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0);
484 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0);
485 } else {
486 pr_debug("%s PA is off\n", __func__);
487 }
488 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0);
489 usleep_range(wg_time * 1000, wg_time * 1000 + 50);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530490
491
492 if (mbhc->mbhc_cb->is_anc_on && mbhc->mbhc_cb->is_anc_on(mbhc)) {
493 usleep_range(20000, 20100);
494 pr_debug("%s ANC is on, setting ANC_OFF_ACK\n", __func__);
495 set_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state);
496 set_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state);
497 if (mbhc->mbhc_cb->update_anc_state) {
Meng Wang15c825d2018-09-06 10:49:18 +0800498 mbhc->mbhc_cb->update_anc_state(mbhc->component,
499 false, 0);
500 mbhc->mbhc_cb->update_anc_state(mbhc->component,
501 false, 1);
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530502 } else {
503 pr_debug("%s ANC is off\n", __func__);
504 }
505 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530506}
507
508int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
509 uint32_t *zr)
510{
511 *zl = mbhc->zl;
512 *zr = mbhc->zr;
513
514 if (*zl && *zr)
515 return 0;
516 else
517 return -EINVAL;
518}
519EXPORT_SYMBOL(wcd_mbhc_get_impedance);
520
521void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type,
522 bool enable)
523{
524 int irq;
525
526 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
527
528 if (irq_type == WCD_MBHC_ELEC_HS_INS)
529 irq = mbhc->intr_ids->mbhc_hs_ins_intr;
530 else if (irq_type == WCD_MBHC_ELEC_HS_REM)
531 irq = mbhc->intr_ids->mbhc_hs_rem_intr;
532 else {
533 pr_debug("%s: irq_type: %d, enable: %d\n",
534 __func__, irq_type, enable);
535 return;
536 }
537
538 pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n",
539 __func__, irq, enable, mbhc->intr_status);
540 if ((test_bit(irq_type, &mbhc->intr_status)) != enable) {
Meng Wang15c825d2018-09-06 10:49:18 +0800541 mbhc->mbhc_cb->irq_control(mbhc->component, irq, enable);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530542 if (enable)
543 set_bit(irq_type, &mbhc->intr_status);
544 else
545 clear_bit(irq_type, &mbhc->intr_status);
546 }
547}
548EXPORT_SYMBOL(wcd_mbhc_hs_elec_irq);
549
Meng Wang6f901622017-09-19 10:21:57 +0800550void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530551 enum snd_jack_types jack_type)
552{
Meng Wang15c825d2018-09-06 10:49:18 +0800553 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530554 bool is_pa_on = false;
555 u8 fsm_en = 0;
556
557 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
558
559 pr_debug("%s: enter insertion %d hph_status %x\n",
560 __func__, insertion, mbhc->hph_status);
561 if (!insertion) {
562 /* Report removal */
563 mbhc->hph_status &= ~jack_type;
564 /*
565 * cancel possibly scheduled btn work and
566 * report release if we reported button press
567 */
568 if (wcd_cancel_btn_work(mbhc)) {
569 pr_debug("%s: button press is canceled\n", __func__);
570 } else if (mbhc->buttons_pressed) {
571 pr_debug("%s: release of button press%d\n",
572 __func__, jack_type);
573 wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0,
574 mbhc->buttons_pressed);
575 mbhc->buttons_pressed &=
576 ~WCD_MBHC_JACK_BUTTON_MASK;
577 }
578
579 if (mbhc->micbias_enable) {
580 if (mbhc->mbhc_cb->mbhc_micbias_control)
581 mbhc->mbhc_cb->mbhc_micbias_control(
Meng Wang15c825d2018-09-06 10:49:18 +0800582 component, MIC_BIAS_2,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530583 MICB_DISABLE);
584 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
585 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
Meng Wang15c825d2018-09-06 10:49:18 +0800586 component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530587 MIC_BIAS_2, false);
588 if (mbhc->mbhc_cb->set_micbias_value) {
Meng Wang15c825d2018-09-06 10:49:18 +0800589 mbhc->mbhc_cb->set_micbias_value(component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530590 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
591 }
592 mbhc->micbias_enable = false;
593 }
594
595 mbhc->hph_type = WCD_MBHC_HPH_NONE;
596 mbhc->zl = mbhc->zr = 0;
597 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
598 jack_type, mbhc->hph_status);
599 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
600 mbhc->hph_status, WCD_MBHC_JACK_MASK);
601 wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
602 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
603 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
604 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
Vatsal Buchad6d62b82017-12-12 14:22:31 +0530605 mbhc->force_linein = false;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530606 } else {
607 /*
608 * Report removal of current jack type.
609 * Headphone to headset shouldn't report headphone
610 * removal.
611 */
612 if (mbhc->mbhc_cfg->detect_extn_cable &&
613 (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH ||
614 jack_type == SND_JACK_LINEOUT) &&
615 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
616
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530617 if (mbhc->micbias_enable &&
618 mbhc->hph_status == SND_JACK_HEADSET) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530619 if (mbhc->mbhc_cb->mbhc_micbias_control)
620 mbhc->mbhc_cb->mbhc_micbias_control(
Meng Wang15c825d2018-09-06 10:49:18 +0800621 component, MIC_BIAS_2,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530622 MICB_DISABLE);
623 if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
624 mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
Meng Wang15c825d2018-09-06 10:49:18 +0800625 component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530626 MIC_BIAS_2, false);
627 if (mbhc->mbhc_cb->set_micbias_value) {
628 mbhc->mbhc_cb->set_micbias_value(
Meng Wang15c825d2018-09-06 10:49:18 +0800629 component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530630 WCD_MBHC_REG_UPDATE_BITS(
631 WCD_MBHC_MICB_CTRL, 0);
632 }
633 mbhc->micbias_enable = false;
634 }
635 mbhc->hph_type = WCD_MBHC_HPH_NONE;
636 mbhc->zl = mbhc->zr = 0;
637 pr_debug("%s: Reporting removal (%x)\n",
638 __func__, mbhc->hph_status);
639 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
640 0, WCD_MBHC_JACK_MASK);
641
642 if (mbhc->hph_status == SND_JACK_LINEOUT) {
643
644 pr_debug("%s: Enable micbias\n", __func__);
645 /* Disable current source and enable micbias */
646 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
647 pr_debug("%s: set up elec removal detection\n",
648 __func__);
649 usleep_range(200, 210);
650 wcd_mbhc_hs_elec_irq(mbhc,
651 WCD_MBHC_ELEC_HS_REM,
652 true);
653 }
654 mbhc->hph_status &= ~(SND_JACK_HEADSET |
655 SND_JACK_LINEOUT |
656 SND_JACK_ANC_HEADPHONE |
657 SND_JACK_UNSUPPORTED);
658 }
659
660 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
661 jack_type == SND_JACK_HEADPHONE)
662 mbhc->hph_status &= ~SND_JACK_HEADSET;
663
664 /* Report insertion */
665 if (jack_type == SND_JACK_HEADPHONE)
666 mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
667 else if (jack_type == SND_JACK_UNSUPPORTED)
668 mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
669 else if (jack_type == SND_JACK_HEADSET) {
670 mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
671 mbhc->jiffies_atreport = jiffies;
672 } else if (jack_type == SND_JACK_LINEOUT) {
673 mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
674 } else if (jack_type == SND_JACK_ANC_HEADPHONE)
675 mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE;
676
677 if (mbhc->mbhc_cb->hph_pa_on_status)
Meng Wang15c825d2018-09-06 10:49:18 +0800678 is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530679
680 if (mbhc->impedance_detect &&
681 mbhc->mbhc_cb->compute_impedance &&
682 (mbhc->mbhc_cfg->linein_th != 0) &&
683 (!is_pa_on)) {
684 /* Set MUX_CTL to AUTO for Z-det */
685 WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
686 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
687 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL,
688 MUX_CTL_AUTO);
689 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
690 mbhc->mbhc_cb->compute_impedance(mbhc,
691 &mbhc->zl, &mbhc->zr);
692 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN,
693 fsm_en);
Vatsal Buchacd339692019-09-11 12:17:11 +0530694 if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) &&
695 (mbhc->zr > mbhc->mbhc_cfg->linein_th) &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530696 (jack_type == SND_JACK_HEADPHONE)) {
697 jack_type = SND_JACK_LINEOUT;
Vatsal Buchad6d62b82017-12-12 14:22:31 +0530698 mbhc->force_linein = true;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530699 mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
700 if (mbhc->hph_status) {
701 mbhc->hph_status &= ~(SND_JACK_HEADSET |
702 SND_JACK_LINEOUT |
703 SND_JACK_UNSUPPORTED);
704 wcd_mbhc_jack_report(mbhc,
705 &mbhc->headset_jack,
706 mbhc->hph_status,
707 WCD_MBHC_JACK_MASK);
708 }
709 pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n",
710 __func__);
711 }
712 }
713
Vatsal Bucha48003fd2019-07-17 16:25:08 +0530714 /* Do not calculate impedance again for lineout
715 * as during playback pa is on and impedance values
716 * will not be correct resulting in lineout detected
717 * as headphone.
718 */
719 if ((is_pa_on) && mbhc->force_linein == true) {
720 jack_type = SND_JACK_LINEOUT;
721 mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
722 if (mbhc->hph_status) {
723 mbhc->hph_status &= ~(SND_JACK_HEADSET |
724 SND_JACK_LINEOUT |
725 SND_JACK_UNSUPPORTED);
726 wcd_mbhc_jack_report(mbhc,
727 &mbhc->headset_jack,
728 mbhc->hph_status,
729 WCD_MBHC_JACK_MASK);
730 }
731 }
732
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530733 mbhc->hph_status |= jack_type;
734
735 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
736 jack_type, mbhc->hph_status);
737 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
738 (mbhc->hph_status | SND_JACK_MECHANICAL),
739 WCD_MBHC_JACK_MASK);
740 wcd_mbhc_clr_and_turnon_hph_padac(mbhc);
741 }
742 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
743}
Meng Wang6f901622017-09-19 10:21:57 +0800744EXPORT_SYMBOL(wcd_mbhc_report_plug);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530745
746void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
747{
748 /* cancel pending button press */
749 if (wcd_cancel_btn_work(mbhc))
750 pr_debug("%s: button press is canceled\n", __func__);
751 /* cancel correct work function */
752 if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug)
753 mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc,
754 &mbhc->correct_plug_swch);
755 else
756 pr_info("%s: hs_detect_plug work not cancelled\n", __func__);
757
758 pr_debug("%s: Report extension cable\n", __func__);
759 wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
760 /*
761 * If PA is enabled HPHL schmitt trigger can
762 * be unreliable, make sure to disable it
763 */
764 if (test_bit(WCD_MBHC_EVENT_PA_HPHL,
765 &mbhc->event_state))
766 wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
767 /*
768 * Disable HPHL trigger and MIC Schmitt triggers.
769 * Setup for insertion detection.
770 */
771 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
772 false);
773 wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
774 /* Disable HW FSM */
775 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
776 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3);
777
778 /* Set the detection type appropriately */
779 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1);
780 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
781 true);
782}
783EXPORT_SYMBOL(wcd_mbhc_elec_hs_report_unplug);
784
785void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
786 enum wcd_mbhc_plug_type plug_type)
787{
788 bool anc_mic_found = false;
789 enum snd_jack_types jack_type;
790
Karthikeyan Mani3dd07e62018-06-28 18:25:08 -0700791 if (mbhc->deinit_in_progress) {
Md Mansoor Ahmed26d8bdd2018-11-20 10:56:01 +0530792 pr_info("%s: mbhc deinit in progess: ignore report\n", __func__);
Karthikeyan Mani3dd07e62018-06-28 18:25:08 -0700793 return;
794 }
795
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530796 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
797 __func__, mbhc->current_plug, plug_type);
798
799 WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
800
801 if (mbhc->current_plug == plug_type) {
802 pr_debug("%s: cable already reported, exit\n", __func__);
803 goto exit;
804 }
805
806 if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
807 /*
808 * Nothing was reported previously
809 * report a headphone or unsupported
810 */
811 wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
812 } else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
813 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
814 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
815 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
816 wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
817 wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
818 } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
819 if (mbhc->mbhc_cfg->enable_anc_mic_detect &&
820 mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type)
821 anc_mic_found =
822 mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type(mbhc);
823 jack_type = SND_JACK_HEADSET;
824 if (anc_mic_found)
825 jack_type = SND_JACK_ANC_HEADPHONE;
826
827 /*
828 * If Headphone was reported previously, this will
829 * only report the mic line
830 */
831 wcd_mbhc_report_plug(mbhc, 1, jack_type);
832 } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
833 if (mbhc->mbhc_cfg->detect_extn_cable) {
834 /* High impedance device found. Report as LINEOUT */
835 wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
836 pr_debug("%s: setup mic trigger for further detection\n",
837 __func__);
838
839 /* Disable HW FSM and current source */
840 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
841 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
842 /* Setup for insertion detection */
843 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
844 1);
845 /*
846 * Enable HPHL trigger and MIC Schmitt triggers
847 * and request for elec insertion interrupts
848 */
849 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC,
850 3);
851 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
852 true);
853 } else {
854 wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
855 }
856 } else {
857 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
858 mbhc->current_plug, plug_type);
859 }
860exit:
861 pr_debug("%s: leave\n", __func__);
862}
863EXPORT_SYMBOL(wcd_mbhc_find_plug_and_report);
864
Sudheer Papothia45a56e2018-08-03 05:30:24 +0530865static bool wcd_mbhc_moisture_detect(struct wcd_mbhc *mbhc, bool detection_type)
866{
867 bool ret = false;
868
Vatsal Buchae1728c52019-01-17 17:24:55 +0530869 if (!mbhc->mbhc_cfg->moisture_en &&
Sudheer Papothia45a56e2018-08-03 05:30:24 +0530870 !mbhc->mbhc_cfg->moisture_duty_cycle_en)
871 return ret;
872
873 if (!mbhc->mbhc_cb->mbhc_get_moisture_status ||
874 !mbhc->mbhc_cb->mbhc_moisture_polling_ctrl ||
875 !mbhc->mbhc_cb->mbhc_moisture_detect_en)
876 return ret;
877
878 if (mbhc->mbhc_cb->mbhc_get_moisture_status(mbhc)) {
879 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
880 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_DET_EN, 0);
881 mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc, true);
882 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE,
883 detection_type);
884 ret = true;
885 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
886 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_DET_EN, 1);
887 } else {
888 mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc, false);
889 mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc, false);
890 }
891
892 return ret;
893}
894
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530895static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
896{
897 bool detection_type = 0;
898 bool micbias1 = false;
Meng Wang15c825d2018-09-06 10:49:18 +0800899 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530900 enum snd_jack_types jack_type;
901
Meng Wang15c825d2018-09-06 10:49:18 +0800902 dev_dbg(component->dev, "%s: enter\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530903 WCD_MBHC_RSC_LOCK(mbhc);
904 mbhc->in_swch_irq_handler = true;
905
906 /* cancel pending button press */
907 if (wcd_cancel_btn_work(mbhc))
908 pr_debug("%s: button press is canceled\n", __func__);
909
910 WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type);
911
912 /* Set the detection type appropriately */
913 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE,
914 !detection_type);
915
916 pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
917 mbhc->current_plug, detection_type);
918 if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug)
919 mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc,
920 &mbhc->correct_plug_swch);
921 else
922 pr_info("%s: hs_detect_plug work not cancelled\n", __func__);
923
924 if (mbhc->mbhc_cb->micbias_enable_status)
925 micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
926 MIC_BIAS_1);
927
928 if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
929 detection_type) {
Sudheer Papothia45a56e2018-08-03 05:30:24 +0530930
931 /* If moisture is present, then enable polling, disable
932 * moisture detection and wait for interrupt
933 */
934 if (wcd_mbhc_moisture_detect(mbhc, detection_type))
935 goto done;
936
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530937 /* Make sure MASTER_BIAS_CTL is enabled */
Meng Wang15c825d2018-09-06 10:49:18 +0800938 mbhc->mbhc_cb->mbhc_bias(component, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530939
940 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
Meng Wang15c825d2018-09-06 10:49:18 +0800941 mbhc->mbhc_cb->mbhc_common_micb_ctrl(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530942 MBHC_COMMON_MICB_TAIL_CURR, true);
943
944 if (!mbhc->mbhc_cfg->hs_ext_micbias &&
945 mbhc->mbhc_cb->micb_internal)
946 /*
947 * Enable Tx2 RBias if the headset
948 * is using internal micbias
949 */
Meng Wang15c825d2018-09-06 10:49:18 +0800950 mbhc->mbhc_cb->micb_internal(component, 1, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530951
952 /* Remove micbias pulldown */
953 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0);
954 /* Apply trim if needed on the device */
955 if (mbhc->mbhc_cb->trim_btn_reg)
Meng Wang15c825d2018-09-06 10:49:18 +0800956 mbhc->mbhc_cb->trim_btn_reg(component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530957 /* Enable external voltage source to micbias if present */
958 if (mbhc->mbhc_cb->enable_mb_source)
959 mbhc->mbhc_cb->enable_mb_source(mbhc, true);
960 mbhc->btn_press_intr = false;
961 mbhc->is_btn_press = false;
962 if (mbhc->mbhc_fn)
963 mbhc->mbhc_fn->wcd_mbhc_detect_plug_type(mbhc);
964 } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
965 && !detection_type) {
966 /* Disable external voltage source to micbias if present */
967 if (mbhc->mbhc_cb->enable_mb_source)
968 mbhc->mbhc_cb->enable_mb_source(mbhc, false);
969 /* Disable HW FSM */
970 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
971 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
972 if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
Meng Wang15c825d2018-09-06 10:49:18 +0800973 mbhc->mbhc_cb->mbhc_common_micb_ctrl(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530974 MBHC_COMMON_MICB_TAIL_CURR, false);
975
976 if (mbhc->mbhc_cb->set_cap_mode)
Meng Wang15c825d2018-09-06 10:49:18 +0800977 mbhc->mbhc_cb->set_cap_mode(component, micbias1, false);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530978
979 mbhc->btn_press_intr = false;
980 mbhc->is_btn_press = false;
981 switch (mbhc->current_plug) {
982 case MBHC_PLUG_TYPE_HEADPHONE:
983 jack_type = SND_JACK_HEADPHONE;
984 break;
985 case MBHC_PLUG_TYPE_GND_MIC_SWAP:
986 jack_type = SND_JACK_UNSUPPORTED;
987 break;
988 case MBHC_PLUG_TYPE_HEADSET:
989 /* make sure to turn off Rbias */
990 if (mbhc->mbhc_cb->micb_internal)
Meng Wang15c825d2018-09-06 10:49:18 +0800991 mbhc->mbhc_cb->micb_internal(component,
992 1, false);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530993 /* Pulldown micbias */
994 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1);
995 jack_type = SND_JACK_HEADSET;
996 break;
997 case MBHC_PLUG_TYPE_HIGH_HPH:
Vatsal Bucha4b8884a2018-05-10 14:20:18 +0530998 if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
999 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301000 mbhc->is_extn_cable = false;
1001 jack_type = SND_JACK_LINEOUT;
1002 break;
1003 case MBHC_PLUG_TYPE_ANC_HEADPHONE:
1004 jack_type = SND_JACK_ANC_HEADPHONE;
1005 break;
1006 default:
1007 pr_info("%s: Invalid current plug: %d\n",
1008 __func__, mbhc->current_plug);
1009 jack_type = SND_JACK_UNSUPPORTED;
1010 break;
1011 }
1012 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false);
1013 wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
1014 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1);
1015 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
1016 mbhc->extn_cable_hph_rem = false;
1017 wcd_mbhc_report_plug(mbhc, 0, jack_type);
1018
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001019 if (mbhc->mbhc_cfg->enable_usbc_analog) {
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001020 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001021 if (mbhc->mbhc_cb->clk_setup)
Meng Wang15c825d2018-09-06 10:49:18 +08001022 mbhc->mbhc_cb->clk_setup(
1023 mbhc->component, false);
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001024 }
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001025
Sudheer Papothia45a56e2018-08-03 05:30:24 +05301026 if (mbhc->mbhc_cfg->moisture_en &&
1027 mbhc->mbhc_cfg->moisture_duty_cycle_en) {
1028 if (mbhc->mbhc_cb->mbhc_moisture_polling_ctrl)
1029 mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc,
1030 false);
1031 if (mbhc->mbhc_cb->mbhc_moisture_detect_en)
1032 mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc,
1033 false);
1034 }
1035
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301036 } else if (!detection_type) {
1037 /* Disable external voltage source to micbias if present */
1038 if (mbhc->mbhc_cb->enable_mb_source)
1039 mbhc->mbhc_cb->enable_mb_source(mbhc, false);
1040 /* Disable HW FSM */
1041 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
1042 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
1043 mbhc->extn_cable_hph_rem = false;
1044 }
1045
Sudheer Papothia45a56e2018-08-03 05:30:24 +05301046done:
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301047 mbhc->in_swch_irq_handler = false;
1048 WCD_MBHC_RSC_UNLOCK(mbhc);
1049 pr_debug("%s: leave\n", __func__);
1050}
1051
1052static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
1053{
1054 int r = IRQ_HANDLED;
1055 struct wcd_mbhc *mbhc = data;
1056
1057 pr_debug("%s: enter\n", __func__);
1058 if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) {
1059 pr_warn("%s: failed to hold suspend\n", __func__);
1060 r = IRQ_NONE;
1061 } else {
1062 /* Call handler */
1063 wcd_mbhc_swch_irq_handler(mbhc);
1064 mbhc->mbhc_cb->lock_sleep(mbhc, false);
1065 }
1066 pr_debug("%s: leave %d\n", __func__, r);
1067 return r;
1068}
1069
1070int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
1071{
1072 int mask = 0;
1073 int btn;
1074
Meng Wang15c825d2018-09-06 10:49:18 +08001075 btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301076
1077 switch (btn) {
1078 case 0:
1079 mask = SND_JACK_BTN_0;
1080 break;
1081 case 1:
1082 mask = SND_JACK_BTN_1;
1083 break;
1084 case 2:
1085 mask = SND_JACK_BTN_2;
1086 break;
1087 case 3:
1088 mask = SND_JACK_BTN_3;
1089 break;
1090 case 4:
1091 mask = SND_JACK_BTN_4;
1092 break;
1093 case 5:
1094 mask = SND_JACK_BTN_5;
1095 break;
1096 default:
1097 break;
1098 }
1099
1100 return mask;
1101}
1102EXPORT_SYMBOL(wcd_mbhc_get_button_mask);
1103
1104static void wcd_btn_lpress_fn(struct work_struct *work)
1105{
1106 struct delayed_work *dwork;
1107 struct wcd_mbhc *mbhc;
1108 s16 btn_result = 0;
1109
1110 pr_debug("%s: Enter\n", __func__);
1111
1112 dwork = to_delayed_work(work);
1113 mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
1114
1115 WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
1116 if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
1117 pr_debug("%s: Reporting long button press event, btn_result: %d\n",
1118 __func__, btn_result);
1119 wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
1120 mbhc->buttons_pressed, mbhc->buttons_pressed);
1121 }
1122 pr_debug("%s: leave\n", __func__);
1123 mbhc->mbhc_cb->lock_sleep(mbhc, false);
1124}
1125
1126static bool wcd_mbhc_fw_validate(const void *data, size_t size)
1127{
1128 u32 cfg_offset;
1129 struct wcd_mbhc_btn_detect_cfg *btn_cfg;
1130 struct firmware_cal fw;
1131
1132 fw.data = (void *)data;
1133 fw.size = size;
1134
1135 if (fw.size < WCD_MBHC_CAL_MIN_SIZE)
1136 return false;
1137
1138 /*
1139 * Previous check guarantees that there is enough fw data up
1140 * to num_btn
1141 */
1142 btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data);
1143 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
1144 if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg)))
1145 return false;
1146
1147 return true;
1148}
1149
1150static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
1151{
1152 struct wcd_mbhc *mbhc = data;
1153 int mask;
1154 unsigned long msec_val;
1155
1156 pr_debug("%s: enter\n", __func__);
1157 complete(&mbhc->btn_press_compl);
1158 WCD_MBHC_RSC_LOCK(mbhc);
1159 wcd_cancel_btn_work(mbhc);
1160 if (wcd_swch_level_remove(mbhc)) {
1161 pr_debug("%s: Switch level is low ", __func__);
1162 goto done;
1163 }
1164
1165 mbhc->is_btn_press = true;
1166 msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
1167 pr_debug("%s: msec_val = %ld\n", __func__, msec_val);
1168 if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) {
1169 pr_debug("%s: Too short, ignore button press\n", __func__);
1170 goto done;
1171 }
1172
1173 /* If switch interrupt already kicked in, ignore button press */
1174 if (mbhc->in_swch_irq_handler) {
1175 pr_debug("%s: Swtich level changed, ignore button press\n",
1176 __func__);
1177 goto done;
1178 }
1179 mask = wcd_mbhc_get_button_mask(mbhc);
1180 if (mask == SND_JACK_BTN_0)
1181 mbhc->btn_press_intr = true;
1182
1183 if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) {
1184 pr_debug("%s: Plug isn't headset, ignore button press\n",
1185 __func__);
1186 goto done;
1187 }
1188 mbhc->buttons_pressed |= mask;
1189 mbhc->mbhc_cb->lock_sleep(mbhc, true);
1190 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
1191 msecs_to_jiffies(400)) == 0) {
1192 WARN(1, "Button pressed twice without release event\n");
1193 mbhc->mbhc_cb->lock_sleep(mbhc, false);
1194 }
1195done:
1196 pr_debug("%s: leave\n", __func__);
1197 WCD_MBHC_RSC_UNLOCK(mbhc);
1198 return IRQ_HANDLED;
1199}
1200
1201static irqreturn_t wcd_mbhc_release_handler(int irq, void *data)
1202{
1203 struct wcd_mbhc *mbhc = data;
1204 int ret;
1205
1206 pr_debug("%s: enter\n", __func__);
1207 WCD_MBHC_RSC_LOCK(mbhc);
1208 if (wcd_swch_level_remove(mbhc)) {
1209 pr_debug("%s: Switch level is low ", __func__);
1210 goto exit;
1211 }
1212
1213 if (mbhc->is_btn_press) {
1214 mbhc->is_btn_press = false;
1215 } else {
1216 pr_debug("%s: This release is for fake btn press\n", __func__);
1217 goto exit;
1218 }
1219
1220 /*
1221 * If current plug is headphone then there is no chance to
1222 * get btn release interrupt, so connected cable should be
1223 * headset not headphone.
1224 * For ADC MBHC, ADC_COMPLETE interrupt will be generated
1225 * in this case. So skip the check here.
1226 */
Asish Bhattacharya84f7f732017-07-25 16:29:27 +05301227 if (mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY &&
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301228 mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
1229 wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
1230 goto exit;
1231
1232 }
1233 if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) {
1234 ret = wcd_cancel_btn_work(mbhc);
1235 if (ret == 0) {
1236 pr_debug("%s: Reporting long button release event\n",
1237 __func__);
1238 wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
1239 0, mbhc->buttons_pressed);
1240 } else {
1241 if (mbhc->in_swch_irq_handler) {
1242 pr_debug("%s: Switch irq kicked in, ignore\n",
1243 __func__);
1244 } else {
1245 pr_debug("%s: Reporting btn press\n",
1246 __func__);
1247 wcd_mbhc_jack_report(mbhc,
1248 &mbhc->button_jack,
1249 mbhc->buttons_pressed,
1250 mbhc->buttons_pressed);
1251 pr_debug("%s: Reporting btn release\n",
1252 __func__);
1253 wcd_mbhc_jack_report(mbhc,
1254 &mbhc->button_jack,
1255 0, mbhc->buttons_pressed);
1256 }
1257 }
1258 mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
1259 }
1260exit:
1261 pr_debug("%s: leave\n", __func__);
1262 WCD_MBHC_RSC_UNLOCK(mbhc);
1263 return IRQ_HANDLED;
1264}
1265
1266static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
1267{
1268 struct wcd_mbhc *mbhc = data;
1269 int val;
1270
1271 pr_debug("%s: received HPHL OCP irq\n", __func__);
1272 if (mbhc) {
1273 if (mbhc->mbhc_cb->hph_register_recovery) {
1274 if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) {
1275 WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS,
1276 val);
1277 if ((val != -EINVAL) && val)
1278 mbhc->is_hph_ocp_pending = true;
1279 goto done;
1280 }
1281 }
1282
1283 if (mbhc->hphlocp_cnt < OCP_ATTEMPT) {
1284 mbhc->hphlocp_cnt++;
1285 pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__,
1286 mbhc->hphlocp_cnt);
1287 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
1288 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
1289 } else {
Meng Wang15c825d2018-09-06 10:49:18 +08001290 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301291 mbhc->intr_ids->hph_left_ocp,
1292 false);
1293 mbhc->hph_status |= SND_JACK_OC_HPHL;
1294 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
1295 mbhc->hph_status,
1296 WCD_MBHC_JACK_MASK);
1297 }
1298 } else {
1299 pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__);
1300 }
1301done:
1302 return IRQ_HANDLED;
1303}
1304
1305static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
1306{
1307 struct wcd_mbhc *mbhc = data;
1308
1309 pr_debug("%s: received HPHR OCP irq\n", __func__);
1310
1311 if (!mbhc) {
1312 pr_err("%s: Bad mbhc private data\n", __func__);
1313 goto done;
1314 }
1315
1316 if (mbhc->is_hph_ocp_pending) {
1317 mbhc->is_hph_ocp_pending = false;
1318 goto done;
1319 }
1320
1321 if (mbhc->mbhc_cb->hph_register_recovery) {
1322 if (mbhc->mbhc_cb->hph_register_recovery(mbhc))
1323 /* register corruption, hence reset registers */
1324 goto done;
1325 }
1326 if (mbhc->hphrocp_cnt < OCP_ATTEMPT) {
1327 mbhc->hphrocp_cnt++;
1328 pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__,
1329 mbhc->hphrocp_cnt);
1330 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
1331 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
1332 } else {
Meng Wang15c825d2018-09-06 10:49:18 +08001333 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301334 mbhc->intr_ids->hph_right_ocp,
1335 false);
1336 mbhc->hph_status |= SND_JACK_OC_HPHR;
1337 wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
1338 mbhc->hph_status, WCD_MBHC_JACK_MASK);
1339 }
1340done:
1341 return IRQ_HANDLED;
1342}
1343
1344static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
1345{
1346 int ret = 0;
Meng Wang15c825d2018-09-06 10:49:18 +08001347 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301348
1349 pr_debug("%s: enter\n", __func__);
1350 WCD_MBHC_RSC_LOCK(mbhc);
1351
1352 /* enable HS detection */
Sudheer Papothi8c9bd0f2017-12-29 02:18:43 +05301353 if (mbhc->mbhc_cb->hph_pull_up_control_v2)
Meng Wang15c825d2018-09-06 10:49:18 +08001354 mbhc->mbhc_cb->hph_pull_up_control_v2(component,
Sudheer Papothi8c9bd0f2017-12-29 02:18:43 +05301355 HS_PULLUP_I_DEFAULT);
1356 else if (mbhc->mbhc_cb->hph_pull_up_control)
Meng Wang15c825d2018-09-06 10:49:18 +08001357 mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301358 else
1359 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
1360
Vatsal Buchae1728c52019-01-17 17:24:55 +05301361 /* Configure for moisture detection when duty cycle is not enabled.
1362 * Otherwise disable moisture detection.
1363 */
Sudheer Papothia45a56e2018-08-03 05:30:24 +05301364 if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config
1365 && !mbhc->mbhc_cfg->moisture_duty_cycle_en)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301366 mbhc->mbhc_cb->mbhc_moisture_config(mbhc);
Vatsal Buchae1728c52019-01-17 17:24:55 +05301367 else if (mbhc->mbhc_cfg->moisture_duty_cycle_en &&
1368 mbhc->mbhc_cb->mbhc_moisture_detect_en)
1369 mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc, false);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301370
1371 /*
1372 * For USB analog we need to override the switch configuration.
1373 * Also, disable hph_l pull-up current source as HS_DET_L is driven
1374 * by an external source
1375 */
1376 if (mbhc->mbhc_cfg->enable_usbc_analog) {
Sudheer Papothi8c9bd0f2017-12-29 02:18:43 +05301377 if (mbhc->mbhc_cb->hph_pull_up_control_v2)
Meng Wang15c825d2018-09-06 10:49:18 +08001378 mbhc->mbhc_cb->hph_pull_up_control_v2(component,
Sudheer Papothi8c9bd0f2017-12-29 02:18:43 +05301379 HS_PULLUP_I_OFF);
1380 else if (mbhc->mbhc_cb->hph_pull_up_control)
Meng Wang15c825d2018-09-06 10:49:18 +08001381 mbhc->mbhc_cb->hph_pull_up_control(component, I_OFF);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301382 else
1383 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
1384 0);
1385 }
1386
1387 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh);
1388 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh);
1389 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
1390 if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
Meng Wang15c825d2018-09-06 10:49:18 +08001391 mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301392 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001393
1394 /*
1395 * Disable L_DET for USB-C analog audio to avoid spurious interrupts
1396 * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA
1397 * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted
1398 */
1399 if (mbhc->mbhc_cfg->enable_usbc_analog)
1400 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
1401 else
1402 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301403
1404 if (mbhc->mbhc_cfg->enable_usbc_analog) {
1405 /* Insertion debounce set to 48ms */
1406 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 4);
1407 } else {
1408 /* Insertion debounce set to 96ms */
1409 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6);
1410 }
1411
1412 /* Button Debounce set to 16ms */
1413 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2);
1414
1415 /* Enable micbias ramp */
1416 if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
Meng Wang15c825d2018-09-06 10:49:18 +08001417 mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301418 /* enable bias */
Meng Wang15c825d2018-09-06 10:49:18 +08001419 mbhc->mbhc_cb->mbhc_bias(component, true);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301420 /* enable MBHC clock */
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001421 if (mbhc->mbhc_cb->clk_setup) {
1422 if (mbhc->mbhc_cfg->enable_usbc_analog)
Meng Wang15c825d2018-09-06 10:49:18 +08001423 mbhc->mbhc_cb->clk_setup(component, false);
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001424 else
Meng Wang15c825d2018-09-06 10:49:18 +08001425 mbhc->mbhc_cb->clk_setup(component, true);
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001426 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301427
1428 /* program HS_VREF value */
1429 wcd_program_hs_vref(mbhc);
1430
1431 wcd_program_btn_threshold(mbhc, false);
1432
1433
1434 reinit_completion(&mbhc->btn_press_compl);
1435
1436 WCD_MBHC_RSC_UNLOCK(mbhc);
1437 pr_debug("%s: leave\n", __func__);
1438 return ret;
1439}
1440
1441static void wcd_mbhc_fw_read(struct work_struct *work)
1442{
1443 struct delayed_work *dwork;
1444 struct wcd_mbhc *mbhc;
Meng Wang15c825d2018-09-06 10:49:18 +08001445 struct snd_soc_component *component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301446 const struct firmware *fw;
1447 struct firmware_cal *fw_data = NULL;
1448 int ret = -1, retry = 0;
1449 bool use_default_cal = false;
1450
1451 dwork = to_delayed_work(work);
1452 mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork);
Meng Wang15c825d2018-09-06 10:49:18 +08001453 component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301454
1455 while (retry < FW_READ_ATTEMPTS) {
1456 retry++;
1457 pr_debug("%s:Attempt %d to request MBHC firmware\n",
1458 __func__, retry);
1459 if (mbhc->mbhc_cb->get_hwdep_fw_cal)
1460 fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc,
1461 WCD9XXX_MBHC_CAL);
1462 if (!fw_data)
1463 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
Meng Wang15c825d2018-09-06 10:49:18 +08001464 component->dev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301465 /*
1466 * if request_firmware and hwdep cal both fail then
1467 * sleep for 4sec for the userspace to send data to kernel
1468 * retry for few times before bailing out
1469 */
1470 if ((ret != 0) && !fw_data) {
1471 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
1472 WCD_MBHC_USLEEP_RANGE_MARGIN_US);
1473 } else {
1474 pr_debug("%s: MBHC Firmware read successful\n",
1475 __func__);
1476 break;
1477 }
1478 }
1479 if (!fw_data)
1480 pr_debug("%s: using request_firmware\n", __func__);
1481 else
1482 pr_debug("%s: using hwdep cal\n", __func__);
1483
1484 if (ret != 0 && !fw_data) {
1485 pr_err("%s: Cannot load MBHC firmware use default cal\n",
1486 __func__);
1487 use_default_cal = true;
1488 }
1489 if (!use_default_cal) {
1490 const void *data;
1491 size_t size;
1492
1493 if (fw_data) {
1494 data = fw_data->data;
1495 size = fw_data->size;
1496 } else {
1497 data = fw->data;
1498 size = fw->size;
1499 }
1500 if (wcd_mbhc_fw_validate(data, size) == false) {
1501 pr_err("%s: Invalid MBHC cal data size use default cal\n",
1502 __func__);
1503 if (!fw_data)
1504 release_firmware(fw);
1505 } else {
1506 if (fw_data) {
1507 mbhc->mbhc_cfg->calibration =
1508 (void *)fw_data->data;
1509 mbhc->mbhc_cal = fw_data;
1510 } else {
1511 mbhc->mbhc_cfg->calibration =
1512 (void *)fw->data;
1513 mbhc->mbhc_fw = fw;
1514 }
1515 }
1516
1517 }
1518
1519 (void) wcd_mbhc_initialise(mbhc);
1520}
1521
1522static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc)
1523{
1524 enum snd_jack_types type;
1525 int i, ret, result = 0;
1526 int *btn_key_code;
1527
1528 btn_key_code = mbhc->mbhc_cfg->key_code;
1529
1530 for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) {
1531 if (btn_key_code[i] != 0) {
1532 switch (i) {
1533 case 0:
1534 type = SND_JACK_BTN_0;
1535 break;
1536 case 1:
1537 type = SND_JACK_BTN_1;
1538 break;
1539 case 2:
1540 type = SND_JACK_BTN_2;
1541 break;
1542 case 3:
1543 type = SND_JACK_BTN_3;
1544 break;
1545 case 4:
1546 type = SND_JACK_BTN_4;
1547 break;
1548 case 5:
1549 type = SND_JACK_BTN_5;
1550 break;
1551 default:
1552 WARN_ONCE(1, "Wrong button number:%d\n", i);
1553 result = -1;
1554 return result;
1555 }
1556 ret = snd_jack_set_key(mbhc->button_jack.jack,
1557 type,
1558 btn_key_code[i]);
1559 if (ret) {
1560 pr_err("%s: Failed to set code for %d\n",
1561 __func__, btn_key_code[i]);
1562 result = -1;
1563 return result;
1564 }
1565 input_set_capability(
1566 mbhc->button_jack.jack->input_dev,
1567 EV_KEY, btn_key_code[i]);
1568 pr_debug("%s: set btn%d key code:%d\n", __func__,
1569 i, btn_key_code[i]);
1570 }
1571 }
1572 if (btn_key_code[0])
1573 mbhc->is_btn_already_regd = true;
1574 return result;
1575}
1576
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001577static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb,
1578 unsigned long mode, void *ptr)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301579{
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001580 struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, fsa_nb);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301581
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001582 if (!mbhc)
1583 return -EINVAL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301584
Meng Wang15c825d2018-09-06 10:49:18 +08001585 dev_dbg(mbhc->component->dev, "%s: mode = %lu\n", __func__, mode);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301586
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001587 if (mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) {
Karthikeyan Mani8d772b02018-03-21 19:52:32 -07001588 if (mbhc->mbhc_cb->clk_setup)
Meng Wang15c825d2018-09-06 10:49:18 +08001589 mbhc->mbhc_cb->clk_setup(mbhc->component, true);
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001590 /* insertion detected, enable L_DET_EN */
1591 WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301592 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301593 return 0;
1594}
1595
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301596int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
1597{
1598 int rc = 0;
Meng Wang15c825d2018-09-06 10:49:18 +08001599 struct snd_soc_component *component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301600 struct snd_soc_card *card;
1601 const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported";
1602
1603 if (!mbhc || !mbhc_cfg)
1604 return -EINVAL;
1605
Meng Wang15c825d2018-09-06 10:49:18 +08001606 component = mbhc->component;
1607 card = component->card;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301608
1609 /* update the mbhc config */
1610 mbhc->mbhc_cfg = mbhc_cfg;
1611
Meng Wang15c825d2018-09-06 10:49:18 +08001612 dev_dbg(mbhc->component->dev, "%s: enter\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301613
1614 /* check if USB C analog is defined on device tree */
1615 mbhc_cfg->enable_usbc_analog = 0;
1616 if (of_find_property(card->dev->of_node, usb_c_dt, NULL)) {
1617 rc = of_property_read_u32(card->dev->of_node, usb_c_dt,
1618 &mbhc_cfg->enable_usbc_analog);
1619 }
1620 if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) {
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001621 dev_dbg(card->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301622 "%s: %s in dt node is missing or false\n",
1623 __func__, usb_c_dt);
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001624 dev_dbg(card->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301625 "%s: skipping USB c analog configuration\n", __func__);
1626 }
1627
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001628 /* Parse fsa switch handle */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301629 if (mbhc_cfg->enable_usbc_analog) {
Meng Wang15c825d2018-09-06 10:49:18 +08001630 dev_dbg(mbhc->component->dev, "%s: usbc analog enabled\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301631 __func__);
Karthikeyan Mani5392d802017-11-08 20:32:38 -08001632 mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001633 mbhc->fsa_np = of_parse_phandle(card->dev->of_node,
1634 "fsa4480-i2c-handle", 0);
1635 if (!mbhc->fsa_np) {
1636 dev_err(card->dev, "%s: fsa4480 i2c node not found\n",
1637 __func__);
1638 rc = -EINVAL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301639 goto err;
1640 }
1641 }
1642
1643 /* Set btn key code */
1644 if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc))
1645 pr_err("Set btn key code error!!!\n");
1646
1647 if (!mbhc->mbhc_cfg->read_fw_bin ||
1648 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
1649 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
1650 rc = wcd_mbhc_initialise(mbhc);
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001651 if (rc) {
1652 dev_err(card->dev, "%s: wcd mbhc initialize failed\n",
1653 __func__);
1654 goto err;
1655 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301656 } else {
1657 if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
1658 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
1659 usecs_to_jiffies(FW_READ_TIMEOUT));
1660 else
1661 pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
1662 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
1663 }
1664
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001665 if (mbhc_cfg->enable_usbc_analog) {
1666 mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler;
1667 mbhc->fsa_nb.priority = 0;
1668 rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np);
1669 }
1670
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301671 return rc;
1672err:
Meng Wang15c825d2018-09-06 10:49:18 +08001673 dev_dbg(mbhc->component->dev, "%s: leave %d\n", __func__, rc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301674 return rc;
1675}
1676EXPORT_SYMBOL(wcd_mbhc_start);
1677
1678void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
1679{
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301680 pr_debug("%s: enter\n", __func__);
1681
1682 if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) {
1683 if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect)
Meng Wang15c825d2018-09-06 10:49:18 +08001684 mbhc->mbhc_cb->skip_imped_detect(mbhc->component);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301685 }
1686 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
1687 mbhc->hph_status = 0;
1688 if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) {
Meng Wang15c825d2018-09-06 10:49:18 +08001689 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301690 mbhc->intr_ids->hph_left_ocp,
1691 false);
Meng Wang15c825d2018-09-06 10:49:18 +08001692 mbhc->mbhc_cb->irq_control(mbhc->component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301693 mbhc->intr_ids->hph_right_ocp,
1694 false);
1695 }
1696 if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
1697 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
1698 if (!mbhc->mbhc_cal)
1699 release_firmware(mbhc->mbhc_fw);
1700 mbhc->mbhc_fw = NULL;
1701 mbhc->mbhc_cal = NULL;
1702 }
1703
Phani Kumar Uppalapati8fe02472018-01-17 18:42:52 -08001704 if (mbhc->mbhc_cfg->enable_usbc_analog)
1705 fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301706
1707 pr_debug("%s: leave\n", __func__);
1708}
1709EXPORT_SYMBOL(wcd_mbhc_stop);
1710
1711/*
1712 * wcd_mbhc_init : initialize MBHC internal structures.
1713 *
1714 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
1715 */
Meng Wang15c825d2018-09-06 10:49:18 +08001716int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_component *component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301717 const struct wcd_mbhc_cb *mbhc_cb,
1718 const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
1719 struct wcd_mbhc_register *wcd_mbhc_regs,
1720 bool impedance_det_en)
1721{
1722 int ret = 0;
1723 int hph_swh = 0;
1724 int gnd_swh = 0;
1725 u32 hph_moist_config[3];
Meng Wang15c825d2018-09-06 10:49:18 +08001726 struct snd_soc_card *card = component->card;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301727 const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
1728 const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
Meng Wang4d6a6be2017-09-15 10:35:44 +08001729 const char *hs_thre = "qcom,msm-mbhc-hs-mic-max-threshold-mv";
1730 const char *hph_thre = "qcom,msm-mbhc-hs-mic-min-threshold-mv";
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301731
1732 pr_debug("%s: enter\n", __func__);
1733
1734 ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh);
1735 if (ret) {
1736 dev_err(card->dev,
1737 "%s: missing %s in dt node\n", __func__, hph_switch);
1738 goto err;
1739 }
1740
1741 ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
1742 if (ret) {
1743 dev_err(card->dev,
1744 "%s: missing %s in dt node\n", __func__, gnd_switch);
1745 goto err;
1746 }
1747
Meng Wang4d6a6be2017-09-15 10:35:44 +08001748 ret = of_property_read_u32(card->dev->of_node, hs_thre,
1749 &(mbhc->hs_thr));
1750 if (ret)
1751 dev_dbg(card->dev,
1752 "%s: missing %s in dt node\n", __func__, hs_thre);
1753
1754 ret = of_property_read_u32(card->dev->of_node, hph_thre,
1755 &(mbhc->hph_thr));
1756 if (ret)
1757 dev_dbg(card->dev,
1758 "%s: missing %s in dt node\n", __func__, hph_thre);
1759
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301760 ret = of_property_read_u32_array(card->dev->of_node,
1761 "qcom,msm-mbhc-moist-cfg",
1762 hph_moist_config, 3);
1763 if (ret) {
1764 dev_dbg(card->dev, "%s: no qcom,msm-mbhc-moist-cfg in DT\n",
1765 __func__);
1766 mbhc->moist_vref = V_45_MV;
1767 mbhc->moist_iref = I_3P0_UA;
1768 mbhc->moist_rref = R_24_KOHM;
1769 } else {
1770 mbhc->moist_vref = hph_moist_config[0];
1771 mbhc->moist_iref = hph_moist_config[1];
1772 mbhc->moist_rref = hph_moist_config[2];
1773 }
1774
1775 mbhc->in_swch_irq_handler = false;
1776 mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
1777 mbhc->is_btn_press = false;
Meng Wang15c825d2018-09-06 10:49:18 +08001778 mbhc->component = component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301779 mbhc->intr_ids = mbhc_cdc_intr_ids;
1780 mbhc->impedance_detect = impedance_det_en;
1781 mbhc->hphl_swh = hph_swh;
1782 mbhc->gnd_swh = gnd_swh;
1783 mbhc->micbias_enable = false;
1784 mbhc->mbhc_cb = mbhc_cb;
1785 mbhc->btn_press_intr = false;
1786 mbhc->is_hs_recording = false;
1787 mbhc->is_extn_cable = false;
1788 mbhc->extn_cable_hph_rem = false;
1789 mbhc->hph_type = WCD_MBHC_HPH_NONE;
1790 mbhc->wcd_mbhc_regs = wcd_mbhc_regs;
Karthikeyan Mani5392d802017-11-08 20:32:38 -08001791 mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301792
1793 if (mbhc->intr_ids == NULL) {
1794 pr_err("%s: Interrupt mapping not provided\n", __func__);
1795 return -EINVAL;
1796 }
1797 if (!mbhc->wcd_mbhc_regs) {
Meng Wang15c825d2018-09-06 10:49:18 +08001798 dev_err(component->dev, "%s: mbhc registers are not defined\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301799 __func__);
1800 return -EINVAL;
1801 }
1802
1803 /* Check if IRQ and other required callbacks are defined or not */
1804 if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control ||
1805 !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num ||
1806 !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias ||
1807 !mbhc_cb->set_btn_thr) {
Meng Wang15c825d2018-09-06 10:49:18 +08001808 dev_err(component->dev, "%s: required mbhc callbacks are not defined\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301809 __func__);
1810 return -EINVAL;
1811 }
1812
1813 /* No need to create new sound card jacks if is is already created */
1814 if (mbhc->headset_jack.jack == NULL) {
Meng Wang15c825d2018-09-06 10:49:18 +08001815 ret = snd_soc_card_jack_new(component->card,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301816 "Headset Jack", WCD_MBHC_JACK_MASK,
1817 &mbhc->headset_jack, NULL, 0);
1818 if (ret) {
1819 pr_err("%s: Failed to create new jack\n", __func__);
1820 return ret;
1821 }
1822
Meng Wang15c825d2018-09-06 10:49:18 +08001823 ret = snd_soc_card_jack_new(component->card,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301824 "Button Jack",
1825 WCD_MBHC_JACK_BUTTON_MASK,
1826 &mbhc->button_jack, NULL, 0);
1827 if (ret) {
1828 pr_err("Failed to create new jack\n");
1829 return ret;
1830 }
1831
1832 ret = snd_jack_set_key(mbhc->button_jack.jack,
1833 SND_JACK_BTN_0,
1834 KEY_MEDIA);
1835 if (ret) {
1836 pr_err("%s: Failed to set code for btn-0\n",
1837 __func__);
1838 return ret;
1839 }
1840
1841 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
1842 wcd_mbhc_fw_read);
1843 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
1844 }
1845 mutex_init(&mbhc->hphl_pa_lock);
1846 mutex_init(&mbhc->hphr_pa_lock);
1847 init_completion(&mbhc->btn_press_compl);
1848
1849 /* Register event notifier */
1850 mbhc->nblock.notifier_call = wcd_event_notify;
1851 if (mbhc->mbhc_cb->register_notifier) {
1852 ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock,
1853 true);
1854 if (ret) {
1855 pr_err("%s: Failed to register notifier %d\n",
1856 __func__, ret);
1857 return ret;
1858 }
1859 }
1860
1861 init_waitqueue_head(&mbhc->wait_btn_press);
1862 mutex_init(&mbhc->codec_resource_lock);
1863
Asish Bhattacharya84f7f732017-07-25 16:29:27 +05301864 switch (mbhc->mbhc_detection_logic) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301865 case WCD_DETECTION_LEGACY:
1866 wcd_mbhc_legacy_init(mbhc);
1867 break;
1868 case WCD_DETECTION_ADC:
1869 wcd_mbhc_adc_init(mbhc);
1870 break;
1871 default:
1872 pr_err("%s: Unknown detection logic type %d\n",
Asish Bhattacharya84f7f732017-07-25 16:29:27 +05301873 __func__, mbhc->mbhc_detection_logic);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301874 break;
1875 }
1876
1877 if (!mbhc->mbhc_fn ||
1878 !mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq ||
1879 !mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq ||
1880 !mbhc->mbhc_fn->wcd_mbhc_detect_plug_type ||
1881 !mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) {
1882 pr_err("%s: mbhc function pointer is NULL\n", __func__);
1883 goto err_mbhc_sw_irq;
1884 }
Meng Wang15c825d2018-09-06 10:49:18 +08001885 ret = mbhc->mbhc_cb->request_irq(component,
1886 mbhc->intr_ids->mbhc_sw_intr,
1887 wcd_mbhc_mech_plug_detect_irq,
1888 "mbhc sw intr", mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301889 if (ret) {
1890 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
1891 mbhc->intr_ids->mbhc_sw_intr, ret);
1892 goto err_mbhc_sw_irq;
1893 }
1894
Meng Wang15c825d2018-09-06 10:49:18 +08001895 ret = mbhc->mbhc_cb->request_irq(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301896 mbhc->intr_ids->mbhc_btn_press_intr,
1897 wcd_mbhc_btn_press_handler,
1898 "Button Press detect", mbhc);
1899 if (ret) {
1900 pr_err("%s: Failed to request irq %d\n", __func__,
1901 mbhc->intr_ids->mbhc_btn_press_intr);
1902 goto err_btn_press_irq;
1903 }
1904
Meng Wang15c825d2018-09-06 10:49:18 +08001905 ret = mbhc->mbhc_cb->request_irq(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301906 mbhc->intr_ids->mbhc_btn_release_intr,
1907 wcd_mbhc_release_handler,
1908 "Button Release detect", mbhc);
1909 if (ret) {
1910 pr_err("%s: Failed to request irq %d\n", __func__,
1911 mbhc->intr_ids->mbhc_btn_release_intr);
1912 goto err_btn_release_irq;
1913 }
1914
Meng Wang15c825d2018-09-06 10:49:18 +08001915 ret = mbhc->mbhc_cb->request_irq(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301916 mbhc->intr_ids->mbhc_hs_ins_intr,
1917 mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq,
1918 "Elect Insert", mbhc);
1919 if (ret) {
1920 pr_err("%s: Failed to request irq %d\n", __func__,
1921 mbhc->intr_ids->mbhc_hs_ins_intr);
1922 goto err_mbhc_hs_ins_irq;
1923 }
Meng Wang15c825d2018-09-06 10:49:18 +08001924 mbhc->mbhc_cb->irq_control(component, mbhc->intr_ids->mbhc_hs_ins_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301925 false);
1926 clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status);
1927
Meng Wang15c825d2018-09-06 10:49:18 +08001928 ret = mbhc->mbhc_cb->request_irq(component,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301929 mbhc->intr_ids->mbhc_hs_rem_intr,
1930 mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq,
1931 "Elect Remove", mbhc);
1932 if (ret) {
1933 pr_err("%s: Failed to request irq %d\n", __func__,
1934 mbhc->intr_ids->mbhc_hs_rem_intr);
1935 goto err_mbhc_hs_rem_irq;
1936 }
Meng Wang15c825d2018-09-06 10:49:18 +08001937 mbhc->mbhc_cb->irq_control(component, mbhc->intr_ids->mbhc_hs_rem_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301938 false);
1939 clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status);
1940
Meng Wang15c825d2018-09-06 10:49:18 +08001941 ret = mbhc->mbhc_cb->request_irq(component,
1942 mbhc->intr_ids->hph_left_ocp,
1943 wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
1944 mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301945 if (ret) {
1946 pr_err("%s: Failed to request irq %d\n", __func__,
1947 mbhc->intr_ids->hph_left_ocp);
1948 goto err_hphl_ocp_irq;
1949 }
1950
Meng Wang15c825d2018-09-06 10:49:18 +08001951 ret = mbhc->mbhc_cb->request_irq(component,
1952 mbhc->intr_ids->hph_right_ocp,
1953 wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
1954 mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301955 if (ret) {
1956 pr_err("%s: Failed to request irq %d\n", __func__,
1957 mbhc->intr_ids->hph_right_ocp);
1958 goto err_hphr_ocp_irq;
1959 }
1960
Karthikeyan Mani3dd07e62018-06-28 18:25:08 -07001961 mbhc->deinit_in_progress = false;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301962 pr_debug("%s: leave ret %d\n", __func__, ret);
1963 return ret;
1964
1965err_hphr_ocp_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001966 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->hph_left_ocp, mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301967err_hphl_ocp_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001968 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_hs_rem_intr,
1969 mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301970err_mbhc_hs_rem_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001971 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_hs_ins_intr,
1972 mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301973err_mbhc_hs_ins_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001974 mbhc->mbhc_cb->free_irq(component,
1975 mbhc->intr_ids->mbhc_btn_release_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301976 mbhc);
1977err_btn_release_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001978 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_btn_press_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301979 mbhc);
1980err_btn_press_irq:
Meng Wang15c825d2018-09-06 10:49:18 +08001981 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_sw_intr, mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301982err_mbhc_sw_irq:
1983 if (mbhc->mbhc_cb->register_notifier)
1984 mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
1985 mutex_destroy(&mbhc->codec_resource_lock);
1986err:
1987 pr_debug("%s: leave ret %d\n", __func__, ret);
1988 return ret;
1989}
1990EXPORT_SYMBOL(wcd_mbhc_init);
1991
1992void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
1993{
Meng Wang15c825d2018-09-06 10:49:18 +08001994 struct snd_soc_component *component = mbhc->component;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301995
Meng Wang15c825d2018-09-06 10:49:18 +08001996 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_sw_intr, mbhc);
1997 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_btn_press_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301998 mbhc);
Meng Wang15c825d2018-09-06 10:49:18 +08001999 mbhc->mbhc_cb->free_irq(component,
2000 mbhc->intr_ids->mbhc_btn_release_intr,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302001 mbhc);
Meng Wang15c825d2018-09-06 10:49:18 +08002002 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_hs_ins_intr,
2003 mbhc);
2004 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_hs_rem_intr,
2005 mbhc);
2006 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->hph_left_ocp, mbhc);
2007 mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->hph_right_ocp, mbhc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302008 if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier)
2009 mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
2010 if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) {
2011 WCD_MBHC_RSC_LOCK(mbhc);
2012 mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc,
2013 &mbhc->correct_plug_swch);
2014 WCD_MBHC_RSC_UNLOCK(mbhc);
2015 }
2016 mutex_destroy(&mbhc->codec_resource_lock);
2017 mutex_destroy(&mbhc->hphl_pa_lock);
2018 mutex_destroy(&mbhc->hphr_pa_lock);
2019}
2020EXPORT_SYMBOL(wcd_mbhc_deinit);
2021
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05302022static int __init mbhc_init(void)
2023{
2024 return 0;
2025}
2026
2027static void __exit mbhc_exit(void)
2028{
2029}
2030
2031module_init(mbhc_init);
2032module_exit(mbhc_exit);
2033
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302034MODULE_DESCRIPTION("wcd MBHC v2 module");
2035MODULE_LICENSE("GPL v2");