nouveau_abi16.c 15.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2012 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 */

24 25 26
#include <nvif/client.h>
#include <nvif/driver.h>
#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 33
#include "nouveau_gem.h"
#include "nouveau_chan.h"
34
#include "nouveau_abi16.h"
35

36 37
static struct nouveau_abi16 *
nouveau_abi16(struct drm_file *file_priv)
38 39 40 41 42 43
{
	struct nouveau_cli *cli = nouveau_cli(file_priv);
	if (!cli->abi16) {
		struct nouveau_abi16 *abi16;
		cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
		if (cli->abi16) {
44 45 46 47
			struct nv_device_v0 args = {
				.device = ~0ULL,
			};

48 49 50 51 52 53
			INIT_LIST_HEAD(&abi16->channels);

			/* allocate device object targeting client's default
			 * device (ie. the one that belongs to the fd it
			 * opened)
			 */
54
			if (nvif_device_init(&cli->base.object, 0, NV_DEVICE,
55
					     &args, sizeof(args),
56
					     &abi16->device) == 0)
57 58 59 60 61 62 63 64 65
				return cli->abi16;

			kfree(cli->abi16);
			cli->abi16 = NULL;
		}
	}
	return cli->abi16;
}

66 67 68 69 70 71 72 73 74 75 76
struct nouveau_abi16 *
nouveau_abi16_get(struct drm_file *file_priv)
{
	struct nouveau_cli *cli = nouveau_cli(file_priv);
	mutex_lock(&cli->mutex);
	if (nouveau_abi16(file_priv))
		return cli->abi16;
	mutex_unlock(&cli->mutex);
	return NULL;
}

77 78 79
int
nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
{
80
	struct nouveau_cli *cli = (void *)abi16->device.object.client;
81 82 83 84
	mutex_unlock(&cli->mutex);
	return ret;
}

85
s32
86 87
nouveau_abi16_swclass(struct nouveau_drm *drm)
{
88 89
	switch (drm->device.info.family) {
	case NV_DEVICE_INFO_V0_TNT:
90
		return NVIF_IOCTL_NEW_V0_SW_NV04;
91 92 93 94
	case NV_DEVICE_INFO_V0_CELSIUS:
	case NV_DEVICE_INFO_V0_KELVIN:
	case NV_DEVICE_INFO_V0_RANKINE:
	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 99 100
	case NV_DEVICE_INFO_V0_FERMI:
	case NV_DEVICE_INFO_V0_KEPLER:
	case NV_DEVICE_INFO_V0_MAXWELL:
101
		return NVIF_IOCTL_NEW_V0_SW_GF100;
102 103 104 105 106 107 108 109 110
	}

	return 0x0000;
}

static void
nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
			struct nouveau_abi16_ntfy *ntfy)
{
111
	nvif_object_fini(&ntfy->object);
112
	nvkm_mm_free(&chan->heap, &ntfy->node);
113 114 115 116 117 118 119 120 121 122
	list_del(&ntfy->head);
	kfree(ntfy);
}

static void
nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
			struct nouveau_abi16_chan *chan)
{
	struct nouveau_abi16_ntfy *ntfy, *temp;

123 124 125 126 127
	/* wait for all activity to stop before releasing notify object, which
	 * may be still in use */
	if (chan->chan && chan->ntfy)
		nouveau_channel_idle(chan->chan);

128 129 130 131 132 133 134
	/* cleanup notifier state */
	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
		nouveau_abi16_ntfy_fini(chan, ntfy);
	}

	if (chan->ntfy) {
		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 143

	/* destroy channel object, all children will be killed too */
	if (chan->chan) {
144
		nouveau_channel_idle(chan->chan);
145 146 147 148 149 150 151 152 153 154
		nouveau_channel_del(&chan->chan);
	}

	list_del(&chan->head);
	kfree(chan);
}

void
nouveau_abi16_fini(struct nouveau_abi16 *abi16)
{
155
	struct nouveau_cli *cli = (void *)abi16->device.object.client;
156 157 158 159 160 161 162 163
	struct nouveau_abi16_chan *chan, *temp;

	/* cleanup channels */
	list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
		nouveau_abi16_chan_fini(abi16, chan);
	}

	/* destroy the device object */
164
	nvif_device_fini(&abi16->device);
165 166 167 168

	kfree(cli->abi16);
	cli->abi16 = NULL;
}
169 170 171 172

int
nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
{
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 178 179 180
	struct drm_nouveau_getparam *getparam = data;

	switch (getparam->param) {
	case NOUVEAU_GETPARAM_CHIPSET_ID:
181
		getparam->value = device->info.chipset;
182 183
		break;
	case NOUVEAU_GETPARAM_PCI_VENDOR:
184
		if (nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
185 186 187
			getparam->value = dev->pdev->vendor;
		else
			getparam->value = 0;
188 189
		break;
	case NOUVEAU_GETPARAM_PCI_DEVICE:
190
		if (nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
191 192 193
			getparam->value = dev->pdev->device;
		else
			getparam->value = 0;
194 195
		break;
	case NOUVEAU_GETPARAM_BUS_TYPE:
196
		if (!nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
197 198
			getparam->value = 3;
		else
199 200 201 202 203 204 205 206 207
		if (drm_pci_device_is_agp(dev))
			getparam->value = 0;
		else
		if (!pci_is_pcie(dev->pdev))
			getparam->value = 1;
		else
			getparam->value = 2;
		break;
	case NOUVEAU_GETPARAM_FB_SIZE:
208
		getparam->value = drm->gem.vram_available;
209 210
		break;
	case NOUVEAU_GETPARAM_AGP_SIZE:
211
		getparam->value = drm->gem.gart_available;
212 213 214 215 216
		break;
	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
		getparam->value = 0; /* deprecated */
		break;
	case NOUVEAU_GETPARAM_PTIMER_TIME:
217
		getparam->value = nvif_device_time(device);
218 219 220 221 222 223 224 225
		break;
	case NOUVEAU_GETPARAM_HAS_BO_USAGE:
		getparam->value = 1;
		break;
	case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
		getparam->value = 1;
		break;
	case NOUVEAU_GETPARAM_GRAPH_UNITS:
226
		getparam->value = nvkm_gr_units(gr);
227
		break;
228
	default:
B
Ben Skeggs 已提交
229
		NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
		return -EINVAL;
	}

	return 0;
}

int
nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
{
	return -EINVAL;
}

int
nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
{
	struct drm_nouveau_channel_alloc *init = data;
246 247
	struct nouveau_cli *cli = nouveau_cli(file_priv);
	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 252
	int ret;

253 254
	if (unlikely(!abi16))
		return -ENOMEM;
255 256 257 258

	if (!drm->channel)
		return nouveau_abi16_put(abi16, -ENODEV);

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 266 267 268 269
		else
			init->fb_ctxdma_handle = init->tt_ctxdma_handle;

		/* allow flips to be executed if this is a graphics channel */
		init->tt_ctxdma_handle = 0;
270
		if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
271 272 273 274 275 276
			init->tt_ctxdma_handle = 1;
	}

	if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
		return nouveau_abi16_put(abi16, -EINVAL);

277 278 279 280 281 282 283
	/* allocate "abi16 channel" data and make up a handle for it */
	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
	if (!chan)
		return nouveau_abi16_put(abi16, -ENOMEM);

	INIT_LIST_HEAD(&chan->notifiers);
	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 290
		goto done;

291 292
	init->channel = chan->chan->chid;

293
	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
294 295 296 297
		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
					NOUVEAU_GEM_DOMAIN_GART;
	else
	if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
298
		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
299 300
	else
		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
301

302
	if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
303 304
		init->subchan[0].handle = 0x00000000;
		init->subchan[0].grclass = 0x0000;
305
		init->subchan[1].handle = chan->chan->nvsw.handle;
306
		init->subchan[1].grclass = 0x506e;
307 308 309 310
		init->nr_subchan = 2;
	}

	/* Named memory object area */
311 312 313
	ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
			      0, 0, &chan->ntfy);
	if (ret == 0)
314
		ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false);
315 316 317
	if (ret)
		goto done;

318
	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
319
		ret = nouveau_bo_vma_add(chan->ntfy, cli->vm,
320 321 322 323 324
					&chan->ntfy_vma);
		if (ret)
			goto done;
	}

325
	ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
326
				    &init->notifier_handle);
327 328
	if (ret)
		goto done;
329

330
	ret = nvkm_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
331 332 333 334
done:
	if (ret)
		nouveau_abi16_chan_fini(abi16, chan);
	return nouveau_abi16_put(abi16, ret);
335 336
}

337 338 339 340 341 342
static struct nouveau_abi16_chan *
nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel)
{
	struct nouveau_abi16_chan *chan;

	list_for_each_entry(chan, &abi16->channels, head) {
343
		if (chan->chan->chid == channel)
344 345 346 347 348
			return chan;
	}

	return NULL;
}
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
int
nouveau_abi16_usif(struct drm_file *file_priv, void *data, u32 size)
{
	union {
		struct nvif_ioctl_v0 v0;
	} *args = data;
	struct nouveau_abi16_chan *chan;
	struct nouveau_abi16 *abi16;
	int ret;

	if (nvif_unpack(args->v0, 0, 0, true)) {
		switch (args->v0.type) {
		case NVIF_IOCTL_V0_NEW:
		case NVIF_IOCTL_V0_MTHD:
		case NVIF_IOCTL_V0_SCLASS:
			break;
		default:
			return -EACCES;
		}
	} else
		return ret;

	if (!(abi16 = nouveau_abi16(file_priv)))
		return -ENOMEM;

	if (args->v0.token != ~0ULL) {
		if (!(chan = nouveau_abi16_chan(abi16, args->v0.token)))
			return -EINVAL;
		args->v0.object = nvif_handle(&chan->chan->user);
		args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
		return 0;
	}

	args->v0.object = nvif_handle(&abi16->device.object);
	args->v0.owner  = NVIF_IOCTL_V0_OWNER_ANY;
	return 0;
}

388 389 390 391
int
nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
{
	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 396
	if (unlikely(!abi16))
		return -ENOMEM;
397

398 399 400 401 402
	chan = nouveau_abi16_chan(abi16, req->channel);
	if (!chan)
		return nouveau_abi16_put(abi16, -ENOENT);
	nouveau_abi16_chan_fini(abi16, chan);
	return nouveau_abi16_put(abi16, 0);
403 404 405 406 407 408
}

int
nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
{
	struct drm_nouveau_grobj_alloc *init = data;
409
	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
410 411
	struct nouveau_abi16_chan *chan;
	struct nouveau_abi16_ntfy *ntfy;
412
	struct nvif_client *client;
413
	struct nvif_sclass *sclass;
414 415
	s32 oclass = 0;
	int ret, i;
416

417 418 419
	if (unlikely(!abi16))
		return -ENOMEM;

420
	if (init->handle == ~0)
421
		return nouveau_abi16_put(abi16, -EINVAL);
422
	client = abi16->device.object.client;
423

424 425 426 427
	chan = nouveau_abi16_chan(abi16, init->channel);
	if (!chan)
		return nouveau_abi16_put(abi16, -ENOENT);

428
	ret = nvif_object_sclass_get(&chan->chan->user, &sclass);
429 430 431 432 433 434
	if (ret < 0)
		return nouveau_abi16_put(abi16, ret);

	if ((init->class & 0x00ff) == 0x006e) {
		/* nvsw: compatibility with older 0x*6e class identifier */
		for (i = 0; !oclass && i < ret; i++) {
435
			switch (sclass[i].oclass) {
436 437 438 439
			case NVIF_IOCTL_NEW_V0_SW_NV04:
			case NVIF_IOCTL_NEW_V0_SW_NV10:
			case NVIF_IOCTL_NEW_V0_SW_NV50:
			case NVIF_IOCTL_NEW_V0_SW_GF100:
440
				oclass = sclass[i].oclass;
441 442 443 444 445 446 447 448 449
				break;
			default:
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b1) {
		/* msvld: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
450 451
			if ((sclass[i].oclass & 0x00ff) == 0x00b1) {
				oclass = sclass[i].oclass;
452 453 454 455 456 457 458
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */
		/* mspdec: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
459 460
			if ((sclass[i].oclass & 0x00ff) == 0x00b2) {
				oclass = sclass[i].oclass;
461 462 463 464 465 466 467
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b3) { /* msppp */
		/* msppp: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
468 469
			if ((sclass[i].oclass & 0x00ff) == 0x00b3) {
				oclass = sclass[i].oclass;
470 471 472 473 474 475 476
				break;
			}
		}
	} else {
		oclass = init->class;
	}

477
	nvif_object_sclass_put(&sclass);
478 479 480
	if (!oclass)
		return nouveau_abi16_put(abi16, -EINVAL);

481 482 483 484 485 486 487
	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
	if (!ntfy)
		return nouveau_abi16_put(abi16, -ENOMEM);

	list_add(&ntfy->head, &chan->notifiers);

	client->route = NVDRM_OBJECT_ABI16;
488
	ret = nvif_object_init(&chan->chan->user, init->handle, oclass,
489 490 491 492 493
			       NULL, 0, &ntfy->object);
	client->route = NVDRM_OBJECT_NVIF;

	if (ret)
		nouveau_abi16_ntfy_fini(chan, ntfy);
494
	return nouveau_abi16_put(abi16, ret);
495 496 497 498 499
}

int
nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
{
500 501
	struct drm_nouveau_notifierobj_alloc *info = data;
	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 509
	int ret;

510 511 512
	if (unlikely(!abi16))
		return -ENOMEM;

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 520 521 522 523 524 525 526 527
	if (!chan)
		return nouveau_abi16_put(abi16, -ENOENT);

	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
	if (!ntfy)
		return nouveau_abi16_put(abi16, -ENOMEM);

	list_add(&ntfy->head, &chan->notifiers);

528 529
	ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1,
			   &ntfy->node);
530 531 532
	if (ret)
		goto done;

533 534
	args.start = ntfy->node->offset;
	args.limit = ntfy->node->offset + ntfy->node->length - 1;
535
	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
536 537 538 539
		args.target = NV_DMA_V0_TARGET_VM;
		args.access = NV_DMA_V0_ACCESS_VM;
		args.start += chan->ntfy_vma.offset;
		args.limit += chan->ntfy_vma.offset;
540
	} else
541
	if (drm->agp.bridge) {
542 543 544 545
		args.target = NV_DMA_V0_TARGET_AGP;
		args.access = NV_DMA_V0_ACCESS_RDWR;
		args.start += drm->agp.base + chan->ntfy->bo.offset;
		args.limit += drm->agp.base + chan->ntfy->bo.offset;
546
	} else {
547 548 549 550
		args.target = NV_DMA_V0_TARGET_VM;
		args.access = NV_DMA_V0_ACCESS_RDWR;
		args.start += chan->ntfy->bo.offset;
		args.limit += chan->ntfy->bo.offset;
551 552
	}

553 554 555 556 557
	client->route = NVDRM_OBJECT_ABI16;
	client->super = true;
	ret = nvif_object_init(&chan->chan->user, info->handle,
			       NV_DMA_IN_MEMORY, &args, sizeof(args),
			       &ntfy->object);
558
	client->super = false;
559
	client->route = NVDRM_OBJECT_NVIF;
560 561 562
	if (ret)
		goto done;

563
	info->offset = ntfy->node->offset;
564 565 566 567
done:
	if (ret)
		nouveau_abi16_ntfy_fini(chan, ntfy);
	return nouveau_abi16_put(abi16, ret);
568 569 570 571 572
}

int
nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
{
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 580 581
	if (unlikely(!abi16))
		return -ENOMEM;

582
	chan = nouveau_abi16_chan(abi16, fini->channel);
583
	if (!chan)
584
		return nouveau_abi16_put(abi16, -EINVAL);
585

586 587
	/* synchronize with the user channel and destroy the gpu object */
	nouveau_channel_idle(chan->chan);
588

589
	list_for_each_entry(ntfy, &chan->notifiers, head) {
590 591 592
		if (ntfy->object.handle == fini->handle) {
			nouveau_abi16_ntfy_fini(chan, ntfy);
			ret = 0;
593 594 595 596
			break;
		}
	}

597
	return nouveau_abi16_put(abi16, ret);
598
}