@@ -99,13 +99,70 @@ static int context_init(struct drm_i915_gem_context *ctx,
bool wait_for_switch)
{
struct drm_i915_private *dev_priv = ctx->dev->dev_private;
- struct drm_i915_gem_context *last;
+ struct drm_i915_gem_context *last = NULL;
int ret;
if (ring->context_switch == NULL)
return 0;
- return -ENOMEM;
+ drm_gem_object_reference(&ctx->obj->base);
+ ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
+ if (ret) {
+ drm_gem_object_unreference(&ctx->obj->base);
+ return ret;
+ }
+
+ if (ctx->is_default) {
+ /*
+ * XXX default context is always first. The first context needs
+ * to do an extra save because the first save (according to the
+ * spec) doesn't actually do anything. So the outcome is
+ * 1. Save without restore (no context saved)
+ * 2. Save without restore (context is saved)
+ * 3. Save with restore (loads the ctx from step 2)
+ */
+ last = ring->context_switch(ring, ctx, 0,
+ I915_CONTEXT_SAVE_ONLY);
+ if (!last) {
+ ret = EIO;
+ goto err_out;
+ }
+ last = ring->context_switch(ring, ctx, 0,
+ I915_CONTEXT_SAVE_ONLY |
+ I915_CONTEXT_FORCED_SWITCH);
+ if (!last) {
+ ret = EIO;
+ goto err_out;
+ }
+ last = ring->context_switch(ring, ctx, 0,
+ I915_CONTEXT_NORMAL_SWITCH |
+ I915_CONTEXT_FORCED_SWITCH);
+ } else {
+ last = ring->context_switch(ring, ctx, 0,
+ I915_CONTEXT_SAVE_ONLY);
+ }
+
+ if (!last) {
+ ret = EIO;
+ goto err_out;
+ }
+
+ if (!last->is_default)
+ i915_release_context(last);
+
+ if (wait_for_switch) {
+ ret = wait_for_context_switch(ring);
+ if (ret)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ WARN_ON(ctx->is_default);
+ i915_gem_object_unpin(ctx->obj);
+ drm_gem_object_unreference(&ctx->obj->base);
+ return ret;
}
/*
@@ -316,3 +373,9 @@ struct drm_i915_gem_context *i915_get_context(struct drm_file *file,
{
return NULL;
}
+
+void i915_release_context(struct drm_i915_gem_context *ctx)
+{
+ i915_gem_object_unpin(ctx->obj);
+ drm_gem_object_unreference(&ctx->obj->base);
+}
@@ -914,6 +914,10 @@ struct drm_i915_gem_context {
int slot_count;
};
+#define I915_CONTEXT_NORMAL_SWITCH (1 << 0)
+#define I915_CONTEXT_SAVE_ONLY (1 << 1)
+#define I915_CONTEXT_FORCED_SWITCH (1 << 2)
+
enum intel_chip_family {
CHIP_I8XX = 0x01,
CHIP_I9XX = 0x02,
@@ -1157,6 +1161,7 @@ extern void i915_context_open(struct drm_device *dev, struct drm_file *file);
extern void i915_context_close(struct drm_device *dev, struct drm_file *file);
extern struct drm_i915_gem_context *i915_get_context(struct drm_file *file,
uint32_t id);
+extern void i915_release_context(struct drm_i915_gem_context *ctx);
/**
* Returns true if seq1 is later than seq2.