1 /* $OpenBSD: rkdrm.c,v 1.11 2021/07/07 02:38:21 jsg 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/drm_atomic.h> 48 #include <drm/drm_atomic_helper.h> 49 #include <drm/drm_drv.h> 50 #include <drm/drm_vblank.h> 51 #include <drm/drm_crtc_helper.h> 52 #include <drm/drm_fb_helper.h> 53 #include <drm/drm_gem.h> 54 55 #include <dev/fdt/rkdrm.h> 56 57 #define RK_DRM_MAX_WIDTH 3840 58 #define RK_DRM_MAX_HEIGHT 2160 59 60 TAILQ_HEAD(, rkdrm_ports) rkdrm_ports = 61 TAILQ_HEAD_INITIALIZER(rkdrm_ports); 62 63 int rkdrm_match(struct device *, void *, void *); 64 void rkdrm_attach(struct device *, struct device *, void *); 65 void rkdrm_attachhook(struct device *); 66 67 #ifdef notyet 68 vmem_t *rkdrm_alloc_cma_pool(struct drm_device *, size_t); 69 #endif 70 71 int rkdrm_load(struct drm_device *, unsigned long); 72 int rkdrm_unload(struct drm_device *); 73 74 int rkdrm_gem_fault(struct drm_gem_object *, struct uvm_faultinfo *, 75 off_t, vaddr_t, vm_page_t *, int, int, vm_prot_t, int); 76 77 struct drm_driver rkdrm_driver = { 78 .driver_features = DRIVER_ATOMIC | DRIVER_MODESET | DRIVER_GEM, 79 80 .dumb_create = drm_gem_cma_dumb_create, 81 .dumb_map_offset = drm_gem_dumb_map_offset, 82 83 .gem_free_object_unlocked = drm_gem_cma_free_object, 84 .gem_fault = drm_gem_cma_fault, 85 86 .name = DRIVER_NAME, 87 .desc = DRIVER_DESC, 88 .date = DRIVER_DATE, 89 .major = DRIVER_MAJOR, 90 .minor = DRIVER_MINOR, 91 .patchlevel = DRIVER_PATCHLEVEL, 92 }; 93 94 struct cfattach rkdrm_ca = { 95 sizeof (struct rkdrm_softc), rkdrm_match, rkdrm_attach 96 }; 97 98 struct cfdriver rkdrm_cd = { 99 NULL, "rkdrm", DV_DULL 100 }; 101 102 int 103 rkdrm_match(struct device *parent, void *match, void *aux) 104 { 105 struct fdt_attach_args *faa = aux; 106 107 return OF_is_compatible(faa->fa_node, "rockchip,display-subsystem"); 108 } 109 110 void 111 rkdrm_attach(struct device *parent, struct device *self, void *aux) 112 { 113 struct rkdrm_softc *sc = (struct rkdrm_softc *)self; 114 struct fdt_attach_args *faa = aux; 115 116 sc->sc_dmat = faa->fa_dmat; 117 sc->sc_iot = faa->fa_iot; 118 sc->sc_node = faa->fa_node; 119 120 printf("\n"); 121 122 /* 123 * Update our understanding of the console output node if 124 * we're using the framebuffer console. 125 */ 126 if (OF_is_compatible(stdout_node, "simple-framebuffer")) 127 stdout_node = sc->sc_node; 128 129 drm_attach_platform(&rkdrm_driver, faa->fa_iot, faa->fa_dmat, self, 130 &sc->sc_ddev); 131 config_mountroot(self, rkdrm_attachhook); 132 } 133 134 int 135 rkdrm_fb_create_handle(struct drm_framebuffer *fb, 136 struct drm_file *file, unsigned int *handle) 137 { 138 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb); 139 140 return drm_gem_handle_create(file, &sfb->obj->base, handle); 141 } 142 143 void 144 rkdrm_fb_destroy(struct drm_framebuffer *fb) 145 { 146 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb); 147 148 drm_framebuffer_cleanup(fb); 149 drm_gem_object_put(&sfb->obj->base); 150 free(sfb, M_DRM, sizeof(*sfb)); 151 } 152 153 struct drm_framebuffer_funcs rkdrm_framebuffer_funcs = { 154 .create_handle = rkdrm_fb_create_handle, 155 .destroy = rkdrm_fb_destroy, 156 }; 157 158 struct drm_framebuffer * 159 rkdrm_fb_create(struct drm_device *ddev, struct drm_file *file, 160 const struct drm_mode_fb_cmd2 *cmd) 161 { 162 struct rkdrm_framebuffer *fb; 163 struct drm_gem_object *gem_obj; 164 int error; 165 166 if (cmd->flags) 167 return NULL; 168 169 gem_obj = drm_gem_object_lookup(file, cmd->handles[0]); 170 if (gem_obj == NULL) 171 return NULL; 172 173 fb = malloc(sizeof(*fb), M_DRM, M_ZERO | M_WAITOK); 174 drm_helper_mode_fill_fb_struct(ddev, &fb->base, cmd); 175 fb->base.format = drm_format_info(DRM_FORMAT_ARGB8888); 176 fb->obj = to_drm_gem_cma_obj(gem_obj); 177 178 error = drm_framebuffer_init(ddev, &fb->base, &rkdrm_framebuffer_funcs); 179 if (error != 0) 180 goto dealloc; 181 182 return &fb->base; 183 184 dealloc: 185 drm_framebuffer_cleanup(&fb->base); 186 free(fb, M_DRM, sizeof(*fb)); 187 drm_gem_object_put(gem_obj); 188 189 return NULL; 190 } 191 192 struct drm_mode_config_helper_funcs rkdrm_mode_config_helper_funcs = 193 { 194 .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 195 }; 196 197 struct drm_mode_config_funcs rkdrm_mode_config_funcs = { 198 .fb_create = rkdrm_fb_create, 199 .atomic_check = drm_atomic_helper_check, 200 .atomic_commit = drm_atomic_helper_commit, 201 }; 202 203 int rkdrm_fb_probe(struct drm_fb_helper *, struct drm_fb_helper_surface_size *); 204 205 struct drm_fb_helper_funcs rkdrm_fb_helper_funcs = { 206 .fb_probe = rkdrm_fb_probe, 207 }; 208 209 int 210 rkdrm_unload(struct drm_device *ddev) 211 { 212 drm_mode_config_cleanup(ddev); 213 214 return 0; 215 } 216 217 void rkdrm_burner(void *, u_int, u_int); 218 int rkdrm_wsioctl(void *, u_long, caddr_t, int, struct proc *); 219 paddr_t rkdrm_wsmmap(void *, off_t, int); 220 int rkdrm_alloc_screen(void *, const struct wsscreen_descr *, 221 void **, int *, int *, uint32_t *); 222 void rkdrm_free_screen(void *, void *); 223 int rkdrm_show_screen(void *, void *, int, 224 void (*)(void *, int, int), void *); 225 void rkdrm_doswitch(void *); 226 void rkdrm_enter_ddb(void *, void *); 227 int rkdrm_get_param(struct wsdisplay_param *); 228 int rkdrm_set_param(struct wsdisplay_param *); 229 230 struct wsscreen_descr rkdrm_stdscreen = { 231 "std", 232 0, 0, 233 0, 234 0, 0, 235 WSSCREEN_UNDERLINE | WSSCREEN_HILIT | 236 WSSCREEN_REVERSE | WSSCREEN_WSCOLORS 237 }; 238 239 const struct wsscreen_descr *rkdrm_scrlist[] = { 240 &rkdrm_stdscreen, 241 }; 242 243 struct wsscreen_list rkdrm_screenlist = { 244 nitems(rkdrm_scrlist), rkdrm_scrlist 245 }; 246 247 struct wsdisplay_accessops rkdrm_accessops = { 248 .ioctl = rkdrm_wsioctl, 249 .mmap = rkdrm_wsmmap, 250 .alloc_screen = rkdrm_alloc_screen, 251 .free_screen = rkdrm_free_screen, 252 .show_screen = rkdrm_show_screen, 253 .enter_ddb = rkdrm_enter_ddb, 254 .getchar = rasops_getchar, 255 .load_font = rasops_load_font, 256 .list_font = rasops_list_font, 257 .scrollback = rasops_scrollback, 258 #ifdef notyet 259 .burn_screen = rkdrm_burner 260 #endif 261 }; 262 263 int 264 rkdrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 265 { 266 struct rasops_info *ri = v; 267 struct rkdrm_softc *sc = ri->ri_hw; 268 struct wsdisplay_param *dp = (struct wsdisplay_param *)data; 269 struct wsdisplay_fbinfo *wdf; 270 271 switch (cmd) { 272 case WSDISPLAYIO_GETPARAM: 273 if (ws_get_param) 274 return ws_get_param(dp); 275 return -1; 276 case WSDISPLAYIO_SETPARAM: 277 if (ws_set_param) 278 return ws_set_param(dp); 279 return -1; 280 case WSDISPLAYIO_GTYPE: 281 *(u_int *)data = WSDISPLAY_TYPE_RKDRM; 282 return 0; 283 case WSDISPLAYIO_GINFO: 284 wdf = (struct wsdisplay_fbinfo *)data; 285 wdf->width = ri->ri_width; 286 wdf->height = ri->ri_height; 287 wdf->depth = ri->ri_depth; 288 wdf->cmsize = 0; 289 return 0; 290 case WSDISPLAYIO_LINEBYTES: 291 *(u_int *)data = ri->ri_stride; 292 return 0; 293 } 294 295 return (-1); 296 } 297 298 paddr_t 299 rkdrm_wsmmap(void *v, off_t off, int prot) 300 { 301 struct rasops_info *ri = v; 302 struct rkdrm_softc *sc = ri->ri_hw; 303 struct drm_fb_helper *helper = &sc->helper; 304 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb); 305 uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr; 306 size_t size = sfb->obj->dmamap->dm_segs[0].ds_len; 307 308 if (off < 0 || off >= size) 309 return -1; 310 311 return ((paddr + off) | PMAP_NOCACHE); 312 } 313 314 int 315 rkdrm_alloc_screen(void *v, const struct wsscreen_descr *type, 316 void **cookiep, int *curxp, int *curyp, uint32_t *attrp) 317 { 318 return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); 319 } 320 321 void 322 rkdrm_free_screen(void *v, void *cookie) 323 { 324 return rasops_free_screen(v, cookie); 325 } 326 327 int 328 rkdrm_show_screen(void *v, void *cookie, int waitok, 329 void (*cb)(void *, int, int), void *cbarg) 330 { 331 struct rasops_info *ri = v; 332 struct rkdrm_softc *sc = ri->ri_hw; 333 334 if (cookie == ri->ri_active) 335 return (0); 336 337 sc->switchcb = cb; 338 sc->switchcbarg = cbarg; 339 sc->switchcookie = cookie; 340 if (cb) { 341 task_add(systq, &sc->switchtask); 342 return (EAGAIN); 343 } 344 345 rkdrm_doswitch(v); 346 347 return (0); 348 } 349 350 void 351 rkdrm_doswitch(void *v) 352 { 353 struct rasops_info *ri = v; 354 struct rkdrm_softc *sc = ri->ri_hw; 355 struct rkdrm_crtc *rkdrm_crtc; 356 int i, crtc; 357 358 rasops_show_screen(ri, sc->switchcookie, 0, NULL, NULL); 359 drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper); 360 361 if (sc->switchcb) 362 (sc->switchcb)(sc->switchcbarg, 0, 0); 363 } 364 365 void 366 rkdrm_enter_ddb(void *v, void *cookie) 367 { 368 struct rasops_info *ri = v; 369 struct rkdrm_softc *sc = ri->ri_hw; 370 struct drm_fb_helper *fb_helper = &sc->helper; 371 372 if (cookie == ri->ri_active) 373 return; 374 375 rasops_show_screen(ri, cookie, 0, NULL, NULL); 376 drm_fb_helper_debug_enter(fb_helper->fbdev); 377 } 378 379 void 380 rkdrm_attachhook(struct device *dev) 381 { 382 struct rkdrm_softc *sc = (struct rkdrm_softc *)dev; 383 struct wsemuldisplaydev_attach_args aa; 384 struct drm_fb_helper *helper = &sc->helper; 385 struct rasops_info *ri = &sc->ro; 386 struct rkdrm_framebuffer *sfb; 387 struct drm_device *ddev; 388 uint32_t *ports; 389 int i, portslen, nports; 390 int console = 0; 391 uint32_t defattr; 392 int error; 393 394 if (sc->sc_node == stdout_node) 395 console = 1; 396 397 portslen = OF_getproplen(sc->sc_node, "ports"); 398 if (portslen < 0) { 399 printf("%s: no display interface ports specified\n", 400 sc->sc_dev.dv_xname); 401 return; 402 } 403 404 drm_mode_config_init(&sc->sc_ddev); 405 sc->sc_ddev.mode_config.min_width = 0; 406 sc->sc_ddev.mode_config.min_height = 0; 407 sc->sc_ddev.mode_config.max_width = RK_DRM_MAX_WIDTH; 408 sc->sc_ddev.mode_config.max_height = RK_DRM_MAX_HEIGHT; 409 sc->sc_ddev.mode_config.funcs = &rkdrm_mode_config_funcs; 410 sc->sc_ddev.mode_config.helper_private = 411 &rkdrm_mode_config_helper_funcs; 412 413 nports = 0; 414 ports = malloc(portslen, M_TEMP, M_WAITOK); 415 OF_getpropintarray(sc->sc_node, "ports", ports, portslen); 416 for (i = 0; i < portslen / sizeof(uint32_t); i++) { 417 error = device_port_activate(ports[i], &sc->sc_ddev); 418 if (error == 0) 419 nports++; 420 } 421 free(ports, M_TEMP, portslen); 422 423 if (nports == 0) { 424 printf("%s: no display interface ports configured\n", 425 sc->sc_dev.dv_xname); 426 drm_mode_config_cleanup(&sc->sc_ddev); 427 return; 428 } 429 430 drm_mode_config_reset(&sc->sc_ddev); 431 432 drm_fb_helper_prepare(&sc->sc_ddev, &sc->helper, &rkdrm_fb_helper_funcs); 433 if (drm_fb_helper_init(&sc->sc_ddev, &sc->helper)) { 434 printf("%s: can't initialize framebuffer helper\n", 435 sc->sc_dev.dv_xname); 436 drm_mode_config_cleanup(&sc->sc_ddev); 437 return; 438 } 439 440 sc->helper.fb = malloc(sizeof(struct rkdrm_framebuffer), 441 M_DRM, M_WAITOK | M_ZERO); 442 443 drm_fb_helper_initial_config(&sc->helper, 32); 444 445 task_set(&sc->switchtask, rkdrm_doswitch, ri); 446 447 drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper); 448 449 sfb = to_rkdrm_framebuffer(helper->fb); 450 ri->ri_bits = sfb->obj->vaddr; 451 ri->ri_flg = RI_CENTER | RI_VCONS; 452 ri->ri_depth = helper->fb->format->depth; 453 ri->ri_width = helper->fb->width; 454 ri->ri_height = helper->fb->height; 455 ri->ri_stride = ri->ri_width * ri->ri_depth / 8; 456 ri->ri_rnum = 8; /* ARGB8888 */ 457 ri->ri_rpos = 16; 458 ri->ri_gnum = 8; 459 ri->ri_gpos = 8; 460 ri->ri_bnum = 8; 461 ri->ri_bpos = 0; 462 rasops_init(ri, 160, 160); 463 ri->ri_hw = sc; 464 465 rkdrm_stdscreen.capabilities = ri->ri_caps; 466 rkdrm_stdscreen.nrows = ri->ri_rows; 467 rkdrm_stdscreen.ncols = ri->ri_cols; 468 rkdrm_stdscreen.textops = &ri->ri_ops; 469 rkdrm_stdscreen.fontwidth = ri->ri_font->fontwidth; 470 rkdrm_stdscreen.fontheight = ri->ri_font->fontheight; 471 472 if (console) { 473 ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); 474 wsdisplay_cnattach(&rkdrm_stdscreen, ri->ri_active, 475 ri->ri_ccol, ri->ri_crow, defattr); 476 } 477 478 memset(&aa, 0, sizeof(aa)); 479 aa.scrdata = &rkdrm_screenlist; 480 aa.accessops = &rkdrm_accessops; 481 aa.accesscookie = ri; 482 aa.console = console; 483 484 printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dv_xname, 485 ri->ri_width, ri->ri_height, ri->ri_depth); 486 487 config_found_sm(&sc->sc_dev, &aa, wsemuldisplaydevprint, 488 wsemuldisplaydevsubmatch); 489 490 drm_dev_register(&sc->sc_ddev, 0); 491 } 492 493 int 494 rkdrm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) 495 { 496 struct rkdrm_softc *sc = rkdrm_private(helper->dev); 497 struct drm_device *ddev = helper->dev; 498 struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb); 499 struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 500 struct drm_framebuffer *fb = helper->fb; 501 struct wsemuldisplaydev_attach_args aa; 502 struct rasops_info *ri = &sc->ro; 503 struct rkdrmfb_attach_args sfa; 504 unsigned int bytes_per_pixel; 505 struct fb_info *info; 506 size_t size; 507 int error; 508 509 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 510 511 mode_cmd.width = sizes->surface_width; 512 mode_cmd.height = sizes->surface_height; 513 mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; 514 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 515 sizes->surface_depth); 516 517 size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE); 518 519 /* FIXME: CMA pool? */ 520 521 sfb->obj = drm_gem_cma_create(ddev, size); 522 if (sfb->obj == NULL) { 523 DRM_ERROR("failed to allocate memory for framebuffer\n"); 524 return -ENOMEM; 525 } 526 527 drm_helper_mode_fill_fb_struct(ddev, fb, &mode_cmd); 528 fb->format = drm_format_info(DRM_FORMAT_ARGB8888); 529 error = drm_framebuffer_init(ddev, fb, &rkdrm_framebuffer_funcs); 530 if (error != 0) { 531 DRM_ERROR("failed to initialize framebuffer\n"); 532 return error; 533 } 534 535 info = drm_fb_helper_alloc_fbi(helper); 536 if (IS_ERR(info)) { 537 DRM_ERROR("Failed to allocate fb_info\n"); 538 return error; 539 } 540 info->par = helper; 541 return 0; 542 } 543