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 12 struct 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 95 enum { 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 106 static 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 120 static 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 126 static 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 140 static 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 154 static 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 173 static 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 195 static 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 222 static 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 267 static 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 279 static 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 292 static 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 319 static 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 377 static 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 435 static 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 470 static 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 case WCD_CLSH_STATE_LO: 484 wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); 485 break; 486 default: 487 break; 488 } 489 490 return 0; 491 } 492 493 /* 494 * Function: wcd_clsh_is_state_valid 495 * Params: state 496 * Description: 497 * Provides information on valid states of Class H configuration 498 */ 499 static bool wcd_clsh_is_state_valid(int state) 500 { 501 switch (state) { 502 case WCD_CLSH_STATE_IDLE: 503 case WCD_CLSH_STATE_EAR: 504 case WCD_CLSH_STATE_HPHL: 505 case WCD_CLSH_STATE_HPHR: 506 case WCD_CLSH_STATE_LO: 507 return true; 508 default: 509 return false; 510 }; 511 } 512 513 /* 514 * Function: wcd_clsh_fsm 515 * Params: ctrl, req_state, req_type, clsh_event 516 * Description: 517 * This function handles PRE DAC and POST DAC conditions of different devices 518 * and updates class H configuration of different combination of devices 519 * based on validity of their states. ctrl will contain current 520 * class h state information 521 */ 522 int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, 523 enum wcd_clsh_event clsh_event, 524 int nstate, 525 enum wcd_clsh_mode mode) 526 { 527 struct snd_soc_component *comp = ctrl->comp; 528 529 if (nstate == ctrl->state) 530 return 0; 531 532 if (!wcd_clsh_is_state_valid(nstate)) { 533 dev_err(comp->dev, "Class-H not a valid new state:\n"); 534 return -EINVAL; 535 } 536 537 switch (clsh_event) { 538 case WCD_CLSH_EVENT_PRE_DAC: 539 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode); 540 break; 541 case WCD_CLSH_EVENT_POST_PA: 542 _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode); 543 break; 544 } 545 546 ctrl->state = nstate; 547 ctrl->mode = mode; 548 549 return 0; 550 } 551 552 int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) 553 { 554 return ctrl->state; 555 } 556 557 struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, 558 int version) 559 { 560 struct wcd_clsh_ctrl *ctrl; 561 562 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 563 if (!ctrl) 564 return ERR_PTR(-ENOMEM); 565 566 ctrl->state = WCD_CLSH_STATE_IDLE; 567 ctrl->comp = comp; 568 569 return ctrl; 570 } 571 572 void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) 573 { 574 kfree(ctrl); 575 } 576