1 /* $OpenBSD: rkdrm.c,v 1.2 2020/03/04 21:19:15 kettenis Exp $ */ 2 /* $NetBSD: rk_drm.c,v 1.3 2019/12/15 01:00:58 mrg Exp $ */ 3 /*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 5 * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/device.h> 32 #include <sys/systm.h> 33 34 #include <machine/bus.h> 35 #include <machine/fdt.h> 36 37 #include <dev/ofw/openfirm.h> 38 #include <dev/ofw/ofw_clock.h> 39 #include <dev/ofw/ofw_gpio.h> 40 #include <dev/ofw/ofw_misc.h> 41 #include <dev/ofw/fdt.h> 42 43 #include <uvm/uvm_extern.h> 44 #include <uvm/uvm_object.h> 45 #include <uvm/uvm_device.h> 46 47 #include <drm/drmP.h> 48 #include <drm/drm_crtc_helper.h> 49 #include <drm/drm_fb_helper.h> 50 #include <drm/drm_gem.h> 51 52 #include <dev/fdt/rkdrm.h> 53 54 #define RK_DRM_MAX_WIDTH 3840 55 #define RK_DRM_MAX_HEIGHT 2160 56 57 TAILQ_HEAD(, rkdrm_ports) rkdrm_ports = 58 TAILQ_HEAD_INITIALIZER(rkdrm_ports); 59 60 int rkdrm_match(struct device *, void *, void *); 61 void rkdrm_attach(struct device *, struct device *, void *); 62 void rkdrm_attachhook(struct device *); 63 64 #ifdef notyet 65 vmem_t *rkdrm_alloc_cma_pool(struct drm_device *, size_t); 66 #endif 67 68 uint32_t rkdrm_get_vblank_counter(struct drm_device *, unsigned int); 69 int rkdrm_enable_vblank(struct drm_device *, unsigned int); 70 void rkdrm_disable_vblank(struct drm_device *, unsigned int); 71 72 int rkdrm_load(struct drm_device *, unsigned long); 73 int rkdrm_unload(struct drm_device *); 74 75 int rkdrm_gem_fault(struct drm_gem_object *, struct uvm_faultinfo *, 76 off_t, vaddr_t, vm_page_t *, int, int, vm_prot_t, int); 77 78 struct drm_driver rkdrm_driver = { 79 .driver_features = DRIVER_MODESET | DRIVER_GEM, 80 81 .get_vblank_counter = rkdrm_get_vblank_counter, 82 .enable_vblank = rkdrm_enable_vblank, 83 .disable_vblank = rkdrm_disable_vblank, 84 85 .dumb_create = drm_gem_cma_dumb_create, 86 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 87 88 .gem_free_object_unlocked = drm_gem_cma_free_object, 89 .gem_fault = drm_gem_cma_fault, 90 91 .name = DRIVER_NAME, 92 .desc = DRIVER_DESC, 93 .date = DRIVER_DATE, 94 .major = DRIVER_MAJOR, 95 .minor = DRIVER_MINOR, 96 .patchlevel = DRIVER_PATCHLEVEL, 97 }; 98 99 struct cfattach rkdrm_ca = { 100 sizeof (struct rkdrm_softc), rkdrm_match, rkdrm_attach 101 }; 102 103 struct cfdriver rkdrm_cd = { 104 NULL, "rkdrm", DV_DULL 105 }; 106 107 int 108 rkdrm_match(struct device *parent, void *match, void *aux) 109 { 110 struct fdt_attach_args *faa = aux; 111 112 return OF_is_compatible(faa->fa_node, "rockchip,display-subsystem"); 113 } 114 115 void 116 rkdrm_attach(struct device *parent, struct device *self, void *aux) 117 { 118 struct rkdrm_softc *sc = (struct rkdrm_softc *)self; 119 struct fdt_attach_args *faa = aux; 120 struct drm_attach_args arg; 121 122 sc->sc_dmat = faa->fa_dmat; 123 sc->sc_iot = faa->fa_iot; 124 sc->sc_node = faa->fa_node; 125 126 printf("\n"); 127 128 memset(&arg, 0, sizeof(arg)); 129 arg.driver = &rkdrm_driver; 130 arg.drm = &sc->sc_ddev; 131 arg.dmat = faa->fa_dmat; 132 arg.bst = faa->fa_iot; 133 arg.busid = sc->sc_dev.dv_xname; 134 arg.busid_len = strlen(sc->sc_dev.dv_xname) + 1; 135 config_found_sm(self, &arg, drmprint, drmsubmatch); 136 137 config_mountroot(self, rkdrm_attachhook); 138 } 139 140 int 141 rkdrm_fb_create_handle(struct drm_framebuffer *fb, 142 struct drm_file *file, unsigned int *handle) 143 { 144 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb); 145 146 return drm_gem_handle_create(file, &sfb->obj->base, handle); 147 } 148 149 void 150 rkdrm_fb_destroy(struct drm_framebuffer *fb) 151 { 152 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb); 153 154 drm_framebuffer_cleanup(fb); 155 drm_gem_object_unreference_unlocked(&sfb->obj->base); 156 free(sfb, M_DRM, sizeof(*sfb)); 157 } 158 159 struct drm_framebuffer_funcs rkdrm_framebuffer_funcs = { 160 .create_handle = rkdrm_fb_create_handle, 161 .destroy = rkdrm_fb_destroy, 162 }; 163 164 struct drm_framebuffer * 165 rkdrm_fb_create(struct drm_device *ddev, struct drm_file *file, 166 const struct drm_mode_fb_cmd2 *cmd) 167 { 168 struct rkdrm_framebuffer *fb; 169 struct drm_gem_object *gem_obj; 170 int error; 171 172 if (cmd->flags) 173 return NULL; 174 175 gem_obj = drm_gem_object_lookup(file, cmd->handles[0]); 176 if (gem_obj == NULL) 177 return NULL; 178 179 fb = malloc(sizeof(*fb), M_DRM, M_ZERO | M_WAITOK); 180 drm_helper_mode_fill_fb_struct(ddev, &fb->base, cmd); 181 fb->base.format = drm_format_info(DRM_FORMAT_ARGB8888); 182 fb->obj = to_drm_gem_cma_obj(gem_obj); 183 184 error = drm_framebuffer_init(ddev, &fb->base, &rkdrm_framebuffer_funcs); 185 if (error != 0) 186 goto dealloc; 187 188 return &fb->base; 189 190 dealloc: 191 drm_framebuffer_cleanup(&fb->base); 192 free(fb, M_DRM, sizeof(*fb)); 193 drm_gem_object_unreference_unlocked(gem_obj); 194 195 return NULL; 196 } 197 198 struct drm_mode_config_funcs rkdrm_mode_config_funcs = { 199 .fb_create = rkdrm_fb_create, 200 }; 201 202 int rkdrm_fb_probe(struct drm_fb_helper *, struct drm_fb_helper_surface_size *); 203 204 struct drm_fb_helper_funcs rkdrm_fb_helper_funcs = { 205 .fb_probe = rkdrm_fb_probe, 206 }; 207 208 uint32_t 209 rkdrm_get_vblank_counter(struct drm_device *ddev, unsigned int crtc) 210 { 211 struct rkdrm_softc *sc = rkdrm_private(ddev); 212 213 if (crtc >= nitems(sc->sc_vbl)) 214 return 0; 215 216 if (sc->sc_vbl[crtc].get_vblank_counter == NULL) 217 return 0; 218 219 return sc->sc_vbl[crtc].get_vblank_counter(sc->sc_vbl[crtc].priv); 220 } 221 222 int 223 rkdrm_enable_vblank(struct drm_device *ddev, unsigned int crtc) 224 { 225 struct rkdrm_softc *sc = rkdrm_private(ddev); 226 227 if (crtc >= nitems(sc->sc_vbl)) 228 return 0; 229 230 if (sc->sc_vbl[crtc].enable_vblank == NULL) 231 return 0; 232 233 sc->sc_vbl[crtc].enable_vblank(sc->sc_vbl[crtc].priv); 234 235 return 0; 236 } 237 238 void 239 rkdrm_disable_vblank(struct drm_device *ddev, unsigned int crtc) 240 { 241 struct rkdrm_softc *sc = rkdrm_private(ddev); 242 243 if (crtc >= nitems(sc->sc_vbl)) 244 return; 245 246 if (sc->sc_vbl[crtc].disable_vblank == NULL) 247 return; 248 249 sc->sc_vbl[crtc].disable_vblank(sc->sc_vbl[crtc].priv); 250 } 251 252 int 253 rkdrm_unload(struct drm_device *ddev) 254 { 255 drm_mode_config_cleanup(ddev); 256 257 return 0; 258 } 259 260 void rkdrm_burner(void *, u_int, u_int); 261 int rkdrm_wsioctl(void *, u_long, caddr_t, int, struct proc *); 262 paddr_t rkdrm_wsmmap(void *, off_t, int); 263 int rkdrm_alloc_screen(void *, const struct wsscreen_descr *, 264 void **, int *, int *, long *); 265 void rkdrm_free_screen(void *, void *); 266 int rkdrm_show_screen(void *, void *, int, 267 void (*)(void *, int, int), void *); 268 void rkdrm_doswitch(void *); 269 void rkdrm_enter_ddb(void *, void *); 270 int rkdrm_get_param(struct wsdisplay_param *); 271 int rkdrm_set_param(struct wsdisplay_param *); 272 273 struct wsscreen_descr rkdrm_stdscreen = { 274 "std", 275 0, 0, 276 0, 277 0, 0, 278 WSSCREEN_UNDERLINE | WSSCREEN_HILIT | 279 WSSCREEN_REVERSE | WSSCREEN_WSCOLORS 280 }; 281 282 const struct wsscreen_descr *rkdrm_scrlist[] = { 283 &rkdrm_stdscreen, 284 }; 285 286 struct wsscreen_list rkdrm_screenlist = { 287 nitems(rkdrm_scrlist), rkdrm_scrlist 288 }; 289 290 struct wsdisplay_accessops rkdrm_accessops = { 291 .ioctl = rkdrm_wsioctl, 292 .mmap = rkdrm_wsmmap, 293 .alloc_screen = rkdrm_alloc_screen, 294 .free_screen = rkdrm_free_screen, 295 .show_screen = rkdrm_show_screen, 296 .enter_ddb = rkdrm_enter_ddb, 297 .getchar = rasops_getchar, 298 .load_font = rasops_load_font, 299 .list_font = rasops_list_font, 300 .scrollback = rasops_scrollback, 301 #ifdef notyet 302 .burn_screen = rkdrm_burner 303 #endif 304 }; 305 306 int 307 rkdrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 308 { 309 struct rasops_info *ri = v; 310 struct rkdrm_softc *sc = ri->ri_hw; 311 struct wsdisplay_param *dp = (struct wsdisplay_param *)data; 312 struct wsdisplay_fbinfo *wdf; 313 314 switch (cmd) { 315 case WSDISPLAYIO_GTYPE: 316 *(u_int *)data = WSDISPLAY_TYPE_RKDRM; 317 return 0; 318 case WSDISPLAYIO_GINFO: 319 wdf = (struct wsdisplay_fbinfo *)data; 320 wdf->width = ri->ri_width; 321 wdf->height = ri->ri_height; 322 wdf->depth = ri->ri_depth; 323 wdf->cmsize = 0; 324 return 0; 325 case WSDISPLAYIO_LINEBYTES: 326 *(u_int *)data = ri->ri_stride; 327 return 0; 328 } 329 330 return (-1); 331 } 332 333 paddr_t 334 rkdrm_wsmmap(void *v, off_t off, int prot) 335 { 336 struct rasops_info *ri = v; 337 struct rkdrm_softc *sc = ri->ri_hw; 338 struct drm_fb_helper *helper = &sc->helper; 339 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb); 340 uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr; 341 size_t size = sfb->obj->dmamap->dm_segs[0].ds_len; 342 343 if (off < 0 || off >= size) 344 return -1; 345 346 return ((paddr + off) | PMAP_NOCACHE); 347 } 348 349 int 350 rkdrm_alloc_screen(void *v, const struct wsscreen_descr *type, 351 void **cookiep, int *curxp, int *curyp, long *attrp) 352 { 353 return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); 354 } 355 356 void 357 rkdrm_free_screen(void *v, void *cookie) 358 { 359 return rasops_free_screen(v, cookie); 360 } 361 362 int 363 rkdrm_show_screen(void *v, void *cookie, int waitok, 364 void (*cb)(void *, int, int), void *cbarg) 365 { 366 struct rasops_info *ri = v; 367 struct rkdrm_softc *sc = ri->ri_hw; 368 369 if (cookie == ri->ri_active) 370 return (0); 371 372 sc->switchcb = cb; 373 sc->switchcbarg = cbarg; 374 sc->switchcookie = cookie; 375 if (cb) { 376 task_add(systq, &sc->switchtask); 377 return (EAGAIN); 378 } 379 380 rkdrm_doswitch(v); 381 382 return (0); 383 } 384 385 void 386 rkdrm_doswitch(void *v) 387 { 388 struct rasops_info *ri = v; 389 struct rkdrm_softc *sc = ri->ri_hw; 390 struct rkdrm_crtc *rkdrm_crtc; 391 int i, crtc; 392 393 rasops_show_screen(ri, sc->switchcookie, 0, NULL, NULL); 394 drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper); 395 396 if (sc->switchcb) 397 (sc->switchcb)(sc->switchcbarg, 0, 0); 398 } 399 400 void 401 rkdrm_enter_ddb(void *v, void *cookie) 402 { 403 struct rasops_info *ri = v; 404 struct rkdrm_softc *sc = ri->ri_hw; 405 struct drm_fb_helper *fb_helper = &sc->helper; 406 407 if (cookie == ri->ri_active) 408 return; 409 410 rasops_show_screen(ri, cookie, 0, NULL, NULL); 411 drm_fb_helper_debug_enter(fb_helper->fbdev); 412 } 413 414 void 415 rkdrm_attachhook(struct device *dev) 416 { 417 struct rkdrm_softc *sc = (struct rkdrm_softc *)dev; 418 struct wsemuldisplaydev_attach_args aa; 419 struct drm_fb_helper *helper = &sc->helper; 420 struct rasops_info *ri = &sc->ro; 421 struct rkdrm_framebuffer *sfb; 422 struct drm_device *ddev; 423 uint32_t *ports; 424 int i, portslen, nports; 425 426 portslen = OF_getproplen(sc->sc_node, "ports"); 427 if (portslen < 0) { 428 printf("%s: no display interface ports specified\n", 429 sc->sc_dev.dv_xname); 430 return; 431 } 432 433 drm_mode_config_init(&sc->sc_ddev); 434 sc->sc_ddev.mode_config.min_width = 0; 435 sc->sc_ddev.mode_config.min_height = 0; 436 sc->sc_ddev.mode_config.max_width = RK_DRM_MAX_WIDTH; 437 sc->sc_ddev.mode_config.max_height = RK_DRM_MAX_HEIGHT; 438 sc->sc_ddev.mode_config.funcs = &rkdrm_mode_config_funcs; 439 440 nports = 0; 441 ports = malloc(portslen, M_TEMP, M_WAITOK); 442 OF_getpropintarray(sc->sc_node, "ports", ports, portslen); 443 for (i = 0; i < portslen / sizeof(uint32_t); i++) { 444 video_port_activate(ports[i], &sc->sc_ddev); 445 nports++; 446 } 447 free(ports, M_TEMP, portslen); 448 449 if (nports == 0) { 450 printf("%s: no display interface ports configured\n", 451 sc->sc_dev.dv_xname); 452 drm_mode_config_cleanup(&sc->sc_ddev); 453 return; 454 } 455 456 drm_fb_helper_prepare(&sc->sc_ddev, &sc->helper, &rkdrm_fb_helper_funcs); 457 if (drm_fb_helper_init(&sc->sc_ddev, &sc->helper, 1)) { 458 printf("%s: can't initialize framebuffer helper\n", 459 sc->sc_dev.dv_xname); 460 drm_mode_config_cleanup(&sc->sc_ddev); 461 return; 462 } 463 464 sc->helper.fb = malloc(sizeof(struct rkdrm_framebuffer), 465 M_DRM, M_WAITOK | M_ZERO); 466 467 drm_fb_helper_single_add_all_connectors(&sc->helper); 468 drm_helper_disable_unused_functions(&sc->sc_ddev); 469 drm_fb_helper_initial_config(&sc->helper, 32); 470 471 /* XXX */ 472 sc->sc_ddev.irq_enabled = true; 473 drm_vblank_init(&sc->sc_ddev, 1); 474 475 task_set(&sc->switchtask, rkdrm_doswitch, ri); 476 477 drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper); 478 479 sfb = to_rkdrm_framebuffer(helper->fb); 480 ri->ri_bits = sfb->obj->vaddr; 481 ri->ri_flg = RI_CENTER | RI_VCONS; 482 ri->ri_depth = helper->fb->format->depth; 483 ri->ri_width = helper->fb->width; 484 ri->ri_height = helper->fb->height; 485 ri->ri_stride = ri->ri_width * ri->ri_depth / 8; 486 rasops_init(ri, helper->fb->height, helper->fb->width); 487 ri->ri_hw = sc; 488 489 rkdrm_stdscreen.capabilities = ri->ri_caps; 490 rkdrm_stdscreen.nrows = ri->ri_rows; 491 rkdrm_stdscreen.ncols = ri->ri_cols; 492 rkdrm_stdscreen.textops = &ri->ri_ops; 493 rkdrm_stdscreen.fontwidth = ri->ri_font->fontwidth; 494 rkdrm_stdscreen.fontheight = ri->ri_font->fontheight; 495 496 memset(&aa, 0, sizeof(aa)); 497 aa.scrdata = &rkdrm_screenlist; 498 aa.accessops = &rkdrm_accessops; 499 aa.accesscookie = ri; 500 501 printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dv_xname, 502 ri->ri_width, ri->ri_height, ri->ri_depth); 503 504 config_found_sm(&sc->sc_dev, &aa, wsemuldisplaydevprint, 505 wsemuldisplaydevsubmatch); 506 507 drm_dev_register(&sc->sc_ddev, 0); 508 } 509 510 int 511 rkdrm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) 512 { 513 struct rkdrm_softc *sc = rkdrm_private(helper->dev); 514 struct drm_device *ddev = helper->dev; 515 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb); 516 struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 517 struct drm_framebuffer *fb = helper->fb; 518 struct wsemuldisplaydev_attach_args aa; 519 struct rasops_info *ri = &sc->ro; 520 struct rkdrmfb_attach_args sfa; 521 unsigned int bytes_per_pixel; 522 struct fb_info *info; 523 size_t size; 524 int error; 525 526 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 527 528 mode_cmd.width = sizes->surface_width; 529 mode_cmd.height = sizes->surface_height; 530 mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; 531 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 532 sizes->surface_depth); 533 534 size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE); 535 536 /* FIXME: CMA pool? */ 537 538 sfb->obj = drm_gem_cma_create(ddev, size); 539 if (sfb->obj == NULL) { 540 DRM_ERROR("failed to allocate memory for framebuffer\n"); 541 return -ENOMEM; 542 } 543 544 drm_helper_mode_fill_fb_struct(ddev, fb, &mode_cmd); 545 fb->format = drm_format_info(DRM_FORMAT_ARGB8888); 546 error = drm_framebuffer_init(ddev, fb, &rkdrm_framebuffer_funcs); 547 if (error != 0) { 548 DRM_ERROR("failed to initialize framebuffer\n"); 549 return error; 550 } 551 552 info = drm_fb_helper_alloc_fbi(helper); 553 if (IS_ERR(info)) { 554 DRM_ERROR("Failed to allocate fb_info\n"); 555 return error; 556 } 557 info->par = helper; 558 return 0; 559 } 560