ASoC: wcd9310: Add MBHC Calibration support.
The MBHC portion of hardware on WCD9310 needs to be calibrated
in order to preform accurate operations on different types of
headset. Add logic to parametrize the codec driver to use
calibration data. The default data is provided by machine driver.
Separate out generic data to the machine driver, whereas WCD9310
specific data exists in the codec driver.
CRs-fixed: 327078
Change-Id: I4db74863c46e8cfbb35c859d520541ad12e6c93a
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 33afdc9..0370b17 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -48,6 +48,9 @@
#define TABLA_OCP_ATTEMPT 1
+#define TABLA_MCLK_RATE_12288KHZ 12288000
+#define TABLA_MCLK_RATE_9600KHZ 9600000
+
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -93,8 +96,31 @@
TABLA_HPHR_DAC_OFF_ACK
};
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+ u16 dce_z;
+ u16 dce_mb;
+ u16 sta_z;
+ u16 sta_mb;
+ u32 t_dce;
+ u32 t_sta;
+ u32 micb_mv;
+ u16 v_ins_hu;
+ u16 v_ins_h;
+ u16 v_b1_hu;
+ u16 v_b1_h;
+ u16 v_b1_huc;
+ u16 v_brh;
+ u16 v_brl;
+ u16 v_no_mic;
+ u8 nready;
+ u8 npoll;
+ u8 nbounce_wait;
+};
+
struct tabla_priv {
struct snd_soc_codec *codec;
+ u32 mclk_freq;
u32 adc_count;
u32 cfilt1_cnt;
u32 cfilt2_cnt;
@@ -108,7 +134,17 @@
bool fake_insert_context;
int buttons_pressed;
- struct tabla_mbhc_calibration *calibration;
+ enum tabla_micbias_num micbias;
+ /* void* calibration contains:
+ * struct tabla_mbhc_general_cfg generic;
+ * struct tabla_mbhc_plug_detect_cfg plug_det;
+ * struct tabla_mbhc_plug_type_cfg plug_type;
+ * struct tabla_mbhc_btn_detect_cfg btn_det;
+ * struct tabla_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ struct mbhc_internal_cal_data mbhc_data;
struct snd_soc_jack *headset_jack;
struct snd_soc_jack *button_jack;
@@ -145,6 +181,9 @@
u8 hphlocp_cnt; /* headphone left ocp retry */
u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ /* Callback function to enable MCLK */
+ int (*mclk_cb) (struct snd_soc_codec*, int);
};
#ifdef CONFIG_DEBUG_FS
@@ -1409,7 +1448,7 @@
if (tabla->mbhc_polling_active) {
tabla_codec_pause_hs_polling(codec);
- /* Enable Mic Bias switch to VDDIO */
+ /* VDDIO switch enabled */
tabla->cfilt_k_value = snd_soc_read(codec,
tabla->mbhc_bias_regs.cfilt_val);
cfilt_k_val = tabla_find_k_value(
@@ -1425,8 +1464,7 @@
tabla_codec_start_hs_polling(codec);
tabla->mbhc_micbias_switched = true;
- pr_debug("%s: Enabled MBHC Mic bias to VDDIO Switch\n",
- __func__);
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
}
break;
@@ -1436,7 +1474,7 @@
tabla_codec_pause_hs_polling(codec);
mbhc_was_polling = true;
}
- /* Disable Mic Bias switch to VDDIO */
+ /* VDDIO switch disabled */
if (tabla->cfilt_k_value != 0)
snd_soc_update_bits(codec,
tabla->mbhc_bias_regs.cfilt_val, 0XFC,
@@ -1450,8 +1488,7 @@
tabla_codec_start_hs_polling(codec);
tabla->mbhc_micbias_switched = false;
- pr_debug("%s: Disabled MBHC Mic bias to VDDIO Switch\n",
- __func__);
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
}
break;
}
@@ -1516,7 +1553,7 @@
break;
case SND_SOC_DAPM_POST_PMU:
if (tabla->mbhc_polling_active &&
- (tabla->calibration->bias == micb_line)) {
+ tabla->micbias == micb_line) {
tabla_codec_pause_hs_polling(codec);
tabla_codec_start_hs_polling(codec);
}
@@ -1673,10 +1710,8 @@
tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
tabla->hph_status,
TABLA_JACK_MASK);
- snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
/* reset retry counter as PA is turned off signifying
* start of new OCP detection session
*/
@@ -1761,10 +1796,9 @@
struct mbhc_micbias_regs *micbias_regs)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_calibration *calibration = tabla->calibration;
unsigned int cfilt;
- switch (calibration->bias) {
+ switch (tabla->micbias) {
case TABLA_MICBIAS1:
cfilt = tabla->pdata->micbias.bias1_cfilt_sel;
micbias_regs->mbhc_reg = TABLA_A_MICB_1_MBHC;
@@ -1801,14 +1835,17 @@
case TABLA_CFILT1_SEL:
micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_1_VAL;
micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_1_CTL;
+ tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt1_mv;
break;
case TABLA_CFILT2_SEL:
micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_2_VAL;
micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_2_CTL;
+ tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt2_mv;
break;
case TABLA_CFILT3_SEL:
micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_3_VAL;
micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_3_CTL;
+ tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt3_mv;
break;
}
}
@@ -2612,24 +2649,46 @@
static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
{
- /* TODO store register values in calibration */
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B5_CTL, 0x20);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B6_CTL, 0xFF);
+ u8 *n_cic;
+ struct tabla_mbhc_btn_detect_cfg *btn_det;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B10_CTL, 0xFF);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B9_CTL, 0x20);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0xF8);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0xEE);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL, 0xFC);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL, 0xCE);
+ 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);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 3);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL, 9);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL, 30);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 120);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 0x78, 0x58);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_B2_CTL, 11);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL,
+ tabla->mbhc_data.v_b1_hu & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL,
+ (tabla->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B5_CTL,
+ tabla->mbhc_data.v_b1_h & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B6_CTL,
+ (tabla->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B9_CTL,
+ tabla->mbhc_data.v_brh & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B10_CTL,
+ (tabla->mbhc_data.v_brh >> 8) & 0xFF);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B11_CTL,
+ tabla->mbhc_data.v_brl & 0xFF);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B12_CTL,
+ (tabla->mbhc_data.v_brl >> 8) & 0xFF);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL,
+ tabla->mbhc_data.nready);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL,
+ tabla->mbhc_data.npoll);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL,
+ tabla->mbhc_data.nbounce_wait);
+
+ n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, n_cic[0]);
}
static int tabla_startup(struct snd_pcm_substream *substream,
@@ -2956,9 +3015,9 @@
return bias_value;
}
-static short tabla_codec_measure_micbias_voltage(struct snd_soc_codec *codec,
- int dce)
+static short tabla_codec_sta_dce(struct snd_soc_codec *codec, int dce)
{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
short bias_value;
if (dce) {
@@ -2966,32 +3025,33 @@
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
- usleep_range(60000, 60000);
+ usleep_range(tabla->mbhc_data.t_dce,
+ tabla->mbhc_data.t_dce);
bias_value = tabla_codec_read_dce_result(codec);
} else {
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(5000, 5000);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
usleep_range(50, 50);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
+ usleep_range(tabla->mbhc_data.t_sta,
+ tabla->mbhc_data.t_sta);
bias_value = tabla_codec_read_sta_result(codec);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x0);
}
- pr_debug("read microphone bias value %x\n", bias_value);
+ pr_debug("read microphone bias value %04x\n", bias_value);
return bias_value;
}
static short tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_calibration *calibration = tabla->calibration;
short bias_value;
u8 cfilt_mode;
- if (!calibration) {
+ if (!tabla->calibration) {
pr_err("Error, no tabla calibration\n");
return -ENODEV;
}
@@ -3033,9 +3093,9 @@
tabla_codec_calibrate_hs_polling(codec);
- bias_value = tabla_codec_measure_micbias_voltage(codec, 0);
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.cfilt_ctl, 0x40, cfilt_mode);
+ bias_value = tabla_codec_sta_dce(codec, 0);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
return bias_value;
@@ -3045,11 +3105,14 @@
int insertion)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_calibration *calibration = tabla->calibration;
int central_bias_enabled = 0;
+ const struct tabla_mbhc_general_cfg *generic =
+ TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
+ const struct tabla_mbhc_plug_detect_cfg *plug_det =
+ TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->calibration);
u8 wg_time;
- if (!calibration) {
+ if (!tabla->calibration) {
pr_err("Error, no tabla calibration\n");
return -EINVAL;
}
@@ -3070,7 +3133,7 @@
/* Enable HPH Schmitt Trigger */
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x11, 0x11);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x0C,
- calibration->hph_current << 2);
+ plug_det->hph_current << 2);
/* Turn off HPH PAs and DAC's during insertion detection to
* avoid false insertion interrupts
@@ -3079,9 +3142,9 @@
tabla_codec_switch_micbias(codec, 0);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30, 0x00);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0x00);
+ 0xC0, 0x00);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0x00);
+ 0xC0, 0x00);
usleep_range(wg_time * 1000, wg_time * 1000);
/* setup for insetion detection */
@@ -3093,10 +3156,10 @@
/* enable the mic line schmitt trigger */
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x60,
- calibration->mic_current << 5);
+ plug_det->mic_current << 5);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
0x80, 0x80);
- usleep_range(calibration->mic_pid, calibration->mic_pid);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
0x10, 0x10);
@@ -3109,8 +3172,8 @@
tabla_codec_enable_config_mode(codec, 1);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
0x06, 0);
- usleep_range(calibration->shutdown_plug_removal,
- calibration->shutdown_plug_removal);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
tabla_codec_enable_config_mode(codec, 0);
} else
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
@@ -3122,8 +3185,8 @@
/* If central bandgap disabled */
if (!(snd_soc_read(codec, TABLA_A_PIN_CTL_OE1) & 1)) {
snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x3, 0x3);
- usleep_range(calibration->bg_fast_settle,
- calibration->bg_fast_settle);
+ usleep_range(generic->t_bg_fast_settle,
+ generic->t_bg_fast_settle);
central_bias_enabled = 1;
}
@@ -3131,14 +3194,14 @@
if (snd_soc_read(codec, TABLA_A_PIN_CTL_OE0) & 0x80) {
snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x10, 0);
snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0x80);
- usleep_range(calibration->tldoh, calibration->tldoh);
+ usleep_range(generic->t_ldoh, generic->t_ldoh);
snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0);
if (central_bias_enabled)
snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x1, 0);
}
- snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, calibration->bias);
+ snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, tabla->micbias);
tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
@@ -3163,10 +3226,60 @@
wake_up(&tabla->pm_wq);
}
+static u16 tabla_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
+ s16 vin_mv)
+{
+ short diff, zero;
+ struct tabla_priv *tabla;
+ u32 mb_mv, in;
+
+ tabla = snd_soc_codec_get_drvdata(codec);
+ mb_mv = tabla->mbhc_data.micb_mv;
+
+ if (mb_mv == 0) {
+ pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dce) {
+ diff = tabla->mbhc_data.dce_mb - tabla->mbhc_data.dce_z;
+ zero = tabla->mbhc_data.dce_z;
+ } else {
+ diff = tabla->mbhc_data.sta_mb - tabla->mbhc_data.sta_z;
+ zero = tabla->mbhc_data.sta_z;
+ }
+ in = (u32) diff * vin_mv;
+
+ return (u16) (in / mb_mv) + zero;
+}
+
+static s32 tabla_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
+ u16 bias_value)
+{
+ struct tabla_priv *tabla;
+ s32 mv;
+
+ tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (dce) {
+ mv = ((s32)bias_value - (s32)tabla->mbhc_data.dce_z) *
+ (s32)tabla->mbhc_data.micb_mv /
+ (s32)(tabla->mbhc_data.dce_mb - tabla->mbhc_data.dce_z);
+ } else {
+ mv = ((s32)bias_value - (s32)tabla->mbhc_data.sta_z) *
+ (s32)tabla->mbhc_data.micb_mv /
+ (s32)(tabla->mbhc_data.sta_mb - tabla->mbhc_data.sta_z);
+ }
+
+ return mv;
+}
+
static void btn0_lpress_fn(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct tabla_priv *tabla;
+ short bias_value;
+ int dce_mv, sta_mv;
pr_debug("%s:\n", __func__);
@@ -3175,8 +3288,15 @@
if (tabla) {
if (tabla->button_jack) {
- pr_debug("%s: Reporting long button press event\n",
- __func__);
+ bias_value = tabla_codec_read_sta_result(tabla->codec);
+ sta_mv = tabla_codec_sta_dce_v(tabla->codec, 0,
+ bias_value);
+ bias_value = tabla_codec_read_dce_result(tabla->codec);
+ dce_mv = tabla_codec_sta_dce_v(tabla->codec, 1,
+ bias_value);
+ pr_debug("%s: Reporting long button press event"
+ " STA: %d, DCE: %d\n", __func__,
+ sta_mv, dce_mv);
tabla_snd_soc_jack_report(tabla, tabla->button_jack,
SND_JACK_BTN_0,
SND_JACK_BTN_0);
@@ -3188,9 +3308,246 @@
tabla_unlock_sleep(tabla);
}
+void tabla_mbhc_cal(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla;
+ struct tabla_mbhc_btn_detect_cfg *btn_det;
+ u8 cfilt_mode, bg_mode;
+ u8 ncic, nmeas, navg;
+ u32 mclk_rate;
+ u32 dce_wait, sta_wait;
+ u8 *n_cic;
+
+ tabla = snd_soc_codec_get_drvdata(codec);
+
+ /* First compute the DCE / STA wait times
+ * depending on tunable parameters.
+ * The value is computed in microseconds
+ */
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+ n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+ ncic = n_cic[0];
+ nmeas = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration)->n_meas;
+ navg = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration)->mbhc_navg;
+ mclk_rate = tabla->mclk_freq;
+ dce_wait = (1000 * 512 * ncic * nmeas) / (mclk_rate / 1000);
+ if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ)
+ dce_wait = dce_wait + 10000;
+ else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ)
+ dce_wait = dce_wait + 9810;
+ else
+ WARN(1, "Unsupported mclk freq %d\n", tabla->mclk_freq);
+
+ sta_wait = (1000 * 128 * navg) / (mclk_rate / 1000);
+
+ /* Add 10 microseconds to handle error margin */
+ dce_wait = dce_wait + 10;
+ sta_wait = sta_wait + 10;
+
+ tabla->mbhc_data.t_dce = dce_wait;
+ tabla->mbhc_data.t_sta = sta_wait;
+
+ /* LDOH and CFILT are already configured during pdata handling.
+ * Only need to make sure CFILT and bandgap are in Fast mode.
+ * Need to restore defaults once calculation is done.
+ */
+ cfilt_mode = snd_soc_read(codec, tabla->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+ bg_mode = snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x02,
+ 0x02);
+
+ /* Micbias, CFILT, LDOH, MBHC MUX mode settings
+ * to perform ADC calibration
+ */
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x60,
+ tabla->micbias << 5);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x60, 0x60);
+ snd_soc_write(codec, TABLA_A_TX_7_MBHC_TEST_CTL, 0x78);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+ /* DCE measurement for 0 volts */
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(tabla->mbhc_data.t_dce, tabla->mbhc_data.t_dce);
+ tabla->mbhc_data.dce_z = tabla_codec_read_dce_result(codec);
+
+ /* DCE measurment for MB voltage */
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(tabla->mbhc_data.t_dce, tabla->mbhc_data.t_dce);
+ tabla->mbhc_data.dce_mb = tabla_codec_read_dce_result(codec);
+
+ /* Sta measuremnt for 0 volts */
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(tabla->mbhc_data.t_sta, tabla->mbhc_data.t_sta);
+ tabla->mbhc_data.sta_z = tabla_codec_read_sta_result(codec);
+
+ /* STA Measurement for MB Voltage */
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(tabla->mbhc_data.t_sta, tabla->mbhc_data.t_sta);
+ tabla->mbhc_data.sta_mb = tabla_codec_read_sta_result(codec);
+
+ /* Restore default settings. */
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
+
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
+ usleep_range(100, 100);
+}
+
+void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg* btn_det,
+ const enum tabla_mbhc_btn_det_mem mem)
+{
+ void *ret = &btn_det->_v_btn_low;
+
+ switch (mem) {
+ case TABLA_BTN_DET_GAIN:
+ ret += sizeof(btn_det->_n_cic);
+ case TABLA_BTN_DET_N_CIC:
+ ret += sizeof(btn_det->_n_ready);
+ case TABLA_BTN_DET_V_N_READY:
+ ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+ case TABLA_BTN_DET_V_BTN_HIGH:
+ ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+ case TABLA_BTN_DET_V_BTN_LOW:
+ /* do nothing */
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+static void tabla_mbhc_calc_thres(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla;
+ s16 btn_mv = 0, btn_delta_mv;
+ struct tabla_mbhc_btn_detect_cfg *btn_det;
+ struct tabla_mbhc_plug_type_cfg *plug_type;
+ u16 *btn_high;
+ int i;
+
+ tabla = snd_soc_codec_get_drvdata(codec);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+ plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
+
+ if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ) {
+ tabla->mbhc_data.nready = 3;
+ tabla->mbhc_data.npoll = 9;
+ tabla->mbhc_data.nbounce_wait = 30;
+ } else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ) {
+ tabla->mbhc_data.nready = 2;
+ tabla->mbhc_data.npoll = 7;
+ tabla->mbhc_data.nbounce_wait = 23;
+ }
+
+ tabla->mbhc_data.v_ins_hu =
+ tabla_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
+ tabla->mbhc_data.v_ins_h =
+ tabla_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
+
+ 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);
+
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+ tabla->mbhc_data.v_b1_huc =
+ tabla_codec_v_sta_dce(codec, DCE, btn_delta_mv);
+
+ tabla->mbhc_data.v_brh = tabla->mbhc_data.v_b1_h;
+ tabla->mbhc_data.v_brl = 0xFA55;
+
+ tabla->mbhc_data.v_no_mic =
+ tabla_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
+}
+
+void tabla_mbhc_init(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla;
+ struct tabla_mbhc_general_cfg *generic;
+ struct tabla_mbhc_btn_detect_cfg *btn_det;
+ int n;
+ u8 tabla_ver;
+ u8 *n_cic, *gain;
+
+ tabla = snd_soc_codec_get_drvdata(codec);
+ generic = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+
+ tabla_ver = snd_soc_read(codec, TABLA_A_CHIP_VERSION);
+ tabla_ver &= 0x1F;
+
+ for (n = 0; n < 8; n++) {
+ if ((tabla_ver != TABLA_VERSION_1_0 &&
+ tabla_ver != TABLA_VERSION_1_1) || n != 7) {
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_MBHC_FEATURE_B1_CFG,
+ 0x07, n);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_FEATURE_B2_CFG,
+ btn_det->c[n]);
+ }
+ }
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x07,
+ btn_det->nc);
+
+ n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
+ n_cic[0]);
+
+ gain = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_GAIN);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78, gain[0] << 3);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+ generic->mbhc_nsa << 4);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+ btn_det->n_meas);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+
+ snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x03, TABLA_MICBIAS2);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+}
+
int tabla_hs_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *headset_jack, struct snd_soc_jack *button_jack,
- struct tabla_mbhc_calibration *calibration)
+ struct snd_soc_jack *headset_jack,
+ struct snd_soc_jack *button_jack,
+ void *calibration, enum tabla_micbias_num micbias,
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int),
+ int read_fw_bin, u32 mclk_rate)
{
struct tabla_priv *tabla;
int rc;
@@ -3202,7 +3559,10 @@
tabla = snd_soc_codec_get_drvdata(codec);
tabla->headset_jack = headset_jack;
tabla->button_jack = button_jack;
+ tabla->micbias = micbias;
tabla->calibration = calibration;
+ tabla->mclk_cb = mclk_cb_fn;
+ tabla->mclk_freq = mclk_rate;
tabla_get_mbhc_micbias_regs(codec, &tabla->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
@@ -3212,7 +3572,19 @@
INIT_DELAYED_WORK(&tabla->btn0_dwork, btn0_lpress_fn);
INIT_WORK(&tabla->hphlocp_work, hphlocp_off_report);
INIT_WORK(&tabla->hphrocp_work, hphrocp_off_report);
- rc = tabla_codec_enable_hs_detect(codec, 1);
+
+ if (!read_fw_bin) {
+ tabla->mclk_cb(codec, 1);
+ tabla_mbhc_init(codec);
+ tabla_mbhc_cal(codec);
+ tabla_mbhc_calc_thres(codec);
+ tabla->mclk_cb(codec, 0);
+ tabla_codec_calibrate_hs_polling(codec);
+ rc = tabla_codec_enable_hs_detect(codec, 1);
+ } else {
+ pr_err("%s: MBHC firmware read not supported\n", __func__);
+ rc = -EINVAL;
+ }
if (!IS_ERR_VALUE(rc)) {
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
@@ -3238,12 +3610,14 @@
tabla_lock_sleep(priv);
bias_value = tabla_codec_read_dce_result(codec);
- pr_debug("%s: button press interrupt, bias value(DCE Read)=%d\n",
- __func__, bias_value);
+ pr_debug("%s: button press interrupt, DCE: %d,%d\n",
+ __func__, bias_value,
+ tabla_codec_sta_dce_v(codec, 1, bias_value));
bias_value = tabla_codec_read_sta_result(codec);
- pr_debug("%s: button press interrupt, bias value(STA Read)=%d\n",
- __func__, bias_value);
+ pr_debug("%s: button press interrupt, STA: %d,%d\n",
+ __func__, bias_value,
+ tabla_codec_sta_dce_v(codec, 0, bias_value));
/*
* TODO: If button pressed is not button 0,
* report the button press event immediately.
@@ -3265,54 +3639,44 @@
{
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
- int ret, mic_voltage;
+ int ret, mb_v;
pr_debug("%s\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
tabla_lock_sleep(priv);
- mic_voltage = tabla_codec_read_dce_result(codec);
- pr_debug("%s: Microphone Voltage on release(DCE Read) = %d\n",
- __func__, mic_voltage);
-
if (priv->buttons_pressed & SND_JACK_BTN_0) {
ret = cancel_delayed_work(&priv->btn0_dwork);
if (ret == 0) {
-
pr_debug("%s: Reporting long button release event\n",
__func__);
- if (priv->button_jack) {
+ if (priv->button_jack)
tabla_snd_soc_jack_report(priv,
priv->button_jack, 0,
SND_JACK_BTN_0);
- }
-
} else {
/* if scheduled btn0_dwork is canceled from here,
* we have to unlock from here instead btn0_work */
tabla_unlock_sleep(priv);
- mic_voltage =
- tabla_codec_measure_micbias_voltage(codec, 0);
- pr_debug("%s: Mic Voltage on release(new STA) = %d\n",
- __func__, mic_voltage);
+ mb_v = tabla_codec_sta_dce(codec, 0);
+ pr_debug("%s: Mic Voltage on release STA: %d,%d\n",
+ __func__, mb_v,
+ tabla_codec_sta_dce_v(codec, 0, mb_v));
- if (mic_voltage < -2000 || mic_voltage > -670) {
+ if (mb_v < -2000 || mb_v > -670)
pr_debug("%s: Fake buttton press interrupt\n",
__func__);
- } else {
-
- if (priv->button_jack) {
- pr_debug("%s:reporting short button press and release\n",
- __func__);
-
- tabla_snd_soc_jack_report(priv,
- priv->button_jack,
- SND_JACK_BTN_0, SND_JACK_BTN_0);
- tabla_snd_soc_jack_report(priv,
- priv->button_jack,
- 0, SND_JACK_BTN_0);
- }
+ else if (priv->button_jack) {
+ pr_debug("%s:reporting short button "
+ "press and release\n", __func__);
+ tabla_snd_soc_jack_report(priv,
+ priv->button_jack,
+ SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
+ tabla_snd_soc_jack_report(priv,
+ priv->button_jack,
+ 0, SND_JACK_BTN_0);
}
}
@@ -3327,7 +3691,8 @@
static void tabla_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- struct tabla_mbhc_calibration *calibration = tabla->calibration;
+ const struct tabla_mbhc_general_cfg *generic =
+ TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
tabla_codec_enable_config_mode(codec, 1);
@@ -3335,10 +3700,10 @@
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
- snd_soc_update_bits(codec,
- tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
- usleep_range(calibration->shutdown_plug_removal,
- calibration->shutdown_plug_removal);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
@@ -3461,11 +3826,12 @@
{
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
+ const struct tabla_mbhc_plug_detect_cfg *plug_det =
+ TABLA_MBHC_CAL_PLUG_DET_PTR(priv->calibration);
int ldo_h_on, micb_cfilt_on;
- short mic_voltage;
- short threshold_no_mic = 0xF7F6;
- short threshold_fake_insert = 0xFD30;
+ short mb_v;
u8 is_removal;
+ int mic_mv;
pr_debug("%s\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
@@ -3475,8 +3841,7 @@
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
/* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg,
- 0x90, 0x00);
+ snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
if (priv->fake_insert_context) {
@@ -3488,25 +3853,26 @@
return IRQ_HANDLED;
}
-
ldo_h_on = snd_soc_read(codec, TABLA_A_LDO_H_MODE_1) & 0x80;
- micb_cfilt_on = snd_soc_read(codec,
- priv->mbhc_bias_regs.cfilt_ctl) & 0x80;
+ micb_cfilt_on = snd_soc_read(codec, priv->mbhc_bias_regs.cfilt_ctl)
+ & 0x80;
if (!ldo_h_on)
snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x80);
if (!micb_cfilt_on)
snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
- 0x80, 0x80);
-
- usleep_range(priv->calibration->setup_plug_removal_delay,
- priv->calibration->setup_plug_removal_delay);
+ 0x80, 0x80);
+ if (plug_det->t_ins_complete > 20)
+ msleep(plug_det->t_ins_complete);
+ else
+ usleep_range(plug_det->t_ins_complete * 1000,
+ plug_det->t_ins_complete * 1000);
if (!ldo_h_on)
snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x0);
if (!micb_cfilt_on)
snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
- 0x80, 0x0);
+ 0x80, 0x0);
if (is_removal) {
/*
@@ -3543,23 +3909,23 @@
return IRQ_HANDLED;
}
- mic_voltage = tabla_codec_setup_hs_polling(codec);
+ mb_v = tabla_codec_setup_hs_polling(codec);
+ mic_mv = tabla_codec_sta_dce_v(codec, 0, mb_v);
- if (mic_voltage > threshold_fake_insert) {
- pr_debug("%s: Fake insertion interrupt, mic_voltage = %x\n",
- __func__, mic_voltage);
+ if (mb_v > (short) priv->mbhc_data.v_ins_hu) {
+ pr_debug("%s: Fake insertion interrupt, STA: %d,%d\n",
+ __func__, mb_v, mic_mv);
/* Disable HPH trigger and enable MIC line trigger */
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x12, 0x00);
snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x60,
- priv->calibration->mic_current << 5);
+ plug_det->mic_current << 5);
snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(priv->calibration->mic_pid,
- priv->calibration->mic_pid);
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
+ 0x10, 0x10);
/* Setup for insertion detection */
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x2, 0);
@@ -3567,13 +3933,13 @@
tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
- } else if (mic_voltage < threshold_no_mic) {
- pr_debug("%s: Headphone Detected, mic_voltage = %x\n",
- __func__, mic_voltage);
+ } else if (mb_v < (short) priv->mbhc_data.v_no_mic) {
+ pr_debug("%s: Headphone Detected, mb_v: %d,%d\n",
+ __func__, mb_v, mic_mv);
priv->hph_status |= SND_JACK_HEADPHONE;
if (priv->headset_jack) {
pr_debug("%s: Reporting insertion %d\n", __func__,
- SND_JACK_HEADPHONE);
+ SND_JACK_HEADPHONE);
tabla_snd_soc_jack_report(priv, priv->headset_jack,
priv->hph_status,
TABLA_JACK_MASK);
@@ -3582,12 +3948,12 @@
tabla_codec_enable_hs_detect(codec, 0);
tabla_sync_hph_state(priv);
} else {
- pr_debug("%s: Headset detected, mic_voltage = %x\n",
- __func__, mic_voltage);
+ pr_debug("%s: Headset detected, mb_v: %d,%d\n",
+ __func__, mb_v, mic_mv);
priv->hph_status |= SND_JACK_HEADSET;
if (priv->headset_jack) {
pr_debug("%s: Reporting insertion %d\n", __func__,
- SND_JACK_HEADSET);
+ SND_JACK_HEADSET);
tabla_snd_soc_jack_report(priv, priv->headset_jack,
priv->hph_status,
TABLA_JACK_MASK);
@@ -3604,6 +3970,8 @@
{
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
+ const struct tabla_mbhc_general_cfg *generic =
+ TABLA_MBHC_CAL_GENERAL_PTR(priv->calibration);
short bias_value;
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
@@ -3611,13 +3979,14 @@
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
tabla_lock_sleep(priv);
- usleep_range(priv->calibration->shutdown_plug_removal,
- priv->calibration->shutdown_plug_removal);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
- bias_value = tabla_codec_measure_micbias_voltage(codec, 1);
- pr_debug("removal interrupt, bias value is %d\n", bias_value);
+ bias_value = tabla_codec_sta_dce(codec, 1);
+ pr_debug("removal interrupt, DCE: %d,%d\n",
+ bias_value, tabla_codec_sta_dce_v(codec, 1, bias_value));
- if (bias_value < -90) {
+ if (bias_value < (short) priv->mbhc_data.v_ins_h) {
pr_debug("False alarm, headset not actually removed\n");
tabla_codec_start_hs_polling(codec);
} else {
@@ -3940,6 +4309,11 @@
tabla->cfilt_k_value = 0;
tabla->mbhc_micbias_switched = false;
+ /* Make sure mbhc intenal calibration data is zeroed out */
+ memset(&tabla->mbhc_data, 0,
+ sizeof(struct mbhc_internal_cal_data));
+ tabla->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+ tabla->mbhc_data.t_sta = DEFAULT_STA_WAIT;
snd_soc_codec_set_drvdata(codec, tabla);
tabla->mclk_enabled = false;
@@ -3981,7 +4355,8 @@
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
tabla_version = snd_soc_read(codec, TABLA_A_CHIP_VERSION);
- pr_info("%s : Tabla version reg 0x%2x\n", __func__, (u32)tabla_version);
+ pr_info("%s : Tabla version reg 0x%2x\n", __func__,
+ (u32)tabla_version);
tabla_version &= 0x1F;
pr_info("%s : Tabla version %u\n", __func__, (u32)tabla_version);
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 32fc48f..66c3e39 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -9,7 +9,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <sound/soc.h>
#define TABLA_VERSION_1_0 0
@@ -22,6 +21,13 @@
#define TABLA_REG_VAL(reg, val) {reg, 0, val}
+
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define STA 0
+#define DCE 1
+
extern const u8 tabla_reg_readable[TABLA_CACHE_SIZE];
extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
@@ -39,26 +45,116 @@
TABLA_PID_MIC_20_UA,
};
-struct tabla_mbhc_calibration {
- enum tabla_micbias_num bias;
- int tldoh;
- int bg_fast_settle;
- enum tabla_pid_current mic_current;
- int mic_pid;
- enum tabla_pid_current hph_current;
- int setup_plug_removal_delay;
- int shutdown_plug_removal;
-};
-
struct tabla_reg_mask_val {
u16 reg;
u8 mask;
u8 val;
};
+enum tabla_mbhc_clk_freq {
+ TABLA_MCLK_12P2MHZ = 0,
+ TABLA_MCLK_9P6MHZ,
+ TABLA_NUM_CLK_FREQS,
+};
+
+enum tabla_mbhc_analog_pwr_cfg {
+ TABLA_ANALOG_PWR_COLLAPSED = 0,
+ TABLA_ANALOG_PWR_ON,
+ TABLA_NUM_ANALOG_PWR_CONFIGS,
+};
+
+enum tabla_mbhc_btn_det_mem {
+ TABLA_BTN_DET_V_BTN_LOW,
+ TABLA_BTN_DET_V_BTN_HIGH,
+ TABLA_BTN_DET_V_N_READY,
+ TABLA_BTN_DET_N_CIC,
+ TABLA_BTN_DET_GAIN
+};
+
+struct tabla_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct tabla_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct tabla_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+
+struct tabla_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[TABLA_NUM_CLK_FREQS];
+ u8 _n_cic[TABLA_NUM_CLK_FREQS];
+ u8 _gain[TABLA_NUM_CLK_FREQS];
+} __packed;
+
+struct tabla_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
extern int tabla_hs_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *headset_jack, struct snd_soc_jack *button_jack,
- struct tabla_mbhc_calibration *calibration);
+ struct snd_soc_jack *headset_jack,
+ struct snd_soc_jack *button_jack,
+ void *calibration, enum tabla_micbias_num micbis,
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int),
+ int read_fw_bin, u32 mclk_rate);
struct anc_header {
u32 reserved[3];
@@ -66,3 +162,38 @@
};
extern int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable);
+
+extern void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg
+ *btn_det,
+ const enum tabla_mbhc_btn_det_mem mem);
+
+#define TABLA_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(enum tabla_micbias_num) + \
+ sizeof(struct tabla_mbhc_general_cfg) + \
+ sizeof(struct tabla_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct tabla_mbhc_plug_type_cfg) + \
+ sizeof(struct tabla_mbhc_btn_detect_cfg) + \
+ sizeof(struct tabla_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+#define TABLA_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct tabla_mbhc_general_cfg *) cali)
+#define TABLA_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct tabla_mbhc_plug_detect_cfg *) \
+ &(TABLA_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct tabla_mbhc_plug_type_cfg *) \
+ &(TABLA_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct tabla_mbhc_btn_detect_cfg *) \
+ &(TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct tabla_mbhc_imped_detect_cfg *) \
+ (((void *)&TABLA_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (TABLA_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 870bd20..bd41e0b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -52,6 +53,11 @@
#define GPIO_AUX_PCM_SYNC 45
#define GPIO_AUX_PCM_CLK 46
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 3
+#define TABLA_MBHC_DEF_RLOADS 5
+
static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
static int msm_spk_control;
@@ -63,17 +69,6 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
-struct tabla_mbhc_calibration tabla_calib = {
- .bias = TABLA_MICBIAS2,
- .tldoh = 100,
- .bg_fast_settle = 100,
- .mic_current = TABLA_PID_MIC_5_UA,
- .mic_pid = 100,
- .hph_current = TABLA_PID_MIC_5_UA,
- .setup_plug_removal_delay = 1000000,
- .shutdown_plug_removal = 100000,
-};
-
static struct clk *codec_clk;
static int clk_users;
@@ -82,6 +77,8 @@
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
+static void *tabla_mbhc_cal;
+
static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
{
int ret = 0;
@@ -303,6 +300,42 @@
return 0;
}
+int msm_enable_codec_ext_clk(
+ struct snd_soc_codec *codec, int enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ clk_users++;
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+ if (clk_users != 1)
+ return 0;
+
+ codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+ if (codec_clk) {
+ clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
+ clk_enable(codec_clk);
+ tabla_mclk_enable(codec, 1);
+ } else {
+ pr_err("%s: Error setting Tabla MCLK\n", __func__);
+ clk_users--;
+ return -EINVAL;
+ }
+ } else {
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+ if (clk_users == 0)
+ return 0;
+ clk_users--;
+ if (!clk_users) {
+ pr_debug("%s: disabling MCLK. clk_users = %d\n",
+ __func__, clk_users);
+ clk_disable(codec_clk);
+ clk_put(codec_clk);
+ tabla_mclk_enable(codec, 0);
+ }
+ }
+ return 0;
+}
+
static int msm_mclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -570,6 +603,74 @@
return 0;
}
+static void *def_tabla_mbhc_cal(void)
+{
+ void *tabla_cal;
+ struct tabla_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_cic, *gain;
+
+ tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
+ TABLA_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!tabla_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((TABLA_MBHC_CAL_GENERAL_PTR(tabla_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_DET_PTR(tabla_cal)->X) = (Y))
+ S(mic_current, TABLA_PID_MIC_5_UA);
+ S(hph_current, TABLA_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 1000);
+ S(t_ins_retry, 200);
+#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, 1450);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
+ S(c[0], 6);
+ S(c[1], 10);
+ S(c[2], 10);
+ S(c[3], 14);
+ S(c[4], 14);
+ S(c[5], 16);
+ S(c[6], 0);
+ S(c[7], 0);
+ S(nc, 5);
+ S(n_meas, 11);
+ S(mbhc_nsc, 11);
+ S(num_btn, TABLA_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
+ btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
+ btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = 0;
+ btn_high[0] = 40;
+ btn_low[1] = 60;
+ btn_high[1] = 140;
+ btn_low[2] = 160;
+ btn_high[2] = 240;
+ n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
+ n_cic[0] = 120;
+ n_cic[1] = 94;
+ gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return tabla_cal;
+}
+
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
int err;
@@ -618,7 +719,9 @@
return err;
}
- tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_calib);
+ tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
+ TABLA_MICBIAS2, msm_enable_codec_ext_clk, 0,
+ TABLA_EXT_CLK_RATE);
return 0;
}
@@ -1122,9 +1225,16 @@
return -ENODEV;
}
+ tabla_mbhc_cal = def_tabla_mbhc_cal();
+ if (!tabla_mbhc_cal) {
+ pr_err("Calibration data allocation failed\n");
+ return -ENOMEM;
+ }
+
msm_snd_device = platform_device_alloc("soc-audio", 0);
if (!msm_snd_device) {
pr_err("Platform device allocation failed\n");
+ kfree(tabla_mbhc_cal);
return -ENOMEM;
}
@@ -1132,6 +1242,7 @@
ret = platform_device_add(msm_snd_device);
if (ret) {
platform_device_put(msm_snd_device);
+ kfree(tabla_mbhc_cal);
return ret;
}
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 2e98627..4047feb 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -52,6 +53,11 @@
#define GPIO_AUX_PCM_SYNC 65
#define GPIO_AUX_PCM_CLK 66
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 3
+#define TABLA_MBHC_DEF_RLOADS 5
+
static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
static int msm8960_spk_control;
@@ -63,17 +69,6 @@
static int msm8960_btsco_rate = BTSCO_RATE_8KHZ;
static int msm8960_btsco_ch = 1;
-struct tabla_mbhc_calibration tabla_cal = {
- .bias = TABLA_MICBIAS2,
- .tldoh = 100,
- .bg_fast_settle = 100,
- .mic_current = TABLA_PID_MIC_5_UA,
- .mic_pid = 100,
- .hph_current = TABLA_PID_MIC_5_UA,
- .setup_plug_removal_delay = 1000000,
- .shutdown_plug_removal = 100000,
-};
-
static struct clk *codec_clk;
static int clk_users;
@@ -82,6 +77,8 @@
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
+static void *tabla_mbhc_cal;
+
static void msm8960_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
{
int ret = 0;
@@ -302,6 +299,41 @@
}
return 0;
}
+int msm8960_enable_codec_ext_clk(
+ struct snd_soc_codec *codec, int enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ clk_users++;
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+ if (clk_users != 1)
+ return 0;
+
+ codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+ if (codec_clk) {
+ clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
+ clk_enable(codec_clk);
+ tabla_mclk_enable(codec, 1);
+ } else {
+ pr_err("%s: Error setting Tabla MCLK\n", __func__);
+ clk_users--;
+ return -EINVAL;
+ }
+ } else {
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+ if (clk_users == 0)
+ return 0;
+ clk_users--;
+ if (!clk_users) {
+ pr_debug("%s: disabling MCLK. clk_users = %d\n",
+ __func__, clk_users);
+ clk_disable(codec_clk);
+ clk_put(codec_clk);
+ tabla_mclk_enable(codec, 0);
+ }
+ }
+ return 0;
+}
static int msm8960_mclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
@@ -310,43 +342,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
-
- clk_users++;
- pr_debug("%s: clk_users = %d\n", __func__, clk_users);
-
- if (clk_users != 1)
- return 0;
-
- codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
- if (codec_clk) {
- clk_set_rate(codec_clk, 12288000);
- clk_enable(codec_clk);
- tabla_mclk_enable(w->codec, 1);
-
- } else {
- pr_err("%s: Error setting Tabla MCLK\n", __func__);
- clk_users--;
- return -EINVAL;
- }
- break;
+ return msm8960_enable_codec_ext_clk(w->codec, 1);
case SND_SOC_DAPM_POST_PMD:
-
- pr_debug("%s: clk_users = %d\n", __func__, clk_users);
-
- if (clk_users == 0)
- return 0;
-
- clk_users--;
-
- if (!clk_users) {
- pr_debug("%s: disabling MCLK. clk_users = %d\n",
- __func__, clk_users);
-
- clk_disable(codec_clk);
- clk_put(codec_clk);
- tabla_mclk_enable(w->codec, 0);
- }
- break;
+ return msm8960_enable_codec_ext_clk(w->codec, 0);
}
return 0;
}
@@ -481,7 +479,7 @@
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
- msm8960_slim_0_rx_ch);
+ msm8960_slim_0_rx_ch);
ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
return 0;
}
@@ -492,7 +490,7 @@
msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
- msm8960_slim_0_rx_ch);
+ msm8960_slim_0_rx_ch);
return 1;
}
@@ -500,7 +498,7 @@
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
- msm8960_slim_0_tx_ch);
+ msm8960_slim_0_tx_ch);
ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
return 0;
}
@@ -511,15 +509,14 @@
msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
- msm8960_slim_0_tx_ch);
+ msm8960_slim_0_tx_ch);
return 1;
}
static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- pr_debug("%s: msm8960_btsco_rate = %d", __func__,
- msm8960_btsco_rate);
+ pr_debug("%s: msm8960_btsco_rate = %d", __func__, msm8960_btsco_rate);
ucontrol->value.integer.value[0] = msm8960_btsco_rate;
return 0;
}
@@ -538,8 +535,7 @@
msm8960_btsco_rate = BTSCO_RATE_8KHZ;
break;
}
- pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
- msm8960_btsco_rate);
+ pr_debug("%s: msm8960_btsco_rate = %d\n", __func__, msm8960_btsco_rate);
return 0;
}
@@ -570,6 +566,74 @@
return 0;
}
+static void *def_tabla_mbhc_cal(void)
+{
+ void *tabla_cal;
+ struct tabla_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_cic, *gain;
+
+ tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
+ TABLA_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!tabla_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((TABLA_MBHC_CAL_GENERAL_PTR(tabla_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_DET_PTR(tabla_cal)->X) = (Y))
+ S(mic_current, TABLA_PID_MIC_5_UA);
+ S(hph_current, TABLA_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 1000);
+ S(t_ins_retry, 200);
+#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, 1450);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
+ S(c[0], 6);
+ S(c[1], 10);
+ S(c[2], 10);
+ S(c[3], 14);
+ S(c[4], 14);
+ S(c[5], 16);
+ S(c[6], 0);
+ S(c[7], 0);
+ S(nc, 5);
+ S(n_meas, 11);
+ S(mbhc_nsc, 11);
+ S(num_btn, TABLA_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
+ btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
+ btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = 0;
+ btn_high[0] = 40;
+ btn_low[1] = 60;
+ btn_high[1] = 140;
+ btn_low[2] = 160;
+ btn_high[2] = 240;
+ n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
+ n_cic[0] = 120;
+ n_cic[1] = 94;
+ gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return tabla_cal;
+}
+
static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
int err;
@@ -618,7 +682,9 @@
return err;
}
- tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
+ tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
+ TABLA_MICBIAS2, msm8960_enable_codec_ext_clk, 0,
+ TABLA_EXT_CLK_RATE);
return 0;
}
@@ -1169,9 +1235,17 @@
pr_err("%s: Not the right machine type\n", __func__);
return -ENODEV ;
}
+
+ tabla_mbhc_cal = def_tabla_mbhc_cal();
+ if (!tabla_mbhc_cal) {
+ pr_err("Calibration data allocation failed\n");
+ return -ENOMEM;
+ }
+
msm8960_snd_device = platform_device_alloc("soc-audio", 0);
if (!msm8960_snd_device) {
pr_err("Platform device allocation failed\n");
+ kfree(tabla_mbhc_cal);
return -ENOMEM;
}
@@ -1179,6 +1253,7 @@
ret = platform_device_add(msm8960_snd_device);
if (ret) {
platform_device_put(msm8960_snd_device);
+ kfree(tabla_mbhc_cal);
return ret;
}
@@ -1201,6 +1276,7 @@
}
msm8960_free_headset_mic_gpios();
platform_device_unregister(msm8960_snd_device);
+ kfree(tabla_mbhc_cal);
}
module_exit(msm8960_audio_exit);