1/* $NetBSD: rk_drm.c,v 1.21 2022/10/30 23:10:43 jmcneill Exp $ */ 2 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/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: rk_drm.c,v 1.21 2022/10/30 23:10:43 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34#include <sys/conf.h> 35#include <sys/device.h> 36#include <sys/intr.h> 37#include <sys/kernel.h> 38#include <sys/systm.h> 39 40#include <uvm/uvm_device.h> 41#include <uvm/uvm_extern.h> 42#include <uvm/uvm_object.h> 43 44#include <dev/fdt/fdt_port.h> 45#include <dev/fdt/fdtvar.h> 46 47#include <arm/rockchip/rk_drm.h> 48 49#include <drm/drm_atomic_helper.h> 50#include <drm/drm_auth.h> 51#include <drm/drm_crtc_helper.h> 52#include <drm/drm_damage_helper.h> 53#include <drm/drm_drv.h> 54#include <drm/drm_fb_helper.h> 55#include <drm/drm_fourcc.h> 56#include <drm/drm_vblank.h> 57 58#define RK_DRM_MAX_WIDTH 3840 59#define RK_DRM_MAX_HEIGHT 2160 60 61static TAILQ_HEAD(, rk_drm_ports) rk_drm_ports = 62 TAILQ_HEAD_INITIALIZER(rk_drm_ports); 63 64static const struct device_compatible_entry compat_data[] = { 65 { .compat = "rockchip,display-subsystem" }, 66 DEVICE_COMPAT_EOL 67}; 68 69static const char * fb_compatible[] = { 70 "simple-framebuffer", 71 NULL 72}; 73 74static int rk_drm_match(device_t, cfdata_t, void *); 75static void rk_drm_attach(device_t, device_t, void *); 76 77static void rk_drm_init(device_t); 78static vmem_t *rk_drm_alloc_cma_pool(struct drm_device *, size_t); 79 80static int rk_drm_load(struct drm_device *, unsigned long); 81static void rk_drm_unload(struct drm_device *); 82 83static void rk_drm_task_work(struct work *, void *); 84 85static struct drm_driver rk_drm_driver = { 86 .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, 87 .dev_priv_size = 0, 88 .load = rk_drm_load, 89 .unload = rk_drm_unload, 90 91 .gem_free_object = drm_gem_cma_free_object, 92 .mmap_object = drm_gem_or_legacy_mmap_object, 93 .gem_uvm_ops = &drm_gem_cma_uvm_ops, 94 95 .dumb_create = drm_gem_cma_dumb_create, 96 .dumb_destroy = drm_gem_dumb_destroy, 97 98 .name = DRIVER_NAME, 99 .desc = DRIVER_DESC, 100 .date = DRIVER_DATE, 101 .major = DRIVER_MAJOR, 102 .minor = DRIVER_MINOR, 103 .patchlevel = DRIVER_PATCHLEVEL, 104}; 105 106CFATTACH_DECL_NEW(rk_drm, sizeof(struct rk_drm_softc), 107 rk_drm_match, rk_drm_attach, NULL, NULL); 108 109static int 110rk_drm_match(device_t parent, cfdata_t cf, void *aux) 111{ 112 struct fdt_attach_args * const faa = aux; 113 114 return of_compatible_match(faa->faa_phandle, compat_data); 115} 116 117static void 118rk_drm_attach(device_t parent, device_t self, void *aux) 119{ 120 struct rk_drm_softc * const sc = device_private(self); 121 struct fdt_attach_args * const faa = aux; 122 struct drm_driver * const driver = &rk_drm_driver; 123 prop_dictionary_t dict = device_properties(self); 124 bool is_disabled; 125 126 aprint_naive("\n"); 127 128 if (prop_dictionary_get_bool(dict, "disabled", &is_disabled) && 129 is_disabled) { 130 aprint_normal(": (disabled)\n"); 131 return; 132 } 133 134 aprint_normal("\n"); 135 136#ifdef WSDISPLAY_MULTICONS 137 const bool is_console = true; 138 prop_dictionary_set_bool(dict, "is_console", is_console); 139#endif 140 141 sc->sc_dev = self; 142 sc->sc_dmat = faa->faa_dmat; 143 sc->sc_bst = faa->faa_bst; 144 sc->sc_phandle = faa->faa_phandle; 145 sc->sc_task_thread = NULL; 146 SIMPLEQ_INIT(&sc->sc_tasks); 147 if (workqueue_create(&sc->sc_task_wq, "rkdrm", 148 &rk_drm_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE)) { 149 aprint_error_dev(self, "unable to create workqueue\n"); 150 sc->sc_task_wq = NULL; 151 return; 152 } 153 154 sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); 155 if (IS_ERR(sc->sc_ddev)) { 156 aprint_error_dev(self, "couldn't allocate DRM device\n"); 157 return; 158 } 159 sc->sc_ddev->dev_private = sc; 160 sc->sc_ddev->bst = sc->sc_bst; 161 sc->sc_ddev->bus_dmat = sc->sc_dmat; 162 sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat; 163 sc->sc_ddev->dmat_subregion_p = false; 164 165 fdt_remove_bycompat(fb_compatible); 166 167 /* 168 * Wait until rk_vop is attached as a sibling to this device -- 169 * we need that to actually display our framebuffer. 170 */ 171 config_defer(self, rk_drm_init); 172} 173 174static void 175rk_drm_init(device_t dev) 176{ 177 struct rk_drm_softc * const sc = device_private(dev); 178 struct drm_driver * const driver = &rk_drm_driver; 179 int error; 180 181 /* 182 * Cause any tasks issued synchronously during attach to be 183 * processed at the end of this function. 184 */ 185 sc->sc_task_thread = curlwp; 186 187 error = -drm_dev_register(sc->sc_ddev, 0); 188 if (error) { 189 aprint_error_dev(dev, "couldn't register DRM device: %d\n", 190 error); 191 goto out; 192 } 193 sc->sc_dev_registered = true; 194 195 aprint_normal_dev(dev, "initialized %s %d.%d.%d %s on minor %d\n", 196 driver->name, driver->major, driver->minor, driver->patchlevel, 197 driver->date, sc->sc_ddev->primary->index); 198 199 /* 200 * Process asynchronous tasks queued synchronously during 201 * attach. This will be for display detection to attach a 202 * framebuffer, so we have the opportunity for a console device 203 * to attach before autoconf has completed, in time for init(8) 204 * to find that console without panicking. 205 */ 206 while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { 207 struct rk_drm_task *const task = SIMPLEQ_FIRST(&sc->sc_tasks); 208 209 SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, rdt_u.queue); 210 (*task->rdt_fn)(task); 211 } 212 213out: /* Cause any subsequent tasks to be processed by the workqueue. */ 214 atomic_store_relaxed(&sc->sc_task_thread, NULL); 215} 216 217static vmem_t * 218rk_drm_alloc_cma_pool(struct drm_device *ddev, size_t cma_size) 219{ 220 struct rk_drm_softc * const sc = rk_drm_private(ddev); 221 bus_dma_segment_t segs[1]; 222 int nsegs; 223 int error; 224 225 error = bus_dmamem_alloc(sc->sc_dmat, cma_size, PAGE_SIZE, 0, 226 segs, 1, &nsegs, BUS_DMA_NOWAIT); 227 if (error) { 228 aprint_error_dev(sc->sc_dev, "couldn't allocate CMA pool\n"); 229 return NULL; 230 } 231 232 return vmem_create("rkdrm", segs[0].ds_addr, segs[0].ds_len, 233 PAGE_SIZE, NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE); 234} 235 236static int 237rk_drm_fb_create_handle(struct drm_framebuffer *fb, 238 struct drm_file *file, unsigned int *handle) 239{ 240 struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb); 241 242 return drm_gem_handle_create(file, &sfb->obj->base, handle); 243} 244 245static void 246rk_drm_fb_destroy(struct drm_framebuffer *fb) 247{ 248 struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb); 249 250 drm_framebuffer_cleanup(fb); 251 drm_gem_object_put_unlocked(&sfb->obj->base); 252 kmem_free(sfb, sizeof(*sfb)); 253} 254 255static const struct drm_framebuffer_funcs rk_drm_framebuffer_funcs = { 256 .create_handle = rk_drm_fb_create_handle, 257 .destroy = rk_drm_fb_destroy, 258}; 259 260static struct drm_framebuffer * 261rk_drm_fb_create(struct drm_device *ddev, struct drm_file *file, 262 const struct drm_mode_fb_cmd2 *cmd) 263{ 264 struct rk_drm_framebuffer *fb; 265 struct drm_gem_object *gem_obj; 266 int error; 267 268 if (cmd->flags) 269 return NULL; 270 271 gem_obj = drm_gem_object_lookup(file, cmd->handles[0]); 272 if (gem_obj == NULL) 273 return NULL; 274 275 fb = kmem_zalloc(sizeof(*fb), KM_SLEEP); 276 drm_helper_mode_fill_fb_struct(ddev, &fb->base, cmd); 277 fb->obj = to_drm_gem_cma_obj(gem_obj); 278 279 error = drm_framebuffer_init(ddev, &fb->base, &rk_drm_framebuffer_funcs); 280 if (error != 0) 281 goto dealloc; 282 283 return &fb->base; 284 285dealloc: 286 drm_framebuffer_cleanup(&fb->base); 287 kmem_free(fb, sizeof(*fb)); 288 drm_gem_object_put_unlocked(gem_obj); 289 290 return NULL; 291} 292 293static struct drm_mode_config_funcs rk_drm_mode_config_funcs = { 294 .fb_create = rk_drm_fb_create, 295 .atomic_check = drm_atomic_helper_check, 296 .atomic_commit = drm_atomic_helper_commit, 297}; 298 299static struct drm_mode_config_helper_funcs rk_drm_mode_config_helper_funcs = { 300 .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 301}; 302 303static int 304rk_drm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) 305{ 306 struct rk_drm_softc * const sc = rk_drm_private(helper->dev); 307 struct drm_device *ddev = helper->dev; 308 struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(helper->fb); 309 struct drm_framebuffer *fb = helper->fb; 310 struct rk_drmfb_attach_args sfa; 311 size_t cma_size; 312 int error; 313 314 const u_int width = sizes->surface_width; 315 const u_int height = sizes->surface_height; 316 const u_int pitch = width * (32 / 8); 317 318 const size_t size = roundup(height * pitch, PAGE_SIZE); 319 320 /* Reserve enough memory for the FB console plus a 4K plane, rounded to 1MB */ 321 cma_size = size; 322 cma_size += (RK_DRM_MAX_WIDTH * RK_DRM_MAX_HEIGHT * 4); 323 cma_size = roundup(cma_size, 1024 * 1024); 324 sc->sc_ddev->cma_pool = rk_drm_alloc_cma_pool(sc->sc_ddev, cma_size); 325 if (sc->sc_ddev->cma_pool != NULL) 326 aprint_normal_dev(sc->sc_dev, "reserved %u MB DRAM for CMA\n", 327 (u_int)(cma_size / (1024 * 1024))); 328 329 sfb->obj = drm_gem_cma_create(ddev, size); 330 if (sfb->obj == NULL) { 331 DRM_ERROR("failed to allocate memory for framebuffer\n"); 332 return -ENOMEM; 333 } 334 335 /* similar to drm_helper_mode_fill_fb_struct(), but we have no cmd */ 336 fb->pitches[0] = pitch; 337 fb->offsets[0] = 0; 338 fb->width = width; 339 fb->height = height; 340 fb->modifier = 0; 341 fb->flags = 0; 342#ifdef __ARM_BIG_ENDIAN 343 fb->format = drm_format_info(DRM_FORMAT_BGRX8888); 344#else 345 fb->format = drm_format_info(DRM_FORMAT_XRGB8888); 346#endif 347 fb->dev = ddev; 348 349 error = drm_framebuffer_init(ddev, fb, &rk_drm_framebuffer_funcs); 350 if (error != 0) { 351 DRM_ERROR("failed to initialize framebuffer\n"); 352 return error; 353 } 354 355 memset(&sfa, 0, sizeof(sfa)); 356 sfa.sfa_drm_dev = ddev; 357 sfa.sfa_fb_helper = helper; 358 sfa.sfa_fb_sizes = *sizes; 359 sfa.sfa_fb_bst = sc->sc_bst; 360 sfa.sfa_fb_dmat = sc->sc_dmat; 361 sfa.sfa_fb_linebytes = helper->fb->pitches[0]; 362 363 helper->fbdev = config_found(ddev->dev, &sfa, NULL, 364 CFARGS(.iattr = "rkfbbus")); 365 if (helper->fbdev == NULL) { 366 DRM_ERROR("unable to attach framebuffer\n"); 367 return -ENXIO; 368 } 369 370 return 0; 371} 372 373static struct drm_fb_helper_funcs rk_drm_fb_helper_funcs = { 374 .fb_probe = rk_drm_fb_probe, 375}; 376 377static int 378rk_drm_load(struct drm_device *ddev, unsigned long flags) 379{ 380 struct rk_drm_softc * const sc = rk_drm_private(ddev); 381 struct rk_drm_ports *sport; 382 struct rk_drm_fbdev *fbdev; 383 struct fdt_endpoint *ep; 384 const u_int *data; 385 int datalen, error, num_crtc, ep_index; 386 387 drm_mode_config_init(ddev); 388 ddev->mode_config.min_width = 0; 389 ddev->mode_config.min_height = 0; 390 ddev->mode_config.max_width = RK_DRM_MAX_WIDTH; 391 ddev->mode_config.max_height = RK_DRM_MAX_HEIGHT; 392 ddev->mode_config.funcs = &rk_drm_mode_config_funcs; 393 ddev->mode_config.helper_private = &rk_drm_mode_config_helper_funcs; 394 395 num_crtc = 0; 396 data = fdtbus_get_prop(sc->sc_phandle, "ports", &datalen); 397 while (datalen >= 4) { 398 const int crtc_phandle = fdtbus_get_phandle_from_native(be32dec(data)); 399 400 TAILQ_FOREACH(sport, &rk_drm_ports, entries) 401 if (sport->phandle == crtc_phandle && sport->ddev == NULL) { 402 sport->ddev = ddev; 403 for (ep_index = 0; (ep = fdt_endpoint_get_from_index(sport->port, 0, ep_index)) != NULL; ep_index++) { 404 error = fdt_endpoint_activate_direct(ep, true); 405 if (error != 0) 406 aprint_debug_dev(sc->sc_dev, 407 "failed to activate endpoint %d: %d\n", 408 ep_index, error); 409 } 410 num_crtc++; 411 } 412 413 datalen -= 4; 414 data++; 415 } 416 417 if (num_crtc == 0) { 418 aprint_error_dev(sc->sc_dev, "no display interface ports configured\n"); 419 error = ENXIO; 420 goto drmerr; 421 } 422 423 drm_mode_config_reset(ddev); 424 425 fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP); 426 427 drm_fb_helper_prepare(ddev, &fbdev->helper, &rk_drm_fb_helper_funcs); 428 429 error = drm_fb_helper_init(ddev, &fbdev->helper, num_crtc); 430 if (error) 431 goto allocerr; 432 433 fbdev->helper.fb = kmem_zalloc(sizeof(struct rk_drm_framebuffer), KM_SLEEP); 434 435 drm_fb_helper_single_add_all_connectors(&fbdev->helper); 436 437 drm_fb_helper_initial_config(&fbdev->helper, 32); 438 439 /* XXX Delegate this to rk_vop.c? */ 440 ddev->irq_enabled = true; 441 drm_vblank_init(ddev, num_crtc); 442 443 return 0; 444 445allocerr: 446 kmem_free(fbdev, sizeof(*fbdev)); 447drmerr: 448 drm_mode_config_cleanup(ddev); 449 450 return error; 451} 452 453static void 454rk_drm_unload(struct drm_device *ddev) 455{ 456 drm_mode_config_cleanup(ddev); 457} 458 459int 460rk_drm_register_port(int phandle, struct fdt_device_ports *port) 461{ 462 struct rk_drm_ports *sport; 463 464 sport = kmem_zalloc(sizeof(*sport), KM_SLEEP); 465 sport->phandle = phandle; 466 sport->port = port; 467 sport->ddev = NULL; 468 TAILQ_INSERT_TAIL(&rk_drm_ports, sport, entries); 469 470 return 0; 471} 472 473struct drm_device * 474rk_drm_port_device(struct fdt_device_ports *port) 475{ 476 struct rk_drm_ports *sport; 477 478 TAILQ_FOREACH(sport, &rk_drm_ports, entries) 479 if (sport->port == port) 480 return sport->ddev; 481 482 return NULL; 483} 484 485static void 486rk_drm_task_work(struct work *work, void *cookie) 487{ 488 struct rk_drm_task *task = container_of(work, struct rk_drm_task, 489 rdt_u.work); 490 491 (*task->rdt_fn)(task); 492} 493 494void 495rk_task_init(struct rk_drm_task *task, 496 void (*fn)(struct rk_drm_task *)) 497{ 498 499 task->rdt_fn = fn; 500} 501 502void 503rk_task_schedule(device_t self, struct rk_drm_task *task) 504{ 505 struct rk_drm_softc *sc = device_private(self); 506 507 if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) 508 SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, rdt_u.queue); 509 else 510 workqueue_enqueue(sc->sc_task_wq, &task->rdt_u.work, NULL); 511} 512