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 µvolt); 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 µvolt); 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.