summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c166
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h9
-rwxr-xr-xsound/soc/codecs/wcd9335.c18
3 files changed, 170 insertions, 23 deletions
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 445b7e8a2345..ade9c704ea48 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -32,7 +32,9 @@
#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
- SND_JACK_UNSUPPORTED | SND_JACK_MECHANICAL)
+ SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \
+ SND_JACK_UNSUPPORTED)
+
#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
SND_JACK_BTN_4 | SND_JACK_BTN_5 )
@@ -49,6 +51,7 @@
#define MAX_IMPED 60000
#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50
+#define ANC_DETECT_RETRY_CNT 7
static int det_extn_cable_en;
module_param(det_extn_cable_en, int,
@@ -568,8 +571,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
if (mbhc->micbias_enable) {
if (mbhc->mbhc_cb->mbhc_micbias_control)
mbhc->mbhc_cb->mbhc_micbias_control(
- mbhc->codec,
- MICB_DISABLE);
+ mbhc->codec, MIC_BIAS_2,
+ MICB_DISABLE);
if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
mbhc->codec,
@@ -601,12 +604,12 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
if (mbhc->micbias_enable) {
if (mbhc->mbhc_cb->mbhc_micbias_control)
mbhc->mbhc_cb->mbhc_micbias_control(
- mbhc->codec,
- MICB_DISABLE);
+ mbhc->codec, MIC_BIAS_2,
+ MICB_DISABLE);
if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
- mbhc->codec,
- MIC_BIAS_2, false);
+ mbhc->codec,
+ MIC_BIAS_2, false);
mbhc->micbias_enable = false;
}
mbhc->hph_type = WCD_MBHC_HPH_NONE;
@@ -633,6 +636,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
}
mbhc->hph_status &= ~(SND_JACK_HEADSET |
SND_JACK_LINEOUT |
+ SND_JACK_ANC_HEADPHONE |
SND_JACK_UNSUPPORTED);
}
@@ -648,8 +652,10 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
else if (jack_type == SND_JACK_HEADSET) {
mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
mbhc->jiffies_atreport = jiffies;
- } else if (jack_type == SND_JACK_LINEOUT)
+ } else if (jack_type == SND_JACK_LINEOUT) {
mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ } else if (jack_type == SND_JACK_ANC_HEADPHONE)
+ mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE;
if (mbhc->impedance_detect &&
mbhc->mbhc_cb->compute_impedance &&
@@ -689,9 +695,98 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
}
+static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
+{
+ bool anc_mic_found = false;
+ u16 val, hs_comp_res, btn_status = 0;
+ unsigned long retry = 0;
+ int valid_plug_cnt = 0, invalid_plug_cnt = 0;
+ int btn_status_cnt = 0;
+ bool is_check_btn_press = false;
+
+
+ if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 ||
+ mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4)
+ return false;
+
+ if (!mbhc->mbhc_cb->mbhc_micbias_control)
+ return false;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val);
+
+ if (val)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+ mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+ mbhc->mbhc_cfg->anc_micbias,
+ MICB_ENABLE);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ /*
+ * wait for button debounce time 20ms. If 4-pole plug is inserted
+ * into 5-pole jack, then there will be a button press interrupt
+ * during anc plug detection. In that case though Hs_comp_res is 0,
+ * it should not be declared as ANC plug type
+ */
+ usleep_range(20000, 20100);
+
+ /*
+ * After enabling FSM, to handle slow insertion scenarios,
+ * check hs_comp_result for few times to see if the IN3 voltage
+ * is below the Vref
+ */
+ do {
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ goto exit;
+ }
+ pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+ if (!hs_comp_res) {
+ valid_plug_cnt++;
+ is_check_btn_press = true;
+ } else
+ invalid_plug_cnt++;
+ /* Wait 1ms before taking another reading */
+ usleep_range(1000, 1100);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status);
+ if (btn_status)
+ btn_status_cnt++;
+
+ retry++;
+ } while (retry < ANC_DETECT_RETRY_CNT);
+
+ pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n",
+ __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt);
+
+ /* decision logic */
+ if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press &&
+ (btn_status_cnt == 0))
+ anc_mic_found = true;
+exit:
+ if (!val)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0);
+
+ mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+ mbhc->mbhc_cfg->anc_micbias,
+ MICB_DISABLE);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0);
+ pr_debug("%s: anc mic %sfound\n", __func__,
+ anc_mic_found ? "" : "not ");
+ return anc_mic_found;
+}
+
static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
enum wcd_mbhc_plug_type plug_type)
{
+ bool anc_mic_found;
+ enum snd_jack_types jack_type;
+
pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
__func__, mbhc->current_plug, plug_type);
@@ -716,11 +811,18 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
} else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
+ if (mbhc->mbhc_cfg->enable_anc_mic_detect)
+ anc_mic_found = wcd_mbhc_detect_anc_plug_type(mbhc);
+
+ jack_type = SND_JACK_HEADSET;
+ if (anc_mic_found)
+ jack_type = SND_JACK_ANC_HEADPHONE;
+
/*
* If Headphone was reported previously, this will
* only report the mic line
*/
- wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ wcd_mbhc_report_plug(mbhc, 1, jack_type);
} else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
if (mbhc->mbhc_cfg->detect_extn_cable) {
/* High impedance device found. Report as LINEOUT */
@@ -807,7 +909,8 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
struct snd_soc_codec *codec = mbhc->codec;
int delay = 0, rc;
bool ret = false;
- bool hs_comp_res;
+ u16 hs_comp_res;
+ bool is_spl_hs = false;
/*
* Increase micbias to 2.7V to detect headsets with
@@ -855,16 +958,18 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
/* Wait for 50msec for FSM to update result values */
msleep(50);
WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
- if (!(hs_comp_res))
+ if (!(hs_comp_res)) {
pr_debug("%s: Special headset detected in %d msecs\n",
__func__, (delay * 2));
+ is_spl_hs = true;
+ }
if (delay == SPECIAL_HS_DETECT_TIME_MS) {
pr_debug("%s: Spl headset didnt get detect in 4 sec\n",
__func__);
break;
}
}
- if (!(hs_comp_res)) {
+ if (is_spl_hs) {
pr_debug("%s: Headset with threshold found\n", __func__);
mbhc->micbias_enable = true;
ret = true;
@@ -900,6 +1005,7 @@ static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc,
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
break;
case MBHC_PLUG_TYPE_HEADSET:
+ case MBHC_PLUG_TYPE_ANC_HEADPHONE:
if (!mbhc->is_hs_recording && !micbias2)
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
break;
@@ -1131,9 +1237,11 @@ correct_plug_type:
* and if there is not button press without
* release
*/
- if (mbhc->current_plug !=
- MBHC_PLUG_TYPE_HEADSET &&
- !mbhc->btn_press_intr) {
+ if (((mbhc->current_plug !=
+ MBHC_PLUG_TYPE_HEADSET) &&
+ (mbhc->current_plug !=
+ MBHC_PLUG_TYPE_ANC_HEADPHONE)) &&
+ !mbhc->btn_press_intr) {
pr_debug("%s: cable is headset\n",
__func__);
goto report;
@@ -1151,8 +1259,10 @@ correct_plug_type:
* 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: Headset already reported\n", __func__);
+ if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
+ (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) {
+ pr_debug("%s: plug_type:0x%x already reported\n",
+ __func__, mbhc->current_plug);
goto enable_supply;
}
@@ -1167,6 +1277,10 @@ correct_plug_type:
}
report:
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ goto exit;
+ }
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);
@@ -1181,7 +1295,7 @@ enable_supply:
exit:
if (mbhc->mbhc_cb->mbhc_micbias_control &&
!mbhc->micbias_enable)
- mbhc->mbhc_cb->mbhc_micbias_control(codec,
+ mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
MICB_DISABLE);
if (mbhc->mbhc_cb->micbias_enable_status) {
micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
@@ -1222,7 +1336,8 @@ static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
if (mbhc->mbhc_cb->mbhc_micbias_control)
- mbhc->mbhc_cb->mbhc_micbias_control(codec, MICB_ENABLE);
+ mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
+ MICB_ENABLE);
else
wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
@@ -1349,6 +1464,17 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
1);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+ } else if (mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) {
+ mbhc->mbhc_cb->irq_control(codec,
+ mbhc->intr_ids->mbhc_hs_rem_intr,
+ false);
+ mbhc->mbhc_cb->irq_control(codec,
+ mbhc->intr_ids->mbhc_hs_ins_intr,
+ false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
}
} else if (!detection_type) {
/* Disable external voltage source to micbias if present */
@@ -1727,7 +1853,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data)
* headset not headphone.
*/
if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
- wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
goto exit;
}
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
index f6cc238ec6e5..bc48cbff4576 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.h
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -64,6 +64,9 @@ enum wcd_mbhc_register_function {
WCD_MBHC_SWCH_LEVEL_REMOVE,
WCD_MBHC_MOISTURE_VREF,
WCD_MBHC_PULLDOWN_CTRL,
+ WCD_MBHC_ANC_DET_EN,
+ WCD_MBHC_FSM_STATUS,
+ WCD_MBHC_MUX_CTL,
WCD_MBHC_REG_FUNC_MAX,
};
@@ -74,6 +77,7 @@ enum wcd_mbhc_plug_type {
MBHC_PLUG_TYPE_HEADPHONE,
MBHC_PLUG_TYPE_HIGH_HPH,
MBHC_PLUG_TYPE_GND_MIC_SWAP,
+ MBHC_PLUG_TYPE_ANC_HEADPHONE,
};
enum pa_dac_ack_flags {
@@ -249,6 +253,9 @@ struct wcd_mbhc_config {
int key_code[WCD_MBHC_KEYCODE_NUM];
uint32_t linein_th;
struct wcd_mbhc_moisture_cfg moist_cfg;
+ int mbhc_micbias;
+ int anc_micbias;
+ bool enable_anc_mic_detect;
};
struct wcd_mbhc_intr {
@@ -349,7 +356,7 @@ struct wcd_mbhc_cb {
int num_btn, bool);
void (*hph_pull_up_control)(struct snd_soc_codec *,
enum mbhc_hs_pullup_iref);
- int (*mbhc_micbias_control)(struct snd_soc_codec *, int req);
+ int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req);
void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool);
void (*skip_imped_detect)(struct snd_soc_codec *);
bool (*extn_use_mb)(struct snd_soc_codec *);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index f4992acb5468..fb1e939459db 100755
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -621,6 +621,17 @@ static struct wcd_mbhc_register
0, 0, 0, 0),
WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
+ WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0),
+ /*
+ * MBHC FSM status register is only available in Tasha 2.0.
+ * So, init with 0 later once the version is known, then values
+ * will be updated.
+ */
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+ 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+ WCD9335_MBHC_CTL_2, 0x70, 4, 0),
};
static const struct wcd_mbhc_intr intr_ids = {
@@ -1367,7 +1378,7 @@ static int tasha_micbias_control(struct snd_soc_codec *codec,
}
static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec,
- int req)
+ int micb_num, int req)
{
int ret;
@@ -1378,7 +1389,7 @@ static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec,
if (req == MICB_ENABLE)
tasha_cdc_mclk_enable(codec, true, false);
- ret = tasha_micbias_control(codec, MIC_BIAS_2, req, false);
+ ret = tasha_micbias_control(codec, micb_num, req, false);
/*
* Release vote for mclk while requesting for
@@ -12303,6 +12314,9 @@ static int tasha_codec_probe(struct snd_soc_codec *codec)
0x0C;
wcd_mbhc_registers[WCD_MBHC_MOISTURE_VREF].offset =
2;
+ wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg =
+ WCD9335_MBHC_FSM_STATUS;
+ wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01;
}
ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids,
wcd_mbhc_registers, TASHA_ZDET_SUPPORTED);