summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2013-08-07 00:04:29 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2013-08-07 00:04:29 +0000
commit21c0c137ed00e4f1f3e598892410d57cf4f9e734 (patch)
tree915298327e1683124fac079163fb3000c78fba83 /sys
parentc09bc5ce8fd9ba591c4fd2e0fcff66291725ab28 (diff)
add support for hardware contexts on recent intel hardware
based on the code in linux 3.8.13 ok kettenis@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/drm/files.drm3
-rw-r--r--sys/dev/pci/drm/i915/i915_dma.c5
-rw-r--r--sys/dev/pci/drm/i915/i915_drv.c10
-rw-r--r--sys/dev/pci/drm/i915/i915_drv.h34
-rw-r--r--sys/dev/pci/drm/i915/i915_gem.c6
-rw-r--r--sys/dev/pci/drm/i915/i915_gem_context.c573
-rw-r--r--sys/dev/pci/drm/i915/i915_gem_execbuffer.c7
7 files changed, 628 insertions, 10 deletions
diff --git a/sys/dev/pci/drm/files.drm b/sys/dev/pci/drm/files.drm
index e8852a8fbd9..dc4783b9d16 100644
--- a/sys/dev/pci/drm/files.drm
+++ b/sys/dev/pci/drm/files.drm
@@ -1,5 +1,5 @@
# $NetBSD: files.drm,v 1.2 2007/03/28 11:29:37 jmcneill Exp $
-# $OpenBSD: files.drm,v 1.25 2013/03/31 13:30:25 kettenis Exp $
+# $OpenBSD: files.drm,v 1.26 2013/08/07 00:04:27 jsg Exp $
# direct rendering modules
define drmbase {}
@@ -26,6 +26,7 @@ attach inteldrm at drmdev
file dev/pci/drm/i915/i915_dma.c inteldrm
file dev/pci/drm/i915/i915_drv.c inteldrm
file dev/pci/drm/i915/i915_gem.c inteldrm
+file dev/pci/drm/i915/i915_gem_context.c inteldrm
file dev/pci/drm/i915/i915_gem_evict.c inteldrm
file dev/pci/drm/i915/i915_gem_execbuffer.c inteldrm
file dev/pci/drm/i915/i915_gem_gtt.c inteldrm
diff --git a/sys/dev/pci/drm/i915/i915_dma.c b/sys/dev/pci/drm/i915/i915_dma.c
index d596c0764e8..8b13310db12 100644
--- a/sys/dev/pci/drm/i915/i915_dma.c
+++ b/sys/dev/pci/drm/i915/i915_dma.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_dma.c,v 1.8 2013/07/05 07:20:27 jsg Exp $ */
+/* $OpenBSD: i915_dma.c,v 1.9 2013/08/07 00:04:27 jsg Exp $ */
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
*/
/*
@@ -400,6 +400,8 @@ i915_driver_open(struct drm_device *dev, struct drm_file *file)
mtx_init(&file_priv->mm.lock, IPL_NONE);
INIT_LIST_HEAD(&file_priv->mm.request_list);
+ SPLAY_INIT(&file_priv->ctx_tree);
+
return 0;
}
@@ -408,6 +410,7 @@ i915_driver_close(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
+ i915_gem_context_close(dev, file);
i915_gem_release(dev, file);
free(file_priv, M_DRM);
}
diff --git a/sys/dev/pci/drm/i915/i915_drv.c b/sys/dev/pci/drm/i915/i915_drv.c
index f3d23578ee9..a77f55403fc 100644
--- a/sys/dev/pci/drm/i915/i915_drv.c
+++ b/sys/dev/pci/drm/i915/i915_drv.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_drv.c,v 1.35 2013/07/05 07:20:27 jsg Exp $ */
+/* $OpenBSD: i915_drv.c,v 1.36 2013/08/07 00:04:28 jsg Exp $ */
/*
* Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
*
@@ -1390,6 +1390,12 @@ inteldrm_doioctl(struct drm_device *dev, u_long cmd, caddr_t data,
case DRM_IOCTL_I915_GEM_GET_CACHING:
return (i915_gem_get_caching_ioctl(dev, data,
file_priv));
+ case DRM_IOCTL_I915_GEM_CONTEXT_CREATE:
+ return (i915_gem_context_create_ioctl(dev, data,
+ file_priv));
+ case DRM_IOCTL_I915_GEM_CONTEXT_DESTROY:
+ return (i915_gem_context_destroy_ioctl(dev, data,
+ file_priv));
default:
break;
}
@@ -2042,8 +2048,8 @@ i915_reset(struct drm_device *dev)
for_each_ring(ring, dev_priv, i)
ring->init(ring);
-#ifdef notyet
i915_gem_context_init(dev);
+#ifdef notyet
i915_gem_init_ppgtt(dev);
#endif
diff --git a/sys/dev/pci/drm/i915/i915_drv.h b/sys/dev/pci/drm/i915/i915_drv.h
index a47ae71d308..7012b11cb1f 100644
--- a/sys/dev/pci/drm/i915/i915_drv.h
+++ b/sys/dev/pci/drm/i915/i915_drv.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_drv.h,v 1.24 2013/07/05 07:20:27 jsg Exp $ */
+/* $OpenBSD: i915_drv.h,v 1.25 2013/08/07 00:04:28 jsg Exp $ */
/* i915_drv.h -- Private header for the I915 driver -*- linux-c -*-
*/
/*
@@ -203,6 +203,22 @@ struct intel_device_info {
u8 has_llc:1;
};
+/* This must match up with the value previously used for execbuf2.rsvd1. */
+#define DEFAULT_CONTEXT_ID 0
+struct i915_hw_context {
+ int id;
+ bool is_initialized;
+ struct drm_i915_file_private *file_priv;
+ struct intel_ring_buffer *ring;
+ struct drm_i915_gem_object *obj;
+};
+
+struct i915_ctx_handle {
+ SPLAY_ENTRY(i915_ctx_handle) entry;
+ struct i915_hw_context *ctx;
+ uint32_t handle;
+};
+
enum no_fbc_reason {
FBC_NO_OUTPUT, /* no outputs enabled to compress */
FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */
@@ -798,6 +814,9 @@ struct inteldrm_softc {
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
+ bool hw_contexts_disabled;
+ uint32_t hw_context_size;
+
u32 fdi_rx_config;
struct i915_suspend_saved_registers regfile;
@@ -973,6 +992,8 @@ struct drm_i915_file_private {
struct mutex lock;
struct list_head request_list;
} mm;
+ SPLAY_HEAD(i915_ctx_tree, i915_ctx_handle) ctx_tree;
+ uint32_t ctx_id;
};
/**
@@ -1180,6 +1201,17 @@ int i915_reset(struct drm_device *);
void inteldrm_timeout(void *);
bool i915_semaphore_is_enabled(struct drm_device *);
+/* i915_gem_context.c */
+void i915_gem_context_init(struct drm_device *dev);
+void i915_gem_context_fini(struct drm_device *dev);
+void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
+int i915_switch_context(struct intel_ring_buffer *ring,
+ struct drm_file *file, int to_id);
+int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
+int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
+
/* i915_gem_evict.c */
int i915_gem_evict_everything(struct drm_device *);
int i915_gem_evict_something(struct inteldrm_softc *, size_t);
diff --git a/sys/dev/pci/drm/i915/i915_gem.c b/sys/dev/pci/drm/i915/i915_gem.c
index 1302b764036..b5fccc52d4f 100644
--- a/sys/dev/pci/drm/i915/i915_gem.c
+++ b/sys/dev/pci/drm/i915/i915_gem.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_gem.c,v 1.29 2013/07/29 18:32:15 kettenis Exp $ */
+/* $OpenBSD: i915_gem.c,v 1.30 2013/08/07 00:04:28 jsg Exp $ */
/*
* Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
*
@@ -1801,11 +1801,9 @@ i915_gpu_idle(struct drm_device *dev)
/* Flush everything onto the inactive list. */
for_each_ring(ring, dev_priv, i) {
-#ifdef notyet
ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
if (ret)
return ret;
-#endif
ret = intel_ring_idle(ring);
if (ret)
@@ -3173,8 +3171,8 @@ i915_gem_init_hw(struct drm_device *dev)
* XXX: There was some w/a described somewhere suggesting loading
* contexts before PPGTT.
*/
-#ifdef notyet
i915_gem_context_init(dev);
+#ifdef notyet
i915_gem_init_ppgtt(dev);
#endif
diff --git a/sys/dev/pci/drm/i915/i915_gem_context.c b/sys/dev/pci/drm/i915/i915_gem_context.c
new file mode 100644
index 00000000000..f4de9be0932
--- /dev/null
+++ b/sys/dev/pci/drm/i915/i915_gem_context.c
@@ -0,0 +1,573 @@
+/* $OpenBSD: i915_gem_context.c,v 1.1 2013/08/07 00:04:28 jsg Exp $ */
+/*
+ * Copyright © 2011-2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Ben Widawsky <ben@bwidawsk.net>
+ *
+ */
+
+/*
+ * This file implements HW context support. On gen5+ a HW context consists of an
+ * opaque GPU object which is referenced at times of context saves and restores.
+ * With RC6 enabled, the context is also referenced as the GPU enters and exists
+ * from RC6 (GPU has it's own internal power context, except on gen5). Though
+ * something like a context does exist for the media ring, the code only
+ * supports contexts for the render ring.
+ *
+ * In software, there is a distinction between contexts created by the user,
+ * and the default HW context. The default HW context is used by GPU clients
+ * that do not request setup of their own hardware context. The default
+ * context's state is never restored to help prevent programming errors. This
+ * would happen if a client ran and piggy-backed off another clients GPU state.
+ * The default context only exists to give the GPU some offset to load as the
+ * current to invoke a save of the context we actually care about. In fact, the
+ * code could likely be constructed, albeit in a more complicated fashion, to
+ * never use the default context, though that limits the driver's ability to
+ * swap out, and/or destroy other contexts.
+ *
+ * All other contexts are created as a request by the GPU client. These contexts
+ * store GPU state, and thus allow GPU clients to not re-emit state (and
+ * potentially query certain state) at any time. The kernel driver makes
+ * certain that the appropriate commands are inserted.
+ *
+ * The context life cycle is semi-complicated in that context BOs may live
+ * longer than the context itself because of the way the hardware, and object
+ * tracking works. Below is a very crude representation of the state machine
+ * describing the context life.
+ * refcount pincount active
+ * S0: initial state 0 0 0
+ * S1: context created 1 0 0
+ * S2: context is currently running 2 1 X
+ * S3: GPU referenced, but not current 2 0 1
+ * S4: context is current, but destroyed 1 1 0
+ * S5: like S3, but destroyed 1 0 1
+ *
+ * The most common (but not all) transitions:
+ * S0->S1: client creates a context
+ * S1->S2: client submits execbuf with context
+ * S2->S3: other clients submits execbuf with context
+ * S3->S1: context object was retired
+ * S3->S2: clients submits another execbuf
+ * S2->S4: context destroy called with current context
+ * S3->S5->S0: destroy path
+ * S4->S5->S0: destroy path on current context
+ *
+ * There are two confusing terms used above:
+ * The "current context" means the context which is currently running on the
+ * GPU. The GPU has loaded it's state already and has stored away the gtt
+ * offset of the BO. The GPU is not actively referencing the data at this
+ * offset, but it will on the next context switch. The only way to avoid this
+ * is to do a GPU reset.
+ *
+ * An "active context' is one which was previously the "current context" and is
+ * on the active list waiting for the next context switch to occur. Until this
+ * happens, the object must remain at the same gtt offset. It is therefore
+ * possible to destroy a context, but it is still active.
+ *
+ */
+
+#include <dev/pci/drm/drmP.h>
+#include <dev/pci/drm/i915_drm.h>
+#include "i915_drv.h"
+
+/* This is a HW constraint. The value below is the largest known requirement
+ * I've seen in a spec to date, and that was a workaround for a non-shipping
+ * part. It should be safe to decrease this, but it's more future proof as is.
+ */
+#define CONTEXT_ALIGN (64<<10)
+
+static struct i915_hw_context *
+i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
+static int do_switch(struct i915_hw_context *to);
+
+int i915_ctx_handle_cmp(struct i915_ctx_handle *, struct i915_ctx_handle *);
+SPLAY_PROTOTYPE(i915_ctx_tree, i915_ctx_handle, entry, i915_ctx_handle_cmp);
+
+static int get_context_size(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+ u32 reg;
+
+ switch (INTEL_INFO(dev)->gen) {
+ case 6:
+ reg = I915_READ(CXT_SIZE);
+ ret = GEN6_CXT_TOTAL_SIZE(reg) * 64;
+ break;
+ case 7:
+ reg = I915_READ(GEN7_CXT_SIZE);
+ if (IS_HASWELL(dev))
+ ret = HSW_CXT_TOTAL_SIZE(reg) * 64;
+ else
+ ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
+ break;
+ default:
+ BUG();
+ }
+
+ return ret;
+}
+
+static void do_destroy(struct i915_hw_context *ctx)
+{
+ struct drm_device *dev = ctx->obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (ctx->file_priv) {
+ struct i915_ctx_handle *han, find;
+ find.handle = ctx->id;
+ han = SPLAY_FIND(i915_ctx_tree, &ctx->file_priv->ctx_tree, &find);
+ if (han != NULL)
+ SPLAY_REMOVE(i915_ctx_tree, &ctx->file_priv->ctx_tree, han);
+ } else
+ BUG_ON(ctx != dev_priv->ring[RCS].default_context);
+
+ drm_gem_object_unreference(&ctx->obj->base);
+ free(ctx, M_DRM);
+}
+
+static struct i915_hw_context *
+create_hw_context(struct drm_device *dev,
+ struct drm_i915_file_private *file_priv)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_hw_context *ctx;
+ struct i915_ctx_handle *han;
+ int ret;
+
+ ctx = malloc(sizeof(*ctx), M_DRM, M_WAITOK | M_ZERO);
+ if (ctx == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
+ if (ctx->obj == NULL) {
+ free(ctx, M_DRM);
+ DRM_DEBUG_DRIVER("Context object allocated failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (INTEL_INFO(dev)->gen >= 7) {
+ ret = i915_gem_object_set_cache_level(ctx->obj,
+ I915_CACHE_LLC_MLC);
+ if (ret)
+ goto err_out;
+ }
+
+ /* The ring associated with the context object is handled by the normal
+ * object tracking code. We give an initial ring value simple to pass an
+ * assertion in the context switch code.
+ */
+ ctx->ring = &dev_priv->ring[RCS];
+
+ /* Default context will never have a file_priv */
+ if (file_priv == NULL)
+ return ctx;
+
+ ctx->file_priv = file_priv;
+
+ if ((han = drm_calloc(1, sizeof(*han))) == NULL) {
+ ret = -ENOMEM;
+ DRM_DEBUG_DRIVER("idr allocation failed\n");
+ goto err_out;
+ }
+ han->ctx = ctx;
+
+again:
+ han->handle = ++file_priv->ctx_id;
+
+ if (han->handle <= DEFAULT_CONTEXT_ID + 1 || SPLAY_INSERT(i915_ctx_tree,
+ &file_priv->ctx_tree, han))
+ goto again;
+
+ ctx->id = han->handle;
+
+ return ctx;
+
+err_out:
+ do_destroy(ctx);
+ return ERR_PTR(ret);
+}
+
+static inline bool is_default_context(struct i915_hw_context *ctx)
+{
+ return (ctx == ctx->ring->default_context);
+}
+
+/**
+ * The default context needs to exist per ring that uses contexts. It stores the
+ * context state of the GPU for applications that don't utilize HW contexts, as
+ * well as an idle case.
+ */
+static int create_default_context(struct drm_i915_private *dev_priv)
+{
+ struct i915_hw_context *ctx;
+ struct drm_device *dev = (struct drm_device *)dev_priv->drmdev;
+ int ret;
+
+ rw_assert_wrlock(&dev->dev_lock);
+
+ ctx = create_hw_context(dev, NULL);
+ if (IS_ERR(ctx))
+ return (long)(ctx);
+
+ /* We may need to do things with the shrinker which require us to
+ * immediately switch back to the default context. This can cause a
+ * problem as pinning the default context also requires GTT space which
+ * may not be available. To avoid this we always pin the
+ * default context.
+ */
+ dev_priv->ring[RCS].default_context = ctx;
+ ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false);
+ if (ret)
+ goto err_destroy;
+
+ ret = do_switch(ctx);
+ if (ret)
+ goto err_unpin;
+
+ DRM_DEBUG_DRIVER("Default HW context loaded\n");
+ return 0;
+
+err_unpin:
+ i915_gem_object_unpin(ctx->obj);
+err_destroy:
+ do_destroy(ctx);
+ return ret;
+}
+
+void i915_gem_context_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t ctx_size;
+
+ if (!HAS_HW_CONTEXTS(dev)) {
+ dev_priv->hw_contexts_disabled = true;
+ return;
+ }
+
+ /* If called from reset, or thaw... we've been here already */
+ if (dev_priv->hw_contexts_disabled ||
+ dev_priv->ring[RCS].default_context)
+ return;
+
+ ctx_size = get_context_size(dev);
+ dev_priv->hw_context_size = get_context_size(dev);
+ dev_priv->hw_context_size = roundup2(dev_priv->hw_context_size, 4096);
+
+ if (ctx_size <= 0 || ctx_size > (1<<20)) {
+ dev_priv->hw_contexts_disabled = true;
+ return;
+ }
+
+ if (create_default_context(dev_priv)) {
+ dev_priv->hw_contexts_disabled = true;
+ return;
+ }
+
+ DRM_DEBUG_DRIVER("HW context support initialized\n");
+}
+
+void i915_gem_context_fini(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->hw_contexts_disabled)
+ return;
+
+ /* The only known way to stop the gpu from accessing the hw context is
+ * to reset it. Do this as the very last operation to avoid confusing
+ * other code, leading to spurious errors. */
+ intel_gpu_reset(dev);
+
+ i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj);
+
+ do_destroy(dev_priv->ring[RCS].default_context);
+}
+
+static int context_idr_cleanup(int id, void *p, void *data)
+{
+ struct i915_hw_context *ctx = p;
+
+ BUG_ON(id == DEFAULT_CONTEXT_ID);
+
+ do_destroy(ctx);
+
+ return 0;
+}
+
+void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
+{
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct i915_ctx_handle *han, *nxt;
+
+ DRM_LOCK();
+ for (han = SPLAY_MIN(i915_ctx_tree, &file_priv->ctx_tree); han != NULL;
+ han = nxt) {
+ nxt = SPLAY_NEXT(i915_ctx_tree, &file_priv->ctx_tree, han);
+ context_idr_cleanup(han->handle, han->ctx, NULL);
+ SPLAY_REMOVE(i915_ctx_tree, &file_priv->ctx_tree, han);
+ drm_free(han);
+ }
+ DRM_UNLOCK();
+}
+
+static struct i915_hw_context *
+i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
+{
+ struct i915_ctx_handle *han, search;
+ search.handle = id;
+ han = SPLAY_FIND(i915_ctx_tree, &file_priv->ctx_tree, &search);
+ if (han == NULL)
+ return NULL;
+
+ return (han->ctx);
+}
+
+static inline int
+mi_set_context(struct intel_ring_buffer *ring,
+ struct i915_hw_context *new_context,
+ u32 hw_flags)
+{
+ int ret;
+
+ /* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB
+ * invalidation prior to MI_SET_CONTEXT. On GEN6 we don't set the value
+ * explicitly, so we rely on the value at ring init, stored in
+ * itlb_before_ctx_switch.
+ */
+ if (IS_GEN6(ring->dev) && ring->itlb_before_ctx_switch) {
+ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ if (IS_GEN7(ring->dev))
+ intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
+ else
+ intel_ring_emit(ring, MI_NOOP);
+
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_emit(ring, MI_SET_CONTEXT);
+ intel_ring_emit(ring, new_context->obj->gtt_offset |
+ MI_MM_SPACE_GTT |
+ MI_SAVE_EXT_STATE_EN |
+ MI_RESTORE_EXT_STATE_EN |
+ hw_flags);
+ /* w/a: MI_SET_CONTEXT must always be followed by MI_NOOP */
+ intel_ring_emit(ring, MI_NOOP);
+
+ if (IS_GEN7(ring->dev))
+ intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);
+ else
+ intel_ring_emit(ring, MI_NOOP);
+
+ intel_ring_advance(ring);
+
+ return ret;
+}
+
+static int do_switch(struct i915_hw_context *to)
+{
+ struct intel_ring_buffer *ring = to->ring;
+ struct drm_i915_gem_object *from_obj = ring->last_context_obj;
+ u32 hw_flags = 0;
+ int ret;
+
+ BUG_ON(from_obj != NULL && from_obj->pin_count == 0);
+
+ if (from_obj == to->obj)
+ return 0;
+
+ ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false);
+ if (ret)
+ return ret;
+
+ /* Clear this page out of any CPU caches for coherent swap-in/out. Note
+ * that thanks to write = false in this call and us not setting any gpu
+ * write domains when putting a context object onto the active list
+ * (when switching away from it), this won't block.
+ * XXX: We need a real interface to do this instead of trickery. */
+ ret = i915_gem_object_set_to_gtt_domain(to->obj, false);
+ if (ret) {
+ i915_gem_object_unpin(to->obj);
+ return ret;
+ }
+
+#ifdef notyet
+ if (!to->obj->has_global_gtt_mapping)
+ i915_gem_gtt_bind_object(to->obj, to->obj->cache_level);
+#else
+ i915_gem_gtt_rebind_object(to->obj, to->obj->cache_level);
+#endif
+
+ if (!to->is_initialized || is_default_context(to))
+ hw_flags |= MI_RESTORE_INHIBIT;
+ else if (/*WARN_ON_ONCE*/(from_obj == to->obj)) /* not yet expected */
+ hw_flags |= MI_FORCE_RESTORE;
+
+ ret = mi_set_context(ring, to, hw_flags);
+ if (ret) {
+ i915_gem_object_unpin(to->obj);
+ return ret;
+ }
+
+ /* The backing object for the context is done after switching to the
+ * *next* context. Therefore we cannot retire the previous context until
+ * the next context has already started running. In fact, the below code
+ * is a bit suboptimal because the retiring can occur simply after the
+ * MI_SET_CONTEXT instead of when the next seqno has completed.
+ */
+ if (from_obj != NULL) {
+ from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
+ i915_gem_object_move_to_active(from_obj, ring);
+ /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
+ * whole damn pipeline, we don't need to explicitly mark the
+ * object dirty. The only exception is that the context must be
+ * correct in case the object gets swapped out. Ideally we'd be
+ * able to defer doing this until we know the object would be
+ * swapped, but there is no way to do that yet.
+ */
+ from_obj->dirty = 1;
+ BUG_ON(from_obj->ring != ring);
+ i915_gem_object_unpin(from_obj);
+
+ drm_gem_object_unreference(&from_obj->base);
+ }
+
+ drm_gem_object_reference(&to->obj->base);
+ ring->last_context_obj = to->obj;
+ to->is_initialized = true;
+
+ return 0;
+}
+
+/**
+ * i915_switch_context() - perform a GPU context switch.
+ * @ring: ring for which we'll execute the context switch
+ * @file_priv: file_priv associated with the context, may be NULL
+ * @id: context id number
+ * @seqno: sequence number by which the new context will be switched to
+ * @flags:
+ *
+ * The context life cycle is simple. The context refcount is incremented and
+ * decremented by 1 and create and destroy. If the context is in use by the GPU,
+ * it will have a refoucnt > 1. This allows us to destroy the context abstract
+ * object while letting the normal object tracking destroy the backing BO.
+ */
+int i915_switch_context(struct intel_ring_buffer *ring,
+ struct drm_file *file,
+ int to_id)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ struct i915_hw_context *to;
+
+ if (dev_priv->hw_contexts_disabled)
+ return 0;
+
+ if (ring != &dev_priv->ring[RCS])
+ return 0;
+
+ if (to_id == DEFAULT_CONTEXT_ID) {
+ to = ring->default_context;
+ } else {
+ if (file == NULL)
+ return -EINVAL;
+
+ to = i915_gem_context_get(file->driver_priv, to_id);
+ if (to == NULL)
+ return -ENOENT;
+ }
+
+ return do_switch(to);
+}
+
+int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_context_create *args = data;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct i915_hw_context *ctx;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_GEM))
+ return -ENODEV;
+
+ if (dev_priv->hw_contexts_disabled)
+ return -ENODEV;
+
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
+ ctx = create_hw_context(dev, file_priv);
+ DRM_UNLOCK();
+ if (IS_ERR(ctx))
+ return (long)(ctx);
+
+ args->ctx_id = ctx->id;
+ DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id);
+
+ return 0;
+}
+
+int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_i915_gem_context_destroy *args = data;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct i915_hw_context *ctx;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_GEM))
+ return -ENODEV;
+
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
+ ctx = i915_gem_context_get(file_priv, args->ctx_id);
+ if (!ctx) {
+ DRM_UNLOCK();
+ return -ENOENT;
+ }
+
+ do_destroy(ctx);
+
+ DRM_UNLOCK();
+
+ DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id);
+ return 0;
+}
+
+int
+i915_ctx_handle_cmp(struct i915_ctx_handle *a, struct i915_ctx_handle *b)
+{
+ return a->handle < b->handle ? -1 : a->handle > b->handle;
+}
+
+SPLAY_GENERATE(i915_ctx_tree, i915_ctx_handle, entry, i915_ctx_handle_cmp);
diff --git a/sys/dev/pci/drm/i915/i915_gem_execbuffer.c b/sys/dev/pci/drm/i915/i915_gem_execbuffer.c
index a96d3cb9ec4..cfc81528992 100644
--- a/sys/dev/pci/drm/i915/i915_gem_execbuffer.c
+++ b/sys/dev/pci/drm/i915/i915_gem_execbuffer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_gem_execbuffer.c,v 1.7 2013/05/05 13:55:36 kettenis Exp $ */
+/* $OpenBSD: i915_gem_execbuffer.c,v 1.8 2013/08/07 00:04:28 jsg Exp $ */
/*
* Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
*
@@ -425,6 +425,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
struct drm_obj **object_list = NULL;
struct drm_obj *batch_obj, *obj;
struct intel_ring_buffer *ring;
+ uint32_t ctx_id = i915_execbuffer2_get_context_id(*args);
size_t oflow;
int ret, ret2, i;
int pinned = 0, pin_tries;
@@ -630,6 +631,10 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
if (ret)
goto err;
+ ret = i915_switch_context(ring, file_priv, ctx_id);
+ if (ret)
+ goto err;
+
if (ring == &dev_priv->ring[RCS] &&
mode != dev_priv->relative_constants_mode) {
ret = intel_ring_begin(ring, 4);