]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/gpu/drm/i915/i915_gem.c
drm/i915: add support for physical memory objects
[linux-2.6-omap-h63xx.git] / drivers / gpu / drm / i915 / i915_gem.c
index 1384d6686555b4418d5e3be9a1dbf3fc41f4438f..96316fd4723322f7ac277e3747abdc806ab3a98e 100644 (file)
@@ -55,6 +55,9 @@ static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
 static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
 static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
 static int i915_gem_evict_something(struct drm_device *dev);
+static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+                               struct drm_i915_gem_pwrite *args,
+                               struct drm_file *file_priv);
 
 int i915_gem_do_init(struct drm_device *dev, unsigned long start,
                     unsigned long end)
@@ -386,8 +389,10 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
         * pread/pwrite currently are reading and writing from the CPU
         * perspective, requiring manual detiling by the client.
         */
-       if (obj_priv->tiling_mode == I915_TILING_NONE &&
-           dev->gtt_total != 0)
+       if (obj_priv->phys_obj)
+               ret = i915_gem_phys_pwrite(dev, obj, args, file_priv);
+       else if (obj_priv->tiling_mode == I915_TILING_NONE &&
+                dev->gtt_total != 0)
                ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv);
        else
                ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv);
@@ -2858,6 +2863,9 @@ void i915_gem_free_object(struct drm_gem_object *obj)
        while (obj_priv->pin_count > 0)
                i915_gem_object_unpin(obj);
 
+       if (obj_priv->phys_obj)
+               i915_gem_detach_phys_object(dev, obj);
+
        i915_gem_object_unbind(obj);
 
        list = &obj->map_list;
@@ -3293,3 +3301,180 @@ i915_gem_load(struct drm_device *dev)
 
        i915_gem_detect_bit_6_swizzle(dev);
 }
+
+/*
+ * Create a physically contiguous memory object for this object
+ * e.g. for cursor + overlay regs
+ */
+int i915_gem_init_phys_object(struct drm_device *dev,
+                             int id, int size)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_phys_object *phys_obj;
+       int ret;
+
+       if (dev_priv->mm.phys_objs[id - 1] || !size)
+               return 0;
+
+       phys_obj = drm_calloc(1, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+       if (!phys_obj)
+               return -ENOMEM;
+
+       phys_obj->id = id;
+
+       phys_obj->handle = drm_pci_alloc(dev, size, 0, 0xffffffff);
+       if (!phys_obj->handle) {
+               ret = -ENOMEM;
+               goto kfree_obj;
+       }
+#ifdef CONFIG_X86
+       set_memory_wc((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+
+       dev_priv->mm.phys_objs[id - 1] = phys_obj;
+
+       return 0;
+kfree_obj:
+       drm_free(phys_obj, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+       return ret;
+}
+
+void i915_gem_free_phys_object(struct drm_device *dev, int id)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_phys_object *phys_obj;
+
+       if (!dev_priv->mm.phys_objs[id - 1])
+               return;
+
+       phys_obj = dev_priv->mm.phys_objs[id - 1];
+       if (phys_obj->cur_obj) {
+               i915_gem_detach_phys_object(dev, phys_obj->cur_obj);
+       }
+
+#ifdef CONFIG_X86
+       set_memory_wb((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+       drm_pci_free(dev, phys_obj->handle);
+       kfree(phys_obj);
+       dev_priv->mm.phys_objs[id - 1] = NULL;
+}
+
+void i915_gem_free_all_phys_object(struct drm_device *dev)
+{
+       int i;
+
+       for (i = 0; i < I915_MAX_PHYS_OBJECT; i++)
+               i915_gem_free_phys_object(dev, i);
+}
+
+void i915_gem_detach_phys_object(struct drm_device *dev,
+                                struct drm_gem_object *obj)
+{
+       struct drm_i915_gem_object *obj_priv;
+       int i;
+       int ret;
+       int page_count;
+
+       obj_priv = obj->driver_private;
+       if (!obj_priv->phys_obj)
+               return;
+
+       ret = i915_gem_object_get_page_list(obj);
+       if (ret)
+               goto out;
+
+       page_count = obj->size / PAGE_SIZE;
+
+       for (i = 0; i < page_count; i++) {
+               char *dst = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+               char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+               memcpy(dst, src, PAGE_SIZE);
+               kunmap_atomic(dst, KM_USER0);
+       }
+       drm_clflush_pages(obj_priv->page_list, page_count);
+       drm_agp_chipset_flush(dev);
+out:
+       obj_priv->phys_obj->cur_obj = NULL;
+       obj_priv->phys_obj = NULL;
+}
+
+int
+i915_gem_attach_phys_object(struct drm_device *dev,
+                           struct drm_gem_object *obj, int id)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv;
+       int ret = 0;
+       int page_count;
+       int i;
+
+       if (id > I915_MAX_PHYS_OBJECT)
+               return -EINVAL;
+
+       obj_priv = obj->driver_private;
+
+       if (obj_priv->phys_obj) {
+               if (obj_priv->phys_obj->id == id)
+                       return 0;
+               i915_gem_detach_phys_object(dev, obj);
+       }
+
+
+       /* create a new object */
+       if (!dev_priv->mm.phys_objs[id - 1]) {
+               ret = i915_gem_init_phys_object(dev, id,
+                                               obj->size);
+               if (ret) {
+                       DRM_ERROR("failed to init phys object %d size: %d\n", id, obj->size);
+                       goto out;
+               }
+       }
+
+       /* bind to the object */
+       obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1];
+       obj_priv->phys_obj->cur_obj = obj;
+
+       ret = i915_gem_object_get_page_list(obj);
+       if (ret) {
+               DRM_ERROR("failed to get page list\n");
+               goto out;
+       }
+
+       page_count = obj->size / PAGE_SIZE;
+
+       for (i = 0; i < page_count; i++) {
+               char *src = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+               char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+               memcpy(dst, src, PAGE_SIZE);
+               kunmap_atomic(src, KM_USER0);
+       }
+
+       return 0;
+out:
+       return ret;
+}
+
+static int
+i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+                    struct drm_i915_gem_pwrite *args,
+                    struct drm_file *file_priv)
+{
+       struct drm_i915_gem_object *obj_priv = obj->driver_private;
+       void *obj_addr;
+       int ret;
+       char __user *user_data;
+
+       user_data = (char __user *) (uintptr_t) args->data_ptr;
+       obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+
+       DRM_ERROR("obj_addr %p, %lld\n", obj_addr, args->size);
+       ret = copy_from_user(obj_addr, user_data, args->size);
+       if (ret)
+               return -EFAULT;
+
+       drm_agp_chipset_flush(dev);
+       return 0;
+}