blob: 3e00abeb2e62552506a90468c06e5dc420965f4d [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
Simmi Pateriya4e545052013-11-15 07:41:06 +0530131static bool detect_use_vddio_switch;
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);
Simmi Pateriya4e545052013-11-15 07:41:06 +05301769 detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
Joonwoo Parka8890262012-10-15 12:04:27 -07001770
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001771 /*
1772 * There are chances vddio switch is on and cfilt voltage is adjusted
1773 * to vddio voltage even after plug type removal reported.
1774 */
1775 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1776 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1777
Joonwoo Parka8890262012-10-15 12:04:27 -07001778 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001779 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001780
Joonwoo Parkccccba72013-04-26 11:19:46 -07001781 /*
1782 * cfilter in fast mode requires 1ms to charge up and down micbias
1783 * fully.
1784 */
1785 (void) wcd9xxx_pull_down_micbias(mbhc,
1786 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301787
1788 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001789 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001790 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001791 rt[0].swap_gnd = false;
1792 rt[0].vddio = false;
1793 rt[0].hwvalue = true;
1794 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1795 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1796 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001797 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001798 else
1799 rt[i].vddio = false;
1800 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1801 rt[i].hwvalue = false;
1802 if (rt[i].swap_gnd)
1803 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1804 if (rt[i].vddio)
1805 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001806 /*
1807 * Pull down micbias to detect headset with mic which has
1808 * threshold and to have more consistent voltage measurements.
1809 *
1810 * cfilter in fast mode requires 1ms to charge up and down
1811 * micbias fully.
1812 */
1813 (void) wcd9xxx_pull_down_micbias(mbhc,
1814 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001815 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1816 if (rt[i].vddio)
1817 wcd9xxx_onoff_vddio_switch(mbhc, false);
1818 if (rt[i].swap_gnd)
1819 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001820 }
1821
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001822 if (vddioon)
1823 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1824
1825 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1826 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001827
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301828 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001829 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001830 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001831}
1832
1833static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1834{
1835 if (mbhc->mbhc_cfg->gpio)
1836 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1837 mbhc->mbhc_cfg->gpio_level_insert);
1838 else if (mbhc->mbhc_cfg->insert_detect)
1839 return snd_soc_read(mbhc->codec,
1840 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1841 (1 << 2);
1842 else
1843 WARN(1, "Invalid jack detection configuration\n");
1844
1845 return true;
1846}
1847
1848static bool is_clk_active(struct snd_soc_codec *codec)
1849{
1850 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1851}
1852
1853static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1854 int insertion, int trigger, bool padac_off)
1855{
1856 struct snd_soc_codec *codec = mbhc->codec;
1857 int central_bias_enabled = 0;
1858 const struct wcd9xxx_mbhc_general_cfg *generic =
1859 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1860 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1861 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1862
Joonwoo Park80a01172012-10-15 16:05:23 -07001863 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1864 __func__, insertion, trigger);
1865
Joonwoo Parka8890262012-10-15 12:04:27 -07001866 if (!mbhc->mbhc_cfg->calibration) {
1867 pr_err("Error, no wcd9xxx calibration\n");
1868 return -EINVAL;
1869 }
1870
1871 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1872
1873 /*
1874 * Make sure mic bias and Mic line schmitt trigger
1875 * are turned OFF
1876 */
1877 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1878 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1879
1880 if (insertion) {
1881 wcd9xxx_switch_micbias(mbhc, 0);
1882
1883 /* DAPM can manipulate PA/DAC bits concurrently */
1884 if (padac_off == true)
1885 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1886
1887 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1888 /* Enable HPH Schmitt Trigger */
1889 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1890 0x11);
1891 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1892 plug_det->hph_current << 2);
1893 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1894 0x02);
1895 }
1896 if (trigger & MBHC_USE_MB_TRIGGER) {
1897 /* enable the mic line schmitt trigger */
1898 snd_soc_update_bits(codec,
1899 mbhc->mbhc_bias_regs.mbhc_reg,
1900 0x60, plug_det->mic_current << 5);
1901 snd_soc_update_bits(codec,
1902 mbhc->mbhc_bias_regs.mbhc_reg,
1903 0x80, 0x80);
1904 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1905 snd_soc_update_bits(codec,
1906 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1907 0x00);
1908 snd_soc_update_bits(codec,
1909 mbhc->mbhc_bias_regs.mbhc_reg,
1910 0x10, 0x10);
1911 }
1912
1913 /* setup for insetion detection */
1914 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1915 } else {
1916 pr_debug("setup for removal detection\n");
1917 /* Make sure the HPH schmitt trigger is OFF */
1918 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1919
1920 /* enable the mic line schmitt trigger */
1921 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1922 0x01, 0x00);
1923 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1924 plug_det->mic_current << 5);
1925 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1926 0x80, 0x80);
1927 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1928 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1929 0x10, 0x10);
1930
1931 /* Setup for low power removal detection */
1932 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1933 0x2);
1934 }
1935
1936 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1937 /* called by interrupt */
1938 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001939 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001940 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1941 0x06, 0);
1942 usleep_range(generic->t_shutdown_plug_rem,
1943 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001944 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001945 } else
1946 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1947 0x06, 0);
1948 }
1949
1950 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1951
1952 /* If central bandgap disabled */
1953 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1954 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1955 usleep_range(generic->t_bg_fast_settle,
1956 generic->t_bg_fast_settle);
1957 central_bias_enabled = 1;
1958 }
1959
1960 /* If LDO_H disabled */
1961 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1962 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1963 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1964 usleep_range(generic->t_ldoh, generic->t_ldoh);
1965 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1966
1967 if (central_bias_enabled)
1968 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1969 0);
1970 }
1971
Meng Wangeeaaaba2013-09-09 18:37:32 +08001972 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301973 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1974 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001975
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001976 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001977 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001978 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001979
1980 return 0;
1981}
1982
1983/* called under codec_resource_lock acquisition */
1984static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1985 enum wcd9xxx_mbhc_plug_type plug_type)
1986{
Joonwoo Park80a01172012-10-15 16:05:23 -07001987 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1988 __func__, mbhc->current_plug, plug_type);
1989
Joonwoo Parka8890262012-10-15 12:04:27 -07001990 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1991
1992 if (plug_type == PLUG_TYPE_HEADPHONE &&
1993 mbhc->current_plug == PLUG_TYPE_NONE) {
1994 /*
1995 * Nothing was reported previously
1996 * report a headphone or unsupported
1997 */
1998 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1999 wcd9xxx_cleanup_hs_polling(mbhc);
2000 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002001 if (!mbhc->mbhc_cfg->detect_extn_cable) {
2002 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
2003 wcd9xxx_report_plug(mbhc, 0,
2004 SND_JACK_HEADSET);
2005 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
2006 wcd9xxx_report_plug(mbhc, 0,
2007 SND_JACK_HEADPHONE);
2008 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002009 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
2010 wcd9xxx_cleanup_hs_polling(mbhc);
2011 } else if (plug_type == PLUG_TYPE_HEADSET) {
2012 /*
2013 * If Headphone was reported previously, this will
2014 * only report the mic line
2015 */
2016 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302017 /* Button detection required RC oscillator */
2018 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002019 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002020
2021 /* if PA is already on, switch micbias source to VDDIO */
2022 if (mbhc->event_state &
2023 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2024 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002025 wcd9xxx_start_hs_polling(mbhc);
2026 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002027 if (mbhc->mbhc_cfg->detect_extn_cable) {
2028 /* High impedance device found. Report as LINEOUT*/
2029 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2030 wcd9xxx_cleanup_hs_polling(mbhc);
2031 pr_debug("%s: setup mic trigger for further detection\n",
2032 __func__);
2033 mbhc->lpi_enabled = true;
2034 /*
2035 * Do not enable HPHL trigger. If playback is active,
2036 * it might lead to continuous false HPHL triggers
2037 */
2038 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2039 false);
2040 } else {
2041 if (mbhc->current_plug == PLUG_TYPE_NONE)
2042 wcd9xxx_report_plug(mbhc, 1,
2043 SND_JACK_HEADPHONE);
2044 wcd9xxx_cleanup_hs_polling(mbhc);
2045 pr_debug("setup mic trigger for further detection\n");
2046 mbhc->lpi_enabled = true;
2047 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2048 MBHC_USE_HPHL_TRIGGER,
2049 false);
2050 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002051 } else {
2052 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2053 mbhc->current_plug, plug_type);
2054 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002055 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002056}
2057
2058/* called under codec_resource_lock acquisition */
2059static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2060{
2061 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002062 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002063
2064 pr_debug("%s: enter\n", __func__);
2065
2066 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002067 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2068 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002069
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002070 if (current_source_enable) {
2071 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2072 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2073 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2074 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002075 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002076 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002077 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002078 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002079
2080 if (wcd9xxx_swch_level_remove(mbhc)) {
2081 pr_debug("%s: Switch level is low when determining plug\n",
2082 __func__);
2083 return;
2084 }
2085
2086 if (plug_type == PLUG_TYPE_INVALID ||
2087 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002088 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002089 wcd9xxx_schedule_hs_detect_plug(mbhc,
2090 &mbhc->correct_plug_swch);
2091 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2092 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002093 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002094 wcd9xxx_schedule_hs_detect_plug(mbhc,
2095 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002096 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002097 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002098 wcd9xxx_schedule_hs_detect_plug(mbhc,
2099 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002100 } else {
2101 pr_debug("%s: Valid plug found, determine plug type %d\n",
2102 __func__, plug_type);
2103 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2104 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002105 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002106}
2107
2108/* called under codec_resource_lock acquisition */
2109static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2110{
Joonwoo Park80a01172012-10-15 16:05:23 -07002111 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002112 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2113
Joonwoo Parka8890262012-10-15 12:04:27 -07002114 if (wcd9xxx_swch_level_remove(mbhc))
2115 pr_debug("%s: Switch level low when determining plug\n",
2116 __func__);
2117 else
2118 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002119 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002120}
2121
2122/* called only from interrupt which is under codec_resource_lock acquisition */
2123static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2124 bool is_removal)
2125{
2126 if (!is_removal) {
2127 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2128
2129 rmb();
2130 if (mbhc->lpi_enabled)
2131 msleep(100);
2132
2133 rmb();
2134 if (!mbhc->lpi_enabled) {
2135 pr_debug("%s: lpi is disabled\n", __func__);
2136 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2137 pr_debug("%s: Valid insertion, detect plug type\n",
2138 __func__);
2139 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2140 } else {
2141 pr_debug("%s: Invalid insertion stop plug detection\n",
2142 __func__);
2143 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002144 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2145 pr_debug("%s: Removal\n", __func__);
2146 if (!wcd9xxx_swch_level_remove(mbhc)) {
2147 /*
2148 * Switch indicates, something is still inserted.
2149 * This could be extension cable i.e. headset is
2150 * removed from extension cable.
2151 */
2152 /* cancel detect plug */
2153 wcd9xxx_cancel_hs_detect_plug(mbhc,
2154 &mbhc->correct_plug_swch);
2155 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2156 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002157 } else {
2158 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2159 }
2160}
2161
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002162static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2163 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002164{
2165 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2166 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2167 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2168
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002169 if (cs_enable)
2170 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2171 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2172 else
2173 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2174 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2175 (mic_mv > plug_type->v_no_mic) &&
2176 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002177}
2178
2179/*
2180 * called under codec_resource_lock acquisition
2181 * returns true if mic voltage range is back to normal insertion
2182 * returns false either if timedout or removed
2183 */
2184static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2185{
2186 int i;
2187 bool timedout, settled = false;
2188 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2189 short mb_v[NUM_DCE_PLUG_DETECT];
2190 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002191 bool cs_enable;
2192
2193 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2194 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2195
2196 if (cs_enable)
2197 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002198
2199 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2200 while (!(timedout = time_after(jiffies, timeout))) {
2201 retry++;
2202 if (wcd9xxx_swch_level_remove(mbhc)) {
2203 pr_debug("%s: Switch indicates removal\n", __func__);
2204 break;
2205 }
2206
2207 if (retry > 1)
2208 msleep(250);
2209 else
2210 msleep(50);
2211
2212 if (wcd9xxx_swch_level_remove(mbhc)) {
2213 pr_debug("%s: Switch indicates removal\n", __func__);
2214 break;
2215 }
2216
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002217 if (cs_enable) {
2218 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2219 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2220 true, true);
2221 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2222 true,
2223 mb_v[i],
2224 mbhc->mbhc_data.dce_nsc_cs_z,
2225 (u32)VDDIO_MICBIAS_MV);
2226 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2227 __func__, retry, mic_mv[i], mb_v[i]);
2228 }
2229 } else {
2230 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2231 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2232 true);
2233 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2234 mb_v[i]);
2235 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2236 __func__, retry, mic_mv[i],
2237 mb_v[i]);
2238 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002239 }
2240
2241 if (wcd9xxx_swch_level_remove(mbhc)) {
2242 pr_debug("%s: Switcn indicates removal\n", __func__);
2243 break;
2244 }
2245
2246 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2247 pr_debug("%s : headset/headphone is removed\n",
2248 __func__);
2249 break;
2250 }
2251
2252 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002253 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002254 break;
2255
2256 if (i == NUM_DCE_PLUG_DETECT) {
2257 pr_debug("%s: MIC voltage settled\n", __func__);
2258 settled = true;
2259 msleep(200);
2260 break;
2261 }
2262 }
2263
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002264 if (cs_enable)
2265 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2266
Joonwoo Parka8890262012-10-15 12:04:27 -07002267 if (timedout)
2268 pr_debug("%s: Microphone did not settle in %d seconds\n",
2269 __func__, HS_DETECT_PLUG_TIME_MS);
2270 return settled;
2271}
2272
2273/* called only from interrupt which is under codec_resource_lock acquisition */
2274static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2275{
Joonwoo Park80a01172012-10-15 16:05:23 -07002276 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002277 if (wcd9xxx_hs_remove_settle(mbhc))
2278 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002279 pr_debug("%s: leave\n", __func__);
2280}
2281
2282/* called only from interrupt which is under codec_resource_lock acquisition */
2283static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2284{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002285 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002286 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002287 bool removed = true;
2288 struct snd_soc_codec *codec = mbhc->codec;
2289 const struct wcd9xxx_mbhc_general_cfg *generic =
2290 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002291 bool cs_enable;
2292 s16 cur_v_ins_h;
2293 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002294
2295 pr_debug("%s: enter\n", __func__);
2296 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2297 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2298 __func__);
2299 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2300 0x08, 0x08);
2301 return;
2302 }
2303
2304 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002305 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002306
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002307 /* If micbias is enabled, don't enable current source */
2308 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2309 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2310 (!(snd_soc_read(codec,
2311 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002312 if (cs_enable)
2313 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2314
Joonwoo Park50ae0512013-06-04 16:53:12 -07002315 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002316 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002317 if (cs_enable) {
2318 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2319 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2320 mb_mv = VDDIO_MICBIAS_MV;
2321 } else {
2322 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2323 dcez = mbhc->mbhc_data.dce_z;
2324 mb_mv = mbhc->mbhc_data.micb_mv;
2325 }
2326
Joonwoo Park50ae0512013-06-04 16:53:12 -07002327 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002328 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2329 dcez, mb_mv));
2330
2331 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2332 (wcd9xxx_get_current_v(mbhc,
2333 WCD9XXX_CURRENT_V_INS_H));
2334
2335 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002336 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002337 break;
2338 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002339 } while (!time_after(jiffies, timeout));
2340 pr_debug("%s: headset %sactually removed\n", __func__,
2341 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002342
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002343 if (cs_enable)
2344 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2345
Joonwoo Park80a01172012-10-15 16:05:23 -07002346 if (removed) {
2347 if (mbhc->mbhc_cfg->detect_extn_cable) {
2348 if (!wcd9xxx_swch_level_remove(mbhc)) {
2349 /*
2350 * extension cable is still plugged in
2351 * report it as LINEOUT device
2352 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302353 if (mbhc->hph_status == SND_JACK_HEADSET)
2354 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2355 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002356 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2357 wcd9xxx_cleanup_hs_polling(mbhc);
2358 wcd9xxx_enable_hs_detect(mbhc, 1,
2359 MBHC_USE_MB_TRIGGER,
2360 false);
2361 }
2362 } else {
2363 /* Cancel possibly running hs_detect_work */
2364 wcd9xxx_cancel_hs_detect_plug(mbhc,
2365 &mbhc->correct_plug_noswch);
2366 /*
2367 * If this removal is not false, first check the micbias
2368 * switch status and switch it to LDOH if it is already
2369 * switched to VDDIO.
2370 */
2371 wcd9xxx_switch_micbias(mbhc, 0);
2372
2373 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302374 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002375 wcd9xxx_cleanup_hs_polling(mbhc);
2376 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2377 MBHC_USE_HPHL_TRIGGER,
2378 true);
2379 }
2380 } else {
2381 wcd9xxx_start_hs_polling(mbhc);
2382 }
2383 pr_debug("%s: leave\n", __func__);
2384}
2385
2386/* called only from interrupt which is under codec_resource_lock acquisition */
2387static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2388 bool is_mb_trigger)
2389{
2390 /* Cancel possibly running hs_detect_work */
2391 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2392
2393 if (is_mb_trigger) {
2394 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2395 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2396 } else {
2397 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2398 __func__);
2399 wcd9xxx_mbhc_detect_plug_type(mbhc);
2400 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002401}
2402
2403static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2404{
Joonwoo Parka8890262012-10-15 12:04:27 -07002405 struct wcd9xxx_mbhc *mbhc = data;
2406
2407 pr_debug("%s: enter, removal interrupt\n", __func__);
2408 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002409 /*
2410 * While we don't know whether MIC is there or not, let the resmgr know
2411 * so micbias can be disabled temporarily
2412 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002413 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002414 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2415 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002416 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2417 WCD9XXX_COND_HPH, false);
2418 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2419 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2420 WCD9XXX_COND_HPH, false);
2421 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002422
Joonwoo Park80a01172012-10-15 16:05:23 -07002423 if (mbhc->mbhc_cfg->detect_extn_cable &&
2424 !wcd9xxx_swch_level_remove(mbhc))
2425 wcd9xxx_hs_remove_irq_noswch(mbhc);
2426 else
2427 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002428
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002429 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2430 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2431 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002432 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2433 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002434 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2435 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2436 WCD9XXX_COND_HPH, true);
2437 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002438 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2439
2440 return IRQ_HANDLED;
2441}
2442
2443static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2444{
2445 bool is_mb_trigger, is_removal;
2446 struct wcd9xxx_mbhc *mbhc = data;
2447 struct snd_soc_codec *codec = mbhc->codec;
2448
2449 pr_debug("%s: enter\n", __func__);
2450 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002451 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002452
2453 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2454 0x10);
2455 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2456 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2457
2458 /* Turn off both HPH and MIC line schmitt triggers */
2459 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2460 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2461 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2462
Joonwoo Park80a01172012-10-15 16:05:23 -07002463 if (mbhc->mbhc_cfg->detect_extn_cable &&
2464 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2465 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2466 else
2467 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002468
2469 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2470 return IRQ_HANDLED;
2471}
2472
2473static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2474{
2475 struct delayed_work *dwork;
2476 short bias_value;
2477 int dce_mv, sta_mv;
2478 struct wcd9xxx_mbhc *mbhc;
2479
2480 pr_debug("%s:\n", __func__);
2481
2482 dwork = to_delayed_work(work);
2483 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2484
2485 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2486 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2487
2488 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2489 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2490 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2491
2492 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002493 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002494 mbhc->buttons_pressed);
2495
2496 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002497 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002498}
2499
2500static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2501{
2502 struct delayed_work *dwork;
2503 struct wcd9xxx_mbhc *mbhc;
2504 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002505 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002506
2507 dwork = to_delayed_work(work);
2508 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2509 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002510 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002511
2512 pr_debug("%s:\n", __func__);
2513
2514 /* Turn off both HPH and MIC line schmitt triggers */
2515 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2516 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2517 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002518 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002519 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002520 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002521}
2522
2523static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2524{
2525 u32 cfg_offset;
2526 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2527 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2528
2529 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2530 return false;
2531
2532 /*
2533 * Previous check guarantees that there is enough fw data up
2534 * to num_btn
2535 */
2536 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2537 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2538 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2539 return false;
2540
2541 /*
2542 * Previous check guarantees that there is enough fw data up
2543 * to start of impedance detection configuration
2544 */
2545 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2546 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2547
2548 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2549 return false;
2550
2551 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2552 return false;
2553
2554 return true;
2555}
2556
2557static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002558 enum meas_type dce, s16 vin_mv,
2559 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002560{
2561 s16 diff, zero;
2562 u32 mb_mv, in;
2563 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002564 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002565
2566 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002567 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002568
2569 if (mb_mv == 0) {
2570 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2571 return -EINVAL;
2572 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002573 if (cs_enable) {
2574 mb_mv = VDDIO_MICBIAS_MV;
2575 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2576 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002577
2578 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002579 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2580 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002581 } else {
2582 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2583 zero = (mbhc->mbhc_data.sta_z);
2584 }
2585 in = (u32) diff * vin_mv;
2586
2587 value = (u16) (in / mb_mv) + zero;
2588 return value;
2589}
2590
2591static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2592{
2593 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002594 s16 adj_v_hs_max;
2595 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 -07002596 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2597 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2598 u16 *btn_high;
2599 int i;
2600
2601 pr_debug("%s: enter\n", __func__);
2602 codec = mbhc->codec;
2603 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2604 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2605
Joonwoo Park73375212013-05-07 12:42:44 -07002606 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002607 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002608 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002609 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002610
2611 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2612 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2613
2614 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002615 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2616 true);
2617 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002618 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002619 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002620 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002621 mbhc->mbhc_data.v_inval_ins_low =
2622 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2623 false);
2624 mbhc->mbhc_data.v_inval_ins_high =
2625 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2626 false);
2627 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002628 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2629 WCD9XXX_V_CS_HS_MAX,
2630 true);
2631 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2632 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002633
2634 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2635 MBHC_BTN_DET_V_BTN_HIGH);
2636 for (i = 0; i < btn_det->num_btn; i++)
2637 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2638
Joonwoo Park73375212013-05-07 12:42:44 -07002639 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2640 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2641 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2642 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2643 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2644 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002645
Joonwoo Park73375212013-05-07 12:42:44 -07002646 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002647 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2648 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002649 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002650 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2651 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002652 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002653 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2654 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002655 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002656 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2657 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002658
Joonwoo Park73375212013-05-07 12:42:44 -07002659 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2660 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2661 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2662 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002663
Joonwoo Parka8890262012-10-15 12:04:27 -07002664 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2665
2666 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002667 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002668 pr_debug("%s: leave\n", __func__);
2669}
2670
2671static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2672{
2673 /*
2674 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2675 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2676 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2677 */
2678 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2679}
2680
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002681/*
2682 * Mic Bias Enable Decision
2683 * Return true if high_hph_cnt is a power of 2 (!= 2)
2684 * otherwise return false
2685 */
2686static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2687{
2688 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2689}
2690
Joonwoo Parka8890262012-10-15 12:04:27 -07002691static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2692{
2693 struct wcd9xxx_mbhc *mbhc;
2694 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002695 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002696 unsigned long timeout;
2697 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002698 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002699 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002700 bool current_source_enable;
2701 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002702
2703 pr_debug("%s: enter\n", __func__);
2704
2705 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2706 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002707 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2708 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002709
2710 wcd9xxx_onoff_ext_mclk(mbhc, true);
2711
2712 /*
2713 * Keep override on during entire plug type correction work.
2714 *
2715 * This is okay under the assumption that any switch irqs which use
2716 * MBHC block cancel and sync this work so override is off again
2717 * prior to switch interrupt handler's MBHC block usage.
2718 * Also while this correction work is running, we can guarantee
2719 * DAPM doesn't use any MBHC block as this work only runs with
2720 * headphone detection.
2721 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002722 if (current_source_enable)
2723 wcd9xxx_turn_onoff_current_source(mbhc, true,
2724 false);
2725 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002726 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002727
2728 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2729 while (!time_after(jiffies, timeout)) {
2730 ++retry;
2731 rmb();
2732 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002733 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002734 pr_debug("%s: stop requested\n", __func__);
2735 break;
2736 }
2737
2738 msleep(HS_DETECT_PLUG_INERVAL_MS);
2739 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002740 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002741 pr_debug("%s: Switch level is low\n", __func__);
2742 break;
2743 }
2744
2745 /* can race with removal interrupt */
2746 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002747 if (current_source_enable)
2748 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2749 highhph);
2750 else
2751 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002752 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2753
Joonwoo Park80a01172012-10-15 16:05:23 -07002754 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2755 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002756
2757 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2758 (highhph_cnt + 1) :
2759 0;
2760 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002761 if (plug_type == PLUG_TYPE_INVALID) {
2762 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002763 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2764 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002765 mbhc->current_plug == PLUG_TYPE_NONE) {
2766 wcd9xxx_report_plug(mbhc, 1,
2767 SND_JACK_HEADPHONE);
2768 }
2769 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2770 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002771 if (mbhc->mbhc_cfg->detect_extn_cable) {
2772 if (mbhc->current_plug != plug_type)
2773 wcd9xxx_report_plug(mbhc, 1,
2774 SND_JACK_HEADPHONE);
2775 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002776 wcd9xxx_report_plug(mbhc, 1,
2777 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002778 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002779 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2780 pr_debug("%s: High HPH detected, continue polling\n",
2781 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002782 } else {
2783 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2784 pt_gnd_mic_swap_cnt++;
2785 if (pt_gnd_mic_swap_cnt <
2786 GND_MIC_SWAP_THRESHOLD)
2787 continue;
2788 else if (pt_gnd_mic_swap_cnt >
2789 GND_MIC_SWAP_THRESHOLD) {
2790 /*
2791 * This is due to GND/MIC switch didn't
2792 * work, Report unsupported plug
2793 */
2794 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2795 /*
2796 * if switch is toggled, check again,
2797 * otherwise report unsupported plug
2798 */
2799 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2800 continue;
2801 }
2802 } else
2803 pt_gnd_mic_swap_cnt = 0;
2804
2805 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002806 /* Turn off override/current source */
2807 if (current_source_enable)
2808 wcd9xxx_turn_onoff_current_source(mbhc, false,
2809 false);
2810 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002811 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002812 /*
2813 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2814 */
2815 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2816 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2817 pr_debug("Attempt %d found correct plug %d\n", retry,
2818 plug_type);
2819 correction = true;
2820 break;
2821 }
2822 }
2823
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002824 highhph = false;
2825 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002826 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2827 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002828 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002829 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002830 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002831 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002832 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002833
2834 if (!correction && current_source_enable)
2835 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2836 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002837 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002838
2839 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002840
2841 if (mbhc->mbhc_cfg->detect_extn_cable) {
2842 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002843 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2844 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002845 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2846 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002847 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002848 /* Enable removal detection */
2849 wcd9xxx_cleanup_hs_polling(mbhc);
2850 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2851 }
2852 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2853 }
2854 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002855 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002856 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002857}
2858
2859static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2860{
2861 bool insert;
2862 bool is_removed = false;
2863 struct snd_soc_codec *codec = mbhc->codec;
2864
2865 pr_debug("%s: enter\n", __func__);
2866
2867 mbhc->in_swch_irq_handler = true;
2868 /* Wait here for debounce time */
2869 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2870
2871 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2872
2873 /* cancel pending button press */
2874 if (wcd9xxx_cancel_btn_work(mbhc))
2875 pr_debug("%s: button press is canceled\n", __func__);
2876
Santosh Mardic82bd362013-10-25 18:35:29 +05302877 /* cancel detect plug */
2878 wcd9xxx_cancel_hs_detect_plug(mbhc,
2879 &mbhc->correct_plug_swch);
2880
Joonwoo Parka8890262012-10-15 12:04:27 -07002881 insert = !wcd9xxx_swch_level_remove(mbhc);
2882 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2883 mbhc->current_plug, insert);
2884 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2885 mbhc->lpi_enabled = false;
2886 wmb();
2887
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002888 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2889 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2890 (1 << 1)))
2891 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002892
2893 /* Disable Mic Bias pull down and HPH Switch to GND */
2894 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2895 0x00);
2896 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2897 wcd9xxx_mbhc_detect_plug_type(mbhc);
2898 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2899 mbhc->lpi_enabled = false;
2900 wmb();
2901
Joonwoo Parka8890262012-10-15 12:04:27 -07002902 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2903 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2904 is_removed = true;
2905 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2906 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2907 is_removed = true;
2908 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2909 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302910 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002911 wcd9xxx_cleanup_hs_polling(mbhc);
2912 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2913 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002914 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2915 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2916 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002917 }
2918
2919 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002920 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2921 0x00);
2922 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2923 0x02, 0x00);
2924
Joonwoo Parka8890262012-10-15 12:04:27 -07002925 /* Enable Mic Bias pull down and HPH Switch to GND */
2926 snd_soc_update_bits(codec,
2927 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2928 0x01);
2929 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2930 0x01);
2931 /* Make sure mic trigger is turned off */
2932 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2933 0x01, 0x01);
2934 snd_soc_update_bits(codec,
2935 mbhc->mbhc_bias_regs.mbhc_reg,
2936 0x90, 0x00);
2937 /* Reset MBHC State Machine */
2938 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2939 0x08, 0x08);
2940 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2941 0x08, 0x00);
2942 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002943 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002944 }
2945 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002946exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002947 mbhc->in_swch_irq_handler = false;
2948 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2949 pr_debug("%s: leave\n", __func__);
2950}
2951
2952static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2953{
2954 int r = IRQ_HANDLED;
2955 struct wcd9xxx_mbhc *mbhc = data;
2956
2957 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002958 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002959 pr_warn("%s: failed to hold suspend\n", __func__);
2960 r = IRQ_NONE;
2961 } else {
2962 /* Call handler */
2963 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002964 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002965 }
2966
2967 pr_debug("%s: leave %d\n", __func__, r);
2968 return r;
2969}
2970
Joonwoo Park218e73f2013-08-21 16:22:18 -07002971static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002972{
Joonwoo Park73375212013-05-07 12:42:44 -07002973 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002974 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002975 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002976 const s16 v_ins_hu =
2977 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2978 const s16 v_ins_h =
2979 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2980 const s16 v_b1_hu =
2981 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2982 const s16 v_b1_h =
2983 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002984 const unsigned long timeout =
2985 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002986
Joonwoo Park218e73f2013-08-21 16:22:18 -07002987 while (time_before(jiffies, timeout)) {
2988 /*
2989 * This function needs to run measurements just few times during
2990 * release debounce time. Make 1ms interval to avoid
2991 * unnecessary excessive measurements.
2992 */
2993 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002994 if (i == 0) {
2995 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2996 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2997 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002998 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002999 r = 1;
3000 break;
3001 }
3002 } else {
3003 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
3004 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
3005 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003006 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003007 r = 1;
3008 break;
3009 }
3010 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003011 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003012 }
3013
3014 return r;
3015}
3016
3017/* called under codec_resource_lock acquisition */
3018static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3019 const s32 micmv)
3020{
3021 s16 *v_btn_low, *v_btn_high;
3022 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3023 int i, btn = -1;
3024
3025 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3026 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3027 MBHC_BTN_DET_V_BTN_LOW);
3028 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3029 MBHC_BTN_DET_V_BTN_HIGH);
3030
3031 for (i = 0; i < btn_det->num_btn; i++) {
3032 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3033 btn = i;
3034 break;
3035 }
3036 }
3037
3038 if (btn == -1)
3039 pr_debug("%s: couldn't find button number for mic mv %d\n",
3040 __func__, micmv);
3041
3042 return btn;
3043}
3044
3045static int wcd9xxx_get_button_mask(const int btn)
3046{
3047 int mask = 0;
3048 switch (btn) {
3049 case 0:
3050 mask = SND_JACK_BTN_0;
3051 break;
3052 case 1:
3053 mask = SND_JACK_BTN_1;
3054 break;
3055 case 2:
3056 mask = SND_JACK_BTN_2;
3057 break;
3058 case 3:
3059 mask = SND_JACK_BTN_3;
3060 break;
3061 case 4:
3062 mask = SND_JACK_BTN_4;
3063 break;
3064 case 5:
3065 mask = SND_JACK_BTN_5;
3066 break;
3067 case 6:
3068 mask = SND_JACK_BTN_6;
3069 break;
3070 case 7:
3071 mask = SND_JACK_BTN_7;
3072 break;
3073 }
3074 return mask;
3075}
3076
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003077static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003078{
3079 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003080 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003081 struct snd_soc_codec *codec = mbhc->codec;
3082
3083 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3084 /* Pull down micbias to ground and disconnect vddio switch */
3085 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3086 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3087 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3088 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3089
3090 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003091 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3092 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003093 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003094 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003095 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003096 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3097 }
3098 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003099 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003100 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3101 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003102
Joonwoo Park520a0f92013-05-14 19:39:58 -07003103 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003104 if (change)
3105 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3106 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003107 /* Disable pull down micbias to ground */
3108 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3109 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3110}
3111
Joonwoo Park218e73f2013-08-21 16:22:18 -07003112void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3113{
3114 const u16 sta_z = mbhc->mbhc_data.sta_z;
3115 const u16 dce_z = mbhc->mbhc_data.dce_z;
3116
3117 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3118 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3119 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3120 mbhc->mbhc_data.sta_z & 0xFFFF,
3121 mbhc->mbhc_data.dce_z & 0xFFFF);
3122
3123 wcd9xxx_mbhc_calc_thres(mbhc);
3124 wcd9xxx_calibrate_hs_polling(mbhc);
3125}
3126
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003127/*
3128 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3129 * to ceilmv + buffer
3130 */
3131static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3132{
3133 u16 v_brh, v_b1_hu;
3134 int mv;
3135 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3136 void *calibration = mbhc->mbhc_cfg->calibration;
3137 struct snd_soc_codec *codec = mbhc->codec;
3138
3139 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3140 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3141 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3142
3143 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003144 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003145 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3146 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3147 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003148 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003149 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3150 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3151 (v_brh >> 8) & 0xFF);
3152 return 0;
3153}
3154
Joonwoo Parka8890262012-10-15 12:04:27 -07003155irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3156{
3157 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003158 bool vddio;
3159 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003160 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003161 s32 stamv, stamv_s;
3162 s16 *v_btn_high;
3163 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003164 int btn = -1, meas = 0;
3165 struct wcd9xxx_mbhc *mbhc = data;
3166 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3167 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3168 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003169 short dce[d->n_btn_meas + 1], sta;
3170 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003171 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003172 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003173 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003174 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003175
3176 pr_debug("%s: enter\n", __func__);
3177
3178 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3179 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3180
3181 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3182 pr_debug("%s: mbhc is being recovered, skip button press\n",
3183 __func__);
3184 goto done;
3185 }
3186
3187 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3188
3189 if (!mbhc->polling_active) {
3190 pr_warn("%s: mbhc polling is not active, skip button press\n",
3191 __func__);
3192 goto done;
3193 }
3194
Joonwoo Parka8890262012-10-15 12:04:27 -07003195 /* If switch nterrupt already kicked in, ignore button press */
3196 if (mbhc->in_swch_irq_handler) {
3197 pr_debug("%s: Swtich level changed, ignore button press\n",
3198 __func__);
3199 btn = -1;
3200 goto done;
3201 }
3202
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303203 /*
3204 * setup internal micbias if codec uses internal micbias for
3205 * headset detection
3206 */
3207 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
3208 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3209 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3210 else
3211 pr_err("%s: internal bias requested but codec did not provide callback\n",
3212 __func__);
3213 }
3214
3215
Joonwoo Parka8890262012-10-15 12:04:27 -07003216 /* Measure scaled HW DCE */
3217 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3218 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003219
Joonwoo Park218e73f2013-08-21 16:22:18 -07003220 dce_z = mbhc->mbhc_data.dce_z;
3221 sta_z = mbhc->mbhc_data.sta_z;
3222
Joonwoo Parka8890262012-10-15 12:04:27 -07003223 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003224 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003225 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003226 if (mbhc_status != STATUS_REL_DETECTION) {
3227 if (mbhc->mbhc_last_resume &&
3228 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3229 pr_debug("%s: Button is released after resume\n",
3230 __func__);
3231 n_btn_meas = 0;
3232 } else {
3233 pr_debug("%s: Button is released without resume",
3234 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003235 if (mbhc->update_z) {
3236 wcd9xxx_update_z(mbhc);
3237 mbhc->update_z = false;
3238 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003239 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3240 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003241 if (vddio)
3242 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3243 false);
3244 else
3245 stamv_s = stamv;
3246 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003247 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003248 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3249 false) : mv[0];
3250 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003251 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3252 btn = -1;
3253 goto done;
3254 }
3255 }
3256
Joonwoo Park520a0f92013-05-14 19:39:58 -07003257 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3258 meas++)
3259 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3260
Joonwoo Park218e73f2013-08-21 16:22:18 -07003261 if (mbhc->update_z) {
3262 wcd9xxx_update_z(mbhc);
3263 mbhc->update_z = false;
3264 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003265
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003266 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3267 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003268 if (vddio)
3269 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3270 else
3271 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003272 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003273 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003274
3275 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003276 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3277 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003278 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3279 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003280 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003281 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003282 if (n_btn_meas == 0)
3283 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003284 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3285 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003286 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3287 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003288 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3289 mv[meas];
3290 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003291 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003292 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3293 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003294 /*
3295 * if large enough measurements are collected,
3296 * start to check if last all n_btn_con measurements were
3297 * in same button low/high range
3298 */
3299 if (meas + 1 >= d->n_btn_con) {
3300 for (i = 0; i < d->n_btn_con; i++)
3301 if ((btnmeas[meas] < 0) ||
3302 (btnmeas[meas] != btnmeas[meas - i]))
3303 break;
3304 if (i == d->n_btn_con) {
3305 /* button pressed */
3306 btn = btnmeas[meas];
3307 break;
3308 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3309 /*
3310 * if left measurements are less than n_btn_con,
3311 * it's impossible to find button number
3312 */
3313 break;
3314 }
3315 }
3316 }
3317
3318 if (btn >= 0) {
3319 if (mbhc->in_swch_irq_handler) {
3320 pr_debug(
3321 "%s: Switch irq triggered, ignore button press\n",
3322 __func__);
3323 goto done;
3324 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003325 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3326 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3327 MBHC_BTN_DET_V_BTN_HIGH);
3328 WARN_ON(btn >= btn_det->num_btn);
3329 /* reprogram release threshold to catch voltage ramp up early */
3330 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3331
Joonwoo Parka8890262012-10-15 12:04:27 -07003332 mask = wcd9xxx_get_button_mask(btn);
3333 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003334 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003335 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3336 msecs_to_jiffies(400)) == 0) {
3337 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003338 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003339 }
3340 } else {
3341 pr_debug("%s: bogus button press, too short press?\n",
3342 __func__);
3343 }
3344
3345 done:
3346 pr_debug("%s: leave\n", __func__);
3347 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3348 return IRQ_HANDLED;
3349}
3350
3351static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3352{
3353 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003354 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003355 struct wcd9xxx_mbhc *mbhc = data;
3356
3357 pr_debug("%s: enter\n", __func__);
3358 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3359 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3360
Joonwoo Parka8890262012-10-15 12:04:27 -07003361 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3362 ret = wcd9xxx_cancel_btn_work(mbhc);
3363 if (ret == 0) {
3364 pr_debug("%s: Reporting long button release event\n",
3365 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003366 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003367 mbhc->buttons_pressed);
3368 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003369 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003370 pr_debug("%s: Fake button press interrupt\n",
3371 __func__);
3372 } else {
3373 if (mbhc->in_swch_irq_handler) {
3374 pr_debug("%s: Switch irq kicked in, ignore\n",
3375 __func__);
3376 } else {
3377 pr_debug("%s: Reporting btn press\n",
3378 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003379 wcd9xxx_jack_report(mbhc,
3380 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003381 mbhc->buttons_pressed,
3382 mbhc->buttons_pressed);
3383 pr_debug("%s: Reporting btn release\n",
3384 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003385 wcd9xxx_jack_report(mbhc,
3386 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003387 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003388 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003389 }
3390 }
3391 }
3392
3393 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3394 }
3395
3396 wcd9xxx_calibrate_hs_polling(mbhc);
3397
Joonwoo Park218e73f2013-08-21 16:22:18 -07003398 if (waitdebounce)
3399 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003400 wcd9xxx_start_hs_polling(mbhc);
3401
3402 pr_debug("%s: leave\n", __func__);
3403 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3404 return IRQ_HANDLED;
3405}
3406
3407static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3408{
3409 struct wcd9xxx_mbhc *mbhc = data;
3410 struct snd_soc_codec *codec;
3411
3412 pr_info("%s: received HPHL OCP irq\n", __func__);
3413
3414 if (mbhc) {
3415 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003416 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3417 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003418 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003419 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003420 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3421 0x10, 0x00);
3422 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3423 0x10, 0x10);
3424 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003425 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003426 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003427 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003428 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003429 mbhc->hph_status,
3430 WCD9XXX_JACK_MASK);
3431 }
3432 } else {
3433 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3434 }
3435
3436 return IRQ_HANDLED;
3437}
3438
3439static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3440{
3441 struct wcd9xxx_mbhc *mbhc = data;
3442 struct snd_soc_codec *codec;
3443
3444 pr_info("%s: received HPHR OCP irq\n", __func__);
3445 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003446 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3447 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003448 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003449 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003450 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3451 0x00);
3452 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3453 0x10);
3454 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003455 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003456 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003457 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003458 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003459 mbhc->hph_status, WCD9XXX_JACK_MASK);
3460 }
3461
3462 return IRQ_HANDLED;
3463}
3464
3465static int wcd9xxx_acdb_mclk_index(const int rate)
3466{
3467 if (rate == MCLK_RATE_12288KHZ)
3468 return 0;
3469 else if (rate == MCLK_RATE_9600KHZ)
3470 return 1;
3471 else {
3472 BUG_ON(1);
3473 return -EINVAL;
3474 }
3475}
3476
3477static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3478{
3479 u32 dce_wait, sta_wait;
3480 u8 ncic, nmeas, navg;
3481 void *calibration;
3482 u8 *n_cic, *n_ready;
3483 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3484 u8 npoll = 4, nbounce_wait = 30;
3485 struct snd_soc_codec *codec = mbhc->codec;
3486 int idx = wcd9xxx_acdb_mclk_index(rate);
3487 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3488
3489 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3490 rate);
3491 calibration = mbhc->mbhc_cfg->calibration;
3492
3493 /*
3494 * First compute the DCE / STA wait times depending on tunable
3495 * parameters. The value is computed in microseconds
3496 */
3497 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3498 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3499 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3500 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3501 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3502
3503 /* ncic stays with the same what we had during calibration */
3504 ncic = n_cic[idxmclk];
3505 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3506 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3507 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003508 /* give extra margin to sta for safety */
3509 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003510 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3511 n_ready[idx]) + 10;
3512
3513 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3514 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3515
3516 if (rate == MCLK_RATE_12288KHZ) {
3517 npoll = 4;
3518 nbounce_wait = 30;
3519 } else if (rate == MCLK_RATE_9600KHZ) {
3520 npoll = 3;
3521 nbounce_wait = 23;
3522 }
3523
3524 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3525 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3526 pr_debug("%s: leave\n", __func__);
3527}
3528
3529static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3530{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003531 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003532 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003533 struct snd_soc_codec *codec = mbhc->codec;
3534
3535 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003536 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3537 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003538 wcd9xxx_turn_onoff_rel_detection(codec, false);
3539
3540 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3541 WARN_ON(!mbhc->mbhc_data.t_dce);
3542 WARN_ON(!mbhc->mbhc_data.t_sta);
3543
3544 /*
3545 * LDOH and CFILT are already configured during pdata handling.
3546 * Only need to make sure CFILT and bandgap are in Fast mode.
3547 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003548 *
3549 * In case when Micbias is powered by external source, request
3550 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003551 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003552 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3553 mbhc->mbhc_cb->enable_mb_source(codec, true);
3554
Joonwoo Parka8890262012-10-15 12:04:27 -07003555 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303556 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3557 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3558 else
3559 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3560 0x40, 0x00);
3561
Joonwoo Parka8890262012-10-15 12:04:27 -07003562 /*
3563 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3564 * to perform ADC calibration
3565 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303566 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3567 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3568 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003569 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3570 mbhc->mbhc_cfg->micbias << 5);
3571 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3572 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3573 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303574 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3575 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3576 else
3577 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3578 0x04, 0x04);
3579
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003580 /* Pull down micbias to ground */
3581 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3582 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3583 /* Disconnect override from micbias */
3584 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3585 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3586 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303587 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303588 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3589 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3590 else
3591 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3592 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003593 /*
3594 * Hardware that has external cap can delay mic bias ramping down up
3595 * to 50ms.
3596 */
3597 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003598 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003599 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003600 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003601 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003602
3603 /* compute dce_z for current source */
3604 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3605 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3606 WCD9XXX_MBHC_NSC_CS << 3);
3607
3608 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3609 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3610 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3611 false);
3612 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3613 mbhc->mbhc_data.dce_nsc_cs_z);
3614
3615 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3616
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003617 /* STA measurement for 0 voltage */
3618 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3619 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3620 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003621
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003622 /* Restore registers */
3623 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3624 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003625
3626 /* DCE measurment for MB voltage */
3627 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3628 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303629 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303630 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3631 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3632 else
3633 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3634 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003635 /*
3636 * Hardware that has external cap can delay mic bias ramping down up
3637 * to 50ms.
3638 */
3639 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003640 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3641 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3642 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3643
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003644 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003645 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3646 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3647 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303648 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303649 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3650 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3651 else
3652 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3653 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003654 /*
3655 * Hardware that has external cap can delay mic bias ramping down up
3656 * to 50ms.
3657 */
3658 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003659 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3660 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3661 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3662
3663 /* Restore default settings. */
3664 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303665 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303666 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303667 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3668 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3669 else
3670 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3671 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003672 usleep_range(100, 100);
3673
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003674 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3675 mbhc->mbhc_cb->enable_mb_source(codec, false);
3676
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003677 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3678 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003679 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003680
Joonwoo Parka8890262012-10-15 12:04:27 -07003681 pr_debug("%s: leave\n", __func__);
3682}
3683
3684static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3685{
3686 int n;
3687 u8 *gain;
3688 struct wcd9xxx_mbhc_general_cfg *generic;
3689 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3690 struct snd_soc_codec *codec = mbhc->codec;
3691 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3692
3693 pr_debug("%s: enter\n", __func__);
3694 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3695 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3696
3697 for (n = 0; n < 8; n++) {
3698 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3699 0x07, n);
3700 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3701 btn_det->c[n]);
3702 }
3703
3704 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3705 btn_det->nc);
3706
3707 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3708 generic->mbhc_nsa << 4);
3709
3710 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3711 btn_det->n_meas);
3712
3713 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3714 generic->mbhc_navg);
3715
3716 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3717
3718 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3719 btn_det->mbhc_nsc << 3);
3720
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003721 if (mbhc->mbhc_cb &&
3722 mbhc->mbhc_cb->get_cdc_type() !=
3723 WCD9XXX_CDC_TYPE_HELICON) {
3724 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3725 snd_soc_update_bits(codec,
3726 mbhc->resmgr->reg_addr->micb_4_mbhc,
3727 0x03, MBHC_MICBIAS2);
3728 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003729
3730 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3731
3732 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3733
3734 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3735 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3736 gain[idx] << 3);
3737
3738 pr_debug("%s: leave\n", __func__);
3739}
3740
3741static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3742{
3743 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003744 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303745
Joonwoo Parka8890262012-10-15 12:04:27 -07003746 if (mbhc->mbhc_cfg->gpio) {
3747 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3748 wcd9xxx_mech_plug_detect_irq,
3749 (IRQF_TRIGGER_RISING |
3750 IRQF_TRIGGER_FALLING |
3751 IRQF_DISABLED),
3752 "headset detect", mbhc);
3753 if (ret) {
3754 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3755 mbhc->mbhc_cfg->gpio_irq);
3756 } else {
3757 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3758 if (ret)
3759 pr_err("%s: Failed to enable wake up irq %d\n",
3760 __func__, mbhc->mbhc_cfg->gpio_irq);
3761 }
3762 } else if (mbhc->mbhc_cfg->insert_detect) {
3763 /* Enable HPHL_10K_SW */
3764 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3765 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303766
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003767 ret = wcd9xxx_request_irq(core_res,
3768 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003769 wcd9xxx_mech_plug_detect_irq,
3770 "Jack Detect",
3771 mbhc);
3772 if (ret)
3773 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003774 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003775 }
3776
3777 return ret;
3778}
3779
3780static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3781{
3782 int ret = 0;
3783 struct snd_soc_codec *codec = mbhc->codec;
3784
3785 pr_debug("%s: enter\n", __func__);
3786
3787 /* Enable MCLK during calibration */
3788 wcd9xxx_onoff_ext_mclk(mbhc, true);
3789 wcd9xxx_mbhc_setup(mbhc);
3790 wcd9xxx_mbhc_cal(mbhc);
3791 wcd9xxx_mbhc_calc_thres(mbhc);
3792 wcd9xxx_onoff_ext_mclk(mbhc, false);
3793 wcd9xxx_calibrate_hs_polling(mbhc);
3794
3795 /* Enable Mic Bias pull down and HPH Switch to GND */
3796 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3797 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3798 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3799
3800 if (!IS_ERR_VALUE(ret)) {
3801 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3802 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003803 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003804 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003805 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003806 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003807
3808 /* Initialize mechanical mbhc */
3809 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3810
3811 if (!ret && mbhc->mbhc_cfg->gpio) {
3812 /* Requested with IRQF_DISABLED */
3813 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3814
3815 /* Bootup time detection */
3816 wcd9xxx_swch_irq_handler(mbhc);
3817 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3818 pr_debug("%s: Setting up codec own insert detection\n",
3819 __func__);
3820 /* Setup for insertion detection */
3821 wcd9xxx_insert_detect_setup(mbhc, true);
3822 }
3823 }
3824
3825 pr_debug("%s: leave\n", __func__);
3826
3827 return ret;
3828}
3829
3830static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3831{
3832 struct delayed_work *dwork;
3833 struct wcd9xxx_mbhc *mbhc;
3834 struct snd_soc_codec *codec;
3835 const struct firmware *fw;
3836 int ret = -1, retry = 0;
3837
3838 dwork = to_delayed_work(work);
3839 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3840 codec = mbhc->codec;
3841
3842 while (retry < FW_READ_ATTEMPTS) {
3843 retry++;
3844 pr_info("%s:Attempt %d to request MBHC firmware\n",
3845 __func__, retry);
3846 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3847 codec->dev);
3848
3849 if (ret != 0) {
3850 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3851 } else {
3852 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3853 break;
3854 }
3855 }
3856
3857 if (ret != 0) {
3858 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3859 __func__);
3860 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3861 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3862 __func__);
3863 release_firmware(fw);
3864 } else {
3865 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3866 mbhc->mbhc_fw = fw;
3867 }
3868
3869 (void) wcd9xxx_init_and_calibrate(mbhc);
3870}
3871
3872#ifdef CONFIG_DEBUG_FS
3873ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3874 size_t count, loff_t *pos)
3875{
3876 const int size = 768;
3877 char buffer[size];
3878 int n = 0;
3879 struct wcd9xxx_mbhc *mbhc = file->private_data;
3880 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003881 const s16 v_ins_hu =
3882 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3883 const s16 v_ins_h =
3884 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3885 const s16 v_b1_hu =
3886 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3887 const s16 v_b1_h =
3888 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3889 const s16 v_br_h =
3890 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003891
Joonwoo Park520a0f92013-05-14 19:39:58 -07003892 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3893 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003894 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3895 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003896 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3897 p->dce_nsc_cs_z,
3898 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3899 p->dce_nsc_cs_z,
3900 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003901 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3902 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3903 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3904 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003905 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3906 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3907 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3908 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3909 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3910 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3911 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003912 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003913 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003914 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003915 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003916 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003917 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003918 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3919 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3920 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3921 p->v_no_mic,
3922 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3923 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3924 p->v_inval_ins_low);
3925 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3926 p->v_inval_ins_high);
3927 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3928 !wcd9xxx_swch_level_remove(mbhc));
3929 buffer[n] = 0;
3930
3931 return simple_read_from_buffer(buf, count, pos, buffer, n);
3932}
3933
3934static int codec_debug_open(struct inode *inode, struct file *file)
3935{
3936 file->private_data = inode->i_private;
3937 return 0;
3938}
3939
3940static ssize_t codec_debug_write(struct file *filp,
3941 const char __user *ubuf, size_t cnt,
3942 loff_t *ppos)
3943{
3944 char lbuf[32];
3945 char *buf;
3946 int rc;
3947 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3948
3949 if (cnt > sizeof(lbuf) - 1)
3950 return -EINVAL;
3951
3952 rc = copy_from_user(lbuf, ubuf, cnt);
3953 if (rc)
3954 return -EFAULT;
3955
3956 lbuf[cnt] = '\0';
3957 buf = (char *)lbuf;
3958 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3959 false : true;
3960 return rc;
3961}
3962
3963static const struct file_operations mbhc_trrs_debug_ops = {
3964 .open = codec_debug_open,
3965 .write = codec_debug_write,
3966};
3967
3968static const struct file_operations mbhc_debug_ops = {
3969 .open = codec_debug_open,
3970 .read = codec_mbhc_debug_read,
3971};
3972
3973static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3974{
3975 mbhc->debugfs_poke =
3976 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3977 &mbhc_trrs_debug_ops);
3978 mbhc->debugfs_mbhc =
3979 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3980 NULL, mbhc, &mbhc_debug_ops);
3981}
3982
3983static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3984{
3985 debugfs_remove(mbhc->debugfs_poke);
3986 debugfs_remove(mbhc->debugfs_mbhc);
3987}
3988#else
3989static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3990{
3991}
3992
3993static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3994{
3995}
3996#endif
3997
3998int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3999 struct wcd9xxx_mbhc_config *mbhc_cfg)
4000{
Simmi Pateriya95466b12013-05-09 20:08:46 +05304001 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004002 struct snd_soc_codec *codec = mbhc->codec;
4003
4004 pr_debug("%s: enter\n", __func__);
4005
4006 if (!codec) {
4007 pr_err("%s: no codec\n", __func__);
4008 return -EINVAL;
4009 }
4010
4011 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
4012 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
4013 pr_err("Error: unsupported clock rate %d\n",
4014 mbhc_cfg->mclk_rate);
4015 return -EINVAL;
4016 }
4017
4018 /* Save mbhc config */
4019 mbhc->mbhc_cfg = mbhc_cfg;
4020
4021 /* Get HW specific mbhc registers' address */
4022 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4023
4024 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304025 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4026 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4027 else
4028 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4029 0x40, WCD9XXX_CFILT_FAST_MODE);
4030
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004031 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004032 * setup internal micbias if codec uses internal micbias for
4033 * headset detection
4034 */
4035 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304036 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004037 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304038 mbhc->int_rbias_on = true;
4039 } else {
4040 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004041 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304042 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004043 }
4044
4045 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004046 * If codec has specific clock gating for MBHC,
4047 * remove the clock gate
4048 */
4049 if (mbhc->mbhc_cb &&
4050 mbhc->mbhc_cb->enable_clock_gate)
4051 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4052
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004053 if (!mbhc->mbhc_cfg->read_fw_bin ||
4054 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004055 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004056 } else {
4057 if (!mbhc->mbhc_fw)
4058 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4059 usecs_to_jiffies(FW_READ_TIMEOUT));
4060 else
4061 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4062 __func__, mbhc->mbhc_fw);
4063 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004064
4065 pr_debug("%s: leave %d\n", __func__, rc);
4066 return rc;
4067}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004068EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004069
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004070void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4071{
4072 if (mbhc->mbhc_fw) {
4073 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4074 release_firmware(mbhc->mbhc_fw);
4075 mbhc->mbhc_fw = NULL;
4076 }
4077}
4078EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4079
Joonwoo Parka8890262012-10-15 12:04:27 -07004080static enum wcd9xxx_micbias_num
4081wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4082{
4083 enum wcd9xxx_micbias_num ret;
4084 switch (event) {
4085 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004086 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4087 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4088 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004089 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004090 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004091 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004092 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4093 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4094 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004095 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004096 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004097 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004098 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4099 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4100 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004101 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004102 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004103 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004104 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4105 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4106 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004107 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004108 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004109 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004110 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004111 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004112 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004113 }
4114 return ret;
4115}
4116
4117static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4118{
4119 int ret;
4120 switch (event) {
4121 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4122 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4123 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4124 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4125 ret = WCD9XXX_CFILT1_SEL;
4126 break;
4127 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4128 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4129 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4130 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4131 ret = WCD9XXX_CFILT2_SEL;
4132 break;
4133 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4134 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4135 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4136 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4137 ret = WCD9XXX_CFILT3_SEL;
4138 break;
4139 default:
4140 ret = -1;
4141 }
4142 return ret;
4143}
4144
4145static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4146{
4147 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004148 const struct wcd9xxx_micbias_setting *mb_pdata =
4149 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004150
4151 switch (mbhc->mbhc_cfg->micbias) {
4152 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004153 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004154 break;
4155 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004156 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004157 break;
4158 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004159 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004160 break;
4161 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004162 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004163 break;
4164 default:
4165 cfilt = MBHC_MICBIAS_INVALID;
4166 break;
4167 }
4168 return cfilt;
4169}
4170
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004171static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4172{
4173 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4174 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4175 else
4176 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4177 0x40, on ? 0x40 : 0x00);
4178}
4179
Joonwoo Parka8890262012-10-15 12:04:27 -07004180static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4181 void *data)
4182{
4183 int ret = 0;
4184 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4185 struct snd_soc_codec *codec = mbhc->codec;
4186 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4187
4188 pr_debug("%s: enter event %s(%d)\n", __func__,
4189 wcd9xxx_get_event_string(event), event);
4190
4191 switch (event) {
4192 /* MICBIAS usage change */
4193 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4194 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4195 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4196 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304197 mbhc->int_rbias_on = true;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004198 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4199 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004200 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004201 /*
4202 * Enable MBHC TxFE whenever micbias is
4203 * turned ON and polling is active
4204 */
4205 if (mbhc->polling_active)
4206 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4207 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004208 break;
4209 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4210 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4211 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4212 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004213 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004214 wcd9xxx_event_to_micbias(event) &&
4215 wcd9xxx_mbhc_polling(mbhc)) {
4216 /* if polling is on, restart it */
4217 wcd9xxx_pause_hs_polling(mbhc);
4218 wcd9xxx_start_hs_polling(mbhc);
4219 }
4220 break;
4221 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4222 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4223 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4224 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304225 mbhc->int_rbias_on = false;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004226 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4227 wcd9xxx_event_to_micbias(event)) {
4228 if (mbhc->event_state &
4229 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4230 wcd9xxx_switch_micbias(mbhc, 1);
4231 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304232 * Disable MBHC TxFE, in case it was enabled earlier
4233 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004234 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304235 if (!mbhc->polling_active)
4236 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004237 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004238 break;
4239 /* PA usage change */
4240 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004241 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004242 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004243 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004244 wcd9xxx_switch_micbias(mbhc, 1);
4245 break;
4246 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004247 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004248 break;
4249 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004250 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004251 /* if HPH PAs are off, report OCP and switch back to CFILT */
4252 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4253 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4254 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4255 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004256 if (!(mbhc->event_state &
4257 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4258 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004259 break;
4260 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004261 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004262 /* if HPH PAs are off, report OCP and switch back to CFILT */
4263 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4264 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4265 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4266 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004267 if (!(mbhc->event_state &
4268 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4269 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004270 break;
4271 /* Clock usage change */
4272 case WCD9XXX_EVENT_PRE_MCLK_ON:
4273 break;
4274 case WCD9XXX_EVENT_POST_MCLK_ON:
4275 /* Change to lower TxAAF frequency */
4276 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4277 1 << 4);
4278 /* Re-calibrate clock rate dependent values */
4279 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4280 /* If clock source changes, stop and restart polling */
4281 if (wcd9xxx_mbhc_polling(mbhc)) {
4282 wcd9xxx_calibrate_hs_polling(mbhc);
4283 wcd9xxx_start_hs_polling(mbhc);
4284 }
4285 break;
4286 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4287 /* If clock source changes, stop and restart polling */
4288 if (wcd9xxx_mbhc_polling(mbhc))
4289 wcd9xxx_pause_hs_polling(mbhc);
4290 break;
4291 case WCD9XXX_EVENT_POST_MCLK_OFF:
4292 break;
4293 case WCD9XXX_EVENT_PRE_RCO_ON:
4294 break;
4295 case WCD9XXX_EVENT_POST_RCO_ON:
4296 /* Change to higher TxAAF frequency */
4297 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4298 0 << 4);
4299 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004300 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004301 /* If clock source changes, stop and restart polling */
4302 if (wcd9xxx_mbhc_polling(mbhc)) {
4303 wcd9xxx_calibrate_hs_polling(mbhc);
4304 wcd9xxx_start_hs_polling(mbhc);
4305 }
4306 break;
4307 case WCD9XXX_EVENT_PRE_RCO_OFF:
4308 /* If clock source changes, stop and restart polling */
4309 if (wcd9xxx_mbhc_polling(mbhc))
4310 wcd9xxx_pause_hs_polling(mbhc);
4311 break;
4312 case WCD9XXX_EVENT_POST_RCO_OFF:
4313 break;
4314 /* CFILT usage change */
4315 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4316 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4317 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4318 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4319 wcd9xxx_event_to_cfilt(event))
4320 /*
4321 * Switch CFILT to slow mode if MBHC CFILT is being
4322 * used.
4323 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304324 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004325 break;
4326 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4327 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4328 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4329 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4330 wcd9xxx_event_to_cfilt(event))
4331 /*
4332 * Switch CFILT to fast mode if MBHC CFILT is not
4333 * used anymore.
4334 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304335 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004336 break;
4337 /* System resume */
4338 case WCD9XXX_EVENT_POST_RESUME:
4339 mbhc->mbhc_last_resume = jiffies;
4340 break;
4341 /* BG mode chage */
4342 case WCD9XXX_EVENT_PRE_BG_OFF:
4343 case WCD9XXX_EVENT_POST_BG_OFF:
4344 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4345 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4346 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4347 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4348 /* Not used for now */
4349 break;
4350 default:
4351 WARN(1, "Unknown event %d\n", event);
4352 ret = -EINVAL;
4353 }
4354
4355 pr_debug("%s: leave\n", __func__);
4356
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304357 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004358}
4359
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004360static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4361 uint32_t *zr)
4362{
4363 int i;
4364 int ret = 0;
4365 s16 l[3], r[3];
4366 s16 *z[] = {
4367 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4368 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004369 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004370 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004371 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4372 /* Phase 1 */
4373 /* Set MBHC_MUX for HPHL without ical */
4374 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4375 /* Set MBHC_MUX for HPHR without ical */
4376 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4377 /* Set MBHC_MUX for HPHR with ical */
4378 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4379 /* Set MBHC_MUX for HPHL with ical */
4380 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4381
4382 /* Phase 2 */
4383 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4384 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4385 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4386 };
4387
4388 pr_debug("%s: enter\n", __func__);
4389 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4390
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004391 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4392 !mbhc->mbhc_cb->compute_impedance || !zl ||
4393 !zr)
4394 return -EINVAL;
4395
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004396 /*
4397 * Impedance detection is an intrusive function as it mutes RX paths,
4398 * enable PAs and etc. Therefore codec drvier including ALSA
4399 * shouldn't read and write hardware registers during detection.
4400 */
4401 mutex_lock(&codec->mutex);
4402
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004403 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004404
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004405 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004406 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004407
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004408 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4409 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004410
4411 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004412 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004413 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4414 reg_set_mux[i].mask,
4415 reg_set_mux[i].val);
4416 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4417 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4418 else
4419 snd_soc_update_bits(codec,
4420 WCD9XXX_A_MBHC_SCALING_MUX_1,
4421 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004422 /* 25us is required after mux change to settle down */
4423 usleep_range(mux_wait_us,
4424 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004425 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004426 }
4427
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004428 /* Codec specific setup for L2 and R2 measurements */
4429 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004430
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004431 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004432 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4433 reg_set_mux[i].mask,
4434 reg_set_mux[i].val);
4435 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4436 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4437 else
4438 snd_soc_update_bits(codec,
4439 WCD9XXX_A_MBHC_SCALING_MUX_1,
4440 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004441 /* 25us is required after mux change to settle down */
4442 usleep_range(mux_wait_us,
4443 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004444 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004445 }
4446
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004447 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004448
4449 mutex_unlock(&codec->mutex);
4450
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004451 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004452
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004453 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004454 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004455
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004456 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004457 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004458 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4459 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004460 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004461 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004462 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4463 pr_debug("%s: Impedance detection completed\n", __func__);
4464
4465 return ret;
4466}
4467
4468int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4469 uint32_t *zr)
4470{
4471 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4472 *zl = mbhc->zl;
4473 *zr = mbhc->zr;
4474 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4475
4476 if (*zl && *zr)
4477 return 0;
4478 else
4479 return -EINVAL;
4480}
4481
Joonwoo Parka8890262012-10-15 12:04:27 -07004482/*
4483 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4484 *
4485 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4486 */
4487int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004488 struct snd_soc_codec *codec,
4489 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004490 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4491 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4492 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004493 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004494{
4495 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004496 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004497
4498 pr_debug("%s: enter\n", __func__);
4499 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4500 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4501
4502 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4503 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4504 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4505 mbhc->mbhc_micbias_switched = false;
4506 mbhc->polling_active = false;
4507 mbhc->mbhc_state = MBHC_STATE_NONE;
4508 mbhc->in_swch_irq_handler = false;
4509 mbhc->current_plug = PLUG_TYPE_NONE;
4510 mbhc->lpi_enabled = false;
4511 mbhc->no_mic_headset_override = false;
4512 mbhc->mbhc_last_resume = 0;
4513 mbhc->codec = codec;
4514 mbhc->resmgr = resmgr;
4515 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004516 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004517 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304518 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004519 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004520 mbhc->impedance_detect = impedance_det_en;
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304521 mbhc->int_rbias_on = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07004522
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004523 if (mbhc->intr_ids == NULL) {
4524 pr_err("%s: Interrupt mapping not provided\n", __func__);
4525 return -EINVAL;
4526 }
4527
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004528 if (mbhc->headset_jack.jack == NULL) {
4529 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4530 &mbhc->headset_jack);
4531 if (ret) {
4532 pr_err("%s: Failed to create new jack\n", __func__);
4533 return ret;
4534 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004535
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004536 ret = snd_soc_jack_new(codec, "Button Jack",
4537 WCD9XXX_JACK_BUTTON_MASK,
4538 &mbhc->button_jack);
4539 if (ret) {
4540 pr_err("Failed to create new jack\n");
4541 return ret;
4542 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004543
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004544 ret = snd_jack_set_key(mbhc->button_jack.jack,
4545 SND_JACK_BTN_0,
4546 KEY_MEDIA);
4547 if (ret) {
4548 pr_err("%s: Failed to set code for btn-0\n",
4549 __func__);
4550 return ret;
4551 }
4552
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004553 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4554 wcd9xxx_mbhc_fw_read);
4555 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4556 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4557 wcd9xxx_mbhc_insert_work);
4558 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004559
4560 /* Register event notifier */
4561 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4562 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4563 if (ret) {
4564 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4565 return ret;
4566 }
4567
4568 wcd9xxx_init_debugfs(mbhc);
4569
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004570
4571 /* Disable Impedance detection by default for certain codec types */
4572 if (mbhc->mbhc_cb &&
4573 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4574 impedance_detect_en = 0;
4575 else
4576 impedance_detect_en = impedance_det_en ? 1 : 0;
4577
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004578 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004579 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004580 wcd9xxx_hs_insert_irq,
4581 "Headset insert detect", mbhc);
4582 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004583 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004584 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004585 goto err_insert_irq;
4586 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004587 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004588
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004589 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004590 wcd9xxx_hs_remove_irq,
4591 "Headset remove detect", mbhc);
4592 if (ret) {
4593 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004594 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004595 goto err_remove_irq;
4596 }
4597
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004598 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004599 wcd9xxx_dce_handler, "DC Estimation detect",
4600 mbhc);
4601 if (ret) {
4602 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004603 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004604 goto err_potential_irq;
4605 }
4606
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004607 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004608 wcd9xxx_release_handler,
4609 "Button Release detect", mbhc);
4610 if (ret) {
4611 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004612 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004613 goto err_release_irq;
4614 }
4615
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004616 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004617 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4618 mbhc);
4619 if (ret) {
4620 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004621 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004622 goto err_hphl_ocp_irq;
4623 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004624 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004625
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004626 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004627 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4628 mbhc);
4629 if (ret) {
4630 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004631 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004632 goto err_hphr_ocp_irq;
4633 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004634 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004635
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004636 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4637 1 << WCD9XXX_COND_HPH);
4638
Joonwoo Parka8890262012-10-15 12:04:27 -07004639 pr_debug("%s: leave ret %d\n", __func__, ret);
4640 return ret;
4641
4642err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004643 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004644err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004645 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004646err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004647 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004648err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004649 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004650err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004651 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004652err_insert_irq:
4653 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4654
4655 pr_debug("%s: leave ret %d\n", __func__, ret);
4656 return ret;
4657}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004658EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004659
4660void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4661{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004662 struct wcd9xxx_core_resource *core_res =
4663 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004664
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004665 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4666 1 << WCD9XXX_COND_HPH);
4667
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004668 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4669 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4670 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4671 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4672 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4673 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4674 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004675
Joonwoo Parka8890262012-10-15 12:04:27 -07004676 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004677 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004678}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004679EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004680
4681MODULE_DESCRIPTION("wcd9xxx MBHC module");
4682MODULE_LICENSE("GPL v2");