linux/drivers/gpu/drm/nouveau/nouveau_abi16.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 */
  23
  24#include <nvif/client.h>
  25#include <nvif/driver.h>
  26#include <nvif/ioctl.h>
  27#include <nvif/class.h>
  28#include <nvif/unpack.h>
  29
  30#include "nouveau_drm.h"
  31#include "nouveau_dma.h"
  32#include "nouveau_gem.h"
  33#include "nouveau_chan.h"
  34#include "nouveau_abi16.h"
  35
  36static struct nouveau_abi16 *
  37nouveau_abi16(struct drm_file *file_priv)
  38{
  39        struct nouveau_cli *cli = nouveau_cli(file_priv);
  40        if (!cli->abi16) {
  41                struct nouveau_abi16 *abi16;
  42                cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
  43                if (cli->abi16) {
  44                        struct nv_device_v0 args = {
  45                                .device = ~0ULL,
  46                        };
  47
  48                        INIT_LIST_HEAD(&abi16->channels);
  49
  50                        /* allocate device object targeting client's default
  51                         * device (ie. the one that belongs to the fd it
  52                         * opened)
  53                         */
  54                        if (nvif_device_init(&cli->base.object, 0, NV_DEVICE,
  55                                             &args, sizeof(args),
  56                                             &abi16->device) == 0)
  57                                return cli->abi16;
  58
  59                        kfree(cli->abi16);
  60                        cli->abi16 = NULL;
  61                }
  62        }
  63        return cli->abi16;
  64}
  65
  66struct nouveau_abi16 *
  67nouveau_abi16_get(struct drm_file *file_priv)
  68{
  69        struct nouveau_cli *cli = nouveau_cli(file_priv);
  70        mutex_lock(&cli->mutex);
  71        if (nouveau_abi16(file_priv))
  72                return cli->abi16;
  73        mutex_unlock(&cli->mutex);
  74        return NULL;
  75}
  76
  77int
  78nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
  79{
  80        struct nouveau_cli *cli = (void *)abi16->device.object.client;
  81        mutex_unlock(&cli->mutex);
  82        return ret;
  83}
  84
  85s32
  86nouveau_abi16_swclass(struct nouveau_drm *drm)
  87{
  88        switch (drm->device.info.family) {
  89        case NV_DEVICE_INFO_V0_TNT:
  90                return NVIF_IOCTL_NEW_V0_SW_NV04;
  91        case NV_DEVICE_INFO_V0_CELSIUS:
  92        case NV_DEVICE_INFO_V0_KELVIN:
  93        case NV_DEVICE_INFO_V0_RANKINE:
  94        case NV_DEVICE_INFO_V0_CURIE:
  95                return NVIF_IOCTL_NEW_V0_SW_NV10;
  96        case NV_DEVICE_INFO_V0_TESLA:
  97                return NVIF_IOCTL_NEW_V0_SW_NV50;
  98        case NV_DEVICE_INFO_V0_FERMI:
  99        case NV_DEVICE_INFO_V0_KEPLER:
 100        case NV_DEVICE_INFO_V0_MAXWELL:
 101                return NVIF_IOCTL_NEW_V0_SW_GF100;
 102        }
 103
 104        return 0x0000;
 105}
 106
 107static void
 108nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
 109                        struct nouveau_abi16_ntfy *ntfy)
 110{
 111        nvif_object_fini(&ntfy->object);
 112        nvkm_mm_free(&chan->heap, &ntfy->node);
 113        list_del(&ntfy->head);
 114        kfree(ntfy);
 115}
 116
 117static void
 118nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
 119                        struct nouveau_abi16_chan *chan)
 120{
 121        struct nouveau_abi16_ntfy *ntfy, *temp;
 122
 123        /* wait for all activity to stop before releasing notify object, which
 124         * may be still in use */
 125        if (chan->chan && chan->ntfy)
 126                nouveau_channel_idle(chan->chan);
 127
 128        /* cleanup notifier state */
 129        list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
 130                nouveau_abi16_ntfy_fini(chan, ntfy);
 131        }
 132
 133        if (chan->ntfy) {
 134                nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
 135                nouveau_bo_unpin(chan->ntfy);
 136                drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
 137        }
 138
 139        if (chan->heap.block_size)
 140                nvkm_mm_fini(&chan->heap);
 141
 142        /* destroy channel object, all children will be killed too */
 143        if (chan->chan) {
 144                nouveau_channel_idle(chan->chan);
 145                nouveau_channel_del(&chan->chan);
 146        }
 147
 148        list_del(&chan->head);
 149        kfree(chan);
 150}
 151
 152void
 153nouveau_abi16_fini(struct nouveau_abi16 *abi16)
 154{
 155        struct nouveau_cli *cli = (void *)abi16->device.object.client;
 156        struct nouveau_abi16_chan *chan, *temp;
 157
 158        /* cleanup channels */
 159        list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
 160                nouveau_abi16_chan_fini(abi16, chan);
 161        }
 162
 163        /* destroy the device object */
 164        nvif_device_fini(&abi16->device);
 165
 166        kfree(cli->abi16);
 167        cli->abi16 = NULL;
 168}
 169
 170int
 171nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
 172{
 173        struct nouveau_cli *cli = nouveau_cli(file_priv);
 174        struct nouveau_drm *drm = nouveau_drm(dev);
 175        struct nvif_device *device = &drm->device;
 176        struct nvkm_gr *gr = nvxx_gr(device);
 177        struct drm_nouveau_getparam *getparam = data;
 178
 179        switch (getparam->param) {
 180        case NOUVEAU_GETPARAM_CHIPSET_ID:
 181                getparam->value = device->info.chipset;
 182                break;
 183        case NOUVEAU_GETPARAM_PCI_VENDOR:
 184                if (nvxx_device(device)->func->pci)
 185                        getparam->value = dev->pdev->vendor;
 186                else
 187                        getparam->value = 0;
 188                break;
 189        case NOUVEAU_GETPARAM_PCI_DEVICE:
 190                if (nvxx_device(device)->func->pci)
 191                        getparam->value = dev->pdev->device;
 192                else
 193                        getparam->value = 0;
 194                break;
 195        case NOUVEAU_GETPARAM_BUS_TYPE:
 196                if (!nvxx_device(device)->func->pci)
 197                        getparam->value = 3;
 198                else
 199                if (drm_pci_device_is_agp(dev))
 200                        getparam->value = 0;
 201                else
 202                if (!pci_is_pcie(dev->pdev))
 203                        getparam->value = 1;
 204                else
 205                        getparam->value = 2;
 206                break;
 207        case NOUVEAU_GETPARAM_FB_SIZE:
 208                getparam->value = drm->gem.vram_available;
 209                break;
 210        case NOUVEAU_GETPARAM_AGP_SIZE:
 211                getparam->value = drm->gem.gart_available;
 212                break;
 213        case NOUVEAU_GETPARAM_VM_VRAM_BASE:
 214                getparam->value = 0; /* deprecated */
 215                break;
 216        case NOUVEAU_GETPARAM_PTIMER_TIME:
 217                getparam->value = nvif_device_time(device);
 218                break;
 219        case NOUVEAU_GETPARAM_HAS_BO_USAGE:
 220                getparam->value = 1;
 221                break;
 222        case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
 223                getparam->value = 1;
 224                break;
 225        case NOUVEAU_GETPARAM_GRAPH_UNITS:
 226                getparam->value = nvkm_gr_units(gr);
 227                break;
 228        default:
 229                NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param);
 230                return -EINVAL;
 231        }
 232
 233        return 0;
 234}
 235
 236int
 237nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
 238{
 239        return -EINVAL;
 240}
 241
 242int
 243nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
 244{
 245        struct drm_nouveau_channel_alloc *init = data;
 246        struct nouveau_cli *cli = nouveau_cli(file_priv);
 247        struct nouveau_drm *drm = nouveau_drm(dev);
 248        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
 249        struct nouveau_abi16_chan *chan;
 250        struct nvif_device *device;
 251        int ret;
 252
 253        if (unlikely(!abi16))
 254                return -ENOMEM;
 255
 256        if (!drm->channel)
 257                return nouveau_abi16_put(abi16, -ENODEV);
 258
 259        device = &abi16->device;
 260
 261        /* hack to allow channel engine type specification on kepler */
 262        if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
 263                if (init->fb_ctxdma_handle != ~0)
 264                        init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
 265                else
 266                        init->fb_ctxdma_handle = init->tt_ctxdma_handle;
 267
 268                /* allow flips to be executed if this is a graphics channel */
 269                init->tt_ctxdma_handle = 0;
 270                if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
 271                        init->tt_ctxdma_handle = 1;
 272        }
 273
 274        if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
 275                return nouveau_abi16_put(abi16, -EINVAL);
 276
 277        /* allocate "abi16 channel" data and make up a handle for it */
 278        chan = kzalloc(sizeof(*chan), GFP_KERNEL);
 279        if (!chan)
 280                return nouveau_abi16_put(abi16, -ENOMEM);
 281
 282        INIT_LIST_HEAD(&chan->notifiers);
 283        list_add(&chan->head, &abi16->channels);
 284
 285        /* create channel object and initialise dma and fence management */
 286        ret = nouveau_channel_new(drm, device, init->fb_ctxdma_handle,
 287                                  init->tt_ctxdma_handle, &chan->chan);
 288        if (ret)
 289                goto done;
 290
 291        init->channel = chan->chan->chid;
 292
 293        if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
 294                init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
 295                                        NOUVEAU_GEM_DOMAIN_GART;
 296        else
 297        if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
 298                init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
 299        else
 300                init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
 301
 302        if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
 303                init->subchan[0].handle = 0x00000000;
 304                init->subchan[0].grclass = 0x0000;
 305                init->subchan[1].handle = chan->chan->nvsw.handle;
 306                init->subchan[1].grclass = 0x506e;
 307                init->nr_subchan = 2;
 308        }
 309
 310        /* Named memory object area */
 311        ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
 312                              0, 0, &chan->ntfy);
 313        if (ret == 0)
 314                ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false);
 315        if (ret)
 316                goto done;
 317
 318        if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
 319                ret = nouveau_bo_vma_add(chan->ntfy, cli->vm,
 320                                        &chan->ntfy_vma);
 321                if (ret)
 322                        goto done;
 323        }
 324
 325        ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
 326                                    &init->notifier_handle);
 327        if (ret)
 328                goto done;
 329
 330        ret = nvkm_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
 331done:
 332        if (ret)
 333                nouveau_abi16_chan_fini(abi16, chan);
 334        return nouveau_abi16_put(abi16, ret);
 335}
 336
 337static struct nouveau_abi16_chan *
 338nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel)
 339{
 340        struct nouveau_abi16_chan *chan;
 341
 342        list_for_each_entry(chan, &abi16->channels, head) {
 343                if (chan->chan->chid == channel)
 344                        return chan;
 345        }
 346
 347        return NULL;
 348}
 349
 350int
 351nouveau_abi16_usif(struct drm_file *file_priv, void *data, u32 size)
 352{
 353        union {
 354                struct nvif_ioctl_v0 v0;
 355        } *args = data;
 356        struct nouveau_abi16_chan *chan;
 357        struct nouveau_abi16 *abi16;
 358        int ret;
 359
 360        if (nvif_unpack(args->v0, 0, 0, true)) {
 361                switch (args->v0.type) {
 362                case NVIF_IOCTL_V0_NEW:
 363                case NVIF_IOCTL_V0_MTHD:
 364                case NVIF_IOCTL_V0_SCLASS:
 365                        break;
 366                default:
 367                        return -EACCES;
 368                }
 369        } else
 370                return ret;
 371
 372        if (!(abi16 = nouveau_abi16(file_priv)))
 373                return -ENOMEM;
 374
 375        if (args->v0.token != ~0ULL) {
 376                if (!(chan = nouveau_abi16_chan(abi16, args->v0.token)))
 377                        return -EINVAL;
 378                args->v0.object = nvif_handle(&chan->chan->user);
 379                args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
 380                return 0;
 381        }
 382
 383        args->v0.object = nvif_handle(&abi16->device.object);
 384        args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
 385        return 0;
 386}
 387
 388int
 389nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
 390{
 391        struct drm_nouveau_channel_free *req = data;
 392        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
 393        struct nouveau_abi16_chan *chan;
 394
 395        if (unlikely(!abi16))
 396                return -ENOMEM;
 397
 398        chan = nouveau_abi16_chan(abi16, req->channel);
 399        if (!chan)
 400                return nouveau_abi16_put(abi16, -ENOENT);
 401        nouveau_abi16_chan_fini(abi16, chan);
 402        return nouveau_abi16_put(abi16, 0);
 403}
 404
 405int
 406nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
 407{
 408        struct drm_nouveau_grobj_alloc *init = data;
 409        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
 410        struct nouveau_abi16_chan *chan;
 411        struct nouveau_abi16_ntfy *ntfy;
 412        struct nvif_client *client;
 413        struct nvif_sclass *sclass;
 414        s32 oclass = 0;
 415        int ret, i;
 416
 417        if (unlikely(!abi16))
 418                return -ENOMEM;
 419
 420        if (init->handle == ~0)
 421                return nouveau_abi16_put(abi16, -EINVAL);
 422        client = abi16->device.object.client;
 423
 424        chan = nouveau_abi16_chan(abi16, init->channel);
 425        if (!chan)
 426                return nouveau_abi16_put(abi16, -ENOENT);
 427
 428        ret = nvif_object_sclass_get(&chan->chan->user, &sclass);
 429        if (ret < 0)
 430                return nouveau_abi16_put(abi16, ret);
 431
 432        if ((init->class & 0x00ff) == 0x006e) {
 433                /* nvsw: compatibility with older 0x*6e class identifier */
 434                for (i = 0; !oclass && i < ret; i++) {
 435                        switch (sclass[i].oclass) {
 436                        case NVIF_IOCTL_NEW_V0_SW_NV04:
 437                        case NVIF_IOCTL_NEW_V0_SW_NV10:
 438                        case NVIF_IOCTL_NEW_V0_SW_NV50:
 439                        case NVIF_IOCTL_NEW_V0_SW_GF100:
 440                                oclass = sclass[i].oclass;
 441                                break;
 442                        default:
 443                                break;
 444                        }
 445                }
 446        } else
 447        if ((init->class & 0x00ff) == 0x00b1) {
 448                /* msvld: compatibility with incorrect version exposure */
 449                for (i = 0; i < ret; i++) {
 450                        if ((sclass[i].oclass & 0x00ff) == 0x00b1) {
 451                                oclass = sclass[i].oclass;
 452                                break;
 453                        }
 454                }
 455        } else
 456        if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */
 457                /* mspdec: compatibility with incorrect version exposure */
 458                for (i = 0; i < ret; i++) {
 459                        if ((sclass[i].oclass & 0x00ff) == 0x00b2) {
 460                                oclass = sclass[i].oclass;
 461                                break;
 462                        }
 463                }
 464        } else
 465        if ((init->class & 0x00ff) == 0x00b3) { /* msppp */
 466                /* msppp: compatibility with incorrect version exposure */
 467                for (i = 0; i < ret; i++) {
 468                        if ((sclass[i].oclass & 0x00ff) == 0x00b3) {
 469                                oclass = sclass[i].oclass;
 470                                break;
 471                        }
 472                }
 473        } else {
 474                oclass = init->class;
 475        }
 476
 477        nvif_object_sclass_put(&sclass);
 478        if (!oclass)
 479                return nouveau_abi16_put(abi16, -EINVAL);
 480
 481        ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
 482        if (!ntfy)
 483                return nouveau_abi16_put(abi16, -ENOMEM);
 484
 485        list_add(&ntfy->head, &chan->notifiers);
 486
 487        client->route = NVDRM_OBJECT_ABI16;
 488        ret = nvif_object_init(&chan->chan->user, init->handle, oclass,
 489                               NULL, 0, &ntfy->object);
 490        client->route = NVDRM_OBJECT_NVIF;
 491
 492        if (ret)
 493                nouveau_abi16_ntfy_fini(chan, ntfy);
 494        return nouveau_abi16_put(abi16, ret);
 495}
 496
 497int
 498nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
 499{
 500        struct drm_nouveau_notifierobj_alloc *info = data;
 501        struct nouveau_drm *drm = nouveau_drm(dev);
 502        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
 503        struct nouveau_abi16_chan *chan;
 504        struct nouveau_abi16_ntfy *ntfy;
 505        struct nvif_device *device = &abi16->device;
 506        struct nvif_client *client;
 507        struct nv_dma_v0 args = {};
 508        int ret;
 509
 510        if (unlikely(!abi16))
 511                return -ENOMEM;
 512
 513        /* completely unnecessary for these chipsets... */
 514        if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
 515                return nouveau_abi16_put(abi16, -EINVAL);
 516        client = abi16->device.object.client;
 517
 518        chan = nouveau_abi16_chan(abi16, info->channel);
 519        if (!chan)
 520                return nouveau_abi16_put(abi16, -ENOENT);
 521
 522        ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
 523        if (!ntfy)
 524                return nouveau_abi16_put(abi16, -ENOMEM);
 525
 526        list_add(&ntfy->head, &chan->notifiers);
 527
 528        ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1,
 529                           &ntfy->node);
 530        if (ret)
 531                goto done;
 532
 533        args.start = ntfy->node->offset;
 534        args.limit = ntfy->node->offset + ntfy->node->length - 1;
 535        if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
 536                args.target = NV_DMA_V0_TARGET_VM;
 537                args.access = NV_DMA_V0_ACCESS_VM;
 538                args.start += chan->ntfy_vma.offset;
 539                args.limit += chan->ntfy_vma.offset;
 540        } else
 541        if (drm->agp.bridge) {
 542                args.target = NV_DMA_V0_TARGET_AGP;
 543                args.access = NV_DMA_V0_ACCESS_RDWR;
 544                args.start += drm->agp.base + chan->ntfy->bo.offset;
 545                args.limit += drm->agp.base + chan->ntfy->bo.offset;
 546        } else {
 547                args.target = NV_DMA_V0_TARGET_VM;
 548                args.access = NV_DMA_V0_ACCESS_RDWR;
 549                args.start += chan->ntfy->bo.offset;
 550                args.limit += chan->ntfy->bo.offset;
 551        }
 552
 553        client->route = NVDRM_OBJECT_ABI16;
 554        client->super = true;
 555        ret = nvif_object_init(&chan->chan->user, info->handle,
 556                               NV_DMA_IN_MEMORY, &args, sizeof(args),
 557                               &ntfy->object);
 558        client->super = false;
 559        client->route = NVDRM_OBJECT_NVIF;
 560        if (ret)
 561                goto done;
 562
 563        info->offset = ntfy->node->offset;
 564done:
 565        if (ret)
 566                nouveau_abi16_ntfy_fini(chan, ntfy);
 567        return nouveau_abi16_put(abi16, ret);
 568}
 569
 570int
 571nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
 572{
 573        struct drm_nouveau_gpuobj_free *fini = data;
 574        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
 575        struct nouveau_abi16_chan *chan;
 576        struct nouveau_abi16_ntfy *ntfy;
 577        int ret = -ENOENT;
 578
 579        if (unlikely(!abi16))
 580                return -ENOMEM;
 581
 582        chan = nouveau_abi16_chan(abi16, fini->channel);
 583        if (!chan)
 584                return nouveau_abi16_put(abi16, -EINVAL);
 585
 586        /* synchronize with the user channel and destroy the gpu object */
 587        nouveau_channel_idle(chan->chan);
 588
 589        list_for_each_entry(ntfy, &chan->notifiers, head) {
 590                if (ntfy->object.handle == fini->handle) {
 591                        nouveau_abi16_ntfy_fini(chan, ntfy);
 592                        ret = 0;
 593                        break;
 594                }
 595        }
 596
 597        return nouveau_abi16_put(abi16, ret);
 598}
 599