906 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.3// Copyright (c) 2017-2018, Linaro Limited4 5#include <linux/slab.h>6#include <sound/soc.h>7#include <linux/kernel.h>8#include <linux/delay.h>9#include "wcd9335.h"10#include "wcd-clsh-v2.h"11 12struct wcd_clsh_ctrl {13 int state;14 int mode;15 int flyback_users;16 int buck_users;17 int clsh_users;18 int codec_version;19 struct snd_soc_component *comp;20};21 22/* Class-H registers for codecs from and above WCD9335 */23#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42)24#define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6)25#define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6)26#define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 027#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56)28#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A)29#define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08)30#define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0)31#define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09)32#define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0)33#define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08)34#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1)35#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 036#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1)37#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2)38#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2)39#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 040#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3)41#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3)42#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 043#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6)44#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 645#define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6)46#define WCD9XXX_A_ANA_RX_VNEG_DISABLE 047#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7)48#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 749#define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7)50#define WCD9XXX_A_ANA_RX_VPOS_DISABLE 051#define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09)52#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2)53#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x0854#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x0455#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x056#define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01)57#define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0)58#define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0)59#define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 060#define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4)61#define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5)62#define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x4063#define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4)64#define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4)65#define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 066#define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7)67#define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4)68#define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0)69#define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3)70#define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3)71#define WCD9XXX_HPH_CONST_SEL_BYPASS 072#define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x4073#define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x8074#define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6)75#define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD)76#define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0)77#define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B)78#define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4)79#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x2080#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x081#define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55)82#define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69)83#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41)84#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0)85#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1)86#define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C)87#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4)88#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x5089#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x3090 91#define WCD9XXX_BASE_ADDRESS 0x300092#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008)93#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009)94#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098)95#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099)96#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (WCD9XXX_BASE_ADDRESS+0x0A5)97#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8)98#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF)99#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF)100#define WCD9XXX_V3_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7)101#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1)102#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138)103 104#define CLSH_REQ_ENABLE true105#define CLSH_REQ_DISABLE false106#define WCD_USLEEP_RANGE 50107 108enum {109 DAC_GAIN_0DB = 0,110 DAC_GAIN_0P2DB,111 DAC_GAIN_0P4DB,112 DAC_GAIN_0P6DB,113 DAC_GAIN_0P8DB,114 DAC_GAIN_M0P2DB,115 DAC_GAIN_M0P4DB,116 DAC_GAIN_M0P6DB,117};118 119static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl,120 bool enable)121{122 struct snd_soc_component *comp = ctrl->comp;123 124 if ((enable && ++ctrl->clsh_users == 1) ||125 (!enable && --ctrl->clsh_users == 0))126 snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC,127 WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK,128 enable);129 if (ctrl->clsh_users < 0)130 ctrl->clsh_users = 0;131}132 133static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp,134 int mode)135{136 /* set to HIFI */137 if (mode == CLS_H_HIFI)138 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,139 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,140 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA);141 else142 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,143 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK,144 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT);145}146 147static void wcd_clsh_v3_set_buck_mode(struct snd_soc_component *component,148 int mode)149{150 if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||151 mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI)152 snd_soc_component_update_bits(component,153 WCD9XXX_ANA_RX_SUPPLIES,154 0x08, 0x08); /* set to HIFI */155 else156 snd_soc_component_update_bits(component,157 WCD9XXX_ANA_RX_SUPPLIES,158 0x08, 0x00); /* set to default */159}160 161static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp,162 int mode)163{164 /* set to HIFI */165 if (mode == CLS_H_HIFI)166 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,167 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,168 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA);169 else170 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,171 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK,172 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT);173}174 175static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl,176 int mode,177 bool enable)178{179 struct snd_soc_component *comp = ctrl->comp;180 181 /* enable/disable buck */182 if ((enable && (++ctrl->buck_users == 1)) ||183 (!enable && (--ctrl->buck_users == 0)))184 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,185 WCD9XXX_A_ANA_RX_VPOS_EN_MASK,186 enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT);187 /*188 * 500us sleep is required after buck enable/disable189 * as per HW requirement190 */191 usleep_range(500, 500 + WCD_USLEEP_RANGE);192}193 194static void wcd_clsh_v3_buck_ctrl(struct snd_soc_component *component,195 struct wcd_clsh_ctrl *ctrl,196 int mode,197 bool enable)198{199 /* enable/disable buck */200 if ((enable && (++ctrl->buck_users == 1)) ||201 (!enable && (--ctrl->buck_users == 0))) {202 snd_soc_component_update_bits(component,203 WCD9XXX_ANA_RX_SUPPLIES,204 (1 << 7), (enable << 7));205 /*206 * 500us sleep is required after buck enable/disable207 * as per HW requirement208 */209 usleep_range(500, 510);210 if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||211 mode == CLS_H_HIFI || mode == CLS_H_LP)212 snd_soc_component_update_bits(component,213 WCD9XXX_CLASSH_MODE_3,214 0x02, 0x00);215 216 snd_soc_component_update_bits(component,217 WCD9XXX_CLASSH_MODE_2,218 0xFF, 0x3A);219 /* 500usec delay is needed as per HW requirement */220 usleep_range(500, 500 + WCD_USLEEP_RANGE);221 }222}223 224static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl,225 int mode,226 bool enable)227{228 struct snd_soc_component *comp = ctrl->comp;229 230 /* enable/disable flyback */231 if ((enable && (++ctrl->flyback_users == 1)) ||232 (!enable && (--ctrl->flyback_users == 0))) {233 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,234 WCD9XXX_A_ANA_RX_VNEG_EN_MASK,235 enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT);236 /* 100usec delay is needed as per HW requirement */237 usleep_range(100, 110);238 }239 /*240 * 500us sleep is required after flyback enable/disable241 * as per HW requirement242 */243 usleep_range(500, 500 + WCD_USLEEP_RANGE);244}245 246static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode)247{248 struct snd_soc_component *comp = ctrl->comp;249 int val = 0;250 251 switch (mode) {252 case CLS_H_NORMAL:253 case CLS_AB:254 val = WCD9XXX_HPH_CONST_SEL_BYPASS;255 break;256 case CLS_H_HIFI:257 val = WCD9XXX_HPH_CONST_SEL_HQ_PATH;258 break;259 case CLS_H_LP:260 val = WCD9XXX_HPH_CONST_SEL_LP_PATH;261 break;262 }263 264 snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN,265 WCD9XXX_HPH_CONST_SEL_L_MASK,266 val);267 268 snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN,269 WCD9XXX_HPH_CONST_SEL_L_MASK,270 val);271}272 273static void wcd_clsh_v2_set_hph_mode(struct snd_soc_component *comp, int mode)274{275 int val = 0, gain = 0, res_val;276 int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;277 278 res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM;279 switch (mode) {280 case CLS_H_NORMAL:281 res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM;282 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;283 gain = DAC_GAIN_0DB;284 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;285 break;286 case CLS_AB:287 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL;288 gain = DAC_GAIN_0DB;289 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;290 break;291 case CLS_H_HIFI:292 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA;293 gain = DAC_GAIN_M0P2DB;294 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA;295 break;296 case CLS_H_LP:297 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP;298 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA;299 break;300 }301 302 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH,303 WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val);304 snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2,305 WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK,306 res_val);307 if (mode != CLS_H_LP)308 snd_soc_component_update_bits(comp,309 WCD9XXX_HPH_REFBUFF_UHQA_CTL,310 WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK,311 gain);312 snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1,313 WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK,314 ipeak);315}316 317static void wcd_clsh_v3_set_hph_mode(struct snd_soc_component *component,318 int mode)319{320 u8 val;321 322 switch (mode) {323 case CLS_H_NORMAL:324 val = 0x00;325 break;326 case CLS_AB:327 case CLS_H_ULP:328 val = 0x0C;329 break;330 case CLS_AB_HIFI:331 case CLS_H_HIFI:332 val = 0x08;333 break;334 case CLS_H_LP:335 case CLS_H_LOHIFI:336 case CLS_AB_LP:337 case CLS_AB_LOHIFI:338 val = 0x04;339 break;340 default:341 dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode);342 return;343 }344 345 snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val);346}347 348void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode)349{350 struct snd_soc_component *comp = ctrl->comp;351 352 if (ctrl->codec_version >= WCD937X)353 wcd_clsh_v3_set_hph_mode(comp, mode);354 else355 wcd_clsh_v2_set_hph_mode(comp, mode);356 357}358EXPORT_SYMBOL_GPL(wcd_clsh_set_hph_mode);359 360static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp,361 int mode)362{363 364 snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,365 WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A);366 snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF,367 WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A);368 /* Sleep needed to avoid click and pop as per HW requirement */369 usleep_range(100, 110);370}371 372static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp,373 int mode)374{375 if (mode == CLS_AB)376 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,377 WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,378 WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB);379 else380 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES,381 WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK,382 WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H);383}384 385static void wcd_clsh_v3_set_buck_regulator_mode(struct snd_soc_component *component,386 int mode)387{388 snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES,389 0x02, 0x00);390}391 392static void wcd_clsh_v3_set_flyback_mode(struct snd_soc_component *component,393 int mode)394{395 if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||396 mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) {397 snd_soc_component_update_bits(component,398 WCD9XXX_ANA_RX_SUPPLIES,399 0x04, 0x04);400 snd_soc_component_update_bits(component,401 WCD9XXX_FLYBACK_VNEG_CTRL_4,402 0xF0, 0x80);403 } else {404 snd_soc_component_update_bits(component,405 WCD9XXX_ANA_RX_SUPPLIES,406 0x04, 0x00); /* set to Default */407 snd_soc_component_update_bits(component,408 WCD9XXX_FLYBACK_VNEG_CTRL_4,409 0xF0, 0x70);410 }411}412 413static void wcd_clsh_v3_force_iq_ctl(struct snd_soc_component *component,414 int mode, bool enable)415{416 if (enable) {417 snd_soc_component_update_bits(component,418 WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,419 0xE0, 0xA0);420 /* 100usec delay is needed as per HW requirement */421 usleep_range(100, 110);422 snd_soc_component_update_bits(component,423 WCD9XXX_CLASSH_MODE_3,424 0x02, 0x02);425 snd_soc_component_update_bits(component,426 WCD9XXX_CLASSH_MODE_2,427 0xFF, 0x1C);428 if (mode == CLS_H_LOHIFI || mode == CLS_AB_LOHIFI) {429 snd_soc_component_update_bits(component,430 WCD9XXX_HPH_NEW_INT_PA_MISC2,431 0x20, 0x20);432 snd_soc_component_update_bits(component,433 WCD9XXX_RX_BIAS_HPH_LOWPOWER,434 0xF0, 0xC0);435 snd_soc_component_update_bits(component,436 WCD9XXX_HPH_PA_CTL1,437 0x0E, 0x02);438 }439 } else {440 snd_soc_component_update_bits(component,441 WCD9XXX_HPH_NEW_INT_PA_MISC2,442 0x20, 0x00);443 snd_soc_component_update_bits(component,444 WCD9XXX_RX_BIAS_HPH_LOWPOWER,445 0xF0, 0x80);446 snd_soc_component_update_bits(component,447 WCD9XXX_HPH_PA_CTL1,448 0x0E, 0x06);449 }450}451 452static void wcd_clsh_v3_flyback_ctrl(struct snd_soc_component *component,453 struct wcd_clsh_ctrl *ctrl,454 int mode,455 bool enable)456{457 /* enable/disable flyback */458 if ((enable && (++ctrl->flyback_users == 1)) ||459 (!enable && (--ctrl->flyback_users == 0))) {460 snd_soc_component_update_bits(component,461 WCD9XXX_FLYBACK_VNEG_CTRL_1,462 0xE0, 0xE0);463 snd_soc_component_update_bits(component,464 WCD9XXX_ANA_RX_SUPPLIES,465 (1 << 6), (enable << 6));466 /*467 * 100us sleep is required after flyback enable/disable468 * as per HW requirement469 */470 usleep_range(100, 110);471 snd_soc_component_update_bits(component,472 WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,473 0xE0, 0xE0);474 /* 500usec delay is needed as per HW requirement */475 usleep_range(500, 500 + WCD_USLEEP_RANGE);476 }477}478 479static void wcd_clsh_v3_set_flyback_current(struct snd_soc_component *component,480 int mode)481{482 snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,483 0x0F, 0x0A);484 snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF,485 0xF0, 0xA0);486 /* Sleep needed to avoid click and pop as per HW requirement */487 usleep_range(100, 110);488}489 490static void wcd_clsh_v3_state_aux(struct wcd_clsh_ctrl *ctrl, int req_state,491 bool is_enable, int mode)492{493 struct snd_soc_component *component = ctrl->comp;494 495 if (is_enable) {496 wcd_clsh_v3_set_buck_mode(component, mode);497 wcd_clsh_v3_set_flyback_mode(component, mode);498 wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);499 wcd_clsh_v3_set_flyback_current(component, mode);500 wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);501 } else {502 wcd_clsh_v3_buck_ctrl(component, ctrl, mode, false);503 wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, false);504 wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);505 wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);506 }507}508 509static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state,510 bool is_enable, int mode)511{512 struct snd_soc_component *comp = ctrl->comp;513 514 if (mode != CLS_AB) {515 dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n",516 __func__, mode);517 return;518 }519 520 if (is_enable) {521 wcd_clsh_set_buck_regulator_mode(comp, mode);522 wcd_clsh_set_buck_mode(comp, mode);523 wcd_clsh_set_flyback_mode(comp, mode);524 wcd_clsh_flyback_ctrl(ctrl, mode, true);525 wcd_clsh_set_flyback_current(comp, mode);526 wcd_clsh_buck_ctrl(ctrl, mode, true);527 } else {528 wcd_clsh_buck_ctrl(ctrl, mode, false);529 wcd_clsh_flyback_ctrl(ctrl, mode, false);530 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);531 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);532 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);533 }534}535 536static void wcd_clsh_v3_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,537 bool is_enable, int mode)538{539 struct snd_soc_component *component = ctrl->comp;540 541 if (mode == CLS_H_NORMAL) {542 dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n",543 __func__);544 return;545 }546 547 if (is_enable) {548 wcd_clsh_v3_set_buck_regulator_mode(component, mode);549 wcd_clsh_v3_set_flyback_mode(component, mode);550 wcd_clsh_v3_force_iq_ctl(component, mode, true);551 wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);552 wcd_clsh_v3_set_flyback_current(component, mode);553 wcd_clsh_v3_set_buck_mode(component, mode);554 wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);555 wcd_clsh_v3_set_hph_mode(component, mode);556 } else {557 wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);558 559 /* buck and flyback set to default mode and disable */560 wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);561 wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);562 wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);563 wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);564 wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);565 }566}567 568static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state,569 bool is_enable, int mode)570{571 struct snd_soc_component *comp = ctrl->comp;572 573 if (mode == CLS_H_NORMAL) {574 dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n",575 __func__);576 return;577 }578 579 if (is_enable) {580 if (mode != CLS_AB) {581 wcd_enable_clsh_block(ctrl, true);582 /*583 * These K1 values depend on the Headphone Impedance584 * For now it is assumed to be 16 ohm585 */586 snd_soc_component_update_bits(comp,587 WCD9XXX_A_CDC_CLSH_K1_MSB,588 WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,589 0x00);590 snd_soc_component_update_bits(comp,591 WCD9XXX_A_CDC_CLSH_K1_LSB,592 WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,593 0xC0);594 snd_soc_component_update_bits(comp,595 WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,596 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,597 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);598 }599 wcd_clsh_set_buck_regulator_mode(comp, mode);600 wcd_clsh_set_flyback_mode(comp, mode);601 wcd_clsh_flyback_ctrl(ctrl, mode, true);602 wcd_clsh_set_flyback_current(comp, mode);603 wcd_clsh_set_buck_mode(comp, mode);604 wcd_clsh_buck_ctrl(ctrl, mode, true);605 wcd_clsh_v2_set_hph_mode(comp, mode);606 wcd_clsh_set_gain_path(ctrl, mode);607 } else {608 wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);609 610 if (mode != CLS_AB) {611 snd_soc_component_update_bits(comp,612 WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,613 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,614 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);615 wcd_enable_clsh_block(ctrl, false);616 }617 /* buck and flyback set to default mode and disable */618 wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);619 wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);620 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);621 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);622 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);623 }624}625 626static void wcd_clsh_v3_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,627 bool is_enable, int mode)628{629 struct snd_soc_component *component = ctrl->comp;630 631 if (mode == CLS_H_NORMAL) {632 dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n",633 __func__);634 return;635 }636 637 if (is_enable) {638 wcd_clsh_v3_set_buck_regulator_mode(component, mode);639 wcd_clsh_v3_set_flyback_mode(component, mode);640 wcd_clsh_v3_force_iq_ctl(component, mode, true);641 wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);642 wcd_clsh_v3_set_flyback_current(component, mode);643 wcd_clsh_v3_set_buck_mode(component, mode);644 wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);645 wcd_clsh_v3_set_hph_mode(component, mode);646 } else {647 wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);648 649 /* set buck and flyback to Default Mode */650 wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);651 wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);652 wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);653 wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);654 wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);655 }656}657 658static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state,659 bool is_enable, int mode)660{661 struct snd_soc_component *comp = ctrl->comp;662 663 if (mode == CLS_H_NORMAL) {664 dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n",665 __func__);666 return;667 }668 669 if (is_enable) {670 if (mode != CLS_AB) {671 wcd_enable_clsh_block(ctrl, true);672 /*673 * These K1 values depend on the Headphone Impedance674 * For now it is assumed to be 16 ohm675 */676 snd_soc_component_update_bits(comp,677 WCD9XXX_A_CDC_CLSH_K1_MSB,678 WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK,679 0x00);680 snd_soc_component_update_bits(comp,681 WCD9XXX_A_CDC_CLSH_K1_LSB,682 WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK,683 0xC0);684 snd_soc_component_update_bits(comp,685 WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,686 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,687 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);688 }689 wcd_clsh_set_buck_regulator_mode(comp, mode);690 wcd_clsh_set_flyback_mode(comp, mode);691 wcd_clsh_flyback_ctrl(ctrl, mode, true);692 wcd_clsh_set_flyback_current(comp, mode);693 wcd_clsh_set_buck_mode(comp, mode);694 wcd_clsh_buck_ctrl(ctrl, mode, true);695 wcd_clsh_v2_set_hph_mode(comp, mode);696 wcd_clsh_set_gain_path(ctrl, mode);697 } else {698 wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL);699 700 if (mode != CLS_AB) {701 snd_soc_component_update_bits(comp,702 WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,703 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,704 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);705 wcd_enable_clsh_block(ctrl, false);706 }707 /* set buck and flyback to Default Mode */708 wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false);709 wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false);710 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);711 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);712 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL);713 }714}715 716static void wcd_clsh_v3_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,717 bool is_enable, int mode)718{719 struct snd_soc_component *component = ctrl->comp;720 721 if (is_enable) {722 wcd_clsh_v3_set_buck_regulator_mode(component, mode);723 wcd_clsh_v3_set_flyback_mode(component, mode);724 wcd_clsh_v3_force_iq_ctl(component, mode, true);725 wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true);726 wcd_clsh_v3_set_flyback_current(component, mode);727 wcd_clsh_v3_set_buck_mode(component, mode);728 wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true);729 wcd_clsh_v3_set_hph_mode(component, mode);730 } else {731 wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL);732 733 /* set buck and flyback to Default Mode */734 wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false);735 wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false);736 wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false);737 wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL);738 wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL);739 }740}741 742static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state,743 bool is_enable, int mode)744{745 struct snd_soc_component *comp = ctrl->comp;746 747 if (mode != CLS_H_NORMAL) {748 dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n",749 __func__, mode);750 return;751 }752 753 if (is_enable) {754 wcd_enable_clsh_block(ctrl, true);755 snd_soc_component_update_bits(comp,756 WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,757 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,758 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE);759 wcd_clsh_set_buck_mode(comp, mode);760 wcd_clsh_set_flyback_mode(comp, mode);761 wcd_clsh_flyback_ctrl(ctrl, mode, true);762 wcd_clsh_set_flyback_current(comp, mode);763 wcd_clsh_buck_ctrl(ctrl, mode, true);764 } else {765 snd_soc_component_update_bits(comp,766 WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,767 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK,768 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE);769 wcd_enable_clsh_block(ctrl, false);770 wcd_clsh_buck_ctrl(ctrl, mode, false);771 wcd_clsh_flyback_ctrl(ctrl, mode, false);772 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL);773 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL);774 }775}776 777static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state,778 bool is_enable, int mode)779{780 switch (req_state) {781 case WCD_CLSH_STATE_EAR:782 if (ctrl->codec_version >= WCD937X)783 wcd_clsh_v3_state_ear(ctrl, req_state, is_enable, mode);784 else785 wcd_clsh_state_ear(ctrl, req_state, is_enable, mode);786 break;787 case WCD_CLSH_STATE_HPHL:788 if (ctrl->codec_version >= WCD937X)789 wcd_clsh_v3_state_hph_l(ctrl, req_state, is_enable, mode);790 else791 wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode);792 break;793 case WCD_CLSH_STATE_HPHR:794 if (ctrl->codec_version >= WCD937X)795 wcd_clsh_v3_state_hph_r(ctrl, req_state, is_enable, mode);796 else797 wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode);798 break;799 case WCD_CLSH_STATE_LO:800 if (ctrl->codec_version < WCD937X)801 wcd_clsh_state_lo(ctrl, req_state, is_enable, mode);802 break;803 case WCD_CLSH_STATE_AUX:804 if (ctrl->codec_version >= WCD937X)805 wcd_clsh_v3_state_aux(ctrl, req_state, is_enable, mode);806 break;807 default:808 break;809 }810 811 return 0;812}813 814/*815 * Function: wcd_clsh_is_state_valid816 * Params: state817 * Description:818 * Provides information on valid states of Class H configuration819 */820static bool wcd_clsh_is_state_valid(int state)821{822 switch (state) {823 case WCD_CLSH_STATE_IDLE:824 case WCD_CLSH_STATE_EAR:825 case WCD_CLSH_STATE_HPHL:826 case WCD_CLSH_STATE_HPHR:827 case WCD_CLSH_STATE_LO:828 case WCD_CLSH_STATE_AUX:829 return true;830 default:831 return false;832 };833}834 835/*836 * Function: wcd_clsh_fsm837 * Params: ctrl, req_state, req_type, clsh_event838 * Description:839 * This function handles PRE DAC and POST DAC conditions of different devices840 * and updates class H configuration of different combination of devices841 * based on validity of their states. ctrl will contain current842 * class h state information843 */844int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl,845 enum wcd_clsh_event clsh_event,846 int nstate,847 enum wcd_clsh_mode mode)848{849 struct snd_soc_component *comp = ctrl->comp;850 851 if (nstate == ctrl->state)852 return 0;853 854 if (!wcd_clsh_is_state_valid(nstate)) {855 dev_err(comp->dev, "Class-H not a valid new state:\n");856 return -EINVAL;857 }858 859 switch (clsh_event) {860 case WCD_CLSH_EVENT_PRE_DAC:861 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode);862 break;863 case WCD_CLSH_EVENT_POST_PA:864 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode);865 break;866 }867 868 ctrl->state = nstate;869 ctrl->mode = mode;870 871 return 0;872}873EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_set_state);874 875int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl)876{877 return ctrl->state;878}879EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_get_state);880 881struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp,882 int version)883{884 struct wcd_clsh_ctrl *ctrl;885 886 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);887 if (!ctrl)888 return ERR_PTR(-ENOMEM);889 890 ctrl->state = WCD_CLSH_STATE_IDLE;891 ctrl->comp = comp;892 ctrl->codec_version = version;893 894 return ctrl;895}896EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_alloc);897 898void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl)899{900 kfree(ctrl);901}902EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_free);903 904MODULE_DESCRIPTION("WCD93XX Class-H driver");905MODULE_LICENSE("GPL");906