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