blob: 2ecebcdfb9e25b4c226731747852b33c620f09c1 [file] [log] [blame]
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070021#include <linux/list.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070022#include <linux/mfd/wcd9xxx/core.h>
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070023#include <linux/mfd/wcd9xxx/core-resource.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070024#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
25#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
26#include <linux/mfd/wcd9xxx/pdata.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <linux/bitops.h>
33#include <linux/delay.h>
34#include <linux/pm_runtime.h>
35#include <linux/kernel.h>
36#include <linux/gpio.h>
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -070037#include <linux/input.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070038#include "wcd9320.h"
Simmi Pateriya0a44d842013-04-03 01:12:42 +053039#include "wcd9306.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070040#include "wcd9xxx-mbhc.h"
41#include "wcd9xxx-resmgr.h"
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070042#include "wcd9xxx-common.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070043
44#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
Joonwoo Park80a01172012-10-15 16:05:23 -070045 SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
46 SND_JACK_UNSUPPORTED)
Joonwoo Parka8890262012-10-15 12:04:27 -070047#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
48 SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
49 SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
50 SND_JACK_BTN_6 | SND_JACK_BTN_7)
51
52#define NUM_DCE_PLUG_DETECT 3
Joonwoo Parkccccba72013-04-26 11:19:46 -070053#define NUM_DCE_PLUG_INS_DETECT 5
Joonwoo Parka8890262012-10-15 12:04:27 -070054#define NUM_ATTEMPTS_INSERT_DETECT 25
55#define NUM_ATTEMPTS_TO_REPORT 5
56
57#define FAKE_INS_LOW 10
58#define FAKE_INS_HIGH 80
59#define FAKE_INS_HIGH_NO_SWCH 150
60#define FAKE_REMOVAL_MIN_PERIOD_MS 50
61#define FAKE_INS_DELTA_SCALED_MV 300
62
63#define BUTTON_MIN 0x8000
64#define STATUS_REL_DETECTION 0x0C
65
66#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
67#define HS_DETECT_PLUG_INERVAL_MS 100
68#define SWCH_REL_DEBOUNCE_TIME_MS 50
69#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070070#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070071
72#define GND_MIC_SWAP_THRESHOLD 2
73#define OCP_ATTEMPT 1
74
75#define FW_READ_ATTEMPTS 15
76#define FW_READ_TIMEOUT 2000000
77
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070078#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070079
80#define MCLK_RATE_12288KHZ 12288000
81#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070082
83#define DEFAULT_DCE_STA_WAIT 55
84#define DEFAULT_DCE_WAIT 60000
85#define DEFAULT_STA_WAIT 5000
86
87#define VDDIO_MICBIAS_MV 1800
88
Joonwoo Parkccccba72013-04-26 11:19:46 -070089#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
90
Joonwoo Park20bc9da2013-01-16 12:58:06 -080091#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070092#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Joonwoo Park20bc9da2013-01-16 12:58:06 -080093#define WCD9XXX_MEAS_DELTA_MAX_MV 50
Joonwoo Park141d6182013-03-05 12:25:46 -080094#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
95#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070096
97/*
98 * Invalid voltage range for the detection
99 * of plug type with current source
100 */
101#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 110
102#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
103
104/*
105 * Threshold used to detect euro headset
106 * with current source
107 */
108#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
109#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
110
111#define WCD9XXX_MBHC_NSC_CS 9
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800112#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
Joonwoo Park479347a2013-04-15 18:01:05 -0700113#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
Joonwoo Parkccccba72013-04-26 11:19:46 -0700114#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800115
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700116#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
117
118/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
119#define WCD9XXX_WG_TIME_FACTOR_US 240
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800120
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700121#define WCD9XXX_V_CS_HS_MAX 500
122#define WCD9XXX_V_CS_NO_MIC 5
123#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
124#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 10
125
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700126static int impedance_detect_en;
127module_param(impedance_detect_en, int,
128 S_IRUGO | S_IWUSR | S_IWGRP);
129MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
130
Joonwoo Parkccccba72013-04-26 11:19:46 -0700131static bool detect_use_vddio_switch = true;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700216 s16 v_brh, v_b1_hu;
Joonwoo Parka8890262012-10-15 12:04:27 -0700217 struct snd_soc_codec *codec = mbhc->codec;
218 int mbhc_state = mbhc->mbhc_state;
219
220 pr_debug("%s: enter\n", __func__);
221 if (!mbhc->polling_active) {
222 pr_debug("Polling is not active, do not start polling\n");
223 return;
224 }
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530225 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530226 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
227 mbhc->mbhc_cb->enable_mux_bias_block(codec);
228 else
229 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
230 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530231
Joonwoo Parka8890262012-10-15 12:04:27 -0700232 if (!mbhc->no_mic_headset_override &&
233 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530234 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700235 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
236 /* set to max button press threshold */
237 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
238 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
239 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
240 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
241 /* set to max */
242 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
243 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700244
245 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
246 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
247 (v_brh >> 8) & 0xFF);
248 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
249 v_brh & 0xFF);
250 v_b1_hu = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
252 v_b1_hu & 0xFF);
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
254 (v_b1_hu >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700255 }
256
257 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
258 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
260 pr_debug("%s: leave\n", __func__);
261}
262
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700263static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
264 unsigned int cfilt_mv)
265{
266 if (mbhc->mbhc_cb &&
267 mbhc->mbhc_cb->get_cdc_type() ==
268 WCD9XXX_CDC_TYPE_HELICON)
269 return 0x18;
270
271 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
272}
273
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700274/*
275 * called under codec_resource_lock acquisition
276 * return old status
277 */
278static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700279 int vddio_switch, bool restartpolling,
280 bool checkpolling)
281{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700282 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700283 int cfilt_k_val;
284 bool override;
285 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700286 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700287
288 codec = mbhc->codec;
289
Joonwoo Parkccccba72013-04-26 11:19:46 -0700290 if (mbhc->micbias_enable) {
291 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700292 ret = mbhc->mbhc_micbias_switched;
293 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700294 }
295
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700296 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700297 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
298 (!checkpolling || mbhc->polling_active)) {
299 if (restartpolling)
300 wcd9xxx_pause_hs_polling(mbhc);
301 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
302 0x04;
303 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700304 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700305 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700306 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700307 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700308 VDDIO_MICBIAS_MV);
309 usleep_range(10000, 10000);
310 snd_soc_update_bits(codec,
311 mbhc->mbhc_bias_regs.cfilt_val,
312 0xFC, (cfilt_k_val << 2));
313 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700314 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700315 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700316 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700317 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700318 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700319 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700320 /* Threshods for button press */
321 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
322 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
323 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
324 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
325 0xFF);
326 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
327 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
328 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
329 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
330 0xFF);
331 /* Threshods for button release */
332 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
333 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
334 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
335 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700336 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
337 __func__);
338 }
339
340 /* Enable MIC BIAS Switch to VDDIO */
341 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
342 0x80, 0x80);
343 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
344 0x10, 0x00);
345 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700346 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700347 if (restartpolling)
348 wcd9xxx_start_hs_polling(mbhc);
349
350 mbhc->mbhc_micbias_switched = true;
351 pr_debug("%s: VDDIO switch enabled\n", __func__);
352 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
353 if ((!checkpolling || mbhc->polling_active) &&
354 restartpolling)
355 wcd9xxx_pause_hs_polling(mbhc);
356 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700357 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700358 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700359 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700360 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700361 snd_soc_update_bits(codec,
362 mbhc->mbhc_bias_regs.cfilt_val,
363 0xFC, (cfilt_k_val << 2));
364 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700365 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700366 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700367 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700368 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700369 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
370 0xFF);
371 /* Revert threshods for button press */
372 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
373 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
375 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
376 0xFF);
377 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
378 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
379 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
380 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
381 0xFF);
382 /* Revert threshods for button release */
383 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
384 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
385 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
386 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700387 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
388 __func__);
389 }
390
391 /* Disable MIC BIAS Switch to VDDIO */
392 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
393 0x00);
394 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
395 0x00);
396
397 if ((!checkpolling || mbhc->polling_active) && restartpolling)
398 wcd9xxx_start_hs_polling(mbhc);
399
400 mbhc->mbhc_micbias_switched = false;
401 pr_debug("%s: VDDIO switch disabled\n", __func__);
402 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700403
404 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700405}
406
407static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
408{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700409 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700410}
411
Joonwoo Park73375212013-05-07 12:42:44 -0700412static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
413 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700414{
Joonwoo Park73375212013-05-07 12:42:44 -0700415 enum mbhc_v_index vidx;
416 s16 ret = -EINVAL;
417
Joonwoo Parka8890262012-10-15 12:04:27 -0700418 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
419 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700420 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700421 else
Joonwoo Park73375212013-05-07 12:42:44 -0700422 vidx = MBHC_V_IDX_CFILT;
423
424 switch (idx) {
425 case WCD9XXX_CURRENT_V_INS_H:
426 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
427 break;
428 case WCD9XXX_CURRENT_V_INS_HU:
429 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
430 break;
431 case WCD9XXX_CURRENT_V_B1_H:
432 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
433 break;
434 case WCD9XXX_CURRENT_V_B1_HU:
435 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
436 break;
437 case WCD9XXX_CURRENT_V_BR_H:
438 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
439 break;
440 }
441
442 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700443}
444
445void *wcd9xxx_mbhc_cal_btn_det_mp(
446 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
447 const enum wcd9xxx_mbhc_btn_det_mem mem)
448{
449 void *ret = &btn_det->_v_btn_low;
450
451 switch (mem) {
452 case MBHC_BTN_DET_GAIN:
453 ret += sizeof(btn_det->_n_cic);
454 case MBHC_BTN_DET_N_CIC:
455 ret += sizeof(btn_det->_n_ready);
456 case MBHC_BTN_DET_N_READY:
457 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
458 case MBHC_BTN_DET_V_BTN_HIGH:
459 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
460 case MBHC_BTN_DET_V_BTN_LOW:
461 /* do nothing */
462 break;
463 default:
464 ret = NULL;
465 }
466
467 return ret;
468}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700469EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700470
471static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
472{
473 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700474 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
475 WCD9XXX_CURRENT_V_INS_HU);
476 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
477 WCD9XXX_CURRENT_V_B1_HU);
478 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
479 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700480
481 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
482 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
483 (v_ins_hu >> 8) & 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700484 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700485 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700486 (v_b1_hu >> 8) & 0xFF);
487 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700488 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700489 (v_b1_h >> 8) & 0xFF);
490 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700491 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700492 (v_brh >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700493 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
494 mbhc->mbhc_data.v_brl & 0xFF);
495 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
496 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
497}
498
Simmi Pateriya95466b12013-05-09 20:08:46 +0530499static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700500 bool fast)
501{
502 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530503 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700504
Simmi Pateriya95466b12013-05-09 20:08:46 +0530505 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
506 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530507 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530508 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700509 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530510 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700511 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700512
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700513 cfilt_mode.reg_mask = 0x40;
514 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530515 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700516 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700517
518 if (cfilt_mode.cur_mode_val
519 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530520 if (mbhc->polling_active)
521 wcd9xxx_pause_hs_polling(mbhc);
522 snd_soc_update_bits(codec,
523 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700524 cfilt_mode.reg_mask,
525 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530526 if (mbhc->polling_active)
527 wcd9xxx_start_hs_polling(mbhc);
528 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700529 cfilt_mode.cur_mode_val,
530 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530531 } else {
532 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700533 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530534 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700535}
536
Joonwoo Park3699ca32013-02-08 12:06:15 -0800537static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
538 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700539{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800540 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800541 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
542 WCD9XXX_COND_HPH_MIC,
543 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800544 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
545 WCD9XXX_COND_HPH,
546 status & SND_JACK_HEADPHONE);
547 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800548
Joonwoo Parka8890262012-10-15 12:04:27 -0700549 snd_soc_jack_report_no_dapm(jack, status, mask);
550}
551
552static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
553 int irq)
554{
555 struct snd_soc_codec *codec;
556
557 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
558 codec = mbhc->codec;
559 if (mbhc->hph_status & jack_status) {
560 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800561 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700562 mbhc->hph_status, WCD9XXX_JACK_MASK);
563 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
564 0x00);
565 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
566 0x10);
567 /*
568 * reset retry counter as PA is turned off signifying
569 * start of new OCP detection session
570 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700571 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700572 mbhc->hphlocp_cnt = 0;
573 else
574 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700575 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700576 }
577}
578
579static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
580{
581 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700582 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700583}
584
585static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
586{
587 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700588 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700589}
590
591static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
592 struct mbhc_micbias_regs *micbias_regs)
593{
594 unsigned int cfilt;
595 struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
596
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700597 if (mbhc->mbhc_cb &&
598 mbhc->mbhc_cb->get_cdc_type() ==
599 WCD9XXX_CDC_TYPE_HELICON) {
600 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
601 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
602 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
603 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
604 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
605 mbhc->mbhc_data.micb_mv = 1800;
606 return;
607 }
608
Joonwoo Parka8890262012-10-15 12:04:27 -0700609 switch (mbhc->mbhc_cfg->micbias) {
610 case MBHC_MICBIAS1:
611 cfilt = pdata->micbias.bias1_cfilt_sel;
612 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
613 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
614 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
615 break;
616 case MBHC_MICBIAS2:
617 cfilt = pdata->micbias.bias2_cfilt_sel;
618 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
619 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
620 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
621 break;
622 case MBHC_MICBIAS3:
623 cfilt = pdata->micbias.bias3_cfilt_sel;
624 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
625 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
626 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
627 break;
628 case MBHC_MICBIAS4:
629 cfilt = pdata->micbias.bias4_cfilt_sel;
630 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
631 micbias_regs->int_rbias =
632 mbhc->resmgr->reg_addr->micb_4_int_rbias;
633 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
634 break;
635 default:
636 /* Should never reach here */
637 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
638 return;
639 }
640
641 micbias_regs->cfilt_sel = cfilt;
642
643 switch (cfilt) {
644 case WCD9XXX_CFILT1_SEL:
645 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
646 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
647 mbhc->mbhc_data.micb_mv =
648 mbhc->resmgr->pdata->micbias.cfilt1_mv;
649 break;
650 case WCD9XXX_CFILT2_SEL:
651 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
652 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
653 mbhc->mbhc_data.micb_mv =
654 mbhc->resmgr->pdata->micbias.cfilt2_mv;
655 break;
656 case WCD9XXX_CFILT3_SEL:
657 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
658 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
659 mbhc->mbhc_data.micb_mv =
660 mbhc->resmgr->pdata->micbias.cfilt3_mv;
661 break;
662 }
663}
664
665static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
666{
667 bool pa_turned_on = false;
668 struct snd_soc_codec *codec = mbhc->codec;
669 u8 wg_time;
670
671 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
672 wg_time += 1;
673
674 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
675 &mbhc->hph_pa_dac_state)) {
676 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
677 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
678 0xC0, 0xC0);
679 }
680 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
681 &mbhc->hph_pa_dac_state)) {
682 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
683 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700684 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700685 }
686
687 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
688 &mbhc->hph_pa_dac_state)) {
689 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
690 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
691 1 << 4);
692 pa_turned_on = true;
693 }
694 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
695 &mbhc->hph_pa_dac_state)) {
696 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
697 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
698 << 5);
699 pa_turned_on = true;
700 }
701
702 if (pa_turned_on) {
703 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
704 __func__);
705 usleep_range(wg_time * 1000, wg_time * 1000);
706 }
707}
708
709static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
710{
711 int r;
712 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
713 if (r)
714 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
715 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700716 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700717 return r;
718}
719
720static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
721{
722 u8 hph_reg_val = 0;
723 if (left)
724 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
725 else
726 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
727
728 return (hph_reg_val & 0xC0) ? true : false;
729}
730
731static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
732{
733 u8 hph_reg_val = 0;
734 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
735
736 return (hph_reg_val & 0x30) ? true : false;
737}
738
739/* called under codec_resource_lock acquisition */
740static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
741{
742 u8 wg_time;
743 struct snd_soc_codec *codec = mbhc->codec;
744
745 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
746 wg_time += 1;
747
748 /* If headphone PA is on, check if userspace receives
749 * removal event to sync-up PA's state */
750 if (wcd9xxx_is_hph_pa_on(codec)) {
751 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
752 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
753 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
754 } else {
755 pr_debug("%s PA is off\n", __func__);
756 }
757
758 if (wcd9xxx_is_hph_dac_on(codec, 1))
759 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
760 if (wcd9xxx_is_hph_dac_on(codec, 0))
761 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
762
763 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800764 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700765 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
766 usleep_range(wg_time * 1000, wg_time * 1000);
767}
768
769static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
770{
771 if (!mbhc->mbhc_cfg->insert_detect)
772 return;
773 pr_debug("%s: Setting up %s detection\n", __func__,
774 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700775 /* Disable detection to avoid glitch */
776 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530777 if (mbhc->mbhc_cfg->gpio_level_insert)
778 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
779 (0x68 | (ins ? (1 << 1) : 0)));
780 else
781 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
782 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700783 /* Re-enable detection */
784 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700785}
786
787/* called under codec_resource_lock acquisition */
788static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
789 enum snd_jack_types jack_type)
790{
791 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
792
Joonwoo Park80a01172012-10-15 16:05:23 -0700793 pr_debug("%s: enter insertion %d hph_status %x\n",
794 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700795 if (!insertion) {
796 /* Report removal */
797 mbhc->hph_status &= ~jack_type;
798 /*
799 * cancel possibly scheduled btn work and
800 * report release if we reported button press
801 */
802 if (wcd9xxx_cancel_btn_work(mbhc))
803 pr_debug("%s: button press is canceled\n", __func__);
804 else if (mbhc->buttons_pressed) {
805 pr_debug("%s: release of button press%d\n",
806 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800807 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700808 mbhc->buttons_pressed);
809 mbhc->buttons_pressed &=
810 ~WCD9XXX_JACK_BUTTON_MASK;
811 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700812
813 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
814 pr_debug("%s: Disabling micbias\n", __func__);
815 mbhc->micbias_enable_cb(mbhc->codec, false);
816 mbhc->micbias_enable = false;
817 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700818 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700819 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
820 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800821 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700822 WCD9XXX_JACK_MASK);
823 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
824 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
825 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
826 mbhc->current_plug = PLUG_TYPE_NONE;
827 mbhc->polling_active = false;
828 } else {
Joonwoo Park80a01172012-10-15 16:05:23 -0700829 if (mbhc->mbhc_cfg->detect_extn_cable) {
830 /* Report removal of current jack type */
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800831 if (mbhc->hph_status && mbhc->hph_status != jack_type) {
Joonwoo Parkccccba72013-04-26 11:19:46 -0700832 if (mbhc->micbias_enable &&
833 mbhc->micbias_enable_cb &&
834 mbhc->hph_status == SND_JACK_HEADSET) {
835 pr_debug("%s: Disabling micbias\n",
836 __func__);
837 mbhc->micbias_enable_cb(mbhc->codec,
838 false);
839 mbhc->micbias_enable = false;
840 }
Joonwoo Park80a01172012-10-15 16:05:23 -0700841 pr_debug("%s: Reporting removal (%x)\n",
Joonwoo Parkccccba72013-04-26 11:19:46 -0700842 __func__, mbhc->hph_status);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700843 mbhc->zl = mbhc->zr = 0;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800844 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Park80a01172012-10-15 16:05:23 -0700845 0, WCD9XXX_JACK_MASK);
846 mbhc->hph_status = 0;
847 }
848 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700849 /* Report insertion */
850 mbhc->hph_status |= jack_type;
851
852 if (jack_type == SND_JACK_HEADPHONE) {
853 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
854 } else if (jack_type == SND_JACK_UNSUPPORTED) {
855 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
856 } else if (jack_type == SND_JACK_HEADSET) {
857 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
858 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700859 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700860 } else if (jack_type == SND_JACK_LINEOUT) {
861 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700862 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700863
864 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
865 pr_debug("%s: Enabling micbias\n", __func__);
866 mbhc->micbias_enable_cb(mbhc->codec, true);
867 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700868
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700869 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700870 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700871
Joonwoo Parka8890262012-10-15 12:04:27 -0700872 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
873 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800874 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700875 mbhc->hph_status, WCD9XXX_JACK_MASK);
876 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
877 }
878 /* Setup insert detect */
879 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700880
881 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700882}
883
884/* should be called under interrupt context that hold suspend */
885static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
886 struct work_struct *work)
887{
888 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
889 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
890 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700891 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700892 schedule_work(work);
893}
894
895/* called under codec_resource_lock acquisition */
896static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
897 struct work_struct *work)
898{
899 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
900 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
901 mbhc->hs_detect_work_stop = true;
902 wmb();
903 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
904 if (cancel_work_sync(work)) {
905 pr_debug("%s: correct_plug_swch is canceled\n",
906 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700907 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700908 }
909 WCD9XXX_BCL_LOCK(mbhc->resmgr);
910}
911
Joonwoo Park73375212013-05-07 12:42:44 -0700912static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
913{
914 int r;
915 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700916 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
917 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700918 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700919 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700920 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700921 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700922 return r;
923}
924
Joonwoo Parka8890262012-10-15 12:04:27 -0700925static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
926{
927 s16 v_hs_max;
928 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
929
930 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
931 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
932 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700933 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700934 else
935 v_hs_max = plug_type->v_hs_max;
936 return v_hs_max;
937}
938
Joonwoo Parka8890262012-10-15 12:04:27 -0700939static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
940{
941 u8 bias_msb, bias_lsb;
942 short bias_value;
943
944 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
945 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
946 bias_value = (bias_msb << 8) | bias_lsb;
947 return bias_value;
948}
949
950static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
951{
952 u8 bias_msb, bias_lsb;
953 short bias_value;
954
955 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
956 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
957 bias_value = (bias_msb << 8) | bias_lsb;
958 return bias_value;
959}
960
961static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
962 bool on)
963{
964 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
965}
966
967static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
968 bool override_bypass, bool noreldetection)
969{
970 short bias_value;
971 struct snd_soc_codec *codec = mbhc->codec;
972
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700973 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
974 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700975 if (noreldetection)
976 wcd9xxx_turn_onoff_rel_detection(codec, false);
977
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700978 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -0700979 /* Turn on the override */
980 if (!override_bypass)
981 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
982 if (dce) {
983 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
984 0x8);
985 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
986 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
987 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700988 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
989 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700990 usleep_range(mbhc->mbhc_data.t_sta_dce,
991 mbhc->mbhc_data.t_sta_dce);
992 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
993 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
994 bias_value = wcd9xxx_read_dce_result(codec);
995 } else {
996 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
997 0x8);
998 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
999 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1000 0x0);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001001 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
1002 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001003 usleep_range(mbhc->mbhc_data.t_sta_dce,
1004 mbhc->mbhc_data.t_sta_dce);
1005 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1006 usleep_range(mbhc->mbhc_data.t_sta,
1007 mbhc->mbhc_data.t_sta);
1008 bias_value = wcd9xxx_read_sta_result(codec);
1009 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1010 0x8);
1011 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1012 }
1013 /* Turn off the override after measuring mic voltage */
1014 if (!override_bypass)
1015 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1016 0x00);
1017
1018 if (noreldetection)
1019 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001020 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1021 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001022
1023 return bias_value;
1024}
1025
1026static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1027 bool norel)
1028{
1029 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1030}
1031
Joonwoo Park520a0f92013-05-14 19:39:58 -07001032static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001033 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001034{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001035 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001036 s32 mv;
1037
1038 value = bias_value;
1039 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001040 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001041 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001042 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001043 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001044 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001045 }
1046
1047 return mv;
1048}
1049
Joonwoo Park520a0f92013-05-14 19:39:58 -07001050static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1051 u16 bias_value)
1052{
1053 s16 z;
1054 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001055 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1056 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001057}
1058
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301059/* To enable/disable bandgap and RC oscillator */
1060static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1061 bool enable)
1062{
1063 if (enable) {
1064 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1065 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1066 WCD9XXX_BANDGAP_AUDIO_MODE);
1067 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1068 WCD9XXX_CLK_RCO);
1069 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1070 } else {
1071 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1072 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1073 WCD9XXX_CLK_RCO);
1074 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1075 WCD9XXX_BANDGAP_AUDIO_MODE);
1076 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1077 }
1078}
1079
Joonwoo Parka8890262012-10-15 12:04:27 -07001080/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001081static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1082 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001083{
1084 struct snd_soc_codec *codec = mbhc->codec;
1085 short bias_value;
1086 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001087 s16 reg;
1088 int change;
1089 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1090 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001091
1092 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1093
1094 pr_debug("%s: enter\n", __func__);
1095 if (!mbhc->mbhc_cfg->calibration) {
1096 pr_err("%s: Error, no calibration exists\n", __func__);
1097 return -ENODEV;
1098 }
1099
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001100 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001101 /* Enable external voltage source to micbias if present */
1102 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1103 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001104
Joonwoo Parka8890262012-10-15 12:04:27 -07001105 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301106 * setup internal micbias if codec uses internal micbias for
1107 * headset detection
1108 */
1109 if (mbhc->mbhc_cfg->use_int_rbias) {
1110 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1111 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1112 else
1113 pr_err("%s: internal bias is requested but codec did not provide callback\n",
1114 __func__);
1115 }
1116
Joonwoo Parka8890262012-10-15 12:04:27 -07001117 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1118
1119 /* Make sure CFILT is in fast mode, save current mode */
1120 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301121 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1122 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1123 else
1124 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1125 0x70, 0x00);
1126
Joonwoo Parka8890262012-10-15 12:04:27 -07001127 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301128 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301129 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1130 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1131 else
1132 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1133 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001134
1135 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1136 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1137 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1138
1139 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1140 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1141 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1142
1143 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1144 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1145
Joonwoo Parka8890262012-10-15 12:04:27 -07001146 /* don't flip override */
1147 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301148 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001149 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1150
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001151 /* recalibrate dce_z and sta_z */
1152 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1153 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1154 btn_det->mbhc_nsc << 3);
1155 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1156 if (change)
1157 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1158 if (dce_z && sta_z) {
1159 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1160 __func__,
1161 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1162 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1163 mbhc->mbhc_data.dce_z = dce_z;
1164 mbhc->mbhc_data.sta_z = sta_z;
1165 wcd9xxx_mbhc_calc_thres(mbhc);
1166 wcd9xxx_calibrate_hs_polling(mbhc);
1167 } else {
1168 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n", __func__,
1169 dce_z, sta_z);
1170 }
1171
1172 if (is_cs_enable) {
1173 /* recalibrate dce_nsc_cs_z */
1174 reg = snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1175 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1176 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1177 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1178 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1179 if (dce_z) {
1180 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n", __func__,
1181 mbhc->mbhc_data.dce_nsc_cs_z, dce_z & 0xffff);
1182 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1183 } else {
1184 pr_debug("%s: failed get new dce_nsc_cs_z\n", __func__);
1185 }
1186 }
1187
Joonwoo Parka8890262012-10-15 12:04:27 -07001188 return bias_value;
1189}
1190
1191static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1192{
1193 struct snd_soc_codec *codec = mbhc->codec;
1194 const struct wcd9xxx_mbhc_general_cfg *generic =
1195 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1196
1197 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001198 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001199 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001200 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001201
1202 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1203 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001204 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001205
1206 usleep_range(generic->t_shutdown_plug_rem,
1207 generic->t_shutdown_plug_rem);
1208
1209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1210
Joonwoo Park533b3682013-06-13 11:41:21 -07001211 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001212 /* Put requested CLK back */
1213 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001214 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001215
1216 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1217}
1218
1219static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1220{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001221
1222 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001223 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1224
1225 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1226
Joonwoo Parka8890262012-10-15 12:04:27 -07001227
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001228 /* Disable external voltage source to micbias if present */
1229 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1230 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1231
Joonwoo Parka8890262012-10-15 12:04:27 -07001232 mbhc->polling_active = false;
1233 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001234 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001235}
1236
Joonwoo Parka8890262012-10-15 12:04:27 -07001237/* called under codec_resource_lock acquisition */
1238static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1239{
1240 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1241 if (on)
1242 usleep_range(5000, 5000);
1243}
1244
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001245static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1246{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001247 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001248
1249 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1250 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1251 goto exit;
1252 }
1253
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001254 if (on) {
1255 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1256 1 << 7, 1 << 7);
1257 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1258 1 << 4, 0);
1259 } else {
1260 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1261 1 << 4, 1 << 4);
1262 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1263 1 << 7, 0);
1264 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001265
1266exit:
1267 /*
1268 * Wait for the micbias to settle down to vddio
1269 * when the micbias to vddio switch is enabled.
1270 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001271 if (on)
1272 usleep_range(10000, 10000);
1273}
1274
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001275static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1276{
1277 u16 hph, status;
1278 struct snd_soc_codec *codec = mbhc->codec;
1279
1280 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1281 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1282 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1283 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1284 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1285 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1286 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1287 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1288 return status;
1289}
1290
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001291static enum wcd9xxx_mbhc_plug_type
1292wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1293 struct wcd9xxx_mbhc_detect *dt, const int size,
1294 bool highhph,
1295 unsigned long event_state)
1296{
1297 int i;
1298 int vdce, mb_mv;
1299 int ch, sz, delta_thr;
1300 int minv = 0, maxv = INT_MIN;
1301 struct wcd9xxx_mbhc_detect *d = dt;
1302 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1303 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1304
1305 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1306 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1307 s16 hs_max, no_mic, dce_z;
1308
1309 pr_debug("%s: enter\n", __func__);
1310 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1311
1312 sz = size - 1;
1313 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1314 if (d->mic_bias) {
1315 dce_z = mbhc->mbhc_data.dce_z;
1316 mb_mv = mbhc->mbhc_data.micb_mv;
1317 hs_max = plug_type->v_hs_max;
1318 no_mic = plug_type->v_no_mic;
1319 } else {
1320 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1321 mb_mv = VDDIO_MICBIAS_MV;
1322 hs_max = WCD9XXX_V_CS_HS_MAX;
1323 no_mic = WCD9XXX_V_CS_NO_MIC;
1324 }
1325
1326 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1327 dce_z, (u32)mb_mv);
1328
1329 d->_vdces = vdce;
1330 if (d->_vdces < no_mic)
1331 d->_type = PLUG_TYPE_HEADPHONE;
1332 else if (d->_vdces >= hs_max)
1333 d->_type = PLUG_TYPE_HIGH_HPH;
1334 else
1335 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
1369 delta_thr = highhph ? WCD9XXX_MB_MEAS_DELTA_MAX_MV :
1370 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1371
1372 for (i = 0, d = dt; i < sz; i++, d++) {
1373 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1374 (d->_type != dprev->_type)) {
1375 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1376 type = PLUG_TYPE_INVALID;
1377 goto exit;
1378 }
1379
1380 if (!d->swap_gnd && !d->mic_bias &&
1381 (abs(minv - d->_vdces) > delta_thr ||
1382 abs(maxv - d->_vdces) > delta_thr)) {
1383 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1384 __func__, d->_vdces, minv, maxv);
1385 type = PLUG_TYPE_INVALID;
1386 goto exit;
1387 } else if (d->swap_gnd) {
1388 dgnd = d;
1389 }
1390
1391 if (!d->mic_bias && !d->swap_gnd)
1392 dprev = d;
1393 else if (d->mic_bias)
1394 dmicbias = d;
1395 }
1396 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1397 dt->_type != dgnd->_type) {
1398 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1399 type = PLUG_TYPE_INVALID;
1400 goto exit;
1401 }
1402
1403 type = dt->_type;
1404 if (dmicbias) {
1405 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1406 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1407 dt->_type == PLUG_TYPE_HEADSET)) {
1408 type = PLUG_TYPE_HEADSET;
1409 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1410 pr_debug("%s: Headset with threshold on MIC detected\n",
1411 __func__);
1412 if (mbhc->mbhc_cfg->micbias_enable_flags &
1413 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1414 mbhc->micbias_enable = true;
1415 }
1416 }
1417 }
1418
1419 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1420 if (((type == PLUG_TYPE_HEADSET ||
1421 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1422 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1423 __func__, type);
1424 type = PLUG_TYPE_INVALID;
1425 }
1426 }
1427 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1428 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1429 minv) &&
1430 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1431 maxv))
1432 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001433 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001434 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1435 type = PLUG_TYPE_INVALID;
1436 }
1437 }
1438exit:
1439 pr_debug("%s: Plug type %d detected\n", __func__, type);
1440 return type;
1441}
1442
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001443/*
1444 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1445 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001446 * param mbhc wcd9xxx_mbhc structure
1447 * param dt collected measurements
1448 * param size array size of dt
1449 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001450 */
1451static enum wcd9xxx_mbhc_plug_type
1452wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001453 struct wcd9xxx_mbhc_detect *dt, const int size,
1454 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001455{
1456 int i;
1457 int ch;
1458 enum wcd9xxx_mbhc_plug_type type;
1459 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001460 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001461 int maxv = 0, minv = 0;
1462 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1463 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1464 const s16 hs_max = plug_type->v_hs_max;
1465 const s16 no_mic = plug_type->v_no_mic;
1466
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001467 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1468
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001469 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1470 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1471 if (d->vddio)
1472 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1473 else
1474 d->_vdces = vdce;
1475
1476 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1477 d->_type = PLUG_TYPE_HEADSET;
1478 else if (d->_vdces < no_mic)
1479 d->_type = PLUG_TYPE_HEADPHONE;
1480 else
1481 d->_type = PLUG_TYPE_HIGH_HPH;
1482
1483 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001484 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001485 if (maxv < d->_vdces)
1486 maxv = d->_vdces;
1487 if (!minv || minv > d->_vdces)
1488 minv = d->_vdces;
1489 }
1490
1491 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1492 __func__, i, d->dce, vdce, d->_vdces,
1493 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1494 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001495
1496
1497 /*
1498 * If GND and MIC prongs are aligned to HPHR and GND of
1499 * headphone, codec measures the voltage based on
1500 * impedance between HPHR and GND which results in ~80mv.
1501 * Avoid this.
1502 */
1503 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1504 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1505 pr_debug("%s: within invalid range\n", __func__);
1506 type = PLUG_TYPE_INVALID;
1507 goto exit;
1508 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001509 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001510
1511 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1512 pr_debug("%s: HPHL PA was ON\n", __func__);
1513 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001514 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1515 type = PLUG_TYPE_INVALID;
1516 goto exit;
1517 }
1518
Joonwoo Parka84ec452013-07-17 15:02:52 -07001519 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001520 if (d->vddio) {
1521 dvddio = d;
1522 continue;
1523 }
1524
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001525 if ((i > 0) && (d->_type != dprev->_type)) {
1526 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1527 type = PLUG_TYPE_INVALID;
1528 goto exit;
1529 }
1530
1531 if (!d->swap_gnd && !d->hwvalue &&
1532 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1533 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1534 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1535 __func__, d->_vdces, minv, maxv);
1536 type = PLUG_TYPE_INVALID;
1537 goto exit;
1538 } else if (d->swap_gnd) {
1539 dgnd = d;
1540 }
1541 dprev = d;
1542 }
1543
1544 WARN_ON(i != size);
1545 type = dt->_type;
1546 if (type == PLUG_TYPE_HEADSET && dgnd) {
1547 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1548 minv) &&
1549 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1550 maxv))
1551 type = PLUG_TYPE_GND_MIC_SWAP;
1552 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001553
1554 /* if HPHL PA was on, we cannot use hphl status */
1555 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1556 if (((type == PLUG_TYPE_HEADSET ||
1557 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1558 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1559 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1560 __func__, type);
1561 type = PLUG_TYPE_INVALID;
1562 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001563 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001564
Joonwoo Parkccccba72013-04-26 11:19:46 -07001565 if (type == PLUG_TYPE_HEADSET && dvddio) {
1566 if ((dvddio->_vdces > hs_max) ||
1567 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1568 pr_debug("%s: Headset with threshold on MIC detected\n",
1569 __func__);
1570 if (mbhc->mbhc_cfg->micbias_enable_flags &
1571 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1572 mbhc->micbias_enable = true;
1573 } else {
1574 pr_debug("%s: Headset with regular MIC detected\n",
1575 __func__);
1576 if (mbhc->mbhc_cfg->micbias_enable_flags &
1577 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1578 mbhc->micbias_enable = true;
1579 }
1580 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001581exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001582 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1583 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001584 return type;
1585}
1586
Joonwoo Parkccccba72013-04-26 11:19:46 -07001587/*
1588 * Pull down MBHC micbias for provided duration in microsecond.
1589 */
1590static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1591{
1592 bool micbiasconn = false;
1593 struct snd_soc_codec *codec = mbhc->codec;
1594 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1595
1596 /*
1597 * Disable MBHC to micbias connection to pull down
1598 * micbias and pull down micbias for a moment.
1599 */
1600 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1601 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1602 return -EFAULT;
1603 }
1604
1605 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1606 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1607 1 << 4, 0);
1608 micbiasconn = true;
1609 }
1610
1611 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1612
1613 /*
1614 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1615 * able to get consistent result across DCEs.
1616 */
1617 usleep_range(1000, 1000 + 10);
1618
1619 if (micbiasconn)
1620 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1621 1 << 4, 1 << 4);
1622 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1623 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1624
1625 return 0;
1626}
1627
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001628void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1629 bool highhph)
1630{
1631
1632 struct snd_soc_codec *codec;
1633 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1634 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1635 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1636
1637 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1638 codec = mbhc->codec;
1639
1640 if (on) {
1641 pr_debug("%s: enabling current source\n", __func__);
1642 /* Nsc to 9 */
1643 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1644 0x78, 0x48);
1645 /* pull down diode bit to 0 */
1646 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1647 0x01, 0x00);
1648 /*
1649 * Keep the low power insertion/removal
1650 * detection (reg 0x3DD) disabled
1651 */
1652 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1653 0x01, 0x00);
1654 /*
1655 * Enable the Mic Bias current source
1656 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1657 * Write bit[7] of register MICB_2_MBHC to 1
1658 * (INS_DET_ISRC_EN__ENABLE)
1659 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1660 */
1661 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1662 0xF0, 0xF0);
1663 /* Disconnect MBHC Override from MicBias and LDOH */
1664 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1665 } else {
1666 pr_debug("%s: disabling current source\n", __func__);
1667 /* Connect MBHC Override from MicBias and LDOH */
1668 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1669 /* INS_DET_ISRC_CTL to acdb value */
1670 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1671 0x60, plug_det->mic_current << 5);
1672 if (!highhph) {
1673 /* INS_DET_ISRC_EN__ENABLE to 0 */
1674 snd_soc_update_bits(codec,
1675 mbhc->mbhc_bias_regs.mbhc_reg,
1676 0x80, 0x00);
1677 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1678 snd_soc_update_bits(codec,
1679 mbhc->mbhc_bias_regs.mbhc_reg,
1680 0x10, 0x00);
1681 }
1682 /* Nsc to acdb value */
1683 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1684 btn_det->mbhc_nsc << 3);
1685 }
1686}
1687
1688static enum wcd9xxx_mbhc_plug_type
1689wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1690{
1691 struct snd_soc_codec *codec = mbhc->codec;
1692 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1693 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1694 int i;
1695
1696 pr_debug("%s: enter\n", __func__);
1697 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1698
1699 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1700
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301701 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001702 rt[0].swap_gnd = false;
1703 rt[0].vddio = false;
1704 rt[0].hwvalue = true;
1705 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1706 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1707 rt[0].mic_bias = false;
1708
1709 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1710 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1711 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1712 highhph);
1713 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1714 if (rt[i].swap_gnd)
1715 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1716
1717 if (rt[i].mic_bias)
1718 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1719
1720 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1721 if (rt[i].mic_bias)
1722 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1723 if (rt[i].swap_gnd)
1724 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1725 }
1726
1727 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1728 mbhc->event_state);
1729
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301730 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001731 pr_debug("%s: plug_type:%d\n", __func__, type);
1732
1733 return type;
1734}
1735
Joonwoo Parka8890262012-10-15 12:04:27 -07001736static enum wcd9xxx_mbhc_plug_type
1737wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1738{
1739 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001740 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001741 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001742 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1743 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001744 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001745
Joonwoo Park80a01172012-10-15 16:05:23 -07001746 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001747 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1748
1749 /* make sure override is on */
1750 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1751
1752 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001753 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001754
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001755 /*
1756 * There are chances vddio switch is on and cfilt voltage is adjusted
1757 * to vddio voltage even after plug type removal reported.
1758 */
1759 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1760 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1761
Joonwoo Parka8890262012-10-15 12:04:27 -07001762 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001763 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001764
Joonwoo Parkccccba72013-04-26 11:19:46 -07001765 /*
1766 * cfilter in fast mode requires 1ms to charge up and down micbias
1767 * fully.
1768 */
1769 (void) wcd9xxx_pull_down_micbias(mbhc,
1770 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301771
1772 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001773 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001774 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001775 rt[0].swap_gnd = false;
1776 rt[0].vddio = false;
1777 rt[0].hwvalue = true;
1778 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1779 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1780 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001781 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001782 else
1783 rt[i].vddio = false;
1784 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1785 rt[i].hwvalue = false;
1786 if (rt[i].swap_gnd)
1787 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1788 if (rt[i].vddio)
1789 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001790 /*
1791 * Pull down micbias to detect headset with mic which has
1792 * threshold and to have more consistent voltage measurements.
1793 *
1794 * cfilter in fast mode requires 1ms to charge up and down
1795 * micbias fully.
1796 */
1797 (void) wcd9xxx_pull_down_micbias(mbhc,
1798 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001799 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1800 if (rt[i].vddio)
1801 wcd9xxx_onoff_vddio_switch(mbhc, false);
1802 if (rt[i].swap_gnd)
1803 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001804 }
1805
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001806 if (vddioon)
1807 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1808
1809 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1810 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001811
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301812 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001813 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001814 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001815}
1816
1817static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1818{
1819 if (mbhc->mbhc_cfg->gpio)
1820 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1821 mbhc->mbhc_cfg->gpio_level_insert);
1822 else if (mbhc->mbhc_cfg->insert_detect)
1823 return snd_soc_read(mbhc->codec,
1824 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1825 (1 << 2);
1826 else
1827 WARN(1, "Invalid jack detection configuration\n");
1828
1829 return true;
1830}
1831
1832static bool is_clk_active(struct snd_soc_codec *codec)
1833{
1834 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1835}
1836
1837static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1838 int insertion, int trigger, bool padac_off)
1839{
1840 struct snd_soc_codec *codec = mbhc->codec;
1841 int central_bias_enabled = 0;
1842 const struct wcd9xxx_mbhc_general_cfg *generic =
1843 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1844 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1845 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1846
Joonwoo Park80a01172012-10-15 16:05:23 -07001847 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1848 __func__, insertion, trigger);
1849
Joonwoo Parka8890262012-10-15 12:04:27 -07001850 if (!mbhc->mbhc_cfg->calibration) {
1851 pr_err("Error, no wcd9xxx calibration\n");
1852 return -EINVAL;
1853 }
1854
1855 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1856
1857 /*
1858 * Make sure mic bias and Mic line schmitt trigger
1859 * are turned OFF
1860 */
1861 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1862 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1863
1864 if (insertion) {
1865 wcd9xxx_switch_micbias(mbhc, 0);
1866
1867 /* DAPM can manipulate PA/DAC bits concurrently */
1868 if (padac_off == true)
1869 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1870
1871 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1872 /* Enable HPH Schmitt Trigger */
1873 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1874 0x11);
1875 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1876 plug_det->hph_current << 2);
1877 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1878 0x02);
1879 }
1880 if (trigger & MBHC_USE_MB_TRIGGER) {
1881 /* enable the mic line schmitt trigger */
1882 snd_soc_update_bits(codec,
1883 mbhc->mbhc_bias_regs.mbhc_reg,
1884 0x60, plug_det->mic_current << 5);
1885 snd_soc_update_bits(codec,
1886 mbhc->mbhc_bias_regs.mbhc_reg,
1887 0x80, 0x80);
1888 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1889 snd_soc_update_bits(codec,
1890 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1891 0x00);
1892 snd_soc_update_bits(codec,
1893 mbhc->mbhc_bias_regs.mbhc_reg,
1894 0x10, 0x10);
1895 }
1896
1897 /* setup for insetion detection */
1898 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1899 } else {
1900 pr_debug("setup for removal detection\n");
1901 /* Make sure the HPH schmitt trigger is OFF */
1902 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1903
1904 /* enable the mic line schmitt trigger */
1905 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1906 0x01, 0x00);
1907 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1908 plug_det->mic_current << 5);
1909 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1910 0x80, 0x80);
1911 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1912 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1913 0x10, 0x10);
1914
1915 /* Setup for low power removal detection */
1916 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1917 0x2);
1918 }
1919
1920 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1921 /* called by interrupt */
1922 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001923 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001924 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1925 0x06, 0);
1926 usleep_range(generic->t_shutdown_plug_rem,
1927 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001928 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001929 } else
1930 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1931 0x06, 0);
1932 }
1933
1934 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1935
1936 /* If central bandgap disabled */
1937 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1938 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1939 usleep_range(generic->t_bg_fast_settle,
1940 generic->t_bg_fast_settle);
1941 central_bias_enabled = 1;
1942 }
1943
1944 /* If LDO_H disabled */
1945 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1946 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1947 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1948 usleep_range(generic->t_ldoh, generic->t_ldoh);
1949 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1950
1951 if (central_bias_enabled)
1952 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1953 0);
1954 }
1955
Meng Wangeeaaaba2013-09-09 18:37:32 +08001956 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301957 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1958 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001959
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001960 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001961 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001962 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001963
1964 return 0;
1965}
1966
1967/* called under codec_resource_lock acquisition */
1968static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1969 enum wcd9xxx_mbhc_plug_type plug_type)
1970{
Joonwoo Park80a01172012-10-15 16:05:23 -07001971 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1972 __func__, mbhc->current_plug, plug_type);
1973
Joonwoo Parka8890262012-10-15 12:04:27 -07001974 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1975
1976 if (plug_type == PLUG_TYPE_HEADPHONE &&
1977 mbhc->current_plug == PLUG_TYPE_NONE) {
1978 /*
1979 * Nothing was reported previously
1980 * report a headphone or unsupported
1981 */
1982 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
1983 wcd9xxx_cleanup_hs_polling(mbhc);
1984 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07001985 if (!mbhc->mbhc_cfg->detect_extn_cable) {
1986 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
1987 wcd9xxx_report_plug(mbhc, 0,
1988 SND_JACK_HEADSET);
1989 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
1990 wcd9xxx_report_plug(mbhc, 0,
1991 SND_JACK_HEADPHONE);
1992 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001993 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
1994 wcd9xxx_cleanup_hs_polling(mbhc);
1995 } else if (plug_type == PLUG_TYPE_HEADSET) {
1996 /*
1997 * If Headphone was reported previously, this will
1998 * only report the mic line
1999 */
2000 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302001 /* Button detection required RC oscillator */
2002 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002003 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002004
2005 /* if PA is already on, switch micbias source to VDDIO */
2006 if (mbhc->event_state &
2007 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2008 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002009 wcd9xxx_start_hs_polling(mbhc);
2010 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002011 if (mbhc->mbhc_cfg->detect_extn_cable) {
2012 /* High impedance device found. Report as LINEOUT*/
2013 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2014 wcd9xxx_cleanup_hs_polling(mbhc);
2015 pr_debug("%s: setup mic trigger for further detection\n",
2016 __func__);
2017 mbhc->lpi_enabled = true;
2018 /*
2019 * Do not enable HPHL trigger. If playback is active,
2020 * it might lead to continuous false HPHL triggers
2021 */
2022 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2023 false);
2024 } else {
2025 if (mbhc->current_plug == PLUG_TYPE_NONE)
2026 wcd9xxx_report_plug(mbhc, 1,
2027 SND_JACK_HEADPHONE);
2028 wcd9xxx_cleanup_hs_polling(mbhc);
2029 pr_debug("setup mic trigger for further detection\n");
2030 mbhc->lpi_enabled = true;
2031 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2032 MBHC_USE_HPHL_TRIGGER,
2033 false);
2034 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002035 } else {
2036 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2037 mbhc->current_plug, plug_type);
2038 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002039 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002040}
2041
2042/* called under codec_resource_lock acquisition */
2043static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2044{
2045 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002046 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002047
2048 pr_debug("%s: enter\n", __func__);
2049
2050 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002051 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2052 (1 << MBHC_CS_ENABLE_INSERTION)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002053
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002054 if (current_source_enable) {
2055 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2056 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2057 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2058 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002059 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002060 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002061 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002062 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002063
2064 if (wcd9xxx_swch_level_remove(mbhc)) {
2065 pr_debug("%s: Switch level is low when determining plug\n",
2066 __func__);
2067 return;
2068 }
2069
2070 if (plug_type == PLUG_TYPE_INVALID ||
2071 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002072 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002073 wcd9xxx_schedule_hs_detect_plug(mbhc,
2074 &mbhc->correct_plug_swch);
2075 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2076 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002077 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002078 wcd9xxx_schedule_hs_detect_plug(mbhc,
2079 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002080 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002081 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002082 wcd9xxx_schedule_hs_detect_plug(mbhc,
2083 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002084 } else {
2085 pr_debug("%s: Valid plug found, determine plug type %d\n",
2086 __func__, plug_type);
2087 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2088 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002089 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002090}
2091
2092/* called under codec_resource_lock acquisition */
2093static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2094{
Joonwoo Park80a01172012-10-15 16:05:23 -07002095 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002096 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2097
Joonwoo Parka8890262012-10-15 12:04:27 -07002098 if (wcd9xxx_swch_level_remove(mbhc))
2099 pr_debug("%s: Switch level low when determining plug\n",
2100 __func__);
2101 else
2102 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002103 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002104}
2105
2106/* called only from interrupt which is under codec_resource_lock acquisition */
2107static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2108 bool is_removal)
2109{
2110 if (!is_removal) {
2111 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2112
2113 rmb();
2114 if (mbhc->lpi_enabled)
2115 msleep(100);
2116
2117 rmb();
2118 if (!mbhc->lpi_enabled) {
2119 pr_debug("%s: lpi is disabled\n", __func__);
2120 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2121 pr_debug("%s: Valid insertion, detect plug type\n",
2122 __func__);
2123 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2124 } else {
2125 pr_debug("%s: Invalid insertion stop plug detection\n",
2126 __func__);
2127 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002128 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2129 pr_debug("%s: Removal\n", __func__);
2130 if (!wcd9xxx_swch_level_remove(mbhc)) {
2131 /*
2132 * Switch indicates, something is still inserted.
2133 * This could be extension cable i.e. headset is
2134 * removed from extension cable.
2135 */
2136 /* cancel detect plug */
2137 wcd9xxx_cancel_hs_detect_plug(mbhc,
2138 &mbhc->correct_plug_swch);
2139 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2140 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002141 } else {
2142 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2143 }
2144}
2145
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002146static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2147 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002148{
2149 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2150 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2151 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2152
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002153 if (cs_enable)
2154 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2155 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2156 else
2157 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2158 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2159 (mic_mv > plug_type->v_no_mic) &&
2160 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002161}
2162
2163/*
2164 * called under codec_resource_lock acquisition
2165 * returns true if mic voltage range is back to normal insertion
2166 * returns false either if timedout or removed
2167 */
2168static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2169{
2170 int i;
2171 bool timedout, settled = false;
2172 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2173 short mb_v[NUM_DCE_PLUG_DETECT];
2174 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002175 bool cs_enable;
2176
2177 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2178 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0);
2179
2180 if (cs_enable)
2181 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002182
2183 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2184 while (!(timedout = time_after(jiffies, timeout))) {
2185 retry++;
2186 if (wcd9xxx_swch_level_remove(mbhc)) {
2187 pr_debug("%s: Switch indicates removal\n", __func__);
2188 break;
2189 }
2190
2191 if (retry > 1)
2192 msleep(250);
2193 else
2194 msleep(50);
2195
2196 if (wcd9xxx_swch_level_remove(mbhc)) {
2197 pr_debug("%s: Switch indicates removal\n", __func__);
2198 break;
2199 }
2200
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002201 if (cs_enable) {
2202 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2203 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2204 true, true);
2205 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2206 true,
2207 mb_v[i],
2208 mbhc->mbhc_data.dce_nsc_cs_z,
2209 (u32)VDDIO_MICBIAS_MV);
2210 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2211 __func__, retry, mic_mv[i], mb_v[i]);
2212 }
2213 } else {
2214 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2215 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2216 true);
2217 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2218 mb_v[i]);
2219 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2220 __func__, retry, mic_mv[i],
2221 mb_v[i]);
2222 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002223 }
2224
2225 if (wcd9xxx_swch_level_remove(mbhc)) {
2226 pr_debug("%s: Switcn indicates removal\n", __func__);
2227 break;
2228 }
2229
2230 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2231 pr_debug("%s : headset/headphone is removed\n",
2232 __func__);
2233 break;
2234 }
2235
2236 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002237 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002238 break;
2239
2240 if (i == NUM_DCE_PLUG_DETECT) {
2241 pr_debug("%s: MIC voltage settled\n", __func__);
2242 settled = true;
2243 msleep(200);
2244 break;
2245 }
2246 }
2247
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002248 if (cs_enable)
2249 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2250
Joonwoo Parka8890262012-10-15 12:04:27 -07002251 if (timedout)
2252 pr_debug("%s: Microphone did not settle in %d seconds\n",
2253 __func__, HS_DETECT_PLUG_TIME_MS);
2254 return settled;
2255}
2256
2257/* called only from interrupt which is under codec_resource_lock acquisition */
2258static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2259{
Joonwoo Park80a01172012-10-15 16:05:23 -07002260 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002261 if (wcd9xxx_hs_remove_settle(mbhc))
2262 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002263 pr_debug("%s: leave\n", __func__);
2264}
2265
2266/* called only from interrupt which is under codec_resource_lock acquisition */
2267static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2268{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002269 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002270 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002271 bool removed = true;
2272 struct snd_soc_codec *codec = mbhc->codec;
2273 const struct wcd9xxx_mbhc_general_cfg *generic =
2274 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002275 bool cs_enable;
2276 s16 cur_v_ins_h;
2277 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002278
2279 pr_debug("%s: enter\n", __func__);
2280 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2281 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2282 __func__);
2283 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2284 0x08, 0x08);
2285 return;
2286 }
2287
2288 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002289 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002290
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002291 /* If micbias is enabled, don't enable current source */
2292 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2293 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2294 (!(snd_soc_read(codec,
2295 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002296 if (cs_enable)
2297 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2298
Joonwoo Park50ae0512013-06-04 16:53:12 -07002299 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002300 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002301 if (cs_enable) {
2302 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2303 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2304 mb_mv = VDDIO_MICBIAS_MV;
2305 } else {
2306 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2307 dcez = mbhc->mbhc_data.dce_z;
2308 mb_mv = mbhc->mbhc_data.micb_mv;
2309 }
2310
Joonwoo Park50ae0512013-06-04 16:53:12 -07002311 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002312 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2313 dcez, mb_mv));
2314
2315 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2316 (wcd9xxx_get_current_v(mbhc,
2317 WCD9XXX_CURRENT_V_INS_H));
2318
2319 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002320 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002321 break;
2322 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002323 } while (!time_after(jiffies, timeout));
2324 pr_debug("%s: headset %sactually removed\n", __func__,
2325 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002326
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002327 if (cs_enable)
2328 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2329
Joonwoo Park80a01172012-10-15 16:05:23 -07002330 if (removed) {
2331 if (mbhc->mbhc_cfg->detect_extn_cable) {
2332 if (!wcd9xxx_swch_level_remove(mbhc)) {
2333 /*
2334 * extension cable is still plugged in
2335 * report it as LINEOUT device
2336 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302337 if (mbhc->hph_status == SND_JACK_HEADSET)
2338 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2339 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002340 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2341 wcd9xxx_cleanup_hs_polling(mbhc);
2342 wcd9xxx_enable_hs_detect(mbhc, 1,
2343 MBHC_USE_MB_TRIGGER,
2344 false);
2345 }
2346 } else {
2347 /* Cancel possibly running hs_detect_work */
2348 wcd9xxx_cancel_hs_detect_plug(mbhc,
2349 &mbhc->correct_plug_noswch);
2350 /*
2351 * If this removal is not false, first check the micbias
2352 * switch status and switch it to LDOH if it is already
2353 * switched to VDDIO.
2354 */
2355 wcd9xxx_switch_micbias(mbhc, 0);
2356
2357 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302358 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002359 wcd9xxx_cleanup_hs_polling(mbhc);
2360 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2361 MBHC_USE_HPHL_TRIGGER,
2362 true);
2363 }
2364 } else {
2365 wcd9xxx_start_hs_polling(mbhc);
2366 }
2367 pr_debug("%s: leave\n", __func__);
2368}
2369
2370/* called only from interrupt which is under codec_resource_lock acquisition */
2371static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2372 bool is_mb_trigger)
2373{
2374 /* Cancel possibly running hs_detect_work */
2375 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2376
2377 if (is_mb_trigger) {
2378 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2379 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2380 } else {
2381 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2382 __func__);
2383 wcd9xxx_mbhc_detect_plug_type(mbhc);
2384 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002385}
2386
2387static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2388{
Joonwoo Parka8890262012-10-15 12:04:27 -07002389 struct wcd9xxx_mbhc *mbhc = data;
2390
2391 pr_debug("%s: enter, removal interrupt\n", __func__);
2392 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002393 /*
2394 * While we don't know whether MIC is there or not, let the resmgr know
2395 * so micbias can be disabled temporarily
2396 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002397 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002398 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2399 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002400 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2401 WCD9XXX_COND_HPH, false);
2402 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2403 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2404 WCD9XXX_COND_HPH, false);
2405 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002406
Joonwoo Park80a01172012-10-15 16:05:23 -07002407 if (mbhc->mbhc_cfg->detect_extn_cable &&
2408 !wcd9xxx_swch_level_remove(mbhc))
2409 wcd9xxx_hs_remove_irq_noswch(mbhc);
2410 else
2411 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002412
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002413 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2414 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2415 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002416 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2417 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002418 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2419 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2420 WCD9XXX_COND_HPH, true);
2421 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002422 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2423
2424 return IRQ_HANDLED;
2425}
2426
2427static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2428{
2429 bool is_mb_trigger, is_removal;
2430 struct wcd9xxx_mbhc *mbhc = data;
2431 struct snd_soc_codec *codec = mbhc->codec;
2432
2433 pr_debug("%s: enter\n", __func__);
2434 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002435 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002436
2437 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2438 0x10);
2439 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2440 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2441
2442 /* Turn off both HPH and MIC line schmitt triggers */
2443 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2444 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2445 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2446
Joonwoo Park80a01172012-10-15 16:05:23 -07002447 if (mbhc->mbhc_cfg->detect_extn_cable &&
2448 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2449 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2450 else
2451 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002452
2453 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2454 return IRQ_HANDLED;
2455}
2456
2457static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2458{
2459 struct delayed_work *dwork;
2460 short bias_value;
2461 int dce_mv, sta_mv;
2462 struct wcd9xxx_mbhc *mbhc;
2463
2464 pr_debug("%s:\n", __func__);
2465
2466 dwork = to_delayed_work(work);
2467 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2468
2469 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2470 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2471
2472 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2473 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2474 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2475
2476 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002477 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002478 mbhc->buttons_pressed);
2479
2480 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002481 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002482}
2483
2484static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2485{
2486 struct delayed_work *dwork;
2487 struct wcd9xxx_mbhc *mbhc;
2488 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002489 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002490
2491 dwork = to_delayed_work(work);
2492 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2493 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002494 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002495
2496 pr_debug("%s:\n", __func__);
2497
2498 /* Turn off both HPH and MIC line schmitt triggers */
2499 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2500 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2501 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002502 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002503 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002504 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002505}
2506
2507static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2508{
2509 u32 cfg_offset;
2510 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2511 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2512
2513 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2514 return false;
2515
2516 /*
2517 * Previous check guarantees that there is enough fw data up
2518 * to num_btn
2519 */
2520 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2521 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2522 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2523 return false;
2524
2525 /*
2526 * Previous check guarantees that there is enough fw data up
2527 * to start of impedance detection configuration
2528 */
2529 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2530 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2531
2532 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2533 return false;
2534
2535 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2536 return false;
2537
2538 return true;
2539}
2540
2541static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002542 enum meas_type dce, s16 vin_mv,
2543 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002544{
2545 s16 diff, zero;
2546 u32 mb_mv, in;
2547 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002548 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002549
2550 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002551 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002552
2553 if (mb_mv == 0) {
2554 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2555 return -EINVAL;
2556 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002557 if (cs_enable) {
2558 mb_mv = VDDIO_MICBIAS_MV;
2559 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2560 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002561
2562 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002563 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2564 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002565 } else {
2566 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2567 zero = (mbhc->mbhc_data.sta_z);
2568 }
2569 in = (u32) diff * vin_mv;
2570
2571 value = (u16) (in / mb_mv) + zero;
2572 return value;
2573}
2574
2575static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2576{
2577 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002578 s16 adj_v_hs_max;
2579 s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM];
Joonwoo Parka8890262012-10-15 12:04:27 -07002580 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2581 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2582 u16 *btn_high;
2583 int i;
2584
2585 pr_debug("%s: enter\n", __func__);
2586 codec = mbhc->codec;
2587 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2588 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2589
Joonwoo Park73375212013-05-07 12:42:44 -07002590 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002591 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002592 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002593 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002594
2595 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2596 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2597
2598 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002599 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2600 true);
2601 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002602 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002603 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002604 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002605 mbhc->mbhc_data.v_inval_ins_low =
2606 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2607 false);
2608 mbhc->mbhc_data.v_inval_ins_high =
2609 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2610 false);
2611 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002612 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2613 WCD9XXX_V_CS_HS_MAX,
2614 true);
2615 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2616 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002617
2618 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2619 MBHC_BTN_DET_V_BTN_HIGH);
2620 for (i = 0; i < btn_det->num_btn; i++)
2621 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2622
Joonwoo Park73375212013-05-07 12:42:44 -07002623 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2624 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2625 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2626 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2627 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2628 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002629
Joonwoo Park73375212013-05-07 12:42:44 -07002630 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002631 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2632 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002633 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002634 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2635 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002636 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002637 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2638 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002639 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002640 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2641 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002642
Joonwoo Park73375212013-05-07 12:42:44 -07002643 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2644 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2645 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2646 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002647
Joonwoo Parka8890262012-10-15 12:04:27 -07002648 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2649
2650 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002651 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002652 pr_debug("%s: leave\n", __func__);
2653}
2654
2655static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2656{
2657 /*
2658 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2659 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2660 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2661 */
2662 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2663}
2664
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002665/*
2666 * Mic Bias Enable Decision
2667 * Return true if high_hph_cnt is a power of 2 (!= 2)
2668 * otherwise return false
2669 */
2670static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2671{
2672 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2673}
2674
Joonwoo Parka8890262012-10-15 12:04:27 -07002675static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2676{
2677 struct wcd9xxx_mbhc *mbhc;
2678 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002679 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002680 unsigned long timeout;
2681 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002682 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002683 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002684 bool current_source_enable;
2685 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002686
2687 pr_debug("%s: enter\n", __func__);
2688
2689 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2690 codec = mbhc->codec;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002691 current_source_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2692 (1 << MBHC_CS_ENABLE_POLLING)) != 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002693
2694 wcd9xxx_onoff_ext_mclk(mbhc, true);
2695
2696 /*
2697 * Keep override on during entire plug type correction work.
2698 *
2699 * This is okay under the assumption that any switch irqs which use
2700 * MBHC block cancel and sync this work so override is off again
2701 * prior to switch interrupt handler's MBHC block usage.
2702 * Also while this correction work is running, we can guarantee
2703 * DAPM doesn't use any MBHC block as this work only runs with
2704 * headphone detection.
2705 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002706 if (current_source_enable)
2707 wcd9xxx_turn_onoff_current_source(mbhc, true,
2708 false);
2709 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002710 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002711
2712 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2713 while (!time_after(jiffies, timeout)) {
2714 ++retry;
2715 rmb();
2716 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002717 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002718 pr_debug("%s: stop requested\n", __func__);
2719 break;
2720 }
2721
2722 msleep(HS_DETECT_PLUG_INERVAL_MS);
2723 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002724 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002725 pr_debug("%s: Switch level is low\n", __func__);
2726 break;
2727 }
2728
2729 /* can race with removal interrupt */
2730 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002731 if (current_source_enable)
2732 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2733 highhph);
2734 else
2735 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002736 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2737
Joonwoo Park80a01172012-10-15 16:05:23 -07002738 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2739 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002740
2741 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2742 (highhph_cnt + 1) :
2743 0;
2744 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002745 if (plug_type == PLUG_TYPE_INVALID) {
2746 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002747 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2748 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002749 mbhc->current_plug == PLUG_TYPE_NONE) {
2750 wcd9xxx_report_plug(mbhc, 1,
2751 SND_JACK_HEADPHONE);
2752 }
2753 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2754 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002755 if (mbhc->mbhc_cfg->detect_extn_cable) {
2756 if (mbhc->current_plug != plug_type)
2757 wcd9xxx_report_plug(mbhc, 1,
2758 SND_JACK_HEADPHONE);
2759 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002760 wcd9xxx_report_plug(mbhc, 1,
2761 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002762 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002763 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2764 pr_debug("%s: High HPH detected, continue polling\n",
2765 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002766 } else {
2767 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2768 pt_gnd_mic_swap_cnt++;
2769 if (pt_gnd_mic_swap_cnt <
2770 GND_MIC_SWAP_THRESHOLD)
2771 continue;
2772 else if (pt_gnd_mic_swap_cnt >
2773 GND_MIC_SWAP_THRESHOLD) {
2774 /*
2775 * This is due to GND/MIC switch didn't
2776 * work, Report unsupported plug
2777 */
2778 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2779 /*
2780 * if switch is toggled, check again,
2781 * otherwise report unsupported plug
2782 */
2783 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2784 continue;
2785 }
2786 } else
2787 pt_gnd_mic_swap_cnt = 0;
2788
2789 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002790 /* Turn off override/current source */
2791 if (current_source_enable)
2792 wcd9xxx_turn_onoff_current_source(mbhc, false,
2793 false);
2794 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002795 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002796 /*
2797 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2798 */
2799 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2800 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2801 pr_debug("Attempt %d found correct plug %d\n", retry,
2802 plug_type);
2803 correction = true;
2804 break;
2805 }
2806 }
2807
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002808 highhph = false;
2809 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002810 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2811 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002812 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002813 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002814 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002815 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002816 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002817
2818 if (!correction && current_source_enable)
2819 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2820 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002821 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002822
2823 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002824
2825 if (mbhc->mbhc_cfg->detect_extn_cable) {
2826 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002827 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2828 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002829 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2830 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002831 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002832 /* Enable removal detection */
2833 wcd9xxx_cleanup_hs_polling(mbhc);
2834 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2835 }
2836 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2837 }
2838 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002839 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002840 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002841}
2842
2843static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2844{
2845 bool insert;
2846 bool is_removed = false;
2847 struct snd_soc_codec *codec = mbhc->codec;
2848
2849 pr_debug("%s: enter\n", __func__);
2850
2851 mbhc->in_swch_irq_handler = true;
2852 /* Wait here for debounce time */
2853 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2854
2855 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2856
2857 /* cancel pending button press */
2858 if (wcd9xxx_cancel_btn_work(mbhc))
2859 pr_debug("%s: button press is canceled\n", __func__);
2860
2861 insert = !wcd9xxx_swch_level_remove(mbhc);
2862 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2863 mbhc->current_plug, insert);
2864 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2865 mbhc->lpi_enabled = false;
2866 wmb();
2867
2868 /* cancel detect plug */
2869 wcd9xxx_cancel_hs_detect_plug(mbhc,
2870 &mbhc->correct_plug_swch);
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002871 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2872 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2873 (1 << 1)))
2874 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002875
2876 /* Disable Mic Bias pull down and HPH Switch to GND */
2877 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2878 0x00);
2879 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2880 wcd9xxx_mbhc_detect_plug_type(mbhc);
2881 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2882 mbhc->lpi_enabled = false;
2883 wmb();
2884
2885 /* cancel detect plug */
2886 wcd9xxx_cancel_hs_detect_plug(mbhc,
2887 &mbhc->correct_plug_swch);
2888
2889 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2890 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2891 is_removed = true;
2892 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2893 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2894 is_removed = true;
2895 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2896 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302897 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002898 wcd9xxx_cleanup_hs_polling(mbhc);
2899 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2900 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002901 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2902 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2903 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002904 }
2905
2906 if (is_removed) {
2907 /* Enable Mic Bias pull down and HPH Switch to GND */
2908 snd_soc_update_bits(codec,
2909 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2910 0x01);
2911 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2912 0x01);
2913 /* Make sure mic trigger is turned off */
2914 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2915 0x01, 0x01);
2916 snd_soc_update_bits(codec,
2917 mbhc->mbhc_bias_regs.mbhc_reg,
2918 0x90, 0x00);
2919 /* Reset MBHC State Machine */
2920 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2921 0x08, 0x08);
2922 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2923 0x08, 0x00);
2924 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002925 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002926 }
2927 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002928exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002929 mbhc->in_swch_irq_handler = false;
2930 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2931 pr_debug("%s: leave\n", __func__);
2932}
2933
2934static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2935{
2936 int r = IRQ_HANDLED;
2937 struct wcd9xxx_mbhc *mbhc = data;
2938
2939 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002940 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002941 pr_warn("%s: failed to hold suspend\n", __func__);
2942 r = IRQ_NONE;
2943 } else {
2944 /* Call handler */
2945 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002946 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002947 }
2948
2949 pr_debug("%s: leave %d\n", __func__, r);
2950 return r;
2951}
2952
Joonwoo Park218e73f2013-08-21 16:22:18 -07002953static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002954{
Joonwoo Park73375212013-05-07 12:42:44 -07002955 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002956 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002957 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002958 const s16 v_ins_hu =
2959 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2960 const s16 v_ins_h =
2961 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2962 const s16 v_b1_hu =
2963 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2964 const s16 v_b1_h =
2965 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002966 const unsigned long timeout =
2967 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002968
Joonwoo Park218e73f2013-08-21 16:22:18 -07002969 while (time_before(jiffies, timeout)) {
2970 /*
2971 * This function needs to run measurements just few times during
2972 * release debounce time. Make 1ms interval to avoid
2973 * unnecessary excessive measurements.
2974 */
2975 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07002976 if (i == 0) {
2977 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
2978 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
2979 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002980 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002981 r = 1;
2982 break;
2983 }
2984 } else {
2985 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2986 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
2987 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07002988 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002989 r = 1;
2990 break;
2991 }
2992 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07002993 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07002994 }
2995
2996 return r;
2997}
2998
2999/* called under codec_resource_lock acquisition */
3000static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3001 const s32 micmv)
3002{
3003 s16 *v_btn_low, *v_btn_high;
3004 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3005 int i, btn = -1;
3006
3007 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3008 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3009 MBHC_BTN_DET_V_BTN_LOW);
3010 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3011 MBHC_BTN_DET_V_BTN_HIGH);
3012
3013 for (i = 0; i < btn_det->num_btn; i++) {
3014 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3015 btn = i;
3016 break;
3017 }
3018 }
3019
3020 if (btn == -1)
3021 pr_debug("%s: couldn't find button number for mic mv %d\n",
3022 __func__, micmv);
3023
3024 return btn;
3025}
3026
3027static int wcd9xxx_get_button_mask(const int btn)
3028{
3029 int mask = 0;
3030 switch (btn) {
3031 case 0:
3032 mask = SND_JACK_BTN_0;
3033 break;
3034 case 1:
3035 mask = SND_JACK_BTN_1;
3036 break;
3037 case 2:
3038 mask = SND_JACK_BTN_2;
3039 break;
3040 case 3:
3041 mask = SND_JACK_BTN_3;
3042 break;
3043 case 4:
3044 mask = SND_JACK_BTN_4;
3045 break;
3046 case 5:
3047 mask = SND_JACK_BTN_5;
3048 break;
3049 case 6:
3050 mask = SND_JACK_BTN_6;
3051 break;
3052 case 7:
3053 mask = SND_JACK_BTN_7;
3054 break;
3055 }
3056 return mask;
3057}
3058
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003059static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003060{
3061 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003062 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003063 struct snd_soc_codec *codec = mbhc->codec;
3064
3065 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3066 /* Pull down micbias to ground and disconnect vddio switch */
3067 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3068 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3069 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3070 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3071
3072 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003073 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3074 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003075 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003076 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003077 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003078 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3079 }
3080 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003081 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003082 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3083 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003084
Joonwoo Park520a0f92013-05-14 19:39:58 -07003085 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003086 if (change)
3087 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3088 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003089 /* Disable pull down micbias to ground */
3090 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3091 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3092}
3093
Joonwoo Park218e73f2013-08-21 16:22:18 -07003094void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3095{
3096 const u16 sta_z = mbhc->mbhc_data.sta_z;
3097 const u16 dce_z = mbhc->mbhc_data.dce_z;
3098
3099 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3100 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3101 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3102 mbhc->mbhc_data.sta_z & 0xFFFF,
3103 mbhc->mbhc_data.dce_z & 0xFFFF);
3104
3105 wcd9xxx_mbhc_calc_thres(mbhc);
3106 wcd9xxx_calibrate_hs_polling(mbhc);
3107}
3108
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003109/*
3110 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3111 * to ceilmv + buffer
3112 */
3113static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3114{
3115 u16 v_brh, v_b1_hu;
3116 int mv;
3117 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3118 void *calibration = mbhc->mbhc_cfg->calibration;
3119 struct snd_soc_codec *codec = mbhc->codec;
3120
3121 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3122 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3123 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3124
3125 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003126 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003127 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3128 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3129 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003130 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003131 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3132 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3133 (v_brh >> 8) & 0xFF);
3134 return 0;
3135}
3136
Joonwoo Parka8890262012-10-15 12:04:27 -07003137irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3138{
3139 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003140 bool vddio;
3141 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003142 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003143 s32 stamv, stamv_s;
3144 s16 *v_btn_high;
3145 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003146 int btn = -1, meas = 0;
3147 struct wcd9xxx_mbhc *mbhc = data;
3148 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3149 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3150 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003151 short dce[d->n_btn_meas + 1], sta;
3152 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003153 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003154 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003155 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003156 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003157
3158 pr_debug("%s: enter\n", __func__);
3159
3160 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3161 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3162
3163 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3164 pr_debug("%s: mbhc is being recovered, skip button press\n",
3165 __func__);
3166 goto done;
3167 }
3168
3169 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3170
3171 if (!mbhc->polling_active) {
3172 pr_warn("%s: mbhc polling is not active, skip button press\n",
3173 __func__);
3174 goto done;
3175 }
3176
Joonwoo Parka8890262012-10-15 12:04:27 -07003177 /* If switch nterrupt already kicked in, ignore button press */
3178 if (mbhc->in_swch_irq_handler) {
3179 pr_debug("%s: Swtich level changed, ignore button press\n",
3180 __func__);
3181 btn = -1;
3182 goto done;
3183 }
3184
3185 /* Measure scaled HW DCE */
3186 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3187 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003188
Joonwoo Park218e73f2013-08-21 16:22:18 -07003189 dce_z = mbhc->mbhc_data.dce_z;
3190 sta_z = mbhc->mbhc_data.sta_z;
3191
Joonwoo Parka8890262012-10-15 12:04:27 -07003192 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003193 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003194 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003195 if (mbhc_status != STATUS_REL_DETECTION) {
3196 if (mbhc->mbhc_last_resume &&
3197 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3198 pr_debug("%s: Button is released after resume\n",
3199 __func__);
3200 n_btn_meas = 0;
3201 } else {
3202 pr_debug("%s: Button is released without resume",
3203 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003204 if (mbhc->update_z) {
3205 wcd9xxx_update_z(mbhc);
3206 mbhc->update_z = false;
3207 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003208 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3209 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003210 if (vddio)
3211 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3212 false);
3213 else
3214 stamv_s = stamv;
3215 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003216 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003217 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3218 false) : mv[0];
3219 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003220 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3221 btn = -1;
3222 goto done;
3223 }
3224 }
3225
Joonwoo Park520a0f92013-05-14 19:39:58 -07003226 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3227 meas++)
3228 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3229
Joonwoo Park218e73f2013-08-21 16:22:18 -07003230 if (mbhc->update_z) {
3231 wcd9xxx_update_z(mbhc);
3232 mbhc->update_z = false;
3233 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003234
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003235 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3236 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003237 if (vddio)
3238 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3239 else
3240 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003241 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003242 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003243
3244 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003245 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3246 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003247 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3248 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003249 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003250 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003251 if (n_btn_meas == 0)
3252 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003253 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3254 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003255 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3256 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003257 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3258 mv[meas];
3259 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003260 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003261 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3262 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003263 /*
3264 * if large enough measurements are collected,
3265 * start to check if last all n_btn_con measurements were
3266 * in same button low/high range
3267 */
3268 if (meas + 1 >= d->n_btn_con) {
3269 for (i = 0; i < d->n_btn_con; i++)
3270 if ((btnmeas[meas] < 0) ||
3271 (btnmeas[meas] != btnmeas[meas - i]))
3272 break;
3273 if (i == d->n_btn_con) {
3274 /* button pressed */
3275 btn = btnmeas[meas];
3276 break;
3277 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3278 /*
3279 * if left measurements are less than n_btn_con,
3280 * it's impossible to find button number
3281 */
3282 break;
3283 }
3284 }
3285 }
3286
3287 if (btn >= 0) {
3288 if (mbhc->in_swch_irq_handler) {
3289 pr_debug(
3290 "%s: Switch irq triggered, ignore button press\n",
3291 __func__);
3292 goto done;
3293 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003294 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3295 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3296 MBHC_BTN_DET_V_BTN_HIGH);
3297 WARN_ON(btn >= btn_det->num_btn);
3298 /* reprogram release threshold to catch voltage ramp up early */
3299 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3300
Joonwoo Parka8890262012-10-15 12:04:27 -07003301 mask = wcd9xxx_get_button_mask(btn);
3302 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003303 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003304 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3305 msecs_to_jiffies(400)) == 0) {
3306 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003307 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003308 }
3309 } else {
3310 pr_debug("%s: bogus button press, too short press?\n",
3311 __func__);
3312 }
3313
3314 done:
3315 pr_debug("%s: leave\n", __func__);
3316 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3317 return IRQ_HANDLED;
3318}
3319
3320static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3321{
3322 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003323 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003324 struct wcd9xxx_mbhc *mbhc = data;
3325
3326 pr_debug("%s: enter\n", __func__);
3327 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3328 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3329
Joonwoo Parka8890262012-10-15 12:04:27 -07003330 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3331 ret = wcd9xxx_cancel_btn_work(mbhc);
3332 if (ret == 0) {
3333 pr_debug("%s: Reporting long button release event\n",
3334 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003335 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003336 mbhc->buttons_pressed);
3337 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003338 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003339 pr_debug("%s: Fake button press interrupt\n",
3340 __func__);
3341 } else {
3342 if (mbhc->in_swch_irq_handler) {
3343 pr_debug("%s: Switch irq kicked in, ignore\n",
3344 __func__);
3345 } else {
3346 pr_debug("%s: Reporting btn press\n",
3347 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003348 wcd9xxx_jack_report(mbhc,
3349 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003350 mbhc->buttons_pressed,
3351 mbhc->buttons_pressed);
3352 pr_debug("%s: Reporting btn release\n",
3353 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003354 wcd9xxx_jack_report(mbhc,
3355 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003356 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003357 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003358 }
3359 }
3360 }
3361
3362 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3363 }
3364
3365 wcd9xxx_calibrate_hs_polling(mbhc);
3366
Joonwoo Park218e73f2013-08-21 16:22:18 -07003367 if (waitdebounce)
3368 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003369 wcd9xxx_start_hs_polling(mbhc);
3370
3371 pr_debug("%s: leave\n", __func__);
3372 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3373 return IRQ_HANDLED;
3374}
3375
3376static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3377{
3378 struct wcd9xxx_mbhc *mbhc = data;
3379 struct snd_soc_codec *codec;
3380
3381 pr_info("%s: received HPHL OCP irq\n", __func__);
3382
3383 if (mbhc) {
3384 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003385 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3386 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003387 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003388 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003389 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3390 0x10, 0x00);
3391 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3392 0x10, 0x10);
3393 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003394 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003395 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003396 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003397 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003398 mbhc->hph_status,
3399 WCD9XXX_JACK_MASK);
3400 }
3401 } else {
3402 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3403 }
3404
3405 return IRQ_HANDLED;
3406}
3407
3408static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3409{
3410 struct wcd9xxx_mbhc *mbhc = data;
3411 struct snd_soc_codec *codec;
3412
3413 pr_info("%s: received HPHR OCP irq\n", __func__);
3414 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003415 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3416 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003417 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003418 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003419 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3420 0x00);
3421 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3422 0x10);
3423 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003424 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003425 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003426 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003427 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003428 mbhc->hph_status, WCD9XXX_JACK_MASK);
3429 }
3430
3431 return IRQ_HANDLED;
3432}
3433
3434static int wcd9xxx_acdb_mclk_index(const int rate)
3435{
3436 if (rate == MCLK_RATE_12288KHZ)
3437 return 0;
3438 else if (rate == MCLK_RATE_9600KHZ)
3439 return 1;
3440 else {
3441 BUG_ON(1);
3442 return -EINVAL;
3443 }
3444}
3445
3446static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3447{
3448 u32 dce_wait, sta_wait;
3449 u8 ncic, nmeas, navg;
3450 void *calibration;
3451 u8 *n_cic, *n_ready;
3452 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3453 u8 npoll = 4, nbounce_wait = 30;
3454 struct snd_soc_codec *codec = mbhc->codec;
3455 int idx = wcd9xxx_acdb_mclk_index(rate);
3456 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3457
3458 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3459 rate);
3460 calibration = mbhc->mbhc_cfg->calibration;
3461
3462 /*
3463 * First compute the DCE / STA wait times depending on tunable
3464 * parameters. The value is computed in microseconds
3465 */
3466 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3467 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3468 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3469 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3470 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3471
3472 /* ncic stays with the same what we had during calibration */
3473 ncic = n_cic[idxmclk];
3474 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3475 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3476 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003477 /* give extra margin to sta for safety */
3478 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003479 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3480 n_ready[idx]) + 10;
3481
3482 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3483 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3484
3485 if (rate == MCLK_RATE_12288KHZ) {
3486 npoll = 4;
3487 nbounce_wait = 30;
3488 } else if (rate == MCLK_RATE_9600KHZ) {
3489 npoll = 3;
3490 nbounce_wait = 23;
3491 }
3492
3493 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3494 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3495 pr_debug("%s: leave\n", __func__);
3496}
3497
3498static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3499{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003500 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003501 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003502 struct snd_soc_codec *codec = mbhc->codec;
3503
3504 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003505 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3506 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003507 wcd9xxx_turn_onoff_rel_detection(codec, false);
3508
3509 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3510 WARN_ON(!mbhc->mbhc_data.t_dce);
3511 WARN_ON(!mbhc->mbhc_data.t_sta);
3512
3513 /*
3514 * LDOH and CFILT are already configured during pdata handling.
3515 * Only need to make sure CFILT and bandgap are in Fast mode.
3516 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003517 *
3518 * In case when Micbias is powered by external source, request
3519 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003520 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003521 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3522 mbhc->mbhc_cb->enable_mb_source(codec, true);
3523
Joonwoo Parka8890262012-10-15 12:04:27 -07003524 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303525 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3526 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3527 else
3528 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3529 0x40, 0x00);
3530
Joonwoo Parka8890262012-10-15 12:04:27 -07003531 /*
3532 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3533 * to perform ADC calibration
3534 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303535 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3536 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3537 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003538 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3539 mbhc->mbhc_cfg->micbias << 5);
3540 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3541 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3542 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303543 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3544 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3545 else
3546 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3547 0x04, 0x04);
3548
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003549 /* Pull down micbias to ground */
3550 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3551 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3552 /* Disconnect override from micbias */
3553 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3554 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3555 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303556 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303557 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3558 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3559 else
3560 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3561 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003562 /*
3563 * Hardware that has external cap can delay mic bias ramping down up
3564 * to 50ms.
3565 */
3566 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003567 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003568 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003569 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003570 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003571
3572 /* compute dce_z for current source */
3573 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3574 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3575 WCD9XXX_MBHC_NSC_CS << 3);
3576
3577 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3578 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3579 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3580 false);
3581 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3582 mbhc->mbhc_data.dce_nsc_cs_z);
3583
3584 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3585
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003586 /* STA measurement for 0 voltage */
3587 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3588 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3589 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003590
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003591 /* Restore registers */
3592 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3593 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003594
3595 /* DCE measurment for MB voltage */
3596 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3597 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303598 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303599 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3600 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3601 else
3602 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3603 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003604 /*
3605 * Hardware that has external cap can delay mic bias ramping down up
3606 * to 50ms.
3607 */
3608 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003609 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3610 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3611 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3612
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003613 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003614 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3615 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3616 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303617 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303618 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3619 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3620 else
3621 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3622 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003623 /*
3624 * Hardware that has external cap can delay mic bias ramping down up
3625 * to 50ms.
3626 */
3627 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003628 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3629 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3630 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3631
3632 /* Restore default settings. */
3633 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303634 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303635 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303636 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3637 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3638 else
3639 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3640 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003641 usleep_range(100, 100);
3642
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003643 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3644 mbhc->mbhc_cb->enable_mb_source(codec, false);
3645
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003646 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3647 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003648 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003649
Joonwoo Parka8890262012-10-15 12:04:27 -07003650 pr_debug("%s: leave\n", __func__);
3651}
3652
3653static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3654{
3655 int n;
3656 u8 *gain;
3657 struct wcd9xxx_mbhc_general_cfg *generic;
3658 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3659 struct snd_soc_codec *codec = mbhc->codec;
3660 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3661
3662 pr_debug("%s: enter\n", __func__);
3663 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3664 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3665
3666 for (n = 0; n < 8; n++) {
3667 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3668 0x07, n);
3669 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3670 btn_det->c[n]);
3671 }
3672
3673 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3674 btn_det->nc);
3675
3676 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3677 generic->mbhc_nsa << 4);
3678
3679 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3680 btn_det->n_meas);
3681
3682 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3683 generic->mbhc_navg);
3684
3685 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3686
3687 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3688 btn_det->mbhc_nsc << 3);
3689
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003690 if (mbhc->mbhc_cb &&
3691 mbhc->mbhc_cb->get_cdc_type() !=
3692 WCD9XXX_CDC_TYPE_HELICON) {
3693 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3694 snd_soc_update_bits(codec,
3695 mbhc->resmgr->reg_addr->micb_4_mbhc,
3696 0x03, MBHC_MICBIAS2);
3697 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003698
3699 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3700
3701 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3702
3703 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3704 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3705 gain[idx] << 3);
3706
3707 pr_debug("%s: leave\n", __func__);
3708}
3709
3710static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3711{
3712 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003713 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303714
Joonwoo Parka8890262012-10-15 12:04:27 -07003715 if (mbhc->mbhc_cfg->gpio) {
3716 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3717 wcd9xxx_mech_plug_detect_irq,
3718 (IRQF_TRIGGER_RISING |
3719 IRQF_TRIGGER_FALLING |
3720 IRQF_DISABLED),
3721 "headset detect", mbhc);
3722 if (ret) {
3723 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3724 mbhc->mbhc_cfg->gpio_irq);
3725 } else {
3726 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3727 if (ret)
3728 pr_err("%s: Failed to enable wake up irq %d\n",
3729 __func__, mbhc->mbhc_cfg->gpio_irq);
3730 }
3731 } else if (mbhc->mbhc_cfg->insert_detect) {
3732 /* Enable HPHL_10K_SW */
3733 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3734 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303735
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003736 ret = wcd9xxx_request_irq(core_res,
3737 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003738 wcd9xxx_mech_plug_detect_irq,
3739 "Jack Detect",
3740 mbhc);
3741 if (ret)
3742 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003743 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003744 }
3745
3746 return ret;
3747}
3748
3749static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3750{
3751 int ret = 0;
3752 struct snd_soc_codec *codec = mbhc->codec;
3753
3754 pr_debug("%s: enter\n", __func__);
3755
3756 /* Enable MCLK during calibration */
3757 wcd9xxx_onoff_ext_mclk(mbhc, true);
3758 wcd9xxx_mbhc_setup(mbhc);
3759 wcd9xxx_mbhc_cal(mbhc);
3760 wcd9xxx_mbhc_calc_thres(mbhc);
3761 wcd9xxx_onoff_ext_mclk(mbhc, false);
3762 wcd9xxx_calibrate_hs_polling(mbhc);
3763
3764 /* Enable Mic Bias pull down and HPH Switch to GND */
3765 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3766 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3767 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3768
3769 if (!IS_ERR_VALUE(ret)) {
3770 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3771 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003772 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003773 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003774 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003775 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003776
3777 /* Initialize mechanical mbhc */
3778 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3779
3780 if (!ret && mbhc->mbhc_cfg->gpio) {
3781 /* Requested with IRQF_DISABLED */
3782 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3783
3784 /* Bootup time detection */
3785 wcd9xxx_swch_irq_handler(mbhc);
3786 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3787 pr_debug("%s: Setting up codec own insert detection\n",
3788 __func__);
3789 /* Setup for insertion detection */
3790 wcd9xxx_insert_detect_setup(mbhc, true);
3791 }
3792 }
3793
3794 pr_debug("%s: leave\n", __func__);
3795
3796 return ret;
3797}
3798
3799static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3800{
3801 struct delayed_work *dwork;
3802 struct wcd9xxx_mbhc *mbhc;
3803 struct snd_soc_codec *codec;
3804 const struct firmware *fw;
3805 int ret = -1, retry = 0;
3806
3807 dwork = to_delayed_work(work);
3808 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3809 codec = mbhc->codec;
3810
3811 while (retry < FW_READ_ATTEMPTS) {
3812 retry++;
3813 pr_info("%s:Attempt %d to request MBHC firmware\n",
3814 __func__, retry);
3815 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3816 codec->dev);
3817
3818 if (ret != 0) {
3819 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3820 } else {
3821 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3822 break;
3823 }
3824 }
3825
3826 if (ret != 0) {
3827 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3828 __func__);
3829 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3830 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3831 __func__);
3832 release_firmware(fw);
3833 } else {
3834 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3835 mbhc->mbhc_fw = fw;
3836 }
3837
3838 (void) wcd9xxx_init_and_calibrate(mbhc);
3839}
3840
3841#ifdef CONFIG_DEBUG_FS
3842ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3843 size_t count, loff_t *pos)
3844{
3845 const int size = 768;
3846 char buffer[size];
3847 int n = 0;
3848 struct wcd9xxx_mbhc *mbhc = file->private_data;
3849 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003850 const s16 v_ins_hu =
3851 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3852 const s16 v_ins_h =
3853 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3854 const s16 v_b1_hu =
3855 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3856 const s16 v_b1_h =
3857 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3858 const s16 v_br_h =
3859 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003860
Joonwoo Park520a0f92013-05-14 19:39:58 -07003861 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3862 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003863 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3864 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003865 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3866 p->dce_nsc_cs_z,
3867 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3868 p->dce_nsc_cs_z,
3869 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003870 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3871 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3872 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3873 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003874 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3875 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3876 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3877 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3878 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3879 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3880 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003881 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003882 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003883 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003884 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003885 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003886 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003887 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3888 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3889 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3890 p->v_no_mic,
3891 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3892 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3893 p->v_inval_ins_low);
3894 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3895 p->v_inval_ins_high);
3896 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3897 !wcd9xxx_swch_level_remove(mbhc));
3898 buffer[n] = 0;
3899
3900 return simple_read_from_buffer(buf, count, pos, buffer, n);
3901}
3902
3903static int codec_debug_open(struct inode *inode, struct file *file)
3904{
3905 file->private_data = inode->i_private;
3906 return 0;
3907}
3908
3909static ssize_t codec_debug_write(struct file *filp,
3910 const char __user *ubuf, size_t cnt,
3911 loff_t *ppos)
3912{
3913 char lbuf[32];
3914 char *buf;
3915 int rc;
3916 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3917
3918 if (cnt > sizeof(lbuf) - 1)
3919 return -EINVAL;
3920
3921 rc = copy_from_user(lbuf, ubuf, cnt);
3922 if (rc)
3923 return -EFAULT;
3924
3925 lbuf[cnt] = '\0';
3926 buf = (char *)lbuf;
3927 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3928 false : true;
3929 return rc;
3930}
3931
3932static const struct file_operations mbhc_trrs_debug_ops = {
3933 .open = codec_debug_open,
3934 .write = codec_debug_write,
3935};
3936
3937static const struct file_operations mbhc_debug_ops = {
3938 .open = codec_debug_open,
3939 .read = codec_mbhc_debug_read,
3940};
3941
3942static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3943{
3944 mbhc->debugfs_poke =
3945 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3946 &mbhc_trrs_debug_ops);
3947 mbhc->debugfs_mbhc =
3948 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3949 NULL, mbhc, &mbhc_debug_ops);
3950}
3951
3952static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3953{
3954 debugfs_remove(mbhc->debugfs_poke);
3955 debugfs_remove(mbhc->debugfs_mbhc);
3956}
3957#else
3958static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3959{
3960}
3961
3962static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3963{
3964}
3965#endif
3966
3967int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
3968 struct wcd9xxx_mbhc_config *mbhc_cfg)
3969{
Simmi Pateriya95466b12013-05-09 20:08:46 +05303970 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003971 struct snd_soc_codec *codec = mbhc->codec;
3972
3973 pr_debug("%s: enter\n", __func__);
3974
3975 if (!codec) {
3976 pr_err("%s: no codec\n", __func__);
3977 return -EINVAL;
3978 }
3979
3980 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
3981 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
3982 pr_err("Error: unsupported clock rate %d\n",
3983 mbhc_cfg->mclk_rate);
3984 return -EINVAL;
3985 }
3986
3987 /* Save mbhc config */
3988 mbhc->mbhc_cfg = mbhc_cfg;
3989
3990 /* Get HW specific mbhc registers' address */
3991 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
3992
3993 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303994 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3995 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3996 else
3997 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3998 0x40, WCD9XXX_CFILT_FAST_MODE);
3999
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004000 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004001 * setup internal micbias if codec uses internal micbias for
4002 * headset detection
4003 */
4004 if (mbhc->mbhc_cfg->use_int_rbias) {
4005 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
4006 mbhc->mbhc_cb->setup_int_rbias(codec, true);
4007 else
4008 pr_info("%s: internal bias is requested but codec did not provide callback\n",
4009 __func__);
4010 }
4011
4012 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004013 * If codec has specific clock gating for MBHC,
4014 * remove the clock gate
4015 */
4016 if (mbhc->mbhc_cb &&
4017 mbhc->mbhc_cb->enable_clock_gate)
4018 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4019
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004020 if (!mbhc->mbhc_cfg->read_fw_bin ||
4021 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004022 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004023 } else {
4024 if (!mbhc->mbhc_fw)
4025 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4026 usecs_to_jiffies(FW_READ_TIMEOUT));
4027 else
4028 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4029 __func__, mbhc->mbhc_fw);
4030 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004031
4032 pr_debug("%s: leave %d\n", __func__, rc);
4033 return rc;
4034}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004035EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004036
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004037void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4038{
4039 if (mbhc->mbhc_fw) {
4040 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4041 release_firmware(mbhc->mbhc_fw);
4042 mbhc->mbhc_fw = NULL;
4043 }
4044}
4045EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4046
Joonwoo Parka8890262012-10-15 12:04:27 -07004047static enum wcd9xxx_micbias_num
4048wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4049{
4050 enum wcd9xxx_micbias_num ret;
4051 switch (event) {
4052 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004053 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4054 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4055 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004056 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004057 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004058 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004059 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4060 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4061 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004062 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004063 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004064 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004065 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4066 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4067 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004068 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004069 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004070 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004071 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4072 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4073 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004074 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004075 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004076 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004077 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004078 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004079 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004080 }
4081 return ret;
4082}
4083
4084static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4085{
4086 int ret;
4087 switch (event) {
4088 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4089 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4090 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4091 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4092 ret = WCD9XXX_CFILT1_SEL;
4093 break;
4094 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4095 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4096 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4097 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4098 ret = WCD9XXX_CFILT2_SEL;
4099 break;
4100 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4101 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4102 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4103 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4104 ret = WCD9XXX_CFILT3_SEL;
4105 break;
4106 default:
4107 ret = -1;
4108 }
4109 return ret;
4110}
4111
4112static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4113{
4114 int cfilt;
4115 const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
4116
4117 switch (mbhc->mbhc_cfg->micbias) {
4118 case MBHC_MICBIAS1:
4119 cfilt = pdata->micbias.bias1_cfilt_sel;
4120 break;
4121 case MBHC_MICBIAS2:
4122 cfilt = pdata->micbias.bias2_cfilt_sel;
4123 break;
4124 case MBHC_MICBIAS3:
4125 cfilt = pdata->micbias.bias3_cfilt_sel;
4126 break;
4127 case MBHC_MICBIAS4:
4128 cfilt = pdata->micbias.bias4_cfilt_sel;
4129 break;
4130 default:
4131 cfilt = MBHC_MICBIAS_INVALID;
4132 break;
4133 }
4134 return cfilt;
4135}
4136
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004137static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4138{
4139 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4140 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4141 else
4142 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4143 0x40, on ? 0x40 : 0x00);
4144}
4145
Joonwoo Parka8890262012-10-15 12:04:27 -07004146static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4147 void *data)
4148{
4149 int ret = 0;
4150 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4151 struct snd_soc_codec *codec = mbhc->codec;
4152 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4153
4154 pr_debug("%s: enter event %s(%d)\n", __func__,
4155 wcd9xxx_get_event_string(event), event);
4156
4157 switch (event) {
4158 /* MICBIAS usage change */
4159 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4160 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4161 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4162 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004163 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4164 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004165 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004166 /*
4167 * Enable MBHC TxFE whenever micbias is
4168 * turned ON and polling is active
4169 */
4170 if (mbhc->polling_active)
4171 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4172 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004173 break;
4174 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4175 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4176 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4177 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004178 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004179 wcd9xxx_event_to_micbias(event) &&
4180 wcd9xxx_mbhc_polling(mbhc)) {
4181 /* if polling is on, restart it */
4182 wcd9xxx_pause_hs_polling(mbhc);
4183 wcd9xxx_start_hs_polling(mbhc);
4184 }
4185 break;
4186 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4187 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4188 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4189 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004190 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4191 wcd9xxx_event_to_micbias(event)) {
4192 if (mbhc->event_state &
4193 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4194 wcd9xxx_switch_micbias(mbhc, 1);
4195 /*
4196 * Disable MBHC TxFE, in case it was enabled
4197 * earlier when micbias was enabled.
4198 */
4199 wcd9xxx_enable_mbhc_txfe(mbhc, false);
4200 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004201 break;
4202 /* PA usage change */
4203 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004204 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004205 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004206 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004207 wcd9xxx_switch_micbias(mbhc, 1);
4208 break;
4209 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004210 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004211 break;
4212 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004213 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004214 /* if HPH PAs are off, report OCP and switch back to CFILT */
4215 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4216 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4217 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4218 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004219 if (!(mbhc->event_state &
4220 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4221 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004222 break;
4223 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004224 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004225 /* if HPH PAs are off, report OCP and switch back to CFILT */
4226 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4227 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4228 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4229 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004230 if (!(mbhc->event_state &
4231 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR)))
4232 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004233 break;
4234 /* Clock usage change */
4235 case WCD9XXX_EVENT_PRE_MCLK_ON:
4236 break;
4237 case WCD9XXX_EVENT_POST_MCLK_ON:
4238 /* Change to lower TxAAF frequency */
4239 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4240 1 << 4);
4241 /* Re-calibrate clock rate dependent values */
4242 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4243 /* If clock source changes, stop and restart polling */
4244 if (wcd9xxx_mbhc_polling(mbhc)) {
4245 wcd9xxx_calibrate_hs_polling(mbhc);
4246 wcd9xxx_start_hs_polling(mbhc);
4247 }
4248 break;
4249 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4250 /* If clock source changes, stop and restart polling */
4251 if (wcd9xxx_mbhc_polling(mbhc))
4252 wcd9xxx_pause_hs_polling(mbhc);
4253 break;
4254 case WCD9XXX_EVENT_POST_MCLK_OFF:
4255 break;
4256 case WCD9XXX_EVENT_PRE_RCO_ON:
4257 break;
4258 case WCD9XXX_EVENT_POST_RCO_ON:
4259 /* Change to higher TxAAF frequency */
4260 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4261 0 << 4);
4262 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004263 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004264 /* If clock source changes, stop and restart polling */
4265 if (wcd9xxx_mbhc_polling(mbhc)) {
4266 wcd9xxx_calibrate_hs_polling(mbhc);
4267 wcd9xxx_start_hs_polling(mbhc);
4268 }
4269 break;
4270 case WCD9XXX_EVENT_PRE_RCO_OFF:
4271 /* If clock source changes, stop and restart polling */
4272 if (wcd9xxx_mbhc_polling(mbhc))
4273 wcd9xxx_pause_hs_polling(mbhc);
4274 break;
4275 case WCD9XXX_EVENT_POST_RCO_OFF:
4276 break;
4277 /* CFILT usage change */
4278 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4279 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4280 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4281 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4282 wcd9xxx_event_to_cfilt(event))
4283 /*
4284 * Switch CFILT to slow mode if MBHC CFILT is being
4285 * used.
4286 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304287 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004288 break;
4289 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4290 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4291 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4292 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4293 wcd9xxx_event_to_cfilt(event))
4294 /*
4295 * Switch CFILT to fast mode if MBHC CFILT is not
4296 * used anymore.
4297 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304298 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004299 break;
4300 /* System resume */
4301 case WCD9XXX_EVENT_POST_RESUME:
4302 mbhc->mbhc_last_resume = jiffies;
4303 break;
4304 /* BG mode chage */
4305 case WCD9XXX_EVENT_PRE_BG_OFF:
4306 case WCD9XXX_EVENT_POST_BG_OFF:
4307 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4308 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4309 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4310 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4311 /* Not used for now */
4312 break;
4313 default:
4314 WARN(1, "Unknown event %d\n", event);
4315 ret = -EINVAL;
4316 }
4317
4318 pr_debug("%s: leave\n", __func__);
4319
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304320 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004321}
4322
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004323static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4324 uint32_t *zr)
4325{
4326 int i;
4327 int ret = 0;
4328 s16 l[3], r[3];
4329 s16 *z[] = {
4330 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4331 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004332 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004333 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004334 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4335 /* Phase 1 */
4336 /* Set MBHC_MUX for HPHL without ical */
4337 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4338 /* Set MBHC_MUX for HPHR without ical */
4339 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4340 /* Set MBHC_MUX for HPHR with ical */
4341 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4342 /* Set MBHC_MUX for HPHL with ical */
4343 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4344
4345 /* Phase 2 */
4346 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4347 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4348 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4349 };
4350
4351 pr_debug("%s: enter\n", __func__);
4352 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4353
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004354 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4355 !mbhc->mbhc_cb->compute_impedance || !zl ||
4356 !zr)
4357 return -EINVAL;
4358
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004359 /*
4360 * Impedance detection is an intrusive function as it mutes RX paths,
4361 * enable PAs and etc. Therefore codec drvier including ALSA
4362 * shouldn't read and write hardware registers during detection.
4363 */
4364 mutex_lock(&codec->mutex);
4365
Joonwoo Parkc4e06862013-07-18 15:43:45 -07004366 /*
4367 * Fast(mbhc) mode bandagap doesn't need to be enabled explicitly
4368 * since fast mode is set by MBHC hardware when override is on.
4369 * Enable bandgap mode to avoid unnecessary RCO disable and enable
4370 * during clock source change.
4371 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05304372 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004373
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004374 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004375 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004376
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004377 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4378 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004379
4380 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004381 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004382 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4383 reg_set_mux[i].mask,
4384 reg_set_mux[i].val);
4385 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4386 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4387 else
4388 snd_soc_update_bits(codec,
4389 WCD9XXX_A_MBHC_SCALING_MUX_1,
4390 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004391 /* 25us is required after mux change to settle down */
4392 usleep_range(mux_wait_us,
4393 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004394 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004395 }
4396
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004397 /* Codec specific setup for L2 and R2 measurements */
4398 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004399
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004400 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004401 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4402 reg_set_mux[i].mask,
4403 reg_set_mux[i].val);
4404 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4405 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4406 else
4407 snd_soc_update_bits(codec,
4408 WCD9XXX_A_MBHC_SCALING_MUX_1,
4409 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004410 /* 25us is required after mux change to settle down */
4411 usleep_range(mux_wait_us,
4412 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004413 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004414 }
4415
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004416 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004417
4418 mutex_unlock(&codec->mutex);
4419
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05304420 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004421
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004422 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004423 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004424
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004425 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004426 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004427 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4428 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004429 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004430 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004431 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4432 pr_debug("%s: Impedance detection completed\n", __func__);
4433
4434 return ret;
4435}
4436
4437int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4438 uint32_t *zr)
4439{
4440 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4441 *zl = mbhc->zl;
4442 *zr = mbhc->zr;
4443 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4444
4445 if (*zl && *zr)
4446 return 0;
4447 else
4448 return -EINVAL;
4449}
4450
Joonwoo Parka8890262012-10-15 12:04:27 -07004451/*
4452 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4453 *
4454 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4455 */
4456int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004457 struct snd_soc_codec *codec,
4458 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004459 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4460 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4461 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004462 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004463{
4464 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004465 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004466
4467 pr_debug("%s: enter\n", __func__);
4468 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4469 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4470
4471 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4472 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4473 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4474 mbhc->mbhc_micbias_switched = false;
4475 mbhc->polling_active = false;
4476 mbhc->mbhc_state = MBHC_STATE_NONE;
4477 mbhc->in_swch_irq_handler = false;
4478 mbhc->current_plug = PLUG_TYPE_NONE;
4479 mbhc->lpi_enabled = false;
4480 mbhc->no_mic_headset_override = false;
4481 mbhc->mbhc_last_resume = 0;
4482 mbhc->codec = codec;
4483 mbhc->resmgr = resmgr;
4484 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004485 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004486 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304487 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004488 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004489 mbhc->impedance_detect = impedance_det_en;
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -07004490 impedance_detect_en = impedance_det_en ? 1 : 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004491
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004492 if (mbhc->intr_ids == NULL) {
4493 pr_err("%s: Interrupt mapping not provided\n", __func__);
4494 return -EINVAL;
4495 }
4496
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004497 if (mbhc->headset_jack.jack == NULL) {
4498 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4499 &mbhc->headset_jack);
4500 if (ret) {
4501 pr_err("%s: Failed to create new jack\n", __func__);
4502 return ret;
4503 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004504
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004505 ret = snd_soc_jack_new(codec, "Button Jack",
4506 WCD9XXX_JACK_BUTTON_MASK,
4507 &mbhc->button_jack);
4508 if (ret) {
4509 pr_err("Failed to create new jack\n");
4510 return ret;
4511 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004512
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004513 ret = snd_jack_set_key(mbhc->button_jack.jack,
4514 SND_JACK_BTN_0,
4515 KEY_MEDIA);
4516 if (ret) {
4517 pr_err("%s: Failed to set code for btn-0\n",
4518 __func__);
4519 return ret;
4520 }
4521
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004522 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4523 wcd9xxx_mbhc_fw_read);
4524 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4525 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4526 wcd9xxx_mbhc_insert_work);
4527 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004528
4529 /* Register event notifier */
4530 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4531 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4532 if (ret) {
4533 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4534 return ret;
4535 }
4536
4537 wcd9xxx_init_debugfs(mbhc);
4538
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004539 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004540 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004541 wcd9xxx_hs_insert_irq,
4542 "Headset insert detect", mbhc);
4543 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004544 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004545 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004546 goto err_insert_irq;
4547 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004548 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004549
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004550 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004551 wcd9xxx_hs_remove_irq,
4552 "Headset remove detect", mbhc);
4553 if (ret) {
4554 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004555 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004556 goto err_remove_irq;
4557 }
4558
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004559 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004560 wcd9xxx_dce_handler, "DC Estimation detect",
4561 mbhc);
4562 if (ret) {
4563 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004564 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004565 goto err_potential_irq;
4566 }
4567
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004568 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004569 wcd9xxx_release_handler,
4570 "Button Release detect", mbhc);
4571 if (ret) {
4572 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004573 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004574 goto err_release_irq;
4575 }
4576
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004577 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004578 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4579 mbhc);
4580 if (ret) {
4581 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004582 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004583 goto err_hphl_ocp_irq;
4584 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004585 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004586
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004587 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004588 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4589 mbhc);
4590 if (ret) {
4591 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004592 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004593 goto err_hphr_ocp_irq;
4594 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004595 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004596
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004597 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4598 1 << WCD9XXX_COND_HPH);
4599
Joonwoo Parka8890262012-10-15 12:04:27 -07004600 pr_debug("%s: leave ret %d\n", __func__, ret);
4601 return ret;
4602
4603err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004604 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004605err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004606 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004607err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004608 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004609err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004610 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004611err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004612 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004613err_insert_irq:
4614 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4615
4616 pr_debug("%s: leave ret %d\n", __func__, ret);
4617 return ret;
4618}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004619EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004620
4621void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4622{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004623 struct wcd9xxx_core_resource *core_res =
4624 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004625
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004626 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4627 1 << WCD9XXX_COND_HPH);
4628
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004629 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4630 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4631 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4632 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4633 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4634 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4635 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004636
Joonwoo Parka8890262012-10-15 12:04:27 -07004637 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004638 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004639}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004640EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004641
4642MODULE_DESCRIPTION("wcd9xxx MBHC module");
4643MODULE_LICENSE("GPL v2");