1// SPDX-License-Identifier: GPL-2.0 2// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. 3// Copyright (c) 2017-2018, Linaro Limited 4 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 0 27#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 0 36#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 0 40#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 0 43#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6) 44#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6 45#define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6) 46#define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0 47#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7) 48#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7 49#define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7) 50#define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0 51#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 0x08 54#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04 55#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0 56#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 0 60#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 0x40 63#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 0 66#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 0 72#define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40 73#define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80 74#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 0x20 80#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0 81#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 0x50 89#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30 90 91#define CLSH_REQ_ENABLE true 92#define CLSH_REQ_DISABLE false 93#define WCD_USLEEP_RANGE 50 94 95enum { 96 DAC_GAIN_0DB = 0, 97 DAC_GAIN_0P2DB, 98 DAC_GAIN_0P4DB, 99 DAC_GAIN_0P6DB, 100 DAC_GAIN_0P8DB, 101 DAC_GAIN_M0P2DB, 102 DAC_GAIN_M0P4DB, 103 DAC_GAIN_M0P6DB, 104}; 105 106static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl, 107 bool enable) 108{ 109 struct snd_soc_component *comp = ctrl->comp; 110 111 if ((enable && ++ctrl->clsh_users == 1) || 112 (!enable && --ctrl->clsh_users == 0)) 113 snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC, 114 WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK, 115 enable); 116 if (ctrl->clsh_users < 0) 117 ctrl->clsh_users = 0; 118} 119 120static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp) 121{ 122 return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) & 123 WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK; 124} 125 126static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp, 127 int mode) 128{ 129 /* set to HIFI */ 130 if (mode == CLS_H_HIFI) 131 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 132 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, 133 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA); 134 else 135 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 136 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, 137 WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT); 138} 139 140static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp, 141 int mode) 142{ 143 /* set to HIFI */ 144 if (mode == CLS_H_HIFI) 145 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 146 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, 147 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA); 148 else 149 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 150 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, 151 WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT); 152} 153 154static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl, 155 int mode, 156 bool enable) 157{ 158 struct snd_soc_component *comp = ctrl->comp; 159 160 /* enable/disable buck */ 161 if ((enable && (++ctrl->buck_users == 1)) || 162 (!enable && (--ctrl->buck_users == 0))) 163 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 164 WCD9XXX_A_ANA_RX_VPOS_EN_MASK, 165 enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT); 166 /* 167 * 500us sleep is required after buck enable/disable 168 * as per HW requirement 169 */ 170 usleep_range(500, 500 + WCD_USLEEP_RANGE); 171} 172 173static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl, 174 int mode, 175 bool enable) 176{ 177 struct snd_soc_component *comp = ctrl->comp; 178 179 /* enable/disable flyback */ 180 if ((enable && (++ctrl->flyback_users == 1)) || 181 (!enable && (--ctrl->flyback_users == 0))) { 182 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 183 WCD9XXX_A_ANA_RX_VNEG_EN_MASK, 184 enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT); 185 /* 100usec delay is needed as per HW requirement */ 186 usleep_range(100, 110); 187 } 188 /* 189 * 500us sleep is required after flyback enable/disable 190 * as per HW requirement 191 */ 192 usleep_range(500, 500 + WCD_USLEEP_RANGE); 193} 194 195static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode) 196{ 197 struct snd_soc_component *comp = ctrl->comp; 198 int val = 0; 199 200 switch (mode) { 201 case CLS_H_NORMAL: 202 case CLS_AB: 203 val = WCD9XXX_HPH_CONST_SEL_BYPASS; 204 break; 205 case CLS_H_HIFI: 206 val = WCD9XXX_HPH_CONST_SEL_HQ_PATH; 207 break; 208 case CLS_H_LP: 209 val = WCD9XXX_HPH_CONST_SEL_LP_PATH; 210 break; 211 } 212 213 snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN, 214 WCD9XXX_HPH_CONST_SEL_L_MASK, 215 val); 216 217 snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN, 218 WCD9XXX_HPH_CONST_SEL_L_MASK, 219 val); 220} 221 222static void wcd_clsh_set_hph_mode(struct snd_soc_component *comp, 223 int mode) 224{ 225 int val = 0, gain = 0, res_val; 226 int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; 227 228 res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM; 229 switch (mode) { 230 case CLS_H_NORMAL: 231 res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM; 232 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; 233 gain = DAC_GAIN_0DB; 234 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; 235 break; 236 case CLS_AB: 237 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; 238 gain = DAC_GAIN_0DB; 239 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; 240 break; 241 case CLS_H_HIFI: 242 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA; 243 gain = DAC_GAIN_M0P2DB; 244 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; 245 break; 246 case CLS_H_LP: 247 val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP; 248 ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA; 249 break; 250 } 251 252 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH, 253 WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val); 254 snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2, 255 WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK, 256 res_val); 257 if (mode != CLS_H_LP) 258 snd_soc_component_update_bits(comp, 259 WCD9XXX_HPH_REFBUFF_UHQA_CTL, 260 WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK, 261 gain); 262 snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1, 263 WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK, 264 ipeak); 265} 266 267static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp, 268 int mode) 269{ 270 271 snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, 272 WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A); 273 snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, 274 WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A); 275 /* Sleep needed to avoid click and pop as per HW requirement */ 276 usleep_range(100, 110); 277} 278 279static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp, 280 int mode) 281{ 282 if (mode == CLS_AB) 283 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 284 WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, 285 WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB); 286 else 287 snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, 288 WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, 289 WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H); 290} 291 292static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state, 293 bool is_enable, int mode) 294{ 295 struct snd_soc_component *comp = ctrl->comp; 296 297 if (mode != CLS_AB) { 298 dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n", 299 __func__, mode); 300 return; 301 } 302 303 if (is_enable) { 304 wcd_clsh_set_buck_regulator_mode(comp, mode); 305 wcd_clsh_set_buck_mode(comp, mode); 306 wcd_clsh_set_flyback_mode(comp, mode); 307 wcd_clsh_flyback_ctrl(ctrl, mode, true); 308 wcd_clsh_set_flyback_current(comp, mode); 309 wcd_clsh_buck_ctrl(ctrl, mode, true); 310 } else { 311 wcd_clsh_buck_ctrl(ctrl, mode, false); 312 wcd_clsh_flyback_ctrl(ctrl, mode, false); 313 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); 314 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); 315 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); 316 } 317} 318 319static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state, 320 bool is_enable, int mode) 321{ 322 struct snd_soc_component *comp = ctrl->comp; 323 324 if (mode == CLS_H_NORMAL) { 325 dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n", 326 __func__); 327 return; 328 } 329 330 if (is_enable) { 331 if (mode != CLS_AB) { 332 wcd_enable_clsh_block(ctrl, true); 333 /* 334 * These K1 values depend on the Headphone Impedance 335 * For now it is assumed to be 16 ohm 336 */ 337 snd_soc_component_update_bits(comp, 338 WCD9XXX_A_CDC_CLSH_K1_MSB, 339 WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, 340 0x00); 341 snd_soc_component_update_bits(comp, 342 WCD9XXX_A_CDC_CLSH_K1_LSB, 343 WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, 344 0xC0); 345 snd_soc_component_update_bits(comp, 346 WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, 347 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 348 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); 349 } 350 wcd_clsh_set_buck_regulator_mode(comp, mode); 351 wcd_clsh_set_flyback_mode(comp, mode); 352 wcd_clsh_flyback_ctrl(ctrl, mode, true); 353 wcd_clsh_set_flyback_current(comp, mode); 354 wcd_clsh_set_buck_mode(comp, mode); 355 wcd_clsh_buck_ctrl(ctrl, mode, true); 356 wcd_clsh_set_hph_mode(comp, mode); 357 wcd_clsh_set_gain_path(ctrl, mode); 358 } else { 359 wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); 360 361 if (mode != CLS_AB) { 362 snd_soc_component_update_bits(comp, 363 WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, 364 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 365 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); 366 wcd_enable_clsh_block(ctrl, false); 367 } 368 /* buck and flyback set to default mode and disable */ 369 wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); 370 wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); 371 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); 372 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); 373 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); 374 } 375} 376 377static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state, 378 bool is_enable, int mode) 379{ 380 struct snd_soc_component *comp = ctrl->comp; 381 382 if (mode == CLS_H_NORMAL) { 383 dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n", 384 __func__); 385 return; 386 } 387 388 if (is_enable) { 389 if (mode != CLS_AB) { 390 wcd_enable_clsh_block(ctrl, true); 391 /* 392 * These K1 values depend on the Headphone Impedance 393 * For now it is assumed to be 16 ohm 394 */ 395 snd_soc_component_update_bits(comp, 396 WCD9XXX_A_CDC_CLSH_K1_MSB, 397 WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, 398 0x00); 399 snd_soc_component_update_bits(comp, 400 WCD9XXX_A_CDC_CLSH_K1_LSB, 401 WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, 402 0xC0); 403 snd_soc_component_update_bits(comp, 404 WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, 405 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 406 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); 407 } 408 wcd_clsh_set_buck_regulator_mode(comp, mode); 409 wcd_clsh_set_flyback_mode(comp, mode); 410 wcd_clsh_flyback_ctrl(ctrl, mode, true); 411 wcd_clsh_set_flyback_current(comp, mode); 412 wcd_clsh_set_buck_mode(comp, mode); 413 wcd_clsh_buck_ctrl(ctrl, mode, true); 414 wcd_clsh_set_hph_mode(comp, mode); 415 wcd_clsh_set_gain_path(ctrl, mode); 416 } else { 417 wcd_clsh_set_hph_mode(comp, CLS_H_NORMAL); 418 419 if (mode != CLS_AB) { 420 snd_soc_component_update_bits(comp, 421 WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, 422 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 423 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); 424 wcd_enable_clsh_block(ctrl, false); 425 } 426 /* set buck and flyback to Default Mode */ 427 wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); 428 wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); 429 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); 430 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); 431 wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); 432 } 433} 434 435static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state, 436 bool is_enable, int mode) 437{ 438 struct snd_soc_component *comp = ctrl->comp; 439 440 if (mode != CLS_H_NORMAL) { 441 dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n", 442 __func__, mode); 443 return; 444 } 445 446 if (is_enable) { 447 wcd_enable_clsh_block(ctrl, true); 448 snd_soc_component_update_bits(comp, 449 WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, 450 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 451 WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); 452 wcd_clsh_set_buck_mode(comp, mode); 453 wcd_clsh_set_flyback_mode(comp, mode); 454 wcd_clsh_flyback_ctrl(ctrl, mode, true); 455 wcd_clsh_set_flyback_current(comp, mode); 456 wcd_clsh_buck_ctrl(ctrl, mode, true); 457 } else { 458 snd_soc_component_update_bits(comp, 459 WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, 460 WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, 461 WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); 462 wcd_enable_clsh_block(ctrl, false); 463 wcd_clsh_buck_ctrl(ctrl, mode, false); 464 wcd_clsh_flyback_ctrl(ctrl, mode, false); 465 wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); 466 wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); 467 } 468} 469 470static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state, 471 bool is_enable, int mode) 472{ 473 switch (req_state) { 474 case WCD_CLSH_STATE_EAR: 475 wcd_clsh_state_ear(ctrl, req_state, is_enable, mode); 476 break; 477 case WCD_CLSH_STATE_HPHL: 478 wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode); 479 break; 480 case WCD_CLSH_STATE_HPHR: 481 wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode); 482 break; 483 break; 484 case WCD_CLSH_STATE_LO: 485 wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); 486 break; 487 default: 488 break; 489 } 490 491 return 0; 492} 493 494/* 495 * Function: wcd_clsh_is_state_valid 496 * Params: state 497 * Description: 498 * Provides information on valid states of Class H configuration 499 */ 500static bool wcd_clsh_is_state_valid(int state) 501{ 502 switch (state) { 503 case WCD_CLSH_STATE_IDLE: 504 case WCD_CLSH_STATE_EAR: 505 case WCD_CLSH_STATE_HPHL: 506 case WCD_CLSH_STATE_HPHR: 507 case WCD_CLSH_STATE_LO: 508 return true; 509 default: 510 return false; 511 }; 512} 513 514/* 515 * Function: wcd_clsh_fsm 516 * Params: ctrl, req_state, req_type, clsh_event 517 * Description: 518 * This function handles PRE DAC and POST DAC conditions of different devices 519 * and updates class H configuration of different combination of devices 520 * based on validity of their states. ctrl will contain current 521 * class h state information 522 */ 523int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, 524 enum wcd_clsh_event clsh_event, 525 int nstate, 526 enum wcd_clsh_mode mode) 527{ 528 struct snd_soc_component *comp = ctrl->comp; 529 530 if (nstate == ctrl->state) 531 return 0; 532 533 if (!wcd_clsh_is_state_valid(nstate)) { 534 dev_err(comp->dev, "Class-H not a valid new state:\n"); 535 return -EINVAL; 536 } 537 538 switch (clsh_event) { 539 case WCD_CLSH_EVENT_PRE_DAC: 540 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode); 541 break; 542 case WCD_CLSH_EVENT_POST_PA: 543 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode); 544 break; 545 } 546 547 ctrl->state = nstate; 548 ctrl->mode = mode; 549 550 return 0; 551} 552 553int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) 554{ 555 return ctrl->state; 556} 557 558struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, 559 int version) 560{ 561 struct wcd_clsh_ctrl *ctrl; 562 563 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 564 if (!ctrl) 565 return ERR_PTR(-ENOMEM); 566 567 ctrl->state = WCD_CLSH_STATE_IDLE; 568 ctrl->comp = comp; 569 570 return ctrl; 571} 572 573void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) 574{ 575 kfree(ctrl); 576} 577