drm/i915: fix up tiling/fence reg setup on i8xx class hw
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / i915_gem_tiling.c
index 0c1b3a0..6be3f92 100644 (file)
@@ -96,15 +96,16 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                 */
                swizzle_x = I915_BIT_6_SWIZZLE_NONE;
                swizzle_y = I915_BIT_6_SWIZZLE_NONE;
-       } else if (!IS_I965G(dev) || IS_I965GM(dev)) {
+       } else if (IS_MOBILE(dev)) {
                uint32_t dcc;
 
-               /* On 915-945 and GM965, channel interleave by the CPU is
-                * determined by DCC.  The CPU will alternate based on bit 6
-                * in interleaved mode, and the GPU will then also alternate
-                * on bit 6, 9, and 10 for X, but the CPU may also optionally
-                * alternate based on bit 17 (XOR not disabled and XOR
-                * bit == 17).
+               /* On mobile 9xx chipsets, channel interleave by the CPU is
+                * determined by DCC.  For single-channel, neither the CPU
+                * nor the GPU do swizzling.  For dual channel interleaved,
+                * the GPU's interleave is bit 9 and 10 for X tiled, and bit
+                * 9 for Y tiled.  The CPU's interleave is independent, and
+                * can be based on either bit 11 (haven't seen this yet) or
+                * bit 17 (common).
                 */
                dcc = I915_READ(DCC);
                switch (dcc & DCC_ADDRESSING_MODE_MASK) {
@@ -114,18 +115,18 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                        swizzle_y = I915_BIT_6_SWIZZLE_NONE;
                        break;
                case DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED:
-                       if (IS_I915G(dev) || IS_I915GM(dev) ||
-                           dcc & DCC_CHANNEL_XOR_DISABLE) {
+                       if (dcc & DCC_CHANNEL_XOR_DISABLE) {
+                               /* This is the base swizzling by the GPU for
+                                * tiled buffers.
+                                */
                                swizzle_x = I915_BIT_6_SWIZZLE_9_10;
                                swizzle_y = I915_BIT_6_SWIZZLE_9;
-                       } else if (IS_I965GM(dev)) {
-                               /* GM965 only does bit 11-based channel
-                                * randomization
-                                */
+                       } else if ((dcc & DCC_CHANNEL_XOR_BIT_17) == 0) {
+                               /* Bit 11 swizzling by the CPU in addition. */
                                swizzle_x = I915_BIT_6_SWIZZLE_9_10_11;
                                swizzle_y = I915_BIT_6_SWIZZLE_9_11;
                        } else {
-                               /* Bit 17 or perhaps other swizzling */
+                               /* Bit 17 swizzling by the CPU in addition. */
                                swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
                                swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
                        }
@@ -171,6 +172,89 @@ 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;
+
+       /* check maximum stride & object size */
+       if (IS_I965G(dev)) {
+               /* i965 stores the end address of the gtt mapping in the fence
+                * reg, so dont bother to check the size */
+               if (stride / 128 > I965_FENCE_MAX_PITCH_VAL)
+                       return false;
+       } else if (IS_I9XX(dev)) {
+               if (stride / tile_width > I830_FENCE_MAX_PITCH_VAL ||
+                   size > (I830_FENCE_MAX_SIZE_VAL << 20))
+                       return false;
+       } else {
+               if (stride / 128 > I830_FENCE_MAX_PITCH_VAL ||
+                   size > (I830_FENCE_MAX_SIZE_VAL << 19))
+                       return false;
+       }
+
+       /* 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.
@@ -189,6 +273,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) {
@@ -205,11 +294,28 @@ 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;
 
-       mutex_unlock(&dev->struct_mutex);
+               /* 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;
 
        drm_gem_object_unreference(obj);
+       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 }
@@ -248,9 +354,8 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
                DRM_ERROR("unknown tiling mode\n");
        }
 
-       mutex_unlock(&dev->struct_mutex);
-
        drm_gem_object_unreference(obj);
+       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 }