drm: dumb scanout create/mmap for intel/radeon (v3)
Dave Airlie [Mon, 7 Feb 2011 02:16:14 +0000 (12:16 +1000)]
This is just an idea that might or might not be a good idea,
it basically adds two ioctls to create a dumb and map a dumb buffer
suitable for scanout. The handle can be passed to the KMS ioctls to create
a framebuffer.

It looks to me like it would be useful in the following cases:
a) in development drivers - we can always provide a shadowfb fallback.
b) libkms users - we can clean up libkms a lot and avoid linking
to libdrm_*.
c) plymouth via libkms is a lot easier.

Userspace bits would be just calls + mmaps. We could probably
mark these handles somehow as not being suitable for acceleartion
so as top stop people who are dumber than dumb.

Signed-off-by: Dave Airlie <airlied@redhat.com>

15 files changed:
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_gem.c
drivers/gpu/drm/radeon/radeon_mode.h
include/drm/drm.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_mode.h

index 654faa8..4c95b5f 100644 (file)
@@ -2694,3 +2694,36 @@ void drm_mode_config_reset(struct drm_device *dev)
                        connector->funcs->reset(connector);
 }
 EXPORT_SYMBOL(drm_mode_config_reset);
+
+int drm_mode_create_dumb_ioctl(struct drm_device *dev,
+                              void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_create_dumb *args = data;
+
+       if (!dev->driver->dumb_create)
+               return -ENOSYS;
+       return dev->driver->dumb_create(file_priv, dev, args);
+}
+
+int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_map_dumb *args = data;
+
+       /* call driver ioctl to get mmap offset */
+       if (!dev->driver->dumb_map_offset)
+               return -ENOSYS;
+
+       return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset);
+}
+
+int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
+                               void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_destroy_dumb *args = data;
+
+       if (!dev->driver->dumb_destroy)
+               return -ENOSYS;
+
+       return dev->driver->dumb_destroy(file_priv, dev, args->handle);
+}
index 271835a..3e99198 100644 (file)
@@ -150,7 +150,10 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
index ea1c4b0..aa8df25 100644 (file)
@@ -181,7 +181,7 @@ EXPORT_SYMBOL(drm_gem_object_alloc);
 /**
  * Removes the mapping from handle to filp for this object.
  */
-static int
+int
 drm_gem_handle_delete(struct drm_file *filp, u32 handle)
 {
        struct drm_device *dev;
@@ -214,6 +214,7 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle)
 
        return 0;
 }
+EXPORT_SYMBOL(drm_gem_handle_delete);
 
 /**
  * Create a handle for this object. This adds a handle reference
index cfb56d0..17fde2f 100644 (file)
@@ -700,6 +700,9 @@ static struct drm_driver driver = {
        .gem_init_object = i915_gem_init_object,
        .gem_free_object = i915_gem_free_object,
        .gem_vm_ops = &i915_gem_vm_ops,
+       .dumb_create = i915_gem_dumb_create,
+       .dumb_map_offset = i915_gem_mmap_gtt,
+       .dumb_destroy = i915_gem_dumb_destroy,
        .ioctls = i915_ioctls,
        .fops = {
                 .owner = THIS_MODULE,
index a0149c6..a78197d 100644 (file)
@@ -1114,6 +1114,13 @@ void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
                                    struct intel_ring_buffer *ring,
                                    u32 seqno);
 
+int i915_gem_dumb_create(struct drm_file *file_priv,
+                        struct drm_device *dev,
+                        struct drm_mode_create_dumb *args);
+int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev,
+                     uint32_t handle, uint64_t *offset);
+int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev,
+                         uint32_t handle);                       
 /**
  * Returns true if seq1 is later than seq2.
  */
index cf4f74c..bc7f06b 100644 (file)
@@ -193,22 +193,20 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
-/**
- * Creates a new mm object and returns a handle to it.
- */
-int
-i915_gem_create_ioctl(struct drm_device *dev, void *data,
-                     struct drm_file *file)
+static int
+i915_gem_create(struct drm_file *file,
+               struct drm_device *dev,
+               uint64_t size,
+               uint32_t *handle_p)
 {
-       struct drm_i915_gem_create *args = data;
        struct drm_i915_gem_object *obj;
        int ret;
        u32 handle;
 
-       args->size = roundup(args->size, PAGE_SIZE);
+       size = roundup(size, PAGE_SIZE);
 
        /* Allocate the new object */
-       obj = i915_gem_alloc_object(dev, args->size);
+       obj = i915_gem_alloc_object(dev, size);
        if (obj == NULL)
                return -ENOMEM;
 
@@ -224,10 +222,41 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
        drm_gem_object_unreference(&obj->base);
        trace_i915_gem_object_create(obj);
 
-       args->handle = handle;
+       *handle_p = handle;
        return 0;
 }
 
+int
+i915_gem_dumb_create(struct drm_file *file,
+                    struct drm_device *dev,
+                    struct drm_mode_create_dumb *args)
+{
+       /* have to work out size/pitch and return them */
+       args->pitch = ALIGN(args->width & ((args->bpp + 1) / 8), 64);
+       args->size = args->pitch * args->height;
+       return i915_gem_create(file, dev,
+                              args->size, &args->handle);
+}
+
+int i915_gem_dumb_destroy(struct drm_file *file,
+                         struct drm_device *dev,
+                         uint32_t handle)
+{
+       return drm_gem_handle_delete(file, handle);
+}
+
+/**
+ * Creates a new mm object and returns a handle to it.
+ */
+int
+i915_gem_create_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file)
+{
+       struct drm_i915_gem_create *args = data;
+       return i915_gem_create(file, dev,
+                              args->size, &args->handle);
+}
+
 static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
 {
        drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
@@ -1425,27 +1454,13 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj)
        return tile_height * obj->stride * 2;
 }
 
-/**
- * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing
- * @dev: DRM device
- * @data: GTT mapping ioctl data
- * @file: GEM object info
- *
- * Simply returns the fake offset to userspace so it can mmap it.
- * The mmap call will end up in drm_gem_mmap(), which will set things
- * up so we can get faults in the handler above.
- *
- * The fault handler will take care of binding the object into the GTT
- * (since it may have been evicted to make room for something), allocating
- * a fence register, and mapping the appropriate aperture address into
- * userspace.
- */
 int
-i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
-                       struct drm_file *file)
+i915_gem_mmap_gtt(struct drm_file *file,
+                 struct drm_device *dev,
+                 uint32_t handle,
+                 uint64_t *offset)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_i915_gem_mmap_gtt *args = data;
        struct drm_i915_gem_object *obj;
        int ret;
 
@@ -1456,7 +1471,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
        if (ret)
                return ret;
 
-       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
+       obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle));
        if (obj == NULL) {
                ret = -ENOENT;
                goto unlock;
@@ -1479,7 +1494,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
                        goto out;
        }
 
-       args->offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
+       *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
 
 out:
        drm_gem_object_unreference(&obj->base);
@@ -1488,6 +1503,34 @@ unlock:
        return ret;
 }
 
+/**
+ * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing
+ * @dev: DRM device
+ * @data: GTT mapping ioctl data
+ * @file: GEM object info
+ *
+ * Simply returns the fake offset to userspace so it can mmap it.
+ * The mmap call will end up in drm_gem_mmap(), which will set things
+ * up so we can get faults in the handler above.
+ *
+ * The fault handler will take care of binding the object into the GTT
+ * (since it may have been evicted to make room for something), allocating
+ * a fence register, and mapping the appropriate aperture address into
+ * userspace.
+ */
+int
+i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file)
+{
+       struct drm_i915_gem_mmap_gtt *args = data;
+
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
+       return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
+}
+
+
 static int
 i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
                              gfp_t gfpmask)
index 56c48b6..a460536 100644 (file)
@@ -288,6 +288,15 @@ int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
                          uint64_t *gpu_addr);
 void radeon_gem_object_unpin(struct drm_gem_object *obj);
 
+int radeon_mode_dumb_create(struct drm_file *file_priv,
+                           struct drm_device *dev,
+                           struct drm_mode_create_dumb *args);
+int radeon_mode_dumb_mmap(struct drm_file *filp,
+                         struct drm_device *dev,
+                         uint32_t handle, uint64_t *offset_p);
+int radeon_mode_dumb_destroy(struct drm_file *file_priv,
+                            struct drm_device *dev,
+                            uint32_t handle);
 
 /*
  * GART structures, functions & helpers
index 275b26a..ca1b7d4 100644 (file)
@@ -84,6 +84,16 @@ extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
 extern struct drm_ioctl_desc radeon_ioctls_kms[];
 extern int radeon_max_kms_ioctl;
 int radeon_mmap(struct file *filp, struct vm_area_struct *vma);
+int radeon_mode_dumb_mmap(struct drm_file *filp,
+                         struct drm_device *dev,
+                         uint32_t handle, uint64_t *offset_p);
+int radeon_mode_dumb_create(struct drm_file *file_priv,
+                           struct drm_device *dev,
+                           struct drm_mode_create_dumb *args);
+int radeon_mode_dumb_destroy(struct drm_file *file_priv,
+                            struct drm_device *dev,
+                            uint32_t handle);
+
 #if defined(CONFIG_DEBUG_FS)
 int radeon_debugfs_init(struct drm_minor *minor);
 void radeon_debugfs_cleanup(struct drm_minor *minor);
@@ -322,6 +332,9 @@ static struct drm_driver kms_driver = {
        .gem_init_object = radeon_gem_object_init,
        .gem_free_object = radeon_gem_object_free,
        .dma_ioctl = radeon_dma_ioctl_kms,
+       .dumb_create = radeon_mode_dumb_create,
+       .dumb_map_offset = radeon_mode_dumb_mmap,
+       .dumb_destroy = radeon_mode_dumb_destroy,
        .fops = {
                 .owner = THIS_MODULE,
                 .open = drm_open,
index 66324b5..cb968f9 100644 (file)
@@ -64,7 +64,7 @@ static struct fb_ops radeonfb_ops = {
 };
 
 
-static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
+int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
 {
        int aligned = width;
        int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
index df95eb8..ede5dcc 100644 (file)
@@ -236,23 +236,31 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        return r;
 }
 
-int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
-                         struct drm_file *filp)
+int radeon_mode_dumb_mmap(struct drm_file *filp,
+                         struct drm_device *dev,
+                         uint32_t handle, uint64_t *offset_p)
 {
-       struct drm_radeon_gem_mmap *args = data;
        struct drm_gem_object *gobj;
        struct radeon_bo *robj;
 
-       gobj = drm_gem_object_lookup(dev, filp, args->handle);
+       gobj = drm_gem_object_lookup(dev, filp, handle);
        if (gobj == NULL) {
                return -ENOENT;
        }
        robj = gobj->driver_private;
-       args->addr_ptr = radeon_bo_mmap_offset(robj);
+       *offset_p = radeon_bo_mmap_offset(robj);
        drm_gem_object_unreference_unlocked(gobj);
        return 0;
 }
 
+int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
+                         struct drm_file *filp)
+{
+       struct drm_radeon_gem_mmap *args = data;
+
+       return radeon_mode_dumb_mmap(filp, dev, args->handle, &args->addr_ptr);
+}
+
 int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
                          struct drm_file *filp)
 {
@@ -345,3 +353,38 @@ out:
        drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
+
+int radeon_mode_dumb_create(struct drm_file *file_priv,
+                           struct drm_device *dev,
+                           struct drm_mode_create_dumb *args)
+{
+       struct radeon_device *rdev = dev->dev_private;
+       struct drm_gem_object *gobj;
+       int r;
+
+       args->pitch = radeon_align_pitch(rdev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8);
+       args->size = args->pitch * args->height;
+       args->size = ALIGN(args->size, PAGE_SIZE);
+
+       r = radeon_gem_object_create(rdev, args->size, 0,
+                                    RADEON_GEM_DOMAIN_VRAM,
+                                    false, ttm_bo_type_device,
+                                    &gobj);
+       if (r)
+               return -ENOMEM;
+
+       r = drm_gem_handle_create(file_priv, gobj, &args->handle);
+       if (r) {
+               drm_gem_object_unreference_unlocked(gobj);
+               return r;
+       }
+       drm_gem_object_handle_unreference_unlocked(gobj);
+       return 0;
+}
+
+int radeon_mode_dumb_destroy(struct drm_file *file_priv,
+                            struct drm_device *dev,
+                            uint32_t handle)
+{
+       return drm_gem_handle_delete(file_priv, handle);
+}
index 6794cdf..c3f23f6 100644 (file)
@@ -675,4 +675,5 @@ void radeon_fb_output_poll_changed(struct radeon_device *rdev);
 
 void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
 
+int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled);
 #endif
index e5f7061..8598cc9 100644 (file)
@@ -701,6 +701,10 @@ struct drm_gem_open {
 #define DRM_IOCTL_MODE_PAGE_FLIP       DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip)
 #define DRM_IOCTL_MODE_DIRTYFB         DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd)
 
+#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb)
+#define DRM_IOCTL_MODE_MAP_DUMB    DRM_IOWR(0xB3, struct drm_mode_map_dumb)
+#define DRM_IOCTL_MODE_DESTROY_DUMB    DRM_IOWR(0xB4, struct drm_mode_destroy_dumb)
+
 /**
  * Device specific ioctls should only be in their respective headers
  * The device specific ioctl range is from 0x40 to 0x99.
index fe29aad..3cbe7a0 100644 (file)
@@ -880,6 +880,17 @@ struct drm_driver {
        /* vga arb irq handler */
        void (*vgaarb_irq)(struct drm_device *dev, bool state);
 
+       /* dumb alloc support */
+       int (*dumb_create)(struct drm_file *file_priv,
+                          struct drm_device *dev,
+                          struct drm_mode_create_dumb *args);
+       int (*dumb_map_offset)(struct drm_file *file_priv,
+                              struct drm_device *dev, uint32_t handle,
+                              uint64_t *offset);
+       int (*dumb_destroy)(struct drm_file *file_priv,
+                           struct drm_device *dev,
+                           uint32_t handle);
+
        /* Driver private ops for this object */
        struct vm_operations_struct *gem_vm_ops;
 
@@ -1544,6 +1555,7 @@ drm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
 int drm_gem_handle_create(struct drm_file *file_priv,
                          struct drm_gem_object *obj,
                          u32 *handlep);
+int drm_gem_handle_delete(struct drm_file *filp, u32 handle);
 
 static inline void
 drm_gem_object_handle_reference(struct drm_gem_object *obj)
index 801be59..080a6e3 100644 (file)
@@ -798,4 +798,11 @@ extern int drm_add_modes_noedid(struct drm_connector *connector,
 extern bool drm_edid_is_valid(struct edid *edid);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh);
+
+extern int drm_mode_create_dumb_ioctl(struct drm_device *dev,
+                                     void *data, struct drm_file *file_priv);
+extern int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
+                                   void *data, struct drm_file *file_priv);
+extern int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
+                                     void *data, struct drm_file *file_priv);
 #endif /* __DRM_CRTC_H__ */
index 0fc7397..ae6b7a3 100644 (file)
@@ -344,4 +344,33 @@ struct drm_mode_crtc_page_flip {
        __u64 user_data;
 };
 
+/* create a dumb scanout buffer */
+struct drm_mode_create_dumb {
+       uint32_t height;
+       uint32_t width;
+       uint32_t bpp;
+       uint32_t flags;
+       /* handle, pitch, size will be returned */
+       uint32_t handle;
+       uint32_t pitch;
+       uint64_t size;
+};
+
+/* set up for mmap of a dumb scanout buffer */
+struct drm_mode_map_dumb {
+       /** Handle for the object being mapped. */
+       __u32 handle;
+       __u32 pad;
+       /**
+        * Fake offset to use for subsequent mmap call
+        *
+        * This is a fixed-size type for 32/64 compatibility.
+        */
+       __u64 offset;
+};
+
+struct drm_mode_destroy_dumb {
+       uint32_t handle;
+};
+
 #endif