ASoC: wcd-mbhc: Update button press detection logic
Current button press detection method is relying on button press interrupt with a wait event insdie mechanical insertion handler. However, though Codec hardware raises button press interrupt status bit, the handler will not get invoked because mechanical insertion interrupt handler does not return IRQ_HANDLED. So the wait-event always gets timedout assuming that there is no button press interrupt. This would cause delayed button press detection and it will result into HEADSET detection first followed by HEADPHONE when a valid 3-pole headphone cable is inserted into the headset jack. Move the wait-event outside the interrupt context to a workqueue to handle the button press interrupts properly. Change-Id: I49b71c2d326f45b7be91b7d470f60c487e69dedc Signed-off-by: Phani Kumar Uppalapati <phaniu@codeaurora.org>
This commit is contained in:
parent
1156c2e7a4
commit
2b1fb63bcc
1
Makefile
1
Makefile
|
@ -358,6 +358,7 @@ CFLAGS_GCOV = -fprofile-arcs -ftest-coverage
|
|||
USERINCLUDE := \
|
||||
-I$(srctree)/arch/$(hdr-arch)/include/uapi \
|
||||
-Iarch/$(hdr-arch)/include/generated/uapi \
|
||||
-I$(srctree)/drivers/soc/qcom \
|
||||
-I$(srctree)/include/uapi \
|
||||
-Iinclude/generated/uapi \
|
||||
-include $(srctree)/include/linux/kconfig.h
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/completion.h>
|
||||
#include <glink_private.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "wcd-mbhc-v2.h"
|
||||
|
@ -47,6 +49,8 @@
|
|||
#define FW_READ_TIMEOUT 4000000
|
||||
#define FAKE_REM_RETRY_ATTEMPTS 3
|
||||
|
||||
#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50
|
||||
|
||||
static int det_extn_cable_en;
|
||||
module_param(det_extn_cable_en, int,
|
||||
S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
@ -796,7 +800,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
|
|||
struct snd_soc_codec *codec;
|
||||
enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
|
||||
unsigned long timeout;
|
||||
u16 hs_comp_res, hphl_sch, mic_sch;
|
||||
u16 hs_comp_res, hphl_sch, mic_sch, btn_result;
|
||||
bool wrk_complete = false;
|
||||
int pt_gnd_mic_swap_cnt = 0;
|
||||
int no_gnd_mic_swap_cnt = 0;
|
||||
|
@ -804,6 +808,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
|
|||
bool micbias2 = false;
|
||||
bool micbias1 = false;
|
||||
int ret = 0;
|
||||
int rc;
|
||||
|
||||
pr_debug("%s: enter\n", __func__);
|
||||
|
||||
|
@ -820,6 +825,48 @@ static void wcd_correct_swch_plug(struct work_struct *work)
|
|||
|
||||
wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
|
||||
|
||||
|
||||
if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP)
|
||||
goto correct_plug_type;
|
||||
|
||||
/* Enable HW FSM */
|
||||
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
|
||||
/*
|
||||
* Check for any button press interrupts before starting 3-sec
|
||||
* loop.
|
||||
*/
|
||||
rc = wait_for_completion_timeout(&mbhc->btn_press_compl,
|
||||
msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS));
|
||||
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
|
||||
|
||||
if (!rc) {
|
||||
pr_debug("%s No btn press interrupt\n", __func__);
|
||||
if (!btn_result && !hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HEADSET;
|
||||
else if (!btn_result && hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
|
||||
else
|
||||
plug_type = MBHC_PLUG_TYPE_INVALID;
|
||||
} else {
|
||||
if (!btn_result && !hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
|
||||
else
|
||||
plug_type = MBHC_PLUG_TYPE_INVALID;
|
||||
}
|
||||
|
||||
pr_debug("%s: Valid plug found, plug type is %d\n",
|
||||
__func__, plug_type);
|
||||
if (plug_type == MBHC_PLUG_TYPE_HEADSET ||
|
||||
plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
|
||||
WCD_MBHC_RSC_LOCK(mbhc);
|
||||
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
|
||||
WCD_MBHC_RSC_UNLOCK(mbhc);
|
||||
}
|
||||
|
||||
correct_plug_type:
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
if (mbhc->hs_detect_work_stop) {
|
||||
|
@ -990,11 +1037,7 @@ exit:
|
|||
static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
|
||||
{
|
||||
struct snd_soc_codec *codec = mbhc->codec;
|
||||
long timeout = msecs_to_jiffies(50); /* 50ms */
|
||||
enum wcd_mbhc_plug_type plug_type;
|
||||
int timeout_result;
|
||||
u8 btn_result;
|
||||
u8 hs_comp_res;
|
||||
bool micbias1 = false;
|
||||
int cross_conn;
|
||||
int try = 0;
|
||||
|
@ -1012,82 +1055,24 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
|
|||
mbhc->mbhc_cb->mbhc_micbias_control(codec, MICB_PULLUP_ENABLE);
|
||||
else
|
||||
wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
|
||||
/* Enable HW FSM */
|
||||
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
|
||||
/*
|
||||
* Wait for 50msec for FSM to complete its task.
|
||||
* wakeup if btn pres intr occurs
|
||||
*/
|
||||
mbhc->is_btn_press = false;
|
||||
WCD_MBHC_RSC_UNLOCK(mbhc);
|
||||
timeout_result = wait_event_interruptible_timeout(mbhc->wait_btn_press,
|
||||
mbhc->is_btn_press,
|
||||
timeout);
|
||||
|
||||
WCD_MBHC_RSC_LOCK(mbhc);
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
|
||||
do {
|
||||
cross_conn = wcd_check_cross_conn(mbhc);
|
||||
try++;
|
||||
} while (try < GND_MIC_SWAP_THRESHOLD);
|
||||
|
||||
if (!timeout_result) {
|
||||
pr_debug("%s No btn press interrupt\n", __func__);
|
||||
/*
|
||||
* Check if there is any cross connection,
|
||||
* Micbias and schmitt trigger (HPHL-HPHR)
|
||||
* needs to be enabled.
|
||||
*/
|
||||
pr_debug("%s: btn_result %x, hs_comp_res %x\n", __func__,
|
||||
btn_result, hs_comp_res);
|
||||
if (!hs_comp_res) {
|
||||
/*
|
||||
* Cross connection result is not reliable
|
||||
* so do check for it for 4 times to conclude
|
||||
* cross connection occured or not.
|
||||
*/
|
||||
do {
|
||||
cross_conn = wcd_check_cross_conn(mbhc);
|
||||
try++;
|
||||
} while (try < GND_MIC_SWAP_THRESHOLD);
|
||||
if (cross_conn > 0) {
|
||||
pr_debug("%s: cross con found, start polling\n",
|
||||
__func__);
|
||||
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read back btn_result and hs_comp_result value
|
||||
* again to re-confirm
|
||||
*/
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
|
||||
WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
|
||||
|
||||
if (!btn_result && !hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HEADSET;
|
||||
else if (!btn_result && hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
|
||||
else {
|
||||
plug_type = MBHC_PLUG_TYPE_INVALID;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (!btn_result && !hs_comp_res)
|
||||
plug_type = MBHC_PLUG_TYPE_HEADPHONE;
|
||||
else {
|
||||
plug_type = MBHC_PLUG_TYPE_INVALID;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
pr_debug("%s: Valid plug found, plug type is %d\n",
|
||||
if (cross_conn > 0) {
|
||||
pr_debug("%s: cross con found, start polling\n",
|
||||
__func__);
|
||||
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
|
||||
mbhc->current_plug = plug_type;
|
||||
pr_debug("%s: Plug found, plug type is %d\n",
|
||||
__func__, plug_type);
|
||||
if (plug_type == MBHC_PLUG_TYPE_HEADSET ||
|
||||
plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
|
||||
wcd_mbhc_find_plug_and_report(mbhc, plug_type);
|
||||
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
|
||||
} else {
|
||||
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
|
||||
}
|
||||
|
||||
/* Re-initialize button press completion object */
|
||||
reinit_completion(&mbhc->btn_press_compl);
|
||||
wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
|
||||
pr_debug("%s: leave\n", __func__);
|
||||
}
|
||||
|
||||
|
@ -1119,8 +1104,10 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
|
|||
* pull up current, in which case this callback function
|
||||
* will not be defined.
|
||||
*/
|
||||
if (mbhc->mbhc_cb->hph_pull_up_control)
|
||||
mbhc->mbhc_cb->hph_pull_up_control(codec, REMOVE);
|
||||
if (mbhc->mbhc_cb->hph_pull_up_control) {
|
||||
mbhc->mbhc_cb->hph_pull_up_control(codec,
|
||||
(detection_type ? REMOVE : INSERT));
|
||||
}
|
||||
|
||||
pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
|
||||
mbhc->current_plug, detection_type);
|
||||
|
@ -1522,10 +1509,10 @@ irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
|
|||
unsigned long msec_val;
|
||||
|
||||
pr_debug("%s: enter\n", __func__);
|
||||
complete(&mbhc->btn_press_compl);
|
||||
WCD_MBHC_RSC_LOCK(mbhc);
|
||||
/* send event to sw intr handler*/
|
||||
mbhc->is_btn_press = true;
|
||||
wake_up_interruptible(&mbhc->wait_btn_press);
|
||||
wcd_cancel_btn_work(mbhc);
|
||||
if (wcd_swch_level_remove(mbhc)) {
|
||||
pr_debug("%s: Switch level is low ", __func__);
|
||||
|
@ -1715,6 +1702,9 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
|
|||
wcd_program_btn_threshold(mbhc, false);
|
||||
|
||||
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
|
||||
|
||||
init_completion(&mbhc->btn_press_compl);
|
||||
|
||||
WCD_MBHC_RSC_UNLOCK(mbhc);
|
||||
pr_debug("%s: leave\n", __func__);
|
||||
return ret;
|
||||
|
|
|
@ -357,6 +357,8 @@ struct wcd_mbhc {
|
|||
struct notifier_block nblock;
|
||||
|
||||
struct wcd_mbhc_register *wcd_mbhc_regs;
|
||||
|
||||
struct completion btn_press_compl;
|
||||
};
|
||||
#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
|
||||
sizeof(struct wcd_mbhc_general_cfg) + \
|
||||
|
|
Loading…
Reference in New Issue