From 7adc34e496464cdd2847bb9f22476a4d16239552 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Fri, 9 Nov 2018 11:24:38 +0530 Subject: [PATCH] asoc: codecs: Add new class-H driver for wcd937x Add new class-H driver with sequences to support both class-H and class-AB modes on wcd937x variants. Change-Id: Ie109f3d951af1e8a0079dd39ab2fe1e9883bd6c2 Signed-off-by: Laxminath Kasam --- asoc/codecs/Kbuild | 1 + asoc/codecs/bolero/rx-macro.c | 15 +- asoc/codecs/wcd-clsh.c | 575 +++++++++++++++++++++++++++++++ asoc/codecs/wcd-clsh.h | 104 ++++++ asoc/codecs/wcd937x/internal.h | 3 + asoc/codecs/wcd937x/wcd937x.c | 203 +++++++---- include/asoc/wcd9xxx_registers.h | 30 ++ 7 files changed, 846 insertions(+), 85 deletions(-) create mode 100644 asoc/codecs/wcd-clsh.c create mode 100644 asoc/codecs/wcd-clsh.h create mode 100644 include/asoc/wcd9xxx_registers.h diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 3e5fc12e71..48e043119b 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -98,6 +98,7 @@ ifdef CONFIG_WCD9XXX_CODEC_CORE endif ifdef CONFIG_SND_SOC_WCD9XXX_V2 + WCD9XXX_OBJS += wcd-clsh.o WCD9XXX_OBJS += wcd9xxx-common-v2.o WCD9XXX_OBJS += wcd9xxx-resmgr-v2.o WCD9XXX_OBJS += wcdcal-hwdep.o diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c index 986375dec7..0b8256b6a8 100644 --- a/asoc/codecs/bolero/rx-macro.c +++ b/asoc/codecs/bolero/rx-macro.c @@ -1535,12 +1535,12 @@ static void rx_macro_hd2_control(struct snd_soc_codec *codec, switch (interp_idx) { case INTERP_HPHL: - hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3; - hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; + hd2_scale_reg = BOLERO_CDC_RX_RX0_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0; break; case INTERP_HPHR: - hd2_scale_reg = BOLERO_CDC_RX_RX2_RX_PATH_SEC3; - hd2_enable_reg = BOLERO_CDC_RX_RX2_RX_PATH_CFG0; + hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; break; } @@ -1908,18 +1908,15 @@ static void rx_macro_hphdelay_lutbypass(struct snd_soc_codec *codec, struct rx_macro_priv *rx_priv, u16 interp_idx, int event) { - u8 hph_dly_mask = 0; u16 hph_lut_bypass_reg = 0; u16 hph_comp_ctrl7 = 0; switch (interp_idx) { case INTERP_HPHL: - hph_dly_mask = 1; hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_LUT; hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER0_CTL7; break; case INTERP_HPHR: - hph_dly_mask = 2; hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_LUT; hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER1_CTL7; break; @@ -1928,8 +1925,6 @@ static void rx_macro_hphdelay_lutbypass(struct snd_soc_codec *codec, } if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { - snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0, - hph_dly_mask, 0x0); if (interp_idx == INTERP_HPHL) { if (rx_priv->is_ear_mode_on) snd_soc_update_bits(codec, @@ -1947,8 +1942,6 @@ static void rx_macro_hphdelay_lutbypass(struct snd_soc_codec *codec, } if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { - snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0, - hph_dly_mask, hph_dly_mask); snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG1, 0x02, 0x00); snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); diff --git a/asoc/codecs/wcd-clsh.c b/asoc/codecs/wcd-clsh.c new file mode 100644 index 0000000000..d119b837c3 --- /dev/null +++ b/asoc/codecs/wcd-clsh.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2015-2018, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "wcd-clsh.h" + +#define WCD_USLEEP_RANGE 50 + +static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *, + struct wcd_clsh_cdc_info *, + u8 req_state, bool en, int mode); + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return WCD_CLSH_STRINGIFY(CLS_H_NORMAL); + case CLS_H_HIFI: + return WCD_CLSH_STRINGIFY(CLS_H_HIFI); + case CLS_H_LOHIFI: + return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI); + case CLS_H_LP: + return WCD_CLSH_STRINGIFY(CLS_H_LP); + case CLS_H_ULP: + return WCD_CLSH_STRINGIFY(CLS_H_ULP); + case CLS_AB: + return WCD_CLSH_STRINGIFY(CLS_AB); + case CLS_AB_HIFI: + return WCD_CLSH_STRINGIFY(CLS_AB_HIFI); + default: + return WCD_CLSH_STRINGIFY(CLS_H_INVALID); + }; +} + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_AUX", + }; + + if (state == WCD_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_AUX)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_AUX)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI) { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xA0); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, + 0xFF, 0x1C); + if (mode == CLS_H_LOHIFI) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x00); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 510); + if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || + mode == CLS_H_HIFI || mode == CLS_H_LP) + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3, + 0x02, 0x00); + + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, 0xFF, 0x3A); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s\n", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); +} + +static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + int mode, + bool enable) +{ + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* + * 100us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xE0); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s\n", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_H_LOHIFI: + val = 0x00; + break; + case CLS_AB: + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_AB_HIFI: + case CLS_H_HIFI: + val = 0x08; + break; + case CLS_H_LP: + val = 0x04; + break; + default: + dev_err(codec->dev, "%s:Invalid mode %d\n", __func__, mode); + return; + }; + + snd_soc_update_bits(codec, WCD9XXX_ANA_HPH, 0x0C, val); +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) +{ + + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_state_ear_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* buck and flyback set to default mode and disable */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_err(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + char msg[128]; + + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s\n", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_HPH_ST: + case WCD_CLSH_STATE_AUX: + case WCD_CLSH_STATE_HPHL_AUX: + case WCD_CLSH_STATE_HPHR_AUX: + case WCD_CLSH_STATE_HPH_ST_AUX: + case WCD_CLSH_STATE_EAR_AUX: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_cls_h_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + break; + case WCD_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES) { + if (!wcd_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + } + break; + }; +} +EXPORT_SYMBOL(wcd_cls_h_fsm); + +/* + * wcd_cls_h_init: Called to init clsh info + * + * @clsh: pointer for clsh state information. + */ +void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh) +{ + int i; + + clsh->state = WCD_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES; i++) + clsh_state_fp[i] = wcd_clsh_state_err; + + clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l; + clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st; + clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] = + wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear; + /* Set interpolaotr modes to NONE */ + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; +} +EXPORT_SYMBOL(wcd_cls_h_init); + +MODULE_DESCRIPTION("WCD Class-H Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/wcd-clsh.h b/asoc/codecs/wcd-clsh.h new file mode 100644 index 0000000000..df305bcf7a --- /dev/null +++ b/asoc/codecs/wcd-clsh.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015-2018, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD_CLSH +#define _WCD_CLSH + +#include + +#define WCD_CLSH_STRINGIFY(s) __stringify(s) +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define WCD_CLSH_EVENT_PRE_DAC 0x01 +#define WCD_CLSH_EVENT_POST_PA 0x02 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: AUX mode + */ +#define WCD_CLSH_STATE_IDLE 0x00 +#define WCD_CLSH_STATE_EAR (0x01 << 0) +#define WCD_CLSH_STATE_HPHL (0x01 << 1) +#define WCD_CLSH_STATE_HPHR (0x01 << 2) +#define WCD_CLSH_STATE_AUX (0x01 << 3) + +/* + * Though number of CLSH states is 4, max state should be 5 + * because state array index starts from 1. + */ +#define WCD_CLSH_STATE_MAX 5 +#define NUM_CLSH_STATES (0x01 << WCD_CLSH_STATE_MAX) + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_HPHR) + +#define WCD_CLSH_STATE_HPHL_AUX (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPHR_AUX (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPH_ST_AUX (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_EAR_AUX (WCD_CLSH_STATE_EAR | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_EAR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +/* Class H data that the codec driver will maintain */ +struct wcd_clsh_cdc_info { + u8 state; + int flyback_users; + int buck_users; + int interpolator_modes[WCD_CLSH_STATE_MAX]; +}; + +#ifdef CONFIG_SND_SOC_WCD9XXX_V2 +extern void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh); +#else +extern void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ +} + +extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh) +{ +} +#endif /* CONFIG_SND_SOC_WCD9XXX_V2 */ + +#endif diff --git a/asoc/codecs/wcd937x/internal.h b/asoc/codecs/wcd937x/internal.h index eb2de9d69e..c11baa9abd 100644 --- a/asoc/codecs/wcd937x/internal.h +++ b/asoc/codecs/wcd937x/internal.h @@ -13,6 +13,7 @@ #ifndef _WCD937X_INTERNAL_H #define _WCD937X_INTERNAL_H +#include "../wcd-clsh.h" #include "../wcd-mbhc-v2.h" #include "asoc/wcd-irq.h" #include "wcd937x-mbhc.h" @@ -54,6 +55,8 @@ struct wcd937x_priv { s32 dmic_0_1_clk_cnt; s32 dmic_2_3_clk_cnt; s32 dmic_4_5_clk_cnt; + /* class h specific info */ + struct wcd_clsh_cdc_info clsh_info; /* mbhc module */ struct wcd937x_mbhc *mbhc; diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 485d328389..02c952b756 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -297,7 +297,6 @@ static int wcd937x_rx_connect_port(struct snd_soc_codec *codec, static int wcd937x_rx_clk_enable(struct snd_soc_codec *codec) { - struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); if (wcd937x->rx_clk_cnt == 0) { @@ -324,10 +323,12 @@ static int wcd937x_rx_clk_disable(struct snd_soc_codec *codec) { struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + if (wcd937x->rx_clk_cnt == 0) { + dev_dbg(wcd937x->dev, "%s:clk already disabled\n", __func__); + return 0; + } wcd937x->rx_clk_cnt--; if (wcd937x->rx_clk_cnt == 0) { - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x40, 0x00); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x80, 0x00); snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x01, 0x00); snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); @@ -368,6 +369,7 @@ static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, @@ -384,8 +386,14 @@ static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, 0x80, 0x00); break; case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, - 0x0F, 0x02); + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x06); if (wcd937x->comp1_enable) { snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0, @@ -402,8 +410,15 @@ static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, usleep_range(5000, 5010); snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); break; case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x01); ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, false); @@ -419,6 +434,7 @@ static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; @@ -436,8 +452,14 @@ static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, 0x80, 0x00); break; case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, - 0x0F, 0x02); + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x06); if (wcd937x->comp2_enable) { snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0, @@ -454,8 +476,15 @@ static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, usleep_range(5000, 5010); snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); break; case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x01); ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, false); @@ -471,6 +500,7 @@ static int wcd937x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, @@ -483,11 +513,29 @@ static int wcd937x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, 0x04, 0x04); snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01); + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x06); snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0, 0x02, 0x02); usleep_range(5000, 5010); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + hph_mode); + break; case SND_SOC_DAPM_POST_PMD: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x01); ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, false); @@ -503,6 +551,7 @@ static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, @@ -517,6 +566,11 @@ static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, 0x04, 0x04); snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, 0x01, 0x01); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_AUX, + hph_mode); + break; case SND_SOC_DAPM_POST_PMD: ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, @@ -538,6 +592,7 @@ static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); int ret = 0; + int hph_mode = wcd937x->hph_mode; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, w->name, event); @@ -554,8 +609,9 @@ static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7010); snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x02, 0x02); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); if (wcd937x->update_wcd_event) wcd937x->update_wcd_event(wcd937x->handle, WCD_BOLERO_EVT_RX_MUTE, @@ -576,6 +632,10 @@ static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, WCD_EVENT_POST_HPHR_PA_OFF, &wcd937x->mbhc->wcd_mbhc); snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x10, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); break; }; return ret; @@ -588,6 +648,7 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); int ret = 0; + int hph_mode = wcd937x->hph_mode; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -601,8 +662,9 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, usleep_range(7000, 7010); snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x02, 0x02); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); if (wcd937x->update_wcd_event) wcd937x->update_wcd_event(wcd937x->handle, WCD_BOLERO_EVT_RX_MUTE, @@ -623,6 +685,10 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, WCD_EVENT_POST_HPHL_PA_OFF, &wcd937x->mbhc->wcd_mbhc); snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x20, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); break; }; return ret; @@ -634,6 +700,7 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, @@ -641,19 +708,15 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x80, 0x80); - usleep_range(500, 510); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A); - usleep_range(500, 510); ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); break; case SND_SOC_DAPM_POST_PMU: usleep_range(1000, 1010); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x20, 0x20); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); if (wcd937x->update_wcd_event) wcd937x->update_wcd_event(wcd937x->handle, WCD_BOLERO_EVT_RX_MUTE, @@ -668,6 +731,10 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMD: usleep_range(1000, 1010); usleep_range(1000, 1010); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_AUX, + hph_mode); break; }; return ret; @@ -679,6 +746,7 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; int ret = 0; dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, @@ -686,19 +754,15 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x08, 0x08); - usleep_range(500, 510); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A); - usleep_range(500, 510); ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); break; case SND_SOC_DAPM_POST_PMU: usleep_range(6000, 6010); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x02, 0x02); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); if (wcd937x->update_wcd_event) wcd937x->update_wcd_event(wcd937x->handle, WCD_BOLERO_EVT_RX_MUTE, @@ -712,16 +776,44 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: usleep_range(7000, 7010); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + hph_mode); break; }; return ret; } +static int wcd937x_enable_clsh(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 wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int mode = wcd937x->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || + mode == CLS_H_HIFI || mode == CLS_H_LP) { + wcd937x_rx_connect_port(codec, CLSH, + SND_SOC_DAPM_EVENT_ON(event)); + if (SND_SOC_DAPM_EVENT_OFF(event)) + ret = swr_slvdev_datapath_control( + wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + false); + } + return ret; +} + static int wcd937x_enable_rx1(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 wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); @@ -730,26 +822,6 @@ static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4, - 0xF0, 0x80); - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2, - 0xE0, 0xA0); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, - 0x02, 0x02); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, - 0xFF, 0x1C); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x40, 0x40); - usleep_range(100, 110); - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2, - 0xE0, 0xE0); - usleep_range(100, 110); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x80, 0x80); - usleep_range(500, 510); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A); - usleep_range(500, 510); - wcd937x_rx_connect_port(codec, HPH_L, true); if (wcd937x->comp1_enable) wcd937x_rx_connect_port(codec, COMP_L, true); @@ -765,6 +837,7 @@ static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, }; return 0; } + static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -776,24 +849,6 @@ static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4, - 0xF0, 0x80); - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2, - 0xE0, 0xA0); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x40, 0x40); - usleep_range(100, 110); - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2, - 0xE0, 0xE0); - usleep_range(100, 110); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x80, 0x80); - usleep_range(500, 510); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A); - usleep_range(500, 510); - wcd937x_rx_connect_port(codec, HPH_R, true); if (wcd937x->comp2_enable) wcd937x_rx_connect_port(codec, COMP_R, true); @@ -823,16 +878,6 @@ static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2, - 0xE0, 0xA0); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02); - snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C); - snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, - 0x40, 0x40); - usleep_range(100, 110); - snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2, - 0xE0, 0xE0); - usleep_range(100, 110); wcd937x_rx_connect_port(codec, LO, true); break; case SND_SOC_DAPM_POST_PMD: @@ -1589,6 +1634,10 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { wcd937x_codec_enable_vdd_buck, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("CLS_H_PORT", SND_SOC_NOPM, 0, 0, + wcd937x_enable_clsh, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /*rx widgets*/ SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0, wcd937x_codec_enable_ear_pa, @@ -1776,6 +1825,11 @@ static const struct snd_soc_dapm_route wcd937x_audio_map[] = { {"HPHR", NULL, "VDD_BUCK"}, {"HPHL", NULL, "VDD_BUCK"}, {"AUX", NULL, "VDD_BUCK"}, + + {"EAR", NULL, "CLS_H_PORT"}, + {"HPHR", NULL, "CLS_H_PORT"}, + {"HPHL", NULL, "CLS_H_PORT"}, + {"AUX", NULL, "CLS_H_PORT"}, }; static const struct snd_soc_dapm_route wcd9375_audio_map[] = { @@ -1948,6 +2002,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "HPHR"); snd_soc_dapm_sync(dapm); + wcd_cls_h_init(&wcd937x->clsh_info); wcd937x_init_reg(codec); if (wcd937x->variant == WCD9375_VARIANT) { diff --git a/include/asoc/wcd9xxx_registers.h b/include/asoc/wcd9xxx_registers.h new file mode 100644 index 0000000000..928dfcc56d --- /dev/null +++ b/include/asoc/wcd9xxx_registers.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WCD9XXX_REGISTERS_H +#define _WCD9XXX_REGISTERS_H + +#define WCD9XXX_BASE_ADDRESS 0x3000 + +#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008) +#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009) +#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098) +#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8) +#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF) +#define WCD9XXX_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7) +#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138) + +#endif