1 /* $OpenBSD: rkvop.c,v 1.8 2024/08/21 11:24:12 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
rkvop_match(struct device * parent,void * match,void * aux)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
rkvop_attach(struct device * parent,struct device * self,void * aux)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 paddr_t paddr;
211
212 if (faa->fa_nreg < 1)
213 return;
214
215 clock_set_assigned(faa->fa_node);
216
217 reset_deassert(faa->fa_node, "axi");
218 reset_deassert(faa->fa_node, "ahb");
219 reset_deassert(faa->fa_node, "dclk");
220
221 clock_enable(faa->fa_node, "aclk_vop");
222 clock_enable(faa->fa_node, "hclk_vop");
223 clock_enable(faa->fa_node, "dclk_vop");
224
225 sc->sc_iot = faa->fa_iot;
226 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
227 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
228 printf(": can't map registers\n");
229 return;
230 }
231 sc->sc_node = faa->fa_node;
232
233 if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-big"))
234 sc->sc_conf = &rk3399_vop_big_config;
235 if (OF_is_compatible(faa->fa_node, "rockchip,rk3399-vop-lit"))
236 sc->sc_conf = &rk3399_vop_lit_config;
237
238 printf(": %s\n", sc->sc_conf->descr);
239
240 if (sc->sc_conf->init != NULL)
241 sc->sc_conf->init(sc);
242
243 sc->sc_ports.dp_node = faa->fa_node;
244 sc->sc_ports.dp_cookie = sc;
245 sc->sc_ports.dp_ep_activate = rkvop_ep_activate;
246 sc->sc_ports.dp_ep_get_cookie = rkvop_ep_get_cookie;
247 device_ports_register(&sc->sc_ports, EP_DRM_CRTC);
248
249 paddr = HREAD4(sc, VOP_WIN0_YRGB_MST);
250 if (paddr != 0) {
251 uint32_t stride, height;
252
253 stride = HREAD4(sc, VOP_WIN0_VIR) & 0xffff;
254 height = (HREAD4(sc, VOP_WIN0_DSP_INFO) >> 16) + 1;
255 rasops_claim_framebuffer(paddr, height * stride * 4, self);
256 }
257 }
258
259 int
rkvop_plane_check(struct drm_plane * plane,struct drm_atomic_state * das)260 rkvop_plane_check(struct drm_plane *plane, struct drm_atomic_state *das)
261 {
262 struct drm_crtc_state *crtc_state;
263 struct drm_plane_state *state = drm_atomic_get_new_plane_state(das,
264 plane);
265
266 if (state->crtc == NULL)
267 return 0;
268
269 crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
270 if (IS_ERR(crtc_state))
271 return PTR_ERR(crtc_state);
272
273 return drm_atomic_helper_check_plane_state(state, crtc_state,
274 DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING,
275 false, true);
276 }
277
278 void
rkvop_plane_update(struct drm_plane * plane,struct drm_atomic_state * das)279 rkvop_plane_update(struct drm_plane *plane, struct drm_atomic_state *das)
280 {
281 struct drm_plane_state *state = plane->state;
282 struct drm_crtc *crtc = state->crtc;
283 struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
284 struct rkvop_softc *sc = rkcrtc->sc;
285 struct drm_framebuffer *fb = state->fb;
286 struct rkdrm_framebuffer *rkfb = to_rkdrm_framebuffer(fb);
287 struct drm_rect *src = &state->src;
288 struct drm_rect *dst = &state->dst;
289 u_int act_width = drm_rect_width(src) >> 16;
290 u_int act_height = drm_rect_height(src) >> 16;
291 u_int htotal = crtc->mode.htotal;
292 u_int vtotal = crtc->mode.vtotal;
293 u_int hsync_start = crtc->mode.hsync_start;
294 u_int vsync_start = crtc->mode.vsync_start;
295 uint64_t paddr;
296 u_int lb_mode;
297 uint32_t val;
298
299 val = WIN0_ACT_WIDTH(act_width - 1) |
300 WIN0_ACT_HEIGHT(act_height - 1);
301 HWRITE4(sc, VOP_WIN0_ACT_INFO, val);
302
303 val = WIN0_DSP_WIDTH(drm_rect_width(dst) - 1) |
304 WIN0_DSP_HEIGHT(drm_rect_height(dst) - 1);
305 HWRITE4(sc, VOP_WIN0_DSP_INFO, val);
306
307 val = WIN0_DSP_XST(dst->x1 + htotal - hsync_start) |
308 WIN0_DSP_YST(dst->y1 + vtotal - vsync_start);
309 HWRITE4(sc, VOP_WIN0_DSP_ST, val);
310
311 HWRITE4(sc, VOP_WIN0_COLOR_KEY, 0);
312
313 if (act_width > 2560)
314 lb_mode = WIN0_LB_MODE_RGB_3840X2;
315 else if (act_width > 1920)
316 lb_mode = WIN0_LB_MODE_RGB_2560X4;
317 else if (act_width > 1280)
318 lb_mode = WIN0_LB_MODE_RGB_1920X5;
319 else
320 lb_mode = WIN0_LB_MODE_RGB_1280X8;
321
322 val = WIN0_LB_MODE(lb_mode) |
323 WIN0_DATA_FMT(WIN0_DATA_FMT_ARGB888) |
324 WIN0_EN;
325 HWRITE4(sc, VOP_WIN0_CTRL, val);
326
327 val = WIN0_VIR_STRIDE(fb->pitches[0] / 4);
328 HWRITE4(sc, VOP_WIN0_VIR, val);
329
330 /* Framebuffer start address */
331 paddr = (uint64_t)rkfb->obj->dmamap->dm_segs[0].ds_addr;
332 paddr += (src->y1 >> 16) * fb->pitches[0];
333 paddr += (src->x1 >> 16) * fb->format->cpp[0];
334 KASSERT((paddr & ~0xffffffff) == 0);
335 HWRITE4(sc, VOP_WIN0_YRGB_MST, (uint32_t)paddr);
336 }
337
338 struct drm_plane_helper_funcs rkvop_plane_helper_funcs = {
339 .atomic_check = rkvop_plane_check,
340 .atomic_update = rkvop_plane_update,
341 };
342
343 struct drm_plane_funcs rkvop_plane_funcs = {
344 .update_plane = drm_atomic_helper_update_plane,
345 .disable_plane = drm_atomic_helper_disable_plane,
346 .destroy = drm_plane_cleanup,
347 .reset = drm_atomic_helper_plane_reset,
348 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
349 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
350 };
351
352 void
rkvop_dpms(struct drm_crtc * crtc,int mode)353 rkvop_dpms(struct drm_crtc *crtc, int mode)
354 {
355 struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
356 struct rkvop_softc *sc = rkcrtc->sc;
357 uint32_t val;
358
359 val = HREAD4(sc, VOP_SYS_CTRL);
360
361 switch (mode) {
362 case DRM_MODE_DPMS_ON:
363 val &= ~VOP_STANDBY_EN;
364 break;
365 case DRM_MODE_DPMS_STANDBY:
366 case DRM_MODE_DPMS_SUSPEND:
367 case DRM_MODE_DPMS_OFF:
368 val |= VOP_STANDBY_EN;
369 break;
370 }
371
372 HWRITE4(sc, VOP_SYS_CTRL, val);
373
374 /* Commit settings */
375 HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
376 }
377
378 bool
rkvop_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)379 rkvop_mode_fixup(struct drm_crtc *crtc,
380 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
381 {
382 return true;
383 }
384
385 int
rkvop_crtc_check(struct drm_crtc * crtc,struct drm_atomic_state * das)386 rkvop_crtc_check(struct drm_crtc *crtc, struct drm_atomic_state *das)
387 {
388 struct drm_crtc_state *state = drm_atomic_get_new_crtc_state(das,
389 crtc);
390 bool enabled = state->plane_mask & drm_plane_mask(crtc->primary);
391
392 if (enabled != state->enable)
393 return -EINVAL;
394
395 return drm_atomic_add_affected_planes(state->state, crtc);
396 }
397
398 void
rkvop_crtc_enable(struct drm_crtc * crtc,struct drm_atomic_state * das)399 rkvop_crtc_enable(struct drm_crtc *crtc, struct drm_atomic_state *das)
400 {
401 struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
402 struct rkvop_softc *sc = rkcrtc->sc;
403 struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
404 uint32_t val;
405 u_int pol;
406 int connector_type = 0;
407 struct drm_connector *connector;
408 struct drm_connector_list_iter conn_iter;
409
410 u_int hactive = adjusted_mode->hdisplay;
411 u_int hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
412 u_int hback_porch = adjusted_mode->htotal - adjusted_mode->hsync_end;
413 u_int hfront_porch = adjusted_mode->hsync_start - adjusted_mode->hdisplay;
414
415 u_int vactive = adjusted_mode->vdisplay;
416 u_int vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
417 u_int vback_porch = adjusted_mode->vtotal - adjusted_mode->vsync_end;
418 u_int vfront_porch = adjusted_mode->vsync_start - adjusted_mode->vdisplay;
419
420 clock_set_frequency(sc->sc_node, "dclk_vop", adjusted_mode->clock * 1000);
421
422 pol = DSP_DCLK_POL;
423 if ((adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) != 0)
424 pol |= DSP_HSYNC_POL;
425 if ((adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) != 0)
426 pol |= DSP_VSYNC_POL;
427
428 drm_connector_list_iter_begin(crtc->dev, &conn_iter);
429 drm_for_each_connector_iter(connector, &conn_iter) {
430 if ((connector->encoder) == NULL)
431 continue;
432 if (connector->encoder->crtc == crtc) {
433 connector_type = connector->connector_type;
434 break;
435 }
436 }
437
438 switch (connector_type) {
439 case DRM_MODE_CONNECTOR_HDMIA:
440 sc->sc_conf->set_polarity(sc, VOP_EP_HDMI, pol);
441 break;
442 case DRM_MODE_CONNECTOR_eDP:
443 sc->sc_conf->set_polarity(sc, VOP_EP_EDP, pol);
444 break;
445 }
446
447 val = HREAD4(sc, VOP_SYS_CTRL);
448 val &= ~VOP_STANDBY_EN;
449 val &= ~(MIPI_OUT_EN|EDP_OUT_EN|HDMI_OUT_EN|RGB_OUT_EN);
450
451 switch (connector_type) {
452 case DRM_MODE_CONNECTOR_HDMIA:
453 val |= HDMI_OUT_EN;
454 break;
455 case DRM_MODE_CONNECTOR_eDP:
456 val |= EDP_OUT_EN;
457 break;
458 }
459 HWRITE4(sc, VOP_SYS_CTRL, val);
460
461 val = HREAD4(sc, VOP_DSP_CTRL0);
462 val &= ~DSP_OUT_MODE(DSP_OUT_MODE_MASK);
463 val |= DSP_OUT_MODE(sc->sc_conf->out_mode);
464 HWRITE4(sc, VOP_DSP_CTRL0, val);
465
466 val = DSP_HACT_ST_POST(hsync_len + hback_porch) |
467 DSP_HACT_END_POST(hsync_len + hback_porch + hactive);
468 HWRITE4(sc, VOP_POST_DSP_HACT_INFO, val);
469
470 val = DSP_HACT_ST(hsync_len + hback_porch) |
471 DSP_HACT_END(hsync_len + hback_porch + hactive);
472 HWRITE4(sc, VOP_DSP_HACT_ST_END, val);
473
474 val = DSP_HTOTAL(hsync_len) |
475 DSP_HS_END(hsync_len + hback_porch + hactive + hfront_porch);
476 HWRITE4(sc, VOP_DSP_HTOTAL_HS_END, val);
477
478 val = DSP_VACT_ST_POST(vsync_len + vback_porch) |
479 DSP_VACT_END_POST(vsync_len + vback_porch + vactive);
480 HWRITE4(sc, VOP_POST_DSP_VACT_INFO, val);
481
482 val = DSP_VACT_ST(vsync_len + vback_porch) |
483 DSP_VACT_END(vsync_len + vback_porch + vactive);
484 HWRITE4(sc, VOP_DSP_VACT_ST_END, val);
485
486 val = DSP_VTOTAL(vsync_len) |
487 DSP_VS_END(vsync_len + vback_porch + vactive + vfront_porch);
488 HWRITE4(sc, VOP_DSP_VTOTAL_VS_END, val);
489 }
490
491 void
rkvop_crtc_flush(struct drm_crtc * crtc,struct drm_atomic_state * das)492 rkvop_crtc_flush(struct drm_crtc *crtc, struct drm_atomic_state *das)
493 {
494 struct rkvop_crtc *rkcrtc = to_rkvop_crtc(crtc);
495 struct rkvop_softc *sc = rkcrtc->sc;
496
497 /* Commit settings */
498 HWRITE4(sc, VOP_REG_CFG_DONE, REG_LOAD_EN);
499 }
500
501 struct drm_crtc_helper_funcs rkvop_crtc_helper_funcs = {
502 .dpms = rkvop_dpms,
503 .mode_fixup = rkvop_mode_fixup,
504 .atomic_check = rkvop_crtc_check,
505 .atomic_enable = rkvop_crtc_enable,
506 .atomic_flush = rkvop_crtc_flush,
507 };
508
509 struct drm_crtc_funcs rkvop_crtc_funcs = {
510 .reset = drm_atomic_helper_crtc_reset,
511 .destroy = drm_crtc_cleanup,
512 .set_config = drm_atomic_helper_set_config,
513 .page_flip = drm_atomic_helper_page_flip,
514 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
515 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
516 };
517
518 int
rkvop_ep_activate(void * cookie,struct endpoint * ep,void * arg)519 rkvop_ep_activate(void *cookie, struct endpoint *ep, void *arg)
520 {
521 struct rkvop_softc *sc = cookie;
522 struct drm_device *ddev = arg;
523 struct drm_plane *plane = &sc->sc_plane;
524 struct drm_crtc *crtc = &sc->sc_crtc.base;
525 uint32_t formats[] = { DRM_FORMAT_ARGB8888 };
526 int error;
527
528 if (sc->sc_crtc.sc)
529 return 0;
530
531 drm_plane_helper_add(plane, &rkvop_plane_helper_funcs);
532 error = drm_universal_plane_init(ddev, plane, 0, &rkvop_plane_funcs,
533 formats, nitems(formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
534 if (error)
535 return -error;
536
537 drm_crtc_helper_add(&sc->sc_crtc.base, &rkvop_crtc_helper_funcs);
538 error = drm_crtc_init_with_planes(ddev, crtc, plane, NULL,
539 &rkvop_crtc_funcs, NULL);
540 if (error)
541 return -error;
542
543 printf("%s: using CRTC %d for %s\n", sc->sc_dev.dv_xname,
544 drm_crtc_index(&sc->sc_crtc.base), sc->sc_conf->descr);
545
546 sc->sc_crtc.sc = sc;
547 return 0;
548 }
549
550 void *
rkvop_ep_get_cookie(void * cookie,struct endpoint * ep)551 rkvop_ep_get_cookie(void *cookie, struct endpoint *ep)
552 {
553 struct rkvop_softc *sc = cookie;
554 return &sc->sc_crtc.base;
555 }
556
557 /*
558 * RK3399 VOP
559 */
560 #define RK3399_VOP_POL_MASK 0xf
561 #define RK3399_VOP_MIPI_POL(x) ((x) << 28)
562 #define RK3399_VOP_EDP_POL(x) ((x) << 24)
563 #define RK3399_VOP_HDMI_POL(x) ((x) << 20)
564 #define RK3399_VOP_DP_POL(x) ((x) << 16)
565
566 #define RK3399_VOP_SYS_CTRL_ENABLE (1 << 11)
567
568 void
rk3399_vop_init(struct rkvop_softc * sc)569 rk3399_vop_init(struct rkvop_softc *sc)
570 {
571 uint32_t val;
572
573 val = HREAD4(sc, VOP_SYS_CTRL);
574 val |= RK3399_VOP_SYS_CTRL_ENABLE;
575 HWRITE4(sc, VOP_SYS_CTRL, val);
576 }
577
578 void
rk3399_vop_set_polarity(struct rkvop_softc * sc,enum vop_ep_type ep_type,uint32_t pol)579 rk3399_vop_set_polarity(struct rkvop_softc *sc, enum vop_ep_type ep_type, uint32_t pol)
580 {
581 uint32_t mask, val;
582
583 switch (ep_type) {
584 case VOP_EP_MIPI:
585 case VOP_EP_MIPI1:
586 pol = RK3399_VOP_MIPI_POL(pol);
587 mask = RK3399_VOP_MIPI_POL(RK3399_VOP_POL_MASK);
588 break;
589 case VOP_EP_EDP:
590 pol = RK3399_VOP_EDP_POL(pol);
591 mask = RK3399_VOP_EDP_POL(RK3399_VOP_POL_MASK);
592 break;
593 case VOP_EP_HDMI:
594 pol = RK3399_VOP_HDMI_POL(pol);
595 mask = RK3399_VOP_HDMI_POL(RK3399_VOP_POL_MASK);
596 break;
597 case VOP_EP_DP:
598 pol = RK3399_VOP_DP_POL(pol);
599 mask = RK3399_VOP_DP_POL(RK3399_VOP_POL_MASK);
600 break;
601 default:
602 return;
603 }
604
605 val = HREAD4(sc, VOP_DSP_CTRL1);
606 val &= ~mask;
607 val |= pol;
608 HWRITE4(sc, VOP_DSP_CTRL1, val);
609 }
610