summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
authorSimmi Pateriya <simmip@codeaurora.org>2014-06-20 01:11:57 +0530
committerSimmi Pateriya <simmip@codeaurora.org>2014-06-27 10:41:31 +0530
commitf509f137b7c7ac6f11d3c556d9a20e3bcfaf4b0a (patch)
tree576d18c57362dc541c5d3697d1043f2040be3b0e /sound/soc
parent80b0ec1b421c449d98f68bc4ad418cdc82755a8e (diff)
ASoC: wcd: add polling for cross connection
If headset is inserted slowly and FSM is enabled, it is possible that cross connection is wrongly detected. Add polling to recheck the type of headset. CRs-Fixed: 671195 Change-Id: I0f759b77395403c0f30aa531790ead09217a8d3d Signed-off-by: Simmi Pateriya <simmip@codeaurora.org>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c221
1 files changed, 166 insertions, 55 deletions
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 2ee75a7829f3..e73304a53b72 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -44,6 +44,7 @@
#define OCP_ATTEMPT 1
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
+#define GND_MIC_SWAP_THRESHOLD 4
#define WCD_MBHC_RSC_LOCK(mbhc) \
{ \
@@ -560,6 +561,54 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
pr_debug("%s: leave\n", __func__);
}
+/* To determine if cross connection occured */
+static bool wcd_check_cross_conn(struct wcd_mbhc *mbhc)
+{
+ u16 result1, swap_res;
+ struct snd_soc_codec *codec = mbhc->codec;
+ enum wcd_mbhc_plug_type plug_type = mbhc->current_plug;
+ s16 reg, reg1;
+
+ reg = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL);
+ reg1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2);
+ /*
+ * Check if there is any cross connection,
+ * Micbias and schmitt trigger (HPHL-HPHR)
+ * needs to be enabled.
+ */
+ result1 = snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MBHC_BTN_RESULT);
+ /* Make sure micbias is enabled now */
+ snd_soc_update_bits(codec,
+ MSM8X16_WCD_A_ANALOG_MICB_2_EN,
+ 0x80, 0x80);
+ /*If enabling micbias, turn off current source*/
+ snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
+ 0xB0, 0x80);
+ snd_soc_update_bits(codec,
+ MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
+ 0x6, 0x4);
+ /* read reg MBHC_RESULT_2 value with cross connection bit */
+ swap_res = snd_soc_read(codec,
+ MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
+ pr_debug("%s: swap_res %x\n", __func__, swap_res);
+ if (!result1 && !(swap_res & 0x04)) {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ pr_debug("%s: Cross connection identified\n", __func__);
+ } else {
+ pr_debug("%s: No Cross connection found\n", __func__);
+ }
+
+ /* Disable micbias and schmitt trigger */
+ snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2, reg1);
+ snd_soc_update_bits(codec,
+ MSM8X16_WCD_A_ANALOG_MICB_2_EN,
+ 0x80, 0x00);
+ snd_soc_write(codec, MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL, reg);
+ pr_debug("%s: leave, plug type: %d\n", __func__, plug_type);
+
+ return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
+}
+
static void wcd_correct_swch_plug(struct work_struct *work)
{
struct wcd_mbhc *mbhc;
@@ -568,7 +617,9 @@ static void wcd_correct_swch_plug(struct work_struct *work)
unsigned long timeout;
u16 result1, result2;
bool wrk_complete = false;
+ int pt_gnd_mic_swap_cnt = 0;
int delay = 0;
+ bool is_pa_on;
pr_debug("%s: enter\n", __func__);
@@ -577,14 +628,16 @@ static void wcd_correct_swch_plug(struct work_struct *work)
timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
while (!time_after(jiffies, timeout)) {
- if (mbhc->hs_detect_work_stop) {
- pr_debug("%s: stop requested\n", __func__);
+ if (mbhc->hs_detect_work_stop || wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: stop requested: %d\n", __func__,
+ mbhc->hs_detect_work_stop);
goto exit;
}
/* allow sometime and re-check stop requested again */
msleep(200);
- if (mbhc->hs_detect_work_stop) {
- pr_debug("%s: stop requested\n", __func__);
+ if (mbhc->hs_detect_work_stop || wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: stop requested: %d\n", __func__,
+ mbhc->hs_detect_work_stop);
goto exit;
}
result1 = snd_soc_read(codec,
@@ -592,30 +645,85 @@ static void wcd_correct_swch_plug(struct work_struct *work)
result2 = snd_soc_read(codec,
MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
pr_debug("%s: result2 = %x\n", __func__, result2);
+
+ is_pa_on = snd_soc_read(codec,
+ MSM8X16_WCD_A_ANALOG_RX_HPH_CNP_EN) &
+ 0x30;
+
+ if ((!(result2 & 0x01)) && (!is_pa_on)) {
+ /* Check for cross connection*/
+ if (wcd_check_cross_conn(mbhc)) {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD)
+ continue;
+ else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
+ /*
+ * This is due to GND/MIC switch didn't
+ * work, Report unsupported plug.
+ */
+ pr_debug("%s: switch didnt work\n",
+ __func__);
+ goto report;
+ } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+ pr_debug("%s: US_EU gpio present, flip switch\n",
+ __func__);
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+ continue;
+ }
+ } else {
+ pt_gnd_mic_swap_cnt++;
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD) {
+ continue;
+ } else
+ pt_gnd_mic_swap_cnt = 0;
+ }
+ }
if (result2 == 1) {
pr_debug("%s: cable is extension cable\n", __func__);
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
wrk_complete = true;
} else {
- pr_debug("%s: cable is headset\n", __func__);
- plug_type = MBHC_PLUG_TYPE_HEADSET;
- if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) {
- wcd_mbhc_find_plug_and_report(mbhc, plug_type);
- goto exit;
+ pr_debug("%s: cable might be headset: %d\n", __func__,
+ plug_type);
+ if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ /*
+ * Report headset only if not already reported
+ * and if there is not button press without
+ * release
+ */
+ if (mbhc->current_plug !=
+ MBHC_PLUG_TYPE_HEADSET &&
+ !mbhc->btn_press_intr) {
+ pr_debug("%s: cable is headset\n",
+ __func__);
+ goto report;
+ }
}
wrk_complete = false;
}
}
- if (wrk_complete == true)
- plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
- else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
- if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
- mbhc->btn_press_intr) {
- pr_debug("%s: Can be slow insertion of headphone\n",
- __func__);
+ if (mbhc->btn_press_intr) {
+ pr_debug("%s: Can be slow insertion of headphone\n", __func__);
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
}
- } else
- plug_type = MBHC_PLUG_TYPE_INVALID;
+ /*
+ * If plug_tye is headset, we might have already reported either in
+ * detect_plug-type or in above while loop, no need to report again
+ */
+ if (!wrk_complete && plug_type == MBHC_PLUG_TYPE_HEADSET) {
+ pr_debug("%s: It's neither headset nor headphone\n", __func__);
+ goto exit;
+ }
if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
/* Enable external voltage source to micbias if present */
@@ -683,6 +791,10 @@ static void wcd_correct_swch_plug(struct work_struct *work)
mbhc->mbhc_cb->enable_mb_source(codec, false);
}
+report:
+ pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
+ __func__, plug_type, wrk_complete,
+ mbhc->btn_press_intr);
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
exit:
wcd9xxx_spmi_unlock_sleep();
@@ -696,7 +808,9 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
long timeout = msecs_to_jiffies(50); /* 50ms */
enum wcd_mbhc_plug_type plug_type;
int timeout_result;
- u16 result1, result2, swap_res;
+ u16 result1, result2;
+ bool cross_conn;
+ int try = 0;
pr_debug("%s: enter\n", __func__);
WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
@@ -734,30 +848,21 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
pr_debug("%s: result1 %x, result2 %x\n", __func__,
result1, result2);
if (!(result2 & 0x01)) {
- snd_soc_update_bits(codec,
- MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
- 0x6, 0x2);
/*
- * read reg MBHC_RESULT_2 value with cross
- * connection bit
+ * Cross connection result is not reliable
+ * so do check for it for 4 times to conclude
+ * cross connection occured or not.
*/
- swap_res = snd_soc_read(codec,
- MSM8X16_WCD_A_ANALOG_MBHC_ZDET_ELECT_RESULT);
- pr_debug("%s: swap_res %x\n", __func__, swap_res);
-
- if (!result1 && !(swap_res & 0x04)) {
+ do {
+ cross_conn = wcd_check_cross_conn(mbhc);
+ try++;
+ } while (try < GND_MIC_SWAP_THRESHOLD);
+ if (cross_conn) {
+ pr_debug("%s: cross con found, start polling\n",
+ __func__);
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
- pr_debug("%s: Cross connection identified\n",
- __func__);
- goto eu_us_switch;
- } else {
- pr_debug("%s: No Cross connection found\n",
- __func__);
+ goto exit;
}
- /* Disable micbias and schmitt trigger */
- snd_soc_update_bits(codec,
- MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
- 0x6, 0x0);
}
if (!result1 && !(result2 & 0x01))
plug_type = MBHC_PLUG_TYPE_HEADSET;
@@ -776,20 +881,7 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
}
}
-eu_us_switch:
- if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
- pr_debug("%s: cross connection found\n", __func__);
- if (mbhc->mbhc_cfg->swap_gnd_mic) {
- pr_debug("%s: US_EU gpio present, flip switch\n",
- __func__);
- if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
- plug_type = MBHC_PLUG_TYPE_HEADSET;
- }
- /* Disable micbias and schmitt trigger */
- snd_soc_update_bits(codec,
- MSM8X16_WCD_A_ANALOG_MBHC_DET_CTL_2,
- 0x6, 0x0);
- }
+exit:
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MICB_2_EN,
0x80, 0x00);
@@ -800,6 +892,7 @@ eu_us_switch:
pr_debug("%s: Valid plug found, plug type is %d\n",
__func__, plug_type);
if (plug_type != MBHC_PLUG_TYPE_HIGH_HPH &&
+ plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP &&
plug_type != MBHC_PLUG_TYPE_HEADSET &&
plug_type != MBHC_PLUG_TYPE_INVALID)
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
@@ -808,7 +901,6 @@ eu_us_switch:
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
} else
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
-exit:
pr_debug("%s: leave\n", __func__);
}
@@ -866,9 +958,20 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
snd_soc_update_bits(codec,
MSM8X16_WCD_A_ANALOG_MBHC_FSM_CTL,
0xB0, 0x00);
+ /*
+ * if it is unsupported cable playback happens through speaker
+ * disabling MASTERBIAS results in noise on speaker.So don't
+ * disable MASTERBIAS.
+ */
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_GND_MIC_SWAP
+ && mbhc->current_plug != MBHC_PLUG_TYPE_HIGH_HPH)
+ /* Make sure MASTER_BIAS_CTL is disabled */
+ snd_soc_update_bits(codec,
+ MSM8X16_WCD_A_ANALOG_MASTER_BIAS_CTL,
+ 0x30, 0x00);
+ mbhc->btn_press_intr = false;
if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
- mbhc->btn_press_intr = false;
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
@@ -1003,6 +1106,10 @@ irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
/* send event to sw intr handler*/
mbhc->is_btn_press = true;
wake_up_interruptible(&mbhc->wait_btn_press);
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto done;
+ }
mbhc->btn_press_intr = true;
msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
@@ -1043,6 +1150,10 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data)
pr_debug("%s: enter\n", __func__);
WCD_MBHC_RSC_LOCK(mbhc);
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto exit;
+ }
mbhc->btn_press_intr = false;
/*