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