Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/msm/hdmi: HDMI 8996 PHY/PLL support

Add support for the HDMI PHY/PLL found in MSM8996/APQ8096.

Unlike the previous PHYs supported in the driver, this doesn't need
the powerup/powerdown ops. The PLL prepare/unprepare clock ops
enable/disable the phy itself.

Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>

authored by

Archit Taneja and committed by
Rob Clark
e17afdce e9a2ce13

+780
+1
drivers/gpu/drm/msm/Makefile
··· 54 54 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o 55 55 msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o 56 56 msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o 57 + msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_phy_8996.o 57 58 58 59 msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ 59 60 mdp/mdp4/mdp4_dsi_encoder.o \
+8
drivers/gpu/drm/msm/hdmi/hdmi.h
··· 147 147 MSM_HDMI_PHY_8x60, 148 148 MSM_HDMI_PHY_8960, 149 149 MSM_HDMI_PHY_8x74, 150 + MSM_HDMI_PHY_8996, 150 151 MSM_HDMI_PHY_MAX, 151 152 }; 152 153 ··· 164 163 extern const struct hdmi_phy_cfg hdmi_phy_8x60_cfg; 165 164 extern const struct hdmi_phy_cfg hdmi_phy_8960_cfg; 166 165 extern const struct hdmi_phy_cfg hdmi_phy_8x74_cfg; 166 + extern const struct hdmi_phy_cfg hdmi_phy_8996_cfg; 167 167 168 168 struct hdmi_phy { 169 169 struct platform_device *pdev; ··· 194 192 195 193 #ifdef CONFIG_COMMON_CLK 196 194 int hdmi_pll_8960_init(struct platform_device *pdev); 195 + int hdmi_pll_8996_init(struct platform_device *pdev); 197 196 #else 198 197 int hdmi_pll_8960_init(struct platform_device *pdev); 198 + { 199 + return -ENODEV; 200 + } 201 + 202 + int hdmi_pll_8996_init(struct platform_device *pdev) 199 203 { 200 204 return -ENODEV; 201 205 }
+5
drivers/gpu/drm/msm/hdmi/hdmi_phy.c
··· 127 127 case MSM_HDMI_PHY_8960: 128 128 ret = hdmi_pll_8960_init(pdev); 129 129 break; 130 + case MSM_HDMI_PHY_8996: 131 + ret = hdmi_pll_8996_init(pdev); 132 + break; 130 133 /* 131 134 * we don't have PLL support for these, don't report an error for now 132 135 */ ··· 205 202 .data = &hdmi_phy_8x74_cfg }, 206 203 { .compatible = "qcom,hdmi-phy-8084", 207 204 .data = &hdmi_phy_8x74_cfg }, 205 + { .compatible = "qcom,hdmi-phy-8996", 206 + .data = &hdmi_phy_8996_cfg }, 208 207 {} 209 208 }; 210 209
+766
drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c
··· 1 + /* 2 + * Copyright (c) 2016, The Linux Foundation. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 and 6 + * only version 2 as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #include <linux/clk-provider.h> 15 + 16 + #include "hdmi.h" 17 + 18 + #define HDMI_VCO_MAX_FREQ 12000000000UL 19 + #define HDMI_VCO_MIN_FREQ 8000000000UL 20 + 21 + #define HDMI_PCLK_MAX_FREQ 600000000 22 + #define HDMI_PCLK_MIN_FREQ 25000000 23 + 24 + #define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL 25 + #define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL 26 + #define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL 27 + #define HDMI_CORECLK_DIV 5 28 + #define HDMI_DEFAULT_REF_CLOCK 19200000 29 + #define HDMI_PLL_CMP_CNT 1024 30 + 31 + #define HDMI_PLL_POLL_MAX_READS 100 32 + #define HDMI_PLL_POLL_TIMEOUT_US 150 33 + 34 + #define HDMI_NUM_TX_CHANNEL 4 35 + 36 + struct hdmi_pll_8996 { 37 + struct platform_device *pdev; 38 + struct clk_hw clk_hw; 39 + 40 + /* pll mmio base */ 41 + void __iomem *mmio_qserdes_com; 42 + /* tx channel base */ 43 + void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL]; 44 + }; 45 + 46 + #define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8996, clk_hw) 47 + 48 + struct hdmi_8996_phy_pll_reg_cfg { 49 + u32 tx_lx_lane_mode[HDMI_NUM_TX_CHANNEL]; 50 + u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL]; 51 + u32 com_svs_mode_clk_sel; 52 + u32 com_hsclk_sel; 53 + u32 com_pll_cctrl_mode0; 54 + u32 com_pll_rctrl_mode0; 55 + u32 com_cp_ctrl_mode0; 56 + u32 com_dec_start_mode0; 57 + u32 com_div_frac_start1_mode0; 58 + u32 com_div_frac_start2_mode0; 59 + u32 com_div_frac_start3_mode0; 60 + u32 com_integloop_gain0_mode0; 61 + u32 com_integloop_gain1_mode0; 62 + u32 com_lock_cmp_en; 63 + u32 com_lock_cmp1_mode0; 64 + u32 com_lock_cmp2_mode0; 65 + u32 com_lock_cmp3_mode0; 66 + u32 com_core_clk_en; 67 + u32 com_coreclk_div; 68 + u32 com_vco_tune_ctrl; 69 + 70 + u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL]; 71 + u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL]; 72 + u32 tx_lx_vmode_ctrl1[HDMI_NUM_TX_CHANNEL]; 73 + u32 tx_lx_vmode_ctrl2[HDMI_NUM_TX_CHANNEL]; 74 + u32 tx_lx_res_code_lane_tx[HDMI_NUM_TX_CHANNEL]; 75 + u32 tx_lx_hp_pd_enables[HDMI_NUM_TX_CHANNEL]; 76 + 77 + u32 phy_mode; 78 + }; 79 + 80 + struct hdmi_8996_post_divider { 81 + u64 vco_freq; 82 + int hsclk_divsel; 83 + int vco_ratio; 84 + int tx_band_sel; 85 + int half_rate_mode; 86 + }; 87 + 88 + static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8996 *pll) 89 + { 90 + return platform_get_drvdata(pll->pdev); 91 + } 92 + 93 + static inline void hdmi_pll_write(struct hdmi_pll_8996 *pll, int offset, 94 + u32 data) 95 + { 96 + msm_writel(data, pll->mmio_qserdes_com + offset); 97 + } 98 + 99 + static inline u32 hdmi_pll_read(struct hdmi_pll_8996 *pll, int offset) 100 + { 101 + return msm_readl(pll->mmio_qserdes_com + offset); 102 + } 103 + 104 + static inline void hdmi_tx_chan_write(struct hdmi_pll_8996 *pll, int channel, 105 + int offset, int data) 106 + { 107 + msm_writel(data, pll->mmio_qserdes_tx[channel] + offset); 108 + } 109 + 110 + static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk, 111 + bool gen_ssc) 112 + { 113 + if ((frac_start != 0) || gen_ssc) 114 + return (11000000 / (ref_clk / 20)); 115 + 116 + return 0x23; 117 + } 118 + 119 + static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc) 120 + { 121 + if ((frac_start != 0) || gen_ssc) 122 + return 0x16; 123 + 124 + return 0x10; 125 + } 126 + 127 + static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc) 128 + { 129 + if ((frac_start != 0) || gen_ssc) 130 + return 0x28; 131 + 132 + return 0x1; 133 + } 134 + 135 + static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref_clk, 136 + bool gen_ssc) 137 + { 138 + int digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; 139 + u64 base; 140 + 141 + if ((frac_start != 0) || gen_ssc) 142 + base = (64 * ref_clk) / HDMI_DEFAULT_REF_CLOCK; 143 + else 144 + base = (1022 * ref_clk) / 100; 145 + 146 + base <<= digclk_divsel; 147 + 148 + return (base <= 2046 ? base : 2046); 149 + } 150 + 151 + static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk) 152 + { 153 + u64 dividend = HDMI_PLL_CMP_CNT * fdata; 154 + u32 divisor = ref_clk * 10; 155 + u32 rem; 156 + 157 + rem = do_div(dividend, divisor); 158 + if (rem > (divisor >> 1)) 159 + dividend++; 160 + 161 + return dividend - 1; 162 + } 163 + 164 + static inline u64 pll_cmp_to_fdata(u32 pll_cmp, unsigned long ref_clk) 165 + { 166 + u64 fdata = ((u64)pll_cmp) * ref_clk * 10; 167 + 168 + do_div(fdata, HDMI_PLL_CMP_CNT); 169 + 170 + return fdata; 171 + } 172 + 173 + static int pll_get_post_div(struct hdmi_8996_post_divider *pd, u64 bclk) 174 + { 175 + int ratio[] = { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 }; 176 + int hs_divsel[] = { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 }; 177 + int tx_band_sel[] = { 0, 1, 2, 3 }; 178 + u64 vco_freq[60]; 179 + u64 vco, vco_optimal; 180 + int half_rate_mode = 0; 181 + int vco_optimal_index, vco_freq_index; 182 + int i, j; 183 + 184 + retry: 185 + vco_optimal = HDMI_VCO_MAX_FREQ; 186 + vco_optimal_index = -1; 187 + vco_freq_index = 0; 188 + for (i = 0; i < 15; i++) { 189 + for (j = 0; j < 4; j++) { 190 + u32 ratio_mult = ratio[i] << tx_band_sel[j]; 191 + 192 + vco = bclk >> half_rate_mode; 193 + vco *= ratio_mult; 194 + vco_freq[vco_freq_index++] = vco; 195 + } 196 + } 197 + 198 + for (i = 0; i < 60; i++) { 199 + u64 vco_tmp = vco_freq[i]; 200 + 201 + if ((vco_tmp >= HDMI_VCO_MIN_FREQ) && 202 + (vco_tmp <= vco_optimal)) { 203 + vco_optimal = vco_tmp; 204 + vco_optimal_index = i; 205 + } 206 + } 207 + 208 + if (vco_optimal_index == -1) { 209 + if (!half_rate_mode) { 210 + half_rate_mode = 1; 211 + goto retry; 212 + } 213 + } else { 214 + pd->vco_freq = vco_optimal; 215 + pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4]; 216 + pd->vco_ratio = ratio[vco_optimal_index / 4]; 217 + pd->hsclk_divsel = hs_divsel[vco_optimal_index / 4]; 218 + 219 + return 0; 220 + } 221 + 222 + return -EINVAL; 223 + } 224 + 225 + static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk, 226 + struct hdmi_8996_phy_pll_reg_cfg *cfg) 227 + { 228 + struct hdmi_8996_post_divider pd; 229 + u64 bclk; 230 + u64 tmds_clk; 231 + u64 dec_start; 232 + u64 frac_start; 233 + u64 fdata; 234 + u32 pll_divisor; 235 + u32 rem; 236 + u32 cpctrl; 237 + u32 rctrl; 238 + u32 cctrl; 239 + u32 integloop_gain; 240 + u32 pll_cmp; 241 + int i, ret; 242 + 243 + /* bit clk = 10 * pix_clk */ 244 + bclk = ((u64)pix_clk) * 10; 245 + 246 + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) 247 + tmds_clk = pix_clk >> 2; 248 + else 249 + tmds_clk = pix_clk; 250 + 251 + ret = pll_get_post_div(&pd, bclk); 252 + if (ret) 253 + return ret; 254 + 255 + dec_start = pd.vco_freq; 256 + pll_divisor = 4 * ref_clk; 257 + do_div(dec_start, pll_divisor); 258 + 259 + frac_start = pd.vco_freq * (1 << 20); 260 + 261 + rem = do_div(frac_start, pll_divisor); 262 + frac_start -= dec_start * (1 << 20); 263 + if (rem > (pll_divisor >> 1)) 264 + frac_start++; 265 + 266 + cpctrl = pll_get_cpctrl(frac_start, ref_clk, false); 267 + rctrl = pll_get_rctrl(frac_start, false); 268 + cctrl = pll_get_cctrl(frac_start, false); 269 + integloop_gain = pll_get_integloop_gain(frac_start, bclk, 270 + ref_clk, false); 271 + 272 + fdata = pd.vco_freq; 273 + do_div(fdata, pd.vco_ratio); 274 + 275 + pll_cmp = pll_get_pll_cmp(fdata, ref_clk); 276 + 277 + DBG("VCO freq: %llu", pd.vco_freq); 278 + DBG("fdata: %llu", fdata); 279 + DBG("pix_clk: %lu", pix_clk); 280 + DBG("tmds clk: %llu", tmds_clk); 281 + DBG("HSCLK_SEL: %d", pd.hsclk_divsel); 282 + DBG("DEC_START: %llu", dec_start); 283 + DBG("DIV_FRAC_START: %llu", frac_start); 284 + DBG("PLL_CPCTRL: %u", cpctrl); 285 + DBG("PLL_RCTRL: %u", rctrl); 286 + DBG("PLL_CCTRL: %u", cctrl); 287 + DBG("INTEGLOOP_GAIN: %u", integloop_gain); 288 + DBG("TX_BAND: %d", pd.tx_band_sel); 289 + DBG("PLL_CMP: %u", pll_cmp); 290 + 291 + /* Convert these values to register specific values */ 292 + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) 293 + cfg->com_svs_mode_clk_sel = 1; 294 + else 295 + cfg->com_svs_mode_clk_sel = 2; 296 + 297 + cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel); 298 + cfg->com_pll_cctrl_mode0 = cctrl; 299 + cfg->com_pll_rctrl_mode0 = rctrl; 300 + cfg->com_cp_ctrl_mode0 = cpctrl; 301 + cfg->com_dec_start_mode0 = dec_start; 302 + cfg->com_div_frac_start1_mode0 = (frac_start & 0xff); 303 + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8); 304 + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16); 305 + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff); 306 + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8); 307 + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff); 308 + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8); 309 + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); 310 + cfg->com_lock_cmp_en = 0x0; 311 + cfg->com_core_clk_en = 0x2c; 312 + cfg->com_coreclk_div = HDMI_CORECLK_DIV; 313 + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; 314 + cfg->com_vco_tune_ctrl = 0x0; 315 + 316 + cfg->tx_lx_lane_mode[0] = 317 + cfg->tx_lx_lane_mode[2] = 0x43; 318 + 319 + cfg->tx_lx_hp_pd_enables[0] = 320 + cfg->tx_lx_hp_pd_enables[1] = 321 + cfg->tx_lx_hp_pd_enables[2] = 0x0c; 322 + cfg->tx_lx_hp_pd_enables[3] = 0x3; 323 + 324 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) 325 + cfg->tx_lx_tx_band[i] = pd.tx_band_sel + 4; 326 + 327 + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { 328 + cfg->tx_lx_tx_drv_lvl[0] = 329 + cfg->tx_lx_tx_drv_lvl[1] = 330 + cfg->tx_lx_tx_drv_lvl[2] = 0x25; 331 + cfg->tx_lx_tx_drv_lvl[3] = 0x22; 332 + 333 + cfg->tx_lx_tx_emp_post1_lvl[0] = 334 + cfg->tx_lx_tx_emp_post1_lvl[1] = 335 + cfg->tx_lx_tx_emp_post1_lvl[2] = 0x23; 336 + cfg->tx_lx_tx_emp_post1_lvl[3] = 0x27; 337 + 338 + cfg->tx_lx_vmode_ctrl1[0] = 339 + cfg->tx_lx_vmode_ctrl1[1] = 340 + cfg->tx_lx_vmode_ctrl1[2] = 341 + cfg->tx_lx_vmode_ctrl1[3] = 0x00; 342 + 343 + cfg->tx_lx_vmode_ctrl2[0] = 344 + cfg->tx_lx_vmode_ctrl2[1] = 345 + cfg->tx_lx_vmode_ctrl2[2] = 0x0D; 346 + 347 + cfg->tx_lx_vmode_ctrl2[3] = 0x00; 348 + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { 349 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 350 + cfg->tx_lx_tx_drv_lvl[i] = 0x25; 351 + cfg->tx_lx_tx_emp_post1_lvl[i] = 0x23; 352 + cfg->tx_lx_vmode_ctrl1[i] = 0x00; 353 + } 354 + 355 + cfg->tx_lx_vmode_ctrl2[0] = 356 + cfg->tx_lx_vmode_ctrl2[1] = 357 + cfg->tx_lx_vmode_ctrl2[2] = 0x0D; 358 + cfg->tx_lx_vmode_ctrl2[3] = 0x00; 359 + } else { 360 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 361 + cfg->tx_lx_tx_drv_lvl[i] = 0x20; 362 + cfg->tx_lx_tx_emp_post1_lvl[i] = 0x20; 363 + cfg->tx_lx_vmode_ctrl1[i] = 0x00; 364 + cfg->tx_lx_vmode_ctrl2[i] = 0x0E; 365 + } 366 + } 367 + 368 + DBG("com_svs_mode_clk_sel = 0x%x", cfg->com_svs_mode_clk_sel); 369 + DBG("com_hsclk_sel = 0x%x", cfg->com_hsclk_sel); 370 + DBG("com_lock_cmp_en = 0x%x", cfg->com_lock_cmp_en); 371 + DBG("com_pll_cctrl_mode0 = 0x%x", cfg->com_pll_cctrl_mode0); 372 + DBG("com_pll_rctrl_mode0 = 0x%x", cfg->com_pll_rctrl_mode0); 373 + DBG("com_cp_ctrl_mode0 = 0x%x", cfg->com_cp_ctrl_mode0); 374 + DBG("com_dec_start_mode0 = 0x%x", cfg->com_dec_start_mode0); 375 + DBG("com_div_frac_start1_mode0 = 0x%x", cfg->com_div_frac_start1_mode0); 376 + DBG("com_div_frac_start2_mode0 = 0x%x", cfg->com_div_frac_start2_mode0); 377 + DBG("com_div_frac_start3_mode0 = 0x%x", cfg->com_div_frac_start3_mode0); 378 + DBG("com_integloop_gain0_mode0 = 0x%x", cfg->com_integloop_gain0_mode0); 379 + DBG("com_integloop_gain1_mode0 = 0x%x", cfg->com_integloop_gain1_mode0); 380 + DBG("com_lock_cmp1_mode0 = 0x%x", cfg->com_lock_cmp1_mode0); 381 + DBG("com_lock_cmp2_mode0 = 0x%x", cfg->com_lock_cmp2_mode0); 382 + DBG("com_lock_cmp3_mode0 = 0x%x", cfg->com_lock_cmp3_mode0); 383 + DBG("com_core_clk_en = 0x%x", cfg->com_core_clk_en); 384 + DBG("com_coreclk_div = 0x%x", cfg->com_coreclk_div); 385 + DBG("phy_mode = 0x%x", cfg->phy_mode); 386 + 387 + DBG("tx_l0_lane_mode = 0x%x", cfg->tx_lx_lane_mode[0]); 388 + DBG("tx_l2_lane_mode = 0x%x", cfg->tx_lx_lane_mode[2]); 389 + 390 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 391 + DBG("tx_l%d_tx_band = 0x%x", i, cfg->tx_lx_tx_band[i]); 392 + DBG("tx_l%d_tx_drv_lvl = 0x%x", i, cfg->tx_lx_tx_drv_lvl[i]); 393 + DBG("tx_l%d_tx_emp_post1_lvl = 0x%x", i, 394 + cfg->tx_lx_tx_emp_post1_lvl[i]); 395 + DBG("tx_l%d_vmode_ctrl1 = 0x%x", i, cfg->tx_lx_vmode_ctrl1[i]); 396 + DBG("tx_l%d_vmode_ctrl2 = 0x%x", i, cfg->tx_lx_vmode_ctrl2[i]); 397 + } 398 + 399 + return 0; 400 + } 401 + 402 + static int hdmi_8996_pll_set_clk_rate(struct clk_hw *hw, unsigned long rate, 403 + unsigned long parent_rate) 404 + { 405 + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); 406 + struct hdmi_phy *phy = pll_get_phy(pll); 407 + struct hdmi_8996_phy_pll_reg_cfg cfg; 408 + int i, ret; 409 + 410 + memset(&cfg, 0x00, sizeof(cfg)); 411 + 412 + ret = pll_calculate(rate, parent_rate, &cfg); 413 + if (ret) { 414 + DRM_ERROR("PLL calculation failed\n"); 415 + return ret; 416 + } 417 + 418 + /* Initially shut down PHY */ 419 + DBG("Disabling PHY"); 420 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x0); 421 + udelay(500); 422 + 423 + /* Power up sequence */ 424 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x04); 425 + 426 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1); 427 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20); 428 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX0_TX1_LANE_CTL, 0x0F); 429 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX2_TX3_LANE_CTL, 0x0F); 430 + 431 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 432 + hdmi_tx_chan_write(pll, i, 433 + REG_HDMI_PHY_QSERDES_TX_LX_CLKBUF_ENABLE, 434 + 0x03); 435 + hdmi_tx_chan_write(pll, i, 436 + REG_HDMI_PHY_QSERDES_TX_LX_TX_BAND, 437 + cfg.tx_lx_tx_band[i]); 438 + hdmi_tx_chan_write(pll, i, 439 + REG_HDMI_PHY_QSERDES_TX_LX_RESET_TSYNC_EN, 440 + 0x03); 441 + } 442 + 443 + hdmi_tx_chan_write(pll, 0, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, 444 + cfg.tx_lx_lane_mode[0]); 445 + hdmi_tx_chan_write(pll, 2, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, 446 + cfg.tx_lx_lane_mode[2]); 447 + 448 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); 449 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); 450 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37); 451 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02); 452 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E); 453 + 454 + /* Bypass VCO calibration */ 455 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SVS_MODE_CLK_SEL, 456 + cfg.com_svs_mode_clk_sel); 457 + 458 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_TRIM, 0x0F); 459 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_IVCO, 0x0F); 460 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_CTRL, 461 + cfg.com_vco_tune_ctrl); 462 + 463 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x06); 464 + 465 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_SELECT, 0x30); 466 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_HSCLK_SEL, 467 + cfg.com_hsclk_sel); 468 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_EN, 469 + cfg.com_lock_cmp_en); 470 + 471 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE0, 472 + cfg.com_pll_cctrl_mode0); 473 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE0, 474 + cfg.com_pll_rctrl_mode0); 475 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE0, 476 + cfg.com_cp_ctrl_mode0); 477 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE0, 478 + cfg.com_dec_start_mode0); 479 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0, 480 + cfg.com_div_frac_start1_mode0); 481 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0, 482 + cfg.com_div_frac_start2_mode0); 483 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0, 484 + cfg.com_div_frac_start3_mode0); 485 + 486 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 487 + cfg.com_integloop_gain0_mode0); 488 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 489 + cfg.com_integloop_gain1_mode0); 490 + 491 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0, 492 + cfg.com_lock_cmp1_mode0); 493 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0, 494 + cfg.com_lock_cmp2_mode0); 495 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0, 496 + cfg.com_lock_cmp3_mode0); 497 + 498 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00); 499 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORE_CLK_EN, 500 + cfg.com_core_clk_en); 501 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV, 502 + cfg.com_coreclk_div); 503 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CMN_CONFIG, 0x02); 504 + 505 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESCODE_DIV_NUM, 0x15); 506 + 507 + /* TX lanes setup (TX 0/1/2/3) */ 508 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 509 + hdmi_tx_chan_write(pll, i, 510 + REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL, 511 + cfg.tx_lx_tx_drv_lvl[i]); 512 + hdmi_tx_chan_write(pll, i, 513 + REG_HDMI_PHY_QSERDES_TX_LX_TX_EMP_POST1_LVL, 514 + cfg.tx_lx_tx_emp_post1_lvl[i]); 515 + hdmi_tx_chan_write(pll, i, 516 + REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL1, 517 + cfg.tx_lx_vmode_ctrl1[i]); 518 + hdmi_tx_chan_write(pll, i, 519 + REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL2, 520 + cfg.tx_lx_vmode_ctrl2[i]); 521 + hdmi_tx_chan_write(pll, i, 522 + REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL_OFFSET, 523 + 0x00); 524 + hdmi_tx_chan_write(pll, i, 525 + REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_OFFSET, 526 + 0x00); 527 + hdmi_tx_chan_write(pll, i, 528 + REG_HDMI_PHY_QSERDES_TX_LX_TRAN_DRVR_EMP_EN, 529 + 0x03); 530 + hdmi_tx_chan_write(pll, i, 531 + REG_HDMI_PHY_QSERDES_TX_LX_PARRATE_REC_DETECT_IDLE_EN, 532 + 0x40); 533 + hdmi_tx_chan_write(pll, i, 534 + REG_HDMI_PHY_QSERDES_TX_LX_HP_PD_ENABLES, 535 + cfg.tx_lx_hp_pd_enables[i]); 536 + } 537 + 538 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_MODE, cfg.phy_mode); 539 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1F); 540 + 541 + /* 542 + * Ensure that vco configuration gets flushed to hardware before 543 + * enabling the PLL 544 + */ 545 + wmb(); 546 + 547 + return 0; 548 + } 549 + 550 + static int hdmi_8996_phy_ready_status(struct hdmi_phy *phy) 551 + { 552 + u32 nb_tries = HDMI_PLL_POLL_MAX_READS; 553 + unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; 554 + u32 status; 555 + int phy_ready = 0; 556 + 557 + DBG("Waiting for PHY ready"); 558 + 559 + while (nb_tries--) { 560 + status = hdmi_phy_read(phy, REG_HDMI_8996_PHY_STATUS); 561 + phy_ready = status & BIT(0); 562 + 563 + if (phy_ready) 564 + break; 565 + 566 + udelay(timeout); 567 + } 568 + 569 + DBG("PHY is %sready", phy_ready ? "" : "*not* "); 570 + 571 + return phy_ready; 572 + } 573 + 574 + static int hdmi_8996_pll_lock_status(struct hdmi_pll_8996 *pll) 575 + { 576 + u32 status; 577 + int nb_tries = HDMI_PLL_POLL_MAX_READS; 578 + unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; 579 + int pll_locked = 0; 580 + 581 + DBG("Waiting for PLL lock"); 582 + 583 + while (nb_tries--) { 584 + status = hdmi_pll_read(pll, 585 + REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); 586 + pll_locked = status & BIT(0); 587 + 588 + if (pll_locked) 589 + break; 590 + 591 + udelay(timeout); 592 + } 593 + 594 + DBG("HDMI PLL is %slocked", pll_locked ? "" : "*not* "); 595 + 596 + return pll_locked; 597 + } 598 + 599 + static int hdmi_8996_pll_prepare(struct clk_hw *hw) 600 + { 601 + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); 602 + struct hdmi_phy *phy = pll_get_phy(pll); 603 + int i, ret = 0; 604 + 605 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x1); 606 + udelay(100); 607 + 608 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); 609 + udelay(100); 610 + 611 + ret = hdmi_8996_pll_lock_status(pll); 612 + if (!ret) 613 + return ret; 614 + 615 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) 616 + hdmi_tx_chan_write(pll, i, 617 + REG_HDMI_PHY_QSERDES_TX_LX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 618 + 0x6F); 619 + 620 + /* Disable SSC */ 621 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER1, 0x0); 622 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER2, 0x0); 623 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE1, 0x0); 624 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE2, 0x0); 625 + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_EN_CENTER, 0x2); 626 + 627 + ret = hdmi_8996_phy_ready_status(phy); 628 + if (!ret) 629 + return ret; 630 + 631 + /* Restart the retiming buffer */ 632 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x18); 633 + udelay(1); 634 + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); 635 + 636 + return 0; 637 + } 638 + 639 + static long hdmi_8996_pll_round_rate(struct clk_hw *hw, 640 + unsigned long rate, 641 + unsigned long *parent_rate) 642 + { 643 + if (rate < HDMI_PCLK_MIN_FREQ) 644 + return HDMI_PCLK_MIN_FREQ; 645 + else if (rate > HDMI_PCLK_MAX_FREQ) 646 + return HDMI_PCLK_MAX_FREQ; 647 + else 648 + return rate; 649 + } 650 + 651 + static unsigned long hdmi_8996_pll_recalc_rate(struct clk_hw *hw, 652 + unsigned long parent_rate) 653 + { 654 + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); 655 + u64 fdata; 656 + u32 cmp1, cmp2, cmp3, pll_cmp; 657 + 658 + cmp1 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0); 659 + cmp2 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0); 660 + cmp3 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0); 661 + 662 + pll_cmp = cmp1 | (cmp2 << 8) | (cmp3 << 16); 663 + 664 + fdata = pll_cmp_to_fdata(pll_cmp + 1, parent_rate); 665 + 666 + do_div(fdata, 10); 667 + 668 + return fdata; 669 + } 670 + 671 + static void hdmi_8996_pll_unprepare(struct clk_hw *hw) 672 + { 673 + } 674 + 675 + static int hdmi_8996_pll_is_enabled(struct clk_hw *hw) 676 + { 677 + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); 678 + u32 status; 679 + int pll_locked; 680 + 681 + status = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); 682 + pll_locked = status & BIT(0); 683 + 684 + return pll_locked; 685 + } 686 + 687 + static struct clk_ops hdmi_8996_pll_ops = { 688 + .set_rate = hdmi_8996_pll_set_clk_rate, 689 + .round_rate = hdmi_8996_pll_round_rate, 690 + .recalc_rate = hdmi_8996_pll_recalc_rate, 691 + .prepare = hdmi_8996_pll_prepare, 692 + .unprepare = hdmi_8996_pll_unprepare, 693 + .is_enabled = hdmi_8996_pll_is_enabled, 694 + }; 695 + 696 + static const char * const hdmi_pll_parents[] = { 697 + "xo", 698 + }; 699 + 700 + static struct clk_init_data pll_init = { 701 + .name = "hdmipll", 702 + .ops = &hdmi_8996_pll_ops, 703 + .parent_names = hdmi_pll_parents, 704 + .num_parents = ARRAY_SIZE(hdmi_pll_parents), 705 + }; 706 + 707 + int hdmi_pll_8996_init(struct platform_device *pdev) 708 + { 709 + struct device *dev = &pdev->dev; 710 + struct hdmi_pll_8996 *pll; 711 + struct clk *clk; 712 + int i; 713 + 714 + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); 715 + if (!pll) 716 + return -ENOMEM; 717 + 718 + pll->pdev = pdev; 719 + 720 + pll->mmio_qserdes_com = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL"); 721 + if (IS_ERR(pll->mmio_qserdes_com)) { 722 + dev_err(dev, "failed to map pll base\n"); 723 + return -ENOMEM; 724 + } 725 + 726 + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { 727 + char name[32], label[32]; 728 + 729 + snprintf(name, sizeof(name), "hdmi_tx_l%d", i); 730 + snprintf(label, sizeof(label), "HDMI_TX_L%d", i); 731 + 732 + pll->mmio_qserdes_tx[i] = msm_ioremap(pdev, name, label); 733 + if (IS_ERR(pll->mmio_qserdes_tx[i])) { 734 + dev_err(dev, "failed to map pll base\n"); 735 + return -ENOMEM; 736 + } 737 + } 738 + pll->clk_hw.init = &pll_init; 739 + 740 + clk = devm_clk_register(dev, &pll->clk_hw); 741 + if (IS_ERR(clk)) { 742 + dev_err(dev, "failed to register pll clock\n"); 743 + return -EINVAL; 744 + } 745 + 746 + return 0; 747 + } 748 + 749 + static const char * const hdmi_phy_8996_reg_names[] = { 750 + "vddio", 751 + "vcca", 752 + }; 753 + 754 + static const char * const hdmi_phy_8996_clk_names[] = { 755 + "mmagic_iface_clk", 756 + "iface_clk", 757 + "ref_clk", 758 + }; 759 + 760 + const struct hdmi_phy_cfg hdmi_phy_8996_cfg = { 761 + .type = MSM_HDMI_PHY_8996, 762 + .reg_names = hdmi_phy_8996_reg_names, 763 + .num_regs = ARRAY_SIZE(hdmi_phy_8996_reg_names), 764 + .clk_names = hdmi_phy_8996_clk_names, 765 + .num_clks = ARRAY_SIZE(hdmi_phy_8996_clk_names), 766 + };