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