linux/sound/soc/codecs/wcd-clsh-v2.c
<<
>>
Prefs
   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_read32(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