summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drm/nouveau/nouveau_drm.c1
-rw-r--r--drm/nouveau/nouveau_gem.c127
-rw-r--r--drm/nouveau/nouveau_gem.h2
-rw-r--r--drm/nouveau/uapi/drm/nouveau_drm.h17
4 files changed, 146 insertions, 1 deletions
diff --git a/drm/nouveau/nouveau_drm.c b/drm/nouveau/nouveau_drm.c
index 50b59bdfd..7598577d0 100644
--- a/drm/nouveau/nouveau_drm.c
+++ b/drm/nouveau/nouveau_drm.c
@@ -878,6 +878,7 @@ nouveau_ioctls[] = {
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_SET_TILING, nouveau_gem_ioctl_set_tiling, DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF_2, nouveau_gem_ioctl_pushbuf_2, DRM_AUTH|DRM_RENDER_ALLOW),
};
long
diff --git a/drm/nouveau/nouveau_gem.c b/drm/nouveau/nouveau_gem.c
index 851f18355..5e45e1d98 100644
--- a/drm/nouveau/nouveau_gem.c
+++ b/drm/nouveau/nouveau_gem.c
@@ -605,7 +605,12 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
validate_fini(op, NULL, NULL);
return ret;
}
- *apply_relocs = ret;
+
+ if (apply_relocs)
+ *apply_relocs = ret;
+ else
+ BUG_ON(ret > 0);
+
return 0;
}
@@ -719,6 +724,126 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
}
int
+nouveau_gem_ioctl_pushbuf_2(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv);
+ struct nouveau_cli *cli = nouveau_cli(file_priv);
+ struct nouveau_abi16_chan *temp;
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct drm_nouveau_gem_pushbuf_2 *req = data;
+ struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
+ struct nouveau_channel *chan = NULL;
+ struct validate_op op;
+ struct nouveau_fence *fence = NULL;
+ uint32_t *push = NULL;
+ int i, ret = 0;
+
+ if (unlikely(!abi16))
+ return -ENOMEM;
+
+ list_for_each_entry(temp, &abi16->channels, head) {
+ if (temp->chan->chid == req->channel) {
+ chan = temp->chan;
+ break;
+ }
+ }
+
+ if (!chan)
+ return nouveau_abi16_put(abi16, -ENOENT);
+
+ if (!chan->dma.ib_max)
+ return nouveau_abi16_put(abi16, -ENODEV);
+
+ req->vram_available = drm->gem.vram_available;
+ req->gart_available = drm->gem.gart_available;
+
+ if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) {
+ NV_PRINTK(err, cli, "pushbuf push count exceeds limit: %d max %d\n",
+ req->nr_push, NOUVEAU_GEM_MAX_PUSH);
+ return nouveau_abi16_put(abi16, -EINVAL);
+ }
+
+ if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) {
+ NV_PRINTK(err, cli, "pushbuf bo count exceeds limit: %d max %d\n",
+ req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS);
+ return nouveau_abi16_put(abi16, -EINVAL);
+ }
+
+ if (req->nr_push) {
+ push = u_memcpya(req->push, req->nr_push, 8);
+ if (IS_ERR(push))
+ return nouveau_abi16_put(abi16, PTR_ERR(push));
+ }
+
+ if (req->nr_buffers) {
+ bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
+ if (IS_ERR(bo)) {
+ u_free(push);
+ return nouveau_abi16_put(abi16, PTR_ERR(bo));
+ }
+ }
+
+ /* Validate buffer list */
+ ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
+ req->nr_buffers, &op, NULL);
+ if (ret) {
+ if (ret != -ERESTARTSYS)
+ NV_PRINTK(err, cli, "validate: %d\n", ret);
+
+ goto out_prevalid;
+ }
+
+ if (req->flags & NOUVEAU_GEM_PUSHBUF_2_FENCE_WAIT) {
+ ret = nouveau_fence_sync_fd(req->fence, chan, true);
+ if (ret) {
+ NV_PRINTK(err, cli, "fence wait: %d\n", ret);
+ goto out;
+ }
+ }
+
+ ret = nouveau_dma_wait(chan, req->nr_push + 1, 16);
+ if (ret) {
+ NV_PRINTK(err, cli, "nv50cal_space: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < req->nr_push * 2; i += 2)
+ nv50_dma_push(chan, push[i], push[i + 1]);
+
+ ret = nouveau_fence_new(chan, false, &fence);
+ if (ret) {
+ NV_PRINTK(err, cli, "error fencing pushbuf: %d\n", ret);
+ WIND_RING(chan);
+ goto out;
+ }
+
+ if (req->flags & NOUVEAU_GEM_PUSHBUF_2_FENCE_EMIT) {
+ struct fence *f = fence_get(&fence->base);
+ ret = nouveau_fence_install(f, "nv-pushbuf", &req->fence);
+
+ if (ret) {
+ fence_put(f);
+ NV_PRINTK(err, cli, "fence install: %d\n", ret);
+ WIND_RING(chan);
+ goto out;
+ }
+ }
+
+out:
+ if (req->nr_buffers)
+ validate_fini(&op, fence, bo);
+
+ nouveau_fence_unref(&fence);
+
+out_prevalid:
+ u_free(bo);
+ u_free(push);
+
+ return nouveau_abi16_put(abi16, ret);
+}
+
+int
nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
diff --git a/drm/nouveau/nouveau_gem.h b/drm/nouveau/nouveau_gem.h
index 56e741d98..9e4323f34 100644
--- a/drm/nouveau/nouveau_gem.h
+++ b/drm/nouveau/nouveau_gem.h
@@ -29,6 +29,8 @@ extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
struct drm_file *);
extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
struct drm_file *);
+extern int nouveau_gem_ioctl_pushbuf_2(struct drm_device *, void *,
+ struct drm_file *);
extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *,
struct drm_file *);
extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
diff --git a/drm/nouveau/uapi/drm/nouveau_drm.h b/drm/nouveau/uapi/drm/nouveau_drm.h
index 6f35c2d98..e7df4831e 100644
--- a/drm/nouveau/uapi/drm/nouveau_drm.h
+++ b/drm/nouveau/uapi/drm/nouveau_drm.h
@@ -114,6 +114,21 @@ struct drm_nouveau_gem_pushbuf {
uint64_t gart_available;
};
+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_WAIT 0x00000001
+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_EMIT 0x00000002
+struct drm_nouveau_gem_pushbuf_2 {
+ uint32_t channel;
+ uint32_t flags;
+ uint32_t nr_push;
+ uint32_t nr_buffers;
+ int32_t fence; /* in/out, depends on flags */
+ uint32_t pad;
+ uint64_t push; /* in raw hw format */
+ uint64_t buffers; /* ptr to drm_nouveau_gem_pushbuf_bo */
+ uint64_t vram_available;
+ uint64_t gart_available;
+};
+
#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001
#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004
struct drm_nouveau_gem_cpu_prep {
@@ -139,6 +154,7 @@ struct drm_nouveau_gem_cpu_fini {
#define DRM_NOUVEAU_GEM_CPU_FINI 0x43
#define DRM_NOUVEAU_GEM_INFO 0x44
#define DRM_NOUVEAU_GEM_SET_TILING 0x50
+#define DRM_NOUVEAU_GEM_PUSHBUF_2 0x51
#define DRM_IOCTL_NOUVEAU_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_NEW, struct drm_nouveau_gem_new)
#define DRM_IOCTL_NOUVEAU_GEM_PUSHBUF DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_PUSHBUF, struct drm_nouveau_gem_pushbuf)
@@ -146,5 +162,6 @@ struct drm_nouveau_gem_cpu_fini {
#define DRM_IOCTL_NOUVEAU_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_CPU_FINI, struct drm_nouveau_gem_cpu_fini)
#define DRM_IOCTL_NOUVEAU_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_INFO, struct drm_nouveau_gem_info)
#define DRM_IOCTL_NOUVEAU_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_SET_TILING, struct drm_nouveau_gem_set_tiling)
+#define DRM_IOCTL_NOUVEAU_GEM_PUSHBUF_2 DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GEM_PUSHBUF_2, struct drm_nouveau_gem_pushbuf_2)
#endif /* __NOUVEAU_DRM_H__ */