@@ -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)
@@ -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;
@@ -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
@@ -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 {