xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_lvds.c (revision 73b4df6a98d2d973cbf1e2b18947abbdbdb82bc1)
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 enum lvds_format {
62 	LVDS_8BIT_MODE_FORMAT_1,
63 	LVDS_8BIT_MODE_FORMAT_2,
64 	LVDS_8BIT_MODE_FORMAT_3,
65 	LVDS_6BIT_MODE,
66 	LVDS_10BIT_MODE_FORMAT_1,
67 	LVDS_10BIT_MODE_FORMAT_2,
68 };
69 
70 struct rockchip_lvds;
71 
72 struct rockchip_lvds_funcs {
73 	void (*enable)(struct rockchip_lvds *lvds, int pipe);
74 	void (*disable)(struct rockchip_lvds *lvds);
75 };
76 
77 struct rockchip_lvds {
78 	struct udevice *dev;
79 	struct regmap *grf;
80 	struct rockchip_phy *phy;
81 	const struct drm_display_mode *mode;
82 	const struct rockchip_lvds_funcs *funcs;
83 	enum lvds_format format;
84 	bool data_swap;
85 	bool dual_channel;
86 };
87 
88 static inline struct rockchip_lvds *state_to_lvds(struct display_state *state)
89 {
90 	struct connector_state *conn_state = &state->conn_state;
91 
92 	return dev_get_priv(conn_state->dev);
93 }
94 
95 static int rockchip_lvds_connector_init(struct display_state *state)
96 {
97 	struct rockchip_lvds *lvds = state_to_lvds(state);
98 	struct connector_state *conn_state = &state->conn_state;
99 	struct rockchip_panel *panel = state_get_panel(state);
100 
101 	lvds->mode = &conn_state->mode;
102 	lvds->phy = conn_state->phy;
103 
104 	switch (panel->bus_format) {
105 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:	/* jeida-18 */
106 		lvds->format = LVDS_6BIT_MODE;
107 		break;
108 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:	/* jeida-24 */
109 		lvds->format = LVDS_8BIT_MODE_FORMAT_2;
110 		break;
111 	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
112 		lvds->format = LVDS_10BIT_MODE_FORMAT_1;
113 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
114 		lvds->format = LVDS_8BIT_MODE_FORMAT_3;
115 		break;
116 	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
117 		lvds->format = LVDS_10BIT_MODE_FORMAT_2;
118 		break;
119 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:	/* vesa-24 */
120 	default:
121 		lvds->format = LVDS_8BIT_MODE_FORMAT_1;
122 		break;
123 	}
124 
125 	conn_state->type = DRM_MODE_CONNECTOR_LVDS;
126 	conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
127 
128 	if ((lvds->format == LVDS_10BIT_MODE_FORMAT_1) ||
129 		(lvds->format == LVDS_10BIT_MODE_FORMAT_2))
130 		conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
131 
132 	conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
133 
134 	return 0;
135 }
136 
137 static int rockchip_lvds_connector_enable(struct display_state *state)
138 {
139 	struct rockchip_lvds *lvds = state_to_lvds(state);
140 	struct crtc_state *crtc_state = &state->crtc_state;
141 	int pipe = crtc_state->crtc_id;
142 	int ret;
143 
144 	if (lvds->funcs->enable)
145 		lvds->funcs->enable(lvds, pipe);
146 
147 	ret = rockchip_phy_set_mode(lvds->phy, PHY_MODE_VIDEO_LVDS);
148 	if (ret) {
149 		dev_err(lvds->dev, "failed to set phy mode: %d\n", ret);
150 		return ret;
151 	}
152 
153 	rockchip_phy_power_on(lvds->phy);
154 
155 	return 0;
156 }
157 
158 static int rockchip_lvds_connector_disable(struct display_state *state)
159 {
160 	struct rockchip_lvds *lvds = state_to_lvds(state);
161 
162 	rockchip_phy_power_off(lvds->phy);
163 
164 	if (lvds->funcs->disable)
165 		lvds->funcs->disable(lvds);
166 
167 	return 0;
168 }
169 
170 static const struct rockchip_connector_funcs rockchip_lvds_connector_funcs = {
171 	.init = rockchip_lvds_connector_init,
172 	.enable = rockchip_lvds_connector_enable,
173 	.disable = rockchip_lvds_connector_disable,
174 };
175 
176 static int rockchip_lvds_probe(struct udevice *dev)
177 {
178 	struct rockchip_lvds *lvds = dev_get_priv(dev);
179 	const struct rockchip_connector *connector =
180 		(const struct rockchip_connector *)dev_get_driver_data(dev);
181 
182 	lvds->dev = dev;
183 	lvds->funcs = connector->data;
184 	lvds->grf = syscon_get_regmap(dev_get_parent(dev));
185 	lvds->dual_channel = dev_read_bool(dev, "dual-channel");
186 	lvds->data_swap = dev_read_bool(dev, "rockchip,data-swap");
187 
188 	return 0;
189 }
190 
191 static void px30_lvds_enable(struct rockchip_lvds *lvds, int pipe)
192 {
193 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
194 		     PX30_LVDS_SELECT(lvds->format) |
195 		     PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
196 		     PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
197 }
198 
199 static void px30_lvds_disable(struct rockchip_lvds *lvds)
200 {
201 	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
202 		     PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
203 }
204 
205 static const struct rockchip_lvds_funcs px30_lvds_funcs = {
206 	.enable = px30_lvds_enable,
207 	.disable = px30_lvds_disable,
208 };
209 
210 static const struct rockchip_connector px30_lvds_driver_data = {
211 	 .funcs = &rockchip_lvds_connector_funcs,
212 	 .data = &px30_lvds_funcs,
213 };
214 
215 static void rk3126_lvds_enable(struct rockchip_lvds *lvds, int pipe)
216 {
217 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
218 		     RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
219 		     RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
220 }
221 
222 static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
223 {
224 	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
225 		     RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
226 }
227 
228 static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
229 	.enable = rk3126_lvds_enable,
230 	.disable = rk3126_lvds_disable,
231 };
232 
233 static const struct rockchip_connector rk3126_lvds_driver_data = {
234 	 .funcs = &rockchip_lvds_connector_funcs,
235 	 .data = &rk3126_lvds_funcs,
236 };
237 
238 static void rk3288_lvds_enable(struct rockchip_lvds *lvds, int pipe)
239 {
240 	const struct drm_display_mode *mode = lvds->mode;
241 	u32 val;
242 
243 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
244 		     RK3288_LVDS_LCDC_SEL(pipe));
245 
246 	val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
247 	      RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
248 	      RK3288_LVDS_CON_SELECT(lvds->format);
249 
250 	if (lvds->dual_channel) {
251 		u32 h_bp = mode->htotal - mode->hsync_start;
252 
253 		val |= RK3288_LVDS_CON_ENABLE_2(1) |
254 		       RK3288_LVDS_CON_ENABLE_1(1) |
255 		       RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
256 
257 		if (h_bp % 2)
258 			val |= RK3288_LVDS_CON_STARTPHASE(1);
259 		else
260 			val |= RK3288_LVDS_CON_STARTPHASE(0);
261 	} else {
262 		val |= RK3288_LVDS_CON_ENABLE_2(0) |
263 		       RK3288_LVDS_CON_ENABLE_1(1);
264 	}
265 
266 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
267 
268 	rockchip_phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
269 }
270 
271 static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
272 {
273 	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
274 }
275 
276 static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
277 	.enable = rk3288_lvds_enable,
278 	.disable = rk3288_lvds_disable,
279 };
280 
281 static const struct rockchip_connector rk3288_lvds_driver_data = {
282 	 .funcs = &rockchip_lvds_connector_funcs,
283 	 .data = &rk3288_lvds_funcs,
284 };
285 
286 static void rk3368_lvds_enable(struct rockchip_lvds *lvds, int pipe)
287 {
288 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
289 		     RK3368_LVDS_SELECT(lvds->format) |
290 		     RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
291 		     RK3368_LVDS_P2S_EN(1));
292 }
293 
294 static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
295 {
296 	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
297 		     RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
298 }
299 
300 static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
301 	.enable = rk3368_lvds_enable,
302 	.disable = rk3368_lvds_disable,
303 };
304 
305 static const struct rockchip_connector rk3368_lvds_driver_data = {
306 	 .funcs = &rockchip_lvds_connector_funcs,
307 	 .data = &rk3368_lvds_funcs,
308 };
309 
310 static const struct udevice_id rockchip_lvds_ids[] = {
311 	{
312 		.compatible = "rockchip,px30-lvds",
313 		.data = (ulong)&px30_lvds_driver_data,
314 	},
315 	{
316 		.compatible = "rockchip,rk3126-lvds",
317 		.data = (ulong)&rk3126_lvds_driver_data,
318 	},
319 	{
320 		.compatible = "rockchip,rk3288-lvds",
321 		.data = (ulong)&rk3288_lvds_driver_data,
322 	},
323 	{
324 		.compatible = "rockchip,rk3368-lvds",
325 		.data = (ulong)&rk3368_lvds_driver_data,
326 	},
327 	{}
328 };
329 
330 U_BOOT_DRIVER(rockchip_lvds) = {
331 	.name = "rockchip_lvds",
332 	.id = UCLASS_DISPLAY,
333 	.of_match = rockchip_lvds_ids,
334 	.probe = rockchip_lvds_probe,
335 	.priv_auto_alloc_size = sizeof(struct rockchip_lvds),
336 };
337