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