]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/gpu/drm/i915/i915_gem_tiling.c
Merge branches 'x86/acpi', 'x86/asm', 'x86/cpudetect', 'x86/crashdump', 'x86/debug...
[linux-2.6-omap-h63xx.git] / drivers / gpu / drm / i915 / i915_gem_tiling.c
index a8cb69469c641ff28f4d57df20b51d0dd5772f7c..fa1685cba8402fdbd456be37f627bd21c170cca4 100644 (file)
@@ -173,6 +173,73 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        dev_priv->mm.bit_6_swizzle_y = swizzle_y;
 }
 
+
+/**
+ * Returns the size of the fence for a tiled object of the given size.
+ */
+static int
+i915_get_fence_size(struct drm_device *dev, int size)
+{
+       int i;
+       int start;
+
+       if (IS_I965G(dev)) {
+               /* The 965 can have fences at any page boundary. */
+               return ALIGN(size, 4096);
+       } else {
+               /* Align the size to a power of two greater than the smallest
+                * fence size.
+                */
+               if (IS_I9XX(dev))
+                       start = 1024 * 1024;
+               else
+                       start = 512 * 1024;
+
+               for (i = start; i < size; i <<= 1)
+                       ;
+
+               return i;
+       }
+}
+
+/* Check pitch constriants for all chips & tiling formats */
+static bool
+i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
+{
+       int tile_width;
+
+       /* Linear is always fine */
+       if (tiling_mode == I915_TILING_NONE)
+               return true;
+
+       if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
+               tile_width = 128;
+       else
+               tile_width = 512;
+
+       /* 965+ just needs multiples of tile width */
+       if (IS_I965G(dev)) {
+               if (stride & (tile_width - 1))
+                       return false;
+               return true;
+       }
+
+       /* Pre-965 needs power of two tile widths */
+       if (stride < tile_width)
+               return false;
+
+       if (stride & (stride - 1))
+               return false;
+
+       /* We don't handle the aperture area covered by the fence being bigger
+        * than the object size.
+        */
+       if (i915_get_fence_size(dev, size) != size)
+               return false;
+
+       return true;
+}
+
 /**
  * Sets the tiling mode of an object, returning the required swizzling of
  * bit 6 of addresses in the object.
@@ -191,6 +258,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                return -EINVAL;
        obj_priv = obj->driver_private;
 
+       if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) {
+               drm_gem_object_unreference(obj);
+               return -EINVAL;
+       }
+
        mutex_lock(&dev->struct_mutex);
 
        if (args->tiling_mode == I915_TILING_NONE) {
@@ -207,7 +279,25 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                        args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
                }
        }
-       obj_priv->tiling_mode = args->tiling_mode;
+       if (args->tiling_mode != obj_priv->tiling_mode) {
+               int ret;
+
+               /* Unbind the object, as switching tiling means we're
+                * switching the cache organization due to fencing, probably.
+                */
+               ret = i915_gem_object_unbind(obj);
+               if (ret != 0) {
+                       WARN(ret != -ERESTARTSYS,
+                            "failed to unbind object for tiling switch");
+                       args->tiling_mode = obj_priv->tiling_mode;
+                       mutex_unlock(&dev->struct_mutex);
+                       drm_gem_object_unreference(obj);
+
+                       return ret;
+               }
+               obj_priv->tiling_mode = args->tiling_mode;
+       }
+       obj_priv->stride = args->stride;
 
        mutex_unlock(&dev->struct_mutex);