ASoC: wcd9335: Add support to enable vbat feature

Add support to enable vbat monitor feature on tasha codec. Vbat module
monitors the battery voltage and updates the gain on rx path if battery
volatage falls below certain threshold value.
This module can prevent brownout scenarios on the mobile devices when
the loads are heavy on the battery and it can also prolongs battery life.

Change-Id: I66bacf3b861618fc44f50a8e22ca3f2f0fc7763b
Signed-off-by: Venkata Narendra Kumar Gutta <vgutta@codeaurora.org>
This commit is contained in:
Venkata Narendra Kumar Gutta 2015-08-12 18:58:09 +05:30 committed by Gerrit - the friendly Code Review server
parent 5979e10dbc
commit 1bdcfef657
4 changed files with 438 additions and 6 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Copyright (c) 2015, The Linux Foundation. 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
@ -19,6 +19,7 @@ enum wcd_cal_type {
WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL,
WCD9XXX_MAD_CAL,
WCD9XXX_MBHC_CAL,
WCD9XXX_VBAT_CAL,
WCD9XXX_MAX_CAL,
};

View File

@ -193,6 +193,46 @@ static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0
},
{
1,
(TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE),
@ -500,6 +540,16 @@ static const struct wcd_mbhc_intr intr_ids = {
.hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT,
};
struct wcd_vbat {
bool is_calibrated;
bool is_enabled;
bool gsm_mode_set;
bool adc_config;
/* Variables to cache Vbat ADC output values */
u16 dcp1;
u16 dcp2;
};
struct tasha_priv {
struct device *dev;
struct wcd9xxx *wcd9xxx;
@ -517,6 +567,9 @@ struct tasha_priv {
u32 anc_slot;
bool anc_func;
/* Vbat module */
struct wcd_vbat vbat;
/* cal info for codec */
struct fw_info *fw_data;
@ -1824,6 +1877,23 @@ static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = {
SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = {
SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = {
SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = {
SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = {
SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
};
static int tasha_put_iir_enable_audio_mixer(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@ -3935,6 +4005,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3945,6 +4019,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3955,6 +4033,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3965,6 +4047,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3975,6 +4061,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3985,6 +4075,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -3995,6 +4090,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -4005,6 +4105,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
@ -4015,6 +4119,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT SEC MIX"},
{"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT SEC MIX"},
{"ADC MUX0", "DMIC", "DMIC MUX0"},
{"ADC MUX0", "AMIC", "AMIC MUX0"},
@ -4313,6 +4421,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"},
{"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"},
{"RX INT5 INTERP", NULL, "RX INT5 MIX2"},
{"RX INT5 VBAT MIX", NULL, "RX INT5 INTERP"},
{"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 VBAT MIX"},
{"RX INT5 VBAT SEC MIX", NULL, "RX INT5 VBAT"},
{"RX INT5 DAC", NULL, "RX INT5 VBAT SEC MIX"},
{"RX INT5 DAC", NULL, "RX INT5 INTERP"},
{"RX INT5 DAC", NULL, "RX_BIAS"},
{"LINEOUT3 PA", NULL, "RX INT5 DAC"},
@ -4324,6 +4438,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"},
{"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"},
{"RX INT6 INTERP", NULL, "RX INT6 MIX2"},
{"RX INT6 VBAT MIX", NULL, "RX INT6 INTERP"},
{"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 VBAT MIX"},
{"RX INT6 VBAT SEC MIX", NULL, "RX INT6 VBAT"},
{"RX INT6 DAC", NULL, "RX INT6 VBAT SEC MIX"},
{"RX INT6 DAC", NULL, "RX INT6 INTERP"},
{"RX INT6 DAC", NULL, "RX_BIAS"},
{"LINEOUT4 PA", NULL, "RX INT6 DAC"},
@ -4346,6 +4466,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX INT8 SEC MIX", NULL, "COMP8_CLK"},
{"RX INT7 INTERP", NULL, "RX INT7 MIX2"},
{"RX INT7 VBAT MIX", NULL, "RX INT7 INTERP"},
{"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 VBAT MIX"},
{"RX INT7 VBAT SEC MIX", NULL, "RX INT7 VBAT"},
{"RX INT7 CHAIN", NULL, "RX INT7 VBAT SEC MIX"},
{"RX INT7 CHAIN", NULL, "RX INT7 INTERP"},
{"RX INT7 CHAIN", NULL, "RX_BIAS"},
{"SPK1 OUT", NULL, "RX INT7 CHAIN"},
@ -4355,6 +4481,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"},
{"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"},
{"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"},
{"RX INT8 VBAT MIX", NULL, "RX INT8 INTERP"},
{"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 VBAT MIX"},
{"RX INT8 VBAT SEC MIX", NULL, "RX INT8 VBAT"},
{"RX INT8 CHAIN", NULL, "RX INT8 VBAT SEC MIX"},
{"RX INT8 CHAIN", NULL, "RX INT8 INTERP"},
{"RX INT8 CHAIN", NULL, "RX_BIAS"},
{"SPK2 OUT", NULL, "RX INT8 CHAIN"},
@ -5147,6 +5279,256 @@ static int tasha_mad_input_put(struct snd_kcontrol *kcontrol,
return 0;
}
static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat,
struct snd_soc_codec *codec)
{
u8 val1, val2;
if (!vbat->adc_config) {
tasha_cdc_mclk_enable(codec, true, false);
/* Measure dcp1 by applying band gap voltage(Vbg) of 0.85V */
snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20);
snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05);
snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
usleep(2000000); /* Wait 2 sec after enabling band gap bias */
snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82);
snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87);
snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL,
0x10, 0x10);
snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D);
snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01);
snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE);
snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C);
usleep(1000); /* Wait 1 msec after calibration select as Vbg */
snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
/* Measure dcp2 by applying band gap voltage(Vbg) of 1.05V */
snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68);
usleep(2000); /* Wait 2 msec after selecting Vbg as 1.05V */
snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
usleep(1000000); /* Wait 1 sec after enabling band gap bias */
snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
pr_debug("%s: dcp1:0x%x, dcp2:0x%x\n",
__func__, vbat->dcp1, vbat->dcp2);
/* Reset the Vbat ADC configuration */
snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
usleep(2000); /* Wait 2 msec after selecting Vbg as 0.85V */
snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
usleep(1000000); /* Wait 1 sec after enabling band gap bias */
snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C);
snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE);
snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00);
snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00);
snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A);
tasha_cdc_mclk_enable(codec, false, false);
vbat->adc_config = true;
}
}
static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec)
{
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
struct firmware_cal *hwdep_cal = NULL;
struct vbat_monitor_reg *vbat_reg_ptr = NULL;
const void *data;
size_t cal_size, vbat_size_remaining;
int ret = 0, i;
u32 vbat_writes_size = 0;
u16 reg;
u8 mask, val, old_val;
hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL);
if (hwdep_cal) {
data = hwdep_cal->data;
cal_size = hwdep_cal->size;
dev_dbg(codec->dev, "%s: using hwdep calibration\n",
__func__);
} else {
dev_err(codec->dev, "%s: Vbat cal not received\n",
__func__);
ret = -EINVAL;
goto done;
}
if (cal_size < sizeof(*vbat_reg_ptr)) {
dev_err(codec->dev,
"%s: Incorrect size %zd for Vbat Cal, expected %zd\n",
__func__, cal_size, sizeof(*vbat_reg_ptr));
ret = -EINVAL;
goto done;
}
vbat_reg_ptr = (struct vbat_monitor_reg *) (data);
if (!vbat_reg_ptr) {
dev_err(codec->dev,
"%s: Invalid calibration data for Vbat\n",
__func__);
ret = -EINVAL;
goto done;
}
vbat_writes_size = vbat_reg_ptr->size;
vbat_size_remaining = cal_size - sizeof(u32);
dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n",
__func__, vbat_writes_size, vbat_size_remaining);
if ((vbat_writes_size * TASHA_PACKED_REG_SIZE)
> vbat_size_remaining) {
pr_err("%s: Incorrect Vbat calibration data\n", __func__);
ret = -EINVAL;
goto done;
}
for (i = 0 ; i < vbat_writes_size; i++) {
TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i],
reg, mask, val);
old_val = snd_soc_read(codec, reg);
snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask));
}
tasha->vbat.is_calibrated = true;
done:
return ret;
}
static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
wcd_vbat_adc_out_config(&tasha->vbat, codec);
ucontrol->value.integer.value[0] = tasha->vbat.dcp1;
ucontrol->value.integer.value[1] = tasha->vbat.dcp2;
pr_debug("%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n",
__func__, ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1]);
return 0;
}
static const char * const tasha_vbat_gsm_mode_text[] = {
"OFF", "ON"};
static const struct soc_enum tasha_vbat_gsm_mode_enum =
SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text);
static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] =
(tasha->vbat.gsm_mode_set == true ? 1 : 0);
return 0;
}
static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
tasha->vbat.gsm_mode_set = ucontrol->value.integer.value[0];
pr_debug("%s: value: %d\n", __func__, tasha->vbat.gsm_mode_set);
/* Set Vbat register configuration for GSM mode bit based on value */
if (tasha->vbat.gsm_mode_set)
snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
0x04, 0x04);
else
snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
0x04, 0x00);
return 0;
}
static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
int ret = 0;
struct snd_soc_codec *codec = w->codec;
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg;
vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL;
vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG;
if (!strcmp(w->name, "RX INT8 VBAT"))
vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1;
else if (!strcmp(w->name, "RX INT7 VBAT"))
vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1;
else if (!strcmp(w->name, "RX INT6 VBAT"))
vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1;
else if (!strcmp(w->name, "RX INT5 VBAT"))
vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
ret = tasha_update_vbat_reg_config(codec);
if (!ret && (!tasha->vbat.is_calibrated)) {
pr_debug("%s : VBAT isn't calibrated, So not enabling it\n",
__func__);
return 0;
}
snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02);
snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10);
snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01);
tasha->vbat.is_enabled = true;
break;
case SND_SOC_DAPM_POST_PMD:
if (tasha->vbat.is_enabled) {
snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00);
snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00);
snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00);
snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
tasha->vbat.is_enabled = false;
}
break;
};
return ret;
}
static const char * const rx_hph_mode_mux_text[] = {
"CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB"
};
@ -5336,6 +5718,13 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = {
SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum,
tasha_mad_input_get, tasha_mad_input_put),
SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2,
tasha_vbat_adc_data_get, NULL),
SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum,
tasha_vbat_gsm_mode_func_get,
tasha_vbat_gsm_mode_func_put),
};
static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol,
@ -7063,21 +7452,32 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
rx_int5_spline_mix_switch,
ARRAY_SIZE(rx_int5_spline_mix_switch)),
SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT5 VBAT MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT5 VBAT SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0,
rx_int6_spline_mix_switch,
ARRAY_SIZE(rx_int6_spline_mix_switch)),
SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT6 VBAT MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT6 VBAT SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0,
rx_int7_spline_mix_switch,
ARRAY_SIZE(rx_int7_spline_mix_switch)),
SND_SOC_DAPM_MIXER("RX INT7 VBAT MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT7 VBAT SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0,
rx_int8_spline_mix_switch,
ARRAY_SIZE(rx_int8_spline_mix_switch)),
SND_SOC_DAPM_MIXER("RX INT8 VBAT MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT8 VBAT SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
@ -7093,6 +7493,28 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
NULL, 0, tasha_codec_spk_boost_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0,
rx_int5_vbat_mix_switch,
ARRAY_SIZE(rx_int5_vbat_mix_switch),
tasha_codec_vbat_enable_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0,
rx_int6_vbat_mix_switch,
ARRAY_SIZE(rx_int6_vbat_mix_switch),
tasha_codec_vbat_enable_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0,
rx_int7_vbat_mix_switch,
ARRAY_SIZE(rx_int7_vbat_mix_switch),
tasha_codec_vbat_enable_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0,
rx_int8_vbat_mix_switch,
ARRAY_SIZE(rx_int8_vbat_mix_switch),
tasha_codec_vbat_enable_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4,
0, &rx_int0_mix2_inp_mux),
SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4,
@ -9407,6 +9829,8 @@ static int tasha_codec_probe(struct snd_soc_codec *codec)
set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit);
set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit);
set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit);
set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit);
ret = wcd_cal_create_hwdep(tasha->fw_data,
WCD9XXX_CODEC_HWDEP_NODE, codec);
if (ret < 0) {

View File

@ -20,7 +20,7 @@
#define WCD_CLSH_EVENT_PRE_DAC 0x01
#define WCD_CLSH_EVENT_POST_PA 0x02
#define MAX_VBAT_MONITOR_WRITES 17
/*
* Basic states for Class H state machine.
* represented as a bit mask within a u8 data type
@ -130,6 +130,11 @@ struct wcd9xxx_anc_header {
u32 num_anc_slots;
};
struct vbat_monitor_reg {
u32 size;
u32 writes[MAX_VBAT_MONITOR_WRITES];
} __packed;
extern void wcd_clsh_fsm(struct snd_soc_codec *codec,
struct wcd_clsh_cdc_data *cdc_clsh_d,
u8 clsh_event, u8 req_state,
@ -155,22 +160,22 @@ enum {
MAD_ULT_INT_DEST_SELECT_REG,
MAD_BEACON_INT_DEST_SELECT_REG,
MAD_CLIP_INT_DEST_SELECT_REG,
MAD_VBAT_INT_DEST_SELECT_REG,
VBAT_INT_DEST_SELECT_REG,
MAD_AUDIO_INT_MASK_REG,
MAD_ULT_INT_MASK_REG,
MAD_BEACON_INT_MASK_REG,
MAD_CLIP_INT_MASK_REG,
MAD_VBAT_INT_MASK_REG,
VBAT_INT_MASK_REG,
MAD_AUDIO_INT_STATUS_REG,
MAD_ULT_INT_STATUS_REG,
MAD_BEACON_INT_STATUS_REG,
MAD_CLIP_INT_STATUS_REG,
MAD_VBAT_INT_STATUS_REG,
VBAT_INT_STATUS_REG,
MAD_AUDIO_INT_CLEAR_REG,
MAD_ULT_INT_CLEAR_REG,
MAD_BEACON_INT_CLEAR_REG,
MAD_CLIP_INT_CLEAR_REG,
MAD_VBAT_INT_CLEAR_REG,
VBAT_INT_CLEAR_REG,
SB_PGD_PORT_TX_WATERMARK_N,
SB_PGD_PORT_TX_ENABLE_N,
SB_PGD_PORT_RX_WATERMARK_N,

View File

@ -23,12 +23,14 @@ const int cal_size_info[WCD9XXX_MAX_CAL] = {
[WCD9XXX_ANC_CAL] = 16384,
[WCD9XXX_MBHC_CAL] = 4096,
[WCD9XXX_MAD_CAL] = 4096,
[WCD9XXX_VBAT_CAL] = 72,
};
const char *cal_name_info[WCD9XXX_MAX_CAL] = {
[WCD9XXX_ANC_CAL] = "anc",
[WCD9XXX_MBHC_CAL] = "mbhc",
[WCD9XXX_MAD_CAL] = "mad",
[WCD9XXX_VBAT_CAL] = "vbat",
};
struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,