blob: 40d67b39becf317b66fda916619d9ccc72a6a6ee [file] [log] [blame]
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070021#include <linux/list.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070022#include <linux/mfd/wcd9xxx/core.h>
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070023#include <linux/mfd/wcd9xxx/core-resource.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070024#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
25#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
26#include <linux/mfd/wcd9xxx/pdata.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <linux/bitops.h>
33#include <linux/delay.h>
34#include <linux/pm_runtime.h>
35#include <linux/kernel.h>
36#include <linux/gpio.h>
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -070037#include <linux/input.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070038#include "wcd9320.h"
Simmi Pateriya0a44d842013-04-03 01:12:42 +053039#include "wcd9306.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070040#include "wcd9xxx-mbhc.h"
41#include "wcd9xxx-resmgr.h"
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070042#include "wcd9xxx-common.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070043
44#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
Joonwoo Park80a01172012-10-15 16:05:23 -070045 SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
46 SND_JACK_UNSUPPORTED)
Joonwoo Parka8890262012-10-15 12:04:27 -070047#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
48 SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
49 SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
50 SND_JACK_BTN_6 | SND_JACK_BTN_7)
51
52#define NUM_DCE_PLUG_DETECT 3
Joonwoo Parkccccba72013-04-26 11:19:46 -070053#define NUM_DCE_PLUG_INS_DETECT 5
Joonwoo Parka8890262012-10-15 12:04:27 -070054#define NUM_ATTEMPTS_INSERT_DETECT 25
55#define NUM_ATTEMPTS_TO_REPORT 5
56
57#define FAKE_INS_LOW 10
58#define FAKE_INS_HIGH 80
59#define FAKE_INS_HIGH_NO_SWCH 150
60#define FAKE_REMOVAL_MIN_PERIOD_MS 50
61#define FAKE_INS_DELTA_SCALED_MV 300
62
63#define BUTTON_MIN 0x8000
64#define STATUS_REL_DETECTION 0x0C
65
66#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
67#define HS_DETECT_PLUG_INERVAL_MS 100
68#define SWCH_REL_DEBOUNCE_TIME_MS 50
69#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070070#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070071
72#define GND_MIC_SWAP_THRESHOLD 2
73#define OCP_ATTEMPT 1
74
75#define FW_READ_ATTEMPTS 15
76#define FW_READ_TIMEOUT 2000000
77
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070078#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070079
80#define MCLK_RATE_12288KHZ 12288000
81#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070082
83#define DEFAULT_DCE_STA_WAIT 55
84#define DEFAULT_DCE_WAIT 60000
85#define DEFAULT_STA_WAIT 5000
86
87#define VDDIO_MICBIAS_MV 1800
88
Joonwoo Parkccccba72013-04-26 11:19:46 -070089#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
90
Joonwoo Park20bc9da2013-01-16 12:58:06 -080091#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070092#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Joonwoo Park20bc9da2013-01-16 12:58:06 -080093#define WCD9XXX_MEAS_DELTA_MAX_MV 50
Joonwoo Park141d6182013-03-05 12:25:46 -080094#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
95#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070096
97/*
98 * Invalid voltage range for the detection
99 * of plug type with current source
100 */
101#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 110
102#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
103
104/*
105 * Threshold used to detect euro headset
106 * with current source
107 */
108#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
109#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
110
111#define WCD9XXX_MBHC_NSC_CS 9
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800112#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
Joonwoo Park479347a2013-04-15 18:01:05 -0700113#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
Joonwoo Parkccccba72013-04-26 11:19:46 -0700114#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800115
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700116#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
117
118/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
119#define WCD9XXX_WG_TIME_FACTOR_US 240
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800120
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700121#define WCD9XXX_V_CS_HS_MAX 500
122#define WCD9XXX_V_CS_NO_MIC 5
123#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
124#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 10
125
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700126static int impedance_detect_en;
127module_param(impedance_detect_en, int,
128 S_IRUGO | S_IWUSR | S_IWGRP);
129MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
130
Joonwoo Parkccccba72013-04-26 11:19:46 -0700131static bool detect_use_vddio_switch = true;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700216 s16 v_brh, v_b1_hu;
Joonwoo Parka8890262012-10-15 12:04:27 -0700217 struct snd_soc_codec *codec = mbhc->codec;
218 int mbhc_state = mbhc->mbhc_state;
219
220 pr_debug("%s: enter\n", __func__);
221 if (!mbhc->polling_active) {
222 pr_debug("Polling is not active, do not start polling\n");
223 return;
224 }
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530225 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530226 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
227 mbhc->mbhc_cb->enable_mux_bias_block(codec);
228 else
229 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
230 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530231
Joonwoo Parka8890262012-10-15 12:04:27 -0700232 if (!mbhc->no_mic_headset_override &&
233 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530234 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700235 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
236 /* set to max button press threshold */
237 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
238 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
239 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
240 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
241 /* set to max */
242 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
243 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700244
245 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
246 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
247 (v_brh >> 8) & 0xFF);
248 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
249 v_brh & 0xFF);
250 v_b1_hu = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
252 v_b1_hu & 0xFF);
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
254 (v_b1_hu >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700255 }
256
257 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
258 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
260 pr_debug("%s: leave\n", __func__);
261}
262
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700263static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
264 unsigned int cfilt_mv)
265{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700266 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
267}
268
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700269/*
270 * called under codec_resource_lock acquisition
271 * return old status
272 */
273static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700274 int vddio_switch, bool restartpolling,
275 bool checkpolling)
276{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700277 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700278 int cfilt_k_val;
279 bool override;
280 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700281 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700282
283 codec = mbhc->codec;
284
Joonwoo Parkccccba72013-04-26 11:19:46 -0700285 if (mbhc->micbias_enable) {
286 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700287 ret = mbhc->mbhc_micbias_switched;
288 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700289 }
290
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700291 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700292 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
293 (!checkpolling || mbhc->polling_active)) {
294 if (restartpolling)
295 wcd9xxx_pause_hs_polling(mbhc);
296 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
297 0x04;
298 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700299 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700300 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700301 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700302 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700303 VDDIO_MICBIAS_MV);
304 usleep_range(10000, 10000);
305 snd_soc_update_bits(codec,
306 mbhc->mbhc_bias_regs.cfilt_val,
307 0xFC, (cfilt_k_val << 2));
308 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700309 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700310 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700311 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700312 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700313 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700314 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700315 /* Threshods for button press */
316 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
317 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
318 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
319 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
320 0xFF);
321 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
322 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
323 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
324 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
325 0xFF);
326 /* Threshods for button release */
327 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
328 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
329 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
330 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700331 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
332 __func__);
333 }
334
335 /* Enable MIC BIAS Switch to VDDIO */
336 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
337 0x80, 0x80);
338 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
339 0x10, 0x00);
340 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700341 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700342 if (restartpolling)
343 wcd9xxx_start_hs_polling(mbhc);
344
345 mbhc->mbhc_micbias_switched = true;
346 pr_debug("%s: VDDIO switch enabled\n", __func__);
347 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
348 if ((!checkpolling || mbhc->polling_active) &&
349 restartpolling)
350 wcd9xxx_pause_hs_polling(mbhc);
351 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700352 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700353 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700354 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700355 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700356 snd_soc_update_bits(codec,
357 mbhc->mbhc_bias_regs.cfilt_val,
358 0xFC, (cfilt_k_val << 2));
359 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700360 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700361 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700362 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700363 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700364 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
365 0xFF);
366 /* Revert threshods for button press */
367 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
368 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
369 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
370 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
371 0xFF);
372 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
373 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
375 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
376 0xFF);
377 /* Revert threshods for button release */
378 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
379 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
380 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
381 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700382 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
383 __func__);
384 }
385
386 /* Disable MIC BIAS Switch to VDDIO */
387 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
388 0x00);
389 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
390 0x00);
391
392 if ((!checkpolling || mbhc->polling_active) && restartpolling)
393 wcd9xxx_start_hs_polling(mbhc);
394
395 mbhc->mbhc_micbias_switched = false;
396 pr_debug("%s: VDDIO switch disabled\n", __func__);
397 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700398
399 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700400}
401
402static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
403{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700404 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700405}
406
Joonwoo Park73375212013-05-07 12:42:44 -0700407static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
408 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700409{
Joonwoo Park73375212013-05-07 12:42:44 -0700410 enum mbhc_v_index vidx;
411 s16 ret = -EINVAL;
412
Joonwoo Parka8890262012-10-15 12:04:27 -0700413 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
414 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700415 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700416 else
Joonwoo Park73375212013-05-07 12:42:44 -0700417 vidx = MBHC_V_IDX_CFILT;
418
419 switch (idx) {
420 case WCD9XXX_CURRENT_V_INS_H:
421 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
422 break;
423 case WCD9XXX_CURRENT_V_INS_HU:
424 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
425 break;
426 case WCD9XXX_CURRENT_V_B1_H:
427 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
428 break;
429 case WCD9XXX_CURRENT_V_B1_HU:
430 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
431 break;
432 case WCD9XXX_CURRENT_V_BR_H:
433 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
434 break;
435 }
436
437 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700438}
439
440void *wcd9xxx_mbhc_cal_btn_det_mp(
441 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
442 const enum wcd9xxx_mbhc_btn_det_mem mem)
443{
444 void *ret = &btn_det->_v_btn_low;
445
446 switch (mem) {
447 case MBHC_BTN_DET_GAIN:
448 ret += sizeof(btn_det->_n_cic);
449 case MBHC_BTN_DET_N_CIC:
450 ret += sizeof(btn_det->_n_ready);
451 case MBHC_BTN_DET_N_READY:
452 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
453 case MBHC_BTN_DET_V_BTN_HIGH:
454 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
455 case MBHC_BTN_DET_V_BTN_LOW:
456 /* do nothing */
457 break;
458 default:
459 ret = NULL;
460 }
461
462 return ret;
463}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700464EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700465
466static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
467{
468 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700469 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
470 WCD9XXX_CURRENT_V_INS_HU);
471 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
472 WCD9XXX_CURRENT_V_B1_HU);
473 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
474 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700475
476 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
477 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
478 (v_ins_hu >> 8) & 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700479 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700480 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700481 (v_b1_hu >> 8) & 0xFF);
482 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700483 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700484 (v_b1_h >> 8) & 0xFF);
485 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700486 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700487 (v_brh >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700488 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
489 mbhc->mbhc_data.v_brl & 0xFF);
490 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
491 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
492}
493
Simmi Pateriya95466b12013-05-09 20:08:46 +0530494static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700495 bool fast)
496{
497 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530498 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700499
Simmi Pateriya95466b12013-05-09 20:08:46 +0530500 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
501 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530502 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530503 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700504 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530505 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700506 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700507
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700508 cfilt_mode.reg_mask = 0x40;
509 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530510 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700511 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700512
513 if (cfilt_mode.cur_mode_val
514 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530515 if (mbhc->polling_active)
516 wcd9xxx_pause_hs_polling(mbhc);
517 snd_soc_update_bits(codec,
518 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700519 cfilt_mode.reg_mask,
520 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530521 if (mbhc->polling_active)
522 wcd9xxx_start_hs_polling(mbhc);
523 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700524 cfilt_mode.cur_mode_val,
525 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530526 } else {
527 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700528 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530529 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700530}
531
Joonwoo Park3699ca32013-02-08 12:06:15 -0800532static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
533 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700534{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800535 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800536 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
537 WCD9XXX_COND_HPH_MIC,
538 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800539 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
540 WCD9XXX_COND_HPH,
541 status & SND_JACK_HEADPHONE);
542 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800543
Joonwoo Parka8890262012-10-15 12:04:27 -0700544 snd_soc_jack_report_no_dapm(jack, status, mask);
545}
546
547static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
548 int irq)
549{
550 struct snd_soc_codec *codec;
551
552 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
553 codec = mbhc->codec;
554 if (mbhc->hph_status & jack_status) {
555 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800556 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700557 mbhc->hph_status, WCD9XXX_JACK_MASK);
558 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
559 0x00);
560 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
561 0x10);
562 /*
563 * reset retry counter as PA is turned off signifying
564 * start of new OCP detection session
565 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700566 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700567 mbhc->hphlocp_cnt = 0;
568 else
569 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700570 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700571 }
572}
573
574static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
575{
576 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700577 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700578}
579
580static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
581{
582 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700583 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700584}
585
586static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
587 struct mbhc_micbias_regs *micbias_regs)
588{
589 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700590 struct wcd9xxx_micbias_setting *micbias_pdata =
591 mbhc->resmgr->micbias_pdata;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700592
Joonwoo Parka8890262012-10-15 12:04:27 -0700593 switch (mbhc->mbhc_cfg->micbias) {
594 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700595 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700596 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
597 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
598 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
599 break;
600 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700601 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700602 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
603 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
604 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
605 break;
606 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700607 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700608 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
609 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
610 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
611 break;
612 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700613 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700614 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
615 micbias_regs->int_rbias =
616 mbhc->resmgr->reg_addr->micb_4_int_rbias;
617 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
618 break;
619 default:
620 /* Should never reach here */
621 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
622 return;
623 }
624
625 micbias_regs->cfilt_sel = cfilt;
626
627 switch (cfilt) {
628 case WCD9XXX_CFILT1_SEL:
629 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
630 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700631 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700632 break;
633 case WCD9XXX_CFILT2_SEL:
634 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
635 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700636 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700637 break;
638 case WCD9XXX_CFILT3_SEL:
639 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
640 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700641 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700642 break;
643 }
644}
645
646static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
647{
648 bool pa_turned_on = false;
649 struct snd_soc_codec *codec = mbhc->codec;
650 u8 wg_time;
651
652 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
653 wg_time += 1;
654
655 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
656 &mbhc->hph_pa_dac_state)) {
657 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
658 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
659 0xC0, 0xC0);
660 }
661 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
662 &mbhc->hph_pa_dac_state)) {
663 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
664 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700665 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700666 }
667
668 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
669 &mbhc->hph_pa_dac_state)) {
670 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
671 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
672 1 << 4);
673 pa_turned_on = true;
674 }
675 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
676 &mbhc->hph_pa_dac_state)) {
677 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
678 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
679 << 5);
680 pa_turned_on = true;
681 }
682
683 if (pa_turned_on) {
684 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
685 __func__);
686 usleep_range(wg_time * 1000, wg_time * 1000);
687 }
688}
689
690static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
691{
692 int r;
693 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
694 if (r)
695 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
696 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700697 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700698 return r;
699}
700
701static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
702{
703 u8 hph_reg_val = 0;
704 if (left)
705 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
706 else
707 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
708
709 return (hph_reg_val & 0xC0) ? true : false;
710}
711
712static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
713{
714 u8 hph_reg_val = 0;
715 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
716
717 return (hph_reg_val & 0x30) ? true : false;
718}
719
720/* called under codec_resource_lock acquisition */
721static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
722{
723 u8 wg_time;
724 struct snd_soc_codec *codec = mbhc->codec;
725
726 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
727 wg_time += 1;
728
729 /* If headphone PA is on, check if userspace receives
730 * removal event to sync-up PA's state */
731 if (wcd9xxx_is_hph_pa_on(codec)) {
732 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
733 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
734 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
735 } else {
736 pr_debug("%s PA is off\n", __func__);
737 }
738
739 if (wcd9xxx_is_hph_dac_on(codec, 1))
740 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
741 if (wcd9xxx_is_hph_dac_on(codec, 0))
742 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
743
744 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800745 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700746 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
747 usleep_range(wg_time * 1000, wg_time * 1000);
748}
749
750static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
751{
752 if (!mbhc->mbhc_cfg->insert_detect)
753 return;
754 pr_debug("%s: Setting up %s detection\n", __func__,
755 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700756 /* Disable detection to avoid glitch */
757 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530758 if (mbhc->mbhc_cfg->gpio_level_insert)
759 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
760 (0x68 | (ins ? (1 << 1) : 0)));
761 else
762 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
763 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700764 /* Re-enable detection */
765 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700766}
767
768/* called under codec_resource_lock acquisition */
769static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
770 enum snd_jack_types jack_type)
771{
772 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
773
Joonwoo Park80a01172012-10-15 16:05:23 -0700774 pr_debug("%s: enter insertion %d hph_status %x\n",
775 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700776 if (!insertion) {
777 /* Report removal */
778 mbhc->hph_status &= ~jack_type;
779 /*
780 * cancel possibly scheduled btn work and
781 * report release if we reported button press
782 */
783 if (wcd9xxx_cancel_btn_work(mbhc))
784 pr_debug("%s: button press is canceled\n", __func__);
785 else if (mbhc->buttons_pressed) {
786 pr_debug("%s: release of button press%d\n",
787 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800788 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700789 mbhc->buttons_pressed);
790 mbhc->buttons_pressed &=
791 ~WCD9XXX_JACK_BUTTON_MASK;
792 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700793
794 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
795 pr_debug("%s: Disabling micbias\n", __func__);
796 mbhc->micbias_enable_cb(mbhc->codec, false);
797 mbhc->micbias_enable = false;
798 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700799 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700800 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
801 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800802 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700803 WCD9XXX_JACK_MASK);
804 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
805 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
806 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
807 mbhc->current_plug = PLUG_TYPE_NONE;
808 mbhc->polling_active = false;
809 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700810 /*
811 * Report removal of current jack type.
812 * Headphone to headset shouldn't report headphone
813 * removal.
814 */
815 if (mbhc->mbhc_cfg->detect_extn_cable &&
816 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
817 jack_type == SND_JACK_HEADSET) &&
818 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
819 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
820 mbhc->hph_status == SND_JACK_HEADSET) {
821 pr_debug("%s: Disabling micbias\n", __func__);
822 mbhc->micbias_enable_cb(mbhc->codec, false);
823 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700824 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700825
826 pr_debug("%s: Reporting removal (%x)\n",
827 __func__, mbhc->hph_status);
828 mbhc->zl = mbhc->zr = 0;
829 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
830 0, WCD9XXX_JACK_MASK);
831 mbhc->hph_status = 0;
Joonwoo Park80a01172012-10-15 16:05:23 -0700832 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700833 /* Report insertion */
834 mbhc->hph_status |= jack_type;
835
836 if (jack_type == SND_JACK_HEADPHONE) {
837 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
838 } else if (jack_type == SND_JACK_UNSUPPORTED) {
839 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
840 } else if (jack_type == SND_JACK_HEADSET) {
841 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
842 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700843 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700844 } else if (jack_type == SND_JACK_LINEOUT) {
845 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700846 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700847
848 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
849 pr_debug("%s: Enabling micbias\n", __func__);
850 mbhc->micbias_enable_cb(mbhc->codec, true);
851 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700852
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700853 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700854 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700855
Joonwoo Parka8890262012-10-15 12:04:27 -0700856 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
857 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800858 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700859 mbhc->hph_status, WCD9XXX_JACK_MASK);
860 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
861 }
862 /* Setup insert detect */
863 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700864
865 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700866}
867
868/* should be called under interrupt context that hold suspend */
869static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
870 struct work_struct *work)
871{
872 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
873 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
874 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700875 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700876 schedule_work(work);
877}
878
879/* called under codec_resource_lock acquisition */
880static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
881 struct work_struct *work)
882{
883 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
884 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
885 mbhc->hs_detect_work_stop = true;
886 wmb();
887 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
888 if (cancel_work_sync(work)) {
889 pr_debug("%s: correct_plug_swch is canceled\n",
890 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700891 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700892 }
893 WCD9XXX_BCL_LOCK(mbhc->resmgr);
894}
895
Joonwoo Park73375212013-05-07 12:42:44 -0700896static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
897{
898 int r;
899 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700900 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
901 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700902 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700903 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700904 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700905 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700906 return r;
907}
908
Joonwoo Parka8890262012-10-15 12:04:27 -0700909static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
910{
911 s16 v_hs_max;
912 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
913
914 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
915 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
916 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700917 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700918 else
919 v_hs_max = plug_type->v_hs_max;
920 return v_hs_max;
921}
922
Joonwoo Parka8890262012-10-15 12:04:27 -0700923static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
924{
925 u8 bias_msb, bias_lsb;
926 short bias_value;
927
928 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
929 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
930 bias_value = (bias_msb << 8) | bias_lsb;
931 return bias_value;
932}
933
934static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
935{
936 u8 bias_msb, bias_lsb;
937 short bias_value;
938
939 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
940 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
941 bias_value = (bias_msb << 8) | bias_lsb;
942 return bias_value;
943}
944
945static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
946 bool on)
947{
948 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
949}
950
951static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
952 bool override_bypass, bool noreldetection)
953{
954 short bias_value;
955 struct snd_soc_codec *codec = mbhc->codec;
956
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700957 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
958 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700959 if (noreldetection)
960 wcd9xxx_turn_onoff_rel_detection(codec, false);
961
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700962 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700963 /* Turn on the override */
964 if (!override_bypass)
965 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
966 if (dce) {
967 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
968 0x8);
969 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
970 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
971 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700972 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
973 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700974 usleep_range(mbhc->mbhc_data.t_sta_dce,
975 mbhc->mbhc_data.t_sta_dce);
976 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
977 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
978 bias_value = wcd9xxx_read_dce_result(codec);
979 } else {
980 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
981 0x8);
982 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
983 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
984 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700985 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
986 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700987 usleep_range(mbhc->mbhc_data.t_sta_dce,
988 mbhc->mbhc_data.t_sta_dce);
989 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
990 usleep_range(mbhc->mbhc_data.t_sta,
991 mbhc->mbhc_data.t_sta);
992 bias_value = wcd9xxx_read_sta_result(codec);
993 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
994 0x8);
995 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
996 }
997 /* Turn off the override after measuring mic voltage */
998 if (!override_bypass)
999 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1000 0x00);
1001
1002 if (noreldetection)
1003 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001004 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1005 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001006
1007 return bias_value;
1008}
1009
1010static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1011 bool norel)
1012{
1013 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1014}
1015
Joonwoo Park520a0f92013-05-14 19:39:58 -07001016static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001017 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001018{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001019 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001020 s32 mv;
1021
1022 value = bias_value;
1023 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001024 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001025 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001026 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001027 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001028 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001029 }
1030
1031 return mv;
1032}
1033
Joonwoo Park520a0f92013-05-14 19:39:58 -07001034static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1035 u16 bias_value)
1036{
1037 s16 z;
1038 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001039 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1040 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001041}
1042
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301043/* To enable/disable bandgap and RC oscillator */
1044static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1045 bool enable)
1046{
1047 if (enable) {
1048 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1049 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1050 WCD9XXX_BANDGAP_AUDIO_MODE);
1051 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1052 WCD9XXX_CLK_RCO);
1053 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1054 } else {
1055 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1056 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1057 WCD9XXX_CLK_RCO);
1058 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1059 WCD9XXX_BANDGAP_AUDIO_MODE);
1060 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1061 }
1062}
1063
Joonwoo Parka8890262012-10-15 12:04:27 -07001064/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001065static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1066 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001067{
1068 struct snd_soc_codec *codec = mbhc->codec;
1069 short bias_value;
1070 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001071 s16 reg;
1072 int change;
1073 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1074 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001075
1076 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1077
1078 pr_debug("%s: enter\n", __func__);
1079 if (!mbhc->mbhc_cfg->calibration) {
1080 pr_err("%s: Error, no calibration exists\n", __func__);
1081 return -ENODEV;
1082 }
1083
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001084 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001085 /* Enable external voltage source to micbias if present */
1086 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1087 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001088
Joonwoo Parka8890262012-10-15 12:04:27 -07001089 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301090 * setup internal micbias if codec uses internal micbias for
1091 * headset detection
1092 */
1093 if (mbhc->mbhc_cfg->use_int_rbias) {
1094 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1095 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1096 else
1097 pr_err("%s: internal bias is requested but codec did not provide callback\n",
1098 __func__);
1099 }
1100
Joonwoo Parka8890262012-10-15 12:04:27 -07001101 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1102
1103 /* Make sure CFILT is in fast mode, save current mode */
1104 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301105 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1106 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1107 else
1108 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1109 0x70, 0x00);
1110
Joonwoo Parka8890262012-10-15 12:04:27 -07001111 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301112 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301113 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1114 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1115 else
1116 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1117 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001118
1119 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1120 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1121 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1122
1123 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1124 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1125 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1126
1127 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1128 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1129
Joonwoo Parka8890262012-10-15 12:04:27 -07001130 /* don't flip override */
1131 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301132 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001133 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1134
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001135 /* recalibrate dce_z and sta_z */
1136 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1137 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1138 btn_det->mbhc_nsc << 3);
1139 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1140 if (change)
1141 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1142 if (dce_z && sta_z) {
1143 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1144 __func__,
1145 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1146 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1147 mbhc->mbhc_data.dce_z = dce_z;
1148 mbhc->mbhc_data.sta_z = sta_z;
1149 wcd9xxx_mbhc_calc_thres(mbhc);
1150 wcd9xxx_calibrate_hs_polling(mbhc);
1151 } else {
1152 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", __func__,
1153 dce_z, sta_z);
1154 }
1155
1156 if (is_cs_enable) {
1157 /* recalibrate dce_nsc_cs_z */
1158 reg = snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1159 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1160 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1161 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1162 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1163 if (dce_z) {
1164 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n", __func__,
1165 mbhc->mbhc_data.dce_nsc_cs_z, dce_z & 0xffff);
1166 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1167 } else {
1168 pr_debug("%s: failed get new dce_nsc_cs_z\n", __func__);
1169 }
1170 }
1171
Joonwoo Parka8890262012-10-15 12:04:27 -07001172 return bias_value;
1173}
1174
1175static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1176{
1177 struct snd_soc_codec *codec = mbhc->codec;
1178 const struct wcd9xxx_mbhc_general_cfg *generic =
1179 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1180
1181 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001182 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001183 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001184 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001185
1186 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1187 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001188 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001189
1190 usleep_range(generic->t_shutdown_plug_rem,
1191 generic->t_shutdown_plug_rem);
1192
1193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1194
Joonwoo Park533b3682013-06-13 11:41:21 -07001195 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001196 /* Put requested CLK back */
1197 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001198 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001199
1200 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1201}
1202
1203static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1204{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001205
1206 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001207 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1208
1209 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1210
Joonwoo Parka8890262012-10-15 12:04:27 -07001211
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001212 /* Disable external voltage source to micbias if present */
1213 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1214 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1215
Joonwoo Parka8890262012-10-15 12:04:27 -07001216 mbhc->polling_active = false;
1217 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001218 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001219}
1220
Joonwoo Parka8890262012-10-15 12:04:27 -07001221/* called under codec_resource_lock acquisition */
1222static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1223{
1224 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1225 if (on)
1226 usleep_range(5000, 5000);
1227}
1228
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001229static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1230{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001231 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001232
1233 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1234 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1235 goto exit;
1236 }
1237
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001238 if (on) {
1239 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1240 1 << 7, 1 << 7);
1241 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1242 1 << 4, 0);
1243 } else {
1244 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1245 1 << 4, 1 << 4);
1246 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1247 1 << 7, 0);
1248 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001249
1250exit:
1251 /*
1252 * Wait for the micbias to settle down to vddio
1253 * when the micbias to vddio switch is enabled.
1254 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001255 if (on)
1256 usleep_range(10000, 10000);
1257}
1258
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001259static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1260{
1261 u16 hph, status;
1262 struct snd_soc_codec *codec = mbhc->codec;
1263
1264 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1265 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1266 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1267 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1268 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1269 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1270 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1271 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1272 return status;
1273}
1274
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001275static enum wcd9xxx_mbhc_plug_type
1276wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1277 struct wcd9xxx_mbhc_detect *dt, const int size,
1278 bool highhph,
1279 unsigned long event_state)
1280{
1281 int i;
1282 int vdce, mb_mv;
1283 int ch, sz, delta_thr;
1284 int minv = 0, maxv = INT_MIN;
1285 struct wcd9xxx_mbhc_detect *d = dt;
1286 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1287 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1288
1289 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1290 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1291 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001292 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001293
1294 pr_debug("%s: enter\n", __func__);
1295 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1296
1297 sz = size - 1;
1298 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1299 if (d->mic_bias) {
1300 dce_z = mbhc->mbhc_data.dce_z;
1301 mb_mv = mbhc->mbhc_data.micb_mv;
1302 hs_max = plug_type->v_hs_max;
1303 no_mic = plug_type->v_no_mic;
1304 } else {
1305 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1306 mb_mv = VDDIO_MICBIAS_MV;
1307 hs_max = WCD9XXX_V_CS_HS_MAX;
1308 no_mic = WCD9XXX_V_CS_NO_MIC;
1309 }
1310
1311 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1312 dce_z, (u32)mb_mv);
1313
1314 d->_vdces = vdce;
1315 if (d->_vdces < no_mic)
1316 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001317 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001318 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001319 highhph_cnt++;
1320 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001321 d->_type = PLUG_TYPE_HEADSET;
1322
1323 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1324 __func__, i, d->dce, vdce, d->_vdces,
1325 d->hphl_status & 0x01,
1326 d->_type);
1327
1328 ch += d->hphl_status & 0x01;
1329 if (!d->swap_gnd && !d->mic_bias) {
1330 if (maxv < d->_vdces)
1331 maxv = d->_vdces;
1332 if (!minv || minv > d->_vdces)
1333 minv = d->_vdces;
1334 }
1335 if ((!d->mic_bias &&
1336 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1337 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1338 (d->mic_bias &&
1339 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1340 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1341 pr_debug("%s: within invalid range\n", __func__);
1342 type = PLUG_TYPE_INVALID;
1343 goto exit;
1344 }
1345 }
1346
1347 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1348 pr_debug("%s: HPHL PA was ON\n", __func__);
1349 } else if (ch != sz && ch > 0) {
1350 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1351 type = PLUG_TYPE_INVALID;
1352 goto exit;
1353 }
1354
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001355 delta_thr = ((highhph_cnt == sz) || highhph) ?
1356 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001357 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1358
1359 for (i = 0, d = dt; i < sz; i++, d++) {
1360 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1361 (d->_type != dprev->_type)) {
1362 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1363 type = PLUG_TYPE_INVALID;
1364 goto exit;
1365 }
1366
1367 if (!d->swap_gnd && !d->mic_bias &&
1368 (abs(minv - d->_vdces) > delta_thr ||
1369 abs(maxv - d->_vdces) > delta_thr)) {
1370 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1371 __func__, d->_vdces, minv, maxv);
1372 type = PLUG_TYPE_INVALID;
1373 goto exit;
1374 } else if (d->swap_gnd) {
1375 dgnd = d;
1376 }
1377
1378 if (!d->mic_bias && !d->swap_gnd)
1379 dprev = d;
1380 else if (d->mic_bias)
1381 dmicbias = d;
1382 }
1383 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1384 dt->_type != dgnd->_type) {
1385 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1386 type = PLUG_TYPE_INVALID;
1387 goto exit;
1388 }
1389
1390 type = dt->_type;
1391 if (dmicbias) {
1392 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1393 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1394 dt->_type == PLUG_TYPE_HEADSET)) {
1395 type = PLUG_TYPE_HEADSET;
1396 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1397 pr_debug("%s: Headset with threshold on MIC detected\n",
1398 __func__);
1399 if (mbhc->mbhc_cfg->micbias_enable_flags &
1400 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1401 mbhc->micbias_enable = true;
1402 }
1403 }
1404 }
1405
1406 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1407 if (((type == PLUG_TYPE_HEADSET ||
1408 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1409 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1410 __func__, type);
1411 type = PLUG_TYPE_INVALID;
1412 }
1413 }
1414 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1415 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1416 minv) &&
1417 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1418 maxv))
1419 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001420 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001421 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1422 type = PLUG_TYPE_INVALID;
1423 }
1424 }
1425exit:
1426 pr_debug("%s: Plug type %d detected\n", __func__, type);
1427 return type;
1428}
1429
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001430/*
1431 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1432 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001433 * param mbhc wcd9xxx_mbhc structure
1434 * param dt collected measurements
1435 * param size array size of dt
1436 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001437 */
1438static enum wcd9xxx_mbhc_plug_type
1439wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001440 struct wcd9xxx_mbhc_detect *dt, const int size,
1441 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001442{
1443 int i;
1444 int ch;
1445 enum wcd9xxx_mbhc_plug_type type;
1446 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001447 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001448 int maxv = 0, minv = 0;
1449 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1450 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1451 const s16 hs_max = plug_type->v_hs_max;
1452 const s16 no_mic = plug_type->v_no_mic;
1453
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001454 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1455
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001456 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1457 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1458 if (d->vddio)
1459 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1460 else
1461 d->_vdces = vdce;
1462
1463 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1464 d->_type = PLUG_TYPE_HEADSET;
1465 else if (d->_vdces < no_mic)
1466 d->_type = PLUG_TYPE_HEADPHONE;
1467 else
1468 d->_type = PLUG_TYPE_HIGH_HPH;
1469
1470 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001471 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001472 if (maxv < d->_vdces)
1473 maxv = d->_vdces;
1474 if (!minv || minv > d->_vdces)
1475 minv = d->_vdces;
1476 }
1477
1478 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1479 __func__, i, d->dce, vdce, d->_vdces,
1480 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1481 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001482
1483
1484 /*
1485 * If GND and MIC prongs are aligned to HPHR and GND of
1486 * headphone, codec measures the voltage based on
1487 * impedance between HPHR and GND which results in ~80mv.
1488 * Avoid this.
1489 */
1490 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1491 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1492 pr_debug("%s: within invalid range\n", __func__);
1493 type = PLUG_TYPE_INVALID;
1494 goto exit;
1495 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001496 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001497
1498 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1499 pr_debug("%s: HPHL PA was ON\n", __func__);
1500 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001501 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1502 type = PLUG_TYPE_INVALID;
1503 goto exit;
1504 }
1505
Joonwoo Parka84ec452013-07-17 15:02:52 -07001506 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001507 if (d->vddio) {
1508 dvddio = d;
1509 continue;
1510 }
1511
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001512 if ((i > 0) && (d->_type != dprev->_type)) {
1513 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1514 type = PLUG_TYPE_INVALID;
1515 goto exit;
1516 }
1517
1518 if (!d->swap_gnd && !d->hwvalue &&
1519 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1520 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1521 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1522 __func__, d->_vdces, minv, maxv);
1523 type = PLUG_TYPE_INVALID;
1524 goto exit;
1525 } else if (d->swap_gnd) {
1526 dgnd = d;
1527 }
1528 dprev = d;
1529 }
1530
1531 WARN_ON(i != size);
1532 type = dt->_type;
1533 if (type == PLUG_TYPE_HEADSET && dgnd) {
1534 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1535 minv) &&
1536 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1537 maxv))
1538 type = PLUG_TYPE_GND_MIC_SWAP;
1539 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001540
1541 /* if HPHL PA was on, we cannot use hphl status */
1542 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1543 if (((type == PLUG_TYPE_HEADSET ||
1544 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1545 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1546 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1547 __func__, type);
1548 type = PLUG_TYPE_INVALID;
1549 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001550 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001551
Joonwoo Parkccccba72013-04-26 11:19:46 -07001552 if (type == PLUG_TYPE_HEADSET && dvddio) {
1553 if ((dvddio->_vdces > hs_max) ||
1554 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1555 pr_debug("%s: Headset with threshold on MIC detected\n",
1556 __func__);
1557 if (mbhc->mbhc_cfg->micbias_enable_flags &
1558 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1559 mbhc->micbias_enable = true;
1560 } else {
1561 pr_debug("%s: Headset with regular MIC detected\n",
1562 __func__);
1563 if (mbhc->mbhc_cfg->micbias_enable_flags &
1564 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1565 mbhc->micbias_enable = true;
1566 }
1567 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001568exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001569 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1570 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001571 return type;
1572}
1573
Joonwoo Parkccccba72013-04-26 11:19:46 -07001574/*
1575 * Pull down MBHC micbias for provided duration in microsecond.
1576 */
1577static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1578{
1579 bool micbiasconn = false;
1580 struct snd_soc_codec *codec = mbhc->codec;
1581 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1582
1583 /*
1584 * Disable MBHC to micbias connection to pull down
1585 * micbias and pull down micbias for a moment.
1586 */
1587 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1588 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1589 return -EFAULT;
1590 }
1591
1592 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1593 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1594 1 << 4, 0);
1595 micbiasconn = true;
1596 }
1597
1598 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1599
1600 /*
1601 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1602 * able to get consistent result across DCEs.
1603 */
1604 usleep_range(1000, 1000 + 10);
1605
1606 if (micbiasconn)
1607 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1608 1 << 4, 1 << 4);
1609 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1610 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1611
1612 return 0;
1613}
1614
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001615void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1616 bool highhph)
1617{
1618
1619 struct snd_soc_codec *codec;
1620 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1621 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1622 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1623
1624 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1625 codec = mbhc->codec;
1626
1627 if (on) {
1628 pr_debug("%s: enabling current source\n", __func__);
1629 /* Nsc to 9 */
1630 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1631 0x78, 0x48);
1632 /* pull down diode bit to 0 */
1633 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1634 0x01, 0x00);
1635 /*
1636 * Keep the low power insertion/removal
1637 * detection (reg 0x3DD) disabled
1638 */
1639 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1640 0x01, 0x00);
1641 /*
1642 * Enable the Mic Bias current source
1643 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1644 * Write bit[7] of register MICB_2_MBHC to 1
1645 * (INS_DET_ISRC_EN__ENABLE)
1646 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1647 */
1648 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1649 0xF0, 0xF0);
1650 /* Disconnect MBHC Override from MicBias and LDOH */
1651 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1652 } else {
1653 pr_debug("%s: disabling current source\n", __func__);
1654 /* Connect MBHC Override from MicBias and LDOH */
1655 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1656 /* INS_DET_ISRC_CTL to acdb value */
1657 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1658 0x60, plug_det->mic_current << 5);
1659 if (!highhph) {
1660 /* INS_DET_ISRC_EN__ENABLE to 0 */
1661 snd_soc_update_bits(codec,
1662 mbhc->mbhc_bias_regs.mbhc_reg,
1663 0x80, 0x00);
1664 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1665 snd_soc_update_bits(codec,
1666 mbhc->mbhc_bias_regs.mbhc_reg,
1667 0x10, 0x00);
1668 }
1669 /* Nsc to acdb value */
1670 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1671 btn_det->mbhc_nsc << 3);
1672 }
1673}
1674
1675static enum wcd9xxx_mbhc_plug_type
1676wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1677{
1678 struct snd_soc_codec *codec = mbhc->codec;
1679 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1680 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1681 int i;
1682
1683 pr_debug("%s: enter\n", __func__);
1684 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1685
1686 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1687
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301688 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001689 rt[0].swap_gnd = false;
1690 rt[0].vddio = false;
1691 rt[0].hwvalue = true;
1692 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1693 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1694 rt[0].mic_bias = false;
1695
1696 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1697 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1698 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1699 highhph);
1700 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1701 if (rt[i].swap_gnd)
1702 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1703
1704 if (rt[i].mic_bias)
1705 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1706
1707 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1708 if (rt[i].mic_bias)
1709 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1710 if (rt[i].swap_gnd)
1711 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1712 }
1713
1714 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1715 mbhc->event_state);
1716
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301717 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001718 pr_debug("%s: plug_type:%d\n", __func__, type);
1719
1720 return type;
1721}
1722
Joonwoo Parka8890262012-10-15 12:04:27 -07001723static enum wcd9xxx_mbhc_plug_type
1724wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1725{
1726 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001727 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001728 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001729 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1730 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001731 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001732
Joonwoo Park80a01172012-10-15 16:05:23 -07001733 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001734 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1735
1736 /* make sure override is on */
1737 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1738
1739 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001740 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001741
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001742 /*
1743 * There are chances vddio switch is on and cfilt voltage is adjusted
1744 * to vddio voltage even after plug type removal reported.
1745 */
1746 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1747 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1748
Joonwoo Parka8890262012-10-15 12:04:27 -07001749 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001750 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001751
Joonwoo Parkccccba72013-04-26 11:19:46 -07001752 /*
1753 * cfilter in fast mode requires 1ms to charge up and down micbias
1754 * fully.
1755 */
1756 (void) wcd9xxx_pull_down_micbias(mbhc,
1757 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301758
1759 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001760 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001761 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001762 rt[0].swap_gnd = false;
1763 rt[0].vddio = false;
1764 rt[0].hwvalue = true;
1765 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1766 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1767 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001768 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001769 else
1770 rt[i].vddio = false;
1771 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1772 rt[i].hwvalue = false;
1773 if (rt[i].swap_gnd)
1774 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1775 if (rt[i].vddio)
1776 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001777 /*
1778 * Pull down micbias to detect headset with mic which has
1779 * threshold and to have more consistent voltage measurements.
1780 *
1781 * cfilter in fast mode requires 1ms to charge up and down
1782 * micbias fully.
1783 */
1784 (void) wcd9xxx_pull_down_micbias(mbhc,
1785 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001786 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1787 if (rt[i].vddio)
1788 wcd9xxx_onoff_vddio_switch(mbhc, false);
1789 if (rt[i].swap_gnd)
1790 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001791 }
1792
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001793 if (vddioon)
1794 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1795
1796 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1797 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001798
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301799 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001800 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001801 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001802}
1803
1804static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1805{
1806 if (mbhc->mbhc_cfg->gpio)
1807 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1808 mbhc->mbhc_cfg->gpio_level_insert);
1809 else if (mbhc->mbhc_cfg->insert_detect)
1810 return snd_soc_read(mbhc->codec,
1811 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1812 (1 << 2);
1813 else
1814 WARN(1, "Invalid jack detection configuration\n");
1815
1816 return true;
1817}
1818
1819static bool is_clk_active(struct snd_soc_codec *codec)
1820{
1821 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1822}
1823
1824static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1825 int insertion, int trigger, bool padac_off)
1826{
1827 struct snd_soc_codec *codec = mbhc->codec;
1828 int central_bias_enabled = 0;
1829 const struct wcd9xxx_mbhc_general_cfg *generic =
1830 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1831 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1832 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1833
Joonwoo Park80a01172012-10-15 16:05:23 -07001834 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1835 __func__, insertion, trigger);
1836
Joonwoo Parka8890262012-10-15 12:04:27 -07001837 if (!mbhc->mbhc_cfg->calibration) {
1838 pr_err("Error, no wcd9xxx calibration\n");
1839 return -EINVAL;
1840 }
1841
1842 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1843
1844 /*
1845 * Make sure mic bias and Mic line schmitt trigger
1846 * are turned OFF
1847 */
1848 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1849 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1850
1851 if (insertion) {
1852 wcd9xxx_switch_micbias(mbhc, 0);
1853
1854 /* DAPM can manipulate PA/DAC bits concurrently */
1855 if (padac_off == true)
1856 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1857
1858 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1859 /* Enable HPH Schmitt Trigger */
1860 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1861 0x11);
1862 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1863 plug_det->hph_current << 2);
1864 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1865 0x02);
1866 }
1867 if (trigger & MBHC_USE_MB_TRIGGER) {
1868 /* enable the mic line schmitt trigger */
1869 snd_soc_update_bits(codec,
1870 mbhc->mbhc_bias_regs.mbhc_reg,
1871 0x60, plug_det->mic_current << 5);
1872 snd_soc_update_bits(codec,
1873 mbhc->mbhc_bias_regs.mbhc_reg,
1874 0x80, 0x80);
1875 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1876 snd_soc_update_bits(codec,
1877 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1878 0x00);
1879 snd_soc_update_bits(codec,
1880 mbhc->mbhc_bias_regs.mbhc_reg,
1881 0x10, 0x10);
1882 }
1883
1884 /* setup for insetion detection */
1885 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1886 } else {
1887 pr_debug("setup for removal detection\n");
1888 /* Make sure the HPH schmitt trigger is OFF */
1889 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1890
1891 /* enable the mic line schmitt trigger */
1892 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1893 0x01, 0x00);
1894 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1895 plug_det->mic_current << 5);
1896 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1897 0x80, 0x80);
1898 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1899 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1900 0x10, 0x10);
1901
1902 /* Setup for low power removal detection */
1903 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1904 0x2);
1905 }
1906
1907 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1908 /* called by interrupt */
1909 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001910 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001911 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1912 0x06, 0);
1913 usleep_range(generic->t_shutdown_plug_rem,
1914 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001915 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001916 } else
1917 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1918 0x06, 0);
1919 }
1920
1921 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1922
1923 /* If central bandgap disabled */
1924 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1925 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1926 usleep_range(generic->t_bg_fast_settle,
1927 generic->t_bg_fast_settle);
1928 central_bias_enabled = 1;
1929 }
1930
1931 /* If LDO_H disabled */
1932 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1933 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1934 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1935 usleep_range(generic->t_ldoh, generic->t_ldoh);
1936 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1937
1938 if (central_bias_enabled)
1939 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1940 0);
1941 }
1942
Meng Wangeeaaaba2013-09-09 18:37:32 +08001943 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301944 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1945 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001946
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001947 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001948 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001949 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001950
1951 return 0;
1952}
1953
1954/* called under codec_resource_lock acquisition */
1955static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1956 enum wcd9xxx_mbhc_plug_type plug_type)
1957{
Joonwoo Park80a01172012-10-15 16:05:23 -07001958 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1959 __func__, mbhc->current_plug, plug_type);
1960
Joonwoo Parka8890262012-10-15 12:04:27 -07001961 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1962
1963 if (plug_type == PLUG_TYPE_HEADPHONE &&
1964 mbhc->current_plug == PLUG_TYPE_NONE) {
1965 /*
1966 * Nothing was reported previously
1967 * report a headphone or unsupported
1968 */
1969 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1970 wcd9xxx_cleanup_hs_polling(mbhc);
1971 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07001972 if (!mbhc->mbhc_cfg->detect_extn_cable) {
1973 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
1974 wcd9xxx_report_plug(mbhc, 0,
1975 SND_JACK_HEADSET);
1976 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
1977 wcd9xxx_report_plug(mbhc, 0,
1978 SND_JACK_HEADPHONE);
1979 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001980 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
1981 wcd9xxx_cleanup_hs_polling(mbhc);
1982 } else if (plug_type == PLUG_TYPE_HEADSET) {
1983 /*
1984 * If Headphone was reported previously, this will
1985 * only report the mic line
1986 */
1987 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301988 /* Button detection required RC oscillator */
1989 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07001990 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001991
1992 /* if PA is already on, switch micbias source to VDDIO */
1993 if (mbhc->event_state &
1994 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
1995 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001996 wcd9xxx_start_hs_polling(mbhc);
1997 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07001998 if (mbhc->mbhc_cfg->detect_extn_cable) {
1999 /* High impedance device found. Report as LINEOUT*/
2000 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2001 wcd9xxx_cleanup_hs_polling(mbhc);
2002 pr_debug("%s: setup mic trigger for further detection\n",
2003 __func__);
2004 mbhc->lpi_enabled = true;
2005 /*
2006 * Do not enable HPHL trigger. If playback is active,
2007 * it might lead to continuous false HPHL triggers
2008 */
2009 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2010 false);
2011 } else {
2012 if (mbhc->current_plug == PLUG_TYPE_NONE)
2013 wcd9xxx_report_plug(mbhc, 1,
2014 SND_JACK_HEADPHONE);
2015 wcd9xxx_cleanup_hs_polling(mbhc);
2016 pr_debug("setup mic trigger for further detection\n");
2017 mbhc->lpi_enabled = true;
2018 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2019 MBHC_USE_HPHL_TRIGGER,
2020 false);
2021 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002022 } else {
2023 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2024 mbhc->current_plug, plug_type);
2025 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002026 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002027}
2028
2029/* called under codec_resource_lock acquisition */
2030static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2031{
2032 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002033 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002034
2035 pr_debug("%s: enter\n", __func__);
2036
2037 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002038 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2039 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002040
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002041 if (current_source_enable) {
2042 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2043 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2044 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2045 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002046 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002047 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002048 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002049 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002050
2051 if (wcd9xxx_swch_level_remove(mbhc)) {
2052 pr_debug("%s: Switch level is low when determining plug\n",
2053 __func__);
2054 return;
2055 }
2056
2057 if (plug_type == PLUG_TYPE_INVALID ||
2058 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002059 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002060 wcd9xxx_schedule_hs_detect_plug(mbhc,
2061 &mbhc->correct_plug_swch);
2062 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2063 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002064 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002065 wcd9xxx_schedule_hs_detect_plug(mbhc,
2066 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002067 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002068 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002069 wcd9xxx_schedule_hs_detect_plug(mbhc,
2070 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002071 } else {
2072 pr_debug("%s: Valid plug found, determine plug type %d\n",
2073 __func__, plug_type);
2074 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2075 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002076 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002077}
2078
2079/* called under codec_resource_lock acquisition */
2080static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2081{
Joonwoo Park80a01172012-10-15 16:05:23 -07002082 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002083 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2084
Joonwoo Parka8890262012-10-15 12:04:27 -07002085 if (wcd9xxx_swch_level_remove(mbhc))
2086 pr_debug("%s: Switch level low when determining plug\n",
2087 __func__);
2088 else
2089 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002090 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002091}
2092
2093/* called only from interrupt which is under codec_resource_lock acquisition */
2094static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2095 bool is_removal)
2096{
2097 if (!is_removal) {
2098 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2099
2100 rmb();
2101 if (mbhc->lpi_enabled)
2102 msleep(100);
2103
2104 rmb();
2105 if (!mbhc->lpi_enabled) {
2106 pr_debug("%s: lpi is disabled\n", __func__);
2107 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2108 pr_debug("%s: Valid insertion, detect plug type\n",
2109 __func__);
2110 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2111 } else {
2112 pr_debug("%s: Invalid insertion stop plug detection\n",
2113 __func__);
2114 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002115 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2116 pr_debug("%s: Removal\n", __func__);
2117 if (!wcd9xxx_swch_level_remove(mbhc)) {
2118 /*
2119 * Switch indicates, something is still inserted.
2120 * This could be extension cable i.e. headset is
2121 * removed from extension cable.
2122 */
2123 /* cancel detect plug */
2124 wcd9xxx_cancel_hs_detect_plug(mbhc,
2125 &mbhc->correct_plug_swch);
2126 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2127 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002128 } else {
2129 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2130 }
2131}
2132
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002133static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2134 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002135{
2136 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2137 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2138 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2139
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002140 if (cs_enable)
2141 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2142 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2143 else
2144 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2145 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2146 (mic_mv > plug_type->v_no_mic) &&
2147 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002148}
2149
2150/*
2151 * called under codec_resource_lock acquisition
2152 * returns true if mic voltage range is back to normal insertion
2153 * returns false either if timedout or removed
2154 */
2155static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2156{
2157 int i;
2158 bool timedout, settled = false;
2159 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2160 short mb_v[NUM_DCE_PLUG_DETECT];
2161 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002162 bool cs_enable;
2163
2164 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2165 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2166
2167 if (cs_enable)
2168 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002169
2170 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2171 while (!(timedout = time_after(jiffies, timeout))) {
2172 retry++;
2173 if (wcd9xxx_swch_level_remove(mbhc)) {
2174 pr_debug("%s: Switch indicates removal\n", __func__);
2175 break;
2176 }
2177
2178 if (retry > 1)
2179 msleep(250);
2180 else
2181 msleep(50);
2182
2183 if (wcd9xxx_swch_level_remove(mbhc)) {
2184 pr_debug("%s: Switch indicates removal\n", __func__);
2185 break;
2186 }
2187
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002188 if (cs_enable) {
2189 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2190 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2191 true, true);
2192 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2193 true,
2194 mb_v[i],
2195 mbhc->mbhc_data.dce_nsc_cs_z,
2196 (u32)VDDIO_MICBIAS_MV);
2197 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2198 __func__, retry, mic_mv[i], mb_v[i]);
2199 }
2200 } else {
2201 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2202 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2203 true);
2204 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2205 mb_v[i]);
2206 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2207 __func__, retry, mic_mv[i],
2208 mb_v[i]);
2209 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002210 }
2211
2212 if (wcd9xxx_swch_level_remove(mbhc)) {
2213 pr_debug("%s: Switcn indicates removal\n", __func__);
2214 break;
2215 }
2216
2217 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2218 pr_debug("%s : headset/headphone is removed\n",
2219 __func__);
2220 break;
2221 }
2222
2223 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002224 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002225 break;
2226
2227 if (i == NUM_DCE_PLUG_DETECT) {
2228 pr_debug("%s: MIC voltage settled\n", __func__);
2229 settled = true;
2230 msleep(200);
2231 break;
2232 }
2233 }
2234
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002235 if (cs_enable)
2236 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2237
Joonwoo Parka8890262012-10-15 12:04:27 -07002238 if (timedout)
2239 pr_debug("%s: Microphone did not settle in %d seconds\n",
2240 __func__, HS_DETECT_PLUG_TIME_MS);
2241 return settled;
2242}
2243
2244/* called only from interrupt which is under codec_resource_lock acquisition */
2245static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2246{
Joonwoo Park80a01172012-10-15 16:05:23 -07002247 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002248 if (wcd9xxx_hs_remove_settle(mbhc))
2249 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002250 pr_debug("%s: leave\n", __func__);
2251}
2252
2253/* called only from interrupt which is under codec_resource_lock acquisition */
2254static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2255{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002256 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002257 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002258 bool removed = true;
2259 struct snd_soc_codec *codec = mbhc->codec;
2260 const struct wcd9xxx_mbhc_general_cfg *generic =
2261 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002262 bool cs_enable;
2263 s16 cur_v_ins_h;
2264 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002265
2266 pr_debug("%s: enter\n", __func__);
2267 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2268 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2269 __func__);
2270 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2271 0x08, 0x08);
2272 return;
2273 }
2274
2275 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002276 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002277
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002278 /* If micbias is enabled, don't enable current source */
2279 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2280 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2281 (!(snd_soc_read(codec,
2282 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002283 if (cs_enable)
2284 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2285
Joonwoo Park50ae0512013-06-04 16:53:12 -07002286 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002287 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002288 if (cs_enable) {
2289 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2290 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2291 mb_mv = VDDIO_MICBIAS_MV;
2292 } else {
2293 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2294 dcez = mbhc->mbhc_data.dce_z;
2295 mb_mv = mbhc->mbhc_data.micb_mv;
2296 }
2297
Joonwoo Park50ae0512013-06-04 16:53:12 -07002298 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002299 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2300 dcez, mb_mv));
2301
2302 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2303 (wcd9xxx_get_current_v(mbhc,
2304 WCD9XXX_CURRENT_V_INS_H));
2305
2306 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002307 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002308 break;
2309 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002310 } while (!time_after(jiffies, timeout));
2311 pr_debug("%s: headset %sactually removed\n", __func__,
2312 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002313
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002314 if (cs_enable)
2315 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2316
Joonwoo Park80a01172012-10-15 16:05:23 -07002317 if (removed) {
2318 if (mbhc->mbhc_cfg->detect_extn_cable) {
2319 if (!wcd9xxx_swch_level_remove(mbhc)) {
2320 /*
2321 * extension cable is still plugged in
2322 * report it as LINEOUT device
2323 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302324 if (mbhc->hph_status == SND_JACK_HEADSET)
2325 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2326 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002327 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2328 wcd9xxx_cleanup_hs_polling(mbhc);
2329 wcd9xxx_enable_hs_detect(mbhc, 1,
2330 MBHC_USE_MB_TRIGGER,
2331 false);
2332 }
2333 } else {
2334 /* Cancel possibly running hs_detect_work */
2335 wcd9xxx_cancel_hs_detect_plug(mbhc,
2336 &mbhc->correct_plug_noswch);
2337 /*
2338 * If this removal is not false, first check the micbias
2339 * switch status and switch it to LDOH if it is already
2340 * switched to VDDIO.
2341 */
2342 wcd9xxx_switch_micbias(mbhc, 0);
2343
2344 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302345 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002346 wcd9xxx_cleanup_hs_polling(mbhc);
2347 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2348 MBHC_USE_HPHL_TRIGGER,
2349 true);
2350 }
2351 } else {
2352 wcd9xxx_start_hs_polling(mbhc);
2353 }
2354 pr_debug("%s: leave\n", __func__);
2355}
2356
2357/* called only from interrupt which is under codec_resource_lock acquisition */
2358static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2359 bool is_mb_trigger)
2360{
2361 /* Cancel possibly running hs_detect_work */
2362 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2363
2364 if (is_mb_trigger) {
2365 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2366 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2367 } else {
2368 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2369 __func__);
2370 wcd9xxx_mbhc_detect_plug_type(mbhc);
2371 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002372}
2373
2374static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2375{
Joonwoo Parka8890262012-10-15 12:04:27 -07002376 struct wcd9xxx_mbhc *mbhc = data;
2377
2378 pr_debug("%s: enter, removal interrupt\n", __func__);
2379 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002380 /*
2381 * While we don't know whether MIC is there or not, let the resmgr know
2382 * so micbias can be disabled temporarily
2383 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002384 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002385 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2386 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002387 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2388 WCD9XXX_COND_HPH, false);
2389 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2390 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2391 WCD9XXX_COND_HPH, false);
2392 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002393
Joonwoo Park80a01172012-10-15 16:05:23 -07002394 if (mbhc->mbhc_cfg->detect_extn_cable &&
2395 !wcd9xxx_swch_level_remove(mbhc))
2396 wcd9xxx_hs_remove_irq_noswch(mbhc);
2397 else
2398 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002399
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002400 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2401 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2402 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002403 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2404 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002405 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2406 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2407 WCD9XXX_COND_HPH, true);
2408 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002409 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2410
2411 return IRQ_HANDLED;
2412}
2413
2414static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2415{
2416 bool is_mb_trigger, is_removal;
2417 struct wcd9xxx_mbhc *mbhc = data;
2418 struct snd_soc_codec *codec = mbhc->codec;
2419
2420 pr_debug("%s: enter\n", __func__);
2421 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002422 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002423
2424 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2425 0x10);
2426 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2427 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2428
2429 /* Turn off both HPH and MIC line schmitt triggers */
2430 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2431 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2432 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2433
Joonwoo Park80a01172012-10-15 16:05:23 -07002434 if (mbhc->mbhc_cfg->detect_extn_cable &&
2435 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2436 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2437 else
2438 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002439
2440 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2441 return IRQ_HANDLED;
2442}
2443
2444static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2445{
2446 struct delayed_work *dwork;
2447 short bias_value;
2448 int dce_mv, sta_mv;
2449 struct wcd9xxx_mbhc *mbhc;
2450
2451 pr_debug("%s:\n", __func__);
2452
2453 dwork = to_delayed_work(work);
2454 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2455
2456 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2457 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2458
2459 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2460 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2461 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2462
2463 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002464 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002465 mbhc->buttons_pressed);
2466
2467 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002468 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002469}
2470
2471static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2472{
2473 struct delayed_work *dwork;
2474 struct wcd9xxx_mbhc *mbhc;
2475 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002476 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002477
2478 dwork = to_delayed_work(work);
2479 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2480 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002481 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002482
2483 pr_debug("%s:\n", __func__);
2484
2485 /* Turn off both HPH and MIC line schmitt triggers */
2486 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2487 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2488 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002489 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002490 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002491 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002492}
2493
2494static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2495{
2496 u32 cfg_offset;
2497 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2498 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2499
2500 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2501 return false;
2502
2503 /*
2504 * Previous check guarantees that there is enough fw data up
2505 * to num_btn
2506 */
2507 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2508 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2509 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2510 return false;
2511
2512 /*
2513 * Previous check guarantees that there is enough fw data up
2514 * to start of impedance detection configuration
2515 */
2516 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2517 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2518
2519 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2520 return false;
2521
2522 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2523 return false;
2524
2525 return true;
2526}
2527
2528static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002529 enum meas_type dce, s16 vin_mv,
2530 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002531{
2532 s16 diff, zero;
2533 u32 mb_mv, in;
2534 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002535 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002536
2537 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002538 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002539
2540 if (mb_mv == 0) {
2541 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2542 return -EINVAL;
2543 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002544 if (cs_enable) {
2545 mb_mv = VDDIO_MICBIAS_MV;
2546 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2547 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002548
2549 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002550 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2551 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002552 } else {
2553 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2554 zero = (mbhc->mbhc_data.sta_z);
2555 }
2556 in = (u32) diff * vin_mv;
2557
2558 value = (u16) (in / mb_mv) + zero;
2559 return value;
2560}
2561
2562static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2563{
2564 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002565 s16 adj_v_hs_max;
2566 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 -07002567 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2568 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2569 u16 *btn_high;
2570 int i;
2571
2572 pr_debug("%s: enter\n", __func__);
2573 codec = mbhc->codec;
2574 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2575 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2576
Joonwoo Park73375212013-05-07 12:42:44 -07002577 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002578 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002579 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002580 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002581
2582 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2583 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2584
2585 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002586 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2587 true);
2588 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002589 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002590 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002591 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002592 mbhc->mbhc_data.v_inval_ins_low =
2593 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2594 false);
2595 mbhc->mbhc_data.v_inval_ins_high =
2596 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2597 false);
2598 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002599 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2600 WCD9XXX_V_CS_HS_MAX,
2601 true);
2602 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2603 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002604
2605 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2606 MBHC_BTN_DET_V_BTN_HIGH);
2607 for (i = 0; i < btn_det->num_btn; i++)
2608 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2609
Joonwoo Park73375212013-05-07 12:42:44 -07002610 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2611 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2612 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2613 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2614 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2615 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002616
Joonwoo Park73375212013-05-07 12:42:44 -07002617 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002618 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2619 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002620 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002621 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2622 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002623 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002624 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2625 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002626 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002627 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2628 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002629
Joonwoo Park73375212013-05-07 12:42:44 -07002630 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2631 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2632 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2633 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002634
Joonwoo Parka8890262012-10-15 12:04:27 -07002635 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2636
2637 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002638 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002639 pr_debug("%s: leave\n", __func__);
2640}
2641
2642static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2643{
2644 /*
2645 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2646 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2647 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2648 */
2649 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2650}
2651
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002652/*
2653 * Mic Bias Enable Decision
2654 * Return true if high_hph_cnt is a power of 2 (!= 2)
2655 * otherwise return false
2656 */
2657static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2658{
2659 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2660}
2661
Joonwoo Parka8890262012-10-15 12:04:27 -07002662static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2663{
2664 struct wcd9xxx_mbhc *mbhc;
2665 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002666 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002667 unsigned long timeout;
2668 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002669 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002670 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002671 bool current_source_enable;
2672 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002673
2674 pr_debug("%s: enter\n", __func__);
2675
2676 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2677 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002678 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2679 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002680
2681 wcd9xxx_onoff_ext_mclk(mbhc, true);
2682
2683 /*
2684 * Keep override on during entire plug type correction work.
2685 *
2686 * This is okay under the assumption that any switch irqs which use
2687 * MBHC block cancel and sync this work so override is off again
2688 * prior to switch interrupt handler's MBHC block usage.
2689 * Also while this correction work is running, we can guarantee
2690 * DAPM doesn't use any MBHC block as this work only runs with
2691 * headphone detection.
2692 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002693 if (current_source_enable)
2694 wcd9xxx_turn_onoff_current_source(mbhc, true,
2695 false);
2696 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002697 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002698
2699 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2700 while (!time_after(jiffies, timeout)) {
2701 ++retry;
2702 rmb();
2703 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002704 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002705 pr_debug("%s: stop requested\n", __func__);
2706 break;
2707 }
2708
2709 msleep(HS_DETECT_PLUG_INERVAL_MS);
2710 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002711 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002712 pr_debug("%s: Switch level is low\n", __func__);
2713 break;
2714 }
2715
2716 /* can race with removal interrupt */
2717 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002718 if (current_source_enable)
2719 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2720 highhph);
2721 else
2722 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002723 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2724
Joonwoo Park80a01172012-10-15 16:05:23 -07002725 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2726 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002727
2728 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2729 (highhph_cnt + 1) :
2730 0;
2731 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002732 if (plug_type == PLUG_TYPE_INVALID) {
2733 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002734 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2735 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002736 mbhc->current_plug == PLUG_TYPE_NONE) {
2737 wcd9xxx_report_plug(mbhc, 1,
2738 SND_JACK_HEADPHONE);
2739 }
2740 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2741 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002742 if (mbhc->mbhc_cfg->detect_extn_cable) {
2743 if (mbhc->current_plug != plug_type)
2744 wcd9xxx_report_plug(mbhc, 1,
2745 SND_JACK_HEADPHONE);
2746 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002747 wcd9xxx_report_plug(mbhc, 1,
2748 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002749 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002750 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2751 pr_debug("%s: High HPH detected, continue polling\n",
2752 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002753 } else {
2754 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2755 pt_gnd_mic_swap_cnt++;
2756 if (pt_gnd_mic_swap_cnt <
2757 GND_MIC_SWAP_THRESHOLD)
2758 continue;
2759 else if (pt_gnd_mic_swap_cnt >
2760 GND_MIC_SWAP_THRESHOLD) {
2761 /*
2762 * This is due to GND/MIC switch didn't
2763 * work, Report unsupported plug
2764 */
2765 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2766 /*
2767 * if switch is toggled, check again,
2768 * otherwise report unsupported plug
2769 */
2770 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2771 continue;
2772 }
2773 } else
2774 pt_gnd_mic_swap_cnt = 0;
2775
2776 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002777 /* Turn off override/current source */
2778 if (current_source_enable)
2779 wcd9xxx_turn_onoff_current_source(mbhc, false,
2780 false);
2781 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002782 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002783 /*
2784 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2785 */
2786 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2787 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2788 pr_debug("Attempt %d found correct plug %d\n", retry,
2789 plug_type);
2790 correction = true;
2791 break;
2792 }
2793 }
2794
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002795 highhph = false;
2796 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002797 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2798 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002799 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002800 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002801 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002802 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002803 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002804
2805 if (!correction && current_source_enable)
2806 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2807 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002808 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002809
2810 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002811
2812 if (mbhc->mbhc_cfg->detect_extn_cable) {
2813 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002814 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2815 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002816 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2817 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002818 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002819 /* Enable removal detection */
2820 wcd9xxx_cleanup_hs_polling(mbhc);
2821 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2822 }
2823 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2824 }
2825 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002826 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002827 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002828}
2829
2830static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2831{
2832 bool insert;
2833 bool is_removed = false;
2834 struct snd_soc_codec *codec = mbhc->codec;
2835
2836 pr_debug("%s: enter\n", __func__);
2837
2838 mbhc->in_swch_irq_handler = true;
2839 /* Wait here for debounce time */
2840 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2841
2842 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2843
2844 /* cancel pending button press */
2845 if (wcd9xxx_cancel_btn_work(mbhc))
2846 pr_debug("%s: button press is canceled\n", __func__);
2847
2848 insert = !wcd9xxx_swch_level_remove(mbhc);
2849 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2850 mbhc->current_plug, insert);
2851 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2852 mbhc->lpi_enabled = false;
2853 wmb();
2854
2855 /* cancel detect plug */
2856 wcd9xxx_cancel_hs_detect_plug(mbhc,
2857 &mbhc->correct_plug_swch);
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002858 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2859 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2860 (1 << 1)))
2861 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002862
2863 /* Disable Mic Bias pull down and HPH Switch to GND */
2864 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2865 0x00);
2866 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2867 wcd9xxx_mbhc_detect_plug_type(mbhc);
2868 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2869 mbhc->lpi_enabled = false;
2870 wmb();
2871
2872 /* cancel detect plug */
2873 wcd9xxx_cancel_hs_detect_plug(mbhc,
2874 &mbhc->correct_plug_swch);
2875
2876 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2877 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2878 is_removed = true;
2879 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2880 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2881 is_removed = true;
2882 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2883 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302884 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002885 wcd9xxx_cleanup_hs_polling(mbhc);
2886 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2887 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002888 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2889 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2890 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002891 }
2892
2893 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002894 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2895 0x00);
2896 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2897 0x02, 0x00);
2898
Joonwoo Parka8890262012-10-15 12:04:27 -07002899 /* Enable Mic Bias pull down and HPH Switch to GND */
2900 snd_soc_update_bits(codec,
2901 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2902 0x01);
2903 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2904 0x01);
2905 /* Make sure mic trigger is turned off */
2906 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2907 0x01, 0x01);
2908 snd_soc_update_bits(codec,
2909 mbhc->mbhc_bias_regs.mbhc_reg,
2910 0x90, 0x00);
2911 /* Reset MBHC State Machine */
2912 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2913 0x08, 0x08);
2914 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2915 0x08, 0x00);
2916 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002917 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002918 }
2919 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002920exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002921 mbhc->in_swch_irq_handler = false;
2922 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2923 pr_debug("%s: leave\n", __func__);
2924}
2925
2926static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2927{
2928 int r = IRQ_HANDLED;
2929 struct wcd9xxx_mbhc *mbhc = data;
2930
2931 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002932 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002933 pr_warn("%s: failed to hold suspend\n", __func__);
2934 r = IRQ_NONE;
2935 } else {
2936 /* Call handler */
2937 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002938 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002939 }
2940
2941 pr_debug("%s: leave %d\n", __func__, r);
2942 return r;
2943}
2944
Joonwoo Park218e73f2013-08-21 16:22:18 -07002945static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002946{
Joonwoo Park73375212013-05-07 12:42:44 -07002947 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002948 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002949 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002950 const s16 v_ins_hu =
2951 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2952 const s16 v_ins_h =
2953 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2954 const s16 v_b1_hu =
2955 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2956 const s16 v_b1_h =
2957 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002958 const unsigned long timeout =
2959 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002960
Joonwoo Park218e73f2013-08-21 16:22:18 -07002961 while (time_before(jiffies, timeout)) {
2962 /*
2963 * This function needs to run measurements just few times during
2964 * release debounce time. Make 1ms interval to avoid
2965 * unnecessary excessive measurements.
2966 */
2967 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002968 if (i == 0) {
2969 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2970 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2971 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002972 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002973 r = 1;
2974 break;
2975 }
2976 } else {
2977 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2978 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
2979 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002980 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002981 r = 1;
2982 break;
2983 }
2984 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07002985 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07002986 }
2987
2988 return r;
2989}
2990
2991/* called under codec_resource_lock acquisition */
2992static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
2993 const s32 micmv)
2994{
2995 s16 *v_btn_low, *v_btn_high;
2996 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2997 int i, btn = -1;
2998
2999 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3000 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3001 MBHC_BTN_DET_V_BTN_LOW);
3002 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3003 MBHC_BTN_DET_V_BTN_HIGH);
3004
3005 for (i = 0; i < btn_det->num_btn; i++) {
3006 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3007 btn = i;
3008 break;
3009 }
3010 }
3011
3012 if (btn == -1)
3013 pr_debug("%s: couldn't find button number for mic mv %d\n",
3014 __func__, micmv);
3015
3016 return btn;
3017}
3018
3019static int wcd9xxx_get_button_mask(const int btn)
3020{
3021 int mask = 0;
3022 switch (btn) {
3023 case 0:
3024 mask = SND_JACK_BTN_0;
3025 break;
3026 case 1:
3027 mask = SND_JACK_BTN_1;
3028 break;
3029 case 2:
3030 mask = SND_JACK_BTN_2;
3031 break;
3032 case 3:
3033 mask = SND_JACK_BTN_3;
3034 break;
3035 case 4:
3036 mask = SND_JACK_BTN_4;
3037 break;
3038 case 5:
3039 mask = SND_JACK_BTN_5;
3040 break;
3041 case 6:
3042 mask = SND_JACK_BTN_6;
3043 break;
3044 case 7:
3045 mask = SND_JACK_BTN_7;
3046 break;
3047 }
3048 return mask;
3049}
3050
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003051static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003052{
3053 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003054 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003055 struct snd_soc_codec *codec = mbhc->codec;
3056
3057 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3058 /* Pull down micbias to ground and disconnect vddio switch */
3059 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3060 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3061 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3062 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3063
3064 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003065 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3066 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003067 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003068 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003069 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003070 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3071 }
3072 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003073 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003074 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3075 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003076
Joonwoo Park520a0f92013-05-14 19:39:58 -07003077 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003078 if (change)
3079 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3080 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003081 /* Disable pull down micbias to ground */
3082 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3083 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3084}
3085
Joonwoo Park218e73f2013-08-21 16:22:18 -07003086void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3087{
3088 const u16 sta_z = mbhc->mbhc_data.sta_z;
3089 const u16 dce_z = mbhc->mbhc_data.dce_z;
3090
3091 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3092 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3093 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3094 mbhc->mbhc_data.sta_z & 0xFFFF,
3095 mbhc->mbhc_data.dce_z & 0xFFFF);
3096
3097 wcd9xxx_mbhc_calc_thres(mbhc);
3098 wcd9xxx_calibrate_hs_polling(mbhc);
3099}
3100
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003101/*
3102 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3103 * to ceilmv + buffer
3104 */
3105static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3106{
3107 u16 v_brh, v_b1_hu;
3108 int mv;
3109 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3110 void *calibration = mbhc->mbhc_cfg->calibration;
3111 struct snd_soc_codec *codec = mbhc->codec;
3112
3113 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3114 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3115 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3116
3117 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003118 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003119 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3120 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3121 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003122 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003123 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3124 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3125 (v_brh >> 8) & 0xFF);
3126 return 0;
3127}
3128
Joonwoo Parka8890262012-10-15 12:04:27 -07003129irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3130{
3131 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003132 bool vddio;
3133 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003134 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003135 s32 stamv, stamv_s;
3136 s16 *v_btn_high;
3137 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003138 int btn = -1, meas = 0;
3139 struct wcd9xxx_mbhc *mbhc = data;
3140 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3141 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3142 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003143 short dce[d->n_btn_meas + 1], sta;
3144 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003145 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003146 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003147 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003148 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003149
3150 pr_debug("%s: enter\n", __func__);
3151
3152 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3153 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3154
3155 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3156 pr_debug("%s: mbhc is being recovered, skip button press\n",
3157 __func__);
3158 goto done;
3159 }
3160
3161 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3162
3163 if (!mbhc->polling_active) {
3164 pr_warn("%s: mbhc polling is not active, skip button press\n",
3165 __func__);
3166 goto done;
3167 }
3168
Joonwoo Parka8890262012-10-15 12:04:27 -07003169 /* If switch nterrupt already kicked in, ignore button press */
3170 if (mbhc->in_swch_irq_handler) {
3171 pr_debug("%s: Swtich level changed, ignore button press\n",
3172 __func__);
3173 btn = -1;
3174 goto done;
3175 }
3176
3177 /* Measure scaled HW DCE */
3178 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3179 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003180
Joonwoo Park218e73f2013-08-21 16:22:18 -07003181 dce_z = mbhc->mbhc_data.dce_z;
3182 sta_z = mbhc->mbhc_data.sta_z;
3183
Joonwoo Parka8890262012-10-15 12:04:27 -07003184 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003185 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003186 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003187 if (mbhc_status != STATUS_REL_DETECTION) {
3188 if (mbhc->mbhc_last_resume &&
3189 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3190 pr_debug("%s: Button is released after resume\n",
3191 __func__);
3192 n_btn_meas = 0;
3193 } else {
3194 pr_debug("%s: Button is released without resume",
3195 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003196 if (mbhc->update_z) {
3197 wcd9xxx_update_z(mbhc);
3198 mbhc->update_z = false;
3199 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003200 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3201 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003202 if (vddio)
3203 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3204 false);
3205 else
3206 stamv_s = stamv;
3207 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003208 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003209 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3210 false) : mv[0];
3211 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003212 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3213 btn = -1;
3214 goto done;
3215 }
3216 }
3217
Joonwoo Park520a0f92013-05-14 19:39:58 -07003218 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3219 meas++)
3220 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3221
Joonwoo Park218e73f2013-08-21 16:22:18 -07003222 if (mbhc->update_z) {
3223 wcd9xxx_update_z(mbhc);
3224 mbhc->update_z = false;
3225 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003226
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003227 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3228 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003229 if (vddio)
3230 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3231 else
3232 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003233 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003234 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003235
3236 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003237 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3238 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003239 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3240 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003241 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003242 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003243 if (n_btn_meas == 0)
3244 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003245 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3246 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003247 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3248 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003249 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3250 mv[meas];
3251 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003252 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003253 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3254 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003255 /*
3256 * if large enough measurements are collected,
3257 * start to check if last all n_btn_con measurements were
3258 * in same button low/high range
3259 */
3260 if (meas + 1 >= d->n_btn_con) {
3261 for (i = 0; i < d->n_btn_con; i++)
3262 if ((btnmeas[meas] < 0) ||
3263 (btnmeas[meas] != btnmeas[meas - i]))
3264 break;
3265 if (i == d->n_btn_con) {
3266 /* button pressed */
3267 btn = btnmeas[meas];
3268 break;
3269 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3270 /*
3271 * if left measurements are less than n_btn_con,
3272 * it's impossible to find button number
3273 */
3274 break;
3275 }
3276 }
3277 }
3278
3279 if (btn >= 0) {
3280 if (mbhc->in_swch_irq_handler) {
3281 pr_debug(
3282 "%s: Switch irq triggered, ignore button press\n",
3283 __func__);
3284 goto done;
3285 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003286 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3287 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3288 MBHC_BTN_DET_V_BTN_HIGH);
3289 WARN_ON(btn >= btn_det->num_btn);
3290 /* reprogram release threshold to catch voltage ramp up early */
3291 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3292
Joonwoo Parka8890262012-10-15 12:04:27 -07003293 mask = wcd9xxx_get_button_mask(btn);
3294 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003295 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003296 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3297 msecs_to_jiffies(400)) == 0) {
3298 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003299 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003300 }
3301 } else {
3302 pr_debug("%s: bogus button press, too short press?\n",
3303 __func__);
3304 }
3305
3306 done:
3307 pr_debug("%s: leave\n", __func__);
3308 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3309 return IRQ_HANDLED;
3310}
3311
3312static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3313{
3314 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003315 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003316 struct wcd9xxx_mbhc *mbhc = data;
3317
3318 pr_debug("%s: enter\n", __func__);
3319 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3320 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3321
Joonwoo Parka8890262012-10-15 12:04:27 -07003322 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3323 ret = wcd9xxx_cancel_btn_work(mbhc);
3324 if (ret == 0) {
3325 pr_debug("%s: Reporting long button release event\n",
3326 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003327 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003328 mbhc->buttons_pressed);
3329 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003330 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003331 pr_debug("%s: Fake button press interrupt\n",
3332 __func__);
3333 } else {
3334 if (mbhc->in_swch_irq_handler) {
3335 pr_debug("%s: Switch irq kicked in, ignore\n",
3336 __func__);
3337 } else {
3338 pr_debug("%s: Reporting btn press\n",
3339 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003340 wcd9xxx_jack_report(mbhc,
3341 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003342 mbhc->buttons_pressed,
3343 mbhc->buttons_pressed);
3344 pr_debug("%s: Reporting btn release\n",
3345 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003346 wcd9xxx_jack_report(mbhc,
3347 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003348 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003349 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003350 }
3351 }
3352 }
3353
3354 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3355 }
3356
3357 wcd9xxx_calibrate_hs_polling(mbhc);
3358
Joonwoo Park218e73f2013-08-21 16:22:18 -07003359 if (waitdebounce)
3360 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003361 wcd9xxx_start_hs_polling(mbhc);
3362
3363 pr_debug("%s: leave\n", __func__);
3364 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3365 return IRQ_HANDLED;
3366}
3367
3368static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3369{
3370 struct wcd9xxx_mbhc *mbhc = data;
3371 struct snd_soc_codec *codec;
3372
3373 pr_info("%s: received HPHL OCP irq\n", __func__);
3374
3375 if (mbhc) {
3376 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003377 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3378 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003379 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003380 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003381 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3382 0x10, 0x00);
3383 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3384 0x10, 0x10);
3385 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003386 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003387 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003388 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003389 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003390 mbhc->hph_status,
3391 WCD9XXX_JACK_MASK);
3392 }
3393 } else {
3394 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3395 }
3396
3397 return IRQ_HANDLED;
3398}
3399
3400static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3401{
3402 struct wcd9xxx_mbhc *mbhc = data;
3403 struct snd_soc_codec *codec;
3404
3405 pr_info("%s: received HPHR OCP irq\n", __func__);
3406 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003407 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3408 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003409 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003410 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003411 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3412 0x00);
3413 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3414 0x10);
3415 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003416 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003417 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003418 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003419 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003420 mbhc->hph_status, WCD9XXX_JACK_MASK);
3421 }
3422
3423 return IRQ_HANDLED;
3424}
3425
3426static int wcd9xxx_acdb_mclk_index(const int rate)
3427{
3428 if (rate == MCLK_RATE_12288KHZ)
3429 return 0;
3430 else if (rate == MCLK_RATE_9600KHZ)
3431 return 1;
3432 else {
3433 BUG_ON(1);
3434 return -EINVAL;
3435 }
3436}
3437
3438static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3439{
3440 u32 dce_wait, sta_wait;
3441 u8 ncic, nmeas, navg;
3442 void *calibration;
3443 u8 *n_cic, *n_ready;
3444 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3445 u8 npoll = 4, nbounce_wait = 30;
3446 struct snd_soc_codec *codec = mbhc->codec;
3447 int idx = wcd9xxx_acdb_mclk_index(rate);
3448 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3449
3450 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3451 rate);
3452 calibration = mbhc->mbhc_cfg->calibration;
3453
3454 /*
3455 * First compute the DCE / STA wait times depending on tunable
3456 * parameters. The value is computed in microseconds
3457 */
3458 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3459 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3460 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3461 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3462 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3463
3464 /* ncic stays with the same what we had during calibration */
3465 ncic = n_cic[idxmclk];
3466 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3467 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3468 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003469 /* give extra margin to sta for safety */
3470 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003471 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3472 n_ready[idx]) + 10;
3473
3474 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3475 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3476
3477 if (rate == MCLK_RATE_12288KHZ) {
3478 npoll = 4;
3479 nbounce_wait = 30;
3480 } else if (rate == MCLK_RATE_9600KHZ) {
3481 npoll = 3;
3482 nbounce_wait = 23;
3483 }
3484
3485 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3486 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3487 pr_debug("%s: leave\n", __func__);
3488}
3489
3490static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3491{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003492 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003493 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003494 struct snd_soc_codec *codec = mbhc->codec;
3495
3496 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003497 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3498 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003499 wcd9xxx_turn_onoff_rel_detection(codec, false);
3500
3501 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3502 WARN_ON(!mbhc->mbhc_data.t_dce);
3503 WARN_ON(!mbhc->mbhc_data.t_sta);
3504
3505 /*
3506 * LDOH and CFILT are already configured during pdata handling.
3507 * Only need to make sure CFILT and bandgap are in Fast mode.
3508 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003509 *
3510 * In case when Micbias is powered by external source, request
3511 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003512 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003513 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3514 mbhc->mbhc_cb->enable_mb_source(codec, true);
3515
Joonwoo Parka8890262012-10-15 12:04:27 -07003516 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303517 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3518 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3519 else
3520 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3521 0x40, 0x00);
3522
Joonwoo Parka8890262012-10-15 12:04:27 -07003523 /*
3524 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3525 * to perform ADC calibration
3526 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303527 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3528 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3529 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003530 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3531 mbhc->mbhc_cfg->micbias << 5);
3532 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3533 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3534 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303535 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3536 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3537 else
3538 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3539 0x04, 0x04);
3540
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003541 /* Pull down micbias to ground */
3542 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3543 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3544 /* Disconnect override from micbias */
3545 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3546 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3547 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303548 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303549 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3550 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3551 else
3552 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3553 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003554 /*
3555 * Hardware that has external cap can delay mic bias ramping down up
3556 * to 50ms.
3557 */
3558 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003559 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003560 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003561 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003562 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003563
3564 /* compute dce_z for current source */
3565 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3566 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3567 WCD9XXX_MBHC_NSC_CS << 3);
3568
3569 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3570 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3571 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3572 false);
3573 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3574 mbhc->mbhc_data.dce_nsc_cs_z);
3575
3576 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3577
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003578 /* STA measurement for 0 voltage */
3579 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3580 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3581 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003582
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003583 /* Restore registers */
3584 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3585 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003586
3587 /* DCE measurment for MB voltage */
3588 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3589 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303590 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303591 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3592 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3593 else
3594 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3595 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003596 /*
3597 * Hardware that has external cap can delay mic bias ramping down up
3598 * to 50ms.
3599 */
3600 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003601 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3602 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3603 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3604
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003605 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003606 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3607 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3608 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303609 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303610 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3611 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3612 else
3613 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3614 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003615 /*
3616 * Hardware that has external cap can delay mic bias ramping down up
3617 * to 50ms.
3618 */
3619 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003620 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3621 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3622 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3623
3624 /* Restore default settings. */
3625 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303626 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303627 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303628 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3629 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3630 else
3631 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3632 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003633 usleep_range(100, 100);
3634
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003635 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3636 mbhc->mbhc_cb->enable_mb_source(codec, false);
3637
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003638 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3639 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003640 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003641
Joonwoo Parka8890262012-10-15 12:04:27 -07003642 pr_debug("%s: leave\n", __func__);
3643}
3644
3645static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3646{
3647 int n;
3648 u8 *gain;
3649 struct wcd9xxx_mbhc_general_cfg *generic;
3650 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3651 struct snd_soc_codec *codec = mbhc->codec;
3652 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3653
3654 pr_debug("%s: enter\n", __func__);
3655 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3656 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3657
3658 for (n = 0; n < 8; n++) {
3659 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3660 0x07, n);
3661 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3662 btn_det->c[n]);
3663 }
3664
3665 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3666 btn_det->nc);
3667
3668 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3669 generic->mbhc_nsa << 4);
3670
3671 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3672 btn_det->n_meas);
3673
3674 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3675 generic->mbhc_navg);
3676
3677 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3678
3679 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3680 btn_det->mbhc_nsc << 3);
3681
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003682 if (mbhc->mbhc_cb &&
3683 mbhc->mbhc_cb->get_cdc_type() !=
3684 WCD9XXX_CDC_TYPE_HELICON) {
3685 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3686 snd_soc_update_bits(codec,
3687 mbhc->resmgr->reg_addr->micb_4_mbhc,
3688 0x03, MBHC_MICBIAS2);
3689 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003690
3691 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3692
3693 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3694
3695 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3696 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3697 gain[idx] << 3);
3698
3699 pr_debug("%s: leave\n", __func__);
3700}
3701
3702static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3703{
3704 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003705 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303706
Joonwoo Parka8890262012-10-15 12:04:27 -07003707 if (mbhc->mbhc_cfg->gpio) {
3708 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3709 wcd9xxx_mech_plug_detect_irq,
3710 (IRQF_TRIGGER_RISING |
3711 IRQF_TRIGGER_FALLING |
3712 IRQF_DISABLED),
3713 "headset detect", mbhc);
3714 if (ret) {
3715 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3716 mbhc->mbhc_cfg->gpio_irq);
3717 } else {
3718 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3719 if (ret)
3720 pr_err("%s: Failed to enable wake up irq %d\n",
3721 __func__, mbhc->mbhc_cfg->gpio_irq);
3722 }
3723 } else if (mbhc->mbhc_cfg->insert_detect) {
3724 /* Enable HPHL_10K_SW */
3725 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3726 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303727
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003728 ret = wcd9xxx_request_irq(core_res,
3729 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003730 wcd9xxx_mech_plug_detect_irq,
3731 "Jack Detect",
3732 mbhc);
3733 if (ret)
3734 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003735 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003736 }
3737
3738 return ret;
3739}
3740
3741static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3742{
3743 int ret = 0;
3744 struct snd_soc_codec *codec = mbhc->codec;
3745
3746 pr_debug("%s: enter\n", __func__);
3747
3748 /* Enable MCLK during calibration */
3749 wcd9xxx_onoff_ext_mclk(mbhc, true);
3750 wcd9xxx_mbhc_setup(mbhc);
3751 wcd9xxx_mbhc_cal(mbhc);
3752 wcd9xxx_mbhc_calc_thres(mbhc);
3753 wcd9xxx_onoff_ext_mclk(mbhc, false);
3754 wcd9xxx_calibrate_hs_polling(mbhc);
3755
3756 /* Enable Mic Bias pull down and HPH Switch to GND */
3757 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3758 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3759 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3760
3761 if (!IS_ERR_VALUE(ret)) {
3762 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3763 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003764 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003765 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003766 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003767 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003768
3769 /* Initialize mechanical mbhc */
3770 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3771
3772 if (!ret && mbhc->mbhc_cfg->gpio) {
3773 /* Requested with IRQF_DISABLED */
3774 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3775
3776 /* Bootup time detection */
3777 wcd9xxx_swch_irq_handler(mbhc);
3778 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3779 pr_debug("%s: Setting up codec own insert detection\n",
3780 __func__);
3781 /* Setup for insertion detection */
3782 wcd9xxx_insert_detect_setup(mbhc, true);
3783 }
3784 }
3785
3786 pr_debug("%s: leave\n", __func__);
3787
3788 return ret;
3789}
3790
3791static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3792{
3793 struct delayed_work *dwork;
3794 struct wcd9xxx_mbhc *mbhc;
3795 struct snd_soc_codec *codec;
3796 const struct firmware *fw;
3797 int ret = -1, retry = 0;
3798
3799 dwork = to_delayed_work(work);
3800 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3801 codec = mbhc->codec;
3802
3803 while (retry < FW_READ_ATTEMPTS) {
3804 retry++;
3805 pr_info("%s:Attempt %d to request MBHC firmware\n",
3806 __func__, retry);
3807 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3808 codec->dev);
3809
3810 if (ret != 0) {
3811 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3812 } else {
3813 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3814 break;
3815 }
3816 }
3817
3818 if (ret != 0) {
3819 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3820 __func__);
3821 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3822 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3823 __func__);
3824 release_firmware(fw);
3825 } else {
3826 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3827 mbhc->mbhc_fw = fw;
3828 }
3829
3830 (void) wcd9xxx_init_and_calibrate(mbhc);
3831}
3832
3833#ifdef CONFIG_DEBUG_FS
3834ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3835 size_t count, loff_t *pos)
3836{
3837 const int size = 768;
3838 char buffer[size];
3839 int n = 0;
3840 struct wcd9xxx_mbhc *mbhc = file->private_data;
3841 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003842 const s16 v_ins_hu =
3843 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3844 const s16 v_ins_h =
3845 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3846 const s16 v_b1_hu =
3847 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3848 const s16 v_b1_h =
3849 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3850 const s16 v_br_h =
3851 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003852
Joonwoo Park520a0f92013-05-14 19:39:58 -07003853 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3854 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003855 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3856 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003857 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3858 p->dce_nsc_cs_z,
3859 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3860 p->dce_nsc_cs_z,
3861 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003862 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3863 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3864 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3865 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003866 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3867 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3868 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3869 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3870 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3871 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3872 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003873 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003874 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003875 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003876 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003877 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003878 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003879 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3880 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3881 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3882 p->v_no_mic,
3883 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3884 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3885 p->v_inval_ins_low);
3886 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3887 p->v_inval_ins_high);
3888 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3889 !wcd9xxx_swch_level_remove(mbhc));
3890 buffer[n] = 0;
3891
3892 return simple_read_from_buffer(buf, count, pos, buffer, n);
3893}
3894
3895static int codec_debug_open(struct inode *inode, struct file *file)
3896{
3897 file->private_data = inode->i_private;
3898 return 0;
3899}
3900
3901static ssize_t codec_debug_write(struct file *filp,
3902 const char __user *ubuf, size_t cnt,
3903 loff_t *ppos)
3904{
3905 char lbuf[32];
3906 char *buf;
3907 int rc;
3908 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3909
3910 if (cnt > sizeof(lbuf) - 1)
3911 return -EINVAL;
3912
3913 rc = copy_from_user(lbuf, ubuf, cnt);
3914 if (rc)
3915 return -EFAULT;
3916
3917 lbuf[cnt] = '\0';
3918 buf = (char *)lbuf;
3919 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3920 false : true;
3921 return rc;
3922}
3923
3924static const struct file_operations mbhc_trrs_debug_ops = {
3925 .open = codec_debug_open,
3926 .write = codec_debug_write,
3927};
3928
3929static const struct file_operations mbhc_debug_ops = {
3930 .open = codec_debug_open,
3931 .read = codec_mbhc_debug_read,
3932};
3933
3934static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3935{
3936 mbhc->debugfs_poke =
3937 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3938 &mbhc_trrs_debug_ops);
3939 mbhc->debugfs_mbhc =
3940 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3941 NULL, mbhc, &mbhc_debug_ops);
3942}
3943
3944static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3945{
3946 debugfs_remove(mbhc->debugfs_poke);
3947 debugfs_remove(mbhc->debugfs_mbhc);
3948}
3949#else
3950static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3951{
3952}
3953
3954static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3955{
3956}
3957#endif
3958
3959int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3960 struct wcd9xxx_mbhc_config *mbhc_cfg)
3961{
Simmi Pateriya95466b12013-05-09 20:08:46 +05303962 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003963 struct snd_soc_codec *codec = mbhc->codec;
3964
3965 pr_debug("%s: enter\n", __func__);
3966
3967 if (!codec) {
3968 pr_err("%s: no codec\n", __func__);
3969 return -EINVAL;
3970 }
3971
3972 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
3973 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
3974 pr_err("Error: unsupported clock rate %d\n",
3975 mbhc_cfg->mclk_rate);
3976 return -EINVAL;
3977 }
3978
3979 /* Save mbhc config */
3980 mbhc->mbhc_cfg = mbhc_cfg;
3981
3982 /* Get HW specific mbhc registers' address */
3983 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
3984
3985 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303986 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3987 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3988 else
3989 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3990 0x40, WCD9XXX_CFILT_FAST_MODE);
3991
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003992 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07003993 * setup internal micbias if codec uses internal micbias for
3994 * headset detection
3995 */
3996 if (mbhc->mbhc_cfg->use_int_rbias) {
3997 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3998 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3999 else
4000 pr_info("%s: internal bias is requested but codec did not provide callback\n",
4001 __func__);
4002 }
4003
4004 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004005 * If codec has specific clock gating for MBHC,
4006 * remove the clock gate
4007 */
4008 if (mbhc->mbhc_cb &&
4009 mbhc->mbhc_cb->enable_clock_gate)
4010 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4011
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004012 if (!mbhc->mbhc_cfg->read_fw_bin ||
4013 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004014 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004015 } else {
4016 if (!mbhc->mbhc_fw)
4017 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4018 usecs_to_jiffies(FW_READ_TIMEOUT));
4019 else
4020 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4021 __func__, mbhc->mbhc_fw);
4022 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004023
4024 pr_debug("%s: leave %d\n", __func__, rc);
4025 return rc;
4026}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004027EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004028
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004029void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4030{
4031 if (mbhc->mbhc_fw) {
4032 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4033 release_firmware(mbhc->mbhc_fw);
4034 mbhc->mbhc_fw = NULL;
4035 }
4036}
4037EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4038
Joonwoo Parka8890262012-10-15 12:04:27 -07004039static enum wcd9xxx_micbias_num
4040wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4041{
4042 enum wcd9xxx_micbias_num ret;
4043 switch (event) {
4044 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004045 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4046 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4047 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004048 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004049 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004050 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004051 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4052 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4053 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004054 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004055 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004056 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004057 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4058 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4059 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004060 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004061 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004062 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004063 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4064 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4065 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004066 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004067 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004068 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004069 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004070 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004071 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004072 }
4073 return ret;
4074}
4075
4076static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4077{
4078 int ret;
4079 switch (event) {
4080 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4081 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4082 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4083 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4084 ret = WCD9XXX_CFILT1_SEL;
4085 break;
4086 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4087 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4088 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4089 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4090 ret = WCD9XXX_CFILT2_SEL;
4091 break;
4092 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4093 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4094 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4095 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4096 ret = WCD9XXX_CFILT3_SEL;
4097 break;
4098 default:
4099 ret = -1;
4100 }
4101 return ret;
4102}
4103
4104static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4105{
4106 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004107 const struct wcd9xxx_micbias_setting *mb_pdata =
4108 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004109
4110 switch (mbhc->mbhc_cfg->micbias) {
4111 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004112 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004113 break;
4114 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004115 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004116 break;
4117 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004118 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004119 break;
4120 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004121 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004122 break;
4123 default:
4124 cfilt = MBHC_MICBIAS_INVALID;
4125 break;
4126 }
4127 return cfilt;
4128}
4129
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004130static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4131{
4132 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4133 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4134 else
4135 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4136 0x40, on ? 0x40 : 0x00);
4137}
4138
Joonwoo Parka8890262012-10-15 12:04:27 -07004139static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4140 void *data)
4141{
4142 int ret = 0;
4143 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4144 struct snd_soc_codec *codec = mbhc->codec;
4145 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4146
4147 pr_debug("%s: enter event %s(%d)\n", __func__,
4148 wcd9xxx_get_event_string(event), event);
4149
4150 switch (event) {
4151 /* MICBIAS usage change */
4152 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4153 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4154 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4155 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004156 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4157 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004158 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004159 /*
4160 * Enable MBHC TxFE whenever micbias is
4161 * turned ON and polling is active
4162 */
4163 if (mbhc->polling_active)
4164 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4165 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004166 break;
4167 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4168 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4169 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4170 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004171 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004172 wcd9xxx_event_to_micbias(event) &&
4173 wcd9xxx_mbhc_polling(mbhc)) {
4174 /* if polling is on, restart it */
4175 wcd9xxx_pause_hs_polling(mbhc);
4176 wcd9xxx_start_hs_polling(mbhc);
4177 }
4178 break;
4179 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4180 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4181 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4182 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004183 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4184 wcd9xxx_event_to_micbias(event)) {
4185 if (mbhc->event_state &
4186 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4187 wcd9xxx_switch_micbias(mbhc, 1);
4188 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304189 * Disable MBHC TxFE, in case it was enabled earlier
4190 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004191 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304192 if (!mbhc->polling_active)
4193 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004194 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004195 break;
4196 /* PA usage change */
4197 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004198 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004199 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004200 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004201 wcd9xxx_switch_micbias(mbhc, 1);
4202 break;
4203 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004204 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004205 break;
4206 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004207 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004208 /* if HPH PAs are off, report OCP and switch back to CFILT */
4209 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4210 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4211 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4212 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004213 if (!(mbhc->event_state &
4214 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4215 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004216 break;
4217 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004218 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004219 /* if HPH PAs are off, report OCP and switch back to CFILT */
4220 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4221 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4222 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4223 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004224 if (!(mbhc->event_state &
4225 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4226 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004227 break;
4228 /* Clock usage change */
4229 case WCD9XXX_EVENT_PRE_MCLK_ON:
4230 break;
4231 case WCD9XXX_EVENT_POST_MCLK_ON:
4232 /* Change to lower TxAAF frequency */
4233 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4234 1 << 4);
4235 /* Re-calibrate clock rate dependent values */
4236 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4237 /* If clock source changes, stop and restart polling */
4238 if (wcd9xxx_mbhc_polling(mbhc)) {
4239 wcd9xxx_calibrate_hs_polling(mbhc);
4240 wcd9xxx_start_hs_polling(mbhc);
4241 }
4242 break;
4243 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4244 /* If clock source changes, stop and restart polling */
4245 if (wcd9xxx_mbhc_polling(mbhc))
4246 wcd9xxx_pause_hs_polling(mbhc);
4247 break;
4248 case WCD9XXX_EVENT_POST_MCLK_OFF:
4249 break;
4250 case WCD9XXX_EVENT_PRE_RCO_ON:
4251 break;
4252 case WCD9XXX_EVENT_POST_RCO_ON:
4253 /* Change to higher TxAAF frequency */
4254 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4255 0 << 4);
4256 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004257 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004258 /* If clock source changes, stop and restart polling */
4259 if (wcd9xxx_mbhc_polling(mbhc)) {
4260 wcd9xxx_calibrate_hs_polling(mbhc);
4261 wcd9xxx_start_hs_polling(mbhc);
4262 }
4263 break;
4264 case WCD9XXX_EVENT_PRE_RCO_OFF:
4265 /* If clock source changes, stop and restart polling */
4266 if (wcd9xxx_mbhc_polling(mbhc))
4267 wcd9xxx_pause_hs_polling(mbhc);
4268 break;
4269 case WCD9XXX_EVENT_POST_RCO_OFF:
4270 break;
4271 /* CFILT usage change */
4272 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4273 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4274 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4275 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4276 wcd9xxx_event_to_cfilt(event))
4277 /*
4278 * Switch CFILT to slow mode if MBHC CFILT is being
4279 * used.
4280 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304281 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004282 break;
4283 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4284 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4285 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4286 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4287 wcd9xxx_event_to_cfilt(event))
4288 /*
4289 * Switch CFILT to fast mode if MBHC CFILT is not
4290 * used anymore.
4291 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304292 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004293 break;
4294 /* System resume */
4295 case WCD9XXX_EVENT_POST_RESUME:
4296 mbhc->mbhc_last_resume = jiffies;
4297 break;
4298 /* BG mode chage */
4299 case WCD9XXX_EVENT_PRE_BG_OFF:
4300 case WCD9XXX_EVENT_POST_BG_OFF:
4301 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4302 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4303 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4304 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4305 /* Not used for now */
4306 break;
4307 default:
4308 WARN(1, "Unknown event %d\n", event);
4309 ret = -EINVAL;
4310 }
4311
4312 pr_debug("%s: leave\n", __func__);
4313
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304314 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004315}
4316
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004317static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4318 uint32_t *zr)
4319{
4320 int i;
4321 int ret = 0;
4322 s16 l[3], r[3];
4323 s16 *z[] = {
4324 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4325 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004326 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004327 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004328 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4329 /* Phase 1 */
4330 /* Set MBHC_MUX for HPHL without ical */
4331 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4332 /* Set MBHC_MUX for HPHR without ical */
4333 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4334 /* Set MBHC_MUX for HPHR with ical */
4335 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4336 /* Set MBHC_MUX for HPHL with ical */
4337 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4338
4339 /* Phase 2 */
4340 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4341 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4342 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4343 };
4344
4345 pr_debug("%s: enter\n", __func__);
4346 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4347
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004348 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4349 !mbhc->mbhc_cb->compute_impedance || !zl ||
4350 !zr)
4351 return -EINVAL;
4352
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004353 /*
4354 * Impedance detection is an intrusive function as it mutes RX paths,
4355 * enable PAs and etc. Therefore codec drvier including ALSA
4356 * shouldn't read and write hardware registers during detection.
4357 */
4358 mutex_lock(&codec->mutex);
4359
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004360 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004361
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004362 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004363 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004364
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004365 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4366 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004367
4368 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004369 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004370 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4371 reg_set_mux[i].mask,
4372 reg_set_mux[i].val);
4373 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4374 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4375 else
4376 snd_soc_update_bits(codec,
4377 WCD9XXX_A_MBHC_SCALING_MUX_1,
4378 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004379 /* 25us is required after mux change to settle down */
4380 usleep_range(mux_wait_us,
4381 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004382 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004383 }
4384
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004385 /* Codec specific setup for L2 and R2 measurements */
4386 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004387
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004388 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004389 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4390 reg_set_mux[i].mask,
4391 reg_set_mux[i].val);
4392 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4393 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4394 else
4395 snd_soc_update_bits(codec,
4396 WCD9XXX_A_MBHC_SCALING_MUX_1,
4397 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004398 /* 25us is required after mux change to settle down */
4399 usleep_range(mux_wait_us,
4400 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004401 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004402 }
4403
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004404 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004405
4406 mutex_unlock(&codec->mutex);
4407
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004408 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004409
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004410 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004411 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004412
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004413 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004414 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004415 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4416 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004417 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004418 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004419 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4420 pr_debug("%s: Impedance detection completed\n", __func__);
4421
4422 return ret;
4423}
4424
4425int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4426 uint32_t *zr)
4427{
4428 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4429 *zl = mbhc->zl;
4430 *zr = mbhc->zr;
4431 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4432
4433 if (*zl && *zr)
4434 return 0;
4435 else
4436 return -EINVAL;
4437}
4438
Joonwoo Parka8890262012-10-15 12:04:27 -07004439/*
4440 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4441 *
4442 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4443 */
4444int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004445 struct snd_soc_codec *codec,
4446 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004447 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4448 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4449 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004450 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004451{
4452 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004453 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004454
4455 pr_debug("%s: enter\n", __func__);
4456 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4457 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4458
4459 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4460 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4461 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4462 mbhc->mbhc_micbias_switched = false;
4463 mbhc->polling_active = false;
4464 mbhc->mbhc_state = MBHC_STATE_NONE;
4465 mbhc->in_swch_irq_handler = false;
4466 mbhc->current_plug = PLUG_TYPE_NONE;
4467 mbhc->lpi_enabled = false;
4468 mbhc->no_mic_headset_override = false;
4469 mbhc->mbhc_last_resume = 0;
4470 mbhc->codec = codec;
4471 mbhc->resmgr = resmgr;
4472 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004473 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004474 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304475 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004476 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004477 mbhc->impedance_detect = impedance_det_en;
Joonwoo Parka8890262012-10-15 12:04:27 -07004478
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004479 if (mbhc->intr_ids == NULL) {
4480 pr_err("%s: Interrupt mapping not provided\n", __func__);
4481 return -EINVAL;
4482 }
4483
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004484 if (mbhc->headset_jack.jack == NULL) {
4485 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4486 &mbhc->headset_jack);
4487 if (ret) {
4488 pr_err("%s: Failed to create new jack\n", __func__);
4489 return ret;
4490 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004491
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004492 ret = snd_soc_jack_new(codec, "Button Jack",
4493 WCD9XXX_JACK_BUTTON_MASK,
4494 &mbhc->button_jack);
4495 if (ret) {
4496 pr_err("Failed to create new jack\n");
4497 return ret;
4498 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004499
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004500 ret = snd_jack_set_key(mbhc->button_jack.jack,
4501 SND_JACK_BTN_0,
4502 KEY_MEDIA);
4503 if (ret) {
4504 pr_err("%s: Failed to set code for btn-0\n",
4505 __func__);
4506 return ret;
4507 }
4508
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004509 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4510 wcd9xxx_mbhc_fw_read);
4511 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4512 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4513 wcd9xxx_mbhc_insert_work);
4514 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004515
4516 /* Register event notifier */
4517 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4518 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4519 if (ret) {
4520 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4521 return ret;
4522 }
4523
4524 wcd9xxx_init_debugfs(mbhc);
4525
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004526
4527 /* Disable Impedance detection by default for certain codec types */
4528 if (mbhc->mbhc_cb &&
4529 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4530 impedance_detect_en = 0;
4531 else
4532 impedance_detect_en = impedance_det_en ? 1 : 0;
4533
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004534 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004535 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004536 wcd9xxx_hs_insert_irq,
4537 "Headset insert detect", mbhc);
4538 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004539 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004540 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004541 goto err_insert_irq;
4542 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004543 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004544
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004545 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004546 wcd9xxx_hs_remove_irq,
4547 "Headset remove detect", mbhc);
4548 if (ret) {
4549 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004550 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004551 goto err_remove_irq;
4552 }
4553
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004554 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004555 wcd9xxx_dce_handler, "DC Estimation detect",
4556 mbhc);
4557 if (ret) {
4558 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004559 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004560 goto err_potential_irq;
4561 }
4562
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004563 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004564 wcd9xxx_release_handler,
4565 "Button Release detect", mbhc);
4566 if (ret) {
4567 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004568 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004569 goto err_release_irq;
4570 }
4571
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004572 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004573 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4574 mbhc);
4575 if (ret) {
4576 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004577 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004578 goto err_hphl_ocp_irq;
4579 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004580 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004581
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004582 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004583 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4584 mbhc);
4585 if (ret) {
4586 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004587 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004588 goto err_hphr_ocp_irq;
4589 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004590 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004591
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004592 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4593 1 << WCD9XXX_COND_HPH);
4594
Joonwoo Parka8890262012-10-15 12:04:27 -07004595 pr_debug("%s: leave ret %d\n", __func__, ret);
4596 return ret;
4597
4598err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004599 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004600err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004601 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004602err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004603 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004604err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004605 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004606err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004607 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004608err_insert_irq:
4609 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4610
4611 pr_debug("%s: leave ret %d\n", __func__, ret);
4612 return ret;
4613}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004614EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004615
4616void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4617{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004618 struct wcd9xxx_core_resource *core_res =
4619 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004620
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004621 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4622 1 << WCD9XXX_COND_HPH);
4623
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004624 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4625 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4626 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4627 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4628 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4629 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4630 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004631
Joonwoo Parka8890262012-10-15 12:04:27 -07004632 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004633 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004634}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004635EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004636
4637MODULE_DESCRIPTION("wcd9xxx MBHC module");
4638MODULE_LICENSE("GPL v2");