1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
3*4882a593Smuzhiyun * (C) Copyright 2008-2016 Fuzhou Rockchip Electronics Co., Ltd
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <clk-uclass.h>
7*4882a593Smuzhiyun #include <config.h>
8*4882a593Smuzhiyun #include <common.h>
9*4882a593Smuzhiyun #include <errno.h>
10*4882a593Smuzhiyun #include <malloc.h>
11*4882a593Smuzhiyun #include <misc.h>
12*4882a593Smuzhiyun #include <fdtdec.h>
13*4882a593Smuzhiyun #include <fdt_support.h>
14*4882a593Smuzhiyun #include <asm/unaligned.h>
15*4882a593Smuzhiyun #include <asm/arch/clock.h>
16*4882a593Smuzhiyun #include <dm/device.h>
17*4882a593Smuzhiyun #include <dm/lists.h>
18*4882a593Smuzhiyun #include <dm/read.h>
19*4882a593Smuzhiyun #include <asm/io.h>
20*4882a593Smuzhiyun #include <linux/list.h>
21*4882a593Smuzhiyun #include <div64.h>
22*4882a593Smuzhiyun #include <linux/media-bus-format.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include "rockchip_display.h"
25*4882a593Smuzhiyun #include "rockchip_crtc.h"
26*4882a593Smuzhiyun #include "rockchip_connector.h"
27*4882a593Smuzhiyun #include "rockchip_phy.h"
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #define INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT 1000
30*4882a593Smuzhiyun #define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun /* REG: 0x00 */
33*4882a593Smuzhiyun #define PRE_PLL_REFCLK_SEL_MASK BIT(0)
34*4882a593Smuzhiyun #define PRE_PLL_REFCLK_SEL_PCLK BIT(0)
35*4882a593Smuzhiyun #define PRE_PLL_REFCLK_SEL_OSCCLK 0
36*4882a593Smuzhiyun /* REG: 0x01 */
37*4882a593Smuzhiyun #define BYPASS_RXSENSE_EN_MASK BIT(2)
38*4882a593Smuzhiyun #define BYPASS_RXSENSE_EN BIT(2)
39*4882a593Smuzhiyun #define BYPASS_PWRON_EN_MASK BIT(1)
40*4882a593Smuzhiyun #define BYPASS_PWRON_EN BIT(1)
41*4882a593Smuzhiyun #define BYPASS_PLLPD_EN_MASK BIT(0)
42*4882a593Smuzhiyun #define BYPASS_PLLPD_EN BIT(0)
43*4882a593Smuzhiyun /* REG: 0x02 */
44*4882a593Smuzhiyun #define BYPASS_PDATA_EN_MASK BIT(4)
45*4882a593Smuzhiyun #define BYPASS_PDATA_EN BIT(4)
46*4882a593Smuzhiyun #define PDATAEN_MASK BIT(0)
47*4882a593Smuzhiyun #define PDATAEN_DISABLE BIT(0)
48*4882a593Smuzhiyun #define PDATAEN_ENABLE 0
49*4882a593Smuzhiyun /* REG: 0x03 */
50*4882a593Smuzhiyun #define BYPASS_AUTO_TERM_RES_CAL BIT(7)
51*4882a593Smuzhiyun #define AUDO_TERM_RES_CAL_SPEED_14_8(x) UPDATE(x, 6, 0)
52*4882a593Smuzhiyun /* REG: 0x04 */
53*4882a593Smuzhiyun #define AUDO_TERM_RES_CAL_SPEED_7_0(x) UPDATE(x, 7, 0)
54*4882a593Smuzhiyun /* REG: 0xaa */
55*4882a593Smuzhiyun #define POST_PLL_CTRL_MASK BIT(0)
56*4882a593Smuzhiyun #define POST_PLL_CTRL_MANUAL BIT(0)
57*4882a593Smuzhiyun /* REG: 0xe0 */
58*4882a593Smuzhiyun #define POST_PLL_POWER_MASK BIT(5)
59*4882a593Smuzhiyun #define POST_PLL_POWER_DOWN BIT(5)
60*4882a593Smuzhiyun #define POST_PLL_POWER_UP 0
61*4882a593Smuzhiyun #define PRE_PLL_POWER_MASK BIT(4)
62*4882a593Smuzhiyun #define PRE_PLL_POWER_DOWN BIT(4)
63*4882a593Smuzhiyun #define PRE_PLL_POWER_UP 0
64*4882a593Smuzhiyun #define RXSENSE_CLK_CH_MASK BIT(3)
65*4882a593Smuzhiyun #define RXSENSE_CLK_CH_ENABLE BIT(3)
66*4882a593Smuzhiyun #define RXSENSE_DATA_CH2_MASK BIT(2)
67*4882a593Smuzhiyun #define RXSENSE_DATA_CH2_ENABLE BIT(2)
68*4882a593Smuzhiyun #define RXSENSE_DATA_CH1_MASK BIT(1)
69*4882a593Smuzhiyun #define RXSENSE_DATA_CH1_ENABLE BIT(1)
70*4882a593Smuzhiyun #define RXSENSE_DATA_CH0_MASK BIT(0)
71*4882a593Smuzhiyun #define RXSENSE_DATA_CH0_ENABLE BIT(0)
72*4882a593Smuzhiyun /* REG: 0xe1 */
73*4882a593Smuzhiyun #define BANDGAP_MASK BIT(4)
74*4882a593Smuzhiyun #define BANDGAP_ENABLE BIT(4)
75*4882a593Smuzhiyun #define BANDGAP_DISABLE 0
76*4882a593Smuzhiyun #define TMDS_DRIVER_MASK GENMASK(3, 0)
77*4882a593Smuzhiyun #define TMDS_DRIVER_ENABLE UPDATE(0xf, 3, 0)
78*4882a593Smuzhiyun #define TMDS_DRIVER_DISABLE 0
79*4882a593Smuzhiyun /* REG: 0xe2 */
80*4882a593Smuzhiyun #define PRE_PLL_FB_DIV_8_MASK BIT(7)
81*4882a593Smuzhiyun #define PRE_PLL_FB_DIV_8_SHIFT 7
82*4882a593Smuzhiyun #define PRE_PLL_FB_DIV_8(x) UPDATE(x, 7, 7)
83*4882a593Smuzhiyun #define PCLK_VCO_DIV_5_MASK BIT(5)
84*4882a593Smuzhiyun #define PCLK_VCO_DIV_5_SHIFT 5
85*4882a593Smuzhiyun #define PCLK_VCO_DIV_5(x) UPDATE(x, 5, 5)
86*4882a593Smuzhiyun #define PRE_PLL_PRE_DIV_MASK GENMASK(4, 0)
87*4882a593Smuzhiyun #define PRE_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
88*4882a593Smuzhiyun /* REG: 0xe3 */
89*4882a593Smuzhiyun #define PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
90*4882a593Smuzhiyun /* REG: 0xe4 */
91*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5)
92*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_B_SHIFT 5
93*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5)
94*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0)
95*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_A_SHIFT 0
96*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0)
97*4882a593Smuzhiyun /* REG: 0xe5 */
98*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5)
99*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_C_SHIFT 5
100*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5)
101*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0)
102*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_D_SHIFT 0
103*4882a593Smuzhiyun #define PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0)
104*4882a593Smuzhiyun /* REG: 0xe6 */
105*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(5, 4)
106*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 5, 4)
107*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(3, 2)
108*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 3, 2)
109*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(1, 0)
110*4882a593Smuzhiyun #define PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 1, 0)
111*4882a593Smuzhiyun /* REG: 0xe8 */
112*4882a593Smuzhiyun #define PRE_PLL_LOCK_STATUS BIT(0)
113*4882a593Smuzhiyun /* REG: 0xe9 */
114*4882a593Smuzhiyun #define POST_PLL_POST_DIV_EN_MASK GENMASK(7, 6)
115*4882a593Smuzhiyun #define POST_PLL_POST_DIV_ENABLE UPDATE(3, 7, 6)
116*4882a593Smuzhiyun #define POST_PLL_POST_DIV_DISABLE 0
117*4882a593Smuzhiyun #define POST_PLL_PRE_DIV_MASK GENMASK(4, 0)
118*4882a593Smuzhiyun #define POST_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
119*4882a593Smuzhiyun /* REG: 0xea */
120*4882a593Smuzhiyun #define POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
121*4882a593Smuzhiyun /* REG: 0xeb */
122*4882a593Smuzhiyun #define POST_PLL_FB_DIV_8_MASK BIT(7)
123*4882a593Smuzhiyun #define POST_PLL_FB_DIV_8(x) UPDATE(x, 7, 7)
124*4882a593Smuzhiyun #define POST_PLL_POST_DIV_MASK GENMASK(5, 4)
125*4882a593Smuzhiyun #define POST_PLL_POST_DIV(x) UPDATE(x, 5, 4)
126*4882a593Smuzhiyun #define POST_PLL_LOCK_STATUS BIT(0)
127*4882a593Smuzhiyun /* REG: 0xee */
128*4882a593Smuzhiyun #define TMDS_CH_TA_MASK GENMASK(7, 4)
129*4882a593Smuzhiyun #define TMDS_CH_TA_ENABLE UPDATE(0xf, 7, 4)
130*4882a593Smuzhiyun #define TMDS_CH_TA_DISABLE 0
131*4882a593Smuzhiyun /* REG: 0xef */
132*4882a593Smuzhiyun #define TMDS_CLK_CH_TA(x) UPDATE(x, 7, 6)
133*4882a593Smuzhiyun #define TMDS_DATA_CH2_TA(x) UPDATE(x, 5, 4)
134*4882a593Smuzhiyun #define TMDS_DATA_CH1_TA(x) UPDATE(x, 3, 2)
135*4882a593Smuzhiyun #define TMDS_DATA_CH0_TA(x) UPDATE(x, 1, 0)
136*4882a593Smuzhiyun /* REG: 0xf0 */
137*4882a593Smuzhiyun #define TMDS_DATA_CH2_PRE_EMPHASIS_MASK GENMASK(5, 4)
138*4882a593Smuzhiyun #define TMDS_DATA_CH2_PRE_EMPHASIS(x) UPDATE(x, 5, 4)
139*4882a593Smuzhiyun #define TMDS_DATA_CH1_PRE_EMPHASIS_MASK GENMASK(3, 2)
140*4882a593Smuzhiyun #define TMDS_DATA_CH1_PRE_EMPHASIS(x) UPDATE(x, 3, 2)
141*4882a593Smuzhiyun #define TMDS_DATA_CH0_PRE_EMPHASIS_MASK GENMASK(1, 0)
142*4882a593Smuzhiyun #define TMDS_DATA_CH0_PRE_EMPHASIS(x) UPDATE(x, 1, 0)
143*4882a593Smuzhiyun /* REG: 0xf1 */
144*4882a593Smuzhiyun #define TMDS_CLK_CH_OUTPUT_SWING(x) UPDATE(x, 7, 4)
145*4882a593Smuzhiyun #define TMDS_DATA_CH2_OUTPUT_SWING(x) UPDATE(x, 3, 0)
146*4882a593Smuzhiyun /* REG: 0xf2 */
147*4882a593Smuzhiyun #define TMDS_DATA_CH1_OUTPUT_SWING(x) UPDATE(x, 7, 4)
148*4882a593Smuzhiyun #define TMDS_DATA_CH0_OUTPUT_SWING(x) UPDATE(x, 3, 0)
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun enum inno_hdmi_phy_type {
151*4882a593Smuzhiyun INNO_HDMI_PHY_RK3228,
152*4882a593Smuzhiyun INNO_HDMI_PHY_RK3328,
153*4882a593Smuzhiyun INNO_HDMI_PHY_RK3528
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun struct inno_hdmi_phy_drv_data;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun struct phy_config {
159*4882a593Smuzhiyun unsigned long tmdsclock;
160*4882a593Smuzhiyun u8 regs[14];
161*4882a593Smuzhiyun };
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun struct inno_hdmi_phy {
164*4882a593Smuzhiyun struct udevice *dev;
165*4882a593Smuzhiyun ofnode node;
166*4882a593Smuzhiyun void *regs;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun /* platform data */
169*4882a593Smuzhiyun const struct inno_hdmi_phy_drv_data *plat_data;
170*4882a593Smuzhiyun unsigned long pixclock;
171*4882a593Smuzhiyun u32 bus_width;
172*4882a593Smuzhiyun struct phy_config *phy_cfg;
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun struct pre_pll_config {
176*4882a593Smuzhiyun unsigned long pixclock;
177*4882a593Smuzhiyun unsigned long tmdsclock;
178*4882a593Smuzhiyun u8 prediv;
179*4882a593Smuzhiyun u16 fbdiv;
180*4882a593Smuzhiyun u8 tmds_div_a;
181*4882a593Smuzhiyun u8 tmds_div_b;
182*4882a593Smuzhiyun u8 tmds_div_c;
183*4882a593Smuzhiyun u8 pclk_div_a;
184*4882a593Smuzhiyun u8 pclk_div_b;
185*4882a593Smuzhiyun u8 pclk_div_c;
186*4882a593Smuzhiyun u8 pclk_div_d;
187*4882a593Smuzhiyun u8 vco_div_5_en;
188*4882a593Smuzhiyun u32 fracdiv;
189*4882a593Smuzhiyun };
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun struct post_pll_config {
192*4882a593Smuzhiyun unsigned long tmdsclock;
193*4882a593Smuzhiyun u8 prediv;
194*4882a593Smuzhiyun u16 fbdiv;
195*4882a593Smuzhiyun u8 postdiv;
196*4882a593Smuzhiyun u8 version;
197*4882a593Smuzhiyun };
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun struct inno_hdmi_phy_ops {
200*4882a593Smuzhiyun void (*init)(struct inno_hdmi_phy *inno);
201*4882a593Smuzhiyun int (*power_on)(struct inno_hdmi_phy *inno,
202*4882a593Smuzhiyun const struct post_pll_config *cfg,
203*4882a593Smuzhiyun const struct phy_config *phy_cfg);
204*4882a593Smuzhiyun void (*power_off)(struct inno_hdmi_phy *inno);
205*4882a593Smuzhiyun int (*pre_pll_update)(struct inno_hdmi_phy *inno,
206*4882a593Smuzhiyun const struct pre_pll_config *cfg);
207*4882a593Smuzhiyun unsigned long (*recalc_rate)(struct inno_hdmi_phy *inno,
208*4882a593Smuzhiyun unsigned long parent_rate);
209*4882a593Smuzhiyun };
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun struct inno_hdmi_phy_drv_data {
212*4882a593Smuzhiyun enum inno_hdmi_phy_type dev_type;
213*4882a593Smuzhiyun const struct inno_hdmi_phy_ops *ops;
214*4882a593Smuzhiyun const struct phy_config *phy_cfg_table;
215*4882a593Smuzhiyun };
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun struct rockchip_inno_data {
218*4882a593Smuzhiyun char compatible[30];
219*4882a593Smuzhiyun const void *data;
220*4882a593Smuzhiyun };
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun struct clk_inno_hdmi {
223*4882a593Smuzhiyun struct udevice *dev;
224*4882a593Smuzhiyun ulong rate;
225*4882a593Smuzhiyun };
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* global variables are used to pass reource from phy drivers to clk driver */
228*4882a593Smuzhiyun static struct inno_hdmi_phy *g_inno;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun static const struct pre_pll_config pre_pll_cfg_table[] = {
231*4882a593Smuzhiyun { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
232*4882a593Smuzhiyun { 27000000, 33750000, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 0},
233*4882a593Smuzhiyun { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
234*4882a593Smuzhiyun { 59341000, 59341000, 1, 98, 3, 1, 2, 1, 3, 3, 4, 0, 0xE6AE6B},
235*4882a593Smuzhiyun { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0},
236*4882a593Smuzhiyun { 59341000, 74176250, 1, 98, 0, 3, 3, 1, 3, 3, 4, 0, 0xE6AE6B},
237*4882a593Smuzhiyun { 59400000, 74250000, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 0},
238*4882a593Smuzhiyun { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B},
239*4882a593Smuzhiyun { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0},
240*4882a593Smuzhiyun { 74176000, 92720000, 4, 494, 1, 2, 2, 1, 3, 3, 4, 0, 0x816817},
241*4882a593Smuzhiyun { 74250000, 92812500, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 0},
242*4882a593Smuzhiyun {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B},
243*4882a593Smuzhiyun {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0},
244*4882a593Smuzhiyun {148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817},
245*4882a593Smuzhiyun {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0},
246*4882a593Smuzhiyun {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B},
247*4882a593Smuzhiyun {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0},
248*4882a593Smuzhiyun {296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817},
249*4882a593Smuzhiyun {297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0},
250*4882a593Smuzhiyun {593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B},
251*4882a593Smuzhiyun {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 0},
252*4882a593Smuzhiyun {593407000, 370879375, 4, 494, 1, 2, 0, 1, 3, 1, 1, 1, 0x816817},
253*4882a593Smuzhiyun {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0},
254*4882a593Smuzhiyun {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B},
255*4882a593Smuzhiyun {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0},
256*4882a593Smuzhiyun { ~0UL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
257*4882a593Smuzhiyun };
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun static const struct post_pll_config post_pll_cfg_table[] = {
260*4882a593Smuzhiyun {33750000, 1, 40, 8, 1},
261*4882a593Smuzhiyun {33750000, 1, 80, 8, 2},
262*4882a593Smuzhiyun {33750000, 1, 10, 2, 4},
263*4882a593Smuzhiyun {74250000, 1, 40, 8, 1},
264*4882a593Smuzhiyun {74250000, 18, 80, 8, 2},
265*4882a593Smuzhiyun {74250000, 1, 20, 4, 8},
266*4882a593Smuzhiyun {148500000, 2, 40, 4, 3},
267*4882a593Smuzhiyun {148500000, 1, 10, 2, 8},
268*4882a593Smuzhiyun {297000000, 4, 40, 2, 3},
269*4882a593Smuzhiyun {297000000, 2, 20, 2, 8},
270*4882a593Smuzhiyun {594000000, 8, 40, 1, 3},
271*4882a593Smuzhiyun {594000000, 4, 20, 1, 8},
272*4882a593Smuzhiyun { ~0UL, 0, 0, 0, 0}
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun static const struct phy_config rk3228_phy_cfg[] = {
276*4882a593Smuzhiyun { 165000000, {
277*4882a593Smuzhiyun 0xaa, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
278*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
279*4882a593Smuzhiyun },
280*4882a593Smuzhiyun }, {
281*4882a593Smuzhiyun 340000000, {
282*4882a593Smuzhiyun 0xaa, 0x15, 0x6a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
283*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
284*4882a593Smuzhiyun },
285*4882a593Smuzhiyun }, {
286*4882a593Smuzhiyun 594000000, {
287*4882a593Smuzhiyun 0xaa, 0x15, 0x7a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
288*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
289*4882a593Smuzhiyun },
290*4882a593Smuzhiyun }, {
291*4882a593Smuzhiyun ~0UL, {
292*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
294*4882a593Smuzhiyun },
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun };
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun static const struct phy_config rk3328_phy_cfg[] = {
299*4882a593Smuzhiyun { 165000000, {
300*4882a593Smuzhiyun 0x07, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08,
301*4882a593Smuzhiyun 0x00, 0xac, 0xcc, 0xcc, 0xcc,
302*4882a593Smuzhiyun },
303*4882a593Smuzhiyun }, {
304*4882a593Smuzhiyun 340000000, {
305*4882a593Smuzhiyun 0x0b, 0x0d, 0x0d, 0x0d, 0x07, 0x15, 0x08, 0x08, 0x08,
306*4882a593Smuzhiyun 0x3f, 0xac, 0xcc, 0xcd, 0xdd,
307*4882a593Smuzhiyun },
308*4882a593Smuzhiyun }, {
309*4882a593Smuzhiyun 594000000, {
310*4882a593Smuzhiyun 0x10, 0x1a, 0x1a, 0x1a, 0x07, 0x15, 0x08, 0x08, 0x08,
311*4882a593Smuzhiyun 0x00, 0xac, 0xcc, 0xcc, 0xcc,
312*4882a593Smuzhiyun },
313*4882a593Smuzhiyun }, {
314*4882a593Smuzhiyun ~0UL, {
315*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
317*4882a593Smuzhiyun },
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun };
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun static const struct phy_config rk3528_phy_cfg[] = {
322*4882a593Smuzhiyun /* tmdsclk bias-clk bias-data voltage-clk voltage-data pre-emphasis-data */
323*4882a593Smuzhiyun { 165000000, {
324*4882a593Smuzhiyun 0x03, 0x04, 0x0c, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
325*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
326*4882a593Smuzhiyun },
327*4882a593Smuzhiyun }, {
328*4882a593Smuzhiyun 340000000, {
329*4882a593Smuzhiyun 0x03, 0x04, 0x0c, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
330*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
331*4882a593Smuzhiyun },
332*4882a593Smuzhiyun }, {
333*4882a593Smuzhiyun 594000000, {
334*4882a593Smuzhiyun 0x02, 0x08, 0x0d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
335*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
336*4882a593Smuzhiyun },
337*4882a593Smuzhiyun }, {
338*4882a593Smuzhiyun ~0UL, {
339*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00,
341*4882a593Smuzhiyun },
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun };
344*4882a593Smuzhiyun
inno_write(struct inno_hdmi_phy * inno,u32 reg,u8 val)345*4882a593Smuzhiyun static inline void inno_write(struct inno_hdmi_phy *inno, u32 reg, u8 val)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun writel(val, inno->regs + (reg * 4));
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
inno_read(struct inno_hdmi_phy * inno,u32 reg)350*4882a593Smuzhiyun static inline u8 inno_read(struct inno_hdmi_phy *inno, u32 reg)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun u32 val;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun val = readl(inno->regs + (reg * 4));
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun return val;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
inno_update_bits(struct inno_hdmi_phy * inno,u8 reg,u8 mask,u8 val)359*4882a593Smuzhiyun static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
360*4882a593Smuzhiyun u8 mask, u8 val)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun u32 tmp, orig;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun orig = inno_read(inno, reg);
365*4882a593Smuzhiyun tmp = orig & ~mask;
366*4882a593Smuzhiyun tmp |= val & mask;
367*4882a593Smuzhiyun inno_write(inno, reg, tmp);
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy * inno,unsigned long rate)370*4882a593Smuzhiyun static u32 inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
371*4882a593Smuzhiyun unsigned long rate)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun u32 tmdsclk;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun switch (inno->bus_width) {
376*4882a593Smuzhiyun case 4:
377*4882a593Smuzhiyun tmdsclk = (u32)rate / 2;
378*4882a593Smuzhiyun break;
379*4882a593Smuzhiyun case 5:
380*4882a593Smuzhiyun tmdsclk = (u32)rate * 5 / 8;
381*4882a593Smuzhiyun break;
382*4882a593Smuzhiyun case 6:
383*4882a593Smuzhiyun tmdsclk = (u32)rate * 3 / 4;
384*4882a593Smuzhiyun break;
385*4882a593Smuzhiyun case 10:
386*4882a593Smuzhiyun tmdsclk = (u32)rate * 5 / 4;
387*4882a593Smuzhiyun break;
388*4882a593Smuzhiyun case 12:
389*4882a593Smuzhiyun tmdsclk = (u32)rate * 3 / 2;
390*4882a593Smuzhiyun break;
391*4882a593Smuzhiyun case 16:
392*4882a593Smuzhiyun tmdsclk = (u32)rate * 2;
393*4882a593Smuzhiyun break;
394*4882a593Smuzhiyun default:
395*4882a593Smuzhiyun tmdsclk = rate;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun return tmdsclk;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun
rk_get_cpu_version(void)401*4882a593Smuzhiyun static u8 rk_get_cpu_version(void)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun u8 val = 0;
404*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EFUSE
405*4882a593Smuzhiyun struct udevice *dev;
406*4882a593Smuzhiyun u32 regs[2] = {0};
407*4882a593Smuzhiyun u8 fuses[1];
408*4882a593Smuzhiyun ofnode node;
409*4882a593Smuzhiyun int ret;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun ret = uclass_get_device_by_driver(UCLASS_MISC,
412*4882a593Smuzhiyun DM_GET_DRIVER(rockchip_efuse), &dev);
413*4882a593Smuzhiyun if (ret) {
414*4882a593Smuzhiyun printf("%s: no misc-device found\n", __func__);
415*4882a593Smuzhiyun return -EINVAL;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun node = dev_read_subnode(dev, "cpu-version");
419*4882a593Smuzhiyun if (!ofnode_valid(node))
420*4882a593Smuzhiyun return -EINVAL;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "reg", regs, 2);
423*4882a593Smuzhiyun if (ret) {
424*4882a593Smuzhiyun printf("Cannot get efuse reg\n");
425*4882a593Smuzhiyun return -EINVAL;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun ret = misc_read(dev, regs[0], &fuses, regs[1]);
429*4882a593Smuzhiyun if (ret) {
430*4882a593Smuzhiyun printf("%s: misc_read failed\n", __func__);
431*4882a593Smuzhiyun return 0;
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun val = fuses[0];
435*4882a593Smuzhiyun val = (val >> 3) & 0x1;
436*4882a593Smuzhiyun #endif
437*4882a593Smuzhiyun return val;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun
inno_hdmi_phy_power_on(struct rockchip_phy * phy)440*4882a593Smuzhiyun static int inno_hdmi_phy_power_on(struct rockchip_phy *phy)
441*4882a593Smuzhiyun {
442*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
443*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
444*4882a593Smuzhiyun #else
445*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
446*4882a593Smuzhiyun #endif
447*4882a593Smuzhiyun const struct post_pll_config *cfg = post_pll_cfg_table;
448*4882a593Smuzhiyun const struct phy_config *phy_cfg = inno->plat_data->phy_cfg_table;
449*4882a593Smuzhiyun u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
450*4882a593Smuzhiyun u32 chipversion = 1;
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun printf("start Inno HDMI PHY Power On\n");
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun if (inno->phy_cfg)
455*4882a593Smuzhiyun phy_cfg = inno->phy_cfg;
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun if (!tmdsclock) {
458*4882a593Smuzhiyun printf("TMDS clock is zero!\n");
459*4882a593Smuzhiyun return -EINVAL;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3328 &&
463*4882a593Smuzhiyun rk_get_cpu_version())
464*4882a593Smuzhiyun chipversion = 2;
465*4882a593Smuzhiyun else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228 &&
466*4882a593Smuzhiyun tmdsclock <= 33750000)
467*4882a593Smuzhiyun chipversion = 4;
468*4882a593Smuzhiyun else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3528)
469*4882a593Smuzhiyun chipversion = 8;
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun printf("tmdsclock = %d; chipversion = %d\n", tmdsclock, chipversion);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun for (; cfg->tmdsclock != ~0UL; cfg++)
474*4882a593Smuzhiyun if (tmdsclock <= cfg->tmdsclock &&
475*4882a593Smuzhiyun cfg->version & chipversion)
476*4882a593Smuzhiyun break;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun for (; phy_cfg->tmdsclock != ~0UL; phy_cfg++)
479*4882a593Smuzhiyun if (tmdsclock <= phy_cfg->tmdsclock)
480*4882a593Smuzhiyun break;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun if (cfg->tmdsclock == ~0UL || phy_cfg->tmdsclock == ~0UL)
483*4882a593Smuzhiyun return -EINVAL;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun printf("Inno HDMI PHY Power On\n");
486*4882a593Smuzhiyun if (inno->plat_data->ops->power_on)
487*4882a593Smuzhiyun return inno->plat_data->ops->power_on(inno, cfg, phy_cfg);
488*4882a593Smuzhiyun else
489*4882a593Smuzhiyun return -EINVAL;
490*4882a593Smuzhiyun }
491*4882a593Smuzhiyun
inno_hdmi_phy_power_off(struct rockchip_phy * phy)492*4882a593Smuzhiyun static int inno_hdmi_phy_power_off(struct rockchip_phy *phy)
493*4882a593Smuzhiyun {
494*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
495*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
496*4882a593Smuzhiyun #else
497*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
498*4882a593Smuzhiyun #endif
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun if (inno->plat_data->ops->power_off)
501*4882a593Smuzhiyun inno->plat_data->ops->power_off(inno);
502*4882a593Smuzhiyun printf("Inno HDMI PHY Power Off\n");
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun return 0;
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
inno_hdmi_phy_clk_is_prepared(struct inno_hdmi_phy * inno)507*4882a593Smuzhiyun static int inno_hdmi_phy_clk_is_prepared(struct inno_hdmi_phy *inno)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun u8 status;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228)
512*4882a593Smuzhiyun status = inno_read(inno, 0xe0) & PRE_PLL_POWER_MASK;
513*4882a593Smuzhiyun else
514*4882a593Smuzhiyun status = inno_read(inno, 0xa0) & 1;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun return status ? 0 : 1;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun
inno_hdmi_phy_clk_prepare(struct inno_hdmi_phy * inno)519*4882a593Smuzhiyun static int inno_hdmi_phy_clk_prepare(struct inno_hdmi_phy *inno)
520*4882a593Smuzhiyun {
521*4882a593Smuzhiyun if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228)
522*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK,
523*4882a593Smuzhiyun PRE_PLL_POWER_UP);
524*4882a593Smuzhiyun else
525*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 1, 0);
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun return 0;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
inno_hdmi_phy_clk_set_rate(struct inno_hdmi_phy * inno,unsigned long rate)530*4882a593Smuzhiyun static int inno_hdmi_phy_clk_set_rate(struct inno_hdmi_phy *inno,
531*4882a593Smuzhiyun unsigned long rate)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun const struct pre_pll_config *cfg = pre_pll_cfg_table;
534*4882a593Smuzhiyun u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun for (; cfg->pixclock != ~0UL; cfg++)
537*4882a593Smuzhiyun if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
538*4882a593Smuzhiyun break;
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun if (cfg->pixclock == ~0UL) {
541*4882a593Smuzhiyun printf("unsupported rate %lu\n", rate);
542*4882a593Smuzhiyun return -EINVAL;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun if (inno->plat_data->ops->pre_pll_update)
546*4882a593Smuzhiyun inno->plat_data->ops->pre_pll_update(inno, cfg);
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun inno->pixclock = rate;
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun return 0;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
inno_hdmi_phy_rk3228_init(struct inno_hdmi_phy * inno)553*4882a593Smuzhiyun static void inno_hdmi_phy_rk3228_init(struct inno_hdmi_phy *inno)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun u32 m, v;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun /*
558*4882a593Smuzhiyun * Use phy internal register control
559*4882a593Smuzhiyun * rxsense/poweron/pllpd/pdataen signal.
560*4882a593Smuzhiyun */
561*4882a593Smuzhiyun m = BYPASS_RXSENSE_EN_MASK | BYPASS_PWRON_EN_MASK |
562*4882a593Smuzhiyun BYPASS_PLLPD_EN_MASK;
563*4882a593Smuzhiyun v = BYPASS_RXSENSE_EN | BYPASS_PWRON_EN | BYPASS_PLLPD_EN;
564*4882a593Smuzhiyun inno_update_bits(inno, 0x01, m, v);
565*4882a593Smuzhiyun inno_update_bits(inno, 0x02, BYPASS_PDATA_EN_MASK, BYPASS_PDATA_EN);
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /* manual power down post-PLL */
568*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, POST_PLL_CTRL_MASK, POST_PLL_CTRL_MANUAL);
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3228_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)572*4882a593Smuzhiyun inno_hdmi_phy_rk3228_power_on(struct inno_hdmi_phy *inno,
573*4882a593Smuzhiyun const struct post_pll_config *cfg,
574*4882a593Smuzhiyun const struct phy_config *phy_cfg)
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun int pll_tries;
577*4882a593Smuzhiyun u32 m, v;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun /* pdata_en disable */
580*4882a593Smuzhiyun inno_update_bits(inno, 0x02, PDATAEN_MASK, PDATAEN_DISABLE);
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun /* Power down Post-PLL */
583*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_DOWN);
584*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_DOWN);
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun /* Post-PLL update */
587*4882a593Smuzhiyun m = POST_PLL_PRE_DIV_MASK;
588*4882a593Smuzhiyun v = POST_PLL_PRE_DIV(cfg->prediv);
589*4882a593Smuzhiyun inno_update_bits(inno, 0xe9, m, v);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun m = POST_PLL_FB_DIV_8_MASK;
592*4882a593Smuzhiyun v = POST_PLL_FB_DIV_8(cfg->fbdiv >> 8);
593*4882a593Smuzhiyun inno_update_bits(inno, 0xeb, m, v);
594*4882a593Smuzhiyun inno_write(inno, 0xea, POST_PLL_FB_DIV_7_0(cfg->fbdiv));
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun if (cfg->postdiv == 1) {
597*4882a593Smuzhiyun /* Disable Post-PLL post divider */
598*4882a593Smuzhiyun m = POST_PLL_POST_DIV_EN_MASK;
599*4882a593Smuzhiyun v = POST_PLL_POST_DIV_DISABLE;
600*4882a593Smuzhiyun inno_update_bits(inno, 0xe9, m, v);
601*4882a593Smuzhiyun } else {
602*4882a593Smuzhiyun /* Enable Post-PLL post divider */
603*4882a593Smuzhiyun m = POST_PLL_POST_DIV_EN_MASK;
604*4882a593Smuzhiyun v = POST_PLL_POST_DIV_ENABLE;
605*4882a593Smuzhiyun inno_update_bits(inno, 0xe9, m, v);
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun m = POST_PLL_POST_DIV_MASK;
608*4882a593Smuzhiyun v = POST_PLL_POST_DIV(cfg->postdiv / 2 - 1);
609*4882a593Smuzhiyun inno_update_bits(inno, 0xeb, m, v);
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun for (v = 0; v < 4; v++)
613*4882a593Smuzhiyun inno_write(inno, 0xef + v, phy_cfg->regs[v]);
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun /* Power up Post-PLL */
616*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_UP);
617*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_UP);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun /* BandGap enable */
620*4882a593Smuzhiyun inno_update_bits(inno, 0xe1, BANDGAP_MASK, BANDGAP_ENABLE);
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun /* TMDS driver enable */
623*4882a593Smuzhiyun inno_update_bits(inno, 0xe1, TMDS_DRIVER_MASK, TMDS_DRIVER_ENABLE);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun /* Wait for post PLL lock */
626*4882a593Smuzhiyun pll_tries = 0;
627*4882a593Smuzhiyun while (!(inno_read(inno, 0xeb) & POST_PLL_LOCK_STATUS)) {
628*4882a593Smuzhiyun if (pll_tries == INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT) {
629*4882a593Smuzhiyun printf("Post-PLL unlock\n");
630*4882a593Smuzhiyun return -ETIMEDOUT;
631*4882a593Smuzhiyun }
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun pll_tries++;
634*4882a593Smuzhiyun udelay(100);
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun if (cfg->tmdsclock > 340000000)
638*4882a593Smuzhiyun mdelay(100);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun /* pdata_en enable */
641*4882a593Smuzhiyun inno_update_bits(inno, 0x02, PDATAEN_MASK, PDATAEN_ENABLE);
642*4882a593Smuzhiyun return 0;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun
inno_hdmi_phy_rk3228_power_off(struct inno_hdmi_phy * inno)645*4882a593Smuzhiyun static void inno_hdmi_phy_rk3228_power_off(struct inno_hdmi_phy *inno)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun /* TMDS driver Disable */
648*4882a593Smuzhiyun inno_update_bits(inno, 0xe1, TMDS_DRIVER_MASK, TMDS_DRIVER_DISABLE);
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun /* BandGap Disable */
651*4882a593Smuzhiyun inno_update_bits(inno, 0xe1, BANDGAP_MASK, BANDGAP_DISABLE);
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun /* Post-PLL power down */
654*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, POST_PLL_POWER_MASK, POST_PLL_POWER_DOWN);
655*4882a593Smuzhiyun }
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3228_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)658*4882a593Smuzhiyun inno_hdmi_phy_rk3228_pre_pll_update(struct inno_hdmi_phy *inno,
659*4882a593Smuzhiyun const struct pre_pll_config *cfg)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun int pll_tries;
662*4882a593Smuzhiyun u32 m, v;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun /* Power down PRE-PLL */
665*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_DOWN);
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun m = PRE_PLL_FB_DIV_8_MASK | PCLK_VCO_DIV_5_MASK | PRE_PLL_PRE_DIV_MASK;
668*4882a593Smuzhiyun v = PRE_PLL_FB_DIV_8(cfg->fbdiv >> 8) |
669*4882a593Smuzhiyun PCLK_VCO_DIV_5(cfg->vco_div_5_en) | PRE_PLL_PRE_DIV(cfg->prediv);
670*4882a593Smuzhiyun inno_update_bits(inno, 0xe2, m, v);
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun inno_write(inno, 0xe3, PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun m = PRE_PLL_PCLK_DIV_B_MASK | PRE_PLL_PCLK_DIV_A_MASK;
675*4882a593Smuzhiyun v = PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b) |
676*4882a593Smuzhiyun PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a);
677*4882a593Smuzhiyun inno_update_bits(inno, 0xe4, m, v);
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun m = PRE_PLL_PCLK_DIV_C_MASK | PRE_PLL_PCLK_DIV_D_MASK;
680*4882a593Smuzhiyun v = PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
681*4882a593Smuzhiyun PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d);
682*4882a593Smuzhiyun inno_update_bits(inno, 0xe5, m, v);
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun m = PRE_PLL_TMDSCLK_DIV_C_MASK | PRE_PLL_TMDSCLK_DIV_A_MASK |
685*4882a593Smuzhiyun PRE_PLL_TMDSCLK_DIV_B_MASK;
686*4882a593Smuzhiyun v = PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
687*4882a593Smuzhiyun PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
688*4882a593Smuzhiyun PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b);
689*4882a593Smuzhiyun inno_update_bits(inno, 0xe6, m, v);
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun /* Power up PRE-PLL */
692*4882a593Smuzhiyun inno_update_bits(inno, 0xe0, PRE_PLL_POWER_MASK, PRE_PLL_POWER_UP);
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun /* Wait for Pre-PLL lock */
695*4882a593Smuzhiyun pll_tries = 0;
696*4882a593Smuzhiyun while (!(inno_read(inno, 0xe8) & PRE_PLL_LOCK_STATUS)) {
697*4882a593Smuzhiyun if (pll_tries == INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT) {
698*4882a593Smuzhiyun printf("Pre-PLL unlock\n");
699*4882a593Smuzhiyun return -ETIMEDOUT;
700*4882a593Smuzhiyun }
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun pll_tries++;
703*4882a593Smuzhiyun udelay(100);
704*4882a593Smuzhiyun }
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun return 0;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun
inno_hdmi_phy_rk3328_init(struct inno_hdmi_phy * inno)709*4882a593Smuzhiyun static void inno_hdmi_phy_rk3328_init(struct inno_hdmi_phy *inno)
710*4882a593Smuzhiyun {
711*4882a593Smuzhiyun /*
712*4882a593Smuzhiyun * Use phy internal register control
713*4882a593Smuzhiyun * rxsense/poweron/pllpd/pdataen signal.
714*4882a593Smuzhiyun */
715*4882a593Smuzhiyun inno_write(inno, 0x01, 0x07);
716*4882a593Smuzhiyun inno_write(inno, 0x02, 0x91);
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)720*4882a593Smuzhiyun inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
721*4882a593Smuzhiyun const struct post_pll_config *cfg,
722*4882a593Smuzhiyun const struct phy_config *phy_cfg)
723*4882a593Smuzhiyun {
724*4882a593Smuzhiyun u32 val;
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun /* set pdata_en to 0 */
727*4882a593Smuzhiyun inno_update_bits(inno, 0x02, 1, 0);
728*4882a593Smuzhiyun /* Power off post PLL */
729*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, 1, 1);
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun val = cfg->fbdiv & 0xff;
732*4882a593Smuzhiyun inno_write(inno, 0xac, val);
733*4882a593Smuzhiyun if (cfg->postdiv == 1) {
734*4882a593Smuzhiyun inno_write(inno, 0xaa, 2);
735*4882a593Smuzhiyun val = (cfg->fbdiv >> 8) | cfg->prediv;
736*4882a593Smuzhiyun inno_write(inno, 0xab, val);
737*4882a593Smuzhiyun } else {
738*4882a593Smuzhiyun val = (cfg->postdiv / 2) - 1;
739*4882a593Smuzhiyun inno_write(inno, 0xad, val);
740*4882a593Smuzhiyun val = (cfg->fbdiv >> 8) | cfg->prediv;
741*4882a593Smuzhiyun inno_write(inno, 0xab, val);
742*4882a593Smuzhiyun inno_write(inno, 0xaa, 0x0e);
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun for (val = 0; val < 14; val++)
746*4882a593Smuzhiyun inno_write(inno, 0xb5 + val, phy_cfg->regs[val]);
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun /* bit[7:6] of reg c8/c9/ca/c8 is ESD detect threshold:
749*4882a593Smuzhiyun * 00 - 340mV
750*4882a593Smuzhiyun * 01 - 280mV
751*4882a593Smuzhiyun * 10 - 260mV
752*4882a593Smuzhiyun * 11 - 240mV
753*4882a593Smuzhiyun * default is 240mV, now we set it to 340mV
754*4882a593Smuzhiyun */
755*4882a593Smuzhiyun inno_write(inno, 0xc8, 0);
756*4882a593Smuzhiyun inno_write(inno, 0xc9, 0);
757*4882a593Smuzhiyun inno_write(inno, 0xca, 0);
758*4882a593Smuzhiyun inno_write(inno, 0xcb, 0);
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun if (phy_cfg->tmdsclock > 340000000) {
761*4882a593Smuzhiyun /* Set termination resistor to 100ohm */
762*4882a593Smuzhiyun val = 75000000 / 100000;
763*4882a593Smuzhiyun inno_write(inno, 0xc5, ((val >> 8) & 0xff) | 0x80);
764*4882a593Smuzhiyun inno_write(inno, 0xc6, val & 0xff);
765*4882a593Smuzhiyun inno_write(inno, 0xc7, 3 << 1);
766*4882a593Smuzhiyun inno_write(inno, 0xc5, ((val >> 8) & 0xff));
767*4882a593Smuzhiyun } else if (phy_cfg->tmdsclock > 165000000) {
768*4882a593Smuzhiyun inno_write(inno, 0xc5, 0x81);
769*4882a593Smuzhiyun /* clk termination resistor is 50ohm
770*4882a593Smuzhiyun * data termination resistor is 150ohm
771*4882a593Smuzhiyun */
772*4882a593Smuzhiyun inno_write(inno, 0xc8, 0x30);
773*4882a593Smuzhiyun inno_write(inno, 0xc9, 0x10);
774*4882a593Smuzhiyun inno_write(inno, 0xca, 0x10);
775*4882a593Smuzhiyun inno_write(inno, 0xcb, 0x10);
776*4882a593Smuzhiyun } else {
777*4882a593Smuzhiyun inno_write(inno, 0xc5, 0x81);
778*4882a593Smuzhiyun }
779*4882a593Smuzhiyun
780*4882a593Smuzhiyun /* set TMDS sync detection counter length */
781*4882a593Smuzhiyun val = 47520000000UL / phy_cfg->tmdsclock;
782*4882a593Smuzhiyun inno_write(inno, 0xd8, (val >> 8) & 0xff);
783*4882a593Smuzhiyun inno_write(inno, 0xd9, val & 0xff);
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun /* Power up post PLL */
786*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, 1, 0);
787*4882a593Smuzhiyun /* Power up tmds driver */
788*4882a593Smuzhiyun inno_update_bits(inno, 0xb0, 4, 4);
789*4882a593Smuzhiyun inno_write(inno, 0xb2, 0x0f);
790*4882a593Smuzhiyun
791*4882a593Smuzhiyun /* Wait for post PLL lock */
792*4882a593Smuzhiyun for (val = 0; val < 5; val++) {
793*4882a593Smuzhiyun if (inno_read(inno, 0xaf) & 1)
794*4882a593Smuzhiyun break;
795*4882a593Smuzhiyun udelay(1000);
796*4882a593Smuzhiyun }
797*4882a593Smuzhiyun if (!(inno_read(inno, 0xaf) & 1)) {
798*4882a593Smuzhiyun printf("HDMI PHY Post PLL unlock\n");
799*4882a593Smuzhiyun return -ETIMEDOUT;
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun if (phy_cfg->tmdsclock > 340000000)
802*4882a593Smuzhiyun mdelay(100);
803*4882a593Smuzhiyun /* set pdata_en to 1 */
804*4882a593Smuzhiyun inno_update_bits(inno, 0x02, 1, 1);
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun return 0;
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun
inno_hdmi_phy_rk3328_power_off(struct inno_hdmi_phy * inno)809*4882a593Smuzhiyun static void inno_hdmi_phy_rk3328_power_off(struct inno_hdmi_phy *inno)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun /* Power off driver */
812*4882a593Smuzhiyun inno_write(inno, 0xb2, 0);
813*4882a593Smuzhiyun /* Power off band gap */
814*4882a593Smuzhiyun inno_update_bits(inno, 0xb0, 4, 0);
815*4882a593Smuzhiyun /* Power off post pll */
816*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, 1, 1);
817*4882a593Smuzhiyun }
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3328_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)820*4882a593Smuzhiyun inno_hdmi_phy_rk3328_pre_pll_update(struct inno_hdmi_phy *inno,
821*4882a593Smuzhiyun const struct pre_pll_config *cfg)
822*4882a593Smuzhiyun {
823*4882a593Smuzhiyun u32 val;
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun /* Power off PLL */
826*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 1, 1);
827*4882a593Smuzhiyun /* Configure pre-pll */
828*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 2, (cfg->vco_div_5_en & 1) << 1);
829*4882a593Smuzhiyun inno_write(inno, 0xa1, cfg->prediv);
830*4882a593Smuzhiyun if (cfg->fracdiv)
831*4882a593Smuzhiyun val = ((cfg->fbdiv >> 8) & 0x0f) | 0xc0;
832*4882a593Smuzhiyun else
833*4882a593Smuzhiyun val = ((cfg->fbdiv >> 8) & 0x0f) | 0xf0;
834*4882a593Smuzhiyun inno_write(inno, 0xa2, val);
835*4882a593Smuzhiyun inno_write(inno, 0xa3, cfg->fbdiv & 0xff);
836*4882a593Smuzhiyun val = (cfg->pclk_div_a & 0x1f) |
837*4882a593Smuzhiyun ((cfg->pclk_div_b & 3) << 5);
838*4882a593Smuzhiyun inno_write(inno, 0xa5, val);
839*4882a593Smuzhiyun val = (cfg->pclk_div_d & 0x1f) |
840*4882a593Smuzhiyun ((cfg->pclk_div_c & 3) << 5);
841*4882a593Smuzhiyun inno_write(inno, 0xa6, val);
842*4882a593Smuzhiyun val = ((cfg->tmds_div_a & 3) << 4) |
843*4882a593Smuzhiyun ((cfg->tmds_div_b & 3) << 2) |
844*4882a593Smuzhiyun (cfg->tmds_div_c & 3);
845*4882a593Smuzhiyun inno_write(inno, 0xa4, val);
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun if (cfg->fracdiv) {
848*4882a593Smuzhiyun val = cfg->fracdiv & 0xff;
849*4882a593Smuzhiyun inno_write(inno, 0xd3, val);
850*4882a593Smuzhiyun val = (cfg->fracdiv >> 8) & 0xff;
851*4882a593Smuzhiyun inno_write(inno, 0xd2, val);
852*4882a593Smuzhiyun val = (cfg->fracdiv >> 16) & 0xff;
853*4882a593Smuzhiyun inno_write(inno, 0xd1, val);
854*4882a593Smuzhiyun } else {
855*4882a593Smuzhiyun inno_write(inno, 0xd3, 0);
856*4882a593Smuzhiyun inno_write(inno, 0xd2, 0);
857*4882a593Smuzhiyun inno_write(inno, 0xd1, 0);
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun
860*4882a593Smuzhiyun /* Power up PLL */
861*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 1, 0);
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun /* Wait for PLL lock */
864*4882a593Smuzhiyun for (val = 0; val < 5; val++) {
865*4882a593Smuzhiyun if (inno_read(inno, 0xa9) & 1)
866*4882a593Smuzhiyun break;
867*4882a593Smuzhiyun udelay(1000);
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun if (val == 5) {
870*4882a593Smuzhiyun printf("Pre-PLL unlock\n");
871*4882a593Smuzhiyun return -ETIMEDOUT;
872*4882a593Smuzhiyun }
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun return 0;
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun static unsigned long
inno_hdmi_3328_phy_pll_recalc_rate(struct inno_hdmi_phy * inno,unsigned long parent_rate)878*4882a593Smuzhiyun inno_hdmi_3328_phy_pll_recalc_rate(struct inno_hdmi_phy *inno,
879*4882a593Smuzhiyun unsigned long parent_rate)
880*4882a593Smuzhiyun {
881*4882a593Smuzhiyun unsigned long rate, vco, frac;
882*4882a593Smuzhiyun u8 nd, no_a, no_b, no_d;
883*4882a593Smuzhiyun __maybe_unused u8 no_c;
884*4882a593Smuzhiyun u16 nf;
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun nd = inno_read(inno, 0xa1) & 0x3f;
887*4882a593Smuzhiyun nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3);
888*4882a593Smuzhiyun vco = parent_rate * nf;
889*4882a593Smuzhiyun if ((inno_read(inno, 0xa2) & 0x30) == 0) {
890*4882a593Smuzhiyun frac = inno_read(inno, 0xd3) |
891*4882a593Smuzhiyun (inno_read(inno, 0xd2) << 8) |
892*4882a593Smuzhiyun (inno_read(inno, 0xd1) << 16);
893*4882a593Smuzhiyun vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
894*4882a593Smuzhiyun }
895*4882a593Smuzhiyun if (inno_read(inno, 0xa0) & 2) {
896*4882a593Smuzhiyun rate = vco / (nd * 5);
897*4882a593Smuzhiyun } else {
898*4882a593Smuzhiyun no_a = inno_read(inno, 0xa5) & 0x1f;
899*4882a593Smuzhiyun no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2;
900*4882a593Smuzhiyun no_c = (1 << ((inno_read(inno, 0xa6) >> 5) & 7));
901*4882a593Smuzhiyun no_d = inno_read(inno, 0xa6) & 0x1f;
902*4882a593Smuzhiyun if (no_a == 1)
903*4882a593Smuzhiyun rate = vco / (nd * no_b * no_d * 2);
904*4882a593Smuzhiyun else
905*4882a593Smuzhiyun rate = vco / (nd * no_a * no_d * 2);
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun inno->pixclock = rate;
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun return rate;
910*4882a593Smuzhiyun }
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3528_power_on(struct inno_hdmi_phy * inno,const struct post_pll_config * cfg,const struct phy_config * phy_cfg)913*4882a593Smuzhiyun inno_hdmi_phy_rk3528_power_on(struct inno_hdmi_phy *inno,
914*4882a593Smuzhiyun const struct post_pll_config *cfg,
915*4882a593Smuzhiyun const struct phy_config *phy_cfg)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun u32 val;
918*4882a593Smuzhiyun u64 temp;
919*4882a593Smuzhiyun u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
920*4882a593Smuzhiyun
921*4882a593Smuzhiyun /* Power off post PLL */
922*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, 1, 0);
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun val = cfg->prediv;
925*4882a593Smuzhiyun inno_write(inno, 0xab, val);
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun if (cfg->postdiv == 1) {
928*4882a593Smuzhiyun inno_write(inno, 0xad, 0x8);
929*4882a593Smuzhiyun inno_write(inno, 0xaa, 2);
930*4882a593Smuzhiyun } else {
931*4882a593Smuzhiyun val = (cfg->postdiv / 2) - 1;
932*4882a593Smuzhiyun inno_write(inno, 0xad, val);
933*4882a593Smuzhiyun inno_write(inno, 0xaa, 0x0e);
934*4882a593Smuzhiyun }
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun val = cfg->fbdiv & 0xff;
937*4882a593Smuzhiyun inno_write(inno, 0xac, val);
938*4882a593Smuzhiyun val = (cfg->fbdiv >> 8) & BIT(0);
939*4882a593Smuzhiyun inno_update_bits(inno, 0xad, BIT(4), val);
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun /* current bias clk/data 2 */
942*4882a593Smuzhiyun val = phy_cfg->regs[0] << 4 | phy_cfg->regs[1];
943*4882a593Smuzhiyun inno_write(inno, 0xbf, val);
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun /* current bias data 1/0 */
946*4882a593Smuzhiyun val = phy_cfg->regs[1] << 4 | phy_cfg->regs[1];
947*4882a593Smuzhiyun inno_write(inno, 0xc0, val);
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun /* output voltage */
950*4882a593Smuzhiyun inno_write(inno, 0xb5, phy_cfg->regs[2]);
951*4882a593Smuzhiyun inno_write(inno, 0xb6, phy_cfg->regs[3]);
952*4882a593Smuzhiyun inno_write(inno, 0xb7, phy_cfg->regs[3]);
953*4882a593Smuzhiyun inno_write(inno, 0xb8, phy_cfg->regs[3]);
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun /* pre-emphasis */
956*4882a593Smuzhiyun inno_write(inno, 0xbb, phy_cfg->regs[4]);
957*4882a593Smuzhiyun inno_write(inno, 0xbc, phy_cfg->regs[4]);
958*4882a593Smuzhiyun inno_write(inno, 0xbd, phy_cfg->regs[4]);
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun /* enable LDO */
961*4882a593Smuzhiyun inno_write(inno, 0xb4, 0x7);
962*4882a593Smuzhiyun
963*4882a593Smuzhiyun /* enable serializer */
964*4882a593Smuzhiyun inno_write(inno, 0xbe, 0x70);
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun inno_write(inno, 0xb2, 0x0f);
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun for (val = 0; val < 5; val++) {
969*4882a593Smuzhiyun if (inno_read(inno, 0xaf) & 1)
970*4882a593Smuzhiyun break;
971*4882a593Smuzhiyun udelay(1000);
972*4882a593Smuzhiyun }
973*4882a593Smuzhiyun if (!(inno_read(inno, 0xaf) & 1)) {
974*4882a593Smuzhiyun dev_err(inno->dev, "HDMI PHY Post PLL unlock\n");
975*4882a593Smuzhiyun return -ETIMEDOUT;
976*4882a593Smuzhiyun }
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun /* set termination resistance */
979*4882a593Smuzhiyun if (phy_cfg->tmdsclock > 340000000) {
980*4882a593Smuzhiyun inno_write(inno, 0xc7, 0x76);
981*4882a593Smuzhiyun inno_write(inno, 0xc5, 0x83);
982*4882a593Smuzhiyun inno_write(inno, 0xc8, 0x00);
983*4882a593Smuzhiyun inno_write(inno, 0xc9, 0x2f);
984*4882a593Smuzhiyun inno_write(inno, 0xca, 0x2f);
985*4882a593Smuzhiyun inno_write(inno, 0xcb, 0x2f);
986*4882a593Smuzhiyun } else {
987*4882a593Smuzhiyun inno_write(inno, 0xc7, 0x76);
988*4882a593Smuzhiyun inno_write(inno, 0xc5, 0x83);
989*4882a593Smuzhiyun inno_write(inno, 0xc8, 0x00);
990*4882a593Smuzhiyun inno_write(inno, 0xc9, 0x0f);
991*4882a593Smuzhiyun inno_write(inno, 0xca, 0x0f);
992*4882a593Smuzhiyun inno_write(inno, 0xcb, 0x0f);
993*4882a593Smuzhiyun }
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun
996*4882a593Smuzhiyun /* set TMDS sync detection counter length */
997*4882a593Smuzhiyun temp = 47520000000UL / tmdsclock;
998*4882a593Smuzhiyun inno_write(inno, 0xd8, (temp >> 8) & 0xff);
999*4882a593Smuzhiyun inno_write(inno, 0xd9, temp & 0xff);
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun if (phy_cfg->tmdsclock > 340000000)
1002*4882a593Smuzhiyun mdelay(100);
1003*4882a593Smuzhiyun /* set pdata_en to 0/1 */
1004*4882a593Smuzhiyun inno_update_bits(inno, 0x02, 1, 0);
1005*4882a593Smuzhiyun inno_update_bits(inno, 0x02, 1, 1);
1006*4882a593Smuzhiyun
1007*4882a593Smuzhiyun /* Enable PHY IRQ */
1008*4882a593Smuzhiyun inno_write(inno, 0x05, 0x22);
1009*4882a593Smuzhiyun inno_write(inno, 0x07, 0x22);
1010*4882a593Smuzhiyun inno_write(inno, 0xcc, 0x0f);
1011*4882a593Smuzhiyun
1012*4882a593Smuzhiyun return 0;
1013*4882a593Smuzhiyun }
1014*4882a593Smuzhiyun
inno_hdmi_phy_rk3528_power_off(struct inno_hdmi_phy * inno)1015*4882a593Smuzhiyun static void inno_hdmi_phy_rk3528_power_off(struct inno_hdmi_phy *inno)
1016*4882a593Smuzhiyun {
1017*4882a593Smuzhiyun /* Power off driver */
1018*4882a593Smuzhiyun inno_write(inno, 0xb2, 0);
1019*4882a593Smuzhiyun /* Power off band gap */
1020*4882a593Smuzhiyun inno_update_bits(inno, 0xb0, 4, 0);
1021*4882a593Smuzhiyun /* Power off post pll */
1022*4882a593Smuzhiyun inno_update_bits(inno, 0xaa, 1, 1);
1023*4882a593Smuzhiyun
1024*4882a593Smuzhiyun /* Disable PHY IRQ */
1025*4882a593Smuzhiyun inno_write(inno, 0x05, 0);
1026*4882a593Smuzhiyun inno_write(inno, 0x07, 0);
1027*4882a593Smuzhiyun }
1028*4882a593Smuzhiyun
inno_hdmi_phy_rk3528_init(struct inno_hdmi_phy * inno)1029*4882a593Smuzhiyun static void inno_hdmi_phy_rk3528_init(struct inno_hdmi_phy *inno)
1030*4882a593Smuzhiyun {
1031*4882a593Smuzhiyun /*
1032*4882a593Smuzhiyun * Use phy internal register control
1033*4882a593Smuzhiyun * rxsense/poweron/pllpd/pdataen signal.
1034*4882a593Smuzhiyun */
1035*4882a593Smuzhiyun inno_write(inno, 0x02, 0x81);
1036*4882a593Smuzhiyun }
1037*4882a593Smuzhiyun
1038*4882a593Smuzhiyun static int
inno_hdmi_phy_rk3528_pre_pll_update(struct inno_hdmi_phy * inno,const struct pre_pll_config * cfg)1039*4882a593Smuzhiyun inno_hdmi_phy_rk3528_pre_pll_update(struct inno_hdmi_phy *inno,
1040*4882a593Smuzhiyun const struct pre_pll_config *cfg)
1041*4882a593Smuzhiyun {
1042*4882a593Smuzhiyun u32 val;
1043*4882a593Smuzhiyun
1044*4882a593Smuzhiyun inno_update_bits(inno, 0xb0, 4, 4);
1045*4882a593Smuzhiyun inno_write(inno, 0xcc, 0x0f);
1046*4882a593Smuzhiyun
1047*4882a593Smuzhiyun /* Power on PLL */
1048*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 1, 0);
1049*4882a593Smuzhiyun /* Configure pre-pll */
1050*4882a593Smuzhiyun inno_update_bits(inno, 0xa0, 2, (cfg->vco_div_5_en & 1) << 1);
1051*4882a593Smuzhiyun inno_write(inno, 0xa1, cfg->prediv);
1052*4882a593Smuzhiyun if (cfg->fracdiv)
1053*4882a593Smuzhiyun val = ((cfg->fbdiv >> 8) & 0x0f) | 0xc0;
1054*4882a593Smuzhiyun else
1055*4882a593Smuzhiyun val = ((cfg->fbdiv >> 8) & 0x0f) | 0xf0;
1056*4882a593Smuzhiyun inno_write(inno, 0xa2, val);
1057*4882a593Smuzhiyun inno_write(inno, 0xa3, cfg->fbdiv & 0xff);
1058*4882a593Smuzhiyun val = (cfg->pclk_div_a & 0x1f) |
1059*4882a593Smuzhiyun ((cfg->pclk_div_b & 3) << 5);
1060*4882a593Smuzhiyun inno_write(inno, 0xa5, val);
1061*4882a593Smuzhiyun val = (cfg->pclk_div_d & 0x1f) |
1062*4882a593Smuzhiyun ((cfg->pclk_div_c & 3) << 5);
1063*4882a593Smuzhiyun inno_write(inno, 0xa6, val);
1064*4882a593Smuzhiyun val = ((cfg->tmds_div_a & 3) << 4) |
1065*4882a593Smuzhiyun ((cfg->tmds_div_b & 3) << 2) |
1066*4882a593Smuzhiyun (cfg->tmds_div_c & 3);
1067*4882a593Smuzhiyun inno_write(inno, 0xa4, val);
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun if (cfg->fracdiv) {
1070*4882a593Smuzhiyun val = cfg->fracdiv & 0xff;
1071*4882a593Smuzhiyun inno_write(inno, 0xd3, val);
1072*4882a593Smuzhiyun val = (cfg->fracdiv >> 8) & 0xff;
1073*4882a593Smuzhiyun inno_write(inno, 0xd2, val);
1074*4882a593Smuzhiyun val = (cfg->fracdiv >> 16) & 0xff;
1075*4882a593Smuzhiyun inno_write(inno, 0xd1, val);
1076*4882a593Smuzhiyun } else {
1077*4882a593Smuzhiyun inno_write(inno, 0xd3, 0);
1078*4882a593Smuzhiyun inno_write(inno, 0xd2, 0);
1079*4882a593Smuzhiyun inno_write(inno, 0xd1, 0);
1080*4882a593Smuzhiyun }
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun /* Wait for PLL lock */
1083*4882a593Smuzhiyun for (val = 0; val < 5; val++) {
1084*4882a593Smuzhiyun if (inno_read(inno, 0xa9) & 1)
1085*4882a593Smuzhiyun break;
1086*4882a593Smuzhiyun udelay(1000);
1087*4882a593Smuzhiyun }
1088*4882a593Smuzhiyun if (val == 5) {
1089*4882a593Smuzhiyun dev_err(inno->dev, "Pre-PLL unlock\n");
1090*4882a593Smuzhiyun return -ETIMEDOUT;
1091*4882a593Smuzhiyun }
1092*4882a593Smuzhiyun
1093*4882a593Smuzhiyun return 0;
1094*4882a593Smuzhiyun }
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun static unsigned long
inno_hdmi_rk3528_phy_pll_recalc_rate(struct inno_hdmi_phy * inno,unsigned long parent_rate)1097*4882a593Smuzhiyun inno_hdmi_rk3528_phy_pll_recalc_rate(struct inno_hdmi_phy *inno,
1098*4882a593Smuzhiyun unsigned long parent_rate)
1099*4882a593Smuzhiyun {
1100*4882a593Smuzhiyun unsigned long frac;
1101*4882a593Smuzhiyun u8 nd, no_a, no_b, no_d;
1102*4882a593Smuzhiyun u16 nf;
1103*4882a593Smuzhiyun u64 vco = parent_rate;
1104*4882a593Smuzhiyun
1105*4882a593Smuzhiyun nd = inno_read(inno, 0xa1) & 0x3f;
1106*4882a593Smuzhiyun nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3);
1107*4882a593Smuzhiyun vco *= nf;
1108*4882a593Smuzhiyun if ((inno_read(inno, 0xa2) & 0x30) == 0) {
1109*4882a593Smuzhiyun frac = inno_read(inno, 0xd3) |
1110*4882a593Smuzhiyun (inno_read(inno, 0xd2) << 8) |
1111*4882a593Smuzhiyun (inno_read(inno, 0xd1) << 16);
1112*4882a593Smuzhiyun vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
1113*4882a593Smuzhiyun }
1114*4882a593Smuzhiyun if (inno_read(inno, 0xa0) & 2) {
1115*4882a593Smuzhiyun do_div(vco, nd * 5);
1116*4882a593Smuzhiyun } else {
1117*4882a593Smuzhiyun no_a = inno_read(inno, 0xa5) & 0x1f;
1118*4882a593Smuzhiyun no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2;
1119*4882a593Smuzhiyun no_d = inno_read(inno, 0xa6) & 0x1f;
1120*4882a593Smuzhiyun if (no_a == 1)
1121*4882a593Smuzhiyun do_div(vco, nd * no_b * no_d * 2);
1122*4882a593Smuzhiyun else
1123*4882a593Smuzhiyun do_div(vco, nd * no_a * no_d * 2);
1124*4882a593Smuzhiyun }
1125*4882a593Smuzhiyun
1126*4882a593Smuzhiyun frac = vco;
1127*4882a593Smuzhiyun inno->pixclock = DIV_ROUND_CLOSEST(frac, 1000) * 1000;
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock);
1130*4882a593Smuzhiyun
1131*4882a593Smuzhiyun return frac;
1132*4882a593Smuzhiyun }
1133*4882a593Smuzhiyun
1134*4882a593Smuzhiyun #ifndef CONFIG_SPL_BUILD
1135*4882a593Smuzhiyun #define PHY_TAB_LEN 60
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun static
inno_hdmi_update_phy_table(struct inno_hdmi_phy * inno,u32 * config,struct phy_config * phy_cfg,int phy_table_size)1138*4882a593Smuzhiyun int inno_hdmi_update_phy_table(struct inno_hdmi_phy *inno, u32 *config,
1139*4882a593Smuzhiyun struct phy_config *phy_cfg,
1140*4882a593Smuzhiyun int phy_table_size)
1141*4882a593Smuzhiyun {
1142*4882a593Smuzhiyun int i, j;
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun for (i = 0; i < phy_table_size; i++) {
1145*4882a593Smuzhiyun phy_cfg[i].tmdsclock =
1146*4882a593Smuzhiyun (unsigned long)config[i * 15];
1147*4882a593Smuzhiyun
1148*4882a593Smuzhiyun debug("%ld ", phy_cfg[i].tmdsclock);
1149*4882a593Smuzhiyun for (j = 0; j < 14; j++) {
1150*4882a593Smuzhiyun phy_cfg[i].regs[j] = (u8)config[i * 15 + 1 + j];
1151*4882a593Smuzhiyun debug("0x%02x ", phy_cfg[i].regs[j]);
1152*4882a593Smuzhiyun }
1153*4882a593Smuzhiyun debug("\n");
1154*4882a593Smuzhiyun }
1155*4882a593Smuzhiyun
1156*4882a593Smuzhiyun /*
1157*4882a593Smuzhiyun * The last set of phy cfg is used to indicate whether
1158*4882a593Smuzhiyun * there is no more phy cfg data.
1159*4882a593Smuzhiyun */
1160*4882a593Smuzhiyun phy_cfg[i].tmdsclock = ~0UL;
1161*4882a593Smuzhiyun for (j = 0; j < 14; j++)
1162*4882a593Smuzhiyun phy_cfg[i].regs[j] = 0;
1163*4882a593Smuzhiyun
1164*4882a593Smuzhiyun return 0;
1165*4882a593Smuzhiyun }
1166*4882a593Smuzhiyun #endif
1167*4882a593Smuzhiyun
1168*4882a593Smuzhiyun static const struct inno_hdmi_phy_ops rk3228_hdmi_phy_ops = {
1169*4882a593Smuzhiyun .init = inno_hdmi_phy_rk3228_init,
1170*4882a593Smuzhiyun .power_on = inno_hdmi_phy_rk3228_power_on,
1171*4882a593Smuzhiyun .power_off = inno_hdmi_phy_rk3228_power_off,
1172*4882a593Smuzhiyun .pre_pll_update = inno_hdmi_phy_rk3228_pre_pll_update,
1173*4882a593Smuzhiyun };
1174*4882a593Smuzhiyun
1175*4882a593Smuzhiyun static const struct inno_hdmi_phy_ops rk3328_hdmi_phy_ops = {
1176*4882a593Smuzhiyun .init = inno_hdmi_phy_rk3328_init,
1177*4882a593Smuzhiyun .power_on = inno_hdmi_phy_rk3328_power_on,
1178*4882a593Smuzhiyun .power_off = inno_hdmi_phy_rk3328_power_off,
1179*4882a593Smuzhiyun .pre_pll_update = inno_hdmi_phy_rk3328_pre_pll_update,
1180*4882a593Smuzhiyun .recalc_rate = inno_hdmi_3328_phy_pll_recalc_rate,
1181*4882a593Smuzhiyun };
1182*4882a593Smuzhiyun
1183*4882a593Smuzhiyun static const struct inno_hdmi_phy_ops rk3528_hdmi_phy_ops = {
1184*4882a593Smuzhiyun .init = inno_hdmi_phy_rk3528_init,
1185*4882a593Smuzhiyun .power_on = inno_hdmi_phy_rk3528_power_on,
1186*4882a593Smuzhiyun .power_off = inno_hdmi_phy_rk3528_power_off,
1187*4882a593Smuzhiyun .pre_pll_update = inno_hdmi_phy_rk3528_pre_pll_update,
1188*4882a593Smuzhiyun .recalc_rate = inno_hdmi_rk3528_phy_pll_recalc_rate,
1189*4882a593Smuzhiyun };
1190*4882a593Smuzhiyun
1191*4882a593Smuzhiyun static const struct inno_hdmi_phy_drv_data rk3228_hdmi_phy_drv_data = {
1192*4882a593Smuzhiyun .dev_type = INNO_HDMI_PHY_RK3228,
1193*4882a593Smuzhiyun .ops = &rk3228_hdmi_phy_ops,
1194*4882a593Smuzhiyun .phy_cfg_table = rk3228_phy_cfg,
1195*4882a593Smuzhiyun };
1196*4882a593Smuzhiyun
1197*4882a593Smuzhiyun static const struct inno_hdmi_phy_drv_data rk3328_hdmi_phy_drv_data = {
1198*4882a593Smuzhiyun .dev_type = INNO_HDMI_PHY_RK3328,
1199*4882a593Smuzhiyun .ops = &rk3328_hdmi_phy_ops,
1200*4882a593Smuzhiyun .phy_cfg_table = rk3328_phy_cfg,
1201*4882a593Smuzhiyun };
1202*4882a593Smuzhiyun
1203*4882a593Smuzhiyun static const struct inno_hdmi_phy_drv_data rk3528_hdmi_phy_drv_data = {
1204*4882a593Smuzhiyun .dev_type = INNO_HDMI_PHY_RK3528,
1205*4882a593Smuzhiyun .ops = &rk3528_hdmi_phy_ops,
1206*4882a593Smuzhiyun .phy_cfg_table = rk3528_phy_cfg,
1207*4882a593Smuzhiyun };
1208*4882a593Smuzhiyun
1209*4882a593Smuzhiyun static const struct rockchip_inno_data inno_hdmi_phy_of_match[] = {
1210*4882a593Smuzhiyun { .compatible = "rockchip,rk3228-hdmi-phy",
1211*4882a593Smuzhiyun .data = &rk3228_hdmi_phy_drv_data
1212*4882a593Smuzhiyun },
1213*4882a593Smuzhiyun { .compatible = "rockchip,rk3328-hdmi-phy",
1214*4882a593Smuzhiyun .data = &rk3328_hdmi_phy_drv_data
1215*4882a593Smuzhiyun },
1216*4882a593Smuzhiyun { .compatible = "rockchip,rk3528-hdmi-phy",
1217*4882a593Smuzhiyun .data = &rk3528_hdmi_phy_drv_data
1218*4882a593Smuzhiyun },
1219*4882a593Smuzhiyun {}
1220*4882a593Smuzhiyun };
1221*4882a593Smuzhiyun
inno_hdmi_phy_init(struct rockchip_phy * phy)1222*4882a593Smuzhiyun static int inno_hdmi_phy_init(struct rockchip_phy *phy)
1223*4882a593Smuzhiyun {
1224*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1225*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
1226*4882a593Smuzhiyun #else
1227*4882a593Smuzhiyun struct udevice *dev = phy->dev;
1228*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
1229*4882a593Smuzhiyun int val, phy_table_size, ret;
1230*4882a593Smuzhiyun u32 *phy_config;
1231*4882a593Smuzhiyun #endif
1232*4882a593Smuzhiyun int i;
1233*4882a593Smuzhiyun const char *name;
1234*4882a593Smuzhiyun
1235*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1236*4882a593Smuzhiyun inno->regs = (void *)RK3528_HDMIPHY_BASE;
1237*4882a593Smuzhiyun #else
1238*4882a593Smuzhiyun inno->regs = dev_read_addr_ptr(dev);
1239*4882a593Smuzhiyun inno->node = dev->node;
1240*4882a593Smuzhiyun #endif
1241*4882a593Smuzhiyun if (!inno->regs) {
1242*4882a593Smuzhiyun printf("%s: failed to get phy address\n", __func__);
1243*4882a593Smuzhiyun return -ENOMEM;
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun
1246*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1247*4882a593Smuzhiyun name = "rockchip,rk3528-hdmi-phy";
1248*4882a593Smuzhiyun #else
1249*4882a593Smuzhiyun name = dev_read_string(dev, "compatible");
1250*4882a593Smuzhiyun #endif
1251*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(inno_hdmi_phy_of_match); i++) {
1252*4882a593Smuzhiyun if (!strcmp(name, inno_hdmi_phy_of_match[i].compatible)) {
1253*4882a593Smuzhiyun inno->plat_data = inno_hdmi_phy_of_match[i].data;
1254*4882a593Smuzhiyun break;
1255*4882a593Smuzhiyun }
1256*4882a593Smuzhiyun }
1257*4882a593Smuzhiyun
1258*4882a593Smuzhiyun #ifndef CONFIG_SPL_BUILD
1259*4882a593Smuzhiyun dev_read_prop(dev, "rockchip,phy-table", &val);
1260*4882a593Smuzhiyun
1261*4882a593Smuzhiyun if (val >= 0) {
1262*4882a593Smuzhiyun if (val % PHY_TAB_LEN || !val) {
1263*4882a593Smuzhiyun printf("Invalid phy cfg table format!\n");
1264*4882a593Smuzhiyun return -EINVAL;
1265*4882a593Smuzhiyun }
1266*4882a593Smuzhiyun
1267*4882a593Smuzhiyun phy_config = malloc(val);
1268*4882a593Smuzhiyun if (!phy_config) {
1269*4882a593Smuzhiyun printf("kmalloc phy table failed\n");
1270*4882a593Smuzhiyun return -ENOMEM;
1271*4882a593Smuzhiyun }
1272*4882a593Smuzhiyun
1273*4882a593Smuzhiyun phy_table_size = val / PHY_TAB_LEN;
1274*4882a593Smuzhiyun /* Effective phy cfg data and the end of phy cfg table */
1275*4882a593Smuzhiyun inno->phy_cfg = malloc(val + PHY_TAB_LEN);
1276*4882a593Smuzhiyun if (!inno->phy_cfg) {
1277*4882a593Smuzhiyun free(phy_config);
1278*4882a593Smuzhiyun return -ENOMEM;
1279*4882a593Smuzhiyun }
1280*4882a593Smuzhiyun
1281*4882a593Smuzhiyun dev_read_u32_array(dev, "rockchip,phy-table",
1282*4882a593Smuzhiyun phy_config, val / sizeof(u32));
1283*4882a593Smuzhiyun ret = inno_hdmi_update_phy_table(inno, phy_config,
1284*4882a593Smuzhiyun inno->phy_cfg,
1285*4882a593Smuzhiyun phy_table_size);
1286*4882a593Smuzhiyun if (ret) {
1287*4882a593Smuzhiyun free(phy_config);
1288*4882a593Smuzhiyun return ret;
1289*4882a593Smuzhiyun }
1290*4882a593Smuzhiyun free(phy_config);
1291*4882a593Smuzhiyun } else {
1292*4882a593Smuzhiyun printf("use default hdmi phy table\n");
1293*4882a593Smuzhiyun }
1294*4882a593Smuzhiyun #endif
1295*4882a593Smuzhiyun
1296*4882a593Smuzhiyun if (i >= ARRAY_SIZE(inno_hdmi_phy_of_match))
1297*4882a593Smuzhiyun return 0;
1298*4882a593Smuzhiyun
1299*4882a593Smuzhiyun if (!inno->plat_data || !inno->plat_data->ops)
1300*4882a593Smuzhiyun return -EINVAL;
1301*4882a593Smuzhiyun
1302*4882a593Smuzhiyun if (inno->plat_data->ops->init)
1303*4882a593Smuzhiyun inno->plat_data->ops->init(inno);
1304*4882a593Smuzhiyun
1305*4882a593Smuzhiyun return 0;
1306*4882a593Smuzhiyun }
1307*4882a593Smuzhiyun
inno_hdmi_phy_set_pll(struct rockchip_phy * phy,unsigned long rate)1308*4882a593Smuzhiyun static unsigned long inno_hdmi_phy_set_pll(struct rockchip_phy *phy,
1309*4882a593Smuzhiyun unsigned long rate)
1310*4882a593Smuzhiyun {
1311*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1312*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
1313*4882a593Smuzhiyun #else
1314*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
1315*4882a593Smuzhiyun #endif
1316*4882a593Smuzhiyun
1317*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1318*4882a593Smuzhiyun if (!inno)
1319*4882a593Smuzhiyun inno = g_inno;
1320*4882a593Smuzhiyun #endif
1321*4882a593Smuzhiyun inno_hdmi_phy_clk_prepare(inno);
1322*4882a593Smuzhiyun inno_hdmi_phy_clk_is_prepared(inno);
1323*4882a593Smuzhiyun inno_hdmi_phy_clk_set_rate(inno, rate);
1324*4882a593Smuzhiyun return 0;
1325*4882a593Smuzhiyun }
1326*4882a593Smuzhiyun
1327*4882a593Smuzhiyun static int
inno_hdmi_phy_set_bus_width(struct rockchip_phy * phy,u32 bus_width)1328*4882a593Smuzhiyun inno_hdmi_phy_set_bus_width(struct rockchip_phy *phy, u32 bus_width)
1329*4882a593Smuzhiyun {
1330*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1331*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
1332*4882a593Smuzhiyun #else
1333*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
1334*4882a593Smuzhiyun #endif
1335*4882a593Smuzhiyun
1336*4882a593Smuzhiyun inno->bus_width = bus_width;
1337*4882a593Smuzhiyun
1338*4882a593Smuzhiyun return 0;
1339*4882a593Smuzhiyun }
1340*4882a593Smuzhiyun
1341*4882a593Smuzhiyun static long
inno_hdmi_phy_clk_round_rate(struct rockchip_phy * phy,unsigned long rate)1342*4882a593Smuzhiyun inno_hdmi_phy_clk_round_rate(struct rockchip_phy *phy, unsigned long rate)
1343*4882a593Smuzhiyun {
1344*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
1345*4882a593Smuzhiyun struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data;
1346*4882a593Smuzhiyun #else
1347*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
1348*4882a593Smuzhiyun #endif
1349*4882a593Smuzhiyun int i;
1350*4882a593Smuzhiyun const struct pre_pll_config *cfg = pre_pll_cfg_table;
1351*4882a593Smuzhiyun u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
1352*4882a593Smuzhiyun
1353*4882a593Smuzhiyun for (; cfg->pixclock != ~0UL; cfg++)
1354*4882a593Smuzhiyun if (cfg->pixclock == rate)
1355*4882a593Smuzhiyun break;
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun /*
1358*4882a593Smuzhiyun * XXX: Limit pixel clock under 600MHz
1359*4882a593Smuzhiyun * rk3228 does not support non-zero fracdiv
1360*4882a593Smuzhiyun */
1361*4882a593Smuzhiyun if ((inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228 &&
1362*4882a593Smuzhiyun cfg->fracdiv) || cfg->pixclock > 600000000)
1363*4882a593Smuzhiyun return -EINVAL;
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun /*
1366*4882a593Smuzhiyun * If there is no dts phy cfg table, use default phy cfg table.
1367*4882a593Smuzhiyun * The tmds clock maximum is 594MHz. So there is no need to check
1368*4882a593Smuzhiyun * whether tmds clock is out of range.
1369*4882a593Smuzhiyun */
1370*4882a593Smuzhiyun if (!inno->phy_cfg)
1371*4882a593Smuzhiyun return cfg->pixclock;
1372*4882a593Smuzhiyun
1373*4882a593Smuzhiyun /* Check if tmds clock is out of dts phy config's range. */
1374*4882a593Smuzhiyun for (i = 0; inno->phy_cfg[i].tmdsclock != ~0UL; i++) {
1375*4882a593Smuzhiyun if (inno->phy_cfg[i].tmdsclock >= tmdsclock)
1376*4882a593Smuzhiyun break;
1377*4882a593Smuzhiyun }
1378*4882a593Smuzhiyun
1379*4882a593Smuzhiyun if (inno->phy_cfg[i].tmdsclock == ~0UL)
1380*4882a593Smuzhiyun return -EINVAL;
1381*4882a593Smuzhiyun
1382*4882a593Smuzhiyun return cfg->pixclock;
1383*4882a593Smuzhiyun }
1384*4882a593Smuzhiyun
1385*4882a593Smuzhiyun const struct rockchip_phy_funcs inno_hdmi_phy_funcs = {
1386*4882a593Smuzhiyun .init = inno_hdmi_phy_init,
1387*4882a593Smuzhiyun .power_on = inno_hdmi_phy_power_on,
1388*4882a593Smuzhiyun .power_off = inno_hdmi_phy_power_off,
1389*4882a593Smuzhiyun .set_pll = inno_hdmi_phy_set_pll,
1390*4882a593Smuzhiyun .set_bus_width = inno_hdmi_phy_set_bus_width,
1391*4882a593Smuzhiyun .round_rate = inno_hdmi_phy_clk_round_rate,
1392*4882a593Smuzhiyun };
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun static struct rockchip_phy inno_hdmi_phy_driver_data = {
1395*4882a593Smuzhiyun .funcs = &inno_hdmi_phy_funcs,
1396*4882a593Smuzhiyun };
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun static const struct udevice_id inno_hdmi_phy_ids[] = {
1399*4882a593Smuzhiyun {
1400*4882a593Smuzhiyun .compatible = "rockchip,rk3328-hdmi-phy",
1401*4882a593Smuzhiyun .data = (ulong)&inno_hdmi_phy_driver_data,
1402*4882a593Smuzhiyun },
1403*4882a593Smuzhiyun {
1404*4882a593Smuzhiyun .compatible = "rockchip,rk3228-hdmi-phy",
1405*4882a593Smuzhiyun .data = (ulong)&inno_hdmi_phy_driver_data,
1406*4882a593Smuzhiyun },
1407*4882a593Smuzhiyun {
1408*4882a593Smuzhiyun .compatible = "rockchip,rk3528-hdmi-phy",
1409*4882a593Smuzhiyun .data = (ulong)&inno_hdmi_phy_driver_data,
1410*4882a593Smuzhiyun },
1411*4882a593Smuzhiyun {}
1412*4882a593Smuzhiyun };
1413*4882a593Smuzhiyun
1414*4882a593Smuzhiyun #ifdef CONFIG_SPL_BUILD
inno_spl_hdmi_phy_probe(struct display_state * state)1415*4882a593Smuzhiyun int inno_spl_hdmi_phy_probe(struct display_state *state)
1416*4882a593Smuzhiyun {
1417*4882a593Smuzhiyun struct inno_hdmi_phy *inno = malloc(sizeof(struct inno_hdmi_phy));
1418*4882a593Smuzhiyun
1419*4882a593Smuzhiyun memset(inno, 0, sizeof(*inno));
1420*4882a593Smuzhiyun g_inno = inno;
1421*4882a593Smuzhiyun
1422*4882a593Smuzhiyun state->conn_state.connector->phy = &inno_hdmi_phy_driver_data;
1423*4882a593Smuzhiyun state->conn_state.connector->phy->data = (void *)inno;
1424*4882a593Smuzhiyun return 0;
1425*4882a593Smuzhiyun }
1426*4882a593Smuzhiyun #else
inno_hdmi_phy_probe(struct udevice * dev)1427*4882a593Smuzhiyun static int inno_hdmi_phy_probe(struct udevice *dev)
1428*4882a593Smuzhiyun {
1429*4882a593Smuzhiyun struct inno_hdmi_phy *inno = dev_get_priv(dev);
1430*4882a593Smuzhiyun struct rockchip_phy *phy =
1431*4882a593Smuzhiyun (struct rockchip_phy *)dev_get_driver_data(dev);
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun inno->dev = dev;
1434*4882a593Smuzhiyun phy->dev = dev;
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun g_inno = inno;
1437*4882a593Smuzhiyun dev->driver_data = (ulong)&inno_hdmi_phy_driver_data;
1438*4882a593Smuzhiyun phy = &inno_hdmi_phy_driver_data;
1439*4882a593Smuzhiyun
1440*4882a593Smuzhiyun return 0;
1441*4882a593Smuzhiyun }
1442*4882a593Smuzhiyun #endif
1443*4882a593Smuzhiyun
rockchip_inno_phy_hdmi_bind(struct udevice * parent)1444*4882a593Smuzhiyun static int rockchip_inno_phy_hdmi_bind(struct udevice *parent)
1445*4882a593Smuzhiyun {
1446*4882a593Smuzhiyun struct udevice *child;
1447*4882a593Smuzhiyun ofnode subnode;
1448*4882a593Smuzhiyun int ret;
1449*4882a593Smuzhiyun
1450*4882a593Smuzhiyun subnode = ofnode_find_subnode(parent->node, "clk-port");
1451*4882a593Smuzhiyun if (!ofnode_valid(subnode)) {
1452*4882a593Smuzhiyun printf("%s: no subnode for %s\n", __func__, parent->name);
1453*4882a593Smuzhiyun return -ENXIO;
1454*4882a593Smuzhiyun }
1455*4882a593Smuzhiyun
1456*4882a593Smuzhiyun ret = device_bind_driver_to_node(parent, "clk_inno_hdmi", "inno_hdmi_pll_clk", subnode, &child);
1457*4882a593Smuzhiyun if (ret) {
1458*4882a593Smuzhiyun printf("%s: clk-port cannot bind its driver\n", __func__);
1459*4882a593Smuzhiyun return ret;
1460*4882a593Smuzhiyun }
1461*4882a593Smuzhiyun
1462*4882a593Smuzhiyun return 0;
1463*4882a593Smuzhiyun }
1464*4882a593Smuzhiyun
1465*4882a593Smuzhiyun U_BOOT_DRIVER(inno_hdmi_phy) = {
1466*4882a593Smuzhiyun .name = "inno_hdmi_phy",
1467*4882a593Smuzhiyun .id = UCLASS_PHY,
1468*4882a593Smuzhiyun .of_match = inno_hdmi_phy_ids,
1469*4882a593Smuzhiyun #ifndef CONFIG_SPL_BUILD
1470*4882a593Smuzhiyun .probe = inno_hdmi_phy_probe,
1471*4882a593Smuzhiyun #endif
1472*4882a593Smuzhiyun .bind = rockchip_inno_phy_hdmi_bind,
1473*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct inno_hdmi_phy),
1474*4882a593Smuzhiyun };
1475*4882a593Smuzhiyun
1476*4882a593Smuzhiyun
inno_hdmi_clk_get_rate(struct clk * clk)1477*4882a593Smuzhiyun static ulong inno_hdmi_clk_get_rate(struct clk *clk)
1478*4882a593Smuzhiyun {
1479*4882a593Smuzhiyun struct clk_inno_hdmi *priv = dev_get_priv(clk->dev);
1480*4882a593Smuzhiyun
1481*4882a593Smuzhiyun return priv->rate;
1482*4882a593Smuzhiyun }
1483*4882a593Smuzhiyun
inno_hdmi_clk_set_rate(struct clk * clk,ulong rate)1484*4882a593Smuzhiyun static ulong inno_hdmi_clk_set_rate(struct clk *clk, ulong rate)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun struct clk_inno_hdmi *priv = dev_get_priv(clk->dev);
1487*4882a593Smuzhiyun int ret;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun inno_hdmi_phy_clk_prepare(g_inno);
1490*4882a593Smuzhiyun inno_hdmi_phy_clk_is_prepared(g_inno);
1491*4882a593Smuzhiyun ret = inno_hdmi_phy_clk_set_rate(g_inno, rate);
1492*4882a593Smuzhiyun if (ret < 0) {
1493*4882a593Smuzhiyun printf("inno hdmi set rate failed ret:%d\n", ret);
1494*4882a593Smuzhiyun return ret;
1495*4882a593Smuzhiyun }
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun priv->rate = g_inno->pixclock;
1498*4882a593Smuzhiyun
1499*4882a593Smuzhiyun return priv->rate;
1500*4882a593Smuzhiyun }
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun static const struct clk_ops inno_hdmi_clk_ops = {
1503*4882a593Smuzhiyun .get_rate = inno_hdmi_clk_get_rate,
1504*4882a593Smuzhiyun .set_rate = inno_hdmi_clk_set_rate,
1505*4882a593Smuzhiyun };
1506*4882a593Smuzhiyun
inno_hdmi_clk_probe(struct udevice * dev)1507*4882a593Smuzhiyun static int inno_hdmi_clk_probe(struct udevice *dev)
1508*4882a593Smuzhiyun {
1509*4882a593Smuzhiyun return 0;
1510*4882a593Smuzhiyun }
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun /*
1513*4882a593Smuzhiyun * In order for other display interfaces to use hdmiphy as source
1514*4882a593Smuzhiyun * for dclk, hdmiphy must register a virtual clock driver
1515*4882a593Smuzhiyun */
1516*4882a593Smuzhiyun U_BOOT_DRIVER(clk_inno_hdmi) = {
1517*4882a593Smuzhiyun .name = "clk_inno_hdmi",
1518*4882a593Smuzhiyun .id = UCLASS_CLK,
1519*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct clk_inno_hdmi),
1520*4882a593Smuzhiyun .ops = &inno_hdmi_clk_ops,
1521*4882a593Smuzhiyun .probe = inno_hdmi_clk_probe,
1522*4882a593Smuzhiyun };
1523