text: /* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 and
   * only version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/err.h>
  #include <linux/delay.h>
  #include <linux/iopoll.h>
  #include <linux/clk/msm-clk-provider.h>
  #include <linux/clk/msm-clk.h>
  #include <linux/clk/msm-clock-generic.h>
  #include <dt-bindings/clock/msm-clocks-8996.h>
  
  #include "mdss-pll.h"
  #include "mdss-hdmi-pll.h"
  
  /* CONSTANTS */
  #define HDMI_BIT_CLK_TO_PIX_CLK_RATIO            10
  #define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD         3400000000UL
  #define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD          1500000000UL
  #define HDMI_MID_FREQ_BIT_CLK_THRESHOLD          750000000
  #define HDMI_CLKS_PLL_DIVSEL                     0
  #define HDMI_CORECLK_DIV                         5
  #define HDMI_REF_CLOCK                           19200000
  #define HDMI_64B_ERR_VAL                         0xFFFFFFFFFFFFFFFFULL
  #define HDMI_VERSION_8996_V1                     1
  #define HDMI_VERSION_8996_V2                     2
  #define HDMI_VERSION_8996_V3                     3
  #define HDMI_VERSION_8996_V3_1_8                 4
  
  #define HDMI_VCO_MAX_FREQ                        12000000000UL
  #define HDMI_VCO_MIN_FREQ                        8000000000UL
  #define HDMI_2400MHZ_BIT_CLK_HZ                  2400000000UL
  #define HDMI_2250MHZ_BIT_CLK_HZ                  2250000000UL
  #define HDMI_2000MHZ_BIT_CLK_HZ                  2000000000UL
  #define HDMI_1700MHZ_BIT_CLK_HZ                  1700000000UL
  #define HDMI_1200MHZ_BIT_CLK_HZ                  1200000000UL
  #define HDMI_1334MHZ_BIT_CLK_HZ                  1334000000UL
  #define HDMI_1000MHZ_BIT_CLK_HZ                  1000000000UL
  #define HDMI_850MHZ_BIT_CLK_HZ                   850000000
  #define HDMI_667MHZ_BIT_CLK_HZ                   667000000
  #define HDMI_600MHZ_BIT_CLK_HZ                   600000000
  #define HDMI_500MHZ_BIT_CLK_HZ                   500000000
  #define HDMI_450MHZ_BIT_CLK_HZ                   450000000
  #define HDMI_334MHZ_BIT_CLK_HZ                   334000000
  #define HDMI_300MHZ_BIT_CLK_HZ                   300000000
  #define HDMI_282MHZ_BIT_CLK_HZ                   282000000
  #define HDMI_250MHZ_BIT_CLK_HZ                   250000000
  #define HDMI_KHZ_TO_HZ                           1000
  
  /* PLL REGISTERS */
  #define QSERDES_COM_ATB_SEL1                     (0x000)
  #define QSERDES_COM_ATB_SEL2                     (0x004)
  #define QSERDES_COM_FREQ_UPDATE                  (0x008)
  #define QSERDES_COM_BG_TIMER                     (0x00C)
  #define QSERDES_COM_SSC_EN_CENTER                (0x010)
  #define QSERDES_COM_SSC_ADJ_PER1                 (0x014)
  #define QSERDES_COM_SSC_ADJ_PER2                 (0x018)
  #define QSERDES_COM_SSC_PER1                     (0x01C)
  #define QSERDES_COM_SSC_PER2                     (0x020)
  #define QSERDES_COM_SSC_STEP_SIZE1               (0x024)
  #define QSERDES_COM_SSC_STEP_SIZE2               (0x028)
  #define QSERDES_COM_POST_DIV                     (0x02C)
  #define QSERDES_COM_POST_DIV_MUX                 (0x030)
  #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN          (0x034)
  #define QSERDES_COM_CLK_ENABLE1                  (0x038)
  #define QSERDES_COM_SYS_CLK_CTRL                 (0x03C)
  #define QSERDES_COM_SYSCLK_BUF_ENABLE            (0x040)
  #define QSERDES_COM_PLL_EN                       (0x044)
  #define QSERDES_COM_PLL_IVCO                     (0x048)
  #define QSERDES_COM_LOCK_CMP1_MODE0              (0x04C)
  #define QSERDES_COM_LOCK_CMP2_MODE0              (0x050)
  #define QSERDES_COM_LOCK_CMP3_MODE0              (0x054)
  #define QSERDES_COM_LOCK_CMP1_MODE1              (0x058)
  #define QSERDES_COM_LOCK_CMP2_MODE1              (0x05C)
  #define QSERDES_COM_LOCK_CMP3_MODE1              (0x060)
  #define QSERDES_COM_LOCK_CMP1_MODE2              (0x064)
  #define QSERDES_COM_CMN_RSVD0                    (0x064)
  #define QSERDES_COM_LOCK_CMP2_MODE2              (0x068)
  #define QSERDES_COM_EP_CLOCK_DETECT_CTRL         (0x068)
  #define QSERDES_COM_LOCK_CMP3_MODE2              (0x06C)
  #define QSERDES_COM_SYSCLK_DET_COMP_STATUS       (0x06C)
  #define QSERDES_COM_BG_TRIM                      (0x070)
  #define QSERDES_COM_CLK_EP_DIV                   (0x074)
  #define QSERDES_COM_CP_CTRL_MODE0                (0x078)
  #define QSERDES_COM_CP_CTRL_MODE1                (0x07C)
  #define QSERDES_COM_CP_CTRL_MODE2                (0x080)
  #define QSERDES_COM_CMN_RSVD1                    (0x080)
  #define QSERDES_COM_PLL_RCTRL_MODE0              (0x084)
  #define QSERDES_COM_PLL_RCTRL_MODE1              (0x088)
  #define QSERDES_COM_PLL_RCTRL_MODE2              (0x08C)
  #define QSERDES_COM_CMN_RSVD2                    (0x08C)
  #define QSERDES_COM_PLL_CCTRL_MODE0              (0x090)
  #define QSERDES_COM_PLL_CCTRL_MODE1              (0x094)
  #define QSERDES_COM_PLL_CCTRL_MODE2              (0x098)
  #define QSERDES_COM_CMN_RSVD3                    (0x098)
  #define QSERDES_COM_PLL_CNTRL                    (0x09C)
  #define QSERDES_COM_PHASE_SEL_CTRL               (0x0A0)
  #define QSERDES_COM_PHASE_SEL_DC                 (0x0A4)
  #define QSERDES_COM_CORE_CLK_IN_SYNC_SEL         (0x0A8)
  #define QSERDES_COM_BIAS_EN_CTRL_BY_PSM          (0x0A8)
  #define QSERDES_COM_SYSCLK_EN_SEL                (0x0AC)
  #define QSERDES_COM_CML_SYSCLK_SEL               (0x0B0)
  #define QSERDES_COM_RESETSM_CNTRL                (0x0B4)
  #define QSERDES_COM_RESETSM_CNTRL2               (0x0B8)
  #define QSERDES_COM_RESTRIM_CTRL                 (0x0BC)
  #define QSERDES_COM_RESTRIM_CTRL2                (0x0C0)
  #define QSERDES_COM_RESCODE_DIV_NUM              (0x0C4)
  #define QSERDES_COM_LOCK_CMP_EN                  (0x0C8)
  #define QSERDES_COM_LOCK_CMP_CFG                 (0x0CC)
  #define QSERDES_COM_DEC_START_MODE0              (0x0D0)
  #define QSERDES_COM_DEC_START_MODE1              (0x0D4)
  #define QSERDES_COM_DEC_START_MODE2              (0x0D8)
  #define QSERDES_COM_VCOCAL_DEADMAN_CTRL          (0x0D8)
  #define QSERDES_COM_DIV_FRAC_START1_MODE0        (0x0DC)
  #define QSERDES_COM_DIV_FRAC_START2_MODE0        (0x0E0)
  #define QSERDES_COM_DIV_FRAC_START3_MODE0        (0x0E4)
  #define QSERDES_COM_DIV_FRAC_START1_MODE1        (0x0E8)
  #define QSERDES_COM_DIV_FRAC_START2_MODE1        (0x0EC)
  #define QSERDES_COM_DIV_FRAC_START3_MODE1        (0x0F0)
  #define QSERDES_COM_DIV_FRAC_START1_MODE2        (0x0F4)
  #define QSERDES_COM_VCO_TUNE_MINVAL1             (0x0F4)
  #define QSERDES_COM_DIV_FRAC_START2_MODE2        (0x0F8)
  #define QSERDES_COM_VCO_TUNE_MINVAL2             (0x0F8)
  #define QSERDES_COM_DIV_FRAC_START3_MODE2        (0x0FC)
  #define QSERDES_COM_CMN_RSVD4                    (0x0FC)
  #define QSERDES_COM_INTEGLOOP_INITVAL            (0x100)
  #define QSERDES_COM_INTEGLOOP_EN                 (0x104)
  #define QSERDES_COM_INTEGLOOP_GAIN0_MODE0        (0x108)
  #define QSERDES_COM_INTEGLOOP_GAIN1_MODE0        (0x10C)
  #define QSERDES_COM_INTEGLOOP_GAIN0_MODE1        (0x110)
  #define QSERDES_COM_INTEGLOOP_GAIN1_MODE1        (0x114)
  #define QSERDES_COM_INTEGLOOP_GAIN0_MODE2        (0x118)
  #define QSERDES_COM_VCO_TUNE_MAXVAL1             (0x118)
  #define QSERDES_COM_INTEGLOOP_GAIN1_MODE2        (0x11C)
  #define QSERDES_COM_VCO_TUNE_MAXVAL2             (0x11C)
  #define QSERDES_COM_RES_TRIM_CONTROL2            (0x120)
  #define QSERDES_COM_VCO_TUNE_CTRL                (0x124)
  #define QSERDES_COM_VCO_TUNE_MAP                 (0x128)
  #define QSERDES_COM_VCO_TUNE1_MODE0              (0x12C)
  #define QSERDES_COM_VCO_TUNE2_MODE0              (0x130)
  #define QSERDES_COM_VCO_TUNE1_MODE1              (0x134)
  #define QSERDES_COM_VCO_TUNE2_MODE1              (0x138)
  #define QSERDES_COM_VCO_TUNE1_MODE2              (0x13C)
  #define QSERDES_COM_VCO_TUNE_INITVAL1            (0x13C)
  #define QSERDES_COM_VCO_TUNE2_MODE2              (0x140)
  #define QSERDES_COM_VCO_TUNE_INITVAL2            (0x140)
  #define QSERDES_COM_VCO_TUNE_TIMER1              (0x144)
  #define QSERDES_COM_VCO_TUNE_TIMER2              (0x148)
  #define QSERDES_COM_SAR                          (0x14C)
  #define QSERDES_COM_SAR_CLK                      (0x150)
  #define QSERDES_COM_SAR_CODE_OUT_STATUS          (0x154)
  #define QSERDES_COM_SAR_CODE_READY_STATUS        (0x158)
  #define QSERDES_COM_CMN_STATUS                   (0x15C)
  #define QSERDES_COM_RESET_SM_STATUS              (0x160)
  #define QSERDES_COM_RESTRIM_CODE_STATUS          (0x164)
  #define QSERDES_COM_PLLCAL_CODE1_STATUS          (0x168)
  #define QSERDES_COM_PLLCAL_CODE2_STATUS          (0x16C)
  #define QSERDES_COM_BG_CTRL                      (0x170)
  #define QSERDES_COM_CLK_SELECT                   (0x174)
  #define QSERDES_COM_HSCLK_SEL                    (0x178)
  #define QSERDES_COM_INTEGLOOP_BINCODE_STATUS     (0x17C)
  #define QSERDES_COM_PLL_ANALOG                   (0x180)
  #define QSERDES_COM_CORECLK_DIV                  (0x184)
  #define QSERDES_COM_SW_RESET                     (0x188)
  #define QSERDES_COM_CORE_CLK_EN                  (0x18C)
  #define QSERDES_COM_C_READY_STATUS               (0x190)
  #define QSERDES_COM_CMN_CONFIG                   (0x194)
  #define QSERDES_COM_CMN_RATE_OVERRIDE            (0x198)
  #define QSERDES_COM_SVS_MODE_CLK_SEL             (0x19C)
  #define QSERDES_COM_DEBUG_BUS0                   (0x1A0)
  #define QSERDES_COM_DEBUG_BUS1                   (0x1A4)
  #define QSERDES_COM_DEBUG_BUS2                   (0x1A8)
  #define QSERDES_COM_DEBUG_BUS3                   (0x1AC)
  #define QSERDES_COM_DEBUG_BUS_SEL                (0x1B0)
  #define QSERDES_COM_CMN_MISC1                    (0x1B4)
  #define QSERDES_COM_CMN_MISC2                    (0x1B8)
  #define QSERDES_COM_CORECLK_DIV_MODE1            (0x1BC)
  #define QSERDES_COM_CORECLK_DIV_MODE2            (0x1C0)
  #define QSERDES_COM_CMN_RSVD5                    (0x1C0)
  
  /* Tx Channel base addresses */
  #define HDMI_TX_L0_BASE_OFFSET                   (0x400)
  #define HDMI_TX_L1_BASE_OFFSET                   (0x600)
  #define HDMI_TX_L2_BASE_OFFSET                   (0x800)
  #define HDMI_TX_L3_BASE_OFFSET                   (0xA00)
  
  /* Tx Channel PHY registers */
  #define QSERDES_TX_L0_BIST_MODE_LANENO                    (0x000)
  #define QSERDES_TX_L0_BIST_INVERT                         (0x004)
  #define QSERDES_TX_L0_CLKBUF_ENABLE                       (0x008)
  #define QSERDES_TX_L0_CMN_CONTROL_ONE                     (0x00C)
  #define QSERDES_TX_L0_CMN_CONTROL_TWO                     (0x010)
  #define QSERDES_TX_L0_CMN_CONTROL_THREE                   (0x014)
  #define QSERDES_TX_L0_TX_EMP_POST1_LVL                    (0x018)
  #define QSERDES_TX_L0_TX_POST2_EMPH                       (0x01C)
  #define QSERDES_TX_L0_TX_BOOST_LVL_UP_DN                  (0x020)
  #define QSERDES_TX_L0_HP_PD_ENABLES                       (0x024)
  #define QSERDES_TX_L0_TX_IDLE_LVL_LARGE_AMP               (0x028)
  #define QSERDES_TX_L0_TX_DRV_LVL                          (0x02C)
  #define QSERDES_TX_L0_TX_DRV_LVL_OFFSET                   (0x030)
  #define QSERDES_TX_L0_RESET_TSYNC_EN                      (0x034)
  #define QSERDES_TX_L0_PRE_STALL_LDO_BOOST_EN              (0x038)
  #define QSERDES_TX_L0_TX_BAND                             (0x03C)
  #define QSERDES_TX_L0_SLEW_CNTL                           (0x040)
  #define QSERDES_TX_L0_INTERFACE_SELECT                    (0x044)
  #define QSERDES_TX_L0_LPB_EN                              (0x048)
  #define QSERDES_TX_L0_RES_CODE_LANE_TX                    (0x04C)
  #define QSERDES_TX_L0_RES_CODE_LANE_RX                    (0x050)
  #define QSERDES_TX_L0_RES_CODE_LANE_OFFSET                (0x054)
  #define QSERDES_TX_L0_PERL_LENGTH1                        (0x058)
  #define QSERDES_TX_L0_PERL_LENGTH2                        (0x05C)
  #define QSERDES_TX_L0_SERDES_BYP_EN_OUT                   (0x060)
  #define QSERDES_TX_L0_DEBUG_BUS_SEL                       (0x064)
  #define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN    (0x068)
  #define QSERDES_TX_L0_TX_POL_INV                          (0x06C)
  #define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN          (0x070)
  #define QSERDES_TX_L0_BIST_PATTERN1                       (0x074)
  #define QSERDES_TX_L0_BIST_PATTERN2                       (0x078)
  #define QSERDES_TX_L0_BIST_PATTERN3                       (0x07C)
  #define QSERDES_TX_L0_BIST_PATTERN4                       (0x080)
  #define QSERDES_TX_L0_BIST_PATTERN5                       (0x084)
  #define QSERDES_TX_L0_BIST_PATTERN6                       (0x088)
  #define QSERDES_TX_L0_BIST_PATTERN7                       (0x08C)
  #define QSERDES_TX_L0_BIST_PATTERN8                       (0x090)
  #define QSERDES_TX_L0_LANE_MODE                           (0x094)
  #define QSERDES_TX_L0_IDAC_CAL_LANE_MODE                  (0x098)
  #define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION    (0x09C)
  #define QSERDES_TX_L0_ATB_SEL1                            (0x0A0)
  #define QSERDES_TX_L0_ATB_SEL2                            (0x0A4)
  #define QSERDES_TX_L0_RCV_DETECT_LVL                      (0x0A8)
  #define QSERDES_TX_L0_RCV_DETECT_LVL_2                    (0x0AC)
  #define QSERDES_TX_L0_PRBS_SEED1                          (0x0B0)
  #define QSERDES_TX_L0_PRBS_SEED2                          (0x0B4)
  #define QSERDES_TX_L0_PRBS_SEED3                          (0x0B8)
  #define QSERDES_TX_L0_PRBS_SEED4                          (0x0BC)
  #define QSERDES_TX_L0_RESET_GEN                           (0x0C0)
  #define QSERDES_TX_L0_RESET_GEN_MUXES                     (0x0C4)
  #define QSERDES_TX_L0_TRAN_DRVR_EMP_EN                    (0x0C8)
  #define QSERDES_TX_L0_TX_INTERFACE_MODE                   (0x0CC)
  #define QSERDES_TX_L0_PWM_CTRL                            (0x0D0)
  #define QSERDES_TX_L0_PWM_ENCODED_OR_DATA                 (0x0D4)
  #define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND2            (0x0D8)
  #define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND2            (0x0DC)
  #define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND2            (0x0E0)
  #define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND2            (0x0E4)
  #define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND0_1          (0x0E8)
  #define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND0_1          (0x0EC)
  #define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND0_1          (0x0F0)
  #define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND0_1          (0x0F4)
  #define QSERDES_TX_L0_VMODE_CTRL1                         (0x0F8)
  #define QSERDES_TX_L0_VMODE_CTRL2                         (0x0FC)
  #define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL              (0x100)
  #define QSERDES_TX_L0_BIST_STATUS                         (0x104)
  #define QSERDES_TX_L0_BIST_ERROR_COUNT1                   (0x108)
  #define QSERDES_TX_L0_BIST_ERROR_COUNT2                   (0x10C)
  #define QSERDES_TX_L0_TX_ALOG_INTF_OBSV                   (0x110)
  
  /* HDMI PHY REGISTERS */
  #define HDMI_PHY_BASE_OFFSET                  (0xC00)
  
  #define HDMI_PHY_CFG                          (0x00)
  #define HDMI_PHY_PD_CTL                       (0x04)
  #define HDMI_PHY_MODE                         (0x08)
  #define HDMI_PHY_MISR_CLEAR                   (0x0C)
  #define HDMI_PHY_TX0_TX1_BIST_CFG0            (0x10)
  #define HDMI_PHY_TX0_TX1_BIST_CFG1            (0x14)
  #define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0      (0x18)
  #define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1      (0x1C)
  #define HDMI_PHY_TX0_TX1_BIST_PATTERN0        (0x20)
  #define HDMI_PHY_TX0_TX1_BIST_PATTERN1        (0x24)
  #define HDMI_PHY_TX2_TX3_BIST_CFG0            (0x28)
  #define HDMI_PHY_TX2_TX3_BIST_CFG1            (0x2C)
  #define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0      (0x30)
  #define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1      (0x34)
  #define HDMI_PHY_TX2_TX3_BIST_PATTERN0        (0x38)
  #define HDMI_PHY_TX2_TX3_BIST_PATTERN1        (0x3C)
  #define HDMI_PHY_DEBUG_BUS_SEL                (0x40)
  #define HDMI_PHY_TXCAL_CFG0                   (0x44)
  #define HDMI_PHY_TXCAL_CFG1                   (0x48)
  #define HDMI_PHY_TX0_TX1_LANE_CTL             (0x4C)
  #define HDMI_PHY_TX2_TX3_LANE_CTL             (0x50)
  #define HDMI_PHY_LANE_BIST_CONFIG             (0x54)
  #define HDMI_PHY_CLOCK                        (0x58)
  #define HDMI_PHY_MISC1                        (0x5C)
  #define HDMI_PHY_MISC2                        (0x60)
  #define HDMI_PHY_TX0_TX1_BIST_STATUS0         (0x64)
  #define HDMI_PHY_TX0_TX1_BIST_STATUS1         (0x68)
  #define HDMI_PHY_TX0_TX1_BIST_STATUS2         (0x6C)
  #define HDMI_PHY_TX2_TX3_BIST_STATUS0         (0x70)
  #define HDMI_PHY_TX2_TX3_BIST_STATUS1         (0x74)
  #define HDMI_PHY_TX2_TX3_BIST_STATUS2         (0x78)
  #define HDMI_PHY_PRE_MISR_STATUS0             (0x7C)
  #define HDMI_PHY_PRE_MISR_STATUS1             (0x80)
  #define HDMI_PHY_PRE_MISR_STATUS2             (0x84)
  #define HDMI_PHY_PRE_MISR_STATUS3             (0x88)
  #define HDMI_PHY_POST_MISR_STATUS0            (0x8C)
  #define HDMI_PHY_POST_MISR_STATUS1            (0x90)
  #define HDMI_PHY_POST_MISR_STATUS2            (0x94)
  #define HDMI_PHY_POST_MISR_STATUS3            (0x98)
  #define HDMI_PHY_STATUS                       (0x9C)
  #define HDMI_PHY_MISC3_STATUS                 (0xA0)
  #define HDMI_PHY_MISC4_STATUS                 (0xA4)
  #define HDMI_PHY_DEBUG_BUS0                   (0xA8)
  #define HDMI_PHY_DEBUG_BUS1                   (0xAC)
  #define HDMI_PHY_DEBUG_BUS2                   (0xB0)
  #define HDMI_PHY_DEBUG_BUS3                   (0xB4)
  #define HDMI_PHY_PHY_REVISION_ID0             (0xB8)
  #define HDMI_PHY_PHY_REVISION_ID1             (0xBC)
  #define HDMI_PHY_PHY_REVISION_ID2             (0xC0)
  #define HDMI_PHY_PHY_REVISION_ID3             (0xC4)
  
  #define HDMI_PLL_POLL_MAX_READS                100
  #define HDMI_PLL_POLL_TIMEOUT_US               1500
  
  enum hdmi_pll_freqs {
  	HDMI_PCLK_25200_KHZ,
  	HDMI_PCLK_27027_KHZ,
  	HDMI_PCLK_27000_KHZ,
  	HDMI_PCLK_74250_KHZ,
  	HDMI_PCLK_148500_KHZ,
  	HDMI_PCLK_154000_KHZ,
  	HDMI_PCLK_268500_KHZ,
  	HDMI_PCLK_297000_KHZ,
  	HDMI_PCLK_594000_KHZ,
  	HDMI_PCLK_MAX
  };
  
  struct hdmi_8996_phy_pll_reg_cfg {
  	u32 tx_l0_lane_mode;
  	u32 tx_l2_lane_mode;
  	u32 tx_l0_tx_band;
  	u32 tx_l1_tx_band;
  	u32 tx_l2_tx_band;
  	u32 tx_l3_tx_band;
  	u32 com_svs_mode_clk_sel;
  	u32 com_hsclk_sel;
  	u32 com_pll_cctrl_mode0;
  	u32 com_pll_rctrl_mode0;
  	u32 com_cp_ctrl_mode0;
  	u32 com_dec_start_mode0;
  	u32 com_div_frac_start1_mode0;
  	u32 com_div_frac_start2_mode0;
  	u32 com_div_frac_start3_mode0;
  	u32 com_integloop_gain0_mode0;
  	u32 com_integloop_gain1_mode0;
  	u32 com_lock_cmp_en;
  	u32 com_lock_cmp1_mode0;
  	u32 com_lock_cmp2_mode0;
  	u32 com_lock_cmp3_mode0;
  	u32 com_core_clk_en;
  	u32 com_coreclk_div;
  	u32 com_restrim_ctrl;
  	u32 com_vco_tune_ctrl;
  
  	u32 tx_l0_tx_drv_lvl;
  	u32 tx_l0_tx_emp_post1_lvl;
  	u32 tx_l1_tx_drv_lvl;
  	u32 tx_l1_tx_emp_post1_lvl;
  	u32 tx_l2_tx_drv_lvl;
  	u32 tx_l2_tx_emp_post1_lvl;
  	u32 tx_l3_tx_drv_lvl;
  	u32 tx_l3_tx_emp_post1_lvl;
  	u32 tx_l0_vmode_ctrl1;
  	u32 tx_l0_vmode_ctrl2;
  	u32 tx_l1_vmode_ctrl1;
  	u32 tx_l1_vmode_ctrl2;
  	u32 tx_l2_vmode_ctrl1;
  	u32 tx_l2_vmode_ctrl2;
  	u32 tx_l3_vmode_ctrl1;
  	u32 tx_l3_vmode_ctrl2;
  	u32 tx_l0_res_code_lane_tx;
  	u32 tx_l1_res_code_lane_tx;
  	u32 tx_l2_res_code_lane_tx;
  	u32 tx_l3_res_code_lane_tx;
  
  	u32 phy_mode;
  };
  
  struct hdmi_8996_v3_post_divider {
  	u64 vco_freq;
  	u64 hsclk_divsel;
  	u64 vco_ratio;
  	u64 tx_band_sel;
  	u64 half_rate_mode;
  };
  
  static inline struct hdmi_pll_vco_clk *to_hdmi_8996_vco_clk(struct clk *clk)
  {
  	return container_of(clk, struct hdmi_pll_vco_clk, c);
  }
  
  static inline u64 hdmi_8996_v1_get_post_div_lt_2g(u64 bclk)
  {
  	if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ)
  		return 2;
  	else if (bclk >= HDMI_1700MHZ_BIT_CLK_HZ)
  		return 3;
  	else if (bclk >= HDMI_1200MHZ_BIT_CLK_HZ)
  		return 4;
  	else if (bclk >= HDMI_850MHZ_BIT_CLK_HZ)
  		return 3;
  	else if (bclk >= HDMI_600MHZ_BIT_CLK_HZ)
  		return 4;
  	else if (bclk >= HDMI_450MHZ_BIT_CLK_HZ)
  		return 3;
  	else if (bclk >= HDMI_300MHZ_BIT_CLK_HZ)
  		return 4;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v2_get_post_div_lt_2g(u64 bclk, u64 vco_range)
  {
  	u64 hdmi_8ghz = vco_range;
  	u64 tmp_calc;
  
  	hdmi_8ghz <<= 2;
  	tmp_calc = hdmi_8ghz;
  	do_div(tmp_calc, 6U);
  
  	if (bclk >= vco_range)
  		return 2;
  	else if (bclk >= tmp_calc)
  		return 3;
  	else if (bclk >= vco_range >> 1)
  		return 4;
  
  	tmp_calc = hdmi_8ghz;
  	do_div(tmp_calc, 12U);
  	if (bclk >= tmp_calc)
  		return 3;
  	else if (bclk >= vco_range >> 2)
  		return 4;
  
  	tmp_calc = hdmi_8ghz;
  	do_div(tmp_calc, 24U);
  	if (bclk >= tmp_calc)
  		return 3;
  	else if (bclk >= vco_range >> 3)
  		return 4;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v2_get_post_div_gt_2g(u64 hsclk)
  {
  	if (hsclk >= 0 && hsclk <= 3)
  		return hsclk + 1;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_get_coreclk_div_lt_2g(u64 bclk)
  {
  	if (bclk >= HDMI_1334MHZ_BIT_CLK_HZ)
  		return 1;
  	else if (bclk >= HDMI_1000MHZ_BIT_CLK_HZ)
  		return 1;
  	else if (bclk >= HDMI_667MHZ_BIT_CLK_HZ)
  		return 2;
  	else if (bclk >= HDMI_500MHZ_BIT_CLK_HZ)
  		return 2;
  	else if (bclk >= HDMI_334MHZ_BIT_CLK_HZ)
  		return 3;
  	else if (bclk >= HDMI_250MHZ_BIT_CLK_HZ)
  		return 3;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_get_coreclk_div_ratio(u64 clks_pll_divsel,
  						  u64 coreclk_div)
  {
  	if (clks_pll_divsel == 0)
  		return coreclk_div*2;
  	else if (clks_pll_divsel == 1)
  		return coreclk_div*4;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v1_get_tx_band(u64 bclk)
  {
  	if (bclk >= 2400000000UL)
  		return 0;
  	if (bclk >= 1200000000UL)
  		return 1;
  	if (bclk >= 600000000UL)
  		return 2;
  	if (bclk >= 300000000UL)
  		return 3;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v2_get_tx_band(u64 bclk, u64 vco_range)
  {
  	if (bclk >= vco_range)
  		return 0;
  	else if (bclk >= vco_range >> 1)
  		return 1;
  	else if (bclk >= vco_range >> 2)
  		return 2;
  	else if (bclk >= vco_range >> 3)
  		return 3;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v1_get_hsclk(u64 fdata)
  {
  	if (fdata >= 9600000000UL)
  		return 0;
  	else if (fdata >= 4800000000UL)
  		return 1;
  	else if (fdata >= 3200000000UL)
  		return 2;
  	else if (fdata >= 2400000000UL)
  		return 3;
  
  	return HDMI_64B_ERR_VAL;
  }
  
  static inline u64 hdmi_8996_v2_get_hsclk(u64 fdata, u64 vco_range)
  {
  	u64 tmp_calc = vco_range;
  
  	tmp_calc <<= 2;
  	do_div(tmp_calc, 3U);
  	if (fdata >= (vco_range << 2))
  		return 0;
  	else if (fdata >= (vco_range << 1))
  		return 1;
  	else if (fdata >= tmp_calc)
  		return 2;
  	else if (fdata >= vco_range)
  		return 3;
  
  	return HDMI_64B_ERR_VAL;
  
  }
  
  static inline u64 hdmi_8996_v2_get_vco_freq(u64 bclk, u64 vco_range)
  {
  	u64 tx_band_div_ratio = 1U << hdmi_8996_v2_get_tx_band(bclk, vco_range);
  	u64 pll_post_div_ratio;
  
  	if (bclk >= vco_range) {
  		u64 hsclk = hdmi_8996_v2_get_hsclk(bclk, vco_range);
  
  		pll_post_div_ratio = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
  	} else {
  		pll_post_div_ratio = hdmi_8996_v2_get_post_div_lt_2g(bclk,
  								vco_range);
  	}
  
  	return bclk * (pll_post_div_ratio * tx_band_div_ratio);
  }
  
  static inline u64 hdmi_8996_v2_get_fdata(u64 bclk, u64 vco_range)
  {
  	if (bclk >= vco_range)
  		return bclk;
  
  	{
  		u64 tmp_calc = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
  		u64 pll_post_div_ratio_lt_2g = hdmi_8996_v2_get_post_div_lt_2g(
  							bclk, vco_range);
  		if (pll_post_div_ratio_lt_2g == HDMI_64B_ERR_VAL)
  			return HDMI_64B_ERR_VAL;
  
  		do_div(tmp_calc, pll_post_div_ratio_lt_2g);
  		return tmp_calc;
  	}
  }
  
  static inline u64 hdmi_8996_get_cpctrl(u64 frac_start, bool gen_ssc)
  {
  	if ((frac_start != 0) ||
  	    (gen_ssc == true))
  		/*
  		 * This should be ROUND(11/(19.2/20))).
  		 * Since ref clock does not change, hardcoding to 11
  		 */
  		return 0xB;
  
  	return 0x23;
  }
  
  static inline u64 hdmi_8996_get_rctrl(u64 frac_start, bool gen_ssc)
  {
  	if ((frac_start != 0) || (gen_ssc == true))
  		return 0x16;
  
  	return 0x10;
  }
  
  static inline u64 hdmi_8996_get_cctrl(u64 frac_start, bool gen_ssc)
  {
  	if ((frac_start != 0) || (gen_ssc == true))
  		return 0x28;
  
  	return 0x1;
  }
  
  static inline u64 hdmi_8996_get_integloop_gain(u64 frac_start, bool gen_ssc)
  {
  	if ((frac_start != 0) || (gen_ssc == true))
  		return 0x80;
  
  	return 0xC4;
  }
  
  static inline u64 hdmi_8996_v3_get_integloop_gain(u64 frac_start, u64 bclk,
  							bool gen_ssc)
  {
  	u64 digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
  	u64 base = ((frac_start != 0) || (gen_ssc == true)) ? 0x40 : 0xC4;
  
  	base <<= digclk_divsel;
  
  	return (base <= 2046 ? base : 0x7FE);
  }
  
  static inline u64 hdmi_8996_get_vco_tune(u64 fdata, u64 div)
  {
  	u64 vco_tune;
  
  	vco_tune = fdata * div;
  	do_div(vco_tune, 1000000);
  	vco_tune = 13000 - vco_tune - 256;
  	do_div(vco_tune, 5);
  
  	return vco_tune;
  }
  
  static inline u64 hdmi_8996_get_pll_cmp(u64 pll_cmp_cnt, u64 core_clk)
  {
  	u64 pll_cmp;
  	u64 rem;
  
  	pll_cmp = pll_cmp_cnt * core_clk;
  	rem = do_div(pll_cmp, HDMI_REF_CLOCK);
  	if (rem > (HDMI_REF_CLOCK >> 1))
  		pll_cmp++;
  	pll_cmp -= 1;
  
  	return pll_cmp;
  }
  
  static inline u64 hdmi_8996_v3_get_pll_cmp(u64 pll_cmp_cnt, u64 fdata)
  {
  	u64 dividend = pll_cmp_cnt * fdata;
  	u64 divisor = HDMI_REF_CLOCK * 10;
  	u64 rem;
  
  	rem = do_div(dividend, divisor);
  	if (rem > (divisor >> 1))
  		dividend++;
  
  	return dividend - 1;
  }
  
  static int hdmi_8996_v3_get_post_div(struct hdmi_8996_v3_post_divider *pd,
  						u64 bclk)
  {
  	u32 ratio[] = {2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35};
  	u32 tx_band_sel[] = {0, 1, 2, 3};
  	u64 vco_freq[60];
  	u64 vco, vco_optimal, half_rate_mode = 0;
  	int vco_optimal_index, vco_freq_index;
  	int i, j, k, x;
  
  	for (i = 0; i <= 1; i++) {
  		vco_optimal = HDMI_VCO_MAX_FREQ;
  		vco_optimal_index = -1;
  		vco_freq_index = 0;
  		for (j = 0; j < 15; j++) {
  			for (k = 0; k < 4; k++) {
  				u64 ratio_mult = ratio[j] << tx_band_sel[k];
  
  				vco = bclk >> half_rate_mode;
  				vco *= ratio_mult;
  				vco_freq[vco_freq_index++] = vco;
  			}
  		}
  
  		for (x = 0; x < 60; x++) {
  			u64 vco_tmp = vco_freq[x];
  
  			if ((vco_tmp >= HDMI_VCO_MIN_FREQ) &&
  					(vco_tmp <= vco_optimal)) {
  				vco_optimal = vco_tmp;
  				vco_optimal_index = x;
  			}
  		}
  
  		if (vco_optimal_index == -1) {
  			if (!half_rate_mode)
  				half_rate_mode++;
  			else
  				return -EINVAL;
  		} else {
  			pd->vco_freq = vco_optimal;
  			pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4];
  			pd->vco_ratio = ratio[vco_optimal_index / 4];
  			break;
  		}
  	}
  
  	switch (pd->vco_ratio) {
  	case 2:
  		pd->hsclk_divsel = 0;
  		break;
  	case 3:
  		pd->hsclk_divsel = 4;
  		break;
  	case 4:
  		pd->hsclk_divsel = 8;
  		break;
  	case 5:
  		pd->hsclk_divsel = 12;
  		break;
  	case 6:
  		pd->hsclk_divsel = 1;
  		break;
  	case 9:
  		pd->hsclk_divsel = 5;
  		break;
  	case 10:
  		pd->hsclk_divsel = 2;
  		break;
  	case 12:
  		pd->hsclk_divsel = 9;
  		break;
  	case 14:
  		pd->hsclk_divsel = 3;
  		break;
  	case 15:
  		pd->hsclk_divsel = 13;
  		break;
  	case 20:
  		pd->hsclk_divsel = 10;
  		break;
  	case 21:
  		pd->hsclk_divsel = 7;
  		break;
  	case 25:
  		pd->hsclk_divsel = 14;
  		break;
  	case 28:
  		pd->hsclk_divsel = 11;
  		break;
  	case 35:
  		pd->hsclk_divsel = 15;
  		break;
  	};
  
  	return 0;
  }
  
  static int hdmi_8996_v1_calculate(u32 pix_clk,
  			       struct hdmi_8996_phy_pll_reg_cfg *cfg)
  {
  	int rc = -EINVAL;
  	u64 fdata, clk_divtx, tmds_clk;
  	u64 bclk;
  	u64 post_div_gt_2g;
  	u64 post_div_lt_2g;
  	u64 coreclk_div1_lt_2g;
  	u64 core_clk_div_ratio;
  	u64 core_clk;
  	u64 pll_cmp;
  	u64 tx_band;
  	u64 tx_band_div_ratio;
  	u64 hsclk;
  	u64 dec_start;
  	u64 frac_start;
  	u64 pll_divisor = 4 * HDMI_REF_CLOCK;
  	u64 cpctrl;
  	u64 rctrl;
  	u64 cctrl;
  	u64 integloop_gain;
  	u64 vco_tune;
  	u64 vco_freq;
  	u64 rem;
  
  	/* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
  	bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
  		tmds_clk = bclk/4;
  	else
  		tmds_clk = bclk;
  
  	post_div_lt_2g = hdmi_8996_v1_get_post_div_lt_2g(bclk);
  	if (post_div_lt_2g == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	coreclk_div1_lt_2g = hdmi_8996_get_coreclk_div_lt_2g(bclk);
  
  	core_clk_div_ratio = hdmi_8996_get_coreclk_div_ratio(
  				HDMI_CLKS_PLL_DIVSEL, HDMI_CORECLK_DIV);
  
  	tx_band = hdmi_8996_v1_get_tx_band(bclk);
  	if (tx_band == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	tx_band_div_ratio = 1 << tx_band;
  
  	if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) {
  		fdata = bclk;
  		hsclk = hdmi_8996_v1_get_hsclk(fdata);
  		if (hsclk == HDMI_64B_ERR_VAL)
  			goto fail;
  
  		post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
  		if (post_div_gt_2g == HDMI_64B_ERR_VAL)
  			goto fail;
  
  		vco_freq = bclk * (post_div_gt_2g * tx_band_div_ratio);
  		clk_divtx = vco_freq;
  		do_div(clk_divtx, post_div_gt_2g);
  	} else {
  		vco_freq = bclk * (post_div_lt_2g * tx_band_div_ratio);
  		fdata = vco_freq;
  		do_div(fdata, post_div_lt_2g);
  		hsclk = hdmi_8996_v1_get_hsclk(fdata);
  		if (hsclk == HDMI_64B_ERR_VAL)
  			goto fail;
  
  		clk_divtx = vco_freq;
  		do_div(clk_divtx, post_div_lt_2g);
  		post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
  		if (post_div_gt_2g == HDMI_64B_ERR_VAL)
  			goto fail;
  	}
  
  	/* Decimal and fraction values */
  	dec_start = fdata * post_div_gt_2g;
  	do_div(dec_start, pll_divisor);
  	frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
  			(fdata * post_div_gt_2g))) * (1 << 20));
  	rem = do_div(frac_start, pll_divisor);
  	/* Round off frac_start to closest integer */
  	if (rem >= (pll_divisor >> 1))
  		frac_start++;
  
  	cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
  	rctrl = hdmi_8996_get_rctrl(frac_start, false);
  	cctrl = hdmi_8996_get_cctrl(frac_start, false);
  	integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
  	vco_tune = hdmi_8996_get_vco_tune(fdata, post_div_gt_2g);
  
  	core_clk = clk_divtx;
  	do_div(core_clk, core_clk_div_ratio);
  	pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
  
  	/* Debug dump */
  	DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
  	DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
  	DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
  	DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
  	DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
  	DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
  	DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
  	DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
  	DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
  	DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
  	DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
  	DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
  	DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
  	DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
  	DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
  
  	/* Convert these values to register specific values */
  	cfg->tx_l0_lane_mode = 0x3;
  	cfg->tx_l2_lane_mode = 0x3;
  	cfg->tx_l0_tx_band = tx_band + 4;
  	cfg->tx_l1_tx_band = tx_band + 4;
  	cfg->tx_l2_tx_band = tx_band + 4;
  	cfg->tx_l3_tx_band = tx_band + 4;
  	cfg->tx_l0_res_code_lane_tx = 0x33;
  	cfg->tx_l1_res_code_lane_tx = 0x33;
  	cfg->tx_l2_res_code_lane_tx = 0x33;
  	cfg->tx_l3_res_code_lane_tx = 0x33;
  	cfg->com_restrim_ctrl = 0x0;
  	cfg->com_vco_tune_ctrl = 0x1C;
  
  	cfg->com_svs_mode_clk_sel =
  			(bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2);
  	cfg->com_hsclk_sel = (0x28 | hsclk);
  	cfg->com_pll_cctrl_mode0 = cctrl;
  	cfg->com_pll_rctrl_mode0 = rctrl;
  	cfg->com_cp_ctrl_mode0 = cpctrl;
  	cfg->com_dec_start_mode0 = dec_start;
  	cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
  	cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
  	cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
  	cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
  	cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
  	cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
  	cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
  	cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
  	cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
  	cfg->com_coreclk_div = HDMI_CORECLK_DIV;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x22;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x27;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  		cfg->com_restrim_ctrl = 0x0;
  	} else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x25;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  		cfg->com_restrim_ctrl = 0x0;
  	} else {
  		cfg->tx_l0_tx_drv_lvl = 0x20;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l1_tx_drv_lvl = 0x20;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l2_tx_drv_lvl = 0x20;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l3_tx_drv_lvl = 0x20;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0E;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0E;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0E;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x0E;
  		cfg->com_restrim_ctrl = 0xD8;
  	}
  
  	cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
  	DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
  	DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
  	DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
  	DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
  	DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
  	DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
  	DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
  	DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
  						cfg->com_svs_mode_clk_sel);
  	DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
  	DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
  						cfg->com_pll_cctrl_mode0);
  	DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
  						cfg->com_pll_rctrl_mode0);
  	DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
  						cfg->com_cp_ctrl_mode0);
  	DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
  						cfg->com_dec_start_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
  						cfg->com_div_frac_start1_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
  						cfg->com_div_frac_start2_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
  						cfg->com_div_frac_start3_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
  						cfg->com_integloop_gain0_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
  						cfg->com_integloop_gain1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
  						cfg->com_lock_cmp1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
  						cfg->com_lock_cmp2_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
  						cfg->com_lock_cmp3_mode0);
  	DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
  	DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
  	DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n",	cfg->com_restrim_ctrl);
  
  	DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l0_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l1_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l2_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l3_tx_emp_post1_lvl);
  
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l0_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l1_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l2_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l3_res_code_lane_tx);
  
  	DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
  	rc = 0;
  fail:
  	return rc;
  }
  
  static int hdmi_8996_v2_calculate(u32 pix_clk,
  			       struct hdmi_8996_phy_pll_reg_cfg *cfg)
  {
  	int rc = -EINVAL;
  	u64 fdata, clk_divtx, tmds_clk;
  	u64 bclk;
  	u64 post_div;
  	u64 core_clk_div;
  	u64 core_clk_div_ratio;
  	u64 core_clk;
  	u64 pll_cmp;
  	u64 tx_band;
  	u64 tx_band_div_ratio;
  	u64 hsclk;
  	u64 dec_start;
  	u64 frac_start;
  	u64 pll_divisor = 4 * HDMI_REF_CLOCK;
  	u64 cpctrl;
  	u64 rctrl;
  	u64 cctrl;
  	u64 integloop_gain;
  	u64 vco_tune;
  	u64 vco_freq;
  	u64 vco_range;
  	u64 rem;
  
  	/* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
  	bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
  		tmds_clk = pix_clk >> 2;
  	else
  		tmds_clk = pix_clk;
  
  	vco_range = bclk < HDMI_282MHZ_BIT_CLK_HZ ? HDMI_2000MHZ_BIT_CLK_HZ :
  				HDMI_2250MHZ_BIT_CLK_HZ;
  
  	fdata = hdmi_8996_v2_get_fdata(bclk, vco_range);
  	if (fdata == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	hsclk = hdmi_8996_v2_get_hsclk(fdata, vco_range);
  	if (hsclk == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	if (bclk >= vco_range)
  		post_div = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
  	else
  		post_div = hdmi_8996_v2_get_post_div_lt_2g(bclk, vco_range);
  
  	if (post_div == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	core_clk_div = 5;
  	core_clk_div_ratio = core_clk_div * 2;
  
  	tx_band = hdmi_8996_v2_get_tx_band(bclk, vco_range);
  	if (tx_band == HDMI_64B_ERR_VAL)
  		goto fail;
  
  	tx_band_div_ratio = 1 << tx_band;
  
  	vco_freq = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
  	clk_divtx = vco_freq;
  	do_div(clk_divtx, post_div);
  
  	/* Decimal and fraction values */
  	dec_start = fdata * post_div;
  	do_div(dec_start, pll_divisor);
  	frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
  			(fdata * post_div))) * (1 << 20));
  	rem = do_div(frac_start, pll_divisor);
  	/* Round off frac_start to closest integer */
  	if (rem >= (pll_divisor >> 1))
  		frac_start++;
  
  	cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
  	rctrl = hdmi_8996_get_rctrl(frac_start, false);
  	cctrl = hdmi_8996_get_cctrl(frac_start, false);
  	integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
  	vco_tune = hdmi_8996_get_vco_tune(fdata, post_div);
  
  	core_clk = clk_divtx;
  	do_div(core_clk, core_clk_div_ratio);
  	pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
  
  	/* Debug dump */
  	DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
  	DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
  	DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
  	DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
  	DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
  	DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
  	DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
  	DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
  	DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
  	DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
  	DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
  	DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
  	DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
  	DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
  	DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
  
  	/* Convert these values to register specific values */
  	cfg->tx_l0_lane_mode = 0x3;
  	cfg->tx_l2_lane_mode = 0x3;
  	cfg->tx_l0_tx_band = tx_band + 4;
  	cfg->tx_l1_tx_band = tx_band + 4;
  	cfg->tx_l2_tx_band = tx_band + 4;
  	cfg->tx_l3_tx_band = tx_band + 4;
  
  	if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
  		cfg->com_svs_mode_clk_sel = 1;
  	else
  		cfg->com_svs_mode_clk_sel = 2;
  
  	cfg->com_hsclk_sel = (0x28 | hsclk);
  	cfg->com_pll_cctrl_mode0 = cctrl;
  	cfg->com_pll_rctrl_mode0 = rctrl;
  	cfg->com_cp_ctrl_mode0 = cpctrl;
  	cfg->com_dec_start_mode0 = dec_start;
  	cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
  	cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
  	cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
  	cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
  	cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
  	cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
  	cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
  	cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
  	cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
  	cfg->com_coreclk_div = HDMI_CORECLK_DIV;
  	cfg->com_vco_tune_ctrl = 0x0;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x22;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x27;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  		cfg->tx_l0_res_code_lane_tx = 0x3F;
  		cfg->tx_l1_res_code_lane_tx = 0x3F;
  		cfg->tx_l2_res_code_lane_tx = 0x3F;
  		cfg->tx_l3_res_code_lane_tx = 0x3F;
  		cfg->com_restrim_ctrl = 0x0;
  	} else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x25;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  		cfg->tx_l0_res_code_lane_tx = 0x39;
  		cfg->tx_l1_res_code_lane_tx = 0x39;
  		cfg->tx_l2_res_code_lane_tx = 0x39;
  		cfg->tx_l3_res_code_lane_tx = 0x39;
  		cfg->com_restrim_ctrl = 0x0;
  	} else {
  		cfg->tx_l0_tx_drv_lvl = 0x20;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l1_tx_drv_lvl = 0x20;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l2_tx_drv_lvl = 0x20;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l3_tx_drv_lvl = 0x20;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0E;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0E;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0E;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x0E;
  		cfg->tx_l0_res_code_lane_tx = 0x3F;
  		cfg->tx_l1_res_code_lane_tx = 0x3F;
  		cfg->tx_l2_res_code_lane_tx = 0x3F;
  		cfg->tx_l3_res_code_lane_tx = 0x3F;
  		cfg->com_restrim_ctrl = 0xD8;
  	}
  
  	cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
  	DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
  	DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
  	DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
  	DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
  	DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
  	DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
  	DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
  	DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
  						cfg->com_svs_mode_clk_sel);
  	DEV_DBG("PLL PARAM: com_vco_tune_ctrl = 0x%x\n",
  						cfg->com_vco_tune_ctrl);
  	DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
  	DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
  	DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
  						cfg->com_pll_cctrl_mode0);
  	DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
  						cfg->com_pll_rctrl_mode0);
  	DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
  						cfg->com_cp_ctrl_mode0);
  	DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
  						cfg->com_dec_start_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
  						cfg->com_div_frac_start1_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
  						cfg->com_div_frac_start2_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
  						cfg->com_div_frac_start3_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
  						cfg->com_integloop_gain0_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
  						cfg->com_integloop_gain1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
  						cfg->com_lock_cmp1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
  						cfg->com_lock_cmp2_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
  						cfg->com_lock_cmp3_mode0);
  	DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
  	DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
  
  	DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l0_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l1_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l2_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l3_tx_emp_post1_lvl);
  
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l0_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l1_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l2_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
  					cfg->tx_l3_res_code_lane_tx);
  	DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n",	cfg->com_restrim_ctrl);
  
  	DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
  	rc = 0;
  fail:
  	return rc;
  }
  
  static int hdmi_8996_v3_calculate(u32 pix_clk,
  			       struct hdmi_8996_phy_pll_reg_cfg *cfg)
  {
  	int rc = -EINVAL;
  	struct hdmi_8996_v3_post_divider pd;
  	u64 fdata, tmds_clk;
  	u64 bclk;
  	u64 pll_cmp;
  	u64 tx_band;
  	u64 hsclk;
  	u64 dec_start;
  	u64 frac_start;
  	u64 pll_divisor = 4 * HDMI_REF_CLOCK;
  	u64 cpctrl;
  	u64 rctrl;
  	u64 cctrl;
  	u64 integloop_gain;
  	u64 vco_freq;
  	u64 rem;
  
  	/* FDATA, HSCLK, PIXEL_CLK, TMDS_CLK */
  	bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
  		tmds_clk = pix_clk >> 2;
  	else
  		tmds_clk = pix_clk;
  
  	if (hdmi_8996_v3_get_post_div(&pd, bclk) || pd.vco_ratio <= 0 ||
  			pd.vco_freq <= 0)
  		goto fail;
  
  	vco_freq = pd.vco_freq;
  	fdata = pd.vco_freq;
  	do_div(fdata, pd.vco_ratio);
  
  	hsclk = pd.hsclk_divsel;
  	dec_start = vco_freq;
  	do_div(dec_start, pll_divisor);
  
  	frac_start = vco_freq * (1 << 20);
  	rem = do_div(frac_start, pll_divisor);
  	frac_start -= dec_start * (1 << 20);
  	if (rem > (pll_divisor >> 1))
  		frac_start++;
  
  	cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
  	rctrl = hdmi_8996_get_rctrl(frac_start, false);
  	cctrl = hdmi_8996_get_cctrl(frac_start, false);
  	integloop_gain = hdmi_8996_v3_get_integloop_gain(frac_start, bclk,
  									false);
  	pll_cmp = hdmi_8996_v3_get_pll_cmp(1024, fdata);
  	tx_band = pd.tx_band_sel;
  
  	/* Debug dump */
  	DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
  	DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
  	DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
  	DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
  	DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
  	DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
  	DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
  	DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
  	DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
  	DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
  	DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
  	DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
  	DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
  
  	/* Convert these values to register specific values */
  	cfg->tx_l0_tx_band = tx_band + 4;
  	cfg->tx_l1_tx_band = tx_band + 4;
  	cfg->tx_l2_tx_band = tx_band + 4;
  	cfg->tx_l3_tx_band = tx_band + 4;
  
  	if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
  		cfg->com_svs_mode_clk_sel = 1;
  	else
  		cfg->com_svs_mode_clk_sel = 2;
  
  	cfg->com_hsclk_sel = (0x20 | hsclk);
  	cfg->com_pll_cctrl_mode0 = cctrl;
  	cfg->com_pll_rctrl_mode0 = rctrl;
  	cfg->com_cp_ctrl_mode0 = cpctrl;
  	cfg->com_dec_start_mode0 = dec_start;
  	cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
  	cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
  	cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
  	cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
  	cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
  	cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
  	cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
  	cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
  	cfg->com_lock_cmp_en = 0x04;
  	cfg->com_core_clk_en = 0x2C;
  	cfg->com_coreclk_div = HDMI_CORECLK_DIV;
  	cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
  	cfg->com_vco_tune_ctrl = 0x0;
  
  	cfg->tx_l0_lane_mode = 0x43;
  	cfg->tx_l2_lane_mode = 0x43;
  
  	if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x22;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x27;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  	} else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
  		cfg->tx_l0_tx_drv_lvl = 0x25;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l1_tx_drv_lvl = 0x25;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l2_tx_drv_lvl = 0x25;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l3_tx_drv_lvl = 0x25;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x23;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0D;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0D;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0D;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x00;
  	} else {
  		cfg->tx_l0_tx_drv_lvl = 0x20;
  		cfg->tx_l0_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l1_tx_drv_lvl = 0x20;
  		cfg->tx_l1_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l2_tx_drv_lvl = 0x20;
  		cfg->tx_l2_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l3_tx_drv_lvl = 0x20;
  		cfg->tx_l3_tx_emp_post1_lvl = 0x20;
  		cfg->tx_l0_vmode_ctrl1 = 0x00;
  		cfg->tx_l0_vmode_ctrl2 = 0x0E;
  		cfg->tx_l1_vmode_ctrl1 = 0x00;
  		cfg->tx_l1_vmode_ctrl2 = 0x0E;
  		cfg->tx_l2_vmode_ctrl1 = 0x00;
  		cfg->tx_l2_vmode_ctrl2 = 0x0E;
  		cfg->tx_l3_vmode_ctrl1 = 0x00;
  		cfg->tx_l3_vmode_ctrl2 = 0x0E;
  	}
  
  	DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
  	DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
  	DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
  	DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
  	DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
  	DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
  						cfg->com_svs_mode_clk_sel);
  	DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
  	DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
  	DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
  						cfg->com_pll_cctrl_mode0);
  	DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
  						cfg->com_pll_rctrl_mode0);
  	DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
  						cfg->com_cp_ctrl_mode0);
  	DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
  						cfg->com_dec_start_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
  						cfg->com_div_frac_start1_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
  						cfg->com_div_frac_start2_mode0);
  	DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
  						cfg->com_div_frac_start3_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
  						cfg->com_integloop_gain0_mode0);
  	DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
  						cfg->com_integloop_gain1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
  						cfg->com_lock_cmp1_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
  						cfg->com_lock_cmp2_mode0);
  	DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
  						cfg->com_lock_cmp3_mode0);
  	DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
  	DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
  	DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
  
  	DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
  	DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
  	DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l0_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l1_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l2_tx_emp_post1_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
  	DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
  						cfg->tx_l3_tx_emp_post1_lvl);
  
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
  	DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
  	rc = 0;
  fail:
  	return rc;
  }
  
  static int hdmi_8996_calculate(u32 pix_clk,
  			       struct hdmi_8996_phy_pll_reg_cfg *cfg, u32 ver)
  {
  	switch (ver) {
  	case HDMI_VERSION_8996_V3:
  	case HDMI_VERSION_8996_V3_1_8:
  		return hdmi_8996_v3_calculate(pix_clk, cfg);
  	case HDMI_VERSION_8996_V2:
  		return hdmi_8996_v2_calculate(pix_clk, cfg);
  	default:
  		return hdmi_8996_v1_calculate(pix_clk, cfg);
  	}
  }
  
  static int hdmi_8996_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk, u32 ver)
  {
  	int rc = 0;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  	struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
  
  	rc = hdmi_8996_calculate(tmds_clk, &cfg, ver);
  	if (rc) {
  		DEV_ERR("%s: PLL calculation failed\n", __func__);
  		return rc;
  	}
  
  	/* Initially shut down PHY */
  	DEV_DBG("%s: Disabling PHY\n", __func__);
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0);
  	udelay(500);
  
  	/* Power up sequence */
  	switch (ver) {
  	case HDMI_VERSION_8996_V2:
  	case HDMI_VERSION_8996_V3:
  	case HDMI_VERSION_8996_V3_1_8:
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x04);
  		break;
  	};
  
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX0_TX1_LANE_CTL, 0x0F);
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX2_TX3_LANE_CTL, 0x0F);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  				     QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  				     QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  				     QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  				     QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		     QSERDES_TX_L0_LANE_MODE, cfg.tx_l0_lane_mode);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		     QSERDES_TX_L0_LANE_MODE, cfg.tx_l2_lane_mode);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		     QSERDES_TX_L0_TX_BAND, cfg.tx_l0_tx_band);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		     QSERDES_TX_L0_TX_BAND, cfg.tx_l1_tx_band);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		     QSERDES_TX_L0_TX_BAND, cfg.tx_l2_tx_band);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		     QSERDES_TX_L0_TX_BAND, cfg.tx_l3_tx_band);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  			       QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  			       QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  			       QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  			       QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0E);
  	if (ver == HDMI_VERSION_8996_V1)
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
  
  	/* Bypass VCO calibration */
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
  					cfg.com_svs_mode_clk_sel);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_TRIM, 0x0F);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IVCO, 0x0F);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_CTRL,
  			cfg.com_vco_tune_ctrl);
  
  	switch (ver) {
  	case HDMI_VERSION_8996_V1:
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
  					cfg.com_svs_mode_clk_sel);
  		break;
  	default:
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
  	}
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_SELECT, 0x30);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_HSCLK_SEL,
  		       cfg.com_hsclk_sel);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_EN,
  		       cfg.com_lock_cmp_en);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CCTRL_MODE0,
  		       cfg.com_pll_cctrl_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RCTRL_MODE0,
  		       cfg.com_pll_rctrl_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CP_CTRL_MODE0,
  		       cfg.com_cp_ctrl_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START_MODE0,
  		       cfg.com_dec_start_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1_MODE0,
  		       cfg.com_div_frac_start1_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2_MODE0,
  		       cfg.com_div_frac_start2_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3_MODE0,
  		       cfg.com_div_frac_start3_mode0);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
  			cfg.com_integloop_gain0_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
  			cfg.com_integloop_gain1_mode0);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0,
  			cfg.com_lock_cmp1_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0,
  			cfg.com_lock_cmp2_mode0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0,
  			cfg.com_lock_cmp3_mode0);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_EN,
  		       cfg.com_core_clk_en);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORECLK_DIV,
  		       cfg.com_coreclk_div);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
  
  	if (ver == HDMI_VERSION_8996_V3 || ver == HDMI_VERSION_8996_V3_1_8)
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESCODE_DIV_NUM, 0x15);
  
  	/* TX lanes setup (TX 0/1/2/3) */
  	if (ver == HDMI_VERSION_8996_V3_1_8) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				0x00000023);
  	} else {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				cfg.tx_l0_tx_drv_lvl);
  	}
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_EMP_POST1_LVL,
  		       cfg.tx_l0_tx_emp_post1_lvl);
  
  	if (ver == HDMI_VERSION_8996_V3_1_8) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				0x00000023);
  	} else {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				cfg.tx_l1_tx_drv_lvl);
  	}
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_EMP_POST1_LVL,
  		       cfg.tx_l1_tx_emp_post1_lvl);
  
  	if (ver == HDMI_VERSION_8996_V3_1_8) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				0x00000023);
  	} else {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				cfg.tx_l2_tx_drv_lvl);
  	}
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_EMP_POST1_LVL,
  		       cfg.tx_l2_tx_emp_post1_lvl);
  
  	if (ver == HDMI_VERSION_8996_V3_1_8) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				0x00000020);
  	} else {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  				QSERDES_TX_L0_TX_DRV_LVL,
  				cfg.tx_l3_tx_drv_lvl);
  	}
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_EMP_POST1_LVL,
  		       cfg.tx_l3_tx_emp_post1_lvl);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL1,
  		       cfg.tx_l0_vmode_ctrl1);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL2,
  		       cfg.tx_l0_vmode_ctrl2);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL1,
  		       cfg.tx_l1_vmode_ctrl1);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL2,
  		       cfg.tx_l1_vmode_ctrl2);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL1,
  		       cfg.tx_l2_vmode_ctrl1);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  			QSERDES_TX_L0_VMODE_CTRL2,
  			cfg.tx_l2_vmode_ctrl2);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_VMODE_CTRL1,
  		       cfg.tx_l3_vmode_ctrl1);
  	if (ver == HDMI_VERSION_8996_V3_1_8) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  				QSERDES_TX_L0_VMODE_CTRL2,
  				0x0000000D);
  	} else {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  				QSERDES_TX_L0_VMODE_CTRL2,
  				cfg.tx_l3_vmode_ctrl2);
  	}
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
  
  	if (ver < HDMI_VERSION_8996_V3) {
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  			       QSERDES_TX_L0_RES_CODE_LANE_TX,
  			       cfg.tx_l0_res_code_lane_tx);
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  			       QSERDES_TX_L0_RES_CODE_LANE_TX,
  			       cfg.tx_l1_res_code_lane_tx);
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  			       QSERDES_TX_L0_RES_CODE_LANE_TX,
  			       cfg.tx_l2_res_code_lane_tx);
  		MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  			       QSERDES_TX_L0_RES_CODE_LANE_TX,
  			       cfg.tx_l3_res_code_lane_tx);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESTRIM_CTRL,
  			       cfg.com_restrim_ctrl);
  
  		MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x00);
  		MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG1, 0x05);
  	}
  
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, cfg.phy_mode);
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  			QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  			QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  			QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  			QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  			QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  			QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  			QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  			QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_HP_PD_ENABLES, 0x03);
  
  	if (ver == HDMI_VERSION_8996_V2) {
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01);
  	}
  	/*
  	 * Ensure that vco configuration gets flushed to hardware before
  	 * enabling the PLL
  	 */
  	wmb();
  	return 0;
  }
  
  static int hdmi_8996_phy_ready_status(struct mdss_pll_resources *io)
  {
  	u32 status = 0;
  	int phy_ready = 0;
  	int rc;
  	u32 read_count = 0;
  
  	rc = mdss_pll_resource_enable(io, true);
  	if (rc) {
  		DEV_ERR("%s: pll resource can't be enabled\n", __func__);
  		return rc;
  	}
  
  	DEV_DBG("%s: Waiting for PHY Ready\n", __func__);
  
  	/* Poll for PHY read status */
  	while (read_count < HDMI_PLL_POLL_MAX_READS) {
  		status = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS);
  		if ((status & BIT(0)) == 1) {
  			phy_ready = 1;
  			DEV_DBG("%s: PHY READY\n", __func__);
  			break;
  		}
  		udelay(HDMI_PLL_POLL_TIMEOUT_US);
  		read_count++;
  	}
  
  	if (read_count == HDMI_PLL_POLL_MAX_READS) {
  		phy_ready = 0;
  		DEV_DBG("%s: PHY READY TIMEOUT\n", __func__);
  	}
  
  	mdss_pll_resource_enable(io, false);
  
  	return phy_ready;
  }
  
  static int hdmi_8996_pll_lock_status(struct mdss_pll_resources *io)
  {
  	u32 status;
  	int pll_locked = 0;
  	int rc;
  	u32 read_count = 0;
  
  	rc = mdss_pll_resource_enable(io, true);
  	if (rc) {
  		DEV_ERR("%s: pll resource can't be enabled\n", __func__);
  		return rc;
  	}
  
  	DEV_DBG("%s: Waiting for PLL lock\n", __func__);
  
  	while (read_count < HDMI_PLL_POLL_MAX_READS) {
  		status = MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_C_READY_STATUS);
  		if ((status & BIT(0)) == 1) {
  			pll_locked = 1;
  			DEV_DBG("%s: C READY\n", __func__);
  			break;
  		}
  		udelay(HDMI_PLL_POLL_TIMEOUT_US);
  		read_count++;
  	}
  
  	if (read_count == HDMI_PLL_POLL_MAX_READS) {
  		pll_locked = 0;
  		DEV_DBG("%s: C READY TIMEOUT\n", __func__);
  	}
  
  	mdss_pll_resource_enable(io, false);
  
  	return pll_locked;
  }
  
  static int hdmi_8996_v1_perform_sw_calibration(struct clk *c)
  {
  	int rc = 0;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  
  	u32 max_code = 0x190;
  	u32 min_code = 0x0;
  	u32 max_cnt = 0;
  	u32 min_cnt = 0;
  	u32 expected_counter_value = 0;
  	u32 step = 0;
  	u32 dbus_all = 0;
  	u32 dbus_sel = 0;
  	u32 vco_code = 0;
  	u32 val = 0;
  
  	vco_code = 0xC8;
  
  	DEV_DBG("%s: Starting SW calibration with vco_code = %d\n", __func__,
  		 vco_code);
  
  	expected_counter_value =
  	   (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0) << 16) |
  	   (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0) << 8) |
  	   (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0));
  
  	DEV_DBG("%s: expected_counter_value = %d\n", __func__,
  		 expected_counter_value);
  
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
  	val |= BIT(4);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
  
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
  	val |= BIT(3);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
  
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x4);
  
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
  	val |= BIT(1);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
  
  	udelay(60);
  
  	while (1) {
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
  			       vco_code & 0xFF);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
  			       (vco_code >> 8) & 0x3);
  
  		udelay(20);
  
  		val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
  		val &= ~BIT(1);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
  
  		udelay(60);
  
  		val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
  		val |= BIT(1);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
  
  		udelay(60);
  
  		dbus_all =
  		  (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS3) << 24) |
  		  (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS2) << 16) |
  		  (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS1) << 8) |
  		  (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS0));
  
  		dbus_sel = (dbus_all >> 9) & 0x3FFFF;
  		DEV_DBG("%s: loop[%d], dbus_all = 0x%x, dbus_sel = 0x%x\n",
  			__func__, step, dbus_all, dbus_sel);
  		if (dbus_sel == 0)
  			DEV_ERR("%s: CHECK HDMI REF CLK\n", __func__);
  
  		if (dbus_sel == expected_counter_value) {
  			max_code = vco_code;
  			max_cnt = dbus_sel;
  			min_code = vco_code;
  			min_cnt = dbus_sel;
  		} else if (dbus_sel == 0) {
  			max_code = vco_code;
  			max_cnt = dbus_sel;
  			vco_code = (max_code + min_code)/2;
  		} else if (dbus_sel > expected_counter_value) {
  			min_code = vco_code;
  			min_cnt = dbus_sel;
  			vco_code = (max_code + min_code)/2;
  		} else if (dbus_sel < expected_counter_value) {
  			max_code = vco_code;
  			max_cnt = dbus_sel;
  			vco_code = (max_code + min_code)/2;
  		}
  
  		step++;
  
  		if ((vco_code == 0) || (vco_code == 0x3FF) || (step > 0x3FF)) {
  			DEV_ERR("%s: VCO tune code search failed\n", __func__);
  			rc = -ENOTSUPP;
  			break;
  		}
  		if ((max_code - min_code) <= 1) {
  			if ((max_code - min_code) == 1) {
  				if (abs((int)(max_cnt - expected_counter_value))
  				    < abs((int)(min_cnt - expected_counter_value
  					))) {
  					vco_code = max_code;
  				} else {
  					vco_code = min_code;
  				}
  			}
  			break;
  		}
  		DEV_DBG("%s: loop[%d], new vco_code = %d\n", __func__, step,
  			 vco_code);
  	}
  
  	DEV_DBG("%s: CALIB done. vco_code = %d\n", __func__, vco_code);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
  		       vco_code & 0xFF);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
  		       (vco_code >> 8) & 0x3);
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
  	val &= ~BIT(1);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
  
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
  	val |= BIT(4);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
  
  	val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
  	val &= ~BIT(3);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
  
  	return rc;
  }
  
  static int hdmi_8996_v2_perform_sw_calibration(struct clk *c)
  {
  	int rc = 0;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  	u32 vco_code1, vco_code2, integral_loop, ready_poll;
  	u32 read_count = 0;
  
  	while (read_count < (HDMI_PLL_POLL_MAX_READS << 1)) {
  		ready_poll = MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_C_READY_STATUS);
  		if ((ready_poll & BIT(0)) == 1) {
  			ready_poll = 1;
  			DEV_DBG("%s: C READY\n", __func__);
  			break;
  		}
  		udelay(HDMI_PLL_POLL_TIMEOUT_US);
  		read_count++;
  	}
  
  	if (read_count == (HDMI_PLL_POLL_MAX_READS << 1)) {
  		ready_poll = 0;
  		DEV_DBG("%s: C READY TIMEOUT, TRYING SW CALIBRATION\n",
  								__func__);
  	}
  
  	vco_code1 = MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_PLLCAL_CODE1_STATUS);
  	vco_code2 = MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_PLLCAL_CODE2_STATUS);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x5);
  	integral_loop = MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_DEBUG_BUS0);
  
  	if (((ready_poll & 0x1) == 0) || (((ready_poll & 1) == 1) &&
  			(vco_code1 == 0xFF) && ((vco_code2 & 0x3) == 0x1) &&
  			(integral_loop > 0xC0))) {
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x04);
  		MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x00);
  		MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x17);
  		udelay(100);
  
  		MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x11);
  		udelay(100);
  
  		MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
  	}
  	return rc;
  }
  
  static int hdmi_8996_perform_sw_calibration(struct clk *c, u32 ver)
  {
  	switch (ver) {
  	case HDMI_VERSION_8996_V1:
  		return hdmi_8996_v1_perform_sw_calibration(c);
  	case HDMI_VERSION_8996_V2:
  		return hdmi_8996_v2_perform_sw_calibration(c);
  	}
  	return 0;
  }
  
  static int hdmi_8996_vco_enable(struct clk *c, u32 ver)
  {
  	int rc = 0;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x1);
  	udelay(100);
  
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
  	udelay(100);
  
  	rc = hdmi_8996_perform_sw_calibration(c, ver);
  	if (rc) {
  		DEV_ERR("%s: software calibration failed\n", __func__);
  		return rc;
  	}
  
  	rc = hdmi_8996_pll_lock_status(io);
  	if (!rc) {
  		DEV_ERR("%s: PLL not locked\n", __func__);
  		return rc;
  	}
  
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  		       QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
  		       0x6F);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
  		       QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
  		       0x6F);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
  		       QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
  		       0x6F);
  	MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
  		       QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
  		       0x6F);
  
  	/* Disable SSC */
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER1, 0x0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER2, 0x0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE1, 0x0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE2, 0x0);
  	MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x2);
  
  	rc = hdmi_8996_phy_ready_status(io);
  	if (!rc) {
  		DEV_ERR("%s: PHY not READY\n", __func__);
  		return rc;
  	}
  
  	/* Restart the retiming buffer */
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x18);
  	udelay(1);
  	MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
  
  	io->pll_on = true;
  	return 0;
  }
  
  static int hdmi_8996_v1_vco_enable(struct clk *c)
  {
  	return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V1);
  }
  
  static int hdmi_8996_v2_vco_enable(struct clk *c)
  {
  	return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V2);
  }
  
  static int hdmi_8996_v3_vco_enable(struct clk *c)
  {
  	return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3);
  }
  
  static int hdmi_8996_v3_1p8_vco_enable(struct clk *c)
  {
  	return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3_1_8);
  }
  
  static int hdmi_8996_vco_get_lock_range(struct clk *c, unsigned long pixel_clk)
  {
  	u32 rng = 64, cmp_cnt = 1024;
  	u32 coreclk_div = 5, clks_pll_divsel = 2;
  	u32 vco_freq, vco_ratio, ppm_range;
  	u64 bclk;
  	struct hdmi_8996_v3_post_divider pd;
  
  	bclk = ((u64)pixel_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
  
  	DEV_DBG("%s: rate=%ld\n", __func__, pixel_clk);
  
  	if (hdmi_8996_v3_get_post_div(&pd, bclk) ||
  		pd.vco_ratio <= 0 || pd.vco_freq <= 0) {
  		DEV_ERR("%s: couldn't get post div\n", __func__);
  		return -EINVAL;
  	}
  
  	do_div(pd.vco_freq, HDMI_KHZ_TO_HZ * HDMI_KHZ_TO_HZ);
  
  	vco_freq  = (u32) pd.vco_freq;
  	vco_ratio = (u32) pd.vco_ratio;
  
  	DEV_DBG("%s: freq %d, ratio %d\n", __func__,
  		vco_freq, vco_ratio);
  
  	ppm_range = (rng * HDMI_REF_CLOCK) / cmp_cnt;
  	ppm_range /= vco_freq / vco_ratio;
  	ppm_range *= coreclk_div * clks_pll_divsel;
  
  	DEV_DBG("%s: ppm range: %d\n", __func__, ppm_range);
  
  	return ppm_range;
  }
  
  static int hdmi_8996_vco_rate_atomic_update(struct clk *c,
  	unsigned long rate, u32 ver)
  {
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  	void __iomem *pll;
  	struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
  	int rc = 0;
  
  	rc = hdmi_8996_calculate(rate, &cfg, ver);
  	if (rc) {
  		DEV_ERR("%s: PLL calculation failed\n", __func__);
  		goto end;
  	}
  
  	pll = io->pll_base;
  
  	MDSS_PLL_REG_W(pll, QSERDES_COM_DEC_START_MODE0,
  		       cfg.com_dec_start_mode0);
  	MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START1_MODE0,
  		       cfg.com_div_frac_start1_mode0);
  	MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START2_MODE0,
  		       cfg.com_div_frac_start2_mode0);
  	MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START3_MODE0,
  		       cfg.com_div_frac_start3_mode0);
  
  	MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x01);
  	MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x00);
  
  	DEV_DBG("%s: updated to rate %ld\n", __func__, rate);
  end:
  	return rc;
  }
  
  static int hdmi_8996_vco_set_rate(struct clk *c, unsigned long rate, u32 ver)
  {
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  	unsigned int set_power_dwn = 0;
  	bool atomic_update = false;
  	int rc, pll_lock_range;
  
  	rc = mdss_pll_resource_enable(io, true);
  	if (rc) {
  		DEV_ERR("pll resource can't be enabled\n");
  		return rc;
  	}
  
  	DEV_DBG("%s: rate %ld\n", __func__, rate);
  
  	if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0) &&
  		MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
  		pll_lock_range = hdmi_8996_vco_get_lock_range(c, vco->rate);
  
  		if (pll_lock_range > 0 && vco->rate) {
  			u32 range_limit;
  
  			range_limit  = vco->rate *
  				(pll_lock_range / HDMI_KHZ_TO_HZ);
  			range_limit /= HDMI_KHZ_TO_HZ;
  
  			DEV_DBG("%s: range limit %d\n", __func__, range_limit);
  
  			if (abs(rate - vco->rate) < range_limit)
  				atomic_update = true;
  		}
  	}
  
  	if (io->pll_on && !atomic_update)
  		set_power_dwn = 1;
  
  	if (atomic_update) {
  		hdmi_8996_vco_rate_atomic_update(c, rate, ver);
  	} else {
  		rc = hdmi_8996_phy_pll_set_clk_rate(c, rate, ver);
  		if (rc)
  			DEV_ERR("%s: Failed to set clk rate\n", __func__);
  	}
  
  	mdss_pll_resource_enable(io, false);
  
  	if (set_power_dwn)
  		hdmi_8996_vco_enable(c, ver);
  
  	vco->rate = rate;
  	vco->rate_set = true;
  
  	return 0;
  }
  
  static int hdmi_8996_v1_vco_set_rate(struct clk *c, unsigned long rate)
  {
  	return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V1);
  }
  
  static int hdmi_8996_v2_vco_set_rate(struct clk *c, unsigned long rate)
  {
  	return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V2);
  }
  
  static int hdmi_8996_v3_vco_set_rate(struct clk *c, unsigned long rate)
  {
  	return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3);
  }
  
  static int hdmi_8996_v3_1p8_vco_set_rate(struct clk *c, unsigned long rate)
  {
  	return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3_1_8);
  }
  
  static unsigned long hdmi_get_hsclk_sel_divisor(unsigned long hsclk_sel)
  {
  	unsigned long divisor;
  
  	switch (hsclk_sel) {
  	case 0:
  		divisor = 2;
  		break;
  	case 1:
  		divisor = 6;
  		break;
  	case 2:
  		divisor = 10;
  		break;
  	case 3:
  		divisor = 14;
  		break;
  	case 4:
  		divisor = 3;
  		break;
  	case 5:
  		divisor = 9;
  		break;
  	case 6:
  	case 13:
  		divisor = 15;
  		break;
  	case 7:
  		divisor = 21;
  		break;
  	case 8:
  		divisor = 4;
  		break;
  	case 9:
  		divisor = 12;
  		break;
  	case 10:
  		divisor = 20;
  		break;
  	case 11:
  		divisor = 28;
  		break;
  	case 12:
  		divisor = 5;
  		break;
  	case 14:
  		divisor = 25;
  		break;
  	case 15:
  		divisor = 35;
  		break;
  	default:
  		divisor = 1;
  		DEV_ERR("%s: invalid hsclk_sel value = %lu",
  				__func__, hsclk_sel);
  		break;
  	}
  
  	return divisor;
  }
  
  static unsigned long hdmi_8996_vco_get_rate(struct clk *c)
  {
  	unsigned long freq = 0, hsclk_sel = 0, tx_band = 0, dec_start = 0,
  		      div_frac_start = 0, vco_clock_freq = 0;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  
  	if (mdss_pll_resource_enable(io, true)) {
  		DEV_ERR("%s: pll resource can't be enabled\n", __func__);
  		return freq;
  	}
  
  	dec_start = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEC_START_MODE0);
  
  	div_frac_start =
  		MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_DIV_FRAC_START1_MODE0) |
  		MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_DIV_FRAC_START2_MODE0) << 8 |
  		MDSS_PLL_REG_R(io->pll_base,
  				QSERDES_COM_DIV_FRAC_START3_MODE0) << 16;
  
  	vco_clock_freq = (dec_start + (div_frac_start / (1 << 20)))
  		* 4 * (HDMI_REF_CLOCK);
  
  	hsclk_sel = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_HSCLK_SEL) & 0x15;
  	hsclk_sel = hdmi_get_hsclk_sel_divisor(hsclk_sel);
  	tx_band = MDSS_PLL_REG_R(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
  			QSERDES_TX_L0_TX_BAND) & 0x3;
  
  	freq = vco_clock_freq / (10 * hsclk_sel * (1 << tx_band));
  
  	mdss_pll_resource_enable(io, false);
  
  	DEV_DBG("%s: freq = %lu\n", __func__, freq);
  
  	return freq;
  }
  
  static long hdmi_8996_vco_round_rate(struct clk *c, unsigned long rate)
  {
  	unsigned long rrate = rate;
  
  	DEV_DBG("rrate=%ld\n", rrate);
  
  	return rrate;
  }
  
  static int hdmi_8996_vco_prepare(struct clk *c, u32 ver)
  {
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  	int ret = 0;
  
  	DEV_DBG("rate=%ld\n", vco->rate);
  
  	if (!vco->rate_set && vco->rate)
  		ret = hdmi_8996_vco_set_rate(c, vco->rate, ver);
  
  	if (!ret) {
  		ret = mdss_pll_resource_enable(io, true);
  		if (ret)
  			DEV_ERR("pll resource can't be enabled\n");
  	}
  
  	return ret;
  }
  
  static int hdmi_8996_v1_vco_prepare(struct clk *c)
  {
  	return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V1);
  }
  
  static int hdmi_8996_v2_vco_prepare(struct clk *c)
  {
  	return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V2);
  }
  
  static int hdmi_8996_v3_vco_prepare(struct clk *c)
  {
  	return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3);
  }
  
  static int hdmi_8996_v3_1p8_vco_prepare(struct clk *c)
  {
  	return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3_1_8);
  }
  
  static void hdmi_8996_vco_unprepare(struct clk *c)
  {
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  
  	vco->rate_set = false;
  
  	if (!io) {
  		DEV_ERR("Invalid input parameter\n");
  		return;
  	}
  
  	if (!io->pll_on &&
  		mdss_pll_resource_enable(io, true)) {
  		DEV_ERR("pll resource can't be enabled\n");
  		return;
  	}
  
  	io->handoff_resources = false;
  	mdss_pll_resource_enable(io, false);
  	io->pll_on = false;
  }
  
  static enum handoff hdmi_8996_vco_handoff(struct clk *c)
  {
  	enum handoff ret = HANDOFF_DISABLED_CLK;
  	struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
  	struct mdss_pll_resources *io = vco->priv;
  
  	if (is_gdsc_disabled(io))
  		return HANDOFF_DISABLED_CLK;
  
  	if (mdss_pll_resource_enable(io, true)) {
  		DEV_ERR("pll resource can't be enabled\n");
  		return ret;
  	}
  
  	io->handoff_resources = true;
  
  	if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0)) {
  		if (MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
  			io->pll_on = true;
  			c->rate = hdmi_8996_vco_get_rate(c);
  			vco->rate = c->rate;
  			ret = HANDOFF_ENABLED_CLK;
  		} else {
  			io->handoff_resources = false;
  			mdss_pll_resource_enable(io, false);
  			DEV_DBG("%s: PHY not ready\n", __func__);
  		}
  	} else {
  		io->handoff_resources = false;
  		mdss_pll_resource_enable(io, false);
  		DEV_DBG("%s: PLL not locked\n", __func__);
  	}
  
  	DEV_DBG("done, ret=%d\n", ret);
  	return ret;
  }
  
  static const struct clk_ops hdmi_8996_v1_vco_clk_ops = {
  	.enable = hdmi_8996_v1_vco_enable,
  	.set_rate = hdmi_8996_v1_vco_set_rate,
  	.get_rate = hdmi_8996_vco_get_rate,
  	.round_rate = hdmi_8996_vco_round_rate,
  	.prepare = hdmi_8996_v1_vco_prepare,
  	.unprepare = hdmi_8996_vco_unprepare,
  	.handoff = hdmi_8996_vco_handoff,
  };
  
  static const struct clk_ops hdmi_8996_v2_vco_clk_ops = {
  	.enable = hdmi_8996_v2_vco_enable,
  	.set_rate = hdmi_8996_v2_vco_set_rate,
  	.get_rate = hdmi_8996_vco_get_rate,
  	.round_rate = hdmi_8996_vco_round_rate,
  	.prepare = hdmi_8996_v2_vco_prepare,
  	.unprepare = hdmi_8996_vco_unprepare,
  	.handoff = hdmi_8996_vco_handoff,
  };
  
  static const struct clk_ops hdmi_8996_v3_vco_clk_ops = {
  	.enable = hdmi_8996_v3_vco_enable,
  	.set_rate = hdmi_8996_v3_vco_set_rate,
  	.get_rate = hdmi_8996_vco_get_rate,
  	.round_rate = hdmi_8996_vco_round_rate,
  	.prepare = hdmi_8996_v3_vco_prepare,
  	.unprepare = hdmi_8996_vco_unprepare,
  	.handoff = hdmi_8996_vco_handoff,
  };
  
  static const struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = {
  	.enable = hdmi_8996_v3_1p8_vco_enable,
  	.set_rate = hdmi_8996_v3_1p8_vco_set_rate,
  	.get_rate = hdmi_8996_vco_get_rate,
  	.round_rate = hdmi_8996_vco_round_rate,
  	.prepare = hdmi_8996_v3_1p8_vco_prepare,
  	.unprepare = hdmi_8996_vco_unprepare,
  	.handoff = hdmi_8996_vco_handoff,
  };
  
  
  static struct hdmi_pll_vco_clk hdmi_vco_clk = {
  	.c = {
  		.dbg_name = "hdmi_8996_vco_clk",
  		.ops = &hdmi_8996_v1_vco_clk_ops,
  		.flags = CLKFLAG_NO_RATE_CACHE,
  		CLK_INIT(hdmi_vco_clk.c),
  	},
  };
  
  static struct clk_lookup hdmipllcc_8996[] = {
  	CLK_LIST(hdmi_vco_clk),
  };
  
  int hdmi_8996_pll_clock_register(struct platform_device *pdev,
  				 struct mdss_pll_resources *pll_res, u32 ver)
  {
  	int rc = -ENOTSUPP;
  
  	if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) {
  		DEV_ERR("%s: Invalid input parameters\n", __func__);
  		return -EPROBE_DEFER;
  	}
  
  	/* Set client data for vco, mux and div clocks */
  	hdmi_vco_clk.priv = pll_res;
  
  	switch (ver) {
  	case HDMI_VERSION_8996_V2:
  		hdmi_vco_clk.c.ops = &hdmi_8996_v2_vco_clk_ops;
  		break;
  	case HDMI_VERSION_8996_V3:
  		hdmi_vco_clk.c.ops = &hdmi_8996_v3_vco_clk_ops;
  		break;
  	case HDMI_VERSION_8996_V3_1_8:
  		hdmi_vco_clk.c.ops = &hdmi_8996_v3_1p8_vco_clk_ops;
  		break;
  	default:
  		hdmi_vco_clk.c.ops = &hdmi_8996_v1_vco_clk_ops;
  		break;
  	};
  
  	rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8996,
  					ARRAY_SIZE(hdmipllcc_8996));
  	if (rc) {
  		DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc);
  		rc = -EPROBE_DEFER;
  	} else {
  		DEV_DBG("%s SUCCESS\n", __func__);
  	}
  
  	return rc;
  }
  
  int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
  				 struct mdss_pll_resources *pll_res)
  {
  	return hdmi_8996_pll_clock_register(pdev, pll_res,
  						HDMI_VERSION_8996_V1);
  }
  
  int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
  				 struct mdss_pll_resources *pll_res)
  {
  	return hdmi_8996_pll_clock_register(pdev, pll_res,
  						HDMI_VERSION_8996_V2);
  }
  
  int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
  				 struct mdss_pll_resources *pll_res)
  {
  	return hdmi_8996_pll_clock_register(pdev, pll_res,
  						HDMI_VERSION_8996_V3);
  }
  
  int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
  				 struct mdss_pll_resources *pll_res)
  {
  	return hdmi_8996_pll_clock_register(pdev, pll_res,
  						HDMI_VERSION_8996_V3_1_8);
  }
  