10e5c9e7fSSrinivas Kandagatla // SPDX-License-Identifier: GPL-2.0-only
20e5c9e7fSSrinivas Kandagatla // Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
30e5c9e7fSSrinivas Kandagatla
40e5c9e7fSSrinivas Kandagatla #include <linux/module.h>
50e5c9e7fSSrinivas Kandagatla #include <linux/init.h>
60e5c9e7fSSrinivas Kandagatla #include <linux/slab.h>
70e5c9e7fSSrinivas Kandagatla #include <linux/device.h>
8cc4d891fSSrinivas Kandagatla #include <linux/pm_runtime.h>
90e5c9e7fSSrinivas Kandagatla #include <linux/printk.h>
100e5c9e7fSSrinivas Kandagatla #include <linux/delay.h>
110e5c9e7fSSrinivas Kandagatla #include <linux/kernel.h>
120e5c9e7fSSrinivas Kandagatla #include <sound/soc.h>
130e5c9e7fSSrinivas Kandagatla #include <sound/jack.h>
140e5c9e7fSSrinivas Kandagatla #include "wcd-mbhc-v2.h"
150e5c9e7fSSrinivas Kandagatla
160e5c9e7fSSrinivas Kandagatla #define HS_DETECT_PLUG_TIME_MS (3 * 1000)
170e5c9e7fSSrinivas Kandagatla #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
180e5c9e7fSSrinivas Kandagatla #define GND_MIC_SWAP_THRESHOLD 4
190e5c9e7fSSrinivas Kandagatla #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
200e5c9e7fSSrinivas Kandagatla #define HPHL_CROSS_CONN_THRESHOLD 100
210e5c9e7fSSrinivas Kandagatla #define HS_VREF_MIN_VAL 1400
220e5c9e7fSSrinivas Kandagatla #define FAKE_REM_RETRY_ATTEMPTS 3
230e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700
240e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75
250e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_ADC_MICBIAS_MV 1800
260e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_FAKE_INS_RETRY 4
270e5c9e7fSSrinivas Kandagatla
280e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
290e5c9e7fSSrinivas Kandagatla SND_JACK_MECHANICAL)
300e5c9e7fSSrinivas Kandagatla
310e5c9e7fSSrinivas Kandagatla #define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
320e5c9e7fSSrinivas Kandagatla SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
330e5c9e7fSSrinivas Kandagatla SND_JACK_BTN_4 | SND_JACK_BTN_5)
340e5c9e7fSSrinivas Kandagatla
350e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_adc_mux_ctl {
360e5c9e7fSSrinivas Kandagatla MUX_CTL_AUTO = 0,
370e5c9e7fSSrinivas Kandagatla MUX_CTL_IN2P,
380e5c9e7fSSrinivas Kandagatla MUX_CTL_IN3P,
390e5c9e7fSSrinivas Kandagatla MUX_CTL_IN4P,
400e5c9e7fSSrinivas Kandagatla MUX_CTL_HPH_L,
410e5c9e7fSSrinivas Kandagatla MUX_CTL_HPH_R,
420e5c9e7fSSrinivas Kandagatla MUX_CTL_NONE,
430e5c9e7fSSrinivas Kandagatla };
440e5c9e7fSSrinivas Kandagatla
450e5c9e7fSSrinivas Kandagatla struct wcd_mbhc {
460e5c9e7fSSrinivas Kandagatla struct device *dev;
470e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component;
480e5c9e7fSSrinivas Kandagatla struct snd_soc_jack *jack;
490e5c9e7fSSrinivas Kandagatla struct wcd_mbhc_config *cfg;
500e5c9e7fSSrinivas Kandagatla const struct wcd_mbhc_cb *mbhc_cb;
510e5c9e7fSSrinivas Kandagatla const struct wcd_mbhc_intr *intr_ids;
520e5c9e7fSSrinivas Kandagatla struct wcd_mbhc_field *fields;
530e5c9e7fSSrinivas Kandagatla /* Delayed work to report long button press */
540e5c9e7fSSrinivas Kandagatla struct delayed_work mbhc_btn_dwork;
550e5c9e7fSSrinivas Kandagatla /* Work to correct accessory type */
560e5c9e7fSSrinivas Kandagatla struct work_struct correct_plug_swch;
570e5c9e7fSSrinivas Kandagatla struct mutex lock;
580e5c9e7fSSrinivas Kandagatla int buttons_pressed;
590e5c9e7fSSrinivas Kandagatla u32 hph_status; /* track headhpone status */
600e5c9e7fSSrinivas Kandagatla u8 current_plug;
610e5c9e7fSSrinivas Kandagatla bool is_btn_press;
620e5c9e7fSSrinivas Kandagatla bool in_swch_irq_handler;
630e5c9e7fSSrinivas Kandagatla bool hs_detect_work_stop;
640e5c9e7fSSrinivas Kandagatla bool is_hs_recording;
650e5c9e7fSSrinivas Kandagatla bool extn_cable_hph_rem;
660e5c9e7fSSrinivas Kandagatla bool force_linein;
670e5c9e7fSSrinivas Kandagatla bool impedance_detect;
680e5c9e7fSSrinivas Kandagatla unsigned long event_state;
690e5c9e7fSSrinivas Kandagatla unsigned long jiffies_atreport;
700e5c9e7fSSrinivas Kandagatla /* impedance of hphl and hphr */
710e5c9e7fSSrinivas Kandagatla uint32_t zl, zr;
720e5c9e7fSSrinivas Kandagatla /* Holds type of Headset - Mono/Stereo */
730e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_hph_type hph_type;
740e5c9e7fSSrinivas Kandagatla /* Holds mbhc detection method - ADC/Legacy */
750e5c9e7fSSrinivas Kandagatla int mbhc_detection_logic;
760e5c9e7fSSrinivas Kandagatla };
770e5c9e7fSSrinivas Kandagatla
wcd_mbhc_write_field(const struct wcd_mbhc * mbhc,int field,int val)780e5c9e7fSSrinivas Kandagatla static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
790e5c9e7fSSrinivas Kandagatla int field, int val)
800e5c9e7fSSrinivas Kandagatla {
810e5c9e7fSSrinivas Kandagatla if (!mbhc->fields[field].reg)
820e5c9e7fSSrinivas Kandagatla return 0;
830e5c9e7fSSrinivas Kandagatla
840e5c9e7fSSrinivas Kandagatla return snd_soc_component_write_field(mbhc->component,
850e5c9e7fSSrinivas Kandagatla mbhc->fields[field].reg,
860e5c9e7fSSrinivas Kandagatla mbhc->fields[field].mask, val);
870e5c9e7fSSrinivas Kandagatla }
880e5c9e7fSSrinivas Kandagatla
wcd_mbhc_read_field(const struct wcd_mbhc * mbhc,int field)890e5c9e7fSSrinivas Kandagatla static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
900e5c9e7fSSrinivas Kandagatla {
910e5c9e7fSSrinivas Kandagatla if (!mbhc->fields[field].reg)
920e5c9e7fSSrinivas Kandagatla return 0;
930e5c9e7fSSrinivas Kandagatla
940e5c9e7fSSrinivas Kandagatla return snd_soc_component_read_field(mbhc->component,
950e5c9e7fSSrinivas Kandagatla mbhc->fields[field].reg,
960e5c9e7fSSrinivas Kandagatla mbhc->fields[field].mask);
970e5c9e7fSSrinivas Kandagatla }
980e5c9e7fSSrinivas Kandagatla
wcd_program_hs_vref(struct wcd_mbhc * mbhc)990e5c9e7fSSrinivas Kandagatla static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
1000e5c9e7fSSrinivas Kandagatla {
1010e5c9e7fSSrinivas Kandagatla u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
1020e5c9e7fSSrinivas Kandagatla
1030e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
1040e5c9e7fSSrinivas Kandagatla }
1050e5c9e7fSSrinivas Kandagatla
wcd_program_btn_threshold(const struct wcd_mbhc * mbhc,bool micbias)1060e5c9e7fSSrinivas Kandagatla static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
1070e5c9e7fSSrinivas Kandagatla {
1080e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component = mbhc->component;
1090e5c9e7fSSrinivas Kandagatla
1100e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
1110e5c9e7fSSrinivas Kandagatla mbhc->cfg->btn_high,
1120e5c9e7fSSrinivas Kandagatla mbhc->cfg->num_btn, micbias);
1130e5c9e7fSSrinivas Kandagatla }
1140e5c9e7fSSrinivas Kandagatla
wcd_mbhc_curr_micbias_control(const struct wcd_mbhc * mbhc,const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)1150e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
1160e5c9e7fSSrinivas Kandagatla const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
1170e5c9e7fSSrinivas Kandagatla {
1180e5c9e7fSSrinivas Kandagatla
1190e5c9e7fSSrinivas Kandagatla /*
1200e5c9e7fSSrinivas Kandagatla * Some codecs handle micbias/pullup enablement in codec
1210e5c9e7fSSrinivas Kandagatla * drivers itself and micbias is not needed for regular
1220e5c9e7fSSrinivas Kandagatla * plug type detection. So if micbias_control callback function
1230e5c9e7fSSrinivas Kandagatla * is defined, just return.
1240e5c9e7fSSrinivas Kandagatla */
1250e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control)
1260e5c9e7fSSrinivas Kandagatla return;
1270e5c9e7fSSrinivas Kandagatla
1280e5c9e7fSSrinivas Kandagatla switch (cs_mb_en) {
1290e5c9e7fSSrinivas Kandagatla case WCD_MBHC_EN_CS:
1300e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
1310e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1320e5c9e7fSSrinivas Kandagatla /* Program Button threshold registers as per CS */
1330e5c9e7fSSrinivas Kandagatla wcd_program_btn_threshold(mbhc, false);
1340e5c9e7fSSrinivas Kandagatla break;
1350e5c9e7fSSrinivas Kandagatla case WCD_MBHC_EN_MB:
1360e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1370e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1380e5c9e7fSSrinivas Kandagatla /* Disable PULL_UP_EN & enable MICBIAS */
1390e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
1400e5c9e7fSSrinivas Kandagatla /* Program Button threshold registers as per MICBIAS */
1410e5c9e7fSSrinivas Kandagatla wcd_program_btn_threshold(mbhc, true);
1420e5c9e7fSSrinivas Kandagatla break;
1430e5c9e7fSSrinivas Kandagatla case WCD_MBHC_EN_PULLUP:
1440e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1450e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1460e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
1470e5c9e7fSSrinivas Kandagatla /* Program Button threshold registers as per MICBIAS */
1480e5c9e7fSSrinivas Kandagatla wcd_program_btn_threshold(mbhc, true);
1490e5c9e7fSSrinivas Kandagatla break;
1500e5c9e7fSSrinivas Kandagatla case WCD_MBHC_EN_NONE:
1510e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1520e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
1530e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
1540e5c9e7fSSrinivas Kandagatla break;
1550e5c9e7fSSrinivas Kandagatla default:
1560e5c9e7fSSrinivas Kandagatla dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
1570e5c9e7fSSrinivas Kandagatla break;
1580e5c9e7fSSrinivas Kandagatla }
1590e5c9e7fSSrinivas Kandagatla }
1600e5c9e7fSSrinivas Kandagatla
wcd_mbhc_event_notify(struct wcd_mbhc * mbhc,unsigned long event)1610e5c9e7fSSrinivas Kandagatla int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
1620e5c9e7fSSrinivas Kandagatla {
1630e5c9e7fSSrinivas Kandagatla
1640e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component;
1650e5c9e7fSSrinivas Kandagatla bool micbias2 = false;
1660e5c9e7fSSrinivas Kandagatla
1670e5c9e7fSSrinivas Kandagatla if (!mbhc)
1680e5c9e7fSSrinivas Kandagatla return 0;
1690e5c9e7fSSrinivas Kandagatla
1700e5c9e7fSSrinivas Kandagatla component = mbhc->component;
1710e5c9e7fSSrinivas Kandagatla
1720e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->micbias_enable_status)
1730e5c9e7fSSrinivas Kandagatla micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
1740e5c9e7fSSrinivas Kandagatla
1750e5c9e7fSSrinivas Kandagatla switch (event) {
1760e5c9e7fSSrinivas Kandagatla /* MICBIAS usage change */
1770e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
1780e5c9e7fSSrinivas Kandagatla mbhc->is_hs_recording = true;
1790e5c9e7fSSrinivas Kandagatla break;
1800e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_MICBIAS_2_ON:
1810e5c9e7fSSrinivas Kandagatla /* Disable current source if micbias2 enabled */
1820e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control) {
1830e5c9e7fSSrinivas Kandagatla if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
1840e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
1850e5c9e7fSSrinivas Kandagatla } else {
1860e5c9e7fSSrinivas Kandagatla mbhc->is_hs_recording = true;
1870e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
1880e5c9e7fSSrinivas Kandagatla }
1890e5c9e7fSSrinivas Kandagatla break;
1900e5c9e7fSSrinivas Kandagatla case WCD_EVENT_PRE_MICBIAS_2_OFF:
1910e5c9e7fSSrinivas Kandagatla /*
1920e5c9e7fSSrinivas Kandagatla * Before MICBIAS_2 is turned off, if FSM is enabled,
1930e5c9e7fSSrinivas Kandagatla * make sure current source is enabled so as to detect
1940e5c9e7fSSrinivas Kandagatla * button press/release events
1950e5c9e7fSSrinivas Kandagatla */
1960e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
1970e5c9e7fSSrinivas Kandagatla if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
1980e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
1990e5c9e7fSSrinivas Kandagatla }
2000e5c9e7fSSrinivas Kandagatla break;
2010e5c9e7fSSrinivas Kandagatla /* MICBIAS usage change */
2020e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
2030e5c9e7fSSrinivas Kandagatla mbhc->is_hs_recording = false;
2040e5c9e7fSSrinivas Kandagatla break;
2050e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_MICBIAS_2_OFF:
2060e5c9e7fSSrinivas Kandagatla if (!mbhc->mbhc_cb->mbhc_micbias_control)
2070e5c9e7fSSrinivas Kandagatla mbhc->is_hs_recording = false;
2080e5c9e7fSSrinivas Kandagatla
2090e5c9e7fSSrinivas Kandagatla /* Enable PULL UP if PA's are enabled */
2100e5c9e7fSSrinivas Kandagatla if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
2110e5c9e7fSSrinivas Kandagatla (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
2120e5c9e7fSSrinivas Kandagatla /* enable pullup and cs, disable mb */
2130e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2140e5c9e7fSSrinivas Kandagatla else
2150e5c9e7fSSrinivas Kandagatla /* enable current source and disable mb, pullup*/
2160e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2170e5c9e7fSSrinivas Kandagatla
2180e5c9e7fSSrinivas Kandagatla break;
2190e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_HPHL_PA_OFF:
2200e5c9e7fSSrinivas Kandagatla clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
2210e5c9e7fSSrinivas Kandagatla
2220e5c9e7fSSrinivas Kandagatla /* check if micbias is enabled */
2230e5c9e7fSSrinivas Kandagatla if (micbias2)
2240e5c9e7fSSrinivas Kandagatla /* Disable cs, pullup & enable micbias */
2250e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2260e5c9e7fSSrinivas Kandagatla else
2270e5c9e7fSSrinivas Kandagatla /* Disable micbias, pullup & enable cs */
2280e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2290e5c9e7fSSrinivas Kandagatla break;
2300e5c9e7fSSrinivas Kandagatla case WCD_EVENT_POST_HPHR_PA_OFF:
2310e5c9e7fSSrinivas Kandagatla clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
2320e5c9e7fSSrinivas Kandagatla /* check if micbias is enabled */
2330e5c9e7fSSrinivas Kandagatla if (micbias2)
2340e5c9e7fSSrinivas Kandagatla /* Disable cs, pullup & enable micbias */
2350e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2360e5c9e7fSSrinivas Kandagatla else
2370e5c9e7fSSrinivas Kandagatla /* Disable micbias, pullup & enable cs */
2380e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
2390e5c9e7fSSrinivas Kandagatla break;
2400e5c9e7fSSrinivas Kandagatla case WCD_EVENT_PRE_HPHL_PA_ON:
2410e5c9e7fSSrinivas Kandagatla set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
2420e5c9e7fSSrinivas Kandagatla /* check if micbias is enabled */
2430e5c9e7fSSrinivas Kandagatla if (micbias2)
2440e5c9e7fSSrinivas Kandagatla /* Disable cs, pullup & enable micbias */
2450e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2460e5c9e7fSSrinivas Kandagatla else
2470e5c9e7fSSrinivas Kandagatla /* Disable micbias, enable pullup & cs */
2480e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2490e5c9e7fSSrinivas Kandagatla break;
2500e5c9e7fSSrinivas Kandagatla case WCD_EVENT_PRE_HPHR_PA_ON:
2510e5c9e7fSSrinivas Kandagatla set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
2520e5c9e7fSSrinivas Kandagatla /* check if micbias is enabled */
2530e5c9e7fSSrinivas Kandagatla if (micbias2)
2540e5c9e7fSSrinivas Kandagatla /* Disable cs, pullup & enable micbias */
2550e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
2560e5c9e7fSSrinivas Kandagatla else
2570e5c9e7fSSrinivas Kandagatla /* Disable micbias, enable pullup & cs */
2580e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
2590e5c9e7fSSrinivas Kandagatla break;
2600e5c9e7fSSrinivas Kandagatla default:
2610e5c9e7fSSrinivas Kandagatla break;
2620e5c9e7fSSrinivas Kandagatla }
2630e5c9e7fSSrinivas Kandagatla return 0;
2640e5c9e7fSSrinivas Kandagatla }
2650e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
2660e5c9e7fSSrinivas Kandagatla
wcd_cancel_btn_work(struct wcd_mbhc * mbhc)2670e5c9e7fSSrinivas Kandagatla static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
2680e5c9e7fSSrinivas Kandagatla {
2690e5c9e7fSSrinivas Kandagatla return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
2700e5c9e7fSSrinivas Kandagatla }
2710e5c9e7fSSrinivas Kandagatla
wcd_micbias_disable(struct wcd_mbhc * mbhc)2720e5c9e7fSSrinivas Kandagatla static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
2730e5c9e7fSSrinivas Kandagatla {
2740e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component = mbhc->component;
2750e5c9e7fSSrinivas Kandagatla
2760e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control)
2770e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
2780e5c9e7fSSrinivas Kandagatla
2790e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
2800e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
2810e5c9e7fSSrinivas Kandagatla
2820e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->set_micbias_value) {
2830e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->set_micbias_value(component);
2840e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
2850e5c9e7fSSrinivas Kandagatla }
2860e5c9e7fSSrinivas Kandagatla }
2870e5c9e7fSSrinivas Kandagatla
wcd_mbhc_report_plug_removal(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)2880e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
2890e5c9e7fSSrinivas Kandagatla enum snd_jack_types jack_type)
2900e5c9e7fSSrinivas Kandagatla {
2910e5c9e7fSSrinivas Kandagatla mbhc->hph_status &= ~jack_type;
2920e5c9e7fSSrinivas Kandagatla /*
2930e5c9e7fSSrinivas Kandagatla * cancel possibly scheduled btn work and
2940e5c9e7fSSrinivas Kandagatla * report release if we reported button press
2950e5c9e7fSSrinivas Kandagatla */
2960e5c9e7fSSrinivas Kandagatla if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
2970e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
2980e5c9e7fSSrinivas Kandagatla mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
2990e5c9e7fSSrinivas Kandagatla }
3000e5c9e7fSSrinivas Kandagatla
3010e5c9e7fSSrinivas Kandagatla wcd_micbias_disable(mbhc);
3020e5c9e7fSSrinivas Kandagatla mbhc->hph_type = WCD_MBHC_HPH_NONE;
3030e5c9e7fSSrinivas Kandagatla mbhc->zl = mbhc->zr = 0;
3040e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
3050e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
3060e5c9e7fSSrinivas Kandagatla mbhc->force_linein = false;
3070e5c9e7fSSrinivas Kandagatla }
3080e5c9e7fSSrinivas Kandagatla
wcd_mbhc_compute_impedance(struct wcd_mbhc * mbhc)3090e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
3100e5c9e7fSSrinivas Kandagatla {
3110e5c9e7fSSrinivas Kandagatla
3120e5c9e7fSSrinivas Kandagatla if (!mbhc->impedance_detect)
3130e5c9e7fSSrinivas Kandagatla return;
3140e5c9e7fSSrinivas Kandagatla
3150e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->linein_th != 0) {
3160e5c9e7fSSrinivas Kandagatla u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
3170e5c9e7fSSrinivas Kandagatla /* Set MUX_CTL to AUTO for Z-det */
3180e5c9e7fSSrinivas Kandagatla
3190e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
3200e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
3210e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
3220e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
3230e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
3240e5c9e7fSSrinivas Kandagatla }
3250e5c9e7fSSrinivas Kandagatla }
3260e5c9e7fSSrinivas Kandagatla
wcd_mbhc_report_plug_insertion(struct wcd_mbhc * mbhc,enum snd_jack_types jack_type)3270e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
3280e5c9e7fSSrinivas Kandagatla enum snd_jack_types jack_type)
3290e5c9e7fSSrinivas Kandagatla {
3300e5c9e7fSSrinivas Kandagatla bool is_pa_on;
3310e5c9e7fSSrinivas Kandagatla /*
3320e5c9e7fSSrinivas Kandagatla * Report removal of current jack type.
3330e5c9e7fSSrinivas Kandagatla * Headphone to headset shouldn't report headphone
3340e5c9e7fSSrinivas Kandagatla * removal.
3350e5c9e7fSSrinivas Kandagatla */
3360e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
3370e5c9e7fSSrinivas Kandagatla jack_type == SND_JACK_HEADPHONE)
3380e5c9e7fSSrinivas Kandagatla mbhc->hph_status &= ~SND_JACK_HEADSET;
3390e5c9e7fSSrinivas Kandagatla
3400e5c9e7fSSrinivas Kandagatla /* Report insertion */
3410e5c9e7fSSrinivas Kandagatla switch (jack_type) {
3420e5c9e7fSSrinivas Kandagatla case SND_JACK_HEADPHONE:
3430e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
3440e5c9e7fSSrinivas Kandagatla break;
3450e5c9e7fSSrinivas Kandagatla case SND_JACK_HEADSET:
3460e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
3470e5c9e7fSSrinivas Kandagatla mbhc->jiffies_atreport = jiffies;
3480e5c9e7fSSrinivas Kandagatla break;
3490e5c9e7fSSrinivas Kandagatla case SND_JACK_LINEOUT:
3500e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3510e5c9e7fSSrinivas Kandagatla break;
3520e5c9e7fSSrinivas Kandagatla default:
3530e5c9e7fSSrinivas Kandagatla break;
3540e5c9e7fSSrinivas Kandagatla }
3550e5c9e7fSSrinivas Kandagatla
3560e5c9e7fSSrinivas Kandagatla
3570e5c9e7fSSrinivas Kandagatla is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
3580e5c9e7fSSrinivas Kandagatla
3590e5c9e7fSSrinivas Kandagatla if (!is_pa_on) {
3600e5c9e7fSSrinivas Kandagatla wcd_mbhc_compute_impedance(mbhc);
3610e5c9e7fSSrinivas Kandagatla if ((mbhc->zl > mbhc->cfg->linein_th) &&
3620e5c9e7fSSrinivas Kandagatla (mbhc->zr > mbhc->cfg->linein_th) &&
3630e5c9e7fSSrinivas Kandagatla (jack_type == SND_JACK_HEADPHONE)) {
3640e5c9e7fSSrinivas Kandagatla jack_type = SND_JACK_LINEOUT;
3650e5c9e7fSSrinivas Kandagatla mbhc->force_linein = true;
3660e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3670e5c9e7fSSrinivas Kandagatla if (mbhc->hph_status) {
3680e5c9e7fSSrinivas Kandagatla mbhc->hph_status &= ~(SND_JACK_HEADSET |
3690e5c9e7fSSrinivas Kandagatla SND_JACK_LINEOUT);
3700e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
3710e5c9e7fSSrinivas Kandagatla WCD_MBHC_JACK_MASK);
3720e5c9e7fSSrinivas Kandagatla }
3730e5c9e7fSSrinivas Kandagatla }
3740e5c9e7fSSrinivas Kandagatla }
3750e5c9e7fSSrinivas Kandagatla
3760e5c9e7fSSrinivas Kandagatla /* Do not calculate impedance again for lineout
3770e5c9e7fSSrinivas Kandagatla * as during playback pa is on and impedance values
3780e5c9e7fSSrinivas Kandagatla * will not be correct resulting in lineout detected
3790e5c9e7fSSrinivas Kandagatla * as headphone.
3800e5c9e7fSSrinivas Kandagatla */
3810e5c9e7fSSrinivas Kandagatla if (is_pa_on && mbhc->force_linein) {
3820e5c9e7fSSrinivas Kandagatla jack_type = SND_JACK_LINEOUT;
3830e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
3840e5c9e7fSSrinivas Kandagatla if (mbhc->hph_status) {
3850e5c9e7fSSrinivas Kandagatla mbhc->hph_status &= ~(SND_JACK_HEADSET |
3860e5c9e7fSSrinivas Kandagatla SND_JACK_LINEOUT);
3870e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
3880e5c9e7fSSrinivas Kandagatla WCD_MBHC_JACK_MASK);
3890e5c9e7fSSrinivas Kandagatla }
3900e5c9e7fSSrinivas Kandagatla }
3910e5c9e7fSSrinivas Kandagatla
3920e5c9e7fSSrinivas Kandagatla mbhc->hph_status |= jack_type;
3930e5c9e7fSSrinivas Kandagatla
3940e5c9e7fSSrinivas Kandagatla if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
3950e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
3960e5c9e7fSSrinivas Kandagatla
3970e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
3980e5c9e7fSSrinivas Kandagatla WCD_MBHC_JACK_MASK);
3990e5c9e7fSSrinivas Kandagatla }
4000e5c9e7fSSrinivas Kandagatla
wcd_mbhc_report_plug(struct wcd_mbhc * mbhc,int insertion,enum snd_jack_types jack_type)4010e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
4020e5c9e7fSSrinivas Kandagatla enum snd_jack_types jack_type)
4030e5c9e7fSSrinivas Kandagatla {
4040e5c9e7fSSrinivas Kandagatla
4050e5c9e7fSSrinivas Kandagatla WARN_ON(!mutex_is_locked(&mbhc->lock));
4060e5c9e7fSSrinivas Kandagatla
4070e5c9e7fSSrinivas Kandagatla if (!insertion) /* Report removal */
4080e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug_removal(mbhc, jack_type);
4090e5c9e7fSSrinivas Kandagatla else
4100e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug_insertion(mbhc, jack_type);
4110e5c9e7fSSrinivas Kandagatla
4120e5c9e7fSSrinivas Kandagatla }
4130e5c9e7fSSrinivas Kandagatla
wcd_cancel_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)4140e5c9e7fSSrinivas Kandagatla static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
4150e5c9e7fSSrinivas Kandagatla struct work_struct *work)
4160e5c9e7fSSrinivas Kandagatla {
4170e5c9e7fSSrinivas Kandagatla mbhc->hs_detect_work_stop = true;
4180e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
4190e5c9e7fSSrinivas Kandagatla cancel_work_sync(work);
4200e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
4210e5c9e7fSSrinivas Kandagatla }
4220e5c9e7fSSrinivas Kandagatla
wcd_mbhc_cancel_pending_work(struct wcd_mbhc * mbhc)4230e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
4240e5c9e7fSSrinivas Kandagatla {
4250e5c9e7fSSrinivas Kandagatla /* cancel pending button press */
4260e5c9e7fSSrinivas Kandagatla wcd_cancel_btn_work(mbhc);
4270e5c9e7fSSrinivas Kandagatla /* cancel correct work function */
4280e5c9e7fSSrinivas Kandagatla wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
4290e5c9e7fSSrinivas Kandagatla }
4300e5c9e7fSSrinivas Kandagatla
wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc * mbhc)4310e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
4320e5c9e7fSSrinivas Kandagatla {
4330e5c9e7fSSrinivas Kandagatla wcd_mbhc_cancel_pending_work(mbhc);
4340e5c9e7fSSrinivas Kandagatla /* Report extension cable */
4350e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
4360e5c9e7fSSrinivas Kandagatla /*
4370e5c9e7fSSrinivas Kandagatla * Disable HPHL trigger and MIC Schmitt triggers.
4380e5c9e7fSSrinivas Kandagatla * Setup for insertion detection.
4390e5c9e7fSSrinivas Kandagatla */
4400e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
4410e5c9e7fSSrinivas Kandagatla wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
4420e5c9e7fSSrinivas Kandagatla /* Disable HW FSM */
4430e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
4440e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
4450e5c9e7fSSrinivas Kandagatla
4460e5c9e7fSSrinivas Kandagatla /* Set the detection type appropriately */
4470e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
4480e5c9e7fSSrinivas Kandagatla enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
4490e5c9e7fSSrinivas Kandagatla }
4500e5c9e7fSSrinivas Kandagatla
wcd_mbhc_find_plug_and_report(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)4510e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
4520e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_plug_type plug_type)
4530e5c9e7fSSrinivas Kandagatla {
4540e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == plug_type)
4550e5c9e7fSSrinivas Kandagatla return;
4560e5c9e7fSSrinivas Kandagatla
4570e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
4580e5c9e7fSSrinivas Kandagatla
4590e5c9e7fSSrinivas Kandagatla switch (plug_type) {
4600e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADPHONE:
4610e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
4620e5c9e7fSSrinivas Kandagatla break;
4630e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADSET:
4640e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
4650e5c9e7fSSrinivas Kandagatla break;
4660e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HIGH_HPH:
4670e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
4680e5c9e7fSSrinivas Kandagatla break;
4690e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_GND_MIC_SWAP:
4700e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
4710e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
4720e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
4730e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
4740e5c9e7fSSrinivas Kandagatla break;
4750e5c9e7fSSrinivas Kandagatla default:
4760e5c9e7fSSrinivas Kandagatla WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
4770e5c9e7fSSrinivas Kandagatla mbhc->current_plug, plug_type);
4780e5c9e7fSSrinivas Kandagatla break;
4790e5c9e7fSSrinivas Kandagatla }
4800e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
4810e5c9e7fSSrinivas Kandagatla }
4820e5c9e7fSSrinivas Kandagatla
wcd_schedule_hs_detect_plug(struct wcd_mbhc * mbhc,struct work_struct * work)4830e5c9e7fSSrinivas Kandagatla static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
4840e5c9e7fSSrinivas Kandagatla struct work_struct *work)
4850e5c9e7fSSrinivas Kandagatla {
4860e5c9e7fSSrinivas Kandagatla WARN_ON(!mutex_is_locked(&mbhc->lock));
4870e5c9e7fSSrinivas Kandagatla mbhc->hs_detect_work_stop = false;
4880e5c9e7fSSrinivas Kandagatla schedule_work(work);
4890e5c9e7fSSrinivas Kandagatla }
4900e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc * mbhc)4910e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
4920e5c9e7fSSrinivas Kandagatla {
4930e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component = mbhc->component;
4940e5c9e7fSSrinivas Kandagatla
4950e5c9e7fSSrinivas Kandagatla WARN_ON(!mutex_is_locked(&mbhc->lock));
4960e5c9e7fSSrinivas Kandagatla
4970e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->hph_pull_down_ctrl)
4980e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
4990e5c9e7fSSrinivas Kandagatla
5000e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
5010e5c9e7fSSrinivas Kandagatla
5020e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control) {
5030e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
5040e5c9e7fSSrinivas Kandagatla MICB_ENABLE);
5050e5c9e7fSSrinivas Kandagatla wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
5060e5c9e7fSSrinivas Kandagatla }
5070e5c9e7fSSrinivas Kandagatla }
5080e5c9e7fSSrinivas Kandagatla
wcd_mbhc_mech_plug_detect_irq(int irq,void * data)5090e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
5100e5c9e7fSSrinivas Kandagatla {
5110e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component;
5120e5c9e7fSSrinivas Kandagatla enum snd_jack_types jack_type;
5130e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = data;
5140e5c9e7fSSrinivas Kandagatla bool detection_type;
5150e5c9e7fSSrinivas Kandagatla
5160e5c9e7fSSrinivas Kandagatla component = mbhc->component;
5170e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
5180e5c9e7fSSrinivas Kandagatla
5190e5c9e7fSSrinivas Kandagatla mbhc->in_swch_irq_handler = true;
5200e5c9e7fSSrinivas Kandagatla
5210e5c9e7fSSrinivas Kandagatla wcd_mbhc_cancel_pending_work(mbhc);
5220e5c9e7fSSrinivas Kandagatla
5230e5c9e7fSSrinivas Kandagatla detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
5240e5c9e7fSSrinivas Kandagatla
5250e5c9e7fSSrinivas Kandagatla /* Set the detection type appropriately */
5260e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
5270e5c9e7fSSrinivas Kandagatla
5280e5c9e7fSSrinivas Kandagatla /* Enable micbias ramp */
5290e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
5300e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
5310e5c9e7fSSrinivas Kandagatla
5320e5c9e7fSSrinivas Kandagatla if (detection_type) {
5330e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
5340e5c9e7fSSrinivas Kandagatla goto exit;
5350e5c9e7fSSrinivas Kandagatla /* Make sure MASTER_BIAS_CTL is enabled */
5360e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_bias(component, true);
5370e5c9e7fSSrinivas Kandagatla mbhc->is_btn_press = false;
5380e5c9e7fSSrinivas Kandagatla wcd_mbhc_adc_detect_plug_type(mbhc);
5390e5c9e7fSSrinivas Kandagatla } else {
5400e5c9e7fSSrinivas Kandagatla /* Disable HW FSM */
5410e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
5420e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
5430e5c9e7fSSrinivas Kandagatla mbhc->extn_cable_hph_rem = false;
5440e5c9e7fSSrinivas Kandagatla
5450e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
5460e5c9e7fSSrinivas Kandagatla goto exit;
5470e5c9e7fSSrinivas Kandagatla
5480e5c9e7fSSrinivas Kandagatla mbhc->is_btn_press = false;
5490e5c9e7fSSrinivas Kandagatla switch (mbhc->current_plug) {
5500e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADPHONE:
5510e5c9e7fSSrinivas Kandagatla jack_type = SND_JACK_HEADPHONE;
5520e5c9e7fSSrinivas Kandagatla break;
5530e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADSET:
5540e5c9e7fSSrinivas Kandagatla jack_type = SND_JACK_HEADSET;
5550e5c9e7fSSrinivas Kandagatla break;
5560e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HIGH_HPH:
5570e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
5580e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
5590e5c9e7fSSrinivas Kandagatla jack_type = SND_JACK_LINEOUT;
5600e5c9e7fSSrinivas Kandagatla break;
5610e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_GND_MIC_SWAP:
5620e5c9e7fSSrinivas Kandagatla dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
5630e5c9e7fSSrinivas Kandagatla goto exit;
5640e5c9e7fSSrinivas Kandagatla default:
5650e5c9e7fSSrinivas Kandagatla dev_err(mbhc->dev, "Invalid current plug: %d\n",
5660e5c9e7fSSrinivas Kandagatla mbhc->current_plug);
5670e5c9e7fSSrinivas Kandagatla goto exit;
5680e5c9e7fSSrinivas Kandagatla }
5690e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
5700e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
5710e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
5720e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
5730e5c9e7fSSrinivas Kandagatla wcd_mbhc_report_plug(mbhc, 0, jack_type);
5740e5c9e7fSSrinivas Kandagatla }
5750e5c9e7fSSrinivas Kandagatla
5760e5c9e7fSSrinivas Kandagatla exit:
5770e5c9e7fSSrinivas Kandagatla mbhc->in_swch_irq_handler = false;
5780e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
5790e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
5800e5c9e7fSSrinivas Kandagatla }
5810e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_button_mask(struct wcd_mbhc * mbhc)5820e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
5830e5c9e7fSSrinivas Kandagatla {
5840e5c9e7fSSrinivas Kandagatla int mask = 0;
5850e5c9e7fSSrinivas Kandagatla int btn;
5860e5c9e7fSSrinivas Kandagatla
5870e5c9e7fSSrinivas Kandagatla btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
5880e5c9e7fSSrinivas Kandagatla
5890e5c9e7fSSrinivas Kandagatla switch (btn) {
5900e5c9e7fSSrinivas Kandagatla case 0:
5910e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_0;
5920e5c9e7fSSrinivas Kandagatla break;
5930e5c9e7fSSrinivas Kandagatla case 1:
5940e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_1;
5950e5c9e7fSSrinivas Kandagatla break;
5960e5c9e7fSSrinivas Kandagatla case 2:
5970e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_2;
5980e5c9e7fSSrinivas Kandagatla break;
5990e5c9e7fSSrinivas Kandagatla case 3:
6000e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_3;
6010e5c9e7fSSrinivas Kandagatla break;
6020e5c9e7fSSrinivas Kandagatla case 4:
6030e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_4;
6040e5c9e7fSSrinivas Kandagatla break;
6050e5c9e7fSSrinivas Kandagatla case 5:
6060e5c9e7fSSrinivas Kandagatla mask = SND_JACK_BTN_5;
6070e5c9e7fSSrinivas Kandagatla break;
6080e5c9e7fSSrinivas Kandagatla default:
6090e5c9e7fSSrinivas Kandagatla break;
6100e5c9e7fSSrinivas Kandagatla }
6110e5c9e7fSSrinivas Kandagatla
6120e5c9e7fSSrinivas Kandagatla return mask;
6130e5c9e7fSSrinivas Kandagatla }
6140e5c9e7fSSrinivas Kandagatla
wcd_btn_long_press_fn(struct work_struct * work)6150e5c9e7fSSrinivas Kandagatla static void wcd_btn_long_press_fn(struct work_struct *work)
6160e5c9e7fSSrinivas Kandagatla {
6170e5c9e7fSSrinivas Kandagatla struct delayed_work *dwork = to_delayed_work(work);
6180e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
6190e5c9e7fSSrinivas Kandagatla
6200e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
6210e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
6220e5c9e7fSSrinivas Kandagatla mbhc->buttons_pressed);
6230e5c9e7fSSrinivas Kandagatla }
6240e5c9e7fSSrinivas Kandagatla
wcd_mbhc_btn_press_handler(int irq,void * data)6250e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
6260e5c9e7fSSrinivas Kandagatla {
6270e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = data;
6280e5c9e7fSSrinivas Kandagatla int mask;
6290e5c9e7fSSrinivas Kandagatla unsigned long msec_val;
6300e5c9e7fSSrinivas Kandagatla
6310e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
6320e5c9e7fSSrinivas Kandagatla wcd_cancel_btn_work(mbhc);
6330e5c9e7fSSrinivas Kandagatla mbhc->is_btn_press = true;
6340e5c9e7fSSrinivas Kandagatla msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
6350e5c9e7fSSrinivas Kandagatla
6360e5c9e7fSSrinivas Kandagatla /* Too short, ignore button press */
6370e5c9e7fSSrinivas Kandagatla if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
6380e5c9e7fSSrinivas Kandagatla goto done;
6390e5c9e7fSSrinivas Kandagatla
6400e5c9e7fSSrinivas Kandagatla /* If switch interrupt already kicked in, ignore button press */
6410e5c9e7fSSrinivas Kandagatla if (mbhc->in_swch_irq_handler)
6420e5c9e7fSSrinivas Kandagatla goto done;
6430e5c9e7fSSrinivas Kandagatla
6440e5c9e7fSSrinivas Kandagatla /* Plug isn't headset, ignore button press */
6450e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
6460e5c9e7fSSrinivas Kandagatla goto done;
6470e5c9e7fSSrinivas Kandagatla
6480e5c9e7fSSrinivas Kandagatla mask = wcd_mbhc_get_button_mask(mbhc);
6490e5c9e7fSSrinivas Kandagatla mbhc->buttons_pressed |= mask;
6500e5c9e7fSSrinivas Kandagatla if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
6510e5c9e7fSSrinivas Kandagatla WARN(1, "Button pressed twice without release event\n");
6520e5c9e7fSSrinivas Kandagatla done:
6530e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
6540e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
6550e5c9e7fSSrinivas Kandagatla }
6560e5c9e7fSSrinivas Kandagatla
wcd_mbhc_btn_release_handler(int irq,void * data)6570e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
6580e5c9e7fSSrinivas Kandagatla {
6590e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = data;
6600e5c9e7fSSrinivas Kandagatla int ret;
6610e5c9e7fSSrinivas Kandagatla
6620e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
6630e5c9e7fSSrinivas Kandagatla if (mbhc->is_btn_press)
6640e5c9e7fSSrinivas Kandagatla mbhc->is_btn_press = false;
6650e5c9e7fSSrinivas Kandagatla else /* fake btn press */
6660e5c9e7fSSrinivas Kandagatla goto exit;
6670e5c9e7fSSrinivas Kandagatla
6680e5c9e7fSSrinivas Kandagatla if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
6690e5c9e7fSSrinivas Kandagatla goto exit;
6700e5c9e7fSSrinivas Kandagatla
6710e5c9e7fSSrinivas Kandagatla ret = wcd_cancel_btn_work(mbhc);
6720e5c9e7fSSrinivas Kandagatla if (ret == 0) { /* Reporting long button release event */
6730e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
6740e5c9e7fSSrinivas Kandagatla } else {
6750e5c9e7fSSrinivas Kandagatla if (!mbhc->in_swch_irq_handler) {
6760e5c9e7fSSrinivas Kandagatla /* Reporting btn press n Release */
6770e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
6780e5c9e7fSSrinivas Kandagatla mbhc->buttons_pressed);
6790e5c9e7fSSrinivas Kandagatla snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
6800e5c9e7fSSrinivas Kandagatla }
6810e5c9e7fSSrinivas Kandagatla }
6820e5c9e7fSSrinivas Kandagatla mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
6830e5c9e7fSSrinivas Kandagatla exit:
6840e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
6850e5c9e7fSSrinivas Kandagatla
6860e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
6870e5c9e7fSSrinivas Kandagatla }
6880e5c9e7fSSrinivas Kandagatla
wcd_mbhc_hph_ocp_irq(struct wcd_mbhc * mbhc,bool hphr)6890e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
6900e5c9e7fSSrinivas Kandagatla {
6910e5c9e7fSSrinivas Kandagatla
6920e5c9e7fSSrinivas Kandagatla /* TODO Find a better way to report this to Userspace */
6930e5c9e7fSSrinivas Kandagatla dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
6940e5c9e7fSSrinivas Kandagatla hphr ? "HPHR" : "HPHL");
6950e5c9e7fSSrinivas Kandagatla
6960e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
6970e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
6980e5c9e7fSSrinivas Kandagatla
6990e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
7000e5c9e7fSSrinivas Kandagatla }
7010e5c9e7fSSrinivas Kandagatla
wcd_mbhc_hphl_ocp_irq(int irq,void * data)7020e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
7030e5c9e7fSSrinivas Kandagatla {
7040e5c9e7fSSrinivas Kandagatla return wcd_mbhc_hph_ocp_irq(data, false);
7050e5c9e7fSSrinivas Kandagatla }
7060e5c9e7fSSrinivas Kandagatla
wcd_mbhc_hphr_ocp_irq(int irq,void * data)7070e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
7080e5c9e7fSSrinivas Kandagatla {
7090e5c9e7fSSrinivas Kandagatla return wcd_mbhc_hph_ocp_irq(data, true);
7100e5c9e7fSSrinivas Kandagatla }
7110e5c9e7fSSrinivas Kandagatla
wcd_mbhc_initialise(struct wcd_mbhc * mbhc)7120e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
7130e5c9e7fSSrinivas Kandagatla {
7140e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component = mbhc->component;
715cc4d891fSSrinivas Kandagatla int ret;
716cc4d891fSSrinivas Kandagatla
717e18f6bcfSKrzysztof Kozlowski ret = pm_runtime_get_sync(component->dev);
718cc4d891fSSrinivas Kandagatla if (ret < 0 && ret != -EACCES) {
719cc4d891fSSrinivas Kandagatla dev_err_ratelimited(component->dev,
720e18f6bcfSKrzysztof Kozlowski "pm_runtime_get_sync failed in %s, ret %d\n",
721cc4d891fSSrinivas Kandagatla __func__, ret);
722e18f6bcfSKrzysztof Kozlowski pm_runtime_put_noidle(component->dev);
723cc4d891fSSrinivas Kandagatla return ret;
724cc4d891fSSrinivas Kandagatla }
7250e5c9e7fSSrinivas Kandagatla
7260e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
7270e5c9e7fSSrinivas Kandagatla
7280e5c9e7fSSrinivas Kandagatla /* enable HS detection */
7290e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->hph_pull_up_control_v2)
7300e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->hph_pull_up_control_v2(component,
7310e5c9e7fSSrinivas Kandagatla HS_PULLUP_I_DEFAULT);
7320e5c9e7fSSrinivas Kandagatla else if (mbhc->mbhc_cb->hph_pull_up_control)
7330e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
7340e5c9e7fSSrinivas Kandagatla else
7350e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
7360e5c9e7fSSrinivas Kandagatla
7370e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
7380e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
7390e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
7400e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
7410e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
7420e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
7430e5c9e7fSSrinivas Kandagatla
7440e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
7450e5c9e7fSSrinivas Kandagatla
7460e5c9e7fSSrinivas Kandagatla /* Insertion debounce set to 96ms */
7470e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
7480e5c9e7fSSrinivas Kandagatla
7490e5c9e7fSSrinivas Kandagatla /* Button Debounce set to 16ms */
7500e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
7510e5c9e7fSSrinivas Kandagatla
7520e5c9e7fSSrinivas Kandagatla /* enable bias */
7530e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_bias(component, true);
7540e5c9e7fSSrinivas Kandagatla /* enable MBHC clock */
7550e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->clk_setup)
7560e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->clk_setup(component, true);
7570e5c9e7fSSrinivas Kandagatla
7580e5c9e7fSSrinivas Kandagatla /* program HS_VREF value */
7590e5c9e7fSSrinivas Kandagatla wcd_program_hs_vref(mbhc);
7600e5c9e7fSSrinivas Kandagatla
7610e5c9e7fSSrinivas Kandagatla wcd_program_btn_threshold(mbhc, false);
7620e5c9e7fSSrinivas Kandagatla
7630e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
7640e5c9e7fSSrinivas Kandagatla
765cc4d891fSSrinivas Kandagatla pm_runtime_mark_last_busy(component->dev);
766cc4d891fSSrinivas Kandagatla pm_runtime_put_autosuspend(component->dev);
767cc4d891fSSrinivas Kandagatla
7680e5c9e7fSSrinivas Kandagatla return 0;
7690e5c9e7fSSrinivas Kandagatla }
7700e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_micbias(struct wcd_mbhc * mbhc)7710e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
7720e5c9e7fSSrinivas Kandagatla {
7730e5c9e7fSSrinivas Kandagatla int micbias = 0;
7740e5c9e7fSSrinivas Kandagatla
7750e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->get_micbias_val) {
7760e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
7770e5c9e7fSSrinivas Kandagatla } else {
7780e5c9e7fSSrinivas Kandagatla u8 vout_ctl = 0;
7790e5c9e7fSSrinivas Kandagatla /* Read MBHC Micbias (Mic Bias2) voltage */
7800e5c9e7fSSrinivas Kandagatla vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
7810e5c9e7fSSrinivas Kandagatla /* Formula for getting micbias from vout
7820e5c9e7fSSrinivas Kandagatla * micbias = 1.0V + VOUT_CTL * 50mV
7830e5c9e7fSSrinivas Kandagatla */
7840e5c9e7fSSrinivas Kandagatla micbias = 1000 + (vout_ctl * 50);
7850e5c9e7fSSrinivas Kandagatla }
7860e5c9e7fSSrinivas Kandagatla return micbias;
7870e5c9e7fSSrinivas Kandagatla }
7880e5c9e7fSSrinivas Kandagatla
wcd_get_voltage_from_adc(u8 val,int micbias)7890e5c9e7fSSrinivas Kandagatla static int wcd_get_voltage_from_adc(u8 val, int micbias)
7900e5c9e7fSSrinivas Kandagatla {
7910e5c9e7fSSrinivas Kandagatla /* Formula for calculating voltage from ADC
7920e5c9e7fSSrinivas Kandagatla * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
7930e5c9e7fSSrinivas Kandagatla */
7940e5c9e7fSSrinivas Kandagatla return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
7950e5c9e7fSSrinivas Kandagatla }
7960e5c9e7fSSrinivas Kandagatla
wcd_measure_adc_continuous(struct wcd_mbhc * mbhc)7970e5c9e7fSSrinivas Kandagatla static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
7980e5c9e7fSSrinivas Kandagatla {
7990e5c9e7fSSrinivas Kandagatla u8 adc_result;
8000e5c9e7fSSrinivas Kandagatla int output_mv;
8010e5c9e7fSSrinivas Kandagatla int retry = 3;
8020e5c9e7fSSrinivas Kandagatla u8 adc_en;
8030e5c9e7fSSrinivas Kandagatla
8040e5c9e7fSSrinivas Kandagatla /* Pre-requisites for ADC continuous measurement */
8050e5c9e7fSSrinivas Kandagatla /* Read legacy electircal detection and disable */
8060e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
8070e5c9e7fSSrinivas Kandagatla /* Set ADC to continuous measurement */
8080e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
8090e5c9e7fSSrinivas Kandagatla /* Read ADC Enable bit to restore after adc measurement */
8100e5c9e7fSSrinivas Kandagatla adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
8110e5c9e7fSSrinivas Kandagatla /* Disable ADC_ENABLE bit */
8120e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
8130e5c9e7fSSrinivas Kandagatla /* Disable MBHC FSM */
8140e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
8150e5c9e7fSSrinivas Kandagatla /* Set the MUX selection to IN2P */
8160e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
8170e5c9e7fSSrinivas Kandagatla /* Enable MBHC FSM */
8180e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
8190e5c9e7fSSrinivas Kandagatla /* Enable ADC_ENABLE bit */
8200e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
8210e5c9e7fSSrinivas Kandagatla
8220e5c9e7fSSrinivas Kandagatla while (retry--) {
8230e5c9e7fSSrinivas Kandagatla /* wait for 3 msec before reading ADC result */
8240e5c9e7fSSrinivas Kandagatla usleep_range(3000, 3100);
8250e5c9e7fSSrinivas Kandagatla adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
8260e5c9e7fSSrinivas Kandagatla }
8270e5c9e7fSSrinivas Kandagatla
8280e5c9e7fSSrinivas Kandagatla /* Restore ADC Enable */
8290e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
8300e5c9e7fSSrinivas Kandagatla /* Get voltage from ADC result */
8310e5c9e7fSSrinivas Kandagatla output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
8320e5c9e7fSSrinivas Kandagatla
8330e5c9e7fSSrinivas Kandagatla return output_mv;
8340e5c9e7fSSrinivas Kandagatla }
8350e5c9e7fSSrinivas Kandagatla
wcd_measure_adc_once(struct wcd_mbhc * mbhc,int mux_ctl)8360e5c9e7fSSrinivas Kandagatla static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
8370e5c9e7fSSrinivas Kandagatla {
8380e5c9e7fSSrinivas Kandagatla struct device *dev = mbhc->dev;
8390e5c9e7fSSrinivas Kandagatla u8 adc_timeout = 0;
8400e5c9e7fSSrinivas Kandagatla u8 adc_complete = 0;
8410e5c9e7fSSrinivas Kandagatla u8 adc_result;
8420e5c9e7fSSrinivas Kandagatla int retry = 6;
8430e5c9e7fSSrinivas Kandagatla int ret;
8440e5c9e7fSSrinivas Kandagatla int output_mv = 0;
8450e5c9e7fSSrinivas Kandagatla u8 adc_en;
8460e5c9e7fSSrinivas Kandagatla
8470e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
8480e5c9e7fSSrinivas Kandagatla /* Read ADC Enable bit to restore after adc measurement */
8490e5c9e7fSSrinivas Kandagatla adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
8500e5c9e7fSSrinivas Kandagatla /* Trigger ADC one time measurement */
8510e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
8520e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
8530e5c9e7fSSrinivas Kandagatla /* Set the appropriate MUX selection */
8540e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
8550e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
8560e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
8570e5c9e7fSSrinivas Kandagatla
8580e5c9e7fSSrinivas Kandagatla while (retry--) {
8590e5c9e7fSSrinivas Kandagatla /* wait for 600usec to get adc results */
8600e5c9e7fSSrinivas Kandagatla usleep_range(600, 610);
8610e5c9e7fSSrinivas Kandagatla
8620e5c9e7fSSrinivas Kandagatla /* check for ADC Timeout */
8630e5c9e7fSSrinivas Kandagatla adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
8640e5c9e7fSSrinivas Kandagatla if (adc_timeout)
8650e5c9e7fSSrinivas Kandagatla continue;
8660e5c9e7fSSrinivas Kandagatla
8670e5c9e7fSSrinivas Kandagatla /* Read ADC complete bit */
8680e5c9e7fSSrinivas Kandagatla adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
8690e5c9e7fSSrinivas Kandagatla if (!adc_complete)
8700e5c9e7fSSrinivas Kandagatla continue;
8710e5c9e7fSSrinivas Kandagatla
8720e5c9e7fSSrinivas Kandagatla /* Read ADC result */
8730e5c9e7fSSrinivas Kandagatla adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
8740e5c9e7fSSrinivas Kandagatla
8750e5c9e7fSSrinivas Kandagatla /* Get voltage from ADC result */
8760e5c9e7fSSrinivas Kandagatla output_mv = wcd_get_voltage_from_adc(adc_result,
8770e5c9e7fSSrinivas Kandagatla wcd_mbhc_get_micbias(mbhc));
8780e5c9e7fSSrinivas Kandagatla break;
8790e5c9e7fSSrinivas Kandagatla }
8800e5c9e7fSSrinivas Kandagatla
8810e5c9e7fSSrinivas Kandagatla /* Restore ADC Enable */
8820e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
8830e5c9e7fSSrinivas Kandagatla
8840e5c9e7fSSrinivas Kandagatla if (retry <= 0) {
8850e5c9e7fSSrinivas Kandagatla dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
8860e5c9e7fSSrinivas Kandagatla __func__, adc_complete, adc_timeout);
8870e5c9e7fSSrinivas Kandagatla ret = -EINVAL;
8880e5c9e7fSSrinivas Kandagatla } else {
8890e5c9e7fSSrinivas Kandagatla ret = output_mv;
8900e5c9e7fSSrinivas Kandagatla }
8910e5c9e7fSSrinivas Kandagatla
8920e5c9e7fSSrinivas Kandagatla return ret;
8930e5c9e7fSSrinivas Kandagatla }
8940e5c9e7fSSrinivas Kandagatla
8950e5c9e7fSSrinivas Kandagatla /* To determine if cross connection occurred */
wcd_check_cross_conn(struct wcd_mbhc * mbhc)8960e5c9e7fSSrinivas Kandagatla static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
8970e5c9e7fSSrinivas Kandagatla {
8980e5c9e7fSSrinivas Kandagatla u8 adc_mode, elect_ctl, adc_en, fsm_en;
8990e5c9e7fSSrinivas Kandagatla int hphl_adc_res, hphr_adc_res;
9000e5c9e7fSSrinivas Kandagatla bool is_cross_conn = false;
9010e5c9e7fSSrinivas Kandagatla
9020e5c9e7fSSrinivas Kandagatla /* If PA is enabled, dont check for cross-connection */
9030e5c9e7fSSrinivas Kandagatla if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
9040e5c9e7fSSrinivas Kandagatla return -EINVAL;
9050e5c9e7fSSrinivas Kandagatla
9060e5c9e7fSSrinivas Kandagatla /* Read legacy electircal detection and disable */
9070e5c9e7fSSrinivas Kandagatla elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
9080e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
9090e5c9e7fSSrinivas Kandagatla
9100e5c9e7fSSrinivas Kandagatla /* Read and set ADC to single measurement */
9110e5c9e7fSSrinivas Kandagatla adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
9120e5c9e7fSSrinivas Kandagatla /* Read ADC Enable bit to restore after adc measurement */
9130e5c9e7fSSrinivas Kandagatla adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
9140e5c9e7fSSrinivas Kandagatla /* Read FSM status */
9150e5c9e7fSSrinivas Kandagatla fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
9160e5c9e7fSSrinivas Kandagatla
9170e5c9e7fSSrinivas Kandagatla /* Get adc result for HPH L */
9180e5c9e7fSSrinivas Kandagatla hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
9190e5c9e7fSSrinivas Kandagatla if (hphl_adc_res < 0)
9200e5c9e7fSSrinivas Kandagatla return hphl_adc_res;
9210e5c9e7fSSrinivas Kandagatla
9220e5c9e7fSSrinivas Kandagatla /* Get adc result for HPH R in mV */
9230e5c9e7fSSrinivas Kandagatla hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
9240e5c9e7fSSrinivas Kandagatla if (hphr_adc_res < 0)
9250e5c9e7fSSrinivas Kandagatla return hphr_adc_res;
9260e5c9e7fSSrinivas Kandagatla
9270e5c9e7fSSrinivas Kandagatla if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
9280e5c9e7fSSrinivas Kandagatla hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
9290e5c9e7fSSrinivas Kandagatla is_cross_conn = true;
9300e5c9e7fSSrinivas Kandagatla
9310e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
9320e5c9e7fSSrinivas Kandagatla /* Set the MUX selection to Auto */
9330e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
9340e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
9350e5c9e7fSSrinivas Kandagatla /* Restore ADC Enable */
9360e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
9370e5c9e7fSSrinivas Kandagatla /* Restore ADC mode */
9380e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
9390e5c9e7fSSrinivas Kandagatla /* Restore FSM state */
9400e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
9410e5c9e7fSSrinivas Kandagatla /* Restore electrical detection */
9420e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
9430e5c9e7fSSrinivas Kandagatla
9440e5c9e7fSSrinivas Kandagatla return is_cross_conn;
9450e5c9e7fSSrinivas Kandagatla }
9460e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc * mbhc)9470e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
9480e5c9e7fSSrinivas Kandagatla {
9490e5c9e7fSSrinivas Kandagatla int hs_threshold, micbias_mv;
9500e5c9e7fSSrinivas Kandagatla
9510e5c9e7fSSrinivas Kandagatla micbias_mv = wcd_mbhc_get_micbias(mbhc);
9520e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->hs_thr) {
9530e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->micb_mv == micbias_mv)
9540e5c9e7fSSrinivas Kandagatla hs_threshold = mbhc->cfg->hs_thr;
9550e5c9e7fSSrinivas Kandagatla else
9560e5c9e7fSSrinivas Kandagatla hs_threshold = (mbhc->cfg->hs_thr *
9570e5c9e7fSSrinivas Kandagatla micbias_mv) / mbhc->cfg->micb_mv;
9580e5c9e7fSSrinivas Kandagatla } else {
9590e5c9e7fSSrinivas Kandagatla hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
9600e5c9e7fSSrinivas Kandagatla micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
9610e5c9e7fSSrinivas Kandagatla }
9620e5c9e7fSSrinivas Kandagatla return hs_threshold;
9630e5c9e7fSSrinivas Kandagatla }
9640e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc * mbhc)9650e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
9660e5c9e7fSSrinivas Kandagatla {
9670e5c9e7fSSrinivas Kandagatla int hph_threshold, micbias_mv;
9680e5c9e7fSSrinivas Kandagatla
9690e5c9e7fSSrinivas Kandagatla micbias_mv = wcd_mbhc_get_micbias(mbhc);
9700e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->hph_thr) {
9710e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->micb_mv == micbias_mv)
9720e5c9e7fSSrinivas Kandagatla hph_threshold = mbhc->cfg->hph_thr;
9730e5c9e7fSSrinivas Kandagatla else
9740e5c9e7fSSrinivas Kandagatla hph_threshold = (mbhc->cfg->hph_thr *
9750e5c9e7fSSrinivas Kandagatla micbias_mv) / mbhc->cfg->micb_mv;
9760e5c9e7fSSrinivas Kandagatla } else {
9770e5c9e7fSSrinivas Kandagatla hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
9780e5c9e7fSSrinivas Kandagatla micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
9790e5c9e7fSSrinivas Kandagatla }
9800e5c9e7fSSrinivas Kandagatla return hph_threshold;
9810e5c9e7fSSrinivas Kandagatla }
9820e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc * mbhc,enum wcd_mbhc_plug_type plug_type)9830e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
9840e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_plug_type plug_type)
9850e5c9e7fSSrinivas Kandagatla {
9860e5c9e7fSSrinivas Kandagatla bool micbias2 = false;
9870e5c9e7fSSrinivas Kandagatla
9880e5c9e7fSSrinivas Kandagatla switch (plug_type) {
9890e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADPHONE:
9900e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
9910e5c9e7fSSrinivas Kandagatla break;
9920e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADSET:
9930e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->micbias_enable_status)
9940e5c9e7fSSrinivas Kandagatla micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
9950e5c9e7fSSrinivas Kandagatla MIC_BIAS_2);
9960e5c9e7fSSrinivas Kandagatla
9970e5c9e7fSSrinivas Kandagatla if (!mbhc->is_hs_recording && !micbias2)
9980e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
9990e5c9e7fSSrinivas Kandagatla break;
10000e5c9e7fSSrinivas Kandagatla default:
10010e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
10020e5c9e7fSSrinivas Kandagatla break;
10030e5c9e7fSSrinivas Kandagatla
100403c0cbd9SYang Li }
10050e5c9e7fSSrinivas Kandagatla }
10060e5c9e7fSSrinivas Kandagatla
wcd_mbhc_bcs_enable(struct wcd_mbhc * mbhc,int plug_type,bool enable)10070e5c9e7fSSrinivas Kandagatla static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
10080e5c9e7fSSrinivas Kandagatla {
10090e5c9e7fSSrinivas Kandagatla switch (plug_type) {
10100e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADSET:
10110e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADPHONE:
10120e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->bcs_enable)
10130e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
10140e5c9e7fSSrinivas Kandagatla break;
10150e5c9e7fSSrinivas Kandagatla default:
10160e5c9e7fSSrinivas Kandagatla break;
10170e5c9e7fSSrinivas Kandagatla }
10180e5c9e7fSSrinivas Kandagatla }
10190e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_plug_from_adc(struct wcd_mbhc * mbhc,int adc_result)10200e5c9e7fSSrinivas Kandagatla static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
10210e5c9e7fSSrinivas Kandagatla
10220e5c9e7fSSrinivas Kandagatla {
10230e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_plug_type plug_type;
10240e5c9e7fSSrinivas Kandagatla u32 hph_thr, hs_thr;
10250e5c9e7fSSrinivas Kandagatla
10260e5c9e7fSSrinivas Kandagatla hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
10270e5c9e7fSSrinivas Kandagatla hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
10280e5c9e7fSSrinivas Kandagatla
10290e5c9e7fSSrinivas Kandagatla if (adc_result < hph_thr)
10300e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_HEADPHONE;
10310e5c9e7fSSrinivas Kandagatla else if (adc_result > hs_thr)
10320e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
10330e5c9e7fSSrinivas Kandagatla else
10340e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_HEADSET;
10350e5c9e7fSSrinivas Kandagatla
10360e5c9e7fSSrinivas Kandagatla return plug_type;
10370e5c9e7fSSrinivas Kandagatla }
10380e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc * mbhc)10393c8a3ad4SSrinivasa Rao Mandadapu static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc)
10403c8a3ad4SSrinivasa Rao Mandadapu {
10413c8a3ad4SSrinivasa Rao Mandadapu int hs_threshold, micbias_mv;
10423c8a3ad4SSrinivasa Rao Mandadapu
10433c8a3ad4SSrinivasa Rao Mandadapu micbias_mv = wcd_mbhc_get_micbias(mbhc);
10443c8a3ad4SSrinivasa Rao Mandadapu if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) {
10453c8a3ad4SSrinivasa Rao Mandadapu if (mbhc->cfg->micb_mv == micbias_mv)
10463c8a3ad4SSrinivasa Rao Mandadapu hs_threshold = mbhc->cfg->hs_thr;
10473c8a3ad4SSrinivasa Rao Mandadapu else
10483c8a3ad4SSrinivasa Rao Mandadapu hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv;
10493c8a3ad4SSrinivasa Rao Mandadapu } else {
10503c8a3ad4SSrinivasa Rao Mandadapu hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) /
10513c8a3ad4SSrinivasa Rao Mandadapu WCD_MBHC_ADC_MICBIAS_MV);
10523c8a3ad4SSrinivasa Rao Mandadapu }
10533c8a3ad4SSrinivasa Rao Mandadapu return hs_threshold;
10543c8a3ad4SSrinivasa Rao Mandadapu }
10553c8a3ad4SSrinivasa Rao Mandadapu
wcd_mbhc_check_for_spl_headset(struct wcd_mbhc * mbhc)10563c8a3ad4SSrinivasa Rao Mandadapu static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc)
10573c8a3ad4SSrinivasa Rao Mandadapu {
10583c8a3ad4SSrinivasa Rao Mandadapu bool is_spl_hs = false;
10593c8a3ad4SSrinivasa Rao Mandadapu int output_mv, hs_threshold, hph_threshold;
10603c8a3ad4SSrinivasa Rao Mandadapu
10613c8a3ad4SSrinivasa Rao Mandadapu if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
10623c8a3ad4SSrinivasa Rao Mandadapu return false;
10633c8a3ad4SSrinivasa Rao Mandadapu
10643c8a3ad4SSrinivasa Rao Mandadapu /* Bump up MIC_BIAS2 to 2.7V */
10653c8a3ad4SSrinivasa Rao Mandadapu mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true);
10663c8a3ad4SSrinivasa Rao Mandadapu usleep_range(10000, 10100);
10673c8a3ad4SSrinivasa Rao Mandadapu
10683c8a3ad4SSrinivasa Rao Mandadapu output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
10693c8a3ad4SSrinivasa Rao Mandadapu hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc);
10703c8a3ad4SSrinivasa Rao Mandadapu hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc);
10713c8a3ad4SSrinivasa Rao Mandadapu
1072b38892b5SSrinivasa Rao Mandadapu if (!(output_mv > hs_threshold || output_mv < hph_threshold))
10733c8a3ad4SSrinivasa Rao Mandadapu is_spl_hs = true;
10743c8a3ad4SSrinivasa Rao Mandadapu
10753c8a3ad4SSrinivasa Rao Mandadapu /* Back MIC_BIAS2 to 1.8v if the type is not special headset */
10763c8a3ad4SSrinivasa Rao Mandadapu if (!is_spl_hs) {
10773c8a3ad4SSrinivasa Rao Mandadapu mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false);
10783c8a3ad4SSrinivasa Rao Mandadapu /* Add 10ms delay for micbias to settle */
10793c8a3ad4SSrinivasa Rao Mandadapu usleep_range(10000, 10100);
10803c8a3ad4SSrinivasa Rao Mandadapu }
10813c8a3ad4SSrinivasa Rao Mandadapu
10823c8a3ad4SSrinivasa Rao Mandadapu return is_spl_hs;
10833c8a3ad4SSrinivasa Rao Mandadapu }
10843c8a3ad4SSrinivasa Rao Mandadapu
wcd_correct_swch_plug(struct work_struct * work)10850e5c9e7fSSrinivas Kandagatla static void wcd_correct_swch_plug(struct work_struct *work)
10860e5c9e7fSSrinivas Kandagatla {
10870e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc;
10880e5c9e7fSSrinivas Kandagatla struct snd_soc_component *component;
10890e5c9e7fSSrinivas Kandagatla enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
10900e5c9e7fSSrinivas Kandagatla unsigned long timeout;
10910e5c9e7fSSrinivas Kandagatla int pt_gnd_mic_swap_cnt = 0;
10923c8a3ad4SSrinivasa Rao Mandadapu int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
10933c8a3ad4SSrinivasa Rao Mandadapu bool is_spl_hs = false;
10940e5c9e7fSSrinivas Kandagatla bool is_pa_on;
1095cc4d891fSSrinivas Kandagatla int ret;
10960e5c9e7fSSrinivas Kandagatla
10970e5c9e7fSSrinivas Kandagatla mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
10980e5c9e7fSSrinivas Kandagatla component = mbhc->component;
10990e5c9e7fSSrinivas Kandagatla
1100e18f6bcfSKrzysztof Kozlowski ret = pm_runtime_get_sync(component->dev);
1101cc4d891fSSrinivas Kandagatla if (ret < 0 && ret != -EACCES) {
1102cc4d891fSSrinivas Kandagatla dev_err_ratelimited(component->dev,
1103e18f6bcfSKrzysztof Kozlowski "pm_runtime_get_sync failed in %s, ret %d\n",
1104cc4d891fSSrinivas Kandagatla __func__, ret);
1105e18f6bcfSKrzysztof Kozlowski pm_runtime_put_noidle(component->dev);
1106cc4d891fSSrinivas Kandagatla return;
1107cc4d891fSSrinivas Kandagatla }
11083c8a3ad4SSrinivasa Rao Mandadapu micbias_mv = wcd_mbhc_get_micbias(mbhc);
11090e5c9e7fSSrinivas Kandagatla hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
11100e5c9e7fSSrinivas Kandagatla
11110e5c9e7fSSrinivas Kandagatla /* Mask ADC COMPLETE interrupt */
11120e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
11130e5c9e7fSSrinivas Kandagatla
11140e5c9e7fSSrinivas Kandagatla /* Check for cross connection */
11150e5c9e7fSSrinivas Kandagatla do {
11160e5c9e7fSSrinivas Kandagatla cross_conn = wcd_check_cross_conn(mbhc);
11170e5c9e7fSSrinivas Kandagatla try++;
11180e5c9e7fSSrinivas Kandagatla } while (try < GND_MIC_SWAP_THRESHOLD);
11190e5c9e7fSSrinivas Kandagatla
11200e5c9e7fSSrinivas Kandagatla if (cross_conn > 0) {
11210e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
11220e5c9e7fSSrinivas Kandagatla dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
11230e5c9e7fSSrinivas Kandagatla plug_type);
11240e5c9e7fSSrinivas Kandagatla goto correct_plug_type;
11250e5c9e7fSSrinivas Kandagatla }
11260e5c9e7fSSrinivas Kandagatla
11270e5c9e7fSSrinivas Kandagatla /* Find plug type */
11280e5c9e7fSSrinivas Kandagatla output_mv = wcd_measure_adc_continuous(mbhc);
11290e5c9e7fSSrinivas Kandagatla plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11300e5c9e7fSSrinivas Kandagatla
11310e5c9e7fSSrinivas Kandagatla /*
11320e5c9e7fSSrinivas Kandagatla * Report plug type if it is either headset or headphone
11330e5c9e7fSSrinivas Kandagatla * else start the 3 sec loop
11340e5c9e7fSSrinivas Kandagatla */
11350e5c9e7fSSrinivas Kandagatla switch (plug_type) {
11360e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADPHONE:
11370e5c9e7fSSrinivas Kandagatla wcd_mbhc_find_plug_and_report(mbhc, plug_type);
11380e5c9e7fSSrinivas Kandagatla break;
11390e5c9e7fSSrinivas Kandagatla case MBHC_PLUG_TYPE_HEADSET:
11400e5c9e7fSSrinivas Kandagatla wcd_mbhc_find_plug_and_report(mbhc, plug_type);
11410e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
11420e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
11430e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
11440e5c9e7fSSrinivas Kandagatla break;
11450e5c9e7fSSrinivas Kandagatla default:
11460e5c9e7fSSrinivas Kandagatla break;
11470e5c9e7fSSrinivas Kandagatla }
11480e5c9e7fSSrinivas Kandagatla
11490e5c9e7fSSrinivas Kandagatla correct_plug_type:
11500e5c9e7fSSrinivas Kandagatla
11510e5c9e7fSSrinivas Kandagatla /* Disable BCS slow insertion detection */
11520e5c9e7fSSrinivas Kandagatla wcd_mbhc_bcs_enable(mbhc, plug_type, false);
11530e5c9e7fSSrinivas Kandagatla
11540e5c9e7fSSrinivas Kandagatla timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
11550e5c9e7fSSrinivas Kandagatla
11560e5c9e7fSSrinivas Kandagatla while (!time_after(jiffies, timeout)) {
11570e5c9e7fSSrinivas Kandagatla if (mbhc->hs_detect_work_stop) {
11580e5c9e7fSSrinivas Kandagatla wcd_micbias_disable(mbhc);
11590e5c9e7fSSrinivas Kandagatla goto exit;
11600e5c9e7fSSrinivas Kandagatla }
11610e5c9e7fSSrinivas Kandagatla
11620e5c9e7fSSrinivas Kandagatla msleep(180);
11630e5c9e7fSSrinivas Kandagatla /*
11640e5c9e7fSSrinivas Kandagatla * Use ADC single mode to minimize the chance of missing out
11650e5c9e7fSSrinivas Kandagatla * btn press/release for HEADSET type during correct work.
11660e5c9e7fSSrinivas Kandagatla */
11670e5c9e7fSSrinivas Kandagatla output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
11680e5c9e7fSSrinivas Kandagatla plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11690e5c9e7fSSrinivas Kandagatla is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
11700e5c9e7fSSrinivas Kandagatla
1171b38892b5SSrinivasa Rao Mandadapu if (output_mv > hs_threshold && !is_spl_hs) {
11723c8a3ad4SSrinivasa Rao Mandadapu is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc);
11733c8a3ad4SSrinivasa Rao Mandadapu output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
11743c8a3ad4SSrinivasa Rao Mandadapu
11753c8a3ad4SSrinivasa Rao Mandadapu if (is_spl_hs) {
1176b38892b5SSrinivasa Rao Mandadapu hs_threshold *= wcd_mbhc_get_micbias(mbhc);
1177b38892b5SSrinivasa Rao Mandadapu hs_threshold /= micbias_mv;
11783c8a3ad4SSrinivasa Rao Mandadapu }
11793c8a3ad4SSrinivasa Rao Mandadapu }
11803c8a3ad4SSrinivasa Rao Mandadapu
11810e5c9e7fSSrinivas Kandagatla if ((output_mv <= hs_threshold) && !is_pa_on) {
11820e5c9e7fSSrinivas Kandagatla /* Check for cross connection*/
11830e5c9e7fSSrinivas Kandagatla cross_conn = wcd_check_cross_conn(mbhc);
11840e5c9e7fSSrinivas Kandagatla if (cross_conn > 0) { /* cross-connection */
11850e5c9e7fSSrinivas Kandagatla pt_gnd_mic_swap_cnt++;
11860e5c9e7fSSrinivas Kandagatla if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
11870e5c9e7fSSrinivas Kandagatla continue;
11880e5c9e7fSSrinivas Kandagatla else
11890e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
11900e5c9e7fSSrinivas Kandagatla } else if (!cross_conn) { /* no cross connection */
11910e5c9e7fSSrinivas Kandagatla pt_gnd_mic_swap_cnt = 0;
11920e5c9e7fSSrinivas Kandagatla plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
11930e5c9e7fSSrinivas Kandagatla continue;
119443265ceeSPierre-Louis Bossart } else /* Error if (cross_conn < 0) */
11950e5c9e7fSSrinivas Kandagatla continue;
11960e5c9e7fSSrinivas Kandagatla
11970e5c9e7fSSrinivas Kandagatla if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
11980e5c9e7fSSrinivas Kandagatla /* US_EU gpio present, flip switch */
11990e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->swap_gnd_mic) {
12000e5c9e7fSSrinivas Kandagatla if (mbhc->cfg->swap_gnd_mic(component, true))
12010e5c9e7fSSrinivas Kandagatla continue;
12020e5c9e7fSSrinivas Kandagatla }
12030e5c9e7fSSrinivas Kandagatla }
12040e5c9e7fSSrinivas Kandagatla }
12050e5c9e7fSSrinivas Kandagatla
12063c8a3ad4SSrinivasa Rao Mandadapu /* cable is extension cable */
1207b38892b5SSrinivasa Rao Mandadapu if (output_mv > hs_threshold || mbhc->force_linein)
12080e5c9e7fSSrinivas Kandagatla plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
12090e5c9e7fSSrinivas Kandagatla }
12100e5c9e7fSSrinivas Kandagatla
12110e5c9e7fSSrinivas Kandagatla wcd_mbhc_bcs_enable(mbhc, plug_type, true);
12120e5c9e7fSSrinivas Kandagatla
12133c8a3ad4SSrinivasa Rao Mandadapu if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
12143c8a3ad4SSrinivasa Rao Mandadapu if (is_spl_hs)
12153c8a3ad4SSrinivasa Rao Mandadapu plug_type = MBHC_PLUG_TYPE_HEADSET;
12163c8a3ad4SSrinivasa Rao Mandadapu else
12170e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
12183c8a3ad4SSrinivasa Rao Mandadapu }
12190e5c9e7fSSrinivas Kandagatla
12200e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
12210e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
12220e5c9e7fSSrinivas Kandagatla wcd_mbhc_find_plug_and_report(mbhc, plug_type);
12230e5c9e7fSSrinivas Kandagatla
12240e5c9e7fSSrinivas Kandagatla /*
12250e5c9e7fSSrinivas Kandagatla * Set DETECTION_DONE bit for HEADSET
12260e5c9e7fSSrinivas Kandagatla * so that btn press/release interrupt can be generated.
12270e5c9e7fSSrinivas Kandagatla * For other plug type, clear the bit.
12280e5c9e7fSSrinivas Kandagatla */
12290e5c9e7fSSrinivas Kandagatla if (plug_type == MBHC_PLUG_TYPE_HEADSET)
12300e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
12310e5c9e7fSSrinivas Kandagatla else
12320e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
12330e5c9e7fSSrinivas Kandagatla
12340e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control)
12350e5c9e7fSSrinivas Kandagatla wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
12360e5c9e7fSSrinivas Kandagatla
12370e5c9e7fSSrinivas Kandagatla exit:
12380e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/)
12390e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
12400e5c9e7fSSrinivas Kandagatla
12410e5c9e7fSSrinivas Kandagatla /*
12420e5c9e7fSSrinivas Kandagatla * If plug type is corrected from special headset to headphone,
12430e5c9e7fSSrinivas Kandagatla * clear the micbias enable flag, set micbias back to 1.8V and
12440e5c9e7fSSrinivas Kandagatla * disable micbias.
12450e5c9e7fSSrinivas Kandagatla */
12460e5c9e7fSSrinivas Kandagatla if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
12470e5c9e7fSSrinivas Kandagatla wcd_micbias_disable(mbhc);
12480e5c9e7fSSrinivas Kandagatla /*
12490e5c9e7fSSrinivas Kandagatla * Enable ADC COMPLETE interrupt for HEADPHONE.
12500e5c9e7fSSrinivas Kandagatla * Btn release may happen after the correct work, ADC COMPLETE
12510e5c9e7fSSrinivas Kandagatla * interrupt needs to be captured to correct plug type.
12520e5c9e7fSSrinivas Kandagatla */
12530e5c9e7fSSrinivas Kandagatla enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
12540e5c9e7fSSrinivas Kandagatla }
12550e5c9e7fSSrinivas Kandagatla
12560e5c9e7fSSrinivas Kandagatla if (mbhc->mbhc_cb->hph_pull_down_ctrl)
12570e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
1258cc4d891fSSrinivas Kandagatla
1259cc4d891fSSrinivas Kandagatla pm_runtime_mark_last_busy(component->dev);
1260cc4d891fSSrinivas Kandagatla pm_runtime_put_autosuspend(component->dev);
12610e5c9e7fSSrinivas Kandagatla }
12620e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_hs_rem_irq(int irq,void * data)12630e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
12640e5c9e7fSSrinivas Kandagatla {
12650e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = data;
12660e5c9e7fSSrinivas Kandagatla unsigned long timeout;
12670e5c9e7fSSrinivas Kandagatla int adc_threshold, output_mv, retry = 0;
12680e5c9e7fSSrinivas Kandagatla
12690e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
12700e5c9e7fSSrinivas Kandagatla timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
12710e5c9e7fSSrinivas Kandagatla adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
12720e5c9e7fSSrinivas Kandagatla
12730e5c9e7fSSrinivas Kandagatla do {
12740e5c9e7fSSrinivas Kandagatla retry++;
12750e5c9e7fSSrinivas Kandagatla /*
12760e5c9e7fSSrinivas Kandagatla * read output_mv every 10ms to look for
12770e5c9e7fSSrinivas Kandagatla * any change in IN2_P
12780e5c9e7fSSrinivas Kandagatla */
12790e5c9e7fSSrinivas Kandagatla usleep_range(10000, 10100);
12800e5c9e7fSSrinivas Kandagatla output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
12810e5c9e7fSSrinivas Kandagatla
12820e5c9e7fSSrinivas Kandagatla /* Check for fake removal */
12830e5c9e7fSSrinivas Kandagatla if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
12840e5c9e7fSSrinivas Kandagatla goto exit;
12850e5c9e7fSSrinivas Kandagatla } while (!time_after(jiffies, timeout));
12860e5c9e7fSSrinivas Kandagatla
12870e5c9e7fSSrinivas Kandagatla /*
12880e5c9e7fSSrinivas Kandagatla * ADC COMPLETE and ELEC_REM interrupts are both enabled for
12890e5c9e7fSSrinivas Kandagatla * HEADPHONE, need to reject the ADC COMPLETE interrupt which
12900e5c9e7fSSrinivas Kandagatla * follows ELEC_REM one when HEADPHONE is removed.
12910e5c9e7fSSrinivas Kandagatla */
12920e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
12930e5c9e7fSSrinivas Kandagatla mbhc->extn_cable_hph_rem = true;
12940e5c9e7fSSrinivas Kandagatla
12950e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
12960e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
12970e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
12980e5c9e7fSSrinivas Kandagatla wcd_mbhc_elec_hs_report_unplug(mbhc);
12990e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
13000e5c9e7fSSrinivas Kandagatla
13010e5c9e7fSSrinivas Kandagatla exit:
13020e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
13030e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
13040e5c9e7fSSrinivas Kandagatla }
13050e5c9e7fSSrinivas Kandagatla
wcd_mbhc_adc_hs_ins_irq(int irq,void * data)13060e5c9e7fSSrinivas Kandagatla static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
13070e5c9e7fSSrinivas Kandagatla {
13080e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc = data;
1309cac24a36SPierre-Louis Bossart u8 clamp_state;
13100e5c9e7fSSrinivas Kandagatla u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
13110e5c9e7fSSrinivas Kandagatla
13120e5c9e7fSSrinivas Kandagatla /*
13130e5c9e7fSSrinivas Kandagatla * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
13140e5c9e7fSSrinivas Kandagatla * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
13150e5c9e7fSSrinivas Kandagatla * when HEADPHONE is removed.
13160e5c9e7fSSrinivas Kandagatla */
13170e5c9e7fSSrinivas Kandagatla if (mbhc->extn_cable_hph_rem == true) {
13180e5c9e7fSSrinivas Kandagatla mbhc->extn_cable_hph_rem = false;
13190e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
13200e5c9e7fSSrinivas Kandagatla }
13210e5c9e7fSSrinivas Kandagatla
13220e5c9e7fSSrinivas Kandagatla do {
13230e5c9e7fSSrinivas Kandagatla clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
13240e5c9e7fSSrinivas Kandagatla if (clamp_state)
13250e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
13260e5c9e7fSSrinivas Kandagatla /*
13270e5c9e7fSSrinivas Kandagatla * check clamp for 120ms but at 30ms chunks to leave
13280e5c9e7fSSrinivas Kandagatla * room for other interrupts to be processed
13290e5c9e7fSSrinivas Kandagatla */
13300e5c9e7fSSrinivas Kandagatla usleep_range(30000, 30100);
13310e5c9e7fSSrinivas Kandagatla } while (--clamp_retry);
13320e5c9e7fSSrinivas Kandagatla
13330e5c9e7fSSrinivas Kandagatla /*
13340e5c9e7fSSrinivas Kandagatla * If current plug is headphone then there is no chance to
13350e5c9e7fSSrinivas Kandagatla * get ADC complete interrupt, so connected cable should be
13360e5c9e7fSSrinivas Kandagatla * headset not headphone.
13370e5c9e7fSSrinivas Kandagatla */
13380e5c9e7fSSrinivas Kandagatla if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
13390e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
13400e5c9e7fSSrinivas Kandagatla wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
13410e5c9e7fSSrinivas Kandagatla wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
13420e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
13430e5c9e7fSSrinivas Kandagatla }
13440e5c9e7fSSrinivas Kandagatla
13450e5c9e7fSSrinivas Kandagatla return IRQ_HANDLED;
13460e5c9e7fSSrinivas Kandagatla }
13470e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_impedance(struct wcd_mbhc * mbhc,uint32_t * zl,uint32_t * zr)13480e5c9e7fSSrinivas Kandagatla int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr)
13490e5c9e7fSSrinivas Kandagatla {
13500e5c9e7fSSrinivas Kandagatla *zl = mbhc->zl;
13510e5c9e7fSSrinivas Kandagatla *zr = mbhc->zr;
13520e5c9e7fSSrinivas Kandagatla
13530e5c9e7fSSrinivas Kandagatla if (*zl && *zr)
13540e5c9e7fSSrinivas Kandagatla return 0;
13550e5c9e7fSSrinivas Kandagatla else
13560e5c9e7fSSrinivas Kandagatla return -EINVAL;
13570e5c9e7fSSrinivas Kandagatla }
13580e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_get_impedance);
13590e5c9e7fSSrinivas Kandagatla
wcd_mbhc_set_hph_type(struct wcd_mbhc * mbhc,int hph_type)13600e5c9e7fSSrinivas Kandagatla void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
13610e5c9e7fSSrinivas Kandagatla {
13620e5c9e7fSSrinivas Kandagatla mbhc->hph_type = hph_type;
13630e5c9e7fSSrinivas Kandagatla }
13640e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
13650e5c9e7fSSrinivas Kandagatla
wcd_mbhc_get_hph_type(struct wcd_mbhc * mbhc)13660e5c9e7fSSrinivas Kandagatla int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
13670e5c9e7fSSrinivas Kandagatla {
13680e5c9e7fSSrinivas Kandagatla return mbhc->hph_type;
13690e5c9e7fSSrinivas Kandagatla }
13700e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
13710e5c9e7fSSrinivas Kandagatla
wcd_mbhc_start(struct wcd_mbhc * mbhc,struct wcd_mbhc_config * cfg,struct snd_soc_jack * jack)13720e5c9e7fSSrinivas Kandagatla int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
13730e5c9e7fSSrinivas Kandagatla struct snd_soc_jack *jack)
13740e5c9e7fSSrinivas Kandagatla {
13750e5c9e7fSSrinivas Kandagatla if (!mbhc || !cfg || !jack)
13760e5c9e7fSSrinivas Kandagatla return -EINVAL;
13770e5c9e7fSSrinivas Kandagatla
13780e5c9e7fSSrinivas Kandagatla mbhc->cfg = cfg;
13790e5c9e7fSSrinivas Kandagatla mbhc->jack = jack;
13800e5c9e7fSSrinivas Kandagatla
13810e5c9e7fSSrinivas Kandagatla return wcd_mbhc_initialise(mbhc);
13820e5c9e7fSSrinivas Kandagatla }
13830e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_start);
13840e5c9e7fSSrinivas Kandagatla
wcd_mbhc_stop(struct wcd_mbhc * mbhc)13850e5c9e7fSSrinivas Kandagatla void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
13860e5c9e7fSSrinivas Kandagatla {
13870e5c9e7fSSrinivas Kandagatla mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
13880e5c9e7fSSrinivas Kandagatla mbhc->hph_status = 0;
13890e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
13900e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
13910e5c9e7fSSrinivas Kandagatla }
13920e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_stop);
13930e5c9e7fSSrinivas Kandagatla
wcd_dt_parse_mbhc_data(struct device * dev,struct wcd_mbhc_config * cfg)13940e5c9e7fSSrinivas Kandagatla int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
13950e5c9e7fSSrinivas Kandagatla {
13960e5c9e7fSSrinivas Kandagatla struct device_node *np = dev->of_node;
13970e5c9e7fSSrinivas Kandagatla int ret, i, microvolt;
13980e5c9e7fSSrinivas Kandagatla
13990e5c9e7fSSrinivas Kandagatla if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
14000e5c9e7fSSrinivas Kandagatla cfg->hphl_swh = false;
14010e5c9e7fSSrinivas Kandagatla else
14020e5c9e7fSSrinivas Kandagatla cfg->hphl_swh = true;
14030e5c9e7fSSrinivas Kandagatla
14040e5c9e7fSSrinivas Kandagatla if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
14050e5c9e7fSSrinivas Kandagatla cfg->gnd_swh = false;
14060e5c9e7fSSrinivas Kandagatla else
14070e5c9e7fSSrinivas Kandagatla cfg->gnd_swh = true;
14080e5c9e7fSSrinivas Kandagatla
14090e5c9e7fSSrinivas Kandagatla ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
14100e5c9e7fSSrinivas Kandagatla µvolt);
14110e5c9e7fSSrinivas Kandagatla if (ret)
14120e5c9e7fSSrinivas Kandagatla dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
14130e5c9e7fSSrinivas Kandagatla else
14140e5c9e7fSSrinivas Kandagatla cfg->hs_thr = microvolt/1000;
14150e5c9e7fSSrinivas Kandagatla
14160e5c9e7fSSrinivas Kandagatla ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
14170e5c9e7fSSrinivas Kandagatla µvolt);
14180e5c9e7fSSrinivas Kandagatla if (ret)
14190e5c9e7fSSrinivas Kandagatla dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n");
14200e5c9e7fSSrinivas Kandagatla else
14210e5c9e7fSSrinivas Kandagatla cfg->hph_thr = microvolt/1000;
14220e5c9e7fSSrinivas Kandagatla
14230e5c9e7fSSrinivas Kandagatla ret = of_property_read_u32_array(np,
14240e5c9e7fSSrinivas Kandagatla "qcom,mbhc-buttons-vthreshold-microvolt",
14250e5c9e7fSSrinivas Kandagatla &cfg->btn_high[0],
14260e5c9e7fSSrinivas Kandagatla WCD_MBHC_DEF_BUTTONS);
14270e5c9e7fSSrinivas Kandagatla if (ret)
14280e5c9e7fSSrinivas Kandagatla dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
14290e5c9e7fSSrinivas Kandagatla
14300e5c9e7fSSrinivas Kandagatla for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
14310e5c9e7fSSrinivas Kandagatla if (ret) /* default voltage */
14320e5c9e7fSSrinivas Kandagatla cfg->btn_high[i] = 500000;
14330e5c9e7fSSrinivas Kandagatla else
14340e5c9e7fSSrinivas Kandagatla /* Micro to Milli Volts */
14350e5c9e7fSSrinivas Kandagatla cfg->btn_high[i] = cfg->btn_high[i]/1000;
14360e5c9e7fSSrinivas Kandagatla }
14370e5c9e7fSSrinivas Kandagatla
14380e5c9e7fSSrinivas Kandagatla return 0;
14390e5c9e7fSSrinivas Kandagatla }
14400e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
14410e5c9e7fSSrinivas Kandagatla
wcd_mbhc_init(struct snd_soc_component * component,const struct wcd_mbhc_cb * mbhc_cb,const struct wcd_mbhc_intr * intr_ids,struct wcd_mbhc_field * fields,bool impedance_det_en)14420e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
14430e5c9e7fSSrinivas Kandagatla const struct wcd_mbhc_cb *mbhc_cb,
14440e5c9e7fSSrinivas Kandagatla const struct wcd_mbhc_intr *intr_ids,
14450e5c9e7fSSrinivas Kandagatla struct wcd_mbhc_field *fields,
14460e5c9e7fSSrinivas Kandagatla bool impedance_det_en)
14470e5c9e7fSSrinivas Kandagatla {
14480e5c9e7fSSrinivas Kandagatla struct device *dev = component->dev;
14490e5c9e7fSSrinivas Kandagatla struct wcd_mbhc *mbhc;
14500e5c9e7fSSrinivas Kandagatla int ret;
14510e5c9e7fSSrinivas Kandagatla
14520e5c9e7fSSrinivas Kandagatla if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
14530e5c9e7fSSrinivas Kandagatla dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
14540e5c9e7fSSrinivas Kandagatla return ERR_PTR(-EINVAL);
14550e5c9e7fSSrinivas Kandagatla }
14560e5c9e7fSSrinivas Kandagatla
1457*a5475829SJohan Hovold mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
14580e5c9e7fSSrinivas Kandagatla if (!mbhc)
14590e5c9e7fSSrinivas Kandagatla return ERR_PTR(-ENOMEM);
14600e5c9e7fSSrinivas Kandagatla
14610e5c9e7fSSrinivas Kandagatla mbhc->component = component;
14620e5c9e7fSSrinivas Kandagatla mbhc->dev = dev;
14630e5c9e7fSSrinivas Kandagatla mbhc->intr_ids = intr_ids;
14640e5c9e7fSSrinivas Kandagatla mbhc->mbhc_cb = mbhc_cb;
14650e5c9e7fSSrinivas Kandagatla mbhc->fields = fields;
14660e5c9e7fSSrinivas Kandagatla mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
14670e5c9e7fSSrinivas Kandagatla
14680e5c9e7fSSrinivas Kandagatla if (mbhc_cb->compute_impedance)
14690e5c9e7fSSrinivas Kandagatla mbhc->impedance_detect = impedance_det_en;
14700e5c9e7fSSrinivas Kandagatla
14710e5c9e7fSSrinivas Kandagatla INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
14720e5c9e7fSSrinivas Kandagatla
14730e5c9e7fSSrinivas Kandagatla mutex_init(&mbhc->lock);
14740e5c9e7fSSrinivas Kandagatla
14750e5c9e7fSSrinivas Kandagatla INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
14760e5c9e7fSSrinivas Kandagatla
1477*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
14780e5c9e7fSSrinivas Kandagatla wcd_mbhc_mech_plug_detect_irq,
14790e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14800e5c9e7fSSrinivas Kandagatla "mbhc sw intr", mbhc);
14810e5c9e7fSSrinivas Kandagatla if (ret)
1482*a5475829SJohan Hovold goto err_free_mbhc;
14830e5c9e7fSSrinivas Kandagatla
1484*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
14850e5c9e7fSSrinivas Kandagatla wcd_mbhc_btn_press_handler,
14860e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14870e5c9e7fSSrinivas Kandagatla "Button Press detect", mbhc);
14880e5c9e7fSSrinivas Kandagatla if (ret)
1489*a5475829SJohan Hovold goto err_free_sw_intr;
14900e5c9e7fSSrinivas Kandagatla
1491*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
14920e5c9e7fSSrinivas Kandagatla wcd_mbhc_btn_release_handler,
14930e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14940e5c9e7fSSrinivas Kandagatla "Button Release detect", mbhc);
14950e5c9e7fSSrinivas Kandagatla if (ret)
1496*a5475829SJohan Hovold goto err_free_btn_press_intr;
14970e5c9e7fSSrinivas Kandagatla
1498*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
14990e5c9e7fSSrinivas Kandagatla wcd_mbhc_adc_hs_ins_irq,
15000e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15010e5c9e7fSSrinivas Kandagatla "Elect Insert", mbhc);
15020e5c9e7fSSrinivas Kandagatla if (ret)
1503*a5475829SJohan Hovold goto err_free_btn_release_intr;
15040e5c9e7fSSrinivas Kandagatla
15050e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
15060e5c9e7fSSrinivas Kandagatla
1507*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
15080e5c9e7fSSrinivas Kandagatla wcd_mbhc_adc_hs_rem_irq,
15090e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15100e5c9e7fSSrinivas Kandagatla "Elect Remove", mbhc);
15110e5c9e7fSSrinivas Kandagatla if (ret)
1512*a5475829SJohan Hovold goto err_free_hs_ins_intr;
15130e5c9e7fSSrinivas Kandagatla
15140e5c9e7fSSrinivas Kandagatla disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
15150e5c9e7fSSrinivas Kandagatla
1516*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
15170e5c9e7fSSrinivas Kandagatla wcd_mbhc_hphl_ocp_irq,
15180e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15190e5c9e7fSSrinivas Kandagatla "HPH_L OCP detect", mbhc);
15200e5c9e7fSSrinivas Kandagatla if (ret)
1521*a5475829SJohan Hovold goto err_free_hs_rem_intr;
15220e5c9e7fSSrinivas Kandagatla
1523*a5475829SJohan Hovold ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
15240e5c9e7fSSrinivas Kandagatla wcd_mbhc_hphr_ocp_irq,
15250e5c9e7fSSrinivas Kandagatla IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15260e5c9e7fSSrinivas Kandagatla "HPH_R OCP detect", mbhc);
15270e5c9e7fSSrinivas Kandagatla if (ret)
1528*a5475829SJohan Hovold goto err_free_hph_left_ocp;
15290e5c9e7fSSrinivas Kandagatla
15300e5c9e7fSSrinivas Kandagatla return mbhc;
1531*a5475829SJohan Hovold
1532*a5475829SJohan Hovold err_free_hph_left_ocp:
1533*a5475829SJohan Hovold free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1534*a5475829SJohan Hovold err_free_hs_rem_intr:
1535*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1536*a5475829SJohan Hovold err_free_hs_ins_intr:
1537*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1538*a5475829SJohan Hovold err_free_btn_release_intr:
1539*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1540*a5475829SJohan Hovold err_free_btn_press_intr:
1541*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1542*a5475829SJohan Hovold err_free_sw_intr:
1543*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1544*a5475829SJohan Hovold err_free_mbhc:
1545*a5475829SJohan Hovold kfree(mbhc);
1546*a5475829SJohan Hovold
15470e5c9e7fSSrinivas Kandagatla dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
15480e5c9e7fSSrinivas Kandagatla
15490e5c9e7fSSrinivas Kandagatla return ERR_PTR(ret);
15500e5c9e7fSSrinivas Kandagatla }
15510e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_init);
15520e5c9e7fSSrinivas Kandagatla
wcd_mbhc_deinit(struct wcd_mbhc * mbhc)15530e5c9e7fSSrinivas Kandagatla void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
15540e5c9e7fSSrinivas Kandagatla {
1555*a5475829SJohan Hovold free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
1556*a5475829SJohan Hovold free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1557*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1558*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1559*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1560*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1561*a5475829SJohan Hovold free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1562*a5475829SJohan Hovold
15630e5c9e7fSSrinivas Kandagatla mutex_lock(&mbhc->lock);
15640e5c9e7fSSrinivas Kandagatla wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
15650e5c9e7fSSrinivas Kandagatla mutex_unlock(&mbhc->lock);
1566*a5475829SJohan Hovold
1567*a5475829SJohan Hovold kfree(mbhc);
15680e5c9e7fSSrinivas Kandagatla }
15690e5c9e7fSSrinivas Kandagatla EXPORT_SYMBOL(wcd_mbhc_deinit);
15700e5c9e7fSSrinivas Kandagatla
mbhc_init(void)15710e5c9e7fSSrinivas Kandagatla static int __init mbhc_init(void)
15720e5c9e7fSSrinivas Kandagatla {
15730e5c9e7fSSrinivas Kandagatla return 0;
15740e5c9e7fSSrinivas Kandagatla }
15750e5c9e7fSSrinivas Kandagatla
mbhc_exit(void)15760e5c9e7fSSrinivas Kandagatla static void __exit mbhc_exit(void)
15770e5c9e7fSSrinivas Kandagatla {
15780e5c9e7fSSrinivas Kandagatla }
15790e5c9e7fSSrinivas Kandagatla
15800e5c9e7fSSrinivas Kandagatla module_init(mbhc_init);
15810e5c9e7fSSrinivas Kandagatla module_exit(mbhc_exit);
15820e5c9e7fSSrinivas Kandagatla
15830e5c9e7fSSrinivas Kandagatla MODULE_DESCRIPTION("wcd MBHC v2 module");
15840e5c9e7fSSrinivas Kandagatla MODULE_LICENSE("GPL");
1585