summaryrefslogtreecommitdiff
path: root/src/gallium/winsys/drm/nouveau
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/winsys/drm/nouveau')
-rw-r--r--src/gallium/winsys/drm/nouveau/Makefile45
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_bo.c470
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_channel.c126
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_context.c346
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_context.h113
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_device.c159
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_dma.c219
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_dma.h143
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_dri.h28
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_drmif.h310
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_fence.c214
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_grobj.c107
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_local.h117
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_lock.c94
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_notifier.c137
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_pushbuf.c271
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_resource.c116
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_screen.c310
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_screen.h20
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.c86
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.h10
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_winsys.c158
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.c206
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.h34
-rw-r--r--src/gallium/winsys/drm/nouveau/nouveau_winsys_softpipe.c85
-rw-r--r--src/gallium/winsys/drm/nouveau/nv04_surface.c314
-rw-r--r--src/gallium/winsys/drm/nouveau/nv50_surface.c194
27 files changed, 4432 insertions, 0 deletions
diff --git a/src/gallium/winsys/drm/nouveau/Makefile b/src/gallium/winsys/drm/nouveau/Makefile
new file mode 100644
index 0000000000..be630ff6d1
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/Makefile
@@ -0,0 +1,45 @@
+
+TOP = ../../../../..
+include $(TOP)/configs/current
+
+LIBNAME = nouveau_dri.so
+
+MINIGLX_SOURCES =
+
+PIPE_DRIVERS = \
+ $(TOP)/src/gallium/drivers/softpipe/libsoftpipe.a \
+ $(TOP)/src/gallium/drivers/nv04/libnv04.a \
+ $(TOP)/src/gallium/drivers/nv10/libnv10.a \
+ $(TOP)/src/gallium/drivers/nv30/libnv30.a \
+ $(TOP)/src/gallium/drivers/nv40/libnv40.a \
+ $(TOP)/src/gallium/drivers/nv50/libnv50.a
+
+DRIVER_SOURCES = \
+ nouveau_bo.c \
+ nouveau_channel.c \
+ nouveau_context.c \
+ nouveau_device.c \
+ nouveau_dma.c \
+ nouveau_fence.c \
+ nouveau_grobj.c \
+ nouveau_lock.c \
+ nouveau_notifier.c \
+ nouveau_pushbuf.c \
+ nouveau_resource.c \
+ nouveau_screen.c \
+ nouveau_swapbuffers.c \
+ nouveau_winsys.c \
+ nouveau_winsys_pipe.c \
+ nouveau_winsys_softpipe.c \
+ nv04_surface.c \
+ nv50_surface.c
+
+C_SOURCES = \
+ $(COMMON_GALLIUM_SOURCES) \
+ $(DRIVER_SOURCES)
+
+ASM_SOURCES =
+
+include ../Makefile.template
+
+symlinks:
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_bo.c b/src/gallium/winsys/drm/nouveau/nouveau_bo.c
new file mode 100644
index 0000000000..b5942994d9
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_bo.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+#include "nouveau_local.h"
+
+static void
+nouveau_mem_free(struct nouveau_device *dev, struct drm_nouveau_mem_alloc *ma,
+ void **map)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct drm_nouveau_mem_free mf;
+
+ if (map && *map) {
+ drmUnmap(*map, ma->size);
+ *map = NULL;
+ }
+
+ if (ma->size) {
+ mf.offset = ma->offset;
+ mf.flags = ma->flags;
+ drmCommandWrite(nvdev->fd, DRM_NOUVEAU_MEM_FREE,
+ &mf, sizeof(mf));
+ ma->size = 0;
+ }
+}
+
+static int
+nouveau_mem_alloc(struct nouveau_device *dev, unsigned size, unsigned align,
+ uint32_t flags, struct drm_nouveau_mem_alloc *ma, void **map)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ int ret;
+
+ ma->alignment = align;
+ ma->size = size;
+ ma->flags = flags;
+ if (map)
+ ma->flags |= NOUVEAU_MEM_MAPPED;
+ ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_MEM_ALLOC, ma,
+ sizeof(struct drm_nouveau_mem_alloc));
+ if (ret)
+ return ret;
+
+ if (map) {
+ ret = drmMap(nvdev->fd, ma->map_handle, ma->size, map);
+ if (ret) {
+ *map = NULL;
+ nouveau_mem_free(dev, ma, map);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void
+nouveau_bo_tmp_del(void *priv)
+{
+ struct nouveau_resource *r = priv;
+
+ nouveau_fence_ref(NULL, (struct nouveau_fence **)&r->priv);
+ nouveau_resource_free(&r);
+}
+
+static unsigned
+nouveau_bo_tmp_max(struct nouveau_device_priv *nvdev)
+{
+ struct nouveau_resource *r = nvdev->sa_heap;
+ unsigned max = 0;
+
+ while (r) {
+ if (r->in_use && !nouveau_fence(r->priv)->emitted) {
+ r = r->next;
+ continue;
+ }
+
+ if (max < r->size)
+ max = r->size;
+ r = r->next;
+ }
+
+ return max;
+}
+
+static struct nouveau_resource *
+nouveau_bo_tmp(struct nouveau_channel *chan, unsigned size,
+ struct nouveau_fence *fence)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+ struct nouveau_resource *r = NULL;
+ struct nouveau_fence *ref = NULL;
+
+ if (fence)
+ nouveau_fence_ref(fence, &ref);
+ else
+ nouveau_fence_new(chan, &ref);
+ assert(ref);
+
+ while (nouveau_resource_alloc(nvdev->sa_heap, size, ref, &r)) {
+ if (nouveau_bo_tmp_max(nvdev) < size) {
+ nouveau_fence_ref(NULL, &ref);
+ return NULL;
+ }
+
+ nouveau_fence_flush(chan);
+ }
+ nouveau_fence_signal_cb(ref, nouveau_bo_tmp_del, r);
+
+ return r;
+}
+
+int
+nouveau_bo_init(struct nouveau_device *dev)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ int ret;
+
+ ret = nouveau_mem_alloc(dev, 128*1024, 0, NOUVEAU_MEM_AGP |
+ NOUVEAU_MEM_PCI, &nvdev->sa, &nvdev->sa_map);
+ if (ret)
+ return ret;
+
+ ret = nouveau_resource_init(&nvdev->sa_heap, 0, nvdev->sa.size);
+ if (ret) {
+ nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+ return ret;
+ }
+
+ return 0;
+}
+
+void
+nouveau_bo_takedown(struct nouveau_device *dev)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+ nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+}
+
+int
+nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
+ int size, struct nouveau_bo **bo)
+{
+ struct nouveau_bo_priv *nvbo;
+ int ret;
+
+ if (!dev || !bo || *bo)
+ return -EINVAL;
+
+ nvbo = calloc(1, sizeof(struct nouveau_bo_priv));
+ if (!nvbo)
+ return -ENOMEM;
+ nvbo->base.device = dev;
+ nvbo->base.size = size;
+ nvbo->base.handle = bo_to_ptr(nvbo);
+ nvbo->drm.alignment = align;
+ nvbo->refcount = 1;
+
+ if (flags & NOUVEAU_BO_TILED) {
+ nvbo->tiled = 1;
+ if (flags & NOUVEAU_BO_ZTILE)
+ nvbo->tiled |= 2;
+ flags &= ~NOUVEAU_BO_TILED;
+ }
+
+ ret = nouveau_bo_set_status(&nvbo->base, flags);
+ if (ret) {
+ free(nvbo);
+ return ret;
+ }
+
+ *bo = &nvbo->base;
+ return 0;
+}
+
+int
+nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
+ struct nouveau_bo **bo)
+{
+ struct nouveau_bo_priv *nvbo;
+
+ if (!dev || !bo || *bo)
+ return -EINVAL;
+
+ nvbo = calloc(1, sizeof(*nvbo));
+ if (!nvbo)
+ return -ENOMEM;
+ nvbo->base.device = dev;
+
+ nvbo->sysmem = ptr;
+ nvbo->user = 1;
+
+ nvbo->base.size = size;
+ nvbo->base.offset = nvbo->drm.offset;
+ nvbo->base.handle = bo_to_ptr(nvbo);
+ nvbo->refcount = 1;
+ *bo = &nvbo->base;
+ return 0;
+}
+
+int
+nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
+ struct nouveau_bo **bo)
+{
+ struct nouveau_bo_priv *nvbo = ptr_to_bo(handle);
+
+ if (!dev || !bo || *bo)
+ return -EINVAL;
+
+ nvbo->refcount++;
+ *bo = &nvbo->base;
+ return 0;
+}
+
+static void
+nouveau_bo_del_cb(void *priv)
+{
+ struct nouveau_bo_priv *nvbo = priv;
+
+ nouveau_fence_ref(NULL, &nvbo->fence);
+ nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
+ if (nvbo->sysmem && !nvbo->user)
+ free(nvbo->sysmem);
+ free(nvbo);
+}
+
+void
+nouveau_bo_del(struct nouveau_bo **bo)
+{
+ struct nouveau_bo_priv *nvbo;
+
+ if (!bo || !*bo)
+ return;
+ nvbo = nouveau_bo(*bo);
+ *bo = NULL;
+
+ if (--nvbo->refcount)
+ return;
+
+ if (nvbo->pending)
+ nouveau_pushbuf_flush(nvbo->pending->channel, 0);
+
+ if (nvbo->fence)
+ nouveau_fence_signal_cb(nvbo->fence, nouveau_bo_del_cb, nvbo);
+ else
+ nouveau_bo_del_cb(nvbo);
+}
+
+int
+nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ if (!nvbo)
+ return -EINVAL;
+
+ if (nvbo->pending &&
+ (nvbo->pending->flags & NOUVEAU_BO_WR || flags & NOUVEAU_BO_WR)) {
+ nouveau_pushbuf_flush(nvbo->pending->channel, 0);
+ }
+
+ if (flags & NOUVEAU_BO_WR)
+ nouveau_fence_wait(&nvbo->fence);
+ else
+ nouveau_fence_wait(&nvbo->wr_fence);
+
+ if (nvbo->sysmem)
+ bo->map = nvbo->sysmem;
+ else
+ bo->map = nvbo->map;
+ return 0;
+}
+
+void
+nouveau_bo_unmap(struct nouveau_bo *bo)
+{
+ bo->map = NULL;
+}
+
+static int
+nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
+{
+ if (nvbo->fence)
+ nouveau_fence_wait(&nvbo->fence);
+ memcpy(nvbo->map, nvbo->sysmem, nvbo->drm.size);
+ return 0;
+}
+
+int
+nouveau_bo_set_status(struct nouveau_bo *bo, uint32_t flags)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ struct drm_nouveau_mem_alloc new;
+ void *new_map = NULL, *new_sysmem = NULL;
+ unsigned new_flags = 0, ret;
+
+ assert(!bo->map);
+
+ /* Check current memtype vs requested, if they match do nothing */
+ if ((nvbo->drm.flags & NOUVEAU_MEM_FB) && (flags & NOUVEAU_BO_VRAM))
+ return 0;
+ if ((nvbo->drm.flags & (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI)) &&
+ (flags & NOUVEAU_BO_GART))
+ return 0;
+ if (nvbo->drm.size == 0 && nvbo->sysmem && (flags & NOUVEAU_BO_LOCAL))
+ return 0;
+
+ memset(&new, 0x00, sizeof(new));
+
+ /* Allocate new memory */
+ if (flags & NOUVEAU_BO_VRAM)
+ new_flags |= NOUVEAU_MEM_FB;
+ else
+ if (flags & NOUVEAU_BO_GART)
+ new_flags |= (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI);
+
+ if (nvbo->tiled && flags) {
+ new_flags |= NOUVEAU_MEM_TILE;
+ if (nvbo->tiled & 2)
+ new_flags |= NOUVEAU_MEM_TILE_ZETA;
+ }
+
+ if (new_flags) {
+ ret = nouveau_mem_alloc(bo->device, bo->size,
+ nvbo->drm.alignment, new_flags,
+ &new, &new_map);
+ if (ret)
+ return ret;
+ } else
+ if (!nvbo->user) {
+ new_sysmem = malloc(bo->size);
+ }
+
+ /* Copy old -> new */
+ /*XXX: use M2MF */
+ if (nvbo->sysmem || nvbo->map) {
+ struct nouveau_pushbuf_bo *pbo = nvbo->pending;
+ nvbo->pending = NULL;
+ nouveau_bo_map(bo, NOUVEAU_BO_RD);
+ memcpy(new_map, bo->map, bo->size);
+ nouveau_bo_unmap(bo);
+ nvbo->pending = pbo;
+ }
+
+ /* Free old memory */
+ if (nvbo->fence)
+ nouveau_fence_wait(&nvbo->fence);
+ nouveau_mem_free(bo->device, &nvbo->drm, &nvbo->map);
+ if (nvbo->sysmem && !nvbo->user)
+ free(nvbo->sysmem);
+
+ nvbo->drm = new;
+ nvbo->map = new_map;
+ if (!nvbo->user)
+ nvbo->sysmem = new_sysmem;
+ bo->flags = flags;
+ bo->offset = nvbo->drm.offset;
+ return 0;
+}
+
+static int
+nouveau_bo_validate_user(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ struct nouveau_fence *fence, uint32_t flags)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ struct nouveau_resource *r;
+
+ if (nvchan->user_charge + bo->size > nvdev->sa.size)
+ return 1;
+
+ if (!(flags & NOUVEAU_BO_GART))
+ return 1;
+
+ r = nouveau_bo_tmp(chan, bo->size, fence);
+ if (!r)
+ return 1;
+ nvchan->user_charge += bo->size;
+
+ memcpy(nvdev->sa_map + r->start, nvbo->sysmem, bo->size);
+
+ nvbo->offset = nvdev->sa.offset + r->start;
+ nvbo->flags = NOUVEAU_BO_GART;
+ return 0;
+}
+
+static int
+nouveau_bo_validate_bo(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ struct nouveau_fence *fence, uint32_t flags)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ int ret;
+
+ ret = nouveau_bo_set_status(bo, flags);
+ if (ret) {
+ nouveau_fence_flush(chan);
+
+ ret = nouveau_bo_set_status(bo, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (nvbo->user)
+ nouveau_bo_upload(nvbo);
+
+ nvbo->offset = nvbo->drm.offset;
+ if (nvbo->drm.flags & (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI))
+ nvbo->flags = NOUVEAU_BO_GART;
+ else
+ nvbo->flags = NOUVEAU_BO_VRAM;
+
+ return 0;
+}
+
+int
+nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ uint32_t flags)
+{
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ struct nouveau_fence *fence = nouveau_pushbuf(chan->pushbuf)->fence;
+ int ret;
+
+ assert(bo->map == NULL);
+
+ if (nvbo->user) {
+ ret = nouveau_bo_validate_user(chan, bo, fence, flags);
+ if (ret) {
+ ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+ if (ret)
+ return ret;
+ }
+ } else {
+ ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (flags & NOUVEAU_BO_WR)
+ nouveau_fence_ref(fence, &nvbo->wr_fence);
+ nouveau_fence_ref(fence, &nvbo->fence);
+ return 0;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_channel.c b/src/gallium/winsys/drm/nouveau/nouveau_channel.c
new file mode 100644
index 0000000000..3b4dcd1ecf
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_channel.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+
+int
+nouveau_channel_alloc(struct nouveau_device *dev, uint32_t fb_ctxdma,
+ uint32_t tt_ctxdma, struct nouveau_channel **chan)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct nouveau_channel_priv *nvchan;
+ int ret;
+
+ if (!nvdev || !chan || *chan)
+ return -EINVAL;
+
+ nvchan = calloc(1, sizeof(struct nouveau_channel_priv));
+ if (!nvchan)
+ return -ENOMEM;
+ nvchan->base.device = dev;
+
+ nvchan->drm.fb_ctxdma_handle = fb_ctxdma;
+ nvchan->drm.tt_ctxdma_handle = tt_ctxdma;
+ ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_CHANNEL_ALLOC,
+ &nvchan->drm, sizeof(nvchan->drm));
+ if (ret) {
+ free(nvchan);
+ return ret;
+ }
+
+ nvchan->base.id = nvchan->drm.channel;
+ if (nouveau_grobj_ref(&nvchan->base, nvchan->drm.fb_ctxdma_handle,
+ &nvchan->base.vram) ||
+ nouveau_grobj_ref(&nvchan->base, nvchan->drm.tt_ctxdma_handle,
+ &nvchan->base.gart)) {
+ nouveau_channel_free((void *)&nvchan);
+ return -EINVAL;
+ }
+
+ ret = drmMap(nvdev->fd, nvchan->drm.ctrl, nvchan->drm.ctrl_size,
+ (void*)&nvchan->user);
+ if (ret) {
+ nouveau_channel_free((void *)&nvchan);
+ return ret;
+ }
+ nvchan->put = &nvchan->user[0x40/4];
+ nvchan->get = &nvchan->user[0x44/4];
+ nvchan->ref_cnt = &nvchan->user[0x48/4];
+
+ ret = drmMap(nvdev->fd, nvchan->drm.notifier, nvchan->drm.notifier_size,
+ (drmAddressPtr)&nvchan->notifier_block);
+ if (ret) {
+ nouveau_channel_free((void *)&nvchan);
+ return ret;
+ }
+
+ ret = drmMap(nvdev->fd, nvchan->drm.cmdbuf, nvchan->drm.cmdbuf_size,
+ (void*)&nvchan->pushbuf);
+ if (ret) {
+ nouveau_channel_free((void *)&nvchan);
+ return ret;
+ }
+
+ ret = nouveau_grobj_alloc(&nvchan->base, 0x00000000, 0x0030,
+ &nvchan->base.nullobj);
+ if (ret) {
+ nouveau_channel_free((void *)&nvchan);
+ return ret;
+ }
+
+ nouveau_dma_channel_init(&nvchan->base);
+ nouveau_pushbuf_init(&nvchan->base);
+
+ *chan = &nvchan->base;
+ return 0;
+}
+
+void
+nouveau_channel_free(struct nouveau_channel **chan)
+{
+ struct nouveau_channel_priv *nvchan;
+ struct nouveau_device_priv *nvdev;
+ struct drm_nouveau_channel_free cf;
+
+ if (!chan || !*chan)
+ return;
+ nvchan = nouveau_channel(*chan);
+ *chan = NULL;
+ nvdev = nouveau_device(nvchan->base.device);
+
+ FIRE_RING_CH(&nvchan->base);
+
+ nouveau_grobj_free(&nvchan->base.vram);
+ nouveau_grobj_free(&nvchan->base.gart);
+ nouveau_grobj_free(&nvchan->base.nullobj);
+
+ cf.channel = nvchan->drm.channel;
+ drmCommandWrite(nvdev->fd, DRM_NOUVEAU_CHANNEL_FREE, &cf, sizeof(cf));
+ free(nvchan);
+}
+
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_context.c b/src/gallium/winsys/drm/nouveau/nouveau_context.c
new file mode 100644
index 0000000000..74413c408f
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_context.c
@@ -0,0 +1,346 @@
+#include "main/glheader.h"
+#include "glapi/glthread.h"
+#include <GL/internal/glcore.h>
+#include "utils.h"
+
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_context.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_context.h"
+#include "pipe/p_screen.h"
+
+#include "nouveau_context.h"
+#include "nouveau_dri.h"
+#include "nouveau_local.h"
+#include "nouveau_screen.h"
+#include "nouveau_winsys_pipe.h"
+
+#ifdef DEBUG
+static const struct dri_debug_control debug_control[] = {
+ { "bo", DEBUG_BO },
+ { NULL, 0 }
+};
+int __nouveau_debug = 0;
+#endif
+
+static void
+nouveau_channel_context_destroy(struct nouveau_channel_context *nvc)
+{
+ nouveau_grobj_free(&nvc->NvCtxSurf2D);
+ nouveau_grobj_free(&nvc->NvImageBlit);
+ nouveau_grobj_free(&nvc->NvGdiRect);
+ nouveau_grobj_free(&nvc->NvM2MF);
+ nouveau_grobj_free(&nvc->Nv2D);
+ nouveau_grobj_free(&nvc->NvSwzSurf);
+ nouveau_grobj_free(&nvc->NvSIFM);
+
+ nouveau_notifier_free(&nvc->sync_notifier);
+
+ nouveau_channel_free(&nvc->channel);
+
+ FREE(nvc);
+}
+
+static struct nouveau_channel_context *
+nouveau_channel_context_create(struct nouveau_device *dev)
+{
+ struct nouveau_channel_context *nvc;
+ int ret;
+
+ nvc = CALLOC_STRUCT(nouveau_channel_context);
+ if (!nvc)
+ return NULL;
+
+ if ((ret = nouveau_channel_alloc(dev, 0x8003d001, 0x8003d002,
+ &nvc->channel))) {
+ NOUVEAU_ERR("Error creating GPU channel: %d\n", ret);
+ nouveau_channel_context_destroy(nvc);
+ return NULL;
+ }
+
+ nvc->next_handle = 0x80000000;
+
+ if ((ret = nouveau_notifier_alloc(nvc->channel, nvc->next_handle++, 1,
+ &nvc->sync_notifier))) {
+ NOUVEAU_ERR("Error creating channel sync notifier: %d\n", ret);
+ nouveau_channel_context_destroy(nvc);
+ return NULL;
+ }
+
+ switch (dev->chipset & 0xf0) {
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ ret = nouveau_surface_channel_create_nv50(nvc);
+ break;
+ default:
+ ret = nouveau_surface_channel_create_nv04(nvc);
+ break;
+ }
+
+ if (ret) {
+ NOUVEAU_ERR("Error initialising surface objects: %d\n", ret);
+ nouveau_channel_context_destroy(nvc);
+ return NULL;
+ }
+
+ return nvc;
+}
+
+GLboolean
+nouveau_context_create(const __GLcontextModes *glVis,
+ __DRIcontextPrivate *driContextPriv,
+ void *sharedContextPrivate)
+{
+ __DRIscreenPrivate *driScrnPriv = driContextPriv->driScreenPriv;
+ struct nouveau_screen *nv_screen = driScrnPriv->private;
+ struct nouveau_context *nv = CALLOC_STRUCT(nouveau_context);
+ struct pipe_context *pipe = NULL;
+ struct st_context *st_share = NULL;
+ struct nouveau_channel_context *nvc = NULL;
+ struct nouveau_device *dev = nv_screen->device;
+ int i;
+
+ if (sharedContextPrivate) {
+ st_share = ((struct nouveau_context *)sharedContextPrivate)->st;
+ }
+
+ switch (dev->chipset & 0xf0) {
+ case 0x10:
+ case 0x20:
+ /* NV10 */
+ case 0x30:
+ /* NV30 */
+ case 0x40:
+ case 0x60:
+ /* NV40 */
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ /* G80 */
+ break;
+ default:
+ NOUVEAU_ERR("Unsupported chipset: NV%02x\n", dev->chipset);
+ return GL_FALSE;
+ }
+
+ driContextPriv->driverPrivate = (void *)nv;
+ nv->nv_screen = nv_screen;
+ nv->dri_screen = driScrnPriv;
+
+ {
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+ nvdev->ctx = driContextPriv->hHWContext;
+ nvdev->lock = (drmLock *)&driScrnPriv->pSAREA->lock;
+ }
+
+ driParseConfigFiles(&nv->dri_option_cache, &nv_screen->option_cache,
+ nv->dri_screen->myNum, "nouveau");
+#ifdef DEBUG
+ __nouveau_debug = driParseDebugString(getenv("NOUVEAU_DEBUG"),
+ debug_control);
+#endif
+
+ /*XXX: Hack up a fake region and buffer object for front buffer.
+ * This will go away with TTM, replaced with a simple reference
+ * of the front buffer handle passed to us by the DDX.
+ */
+ {
+ struct pipe_surface *fb_surf;
+ struct nouveau_pipe_buffer *fb_buf;
+ struct nouveau_bo_priv *fb_bo;
+
+ fb_bo = calloc(1, sizeof(struct nouveau_bo_priv));
+ fb_bo->drm.offset = nv_screen->front_offset;
+ fb_bo->drm.flags = NOUVEAU_MEM_FB;
+ fb_bo->drm.size = nv_screen->front_pitch *
+ nv_screen->front_height;
+ fb_bo->refcount = 1;
+ fb_bo->base.flags = NOUVEAU_BO_PIN | NOUVEAU_BO_VRAM;
+ fb_bo->base.offset = fb_bo->drm.offset;
+ fb_bo->base.handle = (unsigned long)fb_bo;
+ fb_bo->base.size = fb_bo->drm.size;
+ fb_bo->base.device = nv_screen->device;
+
+ fb_buf = calloc(1, sizeof(struct nouveau_pipe_buffer));
+ fb_buf->bo = &fb_bo->base;
+
+ fb_surf = calloc(1, sizeof(struct pipe_surface));
+ if (nv_screen->front_cpp == 2)
+ fb_surf->format = PIPE_FORMAT_R5G6B5_UNORM;
+ else
+ fb_surf->format = PIPE_FORMAT_A8R8G8B8_UNORM;
+ pf_get_block(fb_surf->format, &fb_surf->block);
+ fb_surf->width = nv_screen->front_pitch / nv_screen->front_cpp;
+ fb_surf->height = nv_screen->front_height;
+ fb_surf->stride = fb_surf->width * fb_surf->block.size;
+ fb_surf->refcount = 1;
+ fb_surf->buffer = &fb_buf->base;
+
+ nv->frontbuffer = fb_surf;
+ }
+
+ /* Attempt to share a single channel between multiple contexts from
+ * a single process.
+ */
+ nvc = nv_screen->nvc;
+ if (!nvc && st_share) {
+ struct nouveau_context *snv = st_share->pipe->priv;
+ if (snv) {
+ nvc = snv->nvc;
+ }
+ }
+
+ /*XXX: temporary - disable multi-context/single-channel on pre-NV4x */
+ switch (dev->chipset & 0xf0) {
+ case 0x40:
+ case 0x60:
+ /* NV40 class */
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ /* G80 class */
+ break;
+ default:
+ nvc = NULL;
+ break;
+ }
+
+ if (!nvc) {
+ nvc = nouveau_channel_context_create(dev);
+ if (!nvc) {
+ NOUVEAU_ERR("Failed initialising GPU context\n");
+ return GL_FALSE;
+ }
+ nv_screen->nvc = nvc;
+ }
+
+ nvc->refcount++;
+ nv->nvc = nvc;
+
+ /* Find a free slot for a pipe context, allocate a new one if needed */
+ nv->pctx_id = -1;
+ for (i = 0; i < nvc->nr_pctx; i++) {
+ if (nvc->pctx[i] == NULL) {
+ nv->pctx_id = i;
+ break;
+ }
+ }
+
+ if (nv->pctx_id < 0) {
+ nv->pctx_id = nvc->nr_pctx++;
+ nvc->pctx =
+ realloc(nvc->pctx,
+ sizeof(struct pipe_context *) * nvc->nr_pctx);
+ }
+
+ /* Create pipe */
+ switch (dev->chipset & 0xf0) {
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ if (nouveau_surface_init_nv50(nv))
+ return GL_FALSE;
+ break;
+ default:
+ if (nouveau_surface_init_nv04(nv))
+ return GL_FALSE;
+ break;
+ }
+
+ if (!getenv("NOUVEAU_FORCE_SOFTPIPE")) {
+ struct pipe_screen *pscreen;
+
+ pipe = nouveau_pipe_create(nv);
+ if (!pipe)
+ NOUVEAU_ERR("Couldn't create hw pipe\n");
+ pscreen = nvc->pscreen;
+
+ nv->cap.hw_vertex_buffer =
+ pscreen->get_param(pscreen, NOUVEAU_CAP_HW_VTXBUF);
+ nv->cap.hw_index_buffer =
+ pscreen->get_param(pscreen, NOUVEAU_CAP_HW_IDXBUF);
+ }
+
+ if (!pipe) {
+ NOUVEAU_MSG("Using softpipe\n");
+ pipe = nouveau_create_softpipe(nv);
+ if (!pipe) {
+ NOUVEAU_ERR("Error creating pipe, bailing\n");
+ return GL_FALSE;
+ }
+ }
+
+ pipe->priv = nv;
+ nv->st = st_create_context(pipe, glVis, st_share);
+ return GL_TRUE;
+}
+
+void
+nouveau_context_destroy(__DRIcontextPrivate *driContextPriv)
+{
+ struct nouveau_context *nv = driContextPriv->driverPrivate;
+ struct nouveau_channel_context *nvc = nv->nvc;
+
+ assert(nv);
+
+ st_finish(nv->st);
+ st_destroy_context(nv->st);
+
+ if (nv->pctx_id >= 0) {
+ nvc->pctx[nv->pctx_id] = NULL;
+ if (--nvc->refcount <= 0) {
+ nouveau_channel_context_destroy(nvc);
+ nv->nv_screen->nvc = NULL;
+ }
+ }
+
+ free(nv);
+}
+
+GLboolean
+nouveau_context_bind(__DRIcontextPrivate *driContextPriv,
+ __DRIdrawablePrivate *driDrawPriv,
+ __DRIdrawablePrivate *driReadPriv)
+{
+ struct nouveau_context *nv;
+ struct nouveau_framebuffer *draw, *read;
+
+ if (!driContextPriv) {
+ st_make_current(NULL, NULL, NULL);
+ return GL_TRUE;
+ }
+
+ nv = driContextPriv->driverPrivate;
+ draw = driDrawPriv->driverPrivate;
+ read = driReadPriv->driverPrivate;
+
+ st_make_current(nv->st, draw->stfb, read->stfb);
+
+ if ((nv->dri_drawable != driDrawPriv) ||
+ (nv->last_stamp != driDrawPriv->lastStamp)) {
+ nv->dri_drawable = driDrawPriv;
+ st_resize_framebuffer(draw->stfb, driDrawPriv->w,
+ driDrawPriv->h);
+ nv->last_stamp = driDrawPriv->lastStamp;
+ }
+
+ if (driDrawPriv != driReadPriv) {
+ st_resize_framebuffer(read->stfb, driReadPriv->w,
+ driReadPriv->h);
+ }
+
+ return GL_TRUE;
+}
+
+GLboolean
+nouveau_context_unbind(__DRIcontextPrivate *driContextPriv)
+{
+ struct nouveau_context *nv = driContextPriv->driverPrivate;
+ (void)nv;
+
+ st_flush(nv->st, 0, NULL);
+ return GL_TRUE;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_context.h b/src/gallium/winsys/drm/nouveau/nouveau_context.h
new file mode 100644
index 0000000000..77e2147a2c
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_context.h
@@ -0,0 +1,113 @@
+#ifndef __NOUVEAU_CONTEXT_H__
+#define __NOUVEAU_CONTEXT_H__
+
+#include "dri_util.h"
+#include "xmlconfig.h"
+
+#include "nouveau/nouveau_winsys.h"
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+
+struct nouveau_framebuffer {
+ struct st_framebuffer *stfb;
+};
+
+struct nouveau_channel_context {
+ struct pipe_screen *pscreen;
+ int refcount;
+
+ unsigned cur_pctx;
+ unsigned nr_pctx;
+ struct pipe_context **pctx;
+
+ struct nouveau_channel *channel;
+
+ struct nouveau_notifier *sync_notifier;
+
+ /* Common */
+ struct nouveau_grobj *NvM2MF;
+ /* NV04-NV40 */
+ struct nouveau_grobj *NvCtxSurf2D;
+ struct nouveau_grobj *NvSwzSurf;
+ struct nouveau_grobj *NvImageBlit;
+ struct nouveau_grobj *NvGdiRect;
+ struct nouveau_grobj *NvSIFM;
+ /* G80 */
+ struct nouveau_grobj *Nv2D;
+
+ uint32_t next_handle;
+ uint32_t next_subchannel;
+ uint32_t next_sequence;
+};
+
+struct nouveau_context {
+ struct st_context *st;
+
+ /* DRI stuff */
+ __DRIscreenPrivate *dri_screen;
+ __DRIdrawablePrivate *dri_drawable;
+ unsigned int last_stamp;
+ driOptionCache dri_option_cache;
+ drm_context_t drm_context;
+ drmLock drm_lock;
+ GLboolean locked;
+ struct nouveau_screen *nv_screen;
+ struct pipe_surface *frontbuffer;
+
+ struct {
+ int hw_vertex_buffer;
+ int hw_index_buffer;
+ } cap;
+
+ /* Hardware context */
+ struct nouveau_channel_context *nvc;
+ int pctx_id;
+
+ /* pipe_surface accel */
+ struct pipe_surface *surf_src, *surf_dst;
+ unsigned surf_src_offset, surf_dst_offset;
+ int (*surface_copy_prep)(struct nouveau_context *,
+ struct pipe_surface *dst,
+ struct pipe_surface *src);
+ void (*surface_copy)(struct nouveau_context *, unsigned dx, unsigned dy,
+ unsigned sx, unsigned sy, unsigned w, unsigned h);
+ void (*surface_copy_done)(struct nouveau_context *);
+ int (*surface_fill)(struct nouveau_context *, struct pipe_surface *,
+ unsigned, unsigned, unsigned, unsigned, unsigned);
+};
+
+extern GLboolean nouveau_context_create(const __GLcontextModes *,
+ __DRIcontextPrivate *, void *);
+extern void nouveau_context_destroy(__DRIcontextPrivate *);
+extern GLboolean nouveau_context_bind(__DRIcontextPrivate *,
+ __DRIdrawablePrivate *draw,
+ __DRIdrawablePrivate *read);
+extern GLboolean nouveau_context_unbind(__DRIcontextPrivate *);
+
+#ifdef DEBUG
+extern int __nouveau_debug;
+
+#define DEBUG_BO (1 << 0)
+
+#define DBG(flag, ...) do { \
+ if (__nouveau_debug & (DEBUG_##flag)) \
+ NOUVEAU_ERR(__VA_ARGS__); \
+} while(0)
+#else
+#define DBG(flag, ...)
+#endif
+
+extern void LOCK_HARDWARE(struct nouveau_context *);
+extern void UNLOCK_HARDWARE(struct nouveau_context *);
+
+extern int
+nouveau_surface_channel_create_nv04(struct nouveau_channel_context *);
+extern int
+nouveau_surface_channel_create_nv50(struct nouveau_channel_context *);
+extern int nouveau_surface_init_nv04(struct nouveau_context *);
+extern int nouveau_surface_init_nv50(struct nouveau_context *);
+
+extern uint32_t *nouveau_pipe_dma_beginp(struct nouveau_grobj *, int, int);
+extern void nouveau_pipe_dma_kickoff(struct nouveau_channel *);
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_device.c b/src/gallium/winsys/drm/nouveau/nouveau_device.c
new file mode 100644
index 0000000000..0b452fcd02
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_device.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+
+int
+nouveau_device_open_existing(struct nouveau_device **dev, int close,
+ int fd, drm_context_t ctx)
+{
+ struct nouveau_device_priv *nvdev;
+ int ret;
+
+ if (!dev || *dev)
+ return -EINVAL;
+
+ nvdev = calloc(1, sizeof(*nvdev));
+ if (!nvdev)
+ return -ENOMEM;
+ nvdev->fd = fd;
+ nvdev->ctx = ctx;
+ nvdev->needs_close = close;
+
+ drmCommandNone(nvdev->fd, DRM_NOUVEAU_CARD_INIT);
+
+ if ((ret = nouveau_bo_init(&nvdev->base))) {
+ nouveau_device_close((void *)&nvdev);
+ return ret;
+ }
+
+ {
+ uint64_t value;
+
+ ret = nouveau_device_get_param(&nvdev->base,
+ NOUVEAU_GETPARAM_CHIPSET_ID,
+ &value);
+ if (ret) {
+ nouveau_device_close((void *)&nvdev);
+ return ret;
+ }
+ nvdev->base.chipset = value;
+ }
+
+ *dev = &nvdev->base;
+ return 0;
+}
+
+int
+nouveau_device_open(struct nouveau_device **dev, const char *busid)
+{
+ drm_context_t ctx;
+ int fd, ret;
+
+ if (!dev || *dev)
+ return -EINVAL;
+
+ fd = drmOpen("nouveau", busid);
+ if (fd < 0)
+ return -EINVAL;
+
+ ret = drmCreateContext(fd, &ctx);
+ if (ret) {
+ drmClose(fd);
+ return ret;
+ }
+
+ ret = nouveau_device_open_existing(dev, 1, fd, ctx);
+ if (ret) {
+ drmDestroyContext(fd, ctx);
+ drmClose(fd);
+ return ret;
+ }
+
+ return 0;
+}
+
+void
+nouveau_device_close(struct nouveau_device **dev)
+{
+ struct nouveau_device_priv *nvdev;
+
+ if (dev || !*dev)
+ return;
+ nvdev = nouveau_device(*dev);
+ *dev = NULL;
+
+ nouveau_bo_takedown(&nvdev->base);
+
+ if (nvdev->needs_close) {
+ drmDestroyContext(nvdev->fd, nvdev->ctx);
+ drmClose(nvdev->fd);
+ }
+ free(nvdev);
+}
+
+int
+nouveau_device_get_param(struct nouveau_device *dev,
+ uint64_t param, uint64_t *value)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct drm_nouveau_getparam g;
+ int ret;
+
+ if (!nvdev || !value)
+ return -EINVAL;
+
+ g.param = param;
+ ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_GETPARAM,
+ &g, sizeof(g));
+ if (ret)
+ return ret;
+
+ *value = g.value;
+ return 0;
+}
+
+int
+nouveau_device_set_param(struct nouveau_device *dev,
+ uint64_t param, uint64_t value)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ struct drm_nouveau_setparam s;
+ int ret;
+
+ if (!nvdev)
+ return -EINVAL;
+
+ s.param = param;
+ s.value = value;
+ ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_SETPARAM,
+ &s, sizeof(s));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_dma.c b/src/gallium/winsys/drm/nouveau/nouveau_dma.c
new file mode 100644
index 0000000000..f8a8ba04f6
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_dma.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+#include "nouveau_local.h"
+
+static inline uint32_t
+READ_GET(struct nouveau_channel_priv *nvchan)
+{
+ return *nvchan->get;
+}
+
+static inline void
+WRITE_PUT(struct nouveau_channel_priv *nvchan, uint32_t val)
+{
+ uint32_t put = ((val << 2) + nvchan->dma->base);
+ volatile int dum;
+
+ NOUVEAU_DMA_BARRIER;
+ dum = READ_GET(nvchan);
+
+ *nvchan->put = put;
+ nvchan->dma->put = val;
+#ifdef NOUVEAU_DMA_TRACE
+ NOUVEAU_MSG("WRITE_PUT %d/0x%08x\n", nvchan->drm.channel, put);
+#endif
+
+ NOUVEAU_DMA_BARRIER;
+}
+
+static inline int
+LOCAL_GET(struct nouveau_dma_priv *dma, uint32_t *val)
+{
+ uint32_t get = *val;
+
+ if (get >= dma->base && get <= (dma->base + (dma->max << 2))) {
+ *val = (get - dma->base) >> 2;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+nouveau_dma_channel_init(struct nouveau_channel *chan)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ int i;
+
+ nvchan->dma = &nvchan->dma_master;
+ nvchan->dma->base = nvchan->drm.put_base;
+ nvchan->dma->cur = nvchan->dma->put = 0;
+ nvchan->dma->max = (nvchan->drm.cmdbuf_size >> 2) - 2;
+ nvchan->dma->free = nvchan->dma->max - nvchan->dma->cur;
+
+ RING_SPACE_CH(chan, RING_SKIPS);
+ for (i = 0; i < RING_SKIPS; i++)
+ OUT_RING_CH(chan, 0);
+}
+
+#define CHECK_TIMEOUT() do { \
+ if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) \
+ return - EBUSY; \
+} while(0)
+
+int
+nouveau_dma_wait(struct nouveau_channel *chan, int size)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+ uint32_t get, t_start;
+
+ FIRE_RING_CH(chan);
+
+ t_start = NOUVEAU_TIME_MSEC();
+ while (dma->free < size) {
+ CHECK_TIMEOUT();
+
+ get = READ_GET(nvchan);
+ if (!LOCAL_GET(dma, &get))
+ continue;
+
+ if (dma->put >= get) {
+ dma->free = dma->max - dma->cur;
+
+ if (dma->free < size) {
+#ifdef NOUVEAU_DMA_DEBUG
+ dma->push_free = 1;
+#endif
+ OUT_RING_CH(chan, 0x20000000 | dma->base);
+ if (get <= RING_SKIPS) {
+ /*corner case - will be idle*/
+ if (dma->put <= RING_SKIPS)
+ WRITE_PUT(nvchan,
+ RING_SKIPS + 1);
+
+ do {
+ CHECK_TIMEOUT();
+ get = READ_GET(nvchan);
+ if (!LOCAL_GET(dma, &get))
+ get = 0;
+ } while (get <= RING_SKIPS);
+ }
+
+ WRITE_PUT(nvchan, RING_SKIPS);
+ dma->cur = dma->put = RING_SKIPS;
+ dma->free = get - (RING_SKIPS + 1);
+ }
+ } else {
+ dma->free = get - dma->cur - 1;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
+static void
+nouveau_dma_parse_pushbuf(struct nouveau_channel *chan, int get, int put)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ unsigned mthd_count = 0;
+
+ while (get != put) {
+ uint32_t gpuget = (get << 2) + nvchan->drm.put_base;
+ uint32_t data;
+
+ if (get < 0 || get >= nvchan->drm.cmdbuf_size) {
+ NOUVEAU_ERR("DMA_PT 0x%08x\n", gpuget);
+ assert(0);
+ }
+ data = nvchan->pushbuf[get++];
+
+ if (mthd_count) {
+ NOUVEAU_MSG("0x%08x 0x%08x\n", gpuget, data);
+ mthd_count--;
+ continue;
+ }
+
+ switch (data & 0x60000000) {
+ case 0x00000000:
+ mthd_count = (data >> 18) & 0x7ff;
+ NOUVEAU_MSG("0x%08x 0x%08x MTHD "
+ "Sc %d Mthd 0x%04x Size %d\n",
+ gpuget, data, (data>>13) & 7, data & 0x1ffc,
+ mthd_count);
+ break;
+ case 0x20000000:
+ get = (data & 0x1ffffffc) >> 2;
+ NOUVEAU_MSG("0x%08x 0x%08x JUMP 0x%08x\n",
+ gpuget, data, data & 0x1ffffffc);
+ continue;
+ case 0x40000000:
+ mthd_count = (data >> 18) & 0x7ff;
+ NOUVEAU_MSG("0x%08x 0x%08x NINC "
+ "Sc %d Mthd 0x%04x Size %d\n",
+ gpuget, data, (data>>13) & 7, data & 0x1ffc,
+ mthd_count);
+ break;
+ case 0x60000000:
+ /* DMA_OPCODE_CALL apparently, doesn't seem to work on
+ * my NV40 at least..
+ */
+ /* fall-through */
+ default:
+ NOUVEAU_MSG("DMA_PUSHER 0x%08x 0x%08x\n",
+ gpuget, data);
+ assert(0);
+ }
+ }
+}
+#endif
+
+void
+nouveau_dma_kickoff(struct nouveau_channel *chan)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+
+ if (dma->cur == dma->put)
+ return;
+
+#ifdef NOUVEAU_DMA_DEBUG
+ if (dma->push_free) {
+ NOUVEAU_ERR("Packet incomplete: %d left\n", dma->push_free);
+ return;
+ }
+#endif
+
+#ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
+ nouveau_dma_parse_pushbuf(chan, dma->put, dma->cur);
+#endif
+
+ WRITE_PUT(nvchan, dma->cur);
+}
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_dma.h b/src/gallium/winsys/drm/nouveau/nouveau_dma.h
new file mode 100644
index 0000000000..cfa6d26e82
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_dma.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#ifndef __NOUVEAU_DMA_H__
+#define __NOUVEAU_DMA_H__
+
+#include <string.h>
+#include "nouveau_drmif.h"
+#include "nouveau_local.h"
+
+#define RING_SKIPS 8
+
+extern int nouveau_dma_wait(struct nouveau_channel *chan, int size);
+extern void nouveau_dma_subc_bind(struct nouveau_grobj *);
+extern void nouveau_dma_channel_init(struct nouveau_channel *);
+extern void nouveau_dma_kickoff(struct nouveau_channel *);
+
+#ifdef NOUVEAU_DMA_DEBUG
+static char faulty[1024];
+#endif
+
+static inline void
+nouveau_dma_out(struct nouveau_channel *chan, uint32_t data)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+
+#ifdef NOUVEAU_DMA_DEBUG
+ if (dma->push_free == 0) {
+ NOUVEAU_ERR("No space left in packet at %s\n", faulty);
+ return;
+ }
+ dma->push_free--;
+#endif
+#ifdef NOUVEAU_DMA_TRACE
+ {
+ uint32_t offset = (dma->cur << 2) + dma->base;
+ NOUVEAU_MSG("\tOUT_RING %d/0x%08x -> 0x%08x\n",
+ nvchan->drm.channel, offset, data);
+ }
+#endif
+ nvchan->pushbuf[dma->cur + (dma->base - nvchan->drm.put_base)/4] = data;
+ dma->cur++;
+}
+
+static inline void
+nouveau_dma_outp(struct nouveau_channel *chan, uint32_t *ptr, int size)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+ (void)dma;
+
+#ifdef NOUVEAU_DMA_DEBUG
+ if (dma->push_free < size) {
+ NOUVEAU_ERR("Packet too small. Free=%d, Need=%d\n",
+ dma->push_free, size);
+ return;
+ }
+#endif
+#ifdef NOUVEAU_DMA_TRACE
+ while (size--) {
+ nouveau_dma_out(chan, *ptr);
+ ptr++;
+ }
+#else
+ memcpy(&nvchan->pushbuf[dma->cur], ptr, size << 2);
+#ifdef NOUVEAU_DMA_DEBUG
+ dma->push_free -= size;
+#endif
+ dma->cur += size;
+#endif
+}
+
+static inline void
+nouveau_dma_space(struct nouveau_channel *chan, int size)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+
+ if (dma->free < size) {
+ if (nouveau_dma_wait(chan, size) && chan->hang_notify)
+ chan->hang_notify(chan);
+ }
+ dma->free -= size;
+#ifdef NOUVEAU_DMA_DEBUG
+ dma->push_free = size;
+#endif
+}
+
+static inline void
+nouveau_dma_begin(struct nouveau_channel *chan, struct nouveau_grobj *grobj,
+ int method, int size, const char* file, int line)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *dma = nvchan->dma;
+ (void)dma;
+
+#ifdef NOUVEAU_DMA_TRACE
+ NOUVEAU_MSG("BEGIN_RING %d/%08x/%d/0x%04x/%d\n", nvchan->drm.channel,
+ grobj->handle, grobj->subc, method, size);
+#endif
+
+#ifdef NOUVEAU_DMA_DEBUG
+ if (dma->push_free) {
+ NOUVEAU_ERR("Previous packet incomplete: %d left at %s\n",
+ dma->push_free, faulty);
+ return;
+ }
+ sprintf(faulty,"%s:%d",file,line);
+#endif
+
+ nouveau_dma_space(chan, (size + 1));
+ nouveau_dma_out(chan, (size << 18) | (grobj->subc << 13) | method);
+}
+
+#define RING_SPACE_CH(ch,sz) nouveau_dma_space((ch), (sz))
+#define BEGIN_RING_CH(ch,gr,m,sz) nouveau_dma_begin((ch), (gr), (m), (sz), __FUNCTION__, __LINE__ )
+#define OUT_RING_CH(ch, data) nouveau_dma_out((ch), (data))
+#define OUT_RINGp_CH(ch,ptr,dwords) nouveau_dma_outp((ch), (void*)(ptr), \
+ (dwords))
+#define FIRE_RING_CH(ch) nouveau_dma_kickoff((ch))
+#define WAIT_RING_CH(ch,sz) nouveau_dma_wait((ch), (sz))
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_dri.h b/src/gallium/winsys/drm/nouveau/nouveau_dri.h
new file mode 100644
index 0000000000..1207c2d609
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_dri.h
@@ -0,0 +1,28 @@
+#ifndef _NOUVEAU_DRI_
+#define _NOUVEAU_DRI_
+
+#include "xf86drm.h"
+#include "drm.h"
+#include "nouveau_drm.h"
+
+struct nouveau_dri {
+ uint32_t device_id; /**< \brief PCI device ID */
+ uint32_t width; /**< \brief width in pixels of display */
+ uint32_t height; /**< \brief height in scanlines of display */
+ uint32_t depth; /**< \brief depth of display (8, 15, 16, 24) */
+ uint32_t bpp; /**< \brief bit depth of display (8, 16, 24, 32) */
+
+ uint32_t bus_type; /**< \brief ths bus type */
+ uint32_t bus_mode; /**< \brief bus mode (used for AGP, maybe also for PCI-E ?) */
+
+ uint32_t front_offset; /**< \brief front buffer offset */
+ uint32_t front_pitch; /**< \brief front buffer pitch */
+ uint32_t back_offset; /**< \brief private back buffer offset */
+ uint32_t back_pitch; /**< \brief private back buffer pitch */
+ uint32_t depth_offset; /**< \brief private depth buffer offset */
+ uint32_t depth_pitch; /**< \brief private depth buffer pitch */
+
+};
+
+#endif
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_drmif.h b/src/gallium/winsys/drm/nouveau/nouveau_drmif.h
new file mode 100644
index 0000000000..dcd6a5eb0a
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_drmif.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#ifndef __NOUVEAU_DRMIF_H__
+#define __NOUVEAU_DRMIF_H__
+
+#include <stdint.h>
+#include <xf86drm.h>
+#include <nouveau_drm.h>
+
+#include "nouveau/nouveau_device.h"
+#include "nouveau/nouveau_channel.h"
+#include "nouveau/nouveau_grobj.h"
+#include "nouveau/nouveau_notifier.h"
+#include "nouveau/nouveau_bo.h"
+#include "nouveau/nouveau_resource.h"
+#include "nouveau/nouveau_pushbuf.h"
+
+struct nouveau_device_priv {
+ struct nouveau_device base;
+
+ int fd;
+ drm_context_t ctx;
+ drmLock *lock;
+ int needs_close;
+
+ struct drm_nouveau_mem_alloc sa;
+ void *sa_map;
+ struct nouveau_resource *sa_heap;
+};
+#define nouveau_device(n) ((struct nouveau_device_priv *)(n))
+
+extern int
+nouveau_device_open_existing(struct nouveau_device **, int close,
+ int fd, drm_context_t ctx);
+
+extern int
+nouveau_device_open(struct nouveau_device **, const char *busid);
+
+extern void
+nouveau_device_close(struct nouveau_device **);
+
+extern int
+nouveau_device_get_param(struct nouveau_device *, uint64_t param, uint64_t *v);
+
+extern int
+nouveau_device_set_param(struct nouveau_device *, uint64_t param, uint64_t val);
+
+struct nouveau_fence {
+ struct nouveau_channel *channel;
+};
+
+struct nouveau_fence_cb {
+ struct nouveau_fence_cb *next;
+ void (*func)(void *);
+ void *priv;
+};
+
+struct nouveau_fence_priv {
+ struct nouveau_fence base;
+ int refcount;
+
+ struct nouveau_fence *next;
+ struct nouveau_fence_cb *signal_cb;
+
+ uint32_t sequence;
+ int emitted;
+ int signalled;
+};
+#define nouveau_fence(n) ((struct nouveau_fence_priv *)(n))
+
+extern int
+nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **);
+
+extern int
+nouveau_fence_ref(struct nouveau_fence *, struct nouveau_fence **);
+
+extern int
+nouveau_fence_signal_cb(struct nouveau_fence *, void (*)(void *), void *);
+
+extern void
+nouveau_fence_emit(struct nouveau_fence *);
+
+extern int
+nouveau_fence_wait(struct nouveau_fence **);
+
+extern void
+nouveau_fence_flush(struct nouveau_channel *);
+
+struct nouveau_pushbuf_reloc {
+ struct nouveau_pushbuf_bo *pbbo;
+ uint32_t *ptr;
+ uint32_t flags;
+ uint32_t data;
+ uint32_t vor;
+ uint32_t tor;
+};
+
+struct nouveau_pushbuf_bo {
+ struct nouveau_channel *channel;
+ struct nouveau_bo *bo;
+ unsigned flags;
+ unsigned handled;
+};
+
+#define NOUVEAU_PUSHBUF_MAX_BUFFERS 1024
+#define NOUVEAU_PUSHBUF_MAX_RELOCS 1024
+struct nouveau_pushbuf_priv {
+ struct nouveau_pushbuf base;
+
+ struct nouveau_fence *fence;
+
+ unsigned nop_jump;
+ unsigned start;
+ unsigned size;
+
+ struct nouveau_pushbuf_bo *buffers;
+ unsigned nr_buffers;
+ struct nouveau_pushbuf_reloc *relocs;
+ unsigned nr_relocs;
+};
+#define nouveau_pushbuf(n) ((struct nouveau_pushbuf_priv *)(n))
+
+#define pbbo_to_ptr(o) ((uint64_t)(unsigned long)(o))
+#define ptr_to_pbbo(h) ((struct nouveau_pushbuf_bo *)(unsigned long)(h))
+#define pbrel_to_ptr(o) ((uint64_t)(unsigned long)(o))
+#define ptr_to_pbrel(h) ((struct nouveau_pushbuf_reloc *)(unsigned long)(h))
+#define bo_to_ptr(o) ((uint64_t)(unsigned long)(o))
+#define ptr_to_bo(h) ((struct nouveau_bo_priv *)(unsigned long)(h))
+
+extern int
+nouveau_pushbuf_init(struct nouveau_channel *);
+
+extern int
+nouveau_pushbuf_flush(struct nouveau_channel *, unsigned min);
+
+extern int
+nouveau_pushbuf_emit_reloc(struct nouveau_channel *, void *ptr,
+ struct nouveau_bo *, uint32_t data, uint32_t flags,
+ uint32_t vor, uint32_t tor);
+
+struct nouveau_dma_priv {
+ uint32_t base;
+ uint32_t max;
+ uint32_t cur;
+ uint32_t put;
+ uint32_t free;
+
+ int push_free;
+} dma;
+
+struct nouveau_channel_priv {
+ struct nouveau_channel base;
+
+ struct drm_nouveau_channel_alloc drm;
+
+ uint32_t *pushbuf;
+ void *notifier_block;
+
+ volatile uint32_t *user;
+ volatile uint32_t *put;
+ volatile uint32_t *get;
+ volatile uint32_t *ref_cnt;
+
+ struct nouveau_dma_priv dma_master;
+ struct nouveau_dma_priv dma_bufmgr;
+ struct nouveau_dma_priv *dma;
+
+ struct nouveau_fence *fence_head;
+ struct nouveau_fence *fence_tail;
+ uint32_t fence_sequence;
+
+ struct nouveau_pushbuf_priv pb;
+
+ unsigned user_charge;
+};
+#define nouveau_channel(n) ((struct nouveau_channel_priv *)(n))
+
+extern int
+nouveau_channel_alloc(struct nouveau_device *, uint32_t fb, uint32_t tt,
+ struct nouveau_channel **);
+
+extern void
+nouveau_channel_free(struct nouveau_channel **);
+
+struct nouveau_grobj_priv {
+ struct nouveau_grobj base;
+};
+#define nouveau_grobj(n) ((struct nouveau_grobj_priv *)(n))
+
+extern int nouveau_grobj_alloc(struct nouveau_channel *, uint32_t handle,
+ int class, struct nouveau_grobj **);
+extern int nouveau_grobj_ref(struct nouveau_channel *, uint32_t handle,
+ struct nouveau_grobj **);
+extern void nouveau_grobj_free(struct nouveau_grobj **);
+
+
+struct nouveau_notifier_priv {
+ struct nouveau_notifier base;
+
+ struct drm_nouveau_notifierobj_alloc drm;
+ volatile void *map;
+};
+#define nouveau_notifier(n) ((struct nouveau_notifier_priv *)(n))
+
+extern int
+nouveau_notifier_alloc(struct nouveau_channel *, uint32_t handle, int count,
+ struct nouveau_notifier **);
+
+extern void
+nouveau_notifier_free(struct nouveau_notifier **);
+
+extern void
+nouveau_notifier_reset(struct nouveau_notifier *, int id);
+
+extern uint32_t
+nouveau_notifier_status(struct nouveau_notifier *, int id);
+
+extern uint32_t
+nouveau_notifier_return_val(struct nouveau_notifier *, int id);
+
+extern int
+nouveau_notifier_wait_status(struct nouveau_notifier *, int id, int status,
+ int timeout);
+
+struct nouveau_bo_priv {
+ struct nouveau_bo base;
+
+ struct nouveau_pushbuf_bo *pending;
+ struct nouveau_fence *fence;
+ struct nouveau_fence *wr_fence;
+
+ struct drm_nouveau_mem_alloc drm;
+ void *map;
+
+ void *sysmem;
+ int user;
+
+ int refcount;
+
+ uint64_t offset;
+ uint64_t flags;
+ int tiled;
+};
+#define nouveau_bo(n) ((struct nouveau_bo_priv *)(n))
+
+extern int
+nouveau_bo_init(struct nouveau_device *);
+
+extern void
+nouveau_bo_takedown(struct nouveau_device *);
+
+extern int
+nouveau_bo_new(struct nouveau_device *, uint32_t flags, int align, int size,
+ struct nouveau_bo **);
+
+extern int
+nouveau_bo_user(struct nouveau_device *, void *ptr, int size,
+ struct nouveau_bo **);
+
+extern int
+nouveau_bo_ref(struct nouveau_device *, uint64_t handle, struct nouveau_bo **);
+
+extern int
+nouveau_bo_set_status(struct nouveau_bo *, uint32_t flags);
+
+extern void
+nouveau_bo_del(struct nouveau_bo **);
+
+extern int
+nouveau_bo_map(struct nouveau_bo *, uint32_t flags);
+
+extern void
+nouveau_bo_unmap(struct nouveau_bo *);
+
+extern int
+nouveau_bo_validate(struct nouveau_channel *, struct nouveau_bo *,
+ uint32_t flags);
+
+extern int
+nouveau_resource_init(struct nouveau_resource **heap, unsigned start,
+ unsigned size);
+
+extern int
+nouveau_resource_alloc(struct nouveau_resource *heap, int size, void *priv,
+ struct nouveau_resource **);
+
+extern void
+nouveau_resource_free(struct nouveau_resource **);
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_fence.c b/src/gallium/winsys/drm/nouveau/nouveau_fence.c
new file mode 100644
index 0000000000..e7b0b4ff07
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_fence.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+#include "nouveau_local.h"
+
+static void
+nouveau_fence_del_unsignalled(struct nouveau_fence *fence)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(fence->channel);
+ struct nouveau_fence *le;
+
+ if (nvchan->fence_head == fence) {
+ nvchan->fence_head = nouveau_fence(fence)->next;
+ if (nvchan->fence_head == NULL)
+ nvchan->fence_tail = NULL;
+ return;
+ }
+
+ le = nvchan->fence_head;
+ while (le && nouveau_fence(le)->next != fence)
+ le = nouveau_fence(le)->next;
+ assert(le && nouveau_fence(le)->next == fence);
+ nouveau_fence(le)->next = nouveau_fence(fence)->next;
+ if (nvchan->fence_tail == fence)
+ nvchan->fence_tail = le;
+}
+
+static void
+nouveau_fence_del(struct nouveau_fence **fence)
+{
+ struct nouveau_fence_priv *nvfence;
+
+ if (!fence || !*fence)
+ return;
+ nvfence = nouveau_fence(*fence);
+ *fence = NULL;
+
+ if (--nvfence->refcount)
+ return;
+
+ if (nvfence->emitted && !nvfence->signalled) {
+ if (nvfence->signal_cb) {
+ nvfence->refcount++;
+ nouveau_fence_wait((void *)&nvfence);
+ return;
+ }
+
+ nouveau_fence_del_unsignalled(&nvfence->base);
+ }
+ free(nvfence);
+}
+
+int
+nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **fence)
+{
+ struct nouveau_fence_priv *nvfence;
+
+ if (!chan || !fence || *fence)
+ return -EINVAL;
+
+ nvfence = calloc(1, sizeof(struct nouveau_fence_priv));
+ if (!nvfence)
+ return -ENOMEM;
+ nvfence->base.channel = chan;
+ nvfence->refcount = 1;
+
+ *fence = &nvfence->base;
+ return 0;
+}
+
+int
+nouveau_fence_ref(struct nouveau_fence *ref, struct nouveau_fence **fence)
+{
+ struct nouveau_fence_priv *nvfence;
+
+ if (!fence)
+ return -EINVAL;
+
+ if (*fence) {
+ nouveau_fence_del(fence);
+ *fence = NULL;
+ }
+
+ if (ref) {
+ nvfence = nouveau_fence(ref);
+ nvfence->refcount++;
+ *fence = &nvfence->base;
+ }
+
+ return 0;
+}
+
+int
+nouveau_fence_signal_cb(struct nouveau_fence *fence, void (*func)(void *),
+ void *priv)
+{
+ struct nouveau_fence_priv *nvfence = nouveau_fence(fence);
+ struct nouveau_fence_cb *cb;
+
+ if (!nvfence || !func)
+ return -EINVAL;
+
+ cb = malloc(sizeof(struct nouveau_fence_cb));
+ if (!cb)
+ return -ENOMEM;
+
+ cb->func = func;
+ cb->priv = priv;
+ cb->next = nvfence->signal_cb;
+ nvfence->signal_cb = cb;
+ return 0;
+}
+
+void
+nouveau_fence_emit(struct nouveau_fence *fence)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(fence->channel);
+ struct nouveau_fence_priv *nvfence = nouveau_fence(fence);
+
+ nvfence->emitted = 1;
+ nvfence->sequence = ++nvchan->fence_sequence;
+ if (nvfence->sequence == 0xffffffff)
+ NOUVEAU_ERR("AII wrap unhandled\n");
+
+ /*XXX: assumes subc 0 is populated */
+ RING_SPACE_CH(fence->channel, 2);
+ OUT_RING_CH (fence->channel, 0x00040050);
+ OUT_RING_CH (fence->channel, nvfence->sequence);
+
+ if (nvchan->fence_tail) {
+ nouveau_fence(nvchan->fence_tail)->next = fence;
+ } else {
+ nvchan->fence_head = fence;
+ }
+ nvchan->fence_tail = fence;
+}
+
+void
+nouveau_fence_flush(struct nouveau_channel *chan)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ uint32_t sequence = *nvchan->ref_cnt;
+
+ while (nvchan->fence_head) {
+ struct nouveau_fence_priv *nvfence;
+
+ nvfence = nouveau_fence(nvchan->fence_head);
+ if (nvfence->sequence > sequence)
+ break;
+ nouveau_fence_del_unsignalled(&nvfence->base);
+ nvfence->signalled = 1;
+
+ if (nvfence->signal_cb) {
+ struct nouveau_fence *fence = NULL;
+
+ nouveau_fence_ref(&nvfence->base, &fence);
+
+ while (nvfence->signal_cb) {
+ struct nouveau_fence_cb *cb;
+
+ cb = nvfence->signal_cb;
+ nvfence->signal_cb = cb->next;
+ cb->func(cb->priv);
+ free(cb);
+ }
+
+ nouveau_fence_ref(NULL, &fence);
+ }
+ }
+}
+
+int
+nouveau_fence_wait(struct nouveau_fence **fence)
+{
+ struct nouveau_fence_priv *nvfence;
+
+ if (!fence || !*fence)
+ return -EINVAL;
+ nvfence = nouveau_fence(*fence);
+
+ if (nvfence->emitted) {
+ while (!nvfence->signalled)
+ nouveau_fence_flush(nvfence->base.channel);
+ }
+ nouveau_fence_ref(NULL, fence);
+
+ return 0;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_grobj.c b/src/gallium/winsys/drm/nouveau/nouveau_grobj.c
new file mode 100644
index 0000000000..51523897d5
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_grobj.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+
+int
+nouveau_grobj_alloc(struct nouveau_channel *chan, uint32_t handle,
+ int class, struct nouveau_grobj **grobj)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+ struct nouveau_grobj_priv *nvgrobj;
+ struct drm_nouveau_grobj_alloc g;
+ int ret;
+
+ if (!nvdev || !grobj || *grobj)
+ return -EINVAL;
+
+ nvgrobj = calloc(1, sizeof(*nvgrobj));
+ if (!nvgrobj)
+ return -ENOMEM;
+ nvgrobj->base.channel = chan;
+ nvgrobj->base.handle = handle;
+ nvgrobj->base.grclass = class;
+
+ g.channel = chan->id;
+ g.handle = handle;
+ g.class = class;
+ ret = drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GROBJ_ALLOC,
+ &g, sizeof(g));
+ if (ret) {
+ nouveau_grobj_free((void *)&nvgrobj);
+ return ret;
+ }
+
+ *grobj = &nvgrobj->base;
+ return 0;
+}
+
+int
+nouveau_grobj_ref(struct nouveau_channel *chan, uint32_t handle,
+ struct nouveau_grobj **grobj)
+{
+ struct nouveau_grobj_priv *nvgrobj;
+
+ if (!chan || !grobj || *grobj)
+ return -EINVAL;
+
+ nvgrobj = calloc(1, sizeof(struct nouveau_grobj_priv));
+ if (!nvgrobj)
+ return -ENOMEM;
+ nvgrobj->base.channel = chan;
+ nvgrobj->base.handle = handle;
+ nvgrobj->base.grclass = 0;
+
+ *grobj = &nvgrobj->base;
+ return 0;
+}
+
+void
+nouveau_grobj_free(struct nouveau_grobj **grobj)
+{
+ struct nouveau_device_priv *nvdev;
+ struct nouveau_channel_priv *chan;
+ struct nouveau_grobj_priv *nvgrobj;
+
+ if (!grobj || !*grobj)
+ return;
+ nvgrobj = nouveau_grobj(*grobj);
+ *grobj = NULL;
+
+
+ chan = nouveau_channel(nvgrobj->base.channel);
+ nvdev = nouveau_device(chan->base.device);
+
+ if (nvgrobj->base.grclass) {
+ struct drm_nouveau_gpuobj_free f;
+
+ f.channel = chan->drm.channel;
+ f.handle = nvgrobj->base.handle;
+ drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
+ &f, sizeof(f));
+ }
+ free(nvgrobj);
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_local.h b/src/gallium/winsys/drm/nouveau/nouveau_local.h
new file mode 100644
index 0000000000..e878a40803
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_local.h
@@ -0,0 +1,117 @@
+#ifndef __NOUVEAU_LOCAL_H__
+#define __NOUVEAU_LOCAL_H__
+
+#include "pipe/p_compiler.h"
+#include "nouveau_winsys_pipe.h"
+#include <stdio.h>
+
+struct pipe_buffer;
+
+/* Debug output */
+#define NOUVEAU_MSG(fmt, args...) do { \
+ fprintf(stdout, "nouveau: "fmt, ##args); \
+ fflush(stdout); \
+} while(0)
+
+#define NOUVEAU_ERR(fmt, args...) do { \
+ fprintf(stderr, "%s:%d - "fmt, __func__, __LINE__, ##args); \
+ fflush(stderr); \
+} while(0)
+
+#define NOUVEAU_TIME_MSEC() 0
+
+/* User FIFO control */
+//#define NOUVEAU_DMA_TRACE
+//#define NOUVEAU_DMA_DEBUG
+//#define NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
+#define NOUVEAU_DMA_BARRIER
+#define NOUVEAU_DMA_TIMEOUT 2000
+
+/* Push buffer access macros */
+static INLINE void
+OUT_RING(struct nouveau_channel *chan, unsigned data)
+{
+ *(chan->pushbuf->cur++) = (data);
+}
+
+static INLINE void
+OUT_RINGp(struct nouveau_channel *chan, uint32_t *data, unsigned size)
+{
+ memcpy(chan->pushbuf->cur, data, size * 4);
+ chan->pushbuf->cur += size;
+}
+
+static INLINE void
+OUT_RINGf(struct nouveau_channel *chan, float f)
+{
+ union { uint32_t i; float f; } c;
+ c.f = f;
+ OUT_RING(chan, c.i);
+}
+
+static INLINE void
+BEGIN_RING(struct nouveau_channel *chan, struct nouveau_grobj *gr,
+ unsigned mthd, unsigned size)
+{
+ if (chan->pushbuf->remaining < (size + 1))
+ nouveau_pushbuf_flush(chan, (size + 1));
+ OUT_RING(chan, (gr->subc << 13) | (size << 18) | mthd);
+ chan->pushbuf->remaining -= (size + 1);
+}
+
+static INLINE void
+FIRE_RING(struct nouveau_channel *chan)
+{
+ nouveau_pushbuf_flush(chan, 0);
+}
+
+static INLINE void
+BIND_RING(struct nouveau_channel *chan, struct nouveau_grobj *gr, unsigned subc)
+{
+ gr->subc = subc;
+ BEGIN_RING(chan, gr, 0x0000, 1);
+ OUT_RING (chan, gr->handle);
+}
+
+static INLINE void
+OUT_RELOC(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ unsigned data, unsigned flags, unsigned vor, unsigned tor)
+{
+ nouveau_pushbuf_emit_reloc(chan, chan->pushbuf->cur++, bo,
+ data, flags, vor, tor);
+}
+
+/* Raw data + flags depending on FB/TT buffer */
+static INLINE void
+OUT_RELOCd(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ unsigned data, unsigned flags, unsigned vor, unsigned tor)
+{
+ OUT_RELOC(chan, bo, data, flags | NOUVEAU_BO_OR, vor, tor);
+}
+
+/* FB/TT object handle */
+static INLINE void
+OUT_RELOCo(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ unsigned flags)
+{
+ OUT_RELOC(chan, bo, 0, flags | NOUVEAU_BO_OR,
+ chan->vram->handle, chan->gart->handle);
+}
+
+/* Low 32-bits of offset */
+static INLINE void
+OUT_RELOCl(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ unsigned delta, unsigned flags)
+{
+ OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_LOW, 0, 0);
+}
+
+/* High 32-bits of offset */
+static INLINE void
+OUT_RELOCh(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ unsigned delta, unsigned flags)
+{
+ OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_HIGH, 0, 0);
+}
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_lock.c b/src/gallium/winsys/drm/nouveau/nouveau_lock.c
new file mode 100644
index 0000000000..9adb9ac854
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_lock.c
@@ -0,0 +1,94 @@
+/**************************************************************************
+ *
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#include "main/glheader.h"
+#include "glapi/glthread.h"
+#include <GL/internal/glcore.h>
+
+#include "nouveau_context.h"
+#include "nouveau_screen.h"
+
+_glthread_DECLARE_STATIC_MUTEX( lockMutex );
+
+static void
+nouveau_contended_lock(struct nouveau_context *nv, GLuint flags)
+{
+ __DRIdrawablePrivate *dPriv = nv->dri_drawable;
+ __DRIscreenPrivate *sPriv = nv->dri_screen;
+ struct nouveau_screen *nv_screen = nv->nv_screen;
+ struct nouveau_device *dev = nv_screen->device;
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+ drmGetLock(nvdev->fd, nvdev->ctx, flags);
+
+ /* If the window moved, may need to set a new cliprect now.
+ *
+ * NOTE: This releases and regains the hw lock, so all state
+ * checking must be done *after* this call:
+ */
+ if (dPriv)
+ DRI_VALIDATE_DRAWABLE_INFO(sPriv, dPriv);
+}
+
+/* Lock the hardware and validate our state.
+ */
+void
+LOCK_HARDWARE(struct nouveau_context *nv)
+{
+ struct nouveau_screen *nv_screen = nv->nv_screen;
+ struct nouveau_device *dev = nv_screen->device;
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ char __ret=0;
+
+ _glthread_LOCK_MUTEX(lockMutex);
+ assert(!nv->locked);
+
+ DRM_CAS(nvdev->lock, nvdev->ctx,
+ (DRM_LOCK_HELD | nvdev->ctx), __ret);
+
+ if (__ret)
+ nouveau_contended_lock(nv, 0);
+ nv->locked = GL_TRUE;
+}
+
+
+ /* Unlock the hardware using the global current context
+ */
+void
+UNLOCK_HARDWARE(struct nouveau_context *nv)
+{
+ struct nouveau_screen *nv_screen = nv->nv_screen;
+ struct nouveau_device *dev = nv_screen->device;
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+ assert(nv->locked);
+ nv->locked = GL_FALSE;
+
+ DRM_UNLOCK(nvdev->fd, nvdev->lock, nvdev->ctx);
+
+ _glthread_UNLOCK_MUTEX(lockMutex);
+}
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_notifier.c b/src/gallium/winsys/drm/nouveau/nouveau_notifier.c
new file mode 100644
index 0000000000..01e8f38440
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_notifier.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_local.h"
+
+#define NOTIFIER(__v) \
+ struct nouveau_notifier_priv *nvnotify = nouveau_notifier(notifier); \
+ volatile uint32_t *__v = (void*)nvnotify->map + (id * 32)
+
+int
+nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
+ int count, struct nouveau_notifier **notifier)
+{
+ struct nouveau_notifier_priv *nvnotify;
+ int ret;
+
+ if (!chan || !notifier || *notifier)
+ return -EINVAL;
+
+ nvnotify = calloc(1, sizeof(struct nouveau_notifier_priv));
+ if (!nvnotify)
+ return -ENOMEM;
+ nvnotify->base.channel = chan;
+ nvnotify->base.handle = handle;
+
+ nvnotify->drm.channel = chan->id;
+ nvnotify->drm.handle = handle;
+ nvnotify->drm.count = count;
+ if ((ret = drmCommandWriteRead(nouveau_device(chan->device)->fd,
+ DRM_NOUVEAU_NOTIFIEROBJ_ALLOC,
+ &nvnotify->drm,
+ sizeof(nvnotify->drm)))) {
+ nouveau_notifier_free((void *)&nvnotify);
+ return ret;
+ }
+
+ nvnotify->map = (void *)nouveau_channel(chan)->notifier_block +
+ nvnotify->drm.offset;
+ *notifier = &nvnotify->base;
+ return 0;
+}
+
+void
+nouveau_notifier_free(struct nouveau_notifier **notifier)
+{
+
+ struct nouveau_notifier_priv *nvnotify;
+ struct nouveau_channel_priv *nvchan;
+ struct nouveau_device_priv *nvdev;
+ struct drm_nouveau_gpuobj_free f;
+
+ if (!notifier || !*notifier)
+ return;
+ nvnotify = nouveau_notifier(*notifier);
+ *notifier = NULL;
+
+ nvchan = nouveau_channel(nvnotify->base.channel);
+ nvdev = nouveau_device(nvchan->base.device);
+
+ f.channel = nvchan->drm.channel;
+ f.handle = nvnotify->base.handle;
+ drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GPUOBJ_FREE, &f, sizeof(f));
+ free(nvnotify);
+}
+
+void
+nouveau_notifier_reset(struct nouveau_notifier *notifier, int id)
+{
+ NOTIFIER(n);
+
+ n[NV_NOTIFY_TIME_0 /4] = 0x00000000;
+ n[NV_NOTIFY_TIME_1 /4] = 0x00000000;
+ n[NV_NOTIFY_RETURN_VALUE/4] = 0x00000000;
+ n[NV_NOTIFY_STATE /4] = (NV_NOTIFY_STATE_STATUS_IN_PROCESS <<
+ NV_NOTIFY_STATE_STATUS_SHIFT);
+}
+
+uint32_t
+nouveau_notifier_status(struct nouveau_notifier *notifier, int id)
+{
+ NOTIFIER(n);
+
+ return n[NV_NOTIFY_STATE/4] >> NV_NOTIFY_STATE_STATUS_SHIFT;
+}
+
+uint32_t
+nouveau_notifier_return_val(struct nouveau_notifier *notifier, int id)
+{
+ NOTIFIER(n);
+
+ return n[NV_NOTIFY_RETURN_VALUE/4];
+}
+
+int
+nouveau_notifier_wait_status(struct nouveau_notifier *notifier, int id,
+ int status, int timeout)
+{
+ NOTIFIER(n);
+ uint32_t time = 0, t_start = NOUVEAU_TIME_MSEC();
+
+ while (time <= timeout) {
+ uint32_t v;
+
+ v = n[NV_NOTIFY_STATE/4] >> NV_NOTIFY_STATE_STATUS_SHIFT;
+ if (v == status)
+ return 0;
+
+ if (timeout)
+ time = NOUVEAU_TIME_MSEC() - t_start;
+ }
+
+ return -EBUSY;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_pushbuf.c b/src/gallium/winsys/drm/nouveau/nouveau_pushbuf.c
new file mode 100644
index 0000000000..815046ba85
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_pushbuf.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_dma.h"
+
+#define PB_BUFMGR_DWORDS (4096 / 2)
+#define PB_MIN_USER_DWORDS 2048
+
+static int
+nouveau_pushbuf_space(struct nouveau_channel *chan, unsigned min)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
+
+ assert((min + 1) <= nvchan->dma->max);
+
+ /* Wait for enough space in push buffer */
+ min = min < PB_MIN_USER_DWORDS ? PB_MIN_USER_DWORDS : min;
+ min += 1; /* a bit extra for the NOP */
+ if (nvchan->dma->free < min)
+ WAIT_RING_CH(chan, min);
+
+ /* Insert NOP, may turn into a jump later */
+ RING_SPACE_CH(chan, 1);
+ nvpb->nop_jump = nvchan->dma->cur;
+ OUT_RING_CH(chan, 0);
+
+ /* Any remaining space is available to the user */
+ nvpb->start = nvchan->dma->cur;
+ nvpb->size = nvchan->dma->free;
+ nvpb->base.channel = chan;
+ nvpb->base.remaining = nvpb->size;
+ nvpb->base.cur = &nvchan->pushbuf[nvpb->start];
+
+ /* Create a new fence object for this "frame" */
+ nouveau_fence_ref(NULL, &nvpb->fence);
+ nouveau_fence_new(chan, &nvpb->fence);
+
+ return 0;
+}
+
+int
+nouveau_pushbuf_init(struct nouveau_channel *chan)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_dma_priv *m = &nvchan->dma_master;
+ struct nouveau_dma_priv *b = &nvchan->dma_bufmgr;
+ int i;
+
+ if (!nvchan)
+ return -EINVAL;
+
+ /* Reassign last bit of push buffer for a "separate" bufmgr
+ * ring buffer
+ */
+ m->max -= PB_BUFMGR_DWORDS;
+ m->free -= PB_BUFMGR_DWORDS;
+
+ b->base = m->base + ((m->max + 2) << 2);
+ b->max = PB_BUFMGR_DWORDS - 2;
+ b->cur = b->put = 0;
+ b->free = b->max - b->cur;
+
+ /* Some NOPs just to be safe
+ *XXX: RING_SKIPS
+ */
+ nvchan->dma = b;
+ RING_SPACE_CH(chan, 8);
+ for (i = 0; i < 8; i++)
+ OUT_RING_CH(chan, 0);
+ nvchan->dma = m;
+
+ nouveau_pushbuf_space(chan, 0);
+ chan->pushbuf = &nvchan->pb.base;
+
+ nvchan->pb.buffers = calloc(NOUVEAU_PUSHBUF_MAX_BUFFERS,
+ sizeof(struct nouveau_pushbuf_bo));
+ nvchan->pb.relocs = calloc(NOUVEAU_PUSHBUF_MAX_RELOCS,
+ sizeof(struct nouveau_pushbuf_reloc));
+ return 0;
+}
+
+static uint32_t
+nouveau_pushbuf_calc_reloc(struct nouveau_bo *bo,
+ struct nouveau_pushbuf_reloc *r)
+{
+ uint32_t push;
+
+ if (r->flags & NOUVEAU_BO_LOW) {
+ push = bo->offset + r->data;
+ } else
+ if (r->flags & NOUVEAU_BO_HIGH) {
+ push = (bo->offset + r->data) >> 32;
+ } else {
+ push = r->data;
+ }
+
+ if (r->flags & NOUVEAU_BO_OR) {
+ if (bo->flags & NOUVEAU_BO_VRAM)
+ push |= r->vor;
+ else
+ push |= r->tor;
+ }
+
+ return push;
+}
+
+/* This would be our TTM "superioctl" */
+int
+nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
+{
+ struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+ struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
+ int ret, i;
+
+ if (nvpb->base.remaining == nvpb->size)
+ return 0;
+
+ nouveau_fence_flush(chan);
+
+ nvpb->size -= nvpb->base.remaining;
+ nvchan->dma->cur += nvpb->size;
+ nvchan->dma->free -= nvpb->size;
+ assert(nvchan->dma->cur <= nvchan->dma->max);
+
+ nvchan->dma = &nvchan->dma_bufmgr;
+ nvchan->pushbuf[nvpb->nop_jump] = 0x20000000 |
+ (nvchan->dma->base + (nvchan->dma->cur << 2));
+
+ /* Validate buffers + apply relocations */
+ nvchan->user_charge = 0;
+ for (i = 0; i < nvpb->nr_relocs; i++) {
+ struct nouveau_pushbuf_reloc *r = &nvpb->relocs[i];
+ struct nouveau_pushbuf_bo *pbbo = r->pbbo;
+ struct nouveau_bo *bo = pbbo->bo;
+
+ /* Validated, mem matches presumed, no relocation necessary */
+ if (pbbo->handled & 2) {
+ if (!(pbbo->handled & 1))
+ assert(0);
+ continue;
+ }
+
+ /* Not yet validated, do it now */
+ if (!(pbbo->handled & 1)) {
+ ret = nouveau_bo_validate(chan, bo, pbbo->flags);
+ if (ret) {
+ assert(0);
+ return ret;
+ }
+ pbbo->handled |= 1;
+
+ if (bo->offset == nouveau_bo(bo)->offset &&
+ bo->flags == nouveau_bo(bo)->flags) {
+ pbbo->handled |= 2;
+ continue;
+ }
+ bo->offset = nouveau_bo(bo)->offset;
+ bo->flags = nouveau_bo(bo)->flags;
+ }
+
+ /* Apply the relocation */
+ *r->ptr = nouveau_pushbuf_calc_reloc(bo, r);
+ }
+ nvpb->nr_relocs = 0;
+
+ /* Dereference all buffers on validate list */
+ for (i = 0; i < nvpb->nr_buffers; i++) {
+ struct nouveau_pushbuf_bo *pbbo = &nvpb->buffers[i];
+
+ nouveau_bo(pbbo->bo)->pending = NULL;
+ nouveau_bo_del(&pbbo->bo);
+ }
+ nvpb->nr_buffers = 0;
+
+ /* Switch back to user's ring */
+ RING_SPACE_CH(chan, 1);
+ OUT_RING_CH(chan, 0x20000000 | ((nvpb->start << 2) +
+ nvchan->dma_master.base));
+ nvchan->dma = &nvchan->dma_master;
+
+ /* Fence + kickoff */
+ nouveau_fence_emit(nvpb->fence);
+ FIRE_RING_CH(chan);
+
+ /* Allocate space for next push buffer */
+ ret = nouveau_pushbuf_space(chan, min);
+ assert(!ret);
+
+ return 0;
+}
+
+static struct nouveau_pushbuf_bo *
+nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ struct nouveau_pushbuf_bo *pbbo;
+
+ if (nvbo->pending)
+ return nvbo->pending;
+
+ if (nvpb->nr_buffers >= NOUVEAU_PUSHBUF_MAX_BUFFERS)
+ return NULL;
+ pbbo = nvpb->buffers + nvpb->nr_buffers++;
+ nvbo->pending = pbbo;
+
+ nouveau_bo_ref(bo->device, bo->handle, &pbbo->bo);
+ pbbo->channel = chan;
+ pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
+ pbbo->handled = 0;
+ return pbbo;
+}
+
+int
+nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
+ struct nouveau_bo *bo, uint32_t data, uint32_t flags,
+ uint32_t vor, uint32_t tor)
+{
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
+ struct nouveau_pushbuf_bo *pbbo;
+ struct nouveau_pushbuf_reloc *r;
+
+ if (nvpb->nr_relocs >= NOUVEAU_PUSHBUF_MAX_RELOCS)
+ return -ENOMEM;
+
+ pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
+ if (!pbbo)
+ return -ENOMEM;
+ pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
+ pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
+
+ r = nvpb->relocs + nvpb->nr_relocs++;
+ r->pbbo = pbbo;
+ r->ptr = ptr;
+ r->flags = flags;
+ r->data = data;
+ r->vor = vor;
+ r->tor = tor;
+
+ if (flags & NOUVEAU_BO_DUMMY)
+ *(uint32_t *)ptr = 0;
+ else
+ *(uint32_t *)ptr = nouveau_pushbuf_calc_reloc(bo, r);
+ return 0;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_resource.c b/src/gallium/winsys/drm/nouveau/nouveau_resource.c
new file mode 100644
index 0000000000..3bbcb5c45e
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_resource.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2007 Nouveau Project
+ *
+ * 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 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.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "nouveau_drmif.h"
+#include "nouveau_local.h"
+
+int
+nouveau_resource_init(struct nouveau_resource **heap,
+ unsigned start, unsigned size)
+{
+ struct nouveau_resource *r;
+
+ r = calloc(1, sizeof(struct nouveau_resource));
+ if (!r)
+ return 1;
+
+ r->start = start;
+ r->size = size;
+ *heap = r;
+ return 0;
+}
+
+int
+nouveau_resource_alloc(struct nouveau_resource *heap, int size, void *priv,
+ struct nouveau_resource **res)
+{
+ struct nouveau_resource *r;
+
+ if (!heap || !size || !res || *res)
+ return 1;
+
+ while (heap) {
+ if (!heap->in_use && heap->size >= size) {
+ r = calloc(1, sizeof(struct nouveau_resource));
+ if (!r)
+ return 1;
+
+ r->start = (heap->start + heap->size) - size;
+ r->size = size;
+ r->in_use = 1;
+ r->priv = priv;
+
+ heap->size -= size;
+
+ r->next = heap->next;
+ if (heap->next)
+ heap->next->prev = r;
+ r->prev = heap;
+ heap->next = r;
+
+ *res = r;
+ return 0;
+ }
+
+ heap = heap->next;
+ }
+
+ return 1;
+}
+
+void
+nouveau_resource_free(struct nouveau_resource **res)
+{
+ struct nouveau_resource *r;
+
+ if (!res || !*res)
+ return;
+ r = *res;
+ *res = NULL;
+
+ r->in_use = 0;
+
+ if (r->next && !r->next->in_use) {
+ struct nouveau_resource *new = r->next;
+
+ new->prev = r->prev;
+ if (r->prev)
+ r->prev->next = new;
+ new->size += r->size;
+ new->start = r->start;
+
+ free(r);
+ r = new;
+ }
+
+ if (r->prev && !r->prev->in_use) {
+ r->prev->next = r->next;
+ if (r->next)
+ r->next->prev = r->prev;
+ r->prev->size += r->size;
+ free(r);
+ }
+
+}
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_screen.c b/src/gallium/winsys/drm/nouveau/nouveau_screen.c
new file mode 100644
index 0000000000..df1fe7e69b
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_screen.c
@@ -0,0 +1,310 @@
+#include "utils.h"
+#include "vblank.h"
+#include "xmlpool.h"
+
+#include "pipe/p_context.h"
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_cb_fbo.h"
+
+#include "nouveau_context.h"
+#include "nouveau_drm.h"
+#include "nouveau_dri.h"
+#include "nouveau_local.h"
+#include "nouveau_screen.h"
+#include "nouveau_swapbuffers.h"
+
+#if NOUVEAU_DRM_HEADER_PATCHLEVEL != 11
+#error nouveau_drm.h version does not match expected version
+#endif
+
+/* Extension stuff, enabling of extensions handled by Gallium's GL state
+ * tracker. But, we still need to define the entry points we want.
+ */
+#define need_GL_ARB_fragment_program
+#define need_GL_ARB_multisample
+#define need_GL_ARB_occlusion_query
+#define need_GL_ARB_point_parameters
+#define need_GL_ARB_shader_objects
+#define need_GL_ARB_texture_compression
+#define need_GL_ARB_vertex_program
+#define need_GL_ARB_vertex_shader
+#define need_GL_ARB_vertex_buffer_object
+#define need_GL_EXT_compiled_vertex_array
+#define need_GL_EXT_fog_coord
+#define need_GL_EXT_secondary_color
+#define need_GL_EXT_framebuffer_object
+#define need_GL_VERSION_2_0
+#define need_GL_VERSION_2_1
+#include "extension_helper.h"
+
+const struct dri_extension card_extensions[] =
+{
+ { "GL_ARB_multisample", GL_ARB_multisample_functions },
+ { "GL_ARB_occlusion_query", GL_ARB_occlusion_query_functions },
+ { "GL_ARB_point_parameters", GL_ARB_point_parameters_functions },
+ { "GL_ARB_shader_objects", GL_ARB_shader_objects_functions },
+ { "GL_ARB_shading_language_100", GL_VERSION_2_0_functions },
+ { "GL_ARB_shading_language_120", GL_VERSION_2_1_functions },
+ { "GL_ARB_texture_compression", GL_ARB_texture_compression_functions },
+ { "GL_ARB_vertex_program", GL_ARB_vertex_program_functions },
+ { "GL_ARB_vertex_shader", GL_ARB_vertex_shader_functions },
+ { "GL_ARB_vertex_buffer_object", GL_ARB_vertex_buffer_object_functions },
+ { "GL_EXT_compiled_vertex_array", GL_EXT_compiled_vertex_array_functions },
+ { "GL_EXT_fog_coord", GL_EXT_fog_coord_functions },
+ { "GL_EXT_framebuffer_object", GL_EXT_framebuffer_object_functions },
+ { "GL_EXT_secondary_color", GL_EXT_secondary_color_functions },
+ { NULL, 0 }
+};
+
+PUBLIC const char __driConfigOptions[] =
+DRI_CONF_BEGIN
+DRI_CONF_END;
+static const GLuint __driNConfigOptions = 0;
+
+extern const struct dri_extension common_extensions[];
+extern const struct dri_extension nv40_extensions[];
+
+static GLboolean
+nouveau_screen_create(__DRIscreenPrivate *driScrnPriv)
+{
+ struct nouveau_dri *nv_dri = driScrnPriv->pDevPriv;
+ struct nouveau_screen *nv_screen;
+ int ret;
+
+ if (driScrnPriv->devPrivSize != sizeof(struct nouveau_dri)) {
+ NOUVEAU_ERR("DRI struct mismatch between DDX/DRI\n");
+ return GL_FALSE;
+ }
+
+ nv_screen = CALLOC_STRUCT(nouveau_screen);
+ if (!nv_screen)
+ return GL_FALSE;
+ nv_screen->driScrnPriv = driScrnPriv;
+ driScrnPriv->private = (void *)nv_screen;
+
+ driParseOptionInfo(&nv_screen->option_cache,
+ __driConfigOptions, __driNConfigOptions);
+
+ if ((ret = nouveau_device_open_existing(&nv_screen->device, 0,
+ driScrnPriv->fd, 0))) {
+ NOUVEAU_ERR("Failed opening nouveau device: %d\n", ret);
+ return GL_FALSE;
+ }
+
+ nv_screen->front_offset = nv_dri->front_offset;
+ nv_screen->front_pitch = nv_dri->front_pitch * (nv_dri->bpp / 8);
+ nv_screen->front_cpp = nv_dri->bpp / 8;
+ nv_screen->front_height = nv_dri->height;
+
+ return GL_TRUE;
+}
+
+static void
+nouveau_screen_destroy(__DRIscreenPrivate *driScrnPriv)
+{
+ struct nouveau_screen *nv_screen = driScrnPriv->private;
+
+ driScrnPriv->private = NULL;
+ FREE(nv_screen);
+}
+
+static GLboolean
+nouveau_create_buffer(__DRIscreenPrivate * driScrnPriv,
+ __DRIdrawablePrivate * driDrawPriv,
+ const __GLcontextModes *glVis, GLboolean pixmapBuffer)
+{
+ struct nouveau_framebuffer *nvfb;
+ enum pipe_format colour, depth, stencil;
+
+ if (pixmapBuffer)
+ return GL_FALSE;
+
+ nvfb = CALLOC_STRUCT(nouveau_framebuffer);
+ if (!nvfb)
+ return GL_FALSE;
+
+ if (glVis->redBits == 5)
+ colour = PIPE_FORMAT_R5G6B5_UNORM;
+ else
+ colour = PIPE_FORMAT_A8R8G8B8_UNORM;
+
+ if (glVis->depthBits == 16)
+ depth = PIPE_FORMAT_Z16_UNORM;
+ else if (glVis->depthBits == 24)
+ depth = PIPE_FORMAT_Z24S8_UNORM;
+ else
+ depth = PIPE_FORMAT_NONE;
+
+ if (glVis->stencilBits == 8)
+ stencil = PIPE_FORMAT_Z24S8_UNORM;
+ else
+ stencil = PIPE_FORMAT_NONE;
+
+ nvfb->stfb = st_create_framebuffer(glVis, colour, depth, stencil,
+ driDrawPriv->w, driDrawPriv->h,
+ (void*)nvfb);
+ if (!nvfb->stfb) {
+ free(nvfb);
+ return GL_FALSE;
+ }
+
+ driDrawPriv->driverPrivate = (void *)nvfb;
+ return GL_TRUE;
+}
+
+static void
+nouveau_destroy_buffer(__DRIdrawablePrivate * driDrawPriv)
+{
+ struct nouveau_framebuffer *nvfb;
+
+ nvfb = (struct nouveau_framebuffer *)driDrawPriv->driverPrivate;
+ st_unreference_framebuffer(&nvfb->stfb);
+ free(nvfb);
+}
+
+static struct __DriverAPIRec
+nouveau_api = {
+ .InitDriver = nouveau_screen_create,
+ .DestroyScreen = nouveau_screen_destroy,
+ .CreateContext = nouveau_context_create,
+ .DestroyContext = nouveau_context_destroy,
+ .CreateBuffer = nouveau_create_buffer,
+ .DestroyBuffer = nouveau_destroy_buffer,
+ .SwapBuffers = nouveau_swap_buffers,
+ .MakeCurrent = nouveau_context_bind,
+ .UnbindContext = nouveau_context_unbind,
+ .GetSwapInfo = NULL,
+ .GetMSC = NULL,
+ .WaitForMSC = NULL,
+ .WaitForSBC = NULL,
+ .SwapBuffersMSC = NULL,
+ .CopySubBuffer = nouveau_copy_sub_buffer,
+ .setTexOffset = NULL
+};
+
+static __GLcontextModes *
+nouveau_fill_in_modes(unsigned pixel_bits, unsigned depth_bits,
+ unsigned stencil_bits, GLboolean have_back_buffer)
+{
+ __GLcontextModes * modes;
+ __GLcontextModes * m;
+ unsigned num_modes;
+ unsigned depth_buffer_factor;
+ unsigned back_buffer_factor;
+ int i;
+
+ static const struct {
+ GLenum format;
+ GLenum type;
+ } fb_format_array[] = {
+ { GL_RGB , GL_UNSIGNED_SHORT_5_6_5 },
+ { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV },
+ { GL_BGR , GL_UNSIGNED_INT_8_8_8_8_REV },
+ };
+
+ /* GLX_SWAP_COPY_OML is only supported because the Intel driver doesn't
+ * support pageflipping at all.
+ */
+ static const GLenum back_buffer_modes[] = {
+ GLX_NONE, GLX_SWAP_UNDEFINED_OML, GLX_SWAP_COPY_OML
+ };
+
+ uint8_t depth_bits_array[4] = { 0, 16, 24, 24 };
+ uint8_t stencil_bits_array[4] = { 0, 0, 0, 8 };
+ uint8_t msaa_samples_array[1] = { 0 };
+
+ depth_buffer_factor = 4;
+ back_buffer_factor = (have_back_buffer) ? 3 : 1;
+
+ num_modes = ((pixel_bits==16) ? 1 : 2) *
+ depth_buffer_factor * back_buffer_factor * 4;
+ modes = (*dri_interface->createContextModes)(num_modes,
+ sizeof(__GLcontextModes));
+ m = modes;
+
+ for (i=((pixel_bits==16)?0:1);i<((pixel_bits==16)?1:3);i++) {
+ if (!driFillInModes(&m, fb_format_array[i].format,
+ fb_format_array[i].type,
+ depth_bits_array,
+ stencil_bits_array,
+ depth_buffer_factor,
+ back_buffer_modes,
+ back_buffer_factor,
+ msaa_samples_array, 1,
+ GLX_TRUE_COLOR)) {
+ fprintf( stderr, "[%s:%u] Error creating FBConfig!\n",
+ __func__, __LINE__ );
+ return NULL;
+ }
+
+ if (!driFillInModes(&m, fb_format_array[i].format,
+ fb_format_array[i].type,
+ depth_bits_array,
+ stencil_bits_array,
+ depth_buffer_factor,
+ back_buffer_modes,
+ back_buffer_factor,
+ msaa_samples_array, 1,
+ GLX_DIRECT_COLOR)) {
+ fprintf( stderr, "[%s:%u] Error creating FBConfig!\n",
+ __func__, __LINE__ );
+ return NULL;
+ }
+ }
+
+ return modes;
+}
+PUBLIC void *
+__driCreateNewScreen_20050727(__DRInativeDisplay *dpy, int scrn,
+ __DRIscreen *psc, const __GLcontextModes * modes,
+ const __DRIversion * ddx_version,
+ const __DRIversion * dri_version,
+ const __DRIversion * drm_version,
+ const __DRIframebuffer * frame_buffer,
+ void * pSAREA, int fd, int internal_api_version,
+ const __DRIinterfaceMethods * interface,
+ __GLcontextModes ** driver_modes)
+{
+ __DRIscreenPrivate *psp;
+ static const __DRIversion ddx_expected =
+ { 0, 0, NOUVEAU_DRM_HEADER_PATCHLEVEL };
+ static const __DRIversion dri_expected = { 4, 0, 0 };
+ static const __DRIversion drm_expected =
+ { 0, 0, NOUVEAU_DRM_HEADER_PATCHLEVEL };
+ struct nouveau_dri *nv_dri = NULL;
+
+ dri_interface = interface;
+
+ if (!driCheckDriDdxDrmVersions2("nouveau",
+ dri_version, &dri_expected,
+ ddx_version, &ddx_expected,
+ drm_version, &drm_expected)) {
+ return NULL;
+ }
+
+ if (drm_expected.patch != drm_version->patch) {
+ fprintf(stderr, "Incompatible DRM patch level.\n"
+ "Expected: %d\n" "Current : %d\n",
+ drm_expected.patch, drm_version->patch);
+ return NULL;
+ }
+
+ psp = __driUtilCreateNewScreen(dpy, scrn, psc, NULL,
+ ddx_version, dri_version, drm_version,
+ frame_buffer, pSAREA, fd,
+ internal_api_version,
+ &nouveau_api);
+ if (psp == NULL)
+ return NULL;
+ nv_dri = psp->pDevPriv;
+
+ *driver_modes = nouveau_fill_in_modes(nv_dri->bpp,
+ (nv_dri->bpp == 16) ? 16 : 24,
+ (nv_dri->bpp == 16) ? 0 : 8,
+ 1);
+
+ driInitExtensions(NULL, card_extensions, GL_FALSE);
+
+ return (void *)psp;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_screen.h b/src/gallium/winsys/drm/nouveau/nouveau_screen.h
new file mode 100644
index 0000000000..388d6be9bb
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_screen.h
@@ -0,0 +1,20 @@
+#ifndef __NOUVEAU_SCREEN_H__
+#define __NOUVEAU_SCREEN_H__
+
+#include "xmlconfig.h"
+
+struct nouveau_screen {
+ __DRIscreenPrivate *driScrnPriv;
+ driOptionCache option_cache;
+
+ struct nouveau_device *device;
+
+ uint32_t front_offset;
+ uint32_t front_pitch;
+ uint32_t front_cpp;
+ uint32_t front_height;
+
+ void *nvc;
+};
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.c b/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.c
new file mode 100644
index 0000000000..70e0104e83
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.c
@@ -0,0 +1,86 @@
+#include "main/glheader.h"
+#include "glapi/glthread.h"
+#include <GL/internal/glcore.h>
+
+#include "pipe/p_context.h"
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_context.h"
+#include "state_tracker/st_cb_fbo.h"
+
+#include "nouveau_context.h"
+#include "nouveau_local.h"
+#include "nouveau_screen.h"
+#include "nouveau_swapbuffers.h"
+
+void
+nouveau_copy_buffer(__DRIdrawablePrivate *dPriv, struct pipe_surface *surf,
+ const drm_clip_rect_t *rect)
+{
+ struct nouveau_context *nv = dPriv->driContextPriv->driverPrivate;
+ drm_clip_rect_t *pbox;
+ int nbox, i;
+
+ LOCK_HARDWARE(nv);
+ if (!dPriv->numClipRects) {
+ UNLOCK_HARDWARE(nv);
+ return;
+ }
+ pbox = dPriv->pClipRects;
+ nbox = dPriv->numClipRects;
+
+ nv->surface_copy_prep(nv, nv->frontbuffer, surf);
+ for (i = 0; i < nbox; i++, pbox++) {
+ int sx, sy, dx, dy, w, h;
+
+ sx = pbox->x1 - dPriv->x;
+ sy = pbox->y1 - dPriv->y;
+ dx = pbox->x1;
+ dy = pbox->y1;
+ w = pbox->x2 - pbox->x1;
+ h = pbox->y2 - pbox->y1;
+
+ nv->surface_copy(nv, dx, dy, sx, sy, w, h);
+ }
+
+ FIRE_RING(nv->nvc->channel);
+ UNLOCK_HARDWARE(nv);
+
+ if (nv->last_stamp != dPriv->lastStamp) {
+ struct nouveau_framebuffer *nvfb = dPriv->driverPrivate;
+ st_resize_framebuffer(nvfb->stfb, dPriv->w, dPriv->h);
+ nv->last_stamp = dPriv->lastStamp;
+ }
+}
+
+void
+nouveau_copy_sub_buffer(__DRIdrawablePrivate *dPriv, int x, int y, int w, int h)
+{
+ struct nouveau_framebuffer *nvfb = dPriv->driverPrivate;
+ struct pipe_surface *surf;
+
+ surf = st_get_framebuffer_surface(nvfb->stfb, ST_SURFACE_BACK_LEFT);
+ if (surf) {
+ drm_clip_rect_t rect;
+ rect.x1 = x;
+ rect.y1 = y;
+ rect.x2 = x + w;
+ rect.y2 = y + h;
+
+ st_notify_swapbuffers(nvfb->stfb);
+ nouveau_copy_buffer(dPriv, surf, &rect);
+ }
+}
+
+void
+nouveau_swap_buffers(__DRIdrawablePrivate *dPriv)
+{
+ struct nouveau_framebuffer *nvfb = dPriv->driverPrivate;
+ struct pipe_surface *surf;
+
+ surf = st_get_framebuffer_surface(nvfb->stfb, ST_SURFACE_BACK_LEFT);
+ if (surf) {
+ st_notify_swapbuffers(nvfb->stfb);
+ nouveau_copy_buffer(dPriv, surf, NULL);
+ }
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.h b/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.h
new file mode 100644
index 0000000000..825d3da6da
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_swapbuffers.h
@@ -0,0 +1,10 @@
+#ifndef __NOUVEAU_SWAPBUFFERS_H__
+#define __NOUVEAU_SWAPBUFFERS_H__
+
+extern void nouveau_copy_buffer(__DRIdrawablePrivate *, struct pipe_surface *,
+ const drm_clip_rect_t *);
+extern void nouveau_copy_sub_buffer(__DRIdrawablePrivate *,
+ int x, int y, int w, int h);
+extern void nouveau_swap_buffers(__DRIdrawablePrivate *);
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_winsys.c b/src/gallium/winsys/drm/nouveau/nouveau_winsys.c
new file mode 100644
index 0000000000..0878840dcc
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_winsys.c
@@ -0,0 +1,158 @@
+#include "util/u_memory.h"
+
+#include "nouveau_context.h"
+#include "nouveau_screen.h"
+#include "nouveau_winsys_pipe.h"
+
+#include "nouveau/nouveau_winsys.h"
+
+static int
+nouveau_pipe_notifier_alloc(struct nouveau_winsys *nvws, int count,
+ struct nouveau_notifier **notify)
+{
+ struct nouveau_context *nv = nvws->nv;
+
+ return nouveau_notifier_alloc(nv->nvc->channel, nv->nvc->next_handle++,
+ count, notify);
+}
+
+static int
+nouveau_pipe_grobj_alloc(struct nouveau_winsys *nvws, int grclass,
+ struct nouveau_grobj **grobj)
+{
+ struct nouveau_context *nv = nvws->nv;
+ struct nouveau_channel *chan = nv->nvc->channel;
+ int ret;
+
+ ret = nouveau_grobj_alloc(chan, nv->nvc->next_handle++,
+ grclass, grobj);
+ if (ret)
+ return ret;
+
+ assert(nv->nvc->next_subchannel < 7);
+ BIND_RING(chan, *grobj, nv->nvc->next_subchannel++);
+ return 0;
+}
+
+static int
+nouveau_pipe_surface_copy(struct nouveau_winsys *nvws, struct pipe_surface *dst,
+ unsigned dx, unsigned dy, struct pipe_surface *src,
+ unsigned sx, unsigned sy, unsigned w, unsigned h)
+{
+ struct nouveau_context *nv = nvws->nv;
+
+ if (nv->surface_copy_prep(nv, dst, src))
+ return 1;
+ nv->surface_copy(nv, dx, dy, sx, sy, w, h);
+ nv->surface_copy_done(nv);
+
+ return 0;
+}
+
+static int
+nouveau_pipe_surface_fill(struct nouveau_winsys *nvws, struct pipe_surface *dst,
+ unsigned dx, unsigned dy, unsigned w, unsigned h,
+ unsigned value)
+{
+ if (nvws->nv->surface_fill(nvws->nv, dst, dx, dy, w, h, value))
+ return 1;
+ return 0;
+}
+
+static int
+nouveau_pipe_push_reloc(struct nouveau_winsys *nvws, void *ptr,
+ struct pipe_buffer *buf, uint32_t data,
+ uint32_t flags, uint32_t vor, uint32_t tor)
+{
+ return nouveau_pushbuf_emit_reloc(nvws->channel, ptr,
+ nouveau_buffer(buf)->bo,
+ data, flags, vor, tor);
+}
+
+static int
+nouveau_pipe_push_flush(struct nouveau_winsys *nvws, unsigned size,
+ struct pipe_fence_handle **fence)
+{
+ if (fence) {
+ struct nouveau_pushbuf *pb = nvws->channel->pushbuf;
+ struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(pb);
+ struct nouveau_fence *ref = NULL;
+
+ nouveau_fence_ref(nvpb->fence, &ref);
+ *fence = (struct pipe_fence_handle *)ref;
+ }
+
+ return nouveau_pushbuf_flush(nvws->channel, size);
+}
+
+struct pipe_context *
+nouveau_pipe_create(struct nouveau_context *nv)
+{
+ struct nouveau_channel_context *nvc = nv->nvc;
+ struct nouveau_winsys *nvws = CALLOC_STRUCT(nouveau_winsys);
+ struct pipe_screen *(*hws_create)(struct pipe_winsys *,
+ struct nouveau_winsys *);
+ struct pipe_context *(*hw_create)(struct pipe_screen *, unsigned);
+ struct pipe_winsys *ws;
+ unsigned chipset = nv->nv_screen->device->chipset;
+
+ if (!nvws)
+ return NULL;
+
+ switch (chipset & 0xf0) {
+ case 0x10:
+ case 0x20:
+ hws_create = nv10_screen_create;
+ hw_create = nv10_create;
+ break;
+ case 0x30:
+ hws_create = nv30_screen_create;
+ hw_create = nv30_create;
+ break;
+ case 0x40:
+ case 0x60:
+ hws_create = nv40_screen_create;
+ hw_create = nv40_create;
+ break;
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ hws_create = nv50_screen_create;
+ hw_create = nv50_create;
+ break;
+ default:
+ NOUVEAU_ERR("Unknown chipset NV%02x\n", chipset);
+ return NULL;
+ }
+
+ nvws->nv = nv;
+ nvws->channel = nv->nvc->channel;
+
+ nvws->res_init = nouveau_resource_init;
+ nvws->res_alloc = nouveau_resource_alloc;
+ nvws->res_free = nouveau_resource_free;
+
+ nvws->push_reloc = nouveau_pipe_push_reloc;
+ nvws->push_flush = nouveau_pipe_push_flush;
+
+ nvws->grobj_alloc = nouveau_pipe_grobj_alloc;
+ nvws->grobj_free = nouveau_grobj_free;
+
+ nvws->notifier_alloc = nouveau_pipe_notifier_alloc;
+ nvws->notifier_free = nouveau_notifier_free;
+ nvws->notifier_reset = nouveau_notifier_reset;
+ nvws->notifier_status = nouveau_notifier_status;
+ nvws->notifier_retval = nouveau_notifier_return_val;
+ nvws->notifier_wait = nouveau_notifier_wait_status;
+
+ nvws->surface_copy = nouveau_pipe_surface_copy;
+ nvws->surface_fill = nouveau_pipe_surface_fill;
+
+ ws = nouveau_create_pipe_winsys(nv);
+
+ if (!nvc->pscreen)
+ nvc->pscreen = hws_create(ws, nvws);
+ nvc->pctx[nv->pctx_id] = hw_create(nvc->pscreen, nv->pctx_id);
+ return nvc->pctx[nv->pctx_id];
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.c b/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.c
new file mode 100644
index 0000000000..5276806de6
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.c
@@ -0,0 +1,206 @@
+#include "pipe/p_winsys.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_inlines.h"
+
+#include "util/u_memory.h"
+
+#include "nouveau_context.h"
+#include "nouveau_local.h"
+#include "nouveau_screen.h"
+#include "nouveau_swapbuffers.h"
+#include "nouveau_winsys_pipe.h"
+
+static void
+nouveau_flush_frontbuffer(struct pipe_winsys *pws, struct pipe_surface *surf,
+ void *context_private)
+{
+ struct nouveau_context *nv = context_private;
+ __DRIdrawablePrivate *dPriv = nv->dri_drawable;
+
+ nouveau_copy_buffer(dPriv, surf, NULL);
+}
+
+static const char *
+nouveau_get_name(struct pipe_winsys *pws)
+{
+ return "Nouveau/DRI";
+}
+
+static struct pipe_buffer *
+nouveau_pipe_bo_create(struct pipe_winsys *pws, unsigned alignment,
+ unsigned usage, unsigned size)
+{
+ struct nouveau_pipe_winsys *nvpws = (struct nouveau_pipe_winsys *)pws;
+ struct nouveau_context *nv = nvpws->nv;
+ struct nouveau_device *dev = nv->nv_screen->device;
+ struct nouveau_pipe_buffer *nvbuf;
+ uint32_t flags;
+
+ nvbuf = calloc(1, sizeof(*nvbuf));
+ if (!nvbuf)
+ return NULL;
+ nvbuf->base.refcount = 1;
+ nvbuf->base.alignment = alignment;
+ nvbuf->base.usage = usage;
+ nvbuf->base.size = size;
+
+ flags = NOUVEAU_BO_LOCAL;
+
+ if (usage & PIPE_BUFFER_USAGE_PIXEL) {
+ if (usage & NOUVEAU_BUFFER_USAGE_TEXTURE)
+ flags |= NOUVEAU_BO_GART;
+ flags |= NOUVEAU_BO_VRAM;
+
+ switch (dev->chipset & 0xf0) {
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ flags |= NOUVEAU_BO_TILED;
+ if (usage & NOUVEAU_BUFFER_USAGE_ZETA)
+ flags |= NOUVEAU_BO_ZTILE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (usage & PIPE_BUFFER_USAGE_VERTEX) {
+ if (nv->cap.hw_vertex_buffer)
+ flags |= NOUVEAU_BO_GART;
+ }
+
+ if (usage & PIPE_BUFFER_USAGE_INDEX) {
+ if (nv->cap.hw_index_buffer)
+ flags |= NOUVEAU_BO_GART;
+ }
+
+ if (nouveau_bo_new(dev, flags, alignment, size, &nvbuf->bo)) {
+ free(nvbuf);
+ return NULL;
+ }
+
+ return &nvbuf->base;
+}
+
+static struct pipe_buffer *
+nouveau_pipe_bo_user_create(struct pipe_winsys *pws, void *ptr, unsigned bytes)
+{
+ struct nouveau_pipe_winsys *nvpws = (struct nouveau_pipe_winsys *)pws;
+ struct nouveau_device *dev = nvpws->nv->nv_screen->device;
+ struct nouveau_pipe_buffer *nvbuf;
+
+ nvbuf = calloc(1, sizeof(*nvbuf));
+ if (!nvbuf)
+ return NULL;
+ nvbuf->base.refcount = 1;
+ nvbuf->base.size = bytes;
+
+ if (nouveau_bo_user(dev, ptr, bytes, &nvbuf->bo)) {
+ free(nvbuf);
+ return NULL;
+ }
+
+ return &nvbuf->base;
+}
+
+static void
+nouveau_pipe_bo_del(struct pipe_winsys *ws, struct pipe_buffer *buf)
+{
+ struct nouveau_pipe_buffer *nvbuf = nouveau_buffer(buf);
+
+ nouveau_bo_del(&nvbuf->bo);
+ free(nvbuf);
+}
+
+static void *
+nouveau_pipe_bo_map(struct pipe_winsys *pws, struct pipe_buffer *buf,
+ unsigned flags)
+{
+ struct nouveau_pipe_buffer *nvbuf = nouveau_buffer(buf);
+ uint32_t map_flags = 0;
+
+ if (flags & PIPE_BUFFER_USAGE_CPU_READ)
+ map_flags |= NOUVEAU_BO_RD;
+ if (flags & PIPE_BUFFER_USAGE_CPU_WRITE)
+ map_flags |= NOUVEAU_BO_WR;
+
+ if (nouveau_bo_map(nvbuf->bo, map_flags))
+ return NULL;
+ return nvbuf->bo->map;
+}
+
+static void
+nouveau_pipe_bo_unmap(struct pipe_winsys *pws, struct pipe_buffer *buf)
+{
+ struct nouveau_pipe_buffer *nvbuf = nouveau_buffer(buf);
+
+ nouveau_bo_unmap(nvbuf->bo);
+}
+
+static INLINE struct nouveau_fence *
+nouveau_pipe_fence(struct pipe_fence_handle *pfence)
+{
+ return (struct nouveau_fence *)pfence;
+}
+
+static void
+nouveau_pipe_fence_reference(struct pipe_winsys *ws,
+ struct pipe_fence_handle **ptr,
+ struct pipe_fence_handle *pfence)
+{
+ nouveau_fence_ref((void *)pfence, (void *)ptr);
+}
+
+static int
+nouveau_pipe_fence_signalled(struct pipe_winsys *ws,
+ struct pipe_fence_handle *pfence, unsigned flag)
+{
+ struct nouveau_pipe_winsys *nvpws = (struct nouveau_pipe_winsys *)ws;
+ struct nouveau_fence *fence = nouveau_pipe_fence(pfence);
+
+ if (nouveau_fence(fence)->signalled == 0)
+ nouveau_fence_flush(nvpws->nv->nvc->channel);
+
+ return !nouveau_fence(fence)->signalled;
+}
+
+static int
+nouveau_pipe_fence_finish(struct pipe_winsys *ws,
+ struct pipe_fence_handle *pfence, unsigned flag)
+{
+ struct nouveau_fence *fence = nouveau_pipe_fence(pfence);
+ struct nouveau_fence *ref = NULL;
+
+ nouveau_fence_ref(fence, &ref);
+ return nouveau_fence_wait(&ref);
+}
+
+struct pipe_winsys *
+nouveau_create_pipe_winsys(struct nouveau_context *nv)
+{
+ struct nouveau_pipe_winsys *nvpws;
+ struct pipe_winsys *pws;
+
+ nvpws = CALLOC_STRUCT(nouveau_pipe_winsys);
+ if (!nvpws)
+ return NULL;
+ nvpws->nv = nv;
+ pws = &nvpws->pws;
+
+ pws->flush_frontbuffer = nouveau_flush_frontbuffer;
+
+ pws->buffer_create = nouveau_pipe_bo_create;
+ pws->buffer_destroy = nouveau_pipe_bo_del;
+ pws->user_buffer_create = nouveau_pipe_bo_user_create;
+ pws->buffer_map = nouveau_pipe_bo_map;
+ pws->buffer_unmap = nouveau_pipe_bo_unmap;
+
+ pws->fence_reference = nouveau_pipe_fence_reference;
+ pws->fence_signalled = nouveau_pipe_fence_signalled;
+ pws->fence_finish = nouveau_pipe_fence_finish;
+
+ pws->get_name = nouveau_get_name;
+
+ return &nvpws->pws;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.h b/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.h
new file mode 100644
index 0000000000..6a03ac0d77
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_winsys_pipe.h
@@ -0,0 +1,34 @@
+#ifndef NOUVEAU_PIPE_WINSYS_H
+#define NOUVEAU_PIPE_WINSYS_H
+
+#include "pipe/p_context.h"
+#include "pipe/p_winsys.h"
+#include "nouveau_context.h"
+
+struct nouveau_pipe_buffer {
+ struct pipe_buffer base;
+ struct nouveau_bo *bo;
+};
+
+static inline struct nouveau_pipe_buffer *
+nouveau_buffer(struct pipe_buffer *buf)
+{
+ return (struct nouveau_pipe_buffer *)buf;
+}
+
+struct nouveau_pipe_winsys {
+ struct pipe_winsys pws;
+
+ struct nouveau_context *nv;
+};
+
+extern struct pipe_winsys *
+nouveau_create_pipe_winsys(struct nouveau_context *nv);
+
+struct pipe_context *
+nouveau_create_softpipe(struct nouveau_context *nv);
+
+struct pipe_context *
+nouveau_pipe_create(struct nouveau_context *nv);
+
+#endif
diff --git a/src/gallium/winsys/drm/nouveau/nouveau_winsys_softpipe.c b/src/gallium/winsys/drm/nouveau/nouveau_winsys_softpipe.c
new file mode 100644
index 0000000000..704f6c7750
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nouveau_winsys_softpipe.c
@@ -0,0 +1,85 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ **************************************************************************/
+/*
+ * Authors: Keith Whitwell <keithw-at-tungstengraphics-dot-com>
+ */
+
+#include "imports.h"
+
+#include "pipe/p_defines.h"
+#include "pipe/p_format.h"
+#include "softpipe/sp_winsys.h"
+
+#include "nouveau_context.h"
+#include "nouveau_winsys_pipe.h"
+
+struct nouveau_softpipe_winsys {
+ struct softpipe_winsys sws;
+ struct nouveau_context *nv;
+};
+
+/**
+ * Return list of surface formats supported by this driver.
+ */
+static boolean
+nouveau_is_format_supported(struct softpipe_winsys *sws, uint format)
+{
+ switch (format) {
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ case PIPE_FORMAT_Z24S8_UNORM:
+ return TRUE;
+ default:
+ break;
+ };
+
+ return FALSE;
+}
+
+struct pipe_context *
+nouveau_create_softpipe(struct nouveau_context *nv)
+{
+ struct nouveau_softpipe_winsys *nvsws;
+ struct pipe_screen *pscreen;
+ struct pipe_winsys *ws;
+
+ ws = nouveau_create_pipe_winsys(nv);
+ if (!ws)
+ return NULL;
+ pscreen = softpipe_create_screen(ws);
+
+ nvsws = CALLOC_STRUCT(nouveau_softpipe_winsys);
+ if (!nvsws)
+ return NULL;
+
+ nvsws->sws.is_format_supported = nouveau_is_format_supported;
+ nvsws->nv = nv;
+
+ return softpipe_create(pscreen, ws, &nvsws->sws);
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nv04_surface.c b/src/gallium/winsys/drm/nouveau/nv04_surface.c
new file mode 100644
index 0000000000..8fa3d106c8
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nv04_surface.c
@@ -0,0 +1,314 @@
+#include "pipe/p_context.h"
+#include "pipe/p_format.h"
+
+#include "nouveau_context.h"
+
+static INLINE int
+nv04_surface_format(enum pipe_format format)
+{
+ switch (format) {
+ case PIPE_FORMAT_A8_UNORM:
+ return NV04_CONTEXT_SURFACES_2D_FORMAT_Y8;
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ return NV04_CONTEXT_SURFACES_2D_FORMAT_R5G6B5;
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ case PIPE_FORMAT_Z24S8_UNORM:
+ return NV04_CONTEXT_SURFACES_2D_FORMAT_Y32;
+ default:
+ return -1;
+ }
+}
+
+static INLINE int
+nv04_rect_format(enum pipe_format format)
+{
+ switch (format) {
+ case PIPE_FORMAT_A8_UNORM:
+ return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A8R8G8B8;
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A16R5G6B5;
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ case PIPE_FORMAT_Z24S8_UNORM:
+ return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A8R8G8B8;
+ default:
+ return -1;
+ }
+}
+
+static void
+nv04_surface_copy_m2mf(struct nouveau_context *nv, unsigned dx, unsigned dy,
+ unsigned sx, unsigned sy, unsigned w, unsigned h)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ struct pipe_surface *dst = nv->surf_dst;
+ struct pipe_surface *src = nv->surf_src;
+ unsigned dst_offset, src_offset;
+
+ dst_offset = dst->offset + (dy * dst->stride) + (dx * dst->block.size);
+ src_offset = src->offset + (sy * src->stride) + (sx * src->block.size);
+
+ while (h) {
+ int count = (h > 2047) ? 2047 : h;
+
+ BEGIN_RING(chan, nv->nvc->NvM2MF,
+ NV04_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
+ OUT_RELOCl(chan, nouveau_buffer(src->buffer)->bo, src_offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD);
+ OUT_RELOCl(chan, nouveau_buffer(dst->buffer)->bo, dst_offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_WR);
+ OUT_RING (chan, src->stride);
+ OUT_RING (chan, dst->stride);
+ OUT_RING (chan, w * src->block.size);
+ OUT_RING (chan, count);
+ OUT_RING (chan, 0x0101);
+ OUT_RING (chan, 0);
+
+ h -= count;
+ src_offset += src->stride * count;
+ dst_offset += dst->stride * count;
+ }
+}
+
+static void
+nv04_surface_copy_blit(struct nouveau_context *nv, unsigned dx, unsigned dy,
+ unsigned sx, unsigned sy, unsigned w, unsigned h)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+
+ BEGIN_RING(chan, nv->nvc->NvImageBlit, 0x0300, 3);
+ OUT_RING (chan, (sy << 16) | sx);
+ OUT_RING (chan, (dy << 16) | dx);
+ OUT_RING (chan, ( h << 16) | w);
+}
+
+static int
+nv04_surface_copy_prep(struct nouveau_context *nv, struct pipe_surface *dst,
+ struct pipe_surface *src)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ int format;
+
+ if (src->format != dst->format)
+ return 1;
+
+ /* NV_CONTEXT_SURFACES_2D has buffer alignment restrictions, fallback
+ * to NV_MEMORY_TO_MEMORY_FORMAT in this case.
+ */
+ if ((src->offset & 63) || (dst->offset & 63)) {
+ BEGIN_RING(nv->nvc->channel, nv->nvc->NvM2MF,
+ NV04_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN, 2);
+ OUT_RELOCo(chan, nouveau_buffer(src->buffer)->bo,
+ NOUVEAU_BO_GART | NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
+ OUT_RELOCo(chan, nouveau_buffer(dst->buffer)->bo,
+ NOUVEAU_BO_GART | NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+
+ nv->surface_copy = nv04_surface_copy_m2mf;
+ nv->surf_dst = dst;
+ nv->surf_src = src;
+ return 0;
+
+ }
+
+ if ((format = nv04_surface_format(dst->format)) < 0) {
+ NOUVEAU_ERR("Bad surface format 0x%x\n", dst->format);
+ return 1;
+ }
+ nv->surface_copy = nv04_surface_copy_blit;
+
+ BEGIN_RING(chan, nv->nvc->NvCtxSurf2D,
+ NV04_CONTEXT_SURFACES_2D_DMA_IMAGE_SOURCE, 2);
+ OUT_RELOCo(chan, nouveau_buffer(src->buffer)->bo,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
+ OUT_RELOCo(chan, nouveau_buffer(dst->buffer)->bo,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+
+ BEGIN_RING(chan, nv->nvc->NvCtxSurf2D,
+ NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
+ OUT_RING (chan, format);
+ OUT_RING (chan, (dst->stride << 16) | src->stride);
+ OUT_RELOCl(chan, nouveau_buffer(src->buffer)->bo, src->offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
+ OUT_RELOCl(chan, nouveau_buffer(dst->buffer)->bo, dst->offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+
+ return 0;
+}
+
+static void
+nv04_surface_copy_done(struct nouveau_context *nv)
+{
+ FIRE_RING(nv->nvc->channel);
+}
+
+static int
+nv04_surface_fill(struct nouveau_context *nv, struct pipe_surface *dst,
+ unsigned dx, unsigned dy, unsigned w, unsigned h,
+ unsigned value)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ struct nouveau_grobj *surf2d = nv->nvc->NvCtxSurf2D;
+ struct nouveau_grobj *rect = nv->nvc->NvGdiRect;
+ int cs2d_format, gdirect_format;
+
+ if ((cs2d_format = nv04_surface_format(dst->format)) < 0) {
+ NOUVEAU_ERR("Bad format = %d\n", dst->format);
+ return 1;
+ }
+
+ if ((gdirect_format = nv04_rect_format(dst->format)) < 0) {
+ NOUVEAU_ERR("Bad format = %d\n", dst->format);
+ return 1;
+ }
+
+ BEGIN_RING(chan, surf2d, NV04_CONTEXT_SURFACES_2D_DMA_IMAGE_SOURCE, 2);
+ OUT_RELOCo(chan, nouveau_buffer(dst->buffer)->bo,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+ OUT_RELOCo(chan, nouveau_buffer(dst->buffer)->bo,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+ BEGIN_RING(chan, surf2d, NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
+ OUT_RING (chan, cs2d_format);
+ OUT_RING (chan, (dst->stride << 16) | dst->stride);
+ OUT_RELOCl(chan, nouveau_buffer(dst->buffer)->bo, dst->offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+ OUT_RELOCl(chan, nouveau_buffer(dst->buffer)->bo, dst->offset,
+ NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
+
+ BEGIN_RING(chan, rect, NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT, 1);
+ OUT_RING (chan, gdirect_format);
+ BEGIN_RING(chan, rect, NV04_GDI_RECTANGLE_TEXT_COLOR1_A, 1);
+ OUT_RING (chan, value);
+ BEGIN_RING(chan, rect,
+ NV04_GDI_RECTANGLE_TEXT_UNCLIPPED_RECTANGLE_POINT(0), 2);
+ OUT_RING (chan, (dx << 16) | dy);
+ OUT_RING (chan, ( w << 16) | h);
+
+ FIRE_RING(chan);
+ return 0;
+}
+
+int
+nouveau_surface_channel_create_nv04(struct nouveau_channel_context *nvc)
+{
+ struct nouveau_channel *chan = nvc->channel;
+ unsigned chipset = nvc->channel->device->chipset, class;
+ int ret;
+
+ if ((ret = nouveau_grobj_alloc(chan, nvc->next_handle++, 0x39,
+ &nvc->NvM2MF))) {
+ NOUVEAU_ERR("Error creating m2mf object: %d\n", ret);
+ return 1;
+ }
+ BIND_RING (chan, nvc->NvM2MF, nvc->next_subchannel++);
+ BEGIN_RING(chan, nvc->NvM2MF,
+ NV04_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
+ OUT_RING (chan, nvc->sync_notifier->handle);
+
+ class = chipset < 0x10 ? NV04_CONTEXT_SURFACES_2D :
+ NV10_CONTEXT_SURFACES_2D;
+ if ((ret = nouveau_grobj_alloc(chan, nvc->next_handle++, class,
+ &nvc->NvCtxSurf2D))) {
+ NOUVEAU_ERR("Error creating 2D surface object: %d\n", ret);
+ return 1;
+ }
+ BIND_RING (chan, nvc->NvCtxSurf2D, nvc->next_subchannel++);
+ BEGIN_RING(chan, nvc->NvCtxSurf2D,
+ NV04_CONTEXT_SURFACES_2D_DMA_IMAGE_SOURCE, 2);
+ OUT_RING (chan, nvc->channel->vram->handle);
+ OUT_RING (chan, nvc->channel->vram->handle);
+
+ class = chipset < 0x10 ? NV04_IMAGE_BLIT : NV12_IMAGE_BLIT;
+ if ((ret = nouveau_grobj_alloc(chan, nvc->next_handle++, class,
+ &nvc->NvImageBlit))) {
+ NOUVEAU_ERR("Error creating blit object: %d\n", ret);
+ return 1;
+ }
+ BIND_RING (chan, nvc->NvImageBlit, nvc->next_subchannel++);
+ BEGIN_RING(chan, nvc->NvImageBlit, NV04_IMAGE_BLIT_DMA_NOTIFY, 1);
+ OUT_RING (chan, nvc->sync_notifier->handle);
+ BEGIN_RING(chan, nvc->NvImageBlit, NV04_IMAGE_BLIT_SURFACE, 1);
+ OUT_RING (chan, nvc->NvCtxSurf2D->handle);
+ BEGIN_RING(chan, nvc->NvImageBlit, NV04_IMAGE_BLIT_OPERATION, 1);
+ OUT_RING (chan, NV04_IMAGE_BLIT_OPERATION_SRCCOPY);
+
+ class = NV04_GDI_RECTANGLE_TEXT;
+ if ((ret = nouveau_grobj_alloc(chan, nvc->next_handle++, class,
+ &nvc->NvGdiRect))) {
+ NOUVEAU_ERR("Error creating rect object: %d\n", ret);
+ return 1;
+ }
+ BIND_RING (chan, nvc->NvGdiRect, nvc->next_subchannel++);
+ BEGIN_RING(chan, nvc->NvGdiRect, NV04_GDI_RECTANGLE_TEXT_DMA_NOTIFY, 1);
+ OUT_RING (chan, nvc->sync_notifier->handle);
+ BEGIN_RING(chan, nvc->NvGdiRect, NV04_GDI_RECTANGLE_TEXT_SURFACE, 1);
+ OUT_RING (chan, nvc->NvCtxSurf2D->handle);
+ BEGIN_RING(chan, nvc->NvGdiRect, NV04_GDI_RECTANGLE_TEXT_OPERATION, 1);
+ OUT_RING (chan, NV04_GDI_RECTANGLE_TEXT_OPERATION_SRCCOPY);
+ BEGIN_RING(chan, nvc->NvGdiRect,
+ NV04_GDI_RECTANGLE_TEXT_MONOCHROME_FORMAT, 1);
+ OUT_RING (chan, NV04_GDI_RECTANGLE_TEXT_MONOCHROME_FORMAT_LE);
+
+ switch (chipset & 0xf0) {
+ case 0x00:
+ case 0x10:
+ class = NV04_SWIZZLED_SURFACE;
+ break;
+ case 0x20:
+ class = NV20_SWIZZLED_SURFACE;
+ break;
+ case 0x30:
+ class = NV30_SWIZZLED_SURFACE;
+ break;
+ case 0x40:
+ case 0x60:
+ class = NV40_SWIZZLED_SURFACE;
+ break;
+ default:
+ /* Famous last words: this really can't happen.. */
+ assert(0);
+ break;
+ }
+
+ ret = nouveau_grobj_alloc(chan, nvc->next_handle++, class,
+ &nvc->NvSwzSurf);
+ if (ret) {
+ NOUVEAU_ERR("Error creating swizzled surface: %d\n", ret);
+ return 1;
+ }
+
+ BIND_RING (chan, nvc->NvSwzSurf, nvc->next_subchannel++);
+ BEGIN_RING(chan, nvc->NvSwzSurf, NV04_SWIZZLED_SURFACE_DMA_NOTIFY, 1);
+ OUT_RING (chan, nvc->sync_notifier->handle);
+ BEGIN_RING(chan, nvc->NvSwzSurf, NV04_SWIZZLED_SURFACE_DMA_IMAGE, 1);
+ OUT_RING (chan, nvc->channel->vram->handle);
+
+ if (chipset < 0x10) {
+ class = NV04_SCALED_IMAGE_FROM_MEMORY;
+ } else
+ if (chipset < 0x40) {
+ class = NV10_SCALED_IMAGE_FROM_MEMORY;
+ } else {
+ class = NV40_SCALED_IMAGE_FROM_MEMORY;
+ }
+
+ ret = nouveau_grobj_alloc(chan, nvc->next_handle++, class,
+ &nvc->NvSIFM);
+ if (ret) {
+ NOUVEAU_ERR("Error creating scaled image object: %d\n", ret);
+ return 1;
+ }
+
+ BIND_RING (chan, nvc->NvSIFM, nvc->next_subchannel++);
+
+ return 0;
+}
+
+int
+nouveau_surface_init_nv04(struct nouveau_context *nv)
+{
+ nv->surface_copy_prep = nv04_surface_copy_prep;
+ nv->surface_copy = nv04_surface_copy_blit;
+ nv->surface_copy_done = nv04_surface_copy_done;
+ nv->surface_fill = nv04_surface_fill;
+ return 0;
+}
+
diff --git a/src/gallium/winsys/drm/nouveau/nv50_surface.c b/src/gallium/winsys/drm/nouveau/nv50_surface.c
new file mode 100644
index 0000000000..c8ab7f690f
--- /dev/null
+++ b/src/gallium/winsys/drm/nouveau/nv50_surface.c
@@ -0,0 +1,194 @@
+#include "pipe/p_context.h"
+#include "pipe/p_format.h"
+
+#include "nouveau_context.h"
+
+static INLINE int
+nv50_format(enum pipe_format format)
+{
+ switch (format) {
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ case PIPE_FORMAT_Z24S8_UNORM:
+ return NV50_2D_DST_FORMAT_32BPP;
+ case PIPE_FORMAT_X8R8G8B8_UNORM:
+ return NV50_2D_DST_FORMAT_24BPP;
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ return NV50_2D_DST_FORMAT_16BPP;
+ case PIPE_FORMAT_A8_UNORM:
+ return NV50_2D_DST_FORMAT_8BPP;
+ default:
+ return -1;
+ }
+}
+
+static int
+nv50_surface_set(struct nouveau_context *nv, struct pipe_surface *surf, int dst)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ struct nouveau_grobj *eng2d = nv->nvc->Nv2D;
+ struct nouveau_bo *bo = nouveau_buffer(surf->buffer)->bo;
+ int surf_format, mthd = dst ? NV50_2D_DST_FORMAT : NV50_2D_SRC_FORMAT;
+ int flags = NOUVEAU_BO_VRAM | (dst ? NOUVEAU_BO_WR : NOUVEAU_BO_RD);
+
+ surf_format = nv50_format(surf->format);
+ if (surf_format < 0)
+ return 1;
+
+ if (!nouveau_bo(bo)->tiled) {
+ BEGIN_RING(chan, eng2d, mthd, 2);
+ OUT_RING (chan, surf_format);
+ OUT_RING (chan, 1);
+ BEGIN_RING(chan, eng2d, mthd + 0x14, 5);
+ OUT_RING (chan, surf->stride);
+ OUT_RING (chan, surf->width);
+ OUT_RING (chan, surf->height);
+ OUT_RELOCh(chan, bo, surf->offset, flags);
+ OUT_RELOCl(chan, bo, surf->offset, flags);
+ } else {
+ BEGIN_RING(chan, eng2d, mthd, 5);
+ OUT_RING (chan, surf_format);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 1);
+ OUT_RING (chan, 0);
+ BEGIN_RING(chan, eng2d, mthd + 0x18, 4);
+ OUT_RING (chan, surf->width);
+ OUT_RING (chan, surf->height);
+ OUT_RELOCh(chan, bo, surf->offset, flags);
+ OUT_RELOCl(chan, bo, surf->offset, flags);
+ }
+
+#if 0
+ if (dst) {
+ BEGIN_RING(chan, eng2d, NV50_2D_CLIP_X, 4);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, surf->width);
+ OUT_RING (chan, surf->height);
+ }
+#endif
+
+ return 0;
+}
+
+static int
+nv50_surface_copy_prep(struct nouveau_context *nv,
+ struct pipe_surface *dst, struct pipe_surface *src)
+{
+ int ret;
+
+ assert(src->format == dst->format);
+
+ ret = nv50_surface_set(nv, dst, 1);
+ if (ret)
+ return ret;
+
+ ret = nv50_surface_set(nv, src, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void
+nv50_surface_copy(struct nouveau_context *nv, unsigned dx, unsigned dy,
+ unsigned sx, unsigned sy, unsigned w, unsigned h)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ struct nouveau_grobj *eng2d = nv->nvc->Nv2D;
+
+ BEGIN_RING(chan, eng2d, 0x088c, 1);
+ OUT_RING (chan, 0);
+ BEGIN_RING(chan, eng2d, NV50_2D_BLIT_DST_X, 4);
+ OUT_RING (chan, dx);
+ OUT_RING (chan, dy);
+ OUT_RING (chan, w);
+ OUT_RING (chan, h);
+ BEGIN_RING(chan, eng2d, 0x08c0, 4);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 1);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 1);
+ BEGIN_RING(chan, eng2d, 0x08d0, 4);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, sx);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, sy);
+}
+
+static void
+nv50_surface_copy_done(struct nouveau_context *nv)
+{
+ FIRE_RING(nv->nvc->channel);
+}
+
+static int
+nv50_surface_fill(struct nouveau_context *nv, struct pipe_surface *dst,
+ unsigned dx, unsigned dy, unsigned w, unsigned h,
+ unsigned value)
+{
+ struct nouveau_channel *chan = nv->nvc->channel;
+ struct nouveau_grobj *eng2d = nv->nvc->Nv2D;
+ int rect_format, ret;
+
+ rect_format = nv50_format(dst->format);
+ if (rect_format < 0)
+ return 1;
+
+ ret = nv50_surface_set(nv, dst, 1);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, eng2d, 0x0580, 3);
+ OUT_RING (chan, 4);
+ OUT_RING (chan, rect_format);
+ OUT_RING (chan, value);
+
+ BEGIN_RING(chan, eng2d, NV50_2D_RECT_X1, 4);
+ OUT_RING (chan, dx);
+ OUT_RING (chan, dy);
+ OUT_RING (chan, dx + w);
+ OUT_RING (chan, dy + h);
+
+ FIRE_RING(chan);
+ return 0;
+}
+
+int
+nouveau_surface_channel_create_nv50(struct nouveau_channel_context *nvc)
+{
+ struct nouveau_channel *chan = nvc->channel;
+ struct nouveau_grobj *eng2d = NULL;
+ int ret;
+
+ ret = nouveau_grobj_alloc(chan, nvc->next_handle++, NV50_2D, &eng2d);
+ if (ret)
+ return ret;
+ nvc->Nv2D = eng2d;
+
+ BIND_RING (chan, eng2d, nvc->next_subchannel++);
+ BEGIN_RING(chan, eng2d, NV50_2D_DMA_NOTIFY, 4);
+ OUT_RING (chan, nvc->sync_notifier->handle);
+ OUT_RING (chan, chan->vram->handle);
+ OUT_RING (chan, chan->vram->handle);
+ OUT_RING (chan, chan->vram->handle);
+ BEGIN_RING(chan, eng2d, NV50_2D_OPERATION, 1);
+ OUT_RING (chan, NV50_2D_OPERATION_SRCCOPY);
+ BEGIN_RING(chan, eng2d, 0x0290, 1);
+ OUT_RING (chan, 0);
+ BEGIN_RING(chan, eng2d, 0x0888, 1);
+ OUT_RING (chan, 1);
+
+ return 0;
+}
+
+int
+nouveau_surface_init_nv50(struct nouveau_context *nv)
+{
+ nv->surface_copy_prep = nv50_surface_copy_prep;
+ nv->surface_copy = nv50_surface_copy;
+ nv->surface_copy_done = nv50_surface_copy_done;
+ nv->surface_fill = nv50_surface_fill;
+ return 0;
+}
+