xref: /openbsd-src/sys/dev/fdt/rkvop.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /* $OpenBSD: rkvop.c,v 1.1 2020/02/21 15:47:30 patrick 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/drmP.h>
43 #include <drm/drm_crtc.h>
44 #include <drm/drm_crtc_helper.h>
45 #include <drm/drm_plane_helper.h>
46 
47 #include <dev/fdt/rkdrm.h>
48 
49 #define	VOP_REG_CFG_DONE		0x0000
50 #define	 REG_LOAD_EN			(1 << 0)
51 #define	VOP_SYS_CTRL			0x0008
52 #define	 VOP_STANDBY_EN			(1 << 22)
53 #define	 MIPI_OUT_EN			(1 << 15)
54 #define	 EDP_OUT_EN			(1 << 14)
55 #define	 HDMI_OUT_EN			(1 << 13)
56 #define	 RGB_OUT_EN			(1 << 12)
57 #define	VOP_DSP_CTRL0			0x0010
58 #define	 DSP_OUT_MODE(x)		((x) << 0)
59 #define	  DSP_OUT_MODE_MASK		0xf
60 #define	  DSP_OUT_MODE_RGB888		0
61 #define	  DSP_OUT_MODE_RGBaaa		15
62 #define	VOP_DSP_CTRL1			0x0014
63 #define	VOP_WIN0_CTRL			0x0030
64 #define	 WIN0_LB_MODE(x)		((x) << 5)
65 #define	  WIN0_LB_MODE_MASK		0x7
66 #define	  WIN0_LB_MODE_RGB_3840X2	2
67 #define	  WIN0_LB_MODE_RGB_2560X4	3
68 #define	  WIN0_LB_MODE_RGB_1920X5	4
69 #define	  WIN0_LB_MODE_RGB_1280X8	5
70 #define	 WIN0_DATA_FMT(x)		((x) << 1)
71 #define	  WIN0_DATA_FMT_MASK		0x7
72 #define	  WIN0_DATA_FMT_ARGB888		0
73 #define	 WIN0_EN			(1 << 0)
74 #define	VOP_WIN0_COLOR_KEY		0x0038
75 #define	VOP_WIN0_VIR			0x003c
76 #define	 WIN0_VIR_STRIDE(x)		(((x) & 0x3fff) << 0)
77 #define	VOP_WIN0_YRGB_MST		0x0040
78 #define	VOP_WIN0_ACT_INFO		0x0048
79 #define	 WIN0_ACT_HEIGHT(x)		(((x) & 0x1fff) << 16)
80 #define	 WIN0_ACT_WIDTH(x)		(((x) & 0x1fff) << 0)
81 #define	VOP_WIN0_DSP_INFO		0x004c
82 #define	 WIN0_DSP_HEIGHT(x)		(((x) & 0xfff) << 16)
83 #define	 WIN0_DSP_WIDTH(x)		(((x) & 0xfff) << 0)
84 #define	VOP_WIN0_DSP_ST			0x0050
85 #define	 WIN0_DSP_YST(x)		(((x) & 0x1fff) << 16)
86 #define	 WIN0_DSP_XST(x)		(((x) & 0x1fff) << 0)
87 #define	VOP_POST_DSP_HACT_INFO		0x0170
88 #define	 DSP_HACT_ST_POST(x)		(((x) & 0x1fff) << 16)
89 #define	 DSP_HACT_END_POST(x)		(((x) & 0x1fff) << 0)
90 #define	VOP_POST_DSP_VACT_INFO		0x0174
91 #define	 DSP_VACT_ST_POST(x)		(((x) & 0x1fff) << 16)
92 #define	 DSP_VACT_END_POST(x)		(((x) & 0x1fff) << 0)
93 #define	VOP_DSP_HTOTAL_HS_END		0x0188
94 #define	 DSP_HS_END(x)			(((x) & 0x1fff) << 16)
95 #define	 DSP_HTOTAL(x)			(((x) & 0x1fff) << 0)
96 #define	VOP_DSP_HACT_ST_END		0x018c
97 #define	 DSP_HACT_ST(x)			(((x) & 0x1fff) << 16)
98 #define	 DSP_HACT_END(x)		(((x) & 0x1fff) << 0)
99 #define	VOP_DSP_VTOTAL_VS_END		0x0190
100 #define	 DSP_VS_END(x)			(((x) & 0x1fff) << 16)
101 #define	 DSP_VTOTAL(x)			(((x) & 0x1fff) << 0)
102 #define	VOP_DSP_VACT_ST_END		0x0194
103 #define	 DSP_VACT_ST(x)			(((x) & 0x1fff) << 16)
104 #define	 DSP_VACT_END(x)		(((x) & 0x1fff) << 0)
105 
106 /*
107  * Polarity fields are in different locations depending on SoC and output type,
108  * but always in the same order.
109  */
110 #define	DSP_DCLK_POL			(1 << 3)
111 #define	DSP_DEN_POL			(1 << 2)
112 #define	DSP_VSYNC_POL			(1 << 1)
113 #define	DSP_HSYNC_POL			(1 << 0)
114 
115 enum vop_ep_type {
116 	VOP_EP_MIPI,
117 	VOP_EP_EDP,
118 	VOP_EP_HDMI,
119 	VOP_EP_MIPI1,
120 	VOP_EP_DP,
121 	VOP_NEP
122 };
123 
124 struct rkvop_softc;
125 struct rkvop_config;
126 
127 struct rkvop_crtc {
128 	struct drm_crtc		base;
129 	struct rkvop_softc	*sc;
130 };
131 
132 struct rkvop_ep {
133 	struct rkvop_softc	*sc;
134 	struct video_device	vd;
135 };
136 
137 struct rkvop_softc {
138 	struct device		sc_dev;
139 	bus_space_tag_t		sc_iot;
140 	bus_space_handle_t	sc_ioh;
141 	int			sc_node;
142 	struct rkvop_config	*sc_conf;
143 
144 	struct rkvop_crtc	sc_crtc;
145 	struct rkvop_ep		*sc_ep;
146 	int			sc_nep;
147 };
148 
149 #define	to_rkvop_crtc(x)	container_of(x, struct rkvop_crtc, base)
150 
151 #define	HREAD4(sc, reg)				\
152 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
153 #define	HWRITE4(sc, reg, val)			\
154 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
155 
156 struct rkvop_config {
157 	char		*descr;
158 	u_int		out_mode;
159 	void		(*init)(struct rkvop_softc *);
160 	void		(*set_polarity)(struct rkvop_softc *,
161 					enum vop_ep_type, uint32_t);
162 };
163 
164 int rkvop_match(struct device *, void *, void *);
165 void rkvop_attach(struct device *, struct device *, void *);
166 
167 int rkvop_mode_do_set_base(struct drm_crtc *, struct drm_framebuffer *,
168     int, int, int);
169 void rkvop_destroy(struct drm_crtc *);
170 void rkvop_dpms(struct drm_crtc *, int);
171 bool rkvop_mode_fixup(struct drm_crtc *, const struct drm_display_mode *,
172     struct drm_display_mode *);
173 int rkvop_mode_set(struct drm_crtc *, struct drm_display_mode *,
174     struct drm_display_mode *, int, int, struct drm_framebuffer *);
175 int rkvop_mode_set_base(struct drm_crtc *, int, int, struct drm_framebuffer *);
176 int rkvop_mode_set_base_atomic(struct drm_crtc *, struct drm_framebuffer *,
177     int, int, enum mode_set_atomic);
178 void rkvop_disable(struct drm_crtc *);
179 void rkvop_prepare(struct drm_crtc *);
180 void rkvop_commit(struct drm_crtc *);
181 
182 void rk3399_vop_init(struct rkvop_softc *);
183 void rk3399_vop_set_polarity(struct rkvop_softc *, enum vop_ep_type, uint32_t);
184 
185 int rkvop_ep_activate(void *, struct drm_device *);
186 void *rkvop_ep_get_data(void *);
187 
188 struct rkvop_config rk3399_vop_big_config = {
189 	.descr = "RK3399 VOPB",
190 	.out_mode = DSP_OUT_MODE_RGBaaa,
191 	.init = rk3399_vop_init,
192 	.set_polarity = rk3399_vop_set_polarity,
193 };
194 
195 struct rkvop_config rk3399_vop_lit_config = {
196 	.descr = "RK3399 VOPL",
197 	.out_mode = DSP_OUT_MODE_RGB888,
198 	.init = rk3399_vop_init,
199 	.set_polarity = rk3399_vop_set_polarity,
200 };
201 
202 struct cfattach	rkvop_ca = {
203 	sizeof (struct rkvop_softc), rkvop_match, rkvop_attach
204 };
205 
206 struct cfdriver rkvop_cd = {
207 	NULL, "rkvop", DV_DULL
208 };
209 
210 int
211 rkvop_match(struct device *parent, void *match, void *aux)
212 {
213 	struct fdt_attach_args *faa = aux;
214 
215 	return (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-big") ||
216 	    OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-lit"));
217 }
218 
219 void
220 rkvop_attach(struct device *parent, struct device *self, void *aux)
221 {
222 	struct rkvop_softc *sc = (struct rkvop_softc *)self;
223 	struct fdt_attach_args *faa = aux;
224 	int i, port, ep, nep;
225 
226 	if (faa->fa_nreg < 1)
227 		return;
228 
229 	clock_set_assigned(faa->fa_node);
230 
231 	reset_deassert(faa->fa_node, "axi");
232 	reset_deassert(faa->fa_node, "ahb");
233 	reset_deassert(faa->fa_node, "dclk");
234 
235 	clock_enable(faa->fa_node, "aclk_vop");
236 	clock_enable(faa->fa_node, "hclk_vop");
237 	clock_enable(faa->fa_node, "dclk_vop");
238 
239 	sc->sc_iot = faa->fa_iot;
240 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
241 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
242 		printf(": can't map registers\n");
243 		return;
244 	}
245 	sc->sc_node = faa->fa_node;
246 
247 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-big"))
248 		sc->sc_conf = &rk3399_vop_big_config;
249 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-lit"))
250 		sc->sc_conf = &rk3399_vop_lit_config;
251 
252 	printf(": %s\n", sc->sc_conf->descr);
253 
254 	if (sc->sc_conf->init != NULL)
255 		sc->sc_conf->init(sc);
256 
257 	port = OF_getnodebyname(faa->fa_node, "port");
258 	if (!port)
259 		return;
260 
261 	for (ep = OF_child(port); ep; ep = OF_peer(ep))
262 		sc->sc_nep++;
263 	if (!sc->sc_nep)
264 		return;
265 
266 	sc->sc_ep = mallocarray(sc->sc_nep, sizeof(*sc->sc_ep),
267 	    M_DEVBUF, M_WAITOK | M_ZERO);
268 	for (i = 0, ep = OF_child(port); ep; ep = OF_peer(ep), i++) {
269 		sc->sc_ep[i].sc = sc;
270 		sc->sc_ep[i].vd.vd_node = ep;
271 		sc->sc_ep[i].vd.vd_cookie = &sc->sc_ep[i];
272 		sc->sc_ep[i].vd.vd_ep_activate = rkvop_ep_activate;
273 		sc->sc_ep[i].vd.vd_ep_get_data = rkvop_ep_get_data;
274 		video_register(&sc->sc_ep[i].vd);
275 	}
276 }
277 
278 int
279 rkvop_mode_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb,
280     int x, int y, int atomic)
281 {
282 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
283 	struct rkvop_softc *sc = mixer_crtc->sc;
284 	struct rkdrm_framebuffer *sfb = atomic?
285 	    to_rkdrm_framebuffer(fb) :
286 	    to_rkdrm_framebuffer(crtc->primary->fb);
287 
288 	uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
289 
290 	paddr += y * sfb->base.pitches[0];
291 	paddr += x * drm_format_plane_cpp(sfb->base.format->format, 0);
292 
293 	KASSERT((paddr & ~0xffffffff) == 0);
294 
295 	uint32_t vir = WIN0_VIR_STRIDE(sfb->base.pitches[0] / 4);
296 	HWRITE4(sc, VOP_WIN0_VIR, vir);
297 
298 	/* Framebuffer start address */
299 	HWRITE4(sc, VOP_WIN0_YRGB_MST, (uint32_t)paddr);
300 
301 	return 0;
302 }
303 
304 void
305 rkvop_destroy(struct drm_crtc *crtc)
306 {
307 	drm_crtc_cleanup(crtc);
308 }
309 
310 struct drm_crtc_funcs rkvop_crtc_funcs = {
311 	.set_config = drm_crtc_helper_set_config,
312 	.destroy = rkvop_destroy,
313 };
314 
315 void
316 rkvop_dpms(struct drm_crtc *crtc, int mode)
317 {
318 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
319 	struct rkvop_softc *sc = mixer_crtc->sc;
320 	uint32_t val;
321 
322 	val = HREAD4(sc, VOP_SYS_CTRL);
323 
324 	switch (mode) {
325 	case DRM_MODE_DPMS_ON:
326 		val &= ~VOP_STANDBY_EN;
327 		break;
328 	case DRM_MODE_DPMS_STANDBY:
329 	case DRM_MODE_DPMS_SUSPEND:
330 	case DRM_MODE_DPMS_OFF:
331 		val |= VOP_STANDBY_EN;
332 		break;
333 	}
334 
335 	HWRITE4(sc, VOP_SYS_CTRL, val);
336 
337 	/* Commit settings */
338 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
339 }
340 
341 bool
342 rkvop_mode_fixup(struct drm_crtc *crtc,
343    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
344 {
345 	return true;
346 }
347 
348 int
349 rkvop_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
350     struct drm_display_mode *adjusted_mode, int x, int y,
351     struct drm_framebuffer *old_fb)
352 {
353 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
354 	struct rkvop_softc *sc = mixer_crtc->sc;
355 	uint32_t val;
356 	u_int lb_mode;
357 	u_int pol;
358 	int connector_type = 0;
359 	struct drm_connector *connector;
360 	struct drm_connector_list_iter conn_iter;
361 
362 	u_int hactive = adjusted_mode->hdisplay;
363 	u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
364 	u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end;
365 	u_int hfront_porch = adjusted_mode->hsync_start - adjusted_mode->hdisplay;
366 
367 	u_int vactive = adjusted_mode->vdisplay;
368 	u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
369 	u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end;
370 	u_int vfront_porch = adjusted_mode->vsync_start - adjusted_mode->vdisplay;
371 
372 	clock_set_frequency(sc->sc_node, "dclk_vop", adjusted_mode->clock * 1000);
373 
374 	val = WIN0_ACT_WIDTH(hactive - 1) |
375 	      WIN0_ACT_HEIGHT(vactive - 1);
376 	HWRITE4(sc, VOP_WIN0_ACT_INFO, val);
377 
378 	val = WIN0_DSP_WIDTH(hactive - 1) |
379 	      WIN0_DSP_HEIGHT(vactive - 1);
380 	HWRITE4(sc, VOP_WIN0_DSP_INFO, val);
381 
382 	val = WIN0_DSP_XST(hsync_len + hback_porch) |
383 	      WIN0_DSP_YST(vsync_len + vback_porch);
384 	HWRITE4(sc, VOP_WIN0_DSP_ST, val);
385 
386 	HWRITE4(sc, VOP_WIN0_COLOR_KEY, 0);
387 
388 	if (adjusted_mode->hdisplay > 2560)
389 		lb_mode = WIN0_LB_MODE_RGB_3840X2;
390 	else if (adjusted_mode->hdisplay > 1920)
391 		lb_mode = WIN0_LB_MODE_RGB_2560X4;
392 	else if (adjusted_mode->hdisplay > 1280)
393 		lb_mode = WIN0_LB_MODE_RGB_1920X5;
394 	else
395 		lb_mode = WIN0_LB_MODE_RGB_1280X8;
396 
397 	val = WIN0_LB_MODE(lb_mode) |
398 	      WIN0_DATA_FMT(WIN0_DATA_FMT_ARGB888) |
399 	      WIN0_EN;
400 	HWRITE4(sc, VOP_WIN0_CTRL, val);
401 
402 	rkvop_mode_do_set_base(crtc, old_fb, x, y, 0);
403 
404 	pol = DSP_DCLK_POL;
405 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) != 0)
406 		pol |= DSP_HSYNC_POL;
407 	if ((adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) != 0)
408 		pol |= DSP_VSYNC_POL;
409 
410 	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
411 	drm_for_each_connector_iter(connector, &conn_iter) {
412 		if ((connector->encoder) == NULL)
413 			continue;
414 		if (connector->encoder->crtc == crtc) {
415 			connector_type = connector->connector_type;
416 			break;
417 		}
418 	}
419 
420 	switch (connector_type) {
421 	case DRM_MODE_CONNECTOR_HDMIA:
422 		sc->sc_conf->set_polarity(sc, VOP_EP_HDMI, pol);
423 		break;
424 	case DRM_MODE_CONNECTOR_eDP:
425 		sc->sc_conf->set_polarity(sc, VOP_EP_EDP, pol);
426 		break;
427 	}
428 
429 	val = HREAD4(sc, VOP_SYS_CTRL);
430 	val &= ~VOP_STANDBY_EN;
431 	val &= ~(MIPI_OUT_EN|EDP_OUT_EN|HDMI_OUT_EN|RGB_OUT_EN);
432 
433 	switch (connector_type) {
434 	case DRM_MODE_CONNECTOR_HDMIA:
435 		val |= HDMI_OUT_EN;
436 		break;
437 	case DRM_MODE_CONNECTOR_eDP:
438 		val |= EDP_OUT_EN;
439 		break;
440 	}
441 	HWRITE4(sc, VOP_SYS_CTRL, val);
442 
443 	val = HREAD4(sc, VOP_DSP_CTRL0);
444 	val &= ~DSP_OUT_MODE(DSP_OUT_MODE_MASK);
445 	val |= DSP_OUT_MODE(sc->sc_conf->out_mode);
446 	HWRITE4(sc, VOP_DSP_CTRL0, val);
447 
448 	val = DSP_HACT_ST_POST(hsync_len + hback_porch) |
449 	      DSP_HACT_END_POST(hsync_len + hback_porch + hactive);
450 	HWRITE4(sc, VOP_POST_DSP_HACT_INFO, val);
451 
452 	val = DSP_HACT_ST(hsync_len + hback_porch) |
453 	      DSP_HACT_END(hsync_len + hback_porch + hactive);
454 	HWRITE4(sc, VOP_DSP_HACT_ST_END, val);
455 
456 	val = DSP_HTOTAL(hsync_len) |
457 	      DSP_HS_END(hsync_len + hback_porch + hactive + hfront_porch);
458 	HWRITE4(sc, VOP_DSP_HTOTAL_HS_END, val);
459 
460 	val = DSP_VACT_ST_POST(vsync_len + vback_porch) |
461 	      DSP_VACT_END_POST(vsync_len + vback_porch + vactive);
462 	HWRITE4(sc, VOP_POST_DSP_VACT_INFO, val);
463 
464 	val = DSP_VACT_ST(vsync_len + vback_porch) |
465 	      DSP_VACT_END(vsync_len + vback_porch + vactive);
466 	HWRITE4(sc, VOP_DSP_VACT_ST_END, val);
467 
468 	val = DSP_VTOTAL(vsync_len) |
469 	      DSP_VS_END(vsync_len + vback_porch + vactive + vfront_porch);
470 	HWRITE4(sc, VOP_DSP_VTOTAL_VS_END, val);
471 
472 	return 0;
473 }
474 
475 int
476 rkvop_mode_set_base(struct drm_crtc *crtc, int x, int y,
477     struct drm_framebuffer *old_fb)
478 {
479 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
480 	struct rkvop_softc *sc = mixer_crtc->sc;
481 
482 	rkvop_mode_do_set_base(crtc, old_fb, x, y, 0);
483 
484 	/* Commit settings */
485 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
486 
487 	return 0;
488 }
489 
490 int
491 rkvop_mode_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
492     int x, int y, enum mode_set_atomic state)
493 {
494 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
495 	struct rkvop_softc *sc = mixer_crtc->sc;
496 
497 	rkvop_mode_do_set_base(crtc, fb, x, y, 1);
498 
499 	/* Commit settings */
500 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
501 
502 	return 0;
503 }
504 
505 void
506 rkvop_disable(struct drm_crtc *crtc)
507 {
508 }
509 
510 void
511 rkvop_prepare(struct drm_crtc *crtc)
512 {
513 }
514 
515 void
516 rkvop_commit(struct drm_crtc *crtc)
517 {
518 	struct rkvop_crtc *mixer_crtc = to_rkvop_crtc(crtc);
519 	struct rkvop_softc *sc = mixer_crtc->sc;
520 
521 	/* Commit settings */
522 	HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
523 }
524 
525 struct drm_crtc_helper_funcs rkvop_crtc_helper_funcs = {
526 	.dpms = rkvop_dpms,
527 	.mode_fixup = rkvop_mode_fixup,
528 	.mode_set = rkvop_mode_set,
529 	.mode_set_base = rkvop_mode_set_base,
530 	.mode_set_base_atomic = rkvop_mode_set_base_atomic,
531 	.disable = rkvop_disable,
532 	.prepare = rkvop_prepare,
533 	.commit = rkvop_commit,
534 };
535 
536 int
537 rkvop_ep_activate(void *cookie, struct drm_device *ddev)
538 {
539 	struct rkvop_ep *ep = cookie;
540 	struct rkvop_softc *sc = ep->sc;
541 
542 	if (sc->sc_crtc.sc == NULL) {
543 		sc->sc_crtc.sc = sc;
544 
545 		drm_crtc_init(ddev, &sc->sc_crtc.base, &rkvop_crtc_funcs);
546 		drm_crtc_helper_add(&sc->sc_crtc.base, &rkvop_crtc_helper_funcs);
547 
548 		printf("%s: using CRTC %d for %s\n", sc->sc_dev.dv_xname,
549 		    drm_crtc_index(&sc->sc_crtc.base), sc->sc_conf->descr);
550 	}
551 
552 	return 0;
553 }
554 
555 void *
556 rkvop_ep_get_data(void *cookie)
557 {
558 	struct rkvop_ep *ep = cookie;
559 	struct rkvop_softc *sc = ep->sc;
560 
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