xref: /openbsd-src/sys/dev/fdt/rkvop.c (revision 68dd5bb1859285b71cb62a10bf107b8ad54064d9)
1 /* $OpenBSD: rkvop.c,v 1.7 2023/01/01 01:34:33 jsg Exp $ */
2 /* $NetBSD: rk_vop.c,v 1.6 2020/01/05 12:14:35 mrg Exp $ */
3 /*-
4  * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/device.h>
31 #include <sys/systm.h>
32 
33 #include <machine/bus.h>
34 #include <machine/fdt.h>
35 
36 #include <dev/ofw/openfirm.h>
37 #include <dev/ofw/ofw_clock.h>
38 #include <dev/ofw/ofw_gpio.h>
39 #include <dev/ofw/ofw_misc.h>
40 #include <dev/ofw/fdt.h>
41 
42 #include <drm/drm_atomic.h>
43 #include <drm/drm_atomic_helper.h>
44 #include <drm/drm_fourcc.h>
45 #include <drm/drm_crtc.h>
46 #include <drm/drm_crtc_helper.h>
47 #include <drm/drm_plane_helper.h>
48 
49 #include <dev/fdt/rkdrm.h>
50 
51 #define	VOP_REG_CFG_DONE		0x0000
52 #define	 REG_LOAD_EN			(1 << 0)
53 #define	VOP_SYS_CTRL			0x0008
54 #define	 VOP_STANDBY_EN			(1 << 22)
55 #define	 MIPI_OUT_EN			(1 << 15)
56 #define	 EDP_OUT_EN			(1 << 14)
57 #define	 HDMI_OUT_EN			(1 << 13)
58 #define	 RGB_OUT_EN			(1 << 12)
59 #define	VOP_DSP_CTRL0			0x0010
60 #define	 DSP_OUT_MODE(x)		((x) << 0)
61 #define	  DSP_OUT_MODE_MASK		0xf
62 #define	  DSP_OUT_MODE_RGB888		0
63 #define	  DSP_OUT_MODE_RGBaaa		15
64 #define	VOP_DSP_CTRL1			0x0014
65 #define	VOP_WIN0_CTRL			0x0030
66 #define	 WIN0_LB_MODE(x)		((x) << 5)
67 #define	  WIN0_LB_MODE_MASK		0x7
68 #define	  WIN0_LB_MODE_RGB_3840X2	2
69 #define	  WIN0_LB_MODE_RGB_2560X4	3
70 #define	  WIN0_LB_MODE_RGB_1920X5	4
71 #define	  WIN0_LB_MODE_RGB_1280X8	5
72 #define	 WIN0_DATA_FMT(x)		((x) << 1)
73 #define	  WIN0_DATA_FMT_MASK		0x7
74 #define	  WIN0_DATA_FMT_ARGB888		0
75 #define	 WIN0_EN			(1 << 0)
76 #define	VOP_WIN0_COLOR_KEY		0x0038
77 #define	VOP_WIN0_VIR			0x003c
78 #define	 WIN0_VIR_STRIDE(x)		(((x) & 0x3fff) << 0)
79 #define	VOP_WIN0_YRGB_MST		0x0040
80 #define	VOP_WIN0_ACT_INFO		0x0048
81 #define	 WIN0_ACT_HEIGHT(x)		(((x) & 0x1fff) << 16)
82 #define	 WIN0_ACT_WIDTH(x)		(((x) & 0x1fff) << 0)
83 #define	VOP_WIN0_DSP_INFO		0x004c
84 #define	 WIN0_DSP_HEIGHT(x)		(((x) & 0xfff) << 16)
85 #define	 WIN0_DSP_WIDTH(x)		(((x) & 0xfff) << 0)
86 #define	VOP_WIN0_DSP_ST			0x0050
87 #define	 WIN0_DSP_YST(x)		(((x) & 0x1fff) << 16)
88 #define	 WIN0_DSP_XST(x)		(((x) & 0x1fff) << 0)
89 #define	VOP_POST_DSP_HACT_INFO		0x0170
90 #define	 DSP_HACT_ST_POST(x)		(((x) & 0x1fff) << 16)
91 #define	 DSP_HACT_END_POST(x)		(((x) & 0x1fff) << 0)
92 #define	VOP_POST_DSP_VACT_INFO		0x0174
93 #define	 DSP_VACT_ST_POST(x)		(((x) & 0x1fff) << 16)
94 #define	 DSP_VACT_END_POST(x)		(((x) & 0x1fff) << 0)
95 #define	VOP_DSP_HTOTAL_HS_END		0x0188
96 #define	 DSP_HS_END(x)			(((x) & 0x1fff) << 16)
97 #define	 DSP_HTOTAL(x)			(((x) & 0x1fff) << 0)
98 #define	VOP_DSP_HACT_ST_END		0x018c
99 #define	 DSP_HACT_ST(x)			(((x) & 0x1fff) << 16)
100 #define	 DSP_HACT_END(x)		(((x) & 0x1fff) << 0)
101 #define	VOP_DSP_VTOTAL_VS_END		0x0190
102 #define	 DSP_VS_END(x)			(((x) & 0x1fff) << 16)
103 #define	 DSP_VTOTAL(x)			(((x) & 0x1fff) << 0)
104 #define	VOP_DSP_VACT_ST_END		0x0194
105 #define	 DSP_VACT_ST(x)			(((x) & 0x1fff) << 16)
106 #define	 DSP_VACT_END(x)		(((x) & 0x1fff) << 0)
107 
108 /*
109  * Polarity fields are in different locations depending on SoC and output type,
110  * but always in the same order.
111  */
112 #define	DSP_DCLK_POL			(1 << 3)
113 #define	DSP_DEN_POL			(1 << 2)
114 #define	DSP_VSYNC_POL			(1 << 1)
115 #define	DSP_HSYNC_POL			(1 << 0)
116 
117 enum vop_ep_type {
118 	VOP_EP_MIPI,
119 	VOP_EP_EDP,
120 	VOP_EP_HDMI,
121 	VOP_EP_MIPI1,
122 	VOP_EP_DP,
123 	VOP_NEP
124 };
125 
126 struct rkvop_softc;
127 struct rkvop_config;
128 
129 struct rkvop_crtc {
130 	struct drm_crtc		base;
131 	struct rkvop_softc	*sc;
132 };
133 
134 struct rkvop_softc {
135 	struct device		sc_dev;
136 	bus_space_tag_t		sc_iot;
137 	bus_space_handle_t	sc_ioh;
138 	int			sc_node;
139 	struct rkvop_config	*sc_conf;
140 
141 	struct rkvop_crtc	sc_crtc;
142 	struct drm_plane	sc_plane;
143 	struct device_ports	sc_ports;
144 };
145 
146 #define	to_rkvop_crtc(x)	container_of(x, struct rkvop_crtc, base)
147 
148 #define	HREAD4(sc, reg)				\
149 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
150 #define	HWRITE4(sc, reg, val)			\
151 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
152 
153 struct rkvop_config {
154 	char		*descr;
155 	u_int		out_mode;
156 	void		(*init)(struct rkvop_softc *);
157 	void		(*set_polarity)(struct rkvop_softc *,
158 					enum vop_ep_type, uint32_t);
159 };
160 
161 int rkvop_match(struct device *, void *, void *);
162 void rkvop_attach(struct device *, struct device *, void *);
163 
164 void rkvop_dpms(struct drm_crtc *, int);
165 bool rkvop_mode_fixup(struct drm_crtc *, const struct drm_display_mode *,
166     struct drm_display_mode *);
167 
168 void rk3399_vop_init(struct rkvop_softc *);
169 void rk3399_vop_set_polarity(struct rkvop_softc *, enum vop_ep_type, uint32_t);
170 
171 int rkvop_ep_activate(void *, struct endpoint *, void *);
172 void *rkvop_ep_get_cookie(void *, struct endpoint *);
173 
174 struct rkvop_config rk3399_vop_big_config = {
175 	.descr = "RK3399 VOPB",
176 	.out_mode = DSP_OUT_MODE_RGBaaa,
177 	.init = rk3399_vop_init,
178 	.set_polarity = rk3399_vop_set_polarity,
179 };
180 
181 struct rkvop_config rk3399_vop_lit_config = {
182 	.descr = "RK3399 VOPL",
183 	.out_mode = DSP_OUT_MODE_RGB888,
184 	.init = rk3399_vop_init,
185 	.set_polarity = rk3399_vop_set_polarity,
186 };
187 
188 const struct cfattach rkvop_ca = {
189 	sizeof (struct rkvop_softc), rkvop_match, rkvop_attach
190 };
191 
192 struct cfdriver rkvop_cd = {
193 	NULL, "rkvop", DV_DULL
194 };
195 
196 int
197 rkvop_match(struct device *parent, void *match, void *aux)
198 {
199 	struct fdt_attach_args *faa = aux;
200 
201 	return (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-big") ||
202 	    OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-lit"));
203 }
204 
205 void
206 rkvop_attach(struct device *parent, struct device *self, void *aux)
207 {
208 	struct rkvop_softc *sc = (struct rkvop_softc *)self;
209 	struct fdt_attach_args *faa = aux;
210 	int i, port, ep, nep;
211 	paddr_t paddr;
212 
213 	if (faa->fa_nreg < 1)
214 		return;
215 
216 	clock_set_assigned(faa->fa_node);
217 
218 	reset_deassert(faa->fa_node, "axi");
219 	reset_deassert(faa->fa_node, "ahb");
220 	reset_deassert(faa->fa_node, "dclk");
221 
222 	clock_enable(faa->fa_node, "aclk_vop");
223 	clock_enable(faa->fa_node, "hclk_vop");
224 	clock_enable(faa->fa_node, "dclk_vop");
225 
226 	sc->sc_iot = faa->fa_iot;
227 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
228 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
229 		printf(": can't map registers\n");
230 		return;
231 	}
232 	sc->sc_node = faa->fa_node;
233 
234 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-big"))
235 		sc->sc_conf = &rk3399_vop_big_config;
236 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-lit"))
237 		sc->sc_conf = &rk3399_vop_lit_config;
238 
239 	printf(": %s\n", sc->sc_conf->descr);
240 
241 	if (sc->sc_conf->init != NULL)
242 		sc->sc_conf->init(sc);
243 
244 	sc->sc_ports.dp_node = faa->fa_node;
245 	sc->sc_ports.dp_cookie = sc;
246 	sc->sc_ports.dp_ep_activate = rkvop_ep_activate;
247 	sc->sc_ports.dp_ep_get_cookie = rkvop_ep_get_cookie;
248 	device_ports_register(&sc->sc_ports, EP_DRM_CRTC);
249 
250 	paddr = HREAD4(sc, VOP_WIN0_YRGB_MST);
251 	if (paddr != 0) {
252 		uint32_t stride, height;
253 
254 		stride = HREAD4(sc, VOP_WIN0_VIR) & 0xffff;
255 		height = (HREAD4(sc, VOP_WIN0_DSP_INFO) >> 16) + 1;
256 		rasops_claim_framebuffer(paddr, height * stride * 4, self);
257 	}
258 }
259 
260 int
261 rkvop_plane_check(struct drm_plane *plane, struct drm_atomic_state *das)
262 {
263 	struct drm_crtc_state *crtc_state;
264 	struct drm_plane_state *state = drm_atomic_get_new_plane_state(das,
265 	    plane);
266 
267 	if (state->crtc == NULL)
268 		return 0;
269 
270 	crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
271 	if (IS_ERR(crtc_state))
272 		return PTR_ERR(crtc_state);
273 
274 	return drm_atomic_helper_check_plane_state(state, crtc_state,
275 	    DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING,
276 	    false, true);
277 }
278 
279 void
280 rkvop_plane_update(struct drm_plane *plane, struct drm_atomic_state *das)
281 {
282 	struct drm_plane_state *old_state = drm_atomic_get_new_plane_state(das,
283 	    plane);
284 	struct drm_plane_state *state = plane->state;
285 	struct drm_crtc *crtc = state->crtc;
286 	struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
287 	struct rkvop_softc *sc = rkcrtc->sc;
288 	struct drm_framebuffer *fb = state->fb;
289 	struct rkdrm_framebuffer *rkfb = to_rkdrm_framebuffer(fb);
290 	struct drm_rect *src = &state->src;
291 	struct drm_rect *dst = &state->dst;
292 	u_int act_width = drm_rect_width(src) >> 16;
293 	u_int act_height = drm_rect_height(src) >> 16;
294 	u_int htotal = crtc->mode.htotal;
295 	u_int vtotal = crtc->mode.vtotal;
296 	u_int hsync_start = crtc->mode.hsync_start;
297 	u_int vsync_start = crtc->mode.vsync_start;
298 	uint64_t paddr;
299 	u_int lb_mode;
300 	uint32_t val;
301 
302 	val = WIN0_ACT_WIDTH(act_width - 1) |
303 	      WIN0_ACT_HEIGHT(act_height - 1);
304 	HWRITE4(sc, VOP_WIN0_ACT_INFO, val);
305 
306 	val = WIN0_DSP_WIDTH(drm_rect_width(dst) - 1) |
307 	      WIN0_DSP_HEIGHT(drm_rect_height(dst) - 1);
308 	HWRITE4(sc, VOP_WIN0_DSP_INFO, val);
309 
310 	val = WIN0_DSP_XST(dst->x1 + htotal - hsync_start) |
311 	      WIN0_DSP_YST(dst->y1 + vtotal - vsync_start);
312 	HWRITE4(sc, VOP_WIN0_DSP_ST, val);
313 
314 	HWRITE4(sc, VOP_WIN0_COLOR_KEY, 0);
315 
316 	if (act_width > 2560)
317 		lb_mode = WIN0_LB_MODE_RGB_3840X2;
318 	else if (act_width > 1920)
319 		lb_mode = WIN0_LB_MODE_RGB_2560X4;
320 	else if (act_width > 1280)
321 		lb_mode = WIN0_LB_MODE_RGB_1920X5;
322 	else
323 		lb_mode = WIN0_LB_MODE_RGB_1280X8;
324 
325 	val = WIN0_LB_MODE(lb_mode) |
326 	      WIN0_DATA_FMT(WIN0_DATA_FMT_ARGB888) |
327 	      WIN0_EN;
328 	HWRITE4(sc, VOP_WIN0_CTRL, val);
329 
330 	val = WIN0_VIR_STRIDE(fb->pitches[0] / 4);
331 	HWRITE4(sc, VOP_WIN0_VIR, val);
332 
333 	/* Framebuffer start address */
334 	paddr = (uint64_t)rkfb->obj->dmamap->dm_segs[0].ds_addr;
335 	paddr += (src->y1 >> 16) * fb->pitches[0];
336 	paddr += (src->x1 >> 16) * fb->format->cpp[0];
337 	KASSERT((paddr & ~0xffffffff) == 0);
338 	HWRITE4(sc, VOP_WIN0_YRGB_MST, (uint32_t)paddr);
339 }
340 
341 struct drm_plane_helper_funcs rkvop_plane_helper_funcs = {
342 	.atomic_check = rkvop_plane_check,
343 	.atomic_update = rkvop_plane_update,
344 };
345 
346 struct drm_plane_funcs rkvop_plane_funcs = {
347 	.update_plane = drm_atomic_helper_update_plane,
348 	.disable_plane = drm_atomic_helper_disable_plane,
349 	.destroy = drm_plane_cleanup,
350 	.reset = drm_atomic_helper_plane_reset,
351 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
352 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
353 };
354 
355 void
356 rkvop_dpms(struct drm_crtc *crtc, int mode)
357 {
358 	struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
359 	struct rkvop_softc *sc = rkcrtc->sc;
360 	uint32_t val;
361 
362 	val = HREAD4(sc, VOP_SYS_CTRL);
363 
364 	switch (mode) {
365 	case DRM_MODE_DPMS_ON:
366 		val &= ~VOP_STANDBY_EN;
367 		break;
368 	case DRM_MODE_DPMS_STANDBY:
369 	case DRM_MODE_DPMS_SUSPEND:
370 	case DRM_MODE_DPMS_OFF:
371 		val |= VOP_STANDBY_EN;
372 		break;
373 	}
374 
375 	HWRITE4(sc, VOP_SYS_CTRL, val);
376 
377 	/* Commit settings */
378 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
379 }
380 
381 bool
382 rkvop_mode_fixup(struct drm_crtc *crtc,
383    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
384 {
385 	return true;
386 }
387 
388 int
389 rkvop_crtc_check(struct drm_crtc *crtc, struct drm_atomic_state *das)
390 {
391 	struct drm_crtc_state *state = drm_atomic_get_new_crtc_state(das,
392 	    crtc);
393 	bool enabled = state->plane_mask & drm_plane_mask(crtc->primary);
394 
395 	if (enabled != state->enable)
396 		return -EINVAL;
397 
398 	return drm_atomic_add_affected_planes(state->state, crtc);
399 }
400 
401 void
402 rkvop_crtc_enable(struct drm_crtc *crtc, struct drm_atomic_state *das)
403 {
404 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(das,
405 	    crtc);
406 	struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
407 	struct rkvop_softc *sc = rkcrtc->sc;
408 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
409 	uint32_t val;
410 	u_int pol;
411 	int connector_type = 0;
412 	struct drm_connector *connector;
413 	struct drm_connector_list_iter conn_iter;
414 
415 	u_int hactive = adjusted_mode->hdisplay;
416 	u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
417 	u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end;
418 	u_int hfront_porch = adjusted_mode->hsync_start - adjusted_mode->hdisplay;
419 
420 	u_int vactive = adjusted_mode->vdisplay;
421 	u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
422 	u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end;
423 	u_int vfront_porch = adjusted_mode->vsync_start - adjusted_mode->vdisplay;
424 
425 	clock_set_frequency(sc->sc_node, "dclk_vop", adjusted_mode->clock * 1000);
426 
427 	pol = DSP_DCLK_POL;
428 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) != 0)
429 		pol |= DSP_HSYNC_POL;
430 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) != 0)
431 		pol |= DSP_VSYNC_POL;
432 
433 	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
434 	drm_for_each_connector_iter(connector, &conn_iter) {
435 		if ((connector->encoder) == NULL)
436 			continue;
437 		if (connector->encoder->crtc == crtc) {
438 			connector_type = connector->connector_type;
439 			break;
440 		}
441 	}
442 
443 	switch (connector_type) {
444 	case DRM_MODE_CONNECTOR_HDMIA:
445 		sc->sc_conf->set_polarity(sc, VOP_EP_HDMI, pol);
446 		break;
447 	case DRM_MODE_CONNECTOR_eDP:
448 		sc->sc_conf->set_polarity(sc, VOP_EP_EDP, pol);
449 		break;
450 	}
451 
452 	val = HREAD4(sc, VOP_SYS_CTRL);
453 	val &= ~VOP_STANDBY_EN;
454 	val &= ~(MIPI_OUT_EN|EDP_OUT_EN|HDMI_OUT_EN|RGB_OUT_EN);
455 
456 	switch (connector_type) {
457 	case DRM_MODE_CONNECTOR_HDMIA:
458 		val |= HDMI_OUT_EN;
459 		break;
460 	case DRM_MODE_CONNECTOR_eDP:
461 		val |= EDP_OUT_EN;
462 		break;
463 	}
464 	HWRITE4(sc, VOP_SYS_CTRL, val);
465 
466 	val = HREAD4(sc, VOP_DSP_CTRL0);
467 	val &= ~DSP_OUT_MODE(DSP_OUT_MODE_MASK);
468 	val |= DSP_OUT_MODE(sc->sc_conf->out_mode);
469 	HWRITE4(sc, VOP_DSP_CTRL0, val);
470 
471 	val = DSP_HACT_ST_POST(hsync_len + hback_porch) |
472 	      DSP_HACT_END_POST(hsync_len + hback_porch + hactive);
473 	HWRITE4(sc, VOP_POST_DSP_HACT_INFO, val);
474 
475 	val = DSP_HACT_ST(hsync_len + hback_porch) |
476 	      DSP_HACT_END(hsync_len + hback_porch + hactive);
477 	HWRITE4(sc, VOP_DSP_HACT_ST_END, val);
478 
479 	val = DSP_HTOTAL(hsync_len) |
480 	      DSP_HS_END(hsync_len + hback_porch + hactive + hfront_porch);
481 	HWRITE4(sc, VOP_DSP_HTOTAL_HS_END, val);
482 
483 	val = DSP_VACT_ST_POST(vsync_len + vback_porch) |
484 	      DSP_VACT_END_POST(vsync_len + vback_porch + vactive);
485 	HWRITE4(sc, VOP_POST_DSP_VACT_INFO, val);
486 
487 	val = DSP_VACT_ST(vsync_len + vback_porch) |
488 	      DSP_VACT_END(vsync_len + vback_porch + vactive);
489 	HWRITE4(sc, VOP_DSP_VACT_ST_END, val);
490 
491 	val = DSP_VTOTAL(vsync_len) |
492 	      DSP_VS_END(vsync_len + vback_porch + vactive + vfront_porch);
493 	HWRITE4(sc, VOP_DSP_VTOTAL_VS_END, val);
494 }
495 
496 void
497 rkvop_crtc_flush(struct drm_crtc *crtc, struct drm_atomic_state *das)
498 {
499 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(das,
500 	    crtc);
501 	struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
502 	struct rkvop_softc *sc = rkcrtc->sc;
503 
504 	/* Commit settings */
505 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
506 }
507 
508 struct drm_crtc_helper_funcs rkvop_crtc_helper_funcs = {
509 	.dpms = rkvop_dpms,
510 	.mode_fixup = rkvop_mode_fixup,
511 	.atomic_check = rkvop_crtc_check,
512 	.atomic_enable = rkvop_crtc_enable,
513 	.atomic_flush = rkvop_crtc_flush,
514 };
515 
516 struct drm_crtc_funcs rkvop_crtc_funcs = {
517 	.reset = drm_atomic_helper_crtc_reset,
518 	.destroy = drm_crtc_cleanup,
519 	.set_config = drm_atomic_helper_set_config,
520 	.page_flip = drm_atomic_helper_page_flip,
521 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
522 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
523 };
524 
525 int
526 rkvop_ep_activate(void *cookie, struct endpoint *ep, void *arg)
527 {
528 	struct rkvop_softc *sc = cookie;
529 	struct drm_device *ddev = arg;
530 	struct drm_plane *plane = &sc->sc_plane;
531 	struct drm_crtc *crtc = &sc->sc_crtc.base;
532 	uint32_t formats[] = { DRM_FORMAT_ARGB8888 };
533 	int error;
534 
535 	if (sc->sc_crtc.sc)
536 		return 0;
537 
538 	drm_plane_helper_add(plane, &rkvop_plane_helper_funcs);
539 	error = drm_universal_plane_init(ddev, plane, 0, &rkvop_plane_funcs,
540 	    formats, nitems(formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
541 	if (error)
542 		return -error;
543 
544 	drm_crtc_helper_add(&sc->sc_crtc.base, &rkvop_crtc_helper_funcs);
545 	error = drm_crtc_init_with_planes(ddev, crtc, plane, NULL,
546 	    &rkvop_crtc_funcs, NULL);
547 	if (error)
548 		return -error;
549 
550 	printf("%s: using CRTC %d for %s\n", sc->sc_dev.dv_xname,
551 	    drm_crtc_index(&sc->sc_crtc.base), sc->sc_conf->descr);
552 
553 	sc->sc_crtc.sc = sc;
554 	return 0;
555 }
556 
557 void *
558 rkvop_ep_get_cookie(void *cookie, struct endpoint *ep)
559 {
560 	struct rkvop_softc *sc = cookie;
561 	return &sc->sc_crtc.base;
562 }
563 
564 /*
565  * RK3399 VOP
566  */
567 #define	RK3399_VOP_POL_MASK		0xf
568 #define	RK3399_VOP_MIPI_POL(x)		((x) << 28)
569 #define	RK3399_VOP_EDP_POL(x)		((x) << 24)
570 #define	RK3399_VOP_HDMI_POL(x)		((x) << 20)
571 #define	RK3399_VOP_DP_POL(x)		((x) << 16)
572 
573 #define	RK3399_VOP_SYS_CTRL_ENABLE	(1 << 11)
574 
575 void
576 rk3399_vop_init(struct rkvop_softc *sc)
577 {
578 	uint32_t val;
579 
580 	val = HREAD4(sc, VOP_SYS_CTRL);
581 	val |= RK3399_VOP_SYS_CTRL_ENABLE;
582 	HWRITE4(sc, VOP_SYS_CTRL, val);
583 }
584 
585 void
586 rk3399_vop_set_polarity(struct rkvop_softc *sc, enum vop_ep_type ep_type, uint32_t pol)
587 {
588 	uint32_t mask, val;
589 
590 	switch (ep_type) {
591 	case VOP_EP_MIPI:
592 	case VOP_EP_MIPI1:
593 		pol = RK3399_VOP_MIPI_POL(pol);
594 		mask = RK3399_VOP_MIPI_POL(RK3399_VOP_POL_MASK);
595 		break;
596 	case VOP_EP_EDP:
597 		pol = RK3399_VOP_EDP_POL(pol);
598 		mask = RK3399_VOP_EDP_POL(RK3399_VOP_POL_MASK);
599 		break;
600 	case VOP_EP_HDMI:
601 		pol = RK3399_VOP_HDMI_POL(pol);
602 		mask = RK3399_VOP_HDMI_POL(RK3399_VOP_POL_MASK);
603 		break;
604 	case VOP_EP_DP:
605 		pol = RK3399_VOP_DP_POL(pol);
606 		mask = RK3399_VOP_DP_POL(RK3399_VOP_POL_MASK);
607 		break;
608 	default:
609 		return;
610 	}
611 
612 	val = HREAD4(sc, VOP_DSP_CTRL1);
613 	val &= ~mask;
614 	val |= pol;
615 	HWRITE4(sc, VOP_DSP_CTRL1, val);
616 }
617