Set memory attributes on page
[pscnv.git] / pscnv / pscnv_ioctl.c
bloba4bc349044c00eea9db7da9ad3fe7d72ab257229
1 #include "drm.h"
2 #include "nouveau_drv.h"
3 #include "nouveau_reg.h"
4 #include "pscnv_ioctl.h"
5 #include "pscnv_vm.h"
6 #include "pscnv_chan.h"
7 #include "pscnv_fifo.h"
8 #include "pscnv_gem.h"
9 #include "nv50_chan.h"
10 #include "nvc0_graph.h"
11 #include "pscnv_kapi.h"
13 #include "nvc0_pgraph.xml.h"
15 #ifdef PSCNV_KAPI_GETPARAM_BUS_TYPE
16 #define DEVICE_IS_AGP(dev) drm_device_is_agp(dev)
17 #define DEVICE_IS_PCIE(dev) drm_device_is_pcie(dev)
18 #else
19 #define DEVICE_IS_AGP(dev) drm_pci_device_is_agp(dev)
20 #define DEVICE_IS_PCIE(dev) pci_is_pcie(dev->pdev)
21 #endif
23 int pscnv_ioctl_getparam(struct drm_device *dev, void *data,
24 struct drm_file *file_priv)
26 struct drm_nouveau_private *dev_priv = dev->dev_private;
27 struct drm_pscnv_getparam *getparam = data;
28 struct nvc0_graph_engine *nvc0_graph;
30 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
32 switch ((u32)getparam->param) {
33 case PSCNV_GETPARAM_MP_COUNT:
34 if (dev_priv->chipset < 0xc0)
35 goto fail;
36 nvc0_graph = NVC0_GRAPH(dev_priv->engines[PSCNV_ENGINE_GRAPH]);
37 getparam->value = nvc0_graph->tp_count; /* MPs == TPs */
38 break;
39 case PSCNV_GETPARAM_CHIPSET_ID:
40 getparam->value = dev_priv->chipset;
41 break;
42 case PSCNV_GETPARAM_PCI_VENDOR:
43 getparam->value = dev->pci_vendor;
44 break;
45 case PSCNV_GETPARAM_PCI_DEVICE:
46 getparam->value = dev->pci_device;
47 break;
48 case PSCNV_GETPARAM_BUS_TYPE:
49 if (DEVICE_IS_AGP(dev))
50 getparam->value = NV_AGP;
51 else if (DEVICE_IS_PCIE(dev))
52 getparam->value = NV_PCIE;
53 else
54 getparam->value = NV_PCI;
55 break;
56 case PSCNV_GETPARAM_PTIMER_TIME:
57 getparam->value = nv04_timer_read(dev);
58 break;
59 case PSCNV_GETPARAM_FB_SIZE:
60 getparam->value = dev_priv->vram_size;
61 break;
62 case PSCNV_GETPARAM_GPC_COUNT:
63 if (dev_priv->card_type < NV_C0)
64 goto fail;
65 getparam->value = nv_rd32(dev, NVC0_PGRAPH_CTXCTL_UNITS);
66 getparam->value &= NVC0_PGRAPH_CTXCTL_UNITS_GPC_COUNT__MASK;
67 break;
68 case PSCNV_GETPARAM_TP_COUNT_IDX: {
69 u32 i, units;
70 if (dev_priv->card_type < NV_C0)
71 goto fail;
72 units = nv_rd32(dev, NVC0_PGRAPH_CTXCTL_UNITS);
73 units &= NVC0_PGRAPH_CTXCTL_UNITS_GPC_COUNT__MASK;
74 i = getparam->param >> 32ULL;
75 if (i >= units)
76 goto fail;
77 getparam->value = nv_rd32(dev, NVC0_PGRAPH_GPC_CTXCTL_UNITS(i));
78 getparam->value &= NVC0_PGRAPH_GPC_CTXCTL_UNITS_TP_COUNT__MASK;
79 break;
81 case PSCNV_GETPARAM_BAR0_ADDR:
82 getparam->value = drm_get_resource_start(dev, 0);
83 break;
84 case PSCNV_GETPARAM_GRAPH_UNITS:
85 /* NV40 and NV50 versions are quite different, but register
86 * address is the same. User is supposed to know the card
87 * family anyway... */
88 if (dev_priv->card_type >= NV_40 && dev_priv->card_type < NV_C0) {
89 getparam->value = nv_rd32(dev, NV40_PMC_GRAPH_UNITS);
90 break;
92 /* FALLTHRU */
93 default:
94 goto fail;
97 return 0;
98 fail:
99 NV_ERROR(dev, "unknown parameter %lld\n", getparam->param);
100 return -EINVAL;
103 int pscnv_ioctl_gem_new(struct drm_device *dev, void *data,
104 struct drm_file *file_priv)
106 struct drm_pscnv_gem_info *info = data;
107 struct drm_gem_object *obj;
108 struct pscnv_bo *bo;
109 int ret;
111 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
113 obj = pscnv_gem_new(dev, info->size, info->flags, info->tile_flags, info->cookie, info->user);
114 if (!obj) {
115 return -ENOMEM;
117 bo = obj->driver_private;
119 /* could change due to page size align */
120 info->size = bo->size;
121 info->handle = 0; /* FreeBSD expects this to be 0 else allocation fails */
122 ret = drm_gem_handle_create(file_priv, obj, &info->handle);
124 if (pscnv_gem_debug >= 1)
125 NV_INFO(dev, "GEM handle %x is VO %x/%d\n", info->handle, bo->cookie, bo->serial);
127 #ifdef __linux__
128 info->map_handle = (uint64_t)info->handle << 32;
129 #else
130 info->map_handle = DRM_GEM_MAPPING_OFF(obj->map_list.key) |
131 DRM_GEM_MAPPING_KEY;
132 #endif
133 drm_gem_object_handle_unreference_unlocked (obj);
135 return ret;
138 int pscnv_ioctl_gem_info(struct drm_device *dev, void *data,
139 struct drm_file *file_priv)
141 struct drm_pscnv_gem_info *info = data;
142 struct drm_gem_object *obj;
143 struct pscnv_bo *bo;
144 int i;
146 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
148 obj = drm_gem_object_lookup(dev, file_priv, info->handle);
149 if (!obj)
150 return -EBADF;
152 bo = obj->driver_private;
154 info->cookie = bo->cookie;
155 info->flags = bo->flags;
156 info->tile_flags = bo->tile_flags;
157 info->size = obj->size;
158 #ifdef __linux__
159 info->map_handle = (uint64_t)info->handle | 32;
160 #else
161 info->map_handle = DRM_GEM_MAPPING_OFF(obj->map_list.key) |
162 DRM_GEM_MAPPING_KEY;
163 #endif
164 for (i = 0; i < DRM_ARRAY_SIZE(bo->user); i++)
165 info->user[i] = bo->user[i];
167 drm_gem_object_unreference_unlocked(obj);
169 return 0;
172 static struct pscnv_vspace *
173 pscnv_get_vspace(struct drm_device *dev, struct drm_file *file_priv, int vid)
175 struct drm_nouveau_private *dev_priv = dev->dev_private;
176 unsigned long flags;
177 spin_lock_irqsave(&dev_priv->vm->vs_lock, flags);
179 if (vid < 128 && vid >= 0 && dev_priv->vm->vspaces[vid] && dev_priv->vm->vspaces[vid]->filp == file_priv) {
180 struct pscnv_vspace *res = dev_priv->vm->vspaces[vid];
181 pscnv_vspace_ref(res);
182 spin_unlock_irqrestore(&dev_priv->vm->vs_lock, flags);
183 return res;
185 spin_unlock_irqrestore(&dev_priv->vm->vs_lock, flags);
186 return 0;
189 int pscnv_ioctl_vspace_new(struct drm_device *dev, void *data,
190 struct drm_file *file_priv)
192 struct drm_pscnv_vspace_req *req = data;
193 struct pscnv_vspace *vs;
195 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
197 vs = pscnv_vspace_new(dev, 1ull << 40, 0, 0);
198 if (!vs)
199 return -ENOMEM;
201 req->vid = vs->vid;
203 vs->filp = file_priv;
205 return 0;
208 int pscnv_ioctl_vspace_free(struct drm_device *dev, void *data,
209 struct drm_file *file_priv)
211 struct drm_pscnv_vspace_req *req = data;
212 int vid = req->vid;
213 struct pscnv_vspace *vs;
215 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
217 vs = pscnv_get_vspace(dev, file_priv, vid);
218 if (!vs)
219 return -ENOENT;
221 vs->filp = 0;
222 pscnv_vspace_unref(vs);
223 pscnv_vspace_unref(vs);
225 return 0;
228 int pscnv_ioctl_vspace_map(struct drm_device *dev, void *data,
229 struct drm_file *file_priv)
231 struct drm_pscnv_vspace_map *req = data;
232 struct pscnv_vspace *vs;
233 struct drm_gem_object *obj;
234 struct pscnv_bo *bo;
235 struct pscnv_mm_node *map;
236 int ret;
238 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
240 vs = pscnv_get_vspace(dev, file_priv, req->vid);
241 if (!vs)
242 return -ENOENT;
244 obj = drm_gem_object_lookup(dev, file_priv, req->handle);
245 if (!obj) {
246 pscnv_vspace_unref(vs);
247 return -EBADF;
250 bo = obj->driver_private;
252 ret = pscnv_vspace_map(vs, bo, req->start, req->end, req->back, &map);
253 if (!ret)
254 req->offset = map->start;
256 pscnv_vspace_unref(vs);
258 return ret;
261 int pscnv_ioctl_vspace_unmap(struct drm_device *dev, void *data,
262 struct drm_file *file_priv)
264 struct drm_pscnv_vspace_unmap *req = data;
265 struct pscnv_vspace *vs;
266 int ret;
268 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
271 vs = pscnv_get_vspace(dev, file_priv, req->vid);
272 if (!vs)
273 return -ENOENT;
275 ret = pscnv_vspace_unmap(vs, req->offset);
277 pscnv_vspace_unref(vs);
279 return ret;
282 void pscnv_vspace_cleanup(struct drm_device *dev, struct drm_file *file_priv) {
283 int vid;
284 struct pscnv_vspace *vs;
286 for (vid = 0; vid < 128; vid++) {
287 vs = pscnv_get_vspace(dev, file_priv, vid);
288 if (!vs)
289 continue;
290 vs->filp = 0;
291 pscnv_vspace_unref(vs);
292 pscnv_vspace_unref(vs);
296 struct pscnv_chan *
297 pscnv_get_chan(struct drm_device *dev, struct drm_file *file_priv, int cid)
299 struct drm_nouveau_private *dev_priv = dev->dev_private;
300 unsigned long flags;
301 spin_lock_irqsave(&dev_priv->chan->ch_lock, flags);
303 if (cid < 128 && cid >= 0 && dev_priv->chan->chans[cid] && dev_priv->chan->chans[cid]->filp == file_priv) {
304 struct pscnv_chan *res = dev_priv->chan->chans[cid];
305 pscnv_chan_ref(res);
306 spin_unlock_irqrestore(&dev_priv->chan->ch_lock, flags);
307 return res;
309 spin_unlock_irqrestore(&dev_priv->chan->ch_lock, flags);
310 return 0;
313 int pscnv_ioctl_chan_new(struct drm_device *dev, void *data,
314 struct drm_file *file_priv)
316 struct drm_pscnv_chan_new *req = data;
317 struct pscnv_vspace *vs;
318 struct pscnv_chan *ch;
319 #ifndef __linux__
320 struct drm_gem_object *obj;
321 #endif
322 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
324 vs = pscnv_get_vspace(dev, file_priv, req->vid);
325 if (!vs)
326 return -ENOENT;
328 ch = pscnv_chan_new(dev, vs, 0);
329 if (!ch) {
330 pscnv_vspace_unref(vs);
331 return -ENOMEM;
333 pscnv_vspace_unref(vs);
334 #ifndef __linux__
335 if (!(obj = pscnv_gem_wrap(dev, ch->bo))) {
336 pscnv_chan_unref(ch);
337 return -ENOMEM;
339 req->map_handle = DRM_GEM_MAPPING_OFF(obj->map_list.key) |
340 DRM_GEM_MAPPING_KEY;
341 #else
342 req->map_handle = 0xc0000000 | ch->cid << 16;
343 #endif
345 req->cid = ch->cid;
347 ch->filp = file_priv;
349 return 0;
352 int pscnv_ioctl_chan_free(struct drm_device *dev, void *data,
353 struct drm_file *file_priv)
355 struct drm_pscnv_chan_free *req = data;
356 struct pscnv_chan *ch;
358 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
360 ch = pscnv_get_chan(dev, file_priv, req->cid);
361 if (!ch)
362 return -ENOENT;
364 ch->filp = 0;
365 pscnv_chan_unref(ch);
366 pscnv_chan_unref(ch);
368 return 0;
371 int pscnv_ioctl_obj_vdma_new(struct drm_device *dev, void *data,
372 struct drm_file *file_priv) {
373 struct drm_pscnv_obj_vdma_new *req = data;
374 struct drm_nouveau_private *dev_priv = dev->dev_private;
375 struct pscnv_chan *ch;
376 int ret;
377 uint32_t oclass, inst;
379 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
381 if (dev_priv->card_type != NV_50)
382 return -ENOSYS;
384 oclass = req->oclass;
386 if (oclass != 2 && oclass != 3 && oclass != 0x3d)
387 return -EINVAL;
389 ch = pscnv_get_chan(dev, file_priv, req->cid);
390 if (!ch)
391 return -ENOENT;
393 inst = nv50_chan_dmaobj_new(ch, 0x7fc00000 | oclass, req->start, req->size);
394 if (!inst) {
395 pscnv_chan_unref(ch);
396 return -ENOMEM;
399 ret = pscnv_ramht_insert (&ch->ramht, req->handle, inst >> 4);
401 pscnv_chan_unref(ch);
403 return ret;
406 void pscnv_chan_cleanup(struct drm_device *dev, struct drm_file *file_priv) {
407 int cid;
408 struct pscnv_chan *ch;
410 for (cid = 0; cid < 128; cid++) {
411 ch = pscnv_get_chan(dev, file_priv, cid);
412 if (!ch)
413 continue;
414 ch->filp = 0;
415 pscnv_chan_unref(ch);
416 pscnv_chan_unref(ch);
420 int pscnv_ioctl_obj_eng_new(struct drm_device *dev, void *data,
421 struct drm_file *file_priv) {
422 struct drm_pscnv_obj_eng_new *req = data;
423 struct drm_nouveau_private *dev_priv = dev->dev_private;
424 struct pscnv_chan *ch;
425 int ret;
426 int i;
427 uint32_t oclass = req->oclass;
429 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
431 for (i = 0; i < PSCNV_ENGINES_NUM; i++)
432 if (dev_priv->engines[i]) {
433 uint32_t *pclass = dev_priv->engines[i]->oclasses;
434 if (!pclass)
435 continue;
436 while (*pclass) {
437 if (*pclass == oclass)
438 goto found;
439 pclass++;
442 return -ENODEV;
444 found:
445 ch = pscnv_get_chan(dev, file_priv, req->cid);
446 if (!ch)
447 return -ENOENT;
449 if (!ch->engdata[i]) {
450 ret = dev_priv->engines[i]->chan_alloc(dev_priv->engines[i], ch);
451 if (ret) {
452 pscnv_chan_unref(ch);
453 return ret;
457 ret = dev_priv->engines[i]->chan_obj_new(dev_priv->engines[i], ch, req->handle, oclass, req->flags);
459 pscnv_chan_unref(ch);
460 return ret;
463 int pscnv_ioctl_fifo_init(struct drm_device *dev, void *data,
464 struct drm_file *file_priv) {
465 struct drm_pscnv_fifo_init *req = data;
466 struct drm_nouveau_private *dev_priv = dev->dev_private;
467 struct pscnv_chan *ch;
468 int ret;
470 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
472 if (!dev_priv->fifo || !dev_priv->fifo->chan_init_dma)
473 return -ENODEV;
475 ch = pscnv_get_chan(dev, file_priv, req->cid);
476 if (!ch)
477 return -ENOENT;
479 ret = dev_priv->fifo->chan_init_dma(ch, req->pb_handle, req->flags, req->slimask, req->pb_start);
481 pscnv_chan_unref(ch);
483 return ret;
486 int pscnv_ioctl_fifo_init_ib(struct drm_device *dev, void *data,
487 struct drm_file *file_priv) {
488 struct drm_pscnv_fifo_init_ib *req = data;
489 struct drm_nouveau_private *dev_priv = dev->dev_private;
490 struct pscnv_chan *ch;
491 int ret;
493 NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
495 if (!dev_priv->fifo || !dev_priv->fifo->chan_init_ib)
496 return -ENODEV;
498 ch = pscnv_get_chan(dev, file_priv, req->cid);
499 if (!ch)
500 return -ENOENT;
502 ret = dev_priv->fifo->chan_init_ib(ch, req->pb_handle, req->flags, req->slimask, req->ib_start, req->ib_order);
504 pscnv_chan_unref(ch);
506 return ret;