xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_lvds.c (revision dcebcd68cf61af6bd4c4e181f15dd86b77b05ec6)
1186f8572SMark Yao /*
2186f8572SMark Yao  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3186f8572SMark Yao  *
4186f8572SMark Yao  * SPDX-License-Identifier:	GPL-2.0+
5186f8572SMark Yao  */
6186f8572SMark Yao 
7186f8572SMark Yao #include <config.h>
8186f8572SMark Yao #include <common.h>
9186f8572SMark Yao #include <errno.h>
10186f8572SMark Yao #include <dm/device.h>
119aa59efbSKever Yang #include <dm/read.h>
129aa59efbSKever Yang #include <dm/ofnode.h>
13cb17ca6cSSandy Huang #include <dm/of_access.h>
14186f8572SMark Yao #include <syscon.h>
15b69d3ed4SWyon Bi #include <regmap.h>
16b69d3ed4SWyon Bi #include <dm/device.h>
17b69d3ed4SWyon Bi #include <dm/read.h>
18b69d3ed4SWyon Bi #include <linux/media-bus-format.h>
19186f8572SMark Yao 
20186f8572SMark Yao #include "rockchip_display.h"
21186f8572SMark Yao #include "rockchip_connector.h"
22b69d3ed4SWyon Bi #include "rockchip_phy.h"
231a8d717cSWyon Bi #include "rockchip_panel.h"
24*dcebcd68SChaoyi Chen #include "drm_of.h"
251953e619SWyon Bi 
261953e619SWyon Bi #define HIWORD_UPDATE(v, h, l)		(((v) << (l)) | (GENMASK(h, l) << 16))
27186f8572SMark Yao 
28b69d3ed4SWyon Bi #define PX30_GRF_PD_VO_CON1		0x0438
29b69d3ed4SWyon Bi #define PX30_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
30b69d3ed4SWyon Bi #define PX30_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
31b69d3ed4SWyon Bi #define PX30_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
3231018a86SWyon Bi #define PX30_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
33b69d3ed4SWyon Bi #define PX30_LVDS_VOP_SEL(x)		HIWORD_UPDATE(x,  1,  1)
34b69d3ed4SWyon Bi 
35b69d3ed4SWyon Bi #define RK3126_GRF_LVDS_CON0		0x0150
36b69d3ed4SWyon Bi #define RK3126_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  9,  9)
37b69d3ed4SWyon Bi #define RK3126_LVDS_MODE_EN(x)		HIWORD_UPDATE(x,  6,  6)
38b69d3ed4SWyon Bi #define RK3126_LVDS_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
39b69d3ed4SWyon Bi #define RK3126_LVDS_SELECT(x)		HIWORD_UPDATE(x,  2,  1)
40b69d3ed4SWyon Bi 
41b69d3ed4SWyon Bi #define RK3288_GRF_SOC_CON6		0x025c
42b69d3ed4SWyon Bi #define RK3288_LVDS_LCDC_SEL(x)		HIWORD_UPDATE(x,  3,  3)
43b69d3ed4SWyon Bi #define RK3288_GRF_SOC_CON7		0x0260
44b69d3ed4SWyon Bi #define RK3288_LVDS_PWRDWN(x)		HIWORD_UPDATE(x, 15, 15)
45b69d3ed4SWyon Bi #define RK3288_LVDS_CON_ENABLE_2(x)	HIWORD_UPDATE(x, 12, 12)
46b69d3ed4SWyon Bi #define RK3288_LVDS_CON_ENABLE_1(x)	HIWORD_UPDATE(x, 11, 11)
47b69d3ed4SWyon Bi #define RK3288_LVDS_CON_DEN_POL(x)	HIWORD_UPDATE(x, 10, 10)
48b69d3ed4SWyon Bi #define RK3288_LVDS_CON_HS_POL(x)	HIWORD_UPDATE(x,  9,  9)
49b69d3ed4SWyon Bi #define RK3288_LVDS_CON_CLKINV(x)	HIWORD_UPDATE(x,  8,  8)
50b69d3ed4SWyon Bi #define RK3288_LVDS_CON_STARTPHASE(x)	HIWORD_UPDATE(x,  7,  7)
51b69d3ed4SWyon Bi #define RK3288_LVDS_CON_TTL_EN(x)	HIWORD_UPDATE(x,  6,  6)
52b69d3ed4SWyon Bi #define RK3288_LVDS_CON_STARTSEL(x)	HIWORD_UPDATE(x,  5,  5)
53b69d3ed4SWyon Bi #define RK3288_LVDS_CON_CHASEL(x)	HIWORD_UPDATE(x,  4,  4)
54b69d3ed4SWyon Bi #define RK3288_LVDS_CON_MSBSEL(x)	HIWORD_UPDATE(x,  3,  3)
55b69d3ed4SWyon Bi #define RK3288_LVDS_CON_SELECT(x)	HIWORD_UPDATE(x,  2,  0)
56b69d3ed4SWyon Bi 
57b69d3ed4SWyon Bi #define RK3368_GRF_SOC_CON7		0x041c
58b69d3ed4SWyon Bi #define RK3368_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
59b69d3ed4SWyon Bi #define RK3368_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
60b69d3ed4SWyon Bi #define RK3368_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
61b69d3ed4SWyon Bi #define RK3368_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
62b69d3ed4SWyon Bi 
63cc341c3bSZhang Yubing #define RK3562_GRF_VO_CON0		0x05d0
64cc341c3bSZhang Yubing #define RK3562_GRF_VO_CON1		0x05d4
65cc341c3bSZhang Yubing 
66aeb5dd22SSandy Huang #define RK3568_GRF_VO_CON0		0x0360
67aeb5dd22SSandy Huang #define RK3568_LVDS1_SELECT(x)		HIWORD_UPDATE(x, 13, 12)
68aeb5dd22SSandy Huang #define RK3568_LVDS1_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
69aeb5dd22SSandy Huang #define RK3568_LVDS0_SELECT(x)		HIWORD_UPDATE(x,  5,  4)
70aeb5dd22SSandy Huang #define RK3568_LVDS0_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
71aeb5dd22SSandy Huang #define RK3568_GRF_VO_CON2		0x0368
72aeb5dd22SSandy Huang #define RK3568_LVDS0_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
73aeb5dd22SSandy Huang #define RK3568_LVDS0_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
74aeb5dd22SSandy Huang #define RK3568_LVDS0_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
75aeb5dd22SSandy Huang #define RK3568_LVDS0_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
76aeb5dd22SSandy Huang #define RK3568_GRF_VO_CON3		0x036c
77aeb5dd22SSandy Huang #define RK3568_LVDS1_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
78aeb5dd22SSandy Huang #define RK3568_LVDS1_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
79aeb5dd22SSandy Huang #define RK3568_LVDS1_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
80aeb5dd22SSandy Huang #define RK3568_LVDS1_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
81aeb5dd22SSandy Huang 
82b69d3ed4SWyon Bi enum lvds_format {
83b69d3ed4SWyon Bi 	LVDS_8BIT_MODE_FORMAT_1,
84b69d3ed4SWyon Bi 	LVDS_8BIT_MODE_FORMAT_2,
85b69d3ed4SWyon Bi 	LVDS_8BIT_MODE_FORMAT_3,
86b69d3ed4SWyon Bi 	LVDS_6BIT_MODE,
87e2721f29SGuochun Huang 	LVDS_10BIT_MODE_FORMAT_1,
88e2721f29SGuochun Huang 	LVDS_10BIT_MODE_FORMAT_2,
89186f8572SMark Yao };
90186f8572SMark Yao 
91b69d3ed4SWyon Bi struct rockchip_lvds;
92b69d3ed4SWyon Bi 
93b69d3ed4SWyon Bi struct rockchip_lvds_funcs {
94*dcebcd68SChaoyi Chen 	int (*probe)(struct rockchip_lvds *lvds);
95b69d3ed4SWyon Bi 	void (*enable)(struct rockchip_lvds *lvds, int pipe);
96b69d3ed4SWyon Bi 	void (*disable)(struct rockchip_lvds *lvds);
97186f8572SMark Yao };
98186f8572SMark Yao 
99b69d3ed4SWyon Bi struct rockchip_lvds {
1000594ce39SZhang Yubing 	struct rockchip_connector connector;
101cb17ca6cSSandy Huang 	int id;
102b69d3ed4SWyon Bi 	struct udevice *dev;
103b69d3ed4SWyon Bi 	struct regmap *grf;
104b69d3ed4SWyon Bi 	struct rockchip_phy *phy;
105b69d3ed4SWyon Bi 	const struct drm_display_mode *mode;
106b69d3ed4SWyon Bi 	const struct rockchip_lvds_funcs *funcs;
107b69d3ed4SWyon Bi 	enum lvds_format format;
108b69d3ed4SWyon Bi 	bool data_swap;
109b69d3ed4SWyon Bi 	bool dual_channel;
110*dcebcd68SChaoyi Chen 	enum drm_lvds_dual_link_pixels pixel_order;
111186f8572SMark Yao };
112186f8572SMark Yao 
1130594ce39SZhang Yubing static int rockchip_lvds_connector_init(struct rockchip_connector *conn,
1140594ce39SZhang Yubing 					struct display_state *state)
115186f8572SMark Yao {
1160594ce39SZhang Yubing 	struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
117*dcebcd68SChaoyi Chen 	struct rockchip_lvds *primary_lvds = NULL;
118186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
1190594ce39SZhang Yubing 	struct rockchip_panel *panel = conn->panel;
120186f8572SMark Yao 
121b69d3ed4SWyon Bi 	lvds->mode = &conn_state->mode;
1220594ce39SZhang Yubing 	lvds->phy = conn->phy;
123cb17ca6cSSandy Huang 	conn_state->disp_info  = rockchip_get_disp_info(conn_state->type, lvds->id);
124186f8572SMark Yao 
125*dcebcd68SChaoyi Chen 	if (conn_state->secondary)
126*dcebcd68SChaoyi Chen 		primary_lvds = dev_get_priv(conn_state->connector->dev);
127*dcebcd68SChaoyi Chen 
128b69d3ed4SWyon Bi 	switch (panel->bus_format) {
1294888f8a4SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:	/* jeida-18 */
130b69d3ed4SWyon Bi 		lvds->format = LVDS_6BIT_MODE;
131b69d3ed4SWyon Bi 		break;
132b69d3ed4SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:	/* jeida-24 */
133b69d3ed4SWyon Bi 		lvds->format = LVDS_8BIT_MODE_FORMAT_2;
134b69d3ed4SWyon Bi 		break;
135e2721f29SGuochun Huang 	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
136e2721f29SGuochun Huang 		lvds->format = LVDS_10BIT_MODE_FORMAT_1;
1374888f8a4SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
1384888f8a4SWyon Bi 		lvds->format = LVDS_8BIT_MODE_FORMAT_3;
1394888f8a4SWyon Bi 		break;
140e2721f29SGuochun Huang 	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
141e2721f29SGuochun Huang 		lvds->format = LVDS_10BIT_MODE_FORMAT_2;
142e2721f29SGuochun Huang 		break;
143b69d3ed4SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:	/* vesa-24 */
144b69d3ed4SWyon Bi 	default:
145b69d3ed4SWyon Bi 		lvds->format = LVDS_8BIT_MODE_FORMAT_1;
146b69d3ed4SWyon Bi 		break;
147186f8572SMark Yao 	}
148186f8572SMark Yao 
149*dcebcd68SChaoyi Chen 	conn_state->bus_format = panel->bus_format;
150186f8572SMark Yao 	conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
151e2721f29SGuochun Huang 
152e2721f29SGuochun Huang 	if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) ||
153e2721f29SGuochun Huang 		(lvds->format == LVDS_10BIT_MODE_FORMAT_2))
154e2721f29SGuochun Huang 		conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
155e2721f29SGuochun Huang 
15679feefb1SSandy Huang 	conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
157*dcebcd68SChaoyi Chen 	if (primary_lvds) {
158*dcebcd68SChaoyi Chen 		conn_state->output_flags = 0;
159*dcebcd68SChaoyi Chen 		conn_state->output_if = 0;
160*dcebcd68SChaoyi Chen 
161*dcebcd68SChaoyi Chen 		switch (primary_lvds->pixel_order) {
162*dcebcd68SChaoyi Chen 		case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
163*dcebcd68SChaoyi Chen 			conn_state->output_flags |=
164*dcebcd68SChaoyi Chen 				ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
165*dcebcd68SChaoyi Chen 			conn_state->output_if |=
166*dcebcd68SChaoyi Chen 				VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
167*dcebcd68SChaoyi Chen 			break;
168*dcebcd68SChaoyi Chen 		case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
169*dcebcd68SChaoyi Chen 			conn_state->output_flags |=
170*dcebcd68SChaoyi Chen 				ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
171*dcebcd68SChaoyi Chen 			conn_state->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
172*dcebcd68SChaoyi Chen 			conn_state->output_if |=
173*dcebcd68SChaoyi Chen 				VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
174*dcebcd68SChaoyi Chen 			break;
175*dcebcd68SChaoyi Chen 		case DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
176*dcebcd68SChaoyi Chen 			conn_state->output_flags |=
177*dcebcd68SChaoyi Chen 				ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
178*dcebcd68SChaoyi Chen 			conn_state->output_if |=
179*dcebcd68SChaoyi Chen 				VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
180*dcebcd68SChaoyi Chen 			break;
181*dcebcd68SChaoyi Chen 		case DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
182*dcebcd68SChaoyi Chen 			conn_state->output_flags |=
183*dcebcd68SChaoyi Chen 				ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
184*dcebcd68SChaoyi Chen 			conn_state->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
185*dcebcd68SChaoyi Chen 			conn_state->output_if |=
186*dcebcd68SChaoyi Chen 				VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
187*dcebcd68SChaoyi Chen 			break;
188*dcebcd68SChaoyi Chen 		default:
189*dcebcd68SChaoyi Chen 			break;
190*dcebcd68SChaoyi Chen 		}
191*dcebcd68SChaoyi Chen 	}
192*dcebcd68SChaoyi Chen 
193*dcebcd68SChaoyi Chen 	if (lvds->id)
194*dcebcd68SChaoyi Chen 		conn_state->output_if |= VOP_OUTPUT_IF_LVDS1;
195*dcebcd68SChaoyi Chen 	else
196*dcebcd68SChaoyi Chen 		conn_state->output_if |= VOP_OUTPUT_IF_LVDS0;
197186f8572SMark Yao 
198186f8572SMark Yao 	return 0;
199186f8572SMark Yao }
200186f8572SMark Yao 
2010594ce39SZhang Yubing static int rockchip_lvds_connector_enable(struct rockchip_connector *conn,
2020594ce39SZhang Yubing 					  struct display_state *state)
203186f8572SMark Yao {
2040594ce39SZhang Yubing 	struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
205b69d3ed4SWyon Bi 	struct crtc_state *crtc_state = &state->crtc_state;
206b69d3ed4SWyon Bi 	int pipe = crtc_state->crtc_id;
207b69d3ed4SWyon Bi 	int ret;
208186f8572SMark Yao 
209b69d3ed4SWyon Bi 	if (lvds->funcs->enable)
210b69d3ed4SWyon Bi 		lvds->funcs->enable(lvds, pipe);
211b69d3ed4SWyon Bi 
2129e3ffb10SGuochun Huang 	ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS);
213b69d3ed4SWyon Bi 	if (ret) {
214b69d3ed4SWyon Bi 		dev_err(lvds->dev, "failed to set phy mode: %d\n", ret);
215b69d3ed4SWyon Bi 		return ret;
216186f8572SMark Yao 	}
217186f8572SMark Yao 
218b69d3ed4SWyon Bi 	rockchip_phy_power_on(lvds->phy);
219186f8572SMark Yao 
220186f8572SMark Yao 	return 0;
221186f8572SMark Yao }
222186f8572SMark Yao 
2230594ce39SZhang Yubing static int rockchip_lvds_connector_disable(struct rockchip_connector *conn,
2240594ce39SZhang Yubing 					   struct display_state *state)
225186f8572SMark Yao {
2260594ce39SZhang Yubing 	struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
227b69d3ed4SWyon Bi 
228b69d3ed4SWyon Bi 	rockchip_phy_power_off(lvds->phy);
229b69d3ed4SWyon Bi 
230b69d3ed4SWyon Bi 	if (lvds->funcs->disable)
231b69d3ed4SWyon Bi 		lvds->funcs->disable(lvds);
232b69d3ed4SWyon Bi 
233b69d3ed4SWyon Bi 	return 0;
234b69d3ed4SWyon Bi }
235b69d3ed4SWyon Bi 
236b69d3ed4SWyon Bi static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = {
237b69d3ed4SWyon Bi 	.init = rockchip_lvds_connector_init,
238b69d3ed4SWyon Bi 	.enable = rockchip_lvds_connector_enable,
239b69d3ed4SWyon Bi 	.disable = rockchip_lvds_connector_disable,
240b69d3ed4SWyon Bi };
241b69d3ed4SWyon Bi 
242b69d3ed4SWyon Bi static int rockchip_lvds_probe(struct udevice *dev)
243b69d3ed4SWyon Bi {
244b69d3ed4SWyon Bi 	struct rockchip_lvds *lvds = dev_get_priv(dev);
245b69d3ed4SWyon Bi 
246b69d3ed4SWyon Bi 	lvds->dev = dev;
2470594ce39SZhang Yubing 	lvds->funcs = (const struct rockchip_lvds_funcs *)dev_get_driver_data(dev);
248b69d3ed4SWyon Bi 	lvds->grf = syscon_get_regmap(dev_get_parent(dev));
249b69d3ed4SWyon Bi 	lvds->dual_channel = dev_read_bool(dev, "dual-channel");
250b69d3ed4SWyon Bi 	lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap");
251cb17ca6cSSandy Huang 	lvds->id = of_alias_get_id(ofnode_to_np(dev->node), "lvds");
252cb17ca6cSSandy Huang 	if (lvds->id < 0)
253cb17ca6cSSandy Huang 		lvds->id = 0;
254b69d3ed4SWyon Bi 
255*dcebcd68SChaoyi Chen 	lvds->pixel_order = -1;
256*dcebcd68SChaoyi Chen 	if (lvds->funcs->probe)
257*dcebcd68SChaoyi Chen 		lvds->funcs->probe(lvds);
258*dcebcd68SChaoyi Chen 
2590594ce39SZhang Yubing 	rockchip_connector_bind(&lvds->connector, dev, lvds->id, &rockchip_lvds_connector_funcs,
2600594ce39SZhang Yubing 				NULL, DRM_MODE_CONNECTOR_LVDS);
2610594ce39SZhang Yubing 
262b69d3ed4SWyon Bi 	return 0;
263b69d3ed4SWyon Bi }
264b69d3ed4SWyon Bi 
265b69d3ed4SWyon Bi static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe)
266b69d3ed4SWyon Bi {
267b69d3ed4SWyon Bi 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
268b69d3ed4SWyon Bi 		     PX30_LVDS_SELECT(lvds->format) |
269b69d3ed4SWyon Bi 		     PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
270b69d3ed4SWyon Bi 		     PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
271b69d3ed4SWyon Bi }
272b69d3ed4SWyon Bi 
273b69d3ed4SWyon Bi static void px30_lvds_disable(struct rockchip_lvds *lvds)
274b69d3ed4SWyon Bi {
275b69d3ed4SWyon Bi 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
276b69d3ed4SWyon Bi 		     PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
277b69d3ed4SWyon Bi }
278b69d3ed4SWyon Bi 
279b69d3ed4SWyon Bi static const struct rockchip_lvds_funcs px30_lvds_funcs = {
280b69d3ed4SWyon Bi 	.enable = px30_lvds_enable,
281b69d3ed4SWyon Bi 	.disable = px30_lvds_disable,
282b69d3ed4SWyon Bi };
283b69d3ed4SWyon Bi 
284b69d3ed4SWyon Bi static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe)
285b69d3ed4SWyon Bi {
286b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
287b69d3ed4SWyon Bi 		     RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
288b69d3ed4SWyon Bi 		     RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
289b69d3ed4SWyon Bi }
290b69d3ed4SWyon Bi 
291b69d3ed4SWyon Bi static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
292b69d3ed4SWyon Bi {
293b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
294b69d3ed4SWyon Bi 		     RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
295b69d3ed4SWyon Bi }
296b69d3ed4SWyon Bi 
297b69d3ed4SWyon Bi static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
298b69d3ed4SWyon Bi 	.enable = rk3126_lvds_enable,
299b69d3ed4SWyon Bi 	.disable = rk3126_lvds_disable,
300b69d3ed4SWyon Bi };
301b69d3ed4SWyon Bi 
302b69d3ed4SWyon Bi static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe)
303b69d3ed4SWyon Bi {
304b69d3ed4SWyon Bi 	const struct drm_display_mode *mode = lvds->mode;
305186f8572SMark Yao 	u32 val;
306186f8572SMark Yao 
307b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
308b69d3ed4SWyon Bi 		     RK3288_LVDS_LCDC_SEL(pipe));
309b69d3ed4SWyon Bi 
310b69d3ed4SWyon Bi 	val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
311b69d3ed4SWyon Bi 	      RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
312b69d3ed4SWyon Bi 	      RK3288_LVDS_CON_SELECT(lvds->format);
313b69d3ed4SWyon Bi 
314b69d3ed4SWyon Bi 	if (lvds->dual_channel) {
315b69d3ed4SWyon Bi 		u32 h_bp = mode->htotal - mode->hsync_start;
316b69d3ed4SWyon Bi 
317b69d3ed4SWyon Bi 		val |= RK3288_LVDS_CON_ENABLE_2(1) |
318b69d3ed4SWyon Bi 		       RK3288_LVDS_CON_ENABLE_1(1) |
319b69d3ed4SWyon Bi 		       RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
320b69d3ed4SWyon Bi 
321b69d3ed4SWyon Bi 		if (h_bp % 2)
322b69d3ed4SWyon Bi 			val |= RK3288_LVDS_CON_STARTPHASE(1);
323186f8572SMark Yao 		else
324b69d3ed4SWyon Bi 			val |= RK3288_LVDS_CON_STARTPHASE(0);
325186f8572SMark Yao 	} else {
326b69d3ed4SWyon Bi 		val |= RK3288_LVDS_CON_ENABLE_2(0) |
327b69d3ed4SWyon Bi 		       RK3288_LVDS_CON_ENABLE_1(1);
328186f8572SMark Yao 	}
329186f8572SMark Yao 
330b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
331b69d3ed4SWyon Bi 
332b69d3ed4SWyon Bi 	rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
333186f8572SMark Yao }
334186f8572SMark Yao 
335b69d3ed4SWyon Bi static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
336186f8572SMark Yao {
337b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
338186f8572SMark Yao }
339186f8572SMark Yao 
340b69d3ed4SWyon Bi static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
341b69d3ed4SWyon Bi 	.enable = rk3288_lvds_enable,
342b69d3ed4SWyon Bi 	.disable = rk3288_lvds_disable,
343186f8572SMark Yao };
344186f8572SMark Yao 
345b69d3ed4SWyon Bi static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe)
346b69d3ed4SWyon Bi {
347b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
348b69d3ed4SWyon Bi 		     RK3368_LVDS_SELECT(lvds->format) |
349b69d3ed4SWyon Bi 		     RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
350b69d3ed4SWyon Bi 		     RK3368_LVDS_P2S_EN(1));
351b69d3ed4SWyon Bi }
352b69d3ed4SWyon Bi 
353b69d3ed4SWyon Bi static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
354b69d3ed4SWyon Bi {
355b69d3ed4SWyon Bi 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
356b69d3ed4SWyon Bi 		     RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
357b69d3ed4SWyon Bi }
358b69d3ed4SWyon Bi 
359b69d3ed4SWyon Bi static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
360b69d3ed4SWyon Bi 	.enable = rk3368_lvds_enable,
361b69d3ed4SWyon Bi 	.disable = rk3368_lvds_disable,
36230d6d433SWyon Bi };
36330d6d433SWyon Bi 
364cc341c3bSZhang Yubing static void rk3562_lvds_enable(struct rockchip_lvds *lvds, int pipe)
365cc341c3bSZhang Yubing {
366cc341c3bSZhang Yubing 	regmap_write(lvds->grf, RK3562_GRF_VO_CON1,
367cc341c3bSZhang Yubing 		     RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
368cc341c3bSZhang Yubing 		     RK3568_LVDS0_DCLK_INV_SEL(1));
369cc341c3bSZhang Yubing 	regmap_write(lvds->grf, RK3562_GRF_VO_CON0,
370cc341c3bSZhang Yubing 		     RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
371cc341c3bSZhang Yubing }
372cc341c3bSZhang Yubing 
373cc341c3bSZhang Yubing static void rk3562_lvds_disable(struct rockchip_lvds *lvds)
374cc341c3bSZhang Yubing {
375cc341c3bSZhang Yubing 	regmap_write(lvds->grf, RK3562_GRF_VO_CON1, RK3568_LVDS0_MODE_EN(0));
376cc341c3bSZhang Yubing }
377cc341c3bSZhang Yubing 
378cc341c3bSZhang Yubing static const struct rockchip_lvds_funcs rk3562_lvds_funcs = {
379cc341c3bSZhang Yubing 	.enable = rk3562_lvds_enable,
380cc341c3bSZhang Yubing 	.disable = rk3562_lvds_disable,
381cc341c3bSZhang Yubing };
382cc341c3bSZhang Yubing 
383*dcebcd68SChaoyi Chen static int rk3568_lvds_probe(struct rockchip_lvds *lvds)
384*dcebcd68SChaoyi Chen {
385*dcebcd68SChaoyi Chen 	if (lvds->dual_channel) {
386*dcebcd68SChaoyi Chen 		const struct device_node *port0, *port1;
387*dcebcd68SChaoyi Chen 		int pixel_order;
388*dcebcd68SChaoyi Chen 
389*dcebcd68SChaoyi Chen 		port1 = of_alias_get_dev("lvds", 1);
390*dcebcd68SChaoyi Chen 		if (!port1 || !of_device_is_available(port1)) {
391*dcebcd68SChaoyi Chen 			lvds->pixel_order = 0;
392*dcebcd68SChaoyi Chen 			return 0;
393*dcebcd68SChaoyi Chen 		}
394*dcebcd68SChaoyi Chen 
395*dcebcd68SChaoyi Chen 		port0 = rockchip_of_graph_get_port_by_id(lvds->dev->node, 1);
396*dcebcd68SChaoyi Chen 		port1 = rockchip_of_graph_get_port_by_id(np_to_ofnode(port1),
397*dcebcd68SChaoyi Chen 							 1);
398*dcebcd68SChaoyi Chen 		pixel_order =
399*dcebcd68SChaoyi Chen 			drm_of_lvds_get_dual_link_pixel_order(port0, port1);
400*dcebcd68SChaoyi Chen 
401*dcebcd68SChaoyi Chen 		lvds->pixel_order = pixel_order >= 0 ? pixel_order : 0;
402*dcebcd68SChaoyi Chen 	}
403*dcebcd68SChaoyi Chen 
404*dcebcd68SChaoyi Chen 	return 0;
405*dcebcd68SChaoyi Chen }
406*dcebcd68SChaoyi Chen 
407aeb5dd22SSandy Huang static void rk3568_lvds_enable(struct rockchip_lvds *lvds, int pipe)
408aeb5dd22SSandy Huang {
409*dcebcd68SChaoyi Chen 	if (lvds->id) {
410*dcebcd68SChaoyi Chen 		regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
411*dcebcd68SChaoyi Chen 			     RK3568_LVDS1_MODE_EN(1) | RK3568_LVDS1_P2S_EN(1) |
412*dcebcd68SChaoyi Chen 				     RK3568_LVDS1_DCLK_INV_SEL(1));
413*dcebcd68SChaoyi Chen 		regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
414*dcebcd68SChaoyi Chen 			     RK3568_LVDS1_SELECT(lvds->format) |
415*dcebcd68SChaoyi Chen 				     RK3568_LVDS1_MSBSEL(1));
416*dcebcd68SChaoyi Chen 	} else {
417aeb5dd22SSandy Huang 		regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
418aeb5dd22SSandy Huang 			     RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
419aeb5dd22SSandy Huang 				     RK3568_LVDS0_DCLK_INV_SEL(1));
420aeb5dd22SSandy Huang 		regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
421*dcebcd68SChaoyi Chen 			     RK3568_LVDS0_SELECT(lvds->format) |
422*dcebcd68SChaoyi Chen 				     RK3568_LVDS0_MSBSEL(1));
423*dcebcd68SChaoyi Chen 	}
424aeb5dd22SSandy Huang }
425aeb5dd22SSandy Huang 
426aeb5dd22SSandy Huang static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
427aeb5dd22SSandy Huang {
428*dcebcd68SChaoyi Chen 	if (lvds->id)
429*dcebcd68SChaoyi Chen 		regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
430*dcebcd68SChaoyi Chen 			     RK3568_LVDS1_MODE_EN(0));
431*dcebcd68SChaoyi Chen 	else
432*dcebcd68SChaoyi Chen 		regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
433*dcebcd68SChaoyi Chen 			     RK3568_LVDS0_MODE_EN(0));
434aeb5dd22SSandy Huang }
435aeb5dd22SSandy Huang 
436aeb5dd22SSandy Huang static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
437*dcebcd68SChaoyi Chen 	.probe = rk3568_lvds_probe,
438aeb5dd22SSandy Huang 	.enable = rk3568_lvds_enable,
439aeb5dd22SSandy Huang 	.disable = rk3568_lvds_disable,
440aeb5dd22SSandy Huang };
441aeb5dd22SSandy Huang 
442186f8572SMark Yao static const struct udevice_id rockchip_lvds_ids[] = {
443186f8572SMark Yao 	{
44430d6d433SWyon Bi 		.compatible = "rockchip,px30-lvds",
4450594ce39SZhang Yubing 		.data = (ulong)&px30_lvds_funcs,
44630d6d433SWyon Bi 	},
44730d6d433SWyon Bi 	{
44830d6d433SWyon Bi 		.compatible = "rockchip,rk3126-lvds",
4490594ce39SZhang Yubing 		.data = (ulong)&rk3126_lvds_funcs,
45030d6d433SWyon Bi 	},
45130d6d433SWyon Bi 	{
45230d6d433SWyon Bi 		.compatible = "rockchip,rk3288-lvds",
4530594ce39SZhang Yubing 		.data = (ulong)&rk3288_lvds_funcs,
45430d6d433SWyon Bi 	},
45530d6d433SWyon Bi 	{
456186f8572SMark Yao 		.compatible = "rockchip,rk3368-lvds",
4570594ce39SZhang Yubing 		.data = (ulong)&rk3368_lvds_funcs,
45830d6d433SWyon Bi 	},
459aeb5dd22SSandy Huang 	{
460cc341c3bSZhang Yubing 		.compatible = "rockchip,rk3562-lvds",
461cc341c3bSZhang Yubing 		.data = (ulong)&rk3562_lvds_funcs,
462cc341c3bSZhang Yubing 	},
463cc341c3bSZhang Yubing 	{
464aeb5dd22SSandy Huang 		.compatible = "rockchip,rk3568-lvds",
4650594ce39SZhang Yubing 		.data = (ulong)&rk3568_lvds_funcs,
466aeb5dd22SSandy Huang 	},
46730d6d433SWyon Bi 	{}
468186f8572SMark Yao };
469186f8572SMark Yao 
470186f8572SMark Yao U_BOOT_DRIVER(rockchip_lvds) = {
471186f8572SMark Yao 	.name = "rockchip_lvds",
472186f8572SMark Yao 	.id = UCLASS_DISPLAY,
473186f8572SMark Yao 	.of_match = rockchip_lvds_ids,
474b69d3ed4SWyon Bi 	.probe = rockchip_lvds_probe,
475b69d3ed4SWyon Bi 	.priv_auto_alloc_size = sizeof(struct rockchip_lvds),
476186f8572SMark Yao };
477