771 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2016, The Linux Foundation. All rights reserved.4 * Copyright (c) 2024 Freebox SAS5 */6 7#include <linux/clk-provider.h>8#include <linux/delay.h>9 10#include "hdmi.h"11 12#define HDMI_VCO_MAX_FREQ 12000000000UL13#define HDMI_VCO_MIN_FREQ 8000000000UL14 15#define HDMI_PCLK_MAX_FREQ 60000000016#define HDMI_PCLK_MIN_FREQ 2500000017 18#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL19#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL20#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL21#define HDMI_CORECLK_DIV 522#define HDMI_DEFAULT_REF_CLOCK 1920000023#define HDMI_PLL_CMP_CNT 102424 25#define HDMI_PLL_POLL_MAX_READS 10026#define HDMI_PLL_POLL_TIMEOUT_US 15027 28#define HDMI_NUM_TX_CHANNEL 429 30struct hdmi_pll_8998 {31 struct platform_device *pdev;32 struct clk_hw clk_hw;33 unsigned long rate;34 35 /* pll mmio base */36 void __iomem *mmio_qserdes_com;37 /* tx channel base */38 void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL];39};40 41#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8998, clk_hw)42 43struct hdmi_8998_phy_pll_reg_cfg {44 u32 com_svs_mode_clk_sel;45 u32 com_hsclk_sel;46 u32 com_pll_cctrl_mode0;47 u32 com_pll_rctrl_mode0;48 u32 com_cp_ctrl_mode0;49 u32 com_dec_start_mode0;50 u32 com_div_frac_start1_mode0;51 u32 com_div_frac_start2_mode0;52 u32 com_div_frac_start3_mode0;53 u32 com_integloop_gain0_mode0;54 u32 com_integloop_gain1_mode0;55 u32 com_lock_cmp_en;56 u32 com_lock_cmp1_mode0;57 u32 com_lock_cmp2_mode0;58 u32 com_lock_cmp3_mode0;59 u32 com_core_clk_en;60 u32 com_coreclk_div_mode0;61 62 u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL];63 u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL];64 u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL];65 u32 tx_lx_pre_driver_1[HDMI_NUM_TX_CHANNEL];66 u32 tx_lx_pre_driver_2[HDMI_NUM_TX_CHANNEL];67 u32 tx_lx_res_code_offset[HDMI_NUM_TX_CHANNEL];68 69 u32 phy_mode;70};71 72struct hdmi_8998_post_divider {73 u64 vco_freq;74 int hsclk_divsel;75 int vco_ratio;76 int tx_band_sel;77 int half_rate_mode;78};79 80static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8998 *pll)81{82 return platform_get_drvdata(pll->pdev);83}84 85static inline void hdmi_pll_write(struct hdmi_pll_8998 *pll, int offset,86 u32 data)87{88 writel(data, pll->mmio_qserdes_com + offset);89}90 91static inline u32 hdmi_pll_read(struct hdmi_pll_8998 *pll, int offset)92{93 return readl(pll->mmio_qserdes_com + offset);94}95 96static inline void hdmi_tx_chan_write(struct hdmi_pll_8998 *pll, int channel,97 int offset, int data)98{99 writel(data, pll->mmio_qserdes_tx[channel] + offset);100}101 102static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk,103 bool gen_ssc)104{105 if ((frac_start != 0) || gen_ssc)106 return 0x8;107 108 return 0x30;109}110 111static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc)112{113 if ((frac_start != 0) || gen_ssc)114 return 0x16;115 116 return 0x18;117}118 119static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc)120{121 if ((frac_start != 0) || gen_ssc)122 return 0x34;123 124 return 0x2;125}126 127static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref_clk,128 bool gen_ssc)129{130 int digclk_divsel = bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;131 u64 base;132 133 if ((frac_start != 0) || gen_ssc)134 base = 0x3F;135 else136 base = 0xC4;137 138 base <<= (digclk_divsel == 2 ? 1 : 0);139 140 return (base <= 2046 ? base : 2046);141}142 143static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)144{145 u64 dividend = HDMI_PLL_CMP_CNT * fdata;146 u32 divisor = ref_clk * 10;147 u32 rem;148 149 rem = do_div(dividend, divisor);150 if (rem > (divisor >> 1))151 dividend++;152 153 return dividend - 1;154}155 156#define HDMI_REF_CLOCK_HZ ((u64)19200000)157#define HDMI_MHZ_TO_HZ ((u64)1000000)158static int pll_get_post_div(struct hdmi_8998_post_divider *pd, u64 bclk)159{160 u32 const ratio_list[] = {1, 2, 3, 4, 5, 6,161 9, 10, 12, 15, 25};162 u32 const band_list[] = {0, 1, 2, 3};163 u32 const sz_ratio = ARRAY_SIZE(ratio_list);164 u32 const sz_band = ARRAY_SIZE(band_list);165 u32 const cmp_cnt = 1024;166 u32 const th_min = 500, th_max = 1000;167 u32 half_rate_mode = 0;168 u32 list_elements;169 int optimal_index;170 u32 i, j, k;171 u32 found_hsclk_divsel = 0, found_vco_ratio;172 u32 found_tx_band_sel;173 u64 const min_freq = HDMI_VCO_MIN_FREQ, max_freq = HDMI_VCO_MAX_FREQ;174 u64 freq_list[ARRAY_SIZE(ratio_list) * ARRAY_SIZE(band_list)];175 u64 found_vco_freq;176 u64 freq_optimal;177 178find_optimal_index:179 freq_optimal = max_freq;180 optimal_index = -1;181 list_elements = 0;182 183 for (i = 0; i < sz_ratio; i++) {184 for (j = 0; j < sz_band; j++) {185 u64 freq = div_u64(bclk, (1 << half_rate_mode));186 187 freq *= (ratio_list[i] * (1 << band_list[j]));188 freq_list[list_elements++] = freq;189 }190 }191 192 for (k = 0; k < ARRAY_SIZE(freq_list); k++) {193 u32 const clks_pll_div = 2, core_clk_div = 5;194 u32 const rng1 = 16, rng2 = 8;195 u32 th1, th2;196 u64 core_clk, rvar1, rem;197 198 core_clk = div_u64(freq_list[k],199 ratio_list[k / sz_band] * clks_pll_div *200 core_clk_div);201 202 rvar1 = HDMI_REF_CLOCK_HZ * rng1 * HDMI_MHZ_TO_HZ;203 rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);204 if (rem > ((cmp_cnt * core_clk) >> 1))205 rvar1++;206 th1 = rvar1;207 208 rvar1 = HDMI_REF_CLOCK_HZ * rng2 * HDMI_MHZ_TO_HZ;209 rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);210 if (rem > ((cmp_cnt * core_clk) >> 1))211 rvar1++;212 th2 = rvar1;213 214 if (freq_list[k] >= min_freq &&215 freq_list[k] <= max_freq) {216 if ((th1 >= th_min && th1 <= th_max) ||217 (th2 >= th_min && th2 <= th_max)) {218 if (freq_list[k] <= freq_optimal) {219 freq_optimal = freq_list[k];220 optimal_index = k;221 }222 }223 }224 }225 226 if (optimal_index == -1) {227 if (!half_rate_mode) {228 half_rate_mode = 1;229 goto find_optimal_index;230 } else {231 return -EINVAL;232 }233 } else {234 found_vco_ratio = ratio_list[optimal_index / sz_band];235 found_tx_band_sel = band_list[optimal_index % sz_band];236 found_vco_freq = freq_optimal;237 }238 239 switch (found_vco_ratio) {240 case 1:241 found_hsclk_divsel = 15;242 break;243 case 2:244 found_hsclk_divsel = 0;245 break;246 case 3:247 found_hsclk_divsel = 4;248 break;249 case 4:250 found_hsclk_divsel = 8;251 break;252 case 5:253 found_hsclk_divsel = 12;254 break;255 case 6:256 found_hsclk_divsel = 1;257 break;258 case 9:259 found_hsclk_divsel = 5;260 break;261 case 10:262 found_hsclk_divsel = 2;263 break;264 case 12:265 found_hsclk_divsel = 9;266 break;267 case 15:268 found_hsclk_divsel = 13;269 break;270 case 25:271 found_hsclk_divsel = 14;272 break;273 };274 275 pd->vco_freq = found_vco_freq;276 pd->tx_band_sel = found_tx_band_sel;277 pd->vco_ratio = found_vco_ratio;278 pd->hsclk_divsel = found_hsclk_divsel;279 280 return 0;281}282 283static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk,284 struct hdmi_8998_phy_pll_reg_cfg *cfg)285{286 struct hdmi_8998_post_divider pd;287 u64 bclk;288 u64 dec_start;289 u64 frac_start;290 u64 fdata;291 u32 pll_divisor;292 u32 rem;293 u32 cpctrl;294 u32 rctrl;295 u32 cctrl;296 u32 integloop_gain;297 u32 pll_cmp;298 int i, ret;299 300 /* bit clk = 10 * pix_clk */301 bclk = ((u64)pix_clk) * 10;302 303 ret = pll_get_post_div(&pd, bclk);304 if (ret)305 return ret;306 307 dec_start = pd.vco_freq;308 pll_divisor = 4 * ref_clk;309 do_div(dec_start, pll_divisor);310 311 frac_start = pd.vco_freq * (1 << 20);312 313 rem = do_div(frac_start, pll_divisor);314 frac_start -= dec_start * (1 << 20);315 if (rem > (pll_divisor >> 1))316 frac_start++;317 318 cpctrl = pll_get_cpctrl(frac_start, ref_clk, false);319 rctrl = pll_get_rctrl(frac_start, false);320 cctrl = pll_get_cctrl(frac_start, false);321 integloop_gain = pll_get_integloop_gain(frac_start, bclk,322 ref_clk, false);323 324 fdata = pd.vco_freq;325 do_div(fdata, pd.vco_ratio);326 327 pll_cmp = pll_get_pll_cmp(fdata, ref_clk);328 329 /* Convert these values to register specific values */330 if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)331 cfg->com_svs_mode_clk_sel = 1;332 else333 cfg->com_svs_mode_clk_sel = 2;334 335 cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel);336 cfg->com_pll_cctrl_mode0 = cctrl;337 cfg->com_pll_rctrl_mode0 = rctrl;338 cfg->com_cp_ctrl_mode0 = cpctrl;339 cfg->com_dec_start_mode0 = dec_start;340 cfg->com_div_frac_start1_mode0 = (frac_start & 0xff);341 cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8);342 cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16);343 cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff);344 cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8);345 cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff);346 cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8);347 cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);348 cfg->com_lock_cmp_en = 0x0;349 cfg->com_core_clk_en = 0x2c;350 cfg->com_coreclk_div_mode0 = HDMI_CORECLK_DIV;351 cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x5 : 0x4;352 353 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++)354 cfg->tx_lx_tx_band[i] = pd.tx_band_sel;355 356 if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {357 cfg->tx_lx_tx_drv_lvl[0] = 0x0f;358 cfg->tx_lx_tx_drv_lvl[1] = 0x0f;359 cfg->tx_lx_tx_drv_lvl[2] = 0x0f;360 cfg->tx_lx_tx_drv_lvl[3] = 0x0f;361 cfg->tx_lx_tx_emp_post1_lvl[0] = 0x03;362 cfg->tx_lx_tx_emp_post1_lvl[1] = 0x02;363 cfg->tx_lx_tx_emp_post1_lvl[2] = 0x03;364 cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;365 cfg->tx_lx_pre_driver_1[0] = 0x00;366 cfg->tx_lx_pre_driver_1[1] = 0x00;367 cfg->tx_lx_pre_driver_1[2] = 0x00;368 cfg->tx_lx_pre_driver_1[3] = 0x00;369 cfg->tx_lx_pre_driver_2[0] = 0x1C;370 cfg->tx_lx_pre_driver_2[1] = 0x1C;371 cfg->tx_lx_pre_driver_2[2] = 0x1C;372 cfg->tx_lx_pre_driver_2[3] = 0x00;373 cfg->tx_lx_res_code_offset[0] = 0x03;374 cfg->tx_lx_res_code_offset[1] = 0x00;375 cfg->tx_lx_res_code_offset[2] = 0x00;376 cfg->tx_lx_res_code_offset[3] = 0x03;377 } else if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) {378 cfg->tx_lx_tx_drv_lvl[0] = 0x0f;379 cfg->tx_lx_tx_drv_lvl[1] = 0x0f;380 cfg->tx_lx_tx_drv_lvl[2] = 0x0f;381 cfg->tx_lx_tx_drv_lvl[3] = 0x0f;382 cfg->tx_lx_tx_emp_post1_lvl[0] = 0x03;383 cfg->tx_lx_tx_emp_post1_lvl[1] = 0x03;384 cfg->tx_lx_tx_emp_post1_lvl[2] = 0x03;385 cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;386 cfg->tx_lx_pre_driver_1[0] = 0x00;387 cfg->tx_lx_pre_driver_1[1] = 0x00;388 cfg->tx_lx_pre_driver_1[2] = 0x00;389 cfg->tx_lx_pre_driver_1[3] = 0x00;390 cfg->tx_lx_pre_driver_2[0] = 0x16;391 cfg->tx_lx_pre_driver_2[1] = 0x16;392 cfg->tx_lx_pre_driver_2[2] = 0x16;393 cfg->tx_lx_pre_driver_2[3] = 0x18;394 cfg->tx_lx_res_code_offset[0] = 0x03;395 cfg->tx_lx_res_code_offset[1] = 0x00;396 cfg->tx_lx_res_code_offset[2] = 0x00;397 cfg->tx_lx_res_code_offset[3] = 0x00;398 } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {399 cfg->tx_lx_tx_drv_lvl[0] = 0x0f;400 cfg->tx_lx_tx_drv_lvl[1] = 0x0f;401 cfg->tx_lx_tx_drv_lvl[2] = 0x0f;402 cfg->tx_lx_tx_drv_lvl[3] = 0x0f;403 cfg->tx_lx_tx_emp_post1_lvl[0] = 0x05;404 cfg->tx_lx_tx_emp_post1_lvl[1] = 0x05;405 cfg->tx_lx_tx_emp_post1_lvl[2] = 0x05;406 cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;407 cfg->tx_lx_pre_driver_1[0] = 0x00;408 cfg->tx_lx_pre_driver_1[1] = 0x00;409 cfg->tx_lx_pre_driver_1[2] = 0x00;410 cfg->tx_lx_pre_driver_1[3] = 0x00;411 cfg->tx_lx_pre_driver_2[0] = 0x0E;412 cfg->tx_lx_pre_driver_2[1] = 0x0E;413 cfg->tx_lx_pre_driver_2[2] = 0x0E;414 cfg->tx_lx_pre_driver_2[3] = 0x0E;415 cfg->tx_lx_res_code_offset[0] = 0x00;416 cfg->tx_lx_res_code_offset[1] = 0x00;417 cfg->tx_lx_res_code_offset[2] = 0x00;418 cfg->tx_lx_res_code_offset[3] = 0x00;419 } else {420 cfg->tx_lx_tx_drv_lvl[0] = 0x01;421 cfg->tx_lx_tx_drv_lvl[1] = 0x01;422 cfg->tx_lx_tx_drv_lvl[2] = 0x01;423 cfg->tx_lx_tx_drv_lvl[3] = 0x00;424 cfg->tx_lx_tx_emp_post1_lvl[0] = 0x00;425 cfg->tx_lx_tx_emp_post1_lvl[1] = 0x00;426 cfg->tx_lx_tx_emp_post1_lvl[2] = 0x00;427 cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;428 cfg->tx_lx_pre_driver_1[0] = 0x00;429 cfg->tx_lx_pre_driver_1[1] = 0x00;430 cfg->tx_lx_pre_driver_1[2] = 0x00;431 cfg->tx_lx_pre_driver_1[3] = 0x00;432 cfg->tx_lx_pre_driver_2[0] = 0x16;433 cfg->tx_lx_pre_driver_2[1] = 0x16;434 cfg->tx_lx_pre_driver_2[2] = 0x16;435 cfg->tx_lx_pre_driver_2[3] = 0x18;436 cfg->tx_lx_res_code_offset[0] = 0x00;437 cfg->tx_lx_res_code_offset[1] = 0x00;438 cfg->tx_lx_res_code_offset[2] = 0x00;439 cfg->tx_lx_res_code_offset[3] = 0x00;440 }441 442 return 0;443}444 445static int hdmi_8998_pll_set_clk_rate(struct clk_hw *hw, unsigned long rate,446 unsigned long parent_rate)447{448 struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);449 struct hdmi_phy *phy = pll_get_phy(pll);450 struct hdmi_8998_phy_pll_reg_cfg cfg = {};451 int i, ret;452 453 ret = pll_calculate(rate, parent_rate, &cfg);454 if (ret) {455 DRM_ERROR("PLL calculation failed\n");456 return ret;457 }458 459 /* Initially shut down PHY */460 hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0x0);461 udelay(500);462 463 /* Power up sequence */464 hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0x1);465 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20);466 hdmi_phy_write(phy, REG_HDMI_8998_PHY_CMN_CTRL, 0x6);467 468 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {469 hdmi_tx_chan_write(pll, i,470 REG_HDMI_8998_PHY_TXn_INTERFACE_SELECT_TX_BAND,471 cfg.tx_lx_tx_band[i]);472 hdmi_tx_chan_write(pll, i,473 REG_HDMI_8998_PHY_TXn_CLKBUF_TERM_ENABLE,474 0x1);475 hdmi_tx_chan_write(pll, i,476 REG_HDMI_8998_PHY_TXn_LANE_MODE,477 0x20);478 }479 480 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x02);481 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x0B);482 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37);483 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02);484 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E);485 486 /* Bypass VCO calibration */487 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SVS_MODE_CLK_SEL,488 cfg.com_svs_mode_clk_sel);489 490 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_IVCO, 0x07);491 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_VCO_TUNE_CTRL, 0x00);492 493 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CLK_SEL, 0x30);494 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_HSCLK_SEL,495 cfg.com_hsclk_sel);496 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP_EN,497 cfg.com_lock_cmp_en);498 499 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_CCTRL_MODE0,500 cfg.com_pll_cctrl_mode0);501 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_RCTRL_MODE0,502 cfg.com_pll_rctrl_mode0);503 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CP_CTRL_MODE0,504 cfg.com_cp_ctrl_mode0);505 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DEC_START_MODE0,506 cfg.com_dec_start_mode0);507 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0,508 cfg.com_div_frac_start1_mode0);509 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0,510 cfg.com_div_frac_start2_mode0);511 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0,512 cfg.com_div_frac_start3_mode0);513 514 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0,515 cfg.com_integloop_gain0_mode0);516 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0,517 cfg.com_integloop_gain1_mode0);518 519 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP1_MODE0,520 cfg.com_lock_cmp1_mode0);521 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP2_MODE0,522 cfg.com_lock_cmp2_mode0);523 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP3_MODE0,524 cfg.com_lock_cmp3_mode0);525 526 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00);527 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CORE_CLK_EN,528 cfg.com_core_clk_en);529 hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CORECLK_DIV_MODE0,530 cfg.com_coreclk_div_mode0);531 532 /* TX lanes setup (TX 0/1/2/3) */533 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {534 hdmi_tx_chan_write(pll, i,535 REG_HDMI_8998_PHY_TXn_DRV_LVL,536 cfg.tx_lx_tx_drv_lvl[i]);537 hdmi_tx_chan_write(pll, i,538 REG_HDMI_8998_PHY_TXn_EMP_POST1_LVL,539 cfg.tx_lx_tx_emp_post1_lvl[i]);540 hdmi_tx_chan_write(pll, i,541 REG_HDMI_8998_PHY_TXn_PRE_DRIVER_1,542 cfg.tx_lx_pre_driver_1[i]);543 hdmi_tx_chan_write(pll, i,544 REG_HDMI_8998_PHY_TXn_PRE_DRIVER_2,545 cfg.tx_lx_pre_driver_2[i]);546 hdmi_tx_chan_write(pll, i,547 REG_HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET,548 cfg.tx_lx_res_code_offset[i]);549 }550 551 hdmi_phy_write(phy, REG_HDMI_8998_PHY_MODE, cfg.phy_mode);552 553 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {554 hdmi_tx_chan_write(pll, i,555 REG_HDMI_8998_PHY_TXn_LANE_CONFIG,556 0x10);557 }558 559 /*560 * Ensure that vco configuration gets flushed to hardware before561 * enabling the PLL562 */563 wmb();564 565 pll->rate = rate;566 567 return 0;568}569 570static int hdmi_8998_phy_ready_status(struct hdmi_phy *phy)571{572 u32 nb_tries = HDMI_PLL_POLL_MAX_READS;573 unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;574 u32 status;575 int phy_ready = 0;576 577 while (nb_tries--) {578 status = hdmi_phy_read(phy, REG_HDMI_8998_PHY_STATUS);579 phy_ready = status & BIT(0);580 581 if (phy_ready)582 break;583 584 udelay(timeout);585 }586 587 return phy_ready;588}589 590static int hdmi_8998_pll_lock_status(struct hdmi_pll_8998 *pll)591{592 u32 status;593 int nb_tries = HDMI_PLL_POLL_MAX_READS;594 unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;595 int pll_locked = 0;596 597 while (nb_tries--) {598 status = hdmi_pll_read(pll,599 REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATUS);600 pll_locked = status & BIT(0);601 602 if (pll_locked)603 break;604 605 udelay(timeout);606 }607 608 return pll_locked;609}610 611static int hdmi_8998_pll_prepare(struct clk_hw *hw)612{613 struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);614 struct hdmi_phy *phy = pll_get_phy(pll);615 int i, ret = 0;616 617 hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x1);618 udelay(100);619 620 hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x59);621 udelay(100);622 623 ret = hdmi_8998_pll_lock_status(pll);624 if (!ret)625 return ret;626 627 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {628 hdmi_tx_chan_write(pll, i,629 REG_HDMI_8998_PHY_TXn_LANE_CONFIG, 0x1F);630 }631 632 /* Ensure all registers are flushed to hardware */633 wmb();634 635 ret = hdmi_8998_phy_ready_status(phy);636 if (!ret)637 return ret;638 639 /* Restart the retiming buffer */640 hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x58);641 udelay(1);642 hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x59);643 644 /* Ensure all registers are flushed to hardware */645 wmb();646 647 return 0;648}649 650static long hdmi_8998_pll_round_rate(struct clk_hw *hw,651 unsigned long rate,652 unsigned long *parent_rate)653{654 if (rate < HDMI_PCLK_MIN_FREQ)655 return HDMI_PCLK_MIN_FREQ;656 else if (rate > HDMI_PCLK_MAX_FREQ)657 return HDMI_PCLK_MAX_FREQ;658 else659 return rate;660}661 662static unsigned long hdmi_8998_pll_recalc_rate(struct clk_hw *hw,663 unsigned long parent_rate)664{665 struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);666 return pll->rate;667}668 669static void hdmi_8998_pll_unprepare(struct clk_hw *hw)670{671 struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);672 struct hdmi_phy *phy = pll_get_phy(pll);673 674 hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0);675 usleep_range(100, 150);676}677 678static int hdmi_8998_pll_is_enabled(struct clk_hw *hw)679{680 struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);681 u32 status;682 int pll_locked;683 684 status = hdmi_pll_read(pll, REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATUS);685 pll_locked = status & BIT(0);686 687 return pll_locked;688}689 690static const struct clk_ops hdmi_8998_pll_ops = {691 .set_rate = hdmi_8998_pll_set_clk_rate,692 .round_rate = hdmi_8998_pll_round_rate,693 .recalc_rate = hdmi_8998_pll_recalc_rate,694 .prepare = hdmi_8998_pll_prepare,695 .unprepare = hdmi_8998_pll_unprepare,696 .is_enabled = hdmi_8998_pll_is_enabled,697};698 699static const struct clk_init_data pll_init = {700 .name = "hdmipll",701 .ops = &hdmi_8998_pll_ops,702 .parent_data = (const struct clk_parent_data[]){703 { .fw_name = "xo", .name = "xo_board" },704 },705 .num_parents = 1,706 .flags = CLK_IGNORE_UNUSED,707};708 709int msm_hdmi_pll_8998_init(struct platform_device *pdev)710{711 struct device *dev = &pdev->dev;712 struct hdmi_pll_8998 *pll;713 int ret, i;714 715 pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);716 if (!pll)717 return -ENOMEM;718 719 pll->pdev = pdev;720 721 pll->mmio_qserdes_com = msm_ioremap(pdev, "hdmi_pll");722 if (IS_ERR(pll->mmio_qserdes_com)) {723 DRM_DEV_ERROR(dev, "failed to map pll base\n");724 return -ENOMEM;725 }726 727 for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {728 char name[32];729 730 snprintf(name, sizeof(name), "hdmi_tx_l%d", i);731 732 pll->mmio_qserdes_tx[i] = msm_ioremap(pdev, name);733 if (IS_ERR(pll->mmio_qserdes_tx[i])) {734 DRM_DEV_ERROR(dev, "failed to map pll base\n");735 return -ENOMEM;736 }737 }738 pll->clk_hw.init = &pll_init;739 740 ret = devm_clk_hw_register(dev, &pll->clk_hw);741 if (ret) {742 DRM_DEV_ERROR(dev, "failed to register pll clock\n");743 return ret;744 }745 746 ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &pll->clk_hw);747 if (ret) {748 DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret);749 return ret;750 }751 752 return 0;753}754 755static const char * const hdmi_phy_8998_reg_names[] = {756 "vddio",757 "vcca",758};759 760static const char * const hdmi_phy_8998_clk_names[] = {761 "iface", "ref", "xo",762};763 764const struct hdmi_phy_cfg msm_hdmi_phy_8998_cfg = {765 .type = MSM_HDMI_PHY_8998,766 .reg_names = hdmi_phy_8998_reg_names,767 .num_regs = ARRAY_SIZE(hdmi_phy_8998_reg_names),768 .clk_names = hdmi_phy_8998_clk_names,769 .num_clks = ARRAY_SIZE(hdmi_phy_8998_clk_names),770};771