summaryrefslogtreecommitdiffstats
path: root/nouveau
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 09:59:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 09:59:14 +0000
commit01bb4a6c21c225e55a84ac606a3b213909023ba0 (patch)
treeb5875af84ac1bd4015afede1015bb1761e5e85b3 /nouveau
parentInitial commit. (diff)
downloadlibdrm-01bb4a6c21c225e55a84ac606a3b213909023ba0.tar.xz
libdrm-01bb4a6c21c225e55a84ac606a3b213909023ba0.zip
Adding upstream version 2.4.120.upstream/2.4.120
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nouveau')
-rw-r--r--nouveau/Android.mk14
-rw-r--r--nouveau/Makefile.sources9
-rw-r--r--nouveau/abi16.c359
-rw-r--r--nouveau/bufctx.c160
-rw-r--r--nouveau/libdrm_nouveau.pc.in11
-rw-r--r--nouveau/meson.build65
-rw-r--r--nouveau/nouveau-symbols.txt43
-rw-r--r--nouveau/nouveau.c895
-rw-r--r--nouveau/nouveau.h281
-rw-r--r--nouveau/nvif/cl0080.h45
-rw-r--r--nouveau/nvif/cl9097.h44
-rw-r--r--nouveau/nvif/class.h141
-rw-r--r--nouveau/nvif/if0002.h38
-rw-r--r--nouveau/nvif/if0003.h33
-rw-r--r--nouveau/nvif/ioctl.h132
-rw-r--r--nouveau/nvif/unpack.h28
-rw-r--r--nouveau/private.h127
-rw-r--r--nouveau/pushbuf.c800
18 files changed, 3225 insertions, 0 deletions
diff --git a/nouveau/Android.mk b/nouveau/Android.mk
new file mode 100644
index 0000000..b430af4
--- /dev/null
+++ b/nouveau/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_NOUVEAU_FILES, LIBDRM_NOUVEAU_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_nouveau
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_NOUVEAU_FILES)
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/nouveau/Makefile.sources b/nouveau/Makefile.sources
new file mode 100644
index 0000000..89f2a2b
--- /dev/null
+++ b/nouveau/Makefile.sources
@@ -0,0 +1,9 @@
+LIBDRM_NOUVEAU_FILES := \
+ nouveau.c \
+ pushbuf.c \
+ bufctx.c \
+ abi16.c \
+ private.h
+
+LIBDRM_NOUVEAU_H_FILES := \
+ nouveau.h
diff --git a/nouveau/abi16.c b/nouveau/abi16.c
new file mode 100644
index 0000000..ba2501e
--- /dev/null
+++ b/nouveau/abi16.c
@@ -0,0 +1,359 @@
+/*
+ * 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <errno.h>
+
+#include "private.h"
+
+#include "nvif/class.h"
+
+static int
+abi16_chan_nv04(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct nv04_fifo *nv04 = obj->data;
+ struct drm_nouveau_channel_alloc req = {
+ .fb_ctxdma_handle = nv04->vram,
+ .tt_ctxdma_handle = nv04->gart
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_CHANNEL_ALLOC,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ nv04->base.channel = req.channel;
+ nv04->base.pushbuf = req.pushbuf_domains;
+ nv04->notify = req.notifier_handle;
+ nv04->base.object->handle = req.channel;
+ nv04->base.object->length = sizeof(*nv04);
+ return 0;
+}
+
+static int
+abi16_chan_nvc0(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct drm_nouveau_channel_alloc req = {};
+ struct nvc0_fifo *nvc0 = obj->data;
+ int ret;
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_CHANNEL_ALLOC,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ nvc0->base.channel = req.channel;
+ nvc0->base.pushbuf = req.pushbuf_domains;
+ nvc0->notify = req.notifier_handle;
+ nvc0->base.object->handle = req.channel;
+ nvc0->base.object->length = sizeof(*nvc0);
+ return 0;
+}
+
+static int
+abi16_chan_nve0(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct drm_nouveau_channel_alloc req = {};
+ struct nve0_fifo *nve0 = obj->data;
+ int ret;
+
+ if (obj->length > offsetof(struct nve0_fifo, engine)) {
+ req.fb_ctxdma_handle = 0xffffffff;
+ req.tt_ctxdma_handle = nve0->engine;
+ }
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_CHANNEL_ALLOC,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ nve0->base.channel = req.channel;
+ nve0->base.pushbuf = req.pushbuf_domains;
+ nve0->notify = req.notifier_handle;
+ nve0->base.object->handle = req.channel;
+ nve0->base.object->length = sizeof(*nve0);
+ return 0;
+}
+
+static int
+abi16_engobj(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct drm_nouveau_grobj_alloc req = {
+ .channel = obj->parent->handle,
+ .handle = obj->handle,
+ .class = obj->oclass,
+ };
+ int ret;
+
+ /* Older kernel versions did not have the concept of nouveau-
+ * specific classes and abused some NVIDIA-assigned ones for
+ * a SW class. The ABI16 layer has compatibility in place to
+ * translate these older identifiers to the newer ones.
+ *
+ * Clients that have been updated to use NVIF are required to
+ * use the newer class identifiers, which means that they'll
+ * break if running on an older kernel.
+ *
+ * To handle this case, when using ABI16, we translate to the
+ * older values which work on any kernel.
+ */
+ switch (req.class) {
+ case NVIF_CLASS_SW_NV04 : req.class = 0x006e; break;
+ case NVIF_CLASS_SW_NV10 : req.class = 0x016e; break;
+ case NVIF_CLASS_SW_NV50 : req.class = 0x506e; break;
+ case NVIF_CLASS_SW_GF100: req.class = 0x906e; break;
+ default:
+ break;
+ }
+
+ ret = drmCommandWrite(drm->fd, DRM_NOUVEAU_GROBJ_ALLOC,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ obj->length = sizeof(struct nouveau_object *);
+ return 0;
+}
+
+static int
+abi16_ntfy(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct nv04_notify *ntfy = obj->data;
+ struct drm_nouveau_notifierobj_alloc req = {
+ .channel = obj->parent->handle,
+ .handle = ntfy->object->handle,
+ .size = ntfy->length,
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_NOTIFIEROBJ_ALLOC,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ ntfy->offset = req.offset;
+ ntfy->object->length = sizeof(*ntfy);
+ return 0;
+}
+
+drm_private int
+abi16_sclass(struct nouveau_object *obj, struct nouveau_sclass **psclass)
+{
+ struct nouveau_sclass *sclass;
+ struct nouveau_device *dev;
+
+ if (!(sclass = calloc(8, sizeof(*sclass))))
+ return -ENOMEM;
+ *psclass = sclass;
+
+ switch (obj->oclass) {
+ case NOUVEAU_FIFO_CHANNEL_CLASS:
+ /* Older kernel versions were exposing the wrong video engine
+ * classes on certain G98:GF100 boards. This has since been
+ * corrected, but ABI16 has compatibility in place to avoid
+ * breaking older userspace.
+ *
+ * Clients that have been updated to use NVIF are required to
+ * use the correct classes, which means that they'll break if
+ * running on an older kernel.
+ *
+ * To handle this issue, if using the older kernel interfaces,
+ * we'll magic up a list containing the vdec classes that the
+ * kernel will accept for these boards. Clients should make
+ * use of this information instead of hardcoding classes for
+ * specific chipsets.
+ */
+ dev = (struct nouveau_device *)obj->parent;
+ if (dev->chipset >= 0x98 &&
+ dev->chipset != 0xa0 &&
+ dev->chipset < 0xc0) {
+ *sclass++ = (struct nouveau_sclass){
+ GT212_MSVLD, -1, -1
+ };
+ *sclass++ = (struct nouveau_sclass){
+ GT212_MSPDEC, -1, -1
+ };
+ *sclass++ = (struct nouveau_sclass){
+ GT212_MSPPP, -1, -1
+ };
+ }
+ break;
+ default:
+ break;
+ }
+
+ return sclass - *psclass;
+}
+
+drm_private void
+abi16_delete(struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
+ struct drm_nouveau_channel_free req;
+ req.channel = obj->handle;
+ drmCommandWrite(drm->fd, DRM_NOUVEAU_CHANNEL_FREE,
+ &req, sizeof(req));
+ } else {
+ struct drm_nouveau_gpuobj_free req;
+ req.channel = obj->parent->handle;
+ req.handle = obj->handle;
+ drmCommandWrite(drm->fd, DRM_NOUVEAU_GPUOBJ_FREE,
+ &req, sizeof(req));
+ }
+}
+
+drm_private bool
+abi16_object(struct nouveau_object *obj, int (**func)(struct nouveau_object *))
+{
+ struct nouveau_object *parent = obj->parent;
+
+ /* nouveau_object::length is (ab)used to determine whether the
+ * object is a legacy object (!=0), or a real NVIF object.
+ */
+ if ((parent->length != 0 && parent->oclass == NOUVEAU_DEVICE_CLASS) ||
+ (parent->length == 0 && parent->oclass == NV_DEVICE)) {
+ if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
+ struct nouveau_device *dev = (void *)parent;
+ if (dev->chipset < 0xc0)
+ *func = abi16_chan_nv04;
+ else
+ if (dev->chipset < 0xe0)
+ *func = abi16_chan_nvc0;
+ else
+ *func = abi16_chan_nve0;
+ return true;
+ }
+ } else
+ if ((parent->length != 0 &&
+ parent->oclass == NOUVEAU_FIFO_CHANNEL_CLASS)) {
+ if (obj->oclass == NOUVEAU_NOTIFIER_CLASS) {
+ *func = abi16_ntfy;
+ return true;
+ }
+
+ *func = abi16_engobj;
+ return false; /* try NVIF, if supported, before calling func */
+ }
+
+ *func = NULL;
+ return false;
+}
+
+drm_private void
+abi16_bo_info(struct nouveau_bo *bo, struct drm_nouveau_gem_info *info)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ nvbo->map_handle = info->map_handle;
+ bo->handle = info->handle;
+ bo->size = info->size;
+ bo->offset = info->offset;
+
+ bo->flags = 0;
+ if (info->domain & NOUVEAU_GEM_DOMAIN_VRAM)
+ bo->flags |= NOUVEAU_BO_VRAM;
+ if (info->domain & NOUVEAU_GEM_DOMAIN_GART)
+ bo->flags |= NOUVEAU_BO_GART;
+ if (!(info->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG))
+ bo->flags |= NOUVEAU_BO_CONTIG;
+ if (nvbo->map_handle)
+ bo->flags |= NOUVEAU_BO_MAP;
+
+ if (bo->device->chipset >= 0xc0) {
+ bo->config.nvc0.memtype = (info->tile_flags & 0xff00) >> 8;
+ bo->config.nvc0.tile_mode = info->tile_mode;
+ } else
+ if (bo->device->chipset >= 0x80 || bo->device->chipset == 0x50) {
+ bo->config.nv50.memtype = (info->tile_flags & 0x07f00) >> 8 |
+ (info->tile_flags & 0x30000) >> 9;
+ bo->config.nv50.tile_mode = info->tile_mode << 4;
+ } else {
+ bo->config.nv04.surf_flags = info->tile_flags & 7;
+ bo->config.nv04.surf_pitch = info->tile_mode;
+ }
+}
+
+drm_private int
+abi16_bo_init(struct nouveau_bo *bo, uint32_t alignment,
+ union nouveau_bo_config *config)
+{
+ struct nouveau_device *dev = bo->device;
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct drm_nouveau_gem_new req = {};
+ struct drm_nouveau_gem_info *info = &req.info;
+ int ret;
+
+ if (bo->flags & NOUVEAU_BO_VRAM)
+ info->domain |= NOUVEAU_GEM_DOMAIN_VRAM;
+ if (bo->flags & NOUVEAU_BO_GART)
+ info->domain |= NOUVEAU_GEM_DOMAIN_GART;
+ if (!info->domain)
+ info->domain |= NOUVEAU_GEM_DOMAIN_VRAM |
+ NOUVEAU_GEM_DOMAIN_GART;
+
+ if (bo->flags & NOUVEAU_BO_MAP)
+ info->domain |= NOUVEAU_GEM_DOMAIN_MAPPABLE;
+
+ if (bo->flags & NOUVEAU_BO_COHERENT)
+ info->domain |= NOUVEAU_GEM_DOMAIN_COHERENT;
+
+ if (!(bo->flags & NOUVEAU_BO_CONTIG))
+ info->tile_flags = NOUVEAU_GEM_TILE_NONCONTIG;
+
+ info->size = bo->size;
+ req.align = alignment;
+
+ if (config) {
+ if (dev->chipset >= 0xc0) {
+ info->tile_flags = (config->nvc0.memtype & 0xff) << 8;
+ info->tile_mode = config->nvc0.tile_mode;
+ } else
+ if (dev->chipset >= 0x80 || dev->chipset == 0x50) {
+ info->tile_flags = (config->nv50.memtype & 0x07f) << 8 |
+ (config->nv50.memtype & 0x180) << 9;
+ info->tile_mode = config->nv50.tile_mode >> 4;
+ } else {
+ info->tile_flags = config->nv04.surf_flags & 7;
+ info->tile_mode = config->nv04.surf_pitch;
+ }
+ }
+
+ if (!nouveau_device(dev)->have_bo_usage)
+ info->tile_flags &= 0x0000ff00;
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_NEW,
+ &req, sizeof(req));
+ if (ret == 0)
+ abi16_bo_info(bo, &req.info);
+ return ret;
+}
diff --git a/nouveau/bufctx.c b/nouveau/bufctx.c
new file mode 100644
index 0000000..00924b3
--- /dev/null
+++ b/nouveau/bufctx.c
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "libdrm_lists.h"
+
+#include "nouveau.h"
+#include "private.h"
+
+struct nouveau_bufref_priv {
+ struct nouveau_bufref base;
+ struct nouveau_bufref_priv *next;
+ struct nouveau_bufctx *bufctx;
+};
+
+struct nouveau_bufbin_priv {
+ struct nouveau_bufref_priv *list;
+ int relocs;
+};
+
+struct nouveau_bufctx_priv {
+ struct nouveau_bufctx base;
+ struct nouveau_bufref_priv *free;
+ int nr_bins;
+ struct nouveau_bufbin_priv bins[];
+};
+
+static inline struct nouveau_bufctx_priv *
+nouveau_bufctx(struct nouveau_bufctx *bctx)
+{
+ return (struct nouveau_bufctx_priv *)bctx;
+}
+
+drm_public int
+nouveau_bufctx_new(struct nouveau_client *client, int bins,
+ struct nouveau_bufctx **pbctx)
+{
+ struct nouveau_bufctx_priv *priv;
+
+ priv = calloc(1, sizeof(*priv) + sizeof(priv->bins[0]) * bins);
+ if (priv) {
+ DRMINITLISTHEAD(&priv->base.head);
+ DRMINITLISTHEAD(&priv->base.pending);
+ DRMINITLISTHEAD(&priv->base.current);
+ priv->base.client = client;
+ priv->nr_bins = bins;
+ *pbctx = &priv->base;
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+drm_public void
+nouveau_bufctx_del(struct nouveau_bufctx **pbctx)
+{
+ struct nouveau_bufctx_priv *pctx = nouveau_bufctx(*pbctx);
+ struct nouveau_bufref_priv *pref;
+ if (pctx) {
+ while (pctx->nr_bins--)
+ nouveau_bufctx_reset(&pctx->base, pctx->nr_bins);
+ while ((pref = pctx->free)) {
+ pctx->free = pref->next;
+ free(pref);
+ }
+ free(pctx);
+ *pbctx = NULL;
+ }
+}
+
+drm_public void
+nouveau_bufctx_reset(struct nouveau_bufctx *bctx, int bin)
+{
+ struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx);
+ struct nouveau_bufbin_priv *pbin = &pctx->bins[bin];
+ struct nouveau_bufref_priv *pref;
+
+ while ((pref = pbin->list)) {
+ DRMLISTDELINIT(&pref->base.thead);
+ pbin->list = pref->next;
+ pref->next = pctx->free;
+ pctx->free = pref;
+ }
+
+ bctx->relocs -= pbin->relocs;
+ pbin->relocs = 0;
+}
+
+drm_public struct nouveau_bufref *
+nouveau_bufctx_refn(struct nouveau_bufctx *bctx, int bin,
+ struct nouveau_bo *bo, uint32_t flags)
+{
+ struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx);
+ struct nouveau_bufbin_priv *pbin = &pctx->bins[bin];
+ struct nouveau_bufref_priv *pref = pctx->free;
+
+ if (!pref)
+ pref = malloc(sizeof(*pref));
+ else
+ pctx->free = pref->next;
+
+ if (pref) {
+ pref->base.bo = bo;
+ pref->base.flags = flags;
+ pref->base.packet = 0;
+
+ DRMLISTADDTAIL(&pref->base.thead, &bctx->pending);
+ pref->bufctx = bctx;
+ pref->next = pbin->list;
+ pbin->list = pref;
+ }
+
+ return &pref->base;
+}
+
+drm_public struct nouveau_bufref *
+nouveau_bufctx_mthd(struct nouveau_bufctx *bctx, int bin, uint32_t packet,
+ struct nouveau_bo *bo, uint64_t data, uint32_t flags,
+ uint32_t vor, uint32_t tor)
+{
+ struct nouveau_bufctx_priv *pctx = nouveau_bufctx(bctx);
+ struct nouveau_bufbin_priv *pbin = &pctx->bins[bin];
+ struct nouveau_bufref *bref = nouveau_bufctx_refn(bctx, bin, bo, flags);
+ if (bref) {
+ bref->packet = packet;
+ bref->data = data;
+ bref->vor = vor;
+ bref->tor = tor;
+ pbin->relocs++;
+ bctx->relocs++;
+ }
+ return bref;
+}
diff --git a/nouveau/libdrm_nouveau.pc.in b/nouveau/libdrm_nouveau.pc.in
new file mode 100644
index 0000000..7d0622e
--- /dev/null
+++ b/nouveau/libdrm_nouveau.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_nouveau
+Description: Userspace interface to nouveau kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_nouveau
+Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/libdrm/nouveau
+Requires.private: libdrm
diff --git a/nouveau/meson.build b/nouveau/meson.build
new file mode 100644
index 0000000..71c8f55
--- /dev/null
+++ b/nouveau/meson.build
@@ -0,0 +1,65 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# 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
+# AUTHORS OR COPYRIGHT HOLDERS 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.
+
+
+libdrm_nouveau = library(
+ 'drm_nouveau',
+ [files( 'nouveau.c', 'pushbuf.c', 'bufctx.c', 'abi16.c'), config_file],
+ c_args : libdrm_c_args,
+ gnu_symbol_visibility : 'hidden',
+ include_directories : [inc_root, inc_drm],
+ link_with : libdrm,
+ dependencies : [dep_threads, dep_atomic_ops],
+ version : '2.0.0',
+ install : true,
+)
+
+ext_libdrm_nouveau = declare_dependency(
+ link_with : [libdrm, libdrm_nouveau],
+ include_directories : [inc_drm, include_directories('.')],
+)
+
+if meson.version().version_compare('>= 0.54.0')
+ meson.override_dependency('libdrm_nouveau', ext_libdrm_nouveau)
+endif
+
+install_headers('nouveau.h', subdir : 'libdrm/nouveau')
+install_headers(
+ 'nvif/class.h', 'nvif/cl0080.h', 'nvif/cl9097.h', 'nvif/if0002.h',
+ 'nvif/if0003.h', 'nvif/ioctl.h', 'nvif/unpack.h',
+ subdir : 'libdrm/nouveau/nvif'
+)
+
+pkg.generate(
+ libdrm_nouveau,
+ name : 'libdrm_nouveau',
+ subdirs : ['.', 'libdrm', 'libdrm/nouveau'],
+ description : 'Userspace interface to nouveau kernel DRM services',
+)
+
+test(
+ 'nouveau-symbols-check',
+ symbols_check,
+ args : [
+ '--lib', libdrm_nouveau,
+ '--symbols-file', files('nouveau-symbols.txt'),
+ '--nm', prog_nm.full_path(),
+ ],
+)
diff --git a/nouveau/nouveau-symbols.txt b/nouveau/nouveau-symbols.txt
new file mode 100644
index 0000000..7e9b103
--- /dev/null
+++ b/nouveau/nouveau-symbols.txt
@@ -0,0 +1,43 @@
+nouveau_bo_make_global
+nouveau_bo_map
+nouveau_bo_name_get
+nouveau_bo_name_ref
+nouveau_bo_new
+nouveau_bo_prime_handle_ref
+nouveau_bo_ref
+nouveau_bo_set_prime
+nouveau_bo_wait
+nouveau_bo_wrap
+nouveau_bufctx_del
+nouveau_bufctx_mthd
+nouveau_bufctx_new
+nouveau_bufctx_refn
+nouveau_bufctx_reset
+nouveau_check_dead_channel
+nouveau_client_del
+nouveau_client_new
+nouveau_device_del
+nouveau_device_new
+nouveau_device_open
+nouveau_device_open_existing
+nouveau_device_wrap
+nouveau_drm_del
+nouveau_drm_new
+nouveau_getparam
+nouveau_object_del
+nouveau_object_mclass
+nouveau_object_mthd
+nouveau_object_new
+nouveau_object_sclass_get
+nouveau_object_sclass_put
+nouveau_pushbuf_bufctx
+nouveau_pushbuf_data
+nouveau_pushbuf_del
+nouveau_pushbuf_kick
+nouveau_pushbuf_new
+nouveau_pushbuf_refd
+nouveau_pushbuf_refn
+nouveau_pushbuf_reloc
+nouveau_pushbuf_space
+nouveau_pushbuf_validate
+nouveau_setparam
diff --git a/nouveau/nouveau.c b/nouveau/nouveau.c
new file mode 100644
index 0000000..62914a3
--- /dev/null
+++ b/nouveau/nouveau.c
@@ -0,0 +1,895 @@
+/*
+ * 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <xf86drm.h>
+#include <xf86atomic.h>
+#include "libdrm_macros.h"
+#include "libdrm_lists.h"
+#include "nouveau_drm.h"
+
+#include "nouveau.h"
+#include "private.h"
+
+#include "nvif/class.h"
+#include "nvif/cl0080.h"
+#include "nvif/ioctl.h"
+#include "nvif/unpack.h"
+
+drm_private FILE *nouveau_out = NULL;
+drm_private uint32_t nouveau_debug = 0;
+
+static void
+debug_init(void)
+{
+ static bool once = false;
+ char *debug, *out;
+
+ if (once)
+ return;
+ once = true;
+
+ debug = getenv("NOUVEAU_LIBDRM_DEBUG");
+ if (debug) {
+ int n = strtol(debug, NULL, 0);
+ if (n >= 0)
+ nouveau_debug = n;
+
+ }
+
+ nouveau_out = stderr;
+ out = getenv("NOUVEAU_LIBDRM_OUT");
+ if (out) {
+ FILE *fout = fopen(out, "w");
+ if (fout)
+ nouveau_out = fout;
+ }
+}
+
+static int
+nouveau_object_ioctl(struct nouveau_object *obj, void *data, uint32_t size)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ union {
+ struct nvif_ioctl_v0 v0;
+ } *args = data;
+ uint32_t argc = size;
+ int ret = -ENOSYS;
+
+ if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
+ if (!obj->length) {
+ if (obj != &drm->client)
+ args->v0.object = (unsigned long)(void *)obj;
+ else
+ args->v0.object = 0;
+ args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
+ args->v0.route = 0x00;
+ } else {
+ args->v0.route = 0xff;
+ args->v0.token = obj->handle;
+ }
+ } else
+ return ret;
+
+ return drmCommandWriteRead(drm->fd, DRM_NOUVEAU_NVIF, args, argc);
+}
+
+drm_public int
+nouveau_object_mthd(struct nouveau_object *obj,
+ uint32_t mthd, void *data, uint32_t size)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_mthd_v0 mthd;
+ } *args;
+ uint32_t argc = sizeof(*args) + size;
+ uint8_t stack[128];
+ int ret;
+
+ if (!drm->nvif)
+ return -ENOSYS;
+
+ if (argc > sizeof(stack)) {
+ if (!(args = malloc(argc)))
+ return -ENOMEM;
+ } else {
+ args = (void *)stack;
+ }
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_MTHD;
+ args->mthd.version = 0;
+ args->mthd.method = mthd;
+
+ memcpy(args->mthd.data, data, size);
+ ret = nouveau_object_ioctl(obj, args, argc);
+ memcpy(data, args->mthd.data, size);
+ if (args != (void *)stack)
+ free(args);
+ return ret;
+}
+
+drm_public void
+nouveau_object_sclass_put(struct nouveau_sclass **psclass)
+{
+ free(*psclass);
+ *psclass = NULL;
+}
+
+drm_public int
+nouveau_object_sclass_get(struct nouveau_object *obj,
+ struct nouveau_sclass **psclass)
+{
+ struct nouveau_drm *drm = nouveau_drm(obj);
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_sclass_v0 sclass;
+ } *args = NULL;
+ struct nouveau_sclass *sclass;
+ int ret, cnt = 0, i;
+ uint32_t size;
+
+ if (!drm->nvif)
+ return abi16_sclass(obj, psclass);
+
+ while (1) {
+ size = sizeof(*args) + cnt * sizeof(args->sclass.oclass[0]);
+ if (!(args = malloc(size)))
+ return -ENOMEM;
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_SCLASS;
+ args->sclass.version = 0;
+ args->sclass.count = cnt;
+
+ ret = nouveau_object_ioctl(obj, args, size);
+ if (ret == 0 && args->sclass.count <= cnt)
+ break;
+ cnt = args->sclass.count;
+ free(args);
+ if (ret != 0)
+ return ret;
+ }
+
+ if ((sclass = calloc(args->sclass.count, sizeof(*sclass)))) {
+ for (i = 0; i < args->sclass.count; i++) {
+ sclass[i].oclass = args->sclass.oclass[i].oclass;
+ sclass[i].minver = args->sclass.oclass[i].minver;
+ sclass[i].maxver = args->sclass.oclass[i].maxver;
+ }
+ *psclass = sclass;
+ ret = args->sclass.count;
+ } else {
+ ret = -ENOMEM;
+ }
+
+ free(args);
+ return ret;
+}
+
+drm_public int
+nouveau_object_mclass(struct nouveau_object *obj,
+ const struct nouveau_mclass *mclass)
+{
+ struct nouveau_sclass *sclass;
+ int ret = -ENODEV;
+ int cnt, i, j;
+
+ cnt = nouveau_object_sclass_get(obj, &sclass);
+ if (cnt < 0)
+ return cnt;
+
+ for (i = 0; ret < 0 && mclass[i].oclass; i++) {
+ for (j = 0; j < cnt; j++) {
+ if (mclass[i].oclass == sclass[j].oclass &&
+ mclass[i].version >= sclass[j].minver &&
+ mclass[i].version <= sclass[j].maxver) {
+ ret = i;
+ break;
+ }
+ }
+ }
+
+ nouveau_object_sclass_put(&sclass);
+ return ret;
+}
+
+static void
+nouveau_object_fini(struct nouveau_object *obj)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_del del;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_DEL,
+ };
+
+ if (obj->data) {
+ abi16_delete(obj);
+ free(obj->data);
+ obj->data = NULL;
+ return;
+ }
+
+ nouveau_object_ioctl(obj, &args, sizeof(args));
+}
+
+static int
+nouveau_object_init(struct nouveau_object *parent, uint32_t handle,
+ int32_t oclass, void *data, uint32_t size,
+ struct nouveau_object *obj)
+{
+ struct nouveau_drm *drm = nouveau_drm(parent);
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_new_v0 new;
+ } *args;
+ uint32_t argc = sizeof(*args) + size;
+ int (*func)(struct nouveau_object *);
+ int ret = -ENOSYS;
+
+ obj->parent = parent;
+ obj->handle = handle;
+ obj->oclass = oclass;
+ obj->length = 0;
+ obj->data = NULL;
+
+ if (!abi16_object(obj, &func) && drm->nvif) {
+ if (!(args = malloc(argc)))
+ return -ENOMEM;
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_NEW;
+ args->new.version = 0;
+ args->new.route = NVIF_IOCTL_V0_ROUTE_NVIF;
+ args->new.token = (unsigned long)(void *)obj;
+ args->new.object = (unsigned long)(void *)obj;
+ args->new.handle = handle;
+ args->new.oclass = oclass;
+ memcpy(args->new.data, data, size);
+ ret = nouveau_object_ioctl(parent, args, argc);
+ memcpy(data, args->new.data, size);
+ free(args);
+ } else
+ if (func) {
+ obj->length = size ? size : sizeof(struct nouveau_object *);
+ if (!(obj->data = malloc(obj->length)))
+ return -ENOMEM;
+ if (data)
+ memcpy(obj->data, data, obj->length);
+ *(struct nouveau_object **)obj->data = obj;
+
+ ret = func(obj);
+ }
+
+ if (ret) {
+ nouveau_object_fini(obj);
+ return ret;
+ }
+
+ return 0;
+}
+
+drm_public int
+nouveau_object_new(struct nouveau_object *parent, uint64_t handle,
+ uint32_t oclass, void *data, uint32_t length,
+ struct nouveau_object **pobj)
+{
+ struct nouveau_object *obj;
+ int ret;
+
+ if (!(obj = malloc(sizeof(*obj))))
+ return -ENOMEM;
+
+ ret = nouveau_object_init(parent, handle, oclass, data, length, obj);
+ if (ret) {
+ free(obj);
+ return ret;
+ }
+
+ *pobj = obj;
+ return 0;
+}
+
+drm_public void
+nouveau_object_del(struct nouveau_object **pobj)
+{
+ struct nouveau_object *obj = *pobj;
+ if (obj) {
+ nouveau_object_fini(obj);
+ free(obj);
+ *pobj = NULL;
+ }
+}
+
+drm_public void
+nouveau_drm_del(struct nouveau_drm **pdrm)
+{
+ free(*pdrm);
+ *pdrm = NULL;
+}
+
+drm_public int
+nouveau_drm_new(int fd, struct nouveau_drm **pdrm)
+{
+ struct nouveau_drm *drm;
+ drmVersionPtr ver;
+
+ debug_init();
+
+ if (!(drm = calloc(1, sizeof(*drm))))
+ return -ENOMEM;
+ drm->fd = fd;
+
+ if (!(ver = drmGetVersion(fd))) {
+ nouveau_drm_del(&drm);
+ return -EINVAL;
+ }
+ *pdrm = drm;
+
+ drm->version = (ver->version_major << 24) |
+ (ver->version_minor << 8) |
+ ver->version_patchlevel;
+ drm->nvif = (drm->version >= 0x01000301);
+ drmFreeVersion(ver);
+ return 0;
+}
+
+/* this is the old libdrm's version of nouveau_device_wrap(), the symbol
+ * is kept here to prevent AIGLX from crashing if the DDX is linked against
+ * the new libdrm, but the DRI driver against the old
+ */
+drm_public int
+nouveau_device_open_existing(struct nouveau_device **pdev, int close, int fd,
+ drm_context_t ctx)
+{
+ return -EACCES;
+}
+
+drm_public int
+nouveau_device_new(struct nouveau_object *parent, int32_t oclass,
+ void *data, uint32_t size, struct nouveau_device **pdev)
+{
+ struct nv_device_info_v0 info = {};
+ union {
+ struct nv_device_v0 v0;
+ } *args = data;
+ uint32_t argc = size;
+ struct nouveau_drm *drm = nouveau_drm(parent);
+ struct nouveau_device_priv *nvdev;
+ struct nouveau_device *dev;
+ uint64_t v;
+ char *tmp;
+ int ret = -ENOSYS;
+
+ if (oclass != NV_DEVICE ||
+ nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))
+ return ret;
+
+ if (!(nvdev = calloc(1, sizeof(*nvdev))))
+ return -ENOMEM;
+ dev = *pdev = &nvdev->base;
+ dev->fd = -1;
+
+ if (drm->nvif) {
+ ret = nouveau_object_init(parent, 0, oclass, args, argc,
+ &dev->object);
+ if (ret)
+ goto done;
+
+ info.version = 0;
+
+ ret = nouveau_object_mthd(&dev->object, NV_DEVICE_V0_INFO,
+ &info, sizeof(info));
+ if (ret)
+ goto done;
+
+ nvdev->base.chipset = info.chipset;
+ nvdev->have_bo_usage = true;
+ } else
+ if (args->v0.device == ~0ULL) {
+ nvdev->base.object.parent = &drm->client;
+ nvdev->base.object.handle = ~0ULL;
+ nvdev->base.object.oclass = NOUVEAU_DEVICE_CLASS;
+ nvdev->base.object.length = ~0;
+
+ ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_CHIPSET_ID, &v);
+ if (ret)
+ goto done;
+ nvdev->base.chipset = v;
+
+ ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_HAS_BO_USAGE, &v);
+ if (ret == 0)
+ nvdev->have_bo_usage = (v != 0);
+ } else
+ return -ENOSYS;
+
+ ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_FB_SIZE, &v);
+ if (ret)
+ goto done;
+ nvdev->base.vram_size = v;
+
+ ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_AGP_SIZE, &v);
+ if (ret)
+ goto done;
+ nvdev->base.gart_size = v;
+
+ tmp = getenv("NOUVEAU_LIBDRM_VRAM_LIMIT_PERCENT");
+ if (tmp)
+ nvdev->vram_limit_percent = atoi(tmp);
+ else
+ nvdev->vram_limit_percent = 80;
+
+ nvdev->base.vram_limit =
+ (nvdev->base.vram_size * nvdev->vram_limit_percent) / 100;
+
+ tmp = getenv("NOUVEAU_LIBDRM_GART_LIMIT_PERCENT");
+ if (tmp)
+ nvdev->gart_limit_percent = atoi(tmp);
+ else
+ nvdev->gart_limit_percent = 80;
+
+ nvdev->base.gart_limit =
+ (nvdev->base.gart_size * nvdev->gart_limit_percent) / 100;
+
+ ret = pthread_mutex_init(&nvdev->lock, NULL);
+ DRMINITLISTHEAD(&nvdev->bo_list);
+done:
+ if (ret)
+ nouveau_device_del(pdev);
+ return ret;
+}
+
+drm_public int
+nouveau_device_wrap(int fd, int close, struct nouveau_device **pdev)
+{
+ struct nouveau_drm *drm;
+ struct nouveau_device_priv *nvdev;
+ int ret;
+
+ ret = nouveau_drm_new(fd, &drm);
+ if (ret)
+ return ret;
+ drm->nvif = false;
+
+ ret = nouveau_device_new(&drm->client, NV_DEVICE,
+ &(struct nv_device_v0) {
+ .device = ~0ULL,
+ }, sizeof(struct nv_device_v0), pdev);
+ if (ret) {
+ nouveau_drm_del(&drm);
+ return ret;
+ }
+
+ nvdev = nouveau_device(*pdev);
+ nvdev->base.fd = drm->fd;
+ nvdev->base.drm_version = drm->version;
+ nvdev->close = close;
+ return 0;
+}
+
+drm_public int
+nouveau_device_open(const char *busid, struct nouveau_device **pdev)
+{
+ int ret = -ENODEV, fd = drmOpen("nouveau", busid);
+ if (fd >= 0) {
+ ret = nouveau_device_wrap(fd, 1, pdev);
+ if (ret)
+ drmClose(fd);
+ }
+ return ret;
+}
+
+drm_public void
+nouveau_device_del(struct nouveau_device **pdev)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(*pdev);
+ if (nvdev) {
+ free(nvdev->client);
+ pthread_mutex_destroy(&nvdev->lock);
+ if (nvdev->base.fd >= 0) {
+ struct nouveau_drm *drm =
+ nouveau_drm(&nvdev->base.object);
+ nouveau_drm_del(&drm);
+ if (nvdev->close)
+ drmClose(nvdev->base.fd);
+ }
+ free(nvdev);
+ *pdev = NULL;
+ }
+}
+
+drm_public int
+nouveau_getparam(struct nouveau_device *dev, uint64_t param, uint64_t *value)
+{
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct drm_nouveau_getparam r = { .param = param };
+ int fd = drm->fd, ret =
+ drmCommandWriteRead(fd, DRM_NOUVEAU_GETPARAM, &r, sizeof(r));
+ *value = r.value;
+ return ret;
+}
+
+drm_public int
+nouveau_setparam(struct nouveau_device *dev, uint64_t param, uint64_t value)
+{
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct drm_nouveau_setparam r = { .param = param, .value = value };
+ return drmCommandWrite(drm->fd, DRM_NOUVEAU_SETPARAM, &r, sizeof(r));
+}
+
+drm_public int
+nouveau_client_new(struct nouveau_device *dev, struct nouveau_client **pclient)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct nouveau_client_priv *pcli;
+ int id = 0, i, ret = -ENOMEM;
+ uint32_t *clients;
+
+ pthread_mutex_lock(&nvdev->lock);
+
+ for (i = 0; i < nvdev->nr_client; i++) {
+ id = ffs(nvdev->client[i]) - 1;
+ if (id >= 0)
+ goto out;
+ }
+
+ clients = realloc(nvdev->client, sizeof(uint32_t) * (i + 1));
+ if (!clients)
+ goto unlock;
+ nvdev->client = clients;
+ nvdev->client[i] = 0;
+ nvdev->nr_client++;
+
+out:
+ pcli = calloc(1, sizeof(*pcli));
+ if (pcli) {
+ nvdev->client[i] |= (1 << id);
+ pcli->base.device = dev;
+ pcli->base.id = (i * 32) + id;
+ ret = 0;
+ }
+
+ *pclient = &pcli->base;
+
+unlock:
+ pthread_mutex_unlock(&nvdev->lock);
+ return ret;
+}
+
+drm_public void
+nouveau_client_del(struct nouveau_client **pclient)
+{
+ struct nouveau_client_priv *pcli = nouveau_client(*pclient);
+ struct nouveau_device_priv *nvdev;
+ if (pcli) {
+ int id = pcli->base.id;
+ nvdev = nouveau_device(pcli->base.device);
+ pthread_mutex_lock(&nvdev->lock);
+ nvdev->client[id / 32] &= ~(1 << (id % 32));
+ pthread_mutex_unlock(&nvdev->lock);
+ free(pcli->kref);
+ free(pcli);
+ }
+}
+
+static void
+nouveau_bo_del(struct nouveau_bo *bo)
+{
+ struct nouveau_drm *drm = nouveau_drm(&bo->device->object);
+ struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ if (nvbo->head.next) {
+ pthread_mutex_lock(&nvdev->lock);
+ if (atomic_read(&nvbo->refcnt) == 0) {
+ DRMLISTDEL(&nvbo->head);
+ /*
+ * This bo has to be closed with the lock held because
+ * gem handles are not refcounted. If a shared bo is
+ * closed and re-opened in another thread a race
+ * against DRM_IOCTL_GEM_OPEN or drmPrimeFDToHandle
+ * might cause the bo to be closed accidentally while
+ * re-importing.
+ */
+ drmCloseBufferHandle(drm->fd, bo->handle);
+ }
+ pthread_mutex_unlock(&nvdev->lock);
+ } else {
+ drmCloseBufferHandle(drm->fd, bo->handle);
+ }
+ if (bo->map)
+ drm_munmap(bo->map, bo->size);
+ free(nvbo);
+}
+
+drm_public int
+nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, uint32_t align,
+ uint64_t size, union nouveau_bo_config *config,
+ struct nouveau_bo **pbo)
+{
+ struct nouveau_bo_priv *nvbo = calloc(1, sizeof(*nvbo));
+ struct nouveau_bo *bo = &nvbo->base;
+ int ret;
+
+ if (!nvbo)
+ return -ENOMEM;
+ atomic_set(&nvbo->refcnt, 1);
+ bo->device = dev;
+ bo->flags = flags;
+ bo->size = size;
+
+ ret = abi16_bo_init(bo, align, config);
+ if (ret) {
+ free(nvbo);
+ return ret;
+ }
+
+ *pbo = bo;
+ return 0;
+}
+
+static int
+nouveau_bo_wrap_locked(struct nouveau_device *dev, uint32_t handle,
+ struct nouveau_bo **pbo, int name)
+{
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct drm_nouveau_gem_info req = { .handle = handle };
+ struct nouveau_bo_priv *nvbo;
+ int ret;
+
+ DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
+ if (nvbo->base.handle == handle) {
+ if (atomic_inc_return(&nvbo->refcnt) == 1) {
+ /*
+ * Uh oh, this bo is dead and someone else
+ * will free it, but because refcnt is
+ * now non-zero fortunately they won't
+ * call the ioctl to close the bo.
+ *
+ * Remove this bo from the list so other
+ * calls to nouveau_bo_wrap_locked will
+ * see our replacement nvbo.
+ */
+ DRMLISTDEL(&nvbo->head);
+ if (!name)
+ name = nvbo->name;
+ break;
+ }
+
+ *pbo = &nvbo->base;
+ return 0;
+ }
+ }
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_INFO,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ nvbo = calloc(1, sizeof(*nvbo));
+ if (nvbo) {
+ atomic_set(&nvbo->refcnt, 1);
+ nvbo->base.device = dev;
+ abi16_bo_info(&nvbo->base, &req);
+ nvbo->name = name;
+ DRMLISTADD(&nvbo->head, &nvdev->bo_list);
+ *pbo = &nvbo->base;
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+static void
+nouveau_nvbo_make_global(struct nouveau_bo_priv *nvbo)
+{
+ if (!nvbo->head.next) {
+ struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
+ pthread_mutex_lock(&nvdev->lock);
+ if (!nvbo->head.next)
+ DRMLISTADD(&nvbo->head, &nvdev->bo_list);
+ pthread_mutex_unlock(&nvdev->lock);
+ }
+}
+
+drm_public void
+nouveau_bo_make_global(struct nouveau_bo *bo)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ nouveau_nvbo_make_global(nvbo);
+}
+
+drm_public int
+nouveau_bo_wrap(struct nouveau_device *dev, uint32_t handle,
+ struct nouveau_bo **pbo)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ int ret;
+ pthread_mutex_lock(&nvdev->lock);
+ ret = nouveau_bo_wrap_locked(dev, handle, pbo, 0);
+ pthread_mutex_unlock(&nvdev->lock);
+ return ret;
+}
+
+drm_public int
+nouveau_bo_name_ref(struct nouveau_device *dev, uint32_t name,
+ struct nouveau_bo **pbo)
+{
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct nouveau_bo_priv *nvbo;
+ struct drm_gem_open req = { .name = name };
+ int ret;
+
+ pthread_mutex_lock(&nvdev->lock);
+ DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
+ if (nvbo->name == name) {
+ ret = nouveau_bo_wrap_locked(dev, nvbo->base.handle,
+ pbo, name);
+ pthread_mutex_unlock(&nvdev->lock);
+ return ret;
+ }
+ }
+
+ ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_OPEN, &req);
+ if (ret == 0) {
+ ret = nouveau_bo_wrap_locked(dev, req.handle, pbo, name);
+ }
+
+ pthread_mutex_unlock(&nvdev->lock);
+ return ret;
+}
+
+drm_public int
+nouveau_bo_name_get(struct nouveau_bo *bo, uint32_t *name)
+{
+ struct drm_gem_flink req = { .handle = bo->handle };
+ struct nouveau_drm *drm = nouveau_drm(&bo->device->object);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ *name = nvbo->name;
+ if (!*name) {
+ int ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_FLINK, &req);
+
+ if (ret) {
+ *name = 0;
+ return ret;
+ }
+ nvbo->name = *name = req.name;
+
+ nouveau_nvbo_make_global(nvbo);
+ }
+ return 0;
+}
+
+drm_public void
+nouveau_bo_ref(struct nouveau_bo *bo, struct nouveau_bo **pref)
+{
+ struct nouveau_bo *ref = *pref;
+ if (bo) {
+ atomic_inc(&nouveau_bo(bo)->refcnt);
+ }
+ if (ref) {
+ if (atomic_dec_and_test(&nouveau_bo(ref)->refcnt))
+ nouveau_bo_del(ref);
+ }
+ *pref = bo;
+}
+
+drm_public int
+nouveau_bo_prime_handle_ref(struct nouveau_device *dev, int prime_fd,
+ struct nouveau_bo **bo)
+{
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ int ret;
+ unsigned int handle;
+
+ nouveau_bo_ref(NULL, bo);
+
+ pthread_mutex_lock(&nvdev->lock);
+ ret = drmPrimeFDToHandle(drm->fd, prime_fd, &handle);
+ if (ret == 0) {
+ ret = nouveau_bo_wrap_locked(dev, handle, bo, 0);
+ }
+ pthread_mutex_unlock(&nvdev->lock);
+ return ret;
+}
+
+drm_public int
+nouveau_bo_set_prime(struct nouveau_bo *bo, int *prime_fd)
+{
+ struct nouveau_drm *drm = nouveau_drm(&bo->device->object);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ int ret;
+
+ ret = drmPrimeHandleToFD(drm->fd, nvbo->base.handle, DRM_CLOEXEC, prime_fd);
+ if (ret)
+ return ret;
+
+ nouveau_nvbo_make_global(nvbo);
+ return 0;
+}
+
+drm_public int
+nouveau_bo_wait(struct nouveau_bo *bo, uint32_t access,
+ struct nouveau_client *client)
+{
+ struct nouveau_drm *drm = nouveau_drm(&bo->device->object);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ struct drm_nouveau_gem_cpu_prep req;
+ struct nouveau_pushbuf *push;
+ int ret = 0;
+
+ if (!(access & NOUVEAU_BO_RDWR))
+ return 0;
+
+ push = cli_push_get(client, bo);
+ if (push && push->channel)
+ nouveau_pushbuf_kick(push, push->channel);
+
+ if (!nvbo->head.next && !(nvbo->access & NOUVEAU_BO_WR) &&
+ !(access & NOUVEAU_BO_WR))
+ return 0;
+
+ req.handle = bo->handle;
+ req.flags = 0;
+ if (access & NOUVEAU_BO_WR)
+ req.flags |= NOUVEAU_GEM_CPU_PREP_WRITE;
+ if (access & NOUVEAU_BO_NOBLOCK)
+ req.flags |= NOUVEAU_GEM_CPU_PREP_NOWAIT;
+
+ ret = drmCommandWrite(drm->fd, DRM_NOUVEAU_GEM_CPU_PREP,
+ &req, sizeof(req));
+ if (ret == 0)
+ nvbo->access = 0;
+ return ret;
+}
+
+drm_public int
+nouveau_bo_map(struct nouveau_bo *bo, uint32_t access,
+ struct nouveau_client *client)
+{
+ struct nouveau_drm *drm = nouveau_drm(&bo->device->object);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ if (bo->map == NULL) {
+ bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, drm->fd, nvbo->map_handle);
+ if (bo->map == MAP_FAILED) {
+ bo->map = NULL;
+ return -errno;
+ }
+ }
+ return nouveau_bo_wait(bo, access, client);
+}
diff --git a/nouveau/nouveau.h b/nouveau/nouveau.h
new file mode 100644
index 0000000..a5119f9
--- /dev/null
+++ b/nouveau/nouveau.h
@@ -0,0 +1,281 @@
+#ifndef __NOUVEAU_H__
+#define __NOUVEAU_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Supported class information, provided by the kernel */
+struct nouveau_sclass {
+ int32_t oclass;
+ int minver;
+ int maxver;
+};
+
+/* Client-provided array describing class versions that are desired.
+ *
+ * These are used to match against the kernel's list of supported classes.
+ */
+struct nouveau_mclass {
+ int32_t oclass;
+ int version;
+ void *data;
+};
+
+struct nouveau_object {
+ struct nouveau_object *parent;
+ uint64_t handle;
+ uint32_t oclass;
+ uint32_t length; /* deprecated */
+ void *data; /* deprecated */
+};
+
+int nouveau_object_new(struct nouveau_object *parent, uint64_t handle,
+ uint32_t oclass, void *data, uint32_t length,
+ struct nouveau_object **);
+void nouveau_object_del(struct nouveau_object **);
+int nouveau_object_mthd(struct nouveau_object *, uint32_t mthd,
+ void *data, uint32_t size);
+int nouveau_object_sclass_get(struct nouveau_object *,
+ struct nouveau_sclass **);
+void nouveau_object_sclass_put(struct nouveau_sclass **);
+int nouveau_object_mclass(struct nouveau_object *,
+ const struct nouveau_mclass *);
+
+struct nouveau_drm {
+ struct nouveau_object client;
+ int fd;
+ uint32_t version;
+ bool nvif;
+};
+
+static inline struct nouveau_drm *
+nouveau_drm(struct nouveau_object *obj)
+{
+ while (obj && obj->parent)
+ obj = obj->parent;
+ return (struct nouveau_drm *)obj;
+}
+
+int nouveau_drm_new(int fd, struct nouveau_drm **);
+void nouveau_drm_del(struct nouveau_drm **);
+
+struct nouveau_device {
+ struct nouveau_object object;
+ int fd; /* deprecated */
+ uint32_t lib_version; /* deprecated */
+ uint32_t drm_version; /* deprecated */
+ uint32_t chipset;
+ uint64_t vram_size;
+ uint64_t gart_size;
+ uint64_t vram_limit;
+ uint64_t gart_limit;
+};
+
+int nouveau_device_new(struct nouveau_object *parent, int32_t oclass,
+ void *data, uint32_t size, struct nouveau_device **);
+void nouveau_device_del(struct nouveau_device **);
+
+int nouveau_getparam(struct nouveau_device *, uint64_t param, uint64_t *value);
+int nouveau_setparam(struct nouveau_device *, uint64_t param, uint64_t value);
+
+/* deprecated */
+int nouveau_device_wrap(int fd, int close, struct nouveau_device **);
+int nouveau_device_open(const char *busid, struct nouveau_device **);
+
+struct nouveau_client {
+ struct nouveau_device *device;
+ int id;
+};
+
+int nouveau_client_new(struct nouveau_device *, struct nouveau_client **);
+void nouveau_client_del(struct nouveau_client **);
+
+union nouveau_bo_config {
+ struct {
+#define NV04_BO_16BPP 0x00000001
+#define NV04_BO_32BPP 0x00000002
+#define NV04_BO_ZETA 0x00000004
+ uint32_t surf_flags;
+ uint32_t surf_pitch;
+ } nv04;
+ struct {
+ uint32_t memtype;
+ uint32_t tile_mode;
+ } nv50;
+ struct {
+ uint32_t memtype;
+ uint32_t tile_mode;
+ } nvc0;
+ uint32_t data[8];
+};
+
+#define NOUVEAU_BO_VRAM 0x00000001
+#define NOUVEAU_BO_GART 0x00000002
+#define NOUVEAU_BO_APER (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART)
+#define NOUVEAU_BO_RD 0x00000100
+#define NOUVEAU_BO_WR 0x00000200
+#define NOUVEAU_BO_RDWR (NOUVEAU_BO_RD | NOUVEAU_BO_WR)
+#define NOUVEAU_BO_NOBLOCK 0x00000400
+#define NOUVEAU_BO_LOW 0x00001000
+#define NOUVEAU_BO_HIGH 0x00002000
+#define NOUVEAU_BO_OR 0x00004000
+#define NOUVEAU_BO_MAP 0x80000000
+#define NOUVEAU_BO_CONTIG 0x40000000
+#define NOUVEAU_BO_NOSNOOP 0x20000000
+#define NOUVEAU_BO_COHERENT 0x10000000
+
+struct nouveau_bo {
+ struct nouveau_device *device;
+ uint32_t handle;
+ uint64_t size;
+ uint32_t flags;
+ uint64_t offset;
+ void *map;
+ union nouveau_bo_config config;
+};
+
+int nouveau_bo_new(struct nouveau_device *, uint32_t flags, uint32_t align,
+ uint64_t size, union nouveau_bo_config *,
+ struct nouveau_bo **);
+void nouveau_bo_make_global(struct nouveau_bo *);
+int nouveau_bo_wrap(struct nouveau_device *, uint32_t handle,
+ struct nouveau_bo **);
+int nouveau_bo_name_ref(struct nouveau_device *v, uint32_t name,
+ struct nouveau_bo **);
+int nouveau_bo_name_get(struct nouveau_bo *, uint32_t *name);
+void nouveau_bo_ref(struct nouveau_bo *, struct nouveau_bo **);
+int nouveau_bo_map(struct nouveau_bo *, uint32_t access,
+ struct nouveau_client *);
+int nouveau_bo_wait(struct nouveau_bo *, uint32_t access,
+ struct nouveau_client *);
+int nouveau_bo_prime_handle_ref(struct nouveau_device *, int prime_fd,
+ struct nouveau_bo **);
+int nouveau_bo_set_prime(struct nouveau_bo *, int *prime_fd);
+
+struct nouveau_list {
+ struct nouveau_list *prev;
+ struct nouveau_list *next;
+};
+
+struct nouveau_bufref {
+ struct nouveau_list thead;
+ struct nouveau_bo *bo;
+ uint32_t packet;
+ uint32_t flags;
+ uint32_t data;
+ uint32_t vor;
+ uint32_t tor;
+ uint32_t priv_data;
+ void *priv;
+};
+
+struct nouveau_bufctx {
+ struct nouveau_client *client;
+ struct nouveau_list head;
+ struct nouveau_list pending;
+ struct nouveau_list current;
+ int relocs;
+};
+
+int nouveau_bufctx_new(struct nouveau_client *, int bins,
+ struct nouveau_bufctx **);
+void nouveau_bufctx_del(struct nouveau_bufctx **);
+struct nouveau_bufref *
+nouveau_bufctx_refn(struct nouveau_bufctx *, int bin,
+ struct nouveau_bo *, uint32_t flags);
+struct nouveau_bufref *
+nouveau_bufctx_mthd(struct nouveau_bufctx *, int bin, uint32_t packet,
+ struct nouveau_bo *, uint64_t data, uint32_t flags,
+ uint32_t vor, uint32_t tor);
+void nouveau_bufctx_reset(struct nouveau_bufctx *, int bin);
+
+struct nouveau_pushbuf_krec;
+struct nouveau_pushbuf {
+ struct nouveau_client *client;
+ struct nouveau_object *channel;
+ struct nouveau_bufctx *bufctx;
+ void (*kick_notify)(struct nouveau_pushbuf *);
+ void *user_priv;
+ uint32_t rsvd_kick;
+ uint32_t flags;
+ uint32_t *cur;
+ uint32_t *end;
+};
+
+struct nouveau_pushbuf_refn {
+ struct nouveau_bo *bo;
+ uint32_t flags;
+};
+
+int nouveau_pushbuf_new(struct nouveau_client *, struct nouveau_object *chan,
+ int nr, uint32_t size, bool immediate,
+ struct nouveau_pushbuf **);
+void nouveau_pushbuf_del(struct nouveau_pushbuf **);
+int nouveau_pushbuf_space(struct nouveau_pushbuf *, uint32_t dwords,
+ uint32_t relocs, uint32_t pushes);
+void nouveau_pushbuf_data(struct nouveau_pushbuf *, struct nouveau_bo *,
+ uint64_t offset, uint64_t length);
+int nouveau_pushbuf_refn(struct nouveau_pushbuf *,
+ struct nouveau_pushbuf_refn *, int nr);
+/* Emits a reloc into the push buffer at the current position, you *must*
+ * have previously added the referenced buffer to a buffer context, and
+ * validated it against the current push buffer.
+ */
+void nouveau_pushbuf_reloc(struct nouveau_pushbuf *, struct nouveau_bo *,
+ uint32_t data, uint32_t flags,
+ uint32_t vor, uint32_t tor);
+int nouveau_pushbuf_validate(struct nouveau_pushbuf *);
+uint32_t nouveau_pushbuf_refd(struct nouveau_pushbuf *, struct nouveau_bo *);
+int nouveau_pushbuf_kick(struct nouveau_pushbuf *, struct nouveau_object *chan);
+struct nouveau_bufctx *
+nouveau_pushbuf_bufctx(struct nouveau_pushbuf *, struct nouveau_bufctx *);
+
+#define NOUVEAU_DEVICE_CLASS 0x80000000
+#define NOUVEAU_FIFO_CHANNEL_CLASS 0x80000001
+#define NOUVEAU_NOTIFIER_CLASS 0x80000002
+
+struct nouveau_fifo {
+ struct nouveau_object *object;
+ uint32_t channel;
+ uint32_t pushbuf;
+ uint64_t unused1[3];
+};
+
+struct nv04_fifo {
+ struct nouveau_fifo base;
+ uint32_t vram;
+ uint32_t gart;
+ uint32_t notify;
+};
+
+struct nvc0_fifo {
+ struct nouveau_fifo base;
+ uint32_t notify;
+};
+
+#define NVE0_FIFO_ENGINE_GR 0x00000001
+#define NVE0_FIFO_ENGINE_VP 0x00000002
+#define NVE0_FIFO_ENGINE_PPP 0x00000004
+#define NVE0_FIFO_ENGINE_BSP 0x00000008
+#define NVE0_FIFO_ENGINE_CE0 0x00000010
+#define NVE0_FIFO_ENGINE_CE1 0x00000020
+#define NVE0_FIFO_ENGINE_ENC 0x00000040
+
+struct nve0_fifo {
+ struct {
+ struct nouveau_fifo base;
+ uint32_t notify;
+ };
+ uint32_t engine;
+};
+
+struct nv04_notify {
+ struct nouveau_object *object;
+ uint32_t offset;
+ uint32_t length;
+};
+
+bool
+nouveau_check_dead_channel(struct nouveau_drm *, struct nouveau_object *chan);
+
+#endif
diff --git a/nouveau/nvif/cl0080.h b/nouveau/nvif/cl0080.h
new file mode 100644
index 0000000..331620a
--- /dev/null
+++ b/nouveau/nvif/cl0080.h
@@ -0,0 +1,45 @@
+#ifndef __NVIF_CL0080_H__
+#define __NVIF_CL0080_H__
+
+struct nv_device_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u64 device; /* device identifier, ~0 for client default */
+};
+
+#define NV_DEVICE_V0_INFO 0x00
+#define NV_DEVICE_V0_TIME 0x01
+
+struct nv_device_info_v0 {
+ __u8 version;
+#define NV_DEVICE_INFO_V0_IGP 0x00
+#define NV_DEVICE_INFO_V0_PCI 0x01
+#define NV_DEVICE_INFO_V0_AGP 0x02
+#define NV_DEVICE_INFO_V0_PCIE 0x03
+#define NV_DEVICE_INFO_V0_SOC 0x04
+ __u8 platform;
+ __u16 chipset; /* from NV_PMC_BOOT_0 */
+ __u8 revision; /* from NV_PMC_BOOT_0 */
+#define NV_DEVICE_INFO_V0_TNT 0x01
+#define NV_DEVICE_INFO_V0_CELSIUS 0x02
+#define NV_DEVICE_INFO_V0_KELVIN 0x03
+#define NV_DEVICE_INFO_V0_RANKINE 0x04
+#define NV_DEVICE_INFO_V0_CURIE 0x05
+#define NV_DEVICE_INFO_V0_TESLA 0x06
+#define NV_DEVICE_INFO_V0_FERMI 0x07
+#define NV_DEVICE_INFO_V0_KEPLER 0x08
+#define NV_DEVICE_INFO_V0_MAXWELL 0x09
+ __u8 family;
+ __u8 pad06[2];
+ __u64 ram_size;
+ __u64 ram_user;
+ char chip[16];
+ char name[64];
+};
+
+struct nv_device_time_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u64 time;
+};
+#endif
diff --git a/nouveau/nvif/cl9097.h b/nouveau/nvif/cl9097.h
new file mode 100644
index 0000000..4057676
--- /dev/null
+++ b/nouveau/nvif/cl9097.h
@@ -0,0 +1,44 @@
+#ifndef __NVIF_CL9097_H__
+#define __NVIF_CL9097_H__
+
+#define FERMI_A_ZBC_COLOR 0x00
+#define FERMI_A_ZBC_DEPTH 0x01
+
+struct fermi_a_zbc_color_v0 {
+ __u8 version;
+#define FERMI_A_ZBC_COLOR_V0_FMT_ZERO 0x01
+#define FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE 0x02
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32 0x04
+#define FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16 0x08
+#define FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16 0x0c
+#define FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16 0x10
+#define FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16 0x14
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16 0x16
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8 0x18
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8 0x1c
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10 0x20
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10 0x24
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8 0x28
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8 0x2c
+#define FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8 0x30
+#define FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8 0x34
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8 0x38
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10 0x3c
+#define FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11 0x40
+ __u8 format;
+ __u8 index;
+ __u8 pad03[5];
+ __u32 ds[4];
+ __u32 l2[4];
+};
+
+struct fermi_a_zbc_depth_v0 {
+ __u8 version;
+#define FERMI_A_ZBC_DEPTH_V0_FMT_FP32 0x01
+ __u8 format;
+ __u8 index;
+ __u8 pad03[5];
+ __u32 ds;
+ __u32 l2;
+};
+#endif
diff --git a/nouveau/nvif/class.h b/nouveau/nvif/class.h
new file mode 100644
index 0000000..4179cd6
--- /dev/null
+++ b/nouveau/nvif/class.h
@@ -0,0 +1,141 @@
+#ifndef __NVIF_CLASS_H__
+#define __NVIF_CLASS_H__
+
+/* these class numbers are made up by us, and not nvidia-assigned */
+#define NVIF_CLASS_CONTROL /* if0001.h */ -1
+#define NVIF_CLASS_PERFMON /* if0002.h */ -2
+#define NVIF_CLASS_PERFDOM /* if0003.h */ -3
+#define NVIF_CLASS_SW_NV04 /* if0004.h */ -4
+#define NVIF_CLASS_SW_NV10 /* if0005.h */ -5
+#define NVIF_CLASS_SW_NV50 /* if0005.h */ -6
+#define NVIF_CLASS_SW_GF100 /* if0005.h */ -7
+
+/* the below match nvidia-assigned (either in hw, or sw) class numbers */
+#define NV_DEVICE /* cl0080.h */ 0x00000080
+
+#define NV_DMA_FROM_MEMORY /* cl0002.h */ 0x00000002
+#define NV_DMA_TO_MEMORY /* cl0002.h */ 0x00000003
+#define NV_DMA_IN_MEMORY /* cl0002.h */ 0x0000003d
+
+#define FERMI_TWOD_A 0x0000902d
+
+#define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x00009039
+
+#define KEPLER_INLINE_TO_MEMORY_A 0x0000a040
+#define KEPLER_INLINE_TO_MEMORY_B 0x0000a140
+
+#define NV04_DISP /* cl0046.h */ 0x00000046
+
+#define NV03_CHANNEL_DMA /* cl506b.h */ 0x0000006b
+#define NV10_CHANNEL_DMA /* cl506b.h */ 0x0000006e
+#define NV17_CHANNEL_DMA /* cl506b.h */ 0x0000176e
+#define NV40_CHANNEL_DMA /* cl506b.h */ 0x0000406e
+#define NV50_CHANNEL_DMA /* cl506e.h */ 0x0000506e
+#define G82_CHANNEL_DMA /* cl826e.h */ 0x0000826e
+
+#define NV50_CHANNEL_GPFIFO /* cl506f.h */ 0x0000506f
+#define G82_CHANNEL_GPFIFO /* cl826f.h */ 0x0000826f
+#define FERMI_CHANNEL_GPFIFO /* cl906f.h */ 0x0000906f
+#define KEPLER_CHANNEL_GPFIFO_A /* cla06f.h */ 0x0000a06f
+#define MAXWELL_CHANNEL_GPFIFO_A /* cla06f.h */ 0x0000b06f
+
+#define NV50_DISP /* cl5070.h */ 0x00005070
+#define G82_DISP /* cl5070.h */ 0x00008270
+#define GT200_DISP /* cl5070.h */ 0x00008370
+#define GT214_DISP /* cl5070.h */ 0x00008570
+#define GT206_DISP /* cl5070.h */ 0x00008870
+#define GF110_DISP /* cl5070.h */ 0x00009070
+#define GK104_DISP /* cl5070.h */ 0x00009170
+#define GK110_DISP /* cl5070.h */ 0x00009270
+#define GM107_DISP /* cl5070.h */ 0x00009470
+#define GM204_DISP /* cl5070.h */ 0x00009570
+
+#define NV31_MPEG 0x00003174
+#define G82_MPEG 0x00008274
+
+#define NV74_VP2 0x00007476
+
+#define NV50_DISP_CURSOR /* cl507a.h */ 0x0000507a
+#define G82_DISP_CURSOR /* cl507a.h */ 0x0000827a
+#define GT214_DISP_CURSOR /* cl507a.h */ 0x0000857a
+#define GF110_DISP_CURSOR /* cl507a.h */ 0x0000907a
+#define GK104_DISP_CURSOR /* cl507a.h */ 0x0000917a
+
+#define NV50_DISP_OVERLAY /* cl507b.h */ 0x0000507b
+#define G82_DISP_OVERLAY /* cl507b.h */ 0x0000827b
+#define GT214_DISP_OVERLAY /* cl507b.h */ 0x0000857b
+#define GF110_DISP_OVERLAY /* cl507b.h */ 0x0000907b
+#define GK104_DISP_OVERLAY /* cl507b.h */ 0x0000917b
+
+#define NV50_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000507c
+#define G82_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000827c
+#define GT200_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000837c
+#define GT214_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000857c
+#define GF110_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000907c
+#define GK104_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000917c
+#define GK110_DISP_BASE_CHANNEL_DMA /* cl507c.h */ 0x0000927c
+
+#define NV50_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000507d
+#define G82_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000827d
+#define GT200_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000837d
+#define GT214_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000857d
+#define GT206_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000887d
+#define GF110_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000907d
+#define GK104_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000917d
+#define GK110_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000927d
+#define GM107_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000947d
+#define GM204_DISP_CORE_CHANNEL_DMA /* cl507d.h */ 0x0000957d
+
+#define NV50_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000507e
+#define G82_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000827e
+#define GT200_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000837e
+#define GT214_DISP_OVERLAY_CHANNEL_DMA /* cl507e.h */ 0x0000857e
+#define GF110_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000907e
+#define GK104_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000917e
+
+#define FERMI_A /* cl9097.h */ 0x00009097
+#define FERMI_B /* cl9097.h */ 0x00009197
+#define FERMI_C /* cl9097.h */ 0x00009297
+
+#define KEPLER_A /* cl9097.h */ 0x0000a097
+#define KEPLER_B /* cl9097.h */ 0x0000a197
+#define KEPLER_C /* cl9097.h */ 0x0000a297
+
+#define MAXWELL_A /* cl9097.h */ 0x0000b097
+#define MAXWELL_B /* cl9097.h */ 0x0000b197
+
+#define NV74_BSP 0x000074b0
+
+#define GT212_MSVLD 0x000085b1
+#define IGT21A_MSVLD 0x000086b1
+#define G98_MSVLD 0x000088b1
+#define GF100_MSVLD 0x000090b1
+#define GK104_MSVLD 0x000095b1
+
+#define GT212_MSPDEC 0x000085b2
+#define G98_MSPDEC 0x000088b2
+#define GF100_MSPDEC 0x000090b2
+#define GK104_MSPDEC 0x000095b2
+
+#define GT212_MSPPP 0x000085b3
+#define G98_MSPPP 0x000088b3
+#define GF100_MSPPP 0x000090b3
+
+#define G98_SEC 0x000088b4
+
+#define GT212_DMA 0x000085b5
+#define FERMI_DMA 0x000090b5
+#define KEPLER_DMA_COPY_A 0x0000a0b5
+#define MAXWELL_DMA_COPY_A 0x0000b0b5
+
+#define FERMI_DECOMPRESS 0x000090b8
+
+#define FERMI_COMPUTE_A 0x000090c0
+#define FERMI_COMPUTE_B 0x000091c0
+#define KEPLER_COMPUTE_A 0x0000a0c0
+#define KEPLER_COMPUTE_B 0x0000a1c0
+#define MAXWELL_COMPUTE_A 0x0000b0c0
+#define MAXWELL_COMPUTE_B 0x0000b1c0
+
+#define NV74_CIPHER 0x000074c1
+#endif
diff --git a/nouveau/nvif/if0002.h b/nouveau/nvif/if0002.h
new file mode 100644
index 0000000..c04c91d
--- /dev/null
+++ b/nouveau/nvif/if0002.h
@@ -0,0 +1,38 @@
+#ifndef __NVIF_IF0002_H__
+#define __NVIF_IF0002_H__
+
+#define NVIF_PERFMON_V0_QUERY_DOMAIN 0x00
+#define NVIF_PERFMON_V0_QUERY_SIGNAL 0x01
+#define NVIF_PERFMON_V0_QUERY_SOURCE 0x02
+
+struct nvif_perfmon_query_domain_v0 {
+ __u8 version;
+ __u8 id;
+ __u8 counter_nr;
+ __u8 iter;
+ __u16 signal_nr;
+ __u8 pad05[2];
+ char name[64];
+};
+
+struct nvif_perfmon_query_signal_v0 {
+ __u8 version;
+ __u8 domain;
+ __u16 iter;
+ __u8 signal;
+ __u8 source_nr;
+ __u8 pad05[2];
+ char name[64];
+};
+
+struct nvif_perfmon_query_source_v0 {
+ __u8 version;
+ __u8 domain;
+ __u8 signal;
+ __u8 iter;
+ __u8 pad04[4];
+ __u32 source;
+ __u32 mask;
+ char name[64];
+};
+#endif
diff --git a/nouveau/nvif/if0003.h b/nouveau/nvif/if0003.h
new file mode 100644
index 0000000..0cd03ef
--- /dev/null
+++ b/nouveau/nvif/if0003.h
@@ -0,0 +1,33 @@
+#ifndef __NVIF_IF0003_H__
+#define __NVIF_IF0003_H__
+
+struct nvif_perfdom_v0 {
+ __u8 version;
+ __u8 domain;
+ __u8 mode;
+ __u8 pad03[1];
+ struct {
+ __u8 signal[4];
+ __u64 source[4][8];
+ __u16 logic_op;
+ } ctr[4];
+};
+
+#define NVIF_PERFDOM_V0_INIT 0x00
+#define NVIF_PERFDOM_V0_SAMPLE 0x01
+#define NVIF_PERFDOM_V0_READ 0x02
+
+struct nvif_perfdom_init {
+};
+
+struct nvif_perfdom_sample {
+};
+
+struct nvif_perfdom_read_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u32 ctr[4];
+ __u32 clk;
+ __u8 pad04[4];
+};
+#endif
diff --git a/nouveau/nvif/ioctl.h b/nouveau/nvif/ioctl.h
new file mode 100644
index 0000000..c5f5eb8
--- /dev/null
+++ b/nouveau/nvif/ioctl.h
@@ -0,0 +1,132 @@
+#ifndef __NVIF_IOCTL_H__
+#define __NVIF_IOCTL_H__
+
+#define NVIF_VERSION_LATEST 0x0000000000000000ULL
+
+struct nvif_ioctl_v0 {
+ __u8 version;
+#define NVIF_IOCTL_V0_NOP 0x00
+#define NVIF_IOCTL_V0_SCLASS 0x01
+#define NVIF_IOCTL_V0_NEW 0x02
+#define NVIF_IOCTL_V0_DEL 0x03
+#define NVIF_IOCTL_V0_MTHD 0x04
+#define NVIF_IOCTL_V0_RD 0x05
+#define NVIF_IOCTL_V0_WR 0x06
+#define NVIF_IOCTL_V0_MAP 0x07
+#define NVIF_IOCTL_V0_UNMAP 0x08
+#define NVIF_IOCTL_V0_NTFY_NEW 0x09
+#define NVIF_IOCTL_V0_NTFY_DEL 0x0a
+#define NVIF_IOCTL_V0_NTFY_GET 0x0b
+#define NVIF_IOCTL_V0_NTFY_PUT 0x0c
+ __u8 type;
+ __u8 pad02[4];
+#define NVIF_IOCTL_V0_OWNER_NVIF 0x00
+#define NVIF_IOCTL_V0_OWNER_ANY 0xff
+ __u8 owner;
+#define NVIF_IOCTL_V0_ROUTE_NVIF 0x00
+#define NVIF_IOCTL_V0_ROUTE_HIDDEN 0xff
+ __u8 route;
+ __u64 token;
+ __u64 object;
+ __u8 data[]; /* ioctl data (below) */
+};
+
+struct nvif_ioctl_nop_v0 {
+ __u64 version;
+};
+
+struct nvif_ioctl_sclass_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 count;
+ __u8 pad02[6];
+ struct nvif_ioctl_sclass_oclass_v0 {
+ __s32 oclass;
+ __s16 minver;
+ __s16 maxver;
+ } oclass[];
+};
+
+struct nvif_ioctl_new_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 pad01[6];
+ __u8 route;
+ __u64 token;
+ __u64 object;
+ __u32 handle;
+ __s32 oclass;
+ __u8 data[]; /* class data (class.h) */
+};
+
+struct nvif_ioctl_del {
+};
+
+struct nvif_ioctl_rd_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 size;
+ __u8 pad02[2];
+ __u32 data;
+ __u64 addr;
+};
+
+struct nvif_ioctl_wr_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 size;
+ __u8 pad02[2];
+ __u32 data;
+ __u64 addr;
+};
+
+struct nvif_ioctl_map_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 pad01[3];
+ __u32 length;
+ __u64 handle;
+};
+
+struct nvif_ioctl_unmap {
+};
+
+struct nvif_ioctl_ntfy_new_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 event;
+ __u8 index;
+ __u8 pad03[5];
+ __u8 data[]; /* event request data (event.h) */
+};
+
+struct nvif_ioctl_ntfy_del_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_ntfy_get_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_ntfy_put_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_mthd_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 method;
+ __u8 pad02[6];
+ __u8 data[]; /* method data (class.h) */
+};
+
+#endif
diff --git a/nouveau/nvif/unpack.h b/nouveau/nvif/unpack.h
new file mode 100644
index 0000000..751bcf4
--- /dev/null
+++ b/nouveau/nvif/unpack.h
@@ -0,0 +1,28 @@
+#ifndef __NVIF_UNPACK_H__
+#define __NVIF_UNPACK_H__
+
+#define nvif_unvers(r,d,s,m) ({ \
+ void **_data = (d); __u32 *_size = (s); int _ret = (r); \
+ if (_ret == -ENOSYS && *_size == sizeof(m)) { \
+ *_data = NULL; \
+ *_size = _ret = 0; \
+ } \
+ _ret; \
+})
+
+#define nvif_unpack(r,d,s,m,vl,vh,x) ({ \
+ void **_data = (d); __u32 *_size = (s); \
+ int _ret = (r), _vl = (vl), _vh = (vh); \
+ if (_ret == -ENOSYS && *_size >= sizeof(m) && \
+ (m).version >= _vl && (m).version <= _vh) { \
+ *_data = (__u8 *)*_data + sizeof(m); \
+ *_size = *_size - sizeof(m); \
+ if (_ret = 0, !(x)) { \
+ _ret = *_size ? -E2BIG : 0; \
+ *_data = NULL; \
+ *_size = 0; \
+ } \
+ } \
+ _ret; \
+})
+#endif
diff --git a/nouveau/private.h b/nouveau/private.h
new file mode 100644
index 0000000..b81d4b1
--- /dev/null
+++ b/nouveau/private.h
@@ -0,0 +1,127 @@
+#ifndef __NOUVEAU_LIBDRM_PRIVATE_H__
+#define __NOUVEAU_LIBDRM_PRIVATE_H__
+
+#include <stdio.h>
+
+#include <libdrm_macros.h>
+#include <xf86drm.h>
+#include <xf86atomic.h>
+#include <pthread.h>
+#include "nouveau_drm.h"
+
+#include "nouveau.h"
+
+/*
+ * 0x00000001 dump all pushbuffers
+ * 0x00000002 submit pushbuffers synchronously
+ * 0x80000000 if compiled with SIMULATE return -EINVAL for all pb submissions
+ */
+drm_private extern uint32_t nouveau_debug;
+drm_private extern FILE *nouveau_out;
+#define dbg_on(lvl) (nouveau_debug & (1 << lvl))
+#define dbg(lvl, fmt, args...) do { \
+ if (dbg_on((lvl))) \
+ fprintf(nouveau_out, "nouveau: "fmt, ##args); \
+} while(0)
+#define err(fmt, args...) fprintf(nouveau_out, "nouveau: "fmt, ##args)
+
+struct nouveau_client_kref {
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct nouveau_pushbuf *push;
+};
+
+struct nouveau_client_priv {
+ struct nouveau_client base;
+ struct nouveau_client_kref *kref;
+ unsigned kref_nr;
+};
+
+static inline struct nouveau_client_priv *
+nouveau_client(struct nouveau_client *client)
+{
+ return (struct nouveau_client_priv *)client;
+}
+
+static inline struct drm_nouveau_gem_pushbuf_bo *
+cli_kref_get(struct nouveau_client *client, struct nouveau_bo *bo)
+{
+ struct nouveau_client_priv *pcli = nouveau_client(client);
+ struct drm_nouveau_gem_pushbuf_bo *kref = NULL;
+ if (pcli->kref_nr > bo->handle)
+ kref = pcli->kref[bo->handle].kref;
+ return kref;
+}
+
+static inline struct nouveau_pushbuf *
+cli_push_get(struct nouveau_client *client, struct nouveau_bo *bo)
+{
+ struct nouveau_client_priv *pcli = nouveau_client(client);
+ struct nouveau_pushbuf *push = NULL;
+ if (pcli->kref_nr > bo->handle)
+ push = pcli->kref[bo->handle].push;
+ return push;
+}
+
+static inline void
+cli_kref_set(struct nouveau_client *client, struct nouveau_bo *bo,
+ struct drm_nouveau_gem_pushbuf_bo *kref,
+ struct nouveau_pushbuf *push)
+{
+ struct nouveau_client_priv *pcli = nouveau_client(client);
+ if (pcli->kref_nr <= bo->handle) {
+ pcli->kref = realloc(pcli->kref,
+ sizeof(*pcli->kref) * bo->handle * 2);
+ while (pcli->kref_nr < bo->handle * 2) {
+ pcli->kref[pcli->kref_nr].kref = NULL;
+ pcli->kref[pcli->kref_nr].push = NULL;
+ pcli->kref_nr++;
+ }
+ }
+ pcli->kref[bo->handle].kref = kref;
+ pcli->kref[bo->handle].push = push;
+}
+
+struct nouveau_bo_priv {
+ struct nouveau_bo base;
+ struct nouveau_list head;
+ atomic_t refcnt;
+ uint64_t map_handle;
+ uint32_t name;
+ uint32_t access;
+};
+
+static inline struct nouveau_bo_priv *
+nouveau_bo(struct nouveau_bo *bo)
+{
+ return (struct nouveau_bo_priv *)bo;
+}
+
+struct nouveau_device_priv {
+ struct nouveau_device base;
+ int close;
+ pthread_mutex_t lock;
+ struct nouveau_list bo_list;
+ uint32_t *client;
+ int nr_client;
+ bool have_bo_usage;
+ int gart_limit_percent, vram_limit_percent;
+};
+
+static inline struct nouveau_device_priv *
+nouveau_device(struct nouveau_device *dev)
+{
+ return (struct nouveau_device_priv *)dev;
+}
+
+int
+nouveau_device_open_existing(struct nouveau_device **, int, int, drm_context_t);
+
+/* abi16.c */
+drm_private bool abi16_object(struct nouveau_object *, int (**)(struct nouveau_object *));
+drm_private void abi16_delete(struct nouveau_object *);
+drm_private int abi16_sclass(struct nouveau_object *, struct nouveau_sclass **);
+drm_private void abi16_bo_info(struct nouveau_bo *, struct drm_nouveau_gem_info *);
+drm_private int abi16_bo_init(struct nouveau_bo *, uint32_t alignment,
+ union nouveau_bo_config *);
+
+#endif
diff --git a/nouveau/pushbuf.c b/nouveau/pushbuf.c
new file mode 100644
index 0000000..5d54f21
--- /dev/null
+++ b/nouveau/pushbuf.c
@@ -0,0 +1,800 @@
+/*
+ * 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <xf86drm.h>
+#include <xf86atomic.h>
+#include "libdrm_lists.h"
+#include "nouveau_drm.h"
+
+#include "nouveau.h"
+#include "private.h"
+
+struct nouveau_pushbuf_krec {
+ struct nouveau_pushbuf_krec *next;
+ struct drm_nouveau_gem_pushbuf_bo buffer[NOUVEAU_GEM_MAX_BUFFERS];
+ struct drm_nouveau_gem_pushbuf_reloc reloc[NOUVEAU_GEM_MAX_RELOCS];
+ struct drm_nouveau_gem_pushbuf_push push[NOUVEAU_GEM_MAX_PUSH];
+ int nr_buffer;
+ int nr_reloc;
+ int nr_push;
+ uint64_t vram_used;
+ uint64_t gart_used;
+};
+
+struct nouveau_pushbuf_priv {
+ struct nouveau_pushbuf base;
+ struct nouveau_pushbuf_krec *list;
+ struct nouveau_pushbuf_krec *krec;
+ struct nouveau_list bctx_list;
+ struct nouveau_bo *bo;
+ uint32_t type;
+ uint32_t suffix0;
+ uint32_t suffix1;
+ uint32_t *ptr;
+ uint32_t *bgn;
+ int bo_next;
+ int bo_nr;
+ struct nouveau_bo *bos[];
+};
+
+static inline struct nouveau_pushbuf_priv *
+nouveau_pushbuf(struct nouveau_pushbuf *push)
+{
+ return (struct nouveau_pushbuf_priv *)push;
+}
+
+static int pushbuf_validate(struct nouveau_pushbuf *, bool);
+static int pushbuf_flush(struct nouveau_pushbuf *);
+
+static bool
+pushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
+ uint32_t *domains)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct nouveau_device *dev = push->client->device;
+ struct nouveau_bo *kbo;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ int i;
+
+ /* VRAM is the only valid domain. GART and VRAM|GART buffers
+ * are all accounted to GART, so if this doesn't fit in VRAM
+ * straight up, a flush is needed.
+ */
+ if (*domains == NOUVEAU_GEM_DOMAIN_VRAM) {
+ if (krec->vram_used + bo->size > dev->vram_limit)
+ return false;
+ krec->vram_used += bo->size;
+ return true;
+ }
+
+ /* GART or VRAM|GART buffer. Account both of these buffer types
+ * to GART only for the moment, which simplifies things. If the
+ * buffer can fit already, we're done here.
+ */
+ if (krec->gart_used + bo->size <= dev->gart_limit) {
+ krec->gart_used += bo->size;
+ return true;
+ }
+
+ /* Ran out of GART space, if it's a VRAM|GART buffer and it'll
+ * fit into available VRAM, turn it into a VRAM buffer
+ */
+ if ((*domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+ krec->vram_used + bo->size <= dev->vram_limit) {
+ *domains &= NOUVEAU_GEM_DOMAIN_VRAM;
+ krec->vram_used += bo->size;
+ return true;
+ }
+
+ /* Still couldn't fit the buffer in anywhere, so as a last resort;
+ * scan the buffer list for VRAM|GART buffers and turn them into
+ * VRAM buffers until we have enough space in GART for this one
+ */
+ kref = krec->buffer;
+ for (i = 0; i < krec->nr_buffer; i++, kref++) {
+ if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
+ continue;
+
+ kbo = (void *)(unsigned long)kref->user_priv;
+ if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) ||
+ krec->vram_used + kbo->size > dev->vram_limit)
+ continue;
+
+ kref->valid_domains &= NOUVEAU_GEM_DOMAIN_VRAM;
+ krec->gart_used -= kbo->size;
+ krec->vram_used += kbo->size;
+ if (krec->gart_used + bo->size <= dev->gart_limit) {
+ krec->gart_used += bo->size;
+ return true;
+ }
+ }
+
+ /* Couldn't resolve a placement, need to force a flush */
+ return false;
+}
+
+static struct drm_nouveau_gem_pushbuf_bo *
+pushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
+ uint32_t flags)
+{
+ struct nouveau_device *dev = push->client->device;
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct nouveau_pushbuf *fpush;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ uint32_t domains, domains_wr, domains_rd;
+
+ domains = 0;
+ if (flags & NOUVEAU_BO_VRAM)
+ domains |= NOUVEAU_GEM_DOMAIN_VRAM;
+ if (flags & NOUVEAU_BO_GART)
+ domains |= NOUVEAU_GEM_DOMAIN_GART;
+ domains_wr = domains * !!(flags & NOUVEAU_BO_WR);
+ domains_rd = domains * !!(flags & NOUVEAU_BO_RD);
+
+ /* if buffer is referenced on another pushbuf that is owned by the
+ * same client, we need to flush the other pushbuf first to ensure
+ * the correct ordering of commands
+ */
+ fpush = cli_push_get(push->client, bo);
+ if (fpush && fpush != push)
+ pushbuf_flush(fpush);
+
+ kref = cli_kref_get(push->client, bo);
+ if (kref) {
+ /* possible conflict in memory types - flush and retry */
+ if (!(kref->valid_domains & domains))
+ return NULL;
+
+ /* VRAM|GART buffer turning into a VRAM buffer. Make sure
+ * it'll fit in VRAM and force a flush if not.
+ */
+ if ((kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+ ( domains == NOUVEAU_GEM_DOMAIN_VRAM)) {
+ if (krec->vram_used + bo->size > dev->vram_limit)
+ return NULL;
+ krec->vram_used += bo->size;
+ krec->gart_used -= bo->size;
+ }
+
+ kref->valid_domains &= domains;
+ kref->write_domains |= domains_wr;
+ kref->read_domains |= domains_rd;
+ } else {
+ if (krec->nr_buffer == NOUVEAU_GEM_MAX_BUFFERS ||
+ !pushbuf_kref_fits(push, bo, &domains))
+ return NULL;
+
+ kref = &krec->buffer[krec->nr_buffer++];
+ kref->user_priv = (unsigned long)bo;
+ kref->handle = bo->handle;
+ kref->valid_domains = domains;
+ kref->write_domains = domains_wr;
+ kref->read_domains = domains_rd;
+ kref->presumed.valid = 1;
+ kref->presumed.offset = bo->offset;
+ if (bo->flags & NOUVEAU_BO_VRAM)
+ kref->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
+ else
+ kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
+
+ cli_kref_set(push->client, bo, kref, push);
+ atomic_inc(&nouveau_bo(bo)->refcnt);
+ }
+
+ return kref;
+}
+
+static uint32_t
+pushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
+ uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_reloc *krel;
+ struct drm_nouveau_gem_pushbuf_bo *pkref;
+ struct drm_nouveau_gem_pushbuf_bo *bkref;
+ uint32_t reloc = data;
+
+ pkref = cli_kref_get(push->client, nvpb->bo);
+ bkref = cli_kref_get(push->client, bo);
+ krel = &krec->reloc[krec->nr_reloc++];
+
+ assert(pkref);
+ assert(bkref);
+ krel->reloc_bo_index = pkref - krec->buffer;
+ krel->reloc_bo_offset = (push->cur - nvpb->ptr) * 4;
+ krel->bo_index = bkref - krec->buffer;
+ krel->flags = 0;
+ krel->data = data;
+ krel->vor = vor;
+ krel->tor = tor;
+
+ if (flags & NOUVEAU_BO_LOW) {
+ reloc = (bkref->presumed.offset + data);
+ krel->flags |= NOUVEAU_GEM_RELOC_LOW;
+ } else
+ if (flags & NOUVEAU_BO_HIGH) {
+ reloc = (bkref->presumed.offset + data) >> 32;
+ krel->flags |= NOUVEAU_GEM_RELOC_HIGH;
+ }
+ if (flags & NOUVEAU_BO_OR) {
+ if (bkref->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM)
+ reloc |= vor;
+ else
+ reloc |= tor;
+ krel->flags |= NOUVEAU_GEM_RELOC_OR;
+ }
+
+ return reloc;
+}
+
+static void
+pushbuf_dump(struct nouveau_pushbuf_krec *krec, int krec_id, int chid)
+{
+ struct drm_nouveau_gem_pushbuf_reloc *krel;
+ struct drm_nouveau_gem_pushbuf_push *kpsh;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct nouveau_bo *bo;
+ uint32_t *bgn, *end;
+ int i;
+
+ err("ch%d: krec %d pushes %d bufs %d relocs %d\n", chid,
+ krec_id, krec->nr_push, krec->nr_buffer, krec->nr_reloc);
+
+ kref = krec->buffer;
+ for (i = 0; i < krec->nr_buffer; i++, kref++) {
+ bo = (void *)(uintptr_t)kref->user_priv;
+ err("ch%d: buf %08x %08x %08x %08x %08x %p 0x%"PRIx64" 0x%"PRIx64"\n", chid, i,
+ kref->handle, kref->valid_domains,
+ kref->read_domains, kref->write_domains, bo->map, bo->offset, bo->size);
+ }
+
+ krel = krec->reloc;
+ for (i = 0; i < krec->nr_reloc; i++, krel++) {
+ err("ch%d: rel %08x %08x %08x %08x %08x %08x %08x\n",
+ chid, krel->reloc_bo_index, krel->reloc_bo_offset,
+ krel->bo_index, krel->flags, krel->data,
+ krel->vor, krel->tor);
+ }
+
+ kpsh = krec->push;
+ for (i = 0; i < krec->nr_push; i++, kpsh++) {
+ kref = krec->buffer + kpsh->bo_index;
+ bo = (void *)(unsigned long)kref->user_priv;
+ bgn = (uint32_t *)((char *)bo->map + kpsh->offset);
+ end = bgn + ((kpsh->length & 0x7fffff) /4);
+
+ err("ch%d: psh %s%08x %010llx %010llx\n", chid,
+ bo->map ? "" : "(unmapped) ", kpsh->bo_index,
+ (unsigned long long)kpsh->offset,
+ (unsigned long long)(kpsh->offset + kpsh->length));
+ if (!bo->map)
+ continue;
+ while (bgn < end)
+ err("\t0x%08x\n", *bgn++);
+ }
+}
+
+static int
+pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->list;
+ struct nouveau_device *dev = push->client->device;
+ struct nouveau_drm *drm = nouveau_drm(&dev->object);
+ struct drm_nouveau_gem_pushbuf_bo_presumed *info;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct drm_nouveau_gem_pushbuf req;
+ struct nouveau_fifo *fifo = chan->data;
+ struct nouveau_bo *bo;
+ int krec_id = 0;
+ int ret = 0, i;
+
+ if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS)
+ return -EINVAL;
+
+ if (push->kick_notify)
+ push->kick_notify(push);
+
+ nouveau_pushbuf_data(push, NULL, 0, 0);
+
+ while (krec && krec->nr_push) {
+ req.channel = fifo->channel;
+ req.nr_buffers = krec->nr_buffer;
+ req.buffers = (uint64_t)(unsigned long)krec->buffer;
+ req.nr_relocs = krec->nr_reloc;
+ req.nr_push = krec->nr_push;
+ req.relocs = (uint64_t)(unsigned long)krec->reloc;
+ req.push = (uint64_t)(unsigned long)krec->push;
+ req.suffix0 = nvpb->suffix0;
+ req.suffix1 = nvpb->suffix1;
+ req.vram_available = 0; /* for valgrind */
+ if (dbg_on(1))
+ req.vram_available |= NOUVEAU_GEM_PUSHBUF_SYNC;
+ req.gart_available = 0;
+
+ if (dbg_on(0))
+ pushbuf_dump(krec, krec_id++, fifo->channel);
+
+#ifndef SIMULATE
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
+ &req, sizeof(req));
+ nvpb->suffix0 = req.suffix0;
+ nvpb->suffix1 = req.suffix1;
+ dev->vram_limit = (req.vram_available *
+ nouveau_device(dev)->vram_limit_percent) / 100;
+ dev->gart_limit = (req.gart_available *
+ nouveau_device(dev)->gart_limit_percent) / 100;
+#else
+ if (dbg_on(31))
+ ret = -EINVAL;
+#endif
+
+ if (ret) {
+ err("kernel rejected pushbuf: %s\n", strerror(-ret));
+ pushbuf_dump(krec, krec_id++, fifo->channel);
+ break;
+ }
+
+ kref = krec->buffer;
+ for (i = 0; i < krec->nr_buffer; i++, kref++) {
+ bo = (void *)(unsigned long)kref->user_priv;
+
+ info = &kref->presumed;
+ if (!info->valid) {
+ bo->flags &= ~NOUVEAU_BO_APER;
+ if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM)
+ bo->flags |= NOUVEAU_BO_VRAM;
+ else
+ bo->flags |= NOUVEAU_BO_GART;
+ bo->offset = info->offset;
+ }
+
+ if (kref->write_domains)
+ nouveau_bo(bo)->access |= NOUVEAU_BO_WR;
+ if (kref->read_domains)
+ nouveau_bo(bo)->access |= NOUVEAU_BO_RD;
+ }
+
+ krec = krec->next;
+ }
+
+ return ret;
+}
+
+static int
+pushbuf_flush(struct nouveau_pushbuf *push)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct nouveau_bufctx *bctx, *btmp;
+ struct nouveau_bo *bo;
+ int ret = 0, i;
+
+ if (push->channel) {
+ ret = pushbuf_submit(push, push->channel);
+ } else {
+ nouveau_pushbuf_data(push, NULL, 0, 0);
+ krec->next = malloc(sizeof(*krec));
+ nvpb->krec = krec->next;
+ }
+
+ kref = krec->buffer;
+ for (i = 0; i < krec->nr_buffer; i++, kref++) {
+ bo = (void *)(unsigned long)kref->user_priv;
+ cli_kref_set(push->client, bo, NULL, NULL);
+ if (push->channel)
+ nouveau_bo_ref(NULL, &bo);
+ }
+
+ krec = nvpb->krec;
+ krec->vram_used = 0;
+ krec->gart_used = 0;
+ krec->nr_buffer = 0;
+ krec->nr_reloc = 0;
+ krec->nr_push = 0;
+
+ DRMLISTFOREACHENTRYSAFE(bctx, btmp, &nvpb->bctx_list, head) {
+ DRMLISTJOIN(&bctx->current, &bctx->pending);
+ DRMINITLISTHEAD(&bctx->current);
+ DRMLISTDELINIT(&bctx->head);
+ }
+
+ return ret;
+}
+
+static void
+pushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+
+ kref = krec->buffer + sref;
+ while (krec->nr_buffer-- > sref) {
+ struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv;
+ cli_kref_set(push->client, bo, NULL, NULL);
+ nouveau_bo_ref(NULL, &bo);
+ kref++;
+ }
+ krec->nr_buffer = sref;
+ krec->nr_reloc = srel;
+}
+
+static int
+pushbuf_refn(struct nouveau_pushbuf *push, bool retry,
+ struct nouveau_pushbuf_refn *refs, int nr)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ int sref = krec->nr_buffer;
+ int ret = 0, i;
+
+ for (i = 0; i < nr; i++) {
+ kref = pushbuf_kref(push, refs[i].bo, refs[i].flags);
+ if (!kref) {
+ ret = -ENOSPC;
+ break;
+ }
+ }
+
+ if (ret) {
+ pushbuf_refn_fail(push, sref, krec->nr_reloc);
+ if (retry) {
+ pushbuf_flush(push);
+ nouveau_pushbuf_space(push, 0, 0, 0);
+ return pushbuf_refn(push, false, refs, nr);
+ }
+ }
+
+ return ret;
+}
+
+static int
+pushbuf_validate(struct nouveau_pushbuf *push, bool retry)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct nouveau_bufctx *bctx = push->bufctx;
+ struct nouveau_bufref *bref;
+ int relocs = bctx ? bctx->relocs * 2: 0;
+ int sref, srel, ret;
+
+ ret = nouveau_pushbuf_space(push, relocs, relocs, 0);
+ if (ret || bctx == NULL)
+ return ret;
+
+ sref = krec->nr_buffer;
+ srel = krec->nr_reloc;
+
+ DRMLISTDEL(&bctx->head);
+ DRMLISTADD(&bctx->head, &nvpb->bctx_list);
+
+ DRMLISTFOREACHENTRY(bref, &bctx->pending, thead) {
+ kref = pushbuf_kref(push, bref->bo, bref->flags);
+ if (!kref) {
+ ret = -ENOSPC;
+ break;
+ }
+
+ if (bref->packet) {
+ pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0);
+ *push->cur++ = 0;
+ pushbuf_krel(push, bref->bo, bref->data, bref->flags,
+ bref->vor, bref->tor);
+ *push->cur++ = 0;
+ }
+ }
+
+ DRMLISTJOIN(&bctx->pending, &bctx->current);
+ DRMINITLISTHEAD(&bctx->pending);
+
+ if (ret) {
+ pushbuf_refn_fail(push, sref, srel);
+ if (retry) {
+ pushbuf_flush(push);
+ return pushbuf_validate(push, false);
+ }
+ }
+
+ return ret;
+}
+
+drm_public int
+nouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan,
+ int nr, uint32_t size, bool immediate,
+ struct nouveau_pushbuf **ppush)
+{
+ struct nouveau_drm *drm = nouveau_drm(&client->device->object);
+ struct nouveau_fifo *fifo = chan->data;
+ struct nouveau_pushbuf_priv *nvpb;
+ struct nouveau_pushbuf *push;
+ struct drm_nouveau_gem_pushbuf req = {};
+ int ret;
+
+ if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS)
+ return -EINVAL;
+
+ /* nop pushbuf call, to get the current "return to main" sequence
+ * we need to append to the pushbuf on early chipsets
+ */
+ req.channel = fifo->channel;
+ req.nr_push = 0;
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos));
+ if (!nvpb)
+ return -ENOMEM;
+
+#ifndef SIMULATE
+ nvpb->suffix0 = req.suffix0;
+ nvpb->suffix1 = req.suffix1;
+#else
+ nvpb->suffix0 = 0xffffffff;
+ nvpb->suffix1 = 0xffffffff;
+#endif
+ nvpb->krec = calloc(1, sizeof(*nvpb->krec));
+ nvpb->list = nvpb->krec;
+ if (!nvpb->krec) {
+ free(nvpb);
+ return -ENOMEM;
+ }
+
+ push = &nvpb->base;
+ push->client = client;
+ push->channel = immediate ? chan : NULL;
+ push->flags = NOUVEAU_BO_RD;
+ if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) {
+ push->flags |= NOUVEAU_BO_GART;
+ nvpb->type = NOUVEAU_BO_GART;
+ } else
+ if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) {
+ push->flags |= NOUVEAU_BO_VRAM;
+ nvpb->type = NOUVEAU_BO_VRAM;
+ }
+ nvpb->type |= NOUVEAU_BO_MAP;
+
+ for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) {
+ ret = nouveau_bo_new(client->device, nvpb->type, 0, size,
+ NULL, &nvpb->bos[nvpb->bo_nr]);
+ if (ret) {
+ nouveau_pushbuf_del(&push);
+ return ret;
+ }
+ }
+
+ DRMINITLISTHEAD(&nvpb->bctx_list);
+ *ppush = push;
+ return 0;
+}
+
+drm_public void
+nouveau_pushbuf_del(struct nouveau_pushbuf **ppush)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush);
+ if (nvpb) {
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ struct nouveau_pushbuf_krec *krec;
+ while ((krec = nvpb->list)) {
+ kref = krec->buffer;
+ while (krec->nr_buffer--) {
+ unsigned long priv = kref++->user_priv;
+ struct nouveau_bo *bo = (void *)priv;
+ cli_kref_set(nvpb->base.client, bo, NULL, NULL);
+ nouveau_bo_ref(NULL, &bo);
+ }
+ nvpb->list = krec->next;
+ free(krec);
+ }
+ while (nvpb->bo_nr--)
+ nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]);
+ nouveau_bo_ref(NULL, &nvpb->bo);
+ free(nvpb);
+ }
+ *ppush = NULL;
+}
+
+drm_public struct nouveau_bufctx *
+nouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx)
+{
+ struct nouveau_bufctx *prev = push->bufctx;
+ push->bufctx = ctx;
+ return prev;
+}
+
+drm_public int
+nouveau_pushbuf_space(struct nouveau_pushbuf *push,
+ uint32_t dwords, uint32_t relocs, uint32_t pushes)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct nouveau_client *client = push->client;
+ struct nouveau_bo *bo = NULL;
+ bool flushed = false;
+ int ret = 0;
+
+ /* switch to next buffer if insufficient space in the current one */
+ if (push->cur + dwords >= push->end) {
+ if (nvpb->bo_next < nvpb->bo_nr) {
+ nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo);
+ if (nvpb->bo_next == nvpb->bo_nr && push->channel)
+ nvpb->bo_next = 0;
+ } else {
+ ret = nouveau_bo_new(client->device, nvpb->type, 0,
+ nvpb->bos[0]->size, NULL, &bo);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* make sure there's always enough space to queue up the pending
+ * data in the pushbuf proper
+ */
+ pushes++;
+
+ /* need to flush if we've run out of space on an immediate pushbuf,
+ * if the new buffer won't fit, or if the kernel push/reloc limits
+ * have been hit
+ */
+ if ((bo && ( push->channel ||
+ !pushbuf_kref(push, bo, push->flags))) ||
+ krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS ||
+ krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) {
+ if (nvpb->bo && krec->nr_buffer)
+ pushbuf_flush(push);
+ flushed = true;
+ }
+
+ /* if necessary, switch to new buffer */
+ if (bo) {
+ ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client);
+ if (ret)
+ return ret;
+
+ nouveau_pushbuf_data(push, NULL, 0, 0);
+ nouveau_bo_ref(bo, &nvpb->bo);
+ nouveau_bo_ref(NULL, &bo);
+
+ nvpb->bgn = nvpb->bo->map;
+ nvpb->ptr = nvpb->bgn;
+ push->cur = nvpb->bgn;
+ push->end = push->cur + (nvpb->bo->size / 4);
+ push->end -= 2 + push->rsvd_kick; /* space for suffix */
+ }
+
+ pushbuf_kref(push, nvpb->bo, push->flags);
+ return flushed ? pushbuf_validate(push, false) : 0;
+}
+
+drm_public void
+nouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
+ uint64_t offset, uint64_t length)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
+ struct nouveau_pushbuf_krec *krec = nvpb->krec;
+ struct drm_nouveau_gem_pushbuf_push *kpsh;
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+
+ if (bo != nvpb->bo && nvpb->bgn != push->cur) {
+ if (nvpb->suffix0 || nvpb->suffix1) {
+ *push->cur++ = nvpb->suffix0;
+ *push->cur++ = nvpb->suffix1;
+ }
+
+ nouveau_pushbuf_data(push, nvpb->bo,
+ (nvpb->bgn - nvpb->ptr) * 4,
+ (push->cur - nvpb->bgn) * 4);
+ nvpb->bgn = push->cur;
+ }
+
+ if (bo) {
+ kref = cli_kref_get(push->client, bo);
+ assert(kref);
+ kpsh = &krec->push[krec->nr_push++];
+ kpsh->bo_index = kref - krec->buffer;
+ kpsh->offset = offset;
+ kpsh->length = length;
+ }
+}
+
+drm_public int
+nouveau_pushbuf_refn(struct nouveau_pushbuf *push,
+ struct nouveau_pushbuf_refn *refs, int nr)
+{
+ return pushbuf_refn(push, true, refs, nr);
+}
+
+drm_public void
+nouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
+ uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor)
+{
+ *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor);
+ push->cur++;
+}
+
+drm_public int
+nouveau_pushbuf_validate(struct nouveau_pushbuf *push)
+{
+ return pushbuf_validate(push, true);
+}
+
+drm_public uint32_t
+nouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo)
+{
+ struct drm_nouveau_gem_pushbuf_bo *kref;
+ uint32_t flags = 0;
+
+ if (cli_push_get(push->client, bo) == push) {
+ kref = cli_kref_get(push->client, bo);
+ assert(kref);
+ if (kref->read_domains)
+ flags |= NOUVEAU_BO_RD;
+ if (kref->write_domains)
+ flags |= NOUVEAU_BO_WR;
+ }
+
+ return flags;
+}
+
+drm_public int
+nouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan)
+{
+ if (!push->channel)
+ return pushbuf_submit(push, chan);
+ pushbuf_flush(push);
+ return pushbuf_validate(push, false);
+}
+
+drm_public bool
+nouveau_check_dead_channel(struct nouveau_drm *drm, struct nouveau_object *chan)
+{
+ struct drm_nouveau_gem_pushbuf req = {};
+ struct nouveau_fifo *fifo = chan->data;
+ int ret;
+
+ req.channel = fifo->channel;
+ req.nr_push = 0;
+
+ ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
+ &req, sizeof(req));
+ /* nouveau returns ENODEV once the channel was killed */
+ return ret == -ENODEV;
+}