You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2685 lines
86 KiB
2685 lines
86 KiB
/* Copyright (c) 2014-2017, 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 0xFFFFFFFFFFFFFFFF
|
|
#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 12000000000
|
|
#define HDMI_VCO_MIN_FREQ 8000000000
|
|
#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,
|
|
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);
|
|
}
|
|
|