blob: bb0c4de40f138de0999e053dd1dba6f22a89ab29 [file] [log] [blame]
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070021#include <linux/list.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070022#include <linux/mfd/wcd9xxx/core.h>
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070023#include <linux/mfd/wcd9xxx/core-resource.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070024#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
25#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
26#include <linux/mfd/wcd9xxx/pdata.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <linux/bitops.h>
33#include <linux/delay.h>
34#include <linux/pm_runtime.h>
35#include <linux/kernel.h>
36#include <linux/gpio.h>
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -070037#include <linux/input.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070038#include "wcd9320.h"
Simmi Pateriya0a44d842013-04-03 01:12:42 +053039#include "wcd9306.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070040#include "wcd9xxx-mbhc.h"
41#include "wcd9xxx-resmgr.h"
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070042#include "wcd9xxx-common.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070043
44#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
Joonwoo Park80a01172012-10-15 16:05:23 -070045 SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
46 SND_JACK_UNSUPPORTED)
Joonwoo Parka8890262012-10-15 12:04:27 -070047#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
48 SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
49 SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
50 SND_JACK_BTN_6 | SND_JACK_BTN_7)
51
52#define NUM_DCE_PLUG_DETECT 3
Joonwoo Parkccccba72013-04-26 11:19:46 -070053#define NUM_DCE_PLUG_INS_DETECT 5
Joonwoo Parka8890262012-10-15 12:04:27 -070054#define NUM_ATTEMPTS_INSERT_DETECT 25
55#define NUM_ATTEMPTS_TO_REPORT 5
56
57#define FAKE_INS_LOW 10
58#define FAKE_INS_HIGH 80
59#define FAKE_INS_HIGH_NO_SWCH 150
60#define FAKE_REMOVAL_MIN_PERIOD_MS 50
61#define FAKE_INS_DELTA_SCALED_MV 300
62
63#define BUTTON_MIN 0x8000
64#define STATUS_REL_DETECTION 0x0C
65
66#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
67#define HS_DETECT_PLUG_INERVAL_MS 100
68#define SWCH_REL_DEBOUNCE_TIME_MS 50
69#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070070#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070071
72#define GND_MIC_SWAP_THRESHOLD 2
73#define OCP_ATTEMPT 1
74
75#define FW_READ_ATTEMPTS 15
76#define FW_READ_TIMEOUT 2000000
77
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070078#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070079
80#define MCLK_RATE_12288KHZ 12288000
81#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070082
83#define DEFAULT_DCE_STA_WAIT 55
84#define DEFAULT_DCE_WAIT 60000
85#define DEFAULT_STA_WAIT 5000
86
87#define VDDIO_MICBIAS_MV 1800
88
Joonwoo Parkccccba72013-04-26 11:19:46 -070089#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
90
Joonwoo Park20bc9da2013-01-16 12:58:06 -080091#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070092#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Joonwoo Park20bc9da2013-01-16 12:58:06 -080093#define WCD9XXX_MEAS_DELTA_MAX_MV 50
Joonwoo Park141d6182013-03-05 12:25:46 -080094#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
95#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070096
97/*
98 * Invalid voltage range for the detection
99 * of plug type with current source
100 */
101#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 110
102#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
103
104/*
105 * Threshold used to detect euro headset
106 * with current source
107 */
108#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
109#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
110
111#define WCD9XXX_MBHC_NSC_CS 9
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800112#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
Joonwoo Park479347a2013-04-15 18:01:05 -0700113#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
Joonwoo Parkccccba72013-04-26 11:19:46 -0700114#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800115
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700116#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
117
118/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
119#define WCD9XXX_WG_TIME_FACTOR_US 240
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800120
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700121#define WCD9XXX_V_CS_HS_MAX 500
122#define WCD9XXX_V_CS_NO_MIC 5
123#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
124#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 10
125
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700126static int impedance_detect_en;
127module_param(impedance_detect_en, int,
128 S_IRUGO | S_IWUSR | S_IWGRP);
129MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
130
Joonwoo Parkccccba72013-04-26 11:19:46 -0700131static bool detect_use_vddio_switch = true;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700216 s16 v_brh, v_b1_hu;
Joonwoo Parka8890262012-10-15 12:04:27 -0700217 struct snd_soc_codec *codec = mbhc->codec;
218 int mbhc_state = mbhc->mbhc_state;
219
220 pr_debug("%s: enter\n", __func__);
221 if (!mbhc->polling_active) {
222 pr_debug("Polling is not active, do not start polling\n");
223 return;
224 }
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530225
226 /*
227 * setup internal micbias if codec uses internal micbias for
228 * headset detection
229 */
230 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
231 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
232 mbhc->mbhc_cb->setup_int_rbias(codec, true);
233 else
234 pr_err("%s: internal bias requested but codec did not provide callback\n",
235 __func__);
236 }
237
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530238 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530239 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
240 mbhc->mbhc_cb->enable_mux_bias_block(codec);
241 else
242 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
243 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530244
Joonwoo Parka8890262012-10-15 12:04:27 -0700245 if (!mbhc->no_mic_headset_override &&
246 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530247 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700248 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
249 /* set to max button press threshold */
250 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
252 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
254 /* set to max */
255 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
256 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700257
258 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
260 (v_brh >> 8) & 0xFF);
261 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
262 v_brh & 0xFF);
263 v_b1_hu = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
264 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
265 v_b1_hu & 0xFF);
266 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
267 (v_b1_hu >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700268 }
269
270 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
271 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
272 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
273 pr_debug("%s: leave\n", __func__);
274}
275
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700276static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
277 unsigned int cfilt_mv)
278{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700279 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
280}
281
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700282/*
283 * called under codec_resource_lock acquisition
284 * return old status
285 */
286static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700287 int vddio_switch, bool restartpolling,
288 bool checkpolling)
289{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700290 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700291 int cfilt_k_val;
292 bool override;
293 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700294 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700295
296 codec = mbhc->codec;
297
Joonwoo Parkccccba72013-04-26 11:19:46 -0700298 if (mbhc->micbias_enable) {
299 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700300 ret = mbhc->mbhc_micbias_switched;
301 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700302 }
303
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700304 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700305 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
306 (!checkpolling || mbhc->polling_active)) {
307 if (restartpolling)
308 wcd9xxx_pause_hs_polling(mbhc);
309 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
310 0x04;
311 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700312 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700313 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700314 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700315 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700316 VDDIO_MICBIAS_MV);
317 usleep_range(10000, 10000);
318 snd_soc_update_bits(codec,
319 mbhc->mbhc_bias_regs.cfilt_val,
320 0xFC, (cfilt_k_val << 2));
321 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700322 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700323 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700324 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700325 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700326 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700327 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700328 /* Threshods for button press */
329 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
330 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
331 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
332 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
333 0xFF);
334 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
335 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
336 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
337 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
338 0xFF);
339 /* Threshods for button release */
340 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
341 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
342 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
343 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700344 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
345 __func__);
346 }
347
348 /* Enable MIC BIAS Switch to VDDIO */
349 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
350 0x80, 0x80);
351 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
352 0x10, 0x00);
353 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700354 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700355 if (restartpolling)
356 wcd9xxx_start_hs_polling(mbhc);
357
358 mbhc->mbhc_micbias_switched = true;
359 pr_debug("%s: VDDIO switch enabled\n", __func__);
360 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
361 if ((!checkpolling || mbhc->polling_active) &&
362 restartpolling)
363 wcd9xxx_pause_hs_polling(mbhc);
364 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700365 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700366 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700367 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700368 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700369 snd_soc_update_bits(codec,
370 mbhc->mbhc_bias_regs.cfilt_val,
371 0xFC, (cfilt_k_val << 2));
372 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700373 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700375 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700376 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700377 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
378 0xFF);
379 /* Revert threshods for button press */
380 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
381 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
382 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
383 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
384 0xFF);
385 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
386 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
387 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
388 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
389 0xFF);
390 /* Revert threshods for button release */
391 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
392 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
393 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
394 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700395 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
396 __func__);
397 }
398
399 /* Disable MIC BIAS Switch to VDDIO */
400 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
401 0x00);
402 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
403 0x00);
404
405 if ((!checkpolling || mbhc->polling_active) && restartpolling)
406 wcd9xxx_start_hs_polling(mbhc);
407
408 mbhc->mbhc_micbias_switched = false;
409 pr_debug("%s: VDDIO switch disabled\n", __func__);
410 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700411
412 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700413}
414
415static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
416{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700417 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700418}
419
Joonwoo Park73375212013-05-07 12:42:44 -0700420static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
421 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700422{
Joonwoo Park73375212013-05-07 12:42:44 -0700423 enum mbhc_v_index vidx;
424 s16 ret = -EINVAL;
425
Joonwoo Parka8890262012-10-15 12:04:27 -0700426 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
427 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700428 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700429 else
Joonwoo Park73375212013-05-07 12:42:44 -0700430 vidx = MBHC_V_IDX_CFILT;
431
432 switch (idx) {
433 case WCD9XXX_CURRENT_V_INS_H:
434 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
435 break;
436 case WCD9XXX_CURRENT_V_INS_HU:
437 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
438 break;
439 case WCD9XXX_CURRENT_V_B1_H:
440 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
441 break;
442 case WCD9XXX_CURRENT_V_B1_HU:
443 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
444 break;
445 case WCD9XXX_CURRENT_V_BR_H:
446 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
447 break;
448 }
449
450 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700451}
452
453void *wcd9xxx_mbhc_cal_btn_det_mp(
454 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
455 const enum wcd9xxx_mbhc_btn_det_mem mem)
456{
457 void *ret = &btn_det->_v_btn_low;
458
459 switch (mem) {
460 case MBHC_BTN_DET_GAIN:
461 ret += sizeof(btn_det->_n_cic);
462 case MBHC_BTN_DET_N_CIC:
463 ret += sizeof(btn_det->_n_ready);
464 case MBHC_BTN_DET_N_READY:
465 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
466 case MBHC_BTN_DET_V_BTN_HIGH:
467 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
468 case MBHC_BTN_DET_V_BTN_LOW:
469 /* do nothing */
470 break;
471 default:
472 ret = NULL;
473 }
474
475 return ret;
476}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700477EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700478
479static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
480{
481 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700482 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
483 WCD9XXX_CURRENT_V_INS_HU);
484 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
485 WCD9XXX_CURRENT_V_B1_HU);
486 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
487 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700488
489 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
490 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
491 (v_ins_hu >> 8) & 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700492 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700493 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700494 (v_b1_hu >> 8) & 0xFF);
495 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700496 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700497 (v_b1_h >> 8) & 0xFF);
498 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700499 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700500 (v_brh >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700501 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
502 mbhc->mbhc_data.v_brl & 0xFF);
503 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
504 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
505}
506
Simmi Pateriya95466b12013-05-09 20:08:46 +0530507static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700508 bool fast)
509{
510 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530511 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700512
Simmi Pateriya95466b12013-05-09 20:08:46 +0530513 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
514 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530515 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530516 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700517 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530518 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700519 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700520
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700521 cfilt_mode.reg_mask = 0x40;
522 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530523 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700524 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700525
526 if (cfilt_mode.cur_mode_val
527 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530528 if (mbhc->polling_active)
529 wcd9xxx_pause_hs_polling(mbhc);
530 snd_soc_update_bits(codec,
531 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700532 cfilt_mode.reg_mask,
533 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530534 if (mbhc->polling_active)
535 wcd9xxx_start_hs_polling(mbhc);
536 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700537 cfilt_mode.cur_mode_val,
538 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530539 } else {
540 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700541 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530542 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700543}
544
Joonwoo Park3699ca32013-02-08 12:06:15 -0800545static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
546 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700547{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800548 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800549 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
550 WCD9XXX_COND_HPH_MIC,
551 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800552 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
553 WCD9XXX_COND_HPH,
554 status & SND_JACK_HEADPHONE);
555 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800556
Joonwoo Parka8890262012-10-15 12:04:27 -0700557 snd_soc_jack_report_no_dapm(jack, status, mask);
558}
559
560static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
561 int irq)
562{
563 struct snd_soc_codec *codec;
564
565 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
566 codec = mbhc->codec;
567 if (mbhc->hph_status & jack_status) {
568 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800569 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700570 mbhc->hph_status, WCD9XXX_JACK_MASK);
571 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
572 0x00);
573 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
574 0x10);
575 /*
576 * reset retry counter as PA is turned off signifying
577 * start of new OCP detection session
578 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700579 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700580 mbhc->hphlocp_cnt = 0;
581 else
582 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700583 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700584 }
585}
586
587static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
588{
589 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700590 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700591}
592
593static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
594{
595 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700596 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700597}
598
599static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
600 struct mbhc_micbias_regs *micbias_regs)
601{
602 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700603 struct wcd9xxx_micbias_setting *micbias_pdata =
604 mbhc->resmgr->micbias_pdata;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700605
Joonwoo Parka8890262012-10-15 12:04:27 -0700606 switch (mbhc->mbhc_cfg->micbias) {
607 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700608 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700609 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
610 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
611 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
612 break;
613 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700614 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700615 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
616 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
617 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
618 break;
619 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700620 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700621 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
622 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
623 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
624 break;
625 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700626 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700627 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
628 micbias_regs->int_rbias =
629 mbhc->resmgr->reg_addr->micb_4_int_rbias;
630 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
631 break;
632 default:
633 /* Should never reach here */
634 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
635 return;
636 }
637
638 micbias_regs->cfilt_sel = cfilt;
639
640 switch (cfilt) {
641 case WCD9XXX_CFILT1_SEL:
642 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
643 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700644 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700645 break;
646 case WCD9XXX_CFILT2_SEL:
647 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
648 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700649 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700650 break;
651 case WCD9XXX_CFILT3_SEL:
652 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
653 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700654 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700655 break;
656 }
657}
658
659static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
660{
661 bool pa_turned_on = false;
662 struct snd_soc_codec *codec = mbhc->codec;
663 u8 wg_time;
664
665 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
666 wg_time += 1;
667
668 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
669 &mbhc->hph_pa_dac_state)) {
670 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
671 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
672 0xC0, 0xC0);
673 }
674 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
675 &mbhc->hph_pa_dac_state)) {
676 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
677 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700678 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700679 }
680
681 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
682 &mbhc->hph_pa_dac_state)) {
683 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
684 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
685 1 << 4);
686 pa_turned_on = true;
687 }
688 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
689 &mbhc->hph_pa_dac_state)) {
690 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
691 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
692 << 5);
693 pa_turned_on = true;
694 }
695
696 if (pa_turned_on) {
697 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
698 __func__);
699 usleep_range(wg_time * 1000, wg_time * 1000);
700 }
701}
702
703static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
704{
705 int r;
706 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
707 if (r)
708 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
709 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700710 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700711 return r;
712}
713
714static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
715{
716 u8 hph_reg_val = 0;
717 if (left)
718 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
719 else
720 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
721
722 return (hph_reg_val & 0xC0) ? true : false;
723}
724
725static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
726{
727 u8 hph_reg_val = 0;
728 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
729
730 return (hph_reg_val & 0x30) ? true : false;
731}
732
733/* called under codec_resource_lock acquisition */
734static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
735{
736 u8 wg_time;
737 struct snd_soc_codec *codec = mbhc->codec;
738
739 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
740 wg_time += 1;
741
742 /* If headphone PA is on, check if userspace receives
743 * removal event to sync-up PA's state */
744 if (wcd9xxx_is_hph_pa_on(codec)) {
745 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
746 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
747 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
748 } else {
749 pr_debug("%s PA is off\n", __func__);
750 }
751
752 if (wcd9xxx_is_hph_dac_on(codec, 1))
753 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
754 if (wcd9xxx_is_hph_dac_on(codec, 0))
755 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
756
757 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800758 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700759 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
760 usleep_range(wg_time * 1000, wg_time * 1000);
761}
762
763static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
764{
765 if (!mbhc->mbhc_cfg->insert_detect)
766 return;
767 pr_debug("%s: Setting up %s detection\n", __func__,
768 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700769 /* Disable detection to avoid glitch */
770 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530771 if (mbhc->mbhc_cfg->gpio_level_insert)
772 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
773 (0x68 | (ins ? (1 << 1) : 0)));
774 else
775 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
776 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700777 /* Re-enable detection */
778 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700779}
780
781/* called under codec_resource_lock acquisition */
782static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
783 enum snd_jack_types jack_type)
784{
785 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
786
Joonwoo Park80a01172012-10-15 16:05:23 -0700787 pr_debug("%s: enter insertion %d hph_status %x\n",
788 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700789 if (!insertion) {
790 /* Report removal */
791 mbhc->hph_status &= ~jack_type;
792 /*
793 * cancel possibly scheduled btn work and
794 * report release if we reported button press
795 */
796 if (wcd9xxx_cancel_btn_work(mbhc))
797 pr_debug("%s: button press is canceled\n", __func__);
798 else if (mbhc->buttons_pressed) {
799 pr_debug("%s: release of button press%d\n",
800 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800801 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700802 mbhc->buttons_pressed);
803 mbhc->buttons_pressed &=
804 ~WCD9XXX_JACK_BUTTON_MASK;
805 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700806
807 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
808 pr_debug("%s: Disabling micbias\n", __func__);
809 mbhc->micbias_enable_cb(mbhc->codec, false);
810 mbhc->micbias_enable = false;
811 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700812 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700813 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
814 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800815 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700816 WCD9XXX_JACK_MASK);
817 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
818 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
819 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
820 mbhc->current_plug = PLUG_TYPE_NONE;
821 mbhc->polling_active = false;
822 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700823 /*
824 * Report removal of current jack type.
825 * Headphone to headset shouldn't report headphone
826 * removal.
827 */
828 if (mbhc->mbhc_cfg->detect_extn_cable &&
829 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
830 jack_type == SND_JACK_HEADSET) &&
831 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
832 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
833 mbhc->hph_status == SND_JACK_HEADSET) {
834 pr_debug("%s: Disabling micbias\n", __func__);
835 mbhc->micbias_enable_cb(mbhc->codec, false);
836 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700837 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700838
839 pr_debug("%s: Reporting removal (%x)\n",
840 __func__, mbhc->hph_status);
841 mbhc->zl = mbhc->zr = 0;
842 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
843 0, WCD9XXX_JACK_MASK);
844 mbhc->hph_status = 0;
Joonwoo Park80a01172012-10-15 16:05:23 -0700845 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700846 /* Report insertion */
847 mbhc->hph_status |= jack_type;
848
849 if (jack_type == SND_JACK_HEADPHONE) {
850 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
851 } else if (jack_type == SND_JACK_UNSUPPORTED) {
852 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
853 } else if (jack_type == SND_JACK_HEADSET) {
854 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
855 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700856 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700857 } else if (jack_type == SND_JACK_LINEOUT) {
858 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700859 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700860
861 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
862 pr_debug("%s: Enabling micbias\n", __func__);
863 mbhc->micbias_enable_cb(mbhc->codec, true);
864 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700865
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700866 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700867 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700868
Joonwoo Parka8890262012-10-15 12:04:27 -0700869 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
870 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800871 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700872 mbhc->hph_status, WCD9XXX_JACK_MASK);
873 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
874 }
875 /* Setup insert detect */
876 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700877
878 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700879}
880
881/* should be called under interrupt context that hold suspend */
882static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
883 struct work_struct *work)
884{
885 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
886 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
887 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700888 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700889 schedule_work(work);
890}
891
892/* called under codec_resource_lock acquisition */
893static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
894 struct work_struct *work)
895{
896 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
897 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
898 mbhc->hs_detect_work_stop = true;
899 wmb();
900 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
901 if (cancel_work_sync(work)) {
902 pr_debug("%s: correct_plug_swch is canceled\n",
903 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700904 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700905 }
906 WCD9XXX_BCL_LOCK(mbhc->resmgr);
907}
908
Joonwoo Park73375212013-05-07 12:42:44 -0700909static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
910{
911 int r;
912 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700913 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
914 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700915 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700916 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700917 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700918 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700919 return r;
920}
921
Joonwoo Parka8890262012-10-15 12:04:27 -0700922static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
923{
924 s16 v_hs_max;
925 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
926
927 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
928 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
929 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700930 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700931 else
932 v_hs_max = plug_type->v_hs_max;
933 return v_hs_max;
934}
935
Joonwoo Parka8890262012-10-15 12:04:27 -0700936static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
937{
938 u8 bias_msb, bias_lsb;
939 short bias_value;
940
941 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
942 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
943 bias_value = (bias_msb << 8) | bias_lsb;
944 return bias_value;
945}
946
947static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
948{
949 u8 bias_msb, bias_lsb;
950 short bias_value;
951
952 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
953 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
954 bias_value = (bias_msb << 8) | bias_lsb;
955 return bias_value;
956}
957
958static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
959 bool on)
960{
961 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
962}
963
964static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
965 bool override_bypass, bool noreldetection)
966{
967 short bias_value;
968 struct snd_soc_codec *codec = mbhc->codec;
969
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700970 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
971 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700972 if (noreldetection)
973 wcd9xxx_turn_onoff_rel_detection(codec, false);
974
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700975 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700976 /* Turn on the override */
977 if (!override_bypass)
978 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
979 if (dce) {
980 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
981 0x8);
982 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
983 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
984 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700985 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
986 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700987 usleep_range(mbhc->mbhc_data.t_sta_dce,
988 mbhc->mbhc_data.t_sta_dce);
989 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
990 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
991 bias_value = wcd9xxx_read_dce_result(codec);
992 } else {
993 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
994 0x8);
995 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
996 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
997 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700998 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
999 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001000 usleep_range(mbhc->mbhc_data.t_sta_dce,
1001 mbhc->mbhc_data.t_sta_dce);
1002 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1003 usleep_range(mbhc->mbhc_data.t_sta,
1004 mbhc->mbhc_data.t_sta);
1005 bias_value = wcd9xxx_read_sta_result(codec);
1006 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1007 0x8);
1008 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1009 }
1010 /* Turn off the override after measuring mic voltage */
1011 if (!override_bypass)
1012 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1013 0x00);
1014
1015 if (noreldetection)
1016 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001017 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1018 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001019
1020 return bias_value;
1021}
1022
1023static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1024 bool norel)
1025{
1026 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1027}
1028
Joonwoo Park520a0f92013-05-14 19:39:58 -07001029static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001030 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001031{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001032 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001033 s32 mv;
1034
1035 value = bias_value;
1036 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001037 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001038 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001039 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001040 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001041 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001042 }
1043
1044 return mv;
1045}
1046
Joonwoo Park520a0f92013-05-14 19:39:58 -07001047static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1048 u16 bias_value)
1049{
1050 s16 z;
1051 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001052 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1053 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001054}
1055
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301056/* To enable/disable bandgap and RC oscillator */
1057static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1058 bool enable)
1059{
1060 if (enable) {
1061 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1062 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1063 WCD9XXX_BANDGAP_AUDIO_MODE);
1064 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1065 WCD9XXX_CLK_RCO);
1066 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1067 } else {
1068 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1069 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1070 WCD9XXX_CLK_RCO);
1071 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1072 WCD9XXX_BANDGAP_AUDIO_MODE);
1073 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1074 }
1075}
1076
Joonwoo Parka8890262012-10-15 12:04:27 -07001077/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001078static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1079 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001080{
1081 struct snd_soc_codec *codec = mbhc->codec;
1082 short bias_value;
1083 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001084 s16 reg;
1085 int change;
1086 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1087 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001088
1089 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1090
1091 pr_debug("%s: enter\n", __func__);
1092 if (!mbhc->mbhc_cfg->calibration) {
1093 pr_err("%s: Error, no calibration exists\n", __func__);
1094 return -ENODEV;
1095 }
1096
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001097 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001098 /* Enable external voltage source to micbias if present */
1099 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1100 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001101
Joonwoo Parka8890262012-10-15 12:04:27 -07001102 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301103 * setup internal micbias if codec uses internal micbias for
1104 * headset detection
1105 */
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301106 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301107 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1108 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1109 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301110 pr_err("%s: internal bias requested but codec did not provide callback\n",
1111 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301112 }
1113
Joonwoo Parka8890262012-10-15 12:04:27 -07001114 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1115
1116 /* Make sure CFILT is in fast mode, save current mode */
1117 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301118 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1119 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1120 else
1121 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1122 0x70, 0x00);
1123
Joonwoo Parka8890262012-10-15 12:04:27 -07001124 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301125 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301126 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1127 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1128 else
1129 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1130 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001131
1132 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1133 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1134 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1135
1136 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1137 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1138 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1139
1140 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1141 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1142
Joonwoo Parka8890262012-10-15 12:04:27 -07001143 /* don't flip override */
1144 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301145 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001146 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1147
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001148 /* recalibrate dce_z and sta_z */
1149 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1150 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1151 btn_det->mbhc_nsc << 3);
1152 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1153 if (change)
1154 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1155 if (dce_z && sta_z) {
1156 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1157 __func__,
1158 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1159 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1160 mbhc->mbhc_data.dce_z = dce_z;
1161 mbhc->mbhc_data.sta_z = sta_z;
1162 wcd9xxx_mbhc_calc_thres(mbhc);
1163 wcd9xxx_calibrate_hs_polling(mbhc);
1164 } else {
1165 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", __func__,
1166 dce_z, sta_z);
1167 }
1168
1169 if (is_cs_enable) {
1170 /* recalibrate dce_nsc_cs_z */
1171 reg = snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1172 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1173 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1174 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1175 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1176 if (dce_z) {
1177 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n", __func__,
1178 mbhc->mbhc_data.dce_nsc_cs_z, dce_z & 0xffff);
1179 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1180 } else {
1181 pr_debug("%s: failed get new dce_nsc_cs_z\n", __func__);
1182 }
1183 }
1184
Joonwoo Parka8890262012-10-15 12:04:27 -07001185 return bias_value;
1186}
1187
1188static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1189{
1190 struct snd_soc_codec *codec = mbhc->codec;
1191 const struct wcd9xxx_mbhc_general_cfg *generic =
1192 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1193
1194 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001195 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001196 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001197 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001198
1199 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1200 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001201 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001202
1203 usleep_range(generic->t_shutdown_plug_rem,
1204 generic->t_shutdown_plug_rem);
1205
1206 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1207
Joonwoo Park533b3682013-06-13 11:41:21 -07001208 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001209 /* Put requested CLK back */
1210 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001211 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001212
1213 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1214}
1215
1216static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1217{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001218
1219 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001220 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1221
1222 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1223
Joonwoo Parka8890262012-10-15 12:04:27 -07001224
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001225 /* Disable external voltage source to micbias if present */
1226 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1227 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1228
Joonwoo Parka8890262012-10-15 12:04:27 -07001229 mbhc->polling_active = false;
1230 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001231 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001232}
1233
Joonwoo Parka8890262012-10-15 12:04:27 -07001234/* called under codec_resource_lock acquisition */
1235static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1236{
1237 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1238 if (on)
1239 usleep_range(5000, 5000);
1240}
1241
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001242static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1243{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001244 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001245
1246 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1247 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1248 goto exit;
1249 }
1250
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001251 if (on) {
1252 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1253 1 << 7, 1 << 7);
1254 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1255 1 << 4, 0);
1256 } else {
1257 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1258 1 << 4, 1 << 4);
1259 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1260 1 << 7, 0);
1261 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001262
1263exit:
1264 /*
1265 * Wait for the micbias to settle down to vddio
1266 * when the micbias to vddio switch is enabled.
1267 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001268 if (on)
1269 usleep_range(10000, 10000);
1270}
1271
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001272static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1273{
1274 u16 hph, status;
1275 struct snd_soc_codec *codec = mbhc->codec;
1276
1277 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1278 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1279 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1280 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1281 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1282 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1283 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1284 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1285 return status;
1286}
1287
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001288static enum wcd9xxx_mbhc_plug_type
1289wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1290 struct wcd9xxx_mbhc_detect *dt, const int size,
1291 bool highhph,
1292 unsigned long event_state)
1293{
1294 int i;
1295 int vdce, mb_mv;
1296 int ch, sz, delta_thr;
1297 int minv = 0, maxv = INT_MIN;
1298 struct wcd9xxx_mbhc_detect *d = dt;
1299 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1300 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1301
1302 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1303 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1304 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001305 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001306
1307 pr_debug("%s: enter\n", __func__);
1308 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1309
1310 sz = size - 1;
1311 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1312 if (d->mic_bias) {
1313 dce_z = mbhc->mbhc_data.dce_z;
1314 mb_mv = mbhc->mbhc_data.micb_mv;
1315 hs_max = plug_type->v_hs_max;
1316 no_mic = plug_type->v_no_mic;
1317 } else {
1318 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1319 mb_mv = VDDIO_MICBIAS_MV;
1320 hs_max = WCD9XXX_V_CS_HS_MAX;
1321 no_mic = WCD9XXX_V_CS_NO_MIC;
1322 }
1323
1324 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1325 dce_z, (u32)mb_mv);
1326
1327 d->_vdces = vdce;
1328 if (d->_vdces < no_mic)
1329 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001330 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001331 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001332 highhph_cnt++;
1333 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001334 d->_type = PLUG_TYPE_HEADSET;
1335
1336 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1337 __func__, i, d->dce, vdce, d->_vdces,
1338 d->hphl_status & 0x01,
1339 d->_type);
1340
1341 ch += d->hphl_status & 0x01;
1342 if (!d->swap_gnd && !d->mic_bias) {
1343 if (maxv < d->_vdces)
1344 maxv = d->_vdces;
1345 if (!minv || minv > d->_vdces)
1346 minv = d->_vdces;
1347 }
1348 if ((!d->mic_bias &&
1349 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1350 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1351 (d->mic_bias &&
1352 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1353 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1354 pr_debug("%s: within invalid range\n", __func__);
1355 type = PLUG_TYPE_INVALID;
1356 goto exit;
1357 }
1358 }
1359
1360 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1361 pr_debug("%s: HPHL PA was ON\n", __func__);
1362 } else if (ch != sz && ch > 0) {
1363 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1364 type = PLUG_TYPE_INVALID;
1365 goto exit;
1366 }
1367
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001368 delta_thr = ((highhph_cnt == sz) || highhph) ?
1369 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001370 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1371
1372 for (i = 0, d = dt; i < sz; i++, d++) {
1373 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1374 (d->_type != dprev->_type)) {
1375 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1376 type = PLUG_TYPE_INVALID;
1377 goto exit;
1378 }
1379
1380 if (!d->swap_gnd && !d->mic_bias &&
1381 (abs(minv - d->_vdces) > delta_thr ||
1382 abs(maxv - d->_vdces) > delta_thr)) {
1383 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1384 __func__, d->_vdces, minv, maxv);
1385 type = PLUG_TYPE_INVALID;
1386 goto exit;
1387 } else if (d->swap_gnd) {
1388 dgnd = d;
1389 }
1390
1391 if (!d->mic_bias && !d->swap_gnd)
1392 dprev = d;
1393 else if (d->mic_bias)
1394 dmicbias = d;
1395 }
1396 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1397 dt->_type != dgnd->_type) {
1398 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1399 type = PLUG_TYPE_INVALID;
1400 goto exit;
1401 }
1402
1403 type = dt->_type;
1404 if (dmicbias) {
1405 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1406 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1407 dt->_type == PLUG_TYPE_HEADSET)) {
1408 type = PLUG_TYPE_HEADSET;
1409 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1410 pr_debug("%s: Headset with threshold on MIC detected\n",
1411 __func__);
1412 if (mbhc->mbhc_cfg->micbias_enable_flags &
1413 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1414 mbhc->micbias_enable = true;
1415 }
1416 }
1417 }
1418
1419 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1420 if (((type == PLUG_TYPE_HEADSET ||
1421 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1422 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1423 __func__, type);
1424 type = PLUG_TYPE_INVALID;
1425 }
1426 }
1427 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1428 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1429 minv) &&
1430 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1431 maxv))
1432 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001433 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001434 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1435 type = PLUG_TYPE_INVALID;
1436 }
1437 }
1438exit:
1439 pr_debug("%s: Plug type %d detected\n", __func__, type);
1440 return type;
1441}
1442
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001443/*
1444 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1445 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001446 * param mbhc wcd9xxx_mbhc structure
1447 * param dt collected measurements
1448 * param size array size of dt
1449 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001450 */
1451static enum wcd9xxx_mbhc_plug_type
1452wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001453 struct wcd9xxx_mbhc_detect *dt, const int size,
1454 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001455{
1456 int i;
1457 int ch;
1458 enum wcd9xxx_mbhc_plug_type type;
1459 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001460 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001461 int maxv = 0, minv = 0;
1462 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1463 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1464 const s16 hs_max = plug_type->v_hs_max;
1465 const s16 no_mic = plug_type->v_no_mic;
1466
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001467 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1468
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001469 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1470 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1471 if (d->vddio)
1472 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1473 else
1474 d->_vdces = vdce;
1475
1476 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1477 d->_type = PLUG_TYPE_HEADSET;
1478 else if (d->_vdces < no_mic)
1479 d->_type = PLUG_TYPE_HEADPHONE;
1480 else
1481 d->_type = PLUG_TYPE_HIGH_HPH;
1482
1483 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001484 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001485 if (maxv < d->_vdces)
1486 maxv = d->_vdces;
1487 if (!minv || minv > d->_vdces)
1488 minv = d->_vdces;
1489 }
1490
1491 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1492 __func__, i, d->dce, vdce, d->_vdces,
1493 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1494 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001495
1496
1497 /*
1498 * If GND and MIC prongs are aligned to HPHR and GND of
1499 * headphone, codec measures the voltage based on
1500 * impedance between HPHR and GND which results in ~80mv.
1501 * Avoid this.
1502 */
1503 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1504 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1505 pr_debug("%s: within invalid range\n", __func__);
1506 type = PLUG_TYPE_INVALID;
1507 goto exit;
1508 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001509 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001510
1511 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1512 pr_debug("%s: HPHL PA was ON\n", __func__);
1513 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001514 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1515 type = PLUG_TYPE_INVALID;
1516 goto exit;
1517 }
1518
Joonwoo Parka84ec452013-07-17 15:02:52 -07001519 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001520 if (d->vddio) {
1521 dvddio = d;
1522 continue;
1523 }
1524
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001525 if ((i > 0) && (d->_type != dprev->_type)) {
1526 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1527 type = PLUG_TYPE_INVALID;
1528 goto exit;
1529 }
1530
1531 if (!d->swap_gnd && !d->hwvalue &&
1532 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1533 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1534 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1535 __func__, d->_vdces, minv, maxv);
1536 type = PLUG_TYPE_INVALID;
1537 goto exit;
1538 } else if (d->swap_gnd) {
1539 dgnd = d;
1540 }
1541 dprev = d;
1542 }
1543
1544 WARN_ON(i != size);
1545 type = dt->_type;
1546 if (type == PLUG_TYPE_HEADSET && dgnd) {
1547 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1548 minv) &&
1549 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1550 maxv))
1551 type = PLUG_TYPE_GND_MIC_SWAP;
1552 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001553
1554 /* if HPHL PA was on, we cannot use hphl status */
1555 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1556 if (((type == PLUG_TYPE_HEADSET ||
1557 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1558 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1559 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1560 __func__, type);
1561 type = PLUG_TYPE_INVALID;
1562 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001563 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001564
Joonwoo Parkccccba72013-04-26 11:19:46 -07001565 if (type == PLUG_TYPE_HEADSET && dvddio) {
1566 if ((dvddio->_vdces > hs_max) ||
1567 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1568 pr_debug("%s: Headset with threshold on MIC detected\n",
1569 __func__);
1570 if (mbhc->mbhc_cfg->micbias_enable_flags &
1571 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1572 mbhc->micbias_enable = true;
1573 } else {
1574 pr_debug("%s: Headset with regular MIC detected\n",
1575 __func__);
1576 if (mbhc->mbhc_cfg->micbias_enable_flags &
1577 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1578 mbhc->micbias_enable = true;
1579 }
1580 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001581exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001582 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1583 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001584 return type;
1585}
1586
Joonwoo Parkccccba72013-04-26 11:19:46 -07001587/*
1588 * Pull down MBHC micbias for provided duration in microsecond.
1589 */
1590static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1591{
1592 bool micbiasconn = false;
1593 struct snd_soc_codec *codec = mbhc->codec;
1594 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1595
1596 /*
1597 * Disable MBHC to micbias connection to pull down
1598 * micbias and pull down micbias for a moment.
1599 */
1600 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1601 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1602 return -EFAULT;
1603 }
1604
1605 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1606 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1607 1 << 4, 0);
1608 micbiasconn = true;
1609 }
1610
1611 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1612
1613 /*
1614 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1615 * able to get consistent result across DCEs.
1616 */
1617 usleep_range(1000, 1000 + 10);
1618
1619 if (micbiasconn)
1620 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1621 1 << 4, 1 << 4);
1622 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1623 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1624
1625 return 0;
1626}
1627
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001628void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1629 bool highhph)
1630{
1631
1632 struct snd_soc_codec *codec;
1633 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1634 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1635 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1636
1637 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1638 codec = mbhc->codec;
1639
1640 if (on) {
1641 pr_debug("%s: enabling current source\n", __func__);
1642 /* Nsc to 9 */
1643 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1644 0x78, 0x48);
1645 /* pull down diode bit to 0 */
1646 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1647 0x01, 0x00);
1648 /*
1649 * Keep the low power insertion/removal
1650 * detection (reg 0x3DD) disabled
1651 */
1652 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1653 0x01, 0x00);
1654 /*
1655 * Enable the Mic Bias current source
1656 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1657 * Write bit[7] of register MICB_2_MBHC to 1
1658 * (INS_DET_ISRC_EN__ENABLE)
1659 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1660 */
1661 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1662 0xF0, 0xF0);
1663 /* Disconnect MBHC Override from MicBias and LDOH */
1664 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1665 } else {
1666 pr_debug("%s: disabling current source\n", __func__);
1667 /* Connect MBHC Override from MicBias and LDOH */
1668 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1669 /* INS_DET_ISRC_CTL to acdb value */
1670 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1671 0x60, plug_det->mic_current << 5);
1672 if (!highhph) {
1673 /* INS_DET_ISRC_EN__ENABLE to 0 */
1674 snd_soc_update_bits(codec,
1675 mbhc->mbhc_bias_regs.mbhc_reg,
1676 0x80, 0x00);
1677 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1678 snd_soc_update_bits(codec,
1679 mbhc->mbhc_bias_regs.mbhc_reg,
1680 0x10, 0x00);
1681 }
1682 /* Nsc to acdb value */
1683 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1684 btn_det->mbhc_nsc << 3);
1685 }
1686}
1687
1688static enum wcd9xxx_mbhc_plug_type
1689wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1690{
1691 struct snd_soc_codec *codec = mbhc->codec;
1692 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1693 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1694 int i;
1695
1696 pr_debug("%s: enter\n", __func__);
1697 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1698
1699 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1700
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301701 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001702 rt[0].swap_gnd = false;
1703 rt[0].vddio = false;
1704 rt[0].hwvalue = true;
1705 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1706 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1707 rt[0].mic_bias = false;
1708
1709 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1710 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1711 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1712 highhph);
1713 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1714 if (rt[i].swap_gnd)
1715 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1716
1717 if (rt[i].mic_bias)
1718 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1719
1720 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1721 if (rt[i].mic_bias)
1722 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1723 if (rt[i].swap_gnd)
1724 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1725 }
1726
1727 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1728 mbhc->event_state);
1729
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301730 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001731 pr_debug("%s: plug_type:%d\n", __func__, type);
1732
1733 return type;
1734}
1735
Joonwoo Parka8890262012-10-15 12:04:27 -07001736static enum wcd9xxx_mbhc_plug_type
1737wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1738{
1739 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001740 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001741 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001742 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1743 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001744 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001745
Joonwoo Park80a01172012-10-15 16:05:23 -07001746 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001747 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1748
1749 /* make sure override is on */
1750 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1751
1752 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001753 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001754
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001755 /*
1756 * There are chances vddio switch is on and cfilt voltage is adjusted
1757 * to vddio voltage even after plug type removal reported.
1758 */
1759 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1760 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1761
Joonwoo Parka8890262012-10-15 12:04:27 -07001762 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001763 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001764
Joonwoo Parkccccba72013-04-26 11:19:46 -07001765 /*
1766 * cfilter in fast mode requires 1ms to charge up and down micbias
1767 * fully.
1768 */
1769 (void) wcd9xxx_pull_down_micbias(mbhc,
1770 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301771
1772 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001773 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001774 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001775 rt[0].swap_gnd = false;
1776 rt[0].vddio = false;
1777 rt[0].hwvalue = true;
1778 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1779 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1780 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001781 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001782 else
1783 rt[i].vddio = false;
1784 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1785 rt[i].hwvalue = false;
1786 if (rt[i].swap_gnd)
1787 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1788 if (rt[i].vddio)
1789 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001790 /*
1791 * Pull down micbias to detect headset with mic which has
1792 * threshold and to have more consistent voltage measurements.
1793 *
1794 * cfilter in fast mode requires 1ms to charge up and down
1795 * micbias fully.
1796 */
1797 (void) wcd9xxx_pull_down_micbias(mbhc,
1798 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001799 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1800 if (rt[i].vddio)
1801 wcd9xxx_onoff_vddio_switch(mbhc, false);
1802 if (rt[i].swap_gnd)
1803 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001804 }
1805
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001806 if (vddioon)
1807 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1808
1809 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1810 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001811
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301812 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001813 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001814 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001815}
1816
1817static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1818{
1819 if (mbhc->mbhc_cfg->gpio)
1820 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1821 mbhc->mbhc_cfg->gpio_level_insert);
1822 else if (mbhc->mbhc_cfg->insert_detect)
1823 return snd_soc_read(mbhc->codec,
1824 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1825 (1 << 2);
1826 else
1827 WARN(1, "Invalid jack detection configuration\n");
1828
1829 return true;
1830}
1831
1832static bool is_clk_active(struct snd_soc_codec *codec)
1833{
1834 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1835}
1836
1837static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1838 int insertion, int trigger, bool padac_off)
1839{
1840 struct snd_soc_codec *codec = mbhc->codec;
1841 int central_bias_enabled = 0;
1842 const struct wcd9xxx_mbhc_general_cfg *generic =
1843 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1844 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1845 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1846
Joonwoo Park80a01172012-10-15 16:05:23 -07001847 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1848 __func__, insertion, trigger);
1849
Joonwoo Parka8890262012-10-15 12:04:27 -07001850 if (!mbhc->mbhc_cfg->calibration) {
1851 pr_err("Error, no wcd9xxx calibration\n");
1852 return -EINVAL;
1853 }
1854
1855 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1856
1857 /*
1858 * Make sure mic bias and Mic line schmitt trigger
1859 * are turned OFF
1860 */
1861 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1862 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1863
1864 if (insertion) {
1865 wcd9xxx_switch_micbias(mbhc, 0);
1866
1867 /* DAPM can manipulate PA/DAC bits concurrently */
1868 if (padac_off == true)
1869 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1870
1871 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1872 /* Enable HPH Schmitt Trigger */
1873 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1874 0x11);
1875 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1876 plug_det->hph_current << 2);
1877 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1878 0x02);
1879 }
1880 if (trigger & MBHC_USE_MB_TRIGGER) {
1881 /* enable the mic line schmitt trigger */
1882 snd_soc_update_bits(codec,
1883 mbhc->mbhc_bias_regs.mbhc_reg,
1884 0x60, plug_det->mic_current << 5);
1885 snd_soc_update_bits(codec,
1886 mbhc->mbhc_bias_regs.mbhc_reg,
1887 0x80, 0x80);
1888 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1889 snd_soc_update_bits(codec,
1890 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1891 0x00);
1892 snd_soc_update_bits(codec,
1893 mbhc->mbhc_bias_regs.mbhc_reg,
1894 0x10, 0x10);
1895 }
1896
1897 /* setup for insetion detection */
1898 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1899 } else {
1900 pr_debug("setup for removal detection\n");
1901 /* Make sure the HPH schmitt trigger is OFF */
1902 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1903
1904 /* enable the mic line schmitt trigger */
1905 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1906 0x01, 0x00);
1907 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1908 plug_det->mic_current << 5);
1909 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1910 0x80, 0x80);
1911 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1912 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1913 0x10, 0x10);
1914
1915 /* Setup for low power removal detection */
1916 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1917 0x2);
1918 }
1919
1920 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1921 /* called by interrupt */
1922 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001923 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001924 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1925 0x06, 0);
1926 usleep_range(generic->t_shutdown_plug_rem,
1927 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001928 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001929 } else
1930 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1931 0x06, 0);
1932 }
1933
1934 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1935
1936 /* If central bandgap disabled */
1937 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1938 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1939 usleep_range(generic->t_bg_fast_settle,
1940 generic->t_bg_fast_settle);
1941 central_bias_enabled = 1;
1942 }
1943
1944 /* If LDO_H disabled */
1945 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1946 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1947 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1948 usleep_range(generic->t_ldoh, generic->t_ldoh);
1949 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1950
1951 if (central_bias_enabled)
1952 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1953 0);
1954 }
1955
Meng Wangeeaaaba2013-09-09 18:37:32 +08001956 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301957 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1958 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001959
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001960 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001961 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001962 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001963
1964 return 0;
1965}
1966
1967/* called under codec_resource_lock acquisition */
1968static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1969 enum wcd9xxx_mbhc_plug_type plug_type)
1970{
Joonwoo Park80a01172012-10-15 16:05:23 -07001971 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1972 __func__, mbhc->current_plug, plug_type);
1973
Joonwoo Parka8890262012-10-15 12:04:27 -07001974 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1975
1976 if (plug_type == PLUG_TYPE_HEADPHONE &&
1977 mbhc->current_plug == PLUG_TYPE_NONE) {
1978 /*
1979 * Nothing was reported previously
1980 * report a headphone or unsupported
1981 */
1982 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1983 wcd9xxx_cleanup_hs_polling(mbhc);
1984 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07001985 if (!mbhc->mbhc_cfg->detect_extn_cable) {
1986 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
1987 wcd9xxx_report_plug(mbhc, 0,
1988 SND_JACK_HEADSET);
1989 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
1990 wcd9xxx_report_plug(mbhc, 0,
1991 SND_JACK_HEADPHONE);
1992 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001993 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
1994 wcd9xxx_cleanup_hs_polling(mbhc);
1995 } else if (plug_type == PLUG_TYPE_HEADSET) {
1996 /*
1997 * If Headphone was reported previously, this will
1998 * only report the mic line
1999 */
2000 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302001 /* Button detection required RC oscillator */
2002 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002003 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002004
2005 /* if PA is already on, switch micbias source to VDDIO */
2006 if (mbhc->event_state &
2007 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2008 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002009 wcd9xxx_start_hs_polling(mbhc);
2010 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002011 if (mbhc->mbhc_cfg->detect_extn_cable) {
2012 /* High impedance device found. Report as LINEOUT*/
2013 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2014 wcd9xxx_cleanup_hs_polling(mbhc);
2015 pr_debug("%s: setup mic trigger for further detection\n",
2016 __func__);
2017 mbhc->lpi_enabled = true;
2018 /*
2019 * Do not enable HPHL trigger. If playback is active,
2020 * it might lead to continuous false HPHL triggers
2021 */
2022 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2023 false);
2024 } else {
2025 if (mbhc->current_plug == PLUG_TYPE_NONE)
2026 wcd9xxx_report_plug(mbhc, 1,
2027 SND_JACK_HEADPHONE);
2028 wcd9xxx_cleanup_hs_polling(mbhc);
2029 pr_debug("setup mic trigger for further detection\n");
2030 mbhc->lpi_enabled = true;
2031 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2032 MBHC_USE_HPHL_TRIGGER,
2033 false);
2034 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002035 } else {
2036 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2037 mbhc->current_plug, plug_type);
2038 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002039 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002040}
2041
2042/* called under codec_resource_lock acquisition */
2043static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2044{
2045 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002046 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002047
2048 pr_debug("%s: enter\n", __func__);
2049
2050 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002051 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2052 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002053
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002054 if (current_source_enable) {
2055 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2056 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2057 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2058 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002059 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002060 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002061 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002062 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002063
2064 if (wcd9xxx_swch_level_remove(mbhc)) {
2065 pr_debug("%s: Switch level is low when determining plug\n",
2066 __func__);
2067 return;
2068 }
2069
2070 if (plug_type == PLUG_TYPE_INVALID ||
2071 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002072 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002073 wcd9xxx_schedule_hs_detect_plug(mbhc,
2074 &mbhc->correct_plug_swch);
2075 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2076 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002077 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002078 wcd9xxx_schedule_hs_detect_plug(mbhc,
2079 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002080 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002081 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002082 wcd9xxx_schedule_hs_detect_plug(mbhc,
2083 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002084 } else {
2085 pr_debug("%s: Valid plug found, determine plug type %d\n",
2086 __func__, plug_type);
2087 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2088 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002089 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002090}
2091
2092/* called under codec_resource_lock acquisition */
2093static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2094{
Joonwoo Park80a01172012-10-15 16:05:23 -07002095 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002096 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2097
Joonwoo Parka8890262012-10-15 12:04:27 -07002098 if (wcd9xxx_swch_level_remove(mbhc))
2099 pr_debug("%s: Switch level low when determining plug\n",
2100 __func__);
2101 else
2102 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002103 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002104}
2105
2106/* called only from interrupt which is under codec_resource_lock acquisition */
2107static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2108 bool is_removal)
2109{
2110 if (!is_removal) {
2111 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2112
2113 rmb();
2114 if (mbhc->lpi_enabled)
2115 msleep(100);
2116
2117 rmb();
2118 if (!mbhc->lpi_enabled) {
2119 pr_debug("%s: lpi is disabled\n", __func__);
2120 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2121 pr_debug("%s: Valid insertion, detect plug type\n",
2122 __func__);
2123 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2124 } else {
2125 pr_debug("%s: Invalid insertion stop plug detection\n",
2126 __func__);
2127 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002128 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2129 pr_debug("%s: Removal\n", __func__);
2130 if (!wcd9xxx_swch_level_remove(mbhc)) {
2131 /*
2132 * Switch indicates, something is still inserted.
2133 * This could be extension cable i.e. headset is
2134 * removed from extension cable.
2135 */
2136 /* cancel detect plug */
2137 wcd9xxx_cancel_hs_detect_plug(mbhc,
2138 &mbhc->correct_plug_swch);
2139 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2140 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002141 } else {
2142 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2143 }
2144}
2145
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002146static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2147 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002148{
2149 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2150 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2151 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2152
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002153 if (cs_enable)
2154 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2155 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2156 else
2157 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2158 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2159 (mic_mv > plug_type->v_no_mic) &&
2160 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002161}
2162
2163/*
2164 * called under codec_resource_lock acquisition
2165 * returns true if mic voltage range is back to normal insertion
2166 * returns false either if timedout or removed
2167 */
2168static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2169{
2170 int i;
2171 bool timedout, settled = false;
2172 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2173 short mb_v[NUM_DCE_PLUG_DETECT];
2174 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002175 bool cs_enable;
2176
2177 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2178 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2179
2180 if (cs_enable)
2181 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002182
2183 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2184 while (!(timedout = time_after(jiffies, timeout))) {
2185 retry++;
2186 if (wcd9xxx_swch_level_remove(mbhc)) {
2187 pr_debug("%s: Switch indicates removal\n", __func__);
2188 break;
2189 }
2190
2191 if (retry > 1)
2192 msleep(250);
2193 else
2194 msleep(50);
2195
2196 if (wcd9xxx_swch_level_remove(mbhc)) {
2197 pr_debug("%s: Switch indicates removal\n", __func__);
2198 break;
2199 }
2200
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002201 if (cs_enable) {
2202 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2203 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2204 true, true);
2205 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2206 true,
2207 mb_v[i],
2208 mbhc->mbhc_data.dce_nsc_cs_z,
2209 (u32)VDDIO_MICBIAS_MV);
2210 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2211 __func__, retry, mic_mv[i], mb_v[i]);
2212 }
2213 } else {
2214 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2215 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2216 true);
2217 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2218 mb_v[i]);
2219 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2220 __func__, retry, mic_mv[i],
2221 mb_v[i]);
2222 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002223 }
2224
2225 if (wcd9xxx_swch_level_remove(mbhc)) {
2226 pr_debug("%s: Switcn indicates removal\n", __func__);
2227 break;
2228 }
2229
2230 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2231 pr_debug("%s : headset/headphone is removed\n",
2232 __func__);
2233 break;
2234 }
2235
2236 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002237 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002238 break;
2239
2240 if (i == NUM_DCE_PLUG_DETECT) {
2241 pr_debug("%s: MIC voltage settled\n", __func__);
2242 settled = true;
2243 msleep(200);
2244 break;
2245 }
2246 }
2247
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002248 if (cs_enable)
2249 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2250
Joonwoo Parka8890262012-10-15 12:04:27 -07002251 if (timedout)
2252 pr_debug("%s: Microphone did not settle in %d seconds\n",
2253 __func__, HS_DETECT_PLUG_TIME_MS);
2254 return settled;
2255}
2256
2257/* called only from interrupt which is under codec_resource_lock acquisition */
2258static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2259{
Joonwoo Park80a01172012-10-15 16:05:23 -07002260 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002261 if (wcd9xxx_hs_remove_settle(mbhc))
2262 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002263 pr_debug("%s: leave\n", __func__);
2264}
2265
2266/* called only from interrupt which is under codec_resource_lock acquisition */
2267static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2268{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002269 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002270 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002271 bool removed = true;
2272 struct snd_soc_codec *codec = mbhc->codec;
2273 const struct wcd9xxx_mbhc_general_cfg *generic =
2274 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002275 bool cs_enable;
2276 s16 cur_v_ins_h;
2277 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002278
2279 pr_debug("%s: enter\n", __func__);
2280 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2281 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2282 __func__);
2283 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2284 0x08, 0x08);
2285 return;
2286 }
2287
2288 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002289 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002290
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002291 /* If micbias is enabled, don't enable current source */
2292 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2293 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2294 (!(snd_soc_read(codec,
2295 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002296 if (cs_enable)
2297 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2298
Joonwoo Park50ae0512013-06-04 16:53:12 -07002299 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002300 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002301 if (cs_enable) {
2302 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2303 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2304 mb_mv = VDDIO_MICBIAS_MV;
2305 } else {
2306 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2307 dcez = mbhc->mbhc_data.dce_z;
2308 mb_mv = mbhc->mbhc_data.micb_mv;
2309 }
2310
Joonwoo Park50ae0512013-06-04 16:53:12 -07002311 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002312 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2313 dcez, mb_mv));
2314
2315 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2316 (wcd9xxx_get_current_v(mbhc,
2317 WCD9XXX_CURRENT_V_INS_H));
2318
2319 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002320 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002321 break;
2322 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002323 } while (!time_after(jiffies, timeout));
2324 pr_debug("%s: headset %sactually removed\n", __func__,
2325 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002326
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002327 if (cs_enable)
2328 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2329
Joonwoo Park80a01172012-10-15 16:05:23 -07002330 if (removed) {
2331 if (mbhc->mbhc_cfg->detect_extn_cable) {
2332 if (!wcd9xxx_swch_level_remove(mbhc)) {
2333 /*
2334 * extension cable is still plugged in
2335 * report it as LINEOUT device
2336 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302337 if (mbhc->hph_status == SND_JACK_HEADSET)
2338 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2339 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002340 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2341 wcd9xxx_cleanup_hs_polling(mbhc);
2342 wcd9xxx_enable_hs_detect(mbhc, 1,
2343 MBHC_USE_MB_TRIGGER,
2344 false);
2345 }
2346 } else {
2347 /* Cancel possibly running hs_detect_work */
2348 wcd9xxx_cancel_hs_detect_plug(mbhc,
2349 &mbhc->correct_plug_noswch);
2350 /*
2351 * If this removal is not false, first check the micbias
2352 * switch status and switch it to LDOH if it is already
2353 * switched to VDDIO.
2354 */
2355 wcd9xxx_switch_micbias(mbhc, 0);
2356
2357 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302358 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002359 wcd9xxx_cleanup_hs_polling(mbhc);
2360 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2361 MBHC_USE_HPHL_TRIGGER,
2362 true);
2363 }
2364 } else {
2365 wcd9xxx_start_hs_polling(mbhc);
2366 }
2367 pr_debug("%s: leave\n", __func__);
2368}
2369
2370/* called only from interrupt which is under codec_resource_lock acquisition */
2371static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2372 bool is_mb_trigger)
2373{
2374 /* Cancel possibly running hs_detect_work */
2375 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2376
2377 if (is_mb_trigger) {
2378 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2379 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2380 } else {
2381 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2382 __func__);
2383 wcd9xxx_mbhc_detect_plug_type(mbhc);
2384 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002385}
2386
2387static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2388{
Joonwoo Parka8890262012-10-15 12:04:27 -07002389 struct wcd9xxx_mbhc *mbhc = data;
2390
2391 pr_debug("%s: enter, removal interrupt\n", __func__);
2392 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002393 /*
2394 * While we don't know whether MIC is there or not, let the resmgr know
2395 * so micbias can be disabled temporarily
2396 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002397 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002398 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2399 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002400 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2401 WCD9XXX_COND_HPH, false);
2402 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2403 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2404 WCD9XXX_COND_HPH, false);
2405 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002406
Joonwoo Park80a01172012-10-15 16:05:23 -07002407 if (mbhc->mbhc_cfg->detect_extn_cable &&
2408 !wcd9xxx_swch_level_remove(mbhc))
2409 wcd9xxx_hs_remove_irq_noswch(mbhc);
2410 else
2411 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002412
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002413 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2414 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2415 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002416 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2417 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002418 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2419 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2420 WCD9XXX_COND_HPH, true);
2421 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002422 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2423
2424 return IRQ_HANDLED;
2425}
2426
2427static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2428{
2429 bool is_mb_trigger, is_removal;
2430 struct wcd9xxx_mbhc *mbhc = data;
2431 struct snd_soc_codec *codec = mbhc->codec;
2432
2433 pr_debug("%s: enter\n", __func__);
2434 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002435 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002436
2437 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2438 0x10);
2439 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2440 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2441
2442 /* Turn off both HPH and MIC line schmitt triggers */
2443 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2444 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2445 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2446
Joonwoo Park80a01172012-10-15 16:05:23 -07002447 if (mbhc->mbhc_cfg->detect_extn_cable &&
2448 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2449 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2450 else
2451 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002452
2453 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2454 return IRQ_HANDLED;
2455}
2456
2457static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2458{
2459 struct delayed_work *dwork;
2460 short bias_value;
2461 int dce_mv, sta_mv;
2462 struct wcd9xxx_mbhc *mbhc;
2463
2464 pr_debug("%s:\n", __func__);
2465
2466 dwork = to_delayed_work(work);
2467 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2468
2469 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2470 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2471
2472 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2473 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2474 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2475
2476 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002477 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002478 mbhc->buttons_pressed);
2479
2480 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002481 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002482}
2483
2484static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2485{
2486 struct delayed_work *dwork;
2487 struct wcd9xxx_mbhc *mbhc;
2488 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002489 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002490
2491 dwork = to_delayed_work(work);
2492 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2493 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002494 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002495
2496 pr_debug("%s:\n", __func__);
2497
2498 /* Turn off both HPH and MIC line schmitt triggers */
2499 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2500 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2501 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002502 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002503 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002504 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002505}
2506
2507static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2508{
2509 u32 cfg_offset;
2510 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2511 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2512
2513 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2514 return false;
2515
2516 /*
2517 * Previous check guarantees that there is enough fw data up
2518 * to num_btn
2519 */
2520 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2521 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2522 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2523 return false;
2524
2525 /*
2526 * Previous check guarantees that there is enough fw data up
2527 * to start of impedance detection configuration
2528 */
2529 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2530 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2531
2532 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2533 return false;
2534
2535 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2536 return false;
2537
2538 return true;
2539}
2540
2541static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002542 enum meas_type dce, s16 vin_mv,
2543 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002544{
2545 s16 diff, zero;
2546 u32 mb_mv, in;
2547 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002548 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002549
2550 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002551 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002552
2553 if (mb_mv == 0) {
2554 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2555 return -EINVAL;
2556 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002557 if (cs_enable) {
2558 mb_mv = VDDIO_MICBIAS_MV;
2559 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2560 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002561
2562 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002563 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2564 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002565 } else {
2566 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2567 zero = (mbhc->mbhc_data.sta_z);
2568 }
2569 in = (u32) diff * vin_mv;
2570
2571 value = (u16) (in / mb_mv) + zero;
2572 return value;
2573}
2574
2575static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2576{
2577 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002578 s16 adj_v_hs_max;
2579 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 -07002580 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2581 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2582 u16 *btn_high;
2583 int i;
2584
2585 pr_debug("%s: enter\n", __func__);
2586 codec = mbhc->codec;
2587 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2588 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2589
Joonwoo Park73375212013-05-07 12:42:44 -07002590 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002591 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002592 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002593 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002594
2595 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2596 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2597
2598 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002599 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2600 true);
2601 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002602 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002603 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002604 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002605 mbhc->mbhc_data.v_inval_ins_low =
2606 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2607 false);
2608 mbhc->mbhc_data.v_inval_ins_high =
2609 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2610 false);
2611 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002612 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2613 WCD9XXX_V_CS_HS_MAX,
2614 true);
2615 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2616 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002617
2618 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2619 MBHC_BTN_DET_V_BTN_HIGH);
2620 for (i = 0; i < btn_det->num_btn; i++)
2621 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2622
Joonwoo Park73375212013-05-07 12:42:44 -07002623 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2624 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2625 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2626 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2627 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2628 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002629
Joonwoo Park73375212013-05-07 12:42:44 -07002630 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002631 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2632 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002633 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002634 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2635 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002636 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002637 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2638 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002639 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002640 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2641 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002642
Joonwoo Park73375212013-05-07 12:42:44 -07002643 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2644 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2645 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2646 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002647
Joonwoo Parka8890262012-10-15 12:04:27 -07002648 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2649
2650 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002651 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002652 pr_debug("%s: leave\n", __func__);
2653}
2654
2655static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2656{
2657 /*
2658 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2659 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2660 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2661 */
2662 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2663}
2664
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002665/*
2666 * Mic Bias Enable Decision
2667 * Return true if high_hph_cnt is a power of 2 (!= 2)
2668 * otherwise return false
2669 */
2670static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2671{
2672 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2673}
2674
Joonwoo Parka8890262012-10-15 12:04:27 -07002675static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2676{
2677 struct wcd9xxx_mbhc *mbhc;
2678 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002679 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002680 unsigned long timeout;
2681 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002682 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002683 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002684 bool current_source_enable;
2685 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002686
2687 pr_debug("%s: enter\n", __func__);
2688
2689 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2690 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002691 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2692 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002693
2694 wcd9xxx_onoff_ext_mclk(mbhc, true);
2695
2696 /*
2697 * Keep override on during entire plug type correction work.
2698 *
2699 * This is okay under the assumption that any switch irqs which use
2700 * MBHC block cancel and sync this work so override is off again
2701 * prior to switch interrupt handler's MBHC block usage.
2702 * Also while this correction work is running, we can guarantee
2703 * DAPM doesn't use any MBHC block as this work only runs with
2704 * headphone detection.
2705 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002706 if (current_source_enable)
2707 wcd9xxx_turn_onoff_current_source(mbhc, true,
2708 false);
2709 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002710 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002711
2712 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2713 while (!time_after(jiffies, timeout)) {
2714 ++retry;
2715 rmb();
2716 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002717 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002718 pr_debug("%s: stop requested\n", __func__);
2719 break;
2720 }
2721
2722 msleep(HS_DETECT_PLUG_INERVAL_MS);
2723 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002724 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002725 pr_debug("%s: Switch level is low\n", __func__);
2726 break;
2727 }
2728
2729 /* can race with removal interrupt */
2730 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002731 if (current_source_enable)
2732 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2733 highhph);
2734 else
2735 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002736 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2737
Joonwoo Park80a01172012-10-15 16:05:23 -07002738 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2739 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002740
2741 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2742 (highhph_cnt + 1) :
2743 0;
2744 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002745 if (plug_type == PLUG_TYPE_INVALID) {
2746 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002747 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2748 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002749 mbhc->current_plug == PLUG_TYPE_NONE) {
2750 wcd9xxx_report_plug(mbhc, 1,
2751 SND_JACK_HEADPHONE);
2752 }
2753 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2754 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002755 if (mbhc->mbhc_cfg->detect_extn_cable) {
2756 if (mbhc->current_plug != plug_type)
2757 wcd9xxx_report_plug(mbhc, 1,
2758 SND_JACK_HEADPHONE);
2759 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002760 wcd9xxx_report_plug(mbhc, 1,
2761 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002762 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002763 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2764 pr_debug("%s: High HPH detected, continue polling\n",
2765 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002766 } else {
2767 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2768 pt_gnd_mic_swap_cnt++;
2769 if (pt_gnd_mic_swap_cnt <
2770 GND_MIC_SWAP_THRESHOLD)
2771 continue;
2772 else if (pt_gnd_mic_swap_cnt >
2773 GND_MIC_SWAP_THRESHOLD) {
2774 /*
2775 * This is due to GND/MIC switch didn't
2776 * work, Report unsupported plug
2777 */
2778 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2779 /*
2780 * if switch is toggled, check again,
2781 * otherwise report unsupported plug
2782 */
2783 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2784 continue;
2785 }
2786 } else
2787 pt_gnd_mic_swap_cnt = 0;
2788
2789 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002790 /* Turn off override/current source */
2791 if (current_source_enable)
2792 wcd9xxx_turn_onoff_current_source(mbhc, false,
2793 false);
2794 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002795 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002796 /*
2797 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2798 */
2799 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2800 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2801 pr_debug("Attempt %d found correct plug %d\n", retry,
2802 plug_type);
2803 correction = true;
2804 break;
2805 }
2806 }
2807
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002808 highhph = false;
2809 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002810 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2811 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002812 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002813 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002814 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002815 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002816 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002817
2818 if (!correction && current_source_enable)
2819 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2820 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002821 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002822
2823 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002824
2825 if (mbhc->mbhc_cfg->detect_extn_cable) {
2826 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002827 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2828 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002829 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2830 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002831 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002832 /* Enable removal detection */
2833 wcd9xxx_cleanup_hs_polling(mbhc);
2834 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2835 }
2836 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2837 }
2838 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002839 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002840 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002841}
2842
2843static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2844{
2845 bool insert;
2846 bool is_removed = false;
2847 struct snd_soc_codec *codec = mbhc->codec;
2848
2849 pr_debug("%s: enter\n", __func__);
2850
2851 mbhc->in_swch_irq_handler = true;
2852 /* Wait here for debounce time */
2853 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2854
2855 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2856
2857 /* cancel pending button press */
2858 if (wcd9xxx_cancel_btn_work(mbhc))
2859 pr_debug("%s: button press is canceled\n", __func__);
2860
Santosh Mardic82bd362013-10-25 18:35:29 +05302861 /* cancel detect plug */
2862 wcd9xxx_cancel_hs_detect_plug(mbhc,
2863 &mbhc->correct_plug_swch);
2864
Joonwoo Parka8890262012-10-15 12:04:27 -07002865 insert = !wcd9xxx_swch_level_remove(mbhc);
2866 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2867 mbhc->current_plug, insert);
2868 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2869 mbhc->lpi_enabled = false;
2870 wmb();
2871
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002872 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2873 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2874 (1 << 1)))
2875 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002876
2877 /* Disable Mic Bias pull down and HPH Switch to GND */
2878 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2879 0x00);
2880 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2881 wcd9xxx_mbhc_detect_plug_type(mbhc);
2882 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2883 mbhc->lpi_enabled = false;
2884 wmb();
2885
Joonwoo Parka8890262012-10-15 12:04:27 -07002886 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2887 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2888 is_removed = true;
2889 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2890 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2891 is_removed = true;
2892 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2893 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302894 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002895 wcd9xxx_cleanup_hs_polling(mbhc);
2896 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2897 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002898 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2899 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2900 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002901 }
2902
2903 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002904 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2905 0x00);
2906 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2907 0x02, 0x00);
2908
Joonwoo Parka8890262012-10-15 12:04:27 -07002909 /* Enable Mic Bias pull down and HPH Switch to GND */
2910 snd_soc_update_bits(codec,
2911 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2912 0x01);
2913 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2914 0x01);
2915 /* Make sure mic trigger is turned off */
2916 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2917 0x01, 0x01);
2918 snd_soc_update_bits(codec,
2919 mbhc->mbhc_bias_regs.mbhc_reg,
2920 0x90, 0x00);
2921 /* Reset MBHC State Machine */
2922 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2923 0x08, 0x08);
2924 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2925 0x08, 0x00);
2926 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002927 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002928 }
2929 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002930exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002931 mbhc->in_swch_irq_handler = false;
2932 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2933 pr_debug("%s: leave\n", __func__);
2934}
2935
2936static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2937{
2938 int r = IRQ_HANDLED;
2939 struct wcd9xxx_mbhc *mbhc = data;
2940
2941 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002942 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002943 pr_warn("%s: failed to hold suspend\n", __func__);
2944 r = IRQ_NONE;
2945 } else {
2946 /* Call handler */
2947 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002948 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002949 }
2950
2951 pr_debug("%s: leave %d\n", __func__, r);
2952 return r;
2953}
2954
Joonwoo Park218e73f2013-08-21 16:22:18 -07002955static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002956{
Joonwoo Park73375212013-05-07 12:42:44 -07002957 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002958 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002959 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002960 const s16 v_ins_hu =
2961 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2962 const s16 v_ins_h =
2963 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2964 const s16 v_b1_hu =
2965 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2966 const s16 v_b1_h =
2967 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002968 const unsigned long timeout =
2969 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002970
Joonwoo Park218e73f2013-08-21 16:22:18 -07002971 while (time_before(jiffies, timeout)) {
2972 /*
2973 * This function needs to run measurements just few times during
2974 * release debounce time. Make 1ms interval to avoid
2975 * unnecessary excessive measurements.
2976 */
2977 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002978 if (i == 0) {
2979 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2980 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2981 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002982 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002983 r = 1;
2984 break;
2985 }
2986 } else {
2987 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2988 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
2989 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002990 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002991 r = 1;
2992 break;
2993 }
2994 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07002995 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07002996 }
2997
2998 return r;
2999}
3000
3001/* called under codec_resource_lock acquisition */
3002static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3003 const s32 micmv)
3004{
3005 s16 *v_btn_low, *v_btn_high;
3006 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3007 int i, btn = -1;
3008
3009 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3010 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3011 MBHC_BTN_DET_V_BTN_LOW);
3012 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3013 MBHC_BTN_DET_V_BTN_HIGH);
3014
3015 for (i = 0; i < btn_det->num_btn; i++) {
3016 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3017 btn = i;
3018 break;
3019 }
3020 }
3021
3022 if (btn == -1)
3023 pr_debug("%s: couldn't find button number for mic mv %d\n",
3024 __func__, micmv);
3025
3026 return btn;
3027}
3028
3029static int wcd9xxx_get_button_mask(const int btn)
3030{
3031 int mask = 0;
3032 switch (btn) {
3033 case 0:
3034 mask = SND_JACK_BTN_0;
3035 break;
3036 case 1:
3037 mask = SND_JACK_BTN_1;
3038 break;
3039 case 2:
3040 mask = SND_JACK_BTN_2;
3041 break;
3042 case 3:
3043 mask = SND_JACK_BTN_3;
3044 break;
3045 case 4:
3046 mask = SND_JACK_BTN_4;
3047 break;
3048 case 5:
3049 mask = SND_JACK_BTN_5;
3050 break;
3051 case 6:
3052 mask = SND_JACK_BTN_6;
3053 break;
3054 case 7:
3055 mask = SND_JACK_BTN_7;
3056 break;
3057 }
3058 return mask;
3059}
3060
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003061static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003062{
3063 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003064 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003065 struct snd_soc_codec *codec = mbhc->codec;
3066
3067 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3068 /* Pull down micbias to ground and disconnect vddio switch */
3069 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3070 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3071 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3072 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3073
3074 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003075 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3076 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003077 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003078 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003079 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003080 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3081 }
3082 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003083 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003084 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3085 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003086
Joonwoo Park520a0f92013-05-14 19:39:58 -07003087 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003088 if (change)
3089 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3090 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003091 /* Disable pull down micbias to ground */
3092 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3093 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3094}
3095
Joonwoo Park218e73f2013-08-21 16:22:18 -07003096void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3097{
3098 const u16 sta_z = mbhc->mbhc_data.sta_z;
3099 const u16 dce_z = mbhc->mbhc_data.dce_z;
3100
3101 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3102 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3103 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3104 mbhc->mbhc_data.sta_z & 0xFFFF,
3105 mbhc->mbhc_data.dce_z & 0xFFFF);
3106
3107 wcd9xxx_mbhc_calc_thres(mbhc);
3108 wcd9xxx_calibrate_hs_polling(mbhc);
3109}
3110
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003111/*
3112 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3113 * to ceilmv + buffer
3114 */
3115static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3116{
3117 u16 v_brh, v_b1_hu;
3118 int mv;
3119 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3120 void *calibration = mbhc->mbhc_cfg->calibration;
3121 struct snd_soc_codec *codec = mbhc->codec;
3122
3123 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3124 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3125 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3126
3127 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003128 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003129 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3130 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3131 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003132 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003133 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3134 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3135 (v_brh >> 8) & 0xFF);
3136 return 0;
3137}
3138
Joonwoo Parka8890262012-10-15 12:04:27 -07003139irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3140{
3141 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003142 bool vddio;
3143 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003144 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003145 s32 stamv, stamv_s;
3146 s16 *v_btn_high;
3147 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003148 int btn = -1, meas = 0;
3149 struct wcd9xxx_mbhc *mbhc = data;
3150 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3151 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3152 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003153 short dce[d->n_btn_meas + 1], sta;
3154 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003155 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003156 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003157 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003158 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003159
3160 pr_debug("%s: enter\n", __func__);
3161
3162 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3163 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3164
3165 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3166 pr_debug("%s: mbhc is being recovered, skip button press\n",
3167 __func__);
3168 goto done;
3169 }
3170
3171 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3172
3173 if (!mbhc->polling_active) {
3174 pr_warn("%s: mbhc polling is not active, skip button press\n",
3175 __func__);
3176 goto done;
3177 }
3178
Joonwoo Parka8890262012-10-15 12:04:27 -07003179 /* If switch nterrupt already kicked in, ignore button press */
3180 if (mbhc->in_swch_irq_handler) {
3181 pr_debug("%s: Swtich level changed, ignore button press\n",
3182 __func__);
3183 btn = -1;
3184 goto done;
3185 }
3186
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303187 /*
3188 * setup internal micbias if codec uses internal micbias for
3189 * headset detection
3190 */
3191 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
3192 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3193 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3194 else
3195 pr_err("%s: internal bias requested but codec did not provide callback\n",
3196 __func__);
3197 }
3198
3199
Joonwoo Parka8890262012-10-15 12:04:27 -07003200 /* Measure scaled HW DCE */
3201 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3202 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003203
Joonwoo Park218e73f2013-08-21 16:22:18 -07003204 dce_z = mbhc->mbhc_data.dce_z;
3205 sta_z = mbhc->mbhc_data.sta_z;
3206
Joonwoo Parka8890262012-10-15 12:04:27 -07003207 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003208 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003209 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003210 if (mbhc_status != STATUS_REL_DETECTION) {
3211 if (mbhc->mbhc_last_resume &&
3212 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3213 pr_debug("%s: Button is released after resume\n",
3214 __func__);
3215 n_btn_meas = 0;
3216 } else {
3217 pr_debug("%s: Button is released without resume",
3218 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003219 if (mbhc->update_z) {
3220 wcd9xxx_update_z(mbhc);
3221 mbhc->update_z = false;
3222 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003223 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3224 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003225 if (vddio)
3226 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3227 false);
3228 else
3229 stamv_s = stamv;
3230 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003231 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003232 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3233 false) : mv[0];
3234 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003235 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3236 btn = -1;
3237 goto done;
3238 }
3239 }
3240
Joonwoo Park520a0f92013-05-14 19:39:58 -07003241 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3242 meas++)
3243 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3244
Joonwoo Park218e73f2013-08-21 16:22:18 -07003245 if (mbhc->update_z) {
3246 wcd9xxx_update_z(mbhc);
3247 mbhc->update_z = false;
3248 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003249
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003250 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3251 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003252 if (vddio)
3253 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3254 else
3255 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003256 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003257 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003258
3259 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003260 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3261 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003262 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3263 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003264 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003265 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003266 if (n_btn_meas == 0)
3267 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003268 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3269 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003270 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3271 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003272 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3273 mv[meas];
3274 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003275 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003276 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3277 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003278 /*
3279 * if large enough measurements are collected,
3280 * start to check if last all n_btn_con measurements were
3281 * in same button low/high range
3282 */
3283 if (meas + 1 >= d->n_btn_con) {
3284 for (i = 0; i < d->n_btn_con; i++)
3285 if ((btnmeas[meas] < 0) ||
3286 (btnmeas[meas] != btnmeas[meas - i]))
3287 break;
3288 if (i == d->n_btn_con) {
3289 /* button pressed */
3290 btn = btnmeas[meas];
3291 break;
3292 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3293 /*
3294 * if left measurements are less than n_btn_con,
3295 * it's impossible to find button number
3296 */
3297 break;
3298 }
3299 }
3300 }
3301
3302 if (btn >= 0) {
3303 if (mbhc->in_swch_irq_handler) {
3304 pr_debug(
3305 "%s: Switch irq triggered, ignore button press\n",
3306 __func__);
3307 goto done;
3308 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003309 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3310 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3311 MBHC_BTN_DET_V_BTN_HIGH);
3312 WARN_ON(btn >= btn_det->num_btn);
3313 /* reprogram release threshold to catch voltage ramp up early */
3314 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3315
Joonwoo Parka8890262012-10-15 12:04:27 -07003316 mask = wcd9xxx_get_button_mask(btn);
3317 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003318 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003319 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3320 msecs_to_jiffies(400)) == 0) {
3321 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003322 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003323 }
3324 } else {
3325 pr_debug("%s: bogus button press, too short press?\n",
3326 __func__);
3327 }
3328
3329 done:
3330 pr_debug("%s: leave\n", __func__);
3331 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3332 return IRQ_HANDLED;
3333}
3334
3335static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3336{
3337 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003338 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003339 struct wcd9xxx_mbhc *mbhc = data;
3340
3341 pr_debug("%s: enter\n", __func__);
3342 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3343 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3344
Joonwoo Parka8890262012-10-15 12:04:27 -07003345 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3346 ret = wcd9xxx_cancel_btn_work(mbhc);
3347 if (ret == 0) {
3348 pr_debug("%s: Reporting long button release event\n",
3349 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003350 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003351 mbhc->buttons_pressed);
3352 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003353 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003354 pr_debug("%s: Fake button press interrupt\n",
3355 __func__);
3356 } else {
3357 if (mbhc->in_swch_irq_handler) {
3358 pr_debug("%s: Switch irq kicked in, ignore\n",
3359 __func__);
3360 } else {
3361 pr_debug("%s: Reporting btn press\n",
3362 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003363 wcd9xxx_jack_report(mbhc,
3364 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003365 mbhc->buttons_pressed,
3366 mbhc->buttons_pressed);
3367 pr_debug("%s: Reporting btn release\n",
3368 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003369 wcd9xxx_jack_report(mbhc,
3370 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003371 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003372 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003373 }
3374 }
3375 }
3376
3377 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3378 }
3379
3380 wcd9xxx_calibrate_hs_polling(mbhc);
3381
Joonwoo Park218e73f2013-08-21 16:22:18 -07003382 if (waitdebounce)
3383 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003384 wcd9xxx_start_hs_polling(mbhc);
3385
3386 pr_debug("%s: leave\n", __func__);
3387 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3388 return IRQ_HANDLED;
3389}
3390
3391static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3392{
3393 struct wcd9xxx_mbhc *mbhc = data;
3394 struct snd_soc_codec *codec;
3395
3396 pr_info("%s: received HPHL OCP irq\n", __func__);
3397
3398 if (mbhc) {
3399 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003400 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3401 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003402 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003403 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003404 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3405 0x10, 0x00);
3406 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3407 0x10, 0x10);
3408 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003409 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003410 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003411 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003412 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003413 mbhc->hph_status,
3414 WCD9XXX_JACK_MASK);
3415 }
3416 } else {
3417 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3418 }
3419
3420 return IRQ_HANDLED;
3421}
3422
3423static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3424{
3425 struct wcd9xxx_mbhc *mbhc = data;
3426 struct snd_soc_codec *codec;
3427
3428 pr_info("%s: received HPHR OCP irq\n", __func__);
3429 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003430 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3431 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003432 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003433 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003434 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3435 0x00);
3436 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3437 0x10);
3438 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003439 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003440 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003441 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003442 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003443 mbhc->hph_status, WCD9XXX_JACK_MASK);
3444 }
3445
3446 return IRQ_HANDLED;
3447}
3448
3449static int wcd9xxx_acdb_mclk_index(const int rate)
3450{
3451 if (rate == MCLK_RATE_12288KHZ)
3452 return 0;
3453 else if (rate == MCLK_RATE_9600KHZ)
3454 return 1;
3455 else {
3456 BUG_ON(1);
3457 return -EINVAL;
3458 }
3459}
3460
3461static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3462{
3463 u32 dce_wait, sta_wait;
3464 u8 ncic, nmeas, navg;
3465 void *calibration;
3466 u8 *n_cic, *n_ready;
3467 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3468 u8 npoll = 4, nbounce_wait = 30;
3469 struct snd_soc_codec *codec = mbhc->codec;
3470 int idx = wcd9xxx_acdb_mclk_index(rate);
3471 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3472
3473 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3474 rate);
3475 calibration = mbhc->mbhc_cfg->calibration;
3476
3477 /*
3478 * First compute the DCE / STA wait times depending on tunable
3479 * parameters. The value is computed in microseconds
3480 */
3481 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3482 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3483 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3484 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3485 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3486
3487 /* ncic stays with the same what we had during calibration */
3488 ncic = n_cic[idxmclk];
3489 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3490 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3491 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003492 /* give extra margin to sta for safety */
3493 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003494 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3495 n_ready[idx]) + 10;
3496
3497 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3498 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3499
3500 if (rate == MCLK_RATE_12288KHZ) {
3501 npoll = 4;
3502 nbounce_wait = 30;
3503 } else if (rate == MCLK_RATE_9600KHZ) {
3504 npoll = 3;
3505 nbounce_wait = 23;
3506 }
3507
3508 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3509 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3510 pr_debug("%s: leave\n", __func__);
3511}
3512
3513static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3514{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003515 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003516 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003517 struct snd_soc_codec *codec = mbhc->codec;
3518
3519 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003520 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3521 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003522 wcd9xxx_turn_onoff_rel_detection(codec, false);
3523
3524 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3525 WARN_ON(!mbhc->mbhc_data.t_dce);
3526 WARN_ON(!mbhc->mbhc_data.t_sta);
3527
3528 /*
3529 * LDOH and CFILT are already configured during pdata handling.
3530 * Only need to make sure CFILT and bandgap are in Fast mode.
3531 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003532 *
3533 * In case when Micbias is powered by external source, request
3534 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003535 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003536 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3537 mbhc->mbhc_cb->enable_mb_source(codec, true);
3538
Joonwoo Parka8890262012-10-15 12:04:27 -07003539 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303540 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3541 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3542 else
3543 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3544 0x40, 0x00);
3545
Joonwoo Parka8890262012-10-15 12:04:27 -07003546 /*
3547 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3548 * to perform ADC calibration
3549 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303550 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3551 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3552 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003553 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3554 mbhc->mbhc_cfg->micbias << 5);
3555 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3556 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3557 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303558 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3559 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3560 else
3561 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3562 0x04, 0x04);
3563
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003564 /* Pull down micbias to ground */
3565 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3566 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3567 /* Disconnect override from micbias */
3568 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3569 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3570 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303571 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303572 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3573 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3574 else
3575 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3576 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003577 /*
3578 * Hardware that has external cap can delay mic bias ramping down up
3579 * to 50ms.
3580 */
3581 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003582 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003583 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003584 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003585 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003586
3587 /* compute dce_z for current source */
3588 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3589 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3590 WCD9XXX_MBHC_NSC_CS << 3);
3591
3592 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3593 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3594 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3595 false);
3596 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3597 mbhc->mbhc_data.dce_nsc_cs_z);
3598
3599 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3600
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003601 /* STA measurement for 0 voltage */
3602 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3603 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3604 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003605
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003606 /* Restore registers */
3607 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3608 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003609
3610 /* DCE measurment for MB voltage */
3611 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3612 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303613 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303614 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3615 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3616 else
3617 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3618 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003619 /*
3620 * Hardware that has external cap can delay mic bias ramping down up
3621 * to 50ms.
3622 */
3623 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003624 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3625 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3626 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3627
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003628 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003629 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3630 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3631 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303632 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303633 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3634 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3635 else
3636 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3637 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003638 /*
3639 * Hardware that has external cap can delay mic bias ramping down up
3640 * to 50ms.
3641 */
3642 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003643 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3644 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3645 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3646
3647 /* Restore default settings. */
3648 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303649 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303650 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303651 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3652 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3653 else
3654 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3655 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003656 usleep_range(100, 100);
3657
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003658 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3659 mbhc->mbhc_cb->enable_mb_source(codec, false);
3660
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003661 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3662 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003663 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003664
Joonwoo Parka8890262012-10-15 12:04:27 -07003665 pr_debug("%s: leave\n", __func__);
3666}
3667
3668static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3669{
3670 int n;
3671 u8 *gain;
3672 struct wcd9xxx_mbhc_general_cfg *generic;
3673 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3674 struct snd_soc_codec *codec = mbhc->codec;
3675 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3676
3677 pr_debug("%s: enter\n", __func__);
3678 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3679 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3680
3681 for (n = 0; n < 8; n++) {
3682 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3683 0x07, n);
3684 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3685 btn_det->c[n]);
3686 }
3687
3688 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3689 btn_det->nc);
3690
3691 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3692 generic->mbhc_nsa << 4);
3693
3694 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3695 btn_det->n_meas);
3696
3697 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3698 generic->mbhc_navg);
3699
3700 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3701
3702 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3703 btn_det->mbhc_nsc << 3);
3704
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003705 if (mbhc->mbhc_cb &&
3706 mbhc->mbhc_cb->get_cdc_type() !=
3707 WCD9XXX_CDC_TYPE_HELICON) {
3708 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3709 snd_soc_update_bits(codec,
3710 mbhc->resmgr->reg_addr->micb_4_mbhc,
3711 0x03, MBHC_MICBIAS2);
3712 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003713
3714 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3715
3716 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3717
3718 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3719 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3720 gain[idx] << 3);
3721
3722 pr_debug("%s: leave\n", __func__);
3723}
3724
3725static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3726{
3727 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003728 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303729
Joonwoo Parka8890262012-10-15 12:04:27 -07003730 if (mbhc->mbhc_cfg->gpio) {
3731 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3732 wcd9xxx_mech_plug_detect_irq,
3733 (IRQF_TRIGGER_RISING |
3734 IRQF_TRIGGER_FALLING |
3735 IRQF_DISABLED),
3736 "headset detect", mbhc);
3737 if (ret) {
3738 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3739 mbhc->mbhc_cfg->gpio_irq);
3740 } else {
3741 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3742 if (ret)
3743 pr_err("%s: Failed to enable wake up irq %d\n",
3744 __func__, mbhc->mbhc_cfg->gpio_irq);
3745 }
3746 } else if (mbhc->mbhc_cfg->insert_detect) {
3747 /* Enable HPHL_10K_SW */
3748 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3749 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303750
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003751 ret = wcd9xxx_request_irq(core_res,
3752 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003753 wcd9xxx_mech_plug_detect_irq,
3754 "Jack Detect",
3755 mbhc);
3756 if (ret)
3757 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003758 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003759 }
3760
3761 return ret;
3762}
3763
3764static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3765{
3766 int ret = 0;
3767 struct snd_soc_codec *codec = mbhc->codec;
3768
3769 pr_debug("%s: enter\n", __func__);
3770
3771 /* Enable MCLK during calibration */
3772 wcd9xxx_onoff_ext_mclk(mbhc, true);
3773 wcd9xxx_mbhc_setup(mbhc);
3774 wcd9xxx_mbhc_cal(mbhc);
3775 wcd9xxx_mbhc_calc_thres(mbhc);
3776 wcd9xxx_onoff_ext_mclk(mbhc, false);
3777 wcd9xxx_calibrate_hs_polling(mbhc);
3778
3779 /* Enable Mic Bias pull down and HPH Switch to GND */
3780 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3781 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3782 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3783
3784 if (!IS_ERR_VALUE(ret)) {
3785 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3786 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003787 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003788 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003789 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003790 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003791
3792 /* Initialize mechanical mbhc */
3793 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3794
3795 if (!ret && mbhc->mbhc_cfg->gpio) {
3796 /* Requested with IRQF_DISABLED */
3797 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3798
3799 /* Bootup time detection */
3800 wcd9xxx_swch_irq_handler(mbhc);
3801 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3802 pr_debug("%s: Setting up codec own insert detection\n",
3803 __func__);
3804 /* Setup for insertion detection */
3805 wcd9xxx_insert_detect_setup(mbhc, true);
3806 }
3807 }
3808
3809 pr_debug("%s: leave\n", __func__);
3810
3811 return ret;
3812}
3813
3814static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3815{
3816 struct delayed_work *dwork;
3817 struct wcd9xxx_mbhc *mbhc;
3818 struct snd_soc_codec *codec;
3819 const struct firmware *fw;
3820 int ret = -1, retry = 0;
3821
3822 dwork = to_delayed_work(work);
3823 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3824 codec = mbhc->codec;
3825
3826 while (retry < FW_READ_ATTEMPTS) {
3827 retry++;
3828 pr_info("%s:Attempt %d to request MBHC firmware\n",
3829 __func__, retry);
3830 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3831 codec->dev);
3832
3833 if (ret != 0) {
3834 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3835 } else {
3836 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3837 break;
3838 }
3839 }
3840
3841 if (ret != 0) {
3842 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3843 __func__);
3844 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3845 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3846 __func__);
3847 release_firmware(fw);
3848 } else {
3849 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3850 mbhc->mbhc_fw = fw;
3851 }
3852
3853 (void) wcd9xxx_init_and_calibrate(mbhc);
3854}
3855
3856#ifdef CONFIG_DEBUG_FS
3857ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3858 size_t count, loff_t *pos)
3859{
3860 const int size = 768;
3861 char buffer[size];
3862 int n = 0;
3863 struct wcd9xxx_mbhc *mbhc = file->private_data;
3864 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003865 const s16 v_ins_hu =
3866 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3867 const s16 v_ins_h =
3868 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3869 const s16 v_b1_hu =
3870 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3871 const s16 v_b1_h =
3872 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3873 const s16 v_br_h =
3874 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003875
Joonwoo Park520a0f92013-05-14 19:39:58 -07003876 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3877 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003878 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3879 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003880 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3881 p->dce_nsc_cs_z,
3882 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3883 p->dce_nsc_cs_z,
3884 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003885 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3886 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3887 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3888 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003889 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3890 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3891 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3892 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3893 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3894 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3895 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003896 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003897 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003898 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003899 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003900 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003901 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003902 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3903 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3904 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3905 p->v_no_mic,
3906 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3907 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3908 p->v_inval_ins_low);
3909 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3910 p->v_inval_ins_high);
3911 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3912 !wcd9xxx_swch_level_remove(mbhc));
3913 buffer[n] = 0;
3914
3915 return simple_read_from_buffer(buf, count, pos, buffer, n);
3916}
3917
3918static int codec_debug_open(struct inode *inode, struct file *file)
3919{
3920 file->private_data = inode->i_private;
3921 return 0;
3922}
3923
3924static ssize_t codec_debug_write(struct file *filp,
3925 const char __user *ubuf, size_t cnt,
3926 loff_t *ppos)
3927{
3928 char lbuf[32];
3929 char *buf;
3930 int rc;
3931 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3932
3933 if (cnt > sizeof(lbuf) - 1)
3934 return -EINVAL;
3935
3936 rc = copy_from_user(lbuf, ubuf, cnt);
3937 if (rc)
3938 return -EFAULT;
3939
3940 lbuf[cnt] = '\0';
3941 buf = (char *)lbuf;
3942 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3943 false : true;
3944 return rc;
3945}
3946
3947static const struct file_operations mbhc_trrs_debug_ops = {
3948 .open = codec_debug_open,
3949 .write = codec_debug_write,
3950};
3951
3952static const struct file_operations mbhc_debug_ops = {
3953 .open = codec_debug_open,
3954 .read = codec_mbhc_debug_read,
3955};
3956
3957static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3958{
3959 mbhc->debugfs_poke =
3960 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3961 &mbhc_trrs_debug_ops);
3962 mbhc->debugfs_mbhc =
3963 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3964 NULL, mbhc, &mbhc_debug_ops);
3965}
3966
3967static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3968{
3969 debugfs_remove(mbhc->debugfs_poke);
3970 debugfs_remove(mbhc->debugfs_mbhc);
3971}
3972#else
3973static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3974{
3975}
3976
3977static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3978{
3979}
3980#endif
3981
3982int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3983 struct wcd9xxx_mbhc_config *mbhc_cfg)
3984{
Simmi Pateriya95466b12013-05-09 20:08:46 +05303985 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003986 struct snd_soc_codec *codec = mbhc->codec;
3987
3988 pr_debug("%s: enter\n", __func__);
3989
3990 if (!codec) {
3991 pr_err("%s: no codec\n", __func__);
3992 return -EINVAL;
3993 }
3994
3995 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
3996 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
3997 pr_err("Error: unsupported clock rate %d\n",
3998 mbhc_cfg->mclk_rate);
3999 return -EINVAL;
4000 }
4001
4002 /* Save mbhc config */
4003 mbhc->mbhc_cfg = mbhc_cfg;
4004
4005 /* Get HW specific mbhc registers' address */
4006 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4007
4008 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304009 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4010 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4011 else
4012 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4013 0x40, WCD9XXX_CFILT_FAST_MODE);
4014
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004015 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004016 * setup internal micbias if codec uses internal micbias for
4017 * headset detection
4018 */
4019 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304020 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004021 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304022 mbhc->int_rbias_on = true;
4023 } else {
4024 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004025 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304026 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004027 }
4028
4029 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004030 * If codec has specific clock gating for MBHC,
4031 * remove the clock gate
4032 */
4033 if (mbhc->mbhc_cb &&
4034 mbhc->mbhc_cb->enable_clock_gate)
4035 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4036
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004037 if (!mbhc->mbhc_cfg->read_fw_bin ||
4038 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004039 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004040 } else {
4041 if (!mbhc->mbhc_fw)
4042 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4043 usecs_to_jiffies(FW_READ_TIMEOUT));
4044 else
4045 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4046 __func__, mbhc->mbhc_fw);
4047 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004048
4049 pr_debug("%s: leave %d\n", __func__, rc);
4050 return rc;
4051}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004052EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004053
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004054void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4055{
4056 if (mbhc->mbhc_fw) {
4057 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4058 release_firmware(mbhc->mbhc_fw);
4059 mbhc->mbhc_fw = NULL;
4060 }
4061}
4062EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4063
Joonwoo Parka8890262012-10-15 12:04:27 -07004064static enum wcd9xxx_micbias_num
4065wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4066{
4067 enum wcd9xxx_micbias_num ret;
4068 switch (event) {
4069 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004070 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4071 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4072 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004073 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004074 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004075 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004076 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4077 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4078 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004079 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004080 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004081 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004082 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4083 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4084 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004085 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004086 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004087 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004088 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4089 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4090 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004091 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004092 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004093 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004094 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004095 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004096 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004097 }
4098 return ret;
4099}
4100
4101static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4102{
4103 int ret;
4104 switch (event) {
4105 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4106 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4107 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4108 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4109 ret = WCD9XXX_CFILT1_SEL;
4110 break;
4111 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4112 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4113 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4114 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4115 ret = WCD9XXX_CFILT2_SEL;
4116 break;
4117 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4118 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4119 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4120 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4121 ret = WCD9XXX_CFILT3_SEL;
4122 break;
4123 default:
4124 ret = -1;
4125 }
4126 return ret;
4127}
4128
4129static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4130{
4131 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004132 const struct wcd9xxx_micbias_setting *mb_pdata =
4133 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004134
4135 switch (mbhc->mbhc_cfg->micbias) {
4136 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004137 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004138 break;
4139 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004140 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004141 break;
4142 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004143 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004144 break;
4145 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004146 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004147 break;
4148 default:
4149 cfilt = MBHC_MICBIAS_INVALID;
4150 break;
4151 }
4152 return cfilt;
4153}
4154
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004155static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4156{
4157 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4158 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4159 else
4160 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4161 0x40, on ? 0x40 : 0x00);
4162}
4163
Joonwoo Parka8890262012-10-15 12:04:27 -07004164static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4165 void *data)
4166{
4167 int ret = 0;
4168 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4169 struct snd_soc_codec *codec = mbhc->codec;
4170 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4171
4172 pr_debug("%s: enter event %s(%d)\n", __func__,
4173 wcd9xxx_get_event_string(event), event);
4174
4175 switch (event) {
4176 /* MICBIAS usage change */
4177 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4178 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4179 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4180 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304181 mbhc->int_rbias_on = true;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004182 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4183 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004184 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004185 /*
4186 * Enable MBHC TxFE whenever micbias is
4187 * turned ON and polling is active
4188 */
4189 if (mbhc->polling_active)
4190 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4191 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004192 break;
4193 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4194 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4195 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4196 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004197 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004198 wcd9xxx_event_to_micbias(event) &&
4199 wcd9xxx_mbhc_polling(mbhc)) {
4200 /* if polling is on, restart it */
4201 wcd9xxx_pause_hs_polling(mbhc);
4202 wcd9xxx_start_hs_polling(mbhc);
4203 }
4204 break;
4205 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4206 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4207 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4208 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304209 mbhc->int_rbias_on = false;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004210 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4211 wcd9xxx_event_to_micbias(event)) {
4212 if (mbhc->event_state &
4213 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4214 wcd9xxx_switch_micbias(mbhc, 1);
4215 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304216 * Disable MBHC TxFE, in case it was enabled earlier
4217 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004218 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304219 if (!mbhc->polling_active)
4220 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004221 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004222 break;
4223 /* PA usage change */
4224 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004225 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004226 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004227 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004228 wcd9xxx_switch_micbias(mbhc, 1);
4229 break;
4230 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004231 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004232 break;
4233 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004234 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004235 /* if HPH PAs are off, report OCP and switch back to CFILT */
4236 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4237 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4238 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4239 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004240 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004241 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
4242 1 << MBHC_EVENT_PRE_TX_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004243 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004244 break;
4245 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004246 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004247 /* if HPH PAs are off, report OCP and switch back to CFILT */
4248 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4249 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4250 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4251 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004252 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004253 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
4254 1 << MBHC_EVENT_PRE_TX_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004255 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004256 break;
4257 /* Clock usage change */
4258 case WCD9XXX_EVENT_PRE_MCLK_ON:
4259 break;
4260 case WCD9XXX_EVENT_POST_MCLK_ON:
4261 /* Change to lower TxAAF frequency */
4262 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4263 1 << 4);
4264 /* Re-calibrate clock rate dependent values */
4265 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4266 /* If clock source changes, stop and restart polling */
4267 if (wcd9xxx_mbhc_polling(mbhc)) {
4268 wcd9xxx_calibrate_hs_polling(mbhc);
4269 wcd9xxx_start_hs_polling(mbhc);
4270 }
4271 break;
4272 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4273 /* If clock source changes, stop and restart polling */
4274 if (wcd9xxx_mbhc_polling(mbhc))
4275 wcd9xxx_pause_hs_polling(mbhc);
4276 break;
4277 case WCD9XXX_EVENT_POST_MCLK_OFF:
4278 break;
4279 case WCD9XXX_EVENT_PRE_RCO_ON:
4280 break;
4281 case WCD9XXX_EVENT_POST_RCO_ON:
4282 /* Change to higher TxAAF frequency */
4283 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4284 0 << 4);
4285 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004286 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004287 /* If clock source changes, stop and restart polling */
4288 if (wcd9xxx_mbhc_polling(mbhc)) {
4289 wcd9xxx_calibrate_hs_polling(mbhc);
4290 wcd9xxx_start_hs_polling(mbhc);
4291 }
4292 break;
4293 case WCD9XXX_EVENT_PRE_RCO_OFF:
4294 /* If clock source changes, stop and restart polling */
4295 if (wcd9xxx_mbhc_polling(mbhc))
4296 wcd9xxx_pause_hs_polling(mbhc);
4297 break;
4298 case WCD9XXX_EVENT_POST_RCO_OFF:
4299 break;
4300 /* CFILT usage change */
4301 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4302 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4303 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4304 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4305 wcd9xxx_event_to_cfilt(event))
4306 /*
4307 * Switch CFILT to slow mode if MBHC CFILT is being
4308 * used.
4309 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304310 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004311 break;
4312 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4313 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4314 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4315 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4316 wcd9xxx_event_to_cfilt(event))
4317 /*
4318 * Switch CFILT to fast mode if MBHC CFILT is not
4319 * used anymore.
4320 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304321 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004322 break;
4323 /* System resume */
4324 case WCD9XXX_EVENT_POST_RESUME:
4325 mbhc->mbhc_last_resume = jiffies;
4326 break;
4327 /* BG mode chage */
4328 case WCD9XXX_EVENT_PRE_BG_OFF:
4329 case WCD9XXX_EVENT_POST_BG_OFF:
4330 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4331 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4332 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4333 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4334 /* Not used for now */
4335 break;
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004336 case WCD9XXX_EVENT_PRE_TX_3_ON:
4337 /*
4338 * if polling is ON, mbhc micbias not enabled
4339 * switch micbias source to VDDIO
4340 */
4341 set_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
4342 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
4343 & 0x80) &&
4344 mbhc->polling_active && !mbhc->mbhc_micbias_switched)
4345 wcd9xxx_switch_micbias(mbhc, 1);
4346 break;
4347 case WCD9XXX_EVENT_POST_TX_3_OFF:
4348 /*
4349 * Switch back to micbias if HPH PA or TX3 path
4350 * is disabled
4351 */
4352 clear_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
4353 if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
4354 !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
4355 1 << MBHC_EVENT_PA_HPHR)))
4356 wcd9xxx_switch_micbias(mbhc, 0);
4357 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004358 default:
4359 WARN(1, "Unknown event %d\n", event);
4360 ret = -EINVAL;
4361 }
4362
4363 pr_debug("%s: leave\n", __func__);
4364
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304365 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004366}
4367
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004368static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4369 uint32_t *zr)
4370{
4371 int i;
4372 int ret = 0;
4373 s16 l[3], r[3];
4374 s16 *z[] = {
4375 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4376 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004377 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004378 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004379 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4380 /* Phase 1 */
4381 /* Set MBHC_MUX for HPHL without ical */
4382 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4383 /* Set MBHC_MUX for HPHR without ical */
4384 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4385 /* Set MBHC_MUX for HPHR with ical */
4386 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4387 /* Set MBHC_MUX for HPHL with ical */
4388 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4389
4390 /* Phase 2 */
4391 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4392 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4393 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4394 };
4395
4396 pr_debug("%s: enter\n", __func__);
4397 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4398
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004399 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4400 !mbhc->mbhc_cb->compute_impedance || !zl ||
4401 !zr)
4402 return -EINVAL;
4403
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004404 /*
4405 * Impedance detection is an intrusive function as it mutes RX paths,
4406 * enable PAs and etc. Therefore codec drvier including ALSA
4407 * shouldn't read and write hardware registers during detection.
4408 */
4409 mutex_lock(&codec->mutex);
4410
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004411 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004412
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004413 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004414 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004415
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004416 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4417 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004418
4419 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004420 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004421 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4422 reg_set_mux[i].mask,
4423 reg_set_mux[i].val);
4424 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4425 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4426 else
4427 snd_soc_update_bits(codec,
4428 WCD9XXX_A_MBHC_SCALING_MUX_1,
4429 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004430 /* 25us is required after mux change to settle down */
4431 usleep_range(mux_wait_us,
4432 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004433 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004434 }
4435
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004436 /* Codec specific setup for L2 and R2 measurements */
4437 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004438
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004439 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004440 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4441 reg_set_mux[i].mask,
4442 reg_set_mux[i].val);
4443 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4444 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4445 else
4446 snd_soc_update_bits(codec,
4447 WCD9XXX_A_MBHC_SCALING_MUX_1,
4448 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004449 /* 25us is required after mux change to settle down */
4450 usleep_range(mux_wait_us,
4451 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004452 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004453 }
4454
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004455 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004456
4457 mutex_unlock(&codec->mutex);
4458
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004459 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004460
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004461 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004462 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004463
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004464 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004465 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004466 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4467 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004468 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004469 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004470 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4471 pr_debug("%s: Impedance detection completed\n", __func__);
4472
4473 return ret;
4474}
4475
4476int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4477 uint32_t *zr)
4478{
4479 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4480 *zl = mbhc->zl;
4481 *zr = mbhc->zr;
4482 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4483
4484 if (*zl && *zr)
4485 return 0;
4486 else
4487 return -EINVAL;
4488}
4489
Joonwoo Parka8890262012-10-15 12:04:27 -07004490/*
4491 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4492 *
4493 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4494 */
4495int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004496 struct snd_soc_codec *codec,
4497 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004498 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4499 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4500 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004501 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004502{
4503 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004504 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004505
4506 pr_debug("%s: enter\n", __func__);
4507 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4508 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4509
4510 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4511 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4512 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4513 mbhc->mbhc_micbias_switched = false;
4514 mbhc->polling_active = false;
4515 mbhc->mbhc_state = MBHC_STATE_NONE;
4516 mbhc->in_swch_irq_handler = false;
4517 mbhc->current_plug = PLUG_TYPE_NONE;
4518 mbhc->lpi_enabled = false;
4519 mbhc->no_mic_headset_override = false;
4520 mbhc->mbhc_last_resume = 0;
4521 mbhc->codec = codec;
4522 mbhc->resmgr = resmgr;
4523 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004524 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004525 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304526 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004527 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004528 mbhc->impedance_detect = impedance_det_en;
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304529 mbhc->int_rbias_on = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07004530
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004531 if (mbhc->intr_ids == NULL) {
4532 pr_err("%s: Interrupt mapping not provided\n", __func__);
4533 return -EINVAL;
4534 }
4535
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004536 if (mbhc->headset_jack.jack == NULL) {
4537 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4538 &mbhc->headset_jack);
4539 if (ret) {
4540 pr_err("%s: Failed to create new jack\n", __func__);
4541 return ret;
4542 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004543
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004544 ret = snd_soc_jack_new(codec, "Button Jack",
4545 WCD9XXX_JACK_BUTTON_MASK,
4546 &mbhc->button_jack);
4547 if (ret) {
4548 pr_err("Failed to create new jack\n");
4549 return ret;
4550 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004551
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004552 ret = snd_jack_set_key(mbhc->button_jack.jack,
4553 SND_JACK_BTN_0,
4554 KEY_MEDIA);
4555 if (ret) {
4556 pr_err("%s: Failed to set code for btn-0\n",
4557 __func__);
4558 return ret;
4559 }
4560
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004561 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4562 wcd9xxx_mbhc_fw_read);
4563 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4564 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4565 wcd9xxx_mbhc_insert_work);
4566 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004567
4568 /* Register event notifier */
4569 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4570 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4571 if (ret) {
4572 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4573 return ret;
4574 }
4575
4576 wcd9xxx_init_debugfs(mbhc);
4577
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004578
4579 /* Disable Impedance detection by default for certain codec types */
4580 if (mbhc->mbhc_cb &&
4581 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4582 impedance_detect_en = 0;
4583 else
4584 impedance_detect_en = impedance_det_en ? 1 : 0;
4585
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004586 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004587 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004588 wcd9xxx_hs_insert_irq,
4589 "Headset insert detect", mbhc);
4590 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004591 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004592 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004593 goto err_insert_irq;
4594 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004595 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004596
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004597 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004598 wcd9xxx_hs_remove_irq,
4599 "Headset remove detect", mbhc);
4600 if (ret) {
4601 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004602 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004603 goto err_remove_irq;
4604 }
4605
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004606 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004607 wcd9xxx_dce_handler, "DC Estimation detect",
4608 mbhc);
4609 if (ret) {
4610 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004611 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004612 goto err_potential_irq;
4613 }
4614
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004615 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004616 wcd9xxx_release_handler,
4617 "Button Release detect", mbhc);
4618 if (ret) {
4619 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004620 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004621 goto err_release_irq;
4622 }
4623
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004624 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004625 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4626 mbhc);
4627 if (ret) {
4628 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004629 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004630 goto err_hphl_ocp_irq;
4631 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004632 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004633
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004634 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004635 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4636 mbhc);
4637 if (ret) {
4638 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004639 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004640 goto err_hphr_ocp_irq;
4641 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004642 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004643
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004644 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4645 1 << WCD9XXX_COND_HPH);
4646
Joonwoo Parka8890262012-10-15 12:04:27 -07004647 pr_debug("%s: leave ret %d\n", __func__, ret);
4648 return ret;
4649
4650err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004651 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004652err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004653 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004654err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004655 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004656err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004657 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004658err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004659 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004660err_insert_irq:
4661 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4662
4663 pr_debug("%s: leave ret %d\n", __func__, ret);
4664 return ret;
4665}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004666EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004667
4668void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4669{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004670 struct wcd9xxx_core_resource *core_res =
4671 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004672
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004673 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4674 1 << WCD9XXX_COND_HPH);
4675
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004676 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4677 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4678 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4679 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4680 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4681 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4682 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004683
Joonwoo Parka8890262012-10-15 12:04:27 -07004684 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004685 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004686}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004687EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004688
4689MODULE_DESCRIPTION("wcd9xxx MBHC module");
4690MODULE_LICENSE("GPL v2");