Linux Audio

Check our new training course

Embedded Linux training

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