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