blob: 21fa4951c6356b5d9cf054eb31e45f30158aa0fc [file] [log] [blame]
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070021#include <linux/list.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070022#include <linux/mfd/wcd9xxx/core.h>
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070023#include <linux/mfd/wcd9xxx/core-resource.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070024#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
25#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
26#include <linux/mfd/wcd9xxx/pdata.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <linux/bitops.h>
33#include <linux/delay.h>
34#include <linux/pm_runtime.h>
35#include <linux/kernel.h>
36#include <linux/gpio.h>
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -070037#include <linux/input.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070038#include "wcd9320.h"
Simmi Pateriya0a44d842013-04-03 01:12:42 +053039#include "wcd9306.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070040#include "wcd9xxx-mbhc.h"
41#include "wcd9xxx-resmgr.h"
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070042#include "wcd9xxx-common.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070043
44#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
Joonwoo Park80a01172012-10-15 16:05:23 -070045 SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
46 SND_JACK_UNSUPPORTED)
Joonwoo Parka8890262012-10-15 12:04:27 -070047#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
48 SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
49 SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
50 SND_JACK_BTN_6 | SND_JACK_BTN_7)
51
52#define NUM_DCE_PLUG_DETECT 3
Joonwoo Parkccccba72013-04-26 11:19:46 -070053#define NUM_DCE_PLUG_INS_DETECT 5
Joonwoo Parka8890262012-10-15 12:04:27 -070054#define NUM_ATTEMPTS_INSERT_DETECT 25
55#define NUM_ATTEMPTS_TO_REPORT 5
56
57#define FAKE_INS_LOW 10
58#define FAKE_INS_HIGH 80
59#define FAKE_INS_HIGH_NO_SWCH 150
60#define FAKE_REMOVAL_MIN_PERIOD_MS 50
61#define FAKE_INS_DELTA_SCALED_MV 300
62
63#define BUTTON_MIN 0x8000
64#define STATUS_REL_DETECTION 0x0C
65
66#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
67#define HS_DETECT_PLUG_INERVAL_MS 100
68#define SWCH_REL_DEBOUNCE_TIME_MS 50
69#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070070#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070071
72#define GND_MIC_SWAP_THRESHOLD 2
73#define OCP_ATTEMPT 1
74
75#define FW_READ_ATTEMPTS 15
76#define FW_READ_TIMEOUT 2000000
77
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070078#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070079
80#define MCLK_RATE_12288KHZ 12288000
81#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070082
83#define DEFAULT_DCE_STA_WAIT 55
84#define DEFAULT_DCE_WAIT 60000
85#define DEFAULT_STA_WAIT 5000
86
87#define VDDIO_MICBIAS_MV 1800
88
Joonwoo Parkccccba72013-04-26 11:19:46 -070089#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
90
Joonwoo Park20bc9da2013-01-16 12:58:06 -080091#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070092#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Joonwoo Park20bc9da2013-01-16 12:58:06 -080093#define WCD9XXX_MEAS_DELTA_MAX_MV 50
Joonwoo Park141d6182013-03-05 12:25:46 -080094#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
95#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070096
97/*
98 * Invalid voltage range for the detection
99 * of plug type with current source
100 */
101#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 110
102#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
103
104/*
105 * Threshold used to detect euro headset
106 * with current source
107 */
108#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
109#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
110
111#define WCD9XXX_MBHC_NSC_CS 9
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800112#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
Joonwoo Park479347a2013-04-15 18:01:05 -0700113#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
Joonwoo Parkccccba72013-04-26 11:19:46 -0700114#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800115
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700116#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
117
118/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
119#define WCD9XXX_WG_TIME_FACTOR_US 240
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800120
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700121#define WCD9XXX_V_CS_HS_MAX 500
122#define WCD9XXX_V_CS_NO_MIC 5
123#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
124#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 10
125
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700126static int impedance_detect_en;
127module_param(impedance_detect_en, int,
128 S_IRUGO | S_IWUSR | S_IWGRP);
129MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
130
Joonwoo Parkccccba72013-04-26 11:19:46 -0700131static bool detect_use_vddio_switch = true;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700216 s16 v_brh, v_b1_hu;
Joonwoo Parka8890262012-10-15 12:04:27 -0700217 struct snd_soc_codec *codec = mbhc->codec;
218 int mbhc_state = mbhc->mbhc_state;
219
220 pr_debug("%s: enter\n", __func__);
221 if (!mbhc->polling_active) {
222 pr_debug("Polling is not active, do not start polling\n");
223 return;
224 }
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530225
226 /*
227 * setup internal micbias if codec uses internal micbias for
228 * headset detection
229 */
230 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
231 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
232 mbhc->mbhc_cb->setup_int_rbias(codec, true);
233 else
234 pr_err("%s: internal bias requested but codec did not provide callback\n",
235 __func__);
236 }
237
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530238 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530239 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
240 mbhc->mbhc_cb->enable_mux_bias_block(codec);
241 else
242 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
243 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530244
Joonwoo Parka8890262012-10-15 12:04:27 -0700245 if (!mbhc->no_mic_headset_override &&
246 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530247 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700248 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
249 /* set to max button press threshold */
250 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
252 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
254 /* set to max */
255 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
256 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700257
258 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
260 (v_brh >> 8) & 0xFF);
261 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
262 v_brh & 0xFF);
263 v_b1_hu = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
264 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
265 v_b1_hu & 0xFF);
266 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
267 (v_b1_hu >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700268 }
269
270 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
271 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
272 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
273 pr_debug("%s: leave\n", __func__);
274}
275
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700276static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
277 unsigned int cfilt_mv)
278{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700279 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
280}
281
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700282/*
283 * called under codec_resource_lock acquisition
284 * return old status
285 */
286static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700287 int vddio_switch, bool restartpolling,
288 bool checkpolling)
289{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700290 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700291 int cfilt_k_val;
292 bool override;
293 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700294 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700295
296 codec = mbhc->codec;
297
Joonwoo Parkccccba72013-04-26 11:19:46 -0700298 if (mbhc->micbias_enable) {
299 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700300 ret = mbhc->mbhc_micbias_switched;
301 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700302 }
303
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700304 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700305 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
306 (!checkpolling || mbhc->polling_active)) {
307 if (restartpolling)
308 wcd9xxx_pause_hs_polling(mbhc);
309 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
310 0x04;
311 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700312 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700313 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700314 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700315 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700316 VDDIO_MICBIAS_MV);
317 usleep_range(10000, 10000);
318 snd_soc_update_bits(codec,
319 mbhc->mbhc_bias_regs.cfilt_val,
320 0xFC, (cfilt_k_val << 2));
321 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700322 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700323 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700324 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700325 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700326 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700327 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700328 /* Threshods for button press */
329 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
330 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
331 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
332 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
333 0xFF);
334 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
335 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
336 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
337 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
338 0xFF);
339 /* Threshods for button release */
340 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
341 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
342 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
343 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700344 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
345 __func__);
346 }
347
348 /* Enable MIC BIAS Switch to VDDIO */
349 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
350 0x80, 0x80);
351 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
352 0x10, 0x00);
353 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700354 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700355 if (restartpolling)
356 wcd9xxx_start_hs_polling(mbhc);
357
358 mbhc->mbhc_micbias_switched = true;
359 pr_debug("%s: VDDIO switch enabled\n", __func__);
360 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
361 if ((!checkpolling || mbhc->polling_active) &&
362 restartpolling)
363 wcd9xxx_pause_hs_polling(mbhc);
364 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700365 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700366 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700367 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700368 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700369 snd_soc_update_bits(codec,
370 mbhc->mbhc_bias_regs.cfilt_val,
371 0xFC, (cfilt_k_val << 2));
372 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700373 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700375 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700376 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700377 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
378 0xFF);
379 /* Revert threshods for button press */
380 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
381 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
382 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
383 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
384 0xFF);
385 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
386 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
387 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
388 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
389 0xFF);
390 /* Revert threshods for button release */
391 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
392 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
393 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
394 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700395 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
396 __func__);
397 }
398
399 /* Disable MIC BIAS Switch to VDDIO */
400 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
401 0x00);
402 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
403 0x00);
404
405 if ((!checkpolling || mbhc->polling_active) && restartpolling)
406 wcd9xxx_start_hs_polling(mbhc);
407
408 mbhc->mbhc_micbias_switched = false;
409 pr_debug("%s: VDDIO switch disabled\n", __func__);
410 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700411
412 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700413}
414
415static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
416{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700417 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700418}
419
Joonwoo Park73375212013-05-07 12:42:44 -0700420static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
421 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700422{
Joonwoo Park73375212013-05-07 12:42:44 -0700423 enum mbhc_v_index vidx;
424 s16 ret = -EINVAL;
425
Joonwoo Parka8890262012-10-15 12:04:27 -0700426 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
427 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700428 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700429 else
Joonwoo Park73375212013-05-07 12:42:44 -0700430 vidx = MBHC_V_IDX_CFILT;
431
432 switch (idx) {
433 case WCD9XXX_CURRENT_V_INS_H:
434 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
435 break;
436 case WCD9XXX_CURRENT_V_INS_HU:
437 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
438 break;
439 case WCD9XXX_CURRENT_V_B1_H:
440 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
441 break;
442 case WCD9XXX_CURRENT_V_B1_HU:
443 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
444 break;
445 case WCD9XXX_CURRENT_V_BR_H:
446 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
447 break;
448 }
449
450 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700451}
452
453void *wcd9xxx_mbhc_cal_btn_det_mp(
454 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
455 const enum wcd9xxx_mbhc_btn_det_mem mem)
456{
457 void *ret = &btn_det->_v_btn_low;
458
459 switch (mem) {
460 case MBHC_BTN_DET_GAIN:
461 ret += sizeof(btn_det->_n_cic);
462 case MBHC_BTN_DET_N_CIC:
463 ret += sizeof(btn_det->_n_ready);
464 case MBHC_BTN_DET_N_READY:
465 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
466 case MBHC_BTN_DET_V_BTN_HIGH:
467 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
468 case MBHC_BTN_DET_V_BTN_LOW:
469 /* do nothing */
470 break;
471 default:
472 ret = NULL;
473 }
474
475 return ret;
476}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700477EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700478
479static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
480{
481 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700482 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
483 WCD9XXX_CURRENT_V_INS_HU);
484 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
485 WCD9XXX_CURRENT_V_B1_HU);
486 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
487 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700488
489 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
490 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
491 (v_ins_hu >> 8) & 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700492 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700493 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700494 (v_b1_hu >> 8) & 0xFF);
495 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700496 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700497 (v_b1_h >> 8) & 0xFF);
498 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700499 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700500 (v_brh >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700501 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
502 mbhc->mbhc_data.v_brl & 0xFF);
503 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
504 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
505}
506
Simmi Pateriya95466b12013-05-09 20:08:46 +0530507static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700508 bool fast)
509{
510 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530511 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700512
Simmi Pateriya95466b12013-05-09 20:08:46 +0530513 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
514 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530515 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530516 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700517 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530518 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700519 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700520
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700521 cfilt_mode.reg_mask = 0x40;
522 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530523 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700524 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700525
526 if (cfilt_mode.cur_mode_val
527 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530528 if (mbhc->polling_active)
529 wcd9xxx_pause_hs_polling(mbhc);
530 snd_soc_update_bits(codec,
531 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700532 cfilt_mode.reg_mask,
533 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530534 if (mbhc->polling_active)
535 wcd9xxx_start_hs_polling(mbhc);
536 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700537 cfilt_mode.cur_mode_val,
538 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530539 } else {
540 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700541 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530542 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700543}
544
Joonwoo Park3699ca32013-02-08 12:06:15 -0800545static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
546 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700547{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800548 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800549 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
550 WCD9XXX_COND_HPH_MIC,
551 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800552 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
553 WCD9XXX_COND_HPH,
554 status & SND_JACK_HEADPHONE);
555 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800556
Joonwoo Parka8890262012-10-15 12:04:27 -0700557 snd_soc_jack_report_no_dapm(jack, status, mask);
558}
559
560static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
561 int irq)
562{
563 struct snd_soc_codec *codec;
564
565 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
566 codec = mbhc->codec;
567 if (mbhc->hph_status & jack_status) {
568 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800569 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700570 mbhc->hph_status, WCD9XXX_JACK_MASK);
571 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
572 0x00);
573 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
574 0x10);
575 /*
576 * reset retry counter as PA is turned off signifying
577 * start of new OCP detection session
578 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700579 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700580 mbhc->hphlocp_cnt = 0;
581 else
582 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700583 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700584 }
585}
586
587static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
588{
589 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700590 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700591}
592
593static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
594{
595 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700596 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700597}
598
599static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
600 struct mbhc_micbias_regs *micbias_regs)
601{
602 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700603 struct wcd9xxx_micbias_setting *micbias_pdata =
604 mbhc->resmgr->micbias_pdata;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700605
Joonwoo Parka8890262012-10-15 12:04:27 -0700606 switch (mbhc->mbhc_cfg->micbias) {
607 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700608 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700609 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
610 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
611 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
612 break;
613 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700614 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700615 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
616 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
617 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
618 break;
619 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700620 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700621 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
622 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
623 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
624 break;
625 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700626 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700627 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
628 micbias_regs->int_rbias =
629 mbhc->resmgr->reg_addr->micb_4_int_rbias;
630 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
631 break;
632 default:
633 /* Should never reach here */
634 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
635 return;
636 }
637
638 micbias_regs->cfilt_sel = cfilt;
639
640 switch (cfilt) {
641 case WCD9XXX_CFILT1_SEL:
642 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
643 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700644 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700645 break;
646 case WCD9XXX_CFILT2_SEL:
647 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
648 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700649 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700650 break;
651 case WCD9XXX_CFILT3_SEL:
652 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
653 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700654 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700655 break;
656 }
657}
658
659static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
660{
661 bool pa_turned_on = false;
662 struct snd_soc_codec *codec = mbhc->codec;
663 u8 wg_time;
664
665 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
666 wg_time += 1;
667
668 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
669 &mbhc->hph_pa_dac_state)) {
670 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
671 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
672 0xC0, 0xC0);
673 }
674 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
675 &mbhc->hph_pa_dac_state)) {
676 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
677 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700678 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700679 }
680
681 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
682 &mbhc->hph_pa_dac_state)) {
683 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
684 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
685 1 << 4);
686 pa_turned_on = true;
687 }
688 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
689 &mbhc->hph_pa_dac_state)) {
690 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
691 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
692 << 5);
693 pa_turned_on = true;
694 }
695
696 if (pa_turned_on) {
697 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
698 __func__);
699 usleep_range(wg_time * 1000, wg_time * 1000);
700 }
701}
702
703static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
704{
705 int r;
706 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
707 if (r)
708 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
709 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700710 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700711 return r;
712}
713
714static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
715{
716 u8 hph_reg_val = 0;
717 if (left)
718 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
719 else
720 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
721
722 return (hph_reg_val & 0xC0) ? true : false;
723}
724
725static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
726{
727 u8 hph_reg_val = 0;
728 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
729
730 return (hph_reg_val & 0x30) ? true : false;
731}
732
733/* called under codec_resource_lock acquisition */
734static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
735{
736 u8 wg_time;
737 struct snd_soc_codec *codec = mbhc->codec;
738
739 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
740 wg_time += 1;
741
742 /* If headphone PA is on, check if userspace receives
743 * removal event to sync-up PA's state */
744 if (wcd9xxx_is_hph_pa_on(codec)) {
745 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
746 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
747 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
748 } else {
749 pr_debug("%s PA is off\n", __func__);
750 }
751
752 if (wcd9xxx_is_hph_dac_on(codec, 1))
753 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
754 if (wcd9xxx_is_hph_dac_on(codec, 0))
755 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
756
757 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800758 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700759 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
760 usleep_range(wg_time * 1000, wg_time * 1000);
761}
762
763static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
764{
765 if (!mbhc->mbhc_cfg->insert_detect)
766 return;
767 pr_debug("%s: Setting up %s detection\n", __func__,
768 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700769 /* Disable detection to avoid glitch */
770 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530771 if (mbhc->mbhc_cfg->gpio_level_insert)
772 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
773 (0x68 | (ins ? (1 << 1) : 0)));
774 else
775 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
776 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700777 /* Re-enable detection */
778 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700779}
780
781/* called under codec_resource_lock acquisition */
782static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
783 enum snd_jack_types jack_type)
784{
785 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
786
Joonwoo Park80a01172012-10-15 16:05:23 -0700787 pr_debug("%s: enter insertion %d hph_status %x\n",
788 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700789 if (!insertion) {
790 /* Report removal */
791 mbhc->hph_status &= ~jack_type;
792 /*
793 * cancel possibly scheduled btn work and
794 * report release if we reported button press
795 */
796 if (wcd9xxx_cancel_btn_work(mbhc))
797 pr_debug("%s: button press is canceled\n", __func__);
798 else if (mbhc->buttons_pressed) {
799 pr_debug("%s: release of button press%d\n",
800 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800801 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700802 mbhc->buttons_pressed);
803 mbhc->buttons_pressed &=
804 ~WCD9XXX_JACK_BUTTON_MASK;
805 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700806
807 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
808 pr_debug("%s: Disabling micbias\n", __func__);
809 mbhc->micbias_enable_cb(mbhc->codec, false);
810 mbhc->micbias_enable = false;
811 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700812 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700813 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
814 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800815 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700816 WCD9XXX_JACK_MASK);
817 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
818 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
819 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
820 mbhc->current_plug = PLUG_TYPE_NONE;
821 mbhc->polling_active = false;
822 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700823 /*
824 * Report removal of current jack type.
825 * Headphone to headset shouldn't report headphone
826 * removal.
827 */
828 if (mbhc->mbhc_cfg->detect_extn_cable &&
829 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
830 jack_type == SND_JACK_HEADSET) &&
831 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
832 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
833 mbhc->hph_status == SND_JACK_HEADSET) {
834 pr_debug("%s: Disabling micbias\n", __func__);
835 mbhc->micbias_enable_cb(mbhc->codec, false);
836 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700837 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700838
839 pr_debug("%s: Reporting removal (%x)\n",
840 __func__, mbhc->hph_status);
841 mbhc->zl = mbhc->zr = 0;
842 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
843 0, WCD9XXX_JACK_MASK);
844 mbhc->hph_status = 0;
Joonwoo Park80a01172012-10-15 16:05:23 -0700845 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700846 /* Report insertion */
847 mbhc->hph_status |= jack_type;
848
849 if (jack_type == SND_JACK_HEADPHONE) {
850 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
851 } else if (jack_type == SND_JACK_UNSUPPORTED) {
852 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
853 } else if (jack_type == SND_JACK_HEADSET) {
854 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
855 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700856 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700857 } else if (jack_type == SND_JACK_LINEOUT) {
858 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700859 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700860
861 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
862 pr_debug("%s: Enabling micbias\n", __func__);
863 mbhc->micbias_enable_cb(mbhc->codec, true);
864 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700865
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700866 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700867 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700868
Joonwoo Parka8890262012-10-15 12:04:27 -0700869 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
870 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800871 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700872 mbhc->hph_status, WCD9XXX_JACK_MASK);
873 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
874 }
875 /* Setup insert detect */
876 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700877
878 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700879}
880
881/* should be called under interrupt context that hold suspend */
882static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
883 struct work_struct *work)
884{
885 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
886 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
887 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700888 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700889 schedule_work(work);
890}
891
892/* called under codec_resource_lock acquisition */
893static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
894 struct work_struct *work)
895{
896 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
897 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
898 mbhc->hs_detect_work_stop = true;
899 wmb();
900 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
901 if (cancel_work_sync(work)) {
902 pr_debug("%s: correct_plug_swch is canceled\n",
903 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700904 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700905 }
906 WCD9XXX_BCL_LOCK(mbhc->resmgr);
907}
908
Joonwoo Park73375212013-05-07 12:42:44 -0700909static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
910{
911 int r;
912 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700913 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
914 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700915 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700916 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700917 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700918 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700919 return r;
920}
921
Joonwoo Parka8890262012-10-15 12:04:27 -0700922static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
923{
924 s16 v_hs_max;
925 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
926
927 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
928 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
929 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700930 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700931 else
932 v_hs_max = plug_type->v_hs_max;
933 return v_hs_max;
934}
935
Joonwoo Parka8890262012-10-15 12:04:27 -0700936static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
937{
938 u8 bias_msb, bias_lsb;
939 short bias_value;
940
941 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
942 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
943 bias_value = (bias_msb << 8) | bias_lsb;
944 return bias_value;
945}
946
947static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
948{
949 u8 bias_msb, bias_lsb;
950 short bias_value;
951
952 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
953 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
954 bias_value = (bias_msb << 8) | bias_lsb;
955 return bias_value;
956}
957
958static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
959 bool on)
960{
961 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
962}
963
964static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
965 bool override_bypass, bool noreldetection)
966{
967 short bias_value;
968 struct snd_soc_codec *codec = mbhc->codec;
969
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700970 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
971 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700972 if (noreldetection)
973 wcd9xxx_turn_onoff_rel_detection(codec, false);
974
Simmi Pateriya4b5159f2013-11-14 10:43:24 +0530975 if (mbhc->mbhc_cfg->do_recalibration)
976 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
977 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700978 /* Turn on the override */
979 if (!override_bypass)
980 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
981 if (dce) {
982 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
983 0x8);
984 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
985 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
986 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +0530987 if (mbhc->mbhc_cfg->do_recalibration)
988 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
989 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700990 usleep_range(mbhc->mbhc_data.t_sta_dce,
991 mbhc->mbhc_data.t_sta_dce);
992 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
993 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
994 bias_value = wcd9xxx_read_dce_result(codec);
995 } else {
996 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
997 0x8);
998 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
999 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1000 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301001 if (mbhc->mbhc_cfg->do_recalibration)
1002 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1003 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001004 usleep_range(mbhc->mbhc_data.t_sta_dce,
1005 mbhc->mbhc_data.t_sta_dce);
1006 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1007 usleep_range(mbhc->mbhc_data.t_sta,
1008 mbhc->mbhc_data.t_sta);
1009 bias_value = wcd9xxx_read_sta_result(codec);
1010 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1011 0x8);
1012 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1013 }
1014 /* Turn off the override after measuring mic voltage */
1015 if (!override_bypass)
1016 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1017 0x00);
1018
1019 if (noreldetection)
1020 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001021 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1022 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001023
1024 return bias_value;
1025}
1026
1027static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1028 bool norel)
1029{
1030 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1031}
1032
Joonwoo Park520a0f92013-05-14 19:39:58 -07001033static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001034 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001035{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001036 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001037 s32 mv;
1038
1039 value = bias_value;
1040 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001041 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001042 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001043 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001044 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001045 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001046 }
1047
1048 return mv;
1049}
1050
Joonwoo Park520a0f92013-05-14 19:39:58 -07001051static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1052 u16 bias_value)
1053{
1054 s16 z;
1055 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001056 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1057 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001058}
1059
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301060/* To enable/disable bandgap and RC oscillator */
1061static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1062 bool enable)
1063{
1064 if (enable) {
1065 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1066 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1067 WCD9XXX_BANDGAP_AUDIO_MODE);
1068 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1069 WCD9XXX_CLK_RCO);
1070 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1071 } else {
1072 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1073 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1074 WCD9XXX_CLK_RCO);
1075 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1076 WCD9XXX_BANDGAP_AUDIO_MODE);
1077 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1078 }
1079}
1080
Joonwoo Parka8890262012-10-15 12:04:27 -07001081/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001082static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1083 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001084{
1085 struct snd_soc_codec *codec = mbhc->codec;
1086 short bias_value;
1087 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001088 s16 reg;
1089 int change;
1090 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1091 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001092
1093 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1094
1095 pr_debug("%s: enter\n", __func__);
1096 if (!mbhc->mbhc_cfg->calibration) {
1097 pr_err("%s: Error, no calibration exists\n", __func__);
1098 return -ENODEV;
1099 }
1100
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001101 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001102 /* Enable external voltage source to micbias if present */
1103 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1104 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001105
Joonwoo Parka8890262012-10-15 12:04:27 -07001106 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301107 * setup internal micbias if codec uses internal micbias for
1108 * headset detection
1109 */
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301110 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301111 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1112 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1113 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301114 pr_err("%s: internal bias requested but codec did not provide callback\n",
1115 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301116 }
1117
Joonwoo Parka8890262012-10-15 12:04:27 -07001118 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1119
1120 /* Make sure CFILT is in fast mode, save current mode */
1121 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301122 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1123 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1124 else
1125 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1126 0x70, 0x00);
1127
Joonwoo Parka8890262012-10-15 12:04:27 -07001128 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301129 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301130 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1131 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1132 else
1133 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1134 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001135
1136 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1137 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1138 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1139
1140 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1141 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1142 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1143
1144 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1145 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1146
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301147 if (!mbhc->mbhc_cfg->do_recalibration) {
1148 if (!is_cs_enable)
1149 wcd9xxx_calibrate_hs_polling(mbhc);
1150 }
1151
Joonwoo Parka8890262012-10-15 12:04:27 -07001152 /* don't flip override */
1153 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301154 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001155 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1156
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301157 if (mbhc->mbhc_cfg->do_recalibration) {
1158 /* recalibrate dce_z and sta_z */
1159 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1160 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1161 0x78, btn_det->mbhc_nsc << 3);
1162 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1163 if (change)
1164 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1165 if (dce_z && sta_z) {
1166 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1167 __func__,
1168 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1169 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1170 mbhc->mbhc_data.dce_z = dce_z;
1171 mbhc->mbhc_data.sta_z = sta_z;
1172 wcd9xxx_mbhc_calc_thres(mbhc);
1173 wcd9xxx_calibrate_hs_polling(mbhc);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001174 } else {
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301175 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
1176 __func__, dce_z, sta_z);
1177 }
1178
1179 if (is_cs_enable) {
1180 /* recalibrate dce_nsc_cs_z */
1181 reg = snd_soc_read(mbhc->codec,
1182 WCD9XXX_A_CDC_MBHC_B1_CTL);
1183 snd_soc_update_bits(mbhc->codec,
1184 WCD9XXX_A_CDC_MBHC_B1_CTL,
1185 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1186 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1187 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1188 reg);
1189 if (dce_z) {
1190 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n",
1191 __func__, mbhc->mbhc_data.dce_nsc_cs_z,
1192 dce_z & 0xffff);
1193 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1194 } else {
1195 pr_debug("%s: failed get new dce_nsc_cs_z\n",
1196 __func__);
1197 }
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001198 }
1199 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001200 return bias_value;
1201}
1202
1203static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1204{
1205 struct snd_soc_codec *codec = mbhc->codec;
1206 const struct wcd9xxx_mbhc_general_cfg *generic =
1207 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1208
1209 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001210 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001211 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001212 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001213
1214 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1215 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001216 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001217
1218 usleep_range(generic->t_shutdown_plug_rem,
1219 generic->t_shutdown_plug_rem);
1220
1221 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1222
Joonwoo Park533b3682013-06-13 11:41:21 -07001223 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001224 /* Put requested CLK back */
1225 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001226 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001227
1228 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1229}
1230
1231static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1232{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001233
1234 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001235 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1236
1237 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1238
Joonwoo Parka8890262012-10-15 12:04:27 -07001239
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001240 /* Disable external voltage source to micbias if present */
1241 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1242 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1243
Joonwoo Parka8890262012-10-15 12:04:27 -07001244 mbhc->polling_active = false;
1245 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001246 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001247}
1248
Joonwoo Parka8890262012-10-15 12:04:27 -07001249/* called under codec_resource_lock acquisition */
1250static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1251{
1252 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1253 if (on)
1254 usleep_range(5000, 5000);
1255}
1256
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001257static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1258{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001259 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001260
1261 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1262 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1263 goto exit;
1264 }
1265
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001266 if (on) {
1267 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1268 1 << 7, 1 << 7);
1269 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1270 1 << 4, 0);
1271 } else {
1272 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1273 1 << 4, 1 << 4);
1274 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1275 1 << 7, 0);
1276 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001277
1278exit:
1279 /*
1280 * Wait for the micbias to settle down to vddio
1281 * when the micbias to vddio switch is enabled.
1282 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001283 if (on)
1284 usleep_range(10000, 10000);
1285}
1286
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001287static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1288{
1289 u16 hph, status;
1290 struct snd_soc_codec *codec = mbhc->codec;
1291
1292 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1293 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1294 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1295 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1296 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1297 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1298 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1299 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1300 return status;
1301}
1302
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001303static enum wcd9xxx_mbhc_plug_type
1304wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1305 struct wcd9xxx_mbhc_detect *dt, const int size,
1306 bool highhph,
1307 unsigned long event_state)
1308{
1309 int i;
1310 int vdce, mb_mv;
1311 int ch, sz, delta_thr;
1312 int minv = 0, maxv = INT_MIN;
1313 struct wcd9xxx_mbhc_detect *d = dt;
1314 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1315 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1316
1317 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1318 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1319 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001320 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001321
1322 pr_debug("%s: enter\n", __func__);
1323 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1324
1325 sz = size - 1;
1326 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1327 if (d->mic_bias) {
1328 dce_z = mbhc->mbhc_data.dce_z;
1329 mb_mv = mbhc->mbhc_data.micb_mv;
1330 hs_max = plug_type->v_hs_max;
1331 no_mic = plug_type->v_no_mic;
1332 } else {
1333 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1334 mb_mv = VDDIO_MICBIAS_MV;
1335 hs_max = WCD9XXX_V_CS_HS_MAX;
1336 no_mic = WCD9XXX_V_CS_NO_MIC;
1337 }
1338
1339 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1340 dce_z, (u32)mb_mv);
1341
1342 d->_vdces = vdce;
1343 if (d->_vdces < no_mic)
1344 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001345 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001346 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001347 highhph_cnt++;
1348 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001349 d->_type = PLUG_TYPE_HEADSET;
1350
1351 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1352 __func__, i, d->dce, vdce, d->_vdces,
1353 d->hphl_status & 0x01,
1354 d->_type);
1355
1356 ch += d->hphl_status & 0x01;
1357 if (!d->swap_gnd && !d->mic_bias) {
1358 if (maxv < d->_vdces)
1359 maxv = d->_vdces;
1360 if (!minv || minv > d->_vdces)
1361 minv = d->_vdces;
1362 }
1363 if ((!d->mic_bias &&
1364 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1365 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1366 (d->mic_bias &&
1367 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1368 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1369 pr_debug("%s: within invalid range\n", __func__);
1370 type = PLUG_TYPE_INVALID;
1371 goto exit;
1372 }
1373 }
1374
1375 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1376 pr_debug("%s: HPHL PA was ON\n", __func__);
1377 } else if (ch != sz && ch > 0) {
1378 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1379 type = PLUG_TYPE_INVALID;
1380 goto exit;
1381 }
1382
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001383 delta_thr = ((highhph_cnt == sz) || highhph) ?
1384 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001385 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1386
1387 for (i = 0, d = dt; i < sz; i++, d++) {
1388 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1389 (d->_type != dprev->_type)) {
1390 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1391 type = PLUG_TYPE_INVALID;
1392 goto exit;
1393 }
1394
1395 if (!d->swap_gnd && !d->mic_bias &&
1396 (abs(minv - d->_vdces) > delta_thr ||
1397 abs(maxv - d->_vdces) > delta_thr)) {
1398 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1399 __func__, d->_vdces, minv, maxv);
1400 type = PLUG_TYPE_INVALID;
1401 goto exit;
1402 } else if (d->swap_gnd) {
1403 dgnd = d;
1404 }
1405
1406 if (!d->mic_bias && !d->swap_gnd)
1407 dprev = d;
1408 else if (d->mic_bias)
1409 dmicbias = d;
1410 }
1411 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1412 dt->_type != dgnd->_type) {
1413 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1414 type = PLUG_TYPE_INVALID;
1415 goto exit;
1416 }
1417
1418 type = dt->_type;
1419 if (dmicbias) {
1420 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1421 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1422 dt->_type == PLUG_TYPE_HEADSET)) {
1423 type = PLUG_TYPE_HEADSET;
1424 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1425 pr_debug("%s: Headset with threshold on MIC detected\n",
1426 __func__);
1427 if (mbhc->mbhc_cfg->micbias_enable_flags &
1428 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1429 mbhc->micbias_enable = true;
1430 }
1431 }
1432 }
1433
1434 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1435 if (((type == PLUG_TYPE_HEADSET ||
1436 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1437 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1438 __func__, type);
1439 type = PLUG_TYPE_INVALID;
1440 }
1441 }
1442 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1443 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1444 minv) &&
1445 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1446 maxv))
1447 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001448 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001449 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1450 type = PLUG_TYPE_INVALID;
1451 }
1452 }
1453exit:
1454 pr_debug("%s: Plug type %d detected\n", __func__, type);
1455 return type;
1456}
1457
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001458/*
1459 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1460 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001461 * param mbhc wcd9xxx_mbhc structure
1462 * param dt collected measurements
1463 * param size array size of dt
1464 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001465 */
1466static enum wcd9xxx_mbhc_plug_type
1467wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001468 struct wcd9xxx_mbhc_detect *dt, const int size,
1469 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001470{
1471 int i;
1472 int ch;
1473 enum wcd9xxx_mbhc_plug_type type;
1474 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001475 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001476 int maxv = 0, minv = 0;
1477 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1478 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1479 const s16 hs_max = plug_type->v_hs_max;
1480 const s16 no_mic = plug_type->v_no_mic;
1481
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001482 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1483
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001484 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1485 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1486 if (d->vddio)
1487 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1488 else
1489 d->_vdces = vdce;
1490
1491 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1492 d->_type = PLUG_TYPE_HEADSET;
1493 else if (d->_vdces < no_mic)
1494 d->_type = PLUG_TYPE_HEADPHONE;
1495 else
1496 d->_type = PLUG_TYPE_HIGH_HPH;
1497
1498 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001499 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001500 if (maxv < d->_vdces)
1501 maxv = d->_vdces;
1502 if (!minv || minv > d->_vdces)
1503 minv = d->_vdces;
1504 }
1505
1506 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1507 __func__, i, d->dce, vdce, d->_vdces,
1508 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1509 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001510
1511
1512 /*
1513 * If GND and MIC prongs are aligned to HPHR and GND of
1514 * headphone, codec measures the voltage based on
1515 * impedance between HPHR and GND which results in ~80mv.
1516 * Avoid this.
1517 */
1518 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1519 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1520 pr_debug("%s: within invalid range\n", __func__);
1521 type = PLUG_TYPE_INVALID;
1522 goto exit;
1523 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001524 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001525
1526 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1527 pr_debug("%s: HPHL PA was ON\n", __func__);
1528 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001529 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1530 type = PLUG_TYPE_INVALID;
1531 goto exit;
1532 }
1533
Joonwoo Parka84ec452013-07-17 15:02:52 -07001534 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001535 if (d->vddio) {
1536 dvddio = d;
1537 continue;
1538 }
1539
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001540 if ((i > 0) && (d->_type != dprev->_type)) {
1541 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1542 type = PLUG_TYPE_INVALID;
1543 goto exit;
1544 }
1545
1546 if (!d->swap_gnd && !d->hwvalue &&
1547 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1548 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1549 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1550 __func__, d->_vdces, minv, maxv);
1551 type = PLUG_TYPE_INVALID;
1552 goto exit;
1553 } else if (d->swap_gnd) {
1554 dgnd = d;
1555 }
1556 dprev = d;
1557 }
1558
1559 WARN_ON(i != size);
1560 type = dt->_type;
1561 if (type == PLUG_TYPE_HEADSET && dgnd) {
1562 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1563 minv) &&
1564 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1565 maxv))
1566 type = PLUG_TYPE_GND_MIC_SWAP;
1567 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001568
1569 /* if HPHL PA was on, we cannot use hphl status */
1570 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1571 if (((type == PLUG_TYPE_HEADSET ||
1572 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1573 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1574 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1575 __func__, type);
1576 type = PLUG_TYPE_INVALID;
1577 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001578 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001579
Joonwoo Parkccccba72013-04-26 11:19:46 -07001580 if (type == PLUG_TYPE_HEADSET && dvddio) {
1581 if ((dvddio->_vdces > hs_max) ||
1582 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1583 pr_debug("%s: Headset with threshold on MIC detected\n",
1584 __func__);
1585 if (mbhc->mbhc_cfg->micbias_enable_flags &
1586 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1587 mbhc->micbias_enable = true;
1588 } else {
1589 pr_debug("%s: Headset with regular MIC detected\n",
1590 __func__);
1591 if (mbhc->mbhc_cfg->micbias_enable_flags &
1592 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1593 mbhc->micbias_enable = true;
1594 }
1595 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001596exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001597 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1598 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001599 return type;
1600}
1601
Joonwoo Parkccccba72013-04-26 11:19:46 -07001602/*
1603 * Pull down MBHC micbias for provided duration in microsecond.
1604 */
1605static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1606{
1607 bool micbiasconn = false;
1608 struct snd_soc_codec *codec = mbhc->codec;
1609 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1610
1611 /*
1612 * Disable MBHC to micbias connection to pull down
1613 * micbias and pull down micbias for a moment.
1614 */
1615 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1616 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1617 return -EFAULT;
1618 }
1619
1620 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1621 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1622 1 << 4, 0);
1623 micbiasconn = true;
1624 }
1625
1626 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1627
1628 /*
1629 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1630 * able to get consistent result across DCEs.
1631 */
1632 usleep_range(1000, 1000 + 10);
1633
1634 if (micbiasconn)
1635 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1636 1 << 4, 1 << 4);
1637 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1638 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1639
1640 return 0;
1641}
1642
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001643void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1644 bool highhph)
1645{
1646
1647 struct snd_soc_codec *codec;
1648 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1649 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1650 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1651
1652 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1653 codec = mbhc->codec;
1654
1655 if (on) {
1656 pr_debug("%s: enabling current source\n", __func__);
1657 /* Nsc to 9 */
1658 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1659 0x78, 0x48);
1660 /* pull down diode bit to 0 */
1661 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1662 0x01, 0x00);
1663 /*
1664 * Keep the low power insertion/removal
1665 * detection (reg 0x3DD) disabled
1666 */
1667 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1668 0x01, 0x00);
1669 /*
1670 * Enable the Mic Bias current source
1671 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1672 * Write bit[7] of register MICB_2_MBHC to 1
1673 * (INS_DET_ISRC_EN__ENABLE)
1674 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1675 */
1676 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1677 0xF0, 0xF0);
1678 /* Disconnect MBHC Override from MicBias and LDOH */
1679 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1680 } else {
1681 pr_debug("%s: disabling current source\n", __func__);
1682 /* Connect MBHC Override from MicBias and LDOH */
1683 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1684 /* INS_DET_ISRC_CTL to acdb value */
1685 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1686 0x60, plug_det->mic_current << 5);
1687 if (!highhph) {
1688 /* INS_DET_ISRC_EN__ENABLE to 0 */
1689 snd_soc_update_bits(codec,
1690 mbhc->mbhc_bias_regs.mbhc_reg,
1691 0x80, 0x00);
1692 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1693 snd_soc_update_bits(codec,
1694 mbhc->mbhc_bias_regs.mbhc_reg,
1695 0x10, 0x00);
1696 }
1697 /* Nsc to acdb value */
1698 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1699 btn_det->mbhc_nsc << 3);
1700 }
1701}
1702
1703static enum wcd9xxx_mbhc_plug_type
1704wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1705{
1706 struct snd_soc_codec *codec = mbhc->codec;
1707 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1708 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1709 int i;
1710
1711 pr_debug("%s: enter\n", __func__);
1712 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1713
1714 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1715
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301716 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001717 rt[0].swap_gnd = false;
1718 rt[0].vddio = false;
1719 rt[0].hwvalue = true;
1720 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1721 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1722 rt[0].mic_bias = false;
1723
1724 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1725 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1726 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1727 highhph);
1728 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1729 if (rt[i].swap_gnd)
1730 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1731
1732 if (rt[i].mic_bias)
1733 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1734
1735 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1736 if (rt[i].mic_bias)
1737 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1738 if (rt[i].swap_gnd)
1739 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1740 }
1741
1742 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1743 mbhc->event_state);
1744
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301745 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001746 pr_debug("%s: plug_type:%d\n", __func__, type);
1747
1748 return type;
1749}
1750
Joonwoo Parka8890262012-10-15 12:04:27 -07001751static enum wcd9xxx_mbhc_plug_type
1752wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1753{
1754 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001755 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001756 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001757 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1758 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001759 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001760
Joonwoo Park80a01172012-10-15 16:05:23 -07001761 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001762 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1763
1764 /* make sure override is on */
1765 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1766
1767 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001768 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001769
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001770 /*
1771 * There are chances vddio switch is on and cfilt voltage is adjusted
1772 * to vddio voltage even after plug type removal reported.
1773 */
1774 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1775 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1776
Joonwoo Parka8890262012-10-15 12:04:27 -07001777 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001778 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001779
Joonwoo Parkccccba72013-04-26 11:19:46 -07001780 /*
1781 * cfilter in fast mode requires 1ms to charge up and down micbias
1782 * fully.
1783 */
1784 (void) wcd9xxx_pull_down_micbias(mbhc,
1785 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301786
1787 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001788 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001789 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001790 rt[0].swap_gnd = false;
1791 rt[0].vddio = false;
1792 rt[0].hwvalue = true;
1793 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1794 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1795 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001796 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001797 else
1798 rt[i].vddio = false;
1799 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1800 rt[i].hwvalue = false;
1801 if (rt[i].swap_gnd)
1802 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1803 if (rt[i].vddio)
1804 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001805 /*
1806 * Pull down micbias to detect headset with mic which has
1807 * threshold and to have more consistent voltage measurements.
1808 *
1809 * cfilter in fast mode requires 1ms to charge up and down
1810 * micbias fully.
1811 */
1812 (void) wcd9xxx_pull_down_micbias(mbhc,
1813 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001814 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1815 if (rt[i].vddio)
1816 wcd9xxx_onoff_vddio_switch(mbhc, false);
1817 if (rt[i].swap_gnd)
1818 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001819 }
1820
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001821 if (vddioon)
1822 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1823
1824 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1825 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001826
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301827 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001828 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001829 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001830}
1831
1832static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1833{
1834 if (mbhc->mbhc_cfg->gpio)
1835 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1836 mbhc->mbhc_cfg->gpio_level_insert);
1837 else if (mbhc->mbhc_cfg->insert_detect)
1838 return snd_soc_read(mbhc->codec,
1839 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1840 (1 << 2);
1841 else
1842 WARN(1, "Invalid jack detection configuration\n");
1843
1844 return true;
1845}
1846
1847static bool is_clk_active(struct snd_soc_codec *codec)
1848{
1849 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1850}
1851
1852static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1853 int insertion, int trigger, bool padac_off)
1854{
1855 struct snd_soc_codec *codec = mbhc->codec;
1856 int central_bias_enabled = 0;
1857 const struct wcd9xxx_mbhc_general_cfg *generic =
1858 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1859 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1860 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1861
Joonwoo Park80a01172012-10-15 16:05:23 -07001862 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1863 __func__, insertion, trigger);
1864
Joonwoo Parka8890262012-10-15 12:04:27 -07001865 if (!mbhc->mbhc_cfg->calibration) {
1866 pr_err("Error, no wcd9xxx calibration\n");
1867 return -EINVAL;
1868 }
1869
1870 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1871
1872 /*
1873 * Make sure mic bias and Mic line schmitt trigger
1874 * are turned OFF
1875 */
1876 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1877 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1878
1879 if (insertion) {
1880 wcd9xxx_switch_micbias(mbhc, 0);
1881
1882 /* DAPM can manipulate PA/DAC bits concurrently */
1883 if (padac_off == true)
1884 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1885
1886 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1887 /* Enable HPH Schmitt Trigger */
1888 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1889 0x11);
1890 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1891 plug_det->hph_current << 2);
1892 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1893 0x02);
1894 }
1895 if (trigger & MBHC_USE_MB_TRIGGER) {
1896 /* enable the mic line schmitt trigger */
1897 snd_soc_update_bits(codec,
1898 mbhc->mbhc_bias_regs.mbhc_reg,
1899 0x60, plug_det->mic_current << 5);
1900 snd_soc_update_bits(codec,
1901 mbhc->mbhc_bias_regs.mbhc_reg,
1902 0x80, 0x80);
1903 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1904 snd_soc_update_bits(codec,
1905 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1906 0x00);
1907 snd_soc_update_bits(codec,
1908 mbhc->mbhc_bias_regs.mbhc_reg,
1909 0x10, 0x10);
1910 }
1911
1912 /* setup for insetion detection */
1913 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1914 } else {
1915 pr_debug("setup for removal detection\n");
1916 /* Make sure the HPH schmitt trigger is OFF */
1917 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1918
1919 /* enable the mic line schmitt trigger */
1920 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1921 0x01, 0x00);
1922 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1923 plug_det->mic_current << 5);
1924 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1925 0x80, 0x80);
1926 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1927 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1928 0x10, 0x10);
1929
1930 /* Setup for low power removal detection */
1931 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1932 0x2);
1933 }
1934
1935 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1936 /* called by interrupt */
1937 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001938 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001939 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1940 0x06, 0);
1941 usleep_range(generic->t_shutdown_plug_rem,
1942 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001943 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001944 } else
1945 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1946 0x06, 0);
1947 }
1948
1949 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1950
1951 /* If central bandgap disabled */
1952 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1953 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1954 usleep_range(generic->t_bg_fast_settle,
1955 generic->t_bg_fast_settle);
1956 central_bias_enabled = 1;
1957 }
1958
1959 /* If LDO_H disabled */
1960 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1961 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1962 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1963 usleep_range(generic->t_ldoh, generic->t_ldoh);
1964 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1965
1966 if (central_bias_enabled)
1967 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1968 0);
1969 }
1970
Meng Wangeeaaaba2013-09-09 18:37:32 +08001971 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301972 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1973 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001974
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001975 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001976 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001977 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001978
1979 return 0;
1980}
1981
1982/* called under codec_resource_lock acquisition */
1983static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1984 enum wcd9xxx_mbhc_plug_type plug_type)
1985{
Joonwoo Park80a01172012-10-15 16:05:23 -07001986 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1987 __func__, mbhc->current_plug, plug_type);
1988
Joonwoo Parka8890262012-10-15 12:04:27 -07001989 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1990
1991 if (plug_type == PLUG_TYPE_HEADPHONE &&
1992 mbhc->current_plug == PLUG_TYPE_NONE) {
1993 /*
1994 * Nothing was reported previously
1995 * report a headphone or unsupported
1996 */
1997 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1998 wcd9xxx_cleanup_hs_polling(mbhc);
1999 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002000 if (!mbhc->mbhc_cfg->detect_extn_cable) {
2001 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
2002 wcd9xxx_report_plug(mbhc, 0,
2003 SND_JACK_HEADSET);
2004 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
2005 wcd9xxx_report_plug(mbhc, 0,
2006 SND_JACK_HEADPHONE);
2007 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002008 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
2009 wcd9xxx_cleanup_hs_polling(mbhc);
2010 } else if (plug_type == PLUG_TYPE_HEADSET) {
2011 /*
2012 * If Headphone was reported previously, this will
2013 * only report the mic line
2014 */
2015 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302016 /* Button detection required RC oscillator */
2017 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002018 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002019
2020 /* if PA is already on, switch micbias source to VDDIO */
2021 if (mbhc->event_state &
2022 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2023 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002024 wcd9xxx_start_hs_polling(mbhc);
2025 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002026 if (mbhc->mbhc_cfg->detect_extn_cable) {
2027 /* High impedance device found. Report as LINEOUT*/
2028 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2029 wcd9xxx_cleanup_hs_polling(mbhc);
2030 pr_debug("%s: setup mic trigger for further detection\n",
2031 __func__);
2032 mbhc->lpi_enabled = true;
2033 /*
2034 * Do not enable HPHL trigger. If playback is active,
2035 * it might lead to continuous false HPHL triggers
2036 */
2037 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2038 false);
2039 } else {
2040 if (mbhc->current_plug == PLUG_TYPE_NONE)
2041 wcd9xxx_report_plug(mbhc, 1,
2042 SND_JACK_HEADPHONE);
2043 wcd9xxx_cleanup_hs_polling(mbhc);
2044 pr_debug("setup mic trigger for further detection\n");
2045 mbhc->lpi_enabled = true;
2046 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2047 MBHC_USE_HPHL_TRIGGER,
2048 false);
2049 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002050 } else {
2051 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2052 mbhc->current_plug, plug_type);
2053 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002054 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002055}
2056
2057/* called under codec_resource_lock acquisition */
2058static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2059{
2060 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002061 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002062
2063 pr_debug("%s: enter\n", __func__);
2064
2065 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002066 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2067 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002068
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002069 if (current_source_enable) {
2070 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2071 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2072 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2073 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002074 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002075 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002076 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002077 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002078
2079 if (wcd9xxx_swch_level_remove(mbhc)) {
2080 pr_debug("%s: Switch level is low when determining plug\n",
2081 __func__);
2082 return;
2083 }
2084
2085 if (plug_type == PLUG_TYPE_INVALID ||
2086 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002087 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002088 wcd9xxx_schedule_hs_detect_plug(mbhc,
2089 &mbhc->correct_plug_swch);
2090 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2091 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002092 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002093 wcd9xxx_schedule_hs_detect_plug(mbhc,
2094 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002095 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002096 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002097 wcd9xxx_schedule_hs_detect_plug(mbhc,
2098 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002099 } else {
2100 pr_debug("%s: Valid plug found, determine plug type %d\n",
2101 __func__, plug_type);
2102 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2103 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002104 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002105}
2106
2107/* called under codec_resource_lock acquisition */
2108static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2109{
Joonwoo Park80a01172012-10-15 16:05:23 -07002110 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002111 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2112
Joonwoo Parka8890262012-10-15 12:04:27 -07002113 if (wcd9xxx_swch_level_remove(mbhc))
2114 pr_debug("%s: Switch level low when determining plug\n",
2115 __func__);
2116 else
2117 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002118 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002119}
2120
2121/* called only from interrupt which is under codec_resource_lock acquisition */
2122static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2123 bool is_removal)
2124{
2125 if (!is_removal) {
2126 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2127
2128 rmb();
2129 if (mbhc->lpi_enabled)
2130 msleep(100);
2131
2132 rmb();
2133 if (!mbhc->lpi_enabled) {
2134 pr_debug("%s: lpi is disabled\n", __func__);
2135 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2136 pr_debug("%s: Valid insertion, detect plug type\n",
2137 __func__);
2138 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2139 } else {
2140 pr_debug("%s: Invalid insertion stop plug detection\n",
2141 __func__);
2142 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002143 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2144 pr_debug("%s: Removal\n", __func__);
2145 if (!wcd9xxx_swch_level_remove(mbhc)) {
2146 /*
2147 * Switch indicates, something is still inserted.
2148 * This could be extension cable i.e. headset is
2149 * removed from extension cable.
2150 */
2151 /* cancel detect plug */
2152 wcd9xxx_cancel_hs_detect_plug(mbhc,
2153 &mbhc->correct_plug_swch);
2154 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2155 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002156 } else {
2157 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2158 }
2159}
2160
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002161static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2162 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002163{
2164 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2165 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2166 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2167
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002168 if (cs_enable)
2169 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2170 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2171 else
2172 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2173 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2174 (mic_mv > plug_type->v_no_mic) &&
2175 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002176}
2177
2178/*
2179 * called under codec_resource_lock acquisition
2180 * returns true if mic voltage range is back to normal insertion
2181 * returns false either if timedout or removed
2182 */
2183static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2184{
2185 int i;
2186 bool timedout, settled = false;
2187 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2188 short mb_v[NUM_DCE_PLUG_DETECT];
2189 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002190 bool cs_enable;
2191
2192 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2193 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2194
2195 if (cs_enable)
2196 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002197
2198 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2199 while (!(timedout = time_after(jiffies, timeout))) {
2200 retry++;
2201 if (wcd9xxx_swch_level_remove(mbhc)) {
2202 pr_debug("%s: Switch indicates removal\n", __func__);
2203 break;
2204 }
2205
2206 if (retry > 1)
2207 msleep(250);
2208 else
2209 msleep(50);
2210
2211 if (wcd9xxx_swch_level_remove(mbhc)) {
2212 pr_debug("%s: Switch indicates removal\n", __func__);
2213 break;
2214 }
2215
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002216 if (cs_enable) {
2217 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2218 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2219 true, true);
2220 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2221 true,
2222 mb_v[i],
2223 mbhc->mbhc_data.dce_nsc_cs_z,
2224 (u32)VDDIO_MICBIAS_MV);
2225 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2226 __func__, retry, mic_mv[i], mb_v[i]);
2227 }
2228 } else {
2229 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2230 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2231 true);
2232 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2233 mb_v[i]);
2234 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2235 __func__, retry, mic_mv[i],
2236 mb_v[i]);
2237 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002238 }
2239
2240 if (wcd9xxx_swch_level_remove(mbhc)) {
2241 pr_debug("%s: Switcn indicates removal\n", __func__);
2242 break;
2243 }
2244
2245 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2246 pr_debug("%s : headset/headphone is removed\n",
2247 __func__);
2248 break;
2249 }
2250
2251 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002252 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002253 break;
2254
2255 if (i == NUM_DCE_PLUG_DETECT) {
2256 pr_debug("%s: MIC voltage settled\n", __func__);
2257 settled = true;
2258 msleep(200);
2259 break;
2260 }
2261 }
2262
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002263 if (cs_enable)
2264 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2265
Joonwoo Parka8890262012-10-15 12:04:27 -07002266 if (timedout)
2267 pr_debug("%s: Microphone did not settle in %d seconds\n",
2268 __func__, HS_DETECT_PLUG_TIME_MS);
2269 return settled;
2270}
2271
2272/* called only from interrupt which is under codec_resource_lock acquisition */
2273static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2274{
Joonwoo Park80a01172012-10-15 16:05:23 -07002275 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002276 if (wcd9xxx_hs_remove_settle(mbhc))
2277 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002278 pr_debug("%s: leave\n", __func__);
2279}
2280
2281/* called only from interrupt which is under codec_resource_lock acquisition */
2282static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2283{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002284 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002285 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002286 bool removed = true;
2287 struct snd_soc_codec *codec = mbhc->codec;
2288 const struct wcd9xxx_mbhc_general_cfg *generic =
2289 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002290 bool cs_enable;
2291 s16 cur_v_ins_h;
2292 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002293
2294 pr_debug("%s: enter\n", __func__);
2295 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2296 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2297 __func__);
2298 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2299 0x08, 0x08);
2300 return;
2301 }
2302
2303 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002304 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002305
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002306 /* If micbias is enabled, don't enable current source */
2307 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2308 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2309 (!(snd_soc_read(codec,
2310 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002311 if (cs_enable)
2312 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2313
Joonwoo Park50ae0512013-06-04 16:53:12 -07002314 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002315 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002316 if (cs_enable) {
2317 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2318 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2319 mb_mv = VDDIO_MICBIAS_MV;
2320 } else {
2321 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2322 dcez = mbhc->mbhc_data.dce_z;
2323 mb_mv = mbhc->mbhc_data.micb_mv;
2324 }
2325
Joonwoo Park50ae0512013-06-04 16:53:12 -07002326 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002327 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2328 dcez, mb_mv));
2329
2330 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2331 (wcd9xxx_get_current_v(mbhc,
2332 WCD9XXX_CURRENT_V_INS_H));
2333
2334 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002335 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002336 break;
2337 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002338 } while (!time_after(jiffies, timeout));
2339 pr_debug("%s: headset %sactually removed\n", __func__,
2340 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002341
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002342 if (cs_enable)
2343 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2344
Joonwoo Park80a01172012-10-15 16:05:23 -07002345 if (removed) {
2346 if (mbhc->mbhc_cfg->detect_extn_cable) {
2347 if (!wcd9xxx_swch_level_remove(mbhc)) {
2348 /*
2349 * extension cable is still plugged in
2350 * report it as LINEOUT device
2351 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302352 if (mbhc->hph_status == SND_JACK_HEADSET)
2353 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2354 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002355 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2356 wcd9xxx_cleanup_hs_polling(mbhc);
2357 wcd9xxx_enable_hs_detect(mbhc, 1,
2358 MBHC_USE_MB_TRIGGER,
2359 false);
2360 }
2361 } else {
2362 /* Cancel possibly running hs_detect_work */
2363 wcd9xxx_cancel_hs_detect_plug(mbhc,
2364 &mbhc->correct_plug_noswch);
2365 /*
2366 * If this removal is not false, first check the micbias
2367 * switch status and switch it to LDOH if it is already
2368 * switched to VDDIO.
2369 */
2370 wcd9xxx_switch_micbias(mbhc, 0);
2371
2372 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302373 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002374 wcd9xxx_cleanup_hs_polling(mbhc);
2375 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2376 MBHC_USE_HPHL_TRIGGER,
2377 true);
2378 }
2379 } else {
2380 wcd9xxx_start_hs_polling(mbhc);
2381 }
2382 pr_debug("%s: leave\n", __func__);
2383}
2384
2385/* called only from interrupt which is under codec_resource_lock acquisition */
2386static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2387 bool is_mb_trigger)
2388{
2389 /* Cancel possibly running hs_detect_work */
2390 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2391
2392 if (is_mb_trigger) {
2393 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2394 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2395 } else {
2396 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2397 __func__);
2398 wcd9xxx_mbhc_detect_plug_type(mbhc);
2399 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002400}
2401
2402static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2403{
Joonwoo Parka8890262012-10-15 12:04:27 -07002404 struct wcd9xxx_mbhc *mbhc = data;
2405
2406 pr_debug("%s: enter, removal interrupt\n", __func__);
2407 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002408 /*
2409 * While we don't know whether MIC is there or not, let the resmgr know
2410 * so micbias can be disabled temporarily
2411 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002412 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002413 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2414 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002415 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2416 WCD9XXX_COND_HPH, false);
2417 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2418 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2419 WCD9XXX_COND_HPH, false);
2420 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002421
Joonwoo Park80a01172012-10-15 16:05:23 -07002422 if (mbhc->mbhc_cfg->detect_extn_cable &&
2423 !wcd9xxx_swch_level_remove(mbhc))
2424 wcd9xxx_hs_remove_irq_noswch(mbhc);
2425 else
2426 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002427
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002428 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2429 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2430 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002431 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2432 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002433 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2434 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2435 WCD9XXX_COND_HPH, true);
2436 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002437 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2438
2439 return IRQ_HANDLED;
2440}
2441
2442static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2443{
2444 bool is_mb_trigger, is_removal;
2445 struct wcd9xxx_mbhc *mbhc = data;
2446 struct snd_soc_codec *codec = mbhc->codec;
2447
2448 pr_debug("%s: enter\n", __func__);
2449 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002450 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002451
2452 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2453 0x10);
2454 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2455 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2456
2457 /* Turn off both HPH and MIC line schmitt triggers */
2458 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2459 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2460 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2461
Joonwoo Park80a01172012-10-15 16:05:23 -07002462 if (mbhc->mbhc_cfg->detect_extn_cable &&
2463 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2464 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2465 else
2466 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002467
2468 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2469 return IRQ_HANDLED;
2470}
2471
2472static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2473{
2474 struct delayed_work *dwork;
2475 short bias_value;
2476 int dce_mv, sta_mv;
2477 struct wcd9xxx_mbhc *mbhc;
2478
2479 pr_debug("%s:\n", __func__);
2480
2481 dwork = to_delayed_work(work);
2482 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2483
2484 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2485 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2486
2487 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2488 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2489 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2490
2491 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002492 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002493 mbhc->buttons_pressed);
2494
2495 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002496 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002497}
2498
2499static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2500{
2501 struct delayed_work *dwork;
2502 struct wcd9xxx_mbhc *mbhc;
2503 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002504 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002505
2506 dwork = to_delayed_work(work);
2507 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2508 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002509 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002510
2511 pr_debug("%s:\n", __func__);
2512
2513 /* Turn off both HPH and MIC line schmitt triggers */
2514 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2515 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2516 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002517 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002518 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002519 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002520}
2521
2522static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2523{
2524 u32 cfg_offset;
2525 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2526 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2527
2528 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2529 return false;
2530
2531 /*
2532 * Previous check guarantees that there is enough fw data up
2533 * to num_btn
2534 */
2535 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2536 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2537 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2538 return false;
2539
2540 /*
2541 * Previous check guarantees that there is enough fw data up
2542 * to start of impedance detection configuration
2543 */
2544 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2545 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2546
2547 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2548 return false;
2549
2550 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2551 return false;
2552
2553 return true;
2554}
2555
2556static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002557 enum meas_type dce, s16 vin_mv,
2558 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002559{
2560 s16 diff, zero;
2561 u32 mb_mv, in;
2562 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002563 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002564
2565 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002566 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002567
2568 if (mb_mv == 0) {
2569 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2570 return -EINVAL;
2571 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002572 if (cs_enable) {
2573 mb_mv = VDDIO_MICBIAS_MV;
2574 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2575 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002576
2577 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002578 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2579 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002580 } else {
2581 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2582 zero = (mbhc->mbhc_data.sta_z);
2583 }
2584 in = (u32) diff * vin_mv;
2585
2586 value = (u16) (in / mb_mv) + zero;
2587 return value;
2588}
2589
2590static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2591{
2592 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002593 s16 adj_v_hs_max;
2594 s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM];
Joonwoo Parka8890262012-10-15 12:04:27 -07002595 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2596 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2597 u16 *btn_high;
2598 int i;
2599
2600 pr_debug("%s: enter\n", __func__);
2601 codec = mbhc->codec;
2602 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2603 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2604
Joonwoo Park73375212013-05-07 12:42:44 -07002605 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002606 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002607 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002608 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002609
2610 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2611 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2612
2613 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002614 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2615 true);
2616 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002617 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002618 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002619 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002620 mbhc->mbhc_data.v_inval_ins_low =
2621 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2622 false);
2623 mbhc->mbhc_data.v_inval_ins_high =
2624 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2625 false);
2626 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002627 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2628 WCD9XXX_V_CS_HS_MAX,
2629 true);
2630 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2631 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002632
2633 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2634 MBHC_BTN_DET_V_BTN_HIGH);
2635 for (i = 0; i < btn_det->num_btn; i++)
2636 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2637
Joonwoo Park73375212013-05-07 12:42:44 -07002638 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2639 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2640 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2641 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2642 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2643 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002644
Joonwoo Park73375212013-05-07 12:42:44 -07002645 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002646 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2647 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002648 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002649 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2650 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002651 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002652 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2653 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002654 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002655 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2656 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002657
Joonwoo Park73375212013-05-07 12:42:44 -07002658 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2659 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2660 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2661 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002662
Joonwoo Parka8890262012-10-15 12:04:27 -07002663 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2664
2665 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002666 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002667 pr_debug("%s: leave\n", __func__);
2668}
2669
2670static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2671{
2672 /*
2673 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2674 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2675 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2676 */
2677 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2678}
2679
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002680/*
2681 * Mic Bias Enable Decision
2682 * Return true if high_hph_cnt is a power of 2 (!= 2)
2683 * otherwise return false
2684 */
2685static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2686{
2687 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2688}
2689
Joonwoo Parka8890262012-10-15 12:04:27 -07002690static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2691{
2692 struct wcd9xxx_mbhc *mbhc;
2693 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002694 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002695 unsigned long timeout;
2696 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002697 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002698 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002699 bool current_source_enable;
2700 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002701
2702 pr_debug("%s: enter\n", __func__);
2703
2704 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2705 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002706 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2707 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002708
2709 wcd9xxx_onoff_ext_mclk(mbhc, true);
2710
2711 /*
2712 * Keep override on during entire plug type correction work.
2713 *
2714 * This is okay under the assumption that any switch irqs which use
2715 * MBHC block cancel and sync this work so override is off again
2716 * prior to switch interrupt handler's MBHC block usage.
2717 * Also while this correction work is running, we can guarantee
2718 * DAPM doesn't use any MBHC block as this work only runs with
2719 * headphone detection.
2720 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002721 if (current_source_enable)
2722 wcd9xxx_turn_onoff_current_source(mbhc, true,
2723 false);
2724 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002725 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002726
2727 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2728 while (!time_after(jiffies, timeout)) {
2729 ++retry;
2730 rmb();
2731 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002732 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002733 pr_debug("%s: stop requested\n", __func__);
2734 break;
2735 }
2736
2737 msleep(HS_DETECT_PLUG_INERVAL_MS);
2738 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002739 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002740 pr_debug("%s: Switch level is low\n", __func__);
2741 break;
2742 }
2743
2744 /* can race with removal interrupt */
2745 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002746 if (current_source_enable)
2747 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2748 highhph);
2749 else
2750 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002751 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2752
Joonwoo Park80a01172012-10-15 16:05:23 -07002753 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2754 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002755
2756 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2757 (highhph_cnt + 1) :
2758 0;
2759 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002760 if (plug_type == PLUG_TYPE_INVALID) {
2761 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002762 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2763 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002764 mbhc->current_plug == PLUG_TYPE_NONE) {
2765 wcd9xxx_report_plug(mbhc, 1,
2766 SND_JACK_HEADPHONE);
2767 }
2768 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2769 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002770 if (mbhc->mbhc_cfg->detect_extn_cable) {
2771 if (mbhc->current_plug != plug_type)
2772 wcd9xxx_report_plug(mbhc, 1,
2773 SND_JACK_HEADPHONE);
2774 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002775 wcd9xxx_report_plug(mbhc, 1,
2776 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002777 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002778 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2779 pr_debug("%s: High HPH detected, continue polling\n",
2780 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002781 } else {
2782 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2783 pt_gnd_mic_swap_cnt++;
2784 if (pt_gnd_mic_swap_cnt <
2785 GND_MIC_SWAP_THRESHOLD)
2786 continue;
2787 else if (pt_gnd_mic_swap_cnt >
2788 GND_MIC_SWAP_THRESHOLD) {
2789 /*
2790 * This is due to GND/MIC switch didn't
2791 * work, Report unsupported plug
2792 */
2793 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2794 /*
2795 * if switch is toggled, check again,
2796 * otherwise report unsupported plug
2797 */
2798 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2799 continue;
2800 }
2801 } else
2802 pt_gnd_mic_swap_cnt = 0;
2803
2804 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002805 /* Turn off override/current source */
2806 if (current_source_enable)
2807 wcd9xxx_turn_onoff_current_source(mbhc, false,
2808 false);
2809 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002810 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002811 /*
2812 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2813 */
2814 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2815 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2816 pr_debug("Attempt %d found correct plug %d\n", retry,
2817 plug_type);
2818 correction = true;
2819 break;
2820 }
2821 }
2822
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002823 highhph = false;
2824 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002825 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2826 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002827 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002828 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002829 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002830 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002831 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002832
2833 if (!correction && current_source_enable)
2834 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2835 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002836 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002837
2838 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002839
2840 if (mbhc->mbhc_cfg->detect_extn_cable) {
2841 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002842 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2843 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002844 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2845 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002846 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002847 /* Enable removal detection */
2848 wcd9xxx_cleanup_hs_polling(mbhc);
2849 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2850 }
2851 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2852 }
2853 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002854 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002855 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002856}
2857
2858static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2859{
2860 bool insert;
2861 bool is_removed = false;
2862 struct snd_soc_codec *codec = mbhc->codec;
2863
2864 pr_debug("%s: enter\n", __func__);
2865
2866 mbhc->in_swch_irq_handler = true;
2867 /* Wait here for debounce time */
2868 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2869
2870 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2871
2872 /* cancel pending button press */
2873 if (wcd9xxx_cancel_btn_work(mbhc))
2874 pr_debug("%s: button press is canceled\n", __func__);
2875
Santosh Mardic82bd362013-10-25 18:35:29 +05302876 /* cancel detect plug */
2877 wcd9xxx_cancel_hs_detect_plug(mbhc,
2878 &mbhc->correct_plug_swch);
2879
Joonwoo Parka8890262012-10-15 12:04:27 -07002880 insert = !wcd9xxx_swch_level_remove(mbhc);
2881 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2882 mbhc->current_plug, insert);
2883 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2884 mbhc->lpi_enabled = false;
2885 wmb();
2886
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002887 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2888 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2889 (1 << 1)))
2890 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002891
2892 /* Disable Mic Bias pull down and HPH Switch to GND */
2893 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2894 0x00);
2895 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2896 wcd9xxx_mbhc_detect_plug_type(mbhc);
2897 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2898 mbhc->lpi_enabled = false;
2899 wmb();
2900
Joonwoo Parka8890262012-10-15 12:04:27 -07002901 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2902 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2903 is_removed = true;
2904 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2905 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2906 is_removed = true;
2907 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2908 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302909 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002910 wcd9xxx_cleanup_hs_polling(mbhc);
2911 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2912 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002913 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2914 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2915 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002916 }
2917
2918 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002919 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2920 0x00);
2921 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2922 0x02, 0x00);
2923
Joonwoo Parka8890262012-10-15 12:04:27 -07002924 /* Enable Mic Bias pull down and HPH Switch to GND */
2925 snd_soc_update_bits(codec,
2926 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2927 0x01);
2928 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2929 0x01);
2930 /* Make sure mic trigger is turned off */
2931 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2932 0x01, 0x01);
2933 snd_soc_update_bits(codec,
2934 mbhc->mbhc_bias_regs.mbhc_reg,
2935 0x90, 0x00);
2936 /* Reset MBHC State Machine */
2937 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2938 0x08, 0x08);
2939 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2940 0x08, 0x00);
2941 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002942 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002943 }
2944 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002945exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002946 mbhc->in_swch_irq_handler = false;
2947 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2948 pr_debug("%s: leave\n", __func__);
2949}
2950
2951static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2952{
2953 int r = IRQ_HANDLED;
2954 struct wcd9xxx_mbhc *mbhc = data;
2955
2956 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002957 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002958 pr_warn("%s: failed to hold suspend\n", __func__);
2959 r = IRQ_NONE;
2960 } else {
2961 /* Call handler */
2962 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002963 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002964 }
2965
2966 pr_debug("%s: leave %d\n", __func__, r);
2967 return r;
2968}
2969
Joonwoo Park218e73f2013-08-21 16:22:18 -07002970static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002971{
Joonwoo Park73375212013-05-07 12:42:44 -07002972 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002973 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002974 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002975 const s16 v_ins_hu =
2976 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2977 const s16 v_ins_h =
2978 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2979 const s16 v_b1_hu =
2980 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2981 const s16 v_b1_h =
2982 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002983 const unsigned long timeout =
2984 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002985
Joonwoo Park218e73f2013-08-21 16:22:18 -07002986 while (time_before(jiffies, timeout)) {
2987 /*
2988 * This function needs to run measurements just few times during
2989 * release debounce time. Make 1ms interval to avoid
2990 * unnecessary excessive measurements.
2991 */
2992 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002993 if (i == 0) {
2994 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2995 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2996 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002997 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002998 r = 1;
2999 break;
3000 }
3001 } else {
3002 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
3003 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
3004 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003005 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003006 r = 1;
3007 break;
3008 }
3009 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003010 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003011 }
3012
3013 return r;
3014}
3015
3016/* called under codec_resource_lock acquisition */
3017static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3018 const s32 micmv)
3019{
3020 s16 *v_btn_low, *v_btn_high;
3021 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3022 int i, btn = -1;
3023
3024 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3025 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3026 MBHC_BTN_DET_V_BTN_LOW);
3027 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3028 MBHC_BTN_DET_V_BTN_HIGH);
3029
3030 for (i = 0; i < btn_det->num_btn; i++) {
3031 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3032 btn = i;
3033 break;
3034 }
3035 }
3036
3037 if (btn == -1)
3038 pr_debug("%s: couldn't find button number for mic mv %d\n",
3039 __func__, micmv);
3040
3041 return btn;
3042}
3043
3044static int wcd9xxx_get_button_mask(const int btn)
3045{
3046 int mask = 0;
3047 switch (btn) {
3048 case 0:
3049 mask = SND_JACK_BTN_0;
3050 break;
3051 case 1:
3052 mask = SND_JACK_BTN_1;
3053 break;
3054 case 2:
3055 mask = SND_JACK_BTN_2;
3056 break;
3057 case 3:
3058 mask = SND_JACK_BTN_3;
3059 break;
3060 case 4:
3061 mask = SND_JACK_BTN_4;
3062 break;
3063 case 5:
3064 mask = SND_JACK_BTN_5;
3065 break;
3066 case 6:
3067 mask = SND_JACK_BTN_6;
3068 break;
3069 case 7:
3070 mask = SND_JACK_BTN_7;
3071 break;
3072 }
3073 return mask;
3074}
3075
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003076static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003077{
3078 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003079 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003080 struct snd_soc_codec *codec = mbhc->codec;
3081
3082 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3083 /* Pull down micbias to ground and disconnect vddio switch */
3084 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3085 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3086 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3087 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3088
3089 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003090 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3091 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003092 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003093 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003094 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003095 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3096 }
3097 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003098 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003099 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3100 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003101
Joonwoo Park520a0f92013-05-14 19:39:58 -07003102 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003103 if (change)
3104 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3105 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003106 /* Disable pull down micbias to ground */
3107 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3108 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3109}
3110
Joonwoo Park218e73f2013-08-21 16:22:18 -07003111void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3112{
3113 const u16 sta_z = mbhc->mbhc_data.sta_z;
3114 const u16 dce_z = mbhc->mbhc_data.dce_z;
3115
3116 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3117 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3118 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3119 mbhc->mbhc_data.sta_z & 0xFFFF,
3120 mbhc->mbhc_data.dce_z & 0xFFFF);
3121
3122 wcd9xxx_mbhc_calc_thres(mbhc);
3123 wcd9xxx_calibrate_hs_polling(mbhc);
3124}
3125
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003126/*
3127 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3128 * to ceilmv + buffer
3129 */
3130static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3131{
3132 u16 v_brh, v_b1_hu;
3133 int mv;
3134 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3135 void *calibration = mbhc->mbhc_cfg->calibration;
3136 struct snd_soc_codec *codec = mbhc->codec;
3137
3138 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3139 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3140 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3141
3142 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003143 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003144 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3145 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3146 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003147 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003148 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3149 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3150 (v_brh >> 8) & 0xFF);
3151 return 0;
3152}
3153
Joonwoo Parka8890262012-10-15 12:04:27 -07003154irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3155{
3156 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003157 bool vddio;
3158 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003159 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003160 s32 stamv, stamv_s;
3161 s16 *v_btn_high;
3162 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003163 int btn = -1, meas = 0;
3164 struct wcd9xxx_mbhc *mbhc = data;
3165 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3166 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3167 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003168 short dce[d->n_btn_meas + 1], sta;
3169 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003170 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003171 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003172 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003173 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003174
3175 pr_debug("%s: enter\n", __func__);
3176
3177 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3178 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3179
3180 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3181 pr_debug("%s: mbhc is being recovered, skip button press\n",
3182 __func__);
3183 goto done;
3184 }
3185
3186 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3187
3188 if (!mbhc->polling_active) {
3189 pr_warn("%s: mbhc polling is not active, skip button press\n",
3190 __func__);
3191 goto done;
3192 }
3193
Joonwoo Parka8890262012-10-15 12:04:27 -07003194 /* If switch nterrupt already kicked in, ignore button press */
3195 if (mbhc->in_swch_irq_handler) {
3196 pr_debug("%s: Swtich level changed, ignore button press\n",
3197 __func__);
3198 btn = -1;
3199 goto done;
3200 }
3201
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303202 /*
3203 * setup internal micbias if codec uses internal micbias for
3204 * headset detection
3205 */
3206 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
3207 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3208 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3209 else
3210 pr_err("%s: internal bias requested but codec did not provide callback\n",
3211 __func__);
3212 }
3213
3214
Joonwoo Parka8890262012-10-15 12:04:27 -07003215 /* Measure scaled HW DCE */
3216 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3217 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003218
Joonwoo Park218e73f2013-08-21 16:22:18 -07003219 dce_z = mbhc->mbhc_data.dce_z;
3220 sta_z = mbhc->mbhc_data.sta_z;
3221
Joonwoo Parka8890262012-10-15 12:04:27 -07003222 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003223 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003224 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003225 if (mbhc_status != STATUS_REL_DETECTION) {
3226 if (mbhc->mbhc_last_resume &&
3227 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3228 pr_debug("%s: Button is released after resume\n",
3229 __func__);
3230 n_btn_meas = 0;
3231 } else {
3232 pr_debug("%s: Button is released without resume",
3233 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003234 if (mbhc->update_z) {
3235 wcd9xxx_update_z(mbhc);
3236 mbhc->update_z = false;
3237 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003238 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3239 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003240 if (vddio)
3241 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3242 false);
3243 else
3244 stamv_s = stamv;
3245 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003246 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003247 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3248 false) : mv[0];
3249 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003250 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3251 btn = -1;
3252 goto done;
3253 }
3254 }
3255
Joonwoo Park520a0f92013-05-14 19:39:58 -07003256 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3257 meas++)
3258 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3259
Joonwoo Park218e73f2013-08-21 16:22:18 -07003260 if (mbhc->update_z) {
3261 wcd9xxx_update_z(mbhc);
3262 mbhc->update_z = false;
3263 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003264
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003265 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3266 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003267 if (vddio)
3268 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3269 else
3270 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003271 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003272 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003273
3274 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003275 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3276 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003277 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3278 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003279 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003280 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003281 if (n_btn_meas == 0)
3282 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003283 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3284 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003285 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3286 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003287 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3288 mv[meas];
3289 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003290 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003291 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3292 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003293 /*
3294 * if large enough measurements are collected,
3295 * start to check if last all n_btn_con measurements were
3296 * in same button low/high range
3297 */
3298 if (meas + 1 >= d->n_btn_con) {
3299 for (i = 0; i < d->n_btn_con; i++)
3300 if ((btnmeas[meas] < 0) ||
3301 (btnmeas[meas] != btnmeas[meas - i]))
3302 break;
3303 if (i == d->n_btn_con) {
3304 /* button pressed */
3305 btn = btnmeas[meas];
3306 break;
3307 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3308 /*
3309 * if left measurements are less than n_btn_con,
3310 * it's impossible to find button number
3311 */
3312 break;
3313 }
3314 }
3315 }
3316
3317 if (btn >= 0) {
3318 if (mbhc->in_swch_irq_handler) {
3319 pr_debug(
3320 "%s: Switch irq triggered, ignore button press\n",
3321 __func__);
3322 goto done;
3323 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003324 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3325 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3326 MBHC_BTN_DET_V_BTN_HIGH);
3327 WARN_ON(btn >= btn_det->num_btn);
3328 /* reprogram release threshold to catch voltage ramp up early */
3329 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3330
Joonwoo Parka8890262012-10-15 12:04:27 -07003331 mask = wcd9xxx_get_button_mask(btn);
3332 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003333 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003334 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3335 msecs_to_jiffies(400)) == 0) {
3336 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003337 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003338 }
3339 } else {
3340 pr_debug("%s: bogus button press, too short press?\n",
3341 __func__);
3342 }
3343
3344 done:
3345 pr_debug("%s: leave\n", __func__);
3346 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3347 return IRQ_HANDLED;
3348}
3349
3350static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3351{
3352 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003353 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003354 struct wcd9xxx_mbhc *mbhc = data;
3355
3356 pr_debug("%s: enter\n", __func__);
3357 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3358 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3359
Joonwoo Parka8890262012-10-15 12:04:27 -07003360 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3361 ret = wcd9xxx_cancel_btn_work(mbhc);
3362 if (ret == 0) {
3363 pr_debug("%s: Reporting long button release event\n",
3364 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003365 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003366 mbhc->buttons_pressed);
3367 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003368 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003369 pr_debug("%s: Fake button press interrupt\n",
3370 __func__);
3371 } else {
3372 if (mbhc->in_swch_irq_handler) {
3373 pr_debug("%s: Switch irq kicked in, ignore\n",
3374 __func__);
3375 } else {
3376 pr_debug("%s: Reporting btn press\n",
3377 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003378 wcd9xxx_jack_report(mbhc,
3379 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003380 mbhc->buttons_pressed,
3381 mbhc->buttons_pressed);
3382 pr_debug("%s: Reporting btn release\n",
3383 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003384 wcd9xxx_jack_report(mbhc,
3385 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003386 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003387 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003388 }
3389 }
3390 }
3391
3392 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3393 }
3394
3395 wcd9xxx_calibrate_hs_polling(mbhc);
3396
Joonwoo Park218e73f2013-08-21 16:22:18 -07003397 if (waitdebounce)
3398 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003399 wcd9xxx_start_hs_polling(mbhc);
3400
3401 pr_debug("%s: leave\n", __func__);
3402 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3403 return IRQ_HANDLED;
3404}
3405
3406static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3407{
3408 struct wcd9xxx_mbhc *mbhc = data;
3409 struct snd_soc_codec *codec;
3410
3411 pr_info("%s: received HPHL OCP irq\n", __func__);
3412
3413 if (mbhc) {
3414 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003415 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3416 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003417 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003418 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003419 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3420 0x10, 0x00);
3421 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3422 0x10, 0x10);
3423 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003424 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003425 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003426 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003427 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003428 mbhc->hph_status,
3429 WCD9XXX_JACK_MASK);
3430 }
3431 } else {
3432 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3433 }
3434
3435 return IRQ_HANDLED;
3436}
3437
3438static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3439{
3440 struct wcd9xxx_mbhc *mbhc = data;
3441 struct snd_soc_codec *codec;
3442
3443 pr_info("%s: received HPHR OCP irq\n", __func__);
3444 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003445 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3446 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003447 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003448 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003449 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3450 0x00);
3451 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3452 0x10);
3453 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003454 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003455 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003456 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003457 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003458 mbhc->hph_status, WCD9XXX_JACK_MASK);
3459 }
3460
3461 return IRQ_HANDLED;
3462}
3463
3464static int wcd9xxx_acdb_mclk_index(const int rate)
3465{
3466 if (rate == MCLK_RATE_12288KHZ)
3467 return 0;
3468 else if (rate == MCLK_RATE_9600KHZ)
3469 return 1;
3470 else {
3471 BUG_ON(1);
3472 return -EINVAL;
3473 }
3474}
3475
3476static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3477{
3478 u32 dce_wait, sta_wait;
3479 u8 ncic, nmeas, navg;
3480 void *calibration;
3481 u8 *n_cic, *n_ready;
3482 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3483 u8 npoll = 4, nbounce_wait = 30;
3484 struct snd_soc_codec *codec = mbhc->codec;
3485 int idx = wcd9xxx_acdb_mclk_index(rate);
3486 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3487
3488 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3489 rate);
3490 calibration = mbhc->mbhc_cfg->calibration;
3491
3492 /*
3493 * First compute the DCE / STA wait times depending on tunable
3494 * parameters. The value is computed in microseconds
3495 */
3496 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3497 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3498 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3499 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3500 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3501
3502 /* ncic stays with the same what we had during calibration */
3503 ncic = n_cic[idxmclk];
3504 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3505 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3506 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003507 /* give extra margin to sta for safety */
3508 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003509 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3510 n_ready[idx]) + 10;
3511
3512 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3513 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3514
3515 if (rate == MCLK_RATE_12288KHZ) {
3516 npoll = 4;
3517 nbounce_wait = 30;
3518 } else if (rate == MCLK_RATE_9600KHZ) {
3519 npoll = 3;
3520 nbounce_wait = 23;
3521 }
3522
3523 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3524 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3525 pr_debug("%s: leave\n", __func__);
3526}
3527
3528static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3529{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003530 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003531 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003532 struct snd_soc_codec *codec = mbhc->codec;
3533
3534 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003535 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3536 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003537 wcd9xxx_turn_onoff_rel_detection(codec, false);
3538
3539 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3540 WARN_ON(!mbhc->mbhc_data.t_dce);
3541 WARN_ON(!mbhc->mbhc_data.t_sta);
3542
3543 /*
3544 * LDOH and CFILT are already configured during pdata handling.
3545 * Only need to make sure CFILT and bandgap are in Fast mode.
3546 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003547 *
3548 * In case when Micbias is powered by external source, request
3549 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003550 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003551 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3552 mbhc->mbhc_cb->enable_mb_source(codec, true);
3553
Joonwoo Parka8890262012-10-15 12:04:27 -07003554 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303555 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3556 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3557 else
3558 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3559 0x40, 0x00);
3560
Joonwoo Parka8890262012-10-15 12:04:27 -07003561 /*
3562 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3563 * to perform ADC calibration
3564 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303565 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3566 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3567 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003568 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3569 mbhc->mbhc_cfg->micbias << 5);
3570 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3571 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3572 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303573 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3574 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3575 else
3576 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3577 0x04, 0x04);
3578
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003579 /* Pull down micbias to ground */
3580 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3581 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3582 /* Disconnect override from micbias */
3583 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3584 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3585 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303586 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303587 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3588 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3589 else
3590 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3591 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003592 /*
3593 * Hardware that has external cap can delay mic bias ramping down up
3594 * to 50ms.
3595 */
3596 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003597 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003598 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003599 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003600 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003601
3602 /* compute dce_z for current source */
3603 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3604 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3605 WCD9XXX_MBHC_NSC_CS << 3);
3606
3607 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3608 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3609 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3610 false);
3611 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3612 mbhc->mbhc_data.dce_nsc_cs_z);
3613
3614 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3615
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003616 /* STA measurement for 0 voltage */
3617 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3618 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3619 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003620
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003621 /* Restore registers */
3622 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3623 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003624
3625 /* DCE measurment for MB voltage */
3626 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3627 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303628 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303629 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3630 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3631 else
3632 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3633 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003634 /*
3635 * Hardware that has external cap can delay mic bias ramping down up
3636 * to 50ms.
3637 */
3638 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003639 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3640 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3641 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3642
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003643 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003644 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3645 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3646 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303647 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303648 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3649 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3650 else
3651 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3652 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003653 /*
3654 * Hardware that has external cap can delay mic bias ramping down up
3655 * to 50ms.
3656 */
3657 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003658 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3659 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3660 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3661
3662 /* Restore default settings. */
3663 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303664 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303665 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303666 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3667 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3668 else
3669 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3670 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003671 usleep_range(100, 100);
3672
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003673 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3674 mbhc->mbhc_cb->enable_mb_source(codec, false);
3675
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003676 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3677 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003678 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003679
Joonwoo Parka8890262012-10-15 12:04:27 -07003680 pr_debug("%s: leave\n", __func__);
3681}
3682
3683static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3684{
3685 int n;
3686 u8 *gain;
3687 struct wcd9xxx_mbhc_general_cfg *generic;
3688 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3689 struct snd_soc_codec *codec = mbhc->codec;
3690 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3691
3692 pr_debug("%s: enter\n", __func__);
3693 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3694 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3695
3696 for (n = 0; n < 8; n++) {
3697 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3698 0x07, n);
3699 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3700 btn_det->c[n]);
3701 }
3702
3703 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3704 btn_det->nc);
3705
3706 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3707 generic->mbhc_nsa << 4);
3708
3709 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3710 btn_det->n_meas);
3711
3712 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3713 generic->mbhc_navg);
3714
3715 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3716
3717 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3718 btn_det->mbhc_nsc << 3);
3719
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003720 if (mbhc->mbhc_cb &&
3721 mbhc->mbhc_cb->get_cdc_type() !=
3722 WCD9XXX_CDC_TYPE_HELICON) {
3723 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3724 snd_soc_update_bits(codec,
3725 mbhc->resmgr->reg_addr->micb_4_mbhc,
3726 0x03, MBHC_MICBIAS2);
3727 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003728
3729 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3730
3731 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3732
3733 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3734 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3735 gain[idx] << 3);
3736
3737 pr_debug("%s: leave\n", __func__);
3738}
3739
3740static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3741{
3742 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003743 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303744
Joonwoo Parka8890262012-10-15 12:04:27 -07003745 if (mbhc->mbhc_cfg->gpio) {
3746 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3747 wcd9xxx_mech_plug_detect_irq,
3748 (IRQF_TRIGGER_RISING |
3749 IRQF_TRIGGER_FALLING |
3750 IRQF_DISABLED),
3751 "headset detect", mbhc);
3752 if (ret) {
3753 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3754 mbhc->mbhc_cfg->gpio_irq);
3755 } else {
3756 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3757 if (ret)
3758 pr_err("%s: Failed to enable wake up irq %d\n",
3759 __func__, mbhc->mbhc_cfg->gpio_irq);
3760 }
3761 } else if (mbhc->mbhc_cfg->insert_detect) {
3762 /* Enable HPHL_10K_SW */
3763 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3764 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303765
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003766 ret = wcd9xxx_request_irq(core_res,
3767 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003768 wcd9xxx_mech_plug_detect_irq,
3769 "Jack Detect",
3770 mbhc);
3771 if (ret)
3772 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003773 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003774 }
3775
3776 return ret;
3777}
3778
3779static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3780{
3781 int ret = 0;
3782 struct snd_soc_codec *codec = mbhc->codec;
3783
3784 pr_debug("%s: enter\n", __func__);
3785
3786 /* Enable MCLK during calibration */
3787 wcd9xxx_onoff_ext_mclk(mbhc, true);
3788 wcd9xxx_mbhc_setup(mbhc);
3789 wcd9xxx_mbhc_cal(mbhc);
3790 wcd9xxx_mbhc_calc_thres(mbhc);
3791 wcd9xxx_onoff_ext_mclk(mbhc, false);
3792 wcd9xxx_calibrate_hs_polling(mbhc);
3793
3794 /* Enable Mic Bias pull down and HPH Switch to GND */
3795 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3796 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3797 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3798
3799 if (!IS_ERR_VALUE(ret)) {
3800 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3801 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003802 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003803 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003804 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003805 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003806
3807 /* Initialize mechanical mbhc */
3808 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3809
3810 if (!ret && mbhc->mbhc_cfg->gpio) {
3811 /* Requested with IRQF_DISABLED */
3812 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3813
3814 /* Bootup time detection */
3815 wcd9xxx_swch_irq_handler(mbhc);
3816 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3817 pr_debug("%s: Setting up codec own insert detection\n",
3818 __func__);
3819 /* Setup for insertion detection */
3820 wcd9xxx_insert_detect_setup(mbhc, true);
3821 }
3822 }
3823
3824 pr_debug("%s: leave\n", __func__);
3825
3826 return ret;
3827}
3828
3829static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3830{
3831 struct delayed_work *dwork;
3832 struct wcd9xxx_mbhc *mbhc;
3833 struct snd_soc_codec *codec;
3834 const struct firmware *fw;
3835 int ret = -1, retry = 0;
3836
3837 dwork = to_delayed_work(work);
3838 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3839 codec = mbhc->codec;
3840
3841 while (retry < FW_READ_ATTEMPTS) {
3842 retry++;
3843 pr_info("%s:Attempt %d to request MBHC firmware\n",
3844 __func__, retry);
3845 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3846 codec->dev);
3847
3848 if (ret != 0) {
3849 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3850 } else {
3851 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3852 break;
3853 }
3854 }
3855
3856 if (ret != 0) {
3857 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3858 __func__);
3859 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3860 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3861 __func__);
3862 release_firmware(fw);
3863 } else {
3864 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3865 mbhc->mbhc_fw = fw;
3866 }
3867
3868 (void) wcd9xxx_init_and_calibrate(mbhc);
3869}
3870
3871#ifdef CONFIG_DEBUG_FS
3872ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3873 size_t count, loff_t *pos)
3874{
3875 const int size = 768;
3876 char buffer[size];
3877 int n = 0;
3878 struct wcd9xxx_mbhc *mbhc = file->private_data;
3879 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003880 const s16 v_ins_hu =
3881 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3882 const s16 v_ins_h =
3883 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3884 const s16 v_b1_hu =
3885 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3886 const s16 v_b1_h =
3887 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3888 const s16 v_br_h =
3889 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003890
Joonwoo Park520a0f92013-05-14 19:39:58 -07003891 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3892 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003893 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3894 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003895 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3896 p->dce_nsc_cs_z,
3897 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3898 p->dce_nsc_cs_z,
3899 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003900 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3901 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3902 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3903 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003904 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3905 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3906 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3907 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3908 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3909 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3910 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003911 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003912 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003913 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003914 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003915 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003916 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003917 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3918 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3919 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3920 p->v_no_mic,
3921 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3922 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3923 p->v_inval_ins_low);
3924 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3925 p->v_inval_ins_high);
3926 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3927 !wcd9xxx_swch_level_remove(mbhc));
3928 buffer[n] = 0;
3929
3930 return simple_read_from_buffer(buf, count, pos, buffer, n);
3931}
3932
3933static int codec_debug_open(struct inode *inode, struct file *file)
3934{
3935 file->private_data = inode->i_private;
3936 return 0;
3937}
3938
3939static ssize_t codec_debug_write(struct file *filp,
3940 const char __user *ubuf, size_t cnt,
3941 loff_t *ppos)
3942{
3943 char lbuf[32];
3944 char *buf;
3945 int rc;
3946 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3947
3948 if (cnt > sizeof(lbuf) - 1)
3949 return -EINVAL;
3950
3951 rc = copy_from_user(lbuf, ubuf, cnt);
3952 if (rc)
3953 return -EFAULT;
3954
3955 lbuf[cnt] = '\0';
3956 buf = (char *)lbuf;
3957 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3958 false : true;
3959 return rc;
3960}
3961
3962static const struct file_operations mbhc_trrs_debug_ops = {
3963 .open = codec_debug_open,
3964 .write = codec_debug_write,
3965};
3966
3967static const struct file_operations mbhc_debug_ops = {
3968 .open = codec_debug_open,
3969 .read = codec_mbhc_debug_read,
3970};
3971
3972static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3973{
3974 mbhc->debugfs_poke =
3975 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3976 &mbhc_trrs_debug_ops);
3977 mbhc->debugfs_mbhc =
3978 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3979 NULL, mbhc, &mbhc_debug_ops);
3980}
3981
3982static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3983{
3984 debugfs_remove(mbhc->debugfs_poke);
3985 debugfs_remove(mbhc->debugfs_mbhc);
3986}
3987#else
3988static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3989{
3990}
3991
3992static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3993{
3994}
3995#endif
3996
3997int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3998 struct wcd9xxx_mbhc_config *mbhc_cfg)
3999{
Simmi Pateriya95466b12013-05-09 20:08:46 +05304000 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004001 struct snd_soc_codec *codec = mbhc->codec;
4002
4003 pr_debug("%s: enter\n", __func__);
4004
4005 if (!codec) {
4006 pr_err("%s: no codec\n", __func__);
4007 return -EINVAL;
4008 }
4009
4010 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
4011 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
4012 pr_err("Error: unsupported clock rate %d\n",
4013 mbhc_cfg->mclk_rate);
4014 return -EINVAL;
4015 }
4016
4017 /* Save mbhc config */
4018 mbhc->mbhc_cfg = mbhc_cfg;
4019
4020 /* Get HW specific mbhc registers' address */
4021 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4022
4023 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304024 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4025 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4026 else
4027 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4028 0x40, WCD9XXX_CFILT_FAST_MODE);
4029
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004030 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004031 * setup internal micbias if codec uses internal micbias for
4032 * headset detection
4033 */
4034 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304035 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004036 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304037 mbhc->int_rbias_on = true;
4038 } else {
4039 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004040 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304041 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004042 }
4043
4044 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004045 * If codec has specific clock gating for MBHC,
4046 * remove the clock gate
4047 */
4048 if (mbhc->mbhc_cb &&
4049 mbhc->mbhc_cb->enable_clock_gate)
4050 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4051
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004052 if (!mbhc->mbhc_cfg->read_fw_bin ||
4053 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004054 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004055 } else {
4056 if (!mbhc->mbhc_fw)
4057 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4058 usecs_to_jiffies(FW_READ_TIMEOUT));
4059 else
4060 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4061 __func__, mbhc->mbhc_fw);
4062 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004063
4064 pr_debug("%s: leave %d\n", __func__, rc);
4065 return rc;
4066}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004067EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004068
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004069void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4070{
4071 if (mbhc->mbhc_fw) {
4072 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4073 release_firmware(mbhc->mbhc_fw);
4074 mbhc->mbhc_fw = NULL;
4075 }
4076}
4077EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4078
Joonwoo Parka8890262012-10-15 12:04:27 -07004079static enum wcd9xxx_micbias_num
4080wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4081{
4082 enum wcd9xxx_micbias_num ret;
4083 switch (event) {
4084 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004085 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4086 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4087 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004088 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004089 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004090 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004091 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4092 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4093 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004094 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004095 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004096 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004097 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4098 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4099 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004100 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004101 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004102 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004103 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4104 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4105 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004106 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004107 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004108 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004109 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004110 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004111 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004112 }
4113 return ret;
4114}
4115
4116static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4117{
4118 int ret;
4119 switch (event) {
4120 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4121 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4122 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4123 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4124 ret = WCD9XXX_CFILT1_SEL;
4125 break;
4126 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4127 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4128 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4129 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4130 ret = WCD9XXX_CFILT2_SEL;
4131 break;
4132 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4133 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4134 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4135 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4136 ret = WCD9XXX_CFILT3_SEL;
4137 break;
4138 default:
4139 ret = -1;
4140 }
4141 return ret;
4142}
4143
4144static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4145{
4146 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004147 const struct wcd9xxx_micbias_setting *mb_pdata =
4148 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004149
4150 switch (mbhc->mbhc_cfg->micbias) {
4151 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004152 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004153 break;
4154 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004155 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004156 break;
4157 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004158 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004159 break;
4160 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004161 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004162 break;
4163 default:
4164 cfilt = MBHC_MICBIAS_INVALID;
4165 break;
4166 }
4167 return cfilt;
4168}
4169
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004170static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4171{
4172 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4173 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4174 else
4175 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4176 0x40, on ? 0x40 : 0x00);
4177}
4178
Joonwoo Parka8890262012-10-15 12:04:27 -07004179static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4180 void *data)
4181{
4182 int ret = 0;
4183 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4184 struct snd_soc_codec *codec = mbhc->codec;
4185 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4186
4187 pr_debug("%s: enter event %s(%d)\n", __func__,
4188 wcd9xxx_get_event_string(event), event);
4189
4190 switch (event) {
4191 /* MICBIAS usage change */
4192 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4193 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4194 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4195 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304196 mbhc->int_rbias_on = true;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004197 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4198 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004199 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004200 /*
4201 * Enable MBHC TxFE whenever micbias is
4202 * turned ON and polling is active
4203 */
4204 if (mbhc->polling_active)
4205 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4206 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004207 break;
4208 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4209 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4210 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4211 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004212 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004213 wcd9xxx_event_to_micbias(event) &&
4214 wcd9xxx_mbhc_polling(mbhc)) {
4215 /* if polling is on, restart it */
4216 wcd9xxx_pause_hs_polling(mbhc);
4217 wcd9xxx_start_hs_polling(mbhc);
4218 }
4219 break;
4220 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4221 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4222 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4223 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304224 mbhc->int_rbias_on = false;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004225 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4226 wcd9xxx_event_to_micbias(event)) {
4227 if (mbhc->event_state &
4228 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4229 wcd9xxx_switch_micbias(mbhc, 1);
4230 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304231 * Disable MBHC TxFE, in case it was enabled earlier
4232 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004233 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304234 if (!mbhc->polling_active)
4235 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004236 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004237 break;
4238 /* PA usage change */
4239 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004240 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004241 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004242 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004243 wcd9xxx_switch_micbias(mbhc, 1);
4244 break;
4245 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004246 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004247 break;
4248 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004249 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004250 /* if HPH PAs are off, report OCP and switch back to CFILT */
4251 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4252 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4253 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4254 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004255 if (!(mbhc->event_state &
4256 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4257 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004258 break;
4259 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004260 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004261 /* if HPH PAs are off, report OCP and switch back to CFILT */
4262 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4263 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4264 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4265 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004266 if (!(mbhc->event_state &
4267 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4268 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004269 break;
4270 /* Clock usage change */
4271 case WCD9XXX_EVENT_PRE_MCLK_ON:
4272 break;
4273 case WCD9XXX_EVENT_POST_MCLK_ON:
4274 /* Change to lower TxAAF frequency */
4275 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4276 1 << 4);
4277 /* Re-calibrate clock rate dependent values */
4278 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4279 /* If clock source changes, stop and restart polling */
4280 if (wcd9xxx_mbhc_polling(mbhc)) {
4281 wcd9xxx_calibrate_hs_polling(mbhc);
4282 wcd9xxx_start_hs_polling(mbhc);
4283 }
4284 break;
4285 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4286 /* If clock source changes, stop and restart polling */
4287 if (wcd9xxx_mbhc_polling(mbhc))
4288 wcd9xxx_pause_hs_polling(mbhc);
4289 break;
4290 case WCD9XXX_EVENT_POST_MCLK_OFF:
4291 break;
4292 case WCD9XXX_EVENT_PRE_RCO_ON:
4293 break;
4294 case WCD9XXX_EVENT_POST_RCO_ON:
4295 /* Change to higher TxAAF frequency */
4296 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4297 0 << 4);
4298 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004299 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004300 /* If clock source changes, stop and restart polling */
4301 if (wcd9xxx_mbhc_polling(mbhc)) {
4302 wcd9xxx_calibrate_hs_polling(mbhc);
4303 wcd9xxx_start_hs_polling(mbhc);
4304 }
4305 break;
4306 case WCD9XXX_EVENT_PRE_RCO_OFF:
4307 /* If clock source changes, stop and restart polling */
4308 if (wcd9xxx_mbhc_polling(mbhc))
4309 wcd9xxx_pause_hs_polling(mbhc);
4310 break;
4311 case WCD9XXX_EVENT_POST_RCO_OFF:
4312 break;
4313 /* CFILT usage change */
4314 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4315 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4316 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4317 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4318 wcd9xxx_event_to_cfilt(event))
4319 /*
4320 * Switch CFILT to slow mode if MBHC CFILT is being
4321 * used.
4322 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304323 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004324 break;
4325 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4326 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4327 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4328 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4329 wcd9xxx_event_to_cfilt(event))
4330 /*
4331 * Switch CFILT to fast mode if MBHC CFILT is not
4332 * used anymore.
4333 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304334 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004335 break;
4336 /* System resume */
4337 case WCD9XXX_EVENT_POST_RESUME:
4338 mbhc->mbhc_last_resume = jiffies;
4339 break;
4340 /* BG mode chage */
4341 case WCD9XXX_EVENT_PRE_BG_OFF:
4342 case WCD9XXX_EVENT_POST_BG_OFF:
4343 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4344 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4345 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4346 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4347 /* Not used for now */
4348 break;
4349 default:
4350 WARN(1, "Unknown event %d\n", event);
4351 ret = -EINVAL;
4352 }
4353
4354 pr_debug("%s: leave\n", __func__);
4355
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304356 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004357}
4358
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004359static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4360 uint32_t *zr)
4361{
4362 int i;
4363 int ret = 0;
4364 s16 l[3], r[3];
4365 s16 *z[] = {
4366 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4367 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004368 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004369 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004370 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4371 /* Phase 1 */
4372 /* Set MBHC_MUX for HPHL without ical */
4373 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4374 /* Set MBHC_MUX for HPHR without ical */
4375 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4376 /* Set MBHC_MUX for HPHR with ical */
4377 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4378 /* Set MBHC_MUX for HPHL with ical */
4379 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4380
4381 /* Phase 2 */
4382 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4383 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4384 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4385 };
4386
4387 pr_debug("%s: enter\n", __func__);
4388 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4389
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004390 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4391 !mbhc->mbhc_cb->compute_impedance || !zl ||
4392 !zr)
4393 return -EINVAL;
4394
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004395 /*
4396 * Impedance detection is an intrusive function as it mutes RX paths,
4397 * enable PAs and etc. Therefore codec drvier including ALSA
4398 * shouldn't read and write hardware registers during detection.
4399 */
4400 mutex_lock(&codec->mutex);
4401
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004402 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004403
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004404 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004405 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004406
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004407 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4408 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004409
4410 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004411 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004412 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4413 reg_set_mux[i].mask,
4414 reg_set_mux[i].val);
4415 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4416 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4417 else
4418 snd_soc_update_bits(codec,
4419 WCD9XXX_A_MBHC_SCALING_MUX_1,
4420 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004421 /* 25us is required after mux change to settle down */
4422 usleep_range(mux_wait_us,
4423 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004424 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004425 }
4426
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004427 /* Codec specific setup for L2 and R2 measurements */
4428 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004429
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004430 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004431 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4432 reg_set_mux[i].mask,
4433 reg_set_mux[i].val);
4434 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4435 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4436 else
4437 snd_soc_update_bits(codec,
4438 WCD9XXX_A_MBHC_SCALING_MUX_1,
4439 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004440 /* 25us is required after mux change to settle down */
4441 usleep_range(mux_wait_us,
4442 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004443 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004444 }
4445
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004446 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004447
4448 mutex_unlock(&codec->mutex);
4449
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004450 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004451
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004452 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004453 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004454
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004455 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004456 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004457 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4458 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004459 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004460 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004461 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4462 pr_debug("%s: Impedance detection completed\n", __func__);
4463
4464 return ret;
4465}
4466
4467int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4468 uint32_t *zr)
4469{
4470 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4471 *zl = mbhc->zl;
4472 *zr = mbhc->zr;
4473 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4474
4475 if (*zl && *zr)
4476 return 0;
4477 else
4478 return -EINVAL;
4479}
4480
Joonwoo Parka8890262012-10-15 12:04:27 -07004481/*
4482 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4483 *
4484 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4485 */
4486int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004487 struct snd_soc_codec *codec,
4488 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004489 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4490 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4491 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004492 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004493{
4494 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004495 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004496
4497 pr_debug("%s: enter\n", __func__);
4498 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4499 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4500
4501 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4502 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4503 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4504 mbhc->mbhc_micbias_switched = false;
4505 mbhc->polling_active = false;
4506 mbhc->mbhc_state = MBHC_STATE_NONE;
4507 mbhc->in_swch_irq_handler = false;
4508 mbhc->current_plug = PLUG_TYPE_NONE;
4509 mbhc->lpi_enabled = false;
4510 mbhc->no_mic_headset_override = false;
4511 mbhc->mbhc_last_resume = 0;
4512 mbhc->codec = codec;
4513 mbhc->resmgr = resmgr;
4514 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004515 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004516 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304517 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004518 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004519 mbhc->impedance_detect = impedance_det_en;
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304520 mbhc->int_rbias_on = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07004521
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004522 if (mbhc->intr_ids == NULL) {
4523 pr_err("%s: Interrupt mapping not provided\n", __func__);
4524 return -EINVAL;
4525 }
4526
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004527 if (mbhc->headset_jack.jack == NULL) {
4528 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4529 &mbhc->headset_jack);
4530 if (ret) {
4531 pr_err("%s: Failed to create new jack\n", __func__);
4532 return ret;
4533 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004534
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004535 ret = snd_soc_jack_new(codec, "Button Jack",
4536 WCD9XXX_JACK_BUTTON_MASK,
4537 &mbhc->button_jack);
4538 if (ret) {
4539 pr_err("Failed to create new jack\n");
4540 return ret;
4541 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004542
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004543 ret = snd_jack_set_key(mbhc->button_jack.jack,
4544 SND_JACK_BTN_0,
4545 KEY_MEDIA);
4546 if (ret) {
4547 pr_err("%s: Failed to set code for btn-0\n",
4548 __func__);
4549 return ret;
4550 }
4551
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004552 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4553 wcd9xxx_mbhc_fw_read);
4554 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4555 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4556 wcd9xxx_mbhc_insert_work);
4557 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004558
4559 /* Register event notifier */
4560 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4561 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4562 if (ret) {
4563 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4564 return ret;
4565 }
4566
4567 wcd9xxx_init_debugfs(mbhc);
4568
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004569
4570 /* Disable Impedance detection by default for certain codec types */
4571 if (mbhc->mbhc_cb &&
4572 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4573 impedance_detect_en = 0;
4574 else
4575 impedance_detect_en = impedance_det_en ? 1 : 0;
4576
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004577 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004578 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004579 wcd9xxx_hs_insert_irq,
4580 "Headset insert detect", mbhc);
4581 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004582 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004583 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004584 goto err_insert_irq;
4585 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004586 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004587
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004588 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004589 wcd9xxx_hs_remove_irq,
4590 "Headset remove detect", mbhc);
4591 if (ret) {
4592 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004593 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004594 goto err_remove_irq;
4595 }
4596
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004597 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004598 wcd9xxx_dce_handler, "DC Estimation detect",
4599 mbhc);
4600 if (ret) {
4601 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004602 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004603 goto err_potential_irq;
4604 }
4605
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004606 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004607 wcd9xxx_release_handler,
4608 "Button Release detect", mbhc);
4609 if (ret) {
4610 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004611 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004612 goto err_release_irq;
4613 }
4614
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004615 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004616 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4617 mbhc);
4618 if (ret) {
4619 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004620 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004621 goto err_hphl_ocp_irq;
4622 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004623 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004624
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004625 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004626 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4627 mbhc);
4628 if (ret) {
4629 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004630 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004631 goto err_hphr_ocp_irq;
4632 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004633 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004634
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004635 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4636 1 << WCD9XXX_COND_HPH);
4637
Joonwoo Parka8890262012-10-15 12:04:27 -07004638 pr_debug("%s: leave ret %d\n", __func__, ret);
4639 return ret;
4640
4641err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004642 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004643err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004644 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004645err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004646 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004647err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004648 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004649err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004650 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004651err_insert_irq:
4652 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4653
4654 pr_debug("%s: leave ret %d\n", __func__, ret);
4655 return ret;
4656}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004657EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004658
4659void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4660{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004661 struct wcd9xxx_core_resource *core_res =
4662 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004663
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004664 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4665 1 << WCD9XXX_COND_HPH);
4666
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004667 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4668 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4669 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4670 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4671 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4672 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4673 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004674
Joonwoo Parka8890262012-10-15 12:04:27 -07004675 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004676 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004677}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004678EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004679
4680MODULE_DESCRIPTION("wcd9xxx MBHC module");
4681MODULE_LICENSE("GPL v2");