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 #include "drm_of.h"
25
26 #define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16))
27
28 #define PX30_GRF_PD_VO_CON1 0x0438
29 #define PX30_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13)
30 #define PX30_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12)
31 #define PX30_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11)
32 #define PX30_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 6, 6)
33 #define PX30_LVDS_VOP_SEL(x) HIWORD_UPDATE(x, 1, 1)
34
35 #define RK3126_GRF_LVDS_CON0 0x0150
36 #define RK3126_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 9, 9)
37 #define RK3126_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 6, 6)
38 #define RK3126_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 3, 3)
39 #define RK3126_LVDS_SELECT(x) HIWORD_UPDATE(x, 2, 1)
40
41 #define RK3288_GRF_SOC_CON6 0x025c
42 #define RK3288_LVDS_LCDC_SEL(x) HIWORD_UPDATE(x, 3, 3)
43 #define RK3288_GRF_SOC_CON7 0x0260
44 #define RK3288_LVDS_PWRDWN(x) HIWORD_UPDATE(x, 15, 15)
45 #define RK3288_LVDS_CON_ENABLE_2(x) HIWORD_UPDATE(x, 12, 12)
46 #define RK3288_LVDS_CON_ENABLE_1(x) HIWORD_UPDATE(x, 11, 11)
47 #define RK3288_LVDS_CON_DEN_POL(x) HIWORD_UPDATE(x, 10, 10)
48 #define RK3288_LVDS_CON_HS_POL(x) HIWORD_UPDATE(x, 9, 9)
49 #define RK3288_LVDS_CON_CLKINV(x) HIWORD_UPDATE(x, 8, 8)
50 #define RK3288_LVDS_CON_STARTPHASE(x) HIWORD_UPDATE(x, 7, 7)
51 #define RK3288_LVDS_CON_TTL_EN(x) HIWORD_UPDATE(x, 6, 6)
52 #define RK3288_LVDS_CON_STARTSEL(x) HIWORD_UPDATE(x, 5, 5)
53 #define RK3288_LVDS_CON_CHASEL(x) HIWORD_UPDATE(x, 4, 4)
54 #define RK3288_LVDS_CON_MSBSEL(x) HIWORD_UPDATE(x, 3, 3)
55 #define RK3288_LVDS_CON_SELECT(x) HIWORD_UPDATE(x, 2, 0)
56
57 #define RK3368_GRF_SOC_CON7 0x041c
58 #define RK3368_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13)
59 #define RK3368_LVDS_MODE_EN(x) HIWORD_UPDATE(x, 12, 12)
60 #define RK3368_LVDS_MSBSEL(x) HIWORD_UPDATE(x, 11, 11)
61 #define RK3368_LVDS_P2S_EN(x) HIWORD_UPDATE(x, 6, 6)
62
63 #define RK3562_GRF_VO_CON0 0x05d0
64 #define RK3562_GRF_VO_CON1 0x05d4
65
66 #define RK3568_GRF_VO_CON0 0x0360
67 #define RK3568_LVDS1_SELECT(x) HIWORD_UPDATE(x, 13, 12)
68 #define RK3568_LVDS1_MSBSEL(x) HIWORD_UPDATE(x, 11, 11)
69 #define RK3568_LVDS0_SELECT(x) HIWORD_UPDATE(x, 5, 4)
70 #define RK3568_LVDS0_MSBSEL(x) HIWORD_UPDATE(x, 3, 3)
71 #define RK3568_GRF_VO_CON2 0x0368
72 #define RK3568_LVDS0_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9)
73 #define RK3568_LVDS0_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8)
74 #define RK3568_LVDS0_MODE_EN(x) HIWORD_UPDATE(x, 1, 1)
75 #define RK3568_LVDS0_P2S_EN(x) HIWORD_UPDATE(x, 0, 0)
76 #define RK3568_GRF_VO_CON3 0x036c
77 #define RK3568_LVDS1_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9)
78 #define RK3568_LVDS1_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8)
79 #define RK3568_LVDS1_MODE_EN(x) HIWORD_UPDATE(x, 1, 1)
80 #define RK3568_LVDS1_P2S_EN(x) HIWORD_UPDATE(x, 0, 0)
81
82 enum lvds_format {
83 LVDS_8BIT_MODE_FORMAT_1,
84 LVDS_8BIT_MODE_FORMAT_2,
85 LVDS_8BIT_MODE_FORMAT_3,
86 LVDS_6BIT_MODE,
87 LVDS_10BIT_MODE_FORMAT_1,
88 LVDS_10BIT_MODE_FORMAT_2,
89 };
90
91 struct rockchip_lvds;
92
93 struct rockchip_lvds_funcs {
94 int (*probe)(struct rockchip_lvds *lvds);
95 void (*enable)(struct rockchip_lvds *lvds, int pipe);
96 void (*disable)(struct rockchip_lvds *lvds);
97 };
98
99 struct rockchip_lvds {
100 struct rockchip_connector connector;
101 int id;
102 struct udevice *dev;
103 struct regmap *grf;
104 struct rockchip_phy *phy;
105 const struct drm_display_mode *mode;
106 const struct rockchip_lvds_funcs *funcs;
107 enum lvds_format format;
108 bool data_swap;
109 bool dual_channel;
110 enum drm_lvds_dual_link_pixels pixel_order;
111 };
112
rockchip_lvds_connector_init(struct rockchip_connector * conn,struct display_state * state)113 static int rockchip_lvds_connector_init(struct rockchip_connector *conn,
114 struct display_state *state)
115 {
116 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
117 struct rockchip_lvds *primary_lvds = NULL;
118 struct connector_state *conn_state = &state->conn_state;
119 struct rockchip_panel *panel = conn->panel;
120
121 lvds->mode = &conn_state->mode;
122 lvds->phy = conn->phy;
123 conn_state->disp_info = rockchip_get_disp_info(conn_state->type, lvds->id);
124
125 if (conn_state->secondary)
126 primary_lvds = dev_get_priv(conn_state->connector->dev);
127
128 switch (panel->bus_format) {
129 case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: /* jeida-18 */
130 lvds->format = LVDS_6BIT_MODE;
131 break;
132 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */
133 lvds->format = LVDS_8BIT_MODE_FORMAT_2;
134 break;
135 case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
136 lvds->format = LVDS_10BIT_MODE_FORMAT_1;
137 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* vesa-18 */
138 lvds->format = LVDS_8BIT_MODE_FORMAT_3;
139 break;
140 case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
141 lvds->format = LVDS_10BIT_MODE_FORMAT_2;
142 break;
143 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */
144 default:
145 lvds->format = LVDS_8BIT_MODE_FORMAT_1;
146 break;
147 }
148
149 conn_state->bus_format = panel->bus_format;
150 conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
151
152 if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) ||
153 (lvds->format == LVDS_10BIT_MODE_FORMAT_2))
154 conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
155
156 conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
157 conn_state->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
158 if (primary_lvds) {
159 conn_state->output_flags = 0;
160 conn_state->output_if = 0;
161
162 switch (primary_lvds->pixel_order) {
163 case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
164 conn_state->output_flags |=
165 ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
166 conn_state->output_if |=
167 VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
168 break;
169 case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
170 conn_state->output_flags |=
171 ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
172 conn_state->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
173 conn_state->output_if |=
174 VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
175 break;
176 case DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
177 conn_state->output_flags |=
178 ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
179 conn_state->output_if |=
180 VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
181 break;
182 case DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
183 conn_state->output_flags |=
184 ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
185 conn_state->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
186 conn_state->output_if |=
187 VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
188 break;
189 default:
190 break;
191 }
192 }
193
194 if (lvds->id)
195 conn_state->output_if |= VOP_OUTPUT_IF_LVDS1;
196 else
197 conn_state->output_if |= VOP_OUTPUT_IF_LVDS0;
198
199 return 0;
200 }
201
rockchip_lvds_connector_enable(struct rockchip_connector * conn,struct display_state * state)202 static int rockchip_lvds_connector_enable(struct rockchip_connector *conn,
203 struct display_state *state)
204 {
205 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
206 struct crtc_state *crtc_state = &state->crtc_state;
207 int pipe = crtc_state->crtc_id;
208 int ret;
209
210 if (lvds->funcs->enable)
211 lvds->funcs->enable(lvds, pipe);
212
213 ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS);
214 if (ret) {
215 dev_err(lvds->dev, "failed to set phy mode: %d\n", ret);
216 return ret;
217 }
218
219 rockchip_phy_power_on(lvds->phy);
220
221 return 0;
222 }
223
rockchip_lvds_connector_disable(struct rockchip_connector * conn,struct display_state * state)224 static int rockchip_lvds_connector_disable(struct rockchip_connector *conn,
225 struct display_state *state)
226 {
227 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
228
229 rockchip_phy_power_off(lvds->phy);
230
231 if (lvds->funcs->disable)
232 lvds->funcs->disable(lvds);
233
234 return 0;
235 }
236
237 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = {
238 .init = rockchip_lvds_connector_init,
239 .enable = rockchip_lvds_connector_enable,
240 .disable = rockchip_lvds_connector_disable,
241 };
242
rockchip_lvds_probe(struct udevice * dev)243 static int rockchip_lvds_probe(struct udevice *dev)
244 {
245 struct rockchip_lvds *lvds = dev_get_priv(dev);
246
247 lvds->dev = dev;
248 lvds->funcs = (const struct rockchip_lvds_funcs *)dev_get_driver_data(dev);
249 lvds->grf = syscon_get_regmap(dev_get_parent(dev));
250 lvds->dual_channel = dev_read_bool(dev, "dual-channel");
251 lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap");
252 lvds->id = of_alias_get_id(ofnode_to_np(dev->node), "lvds");
253 if (lvds->id < 0)
254 lvds->id = 0;
255
256 lvds->pixel_order = -1;
257 if (lvds->funcs->probe)
258 lvds->funcs->probe(lvds);
259
260 rockchip_connector_bind(&lvds->connector, dev, lvds->id, &rockchip_lvds_connector_funcs,
261 NULL, DRM_MODE_CONNECTOR_LVDS);
262
263 return 0;
264 }
265
px30_lvds_enable(struct rockchip_lvds * lvds,int pipe)266 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe)
267 {
268 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
269 PX30_LVDS_SELECT(lvds->format) |
270 PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
271 PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
272 }
273
px30_lvds_disable(struct rockchip_lvds * lvds)274 static void px30_lvds_disable(struct rockchip_lvds *lvds)
275 {
276 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
277 PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
278 }
279
280 static const struct rockchip_lvds_funcs px30_lvds_funcs = {
281 .enable = px30_lvds_enable,
282 .disable = px30_lvds_disable,
283 };
284
rk3126_lvds_enable(struct rockchip_lvds * lvds,int pipe)285 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe)
286 {
287 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
288 RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
289 RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
290 }
291
rk3126_lvds_disable(struct rockchip_lvds * lvds)292 static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
293 {
294 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
295 RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
296 }
297
298 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
299 .enable = rk3126_lvds_enable,
300 .disable = rk3126_lvds_disable,
301 };
302
rk3288_lvds_enable(struct rockchip_lvds * lvds,int pipe)303 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe)
304 {
305 const struct drm_display_mode *mode = lvds->mode;
306 u32 val;
307
308 regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
309 RK3288_LVDS_LCDC_SEL(pipe));
310
311 val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
312 RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
313 RK3288_LVDS_CON_SELECT(lvds->format);
314
315 if (lvds->dual_channel) {
316 u32 h_bp = mode->htotal - mode->hsync_start;
317
318 val |= RK3288_LVDS_CON_ENABLE_2(1) |
319 RK3288_LVDS_CON_ENABLE_1(1) |
320 RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
321
322 if (h_bp % 2)
323 val |= RK3288_LVDS_CON_STARTPHASE(1);
324 else
325 val |= RK3288_LVDS_CON_STARTPHASE(0);
326 } else {
327 val |= RK3288_LVDS_CON_ENABLE_2(0) |
328 RK3288_LVDS_CON_ENABLE_1(1);
329 }
330
331 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
332
333 rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
334 }
335
rk3288_lvds_disable(struct rockchip_lvds * lvds)336 static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
337 {
338 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
339 }
340
341 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
342 .enable = rk3288_lvds_enable,
343 .disable = rk3288_lvds_disable,
344 };
345
rk3368_lvds_enable(struct rockchip_lvds * lvds,int pipe)346 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe)
347 {
348 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
349 RK3368_LVDS_SELECT(lvds->format) |
350 RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
351 RK3368_LVDS_P2S_EN(1));
352 }
353
rk3368_lvds_disable(struct rockchip_lvds * lvds)354 static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
355 {
356 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
357 RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
358 }
359
360 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
361 .enable = rk3368_lvds_enable,
362 .disable = rk3368_lvds_disable,
363 };
364
rk3562_lvds_enable(struct rockchip_lvds * lvds,int pipe)365 static void rk3562_lvds_enable(struct rockchip_lvds *lvds, int pipe)
366 {
367 regmap_write(lvds->grf, RK3562_GRF_VO_CON1,
368 RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
369 RK3568_LVDS0_DCLK_INV_SEL(1));
370 regmap_write(lvds->grf, RK3562_GRF_VO_CON0,
371 RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
372 }
373
rk3562_lvds_disable(struct rockchip_lvds * lvds)374 static void rk3562_lvds_disable(struct rockchip_lvds *lvds)
375 {
376 regmap_write(lvds->grf, RK3562_GRF_VO_CON1, RK3568_LVDS0_MODE_EN(0));
377 }
378
379 static const struct rockchip_lvds_funcs rk3562_lvds_funcs = {
380 .enable = rk3562_lvds_enable,
381 .disable = rk3562_lvds_disable,
382 };
383
rk3568_lvds_probe(struct rockchip_lvds * lvds)384 static int rk3568_lvds_probe(struct rockchip_lvds *lvds)
385 {
386 if (lvds->dual_channel) {
387 const struct device_node *port0, *port1;
388 int pixel_order;
389
390 port1 = of_alias_get_dev("lvds", 1);
391 if (!port1 || !of_device_is_available(port1)) {
392 lvds->pixel_order = 0;
393 return 0;
394 }
395
396 port0 = rockchip_of_graph_get_port_by_id(lvds->dev->node, 1);
397 port1 = rockchip_of_graph_get_port_by_id(np_to_ofnode(port1),
398 1);
399 pixel_order =
400 drm_of_lvds_get_dual_link_pixel_order(port0, port1);
401
402 lvds->pixel_order = pixel_order >= 0 ? pixel_order : 0;
403 }
404
405 return 0;
406 }
407
rk3568_lvds_enable(struct rockchip_lvds * lvds,int pipe)408 static void rk3568_lvds_enable(struct rockchip_lvds *lvds, int pipe)
409 {
410 if (lvds->id) {
411 regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
412 RK3568_LVDS1_MODE_EN(1) | RK3568_LVDS1_P2S_EN(1) |
413 RK3568_LVDS1_DCLK_INV_SEL(1));
414 regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
415 RK3568_LVDS1_SELECT(lvds->format) |
416 RK3568_LVDS1_MSBSEL(1));
417 } else {
418 regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
419 RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
420 RK3568_LVDS0_DCLK_INV_SEL(1));
421 regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
422 RK3568_LVDS0_SELECT(lvds->format) |
423 RK3568_LVDS0_MSBSEL(1));
424 }
425 }
426
rk3568_lvds_disable(struct rockchip_lvds * lvds)427 static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
428 {
429 if (lvds->id)
430 regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
431 RK3568_LVDS1_MODE_EN(0));
432 else
433 regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
434 RK3568_LVDS0_MODE_EN(0));
435 }
436
437 static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
438 .probe = rk3568_lvds_probe,
439 .enable = rk3568_lvds_enable,
440 .disable = rk3568_lvds_disable,
441 };
442
443 static const struct udevice_id rockchip_lvds_ids[] = {
444 {
445 .compatible = "rockchip,px30-lvds",
446 .data = (ulong)&px30_lvds_funcs,
447 },
448 {
449 .compatible = "rockchip,rk3126-lvds",
450 .data = (ulong)&rk3126_lvds_funcs,
451 },
452 {
453 .compatible = "rockchip,rk3288-lvds",
454 .data = (ulong)&rk3288_lvds_funcs,
455 },
456 {
457 .compatible = "rockchip,rk3368-lvds",
458 .data = (ulong)&rk3368_lvds_funcs,
459 },
460 {
461 .compatible = "rockchip,rk3562-lvds",
462 .data = (ulong)&rk3562_lvds_funcs,
463 },
464 {
465 .compatible = "rockchip,rk3568-lvds",
466 .data = (ulong)&rk3568_lvds_funcs,
467 },
468 {}
469 };
470
471 U_BOOT_DRIVER(rockchip_lvds) = {
472 .name = "rockchip_lvds",
473 .id = UCLASS_DISPLAY,
474 .of_match = rockchip_lvds_ids,
475 .probe = rockchip_lvds_probe,
476 .priv_auto_alloc_size = sizeof(struct rockchip_lvds),
477 };
478