xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_lvds.c (revision 9ac6f4797ca1f9a9c5c1f4bdb8414fefa85f8bd3)
1 /*
2  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <config.h>
8 #include <common.h>
9 #include <errno.h>
10 #include <malloc.h>
11 #include <fdtdec.h>
12 #include <fdt_support.h>
13 #include <asm/unaligned.h>
14 #include <linux/list.h>
15 #include <asm/io.h>
16 #include <dm/device.h>
17 #include <syscon.h>
18 #include <asm/arch-rockchip/clock.h>
19 #include <asm/gpio.h>
20 
21 
22 #include "rockchip_display.h"
23 #include "rockchip_crtc.h"
24 #include "rockchip_connector.h"
25 #include "rockchip_lvds.h"
26 
27 enum rockchip_lvds_sub_devtype {
28 	RK3288_LVDS,
29 	RK3366_LVDS,
30 	RK3368_LVDS,
31 };
32 
33 struct rockchip_lvds_chip_data {
34 	u32	chip_type;
35 	bool	has_vop_sel;
36 	u32	grf_soc_con5;
37 	u32	grf_soc_con6;
38 	u32	grf_soc_con7;
39 	u32	grf_soc_con15;
40 	u32	grf_gpio1d_iomux;
41 };
42 
43 struct rockchip_lvds_device {
44 	u32	regbase;
45 	void	*grf;
46 	u32	ctrl_reg;
47 	u32	channel;
48 	u32	output;
49 	u32	format;
50 	struct drm_display_mode *mode;
51 	const struct rockchip_lvds_chip_data *pdata;
52 };
53 
54 static inline int lvds_name_to_format(const char *s)
55 {
56 	if (!s)
57 		return -EINVAL;
58 
59 	if (strncmp(s, "jeida", 6) == 0)
60 		return LVDS_FORMAT_JEIDA;
61 	else if (strncmp(s, "vesa", 5) == 0)
62 		return LVDS_FORMAT_VESA;
63 
64 	return -EINVAL;
65 }
66 
67 static inline int lvds_name_to_output(const char *s)
68 {
69 	if (!s)
70 		return -EINVAL;
71 
72 	if (strncmp(s, "rgb", 3) == 0)
73 		return DISPLAY_OUTPUT_RGB;
74 	else if (strncmp(s, "lvds", 4) == 0)
75 		return DISPLAY_OUTPUT_LVDS;
76 	else if (strncmp(s, "duallvds", 8) == 0)
77 		return DISPLAY_OUTPUT_DUAL_LVDS;
78 
79 	return -EINVAL;
80 }
81 
82 static inline void lvds_writel(struct rockchip_lvds_device *lvds,
83 			      u32 offset, u32 val)
84 {
85 	writel(val, lvds->regbase + offset);
86 
87 	if ((lvds->pdata->chip_type == RK3288_LVDS) &&
88 	    (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS))
89 		writel(val, lvds->regbase + offset + 0x100);
90 }
91 
92 static inline void lvds_msk_reg(struct rockchip_lvds_device *lvds, u32 offset,
93 			       u32 msk, u32 val)
94 {
95 	u32 temp;
96 
97 	temp = readl(lvds->regbase + offset) & (0xFF - (msk));
98 	writel(temp | ((val) & (msk)), lvds->regbase + offset);
99 }
100 
101 static inline u32 lvds_readl(struct rockchip_lvds_device *lvds, u32 offset)
102 {
103 	return readl(lvds->regbase + offset);
104 }
105 
106 static inline void lvds_ctrl_writel(struct rockchip_lvds_device *lvds,
107 				   u32 offset, u32 val)
108 {
109 	writel(val, lvds->ctrl_reg + offset);
110 }
111 
112 static inline u32 lvds_pmugrf_readl(u32 offset)
113 {
114 	return readl(LVDS_PMUGRF_BASE + offset);
115 }
116 
117 static inline void lvds_pmugrf_writel(u32 offset, u32 val)
118 {
119 	writel(val, LVDS_PMUGRF_BASE + offset);
120 }
121 
122 static inline u32 lvds_phy_lock(struct rockchip_lvds_device *lvds)
123 {
124 	u32 val = 0;
125 	val = readl(lvds->ctrl_reg + 0x10);
126 	val &= 0x1;
127 	return val;
128 }
129 
130 static int rockchip_lvds_clk_enable(struct rockchip_lvds_device *lvds)
131 {
132 	return 0;
133 }
134 
135 const struct rockchip_lvds_chip_data rk3366_lvds_drv_data = {
136 	.chip_type = RK3366_LVDS,
137 	.grf_soc_con7  = RK3366_GRF_SOC_CON5,
138 	.grf_soc_con15 = RK3366_GRF_SOC_CON6,
139 	.has_vop_sel = true,
140 };
141 
142 const struct rockchip_lvds_chip_data rk3368_lvds_drv_data = {
143 	.chip_type = RK3368_LVDS,
144 	.grf_soc_con7  = RK3368_GRF_SOC_CON7,
145 	.grf_soc_con15 = RK3368_GRF_SOC_CON15,
146 	.has_vop_sel = false,
147 };
148 
149 const struct rockchip_lvds_chip_data rk3288_lvds_drv_data = {
150 	.chip_type = RK3288_LVDS,
151 	.has_vop_sel = true,
152 	.grf_soc_con6 = 0x025c,
153 	.grf_soc_con7 = 0x0260,
154 	.grf_gpio1d_iomux = 0x000c,
155 };
156 
157 static int rk336x_lvds_pwr_off(struct display_state *state)
158 {
159 	struct connector_state *conn_state = &state->conn_state;
160 	struct rockchip_lvds_device *lvds = conn_state->private;
161 
162 	/* disable lvds lane and power off pll */
163 	lvds_writel(lvds, MIPIPHY_REGEB,
164 		    v_LANE0_EN(0) | v_LANE1_EN(0) | v_LANE2_EN(0) |
165 		    v_LANE3_EN(0) | v_LANECLK_EN(0) | v_PLL_PWR_OFF(1));
166 
167 	/* power down lvds pll and bandgap */
168 	lvds_msk_reg(lvds, MIPIPHY_REG1,
169 		     m_SYNC_RST | m_LDO_PWR_DOWN | m_PLL_PWR_DOWN,
170 		     v_SYNC_RST(1) | v_LDO_PWR_DOWN(1) | v_PLL_PWR_DOWN(1));
171 
172 	/* disable lvds */
173 	lvds_msk_reg(lvds, MIPIPHY_REGE3, m_LVDS_EN | m_TTL_EN,
174 		     v_LVDS_EN(0) | v_TTL_EN(0));
175 
176 	return 0;
177 }
178 
179 static int rk3288_lvds_pwr_off(struct display_state *state)
180 {
181 	struct connector_state *conn_state = &state->conn_state;
182 	struct rockchip_lvds_device *lvds = conn_state->private;
183 
184 	lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_DISABLE);
185 	lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_DISABLE);
186 
187 	writel(0xffff8000, lvds->grf + lvds->pdata->grf_soc_con7);
188 
189 	return 0;
190 }
191 
192 static int rk336x_lvds_pwr_on(struct display_state *state)
193 {
194 	struct connector_state *conn_state = &state->conn_state;
195 	struct rockchip_lvds_device *lvds = conn_state->private;
196 	u32 delay_times = 20;
197 
198 	if (lvds->output == DISPLAY_OUTPUT_LVDS) {
199 		/* set VOCM 900 mv and V-DIFF 350 mv */
200 		lvds_msk_reg(lvds, MIPIPHY_REGE4, m_VOCM | m_DIFF_V,
201 			     v_VOCM(0) | v_DIFF_V(2));
202 		/* power up lvds pll and ldo */
203 		lvds_msk_reg(lvds, MIPIPHY_REG1,
204 			     m_SYNC_RST | m_LDO_PWR_DOWN | m_PLL_PWR_DOWN,
205 			     v_SYNC_RST(0) | v_LDO_PWR_DOWN(0) |
206 			     v_PLL_PWR_DOWN(0));
207 		/* enable lvds lane and power on pll */
208 		lvds_writel(lvds, MIPIPHY_REGEB,
209 			    v_LANE0_EN(1) | v_LANE1_EN(1) | v_LANE2_EN(1) |
210 			    v_LANE3_EN(1) | v_LANECLK_EN(1) | v_PLL_PWR_OFF(0));
211 
212 		/* enable lvds */
213 		lvds_msk_reg(lvds, MIPIPHY_REGE3,
214 			     m_MIPI_EN | m_LVDS_EN | m_TTL_EN,
215 			     v_MIPI_EN(0) | v_LVDS_EN(1) | v_TTL_EN(0));
216 	} else {
217 		lvds_msk_reg(lvds, MIPIPHY_REGE3,
218 			     m_MIPI_EN | m_LVDS_EN | m_TTL_EN,
219 			     v_MIPI_EN(0) | v_LVDS_EN(0) | v_TTL_EN(1));
220 	}
221 	/* delay for waitting pll lock on */
222 	while (delay_times--) {
223 		if (lvds_phy_lock(lvds))
224 			break;
225 		udelay(100);
226 	}
227 
228 	if (delay_times <= 0)
229 		printf("wait lvds phy lock failed, please check the hardware!\n");
230 
231 	return 0;
232 }
233 
234 
235 static void rk336x_output_ttl(struct display_state *state)
236 {
237 	struct connector_state *conn_state = &state->conn_state;
238 	struct rockchip_lvds_device *lvds = conn_state->private;
239 	u32 val = 0;
240 
241 	/* iomux to lcdc */
242 	if (lvds->pdata->chip_type == RK3368_LVDS) {
243 		/* lcdc data 11 10 */
244 		lvds_pmugrf_writel(0x04, 0xf0005000);
245 		/* lcdc data 12 13 14 15 16 17 18 19 */
246 		lvds_pmugrf_writel(0x08, 0xFFFF5555);
247 		/* lcdc data 20 21 22 23 HSYNC VSYNC DEN DCLK */
248 		lvds_pmugrf_writel(0x0c, 0xFFFF5555);
249 		/* set clock lane enable */
250 		lvds_ctrl_writel(lvds, 0x0, 0x4);
251 	} else {
252 		/* lcdc data 15 ... 10, vsync, hsync */
253 		lvds_pmugrf_writel(0x0c, 0xffff555a);
254 		/* lcdc data 23 ... 16 */
255 		lvds_pmugrf_writel(0x30, 0xffff5555);
256 		/* lcdc dclk, den */
257 		lvds_pmugrf_writel(0x34, 0x000f0005);
258 	}
259 
260 	/* enable lvds mode */
261 	val = v_RK336X_LVDSMODE_EN(0) | v_RK336X_MIPIPHY_TTL_EN(1) |
262 		v_RK336X_MIPIPHY_LANE0_EN(1) |
263 		v_RK336X_MIPIDPI_FORCEX_EN(1);
264 	writel(val, lvds->grf + lvds->pdata->grf_soc_con7);
265 	val = v_RK336X_FORCE_JETAG(0);
266 	writel(val, lvds->grf + lvds->pdata->grf_soc_con15);
267 
268 	/* enable lane */
269 	lvds_writel(lvds, MIPIPHY_REG0, 0x7f);
270 	val = v_LANE0_EN(1) | v_LANE1_EN(1) | v_LANE2_EN(1) | v_LANE3_EN(1) |
271 		v_LANECLK_EN(1) | v_PLL_PWR_OFF(1);
272 	lvds_writel(lvds, MIPIPHY_REGEB, val);
273 
274 	/* set ttl mode and reset phy config */
275 	val = v_LVDS_MODE_EN(0) | v_TTL_MODE_EN(1) | v_MIPI_MODE_EN(0) |
276 		v_MSB_SEL(1) | v_DIG_INTER_RST(1);
277 	lvds_writel(lvds, MIPIPHY_REGE0, val);
278 
279 	rk336x_lvds_pwr_on(state);
280 }
281 
282 static void rk336x_output_lvds(struct display_state *state)
283 {
284 	struct connector_state *conn_state = &state->conn_state;
285 	struct rockchip_lvds_device *lvds = conn_state->private;
286 	u32 val = 0;
287 
288 	/* enable lvds mode */
289 	val |= v_RK336X_LVDSMODE_EN(1) | v_RK336X_MIPIPHY_TTL_EN(0);
290 	/* config lvds_format */
291 	val |= v_RK336X_LVDS_OUTPUT_FORMAT(lvds->format);
292 	/* LSB receive mode */
293 	val |= v_RK336X_LVDS_MSBSEL(LVDS_MSB_D7);
294 	val |= v_RK336X_MIPIPHY_LANE0_EN(1) |
295 	       v_RK336X_MIPIDPI_FORCEX_EN(1);
296 	writel(val, lvds->grf + lvds->pdata->grf_soc_con7);
297 	/* digital internal disable */
298 	lvds_msk_reg(lvds, MIPIPHY_REGE1, m_DIG_INTER_EN, v_DIG_INTER_EN(0));
299 
300 	/* set pll prediv and fbdiv */
301 	lvds_writel(lvds, MIPIPHY_REG3, v_PREDIV(2) | v_FBDIV_MSB(0));
302 	lvds_writel(lvds, MIPIPHY_REG4, v_FBDIV_LSB(28));
303 
304 	lvds_writel(lvds, MIPIPHY_REGE8, 0xfc);
305 
306 	/* set lvds mode and reset phy config */
307 	lvds_msk_reg(lvds, MIPIPHY_REGE0,
308 		     m_MSB_SEL | m_DIG_INTER_RST,
309 		     v_MSB_SEL(1) | v_DIG_INTER_RST(1));
310 
311 	rk336x_lvds_pwr_on(state);
312 	lvds_msk_reg(lvds, MIPIPHY_REGE1, m_DIG_INTER_EN, v_DIG_INTER_EN(1));
313 }
314 
315 static int rk3288_lvds_pwr_on(struct display_state *state)
316 {
317 	struct connector_state *conn_state = &state->conn_state;
318 	struct rockchip_lvds_device *lvds = conn_state->private;
319 	struct drm_display_mode *mode = &conn_state->mode;
320 	u32 val;
321 	u32 h_bp = mode->htotal - mode->hsync_start;
322 	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
323 	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
324 
325 	val = lvds->format;
326 	if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
327 		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
328 	else if (lvds->output == DISPLAY_OUTPUT_LVDS)
329 		val |= LVDS_CH0_EN;
330 	else if (lvds->output == DISPLAY_OUTPUT_RGB)
331 		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
332 
333 	if (h_bp & 0x01)
334 		val |= LVDS_START_PHASE_RST_1;
335 
336 	val |= (pin_dclk << 8) | (pin_hsync << 9);
337 	val |= (0xffff << 16);
338 	writel(val, lvds->grf + lvds->pdata->grf_soc_con7);
339 
340 	return 0;
341 }
342 
343 static void rk3288_output_ttl(struct display_state *state)
344 {
345 	struct connector_state *conn_state = &state->conn_state;
346 	struct rockchip_lvds_device *lvds = conn_state->private;
347 
348 	rk3288_lvds_pwr_on(state);
349 	/* iomux: dclk den hsync vsync */
350 	writel(0x00550055, lvds->grf + lvds->pdata->grf_gpio1d_iomux);
351 	lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
352 		    RK3288_LVDS_CH0_REG0_TTL_EN |
353 		    RK3288_LVDS_CH0_REG0_LANECK_EN |
354 		    RK3288_LVDS_CH0_REG0_LANE4_EN |
355 		    RK3288_LVDS_CH0_REG0_LANE3_EN |
356 		    RK3288_LVDS_CH0_REG0_LANE2_EN |
357 		    RK3288_LVDS_CH0_REG0_LANE1_EN |
358 		    RK3288_LVDS_CH0_REG0_LANE0_EN);
359 	lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
360 		    RK3288_LVDS_PLL_FBDIV_REG2(0x46));
361 
362 	lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
363 		    RK3288_LVDS_PLL_FBDIV_REG3(0x46));
364 	lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
365 		    RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
366 		    RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
367 		    RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
368 		    RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
369 		    RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
370 		    RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
371 	lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
372 		    RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
373 		    RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
374 		    RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
375 		    RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
376 		    RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
377 		    RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
378 	lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
379 		    RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
380 	lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
381 		    RK3288_LVDS_CH0_REG20_LSB);
382 
383 	lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
384 	lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
385 }
386 
387 static void rk3288_output_lvds(struct display_state *state)
388 {
389 	struct connector_state *conn_state = &state->conn_state;
390 	struct rockchip_lvds_device *lvds = conn_state->private;
391 
392 	rk3288_lvds_pwr_on(state);
393 
394 	lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
395 		    RK3288_LVDS_CH0_REG0_LVDS_EN |
396 		    RK3288_LVDS_CH0_REG0_LANECK_EN |
397 		    RK3288_LVDS_CH0_REG0_LANE4_EN |
398 		    RK3288_LVDS_CH0_REG0_LANE3_EN |
399 		    RK3288_LVDS_CH0_REG0_LANE2_EN |
400 		    RK3288_LVDS_CH0_REG0_LANE1_EN |
401 		    RK3288_LVDS_CH0_REG0_LANE0_EN);
402 	lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
403 		    RK3288_LVDS_CH0_REG1_LANECK_BIAS |
404 		    RK3288_LVDS_CH0_REG1_LANE4_BIAS |
405 		    RK3288_LVDS_CH0_REG1_LANE3_BIAS |
406 		    RK3288_LVDS_CH0_REG1_LANE2_BIAS |
407 		    RK3288_LVDS_CH0_REG1_LANE1_BIAS |
408 		    RK3288_LVDS_CH0_REG1_LANE0_BIAS);
409 	lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
410 		    RK3288_LVDS_CH0_REG2_RESERVE_ON |
411 		    RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
412 		    RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
413 		    RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
414 		    RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
415 		    RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
416 		    RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
417 		    RK3288_LVDS_PLL_FBDIV_REG2(0x46));
418 	lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
419 		    RK3288_LVDS_PLL_FBDIV_REG3(0x46));
420 	lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
421 	lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
422 	lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
423 		    RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
424 	lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
425 		    RK3288_LVDS_CH0_REG20_LSB);
426 
427 	lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE);
428 	lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE);
429 }
430 
431 static int rockchip_lvds_init(struct display_state *state)
432 {
433 	struct connector_state *conn_state = &state->conn_state;
434 	const struct rockchip_connector *connector = conn_state->connector;
435 	const struct rockchip_lvds_chip_data *pdata = connector->data;
436 	int lvds_node = conn_state->node;
437 	struct rockchip_lvds_device *lvds;
438 	const char *name;
439 	int i;
440 	struct fdt_resource lvds_phy, lvds_ctrl;
441 	struct panel_state *panel_state = &state->panel_state;
442 	int panel_node = panel_state->node;
443 
444 	lvds = malloc(sizeof(*lvds));
445 	if (!lvds)
446 		return -ENOMEM;
447 	lvds->pdata = pdata;
448 
449 	if (pdata->chip_type == RK3288_LVDS) {
450 		lvds->regbase = (u32)fdtdec_get_addr_size_auto_noparent(state->blob,
451 						lvds_node, "reg", 0, NULL, false);
452 	} else {
453 		i = fdt_get_named_resource(state->blob, lvds_node, "reg", "reg-names",
454 					   "mipi_lvds_phy", &lvds_phy);
455 		if (i) {
456 			printf("can't get regs lvds_phy addresses!\n");
457 			free(lvds);
458 			return -ENOMEM;
459 		}
460 
461 		i = fdt_get_named_resource(state->blob, lvds_node, "reg", "reg-names",
462 					   "mipi_lvds_ctl", &lvds_ctrl);
463 		if (i) {
464 			printf("can't get regs lvds_ctrl addresses!\n");
465 			free(lvds);
466 			return -ENOMEM;
467 		}
468 
469 		lvds->regbase = lvds_phy.start;
470 		lvds->ctrl_reg = lvds_ctrl.start;
471 	}
472 
473 	lvds->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
474 	if (lvds->grf <= 0) {
475 		printf("%s: Get syscon grf failed (ret=%p)\n",
476 		      __func__, lvds->grf);
477 		return  -ENXIO;
478 	}
479 
480 	name = fdt_stringlist_get(state->blob, panel_node, "rockchip,output", 0, NULL);
481 	if (!name)
482 		/* default set it as output rgb */
483 		lvds->output = DISPLAY_OUTPUT_RGB;
484 	else
485 		lvds->output = lvds_name_to_output(name);
486 	if (lvds->output < 0) {
487 		printf("invalid output type [%s]\n", name);
488 		free(lvds);
489 		return lvds->output;
490 	}
491 	name = fdt_stringlist_get(state->blob, panel_node, "rockchip,data-mapping", 0, NULL);
492 	if (!name)
493 		/* default set it as format jeida */
494 		lvds->format = LVDS_FORMAT_JEIDA;
495 	else
496 		lvds->format = lvds_name_to_format(name);
497 
498 	if (lvds->format < 0) {
499 		printf("invalid data-mapping format [%s]\n", name);
500 		free(lvds);
501 		return lvds->format;
502 	}
503 	i = fdtdec_get_int(state->blob, panel_node, "rockchip,data-width", 24);
504 	if (i == 24) {
505 		lvds->format |= LVDS_24BIT;
506 	} else if (i == 18) {
507 		lvds->format |= LVDS_18BIT;
508 	} else {
509 		printf("rockchip-lvds unsupport data-width[%d]\n", i);
510 		free(lvds);
511 		return -EINVAL;
512 	}
513 	printf("LVDS: data mapping: %s, data-width:%d, format:%d,\n",
514 		name, i, lvds->format);
515 	conn_state->private = lvds;
516 	conn_state->type = DRM_MODE_CONNECTOR_LVDS;
517 	conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
518 
519 	return 0;
520 }
521 
522 static void rockchip_lvds_deinit(struct display_state *state)
523 {
524 	struct connector_state *conn_state = &state->conn_state;
525 	struct rockchip_lvds_device *lvds = conn_state->private;
526 
527 	free(lvds);
528 }
529 
530 static int rockchip_lvds_prepare(struct display_state *state)
531 {
532 	struct connector_state *conn_state = &state->conn_state;
533 	struct rockchip_lvds_device *lvds = conn_state->private;
534 	lvds->mode = &conn_state->mode;
535 
536 	rockchip_lvds_clk_enable(lvds);
537 
538 	return 0;
539 }
540 
541 static void rockchip_lvds_vop_routing(struct rockchip_lvds_device *lvds, int pipe)
542 {
543 	u32 val;
544 
545 	if (lvds->pdata->chip_type == RK3288_LVDS) {
546 		if (pipe)
547 			val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
548 				(RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
549 		else
550 			val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
551 		writel(val, lvds->grf + lvds->pdata->grf_soc_con6);
552 	} else {
553 		if (pipe)
554 			val = RK3366_LVDS_VOP_SEL_LIT;
555 		else
556 			val = RK3366_LVDS_VOP_SEL_BIG;
557 
558 		writel(val, lvds->grf + RK3366_GRF_SOC_CON0);
559 	}
560 }
561 
562 static int rockchip_lvds_enable(struct display_state *state)
563 {
564 	struct connector_state *conn_state = &state->conn_state;
565 	struct rockchip_lvds_device *lvds = conn_state->private;
566 	struct crtc_state *crtc_state = &state->crtc_state;
567 
568 	if (lvds->pdata->has_vop_sel)
569 		rockchip_lvds_vop_routing(lvds, crtc_state->crtc_id);
570 
571 	if (lvds->output == DISPLAY_OUTPUT_LVDS) {
572 		if (lvds->pdata->chip_type == RK3288_LVDS)
573 			rk3288_output_lvds(state);
574 		else
575 			rk336x_output_lvds(state);
576 	} else {
577 		if (lvds->pdata->chip_type == RK3288_LVDS)
578 			rk3288_output_ttl(state);
579 		else
580 			rk336x_output_ttl(state);
581 	}
582 
583 	return 0;
584 }
585 
586 static int rockchip_lvds_disable(struct display_state *state)
587 {
588 	struct connector_state *conn_state = &state->conn_state;
589 	struct rockchip_lvds_device *lvds = conn_state->private;
590 
591 	if (lvds->pdata->chip_type == RK3288_LVDS)
592 		rk3288_lvds_pwr_off(state);
593 	else
594 		rk336x_lvds_pwr_off(state);
595 
596 	return 0;
597 }
598 
599 const struct rockchip_connector_funcs rockchip_lvds_funcs = {
600 	.init = rockchip_lvds_init,
601 	.deinit = rockchip_lvds_deinit,
602 	.prepare = rockchip_lvds_prepare,
603 	.enable = rockchip_lvds_enable,
604 	.disable = rockchip_lvds_disable,
605 };
606 
607 static const struct rockchip_connector rk3366_lvds_data = {
608 	 .funcs = &rockchip_analogix_dp_funcs,
609 	 .data = &rk3366_lvds_drv_data,
610 };
611 
612 static const struct rockchip_connector rk3368_lvds_data = {
613 	 .funcs = &rockchip_lvds_funcs,
614 	 .data = &rk3368_lvds_drv_data,
615 };
616 
617 static const struct rockchip_connector rk3288_lvds_data = {
618 	 .funcs = &rockchip_lvds_funcs,
619 	 .data = &rk3288_lvds_drv_data,
620 };
621 
622 static const struct udevice_id rockchip_lvds_ids[] = {
623 	{
624 	 .compatible = "rockchip,rk3366-lvds",
625 	 .data = (ulong)&rk3366_lvds_data,
626 	}, {
627 	 .compatible = "rockchip,rk3368-lvds",
628 	 .data = (ulong)&rk3368_lvds_data,
629 	}, {
630 	 .compatible = "rockchip,rk3288-lvds",
631 	 .data = (ulong)&rk3288_lvds_data,
632 	}, {}
633 };
634 
635 U_BOOT_DRIVER(rockchip_lvds) = {
636 	.name = "rockchip_lvds",
637 	.id = UCLASS_DISPLAY,
638 	.of_match = rockchip_lvds_ids,
639 };
640