blob: 3d07f150e434fea741e300658a927aa94476d5fb [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);
Yeleswarapu, Nagaradheshcff6b342013-11-05 17:12:39 +0530844 mbhc->hph_status &= ~(SND_JACK_HEADSET |
845 SND_JACK_LINEOUT);
Joonwoo Park80a01172012-10-15 16:05:23 -0700846 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700847 /* Report insertion */
848 mbhc->hph_status |= jack_type;
849
850 if (jack_type == SND_JACK_HEADPHONE) {
851 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
852 } else if (jack_type == SND_JACK_UNSUPPORTED) {
853 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
854 } else if (jack_type == SND_JACK_HEADSET) {
855 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
856 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700857 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700858 } else if (jack_type == SND_JACK_LINEOUT) {
859 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700860 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700861
862 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
863 pr_debug("%s: Enabling micbias\n", __func__);
864 mbhc->micbias_enable_cb(mbhc->codec, true);
865 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700866
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700867 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700868 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700869
Joonwoo Parka8890262012-10-15 12:04:27 -0700870 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
871 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800872 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700873 mbhc->hph_status, WCD9XXX_JACK_MASK);
874 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
875 }
876 /* Setup insert detect */
877 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700878
879 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700880}
881
882/* should be called under interrupt context that hold suspend */
883static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
884 struct work_struct *work)
885{
886 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
887 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
888 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700889 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700890 schedule_work(work);
891}
892
893/* called under codec_resource_lock acquisition */
894static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
895 struct work_struct *work)
896{
897 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
898 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
899 mbhc->hs_detect_work_stop = true;
900 wmb();
901 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
902 if (cancel_work_sync(work)) {
903 pr_debug("%s: correct_plug_swch is canceled\n",
904 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700905 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700906 }
907 WCD9XXX_BCL_LOCK(mbhc->resmgr);
908}
909
Joonwoo Park73375212013-05-07 12:42:44 -0700910static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
911{
912 int r;
913 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700914 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
915 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700916 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700917 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700918 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700919 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700920 return r;
921}
922
Joonwoo Parka8890262012-10-15 12:04:27 -0700923static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
924{
925 s16 v_hs_max;
926 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
927
928 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
929 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
930 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700931 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700932 else
933 v_hs_max = plug_type->v_hs_max;
934 return v_hs_max;
935}
936
Joonwoo Parka8890262012-10-15 12:04:27 -0700937static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
938{
939 u8 bias_msb, bias_lsb;
940 short bias_value;
941
942 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
943 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
944 bias_value = (bias_msb << 8) | bias_lsb;
945 return bias_value;
946}
947
948static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
949{
950 u8 bias_msb, bias_lsb;
951 short bias_value;
952
953 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
954 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
955 bias_value = (bias_msb << 8) | bias_lsb;
956 return bias_value;
957}
958
959static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
960 bool on)
961{
962 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
963}
964
965static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
966 bool override_bypass, bool noreldetection)
967{
968 short bias_value;
969 struct snd_soc_codec *codec = mbhc->codec;
970
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700971 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
972 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700973 if (noreldetection)
974 wcd9xxx_turn_onoff_rel_detection(codec, false);
975
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700976 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700977 /* Turn on the override */
978 if (!override_bypass)
979 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
980 if (dce) {
981 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
982 0x8);
983 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
984 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
985 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700986 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
987 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700988 usleep_range(mbhc->mbhc_data.t_sta_dce,
989 mbhc->mbhc_data.t_sta_dce);
990 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
991 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
992 bias_value = wcd9xxx_read_dce_result(codec);
993 } else {
994 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
995 0x8);
996 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
997 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
998 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700999 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
1000 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001001 usleep_range(mbhc->mbhc_data.t_sta_dce,
1002 mbhc->mbhc_data.t_sta_dce);
1003 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1004 usleep_range(mbhc->mbhc_data.t_sta,
1005 mbhc->mbhc_data.t_sta);
1006 bias_value = wcd9xxx_read_sta_result(codec);
1007 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1008 0x8);
1009 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1010 }
1011 /* Turn off the override after measuring mic voltage */
1012 if (!override_bypass)
1013 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1014 0x00);
1015
1016 if (noreldetection)
1017 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001018 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1019 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001020
1021 return bias_value;
1022}
1023
1024static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1025 bool norel)
1026{
1027 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1028}
1029
Joonwoo Park520a0f92013-05-14 19:39:58 -07001030static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001031 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001032{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001033 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001034 s32 mv;
1035
1036 value = bias_value;
1037 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001038 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001039 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001040 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001041 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001042 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001043 }
1044
1045 return mv;
1046}
1047
Joonwoo Park520a0f92013-05-14 19:39:58 -07001048static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1049 u16 bias_value)
1050{
1051 s16 z;
1052 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001053 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1054 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001055}
1056
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301057/* To enable/disable bandgap and RC oscillator */
1058static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1059 bool enable)
1060{
1061 if (enable) {
1062 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1063 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1064 WCD9XXX_BANDGAP_AUDIO_MODE);
1065 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1066 WCD9XXX_CLK_RCO);
1067 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1068 } else {
1069 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1070 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1071 WCD9XXX_CLK_RCO);
1072 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1073 WCD9XXX_BANDGAP_AUDIO_MODE);
1074 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1075 }
1076}
1077
Joonwoo Parka8890262012-10-15 12:04:27 -07001078/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001079static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1080 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001081{
1082 struct snd_soc_codec *codec = mbhc->codec;
1083 short bias_value;
1084 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001085 s16 reg;
1086 int change;
1087 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1088 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001089
1090 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1091
1092 pr_debug("%s: enter\n", __func__);
1093 if (!mbhc->mbhc_cfg->calibration) {
1094 pr_err("%s: Error, no calibration exists\n", __func__);
1095 return -ENODEV;
1096 }
1097
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001098 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001099 /* Enable external voltage source to micbias if present */
1100 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1101 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001102
Joonwoo Parka8890262012-10-15 12:04:27 -07001103 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301104 * setup internal micbias if codec uses internal micbias for
1105 * headset detection
1106 */
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301107 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301108 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1109 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1110 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301111 pr_err("%s: internal bias requested but codec did not provide callback\n",
1112 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301113 }
1114
Joonwoo Parka8890262012-10-15 12:04:27 -07001115 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1116
1117 /* Make sure CFILT is in fast mode, save current mode */
1118 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301119 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1120 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1121 else
1122 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1123 0x70, 0x00);
1124
Joonwoo Parka8890262012-10-15 12:04:27 -07001125 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301126 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301127 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1128 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1129 else
1130 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1131 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001132
1133 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1134 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1135 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1136
1137 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1138 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1139 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1140
1141 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1142 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1143
Joonwoo Parka8890262012-10-15 12:04:27 -07001144 /* don't flip override */
1145 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301146 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001147 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1148
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001149 /* recalibrate dce_z and sta_z */
1150 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1151 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1152 btn_det->mbhc_nsc << 3);
1153 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1154 if (change)
1155 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1156 if (dce_z && sta_z) {
1157 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1158 __func__,
1159 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1160 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1161 mbhc->mbhc_data.dce_z = dce_z;
1162 mbhc->mbhc_data.sta_z = sta_z;
1163 wcd9xxx_mbhc_calc_thres(mbhc);
1164 wcd9xxx_calibrate_hs_polling(mbhc);
1165 } else {
1166 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", __func__,
1167 dce_z, sta_z);
1168 }
1169
1170 if (is_cs_enable) {
1171 /* recalibrate dce_nsc_cs_z */
1172 reg = snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1173 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1174 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1175 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1176 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1177 if (dce_z) {
1178 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n", __func__,
1179 mbhc->mbhc_data.dce_nsc_cs_z, dce_z & 0xffff);
1180 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1181 } else {
1182 pr_debug("%s: failed get new dce_nsc_cs_z\n", __func__);
1183 }
1184 }
1185
Joonwoo Parka8890262012-10-15 12:04:27 -07001186 return bias_value;
1187}
1188
1189static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1190{
1191 struct snd_soc_codec *codec = mbhc->codec;
1192 const struct wcd9xxx_mbhc_general_cfg *generic =
1193 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1194
1195 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001196 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001197 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001198 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001199
1200 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1201 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001202 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001203
1204 usleep_range(generic->t_shutdown_plug_rem,
1205 generic->t_shutdown_plug_rem);
1206
1207 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1208
Joonwoo Park533b3682013-06-13 11:41:21 -07001209 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001210 /* Put requested CLK back */
1211 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001212 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001213
1214 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1215}
1216
1217static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1218{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001219
1220 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001221 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1222
1223 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1224
Joonwoo Parka8890262012-10-15 12:04:27 -07001225
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001226 /* Disable external voltage source to micbias if present */
1227 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1228 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1229
Joonwoo Parka8890262012-10-15 12:04:27 -07001230 mbhc->polling_active = false;
1231 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001232 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001233}
1234
Joonwoo Parka8890262012-10-15 12:04:27 -07001235/* called under codec_resource_lock acquisition */
1236static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1237{
1238 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1239 if (on)
1240 usleep_range(5000, 5000);
1241}
1242
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001243static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1244{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001245 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001246
1247 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1248 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1249 goto exit;
1250 }
1251
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001252 if (on) {
1253 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1254 1 << 7, 1 << 7);
1255 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1256 1 << 4, 0);
1257 } else {
1258 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1259 1 << 4, 1 << 4);
1260 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1261 1 << 7, 0);
1262 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001263
1264exit:
1265 /*
1266 * Wait for the micbias to settle down to vddio
1267 * when the micbias to vddio switch is enabled.
1268 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001269 if (on)
1270 usleep_range(10000, 10000);
1271}
1272
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001273static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1274{
1275 u16 hph, status;
1276 struct snd_soc_codec *codec = mbhc->codec;
1277
1278 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1279 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1280 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1281 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1282 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1283 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1284 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1285 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1286 return status;
1287}
1288
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001289static enum wcd9xxx_mbhc_plug_type
1290wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1291 struct wcd9xxx_mbhc_detect *dt, const int size,
1292 bool highhph,
1293 unsigned long event_state)
1294{
1295 int i;
1296 int vdce, mb_mv;
1297 int ch, sz, delta_thr;
1298 int minv = 0, maxv = INT_MIN;
1299 struct wcd9xxx_mbhc_detect *d = dt;
1300 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1301 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1302
1303 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1304 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1305 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001306 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001307
1308 pr_debug("%s: enter\n", __func__);
1309 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1310
1311 sz = size - 1;
1312 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1313 if (d->mic_bias) {
1314 dce_z = mbhc->mbhc_data.dce_z;
1315 mb_mv = mbhc->mbhc_data.micb_mv;
1316 hs_max = plug_type->v_hs_max;
1317 no_mic = plug_type->v_no_mic;
1318 } else {
1319 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1320 mb_mv = VDDIO_MICBIAS_MV;
1321 hs_max = WCD9XXX_V_CS_HS_MAX;
1322 no_mic = WCD9XXX_V_CS_NO_MIC;
1323 }
1324
1325 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1326 dce_z, (u32)mb_mv);
1327
1328 d->_vdces = vdce;
1329 if (d->_vdces < no_mic)
1330 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001331 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001332 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001333 highhph_cnt++;
1334 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001335 d->_type = PLUG_TYPE_HEADSET;
1336
1337 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1338 __func__, i, d->dce, vdce, d->_vdces,
1339 d->hphl_status & 0x01,
1340 d->_type);
1341
1342 ch += d->hphl_status & 0x01;
1343 if (!d->swap_gnd && !d->mic_bias) {
1344 if (maxv < d->_vdces)
1345 maxv = d->_vdces;
1346 if (!minv || minv > d->_vdces)
1347 minv = d->_vdces;
1348 }
1349 if ((!d->mic_bias &&
1350 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1351 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1352 (d->mic_bias &&
1353 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1354 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1355 pr_debug("%s: within invalid range\n", __func__);
1356 type = PLUG_TYPE_INVALID;
1357 goto exit;
1358 }
1359 }
1360
1361 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1362 pr_debug("%s: HPHL PA was ON\n", __func__);
1363 } else if (ch != sz && ch > 0) {
1364 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1365 type = PLUG_TYPE_INVALID;
1366 goto exit;
1367 }
1368
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001369 delta_thr = ((highhph_cnt == sz) || highhph) ?
1370 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001371 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1372
1373 for (i = 0, d = dt; i < sz; i++, d++) {
1374 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1375 (d->_type != dprev->_type)) {
1376 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1377 type = PLUG_TYPE_INVALID;
1378 goto exit;
1379 }
1380
1381 if (!d->swap_gnd && !d->mic_bias &&
1382 (abs(minv - d->_vdces) > delta_thr ||
1383 abs(maxv - d->_vdces) > delta_thr)) {
1384 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1385 __func__, d->_vdces, minv, maxv);
1386 type = PLUG_TYPE_INVALID;
1387 goto exit;
1388 } else if (d->swap_gnd) {
1389 dgnd = d;
1390 }
1391
1392 if (!d->mic_bias && !d->swap_gnd)
1393 dprev = d;
1394 else if (d->mic_bias)
1395 dmicbias = d;
1396 }
1397 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1398 dt->_type != dgnd->_type) {
1399 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1400 type = PLUG_TYPE_INVALID;
1401 goto exit;
1402 }
1403
1404 type = dt->_type;
1405 if (dmicbias) {
1406 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1407 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1408 dt->_type == PLUG_TYPE_HEADSET)) {
1409 type = PLUG_TYPE_HEADSET;
1410 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1411 pr_debug("%s: Headset with threshold on MIC detected\n",
1412 __func__);
1413 if (mbhc->mbhc_cfg->micbias_enable_flags &
1414 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1415 mbhc->micbias_enable = true;
1416 }
1417 }
1418 }
1419
1420 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1421 if (((type == PLUG_TYPE_HEADSET ||
1422 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1423 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1424 __func__, type);
1425 type = PLUG_TYPE_INVALID;
1426 }
1427 }
1428 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1429 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1430 minv) &&
1431 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1432 maxv))
1433 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001434 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001435 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1436 type = PLUG_TYPE_INVALID;
1437 }
1438 }
1439exit:
1440 pr_debug("%s: Plug type %d detected\n", __func__, type);
1441 return type;
1442}
1443
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001444/*
1445 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1446 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001447 * param mbhc wcd9xxx_mbhc structure
1448 * param dt collected measurements
1449 * param size array size of dt
1450 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001451 */
1452static enum wcd9xxx_mbhc_plug_type
1453wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001454 struct wcd9xxx_mbhc_detect *dt, const int size,
1455 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001456{
1457 int i;
1458 int ch;
1459 enum wcd9xxx_mbhc_plug_type type;
1460 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001461 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001462 int maxv = 0, minv = 0;
1463 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1464 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1465 const s16 hs_max = plug_type->v_hs_max;
1466 const s16 no_mic = plug_type->v_no_mic;
1467
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001468 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1469
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001470 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1471 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1472 if (d->vddio)
1473 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1474 else
1475 d->_vdces = vdce;
1476
1477 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1478 d->_type = PLUG_TYPE_HEADSET;
1479 else if (d->_vdces < no_mic)
1480 d->_type = PLUG_TYPE_HEADPHONE;
1481 else
1482 d->_type = PLUG_TYPE_HIGH_HPH;
1483
1484 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001485 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001486 if (maxv < d->_vdces)
1487 maxv = d->_vdces;
1488 if (!minv || minv > d->_vdces)
1489 minv = d->_vdces;
1490 }
1491
1492 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1493 __func__, i, d->dce, vdce, d->_vdces,
1494 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1495 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001496
1497
1498 /*
1499 * If GND and MIC prongs are aligned to HPHR and GND of
1500 * headphone, codec measures the voltage based on
1501 * impedance between HPHR and GND which results in ~80mv.
1502 * Avoid this.
1503 */
1504 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1505 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1506 pr_debug("%s: within invalid range\n", __func__);
1507 type = PLUG_TYPE_INVALID;
1508 goto exit;
1509 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001510 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001511
1512 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1513 pr_debug("%s: HPHL PA was ON\n", __func__);
1514 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001515 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1516 type = PLUG_TYPE_INVALID;
1517 goto exit;
1518 }
1519
Joonwoo Parka84ec452013-07-17 15:02:52 -07001520 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001521 if (d->vddio) {
1522 dvddio = d;
1523 continue;
1524 }
1525
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001526 if ((i > 0) && (d->_type != dprev->_type)) {
1527 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1528 type = PLUG_TYPE_INVALID;
1529 goto exit;
1530 }
1531
1532 if (!d->swap_gnd && !d->hwvalue &&
1533 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1534 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1535 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1536 __func__, d->_vdces, minv, maxv);
1537 type = PLUG_TYPE_INVALID;
1538 goto exit;
1539 } else if (d->swap_gnd) {
1540 dgnd = d;
1541 }
1542 dprev = d;
1543 }
1544
1545 WARN_ON(i != size);
1546 type = dt->_type;
1547 if (type == PLUG_TYPE_HEADSET && dgnd) {
1548 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1549 minv) &&
1550 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1551 maxv))
1552 type = PLUG_TYPE_GND_MIC_SWAP;
1553 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001554
1555 /* if HPHL PA was on, we cannot use hphl status */
1556 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1557 if (((type == PLUG_TYPE_HEADSET ||
1558 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1559 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1560 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1561 __func__, type);
1562 type = PLUG_TYPE_INVALID;
1563 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001564 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001565
Joonwoo Parkccccba72013-04-26 11:19:46 -07001566 if (type == PLUG_TYPE_HEADSET && dvddio) {
1567 if ((dvddio->_vdces > hs_max) ||
1568 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1569 pr_debug("%s: Headset with threshold on MIC detected\n",
1570 __func__);
1571 if (mbhc->mbhc_cfg->micbias_enable_flags &
1572 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1573 mbhc->micbias_enable = true;
1574 } else {
1575 pr_debug("%s: Headset with regular MIC detected\n",
1576 __func__);
1577 if (mbhc->mbhc_cfg->micbias_enable_flags &
1578 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1579 mbhc->micbias_enable = true;
1580 }
1581 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001582exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001583 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1584 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001585 return type;
1586}
1587
Joonwoo Parkccccba72013-04-26 11:19:46 -07001588/*
1589 * Pull down MBHC micbias for provided duration in microsecond.
1590 */
1591static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1592{
1593 bool micbiasconn = false;
1594 struct snd_soc_codec *codec = mbhc->codec;
1595 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1596
1597 /*
1598 * Disable MBHC to micbias connection to pull down
1599 * micbias and pull down micbias for a moment.
1600 */
1601 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1602 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1603 return -EFAULT;
1604 }
1605
1606 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1607 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1608 1 << 4, 0);
1609 micbiasconn = true;
1610 }
1611
1612 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1613
1614 /*
1615 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1616 * able to get consistent result across DCEs.
1617 */
1618 usleep_range(1000, 1000 + 10);
1619
1620 if (micbiasconn)
1621 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1622 1 << 4, 1 << 4);
1623 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1624 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1625
1626 return 0;
1627}
1628
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001629void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1630 bool highhph)
1631{
1632
1633 struct snd_soc_codec *codec;
1634 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1635 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1636 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1637
1638 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1639 codec = mbhc->codec;
1640
1641 if (on) {
1642 pr_debug("%s: enabling current source\n", __func__);
1643 /* Nsc to 9 */
1644 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1645 0x78, 0x48);
1646 /* pull down diode bit to 0 */
1647 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1648 0x01, 0x00);
1649 /*
1650 * Keep the low power insertion/removal
1651 * detection (reg 0x3DD) disabled
1652 */
1653 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1654 0x01, 0x00);
1655 /*
1656 * Enable the Mic Bias current source
1657 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1658 * Write bit[7] of register MICB_2_MBHC to 1
1659 * (INS_DET_ISRC_EN__ENABLE)
1660 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1661 */
1662 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1663 0xF0, 0xF0);
1664 /* Disconnect MBHC Override from MicBias and LDOH */
1665 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1666 } else {
1667 pr_debug("%s: disabling current source\n", __func__);
1668 /* Connect MBHC Override from MicBias and LDOH */
1669 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1670 /* INS_DET_ISRC_CTL to acdb value */
1671 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1672 0x60, plug_det->mic_current << 5);
1673 if (!highhph) {
1674 /* INS_DET_ISRC_EN__ENABLE to 0 */
1675 snd_soc_update_bits(codec,
1676 mbhc->mbhc_bias_regs.mbhc_reg,
1677 0x80, 0x00);
1678 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1679 snd_soc_update_bits(codec,
1680 mbhc->mbhc_bias_regs.mbhc_reg,
1681 0x10, 0x00);
1682 }
1683 /* Nsc to acdb value */
1684 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1685 btn_det->mbhc_nsc << 3);
1686 }
1687}
1688
1689static enum wcd9xxx_mbhc_plug_type
1690wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1691{
1692 struct snd_soc_codec *codec = mbhc->codec;
1693 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1694 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1695 int i;
1696
1697 pr_debug("%s: enter\n", __func__);
1698 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1699
1700 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1701
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301702 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001703 rt[0].swap_gnd = false;
1704 rt[0].vddio = false;
1705 rt[0].hwvalue = true;
1706 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1707 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1708 rt[0].mic_bias = false;
1709
1710 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1711 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1712 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1713 highhph);
1714 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1715 if (rt[i].swap_gnd)
1716 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1717
1718 if (rt[i].mic_bias)
1719 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1720
1721 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1722 if (rt[i].mic_bias)
1723 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1724 if (rt[i].swap_gnd)
1725 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1726 }
1727
1728 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1729 mbhc->event_state);
1730
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301731 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001732 pr_debug("%s: plug_type:%d\n", __func__, type);
1733
1734 return type;
1735}
1736
Joonwoo Parka8890262012-10-15 12:04:27 -07001737static enum wcd9xxx_mbhc_plug_type
1738wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1739{
1740 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001741 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001742 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001743 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1744 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001745 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001746
Joonwoo Park80a01172012-10-15 16:05:23 -07001747 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001748 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1749
1750 /* make sure override is on */
1751 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1752
1753 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001754 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001755
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001756 /*
1757 * There are chances vddio switch is on and cfilt voltage is adjusted
1758 * to vddio voltage even after plug type removal reported.
1759 */
1760 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1761 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1762
Joonwoo Parka8890262012-10-15 12:04:27 -07001763 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001764 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001765
Joonwoo Parkccccba72013-04-26 11:19:46 -07001766 /*
1767 * cfilter in fast mode requires 1ms to charge up and down micbias
1768 * fully.
1769 */
1770 (void) wcd9xxx_pull_down_micbias(mbhc,
1771 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301772
1773 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001774 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001775 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001776 rt[0].swap_gnd = false;
1777 rt[0].vddio = false;
1778 rt[0].hwvalue = true;
1779 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1780 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1781 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001782 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001783 else
1784 rt[i].vddio = false;
1785 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1786 rt[i].hwvalue = false;
1787 if (rt[i].swap_gnd)
1788 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1789 if (rt[i].vddio)
1790 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001791 /*
1792 * Pull down micbias to detect headset with mic which has
1793 * threshold and to have more consistent voltage measurements.
1794 *
1795 * cfilter in fast mode requires 1ms to charge up and down
1796 * micbias fully.
1797 */
1798 (void) wcd9xxx_pull_down_micbias(mbhc,
1799 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001800 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1801 if (rt[i].vddio)
1802 wcd9xxx_onoff_vddio_switch(mbhc, false);
1803 if (rt[i].swap_gnd)
1804 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001805 }
1806
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001807 if (vddioon)
1808 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1809
1810 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1811 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001812
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301813 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001814 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001815 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001816}
1817
1818static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1819{
1820 if (mbhc->mbhc_cfg->gpio)
1821 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1822 mbhc->mbhc_cfg->gpio_level_insert);
1823 else if (mbhc->mbhc_cfg->insert_detect)
1824 return snd_soc_read(mbhc->codec,
1825 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1826 (1 << 2);
1827 else
1828 WARN(1, "Invalid jack detection configuration\n");
1829
1830 return true;
1831}
1832
1833static bool is_clk_active(struct snd_soc_codec *codec)
1834{
1835 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1836}
1837
1838static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1839 int insertion, int trigger, bool padac_off)
1840{
1841 struct snd_soc_codec *codec = mbhc->codec;
1842 int central_bias_enabled = 0;
1843 const struct wcd9xxx_mbhc_general_cfg *generic =
1844 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1845 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1846 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1847
Joonwoo Park80a01172012-10-15 16:05:23 -07001848 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1849 __func__, insertion, trigger);
1850
Joonwoo Parka8890262012-10-15 12:04:27 -07001851 if (!mbhc->mbhc_cfg->calibration) {
1852 pr_err("Error, no wcd9xxx calibration\n");
1853 return -EINVAL;
1854 }
1855
1856 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1857
1858 /*
1859 * Make sure mic bias and Mic line schmitt trigger
1860 * are turned OFF
1861 */
1862 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1863 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1864
1865 if (insertion) {
1866 wcd9xxx_switch_micbias(mbhc, 0);
1867
1868 /* DAPM can manipulate PA/DAC bits concurrently */
1869 if (padac_off == true)
1870 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1871
1872 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1873 /* Enable HPH Schmitt Trigger */
1874 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1875 0x11);
1876 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1877 plug_det->hph_current << 2);
1878 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1879 0x02);
1880 }
1881 if (trigger & MBHC_USE_MB_TRIGGER) {
1882 /* enable the mic line schmitt trigger */
1883 snd_soc_update_bits(codec,
1884 mbhc->mbhc_bias_regs.mbhc_reg,
1885 0x60, plug_det->mic_current << 5);
1886 snd_soc_update_bits(codec,
1887 mbhc->mbhc_bias_regs.mbhc_reg,
1888 0x80, 0x80);
1889 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1890 snd_soc_update_bits(codec,
1891 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1892 0x00);
1893 snd_soc_update_bits(codec,
1894 mbhc->mbhc_bias_regs.mbhc_reg,
1895 0x10, 0x10);
1896 }
1897
1898 /* setup for insetion detection */
1899 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1900 } else {
1901 pr_debug("setup for removal detection\n");
1902 /* Make sure the HPH schmitt trigger is OFF */
1903 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1904
1905 /* enable the mic line schmitt trigger */
1906 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1907 0x01, 0x00);
1908 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1909 plug_det->mic_current << 5);
1910 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1911 0x80, 0x80);
1912 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1913 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1914 0x10, 0x10);
1915
1916 /* Setup for low power removal detection */
1917 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1918 0x2);
1919 }
1920
1921 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1922 /* called by interrupt */
1923 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001924 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001925 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1926 0x06, 0);
1927 usleep_range(generic->t_shutdown_plug_rem,
1928 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001929 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001930 } else
1931 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1932 0x06, 0);
1933 }
1934
1935 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1936
1937 /* If central bandgap disabled */
1938 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1939 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1940 usleep_range(generic->t_bg_fast_settle,
1941 generic->t_bg_fast_settle);
1942 central_bias_enabled = 1;
1943 }
1944
1945 /* If LDO_H disabled */
1946 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1947 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1948 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1949 usleep_range(generic->t_ldoh, generic->t_ldoh);
1950 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1951
1952 if (central_bias_enabled)
1953 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1954 0);
1955 }
1956
Meng Wangeeaaaba2013-09-09 18:37:32 +08001957 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301958 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1959 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001960
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001961 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001962 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001963 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001964
1965 return 0;
1966}
1967
1968/* called under codec_resource_lock acquisition */
1969static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1970 enum wcd9xxx_mbhc_plug_type plug_type)
1971{
Joonwoo Park80a01172012-10-15 16:05:23 -07001972 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1973 __func__, mbhc->current_plug, plug_type);
1974
Joonwoo Parka8890262012-10-15 12:04:27 -07001975 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1976
1977 if (plug_type == PLUG_TYPE_HEADPHONE &&
1978 mbhc->current_plug == PLUG_TYPE_NONE) {
1979 /*
1980 * Nothing was reported previously
1981 * report a headphone or unsupported
1982 */
1983 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1984 wcd9xxx_cleanup_hs_polling(mbhc);
1985 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07001986 if (!mbhc->mbhc_cfg->detect_extn_cable) {
1987 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
1988 wcd9xxx_report_plug(mbhc, 0,
1989 SND_JACK_HEADSET);
1990 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
1991 wcd9xxx_report_plug(mbhc, 0,
1992 SND_JACK_HEADPHONE);
1993 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001994 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
1995 wcd9xxx_cleanup_hs_polling(mbhc);
1996 } else if (plug_type == PLUG_TYPE_HEADSET) {
1997 /*
1998 * If Headphone was reported previously, this will
1999 * only report the mic line
2000 */
2001 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302002 /* Button detection required RC oscillator */
2003 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002004 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002005
2006 /* if PA is already on, switch micbias source to VDDIO */
2007 if (mbhc->event_state &
2008 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2009 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002010 wcd9xxx_start_hs_polling(mbhc);
2011 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002012 if (mbhc->mbhc_cfg->detect_extn_cable) {
2013 /* High impedance device found. Report as LINEOUT*/
2014 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2015 wcd9xxx_cleanup_hs_polling(mbhc);
2016 pr_debug("%s: setup mic trigger for further detection\n",
2017 __func__);
2018 mbhc->lpi_enabled = true;
2019 /*
2020 * Do not enable HPHL trigger. If playback is active,
2021 * it might lead to continuous false HPHL triggers
2022 */
2023 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2024 false);
2025 } else {
2026 if (mbhc->current_plug == PLUG_TYPE_NONE)
2027 wcd9xxx_report_plug(mbhc, 1,
2028 SND_JACK_HEADPHONE);
2029 wcd9xxx_cleanup_hs_polling(mbhc);
2030 pr_debug("setup mic trigger for further detection\n");
2031 mbhc->lpi_enabled = true;
2032 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2033 MBHC_USE_HPHL_TRIGGER,
2034 false);
2035 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002036 } else {
2037 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2038 mbhc->current_plug, plug_type);
2039 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002040 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002041}
2042
2043/* called under codec_resource_lock acquisition */
2044static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2045{
2046 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002047 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002048
2049 pr_debug("%s: enter\n", __func__);
2050
2051 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002052 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2053 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002054
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002055 if (current_source_enable) {
2056 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2057 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2058 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2059 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002060 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002061 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002062 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002063 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002064
2065 if (wcd9xxx_swch_level_remove(mbhc)) {
2066 pr_debug("%s: Switch level is low when determining plug\n",
2067 __func__);
2068 return;
2069 }
2070
2071 if (plug_type == PLUG_TYPE_INVALID ||
2072 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002073 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002074 wcd9xxx_schedule_hs_detect_plug(mbhc,
2075 &mbhc->correct_plug_swch);
2076 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2077 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002078 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002079 wcd9xxx_schedule_hs_detect_plug(mbhc,
2080 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002081 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002082 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002083 wcd9xxx_schedule_hs_detect_plug(mbhc,
2084 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002085 } else {
2086 pr_debug("%s: Valid plug found, determine plug type %d\n",
2087 __func__, plug_type);
2088 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2089 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002090 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002091}
2092
2093/* called under codec_resource_lock acquisition */
2094static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2095{
Joonwoo Park80a01172012-10-15 16:05:23 -07002096 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002097 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2098
Joonwoo Parka8890262012-10-15 12:04:27 -07002099 if (wcd9xxx_swch_level_remove(mbhc))
2100 pr_debug("%s: Switch level low when determining plug\n",
2101 __func__);
2102 else
2103 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002104 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002105}
2106
2107/* called only from interrupt which is under codec_resource_lock acquisition */
2108static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2109 bool is_removal)
2110{
2111 if (!is_removal) {
2112 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2113
2114 rmb();
2115 if (mbhc->lpi_enabled)
2116 msleep(100);
2117
2118 rmb();
2119 if (!mbhc->lpi_enabled) {
2120 pr_debug("%s: lpi is disabled\n", __func__);
2121 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2122 pr_debug("%s: Valid insertion, detect plug type\n",
2123 __func__);
2124 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2125 } else {
2126 pr_debug("%s: Invalid insertion stop plug detection\n",
2127 __func__);
2128 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002129 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2130 pr_debug("%s: Removal\n", __func__);
2131 if (!wcd9xxx_swch_level_remove(mbhc)) {
2132 /*
2133 * Switch indicates, something is still inserted.
2134 * This could be extension cable i.e. headset is
2135 * removed from extension cable.
2136 */
2137 /* cancel detect plug */
2138 wcd9xxx_cancel_hs_detect_plug(mbhc,
2139 &mbhc->correct_plug_swch);
2140 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2141 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002142 } else {
2143 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2144 }
2145}
2146
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002147static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2148 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002149{
2150 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2151 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2152 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2153
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002154 if (cs_enable)
2155 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2156 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2157 else
2158 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2159 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2160 (mic_mv > plug_type->v_no_mic) &&
2161 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002162}
2163
2164/*
2165 * called under codec_resource_lock acquisition
2166 * returns true if mic voltage range is back to normal insertion
2167 * returns false either if timedout or removed
2168 */
2169static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2170{
2171 int i;
2172 bool timedout, settled = false;
2173 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2174 short mb_v[NUM_DCE_PLUG_DETECT];
2175 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002176 bool cs_enable;
2177
2178 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2179 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2180
2181 if (cs_enable)
2182 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002183
2184 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2185 while (!(timedout = time_after(jiffies, timeout))) {
2186 retry++;
2187 if (wcd9xxx_swch_level_remove(mbhc)) {
2188 pr_debug("%s: Switch indicates removal\n", __func__);
2189 break;
2190 }
2191
2192 if (retry > 1)
2193 msleep(250);
2194 else
2195 msleep(50);
2196
2197 if (wcd9xxx_swch_level_remove(mbhc)) {
2198 pr_debug("%s: Switch indicates removal\n", __func__);
2199 break;
2200 }
2201
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002202 if (cs_enable) {
2203 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2204 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2205 true, true);
2206 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2207 true,
2208 mb_v[i],
2209 mbhc->mbhc_data.dce_nsc_cs_z,
2210 (u32)VDDIO_MICBIAS_MV);
2211 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2212 __func__, retry, mic_mv[i], mb_v[i]);
2213 }
2214 } else {
2215 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2216 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2217 true);
2218 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2219 mb_v[i]);
2220 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2221 __func__, retry, mic_mv[i],
2222 mb_v[i]);
2223 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002224 }
2225
2226 if (wcd9xxx_swch_level_remove(mbhc)) {
2227 pr_debug("%s: Switcn indicates removal\n", __func__);
2228 break;
2229 }
2230
2231 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2232 pr_debug("%s : headset/headphone is removed\n",
2233 __func__);
2234 break;
2235 }
2236
2237 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002238 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002239 break;
2240
2241 if (i == NUM_DCE_PLUG_DETECT) {
2242 pr_debug("%s: MIC voltage settled\n", __func__);
2243 settled = true;
2244 msleep(200);
2245 break;
2246 }
2247 }
2248
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002249 if (cs_enable)
2250 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2251
Joonwoo Parka8890262012-10-15 12:04:27 -07002252 if (timedout)
2253 pr_debug("%s: Microphone did not settle in %d seconds\n",
2254 __func__, HS_DETECT_PLUG_TIME_MS);
2255 return settled;
2256}
2257
2258/* called only from interrupt which is under codec_resource_lock acquisition */
2259static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2260{
Joonwoo Park80a01172012-10-15 16:05:23 -07002261 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002262 if (wcd9xxx_hs_remove_settle(mbhc))
2263 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002264 pr_debug("%s: leave\n", __func__);
2265}
2266
2267/* called only from interrupt which is under codec_resource_lock acquisition */
2268static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2269{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002270 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002271 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002272 bool removed = true;
2273 struct snd_soc_codec *codec = mbhc->codec;
2274 const struct wcd9xxx_mbhc_general_cfg *generic =
2275 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002276 bool cs_enable;
2277 s16 cur_v_ins_h;
2278 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002279
2280 pr_debug("%s: enter\n", __func__);
2281 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2282 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2283 __func__);
2284 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2285 0x08, 0x08);
2286 return;
2287 }
2288
2289 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002290 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002291
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002292 /* If micbias is enabled, don't enable current source */
2293 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2294 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2295 (!(snd_soc_read(codec,
2296 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002297 if (cs_enable)
2298 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2299
Joonwoo Park50ae0512013-06-04 16:53:12 -07002300 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002301 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002302 if (cs_enable) {
2303 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2304 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2305 mb_mv = VDDIO_MICBIAS_MV;
2306 } else {
2307 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2308 dcez = mbhc->mbhc_data.dce_z;
2309 mb_mv = mbhc->mbhc_data.micb_mv;
2310 }
2311
Joonwoo Park50ae0512013-06-04 16:53:12 -07002312 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002313 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2314 dcez, mb_mv));
2315
2316 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2317 (wcd9xxx_get_current_v(mbhc,
2318 WCD9XXX_CURRENT_V_INS_H));
2319
2320 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002321 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002322 break;
2323 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002324 } while (!time_after(jiffies, timeout));
2325 pr_debug("%s: headset %sactually removed\n", __func__,
2326 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002327
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002328 if (cs_enable)
2329 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2330
Joonwoo Park80a01172012-10-15 16:05:23 -07002331 if (removed) {
2332 if (mbhc->mbhc_cfg->detect_extn_cable) {
2333 if (!wcd9xxx_swch_level_remove(mbhc)) {
2334 /*
2335 * extension cable is still plugged in
2336 * report it as LINEOUT device
2337 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302338 if (mbhc->hph_status == SND_JACK_HEADSET)
2339 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2340 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002341 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2342 wcd9xxx_cleanup_hs_polling(mbhc);
2343 wcd9xxx_enable_hs_detect(mbhc, 1,
2344 MBHC_USE_MB_TRIGGER,
2345 false);
2346 }
2347 } else {
2348 /* Cancel possibly running hs_detect_work */
2349 wcd9xxx_cancel_hs_detect_plug(mbhc,
2350 &mbhc->correct_plug_noswch);
2351 /*
2352 * If this removal is not false, first check the micbias
2353 * switch status and switch it to LDOH if it is already
2354 * switched to VDDIO.
2355 */
2356 wcd9xxx_switch_micbias(mbhc, 0);
2357
2358 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302359 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002360 wcd9xxx_cleanup_hs_polling(mbhc);
2361 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2362 MBHC_USE_HPHL_TRIGGER,
2363 true);
2364 }
2365 } else {
2366 wcd9xxx_start_hs_polling(mbhc);
2367 }
2368 pr_debug("%s: leave\n", __func__);
2369}
2370
2371/* called only from interrupt which is under codec_resource_lock acquisition */
2372static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2373 bool is_mb_trigger)
2374{
2375 /* Cancel possibly running hs_detect_work */
2376 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2377
2378 if (is_mb_trigger) {
2379 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2380 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2381 } else {
2382 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2383 __func__);
2384 wcd9xxx_mbhc_detect_plug_type(mbhc);
2385 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002386}
2387
2388static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2389{
Joonwoo Parka8890262012-10-15 12:04:27 -07002390 struct wcd9xxx_mbhc *mbhc = data;
2391
2392 pr_debug("%s: enter, removal interrupt\n", __func__);
2393 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002394 /*
2395 * While we don't know whether MIC is there or not, let the resmgr know
2396 * so micbias can be disabled temporarily
2397 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002398 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002399 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2400 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002401 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2402 WCD9XXX_COND_HPH, false);
2403 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2404 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2405 WCD9XXX_COND_HPH, false);
2406 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002407
Joonwoo Park80a01172012-10-15 16:05:23 -07002408 if (mbhc->mbhc_cfg->detect_extn_cable &&
2409 !wcd9xxx_swch_level_remove(mbhc))
2410 wcd9xxx_hs_remove_irq_noswch(mbhc);
2411 else
2412 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002413
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002414 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2415 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2416 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002417 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2418 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002419 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2420 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2421 WCD9XXX_COND_HPH, true);
2422 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002423 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2424
2425 return IRQ_HANDLED;
2426}
2427
2428static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2429{
2430 bool is_mb_trigger, is_removal;
2431 struct wcd9xxx_mbhc *mbhc = data;
2432 struct snd_soc_codec *codec = mbhc->codec;
2433
2434 pr_debug("%s: enter\n", __func__);
2435 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002436 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002437
2438 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2439 0x10);
2440 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2441 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2442
2443 /* Turn off both HPH and MIC line schmitt triggers */
2444 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2445 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2446 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2447
Joonwoo Park80a01172012-10-15 16:05:23 -07002448 if (mbhc->mbhc_cfg->detect_extn_cable &&
2449 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2450 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2451 else
2452 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002453
2454 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2455 return IRQ_HANDLED;
2456}
2457
2458static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2459{
2460 struct delayed_work *dwork;
2461 short bias_value;
2462 int dce_mv, sta_mv;
2463 struct wcd9xxx_mbhc *mbhc;
2464
2465 pr_debug("%s:\n", __func__);
2466
2467 dwork = to_delayed_work(work);
2468 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2469
2470 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2471 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2472
2473 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2474 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2475 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2476
2477 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002478 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002479 mbhc->buttons_pressed);
2480
2481 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002482 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002483}
2484
2485static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2486{
2487 struct delayed_work *dwork;
2488 struct wcd9xxx_mbhc *mbhc;
2489 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002490 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002491
2492 dwork = to_delayed_work(work);
2493 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2494 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002495 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002496
2497 pr_debug("%s:\n", __func__);
2498
2499 /* Turn off both HPH and MIC line schmitt triggers */
2500 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2501 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2502 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002503 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002504 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002505 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002506}
2507
2508static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2509{
2510 u32 cfg_offset;
2511 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2512 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2513
2514 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2515 return false;
2516
2517 /*
2518 * Previous check guarantees that there is enough fw data up
2519 * to num_btn
2520 */
2521 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2522 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2523 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2524 return false;
2525
2526 /*
2527 * Previous check guarantees that there is enough fw data up
2528 * to start of impedance detection configuration
2529 */
2530 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2531 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2532
2533 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2534 return false;
2535
2536 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2537 return false;
2538
2539 return true;
2540}
2541
2542static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002543 enum meas_type dce, s16 vin_mv,
2544 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002545{
2546 s16 diff, zero;
2547 u32 mb_mv, in;
2548 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002549 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002550
2551 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002552 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002553
2554 if (mb_mv == 0) {
2555 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2556 return -EINVAL;
2557 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002558 if (cs_enable) {
2559 mb_mv = VDDIO_MICBIAS_MV;
2560 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2561 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002562
2563 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002564 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2565 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002566 } else {
2567 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2568 zero = (mbhc->mbhc_data.sta_z);
2569 }
2570 in = (u32) diff * vin_mv;
2571
2572 value = (u16) (in / mb_mv) + zero;
2573 return value;
2574}
2575
2576static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2577{
2578 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002579 s16 adj_v_hs_max;
2580 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 -07002581 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2582 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2583 u16 *btn_high;
2584 int i;
2585
2586 pr_debug("%s: enter\n", __func__);
2587 codec = mbhc->codec;
2588 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2589 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2590
Joonwoo Park73375212013-05-07 12:42:44 -07002591 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002592 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002593 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002594 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002595
2596 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2597 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2598
2599 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002600 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2601 true);
2602 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002603 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002604 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002605 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002606 mbhc->mbhc_data.v_inval_ins_low =
2607 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2608 false);
2609 mbhc->mbhc_data.v_inval_ins_high =
2610 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2611 false);
2612 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002613 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2614 WCD9XXX_V_CS_HS_MAX,
2615 true);
2616 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2617 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002618
2619 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2620 MBHC_BTN_DET_V_BTN_HIGH);
2621 for (i = 0; i < btn_det->num_btn; i++)
2622 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2623
Joonwoo Park73375212013-05-07 12:42:44 -07002624 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2625 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2626 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2627 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2628 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2629 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002630
Joonwoo Park73375212013-05-07 12:42:44 -07002631 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002632 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2633 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002634 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002635 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2636 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002637 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002638 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2639 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002640 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002641 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2642 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002643
Joonwoo Park73375212013-05-07 12:42:44 -07002644 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2645 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2646 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2647 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002648
Joonwoo Parka8890262012-10-15 12:04:27 -07002649 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2650
2651 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002652 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002653 pr_debug("%s: leave\n", __func__);
2654}
2655
2656static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2657{
2658 /*
2659 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2660 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2661 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2662 */
2663 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2664}
2665
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002666/*
2667 * Mic Bias Enable Decision
2668 * Return true if high_hph_cnt is a power of 2 (!= 2)
2669 * otherwise return false
2670 */
2671static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2672{
2673 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2674}
2675
Joonwoo Parka8890262012-10-15 12:04:27 -07002676static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2677{
2678 struct wcd9xxx_mbhc *mbhc;
2679 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002680 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002681 unsigned long timeout;
2682 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002683 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002684 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002685 bool current_source_enable;
2686 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002687
2688 pr_debug("%s: enter\n", __func__);
2689
2690 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2691 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002692 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2693 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002694
2695 wcd9xxx_onoff_ext_mclk(mbhc, true);
2696
2697 /*
2698 * Keep override on during entire plug type correction work.
2699 *
2700 * This is okay under the assumption that any switch irqs which use
2701 * MBHC block cancel and sync this work so override is off again
2702 * prior to switch interrupt handler's MBHC block usage.
2703 * Also while this correction work is running, we can guarantee
2704 * DAPM doesn't use any MBHC block as this work only runs with
2705 * headphone detection.
2706 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002707 if (current_source_enable)
2708 wcd9xxx_turn_onoff_current_source(mbhc, true,
2709 false);
2710 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002711 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002712
2713 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2714 while (!time_after(jiffies, timeout)) {
2715 ++retry;
2716 rmb();
2717 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002718 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002719 pr_debug("%s: stop requested\n", __func__);
2720 break;
2721 }
2722
2723 msleep(HS_DETECT_PLUG_INERVAL_MS);
2724 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002725 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002726 pr_debug("%s: Switch level is low\n", __func__);
2727 break;
2728 }
2729
2730 /* can race with removal interrupt */
2731 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002732 if (current_source_enable)
2733 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2734 highhph);
2735 else
2736 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002737 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2738
Joonwoo Park80a01172012-10-15 16:05:23 -07002739 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2740 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002741
2742 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2743 (highhph_cnt + 1) :
2744 0;
2745 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002746 if (plug_type == PLUG_TYPE_INVALID) {
2747 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002748 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2749 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002750 mbhc->current_plug == PLUG_TYPE_NONE) {
2751 wcd9xxx_report_plug(mbhc, 1,
2752 SND_JACK_HEADPHONE);
2753 }
2754 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2755 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002756 if (mbhc->mbhc_cfg->detect_extn_cable) {
2757 if (mbhc->current_plug != plug_type)
2758 wcd9xxx_report_plug(mbhc, 1,
2759 SND_JACK_HEADPHONE);
2760 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002761 wcd9xxx_report_plug(mbhc, 1,
2762 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002763 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002764 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2765 pr_debug("%s: High HPH detected, continue polling\n",
2766 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002767 } else {
2768 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2769 pt_gnd_mic_swap_cnt++;
2770 if (pt_gnd_mic_swap_cnt <
2771 GND_MIC_SWAP_THRESHOLD)
2772 continue;
2773 else if (pt_gnd_mic_swap_cnt >
2774 GND_MIC_SWAP_THRESHOLD) {
2775 /*
2776 * This is due to GND/MIC switch didn't
2777 * work, Report unsupported plug
2778 */
2779 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2780 /*
2781 * if switch is toggled, check again,
2782 * otherwise report unsupported plug
2783 */
2784 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2785 continue;
2786 }
2787 } else
2788 pt_gnd_mic_swap_cnt = 0;
2789
2790 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002791 /* Turn off override/current source */
2792 if (current_source_enable)
2793 wcd9xxx_turn_onoff_current_source(mbhc, false,
2794 false);
2795 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002796 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002797 /*
2798 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2799 */
2800 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2801 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2802 pr_debug("Attempt %d found correct plug %d\n", retry,
2803 plug_type);
2804 correction = true;
2805 break;
2806 }
2807 }
2808
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002809 highhph = false;
2810 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002811 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2812 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002813 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002814 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002815 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002816 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002817 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002818
2819 if (!correction && current_source_enable)
2820 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2821 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002822 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002823
2824 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002825
2826 if (mbhc->mbhc_cfg->detect_extn_cable) {
2827 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002828 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2829 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002830 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2831 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002832 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002833 /* Enable removal detection */
2834 wcd9xxx_cleanup_hs_polling(mbhc);
2835 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2836 }
2837 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2838 }
2839 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002840 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002841 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002842}
2843
2844static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2845{
2846 bool insert;
2847 bool is_removed = false;
2848 struct snd_soc_codec *codec = mbhc->codec;
2849
2850 pr_debug("%s: enter\n", __func__);
2851
2852 mbhc->in_swch_irq_handler = true;
2853 /* Wait here for debounce time */
2854 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2855
2856 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2857
2858 /* cancel pending button press */
2859 if (wcd9xxx_cancel_btn_work(mbhc))
2860 pr_debug("%s: button press is canceled\n", __func__);
2861
Santosh Mardic82bd362013-10-25 18:35:29 +05302862 /* cancel detect plug */
2863 wcd9xxx_cancel_hs_detect_plug(mbhc,
2864 &mbhc->correct_plug_swch);
2865
Joonwoo Parka8890262012-10-15 12:04:27 -07002866 insert = !wcd9xxx_swch_level_remove(mbhc);
2867 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2868 mbhc->current_plug, insert);
2869 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2870 mbhc->lpi_enabled = false;
2871 wmb();
2872
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002873 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2874 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2875 (1 << 1)))
2876 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002877
2878 /* Disable Mic Bias pull down and HPH Switch to GND */
2879 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2880 0x00);
2881 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2882 wcd9xxx_mbhc_detect_plug_type(mbhc);
2883 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2884 mbhc->lpi_enabled = false;
2885 wmb();
2886
Joonwoo Parka8890262012-10-15 12:04:27 -07002887 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2888 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2889 is_removed = true;
2890 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2891 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2892 is_removed = true;
2893 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2894 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302895 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002896 wcd9xxx_cleanup_hs_polling(mbhc);
2897 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2898 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002899 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2900 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2901 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002902 }
2903
2904 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002905 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2906 0x00);
2907 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2908 0x02, 0x00);
2909
Joonwoo Parka8890262012-10-15 12:04:27 -07002910 /* Enable Mic Bias pull down and HPH Switch to GND */
2911 snd_soc_update_bits(codec,
2912 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2913 0x01);
2914 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2915 0x01);
2916 /* Make sure mic trigger is turned off */
2917 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2918 0x01, 0x01);
2919 snd_soc_update_bits(codec,
2920 mbhc->mbhc_bias_regs.mbhc_reg,
2921 0x90, 0x00);
2922 /* Reset MBHC State Machine */
2923 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2924 0x08, 0x08);
2925 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2926 0x08, 0x00);
2927 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002928 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002929 }
2930 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002931exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002932 mbhc->in_swch_irq_handler = false;
2933 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2934 pr_debug("%s: leave\n", __func__);
2935}
2936
2937static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2938{
2939 int r = IRQ_HANDLED;
2940 struct wcd9xxx_mbhc *mbhc = data;
2941
2942 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002943 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002944 pr_warn("%s: failed to hold suspend\n", __func__);
2945 r = IRQ_NONE;
2946 } else {
2947 /* Call handler */
2948 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002949 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002950 }
2951
2952 pr_debug("%s: leave %d\n", __func__, r);
2953 return r;
2954}
2955
Joonwoo Park218e73f2013-08-21 16:22:18 -07002956static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002957{
Joonwoo Park73375212013-05-07 12:42:44 -07002958 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002959 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002960 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002961 const s16 v_ins_hu =
2962 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2963 const s16 v_ins_h =
2964 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2965 const s16 v_b1_hu =
2966 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2967 const s16 v_b1_h =
2968 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002969 const unsigned long timeout =
2970 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002971
Joonwoo Park218e73f2013-08-21 16:22:18 -07002972 while (time_before(jiffies, timeout)) {
2973 /*
2974 * This function needs to run measurements just few times during
2975 * release debounce time. Make 1ms interval to avoid
2976 * unnecessary excessive measurements.
2977 */
2978 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002979 if (i == 0) {
2980 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2981 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2982 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002983 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002984 r = 1;
2985 break;
2986 }
2987 } else {
2988 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2989 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
2990 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002991 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002992 r = 1;
2993 break;
2994 }
2995 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07002996 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07002997 }
2998
2999 return r;
3000}
3001
3002/* called under codec_resource_lock acquisition */
3003static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3004 const s32 micmv)
3005{
3006 s16 *v_btn_low, *v_btn_high;
3007 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3008 int i, btn = -1;
3009
3010 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3011 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3012 MBHC_BTN_DET_V_BTN_LOW);
3013 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3014 MBHC_BTN_DET_V_BTN_HIGH);
3015
3016 for (i = 0; i < btn_det->num_btn; i++) {
3017 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3018 btn = i;
3019 break;
3020 }
3021 }
3022
3023 if (btn == -1)
3024 pr_debug("%s: couldn't find button number for mic mv %d\n",
3025 __func__, micmv);
3026
3027 return btn;
3028}
3029
3030static int wcd9xxx_get_button_mask(const int btn)
3031{
3032 int mask = 0;
3033 switch (btn) {
3034 case 0:
3035 mask = SND_JACK_BTN_0;
3036 break;
3037 case 1:
3038 mask = SND_JACK_BTN_1;
3039 break;
3040 case 2:
3041 mask = SND_JACK_BTN_2;
3042 break;
3043 case 3:
3044 mask = SND_JACK_BTN_3;
3045 break;
3046 case 4:
3047 mask = SND_JACK_BTN_4;
3048 break;
3049 case 5:
3050 mask = SND_JACK_BTN_5;
3051 break;
3052 case 6:
3053 mask = SND_JACK_BTN_6;
3054 break;
3055 case 7:
3056 mask = SND_JACK_BTN_7;
3057 break;
3058 }
3059 return mask;
3060}
3061
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003062static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003063{
3064 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003065 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003066 struct snd_soc_codec *codec = mbhc->codec;
3067
3068 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3069 /* Pull down micbias to ground and disconnect vddio switch */
3070 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3071 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3072 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3073 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3074
3075 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003076 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3077 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003078 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003079 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003080 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003081 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3082 }
3083 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003084 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003085 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3086 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003087
Joonwoo Park520a0f92013-05-14 19:39:58 -07003088 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003089 if (change)
3090 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3091 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003092 /* Disable pull down micbias to ground */
3093 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3094 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3095}
3096
Joonwoo Park218e73f2013-08-21 16:22:18 -07003097void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3098{
3099 const u16 sta_z = mbhc->mbhc_data.sta_z;
3100 const u16 dce_z = mbhc->mbhc_data.dce_z;
3101
3102 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3103 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3104 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3105 mbhc->mbhc_data.sta_z & 0xFFFF,
3106 mbhc->mbhc_data.dce_z & 0xFFFF);
3107
3108 wcd9xxx_mbhc_calc_thres(mbhc);
3109 wcd9xxx_calibrate_hs_polling(mbhc);
3110}
3111
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003112/*
3113 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3114 * to ceilmv + buffer
3115 */
3116static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3117{
3118 u16 v_brh, v_b1_hu;
3119 int mv;
3120 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3121 void *calibration = mbhc->mbhc_cfg->calibration;
3122 struct snd_soc_codec *codec = mbhc->codec;
3123
3124 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3125 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3126 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3127
3128 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003129 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003130 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3131 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3132 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003133 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003134 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3135 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3136 (v_brh >> 8) & 0xFF);
3137 return 0;
3138}
3139
Joonwoo Parka8890262012-10-15 12:04:27 -07003140irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3141{
3142 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003143 bool vddio;
3144 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003145 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003146 s32 stamv, stamv_s;
3147 s16 *v_btn_high;
3148 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003149 int btn = -1, meas = 0;
3150 struct wcd9xxx_mbhc *mbhc = data;
3151 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3152 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3153 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003154 short dce[d->n_btn_meas + 1], sta;
3155 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003156 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003157 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003158 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003159 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003160
3161 pr_debug("%s: enter\n", __func__);
3162
3163 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3164 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3165
3166 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3167 pr_debug("%s: mbhc is being recovered, skip button press\n",
3168 __func__);
3169 goto done;
3170 }
3171
3172 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3173
3174 if (!mbhc->polling_active) {
3175 pr_warn("%s: mbhc polling is not active, skip button press\n",
3176 __func__);
3177 goto done;
3178 }
3179
Joonwoo Parka8890262012-10-15 12:04:27 -07003180 /* If switch nterrupt already kicked in, ignore button press */
3181 if (mbhc->in_swch_irq_handler) {
3182 pr_debug("%s: Swtich level changed, ignore button press\n",
3183 __func__);
3184 btn = -1;
3185 goto done;
3186 }
3187
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303188 /*
3189 * setup internal micbias if codec uses internal micbias for
3190 * headset detection
3191 */
3192 if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
3193 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3194 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3195 else
3196 pr_err("%s: internal bias requested but codec did not provide callback\n",
3197 __func__);
3198 }
3199
3200
Joonwoo Parka8890262012-10-15 12:04:27 -07003201 /* Measure scaled HW DCE */
3202 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3203 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003204
Joonwoo Park218e73f2013-08-21 16:22:18 -07003205 dce_z = mbhc->mbhc_data.dce_z;
3206 sta_z = mbhc->mbhc_data.sta_z;
3207
Joonwoo Parka8890262012-10-15 12:04:27 -07003208 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003209 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003210 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003211 if (mbhc_status != STATUS_REL_DETECTION) {
3212 if (mbhc->mbhc_last_resume &&
3213 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3214 pr_debug("%s: Button is released after resume\n",
3215 __func__);
3216 n_btn_meas = 0;
3217 } else {
3218 pr_debug("%s: Button is released without resume",
3219 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003220 if (mbhc->update_z) {
3221 wcd9xxx_update_z(mbhc);
3222 mbhc->update_z = false;
3223 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003224 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3225 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003226 if (vddio)
3227 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3228 false);
3229 else
3230 stamv_s = stamv;
3231 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003232 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003233 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3234 false) : mv[0];
3235 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003236 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3237 btn = -1;
3238 goto done;
3239 }
3240 }
3241
Joonwoo Park520a0f92013-05-14 19:39:58 -07003242 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3243 meas++)
3244 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3245
Joonwoo Park218e73f2013-08-21 16:22:18 -07003246 if (mbhc->update_z) {
3247 wcd9xxx_update_z(mbhc);
3248 mbhc->update_z = false;
3249 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003250
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003251 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3252 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003253 if (vddio)
3254 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3255 else
3256 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003257 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003258 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003259
3260 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003261 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3262 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003263 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3264 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003265 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003266 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003267 if (n_btn_meas == 0)
3268 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003269 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3270 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003271 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3272 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003273 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3274 mv[meas];
3275 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003276 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003277 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3278 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003279 /*
3280 * if large enough measurements are collected,
3281 * start to check if last all n_btn_con measurements were
3282 * in same button low/high range
3283 */
3284 if (meas + 1 >= d->n_btn_con) {
3285 for (i = 0; i < d->n_btn_con; i++)
3286 if ((btnmeas[meas] < 0) ||
3287 (btnmeas[meas] != btnmeas[meas - i]))
3288 break;
3289 if (i == d->n_btn_con) {
3290 /* button pressed */
3291 btn = btnmeas[meas];
3292 break;
3293 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3294 /*
3295 * if left measurements are less than n_btn_con,
3296 * it's impossible to find button number
3297 */
3298 break;
3299 }
3300 }
3301 }
3302
3303 if (btn >= 0) {
3304 if (mbhc->in_swch_irq_handler) {
3305 pr_debug(
3306 "%s: Switch irq triggered, ignore button press\n",
3307 __func__);
3308 goto done;
3309 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003310 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3311 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3312 MBHC_BTN_DET_V_BTN_HIGH);
3313 WARN_ON(btn >= btn_det->num_btn);
3314 /* reprogram release threshold to catch voltage ramp up early */
3315 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3316
Joonwoo Parka8890262012-10-15 12:04:27 -07003317 mask = wcd9xxx_get_button_mask(btn);
3318 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003319 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003320 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3321 msecs_to_jiffies(400)) == 0) {
3322 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003323 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003324 }
3325 } else {
3326 pr_debug("%s: bogus button press, too short press?\n",
3327 __func__);
3328 }
3329
3330 done:
3331 pr_debug("%s: leave\n", __func__);
3332 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3333 return IRQ_HANDLED;
3334}
3335
3336static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3337{
3338 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003339 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003340 struct wcd9xxx_mbhc *mbhc = data;
3341
3342 pr_debug("%s: enter\n", __func__);
3343 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3344 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3345
Joonwoo Parka8890262012-10-15 12:04:27 -07003346 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3347 ret = wcd9xxx_cancel_btn_work(mbhc);
3348 if (ret == 0) {
3349 pr_debug("%s: Reporting long button release event\n",
3350 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003351 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003352 mbhc->buttons_pressed);
3353 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003354 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003355 pr_debug("%s: Fake button press interrupt\n",
3356 __func__);
3357 } else {
3358 if (mbhc->in_swch_irq_handler) {
3359 pr_debug("%s: Switch irq kicked in, ignore\n",
3360 __func__);
3361 } else {
3362 pr_debug("%s: Reporting btn press\n",
3363 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003364 wcd9xxx_jack_report(mbhc,
3365 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003366 mbhc->buttons_pressed,
3367 mbhc->buttons_pressed);
3368 pr_debug("%s: Reporting btn release\n",
3369 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003370 wcd9xxx_jack_report(mbhc,
3371 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003372 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003373 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003374 }
3375 }
3376 }
3377
3378 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3379 }
3380
3381 wcd9xxx_calibrate_hs_polling(mbhc);
3382
Joonwoo Park218e73f2013-08-21 16:22:18 -07003383 if (waitdebounce)
3384 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003385 wcd9xxx_start_hs_polling(mbhc);
3386
3387 pr_debug("%s: leave\n", __func__);
3388 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3389 return IRQ_HANDLED;
3390}
3391
3392static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3393{
3394 struct wcd9xxx_mbhc *mbhc = data;
3395 struct snd_soc_codec *codec;
3396
3397 pr_info("%s: received HPHL OCP irq\n", __func__);
3398
3399 if (mbhc) {
3400 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003401 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3402 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003403 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003404 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003405 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3406 0x10, 0x00);
3407 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3408 0x10, 0x10);
3409 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003410 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003411 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003412 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003413 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003414 mbhc->hph_status,
3415 WCD9XXX_JACK_MASK);
3416 }
3417 } else {
3418 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3419 }
3420
3421 return IRQ_HANDLED;
3422}
3423
3424static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3425{
3426 struct wcd9xxx_mbhc *mbhc = data;
3427 struct snd_soc_codec *codec;
3428
3429 pr_info("%s: received HPHR OCP irq\n", __func__);
3430 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003431 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3432 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003433 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003434 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003435 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3436 0x00);
3437 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3438 0x10);
3439 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003440 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003441 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003442 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003443 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003444 mbhc->hph_status, WCD9XXX_JACK_MASK);
3445 }
3446
3447 return IRQ_HANDLED;
3448}
3449
3450static int wcd9xxx_acdb_mclk_index(const int rate)
3451{
3452 if (rate == MCLK_RATE_12288KHZ)
3453 return 0;
3454 else if (rate == MCLK_RATE_9600KHZ)
3455 return 1;
3456 else {
3457 BUG_ON(1);
3458 return -EINVAL;
3459 }
3460}
3461
3462static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3463{
3464 u32 dce_wait, sta_wait;
3465 u8 ncic, nmeas, navg;
3466 void *calibration;
3467 u8 *n_cic, *n_ready;
3468 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3469 u8 npoll = 4, nbounce_wait = 30;
3470 struct snd_soc_codec *codec = mbhc->codec;
3471 int idx = wcd9xxx_acdb_mclk_index(rate);
3472 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3473
3474 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3475 rate);
3476 calibration = mbhc->mbhc_cfg->calibration;
3477
3478 /*
3479 * First compute the DCE / STA wait times depending on tunable
3480 * parameters. The value is computed in microseconds
3481 */
3482 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3483 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3484 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3485 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3486 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3487
3488 /* ncic stays with the same what we had during calibration */
3489 ncic = n_cic[idxmclk];
3490 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3491 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3492 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003493 /* give extra margin to sta for safety */
3494 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003495 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3496 n_ready[idx]) + 10;
3497
3498 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3499 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3500
3501 if (rate == MCLK_RATE_12288KHZ) {
3502 npoll = 4;
3503 nbounce_wait = 30;
3504 } else if (rate == MCLK_RATE_9600KHZ) {
3505 npoll = 3;
3506 nbounce_wait = 23;
3507 }
3508
3509 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3510 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3511 pr_debug("%s: leave\n", __func__);
3512}
3513
3514static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3515{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003516 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003517 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003518 struct snd_soc_codec *codec = mbhc->codec;
3519
3520 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003521 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3522 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003523 wcd9xxx_turn_onoff_rel_detection(codec, false);
3524
3525 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3526 WARN_ON(!mbhc->mbhc_data.t_dce);
3527 WARN_ON(!mbhc->mbhc_data.t_sta);
3528
3529 /*
3530 * LDOH and CFILT are already configured during pdata handling.
3531 * Only need to make sure CFILT and bandgap are in Fast mode.
3532 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003533 *
3534 * In case when Micbias is powered by external source, request
3535 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003536 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003537 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3538 mbhc->mbhc_cb->enable_mb_source(codec, true);
3539
Joonwoo Parka8890262012-10-15 12:04:27 -07003540 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303541 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3542 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3543 else
3544 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3545 0x40, 0x00);
3546
Joonwoo Parka8890262012-10-15 12:04:27 -07003547 /*
3548 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3549 * to perform ADC calibration
3550 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303551 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3552 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3553 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003554 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3555 mbhc->mbhc_cfg->micbias << 5);
3556 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3557 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3558 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303559 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3560 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3561 else
3562 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3563 0x04, 0x04);
3564
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003565 /* Pull down micbias to ground */
3566 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3567 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3568 /* Disconnect override from micbias */
3569 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3570 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3571 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303572 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303573 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3574 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3575 else
3576 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3577 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003578 /*
3579 * Hardware that has external cap can delay mic bias ramping down up
3580 * to 50ms.
3581 */
3582 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003583 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003584 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003585 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003586 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003587
3588 /* compute dce_z for current source */
3589 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3590 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3591 WCD9XXX_MBHC_NSC_CS << 3);
3592
3593 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3594 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3595 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3596 false);
3597 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3598 mbhc->mbhc_data.dce_nsc_cs_z);
3599
3600 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3601
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003602 /* STA measurement for 0 voltage */
3603 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3604 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3605 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003606
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003607 /* Restore registers */
3608 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3609 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003610
3611 /* DCE measurment for MB voltage */
3612 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3613 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303614 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303615 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3616 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3617 else
3618 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3619 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003620 /*
3621 * Hardware that has external cap can delay mic bias ramping down up
3622 * to 50ms.
3623 */
3624 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003625 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3626 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3627 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3628
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003629 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003630 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3631 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3632 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303633 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303634 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3635 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3636 else
3637 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3638 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003639 /*
3640 * Hardware that has external cap can delay mic bias ramping down up
3641 * to 50ms.
3642 */
3643 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003644 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3645 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3646 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3647
3648 /* Restore default settings. */
3649 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303650 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303651 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303652 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3653 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3654 else
3655 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3656 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003657 usleep_range(100, 100);
3658
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003659 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3660 mbhc->mbhc_cb->enable_mb_source(codec, false);
3661
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003662 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3663 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003664 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003665
Joonwoo Parka8890262012-10-15 12:04:27 -07003666 pr_debug("%s: leave\n", __func__);
3667}
3668
3669static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3670{
3671 int n;
3672 u8 *gain;
3673 struct wcd9xxx_mbhc_general_cfg *generic;
3674 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3675 struct snd_soc_codec *codec = mbhc->codec;
3676 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3677
3678 pr_debug("%s: enter\n", __func__);
3679 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3680 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3681
3682 for (n = 0; n < 8; n++) {
3683 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3684 0x07, n);
3685 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3686 btn_det->c[n]);
3687 }
3688
3689 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3690 btn_det->nc);
3691
3692 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3693 generic->mbhc_nsa << 4);
3694
3695 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3696 btn_det->n_meas);
3697
3698 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3699 generic->mbhc_navg);
3700
3701 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3702
3703 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3704 btn_det->mbhc_nsc << 3);
3705
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003706 if (mbhc->mbhc_cb &&
3707 mbhc->mbhc_cb->get_cdc_type() !=
3708 WCD9XXX_CDC_TYPE_HELICON) {
3709 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3710 snd_soc_update_bits(codec,
3711 mbhc->resmgr->reg_addr->micb_4_mbhc,
3712 0x03, MBHC_MICBIAS2);
3713 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003714
3715 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3716
3717 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3718
3719 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3720 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3721 gain[idx] << 3);
3722
3723 pr_debug("%s: leave\n", __func__);
3724}
3725
3726static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3727{
3728 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003729 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303730
Joonwoo Parka8890262012-10-15 12:04:27 -07003731 if (mbhc->mbhc_cfg->gpio) {
3732 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3733 wcd9xxx_mech_plug_detect_irq,
3734 (IRQF_TRIGGER_RISING |
3735 IRQF_TRIGGER_FALLING |
3736 IRQF_DISABLED),
3737 "headset detect", mbhc);
3738 if (ret) {
3739 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3740 mbhc->mbhc_cfg->gpio_irq);
3741 } else {
3742 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3743 if (ret)
3744 pr_err("%s: Failed to enable wake up irq %d\n",
3745 __func__, mbhc->mbhc_cfg->gpio_irq);
3746 }
3747 } else if (mbhc->mbhc_cfg->insert_detect) {
3748 /* Enable HPHL_10K_SW */
3749 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3750 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303751
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003752 ret = wcd9xxx_request_irq(core_res,
3753 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003754 wcd9xxx_mech_plug_detect_irq,
3755 "Jack Detect",
3756 mbhc);
3757 if (ret)
3758 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003759 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003760 }
3761
3762 return ret;
3763}
3764
3765static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3766{
3767 int ret = 0;
3768 struct snd_soc_codec *codec = mbhc->codec;
3769
3770 pr_debug("%s: enter\n", __func__);
3771
3772 /* Enable MCLK during calibration */
3773 wcd9xxx_onoff_ext_mclk(mbhc, true);
3774 wcd9xxx_mbhc_setup(mbhc);
3775 wcd9xxx_mbhc_cal(mbhc);
3776 wcd9xxx_mbhc_calc_thres(mbhc);
3777 wcd9xxx_onoff_ext_mclk(mbhc, false);
3778 wcd9xxx_calibrate_hs_polling(mbhc);
3779
3780 /* Enable Mic Bias pull down and HPH Switch to GND */
3781 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3782 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3783 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3784
3785 if (!IS_ERR_VALUE(ret)) {
3786 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3787 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003788 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003789 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003790 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003791 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003792
3793 /* Initialize mechanical mbhc */
3794 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3795
3796 if (!ret && mbhc->mbhc_cfg->gpio) {
3797 /* Requested with IRQF_DISABLED */
3798 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3799
3800 /* Bootup time detection */
3801 wcd9xxx_swch_irq_handler(mbhc);
3802 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3803 pr_debug("%s: Setting up codec own insert detection\n",
3804 __func__);
3805 /* Setup for insertion detection */
3806 wcd9xxx_insert_detect_setup(mbhc, true);
3807 }
3808 }
3809
3810 pr_debug("%s: leave\n", __func__);
3811
3812 return ret;
3813}
3814
3815static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3816{
3817 struct delayed_work *dwork;
3818 struct wcd9xxx_mbhc *mbhc;
3819 struct snd_soc_codec *codec;
3820 const struct firmware *fw;
3821 int ret = -1, retry = 0;
3822
3823 dwork = to_delayed_work(work);
3824 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3825 codec = mbhc->codec;
3826
3827 while (retry < FW_READ_ATTEMPTS) {
3828 retry++;
3829 pr_info("%s:Attempt %d to request MBHC firmware\n",
3830 __func__, retry);
3831 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3832 codec->dev);
3833
3834 if (ret != 0) {
3835 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3836 } else {
3837 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3838 break;
3839 }
3840 }
3841
3842 if (ret != 0) {
3843 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3844 __func__);
3845 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3846 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3847 __func__);
3848 release_firmware(fw);
3849 } else {
3850 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3851 mbhc->mbhc_fw = fw;
3852 }
3853
3854 (void) wcd9xxx_init_and_calibrate(mbhc);
3855}
3856
3857#ifdef CONFIG_DEBUG_FS
3858ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3859 size_t count, loff_t *pos)
3860{
3861 const int size = 768;
3862 char buffer[size];
3863 int n = 0;
3864 struct wcd9xxx_mbhc *mbhc = file->private_data;
3865 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003866 const s16 v_ins_hu =
3867 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3868 const s16 v_ins_h =
3869 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3870 const s16 v_b1_hu =
3871 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3872 const s16 v_b1_h =
3873 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3874 const s16 v_br_h =
3875 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003876
Joonwoo Park520a0f92013-05-14 19:39:58 -07003877 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3878 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003879 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3880 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003881 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3882 p->dce_nsc_cs_z,
3883 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3884 p->dce_nsc_cs_z,
3885 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003886 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3887 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3888 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3889 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003890 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3891 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3892 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3893 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3894 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3895 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3896 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003897 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003898 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003899 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003900 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003901 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003902 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003903 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3904 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3905 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3906 p->v_no_mic,
3907 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3908 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3909 p->v_inval_ins_low);
3910 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3911 p->v_inval_ins_high);
3912 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3913 !wcd9xxx_swch_level_remove(mbhc));
3914 buffer[n] = 0;
3915
3916 return simple_read_from_buffer(buf, count, pos, buffer, n);
3917}
3918
3919static int codec_debug_open(struct inode *inode, struct file *file)
3920{
3921 file->private_data = inode->i_private;
3922 return 0;
3923}
3924
3925static ssize_t codec_debug_write(struct file *filp,
3926 const char __user *ubuf, size_t cnt,
3927 loff_t *ppos)
3928{
3929 char lbuf[32];
3930 char *buf;
3931 int rc;
3932 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3933
3934 if (cnt > sizeof(lbuf) - 1)
3935 return -EINVAL;
3936
3937 rc = copy_from_user(lbuf, ubuf, cnt);
3938 if (rc)
3939 return -EFAULT;
3940
3941 lbuf[cnt] = '\0';
3942 buf = (char *)lbuf;
3943 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3944 false : true;
3945 return rc;
3946}
3947
3948static const struct file_operations mbhc_trrs_debug_ops = {
3949 .open = codec_debug_open,
3950 .write = codec_debug_write,
3951};
3952
3953static const struct file_operations mbhc_debug_ops = {
3954 .open = codec_debug_open,
3955 .read = codec_mbhc_debug_read,
3956};
3957
3958static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3959{
3960 mbhc->debugfs_poke =
3961 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3962 &mbhc_trrs_debug_ops);
3963 mbhc->debugfs_mbhc =
3964 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3965 NULL, mbhc, &mbhc_debug_ops);
3966}
3967
3968static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3969{
3970 debugfs_remove(mbhc->debugfs_poke);
3971 debugfs_remove(mbhc->debugfs_mbhc);
3972}
3973#else
3974static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3975{
3976}
3977
3978static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3979{
3980}
3981#endif
3982
3983int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3984 struct wcd9xxx_mbhc_config *mbhc_cfg)
3985{
Simmi Pateriya95466b12013-05-09 20:08:46 +05303986 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003987 struct snd_soc_codec *codec = mbhc->codec;
3988
3989 pr_debug("%s: enter\n", __func__);
3990
3991 if (!codec) {
3992 pr_err("%s: no codec\n", __func__);
3993 return -EINVAL;
3994 }
3995
3996 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
3997 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
3998 pr_err("Error: unsupported clock rate %d\n",
3999 mbhc_cfg->mclk_rate);
4000 return -EINVAL;
4001 }
4002
4003 /* Save mbhc config */
4004 mbhc->mbhc_cfg = mbhc_cfg;
4005
4006 /* Get HW specific mbhc registers' address */
4007 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4008
4009 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304010 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4011 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4012 else
4013 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4014 0x40, WCD9XXX_CFILT_FAST_MODE);
4015
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004016 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004017 * setup internal micbias if codec uses internal micbias for
4018 * headset detection
4019 */
4020 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304021 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004022 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304023 mbhc->int_rbias_on = true;
4024 } else {
4025 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004026 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304027 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004028 }
4029
4030 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004031 * If codec has specific clock gating for MBHC,
4032 * remove the clock gate
4033 */
4034 if (mbhc->mbhc_cb &&
4035 mbhc->mbhc_cb->enable_clock_gate)
4036 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4037
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004038 if (!mbhc->mbhc_cfg->read_fw_bin ||
4039 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004040 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004041 } else {
4042 if (!mbhc->mbhc_fw)
4043 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4044 usecs_to_jiffies(FW_READ_TIMEOUT));
4045 else
4046 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4047 __func__, mbhc->mbhc_fw);
4048 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004049
4050 pr_debug("%s: leave %d\n", __func__, rc);
4051 return rc;
4052}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004053EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004054
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004055void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4056{
4057 if (mbhc->mbhc_fw) {
4058 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4059 release_firmware(mbhc->mbhc_fw);
4060 mbhc->mbhc_fw = NULL;
4061 }
4062}
4063EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4064
Joonwoo Parka8890262012-10-15 12:04:27 -07004065static enum wcd9xxx_micbias_num
4066wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4067{
4068 enum wcd9xxx_micbias_num ret;
4069 switch (event) {
4070 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004071 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4072 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4073 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004074 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004075 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004076 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004077 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4078 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4079 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004080 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004081 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004082 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004083 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4084 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4085 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004086 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004087 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004088 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004089 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4090 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4091 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004092 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004093 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004094 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004095 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004096 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004097 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004098 }
4099 return ret;
4100}
4101
4102static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4103{
4104 int ret;
4105 switch (event) {
4106 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4107 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4108 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4109 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4110 ret = WCD9XXX_CFILT1_SEL;
4111 break;
4112 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4113 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4114 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4115 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4116 ret = WCD9XXX_CFILT2_SEL;
4117 break;
4118 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4119 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4120 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4121 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4122 ret = WCD9XXX_CFILT3_SEL;
4123 break;
4124 default:
4125 ret = -1;
4126 }
4127 return ret;
4128}
4129
4130static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4131{
4132 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004133 const struct wcd9xxx_micbias_setting *mb_pdata =
4134 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004135
4136 switch (mbhc->mbhc_cfg->micbias) {
4137 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004138 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004139 break;
4140 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004141 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004142 break;
4143 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004144 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004145 break;
4146 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004147 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004148 break;
4149 default:
4150 cfilt = MBHC_MICBIAS_INVALID;
4151 break;
4152 }
4153 return cfilt;
4154}
4155
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004156static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4157{
4158 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4159 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4160 else
4161 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4162 0x40, on ? 0x40 : 0x00);
4163}
4164
Joonwoo Parka8890262012-10-15 12:04:27 -07004165static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4166 void *data)
4167{
4168 int ret = 0;
4169 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4170 struct snd_soc_codec *codec = mbhc->codec;
4171 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4172
4173 pr_debug("%s: enter event %s(%d)\n", __func__,
4174 wcd9xxx_get_event_string(event), event);
4175
4176 switch (event) {
4177 /* MICBIAS usage change */
4178 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4179 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4180 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4181 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304182 mbhc->int_rbias_on = true;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004183 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4184 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004185 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004186 /*
4187 * Enable MBHC TxFE whenever micbias is
4188 * turned ON and polling is active
4189 */
4190 if (mbhc->polling_active)
4191 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4192 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004193 break;
4194 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4195 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4196 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4197 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004198 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004199 wcd9xxx_event_to_micbias(event) &&
4200 wcd9xxx_mbhc_polling(mbhc)) {
4201 /* if polling is on, restart it */
4202 wcd9xxx_pause_hs_polling(mbhc);
4203 wcd9xxx_start_hs_polling(mbhc);
4204 }
4205 break;
4206 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4207 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4208 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4209 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304210 mbhc->int_rbias_on = false;
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004211 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4212 wcd9xxx_event_to_micbias(event)) {
4213 if (mbhc->event_state &
4214 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4215 wcd9xxx_switch_micbias(mbhc, 1);
4216 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304217 * Disable MBHC TxFE, in case it was enabled earlier
4218 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004219 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304220 if (!mbhc->polling_active)
4221 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004222 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004223 break;
4224 /* PA usage change */
4225 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004226 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004227 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004228 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004229 wcd9xxx_switch_micbias(mbhc, 1);
4230 break;
4231 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004232 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004233 break;
4234 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004235 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004236 /* if HPH PAs are off, report OCP and switch back to CFILT */
4237 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4238 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4239 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4240 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004241 if (!(mbhc->event_state &
4242 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4243 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 &
4253 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4254 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004255 break;
4256 /* Clock usage change */
4257 case WCD9XXX_EVENT_PRE_MCLK_ON:
4258 break;
4259 case WCD9XXX_EVENT_POST_MCLK_ON:
4260 /* Change to lower TxAAF frequency */
4261 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4262 1 << 4);
4263 /* Re-calibrate clock rate dependent values */
4264 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4265 /* If clock source changes, stop and restart polling */
4266 if (wcd9xxx_mbhc_polling(mbhc)) {
4267 wcd9xxx_calibrate_hs_polling(mbhc);
4268 wcd9xxx_start_hs_polling(mbhc);
4269 }
4270 break;
4271 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4272 /* If clock source changes, stop and restart polling */
4273 if (wcd9xxx_mbhc_polling(mbhc))
4274 wcd9xxx_pause_hs_polling(mbhc);
4275 break;
4276 case WCD9XXX_EVENT_POST_MCLK_OFF:
4277 break;
4278 case WCD9XXX_EVENT_PRE_RCO_ON:
4279 break;
4280 case WCD9XXX_EVENT_POST_RCO_ON:
4281 /* Change to higher TxAAF frequency */
4282 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4283 0 << 4);
4284 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004285 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004286 /* If clock source changes, stop and restart polling */
4287 if (wcd9xxx_mbhc_polling(mbhc)) {
4288 wcd9xxx_calibrate_hs_polling(mbhc);
4289 wcd9xxx_start_hs_polling(mbhc);
4290 }
4291 break;
4292 case WCD9XXX_EVENT_PRE_RCO_OFF:
4293 /* If clock source changes, stop and restart polling */
4294 if (wcd9xxx_mbhc_polling(mbhc))
4295 wcd9xxx_pause_hs_polling(mbhc);
4296 break;
4297 case WCD9XXX_EVENT_POST_RCO_OFF:
4298 break;
4299 /* CFILT usage change */
4300 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4301 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4302 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4303 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4304 wcd9xxx_event_to_cfilt(event))
4305 /*
4306 * Switch CFILT to slow mode if MBHC CFILT is being
4307 * used.
4308 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304309 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004310 break;
4311 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4312 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4313 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4314 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4315 wcd9xxx_event_to_cfilt(event))
4316 /*
4317 * Switch CFILT to fast mode if MBHC CFILT is not
4318 * used anymore.
4319 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304320 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004321 break;
4322 /* System resume */
4323 case WCD9XXX_EVENT_POST_RESUME:
4324 mbhc->mbhc_last_resume = jiffies;
4325 break;
4326 /* BG mode chage */
4327 case WCD9XXX_EVENT_PRE_BG_OFF:
4328 case WCD9XXX_EVENT_POST_BG_OFF:
4329 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4330 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4331 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4332 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4333 /* Not used for now */
4334 break;
4335 default:
4336 WARN(1, "Unknown event %d\n", event);
4337 ret = -EINVAL;
4338 }
4339
4340 pr_debug("%s: leave\n", __func__);
4341
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304342 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004343}
4344
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004345static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4346 uint32_t *zr)
4347{
4348 int i;
4349 int ret = 0;
4350 s16 l[3], r[3];
4351 s16 *z[] = {
4352 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4353 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004354 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004355 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004356 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4357 /* Phase 1 */
4358 /* Set MBHC_MUX for HPHL without ical */
4359 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4360 /* Set MBHC_MUX for HPHR without ical */
4361 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4362 /* Set MBHC_MUX for HPHR with ical */
4363 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4364 /* Set MBHC_MUX for HPHL with ical */
4365 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4366
4367 /* Phase 2 */
4368 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4369 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4370 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4371 };
4372
4373 pr_debug("%s: enter\n", __func__);
4374 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4375
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004376 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4377 !mbhc->mbhc_cb->compute_impedance || !zl ||
4378 !zr)
4379 return -EINVAL;
4380
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004381 /*
4382 * Impedance detection is an intrusive function as it mutes RX paths,
4383 * enable PAs and etc. Therefore codec drvier including ALSA
4384 * shouldn't read and write hardware registers during detection.
4385 */
4386 mutex_lock(&codec->mutex);
4387
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004388 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004389
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004390 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004391 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004392
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004393 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4394 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004395
4396 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004397 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004398 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4399 reg_set_mux[i].mask,
4400 reg_set_mux[i].val);
4401 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4402 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4403 else
4404 snd_soc_update_bits(codec,
4405 WCD9XXX_A_MBHC_SCALING_MUX_1,
4406 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004407 /* 25us is required after mux change to settle down */
4408 usleep_range(mux_wait_us,
4409 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004410 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004411 }
4412
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004413 /* Codec specific setup for L2 and R2 measurements */
4414 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004415
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004416 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004417 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4418 reg_set_mux[i].mask,
4419 reg_set_mux[i].val);
4420 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4421 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4422 else
4423 snd_soc_update_bits(codec,
4424 WCD9XXX_A_MBHC_SCALING_MUX_1,
4425 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004426 /* 25us is required after mux change to settle down */
4427 usleep_range(mux_wait_us,
4428 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004429 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004430 }
4431
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004432 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004433
4434 mutex_unlock(&codec->mutex);
4435
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004436 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004437
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004438 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004439 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004440
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004441 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004442 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004443 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4444 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004445 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004446 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004447 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4448 pr_debug("%s: Impedance detection completed\n", __func__);
4449
4450 return ret;
4451}
4452
4453int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4454 uint32_t *zr)
4455{
4456 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4457 *zl = mbhc->zl;
4458 *zr = mbhc->zr;
4459 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4460
4461 if (*zl && *zr)
4462 return 0;
4463 else
4464 return -EINVAL;
4465}
4466
Joonwoo Parka8890262012-10-15 12:04:27 -07004467/*
4468 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4469 *
4470 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4471 */
4472int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004473 struct snd_soc_codec *codec,
4474 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004475 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4476 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4477 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004478 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004479{
4480 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004481 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004482
4483 pr_debug("%s: enter\n", __func__);
4484 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4485 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4486
4487 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4488 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4489 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4490 mbhc->mbhc_micbias_switched = false;
4491 mbhc->polling_active = false;
4492 mbhc->mbhc_state = MBHC_STATE_NONE;
4493 mbhc->in_swch_irq_handler = false;
4494 mbhc->current_plug = PLUG_TYPE_NONE;
4495 mbhc->lpi_enabled = false;
4496 mbhc->no_mic_headset_override = false;
4497 mbhc->mbhc_last_resume = 0;
4498 mbhc->codec = codec;
4499 mbhc->resmgr = resmgr;
4500 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004501 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004502 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304503 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004504 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004505 mbhc->impedance_detect = impedance_det_en;
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304506 mbhc->int_rbias_on = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07004507
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004508 if (mbhc->intr_ids == NULL) {
4509 pr_err("%s: Interrupt mapping not provided\n", __func__);
4510 return -EINVAL;
4511 }
4512
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004513 if (mbhc->headset_jack.jack == NULL) {
4514 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4515 &mbhc->headset_jack);
4516 if (ret) {
4517 pr_err("%s: Failed to create new jack\n", __func__);
4518 return ret;
4519 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004520
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004521 ret = snd_soc_jack_new(codec, "Button Jack",
4522 WCD9XXX_JACK_BUTTON_MASK,
4523 &mbhc->button_jack);
4524 if (ret) {
4525 pr_err("Failed to create new jack\n");
4526 return ret;
4527 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004528
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004529 ret = snd_jack_set_key(mbhc->button_jack.jack,
4530 SND_JACK_BTN_0,
4531 KEY_MEDIA);
4532 if (ret) {
4533 pr_err("%s: Failed to set code for btn-0\n",
4534 __func__);
4535 return ret;
4536 }
4537
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004538 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4539 wcd9xxx_mbhc_fw_read);
4540 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4541 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4542 wcd9xxx_mbhc_insert_work);
4543 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004544
4545 /* Register event notifier */
4546 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4547 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4548 if (ret) {
4549 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4550 return ret;
4551 }
4552
4553 wcd9xxx_init_debugfs(mbhc);
4554
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004555
4556 /* Disable Impedance detection by default for certain codec types */
4557 if (mbhc->mbhc_cb &&
4558 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4559 impedance_detect_en = 0;
4560 else
4561 impedance_detect_en = impedance_det_en ? 1 : 0;
4562
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004563 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004564 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004565 wcd9xxx_hs_insert_irq,
4566 "Headset insert detect", mbhc);
4567 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004568 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004569 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004570 goto err_insert_irq;
4571 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004572 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004573
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004574 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004575 wcd9xxx_hs_remove_irq,
4576 "Headset remove detect", mbhc);
4577 if (ret) {
4578 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004579 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004580 goto err_remove_irq;
4581 }
4582
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004583 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004584 wcd9xxx_dce_handler, "DC Estimation detect",
4585 mbhc);
4586 if (ret) {
4587 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004588 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004589 goto err_potential_irq;
4590 }
4591
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004592 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004593 wcd9xxx_release_handler,
4594 "Button Release detect", mbhc);
4595 if (ret) {
4596 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004597 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004598 goto err_release_irq;
4599 }
4600
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004601 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004602 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4603 mbhc);
4604 if (ret) {
4605 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004606 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004607 goto err_hphl_ocp_irq;
4608 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004609 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004610
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004611 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004612 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4613 mbhc);
4614 if (ret) {
4615 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004616 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004617 goto err_hphr_ocp_irq;
4618 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004619 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004620
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004621 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4622 1 << WCD9XXX_COND_HPH);
4623
Joonwoo Parka8890262012-10-15 12:04:27 -07004624 pr_debug("%s: leave ret %d\n", __func__, ret);
4625 return ret;
4626
4627err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004628 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004629err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004630 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004631err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004632 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004633err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004634 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004635err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004636 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004637err_insert_irq:
4638 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4639
4640 pr_debug("%s: leave ret %d\n", __func__, ret);
4641 return ret;
4642}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004643EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004644
4645void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4646{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004647 struct wcd9xxx_core_resource *core_res =
4648 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004649
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004650 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4651 1 << WCD9XXX_COND_HPH);
4652
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004653 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4654 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4655 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4656 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4657 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4658 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4659 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004660
Joonwoo Parka8890262012-10-15 12:04:27 -07004661 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004662 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004663}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004664EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004665
4666MODULE_DESCRIPTION("wcd9xxx MBHC module");
4667MODULE_LICENSE("GPL v2");