ASoC: wcd934x: Add support for HPH surge recovery

Add support for headphone surge recovery (up to -80v)
on wcd934x audio codec.

Change-Id: Ibcf4a0be857db7054e9a95ad8f78483f4cbc6dd4
Signed-off-by: Yeleswarapu Nagaradhesh <nagaradh@codeaurora.org>
Signed-off-by: Phani Kumar Uppalapati <phaniu@codeaurora.org>
This commit is contained in:
Yeleswarapu Nagaradhesh 2016-09-13 08:35:58 -07:00 committed by Phani Kumar Uppalapati
parent 46451883c7
commit 406b8c31f1
5 changed files with 364 additions and 8 deletions

View file

@ -38,7 +38,7 @@
#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ #define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
SND_JACK_BTN_4 | SND_JACK_BTN_5 ) SND_JACK_BTN_4 | SND_JACK_BTN_5 )
#define OCP_ATTEMPT 1 #define OCP_ATTEMPT 20
#define HS_DETECT_PLUG_TIME_MS (3 * 1000) #define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) #define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
@ -226,6 +226,10 @@ static const char *wcd_mbhc_get_event_string(int event)
return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF: case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF:
return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF); return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF);
case WCD_EVENT_OCP_OFF:
return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF);
case WCD_EVENT_OCP_ON:
return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON);
case WCD_EVENT_INVALID: case WCD_EVENT_INVALID:
default: default:
return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID); return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID);
@ -394,6 +398,16 @@ out_micb_en:
/* Disable micbias, enable pullup & cs */ /* Disable micbias, enable pullup & cs */
wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
break; break;
case WCD_EVENT_OCP_OFF:
mbhc->mbhc_cb->irq_control(mbhc->codec,
mbhc->intr_ids->hph_left_ocp,
false);
break;
case WCD_EVENT_OCP_ON:
mbhc->mbhc_cb->irq_control(mbhc->codec,
mbhc->intr_ids->hph_left_ocp,
true);
break;
default: default:
break; break;
} }
@ -461,6 +475,7 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
&mbhc->hph_pa_dac_state)) { &mbhc->hph_pa_dac_state)) {
pr_debug("%s: HPHR clear flag and enable PA\n", __func__); pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1);
pa_turned_on = true; pa_turned_on = true;
} }
mutex_unlock(&mbhc->hphr_pa_lock); mutex_unlock(&mbhc->hphr_pa_lock);
@ -469,6 +484,7 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
&mbhc->hph_pa_dac_state)) { &mbhc->hph_pa_dac_state)) {
pr_debug("%s: HPHL clear flag and enable PA\n", __func__); pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1);
pa_turned_on = true; pa_turned_on = true;
} }
mutex_unlock(&mbhc->hphl_pa_lock); mutex_unlock(&mbhc->hphl_pa_lock);
@ -502,6 +518,8 @@ static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc)
pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__); pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0);
} else { } else {
pr_debug("%s PA is off\n", __func__); pr_debug("%s PA is off\n", __func__);
} }
@ -2014,13 +2032,24 @@ exit:
static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data) static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
{ {
struct wcd_mbhc *mbhc = data; struct wcd_mbhc *mbhc = data;
int val;
pr_debug("%s: received HPHL OCP irq\n", __func__); pr_debug("%s: received HPHL OCP irq\n", __func__);
if (mbhc) { if (mbhc) {
if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) && if (mbhc->mbhc_cb->hph_register_recovery) {
(!mbhc->hphrocp_cnt)) { if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) {
pr_debug("%s: retry\n", __func__); WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS,
val);
if ((val != -EINVAL) && val)
mbhc->is_hph_ocp_pending = true;
goto done;
}
}
if (mbhc->hphlocp_cnt < OCP_ATTEMPT) {
mbhc->hphlocp_cnt++; mbhc->hphlocp_cnt++;
pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__,
mbhc->hphlocp_cnt);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
} else { } else {
@ -2035,6 +2064,7 @@ static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
} else { } else {
pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__); pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__);
} }
done:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -2043,10 +2073,26 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
struct wcd_mbhc *mbhc = data; struct wcd_mbhc *mbhc = data;
pr_debug("%s: received HPHR OCP irq\n", __func__); pr_debug("%s: received HPHR OCP irq\n", __func__);
if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
(!mbhc->hphlocp_cnt)) { if (!mbhc) {
pr_debug("%s: retry\n", __func__); pr_err("%s: Bad mbhc private data\n", __func__);
goto done;
}
if (mbhc->is_hph_ocp_pending) {
mbhc->is_hph_ocp_pending = false;
goto done;
}
if (mbhc->mbhc_cb->hph_register_recovery) {
if (mbhc->mbhc_cb->hph_register_recovery(mbhc))
/* register corruption, hence reset registers */
goto done;
}
if (mbhc->hphrocp_cnt < OCP_ATTEMPT) {
mbhc->hphrocp_cnt++; mbhc->hphrocp_cnt++;
pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__,
mbhc->hphrocp_cnt);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
} else { } else {
@ -2057,6 +2103,7 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
mbhc->hph_status, WCD_MBHC_JACK_MASK); mbhc->hph_status, WCD_MBHC_JACK_MASK);
} }
done:
return IRQ_HANDLED; return IRQ_HANDLED;
} }

View file

@ -66,6 +66,10 @@ enum wcd_mbhc_register_function {
WCD_MBHC_ANC_DET_EN, WCD_MBHC_ANC_DET_EN,
WCD_MBHC_FSM_STATUS, WCD_MBHC_FSM_STATUS,
WCD_MBHC_MUX_CTL, WCD_MBHC_MUX_CTL,
WCD_MBHC_HPHL_OCP_DET_EN,
WCD_MBHC_HPHR_OCP_DET_EN,
WCD_MBHC_HPHL_OCP_STATUS,
WCD_MBHC_HPHR_OCP_STATUS,
WCD_MBHC_REG_FUNC_MAX, WCD_MBHC_REG_FUNC_MAX,
}; };
@ -127,6 +131,8 @@ enum wcd_notify_event {
WCD_EVENT_POST_HPHR_PA_OFF, WCD_EVENT_POST_HPHR_PA_OFF,
WCD_EVENT_PRE_HPHL_PA_OFF, WCD_EVENT_PRE_HPHL_PA_OFF,
WCD_EVENT_PRE_HPHR_PA_OFF, WCD_EVENT_PRE_HPHR_PA_OFF,
WCD_EVENT_OCP_OFF,
WCD_EVENT_OCP_ON,
WCD_EVENT_LAST, WCD_EVENT_LAST,
}; };
@ -322,7 +328,9 @@ do { \
mbhc->wcd_mbhc_regs[function].reg)) & \ mbhc->wcd_mbhc_regs[function].reg)) & \
(mbhc->wcd_mbhc_regs[function].mask)) >> \ (mbhc->wcd_mbhc_regs[function].mask)) >> \
(mbhc->wcd_mbhc_regs[function].offset)); \ (mbhc->wcd_mbhc_regs[function].offset)); \
} \ } else { \
val = -EINVAL; \
} \
} while (0) } while (0)
struct wcd_mbhc_cb { struct wcd_mbhc_cb {
@ -365,6 +373,7 @@ struct wcd_mbhc_cb {
void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool); void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool);
void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool);
void (*mbhc_moisture_config)(struct wcd_mbhc *); void (*mbhc_moisture_config)(struct wcd_mbhc *);
bool (*hph_register_recovery)(struct wcd_mbhc *);
}; };
struct wcd_mbhc { struct wcd_mbhc {
@ -430,6 +439,7 @@ struct wcd_mbhc {
struct mutex hphr_pa_lock; struct mutex hphr_pa_lock;
unsigned long intr_status; unsigned long intr_status;
bool is_hph_ocp_pending;
}; };
#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ #define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
sizeof(struct wcd_mbhc_general_cfg) + \ sizeof(struct wcd_mbhc_general_cfg) + \

View file

@ -118,6 +118,14 @@ static struct wcd_mbhc_register
WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0), WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0),
WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0), WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0),
WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN",
WCD934X_HPH_L_TEST, 0x01, 0, 0),
WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN",
WCD934X_HPH_R_TEST, 0x01, 0, 0),
WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS",
WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0),
WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS",
WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0),
}; };
static const struct wcd_mbhc_intr intr_ids = { static const struct wcd_mbhc_intr intr_ids = {
@ -778,6 +786,26 @@ static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc)
0x0C, TAVIL_MBHC_MOISTURE_RREF << 2); 0x0C, TAVIL_MBHC_MOISTURE_RREF << 2);
} }
static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc)
{
struct snd_soc_codec *codec = mbhc->codec;
struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
if (!wcd934x_mbhc)
return false;
wcd934x_mbhc->is_hph_recover = false;
snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec),
"RESET_HPH_REGISTERS");
snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
"RESET_HPH_REGISTERS");
snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
return wcd934x_mbhc->is_hph_recover;
}
static const struct wcd_mbhc_cb mbhc_cb = { static const struct wcd_mbhc_cb mbhc_cb = {
.request_irq = tavil_mbhc_request_irq, .request_irq = tavil_mbhc_request_irq,
.irq_control = tavil_mbhc_irq_control, .irq_control = tavil_mbhc_irq_control,
@ -800,6 +828,7 @@ static const struct wcd_mbhc_cb mbhc_cb = {
.mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl, .mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl,
.hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl, .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl,
.mbhc_moisture_config = tavil_mbhc_moisture_config, .mbhc_moisture_config = tavil_mbhc_moisture_config,
.hph_register_recovery = tavil_hph_register_recovery,
}; };
static struct regulator *tavil_codec_find_ondemand_regulator( static struct regulator *tavil_codec_find_ondemand_regulator(

View file

@ -32,6 +32,7 @@ struct wcd934x_mbhc {
struct wcd9xxx *wcd9xxx; struct wcd9xxx *wcd9xxx;
struct fw_info *fw_data; struct fw_info *fw_data;
bool mbhc_started; bool mbhc_started;
bool is_hph_recover;
}; };
extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,

View file

@ -141,6 +141,14 @@ static const struct snd_kcontrol_new name##_mux = \
#define TAVIL_VERSION_ENTRY_SIZE 17 #define TAVIL_VERSION_ENTRY_SIZE 17
#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1)
#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\
WCD934X_HPH_NEW_ANA_HPH2 + 1)
#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\
WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1)
#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\
TAVIL_HPH_REG_RANGE_3)
enum { enum {
VI_SENSE_1, VI_SENSE_1,
VI_SENSE_2, VI_SENSE_2,
@ -1781,6 +1789,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
usleep_range(7000, 7100); usleep_range(7000, 7100);
clear_bit(HPH_PA_DELAY, &tavil->status_mask); clear_bit(HPH_PA_DELAY, &tavil->status_mask);
} }
snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01);
/* Remove mute */ /* Remove mute */
snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
0x10, 0x00); 0x10, 0x00);
@ -1815,6 +1826,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
0x04, 0x04); 0x04, 0x04);
snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00);
snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
0x10, 0x10);
break; break;
case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_POST_PMD:
/* 5ms sleep is required after PA disable */ /* 5ms sleep is required after PA disable */
@ -1856,6 +1870,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
usleep_range(7000, 7100); usleep_range(7000, 7100);
clear_bit(HPH_PA_DELAY, &tavil->status_mask); clear_bit(HPH_PA_DELAY, &tavil->status_mask);
} }
snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01);
/* Remove Mute on primary path */ /* Remove Mute on primary path */
snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
0x10, 0x00); 0x10, 0x00);
@ -1890,6 +1905,10 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
0x04, 0x04); 0x04, 0x04);
snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00);
snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
0x10, 0x10);
break; break;
case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_POST_PMD:
/* 5ms sleep is required after PA disable */ /* 5ms sleep is required after PA disable */
@ -4122,6 +4141,244 @@ static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
return __tavil_codec_enable_micbias(w, event); return __tavil_codec_enable_micbias(w, event);
} }
static const struct reg_sequence tavil_hph_reset_tbl[] = {
{ WCD934X_HPH_CNP_EN, 0x80 },
{ WCD934X_HPH_CNP_WG_CTL, 0x9A },
{ WCD934X_HPH_CNP_WG_TIME, 0x14 },
{ WCD934X_HPH_OCP_CTL, 0x28 },
{ WCD934X_HPH_AUTO_CHOP, 0x16 },
{ WCD934X_HPH_CHOP_CTL, 0x83 },
{ WCD934X_HPH_PA_CTL1, 0x46 },
{ WCD934X_HPH_PA_CTL2, 0x50 },
{ WCD934X_HPH_L_EN, 0x80 },
{ WCD934X_HPH_L_TEST, 0xE0 },
{ WCD934X_HPH_L_ATEST, 0x50 },
{ WCD934X_HPH_R_EN, 0x80 },
{ WCD934X_HPH_R_TEST, 0xE0 },
{ WCD934X_HPH_R_ATEST, 0x54 },
{ WCD934X_HPH_RDAC_CLK_CTL1, 0x99 },
{ WCD934X_HPH_RDAC_CLK_CTL2, 0x9B },
{ WCD934X_HPH_RDAC_LDO_CTL, 0x33 },
{ WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
{ WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 },
{ WCD934X_HPH_REFBUFF_LP_CTL, 0x0A },
{ WCD934X_HPH_L_DAC_CTL, 0x00 },
{ WCD934X_HPH_R_DAC_CTL, 0x00 },
{ WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
{ WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 },
{ WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
{ WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
{ WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 },
{ WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
{ WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
{ WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
{ WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
};
static const struct tavil_reg_mask_val tavil_pa_disable[] = {
{ WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */
{ WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */
{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */
{ WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */
{ WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */
{ WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */
{ WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */
};
static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = {
{ WCD934X_RX_OCP_CTL, 0x0F, 0x01 }, /* OCP number of attempts is 1 */
{ WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
{ WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
{ WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
};
static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = {
{ WCD934X_RX_OCP_CTL, 0x0F, 0x01 }, /* OCP number of attempts is 1 */
{ WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
};
/* LO-HIFI */
static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = {
{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 },
{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
{ WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 },
{ WCD934X_HPH_PA_CTL1, 0x0e, 0x02 },
{ WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
};
static const struct tavil_reg_mask_val tavil_pre_pa_en[] = {
{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 },
{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
{ WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 },
{ WCD934X_HPH_PA_CTL1, 0x0e, 0x06 },
{ WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
};
static const struct tavil_reg_mask_val tavil_post_pa_en[] = {
{ WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
{ WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
{ WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */
{ WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */
{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */
{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 },
};
static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf)
{
regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1);
regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2,
buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2);
regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2,
TAVIL_HPH_REG_RANGE_3);
}
static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil,
struct regmap *map, int pa_status)
{
int i;
blocking_notifier_call_chain(&tavil->mbhc->notifier,
WCD_EVENT_OCP_OFF,
&tavil->mbhc->wcd_mbhc);
if (pa_status & 0xC0)
goto pa_en_restore;
dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n",
__func__, pa_status);
regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10);
regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10);
regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00);
regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00);
regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00);
regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00);
/* Restore to HW defaults */
regmap_multi_reg_write(map, tavil_hph_reset_tbl,
ARRAY_SIZE(tavil_hph_reset_tbl));
for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++)
regmap_write_bits(map, tavil_ocp_en_seq[i].reg,
tavil_ocp_en_seq[i].mask,
tavil_ocp_en_seq[i].val);
goto end;
pa_en_restore:
dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n",
__func__, pa_status);
/* Disable PA and other registers before restoring */
for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++)
regmap_write_bits(map, tavil_pa_disable[i].reg,
tavil_pa_disable[i].mask,
tavil_pa_disable[i].val);
regmap_multi_reg_write(map, tavil_hph_reset_tbl,
ARRAY_SIZE(tavil_hph_reset_tbl));
for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++)
regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg,
tavil_ocp_en_seq_1[i].mask,
tavil_ocp_en_seq_1[i].val);
if (tavil->hph_mode == CLS_H_LOHIFI) {
for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++)
regmap_write_bits(map,
tavil_pre_pa_en_lohifi[i].reg,
tavil_pre_pa_en_lohifi[i].mask,
tavil_pre_pa_en_lohifi[i].val);
} else {
for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++)
regmap_write_bits(map, tavil_pre_pa_en[i].reg,
tavil_pre_pa_en[i].mask,
tavil_pre_pa_en[i].val);
}
regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C);
regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30);
/* wait for 100usec after HPH DAC is enabled */
usleep_range(100, 110);
regmap_write(map, WCD934X_ANA_HPH, pa_status);
/* Sleep for 7msec after PA is enabled */
usleep_range(7000, 7100);
for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++)
regmap_write_bits(map, tavil_post_pa_en[i].reg,
tavil_post_pa_en[i].mask,
tavil_post_pa_en[i].val);
end:
tavil->mbhc->is_hph_recover = true;
blocking_notifier_call_chain(
&tavil->mbhc->notifier,
WCD_EVENT_OCP_ON,
&tavil->mbhc->wcd_mbhc);
}
static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
u8 cache_val[TAVIL_HPH_TOTAL_REG];
u8 hw_val[TAVIL_HPH_TOTAL_REG];
int pa_status;
int ret;
dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
memset(cache_val, 0, TAVIL_HPH_TOTAL_REG);
memset(hw_val, 0, TAVIL_HPH_TOTAL_REG);
regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status);
tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val);
/* Read register values from HW directly */
regcache_cache_bypass(wcd9xxx->regmap, true);
tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val);
regcache_cache_bypass(wcd9xxx->regmap, false);
/* compare both the registers to know if there is corruption */
ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG);
/* If both the values are same, it means no corruption */
if (ret) {
dev_dbg(codec->dev, "%s: cache and hw reg are not same\n",
__func__);
tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap,
pa_status);
} else {
dev_dbg(codec->dev, "%s: cache and hw reg are same\n",
__func__);
tavil->mbhc->is_hph_recover = false;
}
break;
default:
break;
};
return 0;
}
static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
@ -6152,6 +6409,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/*
* Not supply widget, this is used to recover HPH registers.
* It is not connected to any other widgets
*/
SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM,
0, 0, tavil_codec_reset_hph_registers,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
tavil_codec_force_enable_micbias, tavil_codec_force_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@ -7340,6 +7605,10 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00},
{WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00},
{WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00},
{WCD934X_RX_OCP_CTL, 0x0F, 0x01}, /* OCP number of attempts is 1 */
{WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */
{WCD934X_HPH_L_TEST, 0x01, 0x01},
{WCD934X_HPH_R_TEST, 0x01, 0x01},
}; };
static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {