blob: 32ca0c66270f5ed103fc6c145c6eb25a5a928670 [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
Simmi Pateriya795ce442013-11-14 11:15:28 +053093#define WCD9XXX_MEAS_DELTA_MAX_MV 120
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 */
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +0530101#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 160
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700102#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
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +0530124#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 12
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700125
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700126static int impedance_detect_en;
127module_param(impedance_detect_en, int,
128 S_IRUGO | S_IWUSR | S_IWGRP);
129MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
130
Simmi Pateriya4e545052013-11-15 07:41:06 +0530131static bool detect_use_vddio_switch;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
216 struct snd_soc_codec *codec = mbhc->codec;
217 int mbhc_state = mbhc->mbhc_state;
218
219 pr_debug("%s: enter\n", __func__);
220 if (!mbhc->polling_active) {
221 pr_debug("Polling is not active, do not start polling\n");
222 return;
223 }
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530224
225 /*
226 * setup internal micbias if codec uses internal micbias for
227 * headset detection
228 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +0530229 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530230 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
231 mbhc->mbhc_cb->setup_int_rbias(codec, true);
232 else
233 pr_err("%s: internal bias requested but codec did not provide callback\n",
234 __func__);
235 }
236
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530237 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530238 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
239 mbhc->mbhc_cb->enable_mux_bias_block(codec);
240 else
241 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
242 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530243
Joonwoo Parka8890262012-10-15 12:04:27 -0700244 if (!mbhc->no_mic_headset_override &&
245 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530246 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700247 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
248 /* set to max button press threshold */
249 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
250 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
252 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
253 /* set to max */
254 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
255 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
256 }
257
258 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
259 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
260 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
261 pr_debug("%s: leave\n", __func__);
262}
263
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700264static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
265 unsigned int cfilt_mv)
266{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700267 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
268}
269
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700270/*
271 * called under codec_resource_lock acquisition
272 * return old status
273 */
274static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700275 int vddio_switch, bool restartpolling,
276 bool checkpolling)
277{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700278 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700279 int cfilt_k_val;
280 bool override;
281 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700282 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700283
284 codec = mbhc->codec;
285
Joonwoo Parkccccba72013-04-26 11:19:46 -0700286 if (mbhc->micbias_enable) {
287 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700288 ret = mbhc->mbhc_micbias_switched;
289 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700290 }
291
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700292 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700293 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
294 (!checkpolling || mbhc->polling_active)) {
295 if (restartpolling)
296 wcd9xxx_pause_hs_polling(mbhc);
297 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
298 0x04;
299 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700300 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700301 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700302 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700303 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700304 VDDIO_MICBIAS_MV);
305 usleep_range(10000, 10000);
306 snd_soc_update_bits(codec,
307 mbhc->mbhc_bias_regs.cfilt_val,
308 0xFC, (cfilt_k_val << 2));
309 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700310 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700311 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700312 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700313 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700314 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700315 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530316
317 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
318 /* Threshods for button press */
319 snd_soc_write(codec,
320 WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
321 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
322 snd_soc_write(codec,
323 WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
324 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
325 0xFF);
326 snd_soc_write(codec,
327 WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
328 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
329 snd_soc_write(codec,
330 WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
331 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
332 0xFF);
333 /* Threshods for button release */
334 snd_soc_write(codec,
335 WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
336 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
337 snd_soc_write(codec,
338 WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
339 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) &
340 0xFF);
341 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700342 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
343 __func__);
344 }
345
346 /* Enable MIC BIAS Switch to VDDIO */
347 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
348 0x80, 0x80);
349 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
350 0x10, 0x00);
351 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700352 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700353 if (restartpolling)
354 wcd9xxx_start_hs_polling(mbhc);
355
356 mbhc->mbhc_micbias_switched = true;
357 pr_debug("%s: VDDIO switch enabled\n", __func__);
358 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
359 if ((!checkpolling || mbhc->polling_active) &&
360 restartpolling)
361 wcd9xxx_pause_hs_polling(mbhc);
362 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700363 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700364 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700365 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700366 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700367 snd_soc_update_bits(codec,
368 mbhc->mbhc_bias_regs.cfilt_val,
369 0xFC, (cfilt_k_val << 2));
370 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700371 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700372 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700373 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700375 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
376 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530377 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
378 /* Revert threshods for button press */
379 snd_soc_write(codec,
380 WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
381 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
382 snd_soc_write(codec,
383 WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
384 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
385 0xFF);
386 snd_soc_write(codec,
387 WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
388 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
389 snd_soc_write(codec,
390 WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
391 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
392 0xFF);
393 /* Revert threshods for button release */
394 snd_soc_write(codec,
395 WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
396 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
397 snd_soc_write(codec,
398 WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
399 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) &
400 0xFF);
401 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700402 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
403 __func__);
404 }
405
406 /* Disable MIC BIAS Switch to VDDIO */
407 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
408 0x00);
409 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
410 0x00);
411
412 if ((!checkpolling || mbhc->polling_active) && restartpolling)
413 wcd9xxx_start_hs_polling(mbhc);
414
415 mbhc->mbhc_micbias_switched = false;
416 pr_debug("%s: VDDIO switch disabled\n", __func__);
417 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700418
419 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700420}
421
422static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
423{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700424 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700425}
426
Joonwoo Park73375212013-05-07 12:42:44 -0700427static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
428 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700429{
Joonwoo Park73375212013-05-07 12:42:44 -0700430 enum mbhc_v_index vidx;
431 s16 ret = -EINVAL;
432
Joonwoo Parka8890262012-10-15 12:04:27 -0700433 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
434 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700435 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700436 else
Joonwoo Park73375212013-05-07 12:42:44 -0700437 vidx = MBHC_V_IDX_CFILT;
438
439 switch (idx) {
440 case WCD9XXX_CURRENT_V_INS_H:
441 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
442 break;
443 case WCD9XXX_CURRENT_V_INS_HU:
444 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
445 break;
446 case WCD9XXX_CURRENT_V_B1_H:
447 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
448 break;
449 case WCD9XXX_CURRENT_V_B1_HU:
450 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
451 break;
452 case WCD9XXX_CURRENT_V_BR_H:
453 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
454 break;
455 }
456
457 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700458}
459
460void *wcd9xxx_mbhc_cal_btn_det_mp(
461 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
462 const enum wcd9xxx_mbhc_btn_det_mem mem)
463{
464 void *ret = &btn_det->_v_btn_low;
465
466 switch (mem) {
467 case MBHC_BTN_DET_GAIN:
468 ret += sizeof(btn_det->_n_cic);
469 case MBHC_BTN_DET_N_CIC:
470 ret += sizeof(btn_det->_n_ready);
471 case MBHC_BTN_DET_N_READY:
472 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
473 case MBHC_BTN_DET_V_BTN_HIGH:
474 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
475 case MBHC_BTN_DET_V_BTN_LOW:
476 /* do nothing */
477 break;
478 default:
479 ret = NULL;
480 }
481
482 return ret;
483}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700484EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700485
486static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
487{
488 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700489 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
490 WCD9XXX_CURRENT_V_INS_HU);
491 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
492 WCD9XXX_CURRENT_V_B1_HU);
493 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
494 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700495
496 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
497 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
498 (v_ins_hu >> 8) & 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530499
500 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
501 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
502 0xFF);
503 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
504 (v_b1_hu >> 8) & 0xFF);
505 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h &
506 0xFF);
507 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
508 (v_b1_h >> 8) & 0xFF);
509 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
510 0xFF);
511 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
512 (v_brh >> 8) & 0xFF);
513 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
514 mbhc->mbhc_data.v_brl & 0xFF);
515 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
516 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
517 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700518}
519
Simmi Pateriya95466b12013-05-09 20:08:46 +0530520static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700521 bool fast)
522{
523 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530524 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700525
Simmi Pateriya95466b12013-05-09 20:08:46 +0530526 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
527 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530528 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530529 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700530 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530531 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700532 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700533
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700534 cfilt_mode.reg_mask = 0x40;
535 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530536 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700537 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700538
539 if (cfilt_mode.cur_mode_val
540 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530541 if (mbhc->polling_active)
542 wcd9xxx_pause_hs_polling(mbhc);
543 snd_soc_update_bits(codec,
544 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700545 cfilt_mode.reg_mask,
546 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530547 if (mbhc->polling_active)
548 wcd9xxx_start_hs_polling(mbhc);
549 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700550 cfilt_mode.cur_mode_val,
551 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530552 } else {
553 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700554 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530555 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700556}
557
Joonwoo Park3699ca32013-02-08 12:06:15 -0800558static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
559 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700560{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800561 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800562 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
563 WCD9XXX_COND_HPH_MIC,
564 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800565 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
566 WCD9XXX_COND_HPH,
567 status & SND_JACK_HEADPHONE);
568 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800569
Joonwoo Parka8890262012-10-15 12:04:27 -0700570 snd_soc_jack_report_no_dapm(jack, status, mask);
571}
572
573static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
574 int irq)
575{
576 struct snd_soc_codec *codec;
577
578 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
579 codec = mbhc->codec;
580 if (mbhc->hph_status & jack_status) {
581 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800582 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700583 mbhc->hph_status, WCD9XXX_JACK_MASK);
584 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
585 0x00);
586 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
587 0x10);
588 /*
589 * reset retry counter as PA is turned off signifying
590 * start of new OCP detection session
591 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700592 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700593 mbhc->hphlocp_cnt = 0;
594 else
595 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700596 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700597 }
598}
599
600static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
601{
602 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700603 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700604}
605
606static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
607{
608 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700609 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700610}
611
612static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
613 struct mbhc_micbias_regs *micbias_regs)
614{
615 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700616 struct wcd9xxx_micbias_setting *micbias_pdata =
617 mbhc->resmgr->micbias_pdata;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700618
Joonwoo Parka8890262012-10-15 12:04:27 -0700619 switch (mbhc->mbhc_cfg->micbias) {
620 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700621 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700622 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
623 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
624 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
625 break;
626 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700627 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700628 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
629 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
630 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
631 break;
632 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700633 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700634 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
635 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
636 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
637 break;
638 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700639 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700640 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
641 micbias_regs->int_rbias =
642 mbhc->resmgr->reg_addr->micb_4_int_rbias;
643 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
644 break;
645 default:
646 /* Should never reach here */
647 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
648 return;
649 }
650
651 micbias_regs->cfilt_sel = cfilt;
652
653 switch (cfilt) {
654 case WCD9XXX_CFILT1_SEL:
655 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
656 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700657 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700658 break;
659 case WCD9XXX_CFILT2_SEL:
660 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
661 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700662 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700663 break;
664 case WCD9XXX_CFILT3_SEL:
665 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
666 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700667 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700668 break;
669 }
670}
671
672static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
673{
674 bool pa_turned_on = false;
675 struct snd_soc_codec *codec = mbhc->codec;
676 u8 wg_time;
677
678 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
679 wg_time += 1;
680
681 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
682 &mbhc->hph_pa_dac_state)) {
683 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
684 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
685 0xC0, 0xC0);
686 }
687 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
688 &mbhc->hph_pa_dac_state)) {
689 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
690 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700691 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700692 }
693
694 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
695 &mbhc->hph_pa_dac_state)) {
696 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
697 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
698 1 << 4);
699 pa_turned_on = true;
700 }
701 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
702 &mbhc->hph_pa_dac_state)) {
703 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
704 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
705 << 5);
706 pa_turned_on = true;
707 }
708
709 if (pa_turned_on) {
710 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
711 __func__);
712 usleep_range(wg_time * 1000, wg_time * 1000);
713 }
714}
715
716static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
717{
718 int r;
719 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
720 if (r)
721 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
722 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700723 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700724 return r;
725}
726
727static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
728{
729 u8 hph_reg_val = 0;
730 if (left)
731 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
732 else
733 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
734
735 return (hph_reg_val & 0xC0) ? true : false;
736}
737
738static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
739{
740 u8 hph_reg_val = 0;
741 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
742
743 return (hph_reg_val & 0x30) ? true : false;
744}
745
746/* called under codec_resource_lock acquisition */
747static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
748{
749 u8 wg_time;
750 struct snd_soc_codec *codec = mbhc->codec;
751
752 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
753 wg_time += 1;
754
755 /* If headphone PA is on, check if userspace receives
756 * removal event to sync-up PA's state */
757 if (wcd9xxx_is_hph_pa_on(codec)) {
758 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
759 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
760 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
761 } else {
762 pr_debug("%s PA is off\n", __func__);
763 }
764
765 if (wcd9xxx_is_hph_dac_on(codec, 1))
766 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
767 if (wcd9xxx_is_hph_dac_on(codec, 0))
768 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
769
770 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800771 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700772 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
773 usleep_range(wg_time * 1000, wg_time * 1000);
774}
775
776static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
777{
778 if (!mbhc->mbhc_cfg->insert_detect)
779 return;
780 pr_debug("%s: Setting up %s detection\n", __func__,
781 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700782 /* Disable detection to avoid glitch */
783 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530784 if (mbhc->mbhc_cfg->gpio_level_insert)
785 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
786 (0x68 | (ins ? (1 << 1) : 0)));
787 else
788 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
789 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700790 /* Re-enable detection */
791 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700792}
793
794/* called under codec_resource_lock acquisition */
795static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
796 enum snd_jack_types jack_type)
797{
798 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
799
Joonwoo Park80a01172012-10-15 16:05:23 -0700800 pr_debug("%s: enter insertion %d hph_status %x\n",
801 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700802 if (!insertion) {
803 /* Report removal */
804 mbhc->hph_status &= ~jack_type;
805 /*
806 * cancel possibly scheduled btn work and
807 * report release if we reported button press
808 */
809 if (wcd9xxx_cancel_btn_work(mbhc))
810 pr_debug("%s: button press is canceled\n", __func__);
811 else if (mbhc->buttons_pressed) {
812 pr_debug("%s: release of button press%d\n",
813 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800814 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700815 mbhc->buttons_pressed);
816 mbhc->buttons_pressed &=
817 ~WCD9XXX_JACK_BUTTON_MASK;
818 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700819
820 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
821 pr_debug("%s: Disabling micbias\n", __func__);
822 mbhc->micbias_enable_cb(mbhc->codec, false);
823 mbhc->micbias_enable = false;
824 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700825 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700826 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
827 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800828 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700829 WCD9XXX_JACK_MASK);
830 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
831 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
832 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
833 mbhc->current_plug = PLUG_TYPE_NONE;
834 mbhc->polling_active = false;
835 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700836 /*
837 * Report removal of current jack type.
838 * Headphone to headset shouldn't report headphone
839 * removal.
840 */
841 if (mbhc->mbhc_cfg->detect_extn_cable &&
842 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
843 jack_type == SND_JACK_HEADSET) &&
844 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
845 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
846 mbhc->hph_status == SND_JACK_HEADSET) {
847 pr_debug("%s: Disabling micbias\n", __func__);
848 mbhc->micbias_enable_cb(mbhc->codec, false);
849 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700850 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700851
852 pr_debug("%s: Reporting removal (%x)\n",
853 __func__, mbhc->hph_status);
854 mbhc->zl = mbhc->zr = 0;
855 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
856 0, WCD9XXX_JACK_MASK);
Yeleswarapu, Nagaradheshcff6b342013-11-05 17:12:39 +0530857 mbhc->hph_status &= ~(SND_JACK_HEADSET |
858 SND_JACK_LINEOUT);
Joonwoo Park80a01172012-10-15 16:05:23 -0700859 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700860 /* Report insertion */
861 mbhc->hph_status |= jack_type;
862
863 if (jack_type == SND_JACK_HEADPHONE) {
864 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
865 } else if (jack_type == SND_JACK_UNSUPPORTED) {
866 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
867 } else if (jack_type == SND_JACK_HEADSET) {
868 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
869 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700870 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700871 } else if (jack_type == SND_JACK_LINEOUT) {
872 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700873 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700874
875 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
876 pr_debug("%s: Enabling micbias\n", __func__);
877 mbhc->micbias_enable_cb(mbhc->codec, true);
878 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700879
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700880 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700881 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700882
Joonwoo Parka8890262012-10-15 12:04:27 -0700883 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
884 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800885 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700886 mbhc->hph_status, WCD9XXX_JACK_MASK);
887 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
888 }
889 /* Setup insert detect */
890 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700891
892 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700893}
894
895/* should be called under interrupt context that hold suspend */
896static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
897 struct work_struct *work)
898{
899 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
900 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
901 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700902 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700903 schedule_work(work);
904}
905
906/* called under codec_resource_lock acquisition */
907static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
908 struct work_struct *work)
909{
910 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
911 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
912 mbhc->hs_detect_work_stop = true;
913 wmb();
914 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
915 if (cancel_work_sync(work)) {
916 pr_debug("%s: correct_plug_swch is canceled\n",
917 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700918 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700919 }
920 WCD9XXX_BCL_LOCK(mbhc->resmgr);
921}
922
Joonwoo Park73375212013-05-07 12:42:44 -0700923static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
924{
925 int r;
926 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700927 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
928 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700929 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700930 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700931 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700932 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700933 return r;
934}
935
Joonwoo Parka8890262012-10-15 12:04:27 -0700936static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
937{
938 s16 v_hs_max;
939 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
940
941 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
942 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
943 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700944 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700945 else
946 v_hs_max = plug_type->v_hs_max;
947 return v_hs_max;
948}
949
Joonwoo Parka8890262012-10-15 12:04:27 -0700950static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
951{
952 u8 bias_msb, bias_lsb;
953 short bias_value;
954
955 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
956 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
957 bias_value = (bias_msb << 8) | bias_lsb;
958 return bias_value;
959}
960
961static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
962{
963 u8 bias_msb, bias_lsb;
964 short bias_value;
965
966 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
967 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
968 bias_value = (bias_msb << 8) | bias_lsb;
969 return bias_value;
970}
971
972static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
973 bool on)
974{
975 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
976}
977
978static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
979 bool override_bypass, bool noreldetection)
980{
981 short bias_value;
982 struct snd_soc_codec *codec = mbhc->codec;
983
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700984 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
985 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700986 if (noreldetection)
987 wcd9xxx_turn_onoff_rel_detection(codec, false);
988
Simmi Pateriya4b5159f2013-11-14 10:43:24 +0530989 if (mbhc->mbhc_cfg->do_recalibration)
990 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
991 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700992 /* Turn on the override */
993 if (!override_bypass)
994 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
995 if (dce) {
996 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
997 0x8);
998 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
999 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1000 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301001 if (mbhc->mbhc_cfg->do_recalibration)
1002 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1003 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001004 usleep_range(mbhc->mbhc_data.t_sta_dce,
1005 mbhc->mbhc_data.t_sta_dce);
1006 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
1007 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
1008 bias_value = wcd9xxx_read_dce_result(codec);
1009 } else {
1010 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1011 0x8);
1012 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1013 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1014 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301015 if (mbhc->mbhc_cfg->do_recalibration)
1016 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1017 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001018 usleep_range(mbhc->mbhc_data.t_sta_dce,
1019 mbhc->mbhc_data.t_sta_dce);
1020 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1021 usleep_range(mbhc->mbhc_data.t_sta,
1022 mbhc->mbhc_data.t_sta);
1023 bias_value = wcd9xxx_read_sta_result(codec);
1024 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1025 0x8);
1026 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1027 }
1028 /* Turn off the override after measuring mic voltage */
1029 if (!override_bypass)
1030 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1031 0x00);
1032
1033 if (noreldetection)
1034 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001035 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1036 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001037
1038 return bias_value;
1039}
1040
1041static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1042 bool norel)
1043{
1044 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1045}
1046
Joonwoo Park520a0f92013-05-14 19:39:58 -07001047static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001048 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001049{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001050 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001051 s32 mv;
1052
1053 value = bias_value;
1054 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001055 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001056 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001057 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001058 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001059 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001060 }
1061
1062 return mv;
1063}
1064
Joonwoo Park520a0f92013-05-14 19:39:58 -07001065static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1066 u16 bias_value)
1067{
1068 s16 z;
1069 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001070 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1071 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001072}
1073
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301074/* To enable/disable bandgap and RC oscillator */
1075static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1076 bool enable)
1077{
1078 if (enable) {
1079 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1080 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1081 WCD9XXX_BANDGAP_AUDIO_MODE);
1082 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1083 WCD9XXX_CLK_RCO);
1084 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1085 } else {
1086 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1087 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1088 WCD9XXX_CLK_RCO);
1089 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1090 WCD9XXX_BANDGAP_AUDIO_MODE);
1091 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1092 }
1093}
1094
Joonwoo Parka8890262012-10-15 12:04:27 -07001095/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001096static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1097 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001098{
1099 struct snd_soc_codec *codec = mbhc->codec;
1100 short bias_value;
1101 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001102 s16 reg;
1103 int change;
1104 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1105 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001106
1107 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1108
1109 pr_debug("%s: enter\n", __func__);
1110 if (!mbhc->mbhc_cfg->calibration) {
1111 pr_err("%s: Error, no calibration exists\n", __func__);
1112 return -ENODEV;
1113 }
1114
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001115 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001116 /* Enable external voltage source to micbias if present */
1117 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1118 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001119
Joonwoo Parka8890262012-10-15 12:04:27 -07001120 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301121 * setup internal micbias if codec uses internal micbias for
1122 * headset detection
1123 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05301124 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301125 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1126 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1127 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301128 pr_err("%s: internal bias requested but codec did not provide callback\n",
1129 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301130 }
1131
Joonwoo Parka8890262012-10-15 12:04:27 -07001132 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1133
1134 /* Make sure CFILT is in fast mode, save current mode */
1135 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301136 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1137 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1138 else
1139 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1140 0x70, 0x00);
1141
Joonwoo Parka8890262012-10-15 12:04:27 -07001142 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301143 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301144 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1145 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1146 else
1147 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1148 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001149
1150 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1151 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1152 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1153
1154 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1155 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1156 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1157
1158 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1159 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1160
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301161 if (!mbhc->mbhc_cfg->do_recalibration) {
1162 if (!is_cs_enable)
1163 wcd9xxx_calibrate_hs_polling(mbhc);
1164 }
1165
Joonwoo Parka8890262012-10-15 12:04:27 -07001166 /* don't flip override */
1167 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301168 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001169 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1170
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301171 if (mbhc->mbhc_cfg->do_recalibration) {
1172 /* recalibrate dce_z and sta_z */
1173 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1174 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1175 0x78, btn_det->mbhc_nsc << 3);
1176 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1177 if (change)
1178 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1179 if (dce_z && sta_z) {
1180 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1181 __func__,
1182 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1183 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1184 mbhc->mbhc_data.dce_z = dce_z;
1185 mbhc->mbhc_data.sta_z = sta_z;
1186 wcd9xxx_mbhc_calc_thres(mbhc);
1187 wcd9xxx_calibrate_hs_polling(mbhc);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001188 } else {
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301189 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
1190 __func__, dce_z, sta_z);
1191 }
1192
1193 if (is_cs_enable) {
1194 /* recalibrate dce_nsc_cs_z */
1195 reg = snd_soc_read(mbhc->codec,
1196 WCD9XXX_A_CDC_MBHC_B1_CTL);
1197 snd_soc_update_bits(mbhc->codec,
1198 WCD9XXX_A_CDC_MBHC_B1_CTL,
1199 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1200 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1201 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1202 reg);
1203 if (dce_z) {
1204 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n",
1205 __func__, mbhc->mbhc_data.dce_nsc_cs_z,
1206 dce_z & 0xffff);
1207 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1208 } else {
1209 pr_debug("%s: failed get new dce_nsc_cs_z\n",
1210 __func__);
1211 }
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001212 }
1213 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001214 return bias_value;
1215}
1216
1217static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1218{
1219 struct snd_soc_codec *codec = mbhc->codec;
1220 const struct wcd9xxx_mbhc_general_cfg *generic =
1221 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1222
1223 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001224 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001225 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001226 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001227
1228 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1229 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001230 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001231
1232 usleep_range(generic->t_shutdown_plug_rem,
1233 generic->t_shutdown_plug_rem);
1234
1235 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1236
Joonwoo Park533b3682013-06-13 11:41:21 -07001237 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001238 /* Put requested CLK back */
1239 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001240 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001241
1242 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1243}
1244
1245static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1246{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001247
1248 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001249 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1250
1251 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1252
Joonwoo Parka8890262012-10-15 12:04:27 -07001253
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001254 /* Disable external voltage source to micbias if present */
1255 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1256 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1257
Joonwoo Parka8890262012-10-15 12:04:27 -07001258 mbhc->polling_active = false;
1259 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001260 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001261}
1262
Joonwoo Parka8890262012-10-15 12:04:27 -07001263/* called under codec_resource_lock acquisition */
1264static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1265{
1266 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1267 if (on)
1268 usleep_range(5000, 5000);
1269}
1270
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001271static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1272{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001273 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001274
1275 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1276 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1277 goto exit;
1278 }
1279
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001280 if (on) {
1281 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1282 1 << 7, 1 << 7);
1283 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1284 1 << 4, 0);
1285 } else {
1286 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1287 1 << 4, 1 << 4);
1288 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1289 1 << 7, 0);
1290 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001291
1292exit:
1293 /*
1294 * Wait for the micbias to settle down to vddio
1295 * when the micbias to vddio switch is enabled.
1296 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001297 if (on)
1298 usleep_range(10000, 10000);
1299}
1300
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001301static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1302{
1303 u16 hph, status;
1304 struct snd_soc_codec *codec = mbhc->codec;
1305
1306 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1307 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1308 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1309 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1310 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1311 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1312 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1313 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1314 return status;
1315}
1316
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001317static enum wcd9xxx_mbhc_plug_type
1318wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1319 struct wcd9xxx_mbhc_detect *dt, const int size,
1320 bool highhph,
1321 unsigned long event_state)
1322{
1323 int i;
1324 int vdce, mb_mv;
1325 int ch, sz, delta_thr;
1326 int minv = 0, maxv = INT_MIN;
1327 struct wcd9xxx_mbhc_detect *d = dt;
1328 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1329 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1330
1331 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1332 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1333 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001334 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001335
1336 pr_debug("%s: enter\n", __func__);
1337 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1338
1339 sz = size - 1;
1340 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1341 if (d->mic_bias) {
1342 dce_z = mbhc->mbhc_data.dce_z;
1343 mb_mv = mbhc->mbhc_data.micb_mv;
1344 hs_max = plug_type->v_hs_max;
1345 no_mic = plug_type->v_no_mic;
1346 } else {
1347 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1348 mb_mv = VDDIO_MICBIAS_MV;
1349 hs_max = WCD9XXX_V_CS_HS_MAX;
1350 no_mic = WCD9XXX_V_CS_NO_MIC;
1351 }
1352
1353 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1354 dce_z, (u32)mb_mv);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001355 d->_vdces = vdce;
1356 if (d->_vdces < no_mic)
1357 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001358 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001359 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001360 highhph_cnt++;
1361 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001362 d->_type = PLUG_TYPE_HEADSET;
1363
1364 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1365 __func__, i, d->dce, vdce, d->_vdces,
1366 d->hphl_status & 0x01,
1367 d->_type);
1368
1369 ch += d->hphl_status & 0x01;
1370 if (!d->swap_gnd && !d->mic_bias) {
1371 if (maxv < d->_vdces)
1372 maxv = d->_vdces;
1373 if (!minv || minv > d->_vdces)
1374 minv = d->_vdces;
1375 }
1376 if ((!d->mic_bias &&
1377 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1378 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1379 (d->mic_bias &&
1380 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1381 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1382 pr_debug("%s: within invalid range\n", __func__);
1383 type = PLUG_TYPE_INVALID;
1384 goto exit;
1385 }
1386 }
1387
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001388 delta_thr = ((highhph_cnt == sz) || highhph) ?
1389 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001390 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1391
1392 for (i = 0, d = dt; i < sz; i++, d++) {
1393 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1394 (d->_type != dprev->_type)) {
1395 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1396 type = PLUG_TYPE_INVALID;
1397 goto exit;
1398 }
1399
1400 if (!d->swap_gnd && !d->mic_bias &&
1401 (abs(minv - d->_vdces) > delta_thr ||
1402 abs(maxv - d->_vdces) > delta_thr)) {
1403 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1404 __func__, d->_vdces, minv, maxv);
1405 type = PLUG_TYPE_INVALID;
1406 goto exit;
1407 } else if (d->swap_gnd) {
1408 dgnd = d;
1409 }
1410
1411 if (!d->mic_bias && !d->swap_gnd)
1412 dprev = d;
1413 else if (d->mic_bias)
1414 dmicbias = d;
1415 }
1416 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1417 dt->_type != dgnd->_type) {
1418 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1419 type = PLUG_TYPE_INVALID;
1420 goto exit;
1421 }
1422
1423 type = dt->_type;
1424 if (dmicbias) {
1425 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1426 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1427 dt->_type == PLUG_TYPE_HEADSET)) {
1428 type = PLUG_TYPE_HEADSET;
1429 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1430 pr_debug("%s: Headset with threshold on MIC detected\n",
1431 __func__);
1432 if (mbhc->mbhc_cfg->micbias_enable_flags &
1433 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1434 mbhc->micbias_enable = true;
1435 }
1436 }
1437 }
1438
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +05301439 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1440 /* if plug type is Headphone report as GND_MIC_SWAP */
1441 if (dgnd->_type == PLUG_TYPE_HEADPHONE) {
1442 pr_debug("%s: GND_MIC_SWAP\n", __func__);
1443 type = PLUG_TYPE_GND_MIC_SWAP;
1444 /*
1445 * if type is GND_MIC_SWAP we should not check
1446 * HPHL status hence goto exit
1447 */
1448 goto exit;
1449 } else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
1450 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1451 type = PLUG_TYPE_INVALID;
1452 }
1453 }
1454
1455 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1456 pr_debug("%s: HPHL PA was ON\n", __func__);
1457 } else if (ch != sz && ch > 0) {
1458 pr_debug("%s: Invalid, inconsistent HPHL..\n", __func__);
1459 type = PLUG_TYPE_INVALID;
1460 goto exit;
1461 }
1462
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001463 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1464 if (((type == PLUG_TYPE_HEADSET ||
1465 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1466 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1467 __func__, type);
1468 type = PLUG_TYPE_INVALID;
1469 }
1470 }
Simmi Pateriya154b6562013-12-05 04:17:12 +05301471
1472 if (type == PLUG_TYPE_HEADSET &&
1473 (mbhc->mbhc_cfg->micbias_enable_flags &
1474 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET)))
1475 mbhc->micbias_enable = true;
1476
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001477exit:
1478 pr_debug("%s: Plug type %d detected\n", __func__, type);
1479 return type;
1480}
1481
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001482/*
1483 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1484 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001485 * param mbhc wcd9xxx_mbhc structure
1486 * param dt collected measurements
1487 * param size array size of dt
1488 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001489 */
1490static enum wcd9xxx_mbhc_plug_type
1491wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001492 struct wcd9xxx_mbhc_detect *dt, const int size,
1493 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001494{
1495 int i;
1496 int ch;
1497 enum wcd9xxx_mbhc_plug_type type;
1498 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001499 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001500 int maxv = 0, minv = 0;
1501 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1502 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1503 const s16 hs_max = plug_type->v_hs_max;
1504 const s16 no_mic = plug_type->v_no_mic;
1505
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001506 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1507
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001508 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1509 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1510 if (d->vddio)
1511 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1512 else
1513 d->_vdces = vdce;
1514
1515 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1516 d->_type = PLUG_TYPE_HEADSET;
1517 else if (d->_vdces < no_mic)
1518 d->_type = PLUG_TYPE_HEADPHONE;
1519 else
1520 d->_type = PLUG_TYPE_HIGH_HPH;
1521
1522 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001523 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001524 if (maxv < d->_vdces)
1525 maxv = d->_vdces;
1526 if (!minv || minv > d->_vdces)
1527 minv = d->_vdces;
1528 }
1529
1530 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1531 __func__, i, d->dce, vdce, d->_vdces,
1532 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1533 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001534
1535
1536 /*
1537 * If GND and MIC prongs are aligned to HPHR and GND of
1538 * headphone, codec measures the voltage based on
1539 * impedance between HPHR and GND which results in ~80mv.
1540 * Avoid this.
1541 */
1542 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1543 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1544 pr_debug("%s: within invalid range\n", __func__);
1545 type = PLUG_TYPE_INVALID;
1546 goto exit;
1547 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001548 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001549
1550 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1551 pr_debug("%s: HPHL PA was ON\n", __func__);
1552 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001553 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1554 type = PLUG_TYPE_INVALID;
1555 goto exit;
1556 }
1557
Joonwoo Parka84ec452013-07-17 15:02:52 -07001558 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001559 if (d->vddio) {
1560 dvddio = d;
1561 continue;
1562 }
1563
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001564 if ((i > 0) && (d->_type != dprev->_type)) {
1565 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1566 type = PLUG_TYPE_INVALID;
1567 goto exit;
1568 }
1569
1570 if (!d->swap_gnd && !d->hwvalue &&
1571 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1572 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1573 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1574 __func__, d->_vdces, minv, maxv);
1575 type = PLUG_TYPE_INVALID;
1576 goto exit;
1577 } else if (d->swap_gnd) {
1578 dgnd = d;
1579 }
1580 dprev = d;
1581 }
1582
1583 WARN_ON(i != size);
1584 type = dt->_type;
1585 if (type == PLUG_TYPE_HEADSET && dgnd) {
1586 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1587 minv) &&
1588 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1589 maxv))
1590 type = PLUG_TYPE_GND_MIC_SWAP;
1591 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001592
1593 /* if HPHL PA was on, we cannot use hphl status */
1594 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1595 if (((type == PLUG_TYPE_HEADSET ||
1596 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1597 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1598 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1599 __func__, type);
1600 type = PLUG_TYPE_INVALID;
1601 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001602 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001603
Joonwoo Parkccccba72013-04-26 11:19:46 -07001604 if (type == PLUG_TYPE_HEADSET && dvddio) {
1605 if ((dvddio->_vdces > hs_max) ||
1606 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1607 pr_debug("%s: Headset with threshold on MIC detected\n",
1608 __func__);
1609 if (mbhc->mbhc_cfg->micbias_enable_flags &
1610 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1611 mbhc->micbias_enable = true;
1612 } else {
1613 pr_debug("%s: Headset with regular MIC detected\n",
1614 __func__);
1615 if (mbhc->mbhc_cfg->micbias_enable_flags &
1616 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1617 mbhc->micbias_enable = true;
1618 }
1619 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001620exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001621 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1622 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001623 return type;
1624}
1625
Joonwoo Parkccccba72013-04-26 11:19:46 -07001626/*
1627 * Pull down MBHC micbias for provided duration in microsecond.
1628 */
1629static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1630{
1631 bool micbiasconn = false;
1632 struct snd_soc_codec *codec = mbhc->codec;
1633 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1634
1635 /*
1636 * Disable MBHC to micbias connection to pull down
1637 * micbias and pull down micbias for a moment.
1638 */
1639 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1640 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1641 return -EFAULT;
1642 }
1643
1644 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1645 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1646 1 << 4, 0);
1647 micbiasconn = true;
1648 }
1649
1650 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1651
1652 /*
1653 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1654 * able to get consistent result across DCEs.
1655 */
1656 usleep_range(1000, 1000 + 10);
1657
1658 if (micbiasconn)
1659 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1660 1 << 4, 1 << 4);
1661 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1662 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1663
1664 return 0;
1665}
1666
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001667void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1668 bool highhph)
1669{
1670
1671 struct snd_soc_codec *codec;
1672 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1673 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1674 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1675
1676 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1677 codec = mbhc->codec;
1678
1679 if (on) {
1680 pr_debug("%s: enabling current source\n", __func__);
1681 /* Nsc to 9 */
1682 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1683 0x78, 0x48);
1684 /* pull down diode bit to 0 */
1685 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1686 0x01, 0x00);
1687 /*
1688 * Keep the low power insertion/removal
1689 * detection (reg 0x3DD) disabled
1690 */
1691 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1692 0x01, 0x00);
1693 /*
1694 * Enable the Mic Bias current source
1695 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1696 * Write bit[7] of register MICB_2_MBHC to 1
1697 * (INS_DET_ISRC_EN__ENABLE)
1698 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1699 */
1700 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1701 0xF0, 0xF0);
1702 /* Disconnect MBHC Override from MicBias and LDOH */
1703 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1704 } else {
1705 pr_debug("%s: disabling current source\n", __func__);
1706 /* Connect MBHC Override from MicBias and LDOH */
1707 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1708 /* INS_DET_ISRC_CTL to acdb value */
1709 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1710 0x60, plug_det->mic_current << 5);
1711 if (!highhph) {
1712 /* INS_DET_ISRC_EN__ENABLE to 0 */
1713 snd_soc_update_bits(codec,
1714 mbhc->mbhc_bias_regs.mbhc_reg,
1715 0x80, 0x00);
1716 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1717 snd_soc_update_bits(codec,
1718 mbhc->mbhc_bias_regs.mbhc_reg,
1719 0x10, 0x00);
1720 }
1721 /* Nsc to acdb value */
1722 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1723 btn_det->mbhc_nsc << 3);
1724 }
1725}
1726
1727static enum wcd9xxx_mbhc_plug_type
1728wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1729{
1730 struct snd_soc_codec *codec = mbhc->codec;
1731 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1732 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1733 int i;
1734
1735 pr_debug("%s: enter\n", __func__);
1736 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1737
1738 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1739
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301740 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001741 rt[0].swap_gnd = false;
1742 rt[0].vddio = false;
1743 rt[0].hwvalue = true;
1744 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1745 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1746 rt[0].mic_bias = false;
1747
1748 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1749 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1750 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1751 highhph);
1752 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1753 if (rt[i].swap_gnd)
1754 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1755
1756 if (rt[i].mic_bias)
1757 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1758
1759 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1760 if (rt[i].mic_bias)
1761 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1762 if (rt[i].swap_gnd)
1763 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1764 }
1765
1766 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1767 mbhc->event_state);
1768
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301769 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001770 pr_debug("%s: plug_type:%d\n", __func__, type);
1771
1772 return type;
1773}
1774
Joonwoo Parka8890262012-10-15 12:04:27 -07001775static enum wcd9xxx_mbhc_plug_type
1776wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1777{
1778 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001779 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001780 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001781 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1782 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001783 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001784
Joonwoo Park80a01172012-10-15 16:05:23 -07001785 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001786 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1787
1788 /* make sure override is on */
1789 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1790
1791 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001792 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Simmi Pateriya4e545052013-11-15 07:41:06 +05301793 detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
Joonwoo Parka8890262012-10-15 12:04:27 -07001794
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001795 /*
1796 * There are chances vddio switch is on and cfilt voltage is adjusted
1797 * to vddio voltage even after plug type removal reported.
1798 */
1799 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1800 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1801
Joonwoo Parka8890262012-10-15 12:04:27 -07001802 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001803 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001804
Joonwoo Parkccccba72013-04-26 11:19:46 -07001805 /*
1806 * cfilter in fast mode requires 1ms to charge up and down micbias
1807 * fully.
1808 */
1809 (void) wcd9xxx_pull_down_micbias(mbhc,
1810 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301811
1812 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001813 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001814 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001815 rt[0].swap_gnd = false;
1816 rt[0].vddio = false;
1817 rt[0].hwvalue = true;
1818 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1819 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1820 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001821 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001822 else
1823 rt[i].vddio = false;
1824 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1825 rt[i].hwvalue = false;
1826 if (rt[i].swap_gnd)
1827 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1828 if (rt[i].vddio)
1829 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001830 /*
1831 * Pull down micbias to detect headset with mic which has
1832 * threshold and to have more consistent voltage measurements.
1833 *
1834 * cfilter in fast mode requires 1ms to charge up and down
1835 * micbias fully.
1836 */
1837 (void) wcd9xxx_pull_down_micbias(mbhc,
1838 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001839 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1840 if (rt[i].vddio)
1841 wcd9xxx_onoff_vddio_switch(mbhc, false);
1842 if (rt[i].swap_gnd)
1843 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001844 }
1845
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001846 if (vddioon)
1847 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1848
1849 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1850 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001851
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301852 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001853 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001854 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001855}
1856
1857static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1858{
1859 if (mbhc->mbhc_cfg->gpio)
1860 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1861 mbhc->mbhc_cfg->gpio_level_insert);
1862 else if (mbhc->mbhc_cfg->insert_detect)
1863 return snd_soc_read(mbhc->codec,
1864 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1865 (1 << 2);
1866 else
1867 WARN(1, "Invalid jack detection configuration\n");
1868
1869 return true;
1870}
1871
1872static bool is_clk_active(struct snd_soc_codec *codec)
1873{
1874 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1875}
1876
1877static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1878 int insertion, int trigger, bool padac_off)
1879{
1880 struct snd_soc_codec *codec = mbhc->codec;
1881 int central_bias_enabled = 0;
1882 const struct wcd9xxx_mbhc_general_cfg *generic =
1883 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1884 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1885 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1886
Joonwoo Park80a01172012-10-15 16:05:23 -07001887 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1888 __func__, insertion, trigger);
1889
Joonwoo Parka8890262012-10-15 12:04:27 -07001890 if (!mbhc->mbhc_cfg->calibration) {
1891 pr_err("Error, no wcd9xxx calibration\n");
1892 return -EINVAL;
1893 }
1894
1895 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1896
1897 /*
1898 * Make sure mic bias and Mic line schmitt trigger
1899 * are turned OFF
1900 */
1901 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1902 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1903
1904 if (insertion) {
1905 wcd9xxx_switch_micbias(mbhc, 0);
1906
1907 /* DAPM can manipulate PA/DAC bits concurrently */
1908 if (padac_off == true)
1909 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1910
1911 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1912 /* Enable HPH Schmitt Trigger */
1913 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1914 0x11);
1915 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1916 plug_det->hph_current << 2);
1917 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1918 0x02);
1919 }
1920 if (trigger & MBHC_USE_MB_TRIGGER) {
1921 /* enable the mic line schmitt trigger */
1922 snd_soc_update_bits(codec,
1923 mbhc->mbhc_bias_regs.mbhc_reg,
1924 0x60, plug_det->mic_current << 5);
1925 snd_soc_update_bits(codec,
1926 mbhc->mbhc_bias_regs.mbhc_reg,
1927 0x80, 0x80);
1928 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1929 snd_soc_update_bits(codec,
1930 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1931 0x00);
1932 snd_soc_update_bits(codec,
1933 mbhc->mbhc_bias_regs.mbhc_reg,
1934 0x10, 0x10);
1935 }
1936
1937 /* setup for insetion detection */
1938 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1939 } else {
1940 pr_debug("setup for removal detection\n");
1941 /* Make sure the HPH schmitt trigger is OFF */
1942 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1943
1944 /* enable the mic line schmitt trigger */
1945 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1946 0x01, 0x00);
1947 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1948 plug_det->mic_current << 5);
1949 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1950 0x80, 0x80);
1951 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1952 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1953 0x10, 0x10);
1954
1955 /* Setup for low power removal detection */
1956 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1957 0x2);
1958 }
1959
1960 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1961 /* called by interrupt */
1962 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001963 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001964 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1965 0x06, 0);
1966 usleep_range(generic->t_shutdown_plug_rem,
1967 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001968 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001969 } else
1970 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1971 0x06, 0);
1972 }
1973
1974 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1975
1976 /* If central bandgap disabled */
1977 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1978 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1979 usleep_range(generic->t_bg_fast_settle,
1980 generic->t_bg_fast_settle);
1981 central_bias_enabled = 1;
1982 }
1983
1984 /* If LDO_H disabled */
1985 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1986 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1987 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1988 usleep_range(generic->t_ldoh, generic->t_ldoh);
1989 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1990
1991 if (central_bias_enabled)
1992 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1993 0);
1994 }
1995
Meng Wangeeaaaba2013-09-09 18:37:32 +08001996 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301997 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1998 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001999
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002000 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002001 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07002002 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002003
2004 return 0;
2005}
2006
2007/* called under codec_resource_lock acquisition */
2008static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
2009 enum wcd9xxx_mbhc_plug_type plug_type)
2010{
Joonwoo Park80a01172012-10-15 16:05:23 -07002011 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
2012 __func__, mbhc->current_plug, plug_type);
2013
Joonwoo Parka8890262012-10-15 12:04:27 -07002014 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2015
2016 if (plug_type == PLUG_TYPE_HEADPHONE &&
2017 mbhc->current_plug == PLUG_TYPE_NONE) {
2018 /*
2019 * Nothing was reported previously
2020 * report a headphone or unsupported
2021 */
2022 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
2023 wcd9xxx_cleanup_hs_polling(mbhc);
2024 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002025 if (!mbhc->mbhc_cfg->detect_extn_cable) {
2026 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
2027 wcd9xxx_report_plug(mbhc, 0,
2028 SND_JACK_HEADSET);
2029 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
2030 wcd9xxx_report_plug(mbhc, 0,
2031 SND_JACK_HEADPHONE);
2032 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002033 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
2034 wcd9xxx_cleanup_hs_polling(mbhc);
2035 } else if (plug_type == PLUG_TYPE_HEADSET) {
2036 /*
2037 * If Headphone was reported previously, this will
2038 * only report the mic line
2039 */
2040 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302041 /* Button detection required RC oscillator */
2042 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002043 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002044
2045 /* if PA is already on, switch micbias source to VDDIO */
2046 if (mbhc->event_state &
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08002047 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
2048 1 << MBHC_EVENT_PRE_TX_1_3_ON))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002049 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002050 wcd9xxx_start_hs_polling(mbhc);
2051 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002052 if (mbhc->mbhc_cfg->detect_extn_cable) {
2053 /* High impedance device found. Report as LINEOUT*/
2054 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2055 wcd9xxx_cleanup_hs_polling(mbhc);
2056 pr_debug("%s: setup mic trigger for further detection\n",
2057 __func__);
2058 mbhc->lpi_enabled = true;
2059 /*
2060 * Do not enable HPHL trigger. If playback is active,
2061 * it might lead to continuous false HPHL triggers
2062 */
2063 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2064 false);
2065 } else {
2066 if (mbhc->current_plug == PLUG_TYPE_NONE)
2067 wcd9xxx_report_plug(mbhc, 1,
2068 SND_JACK_HEADPHONE);
2069 wcd9xxx_cleanup_hs_polling(mbhc);
2070 pr_debug("setup mic trigger for further detection\n");
2071 mbhc->lpi_enabled = true;
2072 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2073 MBHC_USE_HPHL_TRIGGER,
2074 false);
2075 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002076 } else {
2077 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2078 mbhc->current_plug, plug_type);
2079 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002080 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002081}
2082
2083/* called under codec_resource_lock acquisition */
2084static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2085{
2086 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002087 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002088
2089 pr_debug("%s: enter\n", __func__);
2090
2091 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002092
2093 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2094 (1 << MBHC_CS_ENABLE_INSERTION)) != 0) &&
2095 (!(snd_soc_read(mbhc->codec,
2096 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002097
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002098 if (current_source_enable) {
2099 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2100 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2101 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2102 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002103 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002104 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002105 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002106 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002107
2108 if (wcd9xxx_swch_level_remove(mbhc)) {
2109 pr_debug("%s: Switch level is low when determining plug\n",
2110 __func__);
2111 return;
2112 }
2113
2114 if (plug_type == PLUG_TYPE_INVALID ||
2115 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002116 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002117 wcd9xxx_schedule_hs_detect_plug(mbhc,
2118 &mbhc->correct_plug_swch);
2119 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2120 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002121 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002122 wcd9xxx_schedule_hs_detect_plug(mbhc,
2123 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002124 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002125 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002126 wcd9xxx_schedule_hs_detect_plug(mbhc,
2127 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002128 } else {
2129 pr_debug("%s: Valid plug found, determine plug type %d\n",
2130 __func__, plug_type);
2131 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2132 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002133 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002134}
2135
2136/* called under codec_resource_lock acquisition */
2137static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2138{
Joonwoo Park80a01172012-10-15 16:05:23 -07002139 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002140 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2141
Joonwoo Parka8890262012-10-15 12:04:27 -07002142 if (wcd9xxx_swch_level_remove(mbhc))
2143 pr_debug("%s: Switch level low when determining plug\n",
2144 __func__);
2145 else
2146 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002147 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002148}
2149
2150/* called only from interrupt which is under codec_resource_lock acquisition */
2151static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2152 bool is_removal)
2153{
2154 if (!is_removal) {
2155 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2156
2157 rmb();
2158 if (mbhc->lpi_enabled)
2159 msleep(100);
2160
2161 rmb();
2162 if (!mbhc->lpi_enabled) {
2163 pr_debug("%s: lpi is disabled\n", __func__);
2164 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2165 pr_debug("%s: Valid insertion, detect plug type\n",
2166 __func__);
2167 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2168 } else {
2169 pr_debug("%s: Invalid insertion stop plug detection\n",
2170 __func__);
2171 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002172 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2173 pr_debug("%s: Removal\n", __func__);
2174 if (!wcd9xxx_swch_level_remove(mbhc)) {
2175 /*
2176 * Switch indicates, something is still inserted.
2177 * This could be extension cable i.e. headset is
2178 * removed from extension cable.
2179 */
2180 /* cancel detect plug */
2181 wcd9xxx_cancel_hs_detect_plug(mbhc,
2182 &mbhc->correct_plug_swch);
2183 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2184 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002185 } else {
2186 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2187 }
2188}
2189
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002190static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2191 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002192{
2193 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2194 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2195 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2196
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002197 if (cs_enable)
2198 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2199 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2200 else
2201 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2202 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2203 (mic_mv > plug_type->v_no_mic) &&
2204 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002205}
2206
2207/*
2208 * called under codec_resource_lock acquisition
2209 * returns true if mic voltage range is back to normal insertion
2210 * returns false either if timedout or removed
2211 */
2212static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2213{
2214 int i;
2215 bool timedout, settled = false;
2216 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2217 short mb_v[NUM_DCE_PLUG_DETECT];
2218 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002219 bool cs_enable;
2220
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002221 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2222 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2223 (!(snd_soc_read(mbhc->codec,
2224 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002225 if (cs_enable)
2226 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002227
2228 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2229 while (!(timedout = time_after(jiffies, timeout))) {
2230 retry++;
2231 if (wcd9xxx_swch_level_remove(mbhc)) {
2232 pr_debug("%s: Switch indicates removal\n", __func__);
2233 break;
2234 }
2235
2236 if (retry > 1)
2237 msleep(250);
2238 else
2239 msleep(50);
2240
2241 if (wcd9xxx_swch_level_remove(mbhc)) {
2242 pr_debug("%s: Switch indicates removal\n", __func__);
2243 break;
2244 }
2245
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002246 if (cs_enable) {
2247 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2248 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2249 true, true);
2250 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2251 true,
2252 mb_v[i],
2253 mbhc->mbhc_data.dce_nsc_cs_z,
2254 (u32)VDDIO_MICBIAS_MV);
2255 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2256 __func__, retry, mic_mv[i], mb_v[i]);
2257 }
2258 } else {
2259 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2260 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2261 true);
2262 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2263 mb_v[i]);
2264 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2265 __func__, retry, mic_mv[i],
2266 mb_v[i]);
2267 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002268 }
2269
2270 if (wcd9xxx_swch_level_remove(mbhc)) {
2271 pr_debug("%s: Switcn indicates removal\n", __func__);
2272 break;
2273 }
2274
2275 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2276 pr_debug("%s : headset/headphone is removed\n",
2277 __func__);
2278 break;
2279 }
2280
2281 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002282 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002283 break;
2284
2285 if (i == NUM_DCE_PLUG_DETECT) {
2286 pr_debug("%s: MIC voltage settled\n", __func__);
2287 settled = true;
2288 msleep(200);
2289 break;
2290 }
2291 }
2292
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002293 if (cs_enable)
2294 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2295
Joonwoo Parka8890262012-10-15 12:04:27 -07002296 if (timedout)
2297 pr_debug("%s: Microphone did not settle in %d seconds\n",
2298 __func__, HS_DETECT_PLUG_TIME_MS);
2299 return settled;
2300}
2301
2302/* called only from interrupt which is under codec_resource_lock acquisition */
2303static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2304{
Joonwoo Park80a01172012-10-15 16:05:23 -07002305 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002306 if (wcd9xxx_hs_remove_settle(mbhc))
2307 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002308 pr_debug("%s: leave\n", __func__);
2309}
2310
2311/* called only from interrupt which is under codec_resource_lock acquisition */
2312static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2313{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002314 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002315 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002316 bool removed = true;
2317 struct snd_soc_codec *codec = mbhc->codec;
2318 const struct wcd9xxx_mbhc_general_cfg *generic =
2319 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002320 bool cs_enable;
2321 s16 cur_v_ins_h;
2322 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002323
2324 pr_debug("%s: enter\n", __func__);
2325 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2326 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2327 __func__);
2328 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2329 0x08, 0x08);
2330 return;
2331 }
2332
2333 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002334 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002335
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002336 /* If micbias is enabled, don't enable current source */
2337 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2338 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2339 (!(snd_soc_read(codec,
2340 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002341 if (cs_enable)
2342 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2343
Joonwoo Park50ae0512013-06-04 16:53:12 -07002344 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002345 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002346 if (cs_enable) {
2347 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2348 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2349 mb_mv = VDDIO_MICBIAS_MV;
2350 } else {
2351 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2352 dcez = mbhc->mbhc_data.dce_z;
2353 mb_mv = mbhc->mbhc_data.micb_mv;
2354 }
2355
Joonwoo Park50ae0512013-06-04 16:53:12 -07002356 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002357 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2358 dcez, mb_mv));
2359
2360 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2361 (wcd9xxx_get_current_v(mbhc,
2362 WCD9XXX_CURRENT_V_INS_H));
2363
2364 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002365 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002366 break;
2367 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002368 } while (!time_after(jiffies, timeout));
2369 pr_debug("%s: headset %sactually removed\n", __func__,
2370 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002371
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002372 if (cs_enable)
2373 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2374
Joonwoo Park80a01172012-10-15 16:05:23 -07002375 if (removed) {
2376 if (mbhc->mbhc_cfg->detect_extn_cable) {
2377 if (!wcd9xxx_swch_level_remove(mbhc)) {
2378 /*
2379 * extension cable is still plugged in
2380 * report it as LINEOUT device
2381 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302382 if (mbhc->hph_status == SND_JACK_HEADSET)
2383 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2384 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002385 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2386 wcd9xxx_cleanup_hs_polling(mbhc);
2387 wcd9xxx_enable_hs_detect(mbhc, 1,
2388 MBHC_USE_MB_TRIGGER,
2389 false);
2390 }
2391 } else {
2392 /* Cancel possibly running hs_detect_work */
2393 wcd9xxx_cancel_hs_detect_plug(mbhc,
2394 &mbhc->correct_plug_noswch);
2395 /*
2396 * If this removal is not false, first check the micbias
2397 * switch status and switch it to LDOH if it is already
2398 * switched to VDDIO.
2399 */
2400 wcd9xxx_switch_micbias(mbhc, 0);
2401
2402 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302403 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002404 wcd9xxx_cleanup_hs_polling(mbhc);
2405 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2406 MBHC_USE_HPHL_TRIGGER,
2407 true);
2408 }
2409 } else {
2410 wcd9xxx_start_hs_polling(mbhc);
2411 }
2412 pr_debug("%s: leave\n", __func__);
2413}
2414
2415/* called only from interrupt which is under codec_resource_lock acquisition */
2416static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2417 bool is_mb_trigger)
2418{
2419 /* Cancel possibly running hs_detect_work */
2420 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2421
2422 if (is_mb_trigger) {
2423 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2424 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2425 } else {
2426 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2427 __func__);
2428 wcd9xxx_mbhc_detect_plug_type(mbhc);
2429 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002430}
2431
2432static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2433{
Joonwoo Parka8890262012-10-15 12:04:27 -07002434 struct wcd9xxx_mbhc *mbhc = data;
2435
2436 pr_debug("%s: enter, removal interrupt\n", __func__);
2437 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002438 /*
2439 * While we don't know whether MIC is there or not, let the resmgr know
2440 * so micbias can be disabled temporarily
2441 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002442 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002443 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2444 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002445 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2446 WCD9XXX_COND_HPH, false);
2447 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2448 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2449 WCD9XXX_COND_HPH, false);
2450 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002451
Joonwoo Park80a01172012-10-15 16:05:23 -07002452 if (mbhc->mbhc_cfg->detect_extn_cable &&
2453 !wcd9xxx_swch_level_remove(mbhc))
2454 wcd9xxx_hs_remove_irq_noswch(mbhc);
2455 else
2456 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002457
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002458 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2459 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2460 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002461 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2462 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002463 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2464 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2465 WCD9XXX_COND_HPH, true);
2466 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002467 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2468
2469 return IRQ_HANDLED;
2470}
2471
2472static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2473{
2474 bool is_mb_trigger, is_removal;
2475 struct wcd9xxx_mbhc *mbhc = data;
2476 struct snd_soc_codec *codec = mbhc->codec;
2477
2478 pr_debug("%s: enter\n", __func__);
2479 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002480 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002481
2482 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2483 0x10);
2484 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2485 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2486
2487 /* Turn off both HPH and MIC line schmitt triggers */
2488 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2489 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2490 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2491
Joonwoo Park80a01172012-10-15 16:05:23 -07002492 if (mbhc->mbhc_cfg->detect_extn_cable &&
2493 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2494 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2495 else
2496 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002497
2498 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2499 return IRQ_HANDLED;
2500}
2501
2502static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2503{
2504 struct delayed_work *dwork;
2505 short bias_value;
2506 int dce_mv, sta_mv;
2507 struct wcd9xxx_mbhc *mbhc;
2508
2509 pr_debug("%s:\n", __func__);
2510
2511 dwork = to_delayed_work(work);
2512 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2513
2514 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2515 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2516
2517 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2518 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2519 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2520
2521 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002522 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002523 mbhc->buttons_pressed);
2524
2525 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002526 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002527}
2528
2529static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2530{
2531 struct delayed_work *dwork;
2532 struct wcd9xxx_mbhc *mbhc;
2533 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002534 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002535
2536 dwork = to_delayed_work(work);
2537 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2538 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002539 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002540
2541 pr_debug("%s:\n", __func__);
2542
2543 /* Turn off both HPH and MIC line schmitt triggers */
2544 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2545 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2546 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002547 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002548 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002549 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002550}
2551
2552static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2553{
2554 u32 cfg_offset;
2555 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2556 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2557
2558 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2559 return false;
2560
2561 /*
2562 * Previous check guarantees that there is enough fw data up
2563 * to num_btn
2564 */
2565 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2566 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2567 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2568 return false;
2569
2570 /*
2571 * Previous check guarantees that there is enough fw data up
2572 * to start of impedance detection configuration
2573 */
2574 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2575 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2576
2577 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2578 return false;
2579
2580 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2581 return false;
2582
2583 return true;
2584}
2585
2586static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002587 enum meas_type dce, s16 vin_mv,
2588 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002589{
2590 s16 diff, zero;
2591 u32 mb_mv, in;
2592 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002593 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002594
2595 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002596 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002597
2598 if (mb_mv == 0) {
2599 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2600 return -EINVAL;
2601 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002602 if (cs_enable) {
2603 mb_mv = VDDIO_MICBIAS_MV;
2604 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2605 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002606
2607 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002608 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2609 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002610 } else {
2611 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2612 zero = (mbhc->mbhc_data.sta_z);
2613 }
2614 in = (u32) diff * vin_mv;
2615
2616 value = (u16) (in / mb_mv) + zero;
2617 return value;
2618}
2619
2620static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2621{
2622 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002623 s16 adj_v_hs_max;
2624 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 -07002625 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2626 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2627 u16 *btn_high;
2628 int i;
2629
2630 pr_debug("%s: enter\n", __func__);
2631 codec = mbhc->codec;
2632 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2633 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2634
Joonwoo Park73375212013-05-07 12:42:44 -07002635 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002636 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002637 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002638 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002639
2640 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2641 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2642
2643 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002644 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2645 true);
2646 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002647 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002648 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002649 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002650 mbhc->mbhc_data.v_inval_ins_low =
2651 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2652 false);
2653 mbhc->mbhc_data.v_inval_ins_high =
2654 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2655 false);
2656 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002657 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2658 WCD9XXX_V_CS_HS_MAX,
2659 true);
2660 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2661 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002662
2663 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2664 MBHC_BTN_DET_V_BTN_HIGH);
2665 for (i = 0; i < btn_det->num_btn; i++)
2666 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2667
Joonwoo Park73375212013-05-07 12:42:44 -07002668 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2669 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2670 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2671 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2672 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2673 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002674
Joonwoo Park73375212013-05-07 12:42:44 -07002675 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002676 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2677 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002678 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002679 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2680 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002681 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002682 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2683 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002684 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002685 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2686 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002687
Joonwoo Park73375212013-05-07 12:42:44 -07002688 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2689 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2690 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2691 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002692
Joonwoo Parka8890262012-10-15 12:04:27 -07002693 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2694
2695 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002696 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002697 pr_debug("%s: leave\n", __func__);
2698}
2699
2700static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2701{
2702 /*
2703 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2704 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2705 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2706 */
2707 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2708}
2709
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002710/*
2711 * Mic Bias Enable Decision
2712 * Return true if high_hph_cnt is a power of 2 (!= 2)
2713 * otherwise return false
2714 */
2715static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2716{
2717 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2718}
2719
Joonwoo Parka8890262012-10-15 12:04:27 -07002720static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2721{
2722 struct wcd9xxx_mbhc *mbhc;
2723 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002724 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002725 unsigned long timeout;
2726 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002727 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002728 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002729 bool current_source_enable;
2730 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002731
2732 pr_debug("%s: enter\n", __func__);
2733
2734 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2735 codec = mbhc->codec;
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002736
2737 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2738 (1 << MBHC_CS_ENABLE_POLLING)) != 0) &&
2739 (!(snd_soc_read(codec,
2740 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002741
2742 wcd9xxx_onoff_ext_mclk(mbhc, true);
2743
2744 /*
2745 * Keep override on during entire plug type correction work.
2746 *
2747 * This is okay under the assumption that any switch irqs which use
2748 * MBHC block cancel and sync this work so override is off again
2749 * prior to switch interrupt handler's MBHC block usage.
2750 * Also while this correction work is running, we can guarantee
2751 * DAPM doesn't use any MBHC block as this work only runs with
2752 * headphone detection.
2753 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002754 if (current_source_enable)
2755 wcd9xxx_turn_onoff_current_source(mbhc, true,
2756 false);
2757 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002758 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002759
2760 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2761 while (!time_after(jiffies, timeout)) {
2762 ++retry;
2763 rmb();
2764 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002765 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002766 pr_debug("%s: stop requested\n", __func__);
2767 break;
2768 }
2769
2770 msleep(HS_DETECT_PLUG_INERVAL_MS);
2771 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002772 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002773 pr_debug("%s: Switch level is low\n", __func__);
2774 break;
2775 }
2776
2777 /* can race with removal interrupt */
2778 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002779 if (current_source_enable)
2780 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2781 highhph);
2782 else
2783 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002784 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2785
Joonwoo Park80a01172012-10-15 16:05:23 -07002786 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2787 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002788
2789 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2790 (highhph_cnt + 1) :
2791 0;
2792 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002793 if (plug_type == PLUG_TYPE_INVALID) {
2794 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002795 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2796 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002797 mbhc->current_plug == PLUG_TYPE_NONE) {
2798 wcd9xxx_report_plug(mbhc, 1,
2799 SND_JACK_HEADPHONE);
2800 }
2801 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2802 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002803 if (mbhc->mbhc_cfg->detect_extn_cable) {
2804 if (mbhc->current_plug != plug_type)
2805 wcd9xxx_report_plug(mbhc, 1,
2806 SND_JACK_HEADPHONE);
2807 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002808 wcd9xxx_report_plug(mbhc, 1,
2809 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002810 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002811 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2812 pr_debug("%s: High HPH detected, continue polling\n",
2813 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002814 } else {
2815 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2816 pt_gnd_mic_swap_cnt++;
2817 if (pt_gnd_mic_swap_cnt <
2818 GND_MIC_SWAP_THRESHOLD)
2819 continue;
2820 else if (pt_gnd_mic_swap_cnt >
2821 GND_MIC_SWAP_THRESHOLD) {
2822 /*
2823 * This is due to GND/MIC switch didn't
2824 * work, Report unsupported plug
2825 */
2826 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2827 /*
2828 * if switch is toggled, check again,
2829 * otherwise report unsupported plug
2830 */
2831 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2832 continue;
2833 }
2834 } else
2835 pt_gnd_mic_swap_cnt = 0;
2836
2837 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002838 /* Turn off override/current source */
2839 if (current_source_enable)
2840 wcd9xxx_turn_onoff_current_source(mbhc, false,
2841 false);
2842 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002843 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002844 /*
2845 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2846 */
2847 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2848 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2849 pr_debug("Attempt %d found correct plug %d\n", retry,
2850 plug_type);
2851 correction = true;
2852 break;
2853 }
2854 }
2855
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002856 highhph = false;
2857 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002858 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2859 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002860 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002861 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002862 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002863 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002864 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002865
2866 if (!correction && current_source_enable)
2867 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2868 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002869 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002870
2871 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002872
2873 if (mbhc->mbhc_cfg->detect_extn_cable) {
2874 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002875 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2876 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002877 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2878 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002879 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002880 /* Enable removal detection */
2881 wcd9xxx_cleanup_hs_polling(mbhc);
2882 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2883 }
2884 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2885 }
2886 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002887 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002888 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002889}
2890
2891static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2892{
2893 bool insert;
2894 bool is_removed = false;
2895 struct snd_soc_codec *codec = mbhc->codec;
2896
2897 pr_debug("%s: enter\n", __func__);
2898
2899 mbhc->in_swch_irq_handler = true;
2900 /* Wait here for debounce time */
2901 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2902
2903 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2904
2905 /* cancel pending button press */
2906 if (wcd9xxx_cancel_btn_work(mbhc))
2907 pr_debug("%s: button press is canceled\n", __func__);
2908
Santosh Mardic82bd362013-10-25 18:35:29 +05302909 /* cancel detect plug */
2910 wcd9xxx_cancel_hs_detect_plug(mbhc,
2911 &mbhc->correct_plug_swch);
2912
Joonwoo Parka8890262012-10-15 12:04:27 -07002913 insert = !wcd9xxx_swch_level_remove(mbhc);
2914 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2915 mbhc->current_plug, insert);
2916 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2917 mbhc->lpi_enabled = false;
2918 wmb();
2919
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002920 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2921 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2922 (1 << 1)))
2923 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002924
2925 /* Disable Mic Bias pull down and HPH Switch to GND */
2926 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2927 0x00);
2928 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2929 wcd9xxx_mbhc_detect_plug_type(mbhc);
2930 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2931 mbhc->lpi_enabled = false;
2932 wmb();
2933
Joonwoo Parka8890262012-10-15 12:04:27 -07002934 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2935 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2936 is_removed = true;
2937 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2938 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2939 is_removed = true;
2940 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2941 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302942 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002943 wcd9xxx_cleanup_hs_polling(mbhc);
2944 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2945 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002946 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2947 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2948 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002949 }
2950
2951 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002952 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2953 0x00);
2954 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2955 0x02, 0x00);
2956
Joonwoo Parka8890262012-10-15 12:04:27 -07002957 /* Enable Mic Bias pull down and HPH Switch to GND */
2958 snd_soc_update_bits(codec,
2959 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2960 0x01);
2961 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2962 0x01);
2963 /* Make sure mic trigger is turned off */
2964 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2965 0x01, 0x01);
2966 snd_soc_update_bits(codec,
2967 mbhc->mbhc_bias_regs.mbhc_reg,
2968 0x90, 0x00);
2969 /* Reset MBHC State Machine */
2970 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2971 0x08, 0x08);
2972 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2973 0x08, 0x00);
2974 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002975 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002976 }
2977 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002978exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002979 mbhc->in_swch_irq_handler = false;
2980 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2981 pr_debug("%s: leave\n", __func__);
2982}
2983
2984static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2985{
2986 int r = IRQ_HANDLED;
2987 struct wcd9xxx_mbhc *mbhc = data;
2988
2989 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002990 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002991 pr_warn("%s: failed to hold suspend\n", __func__);
2992 r = IRQ_NONE;
2993 } else {
2994 /* Call handler */
2995 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002996 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002997 }
2998
2999 pr_debug("%s: leave %d\n", __func__, r);
3000 return r;
3001}
3002
Joonwoo Park218e73f2013-08-21 16:22:18 -07003003static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07003004{
Joonwoo Park73375212013-05-07 12:42:44 -07003005 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003006 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003007 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07003008 const s16 v_ins_hu =
3009 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3010 const s16 v_ins_h =
3011 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3012 const s16 v_b1_hu =
3013 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3014 const s16 v_b1_h =
3015 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003016 const unsigned long timeout =
3017 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003018
Joonwoo Park218e73f2013-08-21 16:22:18 -07003019 while (time_before(jiffies, timeout)) {
3020 /*
3021 * This function needs to run measurements just few times during
3022 * release debounce time. Make 1ms interval to avoid
3023 * unnecessary excessive measurements.
3024 */
3025 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07003026 if (i == 0) {
3027 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
3028 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
3029 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003030 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003031 r = 1;
3032 break;
3033 }
3034 } else {
3035 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
3036 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
3037 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003038 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003039 r = 1;
3040 break;
3041 }
3042 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003043 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003044 }
3045
3046 return r;
3047}
3048
3049/* called under codec_resource_lock acquisition */
3050static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3051 const s32 micmv)
3052{
3053 s16 *v_btn_low, *v_btn_high;
3054 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3055 int i, btn = -1;
3056
3057 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3058 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3059 MBHC_BTN_DET_V_BTN_LOW);
3060 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3061 MBHC_BTN_DET_V_BTN_HIGH);
3062
3063 for (i = 0; i < btn_det->num_btn; i++) {
3064 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3065 btn = i;
3066 break;
3067 }
3068 }
3069
3070 if (btn == -1)
3071 pr_debug("%s: couldn't find button number for mic mv %d\n",
3072 __func__, micmv);
3073
3074 return btn;
3075}
3076
3077static int wcd9xxx_get_button_mask(const int btn)
3078{
3079 int mask = 0;
3080 switch (btn) {
3081 case 0:
3082 mask = SND_JACK_BTN_0;
3083 break;
3084 case 1:
3085 mask = SND_JACK_BTN_1;
3086 break;
3087 case 2:
3088 mask = SND_JACK_BTN_2;
3089 break;
3090 case 3:
3091 mask = SND_JACK_BTN_3;
3092 break;
3093 case 4:
3094 mask = SND_JACK_BTN_4;
3095 break;
3096 case 5:
3097 mask = SND_JACK_BTN_5;
3098 break;
3099 case 6:
3100 mask = SND_JACK_BTN_6;
3101 break;
3102 case 7:
3103 mask = SND_JACK_BTN_7;
3104 break;
3105 }
3106 return mask;
3107}
3108
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003109static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003110{
3111 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003112 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003113 struct snd_soc_codec *codec = mbhc->codec;
3114
3115 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3116 /* Pull down micbias to ground and disconnect vddio switch */
3117 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3118 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3119 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3120 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3121
3122 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003123 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3124 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003125 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003126 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003127 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003128 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3129 }
3130 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003131 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003132 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3133 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003134
Joonwoo Park520a0f92013-05-14 19:39:58 -07003135 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003136 if (change)
3137 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3138 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003139 /* Disable pull down micbias to ground */
3140 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3141 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3142}
3143
Joonwoo Park218e73f2013-08-21 16:22:18 -07003144void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3145{
3146 const u16 sta_z = mbhc->mbhc_data.sta_z;
3147 const u16 dce_z = mbhc->mbhc_data.dce_z;
3148
3149 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3150 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3151 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3152 mbhc->mbhc_data.sta_z & 0xFFFF,
3153 mbhc->mbhc_data.dce_z & 0xFFFF);
3154
3155 wcd9xxx_mbhc_calc_thres(mbhc);
3156 wcd9xxx_calibrate_hs_polling(mbhc);
3157}
3158
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003159/*
3160 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3161 * to ceilmv + buffer
3162 */
3163static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3164{
3165 u16 v_brh, v_b1_hu;
3166 int mv;
3167 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3168 void *calibration = mbhc->mbhc_cfg->calibration;
3169 struct snd_soc_codec *codec = mbhc->codec;
3170
3171 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3172 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3173 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3174
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +05303175 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
3176 /*
3177 * update LSB first so mbhc hardware block
3178 * doesn't see too low value.
3179 */
3180 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
3181 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
3182 0xFF);
3183 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3184 (v_b1_hu >> 8) & 0xFF);
3185 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
3186 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
3187 0xFF);
3188 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3189 (v_brh >> 8) & 0xFF);
3190 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003191 return 0;
3192}
3193
Joonwoo Parka8890262012-10-15 12:04:27 -07003194irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3195{
3196 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003197 bool vddio;
3198 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003199 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003200 s32 stamv, stamv_s;
3201 s16 *v_btn_high;
3202 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003203 int btn = -1, meas = 0;
3204 struct wcd9xxx_mbhc *mbhc = data;
3205 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3206 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3207 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003208 short dce[d->n_btn_meas + 1], sta;
3209 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003210 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003211 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003212 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003213 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003214
3215 pr_debug("%s: enter\n", __func__);
3216
3217 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3218 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3219
3220 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3221 pr_debug("%s: mbhc is being recovered, skip button press\n",
3222 __func__);
3223 goto done;
3224 }
3225
3226 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3227
3228 if (!mbhc->polling_active) {
3229 pr_warn("%s: mbhc polling is not active, skip button press\n",
3230 __func__);
3231 goto done;
3232 }
3233
Joonwoo Parka8890262012-10-15 12:04:27 -07003234 /* If switch nterrupt already kicked in, ignore button press */
3235 if (mbhc->in_swch_irq_handler) {
3236 pr_debug("%s: Swtich level changed, ignore button press\n",
3237 __func__);
3238 btn = -1;
3239 goto done;
3240 }
3241
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303242 /*
3243 * setup internal micbias if codec uses internal micbias for
3244 * headset detection
3245 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05303246 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303247 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3248 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3249 else
3250 pr_err("%s: internal bias requested but codec did not provide callback\n",
3251 __func__);
3252 }
3253
3254
Joonwoo Parka8890262012-10-15 12:04:27 -07003255 /* Measure scaled HW DCE */
3256 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3257 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003258
Joonwoo Park218e73f2013-08-21 16:22:18 -07003259 dce_z = mbhc->mbhc_data.dce_z;
3260 sta_z = mbhc->mbhc_data.sta_z;
3261
Joonwoo Parka8890262012-10-15 12:04:27 -07003262 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003263 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003264 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003265 if (mbhc_status != STATUS_REL_DETECTION) {
3266 if (mbhc->mbhc_last_resume &&
3267 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3268 pr_debug("%s: Button is released after resume\n",
3269 __func__);
3270 n_btn_meas = 0;
3271 } else {
3272 pr_debug("%s: Button is released without resume",
3273 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003274 if (mbhc->update_z) {
3275 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003276 dce_z = mbhc->mbhc_data.dce_z;
3277 sta_z = mbhc->mbhc_data.sta_z;
3278 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003279 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003280 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3281 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003282 if (vddio)
3283 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3284 false);
3285 else
3286 stamv_s = stamv;
3287 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003288 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003289 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3290 false) : mv[0];
3291 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003292 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3293 btn = -1;
3294 goto done;
3295 }
3296 }
3297
Joonwoo Park520a0f92013-05-14 19:39:58 -07003298 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3299 meas++)
3300 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3301
Joonwoo Park218e73f2013-08-21 16:22:18 -07003302 if (mbhc->update_z) {
3303 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003304 dce_z = mbhc->mbhc_data.dce_z;
3305 sta_z = mbhc->mbhc_data.sta_z;
3306 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003307 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003308
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003309 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3310 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003311 if (vddio)
3312 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3313 else
3314 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003315 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003316 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003317
3318 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003319 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3320 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003321 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3322 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003323 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003324 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003325 if (n_btn_meas == 0)
3326 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003327 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3328 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003329 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3330 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003331 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3332 mv[meas];
3333 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003334 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003335 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3336 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003337 /*
3338 * if large enough measurements are collected,
3339 * start to check if last all n_btn_con measurements were
3340 * in same button low/high range
3341 */
3342 if (meas + 1 >= d->n_btn_con) {
3343 for (i = 0; i < d->n_btn_con; i++)
3344 if ((btnmeas[meas] < 0) ||
3345 (btnmeas[meas] != btnmeas[meas - i]))
3346 break;
3347 if (i == d->n_btn_con) {
3348 /* button pressed */
3349 btn = btnmeas[meas];
3350 break;
3351 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3352 /*
3353 * if left measurements are less than n_btn_con,
3354 * it's impossible to find button number
3355 */
3356 break;
3357 }
3358 }
3359 }
3360
3361 if (btn >= 0) {
3362 if (mbhc->in_swch_irq_handler) {
3363 pr_debug(
3364 "%s: Switch irq triggered, ignore button press\n",
3365 __func__);
3366 goto done;
3367 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003368 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3369 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3370 MBHC_BTN_DET_V_BTN_HIGH);
3371 WARN_ON(btn >= btn_det->num_btn);
3372 /* reprogram release threshold to catch voltage ramp up early */
3373 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3374
Joonwoo Parka8890262012-10-15 12:04:27 -07003375 mask = wcd9xxx_get_button_mask(btn);
3376 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003377 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003378 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3379 msecs_to_jiffies(400)) == 0) {
3380 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003381 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003382 }
3383 } else {
3384 pr_debug("%s: bogus button press, too short press?\n",
3385 __func__);
3386 }
3387
3388 done:
3389 pr_debug("%s: leave\n", __func__);
3390 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3391 return IRQ_HANDLED;
3392}
3393
3394static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3395{
3396 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003397 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003398 struct wcd9xxx_mbhc *mbhc = data;
3399
3400 pr_debug("%s: enter\n", __func__);
3401 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3402 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3403
Joonwoo Parka8890262012-10-15 12:04:27 -07003404 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3405 ret = wcd9xxx_cancel_btn_work(mbhc);
3406 if (ret == 0) {
3407 pr_debug("%s: Reporting long button release event\n",
3408 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003409 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003410 mbhc->buttons_pressed);
3411 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003412 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003413 pr_debug("%s: Fake button press interrupt\n",
3414 __func__);
3415 } else {
3416 if (mbhc->in_swch_irq_handler) {
3417 pr_debug("%s: Switch irq kicked in, ignore\n",
3418 __func__);
3419 } else {
3420 pr_debug("%s: Reporting btn press\n",
3421 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003422 wcd9xxx_jack_report(mbhc,
3423 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003424 mbhc->buttons_pressed,
3425 mbhc->buttons_pressed);
3426 pr_debug("%s: Reporting btn release\n",
3427 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003428 wcd9xxx_jack_report(mbhc,
3429 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003430 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003431 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003432 }
3433 }
3434 }
3435
3436 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3437 }
3438
3439 wcd9xxx_calibrate_hs_polling(mbhc);
3440
Joonwoo Park218e73f2013-08-21 16:22:18 -07003441 if (waitdebounce)
3442 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003443 wcd9xxx_start_hs_polling(mbhc);
3444
3445 pr_debug("%s: leave\n", __func__);
3446 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3447 return IRQ_HANDLED;
3448}
3449
3450static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3451{
3452 struct wcd9xxx_mbhc *mbhc = data;
3453 struct snd_soc_codec *codec;
3454
3455 pr_info("%s: received HPHL OCP irq\n", __func__);
3456
3457 if (mbhc) {
3458 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003459 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3460 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003461 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003462 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003463 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3464 0x10, 0x00);
3465 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3466 0x10, 0x10);
3467 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003468 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003469 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003470 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003471 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003472 mbhc->hph_status,
3473 WCD9XXX_JACK_MASK);
3474 }
3475 } else {
3476 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3477 }
3478
3479 return IRQ_HANDLED;
3480}
3481
3482static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3483{
3484 struct wcd9xxx_mbhc *mbhc = data;
3485 struct snd_soc_codec *codec;
3486
3487 pr_info("%s: received HPHR OCP irq\n", __func__);
3488 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003489 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3490 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003491 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003492 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003493 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3494 0x00);
3495 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3496 0x10);
3497 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003498 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003499 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003500 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003501 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003502 mbhc->hph_status, WCD9XXX_JACK_MASK);
3503 }
3504
3505 return IRQ_HANDLED;
3506}
3507
3508static int wcd9xxx_acdb_mclk_index(const int rate)
3509{
3510 if (rate == MCLK_RATE_12288KHZ)
3511 return 0;
3512 else if (rate == MCLK_RATE_9600KHZ)
3513 return 1;
3514 else {
3515 BUG_ON(1);
3516 return -EINVAL;
3517 }
3518}
3519
3520static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3521{
3522 u32 dce_wait, sta_wait;
3523 u8 ncic, nmeas, navg;
3524 void *calibration;
3525 u8 *n_cic, *n_ready;
3526 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3527 u8 npoll = 4, nbounce_wait = 30;
3528 struct snd_soc_codec *codec = mbhc->codec;
3529 int idx = wcd9xxx_acdb_mclk_index(rate);
3530 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3531
3532 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3533 rate);
3534 calibration = mbhc->mbhc_cfg->calibration;
3535
3536 /*
3537 * First compute the DCE / STA wait times depending on tunable
3538 * parameters. The value is computed in microseconds
3539 */
3540 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3541 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3542 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3543 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3544 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3545
3546 /* ncic stays with the same what we had during calibration */
3547 ncic = n_cic[idxmclk];
3548 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3549 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3550 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003551 /* give extra margin to sta for safety */
3552 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003553 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3554 n_ready[idx]) + 10;
3555
3556 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3557 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3558
3559 if (rate == MCLK_RATE_12288KHZ) {
3560 npoll = 4;
3561 nbounce_wait = 30;
3562 } else if (rate == MCLK_RATE_9600KHZ) {
3563 npoll = 3;
3564 nbounce_wait = 23;
3565 }
3566
3567 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3568 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3569 pr_debug("%s: leave\n", __func__);
3570}
3571
3572static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3573{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003574 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003575 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003576 struct snd_soc_codec *codec = mbhc->codec;
3577
3578 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003579 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3580 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003581 wcd9xxx_turn_onoff_rel_detection(codec, false);
3582
3583 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3584 WARN_ON(!mbhc->mbhc_data.t_dce);
3585 WARN_ON(!mbhc->mbhc_data.t_sta);
3586
3587 /*
3588 * LDOH and CFILT are already configured during pdata handling.
3589 * Only need to make sure CFILT and bandgap are in Fast mode.
3590 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003591 *
3592 * In case when Micbias is powered by external source, request
3593 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003594 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003595 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3596 mbhc->mbhc_cb->enable_mb_source(codec, true);
3597
Joonwoo Parka8890262012-10-15 12:04:27 -07003598 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303599 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3600 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3601 else
3602 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3603 0x40, 0x00);
3604
Joonwoo Parka8890262012-10-15 12:04:27 -07003605 /*
3606 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3607 * to perform ADC calibration
3608 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303609 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3610 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3611 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003612 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3613 mbhc->mbhc_cfg->micbias << 5);
3614 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3615 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3616 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303617 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3618 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3619 else
3620 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3621 0x04, 0x04);
3622
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003623 /* Pull down micbias to ground */
3624 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3625 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3626 /* Disconnect override from micbias */
3627 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3628 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3629 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303630 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303631 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3632 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3633 else
3634 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3635 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003636 /*
3637 * Hardware that has external cap can delay mic bias ramping down up
3638 * to 50ms.
3639 */
3640 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003641 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003642 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003643 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003644 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003645
3646 /* compute dce_z for current source */
3647 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3648 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3649 WCD9XXX_MBHC_NSC_CS << 3);
3650
3651 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3652 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3653 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3654 false);
3655 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3656 mbhc->mbhc_data.dce_nsc_cs_z);
3657
3658 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3659
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003660 /* STA measurement for 0 voltage */
3661 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3662 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3663 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003664
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003665 /* Restore registers */
3666 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3667 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003668
3669 /* DCE measurment for MB voltage */
3670 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3671 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303672 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303673 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3674 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3675 else
3676 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3677 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003678 /*
3679 * Hardware that has external cap can delay mic bias ramping down up
3680 * to 50ms.
3681 */
3682 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003683 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3684 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3685 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3686
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003687 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003688 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3689 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3690 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303691 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303692 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3693 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3694 else
3695 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3696 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003697 /*
3698 * Hardware that has external cap can delay mic bias ramping down up
3699 * to 50ms.
3700 */
3701 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003702 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3703 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3704 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3705
3706 /* Restore default settings. */
3707 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303708 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303709 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303710 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3711 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3712 else
3713 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3714 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003715 usleep_range(100, 100);
3716
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003717 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3718 mbhc->mbhc_cb->enable_mb_source(codec, false);
3719
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003720 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3721 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003722 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003723
Joonwoo Parka8890262012-10-15 12:04:27 -07003724 pr_debug("%s: leave\n", __func__);
3725}
3726
3727static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3728{
3729 int n;
3730 u8 *gain;
3731 struct wcd9xxx_mbhc_general_cfg *generic;
3732 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3733 struct snd_soc_codec *codec = mbhc->codec;
3734 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3735
3736 pr_debug("%s: enter\n", __func__);
3737 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3738 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3739
3740 for (n = 0; n < 8; n++) {
3741 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3742 0x07, n);
3743 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3744 btn_det->c[n]);
3745 }
3746
3747 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3748 btn_det->nc);
3749
3750 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3751 generic->mbhc_nsa << 4);
3752
3753 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3754 btn_det->n_meas);
3755
3756 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3757 generic->mbhc_navg);
3758
3759 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3760
3761 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3762 btn_det->mbhc_nsc << 3);
3763
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003764 if (mbhc->mbhc_cb &&
3765 mbhc->mbhc_cb->get_cdc_type() !=
3766 WCD9XXX_CDC_TYPE_HELICON) {
3767 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3768 snd_soc_update_bits(codec,
3769 mbhc->resmgr->reg_addr->micb_4_mbhc,
3770 0x03, MBHC_MICBIAS2);
3771 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003772
3773 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3774
3775 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3776
3777 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3778 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3779 gain[idx] << 3);
Yeleswarapu, Nagaradhesh0564ac32013-11-27 11:44:50 +05303780 snd_soc_update_bits(codec, WCD9XXX_A_MICB_2_MBHC, 0x04, 0x04);
Joonwoo Parka8890262012-10-15 12:04:27 -07003781
3782 pr_debug("%s: leave\n", __func__);
3783}
3784
3785static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3786{
3787 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003788 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303789
Joonwoo Parka8890262012-10-15 12:04:27 -07003790 if (mbhc->mbhc_cfg->gpio) {
3791 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3792 wcd9xxx_mech_plug_detect_irq,
3793 (IRQF_TRIGGER_RISING |
3794 IRQF_TRIGGER_FALLING |
3795 IRQF_DISABLED),
3796 "headset detect", mbhc);
3797 if (ret) {
3798 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3799 mbhc->mbhc_cfg->gpio_irq);
3800 } else {
3801 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3802 if (ret)
3803 pr_err("%s: Failed to enable wake up irq %d\n",
3804 __func__, mbhc->mbhc_cfg->gpio_irq);
3805 }
3806 } else if (mbhc->mbhc_cfg->insert_detect) {
3807 /* Enable HPHL_10K_SW */
3808 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3809 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303810
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003811 ret = wcd9xxx_request_irq(core_res,
3812 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003813 wcd9xxx_mech_plug_detect_irq,
3814 "Jack Detect",
3815 mbhc);
3816 if (ret)
3817 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003818 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003819 }
3820
3821 return ret;
3822}
3823
3824static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3825{
3826 int ret = 0;
3827 struct snd_soc_codec *codec = mbhc->codec;
3828
3829 pr_debug("%s: enter\n", __func__);
3830
3831 /* Enable MCLK during calibration */
3832 wcd9xxx_onoff_ext_mclk(mbhc, true);
3833 wcd9xxx_mbhc_setup(mbhc);
3834 wcd9xxx_mbhc_cal(mbhc);
3835 wcd9xxx_mbhc_calc_thres(mbhc);
3836 wcd9xxx_onoff_ext_mclk(mbhc, false);
3837 wcd9xxx_calibrate_hs_polling(mbhc);
3838
3839 /* Enable Mic Bias pull down and HPH Switch to GND */
3840 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3841 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3842 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3843
3844 if (!IS_ERR_VALUE(ret)) {
3845 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3846 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003847 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003848 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003849 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003850 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003851
3852 /* Initialize mechanical mbhc */
3853 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3854
3855 if (!ret && mbhc->mbhc_cfg->gpio) {
3856 /* Requested with IRQF_DISABLED */
3857 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3858
3859 /* Bootup time detection */
3860 wcd9xxx_swch_irq_handler(mbhc);
3861 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3862 pr_debug("%s: Setting up codec own insert detection\n",
3863 __func__);
3864 /* Setup for insertion detection */
3865 wcd9xxx_insert_detect_setup(mbhc, true);
3866 }
3867 }
3868
3869 pr_debug("%s: leave\n", __func__);
3870
3871 return ret;
3872}
3873
3874static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3875{
3876 struct delayed_work *dwork;
3877 struct wcd9xxx_mbhc *mbhc;
3878 struct snd_soc_codec *codec;
3879 const struct firmware *fw;
3880 int ret = -1, retry = 0;
3881
3882 dwork = to_delayed_work(work);
3883 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3884 codec = mbhc->codec;
3885
3886 while (retry < FW_READ_ATTEMPTS) {
3887 retry++;
3888 pr_info("%s:Attempt %d to request MBHC firmware\n",
3889 __func__, retry);
3890 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3891 codec->dev);
3892
3893 if (ret != 0) {
3894 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3895 } else {
3896 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3897 break;
3898 }
3899 }
3900
3901 if (ret != 0) {
3902 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3903 __func__);
3904 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3905 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3906 __func__);
3907 release_firmware(fw);
3908 } else {
3909 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3910 mbhc->mbhc_fw = fw;
3911 }
3912
3913 (void) wcd9xxx_init_and_calibrate(mbhc);
3914}
3915
3916#ifdef CONFIG_DEBUG_FS
3917ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3918 size_t count, loff_t *pos)
3919{
3920 const int size = 768;
3921 char buffer[size];
3922 int n = 0;
3923 struct wcd9xxx_mbhc *mbhc = file->private_data;
3924 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003925 const s16 v_ins_hu =
3926 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3927 const s16 v_ins_h =
3928 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3929 const s16 v_b1_hu =
3930 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3931 const s16 v_b1_h =
3932 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3933 const s16 v_br_h =
3934 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003935
Joonwoo Park520a0f92013-05-14 19:39:58 -07003936 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3937 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003938 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3939 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003940 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3941 p->dce_nsc_cs_z,
3942 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3943 p->dce_nsc_cs_z,
3944 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003945 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3946 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3947 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3948 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003949 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3950 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3951 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3952 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3953 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3954 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3955 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003956 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003957 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003958 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003959 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003960 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003961 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003962 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3963 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3964 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3965 p->v_no_mic,
3966 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3967 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3968 p->v_inval_ins_low);
3969 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3970 p->v_inval_ins_high);
3971 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3972 !wcd9xxx_swch_level_remove(mbhc));
3973 buffer[n] = 0;
3974
3975 return simple_read_from_buffer(buf, count, pos, buffer, n);
3976}
3977
3978static int codec_debug_open(struct inode *inode, struct file *file)
3979{
3980 file->private_data = inode->i_private;
3981 return 0;
3982}
3983
3984static ssize_t codec_debug_write(struct file *filp,
3985 const char __user *ubuf, size_t cnt,
3986 loff_t *ppos)
3987{
3988 char lbuf[32];
3989 char *buf;
3990 int rc;
3991 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3992
3993 if (cnt > sizeof(lbuf) - 1)
3994 return -EINVAL;
3995
3996 rc = copy_from_user(lbuf, ubuf, cnt);
3997 if (rc)
3998 return -EFAULT;
3999
4000 lbuf[cnt] = '\0';
4001 buf = (char *)lbuf;
4002 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
4003 false : true;
4004 return rc;
4005}
4006
4007static const struct file_operations mbhc_trrs_debug_ops = {
4008 .open = codec_debug_open,
4009 .write = codec_debug_write,
4010};
4011
4012static const struct file_operations mbhc_debug_ops = {
4013 .open = codec_debug_open,
4014 .read = codec_mbhc_debug_read,
4015};
4016
4017static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
4018{
4019 mbhc->debugfs_poke =
4020 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
4021 &mbhc_trrs_debug_ops);
4022 mbhc->debugfs_mbhc =
4023 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
4024 NULL, mbhc, &mbhc_debug_ops);
4025}
4026
4027static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
4028{
4029 debugfs_remove(mbhc->debugfs_poke);
4030 debugfs_remove(mbhc->debugfs_mbhc);
4031}
4032#else
4033static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
4034{
4035}
4036
4037static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
4038{
4039}
4040#endif
4041
4042int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
4043 struct wcd9xxx_mbhc_config *mbhc_cfg)
4044{
Simmi Pateriya95466b12013-05-09 20:08:46 +05304045 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004046 struct snd_soc_codec *codec = mbhc->codec;
4047
4048 pr_debug("%s: enter\n", __func__);
4049
4050 if (!codec) {
4051 pr_err("%s: no codec\n", __func__);
4052 return -EINVAL;
4053 }
4054
4055 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
4056 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
4057 pr_err("Error: unsupported clock rate %d\n",
4058 mbhc_cfg->mclk_rate);
4059 return -EINVAL;
4060 }
4061
4062 /* Save mbhc config */
4063 mbhc->mbhc_cfg = mbhc_cfg;
4064
4065 /* Get HW specific mbhc registers' address */
4066 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4067
4068 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304069 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4070 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4071 else
4072 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4073 0x40, WCD9XXX_CFILT_FAST_MODE);
4074
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004075 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004076 * setup internal micbias if codec uses internal micbias for
4077 * headset detection
4078 */
4079 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304080 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004081 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304082 } else {
4083 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004084 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304085 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004086 }
4087
4088 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004089 * If codec has specific clock gating for MBHC,
4090 * remove the clock gate
4091 */
4092 if (mbhc->mbhc_cb &&
4093 mbhc->mbhc_cb->enable_clock_gate)
4094 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4095
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004096 if (!mbhc->mbhc_cfg->read_fw_bin ||
4097 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004098 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004099 } else {
4100 if (!mbhc->mbhc_fw)
4101 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4102 usecs_to_jiffies(FW_READ_TIMEOUT));
4103 else
4104 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4105 __func__, mbhc->mbhc_fw);
4106 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004107
4108 pr_debug("%s: leave %d\n", __func__, rc);
4109 return rc;
4110}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004111EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004112
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004113void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4114{
4115 if (mbhc->mbhc_fw) {
4116 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4117 release_firmware(mbhc->mbhc_fw);
4118 mbhc->mbhc_fw = NULL;
4119 }
4120}
4121EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4122
Joonwoo Parka8890262012-10-15 12:04:27 -07004123static enum wcd9xxx_micbias_num
4124wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4125{
4126 enum wcd9xxx_micbias_num ret;
4127 switch (event) {
4128 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004129 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4130 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4131 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004132 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004133 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004134 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004135 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4136 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4137 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004138 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004139 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004140 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004141 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4142 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4143 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004144 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004145 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004146 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004147 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4148 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4149 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004150 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004151 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004152 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004153 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004154 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004155 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004156 }
4157 return ret;
4158}
4159
4160static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4161{
4162 int ret;
4163 switch (event) {
4164 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4165 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4166 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4167 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4168 ret = WCD9XXX_CFILT1_SEL;
4169 break;
4170 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4171 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4172 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4173 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4174 ret = WCD9XXX_CFILT2_SEL;
4175 break;
4176 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4177 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4178 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4179 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4180 ret = WCD9XXX_CFILT3_SEL;
4181 break;
4182 default:
4183 ret = -1;
4184 }
4185 return ret;
4186}
4187
4188static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4189{
4190 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004191 const struct wcd9xxx_micbias_setting *mb_pdata =
4192 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004193
4194 switch (mbhc->mbhc_cfg->micbias) {
4195 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004196 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004197 break;
4198 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004199 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004200 break;
4201 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004202 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004203 break;
4204 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004205 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004206 break;
4207 default:
4208 cfilt = MBHC_MICBIAS_INVALID;
4209 break;
4210 }
4211 return cfilt;
4212}
4213
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004214static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4215{
4216 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4217 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4218 else
4219 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4220 0x40, on ? 0x40 : 0x00);
4221}
4222
Joonwoo Parka8890262012-10-15 12:04:27 -07004223static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4224 void *data)
4225{
4226 int ret = 0;
4227 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4228 struct snd_soc_codec *codec = mbhc->codec;
4229 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4230
4231 pr_debug("%s: enter event %s(%d)\n", __func__,
4232 wcd9xxx_get_event_string(event), event);
4233
4234 switch (event) {
4235 /* MICBIAS usage change */
4236 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4237 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4238 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4239 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004240 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4241 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004242 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004243 /*
4244 * Enable MBHC TxFE whenever micbias is
4245 * turned ON and polling is active
4246 */
4247 if (mbhc->polling_active)
4248 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4249 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004250 break;
4251 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4252 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4253 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4254 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004255 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004256 wcd9xxx_event_to_micbias(event) &&
4257 wcd9xxx_mbhc_polling(mbhc)) {
4258 /* if polling is on, restart it */
4259 wcd9xxx_pause_hs_polling(mbhc);
4260 wcd9xxx_start_hs_polling(mbhc);
4261 }
4262 break;
4263 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4264 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4265 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4266 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004267 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4268 wcd9xxx_event_to_micbias(event)) {
4269 if (mbhc->event_state &
4270 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4271 wcd9xxx_switch_micbias(mbhc, 1);
4272 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304273 * Disable MBHC TxFE, in case it was enabled earlier
4274 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004275 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304276 if (!mbhc->polling_active)
4277 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004278 }
Simmi Pateriyad54e6db2013-11-28 09:33:31 +05304279 if (mbhc->micbias_enable && mbhc->polling_active &&
4280 !(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg)
4281 & 0x80)) {
4282 pr_debug("%s:Micbias turned off by recording, set up again",
4283 __func__);
4284 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
4285 0x80, 0x80);
4286 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004287 break;
4288 /* PA usage change */
4289 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004290 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004291 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004292 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004293 wcd9xxx_switch_micbias(mbhc, 1);
4294 break;
4295 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004296 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004297 break;
4298 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004299 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004300 /* if HPH PAs are off, report OCP and switch back to CFILT */
4301 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4302 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4303 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4304 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004305 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004306 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004307 1 << MBHC_EVENT_PRE_TX_1_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004308 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004309 break;
4310 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004311 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004312 /* if HPH PAs are off, report OCP and switch back to CFILT */
4313 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4314 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4315 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4316 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004317 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004318 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004319 1 << MBHC_EVENT_PRE_TX_1_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004320 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004321 break;
4322 /* Clock usage change */
4323 case WCD9XXX_EVENT_PRE_MCLK_ON:
4324 break;
4325 case WCD9XXX_EVENT_POST_MCLK_ON:
4326 /* Change to lower TxAAF frequency */
4327 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4328 1 << 4);
4329 /* Re-calibrate clock rate dependent values */
4330 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4331 /* If clock source changes, stop and restart polling */
4332 if (wcd9xxx_mbhc_polling(mbhc)) {
4333 wcd9xxx_calibrate_hs_polling(mbhc);
4334 wcd9xxx_start_hs_polling(mbhc);
4335 }
4336 break;
4337 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4338 /* If clock source changes, stop and restart polling */
4339 if (wcd9xxx_mbhc_polling(mbhc))
4340 wcd9xxx_pause_hs_polling(mbhc);
4341 break;
4342 case WCD9XXX_EVENT_POST_MCLK_OFF:
4343 break;
4344 case WCD9XXX_EVENT_PRE_RCO_ON:
4345 break;
4346 case WCD9XXX_EVENT_POST_RCO_ON:
4347 /* Change to higher TxAAF frequency */
4348 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4349 0 << 4);
4350 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004351 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004352 /* If clock source changes, stop and restart polling */
4353 if (wcd9xxx_mbhc_polling(mbhc)) {
4354 wcd9xxx_calibrate_hs_polling(mbhc);
4355 wcd9xxx_start_hs_polling(mbhc);
4356 }
4357 break;
4358 case WCD9XXX_EVENT_PRE_RCO_OFF:
4359 /* If clock source changes, stop and restart polling */
4360 if (wcd9xxx_mbhc_polling(mbhc))
4361 wcd9xxx_pause_hs_polling(mbhc);
4362 break;
4363 case WCD9XXX_EVENT_POST_RCO_OFF:
4364 break;
4365 /* CFILT usage change */
4366 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4367 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4368 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4369 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4370 wcd9xxx_event_to_cfilt(event))
4371 /*
4372 * Switch CFILT to slow mode if MBHC CFILT is being
4373 * used.
4374 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304375 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004376 break;
4377 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4378 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4379 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4380 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4381 wcd9xxx_event_to_cfilt(event))
4382 /*
4383 * Switch CFILT to fast mode if MBHC CFILT is not
4384 * used anymore.
4385 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304386 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004387 break;
4388 /* System resume */
4389 case WCD9XXX_EVENT_POST_RESUME:
4390 mbhc->mbhc_last_resume = jiffies;
4391 break;
4392 /* BG mode chage */
4393 case WCD9XXX_EVENT_PRE_BG_OFF:
4394 case WCD9XXX_EVENT_POST_BG_OFF:
4395 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4396 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4397 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4398 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4399 /* Not used for now */
4400 break;
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004401 case WCD9XXX_EVENT_PRE_TX_1_3_ON:
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004402 /*
4403 * if polling is ON, mbhc micbias not enabled
4404 * switch micbias source to VDDIO
4405 */
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004406 set_bit(MBHC_EVENT_PRE_TX_1_3_ON, &mbhc->event_state);
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004407 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
4408 & 0x80) &&
4409 mbhc->polling_active && !mbhc->mbhc_micbias_switched)
4410 wcd9xxx_switch_micbias(mbhc, 1);
4411 break;
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004412 case WCD9XXX_EVENT_POST_TX_1_3_OFF:
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004413 /*
4414 * Switch back to micbias if HPH PA or TX3 path
4415 * is disabled
4416 */
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004417 clear_bit(MBHC_EVENT_PRE_TX_1_3_ON, &mbhc->event_state);
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004418 if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
4419 !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
4420 1 << MBHC_EVENT_PA_HPHR)))
4421 wcd9xxx_switch_micbias(mbhc, 0);
4422 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004423 default:
4424 WARN(1, "Unknown event %d\n", event);
4425 ret = -EINVAL;
4426 }
4427
4428 pr_debug("%s: leave\n", __func__);
4429
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304430 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004431}
4432
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004433static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4434 uint32_t *zr)
4435{
4436 int i;
4437 int ret = 0;
4438 s16 l[3], r[3];
4439 s16 *z[] = {
4440 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4441 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004442 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004443 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004444 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4445 /* Phase 1 */
4446 /* Set MBHC_MUX for HPHL without ical */
4447 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4448 /* Set MBHC_MUX for HPHR without ical */
4449 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4450 /* Set MBHC_MUX for HPHR with ical */
4451 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4452 /* Set MBHC_MUX for HPHL with ical */
4453 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4454
4455 /* Phase 2 */
4456 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4457 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4458 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4459 };
4460
4461 pr_debug("%s: enter\n", __func__);
4462 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4463
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004464 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4465 !mbhc->mbhc_cb->compute_impedance || !zl ||
4466 !zr)
4467 return -EINVAL;
4468
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004469 /*
4470 * Impedance detection is an intrusive function as it mutes RX paths,
4471 * enable PAs and etc. Therefore codec drvier including ALSA
4472 * shouldn't read and write hardware registers during detection.
4473 */
4474 mutex_lock(&codec->mutex);
4475
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004476 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004477
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004478 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004479 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004480
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004481 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4482 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004483
4484 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004485 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004486 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4487 reg_set_mux[i].mask,
4488 reg_set_mux[i].val);
4489 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4490 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4491 else
4492 snd_soc_update_bits(codec,
4493 WCD9XXX_A_MBHC_SCALING_MUX_1,
4494 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004495 /* 25us is required after mux change to settle down */
4496 usleep_range(mux_wait_us,
4497 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004498 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004499 }
4500
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004501 /* Codec specific setup for L2 and R2 measurements */
4502 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004503
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004504 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004505 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4506 reg_set_mux[i].mask,
4507 reg_set_mux[i].val);
4508 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4509 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4510 else
4511 snd_soc_update_bits(codec,
4512 WCD9XXX_A_MBHC_SCALING_MUX_1,
4513 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004514 /* 25us is required after mux change to settle down */
4515 usleep_range(mux_wait_us,
4516 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004517 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004518 }
4519
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004520 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004521
4522 mutex_unlock(&codec->mutex);
4523
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004524 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004525
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004526 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004527 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004528
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004529 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004530 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004531 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4532 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004533 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004534 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004535 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4536 pr_debug("%s: Impedance detection completed\n", __func__);
4537
4538 return ret;
4539}
4540
4541int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4542 uint32_t *zr)
4543{
4544 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4545 *zl = mbhc->zl;
4546 *zr = mbhc->zr;
4547 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4548
4549 if (*zl && *zr)
4550 return 0;
4551 else
4552 return -EINVAL;
4553}
4554
Joonwoo Parka8890262012-10-15 12:04:27 -07004555/*
4556 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4557 *
4558 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4559 */
4560int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004561 struct snd_soc_codec *codec,
4562 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004563 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4564 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4565 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004566 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004567{
4568 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004569 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004570
4571 pr_debug("%s: enter\n", __func__);
4572 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4573 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4574
4575 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4576 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4577 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4578 mbhc->mbhc_micbias_switched = false;
4579 mbhc->polling_active = false;
4580 mbhc->mbhc_state = MBHC_STATE_NONE;
4581 mbhc->in_swch_irq_handler = false;
4582 mbhc->current_plug = PLUG_TYPE_NONE;
4583 mbhc->lpi_enabled = false;
4584 mbhc->no_mic_headset_override = false;
4585 mbhc->mbhc_last_resume = 0;
4586 mbhc->codec = codec;
4587 mbhc->resmgr = resmgr;
4588 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004589 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004590 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304591 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004592 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004593 mbhc->impedance_detect = impedance_det_en;
Joonwoo Parka8890262012-10-15 12:04:27 -07004594
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004595 if (mbhc->intr_ids == NULL) {
4596 pr_err("%s: Interrupt mapping not provided\n", __func__);
4597 return -EINVAL;
4598 }
4599
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004600 if (mbhc->headset_jack.jack == NULL) {
4601 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4602 &mbhc->headset_jack);
4603 if (ret) {
4604 pr_err("%s: Failed to create new jack\n", __func__);
4605 return ret;
4606 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004607
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004608 ret = snd_soc_jack_new(codec, "Button Jack",
4609 WCD9XXX_JACK_BUTTON_MASK,
4610 &mbhc->button_jack);
4611 if (ret) {
4612 pr_err("Failed to create new jack\n");
4613 return ret;
4614 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004615
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004616 ret = snd_jack_set_key(mbhc->button_jack.jack,
4617 SND_JACK_BTN_0,
4618 KEY_MEDIA);
4619 if (ret) {
4620 pr_err("%s: Failed to set code for btn-0\n",
4621 __func__);
4622 return ret;
4623 }
4624
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004625 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4626 wcd9xxx_mbhc_fw_read);
4627 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4628 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4629 wcd9xxx_mbhc_insert_work);
4630 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004631
4632 /* Register event notifier */
4633 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4634 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4635 if (ret) {
4636 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4637 return ret;
4638 }
4639
4640 wcd9xxx_init_debugfs(mbhc);
4641
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004642
4643 /* Disable Impedance detection by default for certain codec types */
4644 if (mbhc->mbhc_cb &&
4645 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4646 impedance_detect_en = 0;
4647 else
4648 impedance_detect_en = impedance_det_en ? 1 : 0;
4649
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004650 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004651 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004652 wcd9xxx_hs_insert_irq,
4653 "Headset insert detect", mbhc);
4654 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004655 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004656 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004657 goto err_insert_irq;
4658 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004659 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004660
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004661 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004662 wcd9xxx_hs_remove_irq,
4663 "Headset remove detect", mbhc);
4664 if (ret) {
4665 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004666 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004667 goto err_remove_irq;
4668 }
4669
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004670 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004671 wcd9xxx_dce_handler, "DC Estimation detect",
4672 mbhc);
4673 if (ret) {
4674 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004675 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004676 goto err_potential_irq;
4677 }
4678
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004679 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004680 wcd9xxx_release_handler,
4681 "Button Release detect", mbhc);
4682 if (ret) {
4683 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004684 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004685 goto err_release_irq;
4686 }
4687
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004688 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004689 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4690 mbhc);
4691 if (ret) {
4692 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004693 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004694 goto err_hphl_ocp_irq;
4695 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004696 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004697
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004698 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004699 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4700 mbhc);
4701 if (ret) {
4702 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004703 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004704 goto err_hphr_ocp_irq;
4705 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004706 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004707
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004708 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4709 1 << WCD9XXX_COND_HPH);
4710
Joonwoo Parka8890262012-10-15 12:04:27 -07004711 pr_debug("%s: leave ret %d\n", __func__, ret);
4712 return ret;
4713
4714err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004715 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004716err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004717 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004718err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004719 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004720err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004721 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004722err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004723 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004724err_insert_irq:
4725 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4726
4727 pr_debug("%s: leave ret %d\n", __func__, ret);
4728 return ret;
4729}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004730EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004731
4732void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4733{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004734 struct wcd9xxx_core_resource *core_res =
4735 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004736
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004737 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4738 1 << WCD9XXX_COND_HPH);
4739
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004740 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4741 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4742 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4743 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4744 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4745 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4746 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004747
Joonwoo Parka8890262012-10-15 12:04:27 -07004748 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004749 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004750}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004751EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004752
4753MODULE_DESCRIPTION("wcd9xxx MBHC module");
4754MODULE_LICENSE("GPL v2");