blob: b2ec3471033c4b2381cb1b3d74c94ead3e6e95fb [file] [log] [blame]
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05301/* Copyright (c) 2012-2014, 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 | \
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -070046 SND_JACK_UNSUPPORTED | SND_JACK_MICROPHONE2)
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)
Simmi Pateriya12f63bc2013-11-08 14:43:54 +053067#define ANC_HPH_DETECT_PLUG_TIME_MS (5 * 1000)
Joonwoo Parka8890262012-10-15 12:04:27 -070068#define HS_DETECT_PLUG_INERVAL_MS 100
69#define SWCH_REL_DEBOUNCE_TIME_MS 50
70#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
Joonwoo Park218e73f2013-08-21 16:22:18 -070071#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
Joonwoo Parka8890262012-10-15 12:04:27 -070072
73#define GND_MIC_SWAP_THRESHOLD 2
74#define OCP_ATTEMPT 1
75
76#define FW_READ_ATTEMPTS 15
77#define FW_READ_TIMEOUT 2000000
78
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -070079#define BUTTON_POLLING_SUPPORTED true
Joonwoo Parka8890262012-10-15 12:04:27 -070080
81#define MCLK_RATE_12288KHZ 12288000
82#define MCLK_RATE_9600KHZ 9600000
Joonwoo Parka8890262012-10-15 12:04:27 -070083
84#define DEFAULT_DCE_STA_WAIT 55
85#define DEFAULT_DCE_WAIT 60000
86#define DEFAULT_STA_WAIT 5000
87
88#define VDDIO_MICBIAS_MV 1800
89
Joonwoo Parkccccba72013-04-26 11:19:46 -070090#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
91
Joonwoo Park20bc9da2013-01-16 12:58:06 -080092#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
Joonwoo Parkaec97c72013-05-14 16:51:02 -070093#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
Simmi Pateriya795ce442013-11-14 11:15:28 +053094#define WCD9XXX_MEAS_DELTA_MAX_MV 120
Joonwoo Park141d6182013-03-05 12:25:46 -080095#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
96#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -070097
98/*
99 * Invalid voltage range for the detection
100 * of plug type with current source
101 */
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +0530102#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 160
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700103#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
104
105/*
106 * Threshold used to detect euro headset
107 * with current source
108 */
109#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
110#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
111
112#define WCD9XXX_MBHC_NSC_CS 9
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800113#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
Joonwoo Park479347a2013-04-15 18:01:05 -0700114#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
Joonwoo Parkccccba72013-04-26 11:19:46 -0700115#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800116
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700117#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
118
119/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
120#define WCD9XXX_WG_TIME_FACTOR_US 240
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800121
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700122#define WCD9XXX_V_CS_HS_MAX 500
123#define WCD9XXX_V_CS_NO_MIC 5
124#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +0530125#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 12
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700126
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700127static int impedance_detect_en;
128module_param(impedance_detect_en, int,
129 S_IRUGO | S_IWUSR | S_IWGRP);
130MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
131
Simmi Pateriya4e545052013-11-15 07:41:06 +0530132static bool detect_use_vddio_switch;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800133
134struct wcd9xxx_mbhc_detect {
135 u16 dce;
136 u16 sta;
137 u16 hphl_status;
138 bool swap_gnd;
139 bool vddio;
140 bool hwvalue;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -0700141 bool mic_bias;
Joonwoo Park20bc9da2013-01-16 12:58:06 -0800142 /* internal purpose from here */
143 bool _above_no_mic;
144 bool _below_v_hs_max;
145 s16 _vdces;
146 enum wcd9xxx_mbhc_plug_type _type;
147};
148
Joonwoo Parka8890262012-10-15 12:04:27 -0700149enum meas_type {
150 STA = 0,
151 DCE,
152};
153
154enum {
155 MBHC_USE_HPHL_TRIGGER = 1,
156 MBHC_USE_MB_TRIGGER = 2
157};
158
159/*
160 * Flags to track of PA and DAC state.
161 * PA and DAC should be tracked separately as AUXPGA loopback requires
162 * only PA to be turned on without DAC being on.
163 */
164enum pa_dac_ack_flags {
165 WCD9XXX_HPHL_PA_OFF_ACK = 0,
166 WCD9XXX_HPHR_PA_OFF_ACK,
167 WCD9XXX_HPHL_DAC_OFF_ACK,
168 WCD9XXX_HPHR_DAC_OFF_ACK
169};
170
Joonwoo Park73375212013-05-07 12:42:44 -0700171enum wcd9xxx_current_v_idx {
172 WCD9XXX_CURRENT_V_INS_H,
173 WCD9XXX_CURRENT_V_INS_HU,
174 WCD9XXX_CURRENT_V_B1_H,
175 WCD9XXX_CURRENT_V_B1_HU,
176 WCD9XXX_CURRENT_V_BR_H,
177};
178
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700179static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
180 uint32_t *zr);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -0700181static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
182 const enum wcd9xxx_current_v_idx idx);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700183static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
184 struct mbhc_micbias_regs *micb_regs,
185 bool norel);
186
Joonwoo Park35d4cde2013-08-14 15:11:14 -0700187static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700188
Joonwoo Parka8890262012-10-15 12:04:27 -0700189static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
190{
191 return mbhc->polling_active;
192}
193
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700194static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
Joonwoo Parka8890262012-10-15 12:04:27 -0700195{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700196 struct snd_soc_codec *codec = mbhc->codec;
197 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
198 0x04, on ? 0x04 : 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700199}
200
201/* called under codec_resource_lock acquisition */
202static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
203{
204 struct snd_soc_codec *codec = mbhc->codec;
205
206 pr_debug("%s: enter\n", __func__);
207 if (!mbhc->polling_active) {
208 pr_debug("polling not active, nothing to pause\n");
209 return;
210 }
211
212 /* Soft reset MBHC block */
213 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
214 pr_debug("%s: leave\n", __func__);
215}
216
217/* called under codec_resource_lock acquisition */
218static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
219{
220 struct snd_soc_codec *codec = mbhc->codec;
221 int mbhc_state = mbhc->mbhc_state;
222
223 pr_debug("%s: enter\n", __func__);
224 if (!mbhc->polling_active) {
225 pr_debug("Polling is not active, do not start polling\n");
226 return;
227 }
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530228
229 /*
230 * setup internal micbias if codec uses internal micbias for
231 * headset detection
232 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +0530233 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +0530234 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
235 mbhc->mbhc_cb->setup_int_rbias(codec, true);
236 else
237 pr_err("%s: internal bias requested but codec did not provide callback\n",
238 __func__);
239 }
240
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +0530241 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530242 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
243 mbhc->mbhc_cb->enable_mux_bias_block(codec);
244 else
245 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
246 0x80, 0x80);
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530247
Joonwoo Parka8890262012-10-15 12:04:27 -0700248 if (!mbhc->no_mic_headset_override &&
249 mbhc_state == MBHC_STATE_POTENTIAL) {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530250 pr_debug("%s recovering MBHC state machine\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -0700251 mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
252 /* set to max button press threshold */
253 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
254 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
255 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
256 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
257 /* set to max */
258 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
259 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
260 }
261
262 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
263 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
264 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
265 pr_debug("%s: leave\n", __func__);
266}
267
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700268static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
269 unsigned int cfilt_mv)
270{
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700271 return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
272}
273
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700274/*
275 * called under codec_resource_lock acquisition
276 * return old status
277 */
278static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700279 int vddio_switch, bool restartpolling,
280 bool checkpolling)
281{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700282 bool ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700283 int cfilt_k_val;
284 bool override;
285 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700286 struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
Joonwoo Parka8890262012-10-15 12:04:27 -0700287
288 codec = mbhc->codec;
289
Joonwoo Parkccccba72013-04-26 11:19:46 -0700290 if (mbhc->micbias_enable) {
291 pr_debug("%s: micbias is already on\n", __func__);
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700292 ret = mbhc->mbhc_micbias_switched;
293 return ret;
Joonwoo Parkccccba72013-04-26 11:19:46 -0700294 }
295
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700296 ret = mbhc->mbhc_micbias_switched;
Joonwoo Parka8890262012-10-15 12:04:27 -0700297 if (vddio_switch && !mbhc->mbhc_micbias_switched &&
298 (!checkpolling || mbhc->polling_active)) {
299 if (restartpolling)
300 wcd9xxx_pause_hs_polling(mbhc);
301 override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
302 0x04;
303 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700304 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700305 /* Adjust threshold if Mic Bias voltage changes */
Joonwoo Park73375212013-05-07 12:42:44 -0700306 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700307 cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700308 VDDIO_MICBIAS_MV);
309 usleep_range(10000, 10000);
310 snd_soc_update_bits(codec,
311 mbhc->mbhc_bias_regs.cfilt_val,
312 0xFC, (cfilt_k_val << 2));
313 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700314 /* Threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700315 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700316 d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700317 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700318 (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
Joonwoo Parka8890262012-10-15 12:04:27 -0700319 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530320
321 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
322 /* Threshods for button press */
323 snd_soc_write(codec,
324 WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
325 d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
326 snd_soc_write(codec,
327 WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
328 (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
329 0xFF);
330 snd_soc_write(codec,
331 WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
332 d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
333 snd_soc_write(codec,
334 WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
335 (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
336 0xFF);
337 /* Threshods for button release */
338 snd_soc_write(codec,
339 WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
340 d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
341 snd_soc_write(codec,
342 WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
343 (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) &
344 0xFF);
345 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700346 pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
347 __func__);
348 }
349
350 /* Enable MIC BIAS Switch to VDDIO */
351 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
352 0x80, 0x80);
353 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
354 0x10, 0x00);
355 if (!override)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -0700356 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -0700357 if (restartpolling)
358 wcd9xxx_start_hs_polling(mbhc);
359
360 mbhc->mbhc_micbias_switched = true;
361 pr_debug("%s: VDDIO switch enabled\n", __func__);
362 } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
363 if ((!checkpolling || mbhc->polling_active) &&
364 restartpolling)
365 wcd9xxx_pause_hs_polling(mbhc);
366 /* Reprogram thresholds */
Joonwoo Park73375212013-05-07 12:42:44 -0700367 if (d->micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700368 cfilt_k_val =
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700369 __wcd9xxx_resmgr_get_k_val(mbhc,
Joonwoo Park73375212013-05-07 12:42:44 -0700370 d->micb_mv);
Joonwoo Parka8890262012-10-15 12:04:27 -0700371 snd_soc_update_bits(codec,
372 mbhc->mbhc_bias_regs.cfilt_val,
373 0xFC, (cfilt_k_val << 2));
374 usleep_range(10000, 10000);
Joonwoo Park73375212013-05-07 12:42:44 -0700375 /* Revert threshods for insertion/removal */
Joonwoo Parka8890262012-10-15 12:04:27 -0700376 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700377 d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
Joonwoo Parka8890262012-10-15 12:04:27 -0700378 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
Joonwoo Park73375212013-05-07 12:42:44 -0700379 (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
380 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530381 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
382 /* Revert threshods for button press */
383 snd_soc_write(codec,
384 WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
385 d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
386 snd_soc_write(codec,
387 WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
388 (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
389 0xFF);
390 snd_soc_write(codec,
391 WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
392 d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
393 snd_soc_write(codec,
394 WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
395 (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
396 0xFF);
397 /* Revert threshods for button release */
398 snd_soc_write(codec,
399 WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
400 d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
401 snd_soc_write(codec,
402 WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
403 (d->v_brh[MBHC_V_IDX_CFILT] >> 8) &
404 0xFF);
405 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700406 pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
407 __func__);
408 }
409
410 /* Disable MIC BIAS Switch to VDDIO */
411 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
412 0x00);
413 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
414 0x00);
415
416 if ((!checkpolling || mbhc->polling_active) && restartpolling)
417 wcd9xxx_start_hs_polling(mbhc);
418
419 mbhc->mbhc_micbias_switched = false;
420 pr_debug("%s: VDDIO switch disabled\n", __func__);
421 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700422
423 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700424}
425
426static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
427{
Joonwoo Parkddcd8832013-06-19 15:58:47 -0700428 __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700429}
430
Joonwoo Park73375212013-05-07 12:42:44 -0700431static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
432 const enum wcd9xxx_current_v_idx idx)
Joonwoo Parka8890262012-10-15 12:04:27 -0700433{
Joonwoo Park73375212013-05-07 12:42:44 -0700434 enum mbhc_v_index vidx;
435 s16 ret = -EINVAL;
436
Joonwoo Parka8890262012-10-15 12:04:27 -0700437 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
438 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700439 vidx = MBHC_V_IDX_VDDIO;
Joonwoo Parka8890262012-10-15 12:04:27 -0700440 else
Joonwoo Park73375212013-05-07 12:42:44 -0700441 vidx = MBHC_V_IDX_CFILT;
442
443 switch (idx) {
444 case WCD9XXX_CURRENT_V_INS_H:
445 ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
446 break;
447 case WCD9XXX_CURRENT_V_INS_HU:
448 ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
449 break;
450 case WCD9XXX_CURRENT_V_B1_H:
451 ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
452 break;
453 case WCD9XXX_CURRENT_V_B1_HU:
454 ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
455 break;
456 case WCD9XXX_CURRENT_V_BR_H:
457 ret = (s16)mbhc->mbhc_data.v_brh[vidx];
458 break;
459 }
460
461 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -0700462}
463
464void *wcd9xxx_mbhc_cal_btn_det_mp(
465 const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
466 const enum wcd9xxx_mbhc_btn_det_mem mem)
467{
468 void *ret = &btn_det->_v_btn_low;
469
470 switch (mem) {
471 case MBHC_BTN_DET_GAIN:
472 ret += sizeof(btn_det->_n_cic);
473 case MBHC_BTN_DET_N_CIC:
474 ret += sizeof(btn_det->_n_ready);
475 case MBHC_BTN_DET_N_READY:
476 ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
477 case MBHC_BTN_DET_V_BTN_HIGH:
478 ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
479 case MBHC_BTN_DET_V_BTN_LOW:
480 /* do nothing */
481 break;
482 default:
483 ret = NULL;
484 }
485
486 return ret;
487}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700488EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700489
490static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
491{
492 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Park73375212013-05-07 12:42:44 -0700493 const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
494 WCD9XXX_CURRENT_V_INS_HU);
495 const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
496 WCD9XXX_CURRENT_V_B1_HU);
497 const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
498 const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -0700499
500 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
501 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
502 (v_ins_hu >> 8) & 0xFF);
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +0530503
504 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
505 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
506 0xFF);
507 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
508 (v_b1_hu >> 8) & 0xFF);
509 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h &
510 0xFF);
511 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
512 (v_b1_h >> 8) & 0xFF);
513 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
514 0xFF);
515 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
516 (v_brh >> 8) & 0xFF);
517 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
518 mbhc->mbhc_data.v_brl & 0xFF);
519 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
520 (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
521 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700522}
523
Simmi Pateriya95466b12013-05-09 20:08:46 +0530524static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parka8890262012-10-15 12:04:27 -0700525 bool fast)
526{
527 struct snd_soc_codec *codec = mbhc->codec;
Simmi Pateriya95466b12013-05-09 20:08:46 +0530528 struct wcd9xxx_cfilt_mode cfilt_mode;
Joonwoo Parka8890262012-10-15 12:04:27 -0700529
Simmi Pateriya95466b12013-05-09 20:08:46 +0530530 if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
531 cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530532 } else {
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530533 if (fast)
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700534 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530535 else
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700536 cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700537
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700538 cfilt_mode.reg_mask = 0x40;
539 cfilt_mode.cur_mode_val =
Simmi Pateriya0a44d842013-04-03 01:12:42 +0530540 snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
Joonwoo Parka8890262012-10-15 12:04:27 -0700541 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700542
543 if (cfilt_mode.cur_mode_val
544 != cfilt_mode.reg_mode_val) {
Simmi Pateriya95466b12013-05-09 20:08:46 +0530545 if (mbhc->polling_active)
546 wcd9xxx_pause_hs_polling(mbhc);
547 snd_soc_update_bits(codec,
548 mbhc->mbhc_bias_regs.cfilt_ctl,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700549 cfilt_mode.reg_mask,
550 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530551 if (mbhc->polling_active)
552 wcd9xxx_start_hs_polling(mbhc);
553 pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700554 cfilt_mode.cur_mode_val,
555 cfilt_mode.reg_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530556 } else {
557 pr_debug("%s: CFILT Value is already %x\n",
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700558 __func__, cfilt_mode.cur_mode_val);
Simmi Pateriya95466b12013-05-09 20:08:46 +0530559 }
Joonwoo Parka8890262012-10-15 12:04:27 -0700560}
561
Joonwoo Park3699ca32013-02-08 12:06:15 -0800562static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
563 struct snd_soc_jack *jack, int status, int mask)
Joonwoo Parka8890262012-10-15 12:04:27 -0700564{
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800565 if (jack == &mbhc->headset_jack) {
Joonwoo Park3699ca32013-02-08 12:06:15 -0800566 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
567 WCD9XXX_COND_HPH_MIC,
568 status & SND_JACK_MICROPHONE);
Joonwoo Parkaf21f032013-03-05 18:07:40 -0800569 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
570 WCD9XXX_COND_HPH,
571 status & SND_JACK_HEADPHONE);
572 }
Joonwoo Park3699ca32013-02-08 12:06:15 -0800573
Joonwoo Parka8890262012-10-15 12:04:27 -0700574 snd_soc_jack_report_no_dapm(jack, status, mask);
575}
576
577static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
578 int irq)
579{
580 struct snd_soc_codec *codec;
581
582 pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
583 codec = mbhc->codec;
584 if (mbhc->hph_status & jack_status) {
585 mbhc->hph_status &= ~jack_status;
Joonwoo Park3699ca32013-02-08 12:06:15 -0800586 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700587 mbhc->hph_status, WCD9XXX_JACK_MASK);
588 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
589 0x00);
590 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
591 0x10);
592 /*
593 * reset retry counter as PA is turned off signifying
594 * start of new OCP detection session
595 */
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700596 if (mbhc->intr_ids->hph_left_ocp)
Joonwoo Parka8890262012-10-15 12:04:27 -0700597 mbhc->hphlocp_cnt = 0;
598 else
599 mbhc->hphrocp_cnt = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700600 wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
Joonwoo Parka8890262012-10-15 12:04:27 -0700601 }
602}
603
604static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
605{
606 __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700607 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700608}
609
610static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
611{
612 __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
Bhalchandra Gajare16748932013-10-01 18:16:05 -0700613 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -0700614}
615
616static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700617 enum wcd9xxx_mbhc_micbias_type mb_type)
Joonwoo Parka8890262012-10-15 12:04:27 -0700618{
619 unsigned int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700620 struct wcd9xxx_micbias_setting *micbias_pdata =
621 mbhc->resmgr->micbias_pdata;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700622 struct mbhc_micbias_regs *micbias_regs;
623 enum wcd9xxx_micbias_num mb_num;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700624
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700625 if (mb_type == MBHC_ANC_MIC_MB) {
626 micbias_regs = &mbhc->mbhc_anc_bias_regs;
627 mb_num = mbhc->mbhc_cfg->anc_micbias;
628 } else {
629 micbias_regs = &mbhc->mbhc_bias_regs;
630 mb_num = mbhc->mbhc_cfg->micbias;
631 }
632
633 switch (mb_num) {
Joonwoo Parka8890262012-10-15 12:04:27 -0700634 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700635 cfilt = micbias_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700636 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
637 micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
638 micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
639 break;
640 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700641 cfilt = micbias_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700642 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
643 micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
644 micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
645 break;
646 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700647 cfilt = micbias_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700648 micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
649 micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
650 micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
651 break;
652 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -0700653 cfilt = micbias_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -0700654 micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
655 micbias_regs->int_rbias =
656 mbhc->resmgr->reg_addr->micb_4_int_rbias;
657 micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
658 break;
659 default:
660 /* Should never reach here */
661 pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
662 return;
663 }
664
665 micbias_regs->cfilt_sel = cfilt;
666
667 switch (cfilt) {
668 case WCD9XXX_CFILT1_SEL:
669 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
670 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
Joonwoo Parka8890262012-10-15 12:04:27 -0700671 break;
672 case WCD9XXX_CFILT2_SEL:
673 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
674 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
Joonwoo Parka8890262012-10-15 12:04:27 -0700675 break;
676 case WCD9XXX_CFILT3_SEL:
677 micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
678 micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
Joonwoo Parka8890262012-10-15 12:04:27 -0700679 break;
680 }
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700681
682 if (mb_type == MBHC_PRIMARY_MIC_MB) {
683 switch (cfilt) {
684 case WCD9XXX_CFILT1_SEL:
685 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
686 break;
687 case WCD9XXX_CFILT2_SEL:
688 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
689 break;
690 case WCD9XXX_CFILT3_SEL:
691 mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
692 break;
693 }
694 }
695
Joonwoo Parka8890262012-10-15 12:04:27 -0700696}
697
698static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
699{
700 bool pa_turned_on = false;
701 struct snd_soc_codec *codec = mbhc->codec;
702 u8 wg_time;
703
704 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
705 wg_time += 1;
706
707 if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
708 &mbhc->hph_pa_dac_state)) {
709 pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
710 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
711 0xC0, 0xC0);
712 }
713 if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
714 &mbhc->hph_pa_dac_state)) {
715 pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
716 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
Banajit Goswami94abca92013-05-30 18:15:19 -0700717 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -0700718 }
719
720 if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
721 &mbhc->hph_pa_dac_state)) {
722 pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
723 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
724 1 << 4);
725 pa_turned_on = true;
726 }
727 if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
728 &mbhc->hph_pa_dac_state)) {
729 pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
730 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
731 << 5);
732 pa_turned_on = true;
733 }
734
735 if (pa_turned_on) {
736 pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
737 __func__);
738 usleep_range(wg_time * 1000, wg_time * 1000);
739 }
740}
741
742static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
743{
744 int r;
745 r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
746 if (r)
747 /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
748 * we have to unlock from here instead btn_work */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700749 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700750 return r;
751}
752
753static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
754{
755 u8 hph_reg_val = 0;
756 if (left)
757 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
758 else
759 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
760
761 return (hph_reg_val & 0xC0) ? true : false;
762}
763
764static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
765{
766 u8 hph_reg_val = 0;
767 hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
768
769 return (hph_reg_val & 0x30) ? true : false;
770}
771
772/* called under codec_resource_lock acquisition */
773static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
774{
775 u8 wg_time;
776 struct snd_soc_codec *codec = mbhc->codec;
777
778 wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
779 wg_time += 1;
780
781 /* If headphone PA is on, check if userspace receives
782 * removal event to sync-up PA's state */
783 if (wcd9xxx_is_hph_pa_on(codec)) {
784 pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
785 set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
786 set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
787 } else {
788 pr_debug("%s PA is off\n", __func__);
789 }
790
791 if (wcd9xxx_is_hph_dac_on(codec, 1))
792 set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
793 if (wcd9xxx_is_hph_dac_on(codec, 0))
794 set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
795
796 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
Joonwoo Park67c0dbf2013-01-25 10:47:38 -0800797 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
Joonwoo Parka8890262012-10-15 12:04:27 -0700798 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
799 usleep_range(wg_time * 1000, wg_time * 1000);
800}
801
802static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
803{
804 if (!mbhc->mbhc_cfg->insert_detect)
805 return;
806 pr_debug("%s: Setting up %s detection\n", __func__,
807 ins ? "insert" : "removal");
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700808 /* Disable detection to avoid glitch */
809 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
Simmi Pateriya2397f5c2013-03-25 12:21:55 +0530810 if (mbhc->mbhc_cfg->gpio_level_insert)
811 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
812 (0x68 | (ins ? (1 << 1) : 0)));
813 else
814 snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
815 (0x6C | (ins ? (1 << 1) : 0)));
Joonwoo Park2e6bd1e2012-10-18 13:48:55 -0700816 /* Re-enable detection */
817 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -0700818}
819
820/* called under codec_resource_lock acquisition */
821static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
822 enum snd_jack_types jack_type)
823{
824 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
825
Joonwoo Park80a01172012-10-15 16:05:23 -0700826 pr_debug("%s: enter insertion %d hph_status %x\n",
827 __func__, insertion, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700828 if (!insertion) {
829 /* Report removal */
830 mbhc->hph_status &= ~jack_type;
831 /*
832 * cancel possibly scheduled btn work and
833 * report release if we reported button press
834 */
835 if (wcd9xxx_cancel_btn_work(mbhc))
836 pr_debug("%s: button press is canceled\n", __func__);
837 else if (mbhc->buttons_pressed) {
838 pr_debug("%s: release of button press%d\n",
839 __func__, jack_type);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800840 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -0700841 mbhc->buttons_pressed);
842 mbhc->buttons_pressed &=
843 ~WCD9XXX_JACK_BUTTON_MASK;
844 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700845
846 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
847 pr_debug("%s: Disabling micbias\n", __func__);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700848 mbhc->micbias_enable_cb(mbhc->codec, false,
849 mbhc->mbhc_cfg->micbias);
Joonwoo Parkccccba72013-04-26 11:19:46 -0700850 mbhc->micbias_enable = false;
851 }
Joonwoo Parkb755e9e2013-05-28 13:14:05 -0700852 mbhc->zl = mbhc->zr = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -0700853 pr_debug("%s: Reporting removal %d(%x)\n", __func__,
854 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800855 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
Joonwoo Parka8890262012-10-15 12:04:27 -0700856 WCD9XXX_JACK_MASK);
857 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
858 hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
859 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
860 mbhc->current_plug = PLUG_TYPE_NONE;
861 mbhc->polling_active = false;
862 } else {
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700863 /*
864 * Report removal of current jack type.
865 * Headphone to headset shouldn't report headphone
866 * removal.
867 */
868 if (mbhc->mbhc_cfg->detect_extn_cable &&
869 !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
870 jack_type == SND_JACK_HEADSET) &&
871 (mbhc->hph_status && mbhc->hph_status != jack_type)) {
872 if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
873 mbhc->hph_status == SND_JACK_HEADSET) {
874 pr_debug("%s: Disabling micbias\n", __func__);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700875 mbhc->micbias_enable_cb(mbhc->codec, false,
876 mbhc->mbhc_cfg->micbias);
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700877 mbhc->micbias_enable = false;
Joonwoo Park80a01172012-10-15 16:05:23 -0700878 }
Phani Kumar Uppalapatif3d05242013-10-23 19:36:25 -0700879
880 pr_debug("%s: Reporting removal (%x)\n",
881 __func__, mbhc->hph_status);
882 mbhc->zl = mbhc->zr = 0;
883 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
884 0, WCD9XXX_JACK_MASK);
Yeleswarapu, Nagaradheshcff6b342013-11-05 17:12:39 +0530885 mbhc->hph_status &= ~(SND_JACK_HEADSET |
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700886 SND_JACK_LINEOUT |
887 SND_JACK_ANC_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -0700888 }
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700889
Joonwoo Parka8890262012-10-15 12:04:27 -0700890 /* Report insertion */
891 mbhc->hph_status |= jack_type;
892
893 if (jack_type == SND_JACK_HEADPHONE) {
894 mbhc->current_plug = PLUG_TYPE_HEADPHONE;
895 } else if (jack_type == SND_JACK_UNSUPPORTED) {
896 mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
897 } else if (jack_type == SND_JACK_HEADSET) {
898 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
899 mbhc->current_plug = PLUG_TYPE_HEADSET;
Joonwoo Park218e73f2013-08-21 16:22:18 -0700900 mbhc->update_z = true;
Joonwoo Park80a01172012-10-15 16:05:23 -0700901 } else if (jack_type == SND_JACK_LINEOUT) {
902 mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700903 } else if (jack_type == SND_JACK_ANC_HEADPHONE) {
Simmi Pateriyaf0553ab2013-12-03 11:25:40 +0530904 mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700905 mbhc->current_plug = PLUG_TYPE_ANC_HEADPHONE;
Joonwoo Parka8890262012-10-15 12:04:27 -0700906 }
Joonwoo Parkccccba72013-04-26 11:19:46 -0700907
908 if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
909 pr_debug("%s: Enabling micbias\n", __func__);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -0700910 mbhc->micbias_enable_cb(mbhc->codec, true,
911 mbhc->mbhc_cfg->micbias);
Joonwoo Parkccccba72013-04-26 11:19:46 -0700912 }
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700913
Phani Kumar Uppalapati381fbc82013-09-19 17:11:31 -0700914 if (mbhc->impedance_detect && impedance_detect_en)
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -0700915 wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700916
Joonwoo Parka8890262012-10-15 12:04:27 -0700917 pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
918 jack_type, mbhc->hph_status);
Joonwoo Park3699ca32013-02-08 12:06:15 -0800919 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -0700920 mbhc->hph_status, WCD9XXX_JACK_MASK);
921 wcd9xxx_clr_and_turnon_hph_padac(mbhc);
922 }
923 /* Setup insert detect */
924 wcd9xxx_insert_detect_setup(mbhc, !insertion);
Joonwoo Park80a01172012-10-15 16:05:23 -0700925
926 pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
Joonwoo Parka8890262012-10-15 12:04:27 -0700927}
928
929/* should be called under interrupt context that hold suspend */
930static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
931 struct work_struct *work)
932{
933 pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
934 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
935 mbhc->hs_detect_work_stop = false;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700936 wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700937 schedule_work(work);
938}
939
940/* called under codec_resource_lock acquisition */
941static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
942 struct work_struct *work)
943{
944 pr_debug("%s: Canceling correct_plug_swch\n", __func__);
945 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
946 mbhc->hs_detect_work_stop = true;
947 wmb();
948 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
949 if (cancel_work_sync(work)) {
950 pr_debug("%s: correct_plug_swch is canceled\n",
951 __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -0700952 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -0700953 }
954 WCD9XXX_BCL_LOCK(mbhc->resmgr);
955}
956
Joonwoo Park73375212013-05-07 12:42:44 -0700957static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
958{
959 int r;
960 int vddio_k, mb_k;
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -0700961 vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
962 mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
Joonwoo Park73375212013-05-07 12:42:44 -0700963 if (tovddio)
Joonwoo Park520a0f92013-05-14 19:39:58 -0700964 r = v * (vddio_k + 4) / (mb_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700965 else
Joonwoo Park520a0f92013-05-14 19:39:58 -0700966 r = v * (mb_k + 4) / (vddio_k + 4);
Joonwoo Park73375212013-05-07 12:42:44 -0700967 return r;
968}
969
Joonwoo Parka8890262012-10-15 12:04:27 -0700970static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
971{
972 s16 v_hs_max;
973 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
974
975 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
976 if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
977 mbhc->mbhc_micbias_switched)
Joonwoo Park73375212013-05-07 12:42:44 -0700978 v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
Joonwoo Parka8890262012-10-15 12:04:27 -0700979 else
980 v_hs_max = plug_type->v_hs_max;
981 return v_hs_max;
982}
983
Joonwoo Parka8890262012-10-15 12:04:27 -0700984static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
985{
986 u8 bias_msb, bias_lsb;
987 short bias_value;
988
989 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
990 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
991 bias_value = (bias_msb << 8) | bias_lsb;
992 return bias_value;
993}
994
995static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
996{
997 u8 bias_msb, bias_lsb;
998 short bias_value;
999
1000 bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
1001 bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
1002 bias_value = (bias_msb << 8) | bias_lsb;
1003 return bias_value;
1004}
1005
1006static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
1007 bool on)
1008{
1009 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
1010}
1011
1012static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1013 bool override_bypass, bool noreldetection)
1014{
1015 short bias_value;
1016 struct snd_soc_codec *codec = mbhc->codec;
1017
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001018 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
1019 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001020 if (noreldetection)
1021 wcd9xxx_turn_onoff_rel_detection(codec, false);
1022
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301023 if (mbhc->mbhc_cfg->do_recalibration)
1024 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
1025 0x0);
Joonwoo Parka8890262012-10-15 12:04:27 -07001026 /* Turn on the override */
1027 if (!override_bypass)
1028 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
1029 if (dce) {
1030 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1031 0x8);
1032 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
1033 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1034 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301035 if (mbhc->mbhc_cfg->do_recalibration)
1036 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1037 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001038 usleep_range(mbhc->mbhc_data.t_sta_dce,
1039 mbhc->mbhc_data.t_sta_dce);
1040 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
1041 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
1042 bias_value = wcd9xxx_read_dce_result(codec);
1043 } else {
1044 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1045 0x8);
1046 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1047 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1048 0x0);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301049 if (mbhc->mbhc_cfg->do_recalibration)
1050 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
1051 0x2, 0x2);
Joonwoo Parka8890262012-10-15 12:04:27 -07001052 usleep_range(mbhc->mbhc_data.t_sta_dce,
1053 mbhc->mbhc_data.t_sta_dce);
1054 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
1055 usleep_range(mbhc->mbhc_data.t_sta,
1056 mbhc->mbhc_data.t_sta);
1057 bias_value = wcd9xxx_read_sta_result(codec);
1058 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
1059 0x8);
1060 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
1061 }
1062 /* Turn off the override after measuring mic voltage */
1063 if (!override_bypass)
1064 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
1065 0x00);
1066
1067 if (noreldetection)
1068 wcd9xxx_turn_onoff_rel_detection(codec, true);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07001069 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
1070 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07001071
1072 return bias_value;
1073}
1074
1075static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
1076 bool norel)
1077{
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001078 bool override_bypass;
1079
1080 /* Bypass override if it is already enabled */
1081 override_bypass = (snd_soc_read(mbhc->codec,
1082 WCD9XXX_A_CDC_MBHC_B1_CTL) &
1083 0x04) ? true : false;
1084
1085 return __wcd9xxx_codec_sta_dce(mbhc, dce, override_bypass, norel);
Joonwoo Parka8890262012-10-15 12:04:27 -07001086}
1087
Joonwoo Park520a0f92013-05-14 19:39:58 -07001088static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001089 u16 bias_value, s16 z, u32 micb_mv)
Joonwoo Parka8890262012-10-15 12:04:27 -07001090{
Joonwoo Park520a0f92013-05-14 19:39:58 -07001091 s16 value, mb;
Joonwoo Parka8890262012-10-15 12:04:27 -07001092 s32 mv;
1093
1094 value = bias_value;
1095 if (dce) {
Joonwoo Parka8890262012-10-15 12:04:27 -07001096 mb = (mbhc->mbhc_data.dce_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001097 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001098 } else {
Joonwoo Parka8890262012-10-15 12:04:27 -07001099 mb = (mbhc->mbhc_data.sta_mb);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001100 mv = (value - z) * (s32)micb_mv / (mb - z);
Joonwoo Parka8890262012-10-15 12:04:27 -07001101 }
1102
1103 return mv;
1104}
1105
Joonwoo Park520a0f92013-05-14 19:39:58 -07001106static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
1107 u16 bias_value)
1108{
1109 s16 z;
1110 z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001111 return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
1112 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07001113}
1114
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301115/* To enable/disable bandgap and RC oscillator */
1116static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
1117 bool enable)
1118{
1119 if (enable) {
1120 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1121 wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
1122 WCD9XXX_BANDGAP_AUDIO_MODE);
1123 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
1124 WCD9XXX_CLK_RCO);
1125 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1126 } else {
1127 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
1128 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
1129 WCD9XXX_CLK_RCO);
1130 wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
1131 WCD9XXX_BANDGAP_AUDIO_MODE);
1132 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
1133 }
1134}
1135
Joonwoo Parka8890262012-10-15 12:04:27 -07001136/* called only from interrupt which is under codec_resource_lock acquisition */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001137static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001138 struct mbhc_micbias_regs *mbhc_micb_regs,
1139 bool is_cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07001140{
1141 struct snd_soc_codec *codec = mbhc->codec;
1142 short bias_value;
1143 u8 cfilt_mode;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001144 s16 reg;
1145 int change;
1146 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1147 s16 sta_z = 0, dce_z = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07001148
1149 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1150
1151 pr_debug("%s: enter\n", __func__);
1152 if (!mbhc->mbhc_cfg->calibration) {
1153 pr_err("%s: Error, no calibration exists\n", __func__);
1154 return -ENODEV;
1155 }
1156
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001157 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001158 /* Enable external voltage source to micbias if present */
1159 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
Phani Kumar Uppalapatiaa98c282014-01-28 15:32:22 -08001160 mbhc->mbhc_cb->enable_mb_source(codec, true, true);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001161
Joonwoo Parka8890262012-10-15 12:04:27 -07001162 /*
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301163 * setup internal micbias if codec uses internal micbias for
1164 * headset detection
1165 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05301166 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301167 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
1168 mbhc->mbhc_cb->setup_int_rbias(codec, true);
1169 else
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05301170 pr_err("%s: internal bias requested but codec did not provide callback\n",
1171 __func__);
Simmi Pateriya2727a4e2013-09-27 12:57:32 +05301172 }
1173
Joonwoo Parka8890262012-10-15 12:04:27 -07001174 snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
1175
1176 /* Make sure CFILT is in fast mode, save current mode */
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001177 cfilt_mode = snd_soc_read(codec, mbhc_micb_regs->cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05301178 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
1179 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
1180 else
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001181 snd_soc_update_bits(codec, mbhc_micb_regs->cfilt_ctl,
Simmi Pateriya95466b12013-05-09 20:08:46 +05301182 0x70, 0x00);
1183
Joonwoo Parka8890262012-10-15 12:04:27 -07001184 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001185 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1186 mbhc->scaling_mux_in);
1187 pr_debug("%s: scaling_mux_input: %d\n", __func__,
1188 mbhc->scaling_mux_in);
1189
Simmi Pateriya95466b12013-05-09 20:08:46 +05301190 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
1191 mbhc->mbhc_cb->enable_mux_bias_block(codec);
1192 else
1193 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
1194 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07001195
1196 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
1197 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
1198 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
1199
1200 snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
1201 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1202 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
1203
1204 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
1205 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
1206
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301207 if (!mbhc->mbhc_cfg->do_recalibration) {
1208 if (!is_cs_enable)
1209 wcd9xxx_calibrate_hs_polling(mbhc);
1210 }
1211
Joonwoo Parka8890262012-10-15 12:04:27 -07001212 /* don't flip override */
1213 bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001214 snd_soc_write(codec, mbhc_micb_regs->cfilt_ctl, cfilt_mode);
Joonwoo Parka8890262012-10-15 12:04:27 -07001215 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
1216
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301217 if (mbhc->mbhc_cfg->do_recalibration) {
1218 /* recalibrate dce_z and sta_z */
1219 reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
1220 change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1221 0x78, btn_det->mbhc_nsc << 3);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001222 wcd9xxx_get_z(mbhc, &dce_z, &sta_z, mbhc_micb_regs, true);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301223 if (change)
1224 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
1225 if (dce_z && sta_z) {
1226 pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
1227 __func__,
1228 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
1229 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
1230 mbhc->mbhc_data.dce_z = dce_z;
1231 mbhc->mbhc_data.sta_z = sta_z;
1232 wcd9xxx_mbhc_calc_thres(mbhc);
1233 wcd9xxx_calibrate_hs_polling(mbhc);
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001234 } else {
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301235 pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
1236 __func__, dce_z, sta_z);
1237 }
1238
1239 if (is_cs_enable) {
1240 /* recalibrate dce_nsc_cs_z */
1241 reg = snd_soc_read(mbhc->codec,
1242 WCD9XXX_A_CDC_MBHC_B1_CTL);
1243 snd_soc_update_bits(mbhc->codec,
1244 WCD9XXX_A_CDC_MBHC_B1_CTL,
1245 0x78, WCD9XXX_MBHC_NSC_CS << 3);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001246 wcd9xxx_get_z(mbhc, &dce_z, NULL, mbhc_micb_regs,
1247 true);
Simmi Pateriya4b5159f2013-11-14 10:43:24 +05301248 snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1249 reg);
1250 if (dce_z) {
1251 pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x\n",
1252 __func__, mbhc->mbhc_data.dce_nsc_cs_z,
1253 dce_z & 0xffff);
1254 mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
1255 } else {
1256 pr_debug("%s: failed get new dce_nsc_cs_z\n",
1257 __func__);
1258 }
Joonwoo Park35d4cde2013-08-14 15:11:14 -07001259 }
1260 }
Joonwoo Parka8890262012-10-15 12:04:27 -07001261 return bias_value;
1262}
1263
1264static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
1265{
1266 struct snd_soc_codec *codec = mbhc->codec;
1267 const struct wcd9xxx_mbhc_general_cfg *generic =
1268 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1269
1270 /* Need MBHC clock */
Joonwoo Park533b3682013-06-13 11:41:21 -07001271 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001272 wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001273 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001274
1275 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
1276 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07001277 __wcd9xxx_switch_micbias(mbhc, 0, false, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001278
1279 usleep_range(generic->t_shutdown_plug_rem,
1280 generic->t_shutdown_plug_rem);
1281
1282 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
1283
Joonwoo Park533b3682013-06-13 11:41:21 -07001284 WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001285 /* Put requested CLK back */
1286 wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
Joonwoo Park533b3682013-06-13 11:41:21 -07001287 WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
Joonwoo Parka8890262012-10-15 12:04:27 -07001288
1289 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
1290}
1291
1292static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
1293{
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001294
1295 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001296 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1297
1298 wcd9xxx_shutdown_hs_removal_detect(mbhc);
1299
Joonwoo Parka8890262012-10-15 12:04:27 -07001300
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001301 /* Disable external voltage source to micbias if present */
1302 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
Phani Kumar Uppalapatiaa98c282014-01-28 15:32:22 -08001303 mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001304
Joonwoo Parka8890262012-10-15 12:04:27 -07001305 mbhc->polling_active = false;
1306 mbhc->mbhc_state = MBHC_STATE_NONE;
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07001307 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001308}
1309
Joonwoo Parka8890262012-10-15 12:04:27 -07001310/* called under codec_resource_lock acquisition */
1311static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
1312{
1313 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
1314 if (on)
1315 usleep_range(5000, 5000);
1316}
1317
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001318static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
1319{
Joonwoo Parkccccba72013-04-26 11:19:46 -07001320 pr_debug("%s: vddio %d\n", __func__, on);
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001321
1322 if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
1323 mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
1324 goto exit;
1325 }
1326
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001327 if (on) {
1328 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1329 1 << 7, 1 << 7);
1330 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1331 1 << 4, 0);
1332 } else {
1333 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1334 1 << 4, 1 << 4);
1335 snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
1336 1 << 7, 0);
1337 }
Bhalchandra Gajaref19a9262013-09-19 15:40:08 -07001338
1339exit:
1340 /*
1341 * Wait for the micbias to settle down to vddio
1342 * when the micbias to vddio switch is enabled.
1343 */
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07001344 if (on)
1345 usleep_range(10000, 10000);
1346}
1347
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001348static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
1349{
1350 u16 hph, status;
1351 struct snd_soc_codec *codec = mbhc->codec;
1352
1353 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1354 hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
1355 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
1356 usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
1357 WCD9XXX_HPHL_STATUS_READY_WAIT_US +
1358 WCD9XXX_USLEEP_RANGE_MARGIN_US);
1359 status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
1360 snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
1361 return status;
1362}
1363
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001364static enum wcd9xxx_mbhc_plug_type
1365wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
1366 struct wcd9xxx_mbhc_detect *dt, const int size,
1367 bool highhph,
1368 unsigned long event_state)
1369{
1370 int i;
1371 int vdce, mb_mv;
1372 int ch, sz, delta_thr;
1373 int minv = 0, maxv = INT_MIN;
1374 struct wcd9xxx_mbhc_detect *d = dt;
1375 struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
1376 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1377
1378 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1379 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1380 s16 hs_max, no_mic, dce_z;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001381 int highhph_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001382
1383 pr_debug("%s: enter\n", __func__);
1384 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1385
1386 sz = size - 1;
1387 for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
1388 if (d->mic_bias) {
1389 dce_z = mbhc->mbhc_data.dce_z;
1390 mb_mv = mbhc->mbhc_data.micb_mv;
1391 hs_max = plug_type->v_hs_max;
1392 no_mic = plug_type->v_no_mic;
1393 } else {
1394 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
1395 mb_mv = VDDIO_MICBIAS_MV;
1396 hs_max = WCD9XXX_V_CS_HS_MAX;
1397 no_mic = WCD9XXX_V_CS_NO_MIC;
1398 }
1399
1400 vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
1401 dce_z, (u32)mb_mv);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001402 d->_vdces = vdce;
1403 if (d->_vdces < no_mic)
1404 d->_type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001405 else if (d->_vdces >= hs_max) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001406 d->_type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001407 highhph_cnt++;
1408 } else
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001409 d->_type = PLUG_TYPE_HEADSET;
1410
1411 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
1412 __func__, i, d->dce, vdce, d->_vdces,
1413 d->hphl_status & 0x01,
1414 d->_type);
1415
1416 ch += d->hphl_status & 0x01;
1417 if (!d->swap_gnd && !d->mic_bias) {
1418 if (maxv < d->_vdces)
1419 maxv = d->_vdces;
1420 if (!minv || minv > d->_vdces)
1421 minv = d->_vdces;
1422 }
1423 if ((!d->mic_bias &&
1424 (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
1425 d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
1426 (d->mic_bias &&
1427 (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1428 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
1429 pr_debug("%s: within invalid range\n", __func__);
1430 type = PLUG_TYPE_INVALID;
1431 goto exit;
1432 }
1433 }
1434
Phani Kumar Uppalapati023aaac2013-10-30 16:36:04 -07001435 delta_thr = ((highhph_cnt == sz) || highhph) ?
1436 WCD9XXX_MB_MEAS_DELTA_MAX_MV :
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001437 WCD9XXX_CS_MEAS_DELTA_MAX_MV;
1438
1439 for (i = 0, d = dt; i < sz; i++, d++) {
1440 if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
1441 (d->_type != dprev->_type)) {
1442 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1443 type = PLUG_TYPE_INVALID;
1444 goto exit;
1445 }
1446
1447 if (!d->swap_gnd && !d->mic_bias &&
1448 (abs(minv - d->_vdces) > delta_thr ||
1449 abs(maxv - d->_vdces) > delta_thr)) {
1450 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1451 __func__, d->_vdces, minv, maxv);
1452 type = PLUG_TYPE_INVALID;
1453 goto exit;
1454 } else if (d->swap_gnd) {
1455 dgnd = d;
1456 }
1457
1458 if (!d->mic_bias && !d->swap_gnd)
1459 dprev = d;
1460 else if (d->mic_bias)
1461 dmicbias = d;
1462 }
1463 if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
1464 dt->_type != dgnd->_type) {
1465 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1466 type = PLUG_TYPE_INVALID;
1467 goto exit;
1468 }
1469
1470 type = dt->_type;
1471 if (dmicbias) {
1472 if (dmicbias->_type == PLUG_TYPE_HEADSET &&
1473 (dt->_type == PLUG_TYPE_HIGH_HPH ||
1474 dt->_type == PLUG_TYPE_HEADSET)) {
1475 type = PLUG_TYPE_HEADSET;
1476 if (dt->_type == PLUG_TYPE_HIGH_HPH) {
1477 pr_debug("%s: Headset with threshold on MIC detected\n",
1478 __func__);
1479 if (mbhc->mbhc_cfg->micbias_enable_flags &
1480 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1481 mbhc->micbias_enable = true;
1482 }
1483 }
1484 }
1485
Yeleswarapu, Nagaradheshb9a6e042013-10-07 14:21:13 +05301486 if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
1487 /* if plug type is Headphone report as GND_MIC_SWAP */
1488 if (dgnd->_type == PLUG_TYPE_HEADPHONE) {
1489 pr_debug("%s: GND_MIC_SWAP\n", __func__);
1490 type = PLUG_TYPE_GND_MIC_SWAP;
1491 /*
1492 * if type is GND_MIC_SWAP we should not check
1493 * HPHL status hence goto exit
1494 */
1495 goto exit;
1496 } else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
1497 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1498 type = PLUG_TYPE_INVALID;
1499 }
1500 }
1501
1502 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1503 pr_debug("%s: HPHL PA was ON\n", __func__);
1504 } else if (ch != sz && ch > 0) {
1505 pr_debug("%s: Invalid, inconsistent HPHL..\n", __func__);
1506 type = PLUG_TYPE_INVALID;
1507 goto exit;
1508 }
1509
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001510 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1511 if (((type == PLUG_TYPE_HEADSET ||
1512 type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
1513 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1514 __func__, type);
1515 type = PLUG_TYPE_INVALID;
1516 }
1517 }
Simmi Pateriya154b6562013-12-05 04:17:12 +05301518
1519 if (type == PLUG_TYPE_HEADSET &&
1520 (mbhc->mbhc_cfg->micbias_enable_flags &
1521 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET)))
1522 mbhc->micbias_enable = true;
1523
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001524exit:
1525 pr_debug("%s: Plug type %d detected\n", __func__, type);
1526 return type;
1527}
1528
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001529/*
1530 * wcd9xxx_find_plug_type : Find out and return the best plug type with given
1531 * list of wcd9xxx_mbhc_detect structure.
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001532 * param mbhc wcd9xxx_mbhc structure
1533 * param dt collected measurements
1534 * param size array size of dt
1535 * param event_state mbhc->event_state when dt is collected
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001536 */
1537static enum wcd9xxx_mbhc_plug_type
1538wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001539 struct wcd9xxx_mbhc_detect *dt, const int size,
1540 unsigned long event_state)
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001541{
1542 int i;
1543 int ch;
1544 enum wcd9xxx_mbhc_plug_type type;
1545 int vdce;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001546 struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001547 int maxv = 0, minv = 0;
1548 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
1549 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
1550 const s16 hs_max = plug_type->v_hs_max;
1551 const s16 no_mic = plug_type->v_no_mic;
1552
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001553 pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
1554
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001555 for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
1556 vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
1557 if (d->vddio)
1558 d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
1559 else
1560 d->_vdces = vdce;
1561
1562 if (d->_vdces >= no_mic && d->_vdces < hs_max)
1563 d->_type = PLUG_TYPE_HEADSET;
1564 else if (d->_vdces < no_mic)
1565 d->_type = PLUG_TYPE_HEADPHONE;
1566 else
1567 d->_type = PLUG_TYPE_HIGH_HPH;
1568
1569 ch += d->hphl_status & 0x01;
Joonwoo Parkccccba72013-04-26 11:19:46 -07001570 if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001571 if (maxv < d->_vdces)
1572 maxv = d->_vdces;
1573 if (!minv || minv > d->_vdces)
1574 minv = d->_vdces;
1575 }
1576
1577 pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
1578 __func__, i, d->dce, vdce, d->_vdces,
1579 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
1580 d->_type);
Joonwoo Park141d6182013-03-05 12:25:46 -08001581
1582
1583 /*
1584 * If GND and MIC prongs are aligned to HPHR and GND of
1585 * headphone, codec measures the voltage based on
1586 * impedance between HPHR and GND which results in ~80mv.
1587 * Avoid this.
1588 */
1589 if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
1590 d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
1591 pr_debug("%s: within invalid range\n", __func__);
1592 type = PLUG_TYPE_INVALID;
1593 goto exit;
1594 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001595 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001596
1597 if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
1598 pr_debug("%s: HPHL PA was ON\n", __func__);
1599 } else if (ch != size && ch > 0) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001600 pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
1601 type = PLUG_TYPE_INVALID;
1602 goto exit;
1603 }
1604
Joonwoo Parka84ec452013-07-17 15:02:52 -07001605 for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
Joonwoo Parkccccba72013-04-26 11:19:46 -07001606 if (d->vddio) {
1607 dvddio = d;
1608 continue;
1609 }
1610
Aravind Kumar320ef3a2014-03-21 12:32:34 +05301611 if ((i > 0) && (dprev != NULL) && (d->_type != dprev->_type)) {
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001612 pr_debug("%s: Invalid, inconsistent types\n", __func__);
1613 type = PLUG_TYPE_INVALID;
1614 goto exit;
1615 }
1616
1617 if (!d->swap_gnd && !d->hwvalue &&
1618 (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
1619 abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
1620 pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
1621 __func__, d->_vdces, minv, maxv);
1622 type = PLUG_TYPE_INVALID;
1623 goto exit;
1624 } else if (d->swap_gnd) {
1625 dgnd = d;
1626 }
1627 dprev = d;
1628 }
1629
1630 WARN_ON(i != size);
1631 type = dt->_type;
1632 if (type == PLUG_TYPE_HEADSET && dgnd) {
1633 if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
1634 minv) &&
1635 (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
1636 maxv))
1637 type = PLUG_TYPE_GND_MIC_SWAP;
1638 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001639
1640 /* if HPHL PA was on, we cannot use hphl status */
1641 if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
1642 if (((type == PLUG_TYPE_HEADSET ||
1643 type == PLUG_TYPE_HEADPHONE) && ch != size) ||
1644 (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
1645 pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
1646 __func__, type);
1647 type = PLUG_TYPE_INVALID;
1648 }
Phani Kumar Uppalapatiec818fe2013-03-13 15:39:03 -07001649 }
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001650
Joonwoo Parkccccba72013-04-26 11:19:46 -07001651 if (type == PLUG_TYPE_HEADSET && dvddio) {
1652 if ((dvddio->_vdces > hs_max) ||
1653 (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD)) {
1654 pr_debug("%s: Headset with threshold on MIC detected\n",
1655 __func__);
1656 if (mbhc->mbhc_cfg->micbias_enable_flags &
1657 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
1658 mbhc->micbias_enable = true;
1659 } else {
1660 pr_debug("%s: Headset with regular MIC detected\n",
1661 __func__);
1662 if (mbhc->mbhc_cfg->micbias_enable_flags &
1663 (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
1664 mbhc->micbias_enable = true;
1665 }
1666 }
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001667exit:
Joonwoo Parkccccba72013-04-26 11:19:46 -07001668 pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
1669 type, mbhc->micbias_enable);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001670 return type;
1671}
1672
Joonwoo Parkccccba72013-04-26 11:19:46 -07001673/*
1674 * Pull down MBHC micbias for provided duration in microsecond.
1675 */
1676static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
1677{
1678 bool micbiasconn = false;
1679 struct snd_soc_codec *codec = mbhc->codec;
1680 const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
1681
1682 /*
1683 * Disable MBHC to micbias connection to pull down
1684 * micbias and pull down micbias for a moment.
1685 */
1686 if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
1687 WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
1688 return -EFAULT;
1689 }
1690
1691 if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
1692 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1693 1 << 4, 0);
1694 micbiasconn = true;
1695 }
1696
1697 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1698
1699 /*
1700 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
1701 * able to get consistent result across DCEs.
1702 */
1703 usleep_range(1000, 1000 + 10);
1704
1705 if (micbiasconn)
1706 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
1707 1 << 4, 1 << 4);
1708 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
1709 usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
1710
1711 return 0;
1712}
1713
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301714void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc,
1715 struct mbhc_micbias_regs *mbhc_micb_regs,
1716 bool on, bool highhph)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001717{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001718 struct snd_soc_codec *codec;
1719 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
1720 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1721 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1722
1723 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
1724 codec = mbhc->codec;
1725
1726 if (on) {
1727 pr_debug("%s: enabling current source\n", __func__);
1728 /* Nsc to 9 */
1729 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
1730 0x78, 0x48);
1731 /* pull down diode bit to 0 */
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301732 snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001733 0x01, 0x00);
1734 /*
1735 * Keep the low power insertion/removal
1736 * detection (reg 0x3DD) disabled
1737 */
1738 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
1739 0x01, 0x00);
1740 /*
1741 * Enable the Mic Bias current source
1742 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
1743 * Write bit[7] of register MICB_2_MBHC to 1
1744 * (INS_DET_ISRC_EN__ENABLE)
1745 * MICB_2_MBHC__SCHT_TRIG_EN to 1
1746 */
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301747 snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001748 0xF0, 0xF0);
1749 /* Disconnect MBHC Override from MicBias and LDOH */
1750 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
1751 } else {
1752 pr_debug("%s: disabling current source\n", __func__);
1753 /* Connect MBHC Override from MicBias and LDOH */
1754 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
1755 /* INS_DET_ISRC_CTL to acdb value */
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301756 snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001757 0x60, plug_det->mic_current << 5);
1758 if (!highhph) {
1759 /* INS_DET_ISRC_EN__ENABLE to 0 */
1760 snd_soc_update_bits(codec,
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301761 mbhc_micb_regs->mbhc_reg,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001762 0x80, 0x00);
1763 /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
1764 snd_soc_update_bits(codec,
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301765 mbhc_micb_regs->mbhc_reg,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001766 0x10, 0x00);
1767 }
1768 /* Nsc to acdb value */
1769 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
1770 btn_det->mbhc_nsc << 3);
1771 }
1772}
1773
1774static enum wcd9xxx_mbhc_plug_type
1775wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1776{
1777 struct snd_soc_codec *codec = mbhc->codec;
1778 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1779 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
1780 int i;
1781
1782 pr_debug("%s: enter\n", __func__);
1783 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1784
1785 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
1786
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301787 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001788 rt[0].swap_gnd = false;
1789 rt[0].vddio = false;
1790 rt[0].hwvalue = true;
1791 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001792 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
1793 true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001794 rt[0].mic_bias = false;
1795
1796 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
1797 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
1798 rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
1799 highhph);
1800 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1801 if (rt[i].swap_gnd)
1802 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1803
1804 if (rt[i].mic_bias)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301805 wcd9xxx_turn_onoff_current_source(mbhc,
1806 &mbhc->mbhc_bias_regs,
1807 false, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001808
1809 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
1810 if (rt[i].mic_bias)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05301811 wcd9xxx_turn_onoff_current_source(mbhc,
1812 &mbhc->mbhc_bias_regs,
1813 true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001814 if (rt[i].swap_gnd)
1815 wcd9xxx_codec_hphr_gnd_switch(codec, false);
1816 }
1817
1818 type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
1819 mbhc->event_state);
1820
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301821 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07001822 pr_debug("%s: plug_type:%d\n", __func__, type);
1823
1824 return type;
1825}
1826
Joonwoo Parka8890262012-10-15 12:04:27 -07001827static enum wcd9xxx_mbhc_plug_type
1828wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
1829{
1830 int i;
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001831 bool vddioon;
Joonwoo Parka8890262012-10-15 12:04:27 -07001832 struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001833 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
1834 enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07001835 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parka8890262012-10-15 12:04:27 -07001836
Joonwoo Park80a01172012-10-15 16:05:23 -07001837 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07001838 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
1839
1840 /* make sure override is on */
1841 WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
1842
1843 /* GND and MIC swap detection requires at least 2 rounds of DCE */
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001844 BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
Simmi Pateriya4e545052013-11-15 07:41:06 +05301845 detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
Joonwoo Parka8890262012-10-15 12:04:27 -07001846
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001847 /*
1848 * There are chances vddio switch is on and cfilt voltage is adjusted
1849 * to vddio voltage even after plug type removal reported.
1850 */
1851 vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
1852 pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
1853
Joonwoo Parka8890262012-10-15 12:04:27 -07001854 plug_type_ptr =
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001855 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Joonwoo Parka8890262012-10-15 12:04:27 -07001856
Joonwoo Parkccccba72013-04-26 11:19:46 -07001857 /*
1858 * cfilter in fast mode requires 1ms to charge up and down micbias
1859 * fully.
1860 */
1861 (void) wcd9xxx_pull_down_micbias(mbhc,
1862 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301863
1864 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001865 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07001866 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
1867 false);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001868 rt[0].swap_gnd = false;
1869 rt[0].vddio = false;
1870 rt[0].hwvalue = true;
1871 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
1872 rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
1873 if (detect_use_vddio_switch)
Joonwoo Parkccccba72013-04-26 11:19:46 -07001874 rt[i].vddio = (i == 1);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001875 else
1876 rt[i].vddio = false;
1877 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
1878 rt[i].hwvalue = false;
1879 if (rt[i].swap_gnd)
1880 wcd9xxx_codec_hphr_gnd_switch(codec, true);
1881 if (rt[i].vddio)
1882 wcd9xxx_onoff_vddio_switch(mbhc, true);
Joonwoo Parkccccba72013-04-26 11:19:46 -07001883 /*
1884 * Pull down micbias to detect headset with mic which has
1885 * threshold and to have more consistent voltage measurements.
1886 *
1887 * cfilter in fast mode requires 1ms to charge up and down
1888 * micbias fully.
1889 */
1890 (void) wcd9xxx_pull_down_micbias(mbhc,
1891 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001892 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
1893 if (rt[i].vddio)
1894 wcd9xxx_onoff_vddio_switch(mbhc, false);
1895 if (rt[i].swap_gnd)
1896 wcd9xxx_codec_hphr_gnd_switch(codec, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07001897 }
1898
Joonwoo Parkddcd8832013-06-19 15:58:47 -07001899 if (vddioon)
1900 __wcd9xxx_switch_micbias(mbhc, 1, false, false);
1901
1902 type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
1903 mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07001904
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05301905 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07001906 pr_debug("%s: leave\n", __func__);
Joonwoo Park20bc9da2013-01-16 12:58:06 -08001907 return type;
Joonwoo Parka8890262012-10-15 12:04:27 -07001908}
1909
1910static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
1911{
1912 if (mbhc->mbhc_cfg->gpio)
1913 return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
1914 mbhc->mbhc_cfg->gpio_level_insert);
1915 else if (mbhc->mbhc_cfg->insert_detect)
1916 return snd_soc_read(mbhc->codec,
1917 WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
1918 (1 << 2);
1919 else
1920 WARN(1, "Invalid jack detection configuration\n");
1921
1922 return true;
1923}
1924
1925static bool is_clk_active(struct snd_soc_codec *codec)
1926{
1927 return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
1928}
1929
1930static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
1931 int insertion, int trigger, bool padac_off)
1932{
1933 struct snd_soc_codec *codec = mbhc->codec;
1934 int central_bias_enabled = 0;
1935 const struct wcd9xxx_mbhc_general_cfg *generic =
1936 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
1937 const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
1938 WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
1939
Joonwoo Park80a01172012-10-15 16:05:23 -07001940 pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
1941 __func__, insertion, trigger);
1942
Joonwoo Parka8890262012-10-15 12:04:27 -07001943 if (!mbhc->mbhc_cfg->calibration) {
1944 pr_err("Error, no wcd9xxx calibration\n");
1945 return -EINVAL;
1946 }
1947
1948 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
1949
1950 /*
1951 * Make sure mic bias and Mic line schmitt trigger
1952 * are turned OFF
1953 */
1954 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
1955 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
1956
1957 if (insertion) {
1958 wcd9xxx_switch_micbias(mbhc, 0);
1959
1960 /* DAPM can manipulate PA/DAC bits concurrently */
1961 if (padac_off == true)
1962 wcd9xxx_set_and_turnoff_hph_padac(mbhc);
1963
1964 if (trigger & MBHC_USE_HPHL_TRIGGER) {
1965 /* Enable HPH Schmitt Trigger */
1966 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
1967 0x11);
1968 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
1969 plug_det->hph_current << 2);
1970 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
1971 0x02);
1972 }
1973 if (trigger & MBHC_USE_MB_TRIGGER) {
1974 /* enable the mic line schmitt trigger */
1975 snd_soc_update_bits(codec,
1976 mbhc->mbhc_bias_regs.mbhc_reg,
1977 0x60, plug_det->mic_current << 5);
1978 snd_soc_update_bits(codec,
1979 mbhc->mbhc_bias_regs.mbhc_reg,
1980 0x80, 0x80);
1981 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
1982 snd_soc_update_bits(codec,
1983 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
1984 0x00);
1985 snd_soc_update_bits(codec,
1986 mbhc->mbhc_bias_regs.mbhc_reg,
1987 0x10, 0x10);
1988 }
1989
1990 /* setup for insetion detection */
1991 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
1992 } else {
1993 pr_debug("setup for removal detection\n");
1994 /* Make sure the HPH schmitt trigger is OFF */
1995 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
1996
1997 /* enable the mic line schmitt trigger */
1998 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
1999 0x01, 0x00);
2000 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
2001 plug_det->mic_current << 5);
2002 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
2003 0x80, 0x80);
2004 usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
2005 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
2006 0x10, 0x10);
2007
2008 /* Setup for low power removal detection */
2009 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
2010 0x2);
2011 }
2012
2013 if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
2014 /* called by interrupt */
2015 if (!is_clk_active(codec)) {
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07002016 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
Joonwoo Parka8890262012-10-15 12:04:27 -07002017 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2018 0x06, 0);
2019 usleep_range(generic->t_shutdown_plug_rem,
2020 generic->t_shutdown_plug_rem);
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07002021 wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07002022 } else
2023 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
2024 0x06, 0);
2025 }
2026
2027 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
2028
2029 /* If central bandgap disabled */
2030 if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
2031 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
2032 usleep_range(generic->t_bg_fast_settle,
2033 generic->t_bg_fast_settle);
2034 central_bias_enabled = 1;
2035 }
2036
2037 /* If LDO_H disabled */
2038 if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
2039 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
2040 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
2041 usleep_range(generic->t_ldoh, generic->t_ldoh);
2042 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
2043
2044 if (central_bias_enabled)
2045 snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
2046 0);
2047 }
2048
Meng Wangeeaaaba2013-09-09 18:37:32 +08002049 if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
Simmi Pateriya0a44d842013-04-03 01:12:42 +05302050 snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
2051 0x3, mbhc->mbhc_cfg->micbias);
Joonwoo Parka8890262012-10-15 12:04:27 -07002052
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002053 wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002054 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
Joonwoo Park80a01172012-10-15 16:05:23 -07002055 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002056
2057 return 0;
2058}
2059
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002060/*
2061 * Function to determine whether anc microphone is preset or not.
2062 * Return true if anc microphone is detected or false if not detected.
2063 */
2064static bool wcd9xxx_detect_anc_plug_type(struct wcd9xxx_mbhc *mbhc)
2065{
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302066 struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT - 1];
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002067 bool anc_mic_found = true;
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302068 int i, mb_mv;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002069 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2070 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302071 s16 hs_max, dce_z;
2072 s16 no_mic;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002073 bool override_en;
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302074 bool timedout;
2075 unsigned long timeout, retry = 0;
2076 enum wcd9xxx_mbhc_plug_type type;
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302077 bool cs_enable;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002078
2079 if (mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS3 &&
2080 mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS2)
2081 return false;
2082
2083 pr_debug("%s: enter\n", __func__);
2084
2085 override_en = (snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
2086 0x04) ? true : false;
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302087 cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
2088 (1 << MBHC_CS_ENABLE_DET_ANC)) != 0) &&
2089 (!(snd_soc_read(mbhc->codec,
2090 mbhc->mbhc_anc_bias_regs.ctl_reg) & 0x80)) &&
2091 (mbhc->mbhc_cfg->micbias != mbhc->mbhc_cfg->anc_micbias);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002092
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302093 if (cs_enable) {
2094 wcd9xxx_turn_onoff_current_source(mbhc,
2095 &mbhc->mbhc_anc_bias_regs,
2096 true, false);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002097 } else {
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302098 if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
2099 if (mbhc->micbias_enable_cb)
2100 mbhc->micbias_enable_cb(mbhc->codec, true,
2101 mbhc->mbhc_cfg->anc_micbias);
2102 else
2103 return false;
2104 } else {
2105 /* Enable override */
2106 if (!override_en)
2107 wcd9xxx_turn_onoff_override(mbhc, true);
2108 }
2109 }
2110
2111 if (!cs_enable) {
2112 hs_max = plug_type->v_hs_max;
2113 no_mic = plug_type->v_no_mic;
2114 dce_z = mbhc->mbhc_data.dce_z;
2115 mb_mv = mbhc->mbhc_data.micb_mv;
2116 } else {
2117 hs_max = WCD9XXX_V_CS_HS_MAX;
2118 no_mic = WCD9XXX_V_CS_NO_MIC;
2119 mb_mv = VDDIO_MICBIAS_MV;
2120 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002121 }
2122
2123 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
2124
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302125 timeout = jiffies + msecs_to_jiffies(ANC_HPH_DETECT_PLUG_TIME_MS);
2126 anc_mic_found = true;
2127
2128 while (!(timedout = time_after(jiffies, timeout))) {
2129 retry++;
2130
2131 if (wcd9xxx_swch_level_remove(mbhc)) {
2132 pr_debug("%s: Switch level is low\n", __func__);
2133 anc_mic_found = false;
2134 break;
2135 }
2136
2137 pr_debug("%s: Retry attempt %lu", __func__, retry - 1);
2138
2139 rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
2140 rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc,
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002141 &mbhc->mbhc_anc_bias_regs,
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302142 cs_enable);
2143 rt[0]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, rt[0].dce,
2144 dce_z, (u32)mb_mv);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002145
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302146 if (rt[0]._vdces >= no_mic && rt[0]._vdces < hs_max)
2147 rt[0]._type = PLUG_TYPE_HEADSET;
2148 else if (rt[0]._vdces < no_mic)
2149 rt[0]._type = PLUG_TYPE_HEADPHONE;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002150 else
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302151 rt[0]._type = PLUG_TYPE_HIGH_HPH;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002152
2153 pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302154 __func__, 0, rt[0]._vdces,
2155 rt[0].hphl_status & 0x01,
2156 rt[0]._type);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002157
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302158 for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
2159 rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1,
2160 true, true);
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302161 rt[i]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true,
2162 rt[i].dce, dce_z,
2163 (u32) mb_mv);
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302164
2165 if (rt[i]._vdces >= no_mic && rt[i]._vdces < hs_max)
2166 rt[i]._type = PLUG_TYPE_HEADSET;
2167 else if (rt[i]._vdces < no_mic)
2168 rt[i]._type = PLUG_TYPE_HEADPHONE;
2169 else
2170 rt[i]._type = PLUG_TYPE_HIGH_HPH;
2171
2172 rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
2173
2174 pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
2175 __func__, i, rt[i]._vdces,
2176 rt[i].hphl_status & 0x01,
2177 rt[i]._type);
2178 }
2179
2180 /*
2181 * Check for the "type" of all the 4 measurements
2182 * If all 4 measurements have the Type as PLUG_TYPE_HEADSET
2183 * then it is proper mic and declare that the plug has two mics
2184 */
2185 for (i = 0; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
2186 if (i > 0 && (rt[i - 1]._type != rt[i]._type)) {
2187 type = PLUG_TYPE_INVALID;
2188 break;
2189 } else {
2190 type = rt[0]._type;
2191 }
2192 }
2193
2194 pr_debug("%s: Plug type found in ANC detection :%d",
2195 __func__, type);
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302196
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302197 if (type != PLUG_TYPE_HEADSET)
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002198 anc_mic_found = false;
Simmi Pateriya12f63bc2013-11-08 14:43:54 +05302199 if (anc_mic_found || (type == PLUG_TYPE_HEADPHONE &&
2200 mbhc->mbhc_cfg->hw_jack_type == FIVE_POLE_JACK) ||
2201 (type == PLUG_TYPE_HIGH_HPH &&
2202 mbhc->mbhc_cfg->hw_jack_type == SIX_POLE_JACK))
2203 break;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002204 }
2205
2206 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302207 if (cs_enable) {
2208 wcd9xxx_turn_onoff_current_source(mbhc,
2209 &mbhc->mbhc_anc_bias_regs,
2210 false, false);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002211 } else {
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302212 if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
2213 if (mbhc->micbias_enable_cb)
2214 mbhc->micbias_enable_cb(mbhc->codec, false,
2215 mbhc->mbhc_cfg->anc_micbias);
2216 } else {
2217 /* Disable override */
2218 if (!override_en)
2219 wcd9xxx_turn_onoff_override(mbhc, false);
2220 }
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002221 }
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002222 pr_debug("%s: leave\n", __func__);
2223 return anc_mic_found;
2224}
2225
Joonwoo Parka8890262012-10-15 12:04:27 -07002226/* called under codec_resource_lock acquisition */
2227static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
2228 enum wcd9xxx_mbhc_plug_type plug_type)
2229{
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002230 bool anc_mic_found = false;
2231
Joonwoo Park80a01172012-10-15 16:05:23 -07002232 pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
2233 __func__, mbhc->current_plug, plug_type);
2234
Joonwoo Parka8890262012-10-15 12:04:27 -07002235 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2236
2237 if (plug_type == PLUG_TYPE_HEADPHONE &&
2238 mbhc->current_plug == PLUG_TYPE_NONE) {
2239 /*
2240 * Nothing was reported previously
2241 * report a headphone or unsupported
2242 */
2243 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
2244 wcd9xxx_cleanup_hs_polling(mbhc);
2245 } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002246 if (!mbhc->mbhc_cfg->detect_extn_cable) {
2247 if (mbhc->current_plug == PLUG_TYPE_HEADSET)
2248 wcd9xxx_report_plug(mbhc, 0,
2249 SND_JACK_HEADSET);
2250 else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
2251 wcd9xxx_report_plug(mbhc, 0,
2252 SND_JACK_HEADPHONE);
2253 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002254 wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
2255 wcd9xxx_cleanup_hs_polling(mbhc);
2256 } else if (plug_type == PLUG_TYPE_HEADSET) {
Joonwoo Park2cee50a2013-05-15 14:09:06 -07002257
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002258 if (mbhc->mbhc_cfg->enable_anc_mic_detect) {
2259 /*
2260 * Do not report Headset, because at this point
2261 * it could be a ANC headphone having two mics.
2262 * So, proceed further to detect if there is a
2263 * second mic.
2264 */
2265 mbhc->scaling_mux_in = 0x08;
2266 anc_mic_found = wcd9xxx_detect_anc_plug_type(mbhc);
2267 }
2268
2269 if (anc_mic_found) {
2270 /* Report ANC headphone */
2271 wcd9xxx_report_plug(mbhc, 1, SND_JACK_ANC_HEADPHONE);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002272 } else {
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002273 /*
2274 * If Headphone was reported previously, this will
2275 * only report the mic line
2276 */
2277 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002278 }
Simmi Pateriyaf0553ab2013-12-03 11:25:40 +05302279 /* Button detection required RC oscillator */
2280 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
2281 /*
2282 * sleep so that audio path completely tears down
2283 * before report plug insertion to the user space
2284 */
2285 msleep(100);
2286
2287 /*
2288 * if PA is already on, switch micbias
2289 * source to VDDIO
2290 */
2291 if (mbhc->event_state &
2292 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
2293 __wcd9xxx_switch_micbias(mbhc, 1, false,
2294 false);
2295 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002296 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002297 if (mbhc->mbhc_cfg->detect_extn_cable) {
2298 /* High impedance device found. Report as LINEOUT*/
Simmi Pateriya0ba392d2013-11-01 01:27:01 +05302299 if (mbhc->current_plug == PLUG_TYPE_NONE)
2300 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
Joonwoo Park80a01172012-10-15 16:05:23 -07002301 wcd9xxx_cleanup_hs_polling(mbhc);
2302 pr_debug("%s: setup mic trigger for further detection\n",
2303 __func__);
2304 mbhc->lpi_enabled = true;
2305 /*
2306 * Do not enable HPHL trigger. If playback is active,
2307 * it might lead to continuous false HPHL triggers
2308 */
2309 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
2310 false);
2311 } else {
2312 if (mbhc->current_plug == PLUG_TYPE_NONE)
2313 wcd9xxx_report_plug(mbhc, 1,
2314 SND_JACK_HEADPHONE);
2315 wcd9xxx_cleanup_hs_polling(mbhc);
2316 pr_debug("setup mic trigger for further detection\n");
2317 mbhc->lpi_enabled = true;
2318 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2319 MBHC_USE_HPHL_TRIGGER,
2320 false);
2321 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002322 } else {
2323 WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
2324 mbhc->current_plug, plug_type);
2325 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002326 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002327}
2328
2329/* called under codec_resource_lock acquisition */
2330static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
2331{
2332 enum wcd9xxx_mbhc_plug_type plug_type;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002333 bool current_source_enable;
Joonwoo Parka8890262012-10-15 12:04:27 -07002334
2335 pr_debug("%s: enter\n", __func__);
2336
2337 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002338
2339 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2340 (1 << MBHC_CS_ENABLE_INSERTION)) != 0) &&
2341 (!(snd_soc_read(mbhc->codec,
2342 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002343
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07002344 mbhc->scaling_mux_in = 0x04;
2345
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002346 if (current_source_enable) {
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302347 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2348 true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002349 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302350 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2351 false, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002352 } else {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002353 wcd9xxx_turn_onoff_override(mbhc, true);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002354 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002355 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002356 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002357
2358 if (wcd9xxx_swch_level_remove(mbhc)) {
2359 pr_debug("%s: Switch level is low when determining plug\n",
2360 __func__);
2361 return;
2362 }
2363
2364 if (plug_type == PLUG_TYPE_INVALID ||
2365 plug_type == PLUG_TYPE_GND_MIC_SWAP) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002366 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002367 wcd9xxx_schedule_hs_detect_plug(mbhc,
2368 &mbhc->correct_plug_swch);
2369 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
2370 wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002371 wcd9xxx_cleanup_hs_polling(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002372 wcd9xxx_schedule_hs_detect_plug(mbhc,
2373 &mbhc->correct_plug_swch);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002374 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07002375 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07002376 wcd9xxx_schedule_hs_detect_plug(mbhc,
2377 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07002378 } else {
2379 pr_debug("%s: Valid plug found, determine plug type %d\n",
2380 __func__, plug_type);
2381 wcd9xxx_find_plug_and_report(mbhc, plug_type);
2382 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002383 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002384}
2385
2386/* called under codec_resource_lock acquisition */
2387static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
2388{
Joonwoo Park80a01172012-10-15 16:05:23 -07002389 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002390 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
2391
Joonwoo Parka8890262012-10-15 12:04:27 -07002392 if (wcd9xxx_swch_level_remove(mbhc))
2393 pr_debug("%s: Switch level low when determining plug\n",
2394 __func__);
2395 else
2396 wcd9xxx_mbhc_decide_swch_plug(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002397 pr_debug("%s: leave\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002398}
2399
2400/* called only from interrupt which is under codec_resource_lock acquisition */
2401static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
2402 bool is_removal)
2403{
2404 if (!is_removal) {
2405 pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
2406
2407 rmb();
2408 if (mbhc->lpi_enabled)
2409 msleep(100);
2410
2411 rmb();
2412 if (!mbhc->lpi_enabled) {
2413 pr_debug("%s: lpi is disabled\n", __func__);
2414 } else if (!wcd9xxx_swch_level_remove(mbhc)) {
2415 pr_debug("%s: Valid insertion, detect plug type\n",
2416 __func__);
2417 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2418 } else {
2419 pr_debug("%s: Invalid insertion stop plug detection\n",
2420 __func__);
2421 }
Joonwoo Park80a01172012-10-15 16:05:23 -07002422 } else if (mbhc->mbhc_cfg->detect_extn_cable) {
2423 pr_debug("%s: Removal\n", __func__);
2424 if (!wcd9xxx_swch_level_remove(mbhc)) {
2425 /*
2426 * Switch indicates, something is still inserted.
2427 * This could be extension cable i.e. headset is
2428 * removed from extension cable.
2429 */
2430 /* cancel detect plug */
2431 wcd9xxx_cancel_hs_detect_plug(mbhc,
2432 &mbhc->correct_plug_swch);
2433 wcd9xxx_mbhc_decide_swch_plug(mbhc);
2434 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002435 } else {
2436 pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
2437 }
2438}
2439
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002440static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
2441 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002442{
2443 const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
2444 WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2445 const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
2446
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002447 if (cs_enable)
2448 return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
2449 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
2450 else
2451 return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
2452 mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
2453 (mic_mv > plug_type->v_no_mic) &&
2454 (mic_mv < v_hs_max)) ? true : false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002455}
2456
2457/*
2458 * called under codec_resource_lock acquisition
2459 * returns true if mic voltage range is back to normal insertion
2460 * returns false either if timedout or removed
2461 */
2462static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
2463{
2464 int i;
2465 bool timedout, settled = false;
2466 s32 mic_mv[NUM_DCE_PLUG_DETECT];
2467 short mb_v[NUM_DCE_PLUG_DETECT];
2468 unsigned long retry = 0, timeout;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002469 bool cs_enable;
2470
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002471 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2472 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2473 (!(snd_soc_read(mbhc->codec,
2474 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002475 if (cs_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302476 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2477 true, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002478
2479 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
2480 while (!(timedout = time_after(jiffies, timeout))) {
2481 retry++;
2482 if (wcd9xxx_swch_level_remove(mbhc)) {
2483 pr_debug("%s: Switch indicates removal\n", __func__);
2484 break;
2485 }
2486
2487 if (retry > 1)
2488 msleep(250);
2489 else
2490 msleep(50);
2491
2492 if (wcd9xxx_swch_level_remove(mbhc)) {
2493 pr_debug("%s: Switch indicates removal\n", __func__);
2494 break;
2495 }
2496
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002497 if (cs_enable) {
2498 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2499 mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
2500 true, true);
2501 mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
2502 true,
2503 mb_v[i],
2504 mbhc->mbhc_data.dce_nsc_cs_z,
2505 (u32)VDDIO_MICBIAS_MV);
2506 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2507 __func__, retry, mic_mv[i], mb_v[i]);
2508 }
2509 } else {
2510 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
2511 mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
2512 true);
2513 mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
2514 mb_v[i]);
2515 pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
2516 __func__, retry, mic_mv[i],
2517 mb_v[i]);
2518 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002519 }
2520
2521 if (wcd9xxx_swch_level_remove(mbhc)) {
2522 pr_debug("%s: Switcn indicates removal\n", __func__);
2523 break;
2524 }
2525
2526 if (mbhc->current_plug == PLUG_TYPE_NONE) {
2527 pr_debug("%s : headset/headphone is removed\n",
2528 __func__);
2529 break;
2530 }
2531
2532 for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002533 if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
Joonwoo Parka8890262012-10-15 12:04:27 -07002534 break;
2535
2536 if (i == NUM_DCE_PLUG_DETECT) {
2537 pr_debug("%s: MIC voltage settled\n", __func__);
2538 settled = true;
2539 msleep(200);
2540 break;
2541 }
2542 }
2543
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002544 if (cs_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302545 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2546 false, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002547
Joonwoo Parka8890262012-10-15 12:04:27 -07002548 if (timedout)
2549 pr_debug("%s: Microphone did not settle in %d seconds\n",
2550 __func__, HS_DETECT_PLUG_TIME_MS);
2551 return settled;
2552}
2553
2554/* called only from interrupt which is under codec_resource_lock acquisition */
2555static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
2556{
Joonwoo Park80a01172012-10-15 16:05:23 -07002557 pr_debug("%s: enter\n", __func__);
Joonwoo Parka8890262012-10-15 12:04:27 -07002558 if (wcd9xxx_hs_remove_settle(mbhc))
2559 wcd9xxx_start_hs_polling(mbhc);
Joonwoo Park80a01172012-10-15 16:05:23 -07002560 pr_debug("%s: leave\n", __func__);
2561}
2562
2563/* called only from interrupt which is under codec_resource_lock acquisition */
2564static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
2565{
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002566 s16 dce, dcez;
Joonwoo Park50ae0512013-06-04 16:53:12 -07002567 unsigned long timeout;
Joonwoo Park80a01172012-10-15 16:05:23 -07002568 bool removed = true;
2569 struct snd_soc_codec *codec = mbhc->codec;
2570 const struct wcd9xxx_mbhc_general_cfg *generic =
2571 WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002572 bool cs_enable;
2573 s16 cur_v_ins_h;
2574 u32 mb_mv;
Joonwoo Park80a01172012-10-15 16:05:23 -07002575
2576 pr_debug("%s: enter\n", __func__);
Simmi Pateriyaf0553ab2013-12-03 11:25:40 +05302577 if (mbhc->current_plug != PLUG_TYPE_HEADSET &&
2578 mbhc->current_plug != PLUG_TYPE_ANC_HEADPHONE) {
Joonwoo Park80a01172012-10-15 16:05:23 -07002579 pr_debug("%s(): Headset is not inserted, ignore removal\n",
2580 __func__);
2581 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
2582 0x08, 0x08);
2583 return;
2584 }
2585
2586 usleep_range(generic->t_shutdown_plug_rem,
Joonwoo Park50ae0512013-06-04 16:53:12 -07002587 generic->t_shutdown_plug_rem);
Joonwoo Park80a01172012-10-15 16:05:23 -07002588
Phani Kumar Uppalapati4dce16b2013-08-28 16:22:49 -07002589 /* If micbias is enabled, don't enable current source */
2590 cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2591 (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
2592 (!(snd_soc_read(codec,
2593 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002594 if (cs_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302595 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2596 true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002597
Joonwoo Park50ae0512013-06-04 16:53:12 -07002598 timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
Joonwoo Park80a01172012-10-15 16:05:23 -07002599 do {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002600 if (cs_enable) {
2601 dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
2602 dcez = mbhc->mbhc_data.dce_nsc_cs_z;
2603 mb_mv = VDDIO_MICBIAS_MV;
2604 } else {
2605 dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
2606 dcez = mbhc->mbhc_data.dce_z;
2607 mb_mv = mbhc->mbhc_data.micb_mv;
2608 }
2609
Joonwoo Park50ae0512013-06-04 16:53:12 -07002610 pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002611 __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
2612 dcez, mb_mv));
2613
2614 cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
2615 (wcd9xxx_get_current_v(mbhc,
2616 WCD9XXX_CURRENT_V_INS_H));
2617
2618 if (dce < cur_v_ins_h) {
Joonwoo Park50ae0512013-06-04 16:53:12 -07002619 removed = false;
Joonwoo Park80a01172012-10-15 16:05:23 -07002620 break;
2621 }
Joonwoo Park50ae0512013-06-04 16:53:12 -07002622 } while (!time_after(jiffies, timeout));
2623 pr_debug("%s: headset %sactually removed\n", __func__,
2624 removed ? "" : "not ");
Joonwoo Park80a01172012-10-15 16:05:23 -07002625
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002626 if (cs_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05302627 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
2628 false, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002629
Joonwoo Park80a01172012-10-15 16:05:23 -07002630 if (removed) {
2631 if (mbhc->mbhc_cfg->detect_extn_cable) {
2632 if (!wcd9xxx_swch_level_remove(mbhc)) {
2633 /*
2634 * extension cable is still plugged in
2635 * report it as LINEOUT device
2636 */
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302637 if (mbhc->hph_status == SND_JACK_HEADSET)
2638 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
2639 false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002640 wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
2641 wcd9xxx_cleanup_hs_polling(mbhc);
2642 wcd9xxx_enable_hs_detect(mbhc, 1,
2643 MBHC_USE_MB_TRIGGER,
2644 false);
2645 }
2646 } else {
2647 /* Cancel possibly running hs_detect_work */
2648 wcd9xxx_cancel_hs_detect_plug(mbhc,
2649 &mbhc->correct_plug_noswch);
2650 /*
2651 * If this removal is not false, first check the micbias
2652 * switch status and switch it to LDOH if it is already
2653 * switched to VDDIO.
2654 */
2655 wcd9xxx_switch_micbias(mbhc, 0);
2656
2657 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05302658 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07002659 wcd9xxx_cleanup_hs_polling(mbhc);
2660 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
2661 MBHC_USE_HPHL_TRIGGER,
2662 true);
2663 }
2664 } else {
2665 wcd9xxx_start_hs_polling(mbhc);
2666 }
2667 pr_debug("%s: leave\n", __func__);
2668}
2669
2670/* called only from interrupt which is under codec_resource_lock acquisition */
2671static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
2672 bool is_mb_trigger)
2673{
2674 /* Cancel possibly running hs_detect_work */
2675 wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
2676
2677 if (is_mb_trigger) {
2678 pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
2679 wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
2680 } else {
2681 pr_debug("%s: HPHL trigger received, detecting plug type\n",
2682 __func__);
2683 wcd9xxx_mbhc_detect_plug_type(mbhc);
2684 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002685}
2686
2687static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
2688{
Joonwoo Parka8890262012-10-15 12:04:27 -07002689 struct wcd9xxx_mbhc *mbhc = data;
2690
2691 pr_debug("%s: enter, removal interrupt\n", __func__);
2692 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002693 /*
2694 * While we don't know whether MIC is there or not, let the resmgr know
2695 * so micbias can be disabled temporarily
2696 */
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002697 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
Joonwoo Park3699ca32013-02-08 12:06:15 -08002698 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2699 WCD9XXX_COND_HPH_MIC, false);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002700 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2701 WCD9XXX_COND_HPH, false);
2702 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2703 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2704 WCD9XXX_COND_HPH, false);
2705 }
Joonwoo Park3699ca32013-02-08 12:06:15 -08002706
Joonwoo Park80a01172012-10-15 16:05:23 -07002707 if (mbhc->mbhc_cfg->detect_extn_cable &&
2708 !wcd9xxx_swch_level_remove(mbhc))
2709 wcd9xxx_hs_remove_irq_noswch(mbhc);
2710 else
2711 wcd9xxx_hs_remove_irq_swch(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07002712
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002713 if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
2714 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2715 WCD9XXX_COND_HPH, true);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002716 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2717 WCD9XXX_COND_HPH_MIC, true);
Joonwoo Parkaf21f032013-03-05 18:07:40 -08002718 } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
2719 wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
2720 WCD9XXX_COND_HPH, true);
2721 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002722 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2723
2724 return IRQ_HANDLED;
2725}
2726
2727static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
2728{
2729 bool is_mb_trigger, is_removal;
2730 struct wcd9xxx_mbhc *mbhc = data;
2731 struct snd_soc_codec *codec = mbhc->codec;
2732
2733 pr_debug("%s: enter\n", __func__);
2734 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002735 wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002736
2737 is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
2738 0x10);
2739 is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
2740 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
2741
2742 /* Turn off both HPH and MIC line schmitt triggers */
2743 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2744 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2745 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
2746
Joonwoo Park80a01172012-10-15 16:05:23 -07002747 if (mbhc->mbhc_cfg->detect_extn_cable &&
2748 mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
2749 wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
2750 else
2751 wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
Joonwoo Parka8890262012-10-15 12:04:27 -07002752
2753 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
2754 return IRQ_HANDLED;
2755}
2756
2757static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
2758{
2759 struct delayed_work *dwork;
2760 short bias_value;
2761 int dce_mv, sta_mv;
2762 struct wcd9xxx_mbhc *mbhc;
2763
2764 pr_debug("%s:\n", __func__);
2765
2766 dwork = to_delayed_work(work);
2767 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
2768
2769 bias_value = wcd9xxx_read_sta_result(mbhc->codec);
2770 sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
2771
2772 bias_value = wcd9xxx_read_dce_result(mbhc->codec);
2773 dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
2774 pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
2775
2776 pr_debug("%s: Reporting long button press event\n", __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08002777 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
Joonwoo Parka8890262012-10-15 12:04:27 -07002778 mbhc->buttons_pressed);
2779
2780 pr_debug("%s: leave\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002781 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002782}
2783
2784static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
2785{
2786 struct delayed_work *dwork;
2787 struct wcd9xxx_mbhc *mbhc;
2788 struct snd_soc_codec *codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002789 struct wcd9xxx_core_resource *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002790
2791 dwork = to_delayed_work(work);
2792 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
2793 codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002794 core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07002795
2796 pr_debug("%s:\n", __func__);
2797
2798 /* Turn off both HPH and MIC line schmitt triggers */
2799 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
2800 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
2801 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07002802 wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07002803 wcd9xxx_mbhc_detect_plug_type(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002804 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07002805}
2806
2807static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
2808{
2809 u32 cfg_offset;
2810 struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
2811 struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
2812
2813 if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
2814 return false;
2815
2816 /*
2817 * Previous check guarantees that there is enough fw data up
2818 * to num_btn
2819 */
2820 btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
2821 cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
2822 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
2823 return false;
2824
2825 /*
2826 * Previous check guarantees that there is enough fw data up
2827 * to start of impedance detection configuration
2828 */
2829 imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
2830 cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
2831
2832 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
2833 return false;
2834
2835 if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
2836 return false;
2837
2838 return true;
2839}
2840
2841static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002842 enum meas_type dce, s16 vin_mv,
2843 bool cs_enable)
Joonwoo Parka8890262012-10-15 12:04:27 -07002844{
2845 s16 diff, zero;
2846 u32 mb_mv, in;
2847 u16 value;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002848 s16 dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002849
2850 mb_mv = mbhc->mbhc_data.micb_mv;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002851 dce_z = mbhc->mbhc_data.dce_z;
Joonwoo Parka8890262012-10-15 12:04:27 -07002852
2853 if (mb_mv == 0) {
2854 pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
2855 return -EINVAL;
2856 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002857 if (cs_enable) {
2858 mb_mv = VDDIO_MICBIAS_MV;
2859 dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
2860 }
Joonwoo Parka8890262012-10-15 12:04:27 -07002861
2862 if (dce) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002863 diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
2864 zero = (dce_z);
Joonwoo Parka8890262012-10-15 12:04:27 -07002865 } else {
2866 diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
2867 zero = (mbhc->mbhc_data.sta_z);
2868 }
2869 in = (u32) diff * vin_mv;
2870
2871 value = (u16) (in / mb_mv) + zero;
2872 return value;
2873}
2874
2875static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
2876{
2877 struct snd_soc_codec *codec;
Joonwoo Park73375212013-05-07 12:42:44 -07002878 s16 adj_v_hs_max;
2879 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 -07002880 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
2881 struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
2882 u16 *btn_high;
2883 int i;
2884
2885 pr_debug("%s: enter\n", __func__);
2886 codec = mbhc->codec;
2887 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
2888 plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
2889
Joonwoo Park73375212013-05-07 12:42:44 -07002890 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002891 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002892 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002893 wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002894
2895 mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
2896 mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
2897
2898 if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
Joonwoo Park73375212013-05-07 12:42:44 -07002899 adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
2900 true);
2901 mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002902 wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
Joonwoo Park73375212013-05-07 12:42:44 -07002903 mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002904 wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002905 mbhc->mbhc_data.v_inval_ins_low =
2906 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
2907 false);
2908 mbhc->mbhc_data.v_inval_ins_high =
2909 scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
2910 false);
2911 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002912 mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
2913 WCD9XXX_V_CS_HS_MAX,
2914 true);
2915 pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
2916 mbhc->mbhc_data.v_cs_ins_h);
Joonwoo Parka8890262012-10-15 12:04:27 -07002917
2918 btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
2919 MBHC_BTN_DET_V_BTN_HIGH);
2920 for (i = 0; i < btn_det->num_btn; i++)
2921 btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
2922
Joonwoo Park73375212013-05-07 12:42:44 -07002923 btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
2924 btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
2925 btn_mv_sta[MBHC_V_IDX_VDDIO] =
2926 scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
2927 btn_mv_dce[MBHC_V_IDX_VDDIO] =
2928 scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
Joonwoo Parka8890262012-10-15 12:04:27 -07002929
Joonwoo Park73375212013-05-07 12:42:44 -07002930 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002931 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
2932 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002933 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002934 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
2935 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002936 mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002937 wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
2938 false);
Joonwoo Park73375212013-05-07 12:42:44 -07002939 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002940 wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
2941 false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002942
Joonwoo Park73375212013-05-07 12:42:44 -07002943 mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
2944 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
2945 mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
2946 mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
Joonwoo Parka8890262012-10-15 12:04:27 -07002947
Joonwoo Parka8890262012-10-15 12:04:27 -07002948 mbhc->mbhc_data.v_brl = BUTTON_MIN;
2949
2950 mbhc->mbhc_data.v_no_mic =
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002951 wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07002952 pr_debug("%s: leave\n", __func__);
2953}
2954
2955static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
2956{
2957 /*
2958 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
2959 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
2960 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
2961 */
2962 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
2963}
2964
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002965/*
2966 * Mic Bias Enable Decision
2967 * Return true if high_hph_cnt is a power of 2 (!= 2)
2968 * otherwise return false
2969 */
2970static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
2971{
2972 return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
2973}
2974
Joonwoo Parka8890262012-10-15 12:04:27 -07002975static void wcd9xxx_correct_swch_plug(struct work_struct *work)
2976{
2977 struct wcd9xxx_mbhc *mbhc;
2978 struct snd_soc_codec *codec;
Joonwoo Park80a01172012-10-15 16:05:23 -07002979 enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
Joonwoo Parka8890262012-10-15 12:04:27 -07002980 unsigned long timeout;
2981 int retry = 0, pt_gnd_mic_swap_cnt = 0;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002982 int highhph_cnt = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07002983 bool correction = false;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07002984 bool current_source_enable;
2985 bool wrk_complete = true, highhph = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07002986
2987 pr_debug("%s: enter\n", __func__);
2988
2989 mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
2990 codec = mbhc->codec;
Phani Kumar Uppalapatic630cf62013-10-31 19:24:28 -07002991
2992 current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
2993 (1 << MBHC_CS_ENABLE_POLLING)) != 0) &&
2994 (!(snd_soc_read(codec,
2995 mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
Joonwoo Parka8890262012-10-15 12:04:27 -07002996
2997 wcd9xxx_onoff_ext_mclk(mbhc, true);
2998
2999 /*
3000 * Keep override on during entire plug type correction work.
3001 *
3002 * This is okay under the assumption that any switch irqs which use
3003 * MBHC block cancel and sync this work so override is off again
3004 * prior to switch interrupt handler's MBHC block usage.
3005 * Also while this correction work is running, we can guarantee
3006 * DAPM doesn't use any MBHC block as this work only runs with
3007 * headphone detection.
3008 */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003009 if (current_source_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05303010 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
3011 true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003012 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003013 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07003014
3015 timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
3016 while (!time_after(jiffies, timeout)) {
3017 ++retry;
3018 rmb();
3019 if (mbhc->hs_detect_work_stop) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003020 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003021 pr_debug("%s: stop requested\n", __func__);
3022 break;
3023 }
3024
3025 msleep(HS_DETECT_PLUG_INERVAL_MS);
3026 if (wcd9xxx_swch_level_remove(mbhc)) {
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003027 wrk_complete = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003028 pr_debug("%s: Switch level is low\n", __func__);
3029 break;
3030 }
3031
3032 /* can race with removal interrupt */
3033 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003034 if (current_source_enable)
3035 plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
3036 highhph);
3037 else
3038 plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07003039 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3040
Joonwoo Park80a01172012-10-15 16:05:23 -07003041 pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
3042 __func__, retry, mbhc->current_plug, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003043
3044 highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
3045 (highhph_cnt + 1) :
3046 0;
3047 highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
Joonwoo Parka8890262012-10-15 12:04:27 -07003048 if (plug_type == PLUG_TYPE_INVALID) {
3049 pr_debug("Invalid plug in attempt # %d\n", retry);
Joonwoo Park80a01172012-10-15 16:05:23 -07003050 if (!mbhc->mbhc_cfg->detect_extn_cable &&
3051 retry == NUM_ATTEMPTS_TO_REPORT &&
Joonwoo Parka8890262012-10-15 12:04:27 -07003052 mbhc->current_plug == PLUG_TYPE_NONE) {
3053 wcd9xxx_report_plug(mbhc, 1,
3054 SND_JACK_HEADPHONE);
3055 }
3056 } else if (plug_type == PLUG_TYPE_HEADPHONE) {
3057 pr_debug("Good headphone detected, continue polling\n");
Joonwoo Park80a01172012-10-15 16:05:23 -07003058 if (mbhc->mbhc_cfg->detect_extn_cable) {
3059 if (mbhc->current_plug != plug_type)
3060 wcd9xxx_report_plug(mbhc, 1,
3061 SND_JACK_HEADPHONE);
3062 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003063 wcd9xxx_report_plug(mbhc, 1,
3064 SND_JACK_HEADPHONE);
Joonwoo Park80a01172012-10-15 16:05:23 -07003065 }
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07003066 } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
3067 pr_debug("%s: High HPH detected, continue polling\n",
3068 __func__);
Simmi Pateriya0ba392d2013-11-01 01:27:01 +05303069 if (mbhc->mbhc_cfg->detect_extn_cable) {
3070 if (mbhc->current_plug != plug_type)
3071 wcd9xxx_report_plug(mbhc, 1,
3072 SND_JACK_LINEOUT);
3073 } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
3074 wcd9xxx_report_plug(mbhc, 1,
3075 SND_JACK_HEADPHONE);
3076 }
Joonwoo Parka8890262012-10-15 12:04:27 -07003077 } else {
3078 if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
3079 pt_gnd_mic_swap_cnt++;
3080 if (pt_gnd_mic_swap_cnt <
3081 GND_MIC_SWAP_THRESHOLD)
3082 continue;
3083 else if (pt_gnd_mic_swap_cnt >
3084 GND_MIC_SWAP_THRESHOLD) {
3085 /*
3086 * This is due to GND/MIC switch didn't
3087 * work, Report unsupported plug
3088 */
3089 } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
3090 /*
3091 * if switch is toggled, check again,
3092 * otherwise report unsupported plug
3093 */
3094 if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
3095 continue;
3096 }
3097 } else
3098 pt_gnd_mic_swap_cnt = 0;
3099
3100 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003101 /* Turn off override/current source */
3102 if (current_source_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05303103 wcd9xxx_turn_onoff_current_source(mbhc,
3104 &mbhc->mbhc_bias_regs,
3105 false, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003106 else
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003107 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07003108 /*
3109 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
3110 */
3111 wcd9xxx_find_plug_and_report(mbhc, plug_type);
3112 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3113 pr_debug("Attempt %d found correct plug %d\n", retry,
3114 plug_type);
3115 correction = true;
3116 break;
3117 }
3118 }
3119
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003120 highhph = false;
3121 if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07003122 pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
3123 __func__);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003124 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07003125 wcd9xxx_find_plug_and_report(mbhc, plug_type);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003126 highhph = true;
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003127 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
Phani Kumar Uppalapatida7c7ab2013-04-10 16:38:19 -07003128 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003129
3130 if (!correction && current_source_enable)
Simmi Pateriyaccfde1a2013-11-21 07:43:50 +05303131 wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
3132 false, highhph);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003133 else if (!correction)
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003134 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07003135
3136 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Park80a01172012-10-15 16:05:23 -07003137
3138 if (mbhc->mbhc_cfg->detect_extn_cable) {
3139 WCD9XXX_BCL_LOCK(mbhc->resmgr);
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003140 if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
3141 wrk_complete) ||
Joonwoo Park80a01172012-10-15 16:05:23 -07003142 mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
3143 mbhc->current_plug == PLUG_TYPE_INVALID ||
Phani Kumar Uppalapati6396af72013-06-14 16:37:17 -07003144 (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
Joonwoo Park80a01172012-10-15 16:05:23 -07003145 /* Enable removal detection */
3146 wcd9xxx_cleanup_hs_polling(mbhc);
3147 wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
3148 }
3149 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3150 }
3151 pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
Joonwoo Parka8890262012-10-15 12:04:27 -07003152 /* unlock sleep */
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003153 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003154}
3155
3156static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
3157{
3158 bool insert;
3159 bool is_removed = false;
3160 struct snd_soc_codec *codec = mbhc->codec;
3161
3162 pr_debug("%s: enter\n", __func__);
3163
3164 mbhc->in_swch_irq_handler = true;
3165 /* Wait here for debounce time */
3166 usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
3167
3168 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3169
3170 /* cancel pending button press */
3171 if (wcd9xxx_cancel_btn_work(mbhc))
3172 pr_debug("%s: button press is canceled\n", __func__);
3173
3174 insert = !wcd9xxx_swch_level_remove(mbhc);
3175 pr_debug("%s: Current plug type %d, insert %d\n", __func__,
3176 mbhc->current_plug, insert);
3177 if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
3178 mbhc->lpi_enabled = false;
3179 wmb();
Yeleswarapu, Nagaradheshf2cd6662013-12-16 22:52:24 +05303180 /* cancel detect plug */
3181 wcd9xxx_cancel_hs_detect_plug(mbhc,
3182 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003183
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07003184 if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
3185 !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
3186 (1 << 1)))
3187 goto exit;
Joonwoo Parka8890262012-10-15 12:04:27 -07003188
3189 /* Disable Mic Bias pull down and HPH Switch to GND */
3190 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
3191 0x00);
3192 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
3193 wcd9xxx_mbhc_detect_plug_type(mbhc);
3194 } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
3195 mbhc->lpi_enabled = false;
3196 wmb();
Yeleswarapu, Nagaradheshf2cd6662013-12-16 22:52:24 +05303197 /* cancel detect plug */
3198 wcd9xxx_cancel_hs_detect_plug(mbhc,
3199 &mbhc->correct_plug_swch);
Joonwoo Parka8890262012-10-15 12:04:27 -07003200
Joonwoo Parka8890262012-10-15 12:04:27 -07003201 if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
3202 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
3203 is_removed = true;
3204 } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
3205 wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
3206 is_removed = true;
3207 } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
3208 wcd9xxx_pause_hs_polling(mbhc);
Yeleswarapu, Nagaradhesh3cd45212013-09-30 21:34:27 +05303209 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07003210 wcd9xxx_cleanup_hs_polling(mbhc);
3211 wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
3212 is_removed = true;
Joonwoo Park80a01172012-10-15 16:05:23 -07003213 } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
3214 wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
3215 is_removed = true;
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003216 } else if (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE) {
Simmi Pateriyaf0553ab2013-12-03 11:25:40 +05303217 wcd9xxx_pause_hs_polling(mbhc);
3218 wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
3219 wcd9xxx_cleanup_hs_polling(mbhc);
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003220 wcd9xxx_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
3221 is_removed = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003222 }
3223
3224 if (is_removed) {
Phani Kumar Uppalapaticfbfa2f2013-10-22 15:18:51 -07003225 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3226 0x00);
3227 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3228 0x02, 0x00);
3229
Joonwoo Parka8890262012-10-15 12:04:27 -07003230 /* Enable Mic Bias pull down and HPH Switch to GND */
3231 snd_soc_update_bits(codec,
3232 mbhc->mbhc_bias_regs.ctl_reg, 0x01,
3233 0x01);
3234 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
3235 0x01);
3236 /* Make sure mic trigger is turned off */
3237 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
3238 0x01, 0x01);
3239 snd_soc_update_bits(codec,
3240 mbhc->mbhc_bias_regs.mbhc_reg,
3241 0x90, 0x00);
3242 /* Reset MBHC State Machine */
3243 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
3244 0x08, 0x08);
3245 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
3246 0x08, 0x00);
3247 /* Turn off override */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003248 wcd9xxx_turn_onoff_override(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07003249 }
3250 }
Phani Kumar Uppalapaticf2e1242013-10-08 12:27:18 -07003251exit:
Joonwoo Parka8890262012-10-15 12:04:27 -07003252 mbhc->in_swch_irq_handler = false;
3253 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3254 pr_debug("%s: leave\n", __func__);
3255}
3256
3257static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
3258{
3259 int r = IRQ_HANDLED;
3260 struct wcd9xxx_mbhc *mbhc = data;
3261
3262 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003263 if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003264 pr_warn("%s: failed to hold suspend\n", __func__);
3265 r = IRQ_NONE;
3266 } else {
3267 /* Call handler */
3268 wcd9xxx_swch_irq_handler(mbhc);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003269 wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003270 }
3271
3272 pr_debug("%s: leave %d\n", __func__, r);
3273 return r;
3274}
3275
Joonwoo Park218e73f2013-08-21 16:22:18 -07003276static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
Joonwoo Parka8890262012-10-15 12:04:27 -07003277{
Joonwoo Park73375212013-05-07 12:42:44 -07003278 s16 mb_v;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003279 int i = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07003280 int r = 0;
Joonwoo Park73375212013-05-07 12:42:44 -07003281 const s16 v_ins_hu =
3282 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
3283 const s16 v_ins_h =
3284 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
3285 const s16 v_b1_hu =
3286 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
3287 const s16 v_b1_h =
3288 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003289 const unsigned long timeout =
3290 jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003291
Joonwoo Park218e73f2013-08-21 16:22:18 -07003292 while (time_before(jiffies, timeout)) {
3293 /*
3294 * This function needs to run measurements just few times during
3295 * release debounce time. Make 1ms interval to avoid
3296 * unnecessary excessive measurements.
3297 */
3298 usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Joonwoo Parka8890262012-10-15 12:04:27 -07003299 if (i == 0) {
3300 mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
3301 pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
3302 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003303 if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003304 r = 1;
3305 break;
3306 }
3307 } else {
3308 mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
3309 pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
3310 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
Joonwoo Park73375212013-05-07 12:42:44 -07003311 if (mb_v < v_b1_h || mb_v > v_ins_h) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003312 r = 1;
3313 break;
3314 }
3315 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003316 i++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003317 }
3318
3319 return r;
3320}
3321
3322/* called under codec_resource_lock acquisition */
3323static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
3324 const s32 micmv)
3325{
3326 s16 *v_btn_low, *v_btn_high;
3327 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3328 int i, btn = -1;
3329
3330 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3331 v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3332 MBHC_BTN_DET_V_BTN_LOW);
3333 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3334 MBHC_BTN_DET_V_BTN_HIGH);
3335
3336 for (i = 0; i < btn_det->num_btn; i++) {
3337 if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
3338 btn = i;
3339 break;
3340 }
3341 }
3342
3343 if (btn == -1)
3344 pr_debug("%s: couldn't find button number for mic mv %d\n",
3345 __func__, micmv);
3346
3347 return btn;
3348}
3349
3350static int wcd9xxx_get_button_mask(const int btn)
3351{
3352 int mask = 0;
3353 switch (btn) {
3354 case 0:
3355 mask = SND_JACK_BTN_0;
3356 break;
3357 case 1:
3358 mask = SND_JACK_BTN_1;
3359 break;
3360 case 2:
3361 mask = SND_JACK_BTN_2;
3362 break;
3363 case 3:
3364 mask = SND_JACK_BTN_3;
3365 break;
3366 case 4:
3367 mask = SND_JACK_BTN_4;
3368 break;
3369 case 5:
3370 mask = SND_JACK_BTN_5;
3371 break;
3372 case 6:
3373 mask = SND_JACK_BTN_6;
3374 break;
3375 case 7:
3376 mask = SND_JACK_BTN_7;
3377 break;
3378 }
3379 return mask;
3380}
3381
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003382static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
3383 struct mbhc_micbias_regs *micb_regs,
3384 bool norel_detection)
Joonwoo Park520a0f92013-05-14 19:39:58 -07003385{
3386 s16 reg0, reg1;
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003387 int change;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003388 struct snd_soc_codec *codec = mbhc->codec;
3389
3390 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
3391 /* Pull down micbias to ground and disconnect vddio switch */
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003392 reg0 = snd_soc_read(codec, micb_regs->ctl_reg);
3393 snd_soc_update_bits(codec, micb_regs->ctl_reg, 0x81, 0x1);
3394 reg1 = snd_soc_read(codec, micb_regs->mbhc_reg);
3395 snd_soc_update_bits(codec, micb_regs->mbhc_reg, 1 << 7, 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003396
3397 /* Disconnect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003398 change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3399 1 << 0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003400 usleep_range(1000, 1000 + 1000);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003401 if (sta_z) {
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003402 *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, norel_detection);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003403 pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
3404 }
3405 if (dce_z) {
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003406 *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, norel_detection);
Phani Kumar Uppalapatibb0ad6e2013-10-02 13:08:56 -07003407 pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
3408 }
Joonwoo Park218e73f2013-08-21 16:22:18 -07003409
Joonwoo Park520a0f92013-05-14 19:39:58 -07003410 /* Connect override from micbias */
Joonwoo Park35d4cde2013-08-14 15:11:14 -07003411 if (change)
3412 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
3413 1 << 4);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003414 /* Disable pull down micbias to ground */
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003415 snd_soc_write(codec, micb_regs->mbhc_reg, reg1);
3416 snd_soc_write(codec, micb_regs->ctl_reg, reg0);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003417}
3418
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003419/*
3420 * This function recalibrates dce_z and sta_z parameters.
3421 * No release detection will be false when this function is
3422 * used.
3423 */
Joonwoo Park218e73f2013-08-21 16:22:18 -07003424void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
3425{
3426 const u16 sta_z = mbhc->mbhc_data.sta_z;
3427 const u16 dce_z = mbhc->mbhc_data.dce_z;
3428
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07003429 wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z,
3430 &mbhc->mbhc_bias_regs, false);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003431 pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
3432 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
3433 mbhc->mbhc_data.sta_z & 0xFFFF,
3434 mbhc->mbhc_data.dce_z & 0xFFFF);
3435
3436 wcd9xxx_mbhc_calc_thres(mbhc);
3437 wcd9xxx_calibrate_hs_polling(mbhc);
3438}
3439
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003440/*
3441 * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
3442 * to ceilmv + buffer
3443 */
Phani Kumar Uppalapatibe77d16292014-04-01 08:54:05 -07003444static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv,
3445 bool vddio)
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003446{
3447 u16 v_brh, v_b1_hu;
3448 int mv;
3449 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3450 void *calibration = mbhc->mbhc_cfg->calibration;
3451 struct snd_soc_codec *codec = mbhc->codec;
3452
3453 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3454 mv = ceilmv + btn_det->v_btn_press_delta_cic;
Phani Kumar Uppalapatibe77d16292014-04-01 08:54:05 -07003455 if (vddio)
3456 mv = scale_v_micb_vddio(mbhc, mv, true);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003457 pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
3458
Yeleswarapu, Nagaradhesh753f20d2013-11-25 18:07:13 +05303459 if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
3460 /*
3461 * update LSB first so mbhc hardware block
3462 * doesn't see too low value.
3463 */
3464 v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
3465 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
3466 0xFF);
3467 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
3468 (v_b1_hu >> 8) & 0xFF);
3469 v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
3470 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
3471 0xFF);
3472 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
3473 (v_brh >> 8) & 0xFF);
3474 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003475 return 0;
3476}
3477
Joonwoo Parka8890262012-10-15 12:04:27 -07003478irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
3479{
3480 int i, mask;
Joonwoo Parka8890262012-10-15 12:04:27 -07003481 bool vddio;
3482 u8 mbhc_status;
Joonwoo Park520a0f92013-05-14 19:39:58 -07003483 s16 dce_z, sta_z;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003484 s32 stamv, stamv_s;
3485 s16 *v_btn_high;
3486 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
Joonwoo Parka8890262012-10-15 12:04:27 -07003487 int btn = -1, meas = 0;
3488 struct wcd9xxx_mbhc *mbhc = data;
3489 const struct wcd9xxx_mbhc_btn_detect_cfg *d =
3490 WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
3491 short btnmeas[d->n_btn_meas + 1];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003492 short dce[d->n_btn_meas + 1], sta;
3493 s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
Joonwoo Parka8890262012-10-15 12:04:27 -07003494 struct snd_soc_codec *codec = mbhc->codec;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003495 struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07003496 int n_btn_meas = d->n_btn_meas;
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003497 void *calibration = mbhc->mbhc_cfg->calibration;
Joonwoo Parka8890262012-10-15 12:04:27 -07003498
3499 pr_debug("%s: enter\n", __func__);
3500
3501 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3502 mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
3503
3504 if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
3505 pr_debug("%s: mbhc is being recovered, skip button press\n",
3506 __func__);
3507 goto done;
3508 }
3509
3510 mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
3511
3512 if (!mbhc->polling_active) {
3513 pr_warn("%s: mbhc polling is not active, skip button press\n",
3514 __func__);
3515 goto done;
3516 }
3517
Joonwoo Parka8890262012-10-15 12:04:27 -07003518 /* If switch nterrupt already kicked in, ignore button press */
3519 if (mbhc->in_swch_irq_handler) {
3520 pr_debug("%s: Swtich level changed, ignore button press\n",
3521 __func__);
3522 btn = -1;
3523 goto done;
3524 }
3525
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303526 /*
3527 * setup internal micbias if codec uses internal micbias for
3528 * headset detection
3529 */
Simmi Pateriyad29192f2013-11-14 11:22:21 +05303530 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05303531 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
3532 mbhc->mbhc_cb->setup_int_rbias(codec, true);
3533 else
3534 pr_err("%s: internal bias requested but codec did not provide callback\n",
3535 __func__);
3536 }
3537
3538
Joonwoo Parka8890262012-10-15 12:04:27 -07003539 /* Measure scaled HW DCE */
3540 vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
3541 mbhc->mbhc_micbias_switched);
Joonwoo Parka8890262012-10-15 12:04:27 -07003542
Joonwoo Park218e73f2013-08-21 16:22:18 -07003543 dce_z = mbhc->mbhc_data.dce_z;
3544 sta_z = mbhc->mbhc_data.sta_z;
3545
Joonwoo Parka8890262012-10-15 12:04:27 -07003546 /* Measure scaled HW STA */
Joonwoo Park520a0f92013-05-14 19:39:58 -07003547 dce[0] = wcd9xxx_read_dce_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003548 sta = wcd9xxx_read_sta_result(codec);
Joonwoo Parka8890262012-10-15 12:04:27 -07003549 if (mbhc_status != STATUS_REL_DETECTION) {
3550 if (mbhc->mbhc_last_resume &&
3551 !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
3552 pr_debug("%s: Button is released after resume\n",
3553 __func__);
3554 n_btn_meas = 0;
3555 } else {
3556 pr_debug("%s: Button is released without resume",
3557 __func__);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003558 if (mbhc->update_z) {
3559 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003560 dce_z = mbhc->mbhc_data.dce_z;
3561 sta_z = mbhc->mbhc_data.sta_z;
3562 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003563 }
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003564 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3565 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003566 if (vddio)
3567 stamv_s = scale_v_micb_vddio(mbhc, stamv,
3568 false);
3569 else
3570 stamv_s = stamv;
3571 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003572 dce_z, mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003573 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
3574 false) : mv[0];
3575 btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003576 if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
3577 btn = -1;
3578 goto done;
3579 }
3580 }
3581
Joonwoo Park520a0f92013-05-14 19:39:58 -07003582 for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
3583 meas++)
3584 dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
3585
Joonwoo Park218e73f2013-08-21 16:22:18 -07003586 if (mbhc->update_z) {
3587 wcd9xxx_update_z(mbhc);
Phani Kumar Uppalapatie422bd92013-11-22 11:29:09 -08003588 dce_z = mbhc->mbhc_data.dce_z;
3589 sta_z = mbhc->mbhc_data.sta_z;
3590 mbhc->update_z = true;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003591 }
Joonwoo Park520a0f92013-05-14 19:39:58 -07003592
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003593 stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
3594 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003595 if (vddio)
3596 stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
3597 else
3598 stamv_s = stamv;
Joonwoo Parka8890262012-10-15 12:04:27 -07003599 pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003600 sta & 0xFFFF, stamv, stamv_s);
Joonwoo Parka8890262012-10-15 12:04:27 -07003601
3602 /* determine pressed button */
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003603 mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
3604 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003605 mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
3606 btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003607 pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
Joonwoo Park520a0f92013-05-14 19:39:58 -07003608 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003609 if (n_btn_meas == 0)
3610 btn = btnmeas[0];
Joonwoo Park520a0f92013-05-14 19:39:58 -07003611 for (meas = 1; (n_btn_meas && d->n_btn_meas &&
3612 (meas < (d->n_btn_meas + 1))); meas++) {
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003613 mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
3614 mbhc->mbhc_data.micb_mv);
Joonwoo Park520a0f92013-05-14 19:39:58 -07003615 mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
3616 mv[meas];
3617 btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003618 pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
Joonwoo Park520a0f92013-05-14 19:39:58 -07003619 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
3620 mv_s[meas], btnmeas[meas]);
Joonwoo Parka8890262012-10-15 12:04:27 -07003621 /*
3622 * if large enough measurements are collected,
3623 * start to check if last all n_btn_con measurements were
3624 * in same button low/high range
3625 */
3626 if (meas + 1 >= d->n_btn_con) {
3627 for (i = 0; i < d->n_btn_con; i++)
3628 if ((btnmeas[meas] < 0) ||
3629 (btnmeas[meas] != btnmeas[meas - i]))
3630 break;
3631 if (i == d->n_btn_con) {
3632 /* button pressed */
3633 btn = btnmeas[meas];
3634 break;
3635 } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
3636 /*
3637 * if left measurements are less than n_btn_con,
3638 * it's impossible to find button number
3639 */
3640 break;
3641 }
3642 }
3643 }
3644
3645 if (btn >= 0) {
3646 if (mbhc->in_swch_irq_handler) {
3647 pr_debug(
3648 "%s: Switch irq triggered, ignore button press\n",
3649 __func__);
3650 goto done;
3651 }
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003652 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3653 v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
3654 MBHC_BTN_DET_V_BTN_HIGH);
3655 WARN_ON(btn >= btn_det->num_btn);
3656 /* reprogram release threshold to catch voltage ramp up early */
Phani Kumar Uppalapatibe77d16292014-04-01 08:54:05 -07003657 wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn], vddio);
Joonwoo Parkc6a4e042013-07-17 17:48:04 -07003658
Joonwoo Parka8890262012-10-15 12:04:27 -07003659 mask = wcd9xxx_get_button_mask(btn);
3660 mbhc->buttons_pressed |= mask;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003661 wcd9xxx_lock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003662 if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
3663 msecs_to_jiffies(400)) == 0) {
3664 WARN(1, "Button pressed twice without release event\n");
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003665 wcd9xxx_unlock_sleep(core_res);
Joonwoo Parka8890262012-10-15 12:04:27 -07003666 }
3667 } else {
3668 pr_debug("%s: bogus button press, too short press?\n",
3669 __func__);
3670 }
3671
3672 done:
3673 pr_debug("%s: leave\n", __func__);
3674 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3675 return IRQ_HANDLED;
3676}
3677
3678static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
3679{
3680 int ret;
Joonwoo Park218e73f2013-08-21 16:22:18 -07003681 bool waitdebounce = true;
Joonwoo Parka8890262012-10-15 12:04:27 -07003682 struct wcd9xxx_mbhc *mbhc = data;
3683
3684 pr_debug("%s: enter\n", __func__);
3685 WCD9XXX_BCL_LOCK(mbhc->resmgr);
3686 mbhc->mbhc_state = MBHC_STATE_RELEASE;
3687
Joonwoo Parka8890262012-10-15 12:04:27 -07003688 if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
3689 ret = wcd9xxx_cancel_btn_work(mbhc);
3690 if (ret == 0) {
3691 pr_debug("%s: Reporting long button release event\n",
3692 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003693 wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
Joonwoo Parka8890262012-10-15 12:04:27 -07003694 mbhc->buttons_pressed);
3695 } else {
Joonwoo Park218e73f2013-08-21 16:22:18 -07003696 if (wcd9xxx_is_false_press(mbhc)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003697 pr_debug("%s: Fake button press interrupt\n",
3698 __func__);
3699 } else {
3700 if (mbhc->in_swch_irq_handler) {
3701 pr_debug("%s: Switch irq kicked in, ignore\n",
3702 __func__);
3703 } else {
3704 pr_debug("%s: Reporting btn press\n",
3705 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003706 wcd9xxx_jack_report(mbhc,
3707 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003708 mbhc->buttons_pressed,
3709 mbhc->buttons_pressed);
3710 pr_debug("%s: Reporting btn release\n",
3711 __func__);
Joonwoo Park3699ca32013-02-08 12:06:15 -08003712 wcd9xxx_jack_report(mbhc,
3713 &mbhc->button_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003714 0, mbhc->buttons_pressed);
Joonwoo Park218e73f2013-08-21 16:22:18 -07003715 waitdebounce = false;
Joonwoo Parka8890262012-10-15 12:04:27 -07003716 }
3717 }
3718 }
3719
3720 mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
3721 }
3722
3723 wcd9xxx_calibrate_hs_polling(mbhc);
3724
Joonwoo Park218e73f2013-08-21 16:22:18 -07003725 if (waitdebounce)
3726 msleep(SWCH_REL_DEBOUNCE_TIME_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003727 wcd9xxx_start_hs_polling(mbhc);
3728
3729 pr_debug("%s: leave\n", __func__);
3730 WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
3731 return IRQ_HANDLED;
3732}
3733
3734static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
3735{
3736 struct wcd9xxx_mbhc *mbhc = data;
3737 struct snd_soc_codec *codec;
3738
3739 pr_info("%s: received HPHL OCP irq\n", __func__);
3740
3741 if (mbhc) {
3742 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003743 if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
3744 (!mbhc->hphrocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003745 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003746 mbhc->hphlocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003747 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3748 0x10, 0x00);
3749 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
3750 0x10, 0x10);
3751 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003752 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003753 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003754 mbhc->hph_status |= SND_JACK_OC_HPHL;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003755 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003756 mbhc->hph_status,
3757 WCD9XXX_JACK_MASK);
3758 }
3759 } else {
3760 pr_err("%s: Bad wcd9xxx private data\n", __func__);
3761 }
3762
3763 return IRQ_HANDLED;
3764}
3765
3766static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
3767{
3768 struct wcd9xxx_mbhc *mbhc = data;
3769 struct snd_soc_codec *codec;
3770
3771 pr_info("%s: received HPHR OCP irq\n", __func__);
3772 codec = mbhc->codec;
Patrick Lai56fea882013-03-02 09:11:20 -08003773 if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
3774 (!mbhc->hphlocp_cnt)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07003775 pr_info("%s: retry\n", __func__);
Patrick Lai56fea882013-03-02 09:11:20 -08003776 mbhc->hphrocp_cnt++;
Joonwoo Parka8890262012-10-15 12:04:27 -07003777 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3778 0x00);
3779 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
3780 0x10);
3781 } else {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07003782 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003783 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07003784 mbhc->hph_status |= SND_JACK_OC_HPHR;
Joonwoo Park3699ca32013-02-08 12:06:15 -08003785 wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
Joonwoo Parka8890262012-10-15 12:04:27 -07003786 mbhc->hph_status, WCD9XXX_JACK_MASK);
3787 }
3788
3789 return IRQ_HANDLED;
3790}
3791
3792static int wcd9xxx_acdb_mclk_index(const int rate)
3793{
3794 if (rate == MCLK_RATE_12288KHZ)
3795 return 0;
3796 else if (rate == MCLK_RATE_9600KHZ)
3797 return 1;
3798 else {
3799 BUG_ON(1);
3800 return -EINVAL;
3801 }
3802}
3803
3804static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
3805{
3806 u32 dce_wait, sta_wait;
3807 u8 ncic, nmeas, navg;
3808 void *calibration;
3809 u8 *n_cic, *n_ready;
3810 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
3811 u8 npoll = 4, nbounce_wait = 30;
3812 struct snd_soc_codec *codec = mbhc->codec;
3813 int idx = wcd9xxx_acdb_mclk_index(rate);
3814 int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
3815
3816 pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
3817 rate);
3818 calibration = mbhc->mbhc_cfg->calibration;
3819
3820 /*
3821 * First compute the DCE / STA wait times depending on tunable
3822 * parameters. The value is computed in microseconds
3823 */
3824 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
3825 n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
3826 n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
3827 nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
3828 navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
3829
3830 /* ncic stays with the same what we had during calibration */
3831 ncic = n_cic[idxmclk];
3832 dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
3833 sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
3834 mbhc->mbhc_data.t_dce = dce_wait;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07003835 /* give extra margin to sta for safety */
3836 mbhc->mbhc_data.t_sta = sta_wait + 250;
Joonwoo Parka8890262012-10-15 12:04:27 -07003837 mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
3838 n_ready[idx]) + 10;
3839
3840 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
3841 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
3842
3843 if (rate == MCLK_RATE_12288KHZ) {
3844 npoll = 4;
3845 nbounce_wait = 30;
3846 } else if (rate == MCLK_RATE_9600KHZ) {
3847 npoll = 3;
3848 nbounce_wait = 23;
3849 }
3850
3851 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
3852 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
3853 pr_debug("%s: leave\n", __func__);
3854}
3855
3856static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
3857{
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07003858 u8 cfilt_mode;
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003859 u16 reg0, reg1, reg2;
Joonwoo Parka8890262012-10-15 12:04:27 -07003860 struct snd_soc_codec *codec = mbhc->codec;
3861
3862 pr_debug("%s: enter\n", __func__);
Bhalchandra Gajare16748932013-10-01 18:16:05 -07003863 wcd9xxx_disable_irq(mbhc->resmgr->core_res,
3864 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07003865 wcd9xxx_turn_onoff_rel_detection(codec, false);
3866
3867 /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
3868 WARN_ON(!mbhc->mbhc_data.t_dce);
3869 WARN_ON(!mbhc->mbhc_data.t_sta);
3870
3871 /*
3872 * LDOH and CFILT are already configured during pdata handling.
3873 * Only need to make sure CFILT and bandgap are in Fast mode.
3874 * Need to restore defaults once calculation is done.
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003875 *
3876 * In case when Micbias is powered by external source, request
3877 * turn on the external voltage source for Calibration.
Joonwoo Parka8890262012-10-15 12:04:27 -07003878 */
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003879 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
Phani Kumar Uppalapatiaa98c282014-01-28 15:32:22 -08003880 mbhc->mbhc_cb->enable_mb_source(codec, true, false);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07003881
Joonwoo Parka8890262012-10-15 12:04:27 -07003882 cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303883 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
3884 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
3885 else
3886 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
3887 0x40, 0x00);
3888
Joonwoo Parka8890262012-10-15 12:04:27 -07003889 /*
3890 * Micbias, CFILT, LDOH, MBHC MUX mode settings
3891 * to perform ADC calibration
3892 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05303893 if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
3894 mbhc->mbhc_cb->select_cfilt(codec, mbhc);
3895 else
Joonwoo Parka8890262012-10-15 12:04:27 -07003896 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
3897 mbhc->mbhc_cfg->micbias << 5);
3898 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
3899 snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
3900 snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303901 if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
3902 mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
3903 else
3904 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
3905 0x04, 0x04);
3906
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003907 /* Pull down micbias to ground */
3908 reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
3909 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
3910 /* Disconnect override from micbias */
3911 reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
3912 snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
3913 /* Connect the MUX to micbias */
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303914 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303915 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3916 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3917 else
3918 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3919 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003920 /*
3921 * Hardware that has external cap can delay mic bias ramping down up
3922 * to 50ms.
3923 */
3924 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003925 /* DCE measurement for 0 voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003926 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
Joonwoo Parka8890262012-10-15 12:04:27 -07003927 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003928 mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003929
3930 /* compute dce_z for current source */
3931 reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
3932 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
3933 WCD9XXX_MBHC_NSC_CS << 3);
3934
3935 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3936 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3937 mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
3938 false);
3939 pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
3940 mbhc->mbhc_data.dce_nsc_cs_z);
3941
3942 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
3943
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003944 /* STA measurement for 0 voltage */
3945 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3946 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
3947 mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Phani Kumar Uppalapati3559a7e2013-06-24 13:52:31 -07003948
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003949 /* Restore registers */
3950 snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
3951 snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
Joonwoo Parka8890262012-10-15 12:04:27 -07003952
3953 /* DCE measurment for MB voltage */
3954 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3955 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303956 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303957 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3958 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3959 else
3960 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3961 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003962 /*
3963 * Hardware that has external cap can delay mic bias ramping down up
3964 * to 50ms.
3965 */
3966 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003967 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
3968 usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
3969 mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
3970
Joonwoo Park7902f4c2013-02-20 15:21:25 -08003971 /* STA Measurement for MB Voltage */
Joonwoo Parka8890262012-10-15 12:04:27 -07003972 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
3973 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3974 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303975 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303976 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3977 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3978 else
3979 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3980 0x80, 0x80);
Joonwoo Parkaec97c72013-05-14 16:51:02 -07003981 /*
3982 * Hardware that has external cap can delay mic bias ramping down up
3983 * to 50ms.
3984 */
3985 msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
Joonwoo Parka8890262012-10-15 12:04:27 -07003986 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
3987 usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
3988 mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
3989
3990 /* Restore default settings. */
3991 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05303992 snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
Simmi Pateriya4b9c24b2013-04-10 06:10:53 +05303993 snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
Simmi Pateriya95466b12013-05-09 20:08:46 +05303994 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
3995 mbhc->mbhc_cb->enable_mux_bias_block(codec);
3996 else
3997 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
3998 0x80, 0x80);
Joonwoo Parka8890262012-10-15 12:04:27 -07003999 usleep_range(100, 100);
4000
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004001 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
Phani Kumar Uppalapatiaa98c282014-01-28 15:32:22 -08004002 mbhc->mbhc_cb->enable_mb_source(codec, false, false);
Bhalchandra Gajaredd01bf32013-09-05 14:00:29 -07004003
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004004 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
4005 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004006 wcd9xxx_turn_onoff_rel_detection(codec, true);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004007
Joonwoo Parka8890262012-10-15 12:04:27 -07004008 pr_debug("%s: leave\n", __func__);
4009}
4010
4011static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
4012{
4013 int n;
4014 u8 *gain;
4015 struct wcd9xxx_mbhc_general_cfg *generic;
4016 struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
4017 struct snd_soc_codec *codec = mbhc->codec;
4018 const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
4019
4020 pr_debug("%s: enter\n", __func__);
4021 generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
4022 btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
4023
4024 for (n = 0; n < 8; n++) {
4025 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
4026 0x07, n);
4027 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
4028 btn_det->c[n]);
4029 }
4030
4031 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
4032 btn_det->nc);
4033
4034 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
4035 generic->mbhc_nsa << 4);
4036
4037 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
4038 btn_det->n_meas);
4039
4040 snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
4041 generic->mbhc_navg);
4042
4043 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
4044
4045 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
4046 btn_det->mbhc_nsc << 3);
4047
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004048 if (mbhc->mbhc_cb &&
4049 mbhc->mbhc_cb->get_cdc_type() !=
4050 WCD9XXX_CDC_TYPE_HELICON) {
4051 if (mbhc->resmgr->reg_addr->micb_4_mbhc)
4052 snd_soc_update_bits(codec,
4053 mbhc->resmgr->reg_addr->micb_4_mbhc,
4054 0x03, MBHC_MICBIAS2);
4055 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004056
4057 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
4058
4059 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
4060
4061 gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
4062 snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
4063 gain[idx] << 3);
Yeleswarapu, Nagaradhesh0564ac32013-11-27 11:44:50 +05304064 snd_soc_update_bits(codec, WCD9XXX_A_MICB_2_MBHC, 0x04, 0x04);
Joonwoo Parka8890262012-10-15 12:04:27 -07004065
4066 pr_debug("%s: leave\n", __func__);
4067}
4068
4069static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
4070{
4071 int ret = 0;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004072 void *core_res = mbhc->resmgr->core_res;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304073
Joonwoo Parka8890262012-10-15 12:04:27 -07004074 if (mbhc->mbhc_cfg->gpio) {
4075 ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
4076 wcd9xxx_mech_plug_detect_irq,
4077 (IRQF_TRIGGER_RISING |
4078 IRQF_TRIGGER_FALLING |
4079 IRQF_DISABLED),
4080 "headset detect", mbhc);
4081 if (ret) {
4082 pr_err("%s: Failed to request gpio irq %d\n", __func__,
4083 mbhc->mbhc_cfg->gpio_irq);
4084 } else {
4085 ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
4086 if (ret)
4087 pr_err("%s: Failed to enable wake up irq %d\n",
4088 __func__, mbhc->mbhc_cfg->gpio_irq);
4089 }
4090 } else if (mbhc->mbhc_cfg->insert_detect) {
4091 /* Enable HPHL_10K_SW */
4092 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
4093 1 << 1, 1 << 1);
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304094
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004095 ret = wcd9xxx_request_irq(core_res,
4096 mbhc->intr_ids->hs_jack_switch,
Joonwoo Parka8890262012-10-15 12:04:27 -07004097 wcd9xxx_mech_plug_detect_irq,
4098 "Jack Detect",
4099 mbhc);
4100 if (ret)
4101 pr_err("%s: Failed to request insert detect irq %d\n",
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004102 __func__, mbhc->intr_ids->hs_jack_switch);
Joonwoo Parka8890262012-10-15 12:04:27 -07004103 }
4104
4105 return ret;
4106}
4107
4108static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
4109{
4110 int ret = 0;
4111 struct snd_soc_codec *codec = mbhc->codec;
4112
4113 pr_debug("%s: enter\n", __func__);
4114
4115 /* Enable MCLK during calibration */
4116 wcd9xxx_onoff_ext_mclk(mbhc, true);
4117 wcd9xxx_mbhc_setup(mbhc);
4118 wcd9xxx_mbhc_cal(mbhc);
4119 wcd9xxx_mbhc_calc_thres(mbhc);
4120 wcd9xxx_onoff_ext_mclk(mbhc, false);
4121 wcd9xxx_calibrate_hs_polling(mbhc);
4122
4123 /* Enable Mic Bias pull down and HPH Switch to GND */
4124 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
4125 snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
4126 INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
4127
4128 if (!IS_ERR_VALUE(ret)) {
4129 snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
4130 0x10);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004131 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004132 mbhc->intr_ids->hph_left_ocp);
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004133 wcd9xxx_enable_irq(mbhc->resmgr->core_res,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004134 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004135
4136 /* Initialize mechanical mbhc */
4137 ret = wcd9xxx_setup_jack_detect_irq(mbhc);
4138
4139 if (!ret && mbhc->mbhc_cfg->gpio) {
4140 /* Requested with IRQF_DISABLED */
4141 enable_irq(mbhc->mbhc_cfg->gpio_irq);
4142
4143 /* Bootup time detection */
4144 wcd9xxx_swch_irq_handler(mbhc);
4145 } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
4146 pr_debug("%s: Setting up codec own insert detection\n",
4147 __func__);
4148 /* Setup for insertion detection */
4149 wcd9xxx_insert_detect_setup(mbhc, true);
4150 }
4151 }
4152
4153 pr_debug("%s: leave\n", __func__);
4154
4155 return ret;
4156}
4157
4158static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
4159{
4160 struct delayed_work *dwork;
4161 struct wcd9xxx_mbhc *mbhc;
4162 struct snd_soc_codec *codec;
4163 const struct firmware *fw;
4164 int ret = -1, retry = 0;
4165
4166 dwork = to_delayed_work(work);
4167 mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
4168 codec = mbhc->codec;
4169
4170 while (retry < FW_READ_ATTEMPTS) {
4171 retry++;
4172 pr_info("%s:Attempt %d to request MBHC firmware\n",
4173 __func__, retry);
4174 ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
4175 codec->dev);
4176
4177 if (ret != 0) {
4178 usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
4179 } else {
4180 pr_info("%s: MBHC Firmware read succesful\n", __func__);
4181 break;
4182 }
4183 }
4184
4185 if (ret != 0) {
4186 pr_err("%s: Cannot load MBHC firmware use default cal\n",
4187 __func__);
4188 } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
4189 pr_err("%s: Invalid MBHC cal data size use default cal\n",
4190 __func__);
4191 release_firmware(fw);
4192 } else {
4193 mbhc->mbhc_cfg->calibration = (void *)fw->data;
4194 mbhc->mbhc_fw = fw;
4195 }
4196
4197 (void) wcd9xxx_init_and_calibrate(mbhc);
4198}
4199
4200#ifdef CONFIG_DEBUG_FS
4201ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
4202 size_t count, loff_t *pos)
4203{
4204 const int size = 768;
4205 char buffer[size];
4206 int n = 0;
4207 struct wcd9xxx_mbhc *mbhc = file->private_data;
4208 const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
Joonwoo Park73375212013-05-07 12:42:44 -07004209 const s16 v_ins_hu =
4210 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
4211 const s16 v_ins_h =
4212 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
4213 const s16 v_b1_hu =
4214 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
4215 const s16 v_b1_h =
4216 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
4217 const s16 v_br_h =
4218 wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
Joonwoo Parka8890262012-10-15 12:04:27 -07004219
Joonwoo Park520a0f92013-05-14 19:39:58 -07004220 n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
4221 p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
Joonwoo Parka8890262012-10-15 12:04:27 -07004222 n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
4223 p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
Joonwoo Park35d4cde2013-08-14 15:11:14 -07004224 n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
4225 p->dce_nsc_cs_z,
4226 __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
4227 p->dce_nsc_cs_z,
4228 VDDIO_MICBIAS_MV));
Joonwoo Parka8890262012-10-15 12:04:27 -07004229 n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
4230 p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
4231 n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
4232 p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
Joonwoo Park73375212013-05-07 12:42:44 -07004233 n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
4234 n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
4235 n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
4236 n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
4237 v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
4238 n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
4239 v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07004240 n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07004241 v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
Joonwoo Parka8890262012-10-15 12:04:27 -07004242 n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07004243 v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07004244 n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
Joonwoo Park73375212013-05-07 12:42:44 -07004245 v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
Joonwoo Parka8890262012-10-15 12:04:27 -07004246 n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
4247 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
4248 n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
4249 p->v_no_mic,
4250 wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
4251 n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
4252 p->v_inval_ins_low);
4253 n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
4254 p->v_inval_ins_high);
4255 n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
4256 !wcd9xxx_swch_level_remove(mbhc));
4257 buffer[n] = 0;
4258
4259 return simple_read_from_buffer(buf, count, pos, buffer, n);
4260}
4261
4262static int codec_debug_open(struct inode *inode, struct file *file)
4263{
4264 file->private_data = inode->i_private;
4265 return 0;
4266}
4267
4268static ssize_t codec_debug_write(struct file *filp,
4269 const char __user *ubuf, size_t cnt,
4270 loff_t *ppos)
4271{
4272 char lbuf[32];
4273 char *buf;
4274 int rc;
4275 struct wcd9xxx_mbhc *mbhc = filp->private_data;
4276
4277 if (cnt > sizeof(lbuf) - 1)
4278 return -EINVAL;
4279
4280 rc = copy_from_user(lbuf, ubuf, cnt);
4281 if (rc)
4282 return -EFAULT;
4283
4284 lbuf[cnt] = '\0';
4285 buf = (char *)lbuf;
4286 mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
4287 false : true;
4288 return rc;
4289}
4290
4291static const struct file_operations mbhc_trrs_debug_ops = {
4292 .open = codec_debug_open,
4293 .write = codec_debug_write,
4294};
4295
4296static const struct file_operations mbhc_debug_ops = {
4297 .open = codec_debug_open,
4298 .read = codec_mbhc_debug_read,
4299};
4300
4301static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
4302{
4303 mbhc->debugfs_poke =
4304 debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
4305 &mbhc_trrs_debug_ops);
4306 mbhc->debugfs_mbhc =
4307 debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
4308 NULL, mbhc, &mbhc_debug_ops);
4309}
4310
4311static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
4312{
4313 debugfs_remove(mbhc->debugfs_poke);
4314 debugfs_remove(mbhc->debugfs_mbhc);
4315}
4316#else
4317static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
4318{
4319}
4320
4321static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
4322{
4323}
4324#endif
4325
4326int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
4327 struct wcd9xxx_mbhc_config *mbhc_cfg)
4328{
Simmi Pateriya95466b12013-05-09 20:08:46 +05304329 int rc = 0;
Joonwoo Parka8890262012-10-15 12:04:27 -07004330 struct snd_soc_codec *codec = mbhc->codec;
4331
4332 pr_debug("%s: enter\n", __func__);
4333
4334 if (!codec) {
4335 pr_err("%s: no codec\n", __func__);
4336 return -EINVAL;
4337 }
4338
4339 if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
4340 mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
4341 pr_err("Error: unsupported clock rate %d\n",
4342 mbhc_cfg->mclk_rate);
4343 return -EINVAL;
4344 }
4345
4346 /* Save mbhc config */
4347 mbhc->mbhc_cfg = mbhc_cfg;
4348
4349 /* Get HW specific mbhc registers' address */
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004350 wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_PRIMARY_MIC_MB);
4351
4352 /* Get HW specific mbhc registers' address for anc */
4353 wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_ANC_MIC_MB);
Joonwoo Parka8890262012-10-15 12:04:27 -07004354
4355 /* Put CFILT in fast mode by default */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304356 if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
4357 mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
4358 else
4359 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
4360 0x40, WCD9XXX_CFILT_FAST_MODE);
4361
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004362 /*
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004363 * setup internal micbias if codec uses internal micbias for
4364 * headset detection
4365 */
4366 if (mbhc->mbhc_cfg->use_int_rbias) {
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304367 if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004368 mbhc->mbhc_cb->setup_int_rbias(codec, true);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304369 } else {
4370 pr_info("%s: internal bias requested but codec did not provide callback\n",
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004371 __func__);
Simmi Pateriyaf8e9f682013-10-24 02:15:52 +05304372 }
Bhalchandra Gajare6a2296b2013-09-19 13:15:08 -07004373 }
4374
4375 /*
Bhalchandra Gajare8e5fe252013-07-15 19:42:21 -07004376 * If codec has specific clock gating for MBHC,
4377 * remove the clock gate
4378 */
4379 if (mbhc->mbhc_cb &&
4380 mbhc->mbhc_cb->enable_clock_gate)
4381 mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
4382
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004383 if (!mbhc->mbhc_cfg->read_fw_bin ||
4384 (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004385 rc = wcd9xxx_init_and_calibrate(mbhc);
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004386 } else {
4387 if (!mbhc->mbhc_fw)
4388 schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
4389 usecs_to_jiffies(FW_READ_TIMEOUT));
4390 else
4391 pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
4392 __func__, mbhc->mbhc_fw);
4393 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004394
4395 pr_debug("%s: leave %d\n", __func__, rc);
4396 return rc;
4397}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004398EXPORT_SYMBOL(wcd9xxx_mbhc_start);
Joonwoo Parka8890262012-10-15 12:04:27 -07004399
Joonwoo Parke7d724e2013-08-19 15:51:01 -07004400void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
4401{
4402 if (mbhc->mbhc_fw) {
4403 cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
4404 release_firmware(mbhc->mbhc_fw);
4405 mbhc->mbhc_fw = NULL;
4406 }
4407}
4408EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
4409
Joonwoo Parka8890262012-10-15 12:04:27 -07004410static enum wcd9xxx_micbias_num
4411wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
4412{
4413 enum wcd9xxx_micbias_num ret;
4414 switch (event) {
4415 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004416 case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
4417 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4418 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004419 ret = MBHC_MICBIAS1;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004420 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004421 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004422 case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
4423 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4424 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004425 ret = MBHC_MICBIAS2;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004426 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004427 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004428 case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
4429 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4430 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004431 ret = MBHC_MICBIAS3;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004432 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004433 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004434 case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
4435 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
4436 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Joonwoo Parka8890262012-10-15 12:04:27 -07004437 ret = MBHC_MICBIAS4;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004438 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004439 default:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004440 WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
Joonwoo Parka8890262012-10-15 12:04:27 -07004441 ret = MBHC_MICBIAS_INVALID;
Joonwoo Parkbac18db2013-03-21 15:51:33 -07004442 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004443 }
4444 return ret;
4445}
4446
4447static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
4448{
4449 int ret;
4450 switch (event) {
4451 case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
4452 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4453 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4454 case WCD9XXX_EVENT_POST_CFILT_1_ON:
4455 ret = WCD9XXX_CFILT1_SEL;
4456 break;
4457 case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
4458 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4459 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4460 case WCD9XXX_EVENT_POST_CFILT_2_ON:
4461 ret = WCD9XXX_CFILT2_SEL;
4462 break;
4463 case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
4464 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4465 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4466 case WCD9XXX_EVENT_POST_CFILT_3_ON:
4467 ret = WCD9XXX_CFILT3_SEL;
4468 break;
4469 default:
4470 ret = -1;
4471 }
4472 return ret;
4473}
4474
4475static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
4476{
4477 int cfilt;
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004478 const struct wcd9xxx_micbias_setting *mb_pdata =
4479 mbhc->resmgr->micbias_pdata;
Joonwoo Parka8890262012-10-15 12:04:27 -07004480
4481 switch (mbhc->mbhc_cfg->micbias) {
4482 case MBHC_MICBIAS1:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004483 cfilt = mb_pdata->bias1_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004484 break;
4485 case MBHC_MICBIAS2:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004486 cfilt = mb_pdata->bias2_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004487 break;
4488 case MBHC_MICBIAS3:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004489 cfilt = mb_pdata->bias3_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004490 break;
4491 case MBHC_MICBIAS4:
Bhalchandra Gajare9943aa62013-10-09 18:40:11 -07004492 cfilt = mb_pdata->bias4_cfilt_sel;
Joonwoo Parka8890262012-10-15 12:04:27 -07004493 break;
4494 default:
4495 cfilt = MBHC_MICBIAS_INVALID;
4496 break;
4497 }
4498 return cfilt;
4499}
4500
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004501static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
4502{
4503 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
4504 mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
4505 else
4506 snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
4507 0x40, on ? 0x40 : 0x00);
4508}
4509
Joonwoo Parka8890262012-10-15 12:04:27 -07004510static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
4511 void *data)
4512{
4513 int ret = 0;
4514 struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
4515 struct snd_soc_codec *codec = mbhc->codec;
4516 enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
4517
4518 pr_debug("%s: enter event %s(%d)\n", __func__,
4519 wcd9xxx_get_event_string(event), event);
4520
4521 switch (event) {
4522 /* MICBIAS usage change */
4523 case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
4524 case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
4525 case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
4526 case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004527 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4528 wcd9xxx_event_to_micbias(event)) {
Joonwoo Parka8890262012-10-15 12:04:27 -07004529 wcd9xxx_switch_micbias(mbhc, 0);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004530 /*
4531 * Enable MBHC TxFE whenever micbias is
4532 * turned ON and polling is active
4533 */
4534 if (mbhc->polling_active)
4535 wcd9xxx_enable_mbhc_txfe(mbhc, true);
4536 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004537 break;
4538 case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
4539 case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
4540 case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
4541 case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004542 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
Joonwoo Parka8890262012-10-15 12:04:27 -07004543 wcd9xxx_event_to_micbias(event) &&
4544 wcd9xxx_mbhc_polling(mbhc)) {
4545 /* if polling is on, restart it */
4546 wcd9xxx_pause_hs_polling(mbhc);
4547 wcd9xxx_start_hs_polling(mbhc);
4548 }
4549 break;
4550 case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
4551 case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
4552 case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
4553 case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004554 if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
4555 wcd9xxx_event_to_micbias(event)) {
4556 if (mbhc->event_state &
4557 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
4558 wcd9xxx_switch_micbias(mbhc, 1);
4559 /*
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304560 * Disable MBHC TxFE, in case it was enabled earlier
4561 * when micbias was enabled and polling is not active.
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004562 */
Yeleswarapu, Nagaradheshd1b6af22013-10-17 11:49:17 +05304563 if (!mbhc->polling_active)
4564 wcd9xxx_enable_mbhc_txfe(mbhc, false);
Bhalchandra Gajare2763c722013-09-11 17:10:22 -07004565 }
Simmi Pateriyad54e6db2013-11-28 09:33:31 +05304566 if (mbhc->micbias_enable && mbhc->polling_active &&
4567 !(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg)
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004568 & 0x80)) {
Simmi Pateriyad54e6db2013-11-28 09:33:31 +05304569 pr_debug("%s:Micbias turned off by recording, set up again",
4570 __func__);
4571 snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
4572 0x80, 0x80);
4573 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004574 break;
4575 /* PA usage change */
4576 case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004577 set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parkd87ec4c2012-10-30 15:44:18 -07004578 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004579 /* if micbias is not enabled, switch to vddio */
Joonwoo Parka8890262012-10-15 12:04:27 -07004580 wcd9xxx_switch_micbias(mbhc, 1);
4581 break;
4582 case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004583 set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004584 break;
4585 case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004586 clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004587 /* if HPH PAs are off, report OCP and switch back to CFILT */
4588 clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4589 clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4590 if (mbhc->hph_status & SND_JACK_OC_HPHL)
4591 hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004592 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004593 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004594 1 << MBHC_EVENT_PRE_TX_1_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004595 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004596 break;
4597 case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004598 clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
Joonwoo Parka8890262012-10-15 12:04:27 -07004599 /* if HPH PAs are off, report OCP and switch back to CFILT */
4600 clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
4601 clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
4602 if (mbhc->hph_status & SND_JACK_OC_HPHR)
4603 hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004604 if (!(mbhc->event_state &
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004605 (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004606 1 << MBHC_EVENT_PRE_TX_1_3_ON)))
Joonwoo Park2cee50a2013-05-15 14:09:06 -07004607 wcd9xxx_switch_micbias(mbhc, 0);
Joonwoo Parka8890262012-10-15 12:04:27 -07004608 break;
4609 /* Clock usage change */
4610 case WCD9XXX_EVENT_PRE_MCLK_ON:
4611 break;
4612 case WCD9XXX_EVENT_POST_MCLK_ON:
4613 /* Change to lower TxAAF frequency */
4614 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4615 1 << 4);
4616 /* Re-calibrate clock rate dependent values */
4617 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
4618 /* If clock source changes, stop and restart polling */
4619 if (wcd9xxx_mbhc_polling(mbhc)) {
4620 wcd9xxx_calibrate_hs_polling(mbhc);
4621 wcd9xxx_start_hs_polling(mbhc);
4622 }
4623 break;
4624 case WCD9XXX_EVENT_PRE_MCLK_OFF:
4625 /* If clock source changes, stop and restart polling */
4626 if (wcd9xxx_mbhc_polling(mbhc))
4627 wcd9xxx_pause_hs_polling(mbhc);
4628 break;
4629 case WCD9XXX_EVENT_POST_MCLK_OFF:
4630 break;
4631 case WCD9XXX_EVENT_PRE_RCO_ON:
4632 break;
4633 case WCD9XXX_EVENT_POST_RCO_ON:
4634 /* Change to higher TxAAF frequency */
4635 snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
4636 0 << 4);
4637 /* Re-calibrate clock rate dependent values */
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004638 wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
Joonwoo Parka8890262012-10-15 12:04:27 -07004639 /* If clock source changes, stop and restart polling */
4640 if (wcd9xxx_mbhc_polling(mbhc)) {
4641 wcd9xxx_calibrate_hs_polling(mbhc);
4642 wcd9xxx_start_hs_polling(mbhc);
4643 }
4644 break;
4645 case WCD9XXX_EVENT_PRE_RCO_OFF:
4646 /* If clock source changes, stop and restart polling */
4647 if (wcd9xxx_mbhc_polling(mbhc))
4648 wcd9xxx_pause_hs_polling(mbhc);
4649 break;
4650 case WCD9XXX_EVENT_POST_RCO_OFF:
4651 break;
4652 /* CFILT usage change */
4653 case WCD9XXX_EVENT_PRE_CFILT_1_ON:
4654 case WCD9XXX_EVENT_PRE_CFILT_2_ON:
4655 case WCD9XXX_EVENT_PRE_CFILT_3_ON:
4656 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4657 wcd9xxx_event_to_cfilt(event))
4658 /*
4659 * Switch CFILT to slow mode if MBHC CFILT is being
4660 * used.
4661 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304662 wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
Joonwoo Parka8890262012-10-15 12:04:27 -07004663 break;
4664 case WCD9XXX_EVENT_POST_CFILT_1_OFF:
4665 case WCD9XXX_EVENT_POST_CFILT_2_OFF:
4666 case WCD9XXX_EVENT_POST_CFILT_3_OFF:
4667 if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
4668 wcd9xxx_event_to_cfilt(event))
4669 /*
4670 * Switch CFILT to fast mode if MBHC CFILT is not
4671 * used anymore.
4672 */
Simmi Pateriya95466b12013-05-09 20:08:46 +05304673 wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
Joonwoo Parka8890262012-10-15 12:04:27 -07004674 break;
4675 /* System resume */
4676 case WCD9XXX_EVENT_POST_RESUME:
4677 mbhc->mbhc_last_resume = jiffies;
4678 break;
4679 /* BG mode chage */
4680 case WCD9XXX_EVENT_PRE_BG_OFF:
4681 case WCD9XXX_EVENT_POST_BG_OFF:
4682 case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
4683 case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
4684 case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
4685 case WCD9XXX_EVENT_POST_BG_MBHC_ON:
4686 /* Not used for now */
4687 break;
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004688 case WCD9XXX_EVENT_PRE_TX_1_3_ON:
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004689 /*
4690 * if polling is ON, mbhc micbias not enabled
4691 * switch micbias source to VDDIO
4692 */
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004693 set_bit(MBHC_EVENT_PRE_TX_1_3_ON, &mbhc->event_state);
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004694 if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
4695 & 0x80) &&
4696 mbhc->polling_active && !mbhc->mbhc_micbias_switched)
4697 wcd9xxx_switch_micbias(mbhc, 1);
4698 break;
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004699 case WCD9XXX_EVENT_POST_TX_1_3_OFF:
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004700 /*
4701 * Switch back to micbias if HPH PA or TX3 path
4702 * is disabled
4703 */
Phani Kumar Uppalapati7e3f57d2013-12-12 12:26:59 -08004704 clear_bit(MBHC_EVENT_PRE_TX_1_3_ON, &mbhc->event_state);
Phani Kumar Uppalapati7c0bf702013-11-25 13:39:53 -08004705 if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
4706 !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
4707 1 << MBHC_EVENT_PA_HPHR)))
4708 wcd9xxx_switch_micbias(mbhc, 0);
4709 break;
Joonwoo Parka8890262012-10-15 12:04:27 -07004710 default:
4711 WARN(1, "Unknown event %d\n", event);
4712 ret = -EINVAL;
4713 }
4714
4715 pr_debug("%s: leave\n", __func__);
4716
Simmi Pateriya0a44d842013-04-03 01:12:42 +05304717 return ret;
Joonwoo Parka8890262012-10-15 12:04:27 -07004718}
4719
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004720static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4721 uint32_t *zr)
4722{
4723 int i;
4724 int ret = 0;
4725 s16 l[3], r[3];
4726 s16 *z[] = {
4727 &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
4728 };
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004729 bool override_en;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004730 struct snd_soc_codec *codec = mbhc->codec;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004731 const int mux_wait_us = 25;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004732 const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
4733 /* Phase 1 */
4734 /* Set MBHC_MUX for HPHL without ical */
4735 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4736 /* Set MBHC_MUX for HPHR without ical */
4737 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4738 /* Set MBHC_MUX for HPHR with ical */
4739 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
4740 /* Set MBHC_MUX for HPHL with ical */
4741 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
4742
4743 /* Phase 2 */
4744 {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
4745 /* Set MBHC_MUX for HPHR without ical and wait for 25us */
4746 {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
4747 };
4748
4749 pr_debug("%s: enter\n", __func__);
4750 WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
4751
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004752 if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
4753 !mbhc->mbhc_cb->compute_impedance || !zl ||
4754 !zr)
4755 return -EINVAL;
4756
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004757 /*
4758 * Impedance detection is an intrusive function as it mutes RX paths,
4759 * enable PAs and etc. Therefore codec drvier including ALSA
4760 * shouldn't read and write hardware registers during detection.
4761 */
4762 mutex_lock(&codec->mutex);
4763
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004764 wcd9xxx_onoff_ext_mclk(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004765
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004766 override_en = (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04) ?
4767 true : false;
4768 if (!override_en)
4769 wcd9xxx_turn_onoff_override(mbhc, true);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004770 pr_debug("%s: Setting impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004771
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004772 /* Codec specific setup for L0, R0, L1 and R1 measurements */
4773 mbhc->mbhc_cb->setup_zdet(mbhc, PRE_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004774
4775 pr_debug("%s: Performing impedance detection\n", __func__);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004776 for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004777 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4778 reg_set_mux[i].mask,
4779 reg_set_mux[i].val);
4780 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4781 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4782 else
4783 snd_soc_update_bits(codec,
4784 WCD9XXX_A_MBHC_SCALING_MUX_1,
4785 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004786 /* 25us is required after mux change to settle down */
4787 usleep_range(mux_wait_us,
4788 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004789 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004790 }
4791
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004792 /* Codec specific setup for L2 and R2 measurements */
4793 mbhc->mbhc_cb->setup_zdet(mbhc, POST_MEAS);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004794
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004795 for (; i < ARRAY_SIZE(reg_set_mux); i++) {
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004796 snd_soc_update_bits(codec, reg_set_mux[i].reg,
4797 reg_set_mux[i].mask,
4798 reg_set_mux[i].val);
4799 if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
4800 mbhc->mbhc_cb->enable_mux_bias_block(codec);
4801 else
4802 snd_soc_update_bits(codec,
4803 WCD9XXX_A_MBHC_SCALING_MUX_1,
4804 0x80, 0x80);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004805 /* 25us is required after mux change to settle down */
4806 usleep_range(mux_wait_us,
4807 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004808 *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004809 }
4810
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004811 mbhc->mbhc_cb->setup_zdet(mbhc, PA_DISABLE);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004812
4813 mutex_unlock(&codec->mutex);
4814
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004815 wcd9xxx_onoff_ext_mclk(mbhc, false);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004816
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004817 if (!override_en)
4818 wcd9xxx_turn_onoff_override(mbhc, false);
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004819 mbhc->mbhc_cb->compute_impedance(l, r, zl, zr);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004820
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004821 pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004822 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004823 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
4824 pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004825 __func__,
Phani Kumar Uppalapati01a77e12013-08-08 15:31:35 -07004826 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004827 pr_debug("%s: RL %d milliohm, RR %d milliohm\n", __func__, *zl, *zr);
4828 pr_debug("%s: Impedance detection completed\n", __func__);
4829
4830 return ret;
4831}
4832
4833int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
4834 uint32_t *zr)
4835{
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004836 *zl = mbhc->zl;
4837 *zr = mbhc->zr;
Joonwoo Parkb755e9e2013-05-28 13:14:05 -07004838
4839 if (*zl && *zr)
4840 return 0;
4841 else
4842 return -EINVAL;
4843}
4844
Joonwoo Parka8890262012-10-15 12:04:27 -07004845/*
4846 * wcd9xxx_mbhc_init : initialize MBHC internal structures.
4847 *
4848 * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
4849 */
4850int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
Joonwoo Parkccccba72013-04-26 11:19:46 -07004851 struct snd_soc_codec *codec,
Phani Kumar Uppalapatib7266bd2013-10-21 14:26:10 -07004852 int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
4853 enum wcd9xxx_micbias_num),
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004854 const struct wcd9xxx_mbhc_cb *mbhc_cb,
4855 const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
4856 int rco_clk_rate,
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004857 bool impedance_det_en)
Joonwoo Parka8890262012-10-15 12:04:27 -07004858{
4859 int ret;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004860 void *core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07004861
4862 pr_debug("%s: enter\n", __func__);
4863 memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
4864 memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
4865
4866 mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
4867 mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
4868 mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
4869 mbhc->mbhc_micbias_switched = false;
4870 mbhc->polling_active = false;
4871 mbhc->mbhc_state = MBHC_STATE_NONE;
4872 mbhc->in_swch_irq_handler = false;
4873 mbhc->current_plug = PLUG_TYPE_NONE;
4874 mbhc->lpi_enabled = false;
4875 mbhc->no_mic_headset_override = false;
4876 mbhc->mbhc_last_resume = 0;
4877 mbhc->codec = codec;
4878 mbhc->resmgr = resmgr;
4879 mbhc->resmgr->mbhc = mbhc;
Joonwoo Parkccccba72013-04-26 11:19:46 -07004880 mbhc->micbias_enable_cb = micbias_enable_cb;
Phani Kumar Uppalapati43bc4152013-05-24 00:44:20 -07004881 mbhc->rco_clk_rate = rco_clk_rate;
Simmi Pateriya95466b12013-05-09 20:08:46 +05304882 mbhc->mbhc_cb = mbhc_cb;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004883 mbhc->intr_ids = mbhc_cdc_intr_ids;
Phani Kumar Uppalapatid7549e12013-07-12 22:22:03 -07004884 mbhc->impedance_detect = impedance_det_en;
Joonwoo Parka8890262012-10-15 12:04:27 -07004885
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004886 if (mbhc->intr_ids == NULL) {
4887 pr_err("%s: Interrupt mapping not provided\n", __func__);
4888 return -EINVAL;
4889 }
4890
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004891 if (mbhc->headset_jack.jack == NULL) {
4892 ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
4893 &mbhc->headset_jack);
4894 if (ret) {
4895 pr_err("%s: Failed to create new jack\n", __func__);
4896 return ret;
4897 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004898
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004899 ret = snd_soc_jack_new(codec, "Button Jack",
4900 WCD9XXX_JACK_BUTTON_MASK,
4901 &mbhc->button_jack);
4902 if (ret) {
4903 pr_err("Failed to create new jack\n");
4904 return ret;
4905 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004906
Phani Kumar Uppalapati447a8292013-07-26 16:26:04 -07004907 ret = snd_jack_set_key(mbhc->button_jack.jack,
4908 SND_JACK_BTN_0,
4909 KEY_MEDIA);
4910 if (ret) {
4911 pr_err("%s: Failed to set code for btn-0\n",
4912 __func__);
4913 return ret;
4914 }
4915
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08004916 INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
4917 wcd9xxx_mbhc_fw_read);
4918 INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
4919 INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
4920 wcd9xxx_mbhc_insert_work);
4921 }
Joonwoo Parka8890262012-10-15 12:04:27 -07004922
4923 /* Register event notifier */
4924 mbhc->nblock.notifier_call = wcd9xxx_event_notify;
4925 ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
4926 if (ret) {
4927 pr_err("%s: Failed to register notifier %d\n", __func__, ret);
4928 return ret;
4929 }
4930
4931 wcd9xxx_init_debugfs(mbhc);
4932
Bhalchandra Gajarebbc32742013-10-18 12:32:29 -07004933
4934 /* Disable Impedance detection by default for certain codec types */
4935 if (mbhc->mbhc_cb &&
4936 mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
4937 impedance_detect_en = 0;
4938 else
4939 impedance_detect_en = impedance_det_en ? 1 : 0;
4940
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004941 core_res = mbhc->resmgr->core_res;
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004942 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
Joonwoo Parka8890262012-10-15 12:04:27 -07004943 wcd9xxx_hs_insert_irq,
4944 "Headset insert detect", mbhc);
4945 if (ret) {
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07004946 pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004947 mbhc->intr_ids->insertion, ret);
Joonwoo Parka8890262012-10-15 12:04:27 -07004948 goto err_insert_irq;
4949 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004950 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
Joonwoo Parka8890262012-10-15 12:04:27 -07004951
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004952 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
Joonwoo Parka8890262012-10-15 12:04:27 -07004953 wcd9xxx_hs_remove_irq,
4954 "Headset remove detect", mbhc);
4955 if (ret) {
4956 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004957 mbhc->intr_ids->poll_plug_rem);
Joonwoo Parka8890262012-10-15 12:04:27 -07004958 goto err_remove_irq;
4959 }
4960
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004961 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
Joonwoo Parka8890262012-10-15 12:04:27 -07004962 wcd9xxx_dce_handler, "DC Estimation detect",
4963 mbhc);
4964 if (ret) {
4965 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004966 mbhc->intr_ids->dce_est_complete);
Joonwoo Parka8890262012-10-15 12:04:27 -07004967 goto err_potential_irq;
4968 }
4969
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004970 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
Joonwoo Parka8890262012-10-15 12:04:27 -07004971 wcd9xxx_release_handler,
4972 "Button Release detect", mbhc);
4973 if (ret) {
4974 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004975 mbhc->intr_ids->button_release);
Joonwoo Parka8890262012-10-15 12:04:27 -07004976 goto err_release_irq;
4977 }
4978
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004979 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004980 wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
4981 mbhc);
4982 if (ret) {
4983 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004984 mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004985 goto err_hphl_ocp_irq;
4986 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004987 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004988
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004989 ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
Joonwoo Parka8890262012-10-15 12:04:27 -07004990 wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
4991 mbhc);
4992 if (ret) {
4993 pr_err("%s: Failed to request irq %d\n", __func__,
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004994 mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004995 goto err_hphr_ocp_irq;
4996 }
Bhalchandra Gajare16748932013-10-01 18:16:05 -07004997 wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
Joonwoo Parka8890262012-10-15 12:04:27 -07004998
Joonwoo Park3b268ca2013-07-17 13:11:43 -07004999 wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
5000 1 << WCD9XXX_COND_HPH);
5001
Joonwoo Parka8890262012-10-15 12:04:27 -07005002 pr_debug("%s: leave ret %d\n", __func__, ret);
5003 return ret;
5004
5005err_hphr_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005006 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005007err_hphl_ocp_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005008 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005009err_release_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005010 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005011err_potential_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005012 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005013err_remove_irq:
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005014 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005015err_insert_irq:
5016 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
5017
5018 pr_debug("%s: leave ret %d\n", __func__, ret);
5019 return ret;
5020}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07005021EXPORT_SYMBOL(wcd9xxx_mbhc_init);
Joonwoo Parka8890262012-10-15 12:04:27 -07005022
5023void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
5024{
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07005025 struct wcd9xxx_core_resource *core_res =
5026 mbhc->resmgr->core_res;
Joonwoo Parka8890262012-10-15 12:04:27 -07005027
Joonwoo Park3b268ca2013-07-17 13:11:43 -07005028 wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
5029 1 << WCD9XXX_COND_HPH);
5030
Bhalchandra Gajare16748932013-10-01 18:16:05 -07005031 wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
5032 wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
5033 wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
5034 wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
5035 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
5036 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
5037 wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
Ravishankar Sarawadi2293efe2013-01-11 16:37:23 -08005038
Joonwoo Parka8890262012-10-15 12:04:27 -07005039 wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
Joonwoo Parka8890262012-10-15 12:04:27 -07005040 wcd9xxx_cleanup_debugfs(mbhc);
Joonwoo Parka8890262012-10-15 12:04:27 -07005041}
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07005042EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
Joonwoo Parka8890262012-10-15 12:04:27 -07005043
5044MODULE_DESCRIPTION("wcd9xxx MBHC module");
5045MODULE_LICENSE("GPL v2");