brintos

brintos / linux-shallow public Read only

0
0
Text · 20.4 KiB · e6ffaf9 Raw
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