diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 451b547352b7..7a84f04e8439 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -647,6 +647,7 @@ int i915_gem_object_unbind(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); +int i915_gem_object_put_fence_reg(struct drm_gem_object *obj); void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_work_handler(struct work_struct *work); void i915_gem_clflush_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4f345414fe7c..174aef2d6481 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2162,7 +2162,6 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) val |= I830_FENCE_REG_VALID; I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val); - } /** @@ -2328,6 +2327,42 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj) obj_priv->fence_reg = I915_FENCE_REG_NONE; } +/** + * i915_gem_object_put_fence_reg - waits on outstanding fenced access + * to the buffer to finish, and then resets the fence register. + * @obj: tiled object holding a fence register. + * + * Zeroes out the fence register itself and clears out the associated + * data structures in dev_priv and obj_priv. + */ +int +i915_gem_object_put_fence_reg(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (obj_priv->fence_reg == I915_FENCE_REG_NONE) + return 0; + + /* On the i915, GPU access to tiled buffers is via a fence, + * therefore we must wait for any outstanding access to complete + * before clearing the fence. + */ + if (!IS_I965G(dev)) { + int ret; + + i915_gem_object_flush_gpu_write_domain(obj); + i915_gem_object_flush_gtt_write_domain(obj); + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) + return ret; + } + + i915_gem_clear_fence_reg (obj); + + return 0; +} + /** * Finds free space in the GTT aperture and binds the object there. */ diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 9a05cadaa4ad..5c1ceec49f5b 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -408,7 +408,7 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) if (stride & (stride - 1)) return false; - /* We don't handle the aperture area covered by the fence being bigger + /* We don't 0handle the aperture area covered by the fence being bigger * than the object size. */ if (i915_get_fence_size(dev, size) != size) @@ -417,6 +417,33 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) return true; } +static bool +i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, int tiling_mode) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (obj_priv->gtt_space == NULL) + return true; + + if (tiling_mode == I915_TILING_NONE) + return true; + + if (!IS_I965G(dev)) { + if (obj_priv->gtt_offset & (obj->size - 1)) + return false; + if (IS_I9XX(dev)) { + if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK) + return false; + } else { + if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK) + return false; + } + } + + return true; +} + /** * Sets the tiling mode of an object, returning the required swizzling of * bit 6 of addresses in the object. @@ -429,6 +456,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; + int ret = 0; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) @@ -436,14 +464,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj_priv = obj->driver_private; if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) { + mutex_lock(&dev->struct_mutex); drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); return -EINVAL; } - mutex_lock(&dev->struct_mutex); - if (args->tiling_mode == I915_TILING_NONE) { args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + args->stride = 0; } else { if (args->tiling_mode == I915_TILING_X) args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x; @@ -466,32 +495,38 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, if (args->swizzle_mode == I915_BIT_6_SWIZZLE_UNKNOWN) { args->tiling_mode = I915_TILING_NONE; args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + args->stride = 0; } } - 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. + mutex_lock(&dev->struct_mutex); + if (args->tiling_mode != obj_priv->tiling_mode || + args->stride != obj_priv->stride) { + /* We need to rebind the object if its current allocation + * no longer meets the alignment restrictions for its new + * tiling mode. Otherwise we can just leave it alone, but + * need to ensure that any fence register is cleared. */ - ret = i915_gem_object_unbind(obj); + if (!i915_gem_object_fence_offset_ok(obj, args->tiling_mode)) + ret = i915_gem_object_unbind(obj); + else + ret = i915_gem_object_put_fence_reg(obj); if (ret != 0) { WARN(ret != -ERESTARTSYS, - "failed to unbind object for tiling switch"); + "failed to reset object for tiling switch"); args->tiling_mode = obj_priv->tiling_mode; - mutex_unlock(&dev->struct_mutex); - drm_gem_object_unreference(obj); - - return ret; + args->stride = obj_priv->stride; + goto err; } - obj_priv->tiling_mode = args->tiling_mode; - } - obj_priv->stride = args->stride; + obj_priv->tiling_mode = args->tiling_mode; + obj_priv->stride = args->stride; + } +err: drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); - return 0; + return ret; } /**