~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/codecs/wcd-mbhc-v2.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php