1650 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// 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 25018#define GND_MIC_SWAP_THRESHOLD 419#define GND_MIC_USBC_SWAP_THRESHOLD 220#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 10021#define HPHL_CROSS_CONN_THRESHOLD 10022#define HS_VREF_MIN_VAL 140023#define FAKE_REM_RETRY_ATTEMPTS 324#define WCD_MBHC_ADC_HS_THRESHOLD_MV 170025#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 7526#define WCD_MBHC_ADC_MICBIAS_MV 180027#define WCD_MBHC_FAKE_INS_RETRY 428 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 36enum 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 46struct 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 82static 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 93static 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 103static 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 110static 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 119static 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 codec125 * drivers itself and micbias is not needed for regular126 * plug type detection. So if micbias_control callback function127 * 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 165int 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 detect198 * button press/release events199 */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 else219 /* 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 else231 /* 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 else241 /* 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 else251 /* 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 else261 /* 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}269EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);270 271static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)272{273 return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);274}275 276static 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 292static 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 and298 * report release if we reported button press299 */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 313static 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 331static 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 headphone338 * 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 lineout381 * as during playback pa is on and impedance values382 * will not be correct resulting in lineout detected383 * 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 405static 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 else414 wcd_mbhc_report_plug_insertion(mbhc, jack_type);415 416}417 418static 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 427static 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 435static 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 455static 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 487static 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 495static 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 513static 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 579exit:580 mbhc->in_swch_irq_handler = false;581 mutex_unlock(&mbhc->lock);582}583 584static 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 594int 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}610EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug);611 612int 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}625EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug);626 627static 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 660static 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 670static 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");697done:698 mutex_unlock(&mbhc->lock);699 return IRQ_HANDLED;700}701 702static 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;728exit:729 mutex_unlock(&mbhc->lock);730 731 return IRQ_HANDLED;732}733 734static 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 747static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)748{749 return wcd_mbhc_hph_ocp_irq(data, false);750}751 752static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)753{754 return wcd_mbhc_hph_ocp_irq(data, true);755}756 757static 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 else776 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 else788 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 else802 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 else808 /* 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 834static 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 vout845 * micbias = 1.0V + VOUT_CTL * 50mV846 */847 micbias = 1000 + (vout_ctl * 50);848 }849 return micbias;850}851 852static int wcd_get_voltage_from_adc(u8 val, int micbias)853{854 /* Formula for calculating voltage from ADC855 * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8856 */857 return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));858}859 860static 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 899static 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 */959static 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 1010static 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 else1019 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 1028static 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 else1037 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 1046static 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 1070static 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 1083static 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 else1097 plug_type = MBHC_PLUG_TYPE_HEADSET;1098 1099 return plug_type;1100}1101 1102static 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 else1111 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 1119static 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 1148static 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 headphone1196 * else start the 3 sec loop1197 */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 1212correct_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 out1228 * 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 else1252 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 else1280 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 HEADSET1289 * 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 else1295 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 1300exit: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 and1307 * 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 COMPLETE1314 * 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 1326static 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 for1340 * any change in IN2_P1341 */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 for1352 * HEADPHONE, need to reject the ADC COMPLETE interrupt which1353 * 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 1364exit:1365 mutex_unlock(&mbhc->lock);1366 return IRQ_HANDLED;1367}1368 1369static 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 one1378 * 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 leave1391 * room for other interrupts to be processed1392 */1393 usleep_range(30000, 30100);1394 } while (--clamp_retry);1395 1396 /*1397 * If current plug is headphone then there is no chance to1398 * get ADC complete interrupt, so connected cable should be1399 * 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 1411int 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 else1419 return -EINVAL;1420}1421EXPORT_SYMBOL(wcd_mbhc_get_impedance);1422 1423void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)1424{1425 mbhc->hph_type = hph_type;1426}1427EXPORT_SYMBOL(wcd_mbhc_set_hph_type);1428 1429int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)1430{1431 return mbhc->hph_type;1432}1433EXPORT_SYMBOL(wcd_mbhc_get_hph_type);1434 1435int 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}1446EXPORT_SYMBOL(wcd_mbhc_start);1447 1448void 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}1455EXPORT_SYMBOL(wcd_mbhc_stop);1456 1457int 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 else1465 cfg->hphl_swh = true;1466 1467 if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))1468 cfg->gnd_swh = false;1469 else1470 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 else1477 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 else1484 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 else1497 /* Micro to Milli Volts */1498 cfg->btn_high[i] = cfg->btn_high[i]/1000;1499 }1500 1501 return 0;1502}1503EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);1504 1505struct 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 1596err_free_hph_left_ocp:1597 free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);1598err_free_hs_rem_intr:1599 free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);1600err_free_hs_ins_intr:1601 free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);1602err_free_btn_release_intr:1603 free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);1604err_free_btn_press_intr:1605 free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);1606err_free_sw_intr:1607 free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);1608err_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}1615EXPORT_SYMBOL(wcd_mbhc_init);1616 1617void 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}1634EXPORT_SYMBOL(wcd_mbhc_deinit);1635 1636static int __init mbhc_init(void)1637{1638 return 0;1639}1640 1641static void __exit mbhc_exit(void)1642{1643}1644 1645module_init(mbhc_init);1646module_exit(mbhc_exit);1647 1648MODULE_DESCRIPTION("wcd MBHC v2 module");1649MODULE_LICENSE("GPL");1650