ASoC: wcd9310: Add support for MIC BIAS other than 1.8 volts
When MBHC is set to use microphone bias other than 1.8 volts, codec driver
needs to calculate two sets of thresholds. This is required because the
mic bias can be powered from two different sources. If vddio is used as
supply, then the maximum microphone bias voltage can only be 1.8 volts.
If MBHC originally was using other microphone voltage, the thresholds will
have to be changed when mic bias power is switched to vddio supply.
CRs-fixed: 341458
Change-Id: I428a4adaaa78b273cd598772c38bccdce8d9c27c
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index d055513..6788322 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -89,7 +89,8 @@
#define TABLA_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-#define TABLA_MBHC_FAKE_INSERT_VOLT_DELTA_MV 200
+#define TABLA_MBHC_FAKE_INS_DELTA_MV 200
+#define TABLA_MBHC_FAKE_INS_DELTA_SCALED_MV 300
#define TABLA_HS_DETECT_PLUG_TIME_MS (5 * 1000)
#define TABLA_HS_DETECT_PLUG_INERVAL_MS 100
@@ -187,6 +188,11 @@
u16 v_no_mic;
u8 npoll;
u8 nbounce_wait;
+ s16 adj_v_hs_max;
+ u16 adj_v_ins_hu;
+ u16 adj_v_ins_h;
+ s16 v_inval_ins_low;
+ s16 v_inval_ins_high;
};
struct tabla_reg_address {
@@ -245,7 +251,6 @@
struct delayed_work mbhc_btn_dwork;
struct mbhc_micbias_regs mbhc_bias_regs;
- u8 cfilt_k_value;
bool mbhc_micbias_switched;
/* track PA/DAC state */
@@ -2170,65 +2175,127 @@
return (hph_reg_val & 0xC0) ? true : false;
}
+static void tabla_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
/* called under codec_resource_lock acquisition */
-static void tabla_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch)
+static void tabla_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
+ int usec)
+{
+ int cfilt_k_val;
+ bool set = true;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ tabla->mbhc_micbias_switched) {
+ pr_debug("%s: set mic V to micbias V\n", __func__);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ tabla_turn_onoff_override(codec, true);
+ while (1) {
+ cfilt_k_val = tabla_find_k_value(
+ tabla->pdata->micbias.ldoh_v,
+ set ? tabla->mbhc_data.micb_mv :
+ VDDIO_MICBIAS_MV);
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ if (!set)
+ break;
+ usleep_range(usec, usec);
+ set = false;
+ }
+ tabla_turn_onoff_override(codec, false);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
+static void __tabla_codec_switch_micbias(struct snd_soc_codec *codec,
+ int vddio_switch, bool restartpolling,
+ bool checkpolling)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
int cfilt_k_val;
- bool mbhc_was_polling = false;
- switch (vddio_switch) {
- case 1:
- if (tabla->mbhc_micbias_switched == 0 &&
- tabla->mbhc_polling_active) {
+ if (vddio_switch && !tabla->mbhc_micbias_switched &&
+ (!checkpolling || tabla->mbhc_polling_active)) {
+ if (restartpolling)
tabla_codec_pause_hs_polling(codec);
- /* VDDIO switch enabled */
- tabla->cfilt_k_value = snd_soc_read(codec,
- tabla->mbhc_bias_regs.cfilt_val);
+ tabla_turn_onoff_override(codec, true);
+ /* Adjust threshold if Mic Bias voltage changes */
+ if (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
cfilt_k_val = tabla_find_k_value(
- tabla->pdata->micbias.ldoh_v, 1800);
+ tabla->pdata->micbias.ldoh_v,
+ VDDIO_MICBIAS_MV);
+ usleep_range(10000, 10000);
snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
+ tabla->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL,
+ tabla->mbhc_data.adj_v_ins_hu & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL,
+ (tabla->mbhc_data.adj_v_ins_hu >> 8) &
+ 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+ __func__);
+ }
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x80);
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.mbhc_reg, 0x10, 0x00);
+ /* enable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+ tabla_turn_onoff_override(codec, false);
+ if (restartpolling)
tabla_codec_start_hs_polling(codec);
- tabla->mbhc_micbias_switched = true;
- pr_debug("%s: VDDIO switch enabled\n", __func__);
- }
- break;
+ tabla->mbhc_micbias_switched = true;
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
- case 0:
- if (tabla->mbhc_micbias_switched) {
- if (tabla->mbhc_polling_active) {
- tabla_codec_pause_hs_polling(codec);
- mbhc_was_polling = true;
- }
- /* VDDIO switch disabled */
- if (tabla->cfilt_k_value != 0)
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.cfilt_val, 0XFC,
- tabla->cfilt_k_value);
+ } else if (!vddio_switch && tabla->mbhc_micbias_switched) {
+ if ((!checkpolling || tabla->mbhc_polling_active) &&
+ restartpolling)
+ tabla_codec_pause_hs_polling(codec);
+ /* Reprogram thresholds */
+ if (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val = tabla_find_k_value(
+ tabla->pdata->micbias.ldoh_v,
+ tabla->mbhc_data.micb_mv);
snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.mbhc_reg, 0x10, 0x00);
-
- if (mbhc_was_polling)
- tabla_codec_start_hs_polling(codec);
-
- tabla->mbhc_micbias_switched = false;
- pr_debug("%s: VDDIO switch disabled\n", __func__);
+ tabla->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL,
+ tabla->mbhc_data.v_ins_hu & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL,
+ (tabla->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+ __func__);
}
- break;
+
+ /* Disable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+
+ if ((!checkpolling || tabla->mbhc_polling_active) &&
+ restartpolling)
+ tabla_codec_start_hs_polling(codec);
+
+ tabla->mbhc_micbias_switched = false;
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
}
}
+static void tabla_codec_switch_micbias(struct snd_soc_codec *codec,
+ int vddio_switch)
+{
+ return __tabla_codec_switch_micbias(codec, vddio_switch, true, true);
+}
+
static int tabla_codec_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -3202,17 +3269,46 @@
return val;
}
+static s16 tabla_get_current_v_ins(struct tabla_priv *tabla, bool hu)
+{
+ s16 v_ins;
+ if ((tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ tabla->mbhc_micbias_switched)
+ v_ins = hu ? (s16)tabla->mbhc_data.adj_v_ins_hu :
+ (s16)tabla->mbhc_data.adj_v_ins_h;
+ else
+ v_ins = hu ? (s16)tabla->mbhc_data.v_ins_hu :
+ (s16)tabla->mbhc_data.v_ins_h;
+ return v_ins;
+}
+
+static s16 tabla_get_current_v_hs_max(struct tabla_priv *tabla)
+{
+ s16 v_hs_max;
+ struct tabla_mbhc_plug_type_cfg *plug_type;
+
+ plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ if ((tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ tabla->mbhc_micbias_switched)
+ v_hs_max = tabla->mbhc_data.adj_v_hs_max;
+ else
+ v_hs_max = plug_type->v_hs_max;
+ return v_hs_max;
+}
+
static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
{
u8 *n_ready, *n_cic;
struct tabla_mbhc_btn_detect_cfg *btn_det;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ const s16 v_ins_hu = tabla_get_current_v_ins(tabla, true);
+
btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->mbhc_cfg.calibration);
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL,
- tabla->mbhc_data.v_ins_hu & 0xFF);
+ v_ins_hu & 0xFF);
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL,
- (tabla->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ (v_ins_hu >> 8) & 0xFF);
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL,
tabla->mbhc_data.v_b1_hu & 0xFF);
@@ -3278,8 +3374,8 @@
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s() mclk_enable = %u\n", __func__, mclk_enable);
-
+ pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
+ dapm);
if (dapm)
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
if (mclk_enable) {
@@ -4253,6 +4349,7 @@
short bias_value;
u8 cfilt_mode;
+ pr_debug("%s: enter, mclk_enabled %d\n", __func__, tabla->mclk_enabled);
if (!tabla->mbhc_cfg.calibration) {
pr_err("Error, no tabla calibration\n");
return -ENODEV;
@@ -4480,7 +4577,7 @@
tabla_set_and_turnoff_hph_padac(codec);
}
- if (trigger == MBHC_USE_HPHL_TRIGGER) {
+ if (trigger & MBHC_USE_HPHL_TRIGGER) {
/* Enable HPH Schmitt Trigger */
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x11,
0x11);
@@ -4488,7 +4585,8 @@
plug_det->hph_current << 2);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x02,
0x02);
- } else if (trigger == MBHC_USE_MB_TRIGGER) {
+ }
+ if (trigger & MBHC_USE_MB_TRIGGER) {
/* enable the mic line schmitt trigger */
snd_soc_update_bits(codec,
tabla->mbhc_bias_regs.mbhc_reg,
@@ -4507,7 +4605,6 @@
/* setup for insetion detection */
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x2, 0);
-
} else {
pr_debug("setup for removal detection\n");
/* Make sure the HPH schmitt trigger is OFF */
@@ -4785,6 +4882,22 @@
return ret;
}
+static s16 tabla_scale_v_micb_vddio(struct tabla_priv *tabla, int v,
+ bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = tabla_find_k_value(tabla->pdata->micbias.ldoh_v,
+ VDDIO_MICBIAS_MV);
+ mb_k = tabla_find_k_value(tabla->pdata->micbias.ldoh_v,
+ tabla->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * vddio_k / mb_k;
+ else
+ r = v * mb_k / vddio_k;
+ return r;
+}
+
static void tabla_mbhc_calc_thres(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla;
@@ -4817,13 +4930,39 @@
tabla->mbhc_data.v_ins_h =
tabla_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
+ tabla->mbhc_data.v_inval_ins_low = TABLA_MBHC_FAKE_INSERT_LOW;
+ if (tabla->mbhc_cfg.gpio)
+ tabla->mbhc_data.v_inval_ins_high =
+ TABLA_MBHC_FAKE_INSERT_HIGH;
+ else
+ tabla->mbhc_data.v_inval_ins_high =
+ TABLA_MBHC_FAKE_INS_HIGH_NO_GPIO;
+
+ if (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ tabla->mbhc_data.adj_v_hs_max =
+ tabla_scale_v_micb_vddio(tabla, plug_type->v_hs_max, true);
+ tabla->mbhc_data.adj_v_ins_hu =
+ tabla_codec_v_sta_dce(codec, STA,
+ tabla->mbhc_data.adj_v_hs_max);
+ tabla->mbhc_data.adj_v_ins_h =
+ tabla_codec_v_sta_dce(codec, DCE,
+ tabla->mbhc_data.adj_v_hs_max);
+ tabla->mbhc_data.v_inval_ins_low =
+ tabla_scale_v_micb_vddio(tabla,
+ tabla->mbhc_data.v_inval_ins_low,
+ false);
+ tabla->mbhc_data.v_inval_ins_high =
+ tabla_scale_v_micb_vddio(tabla,
+ tabla->mbhc_data.v_inval_ins_high,
+ false);
+ }
+
btn_high = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_V_BTN_HIGH);
for (i = 0; i < btn_det->num_btn; i++)
btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
tabla->mbhc_data.v_b1_h = tabla_codec_v_sta_dce(codec, DCE, btn_mv);
btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
-
tabla->mbhc_data.v_b1_hu =
tabla_codec_v_sta_dce(codec, STA, btn_delta_mv);
@@ -5108,7 +5247,10 @@
int r = 0;
struct snd_soc_codec *codec = priv->codec;
const int dces = MBHC_NUM_DCE_PLUG_DETECT;
- short mb_v;
+ s16 mb_v, v_ins_hu, v_ins_h;
+
+ v_ins_hu = tabla_get_current_v_ins(priv, true);
+ v_ins_h = tabla_get_current_v_ins(priv, false);
for (i = 0; i < dces; i++) {
usleep_range(10000, 10000);
@@ -5116,8 +5258,8 @@
mb_v = tabla_codec_sta_dce(codec, 0, true);
pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
tabla_codec_sta_dce_v(codec, 0, mb_v));
- if (mb_v < (short)priv->mbhc_data.v_b1_hu ||
- mb_v > (short)priv->mbhc_data.v_ins_hu) {
+ if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
+ mb_v > v_ins_hu) {
r = 1;
break;
}
@@ -5125,8 +5267,8 @@
mb_v = tabla_codec_sta_dce(codec, 1, true);
pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
tabla_codec_sta_dce_v(codec, 1, mb_v));
- if (mb_v < (short)priv->mbhc_data.v_b1_h ||
- mb_v > (short)priv->mbhc_data.v_ins_h) {
+ if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
+ mb_v > v_ins_h) {
r = 1;
break;
}
@@ -5147,6 +5289,8 @@
TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
priv->mbhc_state = MBHC_STATE_RELEASE;
+ tabla_codec_drive_v_to_micbias(codec, 10000);
+
if (priv->buttons_pressed & TABLA_JACK_BUTTON_MASK) {
ret = tabla_cancel_btn_work(priv);
if (ret == 0) {
@@ -5165,7 +5309,7 @@
pr_debug("%s: GPIO kicked in, ignore\n",
__func__);
} else {
- pr_debug("%s: Reporting short button 0 "
+ pr_debug("%s: Reporting short button "
"press and release\n",
__func__);
tabla_snd_soc_jack_report(priv,
@@ -5300,38 +5444,31 @@
}
static bool tabla_is_invalid_insertion_range(struct snd_soc_codec *codec,
- s32 mic_volt)
+ s32 mic_volt)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_plug_type_cfg *plug_type =
- TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
- int fake_insert_high;
bool invalid = false;
-
- if (tabla->mbhc_cfg.gpio)
- fake_insert_high = TABLA_MBHC_FAKE_INSERT_HIGH;
- else
- fake_insert_high = TABLA_MBHC_FAKE_INS_HIGH_NO_GPIO;
+ s16 v_hs_max;
/* Perform this check only when the high voltage headphone
* needs to be considered as invalid
*/
- if (!tabla->mbhc_inval_hs_range_override
- && (mic_volt > plug_type->v_hs_max)) {
+ v_hs_max = tabla_get_current_v_hs_max(tabla);
+ if (!tabla->mbhc_inval_hs_range_override && (mic_volt > v_hs_max))
invalid = true;
- } else if (mic_volt < fake_insert_high
- && (mic_volt > TABLA_MBHC_FAKE_INSERT_LOW)) {
+ else if (mic_volt < tabla->mbhc_data.v_inval_ins_high &&
+ (mic_volt > tabla->mbhc_data.v_inval_ins_low))
invalid = true;
- }
return invalid;
}
-static bool tabla_is_invalid_insert_delta(struct snd_soc_codec *codec,
- int mic_volt, int mic_volt_prev)
+static bool tabla_is_inval_insert_delta(struct snd_soc_codec *codec,
+ int mic_volt, int mic_volt_prev,
+ int threshold)
{
int delta = abs(mic_volt - mic_volt_prev);
- if (delta > TABLA_MBHC_FAKE_INSERT_VOLT_DELTA_MV) {
+ if (delta > threshold) {
pr_debug("%s: volt delta %dmv\n", __func__, delta);
return true;
}
@@ -5348,13 +5485,14 @@
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
struct tabla_mbhc_plug_type_cfg *plug_type_ptr =
TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ s16 v_hs_max = tabla_get_current_v_hs_max(tabla);
for (i = 0 ; i < MBHC_NUM_DCE_PLUG_DETECT && !r; i++) {
if (mic_mv[i] < plug_type_ptr->v_no_mic)
plug_type[i] = PLUG_TYPE_HEADPHONE;
- else if (mic_mv[i] < plug_type_ptr->v_hs_max)
+ else if (mic_mv[i] < v_hs_max)
plug_type[i] = PLUG_TYPE_HEADSET;
- else if (mic_mv[i] > plug_type_ptr->v_hs_max)
+ else if (mic_mv[i] > v_hs_max)
plug_type[i] = PLUG_TYPE_HIGH_HPH;
r = tabla_is_invalid_insertion_range(codec, mic_mv[i]);
@@ -5362,9 +5500,9 @@
if (plug_type[i-1] != plug_type[i])
r = true;
else
- r = tabla_is_invalid_insert_delta(codec,
- mic_mv[i],
- mic_mv[i - 1]);
+ r = tabla_is_inval_insert_delta(codec,
+ mic_mv[i], mic_mv[i - 1],
+ TABLA_MBHC_FAKE_INS_DELTA_MV);
}
}
@@ -5397,7 +5535,9 @@
tabla_codec_cleanup_hs_polling(codec);
pr_debug("setup mic trigger for further detection\n");
tabla->lpi_enabled = true;
- tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
false);
}
}
@@ -5425,11 +5565,6 @@
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
}
-static void tabla_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
static bool tabla_hs_gpio_level_remove(struct tabla_priv *tabla)
{
return (gpio_get_value_cansleep(tabla->mbhc_cfg.gpio) !=
@@ -5571,16 +5706,34 @@
}
/* called under codec_resource_lock acquisition */
+static void tabla_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x01, on);
+ if (on)
+ usleep_range(5000, 5000);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
static void tabla_codec_detect_plug_type(struct snd_soc_codec *codec)
{
int i;
+ bool gndswitch, vddioswitch;
+ int scaled;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
const struct tabla_mbhc_plug_detect_cfg *plug_det =
TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->mbhc_cfg.calibration);
- short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
- s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
- enum tabla_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
struct tabla_mbhc_plug_type_cfg *plug_type_ptr;
+ const bool vddio = (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ !tabla->mbhc_micbias_switched);
+ int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
+ enum tabla_mbhc_plug_type plug_type[num_det];
+ short mb_v[num_det];
+ s32 mic_mv[num_det];
+ bool inval = false;
/* Turn on the override,
* tabla_codec_setup_hs_polling requires override on */
@@ -5603,61 +5756,65 @@
return;
}
- /*
- * First DCE measurement,
- * IF this is fake, discontinue detection
- * and restart insertion detection
- */
- mb_v[0] = tabla_codec_setup_hs_polling(codec);
- mic_mv[0] = tabla_codec_sta_dce_v(codec, 1, mb_v[0]);
- if (tabla_is_invalid_insertion_range(codec, mic_mv[0])) {
- pr_debug("%s: Detect attempt 1, detected Fake\n", __func__);
- tabla_turn_onoff_override(codec, false);
- tabla_codec_cleanup_hs_polling(codec);
- tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
- false);
- return;
- }
- pr_debug("%s: DCE run 1, %x, mic_mv = %d\n", __func__, mb_v[0],
- mic_mv[0]);
-
- /*
- * Perform two more DCE measurements,
- * IF any of them is fake, discontinue detection
- * and restart insertion detection
- */
- for (i = 1; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
- mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
- mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
- pr_debug("%s: DCE run %d, %x, mic_mv = %d\n", __func__, i + 1,
- mb_v[1], mic_mv[i]);
- if (tabla_is_invalid_insertion_range(codec, mic_mv[i])
- || tabla_is_invalid_insert_delta(codec, mic_mv[i],
- mic_mv[i - 1])) {
- pr_debug("%s: Detect attempt %d, detected Fake\n",
- __func__, i + 1);
- tabla_turn_onoff_override(codec, false);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
- 0x02, 0x02);
- tabla_codec_cleanup_hs_polling(codec);
- tabla_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER,
+ /* performs DCEs for N times
+ * 1st: check if voltage is in invalid range
+ * 2nd - N-2nd: check voltage range and delta
+ * N-1st: check voltage range, delta with HPHR GND switch
+ * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+ for (i = 0; i < num_det && !inval; i++) {
+ gndswitch = (i == (num_det - 1 - vddio));
+ vddioswitch = (vddio && (i == num_det - 1));
+ if (i == 0) {
+ mb_v[i] = tabla_codec_setup_hs_polling(codec);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ inval = tabla_is_invalid_insertion_range(codec,
+ mic_mv[i]);
+ scaled = mic_mv[i];
+ } else if (vddioswitch) {
+ __tabla_codec_switch_micbias(tabla->codec, 1, false,
false);
- return;
+ mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ scaled = tabla_scale_v_micb_vddio(tabla, mic_mv[i],
+ false);
+ inval = (tabla_is_invalid_insertion_range(codec,
+ mic_mv[i]) ||
+ tabla_is_inval_insert_delta(codec, scaled,
+ mic_mv[i - 1],
+ TABLA_MBHC_FAKE_INS_DELTA_SCALED_MV));
+ __tabla_codec_switch_micbias(tabla->codec, 0, false,
+ false);
+ } else {
+ if (gndswitch)
+ tabla_codec_hphr_gnd_switch(codec, true);
+ mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ inval = (tabla_is_invalid_insertion_range(codec,
+ mic_mv[i]) ||
+ tabla_is_inval_insert_delta(codec, mic_mv[i],
+ mic_mv[i - 1],
+ TABLA_MBHC_FAKE_INS_DELTA_SCALED_MV));
+ if (gndswitch)
+ tabla_codec_hphr_gnd_switch(codec, false);
+ scaled = mic_mv[i];
}
+ pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, "
+ "invalid %d\n", __func__,
+ i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled, gndswitch,
+ inval);
}
tabla_turn_onoff_override(codec, false);
plug_type_ptr =
TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
- /*
- * If we are here, means none of the three
- * measurements are fake, continue plug type detection.
- * If all three measurements do not produce same
- * plug type, restart insertion detection
- */
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+ for (i = 0; !inval && i < num_det; i++) {
+ /*
+ * If we are here, means none of the all
+ * measurements are fake, continue plug type detection.
+ * If all three measurements do not produce same
+ * plug type, restart insertion detection
+ */
if (mic_mv[i] < plug_type_ptr->v_no_mic) {
plug_type[i] = PLUG_TYPE_HEADPHONE;
pr_debug("%s: Detect attempt %d, detected Headphone\n",
@@ -5671,15 +5828,20 @@
if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
pr_err("%s: Detect attempt %d and %d are not same",
__func__, i - 1, i);
- tabla_codec_cleanup_hs_polling(codec);
- tabla_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER,
- false);
- return;
+ inval = true;
+ break;
}
}
- if (plug_type[0] == PLUG_TYPE_HEADPHONE) {
+ if (inval) {
+ pr_debug("%s: Invalid plug type detected\n", __func__);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
+ 0x02, 0x02);
+ tabla_codec_cleanup_hs_polling(codec);
+ tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER, false);
+ } else if (plug_type[0] == PLUG_TYPE_HEADPHONE) {
pr_debug("%s: Headphone Detected\n", __func__);
tabla_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
tabla_codec_cleanup_hs_polling(codec);
@@ -5742,7 +5904,9 @@
tabla_codec_switch_micbias(codec, 0);
tabla_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
tabla_codec_shutdown_hs_removal_detect(codec);
- tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
true);
} else if (is_mb_trigger && !is_removal) {
pr_debug("%s: Waiting for Headphone left trigger\n",
@@ -5804,11 +5968,12 @@
static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_plug_type_cfg *plug_type =
- TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ const struct tabla_mbhc_plug_type_cfg *plug_type =
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ const s16 v_hs_max = tabla_get_current_v_hs_max(tabla);
return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
- && (mic_mv < plug_type->v_hs_max)) ? true : false;
+ && (mic_mv < v_hs_max)) ? true : false;
}
/* called under codec_resource_lock acquisition
@@ -5822,8 +5987,7 @@
short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
unsigned long retry = 0, timeout;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_plug_type_cfg *plug_type =
- TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ const s16 v_hs_max = tabla_get_current_v_hs_max(tabla);
timeout = jiffies + msecs_to_jiffies(TABLA_HS_DETECT_PLUG_TIME_MS);
while (!(timedout = time_after(jiffies, timeout))) {
@@ -5877,7 +6041,7 @@
/* only for non-GPIO remove irq */
if (!tabla->mbhc_cfg.gpio) {
for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (mic_mv[i] < plug_type->v_hs_max)
+ if (mic_mv[i] < v_hs_max)
break;
if (i == MBHC_NUM_DCE_PLUG_DETECT) {
pr_debug("%s: Headset is removed\n", __func__);
@@ -5927,7 +6091,7 @@
bias_value = tabla_codec_sta_dce(codec, 1, true);
pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
tabla_codec_sta_dce_v(codec, 1, bias_value), min_us);
- if (bias_value < (short)priv->mbhc_data.v_ins_h) {
+ if (bias_value < tabla_get_current_v_ins(priv, false)) {
pr_debug("%s: checking false removal\n", __func__);
msleep(500);
removed = !tabla_hs_remove_settle(codec);
@@ -5950,7 +6114,9 @@
tabla_codec_report_plug(codec, 0, SND_JACK_HEADSET);
tabla_codec_cleanup_hs_polling(codec);
- tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
true);
} else {
tabla_codec_start_hs_polling(codec);
@@ -5960,15 +6126,27 @@
static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
{
struct tabla_priv *priv = data;
+ bool vddio;
pr_debug("%s: enter, removal interrupt\n", __func__);
TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
+ vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ priv->mbhc_micbias_switched);
+ if (vddio)
+ __tabla_codec_switch_micbias(priv->codec, 0, false, true);
+
if (priv->mbhc_cfg.gpio)
tabla_hs_remove_irq_gpio(priv);
else
tabla_hs_remove_irq_nogpio(priv);
+ /* if driver turned off vddio switch and headset is not removed,
+ * turn on the vddio switch back, if headset is removed then vddio
+ * switch is off by time now and shouldn't be turn on again from here */
+ if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
+ __tabla_codec_switch_micbias(priv->codec, 1, true, true);
TABLA_RELEASE_LOCK(priv->codec_resource_lock);
+
return IRQ_HANDLED;
}
@@ -6139,7 +6317,9 @@
tabla_codec_calibrate_hs_polling(codec);
if (!tabla->mbhc_cfg.gpio) {
tabla->mbhc_inval_hs_range_override = false;
- rc = tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ rc = tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
false);
if (IS_ERR_VALUE(rc))
@@ -6206,8 +6386,9 @@
if (!tabla->mbhc_cfg.gpio) {
tabla->mbhc_inval_hs_range_override = false;
rc = tabla_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER,
- false);
+ MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ false);
} else {
tabla->mbhc_inval_hs_range_override = true;
/* Enable Mic Bias pull down and HPH Switch to GND */
@@ -6638,6 +6819,8 @@
struct tabla_priv *tabla = file->private_data;
struct snd_soc_codec *codec = tabla->codec;
const struct mbhc_internal_cal_data *p = &tabla->mbhc_data;
+ const s16 v_ins_hu_cur = tabla_get_current_v_ins(tabla, true);
+ const s16 v_ins_h_cur = tabla_get_current_v_ins(tabla, false);
n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
tabla_codec_sta_dce_v(codec, 1, p->dce_z));
@@ -6651,11 +6834,21 @@
n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
p->micb_mv);
- n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
p->v_ins_hu,
- tabla_codec_sta_dce_v(codec, 0, p->v_ins_hu));
- n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
- p->v_ins_h, tabla_codec_sta_dce_v(codec, 1, p->v_ins_h));
+ tabla_codec_sta_dce_v(codec, 0, p->v_ins_hu),
+ p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+ p->v_ins_h, tabla_codec_sta_dce_v(codec, 1, p->v_ins_h),
+ p->v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+ p->adj_v_ins_hu,
+ tabla_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
+ p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+ p->adj_v_ins_h,
+ tabla_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
+ p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
p->v_b1_hu, tabla_codec_sta_dce_v(codec, 0, p->v_b1_hu));
n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
@@ -6673,6 +6866,10 @@
n += scnprintf(buffer + n, size - n, "npoll = %d\n", p->npoll);
n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
p->nbounce_wait);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+ p->v_inval_ins_low);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+ p->v_inval_ins_high);
buffer[n] = 0;
return simple_read_from_buffer(buf, count, pos, buffer, n);
@@ -6716,7 +6913,6 @@
/* Make sure mbhc micbias register addresses are zeroed out */
memset(&tabla->mbhc_bias_regs, 0,
sizeof(struct mbhc_micbias_regs));
- tabla->cfilt_k_value = 0;
tabla->mbhc_micbias_switched = false;
/* Make sure mbhc intenal calibration data is zeroed out */
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 6e80284..38ec27c 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -24,6 +24,7 @@
#define DEFAULT_DCE_STA_WAIT 55
#define DEFAULT_DCE_WAIT 60000
#define DEFAULT_STA_WAIT 5000
+#define VDDIO_MICBIAS_MV 1800
#define STA 0
#define DCE 1
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 0d45e82..b574fce 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -624,7 +624,7 @@
#undef S
#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla_cal)->X) = (Y))
S(v_no_mic, 30);
- S(v_hs_max, 1550);
+ S(v_hs_max, 2400);
#undef S
#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
S(c[0], 62);
@@ -658,8 +658,8 @@
btn_low[7] = 164;
btn_high[7] = 250;
n_ready = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_READY);
- n_ready[0] = 48;
- n_ready[1] = 38;
+ n_ready[0] = 80;
+ n_ready[1] = 68;
n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
n_cic[0] = 60;
n_cic[1] = 47;