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