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 RK3562_GRF_VO_CON0 0x05d0
63 #define RK3562_GRF_VO_CON1 0x05d4
64
65 #define RK3568_GRF_VO_CON0 0x0360
66 #define RK3568_LVDS1_SELECT(x) HIWORD_UPDATE(x, 13, 12)
67 #define RK3568_LVDS1_MSBSEL(x) HIWORD_UPDATE(x, 11, 11)
68 #define RK3568_LVDS0_SELECT(x) HIWORD_UPDATE(x, 5, 4)
69 #define RK3568_LVDS0_MSBSEL(x) HIWORD_UPDATE(x, 3, 3)
70 #define RK3568_GRF_VO_CON2 0x0368
71 #define RK3568_LVDS0_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9)
72 #define RK3568_LVDS0_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8)
73 #define RK3568_LVDS0_MODE_EN(x) HIWORD_UPDATE(x, 1, 1)
74 #define RK3568_LVDS0_P2S_EN(x) HIWORD_UPDATE(x, 0, 0)
75 #define RK3568_GRF_VO_CON3 0x036c
76 #define RK3568_LVDS1_DCLK_INV_SEL(x) HIWORD_UPDATE(x, 9, 9)
77 #define RK3568_LVDS1_DCLK_DIV2_SEL(x) HIWORD_UPDATE(x, 8, 8)
78 #define RK3568_LVDS1_MODE_EN(x) HIWORD_UPDATE(x, 1, 1)
79 #define RK3568_LVDS1_P2S_EN(x) HIWORD_UPDATE(x, 0, 0)
80
81 enum lvds_format {
82 LVDS_8BIT_MODE_FORMAT_1,
83 LVDS_8BIT_MODE_FORMAT_2,
84 LVDS_8BIT_MODE_FORMAT_3,
85 LVDS_6BIT_MODE,
86 LVDS_10BIT_MODE_FORMAT_1,
87 LVDS_10BIT_MODE_FORMAT_2,
88 };
89
90 struct rockchip_lvds;
91
92 struct rockchip_lvds_funcs {
93 void (*enable)(struct rockchip_lvds *lvds, int pipe);
94 void (*disable)(struct rockchip_lvds *lvds);
95 };
96
97 struct rockchip_lvds {
98 struct rockchip_connector connector;
99 int id;
100 struct udevice *dev;
101 struct regmap *grf;
102 struct rockchip_phy *phy;
103 const struct drm_display_mode *mode;
104 const struct rockchip_lvds_funcs *funcs;
105 enum lvds_format format;
106 bool data_swap;
107 bool dual_channel;
108 };
109
rockchip_lvds_connector_init(struct rockchip_connector * conn,struct display_state * state)110 static int rockchip_lvds_connector_init(struct rockchip_connector *conn,
111 struct display_state *state)
112 {
113 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
114 struct connector_state *conn_state = &state->conn_state;
115 struct rockchip_panel *panel = conn->panel;
116
117 lvds->mode = &conn_state->mode;
118 lvds->phy = conn->phy;
119 conn_state->disp_info = rockchip_get_disp_info(conn_state->type, lvds->id);
120
121 switch (panel->bus_format) {
122 case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: /* jeida-18 */
123 lvds->format = LVDS_6BIT_MODE;
124 break;
125 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* jeida-24 */
126 lvds->format = LVDS_8BIT_MODE_FORMAT_2;
127 break;
128 case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
129 lvds->format = LVDS_10BIT_MODE_FORMAT_1;
130 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* vesa-18 */
131 lvds->format = LVDS_8BIT_MODE_FORMAT_3;
132 break;
133 case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
134 lvds->format = LVDS_10BIT_MODE_FORMAT_2;
135 break;
136 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* vesa-24 */
137 default:
138 lvds->format = LVDS_8BIT_MODE_FORMAT_1;
139 break;
140 }
141
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
rockchip_lvds_connector_enable(struct rockchip_connector * conn,struct display_state * state)154 static int rockchip_lvds_connector_enable(struct rockchip_connector *conn,
155 struct display_state *state)
156 {
157 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
158 struct crtc_state *crtc_state = &state->crtc_state;
159 int pipe = crtc_state->crtc_id;
160 int ret;
161
162 if (lvds->funcs->enable)
163 lvds->funcs->enable(lvds, pipe);
164
165 ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS);
166 if (ret) {
167 dev_err(lvds->dev, "failed to set phy mode: %d\n", ret);
168 return ret;
169 }
170
171 rockchip_phy_power_on(lvds->phy);
172
173 return 0;
174 }
175
rockchip_lvds_connector_disable(struct rockchip_connector * conn,struct display_state * state)176 static int rockchip_lvds_connector_disable(struct rockchip_connector *conn,
177 struct display_state *state)
178 {
179 struct rockchip_lvds *lvds = dev_get_priv(conn->dev);
180
181 rockchip_phy_power_off(lvds->phy);
182
183 if (lvds->funcs->disable)
184 lvds->funcs->disable(lvds);
185
186 return 0;
187 }
188
189 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = {
190 .init = rockchip_lvds_connector_init,
191 .enable = rockchip_lvds_connector_enable,
192 .disable = rockchip_lvds_connector_disable,
193 };
194
rockchip_lvds_probe(struct udevice * dev)195 static int rockchip_lvds_probe(struct udevice *dev)
196 {
197 struct rockchip_lvds *lvds = dev_get_priv(dev);
198
199 lvds->dev = dev;
200 lvds->funcs = (const struct rockchip_lvds_funcs *)dev_get_driver_data(dev);
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 lvds->id = of_alias_get_id(ofnode_to_np(dev->node), "lvds");
205 if (lvds->id < 0)
206 lvds->id = 0;
207
208 rockchip_connector_bind(&lvds->connector, dev, lvds->id, &rockchip_lvds_connector_funcs,
209 NULL, DRM_MODE_CONNECTOR_LVDS);
210
211 return 0;
212 }
213
px30_lvds_enable(struct rockchip_lvds * lvds,int pipe)214 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe)
215 {
216 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
217 PX30_LVDS_SELECT(lvds->format) |
218 PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
219 PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
220 }
221
px30_lvds_disable(struct rockchip_lvds * lvds)222 static void px30_lvds_disable(struct rockchip_lvds *lvds)
223 {
224 regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
225 PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
226 }
227
228 static const struct rockchip_lvds_funcs px30_lvds_funcs = {
229 .enable = px30_lvds_enable,
230 .disable = px30_lvds_disable,
231 };
232
rk3126_lvds_enable(struct rockchip_lvds * lvds,int pipe)233 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe)
234 {
235 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
236 RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
237 RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
238 }
239
rk3126_lvds_disable(struct rockchip_lvds * lvds)240 static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
241 {
242 regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
243 RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
244 }
245
246 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
247 .enable = rk3126_lvds_enable,
248 .disable = rk3126_lvds_disable,
249 };
250
rk3288_lvds_enable(struct rockchip_lvds * lvds,int pipe)251 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe)
252 {
253 const struct drm_display_mode *mode = lvds->mode;
254 u32 val;
255
256 regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
257 RK3288_LVDS_LCDC_SEL(pipe));
258
259 val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
260 RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
261 RK3288_LVDS_CON_SELECT(lvds->format);
262
263 if (lvds->dual_channel) {
264 u32 h_bp = mode->htotal - mode->hsync_start;
265
266 val |= RK3288_LVDS_CON_ENABLE_2(1) |
267 RK3288_LVDS_CON_ENABLE_1(1) |
268 RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
269
270 if (h_bp % 2)
271 val |= RK3288_LVDS_CON_STARTPHASE(1);
272 else
273 val |= RK3288_LVDS_CON_STARTPHASE(0);
274 } else {
275 val |= RK3288_LVDS_CON_ENABLE_2(0) |
276 RK3288_LVDS_CON_ENABLE_1(1);
277 }
278
279 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
280
281 rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
282 }
283
rk3288_lvds_disable(struct rockchip_lvds * lvds)284 static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
285 {
286 regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
287 }
288
289 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
290 .enable = rk3288_lvds_enable,
291 .disable = rk3288_lvds_disable,
292 };
293
rk3368_lvds_enable(struct rockchip_lvds * lvds,int pipe)294 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe)
295 {
296 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
297 RK3368_LVDS_SELECT(lvds->format) |
298 RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
299 RK3368_LVDS_P2S_EN(1));
300 }
301
rk3368_lvds_disable(struct rockchip_lvds * lvds)302 static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
303 {
304 regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
305 RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
306 }
307
308 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
309 .enable = rk3368_lvds_enable,
310 .disable = rk3368_lvds_disable,
311 };
312
rk3562_lvds_enable(struct rockchip_lvds * lvds,int pipe)313 static void rk3562_lvds_enable(struct rockchip_lvds *lvds, int pipe)
314 {
315 regmap_write(lvds->grf, RK3562_GRF_VO_CON1,
316 RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
317 RK3568_LVDS0_DCLK_INV_SEL(1));
318 regmap_write(lvds->grf, RK3562_GRF_VO_CON0,
319 RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
320 }
321
rk3562_lvds_disable(struct rockchip_lvds * lvds)322 static void rk3562_lvds_disable(struct rockchip_lvds *lvds)
323 {
324 regmap_write(lvds->grf, RK3562_GRF_VO_CON1, RK3568_LVDS0_MODE_EN(0));
325 }
326
327 static const struct rockchip_lvds_funcs rk3562_lvds_funcs = {
328 .enable = rk3562_lvds_enable,
329 .disable = rk3562_lvds_disable,
330 };
331
rk3568_lvds_enable(struct rockchip_lvds * lvds,int pipe)332 static void rk3568_lvds_enable(struct rockchip_lvds *lvds, int pipe)
333 {
334 regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
335 RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
336 RK3568_LVDS0_DCLK_INV_SEL(1));
337 regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
338 RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
339 }
340
rk3568_lvds_disable(struct rockchip_lvds * lvds)341 static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
342 {
343 regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0));
344 }
345
346 static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
347 .enable = rk3568_lvds_enable,
348 .disable = rk3568_lvds_disable,
349 };
350
351 static const struct udevice_id rockchip_lvds_ids[] = {
352 {
353 .compatible = "rockchip,px30-lvds",
354 .data = (ulong)&px30_lvds_funcs,
355 },
356 {
357 .compatible = "rockchip,rk3126-lvds",
358 .data = (ulong)&rk3126_lvds_funcs,
359 },
360 {
361 .compatible = "rockchip,rk3288-lvds",
362 .data = (ulong)&rk3288_lvds_funcs,
363 },
364 {
365 .compatible = "rockchip,rk3368-lvds",
366 .data = (ulong)&rk3368_lvds_funcs,
367 },
368 {
369 .compatible = "rockchip,rk3562-lvds",
370 .data = (ulong)&rk3562_lvds_funcs,
371 },
372 {
373 .compatible = "rockchip,rk3568-lvds",
374 .data = (ulong)&rk3568_lvds_funcs,
375 },
376 {}
377 };
378
379 U_BOOT_DRIVER(rockchip_lvds) = {
380 .name = "rockchip_lvds",
381 .id = UCLASS_DISPLAY,
382 .of_match = rockchip_lvds_ids,
383 .probe = rockchip_lvds_probe,
384 .priv_auto_alloc_size = sizeof(struct rockchip_lvds),
385 };
386