diff --git a/drivers/gpu/drm/i915/i915_context.c b/drivers/gpu/drm/i915/i915_context.c
index bae7274..490aa5a 100644
--- a/drivers/gpu/drm/i915/i915_context.c
+++ b/drivers/gpu/drm/i915/i915_context.c
@@ -26,7 +26,163 @@
 
 #include "drmP.h"
 #include "i915_drm.h"
+#include "i915_drv.h"
 
+#define DEFAULT_CONTEXT_ID 0
+
+#define CONTEXT_SIZE dev_priv->context_size
+#define CONTEXT_ALIGN 4096
+
+static int context_generate_id(struct drm_i915_gem_context *ctx)
+{
+	struct drm_i915_file_private *file_priv = ctx->file->driver_priv;
+	int ret, id;
+
+	if (WARN_ON(!mutex_is_locked(&ctx->dev->struct_mutex)))
+		return -ENOENT;
+
+again:
+	if (idr_pre_get(&file_priv->context_idr, GFP_KERNEL) == 0)
+		return -ENOMEM;
+
+	spin_lock(&file_priv->context_idr_lock);
+	ret = idr_get_new_above(&file_priv->context_idr, ctx,
+				DEFAULT_CONTEXT_ID + 1, &id);
+	if (ret == -EAGAIN) {
+		spin_unlock(&file_priv->context_idr_lock);
+		goto again;
+	}
+	spin_unlock(&file_priv->context_idr_lock);
+
+	return id;
+}
+
+static void context_destroy_id(struct drm_i915_gem_context *ctx)
+{
+	struct drm_i915_file_private *file_priv = ctx->file->driver_priv;
+
+	spin_lock(&file_priv->context_idr_lock);
+	idr_remove(&file_priv->context_idr, ctx->id);
+	spin_unlock(&file_priv->context_idr_lock);
+}
+
+/*
+ * Initialize a context for the given ring.
+ * @wait_for_switch: whether or not to wait for the context switch instruction
+ * to actually execute.
+ */
+static int context_init(struct drm_i915_gem_context *ctx,
+			struct intel_ring_buffer *ring,
+			bool wait_for_switch)
+{
+	struct drm_i915_private *dev_priv = ctx->dev->dev_private;
+	struct drm_i915_gem_context *last;
+	int ret;
+
+	if (ring->context_switch == NULL)
+		return 0;
+
+	return -ENOMEM;
+}
+
+/*
+ * Logical context is created, and initialized. The context has a backing buffer
+ * object which is referenced at the end of this function.
+ *
+ * @ring_mask: rings for which this context should be initialized (1 means don't
+ *	init)
+ * @ctx_out: output context which was created
+ *
+ * XXX this function or a wrapper to it needs to be global for power related
+ * stuff
+ */
+static int logical_context_alloc(struct drm_device *dev, struct drm_file *file,
+				 uint8_t ring_mask,
+				 struct drm_i915_gem_context **ctx_out)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_i915_gem_context *ctx;
+	int ret, i;
+#define RING_MASK (uint8_t)((1 << I915_NUM_RINGS) - 1)
+	uint8_t pass = 0;
+
+	if (WARN_ON(!mutex_is_locked(&dev->struct_mutex)))
+		return -EINVAL;
+
+	ctx = kzalloc(sizeof(struct drm_i915_gem_context), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	ctx->file = file;
+
+	if (dev_priv->default_context == *ctx_out) {
+		ctx->is_default = true;
+		ctx->id = DEFAULT_CONTEXT_ID;
+	} else
+		ctx->id = context_generate_id(ctx);
+
+
+	ctx->obj = i915_gem_alloc_object(dev, CONTEXT_SIZE);
+	if (!ctx->obj) {
+		ret = -ENOMEM;
+		goto id_out;
+	}
+
+	ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
+	if (ret) {
+		DRM_ERROR("Failed to pin context: %d\n", ret);
+		goto err_unref;
+	}
+
+	/* XXX do we need this, we can get rid of pinning above if not? */
+	ret = i915_gem_object_set_to_gtt_domain(ctx->obj, false);
+	if (ret) {
+		DRM_ERROR("failed to set domain on context: %d", ret);
+		goto err_unpin;
+	}
+
+	i915_gem_object_unpin(ctx->obj);
+
+	for (i = 0; i < I915_NUM_RINGS; i++) {
+		if (ring_mask & (1 << i))
+			continue;
+
+		ret = context_init(ctx, &dev_priv->ring[i], true);
+		if (!ret)
+			pass |= (1 << i);
+	}
+
+	if ((ring_mask ^ pass) != RING_MASK) {
+		DRM_DEBUG_DRIVER("ring(s) couldn't initialize context "
+				 "mask = %x, passed = %x\n", ring_mask, pass);
+		if (!pass) {
+			DRM_ERROR("Couldn't initialize context for any ring");
+			ret = -EIO;
+			goto err_unref;
+		}
+	}
+
+	ctx->ring_enable = pass;
+
+	DRM_DEBUG_DRIVER("Context %d allocated, rings %x\n", ctx->id, pass);
+	*ctx_out = ctx;
+	return 0;
+
+err_unpin:
+	i915_gem_object_unpin(ctx->obj);
+err_unref:
+	drm_gem_object_unreference(&ctx->obj->base);
+id_out:
+	if (!ctx->is_default)
+		context_destroy_id(ctx);
+	kfree(ctx);
+	return ret;
+}
+
+/**
+ * i915_context_create_ioctl() - not yet supported
+ */
 int i915_context_create_ioctl(struct drm_device *dev, void *data,
 			      struct drm_file *file)
 {
@@ -35,6 +191,9 @@ int i915_context_create_ioctl(struct drm_device *dev, void *data,
 	return -EIO;
 }
 
+/**
+ * i915_context_destroy_ioctl() - not yet supported
+ */
 int i915_context_destroy_ioctl(struct drm_device *dev, void *data,
 			       struct drm_file *file)
 {
@@ -42,14 +201,44 @@ int i915_context_destroy_ioctl(struct drm_device *dev, void *data,
 	return -EINVAL;
 }
 
+/**
+ * i915_context_load() - Creates a default context.
+ * @dev: Device for which the context.
+ */
 void i915_context_load(struct drm_device *dev)
 {
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t size;
+
+	int ret;
+	if (!HAS_HW_CONTEXTS(dev)) {
+		dev_priv->contexts_disabled = true;
+		return;
+	}
 
+	mutex_lock(&dev->struct_mutex);
+	size = I915_READ(CXT_SIZE);
+	if (size > 0x4000) /*  1MB */ {
+		DRM_ERROR("Context bo size seems invalid.");
+		size = 20;
+	}
+	size *= 64;
+	size = roundup(size, 4096);
+	dev_priv->context_size = size;
+	DRM_DEBUG_DRIVER("Logical context size = %d\n", size);
+	ret = logical_context_alloc(dev, NULL, 0, &dev_priv->default_context);
+	if (ret)
+		dev_priv->contexts_disabled = true;
+	mutex_unlock(&dev->struct_mutex);
 }
 
 void i915_context_unload(struct drm_device *dev)
 {
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (dev_priv->contexts_disabled)
+		return;
 
+	kfree(dev_priv->default_context);
 }
 
 void i915_context_open(struct drm_device *dev, struct drm_file *file)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 3bd9875..15f86fb 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -709,6 +709,10 @@ typedef struct drm_i915_private {
 	struct intel_fbdev *fbdev;
 
 	struct drm_property *broadcast_rgb_property;
+
+	uint32_t context_size;
+	struct drm_i915_gem_context *default_context;
+	bool contexts_disabled;
 } drm_i915_private_t;
 
 struct drm_i915_gem_object {
@@ -902,8 +906,8 @@ struct drm_i915_gem_context {
 	struct drm_file *file;
 
 	uint32_t id;
-	struct drm_gem_object *obj;
 	uint8_t ring_enable;
+	struct drm_i915_gem_object *obj;
 	bool is_default;
 
 	struct drm_gem_object **bufs;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index bd6a5fb..a4e707c 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -77,6 +77,12 @@ struct  intel_ring_buffer {
 	int		(*dispatch_execbuffer)(struct intel_ring_buffer *ring,
 					       u32 offset, u32 length);
 	void		(*cleanup)(struct intel_ring_buffer *ring);
+	struct		drm_i915_gem_context *last_context;
+	struct drm_i915_gem_context *(*context_switch)
+				      (struct intel_ring_buffer *ring,
+				       struct drm_i915_gem_context *ctx,
+				       u32 segno,
+				       u32 flags);
 
 	/**
 	 * List of objects currently involved in rendering from the
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 28e3c5f..49df333 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -851,8 +851,12 @@ struct drm_intel_overlay_attrs {
 struct drm_i915_gem_context_create {
 	/* input: number of slots to allocate */
 	__s32 slot_count;
-	/* output: id of new context*/
+	/* input: desired ring mask */
+	__u8 ring_mask;
+	/* output: id of new context */
 	__u32 ctx_id;
+	/* output: rings for which context is valid */
+	__u8 ring_enable;
 };
 
 struct drm_i915_gem_context_destroy {
