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/cla06f.h>
29
#include <nvif/unpack.h>
30

31
#include "nouveau_drm.h"
32
#include "nouveau_dma.h"
33 34
#include "nouveau_gem.h"
#include "nouveau_chan.h"
35
#include "nouveau_abi16.h"
36

37 38
static struct nouveau_abi16 *
nouveau_abi16(struct drm_file *file_priv)
39 40 41 42 43 44
{
	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) {
45 46 47 48
			struct nv_device_v0 args = {
				.device = ~0ULL,
			};

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

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

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

67 68 69 70 71 72 73 74 75 76 77
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;
}

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

86
s32
87 88
nouveau_abi16_swclass(struct nouveau_drm *drm)
{
89 90
	switch (drm->device.info.family) {
	case NV_DEVICE_INFO_V0_TNT:
91
		return NVIF_CLASS_SW_NV04;
92 93 94 95
	case NV_DEVICE_INFO_V0_CELSIUS:
	case NV_DEVICE_INFO_V0_KELVIN:
	case NV_DEVICE_INFO_V0_RANKINE:
	case NV_DEVICE_INFO_V0_CURIE:
96
		return NVIF_CLASS_SW_NV10;
97
	case NV_DEVICE_INFO_V0_TESLA:
98
		return NVIF_CLASS_SW_NV50;
99 100 101
	case NV_DEVICE_INFO_V0_FERMI:
	case NV_DEVICE_INFO_V0_KEPLER:
	case NV_DEVICE_INFO_V0_MAXWELL:
102
		return NVIF_CLASS_SW_GF100;
103 104 105 106 107 108 109 110 111
	}

	return 0x0000;
}

static void
nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
			struct nouveau_abi16_ntfy *ntfy)
{
112
	nvif_object_fini(&ntfy->object);
113
	nvkm_mm_free(&chan->heap, &ntfy->node);
114 115 116 117 118 119 120 121 122 123
	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;

124 125 126 127 128
	/* 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);

129 130 131 132 133 134 135
	/* 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);
136
		nouveau_bo_unpin(chan->ntfy);
137
		drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
138 139 140
	}

	if (chan->heap.block_size)
141
		nvkm_mm_fini(&chan->heap);
142 143 144

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

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

void
nouveau_abi16_fini(struct nouveau_abi16 *abi16)
{
156
	struct nouveau_cli *cli = (void *)abi16->device.object.client;
157 158 159 160 161 162 163 164
	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 */
165
	nvif_device_fini(&abi16->device);
166 167 168 169

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

int
nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
{
174
	struct nouveau_cli *cli = nouveau_cli(file_priv);
175
	struct nouveau_drm *drm = nouveau_drm(dev);
176
	struct nvif_device *device = &drm->device;
177
	struct nvkm_gr *gr = nvxx_gr(device);
178 179 180 181
	struct drm_nouveau_getparam *getparam = data;

	switch (getparam->param) {
	case NOUVEAU_GETPARAM_CHIPSET_ID:
182
		getparam->value = device->info.chipset;
183 184
		break;
	case NOUVEAU_GETPARAM_PCI_VENDOR:
185
		if (nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
186 187 188
			getparam->value = dev->pdev->vendor;
		else
			getparam->value = 0;
189 190
		break;
	case NOUVEAU_GETPARAM_PCI_DEVICE:
191
		if (nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
192 193 194
			getparam->value = dev->pdev->device;
		else
			getparam->value = 0;
195 196
		break;
	case NOUVEAU_GETPARAM_BUS_TYPE:
197
		if (!nvxx_device(device)->func->pci)
A
Alexandre Courbot 已提交
198 199
			getparam->value = 3;
		else
200 201 202 203 204 205 206 207 208
		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:
209
		getparam->value = drm->gem.vram_available;
210 211
		break;
	case NOUVEAU_GETPARAM_AGP_SIZE:
212
		getparam->value = drm->gem.gart_available;
213 214 215 216 217
		break;
	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
		getparam->value = 0; /* deprecated */
		break;
	case NOUVEAU_GETPARAM_PTIMER_TIME:
218
		getparam->value = nvif_device_time(device);
219 220 221 222 223 224 225 226
		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:
227
		getparam->value = nvkm_gr_units(gr);
228
		break;
229
	default:
B
Ben Skeggs 已提交
230
		NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param);
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
		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;
247 248
	struct nouveau_cli *cli = nouveau_cli(file_priv);
	struct nouveau_drm *drm = nouveau_drm(dev);
249
	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
250
	struct nouveau_abi16_chan *chan;
251
	struct nvif_device *device;
252 253
	int ret;

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

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

260
	device = &abi16->device;
261

262
	/* hack to allow channel engine type specification on kepler */
263
	if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
264
		if (init->fb_ctxdma_handle != ~0)
265
			init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
266 267 268 269 270
		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;
271
		if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
272 273 274 275 276 277
			init->tt_ctxdma_handle = 1;
	}

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

278 279 280 281 282 283 284
	/* 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);
285

286
	/* create channel object and initialise dma and fence management */
287
	ret = nouveau_channel_new(drm, device, init->fb_ctxdma_handle,
288
				  init->tt_ctxdma_handle, &chan->chan);
289
	if (ret)
290 291
		goto done;

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

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

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

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

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

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

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

338 339 340 341 342 343
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) {
344
		if (chan->chan->chid == channel)
345 346 347 348 349
			return chan;
	}

	return NULL;
}
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 388
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;
}

389 390 391 392
int
nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
{
	struct drm_nouveau_channel_free *req = data;
393
	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
394
	struct nouveau_abi16_chan *chan;
395

396 397
	if (unlikely(!abi16))
		return -ENOMEM;
398

399 400 401 402 403
	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);
404 405 406 407 408 409
}

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

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

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

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

429
	ret = nvif_object_sclass_get(&chan->chan->user, &sclass);
430 431 432 433 434 435
	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++) {
436
			switch (sclass[i].oclass) {
437 438 439 440
			case NVIF_CLASS_SW_NV04:
			case NVIF_CLASS_SW_NV10:
			case NVIF_CLASS_SW_NV50:
			case NVIF_CLASS_SW_GF100:
441
				oclass = sclass[i].oclass;
442 443 444 445 446 447 448 449 450
				break;
			default:
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b1) {
		/* msvld: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
451 452
			if ((sclass[i].oclass & 0x00ff) == 0x00b1) {
				oclass = sclass[i].oclass;
453 454 455 456 457 458 459
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */
		/* mspdec: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
460 461
			if ((sclass[i].oclass & 0x00ff) == 0x00b2) {
				oclass = sclass[i].oclass;
462 463 464 465 466 467 468
				break;
			}
		}
	} else
	if ((init->class & 0x00ff) == 0x00b3) { /* msppp */
		/* msppp: compatibility with incorrect version exposure */
		for (i = 0; i < ret; i++) {
469 470
			if ((sclass[i].oclass & 0x00ff) == 0x00b3) {
				oclass = sclass[i].oclass;
471 472 473 474 475 476 477
				break;
			}
		}
	} else {
		oclass = init->class;
	}

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

482 483 484 485 486 487 488
	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;
489
	ret = nvif_object_init(&chan->chan->user, init->handle, oclass,
490 491 492 493 494
			       NULL, 0, &ntfy->object);
	client->route = NVDRM_OBJECT_NVIF;

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

int
nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
{
501 502
	struct drm_nouveau_notifierobj_alloc *info = data;
	struct nouveau_drm *drm = nouveau_drm(dev);
503
	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
504
	struct nouveau_abi16_chan *chan;
505
	struct nouveau_abi16_ntfy *ntfy;
506
	struct nvif_device *device = &abi16->device;
507
	struct nvif_client *client;
508
	struct nv_dma_v0 args = {};
509 510
	int ret;

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

514
	/* completely unnecessary for these chipsets... */
515
	if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
516
		return nouveau_abi16_put(abi16, -EINVAL);
517
	client = abi16->device.object.client;
518

519
	chan = nouveau_abi16_chan(abi16, info->channel);
520 521 522 523 524 525 526 527 528
	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);

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

534 535
	args.start = ntfy->node->offset;
	args.limit = ntfy->node->offset + ntfy->node->length - 1;
536
	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
537 538 539 540
		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;
541
	} else
542
	if (drm->agp.bridge) {
543 544 545 546
		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;
547
	} else {
548 549 550 551
		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;
552 553
	}

554 555 556 557 558
	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);
559
	client->super = false;
560
	client->route = NVDRM_OBJECT_NVIF;
561 562 563
	if (ret)
		goto done;

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

int
nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
{
574
	struct drm_nouveau_gpuobj_free *fini = data;
575
	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
576
	struct nouveau_abi16_chan *chan;
577
	struct nouveau_abi16_ntfy *ntfy;
578
	int ret = -ENOENT;
579

580 581 582
	if (unlikely(!abi16))
		return -ENOMEM;

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

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

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

598
	return nouveau_abi16_put(abi16, ret);
599
}