1 /* $NetBSD: nouveau_display.c,v 1.6 2024/04/16 14:34:02 riastradh Exp $ */ 2 3 /* 4 * Copyright (C) 2008 Maarten Maathuis. 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining 8 * a copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sublicense, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the 16 * next paragraph) shall be included in all copies or substantial 17 * portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 23 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: nouveau_display.c,v 1.6 2024/04/16 14:34:02 riastradh Exp $"); 31 32 #include <acpi/video.h> 33 34 #include <drm/drm_atomic.h> 35 #include <drm/drm_atomic_helper.h> 36 #include <drm/drm_crtc_helper.h> 37 #include <drm/drm_fb_helper.h> 38 #include <drm/drm_fourcc.h> 39 #include <drm/drm_probe_helper.h> 40 #include <drm/drm_vblank.h> 41 42 #include "nouveau_fbcon.h" 43 #include "nouveau_crtc.h" 44 #include "nouveau_gem.h" 45 #include "nouveau_connector.h" 46 #include "nv50_display.h" 47 48 #include <nvif/class.h> 49 #include <nvif/cl0046.h> 50 #include <nvif/event.h> 51 52 #ifdef __NetBSD__ 53 /* Used only for runtime power management, not in NetBSD for now. */ 54 #undef CONFIG_ACPI 55 #endif 56 57 static int 58 nouveau_display_vblank_handler(struct nvif_notify *notify) 59 { 60 struct nouveau_crtc *nv_crtc = 61 container_of(notify, typeof(*nv_crtc), vblank); 62 drm_crtc_handle_vblank(&nv_crtc->base); 63 return NVIF_NOTIFY_KEEP; 64 } 65 66 int 67 nouveau_display_vblank_enable(struct drm_device *dev, unsigned int pipe) 68 { 69 struct drm_crtc *crtc; 70 struct nouveau_crtc *nv_crtc; 71 72 crtc = drm_crtc_from_index(dev, pipe); 73 if (!crtc) 74 return -EINVAL; 75 76 nv_crtc = nouveau_crtc(crtc); 77 nvif_notify_get(&nv_crtc->vblank); 78 79 return 0; 80 } 81 82 void 83 nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe) 84 { 85 struct drm_crtc *crtc; 86 struct nouveau_crtc *nv_crtc; 87 88 crtc = drm_crtc_from_index(dev, pipe); 89 if (!crtc) 90 return; 91 92 nv_crtc = nouveau_crtc(crtc); 93 nvif_notify_put(&nv_crtc->vblank); 94 } 95 96 static inline int 97 calc(int blanks, int blanke, int total, int line) 98 { 99 if (blanke >= blanks) { 100 if (line >= blanks) 101 line -= total; 102 } else { 103 if (line >= blanks) 104 line -= total; 105 line -= blanke + 1; 106 } 107 return line; 108 } 109 110 static bool 111 nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, 112 ktime_t *stime, ktime_t *etime) 113 { 114 struct { 115 struct nv04_disp_mthd_v0 base; 116 struct nv04_disp_scanoutpos_v0 scan; 117 } args = { 118 .base.method = NV04_DISP_SCANOUTPOS, 119 .base.head = nouveau_crtc(crtc)->index, 120 }; 121 struct nouveau_display *disp = nouveau_display(crtc->dev); 122 struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; 123 int retry = 20; 124 bool ret = false; 125 126 do { 127 ret = nvif_mthd(&disp->disp.object, 0, &args, sizeof(args)); 128 if (ret != 0) 129 return false; 130 131 if (args.scan.vline) { 132 ret = true; 133 break; 134 } 135 136 if (retry) ndelay(vblank->linedur_ns); 137 } while (retry--); 138 139 *hpos = args.scan.hline; 140 *vpos = calc(args.scan.vblanks, args.scan.vblanke, 141 args.scan.vtotal, args.scan.vline); 142 if (stime) *stime = ns_to_ktime(args.scan.time[0]); 143 if (etime) *etime = ns_to_ktime(args.scan.time[1]); 144 145 return ret; 146 } 147 148 bool 149 nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe, 150 bool in_vblank_irq, int *vpos, int *hpos, 151 ktime_t *stime, ktime_t *etime, 152 const struct drm_display_mode *mode) 153 { 154 struct drm_crtc *crtc; 155 156 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 157 if (nouveau_crtc(crtc)->index == pipe) { 158 return nouveau_display_scanoutpos_head(crtc, vpos, hpos, 159 stime, etime); 160 } 161 } 162 163 return false; 164 } 165 166 static void 167 nouveau_display_vblank_fini(struct drm_device *dev) 168 { 169 struct drm_crtc *crtc; 170 171 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 172 struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 173 nvif_notify_fini(&nv_crtc->vblank); 174 } 175 } 176 177 static int 178 nouveau_display_vblank_init(struct drm_device *dev) 179 { 180 struct nouveau_display *disp = nouveau_display(dev); 181 struct drm_crtc *crtc; 182 int ret; 183 184 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 185 struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 186 ret = nvif_notify_init(&disp->disp.object, 187 nouveau_display_vblank_handler, false, 188 NV04_DISP_NTFY_VBLANK, 189 &(struct nvif_notify_head_req_v0) { 190 .head = nv_crtc->index, 191 }, 192 sizeof(struct nvif_notify_head_req_v0), 193 sizeof(struct nvif_notify_head_rep_v0), 194 &nv_crtc->vblank); 195 if (ret) { 196 nouveau_display_vblank_fini(dev); 197 return ret; 198 } 199 } 200 201 ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 202 if (ret) { 203 nouveau_display_vblank_fini(dev); 204 return ret; 205 } 206 207 return 0; 208 } 209 210 static void 211 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) 212 { 213 struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); 214 215 if (fb->nvbo) 216 drm_gem_object_put_unlocked(&fb->nvbo->bo.base); 217 218 drm_framebuffer_cleanup(drm_fb); 219 kfree(fb); 220 } 221 222 static int 223 nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb, 224 struct drm_file *file_priv, 225 unsigned int *handle) 226 { 227 struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); 228 229 return drm_gem_handle_create(file_priv, &fb->nvbo->bo.base, handle); 230 } 231 232 static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { 233 .destroy = nouveau_user_framebuffer_destroy, 234 .create_handle = nouveau_user_framebuffer_create_handle, 235 }; 236 237 int 238 nouveau_framebuffer_new(struct drm_device *dev, 239 const struct drm_mode_fb_cmd2 *mode_cmd, 240 struct nouveau_bo *nvbo, 241 struct nouveau_framebuffer **pfb) 242 { 243 struct nouveau_drm *drm = nouveau_drm(dev); 244 struct nouveau_framebuffer *fb; 245 int ret; 246 247 /* YUV overlays have special requirements pre-NV50 */ 248 if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA && 249 250 (mode_cmd->pixel_format == DRM_FORMAT_YUYV || 251 mode_cmd->pixel_format == DRM_FORMAT_UYVY || 252 mode_cmd->pixel_format == DRM_FORMAT_NV12 || 253 mode_cmd->pixel_format == DRM_FORMAT_NV21) && 254 (mode_cmd->pitches[0] & 0x3f || /* align 64 */ 255 mode_cmd->pitches[0] >= 0x10000 || /* at most 64k pitch */ 256 (mode_cmd->pitches[1] && /* pitches for planes must match */ 257 mode_cmd->pitches[0] != mode_cmd->pitches[1]))) { 258 struct drm_format_name_buf format_name; 259 DRM_DEBUG_KMS("Unsuitable framebuffer: format: %s; pitches: 0x%x\n 0x%x\n", 260 drm_get_format_name(mode_cmd->pixel_format, 261 &format_name), 262 mode_cmd->pitches[0], 263 mode_cmd->pitches[1]); 264 return -EINVAL; 265 } 266 267 if (!(fb = *pfb = kzalloc(sizeof(*fb), GFP_KERNEL))) 268 return -ENOMEM; 269 270 drm_helper_mode_fill_fb_struct(dev, &fb->base, mode_cmd); 271 fb->nvbo = nvbo; 272 273 ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs); 274 if (ret) 275 kfree(fb); 276 return ret; 277 } 278 279 struct drm_framebuffer * 280 nouveau_user_framebuffer_create(struct drm_device *dev, 281 struct drm_file *file_priv, 282 const struct drm_mode_fb_cmd2 *mode_cmd) 283 { 284 struct nouveau_framebuffer *fb; 285 struct nouveau_bo *nvbo; 286 struct drm_gem_object *gem; 287 int ret; 288 289 gem = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); 290 if (!gem) 291 return ERR_PTR(-ENOENT); 292 nvbo = nouveau_gem_object(gem); 293 294 ret = nouveau_framebuffer_new(dev, mode_cmd, nvbo, &fb); 295 if (ret == 0) 296 return &fb->base; 297 298 drm_gem_object_put_unlocked(gem); 299 return ERR_PTR(ret); 300 } 301 302 static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { 303 .fb_create = nouveau_user_framebuffer_create, 304 .output_poll_changed = nouveau_fbcon_output_poll_changed, 305 }; 306 307 308 struct nouveau_drm_prop_enum_list { 309 u8 gen_mask; 310 int type; 311 const char *name; 312 }; 313 314 static struct nouveau_drm_prop_enum_list underscan[] = { 315 { 6, UNDERSCAN_AUTO, "auto" }, 316 { 6, UNDERSCAN_OFF, "off" }, 317 { 6, UNDERSCAN_ON, "on" }, 318 {} 319 }; 320 321 static struct nouveau_drm_prop_enum_list dither_mode[] = { 322 { 7, DITHERING_MODE_AUTO, "auto" }, 323 { 7, DITHERING_MODE_OFF, "off" }, 324 { 1, DITHERING_MODE_ON, "on" }, 325 { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, 326 { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, 327 { 4, DITHERING_MODE_TEMPORAL, "temporal" }, 328 {} 329 }; 330 331 static struct nouveau_drm_prop_enum_list dither_depth[] = { 332 { 6, DITHERING_DEPTH_AUTO, "auto" }, 333 { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, 334 { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, 335 {} 336 }; 337 338 #define PROP_ENUM(p,gen,n,list) do { \ 339 struct nouveau_drm_prop_enum_list *l = (list); \ 340 int c = 0; \ 341 while (l->gen_mask) { \ 342 if (l->gen_mask & (1 << (gen))) \ 343 c++; \ 344 l++; \ 345 } \ 346 if (c) { \ 347 p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ 348 l = (list); \ 349 while (p && l->gen_mask) { \ 350 if (l->gen_mask & (1 << (gen))) { \ 351 drm_property_add_enum(p, l->type, l->name); \ 352 } \ 353 l++; \ 354 } \ 355 } \ 356 } while(0) 357 358 static void 359 nouveau_display_hpd_work(struct work_struct *work) 360 { 361 struct nouveau_drm *drm = container_of(work, typeof(*drm), hpd_work); 362 363 pm_runtime_get_sync(drm->dev->dev); 364 365 drm_helper_hpd_irq_event(drm->dev); 366 367 pm_runtime_mark_last_busy(drm->dev->dev); 368 pm_runtime_put_sync(drm->dev->dev); 369 } 370 371 #ifdef CONFIG_ACPI 372 373 static int 374 nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val, 375 void *data) 376 { 377 struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); 378 struct acpi_bus_event *info = data; 379 int ret; 380 381 if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { 382 if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { 383 ret = pm_runtime_get(drm->dev->dev); 384 if (ret == 1 || ret == -EACCES) { 385 /* If the GPU is already awake, or in a state 386 * where we can't wake it up, it can handle 387 * it's own hotplug events. 388 */ 389 pm_runtime_put_autosuspend(drm->dev->dev); 390 } else if (ret == 0) { 391 /* This may be the only indication we receive 392 * of a connector hotplug on a runtime 393 * suspended GPU, schedule hpd_work to check. 394 */ 395 NV_DEBUG(drm, "ACPI requested connector reprobe\n"); 396 schedule_work(&drm->hpd_work); 397 pm_runtime_put_noidle(drm->dev->dev); 398 } else { 399 NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n", 400 ret); 401 } 402 403 /* acpi-video should not generate keypresses for this */ 404 return NOTIFY_BAD; 405 } 406 } 407 408 return NOTIFY_DONE; 409 } 410 #endif 411 412 int 413 nouveau_display_init(struct drm_device *dev, bool resume, bool runtime) 414 { 415 struct nouveau_display *disp = nouveau_display(dev); 416 struct drm_connector *connector; 417 struct drm_connector_list_iter conn_iter; 418 int ret; 419 420 /* 421 * Enable hotplug interrupts (done as early as possible, since we need 422 * them for MST) 423 */ 424 drm_connector_list_iter_begin(dev, &conn_iter); 425 nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 426 struct nouveau_connector *conn = nouveau_connector(connector); 427 nvif_notify_get(&conn->hpd); 428 } 429 drm_connector_list_iter_end(&conn_iter); 430 431 ret = disp->init(dev, resume, runtime); 432 if (ret) 433 return ret; 434 435 /* enable connector detection and polling for connectors without HPD 436 * support 437 */ 438 drm_kms_helper_poll_enable(dev); 439 440 return ret; 441 } 442 443 void 444 nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime) 445 { 446 struct nouveau_display *disp = nouveau_display(dev); 447 struct nouveau_drm *drm = nouveau_drm(dev); 448 struct drm_connector *connector; 449 struct drm_connector_list_iter conn_iter; 450 451 if (!suspend) { 452 if (drm_drv_uses_atomic_modeset(dev)) 453 drm_atomic_helper_shutdown(dev); 454 else 455 drm_helper_force_disable_all(dev); 456 } 457 458 /* disable hotplug interrupts */ 459 drm_connector_list_iter_begin(dev, &conn_iter); 460 nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 461 struct nouveau_connector *conn = nouveau_connector(connector); 462 nvif_notify_put(&conn->hpd); 463 } 464 drm_connector_list_iter_end(&conn_iter); 465 466 if (!runtime) 467 cancel_work_sync(&drm->hpd_work); 468 469 drm_kms_helper_poll_disable(dev); 470 disp->fini(dev, suspend); 471 } 472 473 static void 474 nouveau_display_create_properties(struct drm_device *dev) 475 { 476 struct nouveau_display *disp = nouveau_display(dev); 477 int gen; 478 479 if (disp->disp.object.oclass < NV50_DISP) 480 gen = 0; 481 else 482 if (disp->disp.object.oclass < GF110_DISP) 483 gen = 1; 484 else 485 gen = 2; 486 487 PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); 488 PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); 489 PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); 490 491 disp->underscan_hborder_property = 492 drm_property_create_range(dev, 0, "underscan hborder", 0, 128); 493 494 disp->underscan_vborder_property = 495 drm_property_create_range(dev, 0, "underscan vborder", 0, 128); 496 497 if (gen < 1) 498 return; 499 500 /* -90..+90 */ 501 disp->vibrant_hue_property = 502 drm_property_create_range(dev, 0, "vibrant hue", 0, 180); 503 504 /* -100..+100 */ 505 disp->color_vibrance_property = 506 drm_property_create_range(dev, 0, "color vibrance", 0, 200); 507 } 508 509 int 510 nouveau_display_create(struct drm_device *dev) 511 { 512 struct nouveau_drm *drm = nouveau_drm(dev); 513 struct nvkm_device *device = nvxx_device(&drm->client.device); 514 struct nouveau_display *disp; 515 int ret; 516 517 disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL); 518 if (!disp) 519 return -ENOMEM; 520 521 drm_mode_config_init(dev); 522 drm_mode_create_scaling_mode_property(dev); 523 drm_mode_create_dvi_i_properties(dev); 524 525 dev->mode_config.funcs = &nouveau_mode_config_funcs; 526 dev->mode_config.fb_base = device->func->resource_addr(device, 1); 527 528 dev->mode_config.min_width = 0; 529 dev->mode_config.min_height = 0; 530 if (drm->client.device.info.family < NV_DEVICE_INFO_V0_CELSIUS) { 531 dev->mode_config.max_width = 2048; 532 dev->mode_config.max_height = 2048; 533 } else 534 if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { 535 dev->mode_config.max_width = 4096; 536 dev->mode_config.max_height = 4096; 537 } else 538 if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) { 539 dev->mode_config.max_width = 8192; 540 dev->mode_config.max_height = 8192; 541 } else { 542 dev->mode_config.max_width = 16384; 543 dev->mode_config.max_height = 16384; 544 } 545 546 dev->mode_config.preferred_depth = 24; 547 dev->mode_config.prefer_shadow = 1; 548 549 if (drm->client.device.info.chipset < 0x11) 550 dev->mode_config.async_page_flip = false; 551 else 552 dev->mode_config.async_page_flip = true; 553 554 drm_kms_helper_poll_init(dev); 555 drm_kms_helper_poll_disable(dev); 556 557 if (nouveau_modeset != 2 && drm->vbios.dcb.entries) { 558 ret = nvif_disp_ctor(&drm->client.device, 0, &disp->disp); 559 if (ret == 0) { 560 nouveau_display_create_properties(dev); 561 if (disp->disp.object.oclass < NV50_DISP) 562 ret = nv04_display_create(dev); 563 else 564 ret = nv50_display_create(dev); 565 } 566 } else { 567 ret = 0; 568 } 569 570 if (ret) 571 goto disp_create_err; 572 573 drm_mode_config_reset(dev); 574 575 if (dev->mode_config.num_crtc) { 576 ret = nouveau_display_vblank_init(dev); 577 if (ret) 578 goto vblank_err; 579 } 580 581 INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); 582 #ifdef CONFIG_ACPI 583 drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy; 584 register_acpi_notifier(&drm->acpi_nb); 585 #endif 586 587 return 0; 588 589 vblank_err: 590 disp->dtor(dev); 591 disp_create_err: 592 drm_kms_helper_poll_fini(dev); 593 drm_mode_config_cleanup(dev); 594 return ret; 595 } 596 597 void 598 nouveau_display_destroy(struct drm_device *dev) 599 { 600 struct nouveau_display *disp = nouveau_display(dev); 601 602 #ifdef CONFIG_ACPI 603 unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); 604 #endif 605 nouveau_display_vblank_fini(dev); 606 607 drm_kms_helper_poll_fini(dev); 608 drm_mode_config_cleanup(dev); 609 610 if (disp->dtor) 611 disp->dtor(dev); 612 613 nvif_disp_dtor(&disp->disp); 614 615 nouveau_drm(dev)->display = NULL; 616 kfree(disp); 617 } 618 619 int 620 nouveau_display_suspend(struct drm_device *dev, bool runtime) 621 { 622 struct nouveau_display *disp = nouveau_display(dev); 623 624 if (drm_drv_uses_atomic_modeset(dev)) { 625 if (!runtime) { 626 disp->suspend = drm_atomic_helper_suspend(dev); 627 if (IS_ERR(disp->suspend)) { 628 int ret = PTR_ERR(disp->suspend); 629 disp->suspend = NULL; 630 return ret; 631 } 632 } 633 } 634 635 nouveau_display_fini(dev, true, runtime); 636 return 0; 637 } 638 639 void 640 nouveau_display_resume(struct drm_device *dev, bool runtime) 641 { 642 struct nouveau_display *disp = nouveau_display(dev); 643 644 nouveau_display_init(dev, true, runtime); 645 646 if (drm_drv_uses_atomic_modeset(dev)) { 647 if (disp->suspend) { 648 drm_atomic_helper_resume(dev, disp->suspend); 649 disp->suspend = NULL; 650 } 651 return; 652 } 653 } 654 655 int 656 nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, 657 struct drm_mode_create_dumb *args) 658 { 659 struct nouveau_cli *cli = nouveau_cli(file_priv); 660 struct nouveau_bo *bo; 661 uint32_t domain; 662 int ret; 663 664 args->pitch = roundup(args->width * (args->bpp / 8), 256); 665 args->size = args->pitch * args->height; 666 args->size = roundup(args->size, PAGE_SIZE); 667 668 /* Use VRAM if there is any ; otherwise fallback to system memory */ 669 if (nouveau_drm(dev)->client.device.info.ram_size != 0) 670 domain = NOUVEAU_GEM_DOMAIN_VRAM; 671 else 672 domain = NOUVEAU_GEM_DOMAIN_GART; 673 674 ret = nouveau_gem_new(cli, args->size, 0, domain, 0, 0, &bo); 675 if (ret) 676 return ret; 677 678 ret = drm_gem_handle_create(file_priv, &bo->bo.base, &args->handle); 679 drm_gem_object_put_unlocked(&bo->bo.base); 680 return ret; 681 } 682 683 int 684 nouveau_display_dumb_map_offset(struct drm_file *file_priv, 685 struct drm_device *dev, 686 uint32_t handle, uint64_t *poffset) 687 { 688 struct drm_gem_object *gem; 689 690 gem = drm_gem_object_lookup(file_priv, handle); 691 if (gem) { 692 struct nouveau_bo *bo = nouveau_gem_object(gem); 693 *poffset = drm_vma_node_offset_addr(&bo->bo.base.vma_node); 694 drm_gem_object_put_unlocked(gem); 695 return 0; 696 } 697 698 return -ENOENT; 699 } 700