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