mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
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>
This commit is contained in:
parent
82f65cf952
commit
c70e4babb1
1 changed files with 157 additions and 66 deletions
|
@ -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) \
|
||||
{ \
|
||||
|
@ -660,6 +661,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;
|
||||
|
@ -668,6 +714,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__);
|
||||
|
||||
|
@ -679,19 +727,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,
|
||||
|
@ -699,30 +744,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)) {
|
||||
|
@ -734,6 +835,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 */
|
||||
|
@ -750,7 +854,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);
|
||||
|
@ -785,34 +891,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;
|
||||
|
@ -831,20 +927,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);
|
||||
|
@ -855,6 +938,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);
|
||||
|
@ -863,7 +947,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__);
|
||||
}
|
||||
|
||||
|
@ -921,9 +1004,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) {
|
||||
|
@ -1058,6 +1141,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);
|
||||
|
@ -1098,6 +1185,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;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue