xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_lvds.c (revision e091b6c996a68a6a0faa2bd3ffdd90b3ba5f44ce)
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 <dm/device.h>
11 #include <dm/read.h>
12 #include <dm/ofnode.h>
13 #include <syscon.h>
14 #include <regmap.h>
15 #include <dm/device.h>
16 #include <dm/read.h>
17 #include <linux/media-bus-format.h>
18 
19 #include "rockchip_display.h"
20 #include "rockchip_connector.h"
21 #include "rockchip_phy.h"
22 #include "rockchip_panel.h"
23 
24 #define HIWORD_UPDATE(v, h, l)		(((v) << (l)) | (GENMASK(h, l) << 16))
25 
26 #define PX30_GRF_PD_VO_CON1		0x0438
27 #define PX30_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
28 #define PX30_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
29 #define PX30_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
30 #define PX30_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
31 #define PX30_LVDS_VOP_SEL(x)		HIWORD_UPDATE(x,  1,  1)
32 
33 #define RK3126_GRF_LVDS_CON0		0x0150
34 #define RK3126_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  9,  9)
35 #define RK3126_LVDS_MODE_EN(x)		HIWORD_UPDATE(x,  6,  6)
36 #define RK3126_LVDS_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
37 #define RK3126_LVDS_SELECT(x)		HIWORD_UPDATE(x,  2,  1)
38 
39 #define RK3288_GRF_SOC_CON6		0x025c
40 #define RK3288_LVDS_LCDC_SEL(x)		HIWORD_UPDATE(x,  3,  3)
41 #define RK3288_GRF_SOC_CON7		0x0260
42 #define RK3288_LVDS_PWRDWN(x)		HIWORD_UPDATE(x, 15, 15)
43 #define RK3288_LVDS_CON_ENABLE_2(x)	HIWORD_UPDATE(x, 12, 12)
44 #define RK3288_LVDS_CON_ENABLE_1(x)	HIWORD_UPDATE(x, 11, 11)
45 #define RK3288_LVDS_CON_DEN_POL(x)	HIWORD_UPDATE(x, 10, 10)
46 #define RK3288_LVDS_CON_HS_POL(x)	HIWORD_UPDATE(x,  9,  9)
47 #define RK3288_LVDS_CON_CLKINV(x)	HIWORD_UPDATE(x,  8,  8)
48 #define RK3288_LVDS_CON_STARTPHASE(x)	HIWORD_UPDATE(x,  7,  7)
49 #define RK3288_LVDS_CON_TTL_EN(x)	HIWORD_UPDATE(x,  6,  6)
50 #define RK3288_LVDS_CON_STARTSEL(x)	HIWORD_UPDATE(x,  5,  5)
51 #define RK3288_LVDS_CON_CHASEL(x)	HIWORD_UPDATE(x,  4,  4)
52 #define RK3288_LVDS_CON_MSBSEL(x)	HIWORD_UPDATE(x,  3,  3)
53 #define RK3288_LVDS_CON_SELECT(x)	HIWORD_UPDATE(x,  2,  0)
54 
55 #define RK3368_GRF_SOC_CON7		0x041c
56 #define RK3368_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
57 #define RK3368_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
58 #define RK3368_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
59 #define RK3368_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
60 
61 #define RK3568_GRF_VO_CON0		0x0360
62 #define RK3568_LVDS1_SELECT(x)		HIWORD_UPDATE(x, 13, 12)
63 #define RK3568_LVDS1_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
64 #define RK3568_LVDS0_SELECT(x)		HIWORD_UPDATE(x,  5,  4)
65 #define RK3568_LVDS0_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
66 #define RK3568_GRF_VO_CON2		0x0368
67 #define RK3568_LVDS0_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
68 #define RK3568_LVDS0_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
69 #define RK3568_LVDS0_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
70 #define RK3568_LVDS0_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
71 #define RK3568_GRF_VO_CON3		0x036c
72 #define RK3568_LVDS1_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
73 #define RK3568_LVDS1_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
74 #define RK3568_LVDS1_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
75 #define RK3568_LVDS1_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
76 
77 enum lvds_format {
78 	LVDS_8BIT_MODE_FORMAT_1,
79 	LVDS_8BIT_MODE_FORMAT_2,
80 	LVDS_8BIT_MODE_FORMAT_3,
81 	LVDS_6BIT_MODE,
82 	LVDS_10BIT_MODE_FORMAT_1,
83 	LVDS_10BIT_MODE_FORMAT_2,
84 };
85 
86 struct rockchip_lvds;
87 
88 struct rockchip_lvds_funcs {
89 	void (*enable)(struct rockchip_lvds *lvds, int pipe);
90 	void (*disable)(struct rockchip_lvds *lvds);
91 };
92 
93 struct rockchip_lvds {
94 	struct udevice *dev;
95 	struct regmap *grf;
96 	struct rockchip_phy *phy;
97 	const struct drm_display_mode *mode;
98 	const struct rockchip_lvds_funcs *funcs;
99 	enum lvds_format format;
100 	bool data_swap;
101 	bool dual_channel;
102 };
103 
104 static inline struct rockchip_lvds *state_to_lvds(struct display_state *state)
105 {
106 	struct connector_state *conn_state = &state->conn_state;
107 
108 	return dev_get_priv(conn_state->dev);
109 }
110 
111 static int rockchip_lvds_connector_init(struct display_state *state)
112 {
113 	struct rockchip_lvds *lvds = state_to_lvds(state);
114 	struct connector_state *conn_state = &state->conn_state;
115 	struct rockchip_panel *panel = state_get_panel(state);
116 
117 	lvds->mode = &conn_state->mode;
118 	lvds->phy = conn_state->phy;
119 
120 	switch (panel->bus_format) {
121 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:	/* jeida-18 */
122 		lvds->format = LVDS_6BIT_MODE;
123 		break;
124 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:	/* jeida-24 */
125 		lvds->format = LVDS_8BIT_MODE_FORMAT_2;
126 		break;
127 	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
128 		lvds->format = LVDS_10BIT_MODE_FORMAT_1;
129 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
130 		lvds->format = LVDS_8BIT_MODE_FORMAT_3;
131 		break;
132 	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
133 		lvds->format = LVDS_10BIT_MODE_FORMAT_2;
134 		break;
135 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:	/* vesa-24 */
136 	default:
137 		lvds->format = LVDS_8BIT_MODE_FORMAT_1;
138 		break;
139 	}
140 
141 	conn_state->type = DRM_MODE_CONNECTOR_LVDS;
142 	conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
143 
144 	if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) ||
145 		(lvds->format == LVDS_10BIT_MODE_FORMAT_2))
146 		conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
147 
148 	conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
149 	conn_state->output_if = VOP_OUTPUT_IF_LVDS0;
150 
151 	return 0;
152 }
153 
154 static int rockchip_lvds_connector_enable(struct display_state *state)
155 {
156 	struct rockchip_lvds *lvds = state_to_lvds(state);
157 	struct crtc_state *crtc_state = &state->crtc_state;
158 	int pipe = crtc_state->crtc_id;
159 	int ret;
160 
161 	if (lvds->funcs->enable)
162 		lvds->funcs->enable(lvds, pipe);
163 
164 	ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS);
165 	if (ret) {
166 		dev_err(lvds->dev, "failed to set phy mode: %d\n", ret);
167 		return ret;
168 	}
169 
170 	rockchip_phy_power_on(lvds->phy);
171 
172 	return 0;
173 }
174 
175 static int rockchip_lvds_connector_disable(struct display_state *state)
176 {
177 	struct rockchip_lvds *lvds = state_to_lvds(state);
178 
179 	rockchip_phy_power_off(lvds->phy);
180 
181 	if (lvds->funcs->disable)
182 		lvds->funcs->disable(lvds);
183 
184 	return 0;
185 }
186 
187 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = {
188 	.init = rockchip_lvds_connector_init,
189 	.enable = rockchip_lvds_connector_enable,
190 	.disable = rockchip_lvds_connector_disable,
191 };
192 
193 static int rockchip_lvds_probe(struct udevice *dev)
194 {
195 	struct rockchip_lvds *lvds = dev_get_priv(dev);
196 	const struct rockchip_connector *connector =
197 		(const struct rockchip_connector *)dev_get_driver_data(dev);
198 
199 	lvds->dev = dev;
200 	lvds->funcs = connector->data;
201 	lvds->grf = syscon_get_regmap(dev_get_parent(dev));
202 	lvds->dual_channel = dev_read_bool(dev, "dual-channel");
203 	lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap");
204 
205 	return 0;
206 }
207 
208 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe)
209 {
210 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
211 		     PX30_LVDS_SELECT(lvds->format) |
212 		     PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
213 		     PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
214 }
215 
216 static void px30_lvds_disable(struct rockchip_lvds *lvds)
217 {
218 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
219 		     PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
220 }
221 
222 static const struct rockchip_lvds_funcs px30_lvds_funcs = {
223 	.enable = px30_lvds_enable,
224 	.disable = px30_lvds_disable,
225 };
226 
227 static const struct rockchip_connector px30_lvds_driver_data = {
228 	 .funcs = &rockchip_lvds_connector_funcs,
229 	 .data = &px30_lvds_funcs,
230 };
231 
232 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe)
233 {
234 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
235 		     RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
236 		     RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
237 }
238 
239 static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
240 {
241 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
242 		     RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
243 }
244 
245 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
246 	.enable = rk3126_lvds_enable,
247 	.disable = rk3126_lvds_disable,
248 };
249 
250 static const struct rockchip_connector rk3126_lvds_driver_data = {
251 	 .funcs = &rockchip_lvds_connector_funcs,
252 	 .data = &rk3126_lvds_funcs,
253 };
254 
255 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe)
256 {
257 	const struct drm_display_mode *mode = lvds->mode;
258 	u32 val;
259 
260 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
261 		     RK3288_LVDS_LCDC_SEL(pipe));
262 
263 	val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
264 	      RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
265 	      RK3288_LVDS_CON_SELECT(lvds->format);
266 
267 	if (lvds->dual_channel) {
268 		u32 h_bp = mode->htotal - mode->hsync_start;
269 
270 		val |= RK3288_LVDS_CON_ENABLE_2(1) |
271 		       RK3288_LVDS_CON_ENABLE_1(1) |
272 		       RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
273 
274 		if (h_bp % 2)
275 			val |= RK3288_LVDS_CON_STARTPHASE(1);
276 		else
277 			val |= RK3288_LVDS_CON_STARTPHASE(0);
278 	} else {
279 		val |= RK3288_LVDS_CON_ENABLE_2(0) |
280 		       RK3288_LVDS_CON_ENABLE_1(1);
281 	}
282 
283 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
284 
285 	rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
286 }
287 
288 static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
289 {
290 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
291 }
292 
293 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
294 	.enable = rk3288_lvds_enable,
295 	.disable = rk3288_lvds_disable,
296 };
297 
298 static const struct rockchip_connector rk3288_lvds_driver_data = {
299 	 .funcs = &rockchip_lvds_connector_funcs,
300 	 .data = &rk3288_lvds_funcs,
301 };
302 
303 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe)
304 {
305 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
306 		     RK3368_LVDS_SELECT(lvds->format) |
307 		     RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
308 		     RK3368_LVDS_P2S_EN(1));
309 }
310 
311 static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
312 {
313 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
314 		     RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
315 }
316 
317 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
318 	.enable = rk3368_lvds_enable,
319 	.disable = rk3368_lvds_disable,
320 };
321 
322 static const struct rockchip_connector rk3368_lvds_driver_data = {
323 	 .funcs = &rockchip_lvds_connector_funcs,
324 	 .data = &rk3368_lvds_funcs,
325 };
326 
327 static void rk3568_lvds_enable(struct rockchip_lvds *lvds, int pipe)
328 {
329 	regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
330 		     RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
331 		     RK3568_LVDS0_DCLK_INV_SEL(1));
332 	regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
333 		     RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
334 }
335 
336 static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
337 {
338 	regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0));
339 }
340 
341 static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
342 	.enable = rk3568_lvds_enable,
343 	.disable = rk3568_lvds_disable,
344 };
345 
346 static const struct rockchip_connector rk3568_lvds_driver_data = {
347 	.funcs = &rockchip_lvds_connector_funcs,
348 	.data = &rk3568_lvds_funcs,
349 };
350 
351 static const struct udevice_id rockchip_lvds_ids[] = {
352 	{
353 		.compatible = "rockchip,px30-lvds",
354 		.data = (ulong)&px30_lvds_driver_data,
355 	},
356 	{
357 		.compatible = "rockchip,rk3126-lvds",
358 		.data = (ulong)&rk3126_lvds_driver_data,
359 	},
360 	{
361 		.compatible = "rockchip,rk3288-lvds",
362 		.data = (ulong)&rk3288_lvds_driver_data,
363 	},
364 	{
365 		.compatible = "rockchip,rk3368-lvds",
366 		.data = (ulong)&rk3368_lvds_driver_data,
367 	},
368 	{
369 		.compatible = "rockchip,rk3568-lvds",
370 		.data = (ulong)&rk3568_lvds_driver_data,
371 	},
372 	{}
373 };
374 
375 U_BOOT_DRIVER(rockchip_lvds) = {
376 	.name = "rockchip_lvds",
377 	.id = UCLASS_DISPLAY,
378 	.of_match = rockchip_lvds_ids,
379 	.probe = rockchip_lvds_probe,
380 	.priv_auto_alloc_size = sizeof(struct rockchip_lvds),
381 };
382