Merge branch 'drm-intel-fixes' into drm-intel-next
[pandora-kernel.git] / drivers / gpu / drm / i915 / i915_gem.c
index c797d2b..54fc1e7 100644 (file)
 #include <linux/pci.h>
 #include <linux/intel-gtt.h>
 
+struct change_domains {
+       uint32_t invalidate_domains;
+       uint32_t flush_domains;
+       uint32_t flush_rings;
+};
+
 static uint32_t i915_gem_get_gtt_alignment(struct drm_i915_gem_object *obj_priv);
 static uint32_t i915_gem_get_gtt_size(struct drm_i915_gem_object *obj_priv);
 
@@ -53,8 +59,7 @@ static int i915_gem_object_wait_rendering(struct drm_gem_object *obj,
                                          bool interruptible);
 static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                                       unsigned alignment,
-                                      bool mappable,
-                                      bool need_fence);
+                                      bool map_and_fenceable);
 static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
 static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
                                struct drm_i915_gem_pwrite *args,
@@ -1068,7 +1073,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        else if (obj_priv->tiling_mode == I915_TILING_NONE &&
                 obj_priv->gtt_space &&
                 obj->write_domain != I915_GEM_DOMAIN_CPU) {
-               ret = i915_gem_object_pin(obj, 0, true, false);
+               ret = i915_gem_object_pin(obj, 0, true);
                if (ret)
                        goto out;
 
@@ -1294,8 +1299,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        BUG_ON(obj_priv->pin_count && !obj_priv->pin_mappable);
 
        if (obj_priv->gtt_space) {
-               if (!obj_priv->mappable ||
-                   (obj_priv->tiling_mode && !obj_priv->fenceable)) {
+               if (!obj_priv->map_and_fenceable) {
                        ret = i915_gem_object_unbind(obj);
                        if (ret)
                                goto unlock;
@@ -1303,8 +1307,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        }
 
        if (!obj_priv->gtt_space) {
-               ret = i915_gem_object_bind_to_gtt(obj, 0,
-                                                 true, obj_priv->tiling_mode);
+               ret = i915_gem_object_bind_to_gtt(obj, 0, true);
                if (ret)
                        goto unlock;
        }
@@ -1337,11 +1340,12 @@ unlock:
        mutex_unlock(&dev->struct_mutex);
 
        switch (ret) {
+       case -EAGAIN:
+               set_need_resched();
        case 0:
        case -ERESTARTSYS:
                return VM_FAULT_NOPAGE;
        case -ENOMEM:
-       case -EAGAIN:
                return VM_FAULT_OOM;
        default:
                return VM_FAULT_SIGBUS;
@@ -1463,7 +1467,7 @@ i915_gem_free_mmap_offset(struct drm_gem_object *obj)
  * @obj: object to check
  *
  * Return the required GTT alignment for an object, taking into account
- * potential fence register mapping if needed.
+ * potential fence register mapping.
  */
 static uint32_t
 i915_gem_get_gtt_alignment(struct drm_i915_gem_object *obj_priv)
@@ -1485,6 +1489,41 @@ i915_gem_get_gtt_alignment(struct drm_i915_gem_object *obj_priv)
        return i915_gem_get_gtt_size(obj_priv);
 }
 
+/**
+ * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an
+ *                                      unfenced object
+ * @obj: object to check
+ *
+ * Return the required GTT alignment for an object, only taking into account
+ * unfenced tiled surface requirements.
+ */
+static uint32_t
+i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj_priv)
+{
+       struct drm_device *dev = obj_priv->base.dev;
+       int tile_height;
+
+       /*
+        * Minimum alignment is 4k (GTT page size) for sane hw.
+        */
+       if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) ||
+           obj_priv->tiling_mode == I915_TILING_NONE)
+               return 4096;
+
+       /*
+        * Older chips need unfenced tiled buffers to be aligned to the left
+        * edge of an even tile row (where tile rows are counted as if the bo is
+        * placed in a fenced gtt region).
+        */
+       if (IS_GEN2(dev) ||
+           (obj_priv->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)))
+               tile_height = 32;
+       else
+               tile_height = 8;
+
+       return tile_height * obj_priv->stride * 2;
+}
+
 static uint32_t
 i915_gem_get_gtt_size(struct drm_i915_gem_object *obj_priv)
 {
@@ -1660,9 +1699,7 @@ i915_gem_next_request_seqno(struct drm_device *dev,
                            struct intel_ring_buffer *ring)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-
-       ring->outstanding_lazy_request = true;
-       return dev_priv->next_seqno;
+       return ring->outstanding_lazy_request = dev_priv->next_seqno;
 }
 
 static void
@@ -2068,7 +2105,7 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
        if (atomic_read(&dev_priv->mm.wedged))
                return -EAGAIN;
 
-       if (ring->outstanding_lazy_request) {
+       if (seqno == ring->outstanding_lazy_request) {
                struct drm_i915_gem_request *request;
 
                request = kzalloc(sizeof(*request), GFP_KERNEL);
@@ -2083,7 +2120,6 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
 
                seqno = request->seqno;
        }
-       BUG_ON(seqno == dev_priv->next_seqno);
 
        if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) {
                if (HAS_PCH_SPLIT(dev))
@@ -2267,8 +2303,8 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
 
        i915_gem_info_remove_gtt(dev_priv, obj_priv);
        list_del_init(&obj_priv->mm_list);
-       obj_priv->fenceable = true;
-       obj_priv->mappable = true;
+       /* Avoid an unnecessary call to unbind on rebind. */
+       obj_priv->map_and_fenceable = true;
 
        drm_mm_put_block(obj_priv->gtt_space);
        obj_priv->gtt_space = NULL;
@@ -2285,7 +2321,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
 static int i915_ring_idle(struct drm_device *dev,
                          struct intel_ring_buffer *ring)
 {
-       if (list_empty(&ring->gpu_write_list))
+       if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list))
                return 0;
 
        i915_gem_flush_ring(dev, NULL, ring,
@@ -2303,9 +2339,7 @@ i915_gpu_idle(struct drm_device *dev)
        int ret;
 
        lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
-                      list_empty(&dev_priv->render_ring.active_list) &&
-                      list_empty(&dev_priv->bsd_ring.active_list) &&
-                      list_empty(&dev_priv->blt_ring.active_list));
+                      list_empty(&dev_priv->mm.active_list));
        if (lists_empty)
                return 0;
 
@@ -2379,7 +2413,7 @@ static void i915_write_fence_reg(struct drm_gem_object *obj)
        if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
            (obj_priv->gtt_offset & (size - 1))) {
                WARN(1, "%s: object 0x%08x [fenceable? %d] not 1M or size (0x%08x) aligned [gtt_space offset=%lx, size=%lx]\n",
-                    __func__, obj_priv->gtt_offset, obj_priv->fenceable, size,
+                    __func__, obj_priv->gtt_offset, obj_priv->map_and_fenceable, size,
                     obj_priv->gtt_space->start, obj_priv->gtt_space->size);
                return;
        }
@@ -2683,15 +2717,15 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj,
 static int
 i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                            unsigned alignment,
-                           bool mappable,
-                           bool need_fence)
+                           bool map_and_fenceable)
 {
        struct drm_device *dev = obj->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        struct drm_mm_node *free_space;
        gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN;
-       u32 size, fence_size, fence_alignment;
+       u32 size, fence_size, fence_alignment, unfenced_alignment;
+       bool mappable, fenceable;
        int ret;
 
        if (obj_priv->madv != I915_MADV_WILLNEED) {
@@ -2701,27 +2735,29 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
 
        fence_size = i915_gem_get_gtt_size(obj_priv);
        fence_alignment = i915_gem_get_gtt_alignment(obj_priv);
+       unfenced_alignment = i915_gem_get_unfenced_gtt_alignment(obj_priv);
 
        if (alignment == 0)
-               alignment = need_fence ? fence_alignment : 4096;
-       if (need_fence && alignment & (fence_alignment - 1)) {
+               alignment = map_and_fenceable ? fence_alignment :
+                                               unfenced_alignment;
+       if (map_and_fenceable && alignment & (fence_alignment - 1)) {
                DRM_ERROR("Invalid object alignment requested %u\n", alignment);
                return -EINVAL;
        }
 
-       size = need_fence ? fence_size : obj->size;
+       size = map_and_fenceable ? fence_size : obj->size;
 
        /* If the object is bigger than the entire aperture, reject it early
         * before evicting everything in a vain attempt to find space.
         */
        if (obj->size >
-           (mappable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) {
+           (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) {
                DRM_ERROR("Attempting to bind an object larger than the aperture\n");
                return -E2BIG;
        }
 
  search_free:
-       if (mappable)
+       if (map_and_fenceable)
                free_space =
                        drm_mm_search_free_in_range(&dev_priv->mm.gtt_space,
                                                    size, alignment, 0,
@@ -2732,7 +2768,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                                                size, alignment, 0);
 
        if (free_space != NULL) {
-               if (mappable)
+               if (map_and_fenceable)
                        obj_priv->gtt_space =
                                drm_mm_get_block_range_generic(free_space,
                                                               size, alignment, 0,
@@ -2746,7 +2782,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                /* If the gtt is empty and we're still having trouble
                 * fitting our object in, we're out of memory.
                 */
-               ret = i915_gem_evict_something(dev, size, alignment, mappable);
+               ret = i915_gem_evict_something(dev, size, alignment,
+                                              map_and_fenceable);
                if (ret)
                        return ret;
 
@@ -2761,7 +2798,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                if (ret == -ENOMEM) {
                        /* first try to clear up some space from the GTT */
                        ret = i915_gem_evict_something(dev, size,
-                                                      alignment, mappable);
+                                                      alignment,
+                                                      map_and_fenceable);
                        if (ret) {
                                /* now try to shrink everyone else */
                                if (gfpmask) {
@@ -2792,7 +2830,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
                obj_priv->gtt_space = NULL;
 
                ret = i915_gem_evict_something(dev, size,
-                                              alignment, mappable);
+                                              alignment, map_and_fenceable);
                if (ret)
                        return ret;
 
@@ -2812,15 +2850,17 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
        BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
        BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
 
-       trace_i915_gem_object_bind(obj, obj_priv->gtt_offset, mappable);
+       trace_i915_gem_object_bind(obj, obj_priv->gtt_offset, map_and_fenceable);
 
-       obj_priv->fenceable =
+       fenceable =
                obj_priv->gtt_space->size == fence_size &&
                (obj_priv->gtt_space->start & (fence_alignment -1)) == 0;
 
-       obj_priv->mappable =
+       mappable =
                obj_priv->gtt_offset + obj->size <= dev_priv->mm.gtt_mappable_end;
 
+       obj_priv->map_and_fenceable = mappable && fenceable;
+
        return 0;
 }
 
@@ -2995,6 +3035,20 @@ i915_gem_object_set_to_display_plane(struct drm_gem_object *obj,
        return 0;
 }
 
+int
+i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj,
+                         bool interruptible)
+{
+       if (!obj->active)
+               return 0;
+
+       if (obj->base.write_domain & I915_GEM_GPU_DOMAINS)
+               i915_gem_flush_ring(obj->base.dev, NULL, obj->ring,
+                                   0, obj->base.write_domain);
+
+       return i915_gem_object_wait_rendering(&obj->base, interruptible);
+}
+
 /**
  * Moves a single object to the CPU read, and possibly write domain.
  *
@@ -3167,10 +3221,9 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
  */
 static void
 i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
-                                 struct intel_ring_buffer *ring)
+                                 struct intel_ring_buffer *ring,
+                                 struct change_domains *cd)
 {
-       struct drm_device               *dev = obj->dev;
-       struct drm_i915_private         *dev_priv = dev->dev_private;
        struct drm_i915_gem_object      *obj_priv = to_intel_bo(obj);
        uint32_t                        invalidate_domains = 0;
        uint32_t                        flush_domains = 0;
@@ -3216,12 +3269,12 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
        if (flush_domains == 0 && obj->pending_write_domain == 0)
                obj->pending_write_domain = obj->write_domain;
 
-       dev->invalidate_domains |= invalidate_domains;
-       dev->flush_domains |= flush_domains;
+       cd->invalidate_domains |= invalidate_domains;
+       cd->flush_domains |= flush_domains;
        if (flush_domains & I915_GEM_GPU_DOMAINS)
-               dev_priv->mm.flush_rings |= obj_priv->ring->id;
+               cd->flush_rings |= obj_priv->ring->id;
        if (invalidate_domains & I915_GEM_GPU_DOMAINS)
-               dev_priv->mm.flush_rings |= ring->id;
+               cd->flush_rings |= ring->id;
 }
 
 /**
@@ -3535,8 +3588,7 @@ i915_gem_execbuffer_pin(struct drm_device *dev,
                                entry->relocation_count ? true : need_fence;
 
                        /* Check fence reg constraints and rebind if necessary */
-                       if ((need_fence && !obj->fenceable) ||
-                           (need_mappable && !obj->mappable)) {
+                       if (need_mappable && !obj->map_and_fenceable) {
                                ret = i915_gem_object_unbind(&obj->base);
                                if (ret)
                                        break;
@@ -3544,8 +3596,7 @@ i915_gem_execbuffer_pin(struct drm_device *dev,
 
                        ret = i915_gem_object_pin(&obj->base,
                                                  entry->alignment,
-                                                 need_mappable,
-                                                 need_fence);
+                                                 need_mappable);
                        if (ret)
                                break;
 
@@ -3590,30 +3641,26 @@ i915_gem_execbuffer_move_to_gpu(struct drm_device *dev,
                                struct drm_gem_object **objects,
                                int count)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct change_domains cd;
        int ret, i;
 
-       /* Zero the global flush/invalidate flags. These
-        * will be modified as new domains are computed
-        * for each object
-        */
-       dev->invalidate_domains = 0;
-       dev->flush_domains = 0;
-       dev_priv->mm.flush_rings = 0;
+       cd.invalidate_domains = 0;
+       cd.flush_domains = 0;
+       cd.flush_rings = 0;
        for (i = 0; i < count; i++)
-               i915_gem_object_set_to_gpu_domain(objects[i], ring);
+               i915_gem_object_set_to_gpu_domain(objects[i], ring, &cd);
 
-       if (dev->invalidate_domains | dev->flush_domains) {
+       if (cd.invalidate_domains | cd.flush_domains) {
 #if WATCH_EXEC
                DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
                          __func__,
-                        dev->invalidate_domains,
-                        dev->flush_domains);
+                        cd.invalidate_domains,
+                        cd.flush_domains);
 #endif
                i915_gem_flush(dev, file,
-                              dev->invalidate_domains,
-                              dev->flush_domains,
-                              dev_priv->mm.flush_rings);
+                              cd.invalidate_domains,
+                              cd.flush_domains,
+                              cd.flush_rings);
        }
 
        for (i = 0; i < count; i++) {
@@ -3974,7 +4021,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        i915_retire_commands(dev, ring);
 
        if (i915_add_request(dev, file, request, ring))
-               ring->outstanding_lazy_request = true;
+               i915_gem_next_request_seqno(dev, ring);
        else
                request = NULL;
 
@@ -4144,7 +4191,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
 
 int
 i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment,
-                   bool mappable, bool need_fence)
+                   bool map_and_fenceable)
 {
        struct drm_device *dev = obj->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4152,18 +4199,19 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment,
        int ret;
 
        BUG_ON(obj_priv->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
+       BUG_ON(map_and_fenceable && !map_and_fenceable);
        WARN_ON(i915_verify_lists(dev));
 
        if (obj_priv->gtt_space != NULL) {
                if ((alignment && obj_priv->gtt_offset & (alignment - 1)) ||
-                   (need_fence && !obj_priv->fenceable) ||
-                   (mappable && !obj_priv->mappable)) {
+                   (map_and_fenceable && !obj_priv->map_and_fenceable)) {
                        WARN(obj_priv->pin_count,
                             "bo is already pinned with incorrect alignment:"
-                            " offset=%x, req.alignment=%x, need_fence=%d, fenceable=%d, mappable=%d, cpu_accessible=%d\n",
+                            " offset=%x, req.alignment=%x, req.map_and_fenceable=%d,"
+                            " obj->map_and_fenceable=%d\n",
                             obj_priv->gtt_offset, alignment,
-                            need_fence, obj_priv->fenceable,
-                            mappable, obj_priv->mappable);
+                            map_and_fenceable,
+                            obj_priv->map_and_fenceable);
                        ret = i915_gem_object_unbind(obj);
                        if (ret)
                                return ret;
@@ -4172,18 +4220,18 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment,
 
        if (obj_priv->gtt_space == NULL) {
                ret = i915_gem_object_bind_to_gtt(obj, alignment,
-                                                 mappable, need_fence);
+                                                 map_and_fenceable);
                if (ret)
                        return ret;
        }
 
        if (obj_priv->pin_count++ == 0) {
-               i915_gem_info_add_pin(dev_priv, obj_priv, mappable);
+               i915_gem_info_add_pin(dev_priv, obj_priv, map_and_fenceable);
                if (!obj_priv->active)
                        list_move_tail(&obj_priv->mm_list,
                                       &dev_priv->mm.pinned_list);
        }
-       BUG_ON(!obj_priv->pin_mappable && mappable);
+       BUG_ON(!obj_priv->pin_mappable && map_and_fenceable);
 
        WARN_ON(i915_verify_lists(dev));
        return 0;
@@ -4245,8 +4293,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
        obj_priv->user_pin_count++;
        obj_priv->pin_filp = file_priv;
        if (obj_priv->user_pin_count == 1) {
-               ret = i915_gem_object_pin(obj, args->alignment,
-                                         true, obj_priv->tiling_mode);
+               ret = i915_gem_object_pin(obj, args->alignment, true);
                if (ret)
                        goto out;
        }
@@ -4439,8 +4486,8 @@ struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev,
        INIT_LIST_HEAD(&obj->ring_list);
        INIT_LIST_HEAD(&obj->gpu_write_list);
        obj->madv = I915_MADV_WILLNEED;
-       obj->fenceable = true;
-       obj->mappable = true;
+       /* Avoid an unnecessary call to unbind on the first bind. */
+       obj->map_and_fenceable = true;
 
        return &obj->base;
 }
@@ -4560,7 +4607,7 @@ i915_gem_init_pipe_control(struct drm_device *dev)
        obj_priv = to_intel_bo(obj);
        obj_priv->agp_type = AGP_USER_CACHED_MEMORY;
 
-       ret = i915_gem_object_pin(obj, 4096, true, false);
+       ret = i915_gem_object_pin(obj, 4096, true);
        if (ret)
                goto err_unref;
 
@@ -4973,17 +5020,24 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
                     struct drm_file *file_priv)
 {
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
-       void *obj_addr;
-       int ret;
-       char __user *user_data;
+       void *vaddr = obj_priv->phys_obj->handle->vaddr + args->offset;
+       char __user *user_data = (char __user *) (uintptr_t) args->data_ptr;
 
-       user_data = (char __user *) (uintptr_t) args->data_ptr;
-       obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+       DRM_DEBUG_DRIVER("vaddr %p, %lld\n", vaddr, args->size);
 
-       DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
-       ret = copy_from_user(obj_addr, user_data, args->size);
-       if (ret)
-               return -EFAULT;
+       if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) {
+               unsigned long unwritten;
+
+               /* The physical object once assigned is fixed for the lifetime
+                * of the obj, so we can safely drop the lock and continue
+                * to access vaddr.
+                */
+               mutex_unlock(&dev->struct_mutex);
+               unwritten = copy_from_user(vaddr, user_data, args->size);
+               mutex_lock(&dev->struct_mutex);
+               if (unwritten)
+                       return -EFAULT;
+       }
 
        drm_agp_chipset_flush(dev);
        return 0;