Home | History | Annotate | Line # | Download | only in nouveau
nouveau_abi16.c revision 1.1.1.2
      1 /*	$NetBSD: nouveau_abi16.c,v 1.1.1.2 2014/08/06 12:36:23 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.1.1.2 2014/08/06 12:36:23 riastradh Exp $");
     28 
     29 #include <core/object.h>
     30 #include <core/client.h>
     31 #include <core/device.h>
     32 #include <core/class.h>
     33 #include <core/mm.h>
     34 
     35 #include <subdev/fb.h>
     36 #include <subdev/timer.h>
     37 #include <subdev/instmem.h>
     38 #include <engine/graph.h>
     39 
     40 #include "nouveau_drm.h"
     41 #include "nouveau_dma.h"
     42 #include "nouveau_gem.h"
     43 #include "nouveau_chan.h"
     44 #include "nouveau_abi16.h"
     45 
     46 struct nouveau_abi16 *
     47 nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev)
     48 {
     49 	struct nouveau_cli *cli = nouveau_cli(file_priv);
     50 	mutex_lock(&cli->mutex);
     51 	if (!cli->abi16) {
     52 		struct nouveau_abi16 *abi16;
     53 		cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
     54 		if (cli->abi16) {
     55 			INIT_LIST_HEAD(&abi16->channels);
     56 			abi16->client = nv_object(cli);
     57 
     58 			/* allocate device object targeting client's default
     59 			 * device (ie. the one that belongs to the fd it
     60 			 * opened)
     61 			 */
     62 			if (nouveau_object_new(abi16->client, NVDRM_CLIENT,
     63 					       NVDRM_DEVICE, 0x0080,
     64 					       &(struct nv_device_class) {
     65 						.device = ~0ULL,
     66 					       },
     67 					       sizeof(struct nv_device_class),
     68 					       &abi16->device) == 0)
     69 				return cli->abi16;
     70 
     71 			kfree(cli->abi16);
     72 			cli->abi16 = NULL;
     73 		}
     74 
     75 		mutex_unlock(&cli->mutex);
     76 	}
     77 	return cli->abi16;
     78 }
     79 
     80 int
     81 nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
     82 {
     83 	struct nouveau_cli *cli = (void *)abi16->client;
     84 	mutex_unlock(&cli->mutex);
     85 	return ret;
     86 }
     87 
     88 u16
     89 nouveau_abi16_swclass(struct nouveau_drm *drm)
     90 {
     91 	switch (nv_device(drm->device)->card_type) {
     92 	case NV_04:
     93 		return 0x006e;
     94 	case NV_10:
     95 	case NV_11:
     96 	case NV_20:
     97 	case NV_30:
     98 	case NV_40:
     99 		return 0x016e;
    100 	case NV_50:
    101 		return 0x506e;
    102 	case NV_C0:
    103 	case NV_D0:
    104 	case NV_E0:
    105 	case GM100:
    106 		return 0x906e;
    107 	}
    108 
    109 	return 0x0000;
    110 }
    111 
    112 static void
    113 nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
    114 			struct nouveau_abi16_ntfy *ntfy)
    115 {
    116 	nouveau_mm_free(&chan->heap, &ntfy->node);
    117 	list_del(&ntfy->head);
    118 	kfree(ntfy);
    119 }
    120 
    121 static void
    122 nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
    123 			struct nouveau_abi16_chan *chan)
    124 {
    125 	struct nouveau_abi16_ntfy *ntfy, *temp;
    126 
    127 	/* wait for all activity to stop before releasing notify object, which
    128 	 * may be still in use */
    129 	if (chan->chan && chan->ntfy)
    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_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
    139 		nouveau_bo_unpin(chan->ntfy);
    140 		drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
    141 	}
    142 
    143 	if (chan->heap.block_size)
    144 		nouveau_mm_fini(&chan->heap);
    145 
    146 	/* destroy channel object, all children will be killed too */
    147 	if (chan->chan) {
    148 		abi16->handles &= ~(1ULL << (chan->chan->handle & 0xffff));
    149 		nouveau_channel_del(&chan->chan);
    150 	}
    151 
    152 	list_del(&chan->head);
    153 	kfree(chan);
    154 }
    155 
    156 void
    157 nouveau_abi16_fini(struct nouveau_abi16 *abi16)
    158 {
    159 	struct nouveau_cli *cli = (void *)abi16->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 	nouveau_object_del(abi16->client, NVDRM_CLIENT, NVDRM_DEVICE);
    169 
    170 	kfree(cli->abi16);
    171 	cli->abi16 = NULL;
    172 }
    173 
    174 int
    175 nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
    176 {
    177 	struct nouveau_drm *drm = nouveau_drm(dev);
    178 	struct nouveau_device *device = nv_device(drm->device);
    179 	struct nouveau_timer *ptimer = nouveau_timer(device);
    180 	struct nouveau_graph *graph = (void *)nouveau_engine(device, NVDEV_ENGINE_GR);
    181 	struct drm_nouveau_getparam *getparam = data;
    182 
    183 	switch (getparam->param) {
    184 	case NOUVEAU_GETPARAM_CHIPSET_ID:
    185 		getparam->value = device->chipset;
    186 		break;
    187 	case NOUVEAU_GETPARAM_PCI_VENDOR:
    188 		if (nv_device_is_pci(device))
    189 			getparam->value = dev->pdev->vendor;
    190 		else
    191 			getparam->value = 0;
    192 		break;
    193 	case NOUVEAU_GETPARAM_PCI_DEVICE:
    194 		if (nv_device_is_pci(device))
    195 			getparam->value = dev->pdev->device;
    196 		else
    197 			getparam->value = 0;
    198 		break;
    199 	case NOUVEAU_GETPARAM_BUS_TYPE:
    200 		if (!nv_device_is_pci(device))
    201 			getparam->value = 3;
    202 		else
    203 		if (drm_pci_device_is_agp(dev))
    204 			getparam->value = 0;
    205 		else
    206 		if (!pci_is_pcie(dev->pdev))
    207 			getparam->value = 1;
    208 		else
    209 			getparam->value = 2;
    210 		break;
    211 	case NOUVEAU_GETPARAM_FB_SIZE:
    212 		getparam->value = drm->gem.vram_available;
    213 		break;
    214 	case NOUVEAU_GETPARAM_AGP_SIZE:
    215 		getparam->value = drm->gem.gart_available;
    216 		break;
    217 	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
    218 		getparam->value = 0; /* deprecated */
    219 		break;
    220 	case NOUVEAU_GETPARAM_PTIMER_TIME:
    221 		getparam->value = ptimer->read(ptimer);
    222 		break;
    223 	case NOUVEAU_GETPARAM_HAS_BO_USAGE:
    224 		getparam->value = 1;
    225 		break;
    226 	case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
    227 		getparam->value = 1;
    228 		break;
    229 	case NOUVEAU_GETPARAM_GRAPH_UNITS:
    230 		getparam->value = graph->units ? graph->units(graph) : 0;
    231 		break;
    232 	default:
    233 		nv_debug(device, "unknown parameter %lld\n", getparam->param);
    234 		return -EINVAL;
    235 	}
    236 
    237 	return 0;
    238 }
    239 
    240 int
    241 nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
    242 {
    243 	return -EINVAL;
    244 }
    245 
    246 int
    247 nouveau_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, dev);
    253 	struct nouveau_abi16_chan *chan;
    254 	struct nouveau_client *client;
    255 	struct nouveau_device *device;
    256 	struct nouveau_instmem *imem;
    257 	struct nouveau_fb *pfb;
    258 	int ret;
    259 
    260 	if (unlikely(!abi16))
    261 		return -ENOMEM;
    262 
    263 	if (!drm->channel)
    264 		return nouveau_abi16_put(abi16, -ENODEV);
    265 
    266 	client = nv_client(abi16->client);
    267 	device = nv_device(abi16->device);
    268 	imem   = nouveau_instmem(device);
    269 	pfb    = nouveau_fb(device);
    270 
    271 	/* hack to allow channel engine type specification on kepler */
    272 	if (device->card_type >= NV_E0) {
    273 		if (init->fb_ctxdma_handle != ~0)
    274 			init->fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR;
    275 		else
    276 			init->fb_ctxdma_handle = init->tt_ctxdma_handle;
    277 
    278 		/* allow flips to be executed if this is a graphics channel */
    279 		init->tt_ctxdma_handle = 0;
    280 		if (init->fb_ctxdma_handle == NVE0_CHANNEL_IND_ENGINE_GR)
    281 			init->tt_ctxdma_handle = 1;
    282 	}
    283 
    284 	if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
    285 		return nouveau_abi16_put(abi16, -EINVAL);
    286 
    287 	/* allocate "abi16 channel" data and make up a handle for it */
    288 	init->channel = __ffs64(~abi16->handles);
    289 	if (~abi16->handles == 0)
    290 		return nouveau_abi16_put(abi16, -ENOSPC);
    291 
    292 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
    293 	if (!chan)
    294 		return nouveau_abi16_put(abi16, -ENOMEM);
    295 
    296 	INIT_LIST_HEAD(&chan->notifiers);
    297 	list_add(&chan->head, &abi16->channels);
    298 	abi16->handles |= (1ULL << init->channel);
    299 
    300 	/* create channel object and initialise dma and fence management */
    301 	ret = nouveau_channel_new(drm, cli, NVDRM_DEVICE, NVDRM_CHAN |
    302 				  init->channel, init->fb_ctxdma_handle,
    303 				  init->tt_ctxdma_handle, &chan->chan);
    304 	if (ret)
    305 		goto done;
    306 
    307 	if (device->card_type >= NV_50)
    308 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
    309 					NOUVEAU_GEM_DOMAIN_GART;
    310 	else
    311 	if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
    312 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
    313 	else
    314 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
    315 
    316 	if (device->card_type < NV_10) {
    317 		init->subchan[0].handle = 0x00000000;
    318 		init->subchan[0].grclass = 0x0000;
    319 		init->subchan[1].handle = NvSw;
    320 		init->subchan[1].grclass = 0x506e;
    321 		init->nr_subchan = 2;
    322 	}
    323 
    324 	/* Named memory object area */
    325 	ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
    326 			      0, 0, &chan->ntfy);
    327 	if (ret == 0)
    328 		ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT);
    329 	if (ret)
    330 		goto done;
    331 
    332 	if (device->card_type >= NV_50) {
    333 		ret = nouveau_bo_vma_add(chan->ntfy, client->vm,
    334 					&chan->ntfy_vma);
    335 		if (ret)
    336 			goto done;
    337 	}
    338 
    339 	ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
    340 				    &init->notifier_handle);
    341 	if (ret)
    342 		goto done;
    343 
    344 	ret = nouveau_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
    345 done:
    346 	if (ret)
    347 		nouveau_abi16_chan_fini(abi16, chan);
    348 	return nouveau_abi16_put(abi16, ret);
    349 }
    350 
    351 
    352 int
    353 nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
    354 {
    355 	struct drm_nouveau_channel_free *req = data;
    356 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
    357 	struct nouveau_abi16_chan *chan;
    358 	int ret = -ENOENT;
    359 
    360 	if (unlikely(!abi16))
    361 		return -ENOMEM;
    362 
    363 	list_for_each_entry(chan, &abi16->channels, head) {
    364 		if (chan->chan->handle == (NVDRM_CHAN | req->channel)) {
    365 			nouveau_abi16_chan_fini(abi16, chan);
    366 			return nouveau_abi16_put(abi16, 0);
    367 		}
    368 	}
    369 
    370 	return nouveau_abi16_put(abi16, ret);
    371 }
    372 
    373 int
    374 nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
    375 {
    376 	struct drm_nouveau_grobj_alloc *init = data;
    377 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
    378 	struct nouveau_drm *drm = nouveau_drm(dev);
    379 	struct nouveau_object *object;
    380 	int ret;
    381 
    382 	if (unlikely(!abi16))
    383 		return -ENOMEM;
    384 
    385 	if (init->handle == ~0)
    386 		return nouveau_abi16_put(abi16, -EINVAL);
    387 
    388 	/* compatibility with userspace that assumes 506e for all chipsets */
    389 	if (init->class == 0x506e) {
    390 		init->class = nouveau_abi16_swclass(drm);
    391 		if (init->class == 0x906e)
    392 			return nouveau_abi16_put(abi16, 0);
    393 	}
    394 
    395 	ret = nouveau_object_new(abi16->client, NVDRM_CHAN | init->channel,
    396 				  init->handle, init->class, NULL, 0, &object);
    397 	return nouveau_abi16_put(abi16, ret);
    398 }
    399 
    400 int
    401 nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
    402 {
    403 	struct drm_nouveau_notifierobj_alloc *info = data;
    404 	struct nouveau_drm *drm = nouveau_drm(dev);
    405 	struct nouveau_device *device = nv_device(drm->device);
    406 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
    407 	struct nouveau_abi16_chan *chan = NULL, *temp;
    408 	struct nouveau_abi16_ntfy *ntfy;
    409 	struct nouveau_object *object;
    410 	struct nv_dma_class args = {};
    411 	int ret;
    412 
    413 	if (unlikely(!abi16))
    414 		return -ENOMEM;
    415 
    416 	/* completely unnecessary for these chipsets... */
    417 	if (unlikely(nv_device(abi16->device)->card_type >= NV_C0))
    418 		return nouveau_abi16_put(abi16, -EINVAL);
    419 
    420 	list_for_each_entry(temp, &abi16->channels, head) {
    421 		if (temp->chan->handle == (NVDRM_CHAN | info->channel)) {
    422 			chan = temp;
    423 			break;
    424 		}
    425 	}
    426 
    427 	if (!chan)
    428 		return nouveau_abi16_put(abi16, -ENOENT);
    429 
    430 	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
    431 	if (!ntfy)
    432 		return nouveau_abi16_put(abi16, -ENOMEM);
    433 
    434 	list_add(&ntfy->head, &chan->notifiers);
    435 	ntfy->handle = info->handle;
    436 
    437 	ret = nouveau_mm_head(&chan->heap, 1, info->size, info->size, 1,
    438 			      &ntfy->node);
    439 	if (ret)
    440 		goto done;
    441 
    442 	args.start = ntfy->node->offset;
    443 	args.limit = ntfy->node->offset + ntfy->node->length - 1;
    444 	if (device->card_type >= NV_50) {
    445 		args.flags  = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
    446 		args.start += chan->ntfy_vma.offset;
    447 		args.limit += chan->ntfy_vma.offset;
    448 	} else
    449 	if (drm->agp.stat == ENABLED) {
    450 		args.flags  = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR;
    451 		args.start += drm->agp.base + chan->ntfy->bo.offset;
    452 		args.limit += drm->agp.base + chan->ntfy->bo.offset;
    453 	} else {
    454 		args.flags  = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR;
    455 		args.start += chan->ntfy->bo.offset;
    456 		args.limit += chan->ntfy->bo.offset;
    457 	}
    458 
    459 	ret = nouveau_object_new(abi16->client, chan->chan->handle,
    460 				 ntfy->handle, 0x003d, &args,
    461 				 sizeof(args), &object);
    462 	if (ret)
    463 		goto done;
    464 
    465 	info->offset = ntfy->node->offset;
    466 
    467 done:
    468 	if (ret)
    469 		nouveau_abi16_ntfy_fini(chan, ntfy);
    470 	return nouveau_abi16_put(abi16, ret);
    471 }
    472 
    473 int
    474 nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
    475 {
    476 	struct drm_nouveau_gpuobj_free *fini = data;
    477 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
    478 	struct nouveau_abi16_chan *chan = NULL, *temp;
    479 	struct nouveau_abi16_ntfy *ntfy;
    480 	int ret;
    481 
    482 	if (unlikely(!abi16))
    483 		return -ENOMEM;
    484 
    485 	list_for_each_entry(temp, &abi16->channels, head) {
    486 		if (temp->chan->handle == (NVDRM_CHAN | fini->channel)) {
    487 			chan = temp;
    488 			break;
    489 		}
    490 	}
    491 
    492 	if (!chan)
    493 		return nouveau_abi16_put(abi16, -ENOENT);
    494 
    495 	/* synchronize with the user channel and destroy the gpu object */
    496 	nouveau_channel_idle(chan->chan);
    497 
    498 	ret = nouveau_object_del(abi16->client, chan->chan->handle, fini->handle);
    499 	if (ret)
    500 		return nouveau_abi16_put(abi16, ret);
    501 
    502 	/* cleanup extra state if this object was a notifier */
    503 	list_for_each_entry(ntfy, &chan->notifiers, head) {
    504 		if (ntfy->handle == fini->handle) {
    505 			nouveau_mm_free(&chan->heap, &ntfy->node);
    506 			list_del(&ntfy->head);
    507 			break;
    508 		}
    509 	}
    510 
    511 	return nouveau_abi16_put(abi16, 0);
    512 }
    513