Merge "ASoC: wcd: add polling for cross connection"

This commit is contained in:
Linux Build Service Account 2014-07-14 15:17:46 -07:00 committed by Gerrit - the friendly Code Review server
commit 786caaa7f8

View file

@ -46,6 +46,7 @@
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
#define GND_MIC_SWAP_THRESHOLD 4
#define WCD_MBHC_RSC_LOCK(mbhc) \
{ \
@ -667,6 +668,51 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
return ret;
}
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;
@ -675,6 +721,8 @@ 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;
bool is_pa_on;
pr_debug("%s: enter\n", __func__);
@ -686,19 +734,16 @@ static void wcd_correct_swch_plug(struct work_struct *work)
mbhc->mbhc_cb->enable_mb_source(codec, true);
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.
* this is as recommeneded by HW design team after
* multiple test iterations with trial error basis.
*/
/* 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,
@ -706,30 +751,86 @@ 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__);
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
}
} else
plug_type = MBHC_PLUG_TYPE_INVALID;
if (mbhc->btn_press_intr) {
pr_debug("%s: Can be slow insertion of headphone\n", __func__);
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
}
/*
* 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) {
if (wcd_is_special_headset(mbhc)) {
@ -741,6 +842,9 @@ static void wcd_correct_swch_plug(struct work_struct *work)
}
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:
/* Disable external voltage source to micbias if present */
@ -757,7 +861,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);
@ -792,34 +898,24 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
* Micbias and schmitt trigger (HPHL-HPHR)
* needs to be enabled.
*/
pr_debug("%s: result1 %x, result2 %x\n",
__func__, result1, result2);
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;
@ -838,20 +934,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);
@ -862,6 +945,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);
@ -870,7 +954,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__);
}
@ -928,9 +1011,9 @@ 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);
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) {
@ -1065,6 +1148,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);
@ -1105,6 +1192,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;
/*