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