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