blob: 61ae859fa1890c12a945888324f6fd9e95275048 [file] [log] [blame]
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Joonwoo Parka8890262012-10-15 12:04:27 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/firmware.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/device.h>
18#include <linux/printk.h>
19#include <linux/ratelimit.h>
20#include <linux/debugfs.h>
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070021#include <linux/list.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070022#include <linux/mfd/wcd9xxx/core.h>
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070023#include <linux/mfd/wcd9xxx/core-resource.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070024#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
25#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
26#include <linux/mfd/wcd9xxx/pdata.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <linux/bitops.h>
33#include <linux/delay.h>
34#include <linux/pm_runtime.h>
35#include <linux/kernel.h>
36#include <linux/gpio.h>
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -070037#include <linux/input.h>
Joonwoo Parka8890262012-10-15 12:04:27 -070038#include "wcd9320.h"
Simmi Pateriya0a44d842013-04-03 01:12:42 +053039#include "wcd9306.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070040#include "wcd9xxx-mbhc.h"
41#include "wcd9xxx-resmgr.h"
Joonwoo Parkb755e9e2013-05-28 13:14:05 -070042#include "wcd9xxx-common.h"
Joonwoo Parka8890262012-10-15 12:04:27 -070043
44#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
Joonwoo Park80a01172012-10-15 16:05:23 -070045 SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
46 SND_JACK_UNSUPPORTED)
Joonwoo Parka8890262012-10-15 12:04:27 -070047#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
48 SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
49 SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
50 SND_JACK_BTN_6 | SND_JACK_BTN_7)
51
52#define NUM_DCE_PLUG_DETECT 3
Joonwoo Parkccccba72013-04-26 11:19:46 -070053#define NUM_DCE_PLUG_INS_DETECT 5
Joonwoo Parka8890262012-10-15 12:04:27 -070054#define NUM_ATTEMPTS_INSERT_DETECT 25
55#define NUM_ATTEMPTS_TO_REPORT 5
56
57#define FAKE_INS_LOW 10
58#define FAKE_INS_HIGH 80
59#define FAKE_INS_HIGH_NO_SWCH 150
60#define FAKE_REMOVAL_MIN_PERIOD_MS 50
61#define FAKE_INS_DELTA_SCALED_MV 300
62
63#define BUTTON_MIN 0x8000
64#define STATUS_REL_DETECTION 0x0C
65
66#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
67#define HS_DETECT_PLUG_INERVAL_MS 100
68#define SWCH_REL_DEBOUNCE_TIME_MS 50
69#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070070#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070071
72#define GND_MIC_SWAP_THRESHOLD 2
73#define OCP_ATTEMPT 1
74
75#define FW_READ_ATTEMPTS 15
76#define FW_READ_TIMEOUT 2000000
77
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070078#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070079
80#define MCLK_RATE_12288KHZ 12288000
81#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070082
83#define DEFAULT_DCE_STA_WAIT 55
84#define DEFAULT_DCE_WAIT 60000
85#define DEFAULT_STA_WAIT 5000
86
87#define VDDIO_MICBIAS_MV 1800
88
Joonwoo Parkccccba72013-04-26 11:19:46 -070089#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
90
Joonwoo Park20bc9da2013-01-16 12:58:06 -080091#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070092#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Simmi Pateriya795ce442013-11-14 11:15:28 +053093#define WCD9XXX_MEAS_DELTA_MAX_MV 120
Joonwoo Park141d6182013-03-05 12:25:46 -080094#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
95#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070096
97/*
98 * Invalid voltage range for the detection
99 * of plug type with current source
100 */
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
Simmi Pateriya4e545052013-11-15 07:41:06 +0530131static bool detect_use_vddio_switch;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800132
133struct wcd9xxx_mbhc_detect {
134 u16 dce;
135 u16 sta;
136 u16 hphl_status;
137 bool swap_gnd;
138 bool vddio;
139 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700140 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800141 /* internal purpose from here */
142 bool _above_no_mic;
143 bool _below_v_hs_max;
144 s16 _vdces;
145 enum wcd9xxx_mbhc_plug_type _type;
146};
147
Joonwoo Parka8890262012-10-15 12:04:27 -0700148enum meas_type {
149 STA = 0,
150 DCE,
151};
152
153enum {
154 MBHC_USE_HPHL_TRIGGER = 1,
155 MBHC_USE_MB_TRIGGER = 2
156};
157
158/*
159 * Flags to track of PA and DAC state.
160 * PA and DAC should be tracked separately as AUXPGA loopback requires
161 * only PA to be turned on without DAC being on.
162 */
163enum pa_dac_ack_flags {
164 WCD9XXX_HPHL_PA_OFF_ACK = 0,
165 WCD9XXX_HPHR_PA_OFF_ACK,
166 WCD9XXX_HPHL_DAC_OFF_ACK,
167 WCD9XXX_HPHR_DAC_OFF_ACK
168};
169
Joonwoo Park73375212013-05-07 12:42:44 -0700170enum wcd9xxx_current_v_idx {
171 WCD9XXX_CURRENT_V_INS_H,
172 WCD9XXX_CURRENT_V_INS_HU,
173 WCD9XXX_CURRENT_V_B1_H,
174 WCD9XXX_CURRENT_V_B1_HU,
175 WCD9XXX_CURRENT_V_BR_H,
176};
177
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700178static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
179 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700180static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
181 const enum wcd9xxx_current_v_idx idx);
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700182static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z);
183static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700184
Joonwoo Parka8890262012-10-15 12:04:27 -0700185static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
186{
187 return mbhc->polling_active;
188}
189
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700190static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700191{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700192 struct snd_soc_codec *codec = mbhc->codec;
193 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
194 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700195}
196
197/* called under codec_resource_lock acquisition */
198static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
199{
200 struct snd_soc_codec *codec = mbhc->codec;
201
202 pr_debug("%s: enter\n", __func__);
203 if (!mbhc->polling_active) {
204 pr_debug("polling not active, nothing to pause\n");
205 return;
206 }
207
208 /* Soft reset MBHC block */
209 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
210 pr_debug("%s: leave\n", __func__);
211}
212
213/* called under codec_resource_lock acquisition */
214static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
215{
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700216 s16 v_brh, v_b1_hu;
Joonwoo Parka8890262012-10-15 12:04:27 -0700217 struct snd_soc_codec *codec = mbhc->codec;
218 int mbhc_state = mbhc->mbhc_state;
219
220 pr_debug("%s: enter\n", __func__);
221 if (!mbhc->polling_active) {
222 pr_debug("Polling is not active, do not start polling\n");
223 return;
224 }
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530225
226 /*
227 * setup internal micbias if codec uses internal micbias for
228 * headset detection
229 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +0530230 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530231 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
232 mbhc->mbhc_cb->setup_int_rbias(codec, true);
233 else
234 pr_err("%s: internal bias requested but codec did not provide callback\n",
235 __func__);
236 }
237
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530238 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530239 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
240 mbhc->mbhc_cb->enable_mux_bias_block(codec);
241 else
242 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
243 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530244
Joonwoo Parka8890262012-10-15 12:04:27 -0700245 if (!mbhc->no_mic_headset_override &&
246 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530247 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700248 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
249 /* set to max button press threshold */
250 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
251 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
252 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
254 /* set to max */
255 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
256 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700257
258 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
260 (v_brh >> 8) & 0xFF);
261 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
262 v_brh & 0xFF);
263 v_b1_hu = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
264 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
265 v_b1_hu & 0xFF);
266 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
267 (v_b1_hu >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700268 }
269
270 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
271 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
272 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
273 pr_debug("%s: leave\n", __func__);
274}
275
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700276static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
277 unsigned int cfilt_mv)
278{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700279 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
280}
281
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700282/*
283 * called under codec_resource_lock acquisition
284 * return old status
285 */
286static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700287 int vddio_switch, bool restartpolling,
288 bool checkpolling)
289{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700290 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700291 int cfilt_k_val;
292 bool override;
293 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700294 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700295
296 codec = mbhc->codec;
297
Joonwoo Parkccccba72013-04-26 11:19:46 -0700298 if (mbhc->micbias_enable) {
299 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700300 ret = mbhc->mbhc_micbias_switched;
301 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700302 }
303
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700304 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700305 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
306 (!checkpolling || mbhc->polling_active)) {
307 if (restartpolling)
308 wcd9xxx_pause_hs_polling(mbhc);
309 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
310 0x04;
311 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700312 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700313 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700314 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700315 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700316 VDDIO_MICBIAS_MV);
317 usleep_range(10000, 10000);
318 snd_soc_update_bits(codec,
319 mbhc->mbhc_bias_regs.cfilt_val,
320 0xFC, (cfilt_k_val << 2));
321 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700322 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700323 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700324 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700325 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700326 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700327 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700328 /* Threshods for button press */
329 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
330 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
331 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
332 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
333 0xFF);
334 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
335 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
336 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
337 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
338 0xFF);
339 /* Threshods for button release */
340 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
341 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
342 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
343 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700344 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
345 __func__);
346 }
347
348 /* Enable MIC BIAS Switch to VDDIO */
349 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
350 0x80, 0x80);
351 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
352 0x10, 0x00);
353 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700354 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700355 if (restartpolling)
356 wcd9xxx_start_hs_polling(mbhc);
357
358 mbhc->mbhc_micbias_switched = true;
359 pr_debug("%s: VDDIO switch enabled\n", __func__);
360 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
361 if ((!checkpolling || mbhc->polling_active) &&
362 restartpolling)
363 wcd9xxx_pause_hs_polling(mbhc);
364 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700365 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700366 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700367 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700368 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700369 snd_soc_update_bits(codec,
370 mbhc->mbhc_bias_regs.cfilt_val,
371 0xFC, (cfilt_k_val << 2));
372 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700373 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700374 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700375 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700376 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700377 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
378 0xFF);
379 /* Revert threshods for button press */
380 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
381 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
382 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
383 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
384 0xFF);
385 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
386 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
387 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
388 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
389 0xFF);
390 /* Revert threshods for button release */
391 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
392 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
393 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
394 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700395 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
396 __func__);
397 }
398
399 /* Disable MIC BIAS Switch to VDDIO */
400 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
401 0x00);
402 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
403 0x00);
404
405 if ((!checkpolling || mbhc->polling_active) && restartpolling)
406 wcd9xxx_start_hs_polling(mbhc);
407
408 mbhc->mbhc_micbias_switched = false;
409 pr_debug("%s: VDDIO switch disabled\n", __func__);
410 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700411
412 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700413}
414
415static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
416{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700417 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700418}
419
Joonwoo Park73375212013-05-07 12:42:44 -0700420static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
421 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700422{
Joonwoo Park73375212013-05-07 12:42:44 -0700423 enum mbhc_v_index vidx;
424 s16 ret = -EINVAL;
425
Joonwoo Parka8890262012-10-15 12:04:27 -0700426 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
427 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700428 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700429 else
Joonwoo Park73375212013-05-07 12:42:44 -0700430 vidx = MBHC_V_IDX_CFILT;
431
432 switch (idx) {
433 case WCD9XXX_CURRENT_V_INS_H:
434 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
435 break;
436 case WCD9XXX_CURRENT_V_INS_HU:
437 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
438 break;
439 case WCD9XXX_CURRENT_V_B1_H:
440 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
441 break;
442 case WCD9XXX_CURRENT_V_B1_HU:
443 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
444 break;
445 case WCD9XXX_CURRENT_V_BR_H:
446 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
447 break;
448 }
449
450 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700451}
452
453void *wcd9xxx_mbhc_cal_btn_det_mp(
454 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
455 const enum wcd9xxx_mbhc_btn_det_mem mem)
456{
457 void *ret = &btn_det->_v_btn_low;
458
459 switch (mem) {
460 case MBHC_BTN_DET_GAIN:
461 ret += sizeof(btn_det->_n_cic);
462 case MBHC_BTN_DET_N_CIC:
463 ret += sizeof(btn_det->_n_ready);
464 case MBHC_BTN_DET_N_READY:
465 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
466 case MBHC_BTN_DET_V_BTN_HIGH:
467 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
468 case MBHC_BTN_DET_V_BTN_LOW:
469 /* do nothing */
470 break;
471 default:
472 ret = NULL;
473 }
474
475 return ret;
476}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700477EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700478
479static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
480{
481 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700482 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
483 WCD9XXX_CURRENT_V_INS_HU);
484 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
485 WCD9XXX_CURRENT_V_B1_HU);
486 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
487 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700488
489 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
490 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
491 (v_ins_hu >> 8) & 0xFF);
Joonwoo Park73375212013-05-07 12:42:44 -0700492 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700493 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700494 (v_b1_hu >> 8) & 0xFF);
495 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700496 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700497 (v_b1_h >> 8) & 0xFF);
498 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700499 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700500 (v_brh >> 8) & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700501 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
502 mbhc->mbhc_data.v_brl & 0xFF);
503 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
504 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
505}
506
Simmi Pateriya95466b12013-05-09 20:08:46 +0530507static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700508 bool fast)
509{
510 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530511 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700512
Simmi Pateriya95466b12013-05-09 20:08:46 +0530513 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
514 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530515 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530516 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700517 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530518 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700519 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700520
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700521 cfilt_mode.reg_mask = 0x40;
522 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530523 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700524 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700525
526 if (cfilt_mode.cur_mode_val
527 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530528 if (mbhc->polling_active)
529 wcd9xxx_pause_hs_polling(mbhc);
530 snd_soc_update_bits(codec,
531 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700532 cfilt_mode.reg_mask,
533 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530534 if (mbhc->polling_active)
535 wcd9xxx_start_hs_polling(mbhc);
536 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700537 cfilt_mode.cur_mode_val,
538 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530539 } else {
540 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700541 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530542 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700543}
544
Joonwoo Park3699ca32013-02-08 12:06:15 -0800545static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
546 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700547{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800548 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800549 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
550 WCD9XXX_COND_HPH_MIC,
551 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800552 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
553 WCD9XXX_COND_HPH,
554 status & SND_JACK_HEADPHONE);
555 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800556
Joonwoo Parka8890262012-10-15 12:04:27 -0700557 snd_soc_jack_report_no_dapm(jack, status, mask);
558}
559
560static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
561 int irq)
562{
563 struct snd_soc_codec *codec;
564
565 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
566 codec = mbhc->codec;
567 if (mbhc->hph_status & jack_status) {
568 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800569 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700570 mbhc->hph_status, WCD9XXX_JACK_MASK);
571 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
572 0x00);
573 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
574 0x10);
575 /*
576 * reset retry counter as PA is turned off signifying
577 * start of new OCP detection session
578 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700579 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700580 mbhc->hphlocp_cnt = 0;
581 else
582 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700583 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700584 }
585}
586
587static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
588{
589 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700590 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700591}
592
593static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
594{
595 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700596 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700597}
598
599static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
600 struct mbhc_micbias_regs *micbias_regs)
601{
602 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700603 struct wcd9xxx_micbias_setting *micbias_pdata =
604 mbhc->resmgr->micbias_pdata;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700605
Joonwoo Parka8890262012-10-15 12:04:27 -0700606 switch (mbhc->mbhc_cfg->micbias) {
607 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700608 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700609 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
610 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
611 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
612 break;
613 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700614 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700615 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
616 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
617 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
618 break;
619 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700620 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700621 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
622 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
623 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
624 break;
625 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700626 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700627 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
628 micbias_regs->int_rbias =
629 mbhc->resmgr->reg_addr->micb_4_int_rbias;
630 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
631 break;
632 default:
633 /* Should never reach here */
634 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
635 return;
636 }
637
638 micbias_regs->cfilt_sel = cfilt;
639
640 switch (cfilt) {
641 case WCD9XXX_CFILT1_SEL:
642 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
643 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700644 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700645 break;
646 case WCD9XXX_CFILT2_SEL:
647 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
648 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700649 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700650 break;
651 case WCD9XXX_CFILT3_SEL:
652 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
653 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700654 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
Joonwoo Parka8890262012-10-15 12:04:27 -0700655 break;
656 }
657}
658
659static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
660{
661 bool pa_turned_on = false;
662 struct snd_soc_codec *codec = mbhc->codec;
663 u8 wg_time;
664
665 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
666 wg_time += 1;
667
668 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
669 &mbhc->hph_pa_dac_state)) {
670 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
671 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
672 0xC0, 0xC0);
673 }
674 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
675 &mbhc->hph_pa_dac_state)) {
676 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
677 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700678 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700679 }
680
681 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
682 &mbhc->hph_pa_dac_state)) {
683 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
684 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
685 1 << 4);
686 pa_turned_on = true;
687 }
688 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
689 &mbhc->hph_pa_dac_state)) {
690 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
691 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
692 << 5);
693 pa_turned_on = true;
694 }
695
696 if (pa_turned_on) {
697 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
698 __func__);
699 usleep_range(wg_time * 1000, wg_time * 1000);
700 }
701}
702
703static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
704{
705 int r;
706 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
707 if (r)
708 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
709 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700710 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700711 return r;
712}
713
714static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
715{
716 u8 hph_reg_val = 0;
717 if (left)
718 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
719 else
720 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
721
722 return (hph_reg_val & 0xC0) ? true : false;
723}
724
725static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
726{
727 u8 hph_reg_val = 0;
728 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
729
730 return (hph_reg_val & 0x30) ? true : false;
731}
732
733/* called under codec_resource_lock acquisition */
734static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
735{
736 u8 wg_time;
737 struct snd_soc_codec *codec = mbhc->codec;
738
739 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
740 wg_time += 1;
741
742 /* If headphone PA is on, check if userspace receives
743 * removal event to sync-up PA's state */
744 if (wcd9xxx_is_hph_pa_on(codec)) {
745 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
746 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
747 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
748 } else {
749 pr_debug("%s PA is off\n", __func__);
750 }
751
752 if (wcd9xxx_is_hph_dac_on(codec, 1))
753 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
754 if (wcd9xxx_is_hph_dac_on(codec, 0))
755 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
756
757 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800758 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700759 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
760 usleep_range(wg_time * 1000, wg_time * 1000);
761}
762
763static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
764{
765 if (!mbhc->mbhc_cfg->insert_detect)
766 return;
767 pr_debug("%s: Setting up %s detection\n", __func__,
768 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700769 /* Disable detection to avoid glitch */
770 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530771 if (mbhc->mbhc_cfg->gpio_level_insert)
772 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
773 (0x68 | (ins ? (1 << 1) : 0)));
774 else
775 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
776 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700777 /* Re-enable detection */
778 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700779}
780
781/* called under codec_resource_lock acquisition */
782static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
783 enum snd_jack_types jack_type)
784{
785 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
786
Joonwoo Park80a01172012-10-15 16:05:23 -0700787 pr_debug("%s: enter insertion %d hph_status %x\n",
788 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700789 if (!insertion) {
790 /* Report removal */
791 mbhc->hph_status &= ~jack_type;
792 /*
793 * cancel possibly scheduled btn work and
794 * report release if we reported button press
795 */
796 if (wcd9xxx_cancel_btn_work(mbhc))
797 pr_debug("%s: button press is canceled\n", __func__);
798 else if (mbhc->buttons_pressed) {
799 pr_debug("%s: release of button press%d\n",
800 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800801 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700802 mbhc->buttons_pressed);
803 mbhc->buttons_pressed &=
804 ~WCD9XXX_JACK_BUTTON_MASK;
805 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700806
807 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
808 pr_debug("%s: Disabling micbias\n", __func__);
809 mbhc->micbias_enable_cb(mbhc->codec, false);
810 mbhc->micbias_enable = false;
811 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700812 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700813 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
814 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800815 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700816 WCD9XXX_JACK_MASK);
817 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
818 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
819 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
820 mbhc->current_plug = PLUG_TYPE_NONE;
821 mbhc->polling_active = false;
822 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700823 /*
824 * Report removal of current jack type.
825 * Headphone to headset shouldn't report headphone
826 * removal.
827 */
828 if (mbhc->mbhc_cfg->detect_extn_cable &&
829 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
830 jack_type == SND_JACK_HEADSET) &&
831 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
832 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
833 mbhc->hph_status == SND_JACK_HEADSET) {
834 pr_debug("%s: Disabling micbias\n", __func__);
835 mbhc->micbias_enable_cb(mbhc->codec, false);
836 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700837 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700838
839 pr_debug("%s: Reporting removal (%x)\n",
840 __func__, mbhc->hph_status);
841 mbhc->zl = mbhc->zr = 0;
842 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
843 0, WCD9XXX_JACK_MASK);
Yeleswarapu, Nagaradheshcff6b342013-11-05 17:12:39 +0530844 mbhc->hph_status &= ~(SND_JACK_HEADSET |
845 SND_JACK_LINEOUT);
Joonwoo Park80a01172012-10-15 16:05:23 -0700846 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700847 /* Report insertion */
848 mbhc->hph_status |= jack_type;
849
850 if (jack_type == SND_JACK_HEADPHONE) {
851 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
852 } else if (jack_type == SND_JACK_UNSUPPORTED) {
853 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
854 } else if (jack_type == SND_JACK_HEADSET) {
855 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
856 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700857 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700858 } else if (jack_type == SND_JACK_LINEOUT) {
859 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Joonwoo Parka8890262012-10-15 12:04:27 -0700860 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700861
862 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
863 pr_debug("%s: Enabling micbias\n", __func__);
864 mbhc->micbias_enable_cb(mbhc->codec, true);
865 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700866
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700867 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700868 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700869
Joonwoo Parka8890262012-10-15 12:04:27 -0700870 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
871 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800872 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700873 mbhc->hph_status, WCD9XXX_JACK_MASK);
874 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
875 }
876 /* Setup insert detect */
877 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700878
879 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700880}
881
882/* should be called under interrupt context that hold suspend */
883static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
884 struct work_struct *work)
885{
886 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
887 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
888 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700889 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700890 schedule_work(work);
891}
892
893/* called under codec_resource_lock acquisition */
894static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
895 struct work_struct *work)
896{
897 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
898 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
899 mbhc->hs_detect_work_stop = true;
900 wmb();
901 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
902 if (cancel_work_sync(work)) {
903 pr_debug("%s: correct_plug_swch is canceled\n",
904 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700905 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700906 }
907 WCD9XXX_BCL_LOCK(mbhc->resmgr);
908}
909
Joonwoo Park73375212013-05-07 12:42:44 -0700910static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
911{
912 int r;
913 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700914 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
915 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700916 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700917 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700918 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700919 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700920 return r;
921}
922
Joonwoo Parka8890262012-10-15 12:04:27 -0700923static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
924{
925 s16 v_hs_max;
926 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
927
928 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
929 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
930 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700931 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700932 else
933 v_hs_max = plug_type->v_hs_max;
934 return v_hs_max;
935}
936
Joonwoo Parka8890262012-10-15 12:04:27 -0700937static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
938{
939 u8 bias_msb, bias_lsb;
940 short bias_value;
941
942 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
943 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
944 bias_value = (bias_msb << 8) | bias_lsb;
945 return bias_value;
946}
947
948static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
949{
950 u8 bias_msb, bias_lsb;
951 short bias_value;
952
953 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
954 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
955 bias_value = (bias_msb << 8) | bias_lsb;
956 return bias_value;
957}
958
959static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
960 bool on)
961{
962 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
963}
964
965static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
966 bool override_bypass, bool noreldetection)
967{
968 short bias_value;
969 struct snd_soc_codec *codec = mbhc->codec;
970
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700971 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
972 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -0700973 if (noreldetection)
974 wcd9xxx_turn_onoff_rel_detection(codec, false);
975
Simmi Pateriya4b5159f2013-11-14 10:43:24 +0530976 if (mbhc->mbhc_cfg->do_recalibration)
977 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
978 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);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +0530988 if (mbhc->mbhc_cfg->do_recalibration)
989 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
990 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -0700991 usleep_range(mbhc->mbhc_data.t_sta_dce,
992 mbhc->mbhc_data.t_sta_dce);
993 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
994 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
995 bias_value = wcd9xxx_read_dce_result(codec);
996 } else {
997 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
998 0x8);
999 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1000 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1001 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301002 if (mbhc->mbhc_cfg->do_recalibration)
1003 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1004 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001005 usleep_range(mbhc->mbhc_data.t_sta_dce,
1006 mbhc->mbhc_data.t_sta_dce);
1007 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1008 usleep_range(mbhc->mbhc_data.t_sta,
1009 mbhc->mbhc_data.t_sta);
1010 bias_value = wcd9xxx_read_sta_result(codec);
1011 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1012 0x8);
1013 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1014 }
1015 /* Turn off the override after measuring mic voltage */
1016 if (!override_bypass)
1017 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1018 0x00);
1019
1020 if (noreldetection)
1021 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001022 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1023 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001024
1025 return bias_value;
1026}
1027
1028static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1029 bool norel)
1030{
1031 return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
1032}
1033
Joonwoo Park520a0f92013-05-14 19:39:58 -07001034static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001035 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001036{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001037 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001038 s32 mv;
1039
1040 value = bias_value;
1041 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001042 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001043 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001044 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001045 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001046 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001047 }
1048
1049 return mv;
1050}
1051
Joonwoo Park520a0f92013-05-14 19:39:58 -07001052static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1053 u16 bias_value)
1054{
1055 s16 z;
1056 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001057 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1058 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001059}
1060
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301061/* To enable/disable bandgap and RC oscillator */
1062static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1063 bool enable)
1064{
1065 if (enable) {
1066 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1067 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1068 WCD9XXX_BANDGAP_AUDIO_MODE);
1069 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1070 WCD9XXX_CLK_RCO);
1071 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1072 } else {
1073 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1074 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1075 WCD9XXX_CLK_RCO);
1076 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1077 WCD9XXX_BANDGAP_AUDIO_MODE);
1078 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1079 }
1080}
1081
Joonwoo Parka8890262012-10-15 12:04:27 -07001082/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001083static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
1084 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001085{
1086 struct snd_soc_codec *codec = mbhc->codec;
1087 short bias_value;
1088 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001089 s16 reg;
1090 int change;
1091 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1092 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001093
1094 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1095
1096 pr_debug("%s: enter\n", __func__);
1097 if (!mbhc->mbhc_cfg->calibration) {
1098 pr_err("%s: Error, no calibration exists\n", __func__);
1099 return -ENODEV;
1100 }
1101
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001102 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001103 /* Enable external voltage source to micbias if present */
1104 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1105 mbhc->mbhc_cb->enable_mb_source(codec, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001106
Joonwoo Parka8890262012-10-15 12:04:27 -07001107 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301108 * setup internal micbias if codec uses internal micbias for
1109 * headset detection
1110 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05301111 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301112 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1113 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1114 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301115 pr_err("%s: internal bias requested but codec did not provide callback\n",
1116 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301117 }
1118
Joonwoo Parka8890262012-10-15 12:04:27 -07001119 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1120
1121 /* Make sure CFILT is in fast mode, save current mode */
1122 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301123 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1124 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1125 else
1126 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
1127 0x70, 0x00);
1128
Joonwoo Parka8890262012-10-15 12:04:27 -07001129 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05301130 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301131 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1132 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1133 else
1134 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1135 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001136
1137 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1138 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1139 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1140
1141 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1142 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1143 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1144
1145 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1146 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1147
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301148 if (!mbhc->mbhc_cfg->do_recalibration) {
1149 if (!is_cs_enable)
1150 wcd9xxx_calibrate_hs_polling(mbhc);
1151 }
1152
Joonwoo Parka8890262012-10-15 12:04:27 -07001153 /* don't flip override */
1154 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301155 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001156 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1157
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301158 if (mbhc->mbhc_cfg->do_recalibration) {
1159 /* recalibrate dce_z and sta_z */
1160 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1161 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1162 0x78, btn_det->mbhc_nsc << 3);
1163 wcd9xxx_get_z(mbhc, &dce_z, &sta_z);
1164 if (change)
1165 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1166 if (dce_z && sta_z) {
1167 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1168 __func__,
1169 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1170 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1171 mbhc->mbhc_data.dce_z = dce_z;
1172 mbhc->mbhc_data.sta_z = sta_z;
1173 wcd9xxx_mbhc_calc_thres(mbhc);
1174 wcd9xxx_calibrate_hs_polling(mbhc);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001175 } else {
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301176 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
1177 __func__, dce_z, sta_z);
1178 }
1179
1180 if (is_cs_enable) {
1181 /* recalibrate dce_nsc_cs_z */
1182 reg = snd_soc_read(mbhc->codec,
1183 WCD9XXX_A_CDC_MBHC_B1_CTL);
1184 snd_soc_update_bits(mbhc->codec,
1185 WCD9XXX_A_CDC_MBHC_B1_CTL,
1186 0x78, WCD9XXX_MBHC_NSC_CS << 3);
1187 wcd9xxx_get_z(mbhc, &dce_z, NULL);
1188 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1189 reg);
1190 if (dce_z) {
1191 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n",
1192 __func__, mbhc->mbhc_data.dce_nsc_cs_z,
1193 dce_z & 0xffff);
1194 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1195 } else {
1196 pr_debug("%s: failed get new dce_nsc_cs_z\n",
1197 __func__);
1198 }
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001199 }
1200 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001201 return bias_value;
1202}
1203
1204static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1205{
1206 struct snd_soc_codec *codec = mbhc->codec;
1207 const struct wcd9xxx_mbhc_general_cfg *generic =
1208 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1209
1210 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001211 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001212 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001213 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001214
1215 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1216 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001217 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001218
1219 usleep_range(generic->t_shutdown_plug_rem,
1220 generic->t_shutdown_plug_rem);
1221
1222 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1223
Joonwoo Park533b3682013-06-13 11:41:21 -07001224 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001225 /* Put requested CLK back */
1226 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001227 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001228
1229 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1230}
1231
1232static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1233{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001234
1235 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001236 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1237
1238 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1239
Joonwoo Parka8890262012-10-15 12:04:27 -07001240
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001241 /* Disable external voltage source to micbias if present */
1242 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
1243 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false);
1244
Joonwoo Parka8890262012-10-15 12:04:27 -07001245 mbhc->polling_active = false;
1246 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001247 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001248}
1249
Joonwoo Parka8890262012-10-15 12:04:27 -07001250/* called under codec_resource_lock acquisition */
1251static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1252{
1253 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1254 if (on)
1255 usleep_range(5000, 5000);
1256}
1257
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001258static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1259{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001260 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001261
1262 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1263 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1264 goto exit;
1265 }
1266
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001267 if (on) {
1268 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1269 1 << 7, 1 << 7);
1270 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1271 1 << 4, 0);
1272 } else {
1273 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1274 1 << 4, 1 << 4);
1275 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1276 1 << 7, 0);
1277 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001278
1279exit:
1280 /*
1281 * Wait for the micbias to settle down to vddio
1282 * when the micbias to vddio switch is enabled.
1283 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001284 if (on)
1285 usleep_range(10000, 10000);
1286}
1287
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001288static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1289{
1290 u16 hph, status;
1291 struct snd_soc_codec *codec = mbhc->codec;
1292
1293 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1294 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1295 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1296 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1297 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1298 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1299 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1300 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1301 return status;
1302}
1303
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001304static enum wcd9xxx_mbhc_plug_type
1305wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1306 struct wcd9xxx_mbhc_detect *dt, const int size,
1307 bool highhph,
1308 unsigned long event_state)
1309{
1310 int i;
1311 int vdce, mb_mv;
1312 int ch, sz, delta_thr;
1313 int minv = 0, maxv = INT_MIN;
1314 struct wcd9xxx_mbhc_detect *d = dt;
1315 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1316 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1317
1318 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1319 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1320 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001321 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001322
1323 pr_debug("%s: enter\n", __func__);
1324 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1325
1326 sz = size - 1;
1327 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1328 if (d->mic_bias) {
1329 dce_z = mbhc->mbhc_data.dce_z;
1330 mb_mv = mbhc->mbhc_data.micb_mv;
1331 hs_max = plug_type->v_hs_max;
1332 no_mic = plug_type->v_no_mic;
1333 } else {
1334 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1335 mb_mv = VDDIO_MICBIAS_MV;
1336 hs_max = WCD9XXX_V_CS_HS_MAX;
1337 no_mic = WCD9XXX_V_CS_NO_MIC;
1338 }
1339
1340 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1341 dce_z, (u32)mb_mv);
1342
1343 d->_vdces = vdce;
1344 if (d->_vdces < no_mic)
1345 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001346 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001347 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001348 highhph_cnt++;
1349 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001350 d->_type = PLUG_TYPE_HEADSET;
1351
1352 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1353 __func__, i, d->dce, vdce, d->_vdces,
1354 d->hphl_status & 0x01,
1355 d->_type);
1356
1357 ch += d->hphl_status & 0x01;
1358 if (!d->swap_gnd && !d->mic_bias) {
1359 if (maxv < d->_vdces)
1360 maxv = d->_vdces;
1361 if (!minv || minv > d->_vdces)
1362 minv = d->_vdces;
1363 }
1364 if ((!d->mic_bias &&
1365 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1366 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1367 (d->mic_bias &&
1368 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1369 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1370 pr_debug("%s: within invalid range\n", __func__);
1371 type = PLUG_TYPE_INVALID;
1372 goto exit;
1373 }
1374 }
1375
1376 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1377 pr_debug("%s: HPHL PA was ON\n", __func__);
1378 } else if (ch != sz && ch > 0) {
1379 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1380 type = PLUG_TYPE_INVALID;
1381 goto exit;
1382 }
1383
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001384 delta_thr = ((highhph_cnt == sz) || highhph) ?
1385 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001386 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1387
1388 for (i = 0, d = dt; i < sz; i++, d++) {
1389 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1390 (d->_type != dprev->_type)) {
1391 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1392 type = PLUG_TYPE_INVALID;
1393 goto exit;
1394 }
1395
1396 if (!d->swap_gnd && !d->mic_bias &&
1397 (abs(minv - d->_vdces) > delta_thr ||
1398 abs(maxv - d->_vdces) > delta_thr)) {
1399 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1400 __func__, d->_vdces, minv, maxv);
1401 type = PLUG_TYPE_INVALID;
1402 goto exit;
1403 } else if (d->swap_gnd) {
1404 dgnd = d;
1405 }
1406
1407 if (!d->mic_bias && !d->swap_gnd)
1408 dprev = d;
1409 else if (d->mic_bias)
1410 dmicbias = d;
1411 }
1412 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1413 dt->_type != dgnd->_type) {
1414 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1415 type = PLUG_TYPE_INVALID;
1416 goto exit;
1417 }
1418
1419 type = dt->_type;
1420 if (dmicbias) {
1421 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1422 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1423 dt->_type == PLUG_TYPE_HEADSET)) {
1424 type = PLUG_TYPE_HEADSET;
1425 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1426 pr_debug("%s: Headset with threshold on MIC detected\n",
1427 __func__);
1428 if (mbhc->mbhc_cfg->micbias_enable_flags &
1429 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1430 mbhc->micbias_enable = true;
1431 }
1432 }
1433 }
1434
1435 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1436 if (((type == PLUG_TYPE_HEADSET ||
1437 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1438 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1439 __func__, type);
1440 type = PLUG_TYPE_INVALID;
1441 }
1442 }
1443 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1444 if ((dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MIN_MV <
1445 minv) &&
1446 (dgnd->_vdces + WCD9XXX_CS_GM_SWAP_THRES_MAX_MV >
1447 maxv))
1448 type = PLUG_TYPE_GND_MIC_SWAP;
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07001449 else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001450 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1451 type = PLUG_TYPE_INVALID;
1452 }
1453 }
1454exit:
1455 pr_debug("%s: Plug type %d detected\n", __func__, type);
1456 return type;
1457}
1458
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001459/*
1460 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1461 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001462 * param mbhc wcd9xxx_mbhc structure
1463 * param dt collected measurements
1464 * param size array size of dt
1465 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001466 */
1467static enum wcd9xxx_mbhc_plug_type
1468wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001469 struct wcd9xxx_mbhc_detect *dt, const int size,
1470 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001471{
1472 int i;
1473 int ch;
1474 enum wcd9xxx_mbhc_plug_type type;
1475 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001476 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001477 int maxv = 0, minv = 0;
1478 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1479 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1480 const s16 hs_max = plug_type->v_hs_max;
1481 const s16 no_mic = plug_type->v_no_mic;
1482
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001483 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1484
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001485 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1486 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1487 if (d->vddio)
1488 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1489 else
1490 d->_vdces = vdce;
1491
1492 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1493 d->_type = PLUG_TYPE_HEADSET;
1494 else if (d->_vdces < no_mic)
1495 d->_type = PLUG_TYPE_HEADPHONE;
1496 else
1497 d->_type = PLUG_TYPE_HIGH_HPH;
1498
1499 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001500 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001501 if (maxv < d->_vdces)
1502 maxv = d->_vdces;
1503 if (!minv || minv > d->_vdces)
1504 minv = d->_vdces;
1505 }
1506
1507 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1508 __func__, i, d->dce, vdce, d->_vdces,
1509 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1510 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001511
1512
1513 /*
1514 * If GND and MIC prongs are aligned to HPHR and GND of
1515 * headphone, codec measures the voltage based on
1516 * impedance between HPHR and GND which results in ~80mv.
1517 * Avoid this.
1518 */
1519 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1520 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1521 pr_debug("%s: within invalid range\n", __func__);
1522 type = PLUG_TYPE_INVALID;
1523 goto exit;
1524 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001525 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001526
1527 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1528 pr_debug("%s: HPHL PA was ON\n", __func__);
1529 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001530 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1531 type = PLUG_TYPE_INVALID;
1532 goto exit;
1533 }
1534
Joonwoo Parka84ec452013-07-17 15:02:52 -07001535 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001536 if (d->vddio) {
1537 dvddio = d;
1538 continue;
1539 }
1540
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001541 if ((i > 0) && (d->_type != dprev->_type)) {
1542 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1543 type = PLUG_TYPE_INVALID;
1544 goto exit;
1545 }
1546
1547 if (!d->swap_gnd && !d->hwvalue &&
1548 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1549 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1550 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1551 __func__, d->_vdces, minv, maxv);
1552 type = PLUG_TYPE_INVALID;
1553 goto exit;
1554 } else if (d->swap_gnd) {
1555 dgnd = d;
1556 }
1557 dprev = d;
1558 }
1559
1560 WARN_ON(i != size);
1561 type = dt->_type;
1562 if (type == PLUG_TYPE_HEADSET && dgnd) {
1563 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1564 minv) &&
1565 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1566 maxv))
1567 type = PLUG_TYPE_GND_MIC_SWAP;
1568 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001569
1570 /* if HPHL PA was on, we cannot use hphl status */
1571 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1572 if (((type == PLUG_TYPE_HEADSET ||
1573 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1574 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1575 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1576 __func__, type);
1577 type = PLUG_TYPE_INVALID;
1578 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001579 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001580
Joonwoo Parkccccba72013-04-26 11:19:46 -07001581 if (type == PLUG_TYPE_HEADSET && dvddio) {
1582 if ((dvddio->_vdces > hs_max) ||
1583 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1584 pr_debug("%s: Headset with threshold on MIC detected\n",
1585 __func__);
1586 if (mbhc->mbhc_cfg->micbias_enable_flags &
1587 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1588 mbhc->micbias_enable = true;
1589 } else {
1590 pr_debug("%s: Headset with regular MIC detected\n",
1591 __func__);
1592 if (mbhc->mbhc_cfg->micbias_enable_flags &
1593 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1594 mbhc->micbias_enable = true;
1595 }
1596 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001597exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001598 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1599 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001600 return type;
1601}
1602
Joonwoo Parkccccba72013-04-26 11:19:46 -07001603/*
1604 * Pull down MBHC micbias for provided duration in microsecond.
1605 */
1606static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1607{
1608 bool micbiasconn = false;
1609 struct snd_soc_codec *codec = mbhc->codec;
1610 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1611
1612 /*
1613 * Disable MBHC to micbias connection to pull down
1614 * micbias and pull down micbias for a moment.
1615 */
1616 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1617 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1618 return -EFAULT;
1619 }
1620
1621 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1622 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1623 1 << 4, 0);
1624 micbiasconn = true;
1625 }
1626
1627 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1628
1629 /*
1630 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1631 * able to get consistent result across DCEs.
1632 */
1633 usleep_range(1000, 1000 + 10);
1634
1635 if (micbiasconn)
1636 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1637 1 << 4, 1 << 4);
1638 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1639 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1640
1641 return 0;
1642}
1643
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001644void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc, bool on,
1645 bool highhph)
1646{
1647
1648 struct snd_soc_codec *codec;
1649 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1650 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1651 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1652
1653 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1654 codec = mbhc->codec;
1655
1656 if (on) {
1657 pr_debug("%s: enabling current source\n", __func__);
1658 /* Nsc to 9 */
1659 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1660 0x78, 0x48);
1661 /* pull down diode bit to 0 */
1662 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1663 0x01, 0x00);
1664 /*
1665 * Keep the low power insertion/removal
1666 * detection (reg 0x3DD) disabled
1667 */
1668 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1669 0x01, 0x00);
1670 /*
1671 * Enable the Mic Bias current source
1672 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1673 * Write bit[7] of register MICB_2_MBHC to 1
1674 * (INS_DET_ISRC_EN__ENABLE)
1675 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1676 */
1677 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1678 0xF0, 0xF0);
1679 /* Disconnect MBHC Override from MicBias and LDOH */
1680 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1681 } else {
1682 pr_debug("%s: disabling current source\n", __func__);
1683 /* Connect MBHC Override from MicBias and LDOH */
1684 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1685 /* INS_DET_ISRC_CTL to acdb value */
1686 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1687 0x60, plug_det->mic_current << 5);
1688 if (!highhph) {
1689 /* INS_DET_ISRC_EN__ENABLE to 0 */
1690 snd_soc_update_bits(codec,
1691 mbhc->mbhc_bias_regs.mbhc_reg,
1692 0x80, 0x00);
1693 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1694 snd_soc_update_bits(codec,
1695 mbhc->mbhc_bias_regs.mbhc_reg,
1696 0x10, 0x00);
1697 }
1698 /* Nsc to acdb value */
1699 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1700 btn_det->mbhc_nsc << 3);
1701 }
1702}
1703
1704static enum wcd9xxx_mbhc_plug_type
1705wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1706{
1707 struct snd_soc_codec *codec = mbhc->codec;
1708 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1709 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1710 int i;
1711
1712 pr_debug("%s: enter\n", __func__);
1713 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1714
1715 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1716
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301717 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001718 rt[0].swap_gnd = false;
1719 rt[0].vddio = false;
1720 rt[0].hwvalue = true;
1721 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
1722 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, true);
1723 rt[0].mic_bias = false;
1724
1725 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1726 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1727 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1728 highhph);
1729 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1730 if (rt[i].swap_gnd)
1731 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1732
1733 if (rt[i].mic_bias)
1734 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
1735
1736 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1737 if (rt[i].mic_bias)
1738 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
1739 if (rt[i].swap_gnd)
1740 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1741 }
1742
1743 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1744 mbhc->event_state);
1745
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301746 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001747 pr_debug("%s: plug_type:%d\n", __func__, type);
1748
1749 return type;
1750}
1751
Joonwoo Parka8890262012-10-15 12:04:27 -07001752static enum wcd9xxx_mbhc_plug_type
1753wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1754{
1755 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001756 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001757 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001758 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1759 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001760 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001761
Joonwoo Park80a01172012-10-15 16:05:23 -07001762 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001763 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1764
1765 /* make sure override is on */
1766 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1767
1768 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001769 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Simmi Pateriya4e545052013-11-15 07:41:06 +05301770 detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
Joonwoo Parka8890262012-10-15 12:04:27 -07001771
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001772 /*
1773 * There are chances vddio switch is on and cfilt voltage is adjusted
1774 * to vddio voltage even after plug type removal reported.
1775 */
1776 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1777 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1778
Joonwoo Parka8890262012-10-15 12:04:27 -07001779 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001780 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001781
Joonwoo Parkccccba72013-04-26 11:19:46 -07001782 /*
1783 * cfilter in fast mode requires 1ms to charge up and down micbias
1784 * fully.
1785 */
1786 (void) wcd9xxx_pull_down_micbias(mbhc,
1787 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301788
1789 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001790 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001791 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001792 rt[0].swap_gnd = false;
1793 rt[0].vddio = false;
1794 rt[0].hwvalue = true;
1795 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1796 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1797 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001798 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001799 else
1800 rt[i].vddio = false;
1801 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1802 rt[i].hwvalue = false;
1803 if (rt[i].swap_gnd)
1804 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1805 if (rt[i].vddio)
1806 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001807 /*
1808 * Pull down micbias to detect headset with mic which has
1809 * threshold and to have more consistent voltage measurements.
1810 *
1811 * cfilter in fast mode requires 1ms to charge up and down
1812 * micbias fully.
1813 */
1814 (void) wcd9xxx_pull_down_micbias(mbhc,
1815 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001816 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1817 if (rt[i].vddio)
1818 wcd9xxx_onoff_vddio_switch(mbhc, false);
1819 if (rt[i].swap_gnd)
1820 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001821 }
1822
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001823 if (vddioon)
1824 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1825
1826 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1827 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001828
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301829 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001830 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001831 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001832}
1833
1834static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1835{
1836 if (mbhc->mbhc_cfg->gpio)
1837 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1838 mbhc->mbhc_cfg->gpio_level_insert);
1839 else if (mbhc->mbhc_cfg->insert_detect)
1840 return snd_soc_read(mbhc->codec,
1841 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1842 (1 << 2);
1843 else
1844 WARN(1, "Invalid jack detection configuration\n");
1845
1846 return true;
1847}
1848
1849static bool is_clk_active(struct snd_soc_codec *codec)
1850{
1851 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1852}
1853
1854static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1855 int insertion, int trigger, bool padac_off)
1856{
1857 struct snd_soc_codec *codec = mbhc->codec;
1858 int central_bias_enabled = 0;
1859 const struct wcd9xxx_mbhc_general_cfg *generic =
1860 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1861 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1862 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1863
Joonwoo Park80a01172012-10-15 16:05:23 -07001864 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1865 __func__, insertion, trigger);
1866
Joonwoo Parka8890262012-10-15 12:04:27 -07001867 if (!mbhc->mbhc_cfg->calibration) {
1868 pr_err("Error, no wcd9xxx calibration\n");
1869 return -EINVAL;
1870 }
1871
1872 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1873
1874 /*
1875 * Make sure mic bias and Mic line schmitt trigger
1876 * are turned OFF
1877 */
1878 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1879 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1880
1881 if (insertion) {
1882 wcd9xxx_switch_micbias(mbhc, 0);
1883
1884 /* DAPM can manipulate PA/DAC bits concurrently */
1885 if (padac_off == true)
1886 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1887
1888 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1889 /* Enable HPH Schmitt Trigger */
1890 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1891 0x11);
1892 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1893 plug_det->hph_current << 2);
1894 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1895 0x02);
1896 }
1897 if (trigger & MBHC_USE_MB_TRIGGER) {
1898 /* enable the mic line schmitt trigger */
1899 snd_soc_update_bits(codec,
1900 mbhc->mbhc_bias_regs.mbhc_reg,
1901 0x60, plug_det->mic_current << 5);
1902 snd_soc_update_bits(codec,
1903 mbhc->mbhc_bias_regs.mbhc_reg,
1904 0x80, 0x80);
1905 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1906 snd_soc_update_bits(codec,
1907 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1908 0x00);
1909 snd_soc_update_bits(codec,
1910 mbhc->mbhc_bias_regs.mbhc_reg,
1911 0x10, 0x10);
1912 }
1913
1914 /* setup for insetion detection */
1915 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1916 } else {
1917 pr_debug("setup for removal detection\n");
1918 /* Make sure the HPH schmitt trigger is OFF */
1919 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1920
1921 /* enable the mic line schmitt trigger */
1922 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1923 0x01, 0x00);
1924 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
1925 plug_det->mic_current << 5);
1926 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1927 0x80, 0x80);
1928 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1929 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
1930 0x10, 0x10);
1931
1932 /* Setup for low power removal detection */
1933 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
1934 0x2);
1935 }
1936
1937 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
1938 /* called by interrupt */
1939 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001940 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07001941 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1942 0x06, 0);
1943 usleep_range(generic->t_shutdown_plug_rem,
1944 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07001945 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001946 } else
1947 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1948 0x06, 0);
1949 }
1950
1951 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
1952
1953 /* If central bandgap disabled */
1954 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
1955 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
1956 usleep_range(generic->t_bg_fast_settle,
1957 generic->t_bg_fast_settle);
1958 central_bias_enabled = 1;
1959 }
1960
1961 /* If LDO_H disabled */
1962 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
1963 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
1964 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
1965 usleep_range(generic->t_ldoh, generic->t_ldoh);
1966 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
1967
1968 if (central_bias_enabled)
1969 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
1970 0);
1971 }
1972
Meng Wangeeaaaba2013-09-09 18:37:32 +08001973 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05301974 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
1975 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07001976
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001977 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07001978 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07001979 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001980
1981 return 0;
1982}
1983
1984/* called under codec_resource_lock acquisition */
1985static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
1986 enum wcd9xxx_mbhc_plug_type plug_type)
1987{
Joonwoo Park80a01172012-10-15 16:05:23 -07001988 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
1989 __func__, mbhc->current_plug, plug_type);
1990
Joonwoo Parka8890262012-10-15 12:04:27 -07001991 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1992
1993 if (plug_type == PLUG_TYPE_HEADPHONE &&
1994 mbhc->current_plug == PLUG_TYPE_NONE) {
1995 /*
1996 * Nothing was reported previously
1997 * report a headphone or unsupported
1998 */
1999 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
2000 wcd9xxx_cleanup_hs_polling(mbhc);
2001 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002002 if (!mbhc->mbhc_cfg->detect_extn_cable) {
2003 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
2004 wcd9xxx_report_plug(mbhc, 0,
2005 SND_JACK_HEADSET);
2006 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
2007 wcd9xxx_report_plug(mbhc, 0,
2008 SND_JACK_HEADPHONE);
2009 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002010 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
2011 wcd9xxx_cleanup_hs_polling(mbhc);
2012 } else if (plug_type == PLUG_TYPE_HEADSET) {
2013 /*
2014 * If Headphone was reported previously, this will
2015 * only report the mic line
2016 */
2017 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302018 /* Button detection required RC oscillator */
2019 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002020 msleep(100);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002021
2022 /* if PA is already on, switch micbias source to VDDIO */
2023 if (mbhc->event_state &
2024 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2025 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002026 wcd9xxx_start_hs_polling(mbhc);
2027 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002028 if (mbhc->mbhc_cfg->detect_extn_cable) {
2029 /* High impedance device found. Report as LINEOUT*/
2030 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2031 wcd9xxx_cleanup_hs_polling(mbhc);
2032 pr_debug("%s: setup mic trigger for further detection\n",
2033 __func__);
2034 mbhc->lpi_enabled = true;
2035 /*
2036 * Do not enable HPHL trigger. If playback is active,
2037 * it might lead to continuous false HPHL triggers
2038 */
2039 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2040 false);
2041 } else {
2042 if (mbhc->current_plug == PLUG_TYPE_NONE)
2043 wcd9xxx_report_plug(mbhc, 1,
2044 SND_JACK_HEADPHONE);
2045 wcd9xxx_cleanup_hs_polling(mbhc);
2046 pr_debug("setup mic trigger for further detection\n");
2047 mbhc->lpi_enabled = true;
2048 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2049 MBHC_USE_HPHL_TRIGGER,
2050 false);
2051 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002052 } else {
2053 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2054 mbhc->current_plug, plug_type);
2055 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002056 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002057}
2058
2059/* called under codec_resource_lock acquisition */
2060static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2061{
2062 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002063 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002064
2065 pr_debug("%s: enter\n", __func__);
2066
2067 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002068
2069 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2070 (1 << MBHC_CS_ENABLE_INSERTION)) != 0) &&
2071 (!(snd_soc_read(mbhc->codec,
2072 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002073
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002074 if (current_source_enable) {
2075 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2076 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
2077 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2078 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002079 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002080 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002081 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002082 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002083
2084 if (wcd9xxx_swch_level_remove(mbhc)) {
2085 pr_debug("%s: Switch level is low when determining plug\n",
2086 __func__);
2087 return;
2088 }
2089
2090 if (plug_type == PLUG_TYPE_INVALID ||
2091 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002092 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002093 wcd9xxx_schedule_hs_detect_plug(mbhc,
2094 &mbhc->correct_plug_swch);
2095 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2096 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002097 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002098 wcd9xxx_schedule_hs_detect_plug(mbhc,
2099 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002100 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002101 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002102 wcd9xxx_schedule_hs_detect_plug(mbhc,
2103 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002104 } else {
2105 pr_debug("%s: Valid plug found, determine plug type %d\n",
2106 __func__, plug_type);
2107 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2108 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002109 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002110}
2111
2112/* called under codec_resource_lock acquisition */
2113static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2114{
Joonwoo Park80a01172012-10-15 16:05:23 -07002115 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002116 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2117
Joonwoo Parka8890262012-10-15 12:04:27 -07002118 if (wcd9xxx_swch_level_remove(mbhc))
2119 pr_debug("%s: Switch level low when determining plug\n",
2120 __func__);
2121 else
2122 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002123 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002124}
2125
2126/* called only from interrupt which is under codec_resource_lock acquisition */
2127static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2128 bool is_removal)
2129{
2130 if (!is_removal) {
2131 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2132
2133 rmb();
2134 if (mbhc->lpi_enabled)
2135 msleep(100);
2136
2137 rmb();
2138 if (!mbhc->lpi_enabled) {
2139 pr_debug("%s: lpi is disabled\n", __func__);
2140 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2141 pr_debug("%s: Valid insertion, detect plug type\n",
2142 __func__);
2143 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2144 } else {
2145 pr_debug("%s: Invalid insertion stop plug detection\n",
2146 __func__);
2147 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002148 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2149 pr_debug("%s: Removal\n", __func__);
2150 if (!wcd9xxx_swch_level_remove(mbhc)) {
2151 /*
2152 * Switch indicates, something is still inserted.
2153 * This could be extension cable i.e. headset is
2154 * removed from extension cable.
2155 */
2156 /* cancel detect plug */
2157 wcd9xxx_cancel_hs_detect_plug(mbhc,
2158 &mbhc->correct_plug_swch);
2159 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2160 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002161 } else {
2162 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2163 }
2164}
2165
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002166static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2167 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002168{
2169 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2170 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2171 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2172
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002173 if (cs_enable)
2174 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2175 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2176 else
2177 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2178 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2179 (mic_mv > plug_type->v_no_mic) &&
2180 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002181}
2182
2183/*
2184 * called under codec_resource_lock acquisition
2185 * returns true if mic voltage range is back to normal insertion
2186 * returns false either if timedout or removed
2187 */
2188static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2189{
2190 int i;
2191 bool timedout, settled = false;
2192 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2193 short mb_v[NUM_DCE_PLUG_DETECT];
2194 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002195 bool cs_enable;
2196
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002197 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2198 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2199 (!(snd_soc_read(mbhc->codec,
2200 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002201 if (cs_enable)
2202 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002203
2204 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2205 while (!(timedout = time_after(jiffies, timeout))) {
2206 retry++;
2207 if (wcd9xxx_swch_level_remove(mbhc)) {
2208 pr_debug("%s: Switch indicates removal\n", __func__);
2209 break;
2210 }
2211
2212 if (retry > 1)
2213 msleep(250);
2214 else
2215 msleep(50);
2216
2217 if (wcd9xxx_swch_level_remove(mbhc)) {
2218 pr_debug("%s: Switch indicates removal\n", __func__);
2219 break;
2220 }
2221
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002222 if (cs_enable) {
2223 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2224 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2225 true, true);
2226 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2227 true,
2228 mb_v[i],
2229 mbhc->mbhc_data.dce_nsc_cs_z,
2230 (u32)VDDIO_MICBIAS_MV);
2231 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2232 __func__, retry, mic_mv[i], mb_v[i]);
2233 }
2234 } else {
2235 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2236 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2237 true);
2238 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2239 mb_v[i]);
2240 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2241 __func__, retry, mic_mv[i],
2242 mb_v[i]);
2243 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002244 }
2245
2246 if (wcd9xxx_swch_level_remove(mbhc)) {
2247 pr_debug("%s: Switcn indicates removal\n", __func__);
2248 break;
2249 }
2250
2251 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2252 pr_debug("%s : headset/headphone is removed\n",
2253 __func__);
2254 break;
2255 }
2256
2257 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002258 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002259 break;
2260
2261 if (i == NUM_DCE_PLUG_DETECT) {
2262 pr_debug("%s: MIC voltage settled\n", __func__);
2263 settled = true;
2264 msleep(200);
2265 break;
2266 }
2267 }
2268
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002269 if (cs_enable)
2270 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2271
Joonwoo Parka8890262012-10-15 12:04:27 -07002272 if (timedout)
2273 pr_debug("%s: Microphone did not settle in %d seconds\n",
2274 __func__, HS_DETECT_PLUG_TIME_MS);
2275 return settled;
2276}
2277
2278/* called only from interrupt which is under codec_resource_lock acquisition */
2279static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2280{
Joonwoo Park80a01172012-10-15 16:05:23 -07002281 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002282 if (wcd9xxx_hs_remove_settle(mbhc))
2283 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002284 pr_debug("%s: leave\n", __func__);
2285}
2286
2287/* called only from interrupt which is under codec_resource_lock acquisition */
2288static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2289{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002290 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002291 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002292 bool removed = true;
2293 struct snd_soc_codec *codec = mbhc->codec;
2294 const struct wcd9xxx_mbhc_general_cfg *generic =
2295 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002296 bool cs_enable;
2297 s16 cur_v_ins_h;
2298 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002299
2300 pr_debug("%s: enter\n", __func__);
2301 if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
2302 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2303 __func__);
2304 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2305 0x08, 0x08);
2306 return;
2307 }
2308
2309 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002310 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002311
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002312 /* If micbias is enabled, don't enable current source */
2313 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2314 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2315 (!(snd_soc_read(codec,
2316 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002317 if (cs_enable)
2318 wcd9xxx_turn_onoff_current_source(mbhc, true, false);
2319
Joonwoo Park50ae0512013-06-04 16:53:12 -07002320 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002321 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002322 if (cs_enable) {
2323 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2324 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2325 mb_mv = VDDIO_MICBIAS_MV;
2326 } else {
2327 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2328 dcez = mbhc->mbhc_data.dce_z;
2329 mb_mv = mbhc->mbhc_data.micb_mv;
2330 }
2331
Joonwoo Park50ae0512013-06-04 16:53:12 -07002332 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002333 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2334 dcez, mb_mv));
2335
2336 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2337 (wcd9xxx_get_current_v(mbhc,
2338 WCD9XXX_CURRENT_V_INS_H));
2339
2340 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002341 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002342 break;
2343 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002344 } while (!time_after(jiffies, timeout));
2345 pr_debug("%s: headset %sactually removed\n", __func__,
2346 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002347
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002348 if (cs_enable)
2349 wcd9xxx_turn_onoff_current_source(mbhc, false, false);
2350
Joonwoo Park80a01172012-10-15 16:05:23 -07002351 if (removed) {
2352 if (mbhc->mbhc_cfg->detect_extn_cable) {
2353 if (!wcd9xxx_swch_level_remove(mbhc)) {
2354 /*
2355 * extension cable is still plugged in
2356 * report it as LINEOUT device
2357 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302358 if (mbhc->hph_status == SND_JACK_HEADSET)
2359 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2360 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002361 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2362 wcd9xxx_cleanup_hs_polling(mbhc);
2363 wcd9xxx_enable_hs_detect(mbhc, 1,
2364 MBHC_USE_MB_TRIGGER,
2365 false);
2366 }
2367 } else {
2368 /* Cancel possibly running hs_detect_work */
2369 wcd9xxx_cancel_hs_detect_plug(mbhc,
2370 &mbhc->correct_plug_noswch);
2371 /*
2372 * If this removal is not false, first check the micbias
2373 * switch status and switch it to LDOH if it is already
2374 * switched to VDDIO.
2375 */
2376 wcd9xxx_switch_micbias(mbhc, 0);
2377
2378 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302379 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002380 wcd9xxx_cleanup_hs_polling(mbhc);
2381 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2382 MBHC_USE_HPHL_TRIGGER,
2383 true);
2384 }
2385 } else {
2386 wcd9xxx_start_hs_polling(mbhc);
2387 }
2388 pr_debug("%s: leave\n", __func__);
2389}
2390
2391/* called only from interrupt which is under codec_resource_lock acquisition */
2392static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2393 bool is_mb_trigger)
2394{
2395 /* Cancel possibly running hs_detect_work */
2396 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2397
2398 if (is_mb_trigger) {
2399 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2400 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2401 } else {
2402 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2403 __func__);
2404 wcd9xxx_mbhc_detect_plug_type(mbhc);
2405 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002406}
2407
2408static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2409{
Joonwoo Parka8890262012-10-15 12:04:27 -07002410 struct wcd9xxx_mbhc *mbhc = data;
2411
2412 pr_debug("%s: enter, removal interrupt\n", __func__);
2413 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002414 /*
2415 * While we don't know whether MIC is there or not, let the resmgr know
2416 * so micbias can be disabled temporarily
2417 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002418 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002419 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2420 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002421 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2422 WCD9XXX_COND_HPH, false);
2423 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2424 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2425 WCD9XXX_COND_HPH, false);
2426 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002427
Joonwoo Park80a01172012-10-15 16:05:23 -07002428 if (mbhc->mbhc_cfg->detect_extn_cable &&
2429 !wcd9xxx_swch_level_remove(mbhc))
2430 wcd9xxx_hs_remove_irq_noswch(mbhc);
2431 else
2432 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002433
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002434 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2435 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2436 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002437 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2438 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002439 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2440 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2441 WCD9XXX_COND_HPH, true);
2442 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002443 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2444
2445 return IRQ_HANDLED;
2446}
2447
2448static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2449{
2450 bool is_mb_trigger, is_removal;
2451 struct wcd9xxx_mbhc *mbhc = data;
2452 struct snd_soc_codec *codec = mbhc->codec;
2453
2454 pr_debug("%s: enter\n", __func__);
2455 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002456 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002457
2458 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2459 0x10);
2460 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2461 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2462
2463 /* Turn off both HPH and MIC line schmitt triggers */
2464 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2465 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2466 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2467
Joonwoo Park80a01172012-10-15 16:05:23 -07002468 if (mbhc->mbhc_cfg->detect_extn_cable &&
2469 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2470 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2471 else
2472 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002473
2474 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2475 return IRQ_HANDLED;
2476}
2477
2478static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2479{
2480 struct delayed_work *dwork;
2481 short bias_value;
2482 int dce_mv, sta_mv;
2483 struct wcd9xxx_mbhc *mbhc;
2484
2485 pr_debug("%s:\n", __func__);
2486
2487 dwork = to_delayed_work(work);
2488 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2489
2490 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2491 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2492
2493 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2494 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2495 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2496
2497 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002498 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002499 mbhc->buttons_pressed);
2500
2501 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002502 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002503}
2504
2505static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2506{
2507 struct delayed_work *dwork;
2508 struct wcd9xxx_mbhc *mbhc;
2509 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002510 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002511
2512 dwork = to_delayed_work(work);
2513 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2514 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002515 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002516
2517 pr_debug("%s:\n", __func__);
2518
2519 /* Turn off both HPH and MIC line schmitt triggers */
2520 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2521 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2522 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002523 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002524 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002525 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002526}
2527
2528static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2529{
2530 u32 cfg_offset;
2531 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2532 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2533
2534 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2535 return false;
2536
2537 /*
2538 * Previous check guarantees that there is enough fw data up
2539 * to num_btn
2540 */
2541 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2542 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2543 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2544 return false;
2545
2546 /*
2547 * Previous check guarantees that there is enough fw data up
2548 * to start of impedance detection configuration
2549 */
2550 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2551 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2552
2553 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2554 return false;
2555
2556 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2557 return false;
2558
2559 return true;
2560}
2561
2562static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002563 enum meas_type dce, s16 vin_mv,
2564 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002565{
2566 s16 diff, zero;
2567 u32 mb_mv, in;
2568 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002569 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002570
2571 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002572 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002573
2574 if (mb_mv == 0) {
2575 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2576 return -EINVAL;
2577 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002578 if (cs_enable) {
2579 mb_mv = VDDIO_MICBIAS_MV;
2580 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2581 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002582
2583 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002584 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2585 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002586 } else {
2587 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2588 zero = (mbhc->mbhc_data.sta_z);
2589 }
2590 in = (u32) diff * vin_mv;
2591
2592 value = (u16) (in / mb_mv) + zero;
2593 return value;
2594}
2595
2596static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2597{
2598 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002599 s16 adj_v_hs_max;
2600 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 -07002601 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2602 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2603 u16 *btn_high;
2604 int i;
2605
2606 pr_debug("%s: enter\n", __func__);
2607 codec = mbhc->codec;
2608 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2609 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2610
Joonwoo Park73375212013-05-07 12:42:44 -07002611 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002612 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002613 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002614 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002615
2616 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2617 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2618
2619 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002620 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2621 true);
2622 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002623 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002624 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002625 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002626 mbhc->mbhc_data.v_inval_ins_low =
2627 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2628 false);
2629 mbhc->mbhc_data.v_inval_ins_high =
2630 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2631 false);
2632 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002633 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2634 WCD9XXX_V_CS_HS_MAX,
2635 true);
2636 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2637 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002638
2639 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2640 MBHC_BTN_DET_V_BTN_HIGH);
2641 for (i = 0; i < btn_det->num_btn; i++)
2642 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2643
Joonwoo Park73375212013-05-07 12:42:44 -07002644 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2645 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2646 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2647 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2648 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2649 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002650
Joonwoo Park73375212013-05-07 12:42:44 -07002651 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002652 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2653 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002654 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002655 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2656 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002657 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002658 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2659 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002660 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002661 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2662 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002663
Joonwoo Park73375212013-05-07 12:42:44 -07002664 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2665 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2666 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2667 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002668
Joonwoo Parka8890262012-10-15 12:04:27 -07002669 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2670
2671 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002672 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002673 pr_debug("%s: leave\n", __func__);
2674}
2675
2676static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2677{
2678 /*
2679 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2680 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2681 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2682 */
2683 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2684}
2685
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002686/*
2687 * Mic Bias Enable Decision
2688 * Return true if high_hph_cnt is a power of 2 (!= 2)
2689 * otherwise return false
2690 */
2691static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2692{
2693 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2694}
2695
Joonwoo Parka8890262012-10-15 12:04:27 -07002696static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2697{
2698 struct wcd9xxx_mbhc *mbhc;
2699 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002700 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002701 unsigned long timeout;
2702 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002703 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002704 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002705 bool current_source_enable;
2706 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002707
2708 pr_debug("%s: enter\n", __func__);
2709
2710 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2711 codec = mbhc->codec;
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002712
2713 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2714 (1 << MBHC_CS_ENABLE_POLLING)) != 0) &&
2715 (!(snd_soc_read(codec,
2716 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002717
2718 wcd9xxx_onoff_ext_mclk(mbhc, true);
2719
2720 /*
2721 * Keep override on during entire plug type correction work.
2722 *
2723 * This is okay under the assumption that any switch irqs which use
2724 * MBHC block cancel and sync this work so override is off again
2725 * prior to switch interrupt handler's MBHC block usage.
2726 * Also while this correction work is running, we can guarantee
2727 * DAPM doesn't use any MBHC block as this work only runs with
2728 * headphone detection.
2729 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002730 if (current_source_enable)
2731 wcd9xxx_turn_onoff_current_source(mbhc, true,
2732 false);
2733 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002734 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002735
2736 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2737 while (!time_after(jiffies, timeout)) {
2738 ++retry;
2739 rmb();
2740 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002741 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002742 pr_debug("%s: stop requested\n", __func__);
2743 break;
2744 }
2745
2746 msleep(HS_DETECT_PLUG_INERVAL_MS);
2747 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002748 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002749 pr_debug("%s: Switch level is low\n", __func__);
2750 break;
2751 }
2752
2753 /* can race with removal interrupt */
2754 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002755 if (current_source_enable)
2756 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
2757 highhph);
2758 else
2759 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002760 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2761
Joonwoo Park80a01172012-10-15 16:05:23 -07002762 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
2763 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002764
2765 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
2766 (highhph_cnt + 1) :
2767 0;
2768 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07002769 if (plug_type == PLUG_TYPE_INVALID) {
2770 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07002771 if (!mbhc->mbhc_cfg->detect_extn_cable &&
2772 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07002773 mbhc->current_plug == PLUG_TYPE_NONE) {
2774 wcd9xxx_report_plug(mbhc, 1,
2775 SND_JACK_HEADPHONE);
2776 }
2777 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2778 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07002779 if (mbhc->mbhc_cfg->detect_extn_cable) {
2780 if (mbhc->current_plug != plug_type)
2781 wcd9xxx_report_plug(mbhc, 1,
2782 SND_JACK_HEADPHONE);
2783 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002784 wcd9xxx_report_plug(mbhc, 1,
2785 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07002786 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002787 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
2788 pr_debug("%s: High HPH detected, continue polling\n",
2789 __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002790 } else {
2791 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
2792 pt_gnd_mic_swap_cnt++;
2793 if (pt_gnd_mic_swap_cnt <
2794 GND_MIC_SWAP_THRESHOLD)
2795 continue;
2796 else if (pt_gnd_mic_swap_cnt >
2797 GND_MIC_SWAP_THRESHOLD) {
2798 /*
2799 * This is due to GND/MIC switch didn't
2800 * work, Report unsupported plug
2801 */
2802 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
2803 /*
2804 * if switch is toggled, check again,
2805 * otherwise report unsupported plug
2806 */
2807 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
2808 continue;
2809 }
2810 } else
2811 pt_gnd_mic_swap_cnt = 0;
2812
2813 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002814 /* Turn off override/current source */
2815 if (current_source_enable)
2816 wcd9xxx_turn_onoff_current_source(mbhc, false,
2817 false);
2818 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002819 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002820 /*
2821 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
2822 */
2823 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2824 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2825 pr_debug("Attempt %d found correct plug %d\n", retry,
2826 plug_type);
2827 correction = true;
2828 break;
2829 }
2830 }
2831
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002832 highhph = false;
2833 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002834 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
2835 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002836 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002837 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002838 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002839 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002840 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002841
2842 if (!correction && current_source_enable)
2843 wcd9xxx_turn_onoff_current_source(mbhc, false, highhph);
2844 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002845 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002846
2847 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002848
2849 if (mbhc->mbhc_cfg->detect_extn_cable) {
2850 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002851 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
2852 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07002853 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
2854 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07002855 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002856 /* Enable removal detection */
2857 wcd9xxx_cleanup_hs_polling(mbhc);
2858 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
2859 }
2860 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2861 }
2862 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07002863 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002864 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002865}
2866
2867static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
2868{
2869 bool insert;
2870 bool is_removed = false;
2871 struct snd_soc_codec *codec = mbhc->codec;
2872
2873 pr_debug("%s: enter\n", __func__);
2874
2875 mbhc->in_swch_irq_handler = true;
2876 /* Wait here for debounce time */
2877 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
2878
2879 WCD9XXX_BCL_LOCK(mbhc->resmgr);
2880
2881 /* cancel pending button press */
2882 if (wcd9xxx_cancel_btn_work(mbhc))
2883 pr_debug("%s: button press is canceled\n", __func__);
2884
Santosh Mardic82bd362013-10-25 18:35:29 +05302885 /* cancel detect plug */
2886 wcd9xxx_cancel_hs_detect_plug(mbhc,
2887 &mbhc->correct_plug_swch);
2888
Joonwoo Parka8890262012-10-15 12:04:27 -07002889 insert = !wcd9xxx_swch_level_remove(mbhc);
2890 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
2891 mbhc->current_plug, insert);
2892 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
2893 mbhc->lpi_enabled = false;
2894 wmb();
2895
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002896 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
2897 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
2898 (1 << 1)))
2899 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07002900
2901 /* Disable Mic Bias pull down and HPH Switch to GND */
2902 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2903 0x00);
2904 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
2905 wcd9xxx_mbhc_detect_plug_type(mbhc);
2906 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
2907 mbhc->lpi_enabled = false;
2908 wmb();
2909
Joonwoo Parka8890262012-10-15 12:04:27 -07002910 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2911 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
2912 is_removed = true;
2913 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
2914 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
2915 is_removed = true;
2916 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2917 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302918 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002919 wcd9xxx_cleanup_hs_polling(mbhc);
2920 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
2921 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07002922 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
2923 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
2924 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07002925 }
2926
2927 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07002928 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
2929 0x00);
2930 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2931 0x02, 0x00);
2932
Joonwoo Parka8890262012-10-15 12:04:27 -07002933 /* Enable Mic Bias pull down and HPH Switch to GND */
2934 snd_soc_update_bits(codec,
2935 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
2936 0x01);
2937 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
2938 0x01);
2939 /* Make sure mic trigger is turned off */
2940 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
2941 0x01, 0x01);
2942 snd_soc_update_bits(codec,
2943 mbhc->mbhc_bias_regs.mbhc_reg,
2944 0x90, 0x00);
2945 /* Reset MBHC State Machine */
2946 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2947 0x08, 0x08);
2948 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2949 0x08, 0x00);
2950 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002951 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002952 }
2953 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07002954exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07002955 mbhc->in_swch_irq_handler = false;
2956 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2957 pr_debug("%s: leave\n", __func__);
2958}
2959
2960static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
2961{
2962 int r = IRQ_HANDLED;
2963 struct wcd9xxx_mbhc *mbhc = data;
2964
2965 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002966 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07002967 pr_warn("%s: failed to hold suspend\n", __func__);
2968 r = IRQ_NONE;
2969 } else {
2970 /* Call handler */
2971 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002972 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002973 }
2974
2975 pr_debug("%s: leave %d\n", __func__, r);
2976 return r;
2977}
2978
Joonwoo Park218e73f2013-08-21 16:22:18 -07002979static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07002980{
Joonwoo Park73375212013-05-07 12:42:44 -07002981 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07002982 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002983 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07002984 const s16 v_ins_hu =
2985 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
2986 const s16 v_ins_h =
2987 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
2988 const s16 v_b1_hu =
2989 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
2990 const s16 v_b1_h =
2991 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07002992 const unsigned long timeout =
2993 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07002994
Joonwoo Park218e73f2013-08-21 16:22:18 -07002995 while (time_before(jiffies, timeout)) {
2996 /*
2997 * This function needs to run measurements just few times during
2998 * release debounce time. Make 1ms interval to avoid
2999 * unnecessary excessive measurements.
3000 */
3001 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07003002 if (i == 0) {
3003 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
3004 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
3005 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003006 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003007 r = 1;
3008 break;
3009 }
3010 } else {
3011 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
3012 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
3013 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003014 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003015 r = 1;
3016 break;
3017 }
3018 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003019 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003020 }
3021
3022 return r;
3023}
3024
3025/* called under codec_resource_lock acquisition */
3026static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3027 const s32 micmv)
3028{
3029 s16 *v_btn_low, *v_btn_high;
3030 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3031 int i, btn = -1;
3032
3033 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3034 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3035 MBHC_BTN_DET_V_BTN_LOW);
3036 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3037 MBHC_BTN_DET_V_BTN_HIGH);
3038
3039 for (i = 0; i < btn_det->num_btn; i++) {
3040 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3041 btn = i;
3042 break;
3043 }
3044 }
3045
3046 if (btn == -1)
3047 pr_debug("%s: couldn't find button number for mic mv %d\n",
3048 __func__, micmv);
3049
3050 return btn;
3051}
3052
3053static int wcd9xxx_get_button_mask(const int btn)
3054{
3055 int mask = 0;
3056 switch (btn) {
3057 case 0:
3058 mask = SND_JACK_BTN_0;
3059 break;
3060 case 1:
3061 mask = SND_JACK_BTN_1;
3062 break;
3063 case 2:
3064 mask = SND_JACK_BTN_2;
3065 break;
3066 case 3:
3067 mask = SND_JACK_BTN_3;
3068 break;
3069 case 4:
3070 mask = SND_JACK_BTN_4;
3071 break;
3072 case 5:
3073 mask = SND_JACK_BTN_5;
3074 break;
3075 case 6:
3076 mask = SND_JACK_BTN_6;
3077 break;
3078 case 7:
3079 mask = SND_JACK_BTN_7;
3080 break;
3081 }
3082 return mask;
3083}
3084
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003085static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003086{
3087 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003088 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003089 struct snd_soc_codec *codec = mbhc->codec;
3090
3091 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3092 /* Pull down micbias to ground and disconnect vddio switch */
3093 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3094 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x81, 0x1);
3095 reg1 = snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg);
3096 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 1 << 7, 0);
3097
3098 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003099 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3100 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003101 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003102 if (sta_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003103 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003104 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3105 }
3106 if (dce_z) {
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003107 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, false);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003108 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3109 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003110
Joonwoo Park520a0f92013-05-14 19:39:58 -07003111 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003112 if (change)
3113 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3114 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003115 /* Disable pull down micbias to ground */
3116 snd_soc_write(codec, mbhc->mbhc_bias_regs.mbhc_reg, reg1);
3117 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3118}
3119
Joonwoo Park218e73f2013-08-21 16:22:18 -07003120void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3121{
3122 const u16 sta_z = mbhc->mbhc_data.sta_z;
3123 const u16 dce_z = mbhc->mbhc_data.dce_z;
3124
3125 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z);
3126 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3127 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3128 mbhc->mbhc_data.sta_z & 0xFFFF,
3129 mbhc->mbhc_data.dce_z & 0xFFFF);
3130
3131 wcd9xxx_mbhc_calc_thres(mbhc);
3132 wcd9xxx_calibrate_hs_polling(mbhc);
3133}
3134
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003135/*
3136 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3137 * to ceilmv + buffer
3138 */
3139static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
3140{
3141 u16 v_brh, v_b1_hu;
3142 int mv;
3143 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3144 void *calibration = mbhc->mbhc_cfg->calibration;
3145 struct snd_soc_codec *codec = mbhc->codec;
3146
3147 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3148 mv = ceilmv + btn_det->v_btn_press_delta_cic;
3149 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3150
3151 /* update LSB first so mbhc hardware block doesn't see too low value */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003152 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003153 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu & 0xFF);
3154 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3155 (v_b1_hu >> 8) & 0xFF);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003156 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003157 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh & 0xFF);
3158 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3159 (v_brh >> 8) & 0xFF);
3160 return 0;
3161}
3162
Joonwoo Parka8890262012-10-15 12:04:27 -07003163irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3164{
3165 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003166 bool vddio;
3167 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003168 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003169 s32 stamv, stamv_s;
3170 s16 *v_btn_high;
3171 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003172 int btn = -1, meas = 0;
3173 struct wcd9xxx_mbhc *mbhc = data;
3174 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3175 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3176 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003177 short dce[d->n_btn_meas + 1], sta;
3178 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003179 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003180 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003181 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003182 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003183
3184 pr_debug("%s: enter\n", __func__);
3185
3186 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3187 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3188
3189 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3190 pr_debug("%s: mbhc is being recovered, skip button press\n",
3191 __func__);
3192 goto done;
3193 }
3194
3195 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3196
3197 if (!mbhc->polling_active) {
3198 pr_warn("%s: mbhc polling is not active, skip button press\n",
3199 __func__);
3200 goto done;
3201 }
3202
Joonwoo Parka8890262012-10-15 12:04:27 -07003203 /* If switch nterrupt already kicked in, ignore button press */
3204 if (mbhc->in_swch_irq_handler) {
3205 pr_debug("%s: Swtich level changed, ignore button press\n",
3206 __func__);
3207 btn = -1;
3208 goto done;
3209 }
3210
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303211 /*
3212 * setup internal micbias if codec uses internal micbias for
3213 * headset detection
3214 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05303215 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303216 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3217 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3218 else
3219 pr_err("%s: internal bias requested but codec did not provide callback\n",
3220 __func__);
3221 }
3222
3223
Joonwoo Parka8890262012-10-15 12:04:27 -07003224 /* Measure scaled HW DCE */
3225 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3226 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003227
Joonwoo Park218e73f2013-08-21 16:22:18 -07003228 dce_z = mbhc->mbhc_data.dce_z;
3229 sta_z = mbhc->mbhc_data.sta_z;
3230
Joonwoo Parka8890262012-10-15 12:04:27 -07003231 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003232 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003233 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003234 if (mbhc_status != STATUS_REL_DETECTION) {
3235 if (mbhc->mbhc_last_resume &&
3236 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3237 pr_debug("%s: Button is released after resume\n",
3238 __func__);
3239 n_btn_meas = 0;
3240 } else {
3241 pr_debug("%s: Button is released without resume",
3242 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003243 if (mbhc->update_z) {
3244 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003245 dce_z = mbhc->mbhc_data.dce_z;
3246 sta_z = mbhc->mbhc_data.sta_z;
3247 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003248 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003249 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3250 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003251 if (vddio)
3252 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3253 false);
3254 else
3255 stamv_s = stamv;
3256 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003257 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003258 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3259 false) : mv[0];
3260 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003261 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3262 btn = -1;
3263 goto done;
3264 }
3265 }
3266
Joonwoo Park520a0f92013-05-14 19:39:58 -07003267 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3268 meas++)
3269 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3270
Joonwoo Park218e73f2013-08-21 16:22:18 -07003271 if (mbhc->update_z) {
3272 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003273 dce_z = mbhc->mbhc_data.dce_z;
3274 sta_z = mbhc->mbhc_data.sta_z;
3275 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003276 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003277
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003278 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3279 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003280 if (vddio)
3281 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3282 else
3283 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003284 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003285 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003286
3287 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003288 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3289 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003290 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3291 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003292 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003293 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003294 if (n_btn_meas == 0)
3295 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003296 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3297 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003298 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3299 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003300 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3301 mv[meas];
3302 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003303 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003304 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3305 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003306 /*
3307 * if large enough measurements are collected,
3308 * start to check if last all n_btn_con measurements were
3309 * in same button low/high range
3310 */
3311 if (meas + 1 >= d->n_btn_con) {
3312 for (i = 0; i < d->n_btn_con; i++)
3313 if ((btnmeas[meas] < 0) ||
3314 (btnmeas[meas] != btnmeas[meas - i]))
3315 break;
3316 if (i == d->n_btn_con) {
3317 /* button pressed */
3318 btn = btnmeas[meas];
3319 break;
3320 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3321 /*
3322 * if left measurements are less than n_btn_con,
3323 * it's impossible to find button number
3324 */
3325 break;
3326 }
3327 }
3328 }
3329
3330 if (btn >= 0) {
3331 if (mbhc->in_swch_irq_handler) {
3332 pr_debug(
3333 "%s: Switch irq triggered, ignore button press\n",
3334 __func__);
3335 goto done;
3336 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003337 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3338 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3339 MBHC_BTN_DET_V_BTN_HIGH);
3340 WARN_ON(btn >= btn_det->num_btn);
3341 /* reprogram release threshold to catch voltage ramp up early */
3342 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
3343
Joonwoo Parka8890262012-10-15 12:04:27 -07003344 mask = wcd9xxx_get_button_mask(btn);
3345 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003346 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003347 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3348 msecs_to_jiffies(400)) == 0) {
3349 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003350 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003351 }
3352 } else {
3353 pr_debug("%s: bogus button press, too short press?\n",
3354 __func__);
3355 }
3356
3357 done:
3358 pr_debug("%s: leave\n", __func__);
3359 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3360 return IRQ_HANDLED;
3361}
3362
3363static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3364{
3365 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003366 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003367 struct wcd9xxx_mbhc *mbhc = data;
3368
3369 pr_debug("%s: enter\n", __func__);
3370 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3371 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3372
Joonwoo Parka8890262012-10-15 12:04:27 -07003373 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3374 ret = wcd9xxx_cancel_btn_work(mbhc);
3375 if (ret == 0) {
3376 pr_debug("%s: Reporting long button release event\n",
3377 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003378 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003379 mbhc->buttons_pressed);
3380 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003381 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003382 pr_debug("%s: Fake button press interrupt\n",
3383 __func__);
3384 } else {
3385 if (mbhc->in_swch_irq_handler) {
3386 pr_debug("%s: Switch irq kicked in, ignore\n",
3387 __func__);
3388 } else {
3389 pr_debug("%s: Reporting btn press\n",
3390 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003391 wcd9xxx_jack_report(mbhc,
3392 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003393 mbhc->buttons_pressed,
3394 mbhc->buttons_pressed);
3395 pr_debug("%s: Reporting btn release\n",
3396 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003397 wcd9xxx_jack_report(mbhc,
3398 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003399 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003400 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003401 }
3402 }
3403 }
3404
3405 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3406 }
3407
3408 wcd9xxx_calibrate_hs_polling(mbhc);
3409
Joonwoo Park218e73f2013-08-21 16:22:18 -07003410 if (waitdebounce)
3411 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003412 wcd9xxx_start_hs_polling(mbhc);
3413
3414 pr_debug("%s: leave\n", __func__);
3415 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3416 return IRQ_HANDLED;
3417}
3418
3419static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3420{
3421 struct wcd9xxx_mbhc *mbhc = data;
3422 struct snd_soc_codec *codec;
3423
3424 pr_info("%s: received HPHL OCP irq\n", __func__);
3425
3426 if (mbhc) {
3427 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003428 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3429 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003430 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003431 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003432 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3433 0x10, 0x00);
3434 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3435 0x10, 0x10);
3436 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003437 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003438 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003439 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003440 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003441 mbhc->hph_status,
3442 WCD9XXX_JACK_MASK);
3443 }
3444 } else {
3445 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3446 }
3447
3448 return IRQ_HANDLED;
3449}
3450
3451static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3452{
3453 struct wcd9xxx_mbhc *mbhc = data;
3454 struct snd_soc_codec *codec;
3455
3456 pr_info("%s: received HPHR OCP irq\n", __func__);
3457 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003458 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3459 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003460 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003461 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003462 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3463 0x00);
3464 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3465 0x10);
3466 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003467 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003468 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003469 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003470 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003471 mbhc->hph_status, WCD9XXX_JACK_MASK);
3472 }
3473
3474 return IRQ_HANDLED;
3475}
3476
3477static int wcd9xxx_acdb_mclk_index(const int rate)
3478{
3479 if (rate == MCLK_RATE_12288KHZ)
3480 return 0;
3481 else if (rate == MCLK_RATE_9600KHZ)
3482 return 1;
3483 else {
3484 BUG_ON(1);
3485 return -EINVAL;
3486 }
3487}
3488
3489static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3490{
3491 u32 dce_wait, sta_wait;
3492 u8 ncic, nmeas, navg;
3493 void *calibration;
3494 u8 *n_cic, *n_ready;
3495 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3496 u8 npoll = 4, nbounce_wait = 30;
3497 struct snd_soc_codec *codec = mbhc->codec;
3498 int idx = wcd9xxx_acdb_mclk_index(rate);
3499 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3500
3501 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3502 rate);
3503 calibration = mbhc->mbhc_cfg->calibration;
3504
3505 /*
3506 * First compute the DCE / STA wait times depending on tunable
3507 * parameters. The value is computed in microseconds
3508 */
3509 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3510 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3511 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3512 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3513 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3514
3515 /* ncic stays with the same what we had during calibration */
3516 ncic = n_cic[idxmclk];
3517 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3518 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3519 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003520 /* give extra margin to sta for safety */
3521 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003522 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3523 n_ready[idx]) + 10;
3524
3525 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3526 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3527
3528 if (rate == MCLK_RATE_12288KHZ) {
3529 npoll = 4;
3530 nbounce_wait = 30;
3531 } else if (rate == MCLK_RATE_9600KHZ) {
3532 npoll = 3;
3533 nbounce_wait = 23;
3534 }
3535
3536 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3537 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3538 pr_debug("%s: leave\n", __func__);
3539}
3540
3541static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3542{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003543 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003544 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003545 struct snd_soc_codec *codec = mbhc->codec;
3546
3547 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003548 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3549 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003550 wcd9xxx_turn_onoff_rel_detection(codec, false);
3551
3552 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3553 WARN_ON(!mbhc->mbhc_data.t_dce);
3554 WARN_ON(!mbhc->mbhc_data.t_sta);
3555
3556 /*
3557 * LDOH and CFILT are already configured during pdata handling.
3558 * Only need to make sure CFILT and bandgap are in Fast mode.
3559 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003560 *
3561 * In case when Micbias is powered by external source, request
3562 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003563 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003564 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3565 mbhc->mbhc_cb->enable_mb_source(codec, true);
3566
Joonwoo Parka8890262012-10-15 12:04:27 -07003567 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303568 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3569 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3570 else
3571 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3572 0x40, 0x00);
3573
Joonwoo Parka8890262012-10-15 12:04:27 -07003574 /*
3575 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3576 * to perform ADC calibration
3577 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303578 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3579 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3580 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003581 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3582 mbhc->mbhc_cfg->micbias << 5);
3583 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3584 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3585 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303586 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3587 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3588 else
3589 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3590 0x04, 0x04);
3591
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003592 /* Pull down micbias to ground */
3593 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3594 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3595 /* Disconnect override from micbias */
3596 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3597 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3598 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303599 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303600 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3601 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3602 else
3603 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3604 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003605 /*
3606 * Hardware that has external cap can delay mic bias ramping down up
3607 * to 50ms.
3608 */
3609 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003610 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003611 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003612 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003613 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003614
3615 /* compute dce_z for current source */
3616 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3617 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3618 WCD9XXX_MBHC_NSC_CS << 3);
3619
3620 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3621 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3622 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3623 false);
3624 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3625 mbhc->mbhc_data.dce_nsc_cs_z);
3626
3627 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3628
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003629 /* STA measurement for 0 voltage */
3630 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3631 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3632 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003633
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003634 /* Restore registers */
3635 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3636 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003637
3638 /* DCE measurment for MB voltage */
3639 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3640 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303641 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303642 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3643 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3644 else
3645 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3646 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003647 /*
3648 * Hardware that has external cap can delay mic bias ramping down up
3649 * to 50ms.
3650 */
3651 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003652 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3653 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3654 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3655
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003656 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003657 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3658 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3659 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303660 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303661 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3662 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3663 else
3664 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3665 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003666 /*
3667 * Hardware that has external cap can delay mic bias ramping down up
3668 * to 50ms.
3669 */
3670 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003671 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3672 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3673 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3674
3675 /* Restore default settings. */
3676 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303677 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303678 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303679 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3680 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3681 else
3682 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3683 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003684 usleep_range(100, 100);
3685
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003686 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
3687 mbhc->mbhc_cb->enable_mb_source(codec, false);
3688
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003689 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
3690 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003691 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003692
Joonwoo Parka8890262012-10-15 12:04:27 -07003693 pr_debug("%s: leave\n", __func__);
3694}
3695
3696static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
3697{
3698 int n;
3699 u8 *gain;
3700 struct wcd9xxx_mbhc_general_cfg *generic;
3701 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3702 struct snd_soc_codec *codec = mbhc->codec;
3703 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3704
3705 pr_debug("%s: enter\n", __func__);
3706 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
3707 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3708
3709 for (n = 0; n < 8; n++) {
3710 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
3711 0x07, n);
3712 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
3713 btn_det->c[n]);
3714 }
3715
3716 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
3717 btn_det->nc);
3718
3719 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
3720 generic->mbhc_nsa << 4);
3721
3722 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
3723 btn_det->n_meas);
3724
3725 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
3726 generic->mbhc_navg);
3727
3728 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
3729
3730 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3731 btn_det->mbhc_nsc << 3);
3732
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07003733 if (mbhc->mbhc_cb &&
3734 mbhc->mbhc_cb->get_cdc_type() !=
3735 WCD9XXX_CDC_TYPE_HELICON) {
3736 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
3737 snd_soc_update_bits(codec,
3738 mbhc->resmgr->reg_addr->micb_4_mbhc,
3739 0x03, MBHC_MICBIAS2);
3740 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003741
3742 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
3743
3744 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
3745
3746 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
3747 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
3748 gain[idx] << 3);
3749
3750 pr_debug("%s: leave\n", __func__);
3751}
3752
3753static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
3754{
3755 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003756 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05303757
Joonwoo Parka8890262012-10-15 12:04:27 -07003758 if (mbhc->mbhc_cfg->gpio) {
3759 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
3760 wcd9xxx_mech_plug_detect_irq,
3761 (IRQF_TRIGGER_RISING |
3762 IRQF_TRIGGER_FALLING |
3763 IRQF_DISABLED),
3764 "headset detect", mbhc);
3765 if (ret) {
3766 pr_err("%s: Failed to request gpio irq %d\n", __func__,
3767 mbhc->mbhc_cfg->gpio_irq);
3768 } else {
3769 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
3770 if (ret)
3771 pr_err("%s: Failed to enable wake up irq %d\n",
3772 __func__, mbhc->mbhc_cfg->gpio_irq);
3773 }
3774 } else if (mbhc->mbhc_cfg->insert_detect) {
3775 /* Enable HPHL_10K_SW */
3776 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3777 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303778
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003779 ret = wcd9xxx_request_irq(core_res,
3780 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07003781 wcd9xxx_mech_plug_detect_irq,
3782 "Jack Detect",
3783 mbhc);
3784 if (ret)
3785 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003786 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003787 }
3788
3789 return ret;
3790}
3791
3792static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
3793{
3794 int ret = 0;
3795 struct snd_soc_codec *codec = mbhc->codec;
3796
3797 pr_debug("%s: enter\n", __func__);
3798
3799 /* Enable MCLK during calibration */
3800 wcd9xxx_onoff_ext_mclk(mbhc, true);
3801 wcd9xxx_mbhc_setup(mbhc);
3802 wcd9xxx_mbhc_cal(mbhc);
3803 wcd9xxx_mbhc_calc_thres(mbhc);
3804 wcd9xxx_onoff_ext_mclk(mbhc, false);
3805 wcd9xxx_calibrate_hs_polling(mbhc);
3806
3807 /* Enable Mic Bias pull down and HPH Switch to GND */
3808 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
3809 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
3810 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
3811
3812 if (!IS_ERR_VALUE(ret)) {
3813 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3814 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003815 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003816 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003817 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003818 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003819
3820 /* Initialize mechanical mbhc */
3821 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
3822
3823 if (!ret && mbhc->mbhc_cfg->gpio) {
3824 /* Requested with IRQF_DISABLED */
3825 enable_irq(mbhc->mbhc_cfg->gpio_irq);
3826
3827 /* Bootup time detection */
3828 wcd9xxx_swch_irq_handler(mbhc);
3829 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
3830 pr_debug("%s: Setting up codec own insert detection\n",
3831 __func__);
3832 /* Setup for insertion detection */
3833 wcd9xxx_insert_detect_setup(mbhc, true);
3834 }
3835 }
3836
3837 pr_debug("%s: leave\n", __func__);
3838
3839 return ret;
3840}
3841
3842static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
3843{
3844 struct delayed_work *dwork;
3845 struct wcd9xxx_mbhc *mbhc;
3846 struct snd_soc_codec *codec;
3847 const struct firmware *fw;
3848 int ret = -1, retry = 0;
3849
3850 dwork = to_delayed_work(work);
3851 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
3852 codec = mbhc->codec;
3853
3854 while (retry < FW_READ_ATTEMPTS) {
3855 retry++;
3856 pr_info("%s:Attempt %d to request MBHC firmware\n",
3857 __func__, retry);
3858 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
3859 codec->dev);
3860
3861 if (ret != 0) {
3862 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
3863 } else {
3864 pr_info("%s: MBHC Firmware read succesful\n", __func__);
3865 break;
3866 }
3867 }
3868
3869 if (ret != 0) {
3870 pr_err("%s: Cannot load MBHC firmware use default cal\n",
3871 __func__);
3872 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
3873 pr_err("%s: Invalid MBHC cal data size use default cal\n",
3874 __func__);
3875 release_firmware(fw);
3876 } else {
3877 mbhc->mbhc_cfg->calibration = (void *)fw->data;
3878 mbhc->mbhc_fw = fw;
3879 }
3880
3881 (void) wcd9xxx_init_and_calibrate(mbhc);
3882}
3883
3884#ifdef CONFIG_DEBUG_FS
3885ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
3886 size_t count, loff_t *pos)
3887{
3888 const int size = 768;
3889 char buffer[size];
3890 int n = 0;
3891 struct wcd9xxx_mbhc *mbhc = file->private_data;
3892 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07003893 const s16 v_ins_hu =
3894 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3895 const s16 v_ins_h =
3896 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3897 const s16 v_b1_hu =
3898 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3899 const s16 v_b1_h =
3900 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
3901 const s16 v_br_h =
3902 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07003903
Joonwoo Park520a0f92013-05-14 19:39:58 -07003904 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
3905 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07003906 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
3907 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003908 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
3909 p->dce_nsc_cs_z,
3910 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
3911 p->dce_nsc_cs_z,
3912 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07003913 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
3914 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
3915 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
3916 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07003917 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
3918 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
3919 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
3920 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
3921 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
3922 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
3923 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003924 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003925 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07003926 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003927 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003928 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07003929 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07003930 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
3931 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
3932 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
3933 p->v_no_mic,
3934 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
3935 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
3936 p->v_inval_ins_low);
3937 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
3938 p->v_inval_ins_high);
3939 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
3940 !wcd9xxx_swch_level_remove(mbhc));
3941 buffer[n] = 0;
3942
3943 return simple_read_from_buffer(buf, count, pos, buffer, n);
3944}
3945
3946static int codec_debug_open(struct inode *inode, struct file *file)
3947{
3948 file->private_data = inode->i_private;
3949 return 0;
3950}
3951
3952static ssize_t codec_debug_write(struct file *filp,
3953 const char __user *ubuf, size_t cnt,
3954 loff_t *ppos)
3955{
3956 char lbuf[32];
3957 char *buf;
3958 int rc;
3959 struct wcd9xxx_mbhc *mbhc = filp->private_data;
3960
3961 if (cnt > sizeof(lbuf) - 1)
3962 return -EINVAL;
3963
3964 rc = copy_from_user(lbuf, ubuf, cnt);
3965 if (rc)
3966 return -EFAULT;
3967
3968 lbuf[cnt] = '\0';
3969 buf = (char *)lbuf;
3970 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
3971 false : true;
3972 return rc;
3973}
3974
3975static const struct file_operations mbhc_trrs_debug_ops = {
3976 .open = codec_debug_open,
3977 .write = codec_debug_write,
3978};
3979
3980static const struct file_operations mbhc_debug_ops = {
3981 .open = codec_debug_open,
3982 .read = codec_mbhc_debug_read,
3983};
3984
3985static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
3986{
3987 mbhc->debugfs_poke =
3988 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
3989 &mbhc_trrs_debug_ops);
3990 mbhc->debugfs_mbhc =
3991 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
3992 NULL, mbhc, &mbhc_debug_ops);
3993}
3994
3995static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
3996{
3997 debugfs_remove(mbhc->debugfs_poke);
3998 debugfs_remove(mbhc->debugfs_mbhc);
3999}
4000#else
4001static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
4002{
4003}
4004
4005static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
4006{
4007}
4008#endif
4009
4010int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
4011 struct wcd9xxx_mbhc_config *mbhc_cfg)
4012{
Simmi Pateriya95466b12013-05-09 20:08:46 +05304013 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004014 struct snd_soc_codec *codec = mbhc->codec;
4015
4016 pr_debug("%s: enter\n", __func__);
4017
4018 if (!codec) {
4019 pr_err("%s: no codec\n", __func__);
4020 return -EINVAL;
4021 }
4022
4023 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
4024 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
4025 pr_err("Error: unsupported clock rate %d\n",
4026 mbhc_cfg->mclk_rate);
4027 return -EINVAL;
4028 }
4029
4030 /* Save mbhc config */
4031 mbhc->mbhc_cfg = mbhc_cfg;
4032
4033 /* Get HW specific mbhc registers' address */
4034 wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
4035
4036 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304037 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4038 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4039 else
4040 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4041 0x40, WCD9XXX_CFILT_FAST_MODE);
4042
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004043 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004044 * setup internal micbias if codec uses internal micbias for
4045 * headset detection
4046 */
4047 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304048 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004049 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304050 } else {
4051 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004052 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304053 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004054 }
4055
4056 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004057 * If codec has specific clock gating for MBHC,
4058 * remove the clock gate
4059 */
4060 if (mbhc->mbhc_cb &&
4061 mbhc->mbhc_cb->enable_clock_gate)
4062 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4063
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004064 if (!mbhc->mbhc_cfg->read_fw_bin ||
4065 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004066 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004067 } else {
4068 if (!mbhc->mbhc_fw)
4069 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4070 usecs_to_jiffies(FW_READ_TIMEOUT));
4071 else
4072 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4073 __func__, mbhc->mbhc_fw);
4074 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004075
4076 pr_debug("%s: leave %d\n", __func__, rc);
4077 return rc;
4078}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004079EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004080
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004081void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4082{
4083 if (mbhc->mbhc_fw) {
4084 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4085 release_firmware(mbhc->mbhc_fw);
4086 mbhc->mbhc_fw = NULL;
4087 }
4088}
4089EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4090
Joonwoo Parka8890262012-10-15 12:04:27 -07004091static enum wcd9xxx_micbias_num
4092wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4093{
4094 enum wcd9xxx_micbias_num ret;
4095 switch (event) {
4096 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004097 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4098 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4099 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004100 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004101 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004102 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004103 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4104 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4105 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004106 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004107 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004108 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004109 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4110 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4111 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004112 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004113 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004114 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004115 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4116 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4117 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004118 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004119 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004120 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004121 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004122 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004123 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004124 }
4125 return ret;
4126}
4127
4128static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4129{
4130 int ret;
4131 switch (event) {
4132 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4133 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4134 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4135 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4136 ret = WCD9XXX_CFILT1_SEL;
4137 break;
4138 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4139 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4140 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4141 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4142 ret = WCD9XXX_CFILT2_SEL;
4143 break;
4144 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4145 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4146 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4147 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4148 ret = WCD9XXX_CFILT3_SEL;
4149 break;
4150 default:
4151 ret = -1;
4152 }
4153 return ret;
4154}
4155
4156static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4157{
4158 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004159 const struct wcd9xxx_micbias_setting *mb_pdata =
4160 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004161
4162 switch (mbhc->mbhc_cfg->micbias) {
4163 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004164 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004165 break;
4166 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004167 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004168 break;
4169 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004170 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004171 break;
4172 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004173 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004174 break;
4175 default:
4176 cfilt = MBHC_MICBIAS_INVALID;
4177 break;
4178 }
4179 return cfilt;
4180}
4181
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004182static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4183{
4184 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4185 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4186 else
4187 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4188 0x40, on ? 0x40 : 0x00);
4189}
4190
Joonwoo Parka8890262012-10-15 12:04:27 -07004191static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4192 void *data)
4193{
4194 int ret = 0;
4195 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4196 struct snd_soc_codec *codec = mbhc->codec;
4197 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4198
4199 pr_debug("%s: enter event %s(%d)\n", __func__,
4200 wcd9xxx_get_event_string(event), event);
4201
4202 switch (event) {
4203 /* MICBIAS usage change */
4204 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4205 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4206 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4207 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004208 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4209 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004210 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004211 /*
4212 * Enable MBHC TxFE whenever micbias is
4213 * turned ON and polling is active
4214 */
4215 if (mbhc->polling_active)
4216 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4217 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004218 break;
4219 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4220 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4221 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4222 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004223 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004224 wcd9xxx_event_to_micbias(event) &&
4225 wcd9xxx_mbhc_polling(mbhc)) {
4226 /* if polling is on, restart it */
4227 wcd9xxx_pause_hs_polling(mbhc);
4228 wcd9xxx_start_hs_polling(mbhc);
4229 }
4230 break;
4231 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4232 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4233 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4234 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004235 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4236 wcd9xxx_event_to_micbias(event)) {
4237 if (mbhc->event_state &
4238 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4239 wcd9xxx_switch_micbias(mbhc, 1);
4240 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304241 * Disable MBHC TxFE, in case it was enabled earlier
4242 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004243 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304244 if (!mbhc->polling_active)
4245 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004246 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004247 break;
4248 /* PA usage change */
4249 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004250 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004251 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004252 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004253 wcd9xxx_switch_micbias(mbhc, 1);
4254 break;
4255 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004256 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004257 break;
4258 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004259 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004260 /* if HPH PAs are off, report OCP and switch back to CFILT */
4261 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4262 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4263 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4264 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004265 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004266 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
4267 1 << MBHC_EVENT_PRE_TX_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004268 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004269 break;
4270 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004271 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004272 /* if HPH PAs are off, report OCP and switch back to CFILT */
4273 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4274 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4275 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4276 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004277 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004278 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
4279 1 << MBHC_EVENT_PRE_TX_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004280 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004281 break;
4282 /* Clock usage change */
4283 case WCD9XXX_EVENT_PRE_MCLK_ON:
4284 break;
4285 case WCD9XXX_EVENT_POST_MCLK_ON:
4286 /* Change to lower TxAAF frequency */
4287 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4288 1 << 4);
4289 /* Re-calibrate clock rate dependent values */
4290 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4291 /* If clock source changes, stop and restart polling */
4292 if (wcd9xxx_mbhc_polling(mbhc)) {
4293 wcd9xxx_calibrate_hs_polling(mbhc);
4294 wcd9xxx_start_hs_polling(mbhc);
4295 }
4296 break;
4297 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4298 /* If clock source changes, stop and restart polling */
4299 if (wcd9xxx_mbhc_polling(mbhc))
4300 wcd9xxx_pause_hs_polling(mbhc);
4301 break;
4302 case WCD9XXX_EVENT_POST_MCLK_OFF:
4303 break;
4304 case WCD9XXX_EVENT_PRE_RCO_ON:
4305 break;
4306 case WCD9XXX_EVENT_POST_RCO_ON:
4307 /* Change to higher TxAAF frequency */
4308 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4309 0 << 4);
4310 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004311 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004312 /* If clock source changes, stop and restart polling */
4313 if (wcd9xxx_mbhc_polling(mbhc)) {
4314 wcd9xxx_calibrate_hs_polling(mbhc);
4315 wcd9xxx_start_hs_polling(mbhc);
4316 }
4317 break;
4318 case WCD9XXX_EVENT_PRE_RCO_OFF:
4319 /* If clock source changes, stop and restart polling */
4320 if (wcd9xxx_mbhc_polling(mbhc))
4321 wcd9xxx_pause_hs_polling(mbhc);
4322 break;
4323 case WCD9XXX_EVENT_POST_RCO_OFF:
4324 break;
4325 /* CFILT usage change */
4326 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4327 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4328 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4329 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4330 wcd9xxx_event_to_cfilt(event))
4331 /*
4332 * Switch CFILT to slow mode if MBHC CFILT is being
4333 * used.
4334 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304335 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004336 break;
4337 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4338 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4339 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4340 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4341 wcd9xxx_event_to_cfilt(event))
4342 /*
4343 * Switch CFILT to fast mode if MBHC CFILT is not
4344 * used anymore.
4345 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304346 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004347 break;
4348 /* System resume */
4349 case WCD9XXX_EVENT_POST_RESUME:
4350 mbhc->mbhc_last_resume = jiffies;
4351 break;
4352 /* BG mode chage */
4353 case WCD9XXX_EVENT_PRE_BG_OFF:
4354 case WCD9XXX_EVENT_POST_BG_OFF:
4355 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4356 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4357 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4358 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4359 /* Not used for now */
4360 break;
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004361 case WCD9XXX_EVENT_PRE_TX_3_ON:
4362 /*
4363 * if polling is ON, mbhc micbias not enabled
4364 * switch micbias source to VDDIO
4365 */
4366 set_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
4367 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
4368 & 0x80) &&
4369 mbhc->polling_active && !mbhc->mbhc_micbias_switched)
4370 wcd9xxx_switch_micbias(mbhc, 1);
4371 break;
4372 case WCD9XXX_EVENT_POST_TX_3_OFF:
4373 /*
4374 * Switch back to micbias if HPH PA or TX3 path
4375 * is disabled
4376 */
4377 clear_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
4378 if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
4379 !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
4380 1 << MBHC_EVENT_PA_HPHR)))
4381 wcd9xxx_switch_micbias(mbhc, 0);
4382 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004383 default:
4384 WARN(1, "Unknown event %d\n", event);
4385 ret = -EINVAL;
4386 }
4387
4388 pr_debug("%s: leave\n", __func__);
4389
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304390 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004391}
4392
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004393static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4394 uint32_t *zr)
4395{
4396 int i;
4397 int ret = 0;
4398 s16 l[3], r[3];
4399 s16 *z[] = {
4400 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4401 };
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004402 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004403 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004404 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4405 /* Phase 1 */
4406 /* Set MBHC_MUX for HPHL without ical */
4407 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4408 /* Set MBHC_MUX for HPHR without ical */
4409 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4410 /* Set MBHC_MUX for HPHR with ical */
4411 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4412 /* Set MBHC_MUX for HPHL with ical */
4413 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4414
4415 /* Phase 2 */
4416 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4417 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4418 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4419 };
4420
4421 pr_debug("%s: enter\n", __func__);
4422 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4423
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004424 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4425 !mbhc->mbhc_cb->compute_impedance || !zl ||
4426 !zr)
4427 return -EINVAL;
4428
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004429 /*
4430 * Impedance detection is an intrusive function as it mutes RX paths,
4431 * enable PAs and etc. Therefore codec drvier including ALSA
4432 * shouldn't read and write hardware registers during detection.
4433 */
4434 mutex_lock(&codec->mutex);
4435
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004436 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004437
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004438 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004439 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004440
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004441 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4442 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004443
4444 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004445 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004446 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4447 reg_set_mux[i].mask,
4448 reg_set_mux[i].val);
4449 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4450 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4451 else
4452 snd_soc_update_bits(codec,
4453 WCD9XXX_A_MBHC_SCALING_MUX_1,
4454 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004455 /* 25us is required after mux change to settle down */
4456 usleep_range(mux_wait_us,
4457 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004458 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004459 }
4460
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004461 /* Codec specific setup for L2 and R2 measurements */
4462 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004463
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004464 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004465 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4466 reg_set_mux[i].mask,
4467 reg_set_mux[i].val);
4468 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4469 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4470 else
4471 snd_soc_update_bits(codec,
4472 WCD9XXX_A_MBHC_SCALING_MUX_1,
4473 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004474 /* 25us is required after mux change to settle down */
4475 usleep_range(mux_wait_us,
4476 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004477 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004478 }
4479
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004480 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004481
4482 mutex_unlock(&codec->mutex);
4483
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004484 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004485
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004486 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004487 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004488
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004489 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004490 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004491 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4492 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004493 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004494 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004495 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4496 pr_debug("%s: Impedance detection completed\n", __func__);
4497
4498 return ret;
4499}
4500
4501int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4502 uint32_t *zr)
4503{
4504 WCD9XXX_BCL_LOCK(mbhc->resmgr);
4505 *zl = mbhc->zl;
4506 *zr = mbhc->zr;
4507 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
4508
4509 if (*zl && *zr)
4510 return 0;
4511 else
4512 return -EINVAL;
4513}
4514
Joonwoo Parka8890262012-10-15 12:04:27 -07004515/*
4516 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4517 *
4518 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4519 */
4520int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004521 struct snd_soc_codec *codec,
4522 int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004523 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4524 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4525 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004526 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004527{
4528 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004529 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004530
4531 pr_debug("%s: enter\n", __func__);
4532 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4533 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4534
4535 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4536 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4537 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4538 mbhc->mbhc_micbias_switched = false;
4539 mbhc->polling_active = false;
4540 mbhc->mbhc_state = MBHC_STATE_NONE;
4541 mbhc->in_swch_irq_handler = false;
4542 mbhc->current_plug = PLUG_TYPE_NONE;
4543 mbhc->lpi_enabled = false;
4544 mbhc->no_mic_headset_override = false;
4545 mbhc->mbhc_last_resume = 0;
4546 mbhc->codec = codec;
4547 mbhc->resmgr = resmgr;
4548 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004549 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004550 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304551 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004552 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004553 mbhc->impedance_detect = impedance_det_en;
Joonwoo Parka8890262012-10-15 12:04:27 -07004554
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004555 if (mbhc->intr_ids == NULL) {
4556 pr_err("%s: Interrupt mapping not provided\n", __func__);
4557 return -EINVAL;
4558 }
4559
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004560 if (mbhc->headset_jack.jack == NULL) {
4561 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4562 &mbhc->headset_jack);
4563 if (ret) {
4564 pr_err("%s: Failed to create new jack\n", __func__);
4565 return ret;
4566 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004567
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004568 ret = snd_soc_jack_new(codec, "Button Jack",
4569 WCD9XXX_JACK_BUTTON_MASK,
4570 &mbhc->button_jack);
4571 if (ret) {
4572 pr_err("Failed to create new jack\n");
4573 return ret;
4574 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004575
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004576 ret = snd_jack_set_key(mbhc->button_jack.jack,
4577 SND_JACK_BTN_0,
4578 KEY_MEDIA);
4579 if (ret) {
4580 pr_err("%s: Failed to set code for btn-0\n",
4581 __func__);
4582 return ret;
4583 }
4584
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004585 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4586 wcd9xxx_mbhc_fw_read);
4587 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4588 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4589 wcd9xxx_mbhc_insert_work);
4590 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004591
4592 /* Register event notifier */
4593 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4594 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4595 if (ret) {
4596 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4597 return ret;
4598 }
4599
4600 wcd9xxx_init_debugfs(mbhc);
4601
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004602
4603 /* Disable Impedance detection by default for certain codec types */
4604 if (mbhc->mbhc_cb &&
4605 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4606 impedance_detect_en = 0;
4607 else
4608 impedance_detect_en = impedance_det_en ? 1 : 0;
4609
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004610 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004611 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004612 wcd9xxx_hs_insert_irq,
4613 "Headset insert detect", mbhc);
4614 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004615 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004616 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004617 goto err_insert_irq;
4618 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004619 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004620
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004621 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004622 wcd9xxx_hs_remove_irq,
4623 "Headset remove detect", mbhc);
4624 if (ret) {
4625 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004626 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004627 goto err_remove_irq;
4628 }
4629
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004630 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004631 wcd9xxx_dce_handler, "DC Estimation detect",
4632 mbhc);
4633 if (ret) {
4634 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004635 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004636 goto err_potential_irq;
4637 }
4638
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004639 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004640 wcd9xxx_release_handler,
4641 "Button Release detect", mbhc);
4642 if (ret) {
4643 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004644 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004645 goto err_release_irq;
4646 }
4647
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004648 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004649 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4650 mbhc);
4651 if (ret) {
4652 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004653 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004654 goto err_hphl_ocp_irq;
4655 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004656 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004657
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004658 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004659 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4660 mbhc);
4661 if (ret) {
4662 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004663 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004664 goto err_hphr_ocp_irq;
4665 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004666 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004667
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004668 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4669 1 << WCD9XXX_COND_HPH);
4670
Joonwoo Parka8890262012-10-15 12:04:27 -07004671 pr_debug("%s: leave ret %d\n", __func__, ret);
4672 return ret;
4673
4674err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004675 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004676err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004677 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004678err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004679 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004680err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004681 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004682err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004683 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004684err_insert_irq:
4685 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
4686
4687 pr_debug("%s: leave ret %d\n", __func__, ret);
4688 return ret;
4689}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004690EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07004691
4692void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
4693{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004694 struct wcd9xxx_core_resource *core_res =
4695 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004696
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004697 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
4698 1 << WCD9XXX_COND_HPH);
4699
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004700 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
4701 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
4702 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
4703 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
4704 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
4705 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
4706 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004707
Joonwoo Parka8890262012-10-15 12:04:27 -07004708 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07004709 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07004710}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004711EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07004712
4713MODULE_DESCRIPTION("wcd9xxx MBHC module");
4714MODULE_LICENSE("GPL v2");