xref: /rk3399_rockchip-uboot/drivers/video/drm/rk628/rk628_combtxphy.c (revision 48b136cf33e9398c6e5fa53498ee84471b43d6d3)
1ab3bc873SGuochun Huang // SPDX-License-Identifier: GPL-2.0+
2ab3bc873SGuochun Huang /*
3ab3bc873SGuochun Huang  * (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd
4ab3bc873SGuochun Huang  *
5ab3bc873SGuochun Huang  * Author: Wyon Bi <bivvy.bi@rock-chips.com>
6ab3bc873SGuochun Huang  */
7ab3bc873SGuochun Huang 
8ab3bc873SGuochun Huang #include <config.h>
9ab3bc873SGuochun Huang #include <common.h>
10ab3bc873SGuochun Huang #include <errno.h>
11ab3bc873SGuochun Huang #include <dm.h>
12ab3bc873SGuochun Huang #include <div64.h>
13ab3bc873SGuochun Huang #include <asm/io.h>
14ab3bc873SGuochun Huang #include <linux/ioport.h>
15ab3bc873SGuochun Huang #include <linux/iopoll.h>
16ab3bc873SGuochun Huang #include <linux/math64.h>
17ab3bc873SGuochun Huang 
18ab3bc873SGuochun Huang #include "rk628.h"
19ab3bc873SGuochun Huang #include "rk628_cru.h"
20ab3bc873SGuochun Huang #include "rk628_combtxphy.h"
21ab3bc873SGuochun Huang 
rk628_combtxphy_dsi_power_on(struct rk628 * rk628)22ab3bc873SGuochun Huang static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628)
23ab3bc873SGuochun Huang {
24ab3bc873SGuochun Huang 	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
25ab3bc873SGuochun Huang 	u32 val = 0;
26ab3bc873SGuochun Huang 	int ret;
27ab3bc873SGuochun Huang 
28ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_BUS_WIDTH_MASK |
29ab3bc873SGuochun Huang 			      SW_GVI_LVDS_EN_MASK | SW_MIPI_DSI_EN_MASK,
30ab3bc873SGuochun Huang 			       SW_BUS_WIDTH_8BIT | SW_MIPI_DSI_EN);
31ab3bc873SGuochun Huang 
32ab3bc873SGuochun Huang 	if (combtxphy->flags & COMBTXPHY_MODULEA_EN)
33ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
34ab3bc873SGuochun Huang 				      SW_MODULEA_EN_MASK, SW_MODULEA_EN);
35ab3bc873SGuochun Huang 
36ab3bc873SGuochun Huang 	if (combtxphy->flags & COMBTXPHY_MODULEB_EN)
37ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
38ab3bc873SGuochun Huang 				      SW_MODULEB_EN_MASK, SW_MODULEB_EN);
39ab3bc873SGuochun Huang 
40*48b136cfSZhibin Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, SW_PD_PLL);
41*48b136cfSZhibin Huang 
42*48b136cfSZhibin Huang 	if (combtxphy->frac_div)
43*48b136cfSZhibin Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON8, SW_SSC_DEPTH_MASK | SW_SSC_EN_MASK,
44*48b136cfSZhibin Huang 				      SW_SSC_DEPTH(0) | SW_SSC_EN(1));
45*48b136cfSZhibin Huang 
46ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, COMBTXPHY_CON5,
47ab3bc873SGuochun Huang 			SW_REF_DIV(combtxphy->ref_div - 1) |
48ab3bc873SGuochun Huang 			SW_PLL_FB_DIV(combtxphy->fb_div) |
49ab3bc873SGuochun Huang 			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
50ab3bc873SGuochun Huang 			SW_RATE(combtxphy->rate_div / 2));
51ab3bc873SGuochun Huang 
52ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, 0);
53ab3bc873SGuochun Huang 
54ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, GRF_DPHY0_STATUS, val,
55ab3bc873SGuochun Huang 				       val & DPHY_PHYLOCK, 0, 1000);
56ab3bc873SGuochun Huang 	if (ret < 0)
57ab3bc873SGuochun Huang 		printf("rk628 phy is not lock\n");
58ab3bc873SGuochun Huang 
59ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON9,
60ab3bc873SGuochun Huang 			SW_DSI_FSET_EN_MASK | SW_DSI_RCAL_EN_MASK,
61ab3bc873SGuochun Huang 			SW_DSI_FSET_EN | SW_DSI_RCAL_EN(1));
62ab3bc873SGuochun Huang 
63ab3bc873SGuochun Huang 	if (rk628->version == RK628F_VERSION) {
64ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON6,
65ab3bc873SGuochun Huang 				      SW_PLL_CTL_CON0_MASK,
66ab3bc873SGuochun Huang 				      SW_PLL_CTL_CON0(1));
67ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON9,
68ab3bc873SGuochun Huang 				      SW_DSI_LPTX_SR_TRIM_MASK |
69ab3bc873SGuochun Huang 				      SW_DSI_HSTX_AMP_TRIM_MASK,
70ab3bc873SGuochun Huang 				      SW_DSI_LPTX_SR_TRIM(0) |
71ab3bc873SGuochun Huang 				      SW_DSI_HSTX_AMP_TRIM(4));
72ab3bc873SGuochun Huang 	}
73ab3bc873SGuochun Huang 
74ab3bc873SGuochun Huang 	udelay(400);
75ab3bc873SGuochun Huang }
76ab3bc873SGuochun Huang 
rk628_combtxphy_lvds_power_on(struct rk628 * rk628)77ab3bc873SGuochun Huang static void rk628_combtxphy_lvds_power_on(struct rk628 *rk628)
78ab3bc873SGuochun Huang {
79ab3bc873SGuochun Huang 
80ab3bc873SGuochun Huang 	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
81ab3bc873SGuochun Huang 	u32 val;
82ab3bc873SGuochun Huang 	int ret;
83ab3bc873SGuochun Huang 
84ab3bc873SGuochun Huang 	/* Adjust terminal resistance 133 ohm, bypass 0.95v ldo for driver. */
85ab3bc873SGuochun Huang 	if (rk628->version == RK628F_VERSION)
86ab3bc873SGuochun Huang 		val = TX_COM_VOLT_ADJ(3);
87ab3bc873SGuochun Huang 	else
88ab3bc873SGuochun Huang 		val = TX_COM_VOLT_ADJ(0);
89ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON7,
90ab3bc873SGuochun Huang 			      SW_TX_RTERM_MASK | SW_TX_MODE_MASK |
91ab3bc873SGuochun Huang 			      BYPASS_095V_LDO_MASK | TX_COM_VOLT_ADJ_MASK,
92ab3bc873SGuochun Huang 			      SW_TX_RTERM(6) | SW_TX_MODE(3) |
93ab3bc873SGuochun Huang 			      BYPASS_095V_LDO(1) | val);
94ab3bc873SGuochun Huang 
95ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, COMBTXPHY_CON10, TX7_CKDRV_EN | TX2_CKDRV_EN);
96ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
97ab3bc873SGuochun Huang 			      SW_BUS_WIDTH_MASK | SW_GVI_LVDS_EN_MASK |
98ab3bc873SGuochun Huang 			      SW_MIPI_DSI_EN_MASK,
99ab3bc873SGuochun Huang 			      SW_BUS_WIDTH_7BIT | SW_GVI_LVDS_EN);
100ab3bc873SGuochun Huang 
101ab3bc873SGuochun Huang 	if (combtxphy->flags & COMBTXPHY_MODULEA_EN)
102ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
103ab3bc873SGuochun Huang 				      SW_MODULEA_EN_MASK, SW_MODULEA_EN);
104ab3bc873SGuochun Huang 
105ab3bc873SGuochun Huang 	if (combtxphy->flags & COMBTXPHY_MODULEB_EN)
106ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
107ab3bc873SGuochun Huang 				      SW_MODULEB_EN_MASK, SW_MODULEB_EN);
108ab3bc873SGuochun Huang 
109ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, COMBTXPHY_CON5,
110ab3bc873SGuochun Huang 			SW_REF_DIV(combtxphy->ref_div - 1) |
111ab3bc873SGuochun Huang 			SW_PLL_FB_DIV(combtxphy->fb_div) |
112ab3bc873SGuochun Huang 			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
113ab3bc873SGuochun Huang 			SW_RATE(combtxphy->rate_div / 2));
114ab3bc873SGuochun Huang 
115ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
116ab3bc873SGuochun Huang 			      SW_PD_PLL, 0);
117ab3bc873SGuochun Huang 
118ab3bc873SGuochun Huang 	ret = rk628_read_poll_timeout(rk628, GRF_DPHY0_STATUS, val,
119ab3bc873SGuochun Huang 				       val & DPHY_PHYLOCK, 0, 1000);
120ab3bc873SGuochun Huang 	if (ret < 0)
121ab3bc873SGuochun Huang 		printf("rk628 phy is not lock\n");
122ab3bc873SGuochun Huang 
123ab3bc873SGuochun Huang 	udelay(200);
124ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK | SW_TX_PD_MASK, 0);
125ab3bc873SGuochun Huang }
126ab3bc873SGuochun Huang 
rk628_combtxphy_gvi_power_on(struct rk628 * rk628)127ab3bc873SGuochun Huang static void rk628_combtxphy_gvi_power_on(struct rk628 *rk628)
128ab3bc873SGuochun Huang {
129ab3bc873SGuochun Huang 	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
130ab3bc873SGuochun Huang 	int ref_div = 0;
131ab3bc873SGuochun Huang 
132ab3bc873SGuochun Huang 	if (combtxphy->ref_div % 2) {
133ab3bc873SGuochun Huang 		ref_div = combtxphy->ref_div - 1;
134ab3bc873SGuochun Huang 	} else {
135ab3bc873SGuochun Huang 		ref_div = BIT(4);
136ab3bc873SGuochun Huang 		ref_div |= combtxphy->ref_div / 2 - 1;
137ab3bc873SGuochun Huang 	}
138ab3bc873SGuochun Huang 
139ab3bc873SGuochun Huang 	rk628_i2c_write(rk628, COMBTXPHY_CON5,
140ab3bc873SGuochun Huang 			SW_REF_DIV(ref_div) |
141ab3bc873SGuochun Huang 			SW_PLL_FB_DIV(combtxphy->fb_div) |
142ab3bc873SGuochun Huang 			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
143ab3bc873SGuochun Huang 			SW_RATE(combtxphy->rate_div / 2));
144ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
145ab3bc873SGuochun Huang 			      SW_BUS_WIDTH_MASK | SW_GVI_LVDS_EN_MASK |
146ab3bc873SGuochun Huang 			      SW_MIPI_DSI_EN_MASK |
147ab3bc873SGuochun Huang 			      SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK,
148ab3bc873SGuochun Huang 			      SW_BUS_WIDTH_10BIT | SW_GVI_LVDS_EN |
149ab3bc873SGuochun Huang 			      SW_MODULEB_EN | SW_MODULEA_EN);
150ab3bc873SGuochun Huang 
151ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
152ab3bc873SGuochun Huang 			      SW_PD_PLL | SW_TX_PD_MASK, 0);
153ab3bc873SGuochun Huang 	udelay(200);
154ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
155ab3bc873SGuochun Huang 			      SW_TX_IDLE_MASK, 0);
156ab3bc873SGuochun Huang }
157ab3bc873SGuochun Huang 
rk628_combtxphy_power_on(struct rk628 * rk628)158ab3bc873SGuochun Huang void rk628_combtxphy_power_on(struct rk628 *rk628)
159ab3bc873SGuochun Huang {
160ab3bc873SGuochun Huang 	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
161ab3bc873SGuochun Huang 
162ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
163ab3bc873SGuochun Huang 			      SW_TX_IDLE_MASK | SW_TX_PD_MASK |
164ab3bc873SGuochun Huang 			      SW_PD_PLL_MASK, SW_TX_IDLE(0x3ff) |
165ab3bc873SGuochun Huang 			      SW_TX_PD(0x3ff) | SW_PD_PLL);
166ab3bc873SGuochun Huang 
167ab3bc873SGuochun Huang 	switch (combtxphy->mode) {
168ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_MIPI:
169ab3bc873SGuochun Huang 
170ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
171ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL_MASK,
172ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL(0));
173ab3bc873SGuochun Huang 		rk628_combtxphy_dsi_power_on(rk628);
174ab3bc873SGuochun Huang 		break;
175ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_LVDS:
176ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
177ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL_MASK,
178ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL(1));
179ab3bc873SGuochun Huang 		rk628_combtxphy_lvds_power_on(rk628);
180ab3bc873SGuochun Huang 		break;
181ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_GVI:
182ab3bc873SGuochun Huang 		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
183ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL_MASK,
184ab3bc873SGuochun Huang 				      SW_TXPHY_REFCLK_SEL(2));
185ab3bc873SGuochun Huang 		rk628_combtxphy_gvi_power_on(rk628);
186ab3bc873SGuochun Huang 		break;
187ab3bc873SGuochun Huang 	default:
188ab3bc873SGuochun Huang 		break;
189ab3bc873SGuochun Huang 	};
190ab3bc873SGuochun Huang }
191ab3bc873SGuochun Huang 
rk628_combtxphy_power_off(struct rk628 * rk628)192ab3bc873SGuochun Huang void rk628_combtxphy_power_off(struct rk628 *rk628)
193ab3bc873SGuochun Huang {
194ab3bc873SGuochun Huang 	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK |
195ab3bc873SGuochun Huang 			      SW_TX_PD_MASK | SW_PD_PLL_MASK |
196ab3bc873SGuochun Huang 			      SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK,
197ab3bc873SGuochun Huang 			      SW_TX_IDLE(0x3ff) | SW_TX_PD(0x3ff) | SW_PD_PLL);
198ab3bc873SGuochun Huang }
199ab3bc873SGuochun Huang 
rk628_combtxphy_set_bus_width(struct rk628 * rk628,u32 bus_width)200ab3bc873SGuochun Huang void rk628_combtxphy_set_bus_width(struct rk628 *rk628, u32 bus_width)
201ab3bc873SGuochun Huang {
202ab3bc873SGuochun Huang 	rk628->combtxphy.bus_width = bus_width;
203ab3bc873SGuochun Huang }
204ab3bc873SGuochun Huang 
rk628_combtxphy_get_bus_width(struct rk628 * rk628)205ab3bc873SGuochun Huang u32 rk628_combtxphy_get_bus_width(struct rk628 *rk628)
206ab3bc873SGuochun Huang {
207ab3bc873SGuochun Huang 	return rk628->combtxphy.bus_width;
208ab3bc873SGuochun Huang }
209ab3bc873SGuochun Huang 
rk628_combtxphy_set_gvi_division_mode(struct rk628 * rk628,bool division)210ab3bc873SGuochun Huang void rk628_combtxphy_set_gvi_division_mode(struct rk628 *rk628, bool division)
211ab3bc873SGuochun Huang {
212ab3bc873SGuochun Huang 	rk628->combtxphy.division_mode = division;
213ab3bc873SGuochun Huang }
214ab3bc873SGuochun Huang 
rk628_combtxphy_set_mode(struct rk628 * rk628,enum rk628_phy_mode mode)215ab3bc873SGuochun Huang void rk628_combtxphy_set_mode(struct rk628 *rk628, enum rk628_phy_mode mode)
216ab3bc873SGuochun Huang {
217ab3bc873SGuochun Huang 	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
218*48b136cfSZhibin Huang 	unsigned int fvco, fpfd, fin = 24;
219ab3bc873SGuochun Huang 
220ab3bc873SGuochun Huang 	switch (mode) {
221ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_MIPI:
222ab3bc873SGuochun Huang 	{
223ab3bc873SGuochun Huang 		int bus_width = rk628_combtxphy_get_bus_width(rk628);
224ab3bc873SGuochun Huang 		unsigned int fhsc = bus_width >> 8;
225ab3bc873SGuochun Huang 		unsigned int flags = bus_width & 0xff;
226ab3bc873SGuochun Huang 
227*48b136cfSZhibin Huang 		combtxphy->flags = flags;
228*48b136cfSZhibin Huang 
229*48b136cfSZhibin Huang 		/*
230*48b136cfSZhibin Huang 		 * the VCO can work from 1.5GHz to 3GHz.
231*48b136cfSZhibin Huang 		 * fhsc: 80 ~ 374, fvco: 640 ~ 2992
232*48b136cfSZhibin Huang 		 * fhsc: 375 ~ 749, fvco: 1500 ~ 2996
233*48b136cfSZhibin Huang 		 * fhsc: 750 ~ 1500, fvco: 1500 ~ 3000
234*48b136cfSZhibin Huang 		 */
235ab3bc873SGuochun Huang 		if (fhsc < 80 || fhsc > 1500)
236ab3bc873SGuochun Huang 			return;
237ab3bc873SGuochun Huang 		else if (fhsc < 375)
238ab3bc873SGuochun Huang 			combtxphy->rate_div = 4;
239ab3bc873SGuochun Huang 		else if (fhsc < 750)
240ab3bc873SGuochun Huang 			combtxphy->rate_div = 2;
241ab3bc873SGuochun Huang 		else
242ab3bc873SGuochun Huang 			combtxphy->rate_div = 1;
243ab3bc873SGuochun Huang 
244ab3bc873SGuochun Huang 		fvco = fhsc * 2 * combtxphy->rate_div;
245ab3bc873SGuochun Huang 
246*48b136cfSZhibin Huang 		/*
247*48b136cfSZhibin Huang 		 * the reference clock at PFD(FPFD = ref_clk / ref_div) about
248*48b136cfSZhibin Huang 		 * 25MHz is recommende, FPFD must range from 16MHz to 35MHz,
249*48b136cfSZhibin Huang 		 * here to find the best ref_div.
250*48b136cfSZhibin Huang 		 */
251*48b136cfSZhibin Huang 		combtxphy->ref_div = 1;
252*48b136cfSZhibin Huang 
253*48b136cfSZhibin Huang 		/*
254*48b136cfSZhibin Huang 		 * fvco = fin * (fb_div + frac_div / 1024) * 8 / ref_div
255*48b136cfSZhibin Huang 		 */
256*48b136cfSZhibin Huang 		combtxphy->fb_div = fvco * combtxphy->ref_div / 8 / fin;
257*48b136cfSZhibin Huang 		combtxphy->frac_div = 1024 * fvco * combtxphy->ref_div / 8 / fin;
258*48b136cfSZhibin Huang 		combtxphy->frac_div -= 1024 * combtxphy->fb_div;
259*48b136cfSZhibin Huang 
260*48b136cfSZhibin Huang 		/*
261*48b136cfSZhibin Huang 		 * get the actually frequency
262*48b136cfSZhibin Huang 		 */
263*48b136cfSZhibin Huang 		fvco = fin * (1024 * combtxphy->fb_div + combtxphy->frac_div) * 8;
264ab3bc873SGuochun Huang 		fvco = DIV_ROUND_UP(fvco, 1024 * combtxphy->ref_div);
265ab3bc873SGuochun Huang 		fhsc = fvco / 2 / combtxphy->rate_div;
266ab3bc873SGuochun Huang 		combtxphy->bus_width = fhsc;
267ab3bc873SGuochun Huang 
268ab3bc873SGuochun Huang 		break;
269ab3bc873SGuochun Huang 	}
270ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_LVDS:
271ab3bc873SGuochun Huang 	{
272ab3bc873SGuochun Huang 		int bus_width = rk628_combtxphy_get_bus_width(rk628);
273ab3bc873SGuochun Huang 		unsigned int flags = bus_width & 0xff;
274ab3bc873SGuochun Huang 		unsigned int rate = (bus_width >> 8) * 7;
275ab3bc873SGuochun Huang 
276ab3bc873SGuochun Huang 		combtxphy->flags = flags;
277ab3bc873SGuochun Huang 		combtxphy->ref_div = 1;
278ab3bc873SGuochun Huang 		combtxphy->fb_div = 14;
279ab3bc873SGuochun Huang 		combtxphy->frac_div = 0;
280ab3bc873SGuochun Huang 
281ab3bc873SGuochun Huang 		if (rate < 500)
282ab3bc873SGuochun Huang 			combtxphy->rate_div = 4;
283ab3bc873SGuochun Huang 		else if (rate < 1000)
284ab3bc873SGuochun Huang 			combtxphy->rate_div = 2;
285ab3bc873SGuochun Huang 		else
286ab3bc873SGuochun Huang 			combtxphy->rate_div = 1;
287ab3bc873SGuochun Huang 		break;
288ab3bc873SGuochun Huang 	}
289ab3bc873SGuochun Huang 	case RK628_PHY_MODE_VIDEO_GVI:
290ab3bc873SGuochun Huang 	{
291ab3bc873SGuochun Huang 		unsigned int i, delta_freq, best_delta_freq, fb_div;
292ab3bc873SGuochun Huang 		unsigned int bus_width = rk628_combtxphy_get_bus_width(rk628);
293ab3bc873SGuochun Huang 		unsigned long ref_clk;
294ab3bc873SGuochun Huang 		unsigned long long pre_clk;
295ab3bc873SGuochun Huang 
296ab3bc873SGuochun Huang 		if (bus_width < 500000 || bus_width > 4000000)
297ab3bc873SGuochun Huang 			return;
298ab3bc873SGuochun Huang 		else if (bus_width < 1000000)
299ab3bc873SGuochun Huang 			combtxphy->rate_div = 4;
300ab3bc873SGuochun Huang 		else if (bus_width < 2000000)
301ab3bc873SGuochun Huang 			combtxphy->rate_div = 2;
302ab3bc873SGuochun Huang 		else
303ab3bc873SGuochun Huang 			combtxphy->rate_div = 1;
304ab3bc873SGuochun Huang 		fvco = bus_width * combtxphy->rate_div;
305ab3bc873SGuochun Huang 		ref_clk = rk628_cru_clk_get_rate(rk628, CGU_SCLK_VOP);
306ab3bc873SGuochun Huang 		ref_clk = DIV_ROUND_CLOSEST(ref_clk, 1000); /* khz */
307ab3bc873SGuochun Huang 
308ab3bc873SGuochun Huang 		if (combtxphy->division_mode)
309ab3bc873SGuochun Huang 			ref_clk /= 2;
310ab3bc873SGuochun Huang 		/*
311ab3bc873SGuochun Huang 		 * the reference clock at PFD(FPFD = ref_clk / ref_div) about
312ab3bc873SGuochun Huang 		 * 25MHz is recommende, FPFD must range from 16MHz to 35MHz,
313ab3bc873SGuochun Huang 		 * here to find the best rev_div.
314ab3bc873SGuochun Huang 		 */
315ab3bc873SGuochun Huang 		best_delta_freq = ref_clk;
316ab3bc873SGuochun Huang 		for (i = 1; i <= 32; i++) {
317ab3bc873SGuochun Huang 			fpfd = ref_clk / i;
318ab3bc873SGuochun Huang 			delta_freq = abs(fpfd - 25000);
319ab3bc873SGuochun Huang 			if (delta_freq < best_delta_freq) {
320ab3bc873SGuochun Huang 				best_delta_freq = delta_freq;
321ab3bc873SGuochun Huang 				combtxphy->ref_div = i;
322ab3bc873SGuochun Huang 			}
323ab3bc873SGuochun Huang 		}
324ab3bc873SGuochun Huang 
325ab3bc873SGuochun Huang 		/*
326ab3bc873SGuochun Huang 		 * ref_clk / ref_div * 8 * fb_div = FVCO
327ab3bc873SGuochun Huang 		 */
328ab3bc873SGuochun Huang 		pre_clk = (unsigned long long)fvco * combtxphy->ref_div / 8 * 1024;
329ab3bc873SGuochun Huang 		do_div(pre_clk, ref_clk);
330ab3bc873SGuochun Huang 		fb_div = pre_clk / 1024;
331ab3bc873SGuochun Huang 
332ab3bc873SGuochun Huang 		/*
333ab3bc873SGuochun Huang 		 * get the actually frequency
334ab3bc873SGuochun Huang 		 */
335ab3bc873SGuochun Huang 		bus_width = ref_clk / combtxphy->ref_div * 8;
336ab3bc873SGuochun Huang 		bus_width *= fb_div;
337ab3bc873SGuochun Huang 		bus_width /= combtxphy->rate_div;
338ab3bc873SGuochun Huang 
339ab3bc873SGuochun Huang 		combtxphy->frac_div = 0;
340ab3bc873SGuochun Huang 		combtxphy->fb_div = fb_div;
341ab3bc873SGuochun Huang 
342ab3bc873SGuochun Huang 		combtxphy->bus_width = bus_width;
343ab3bc873SGuochun Huang 		break;
344ab3bc873SGuochun Huang 	}
345ab3bc873SGuochun Huang 	default:
346ab3bc873SGuochun Huang 		break;
347ab3bc873SGuochun Huang 	}
348ab3bc873SGuochun Huang 
349ab3bc873SGuochun Huang 	combtxphy->mode = mode;
350ab3bc873SGuochun Huang }
351