drm: Add dirty ioctl and property
Jakob Bornecrantz [Thu, 3 Dec 2009 23:25:47 +0000 (23:25 +0000)]
This commit adds a ioctl and property to allow userspace
to notify the kernel that a framebuffer has changed. Instead
of snooping the command stream this allows finer grained
tracking of which areas have changed.

The primary user for this functionality is virtual hardware
like the vmware svga device, but also Xen hardware likes to
be notify. There is also real hardware like DisplayLink and
DisplayPort that might take advantage of this ioctl.

Signed-off-by: Jakob Bornecrantz <jakob@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>

drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_drv.c
include/drm/drm.h
include/drm/drm_crtc.h
include/drm/drm_mode.h

index 32756e6..4fe321d 100644 (file)
@@ -125,6 +125,15 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
                 drm_tv_subconnector_enum_list)
 
+static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
+       { DRM_MODE_DIRTY_OFF,      "Off"      },
+       { DRM_MODE_DIRTY_ON,       "On"       },
+       { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
+};
+
+DRM_ENUM_NAME_FN(drm_get_dirty_info_name,
+                drm_dirty_info_enum_list)
+
 struct drm_conn_prop_enum_list {
        int type;
        char *name;
@@ -802,6 +811,36 @@ int drm_mode_create_dithering_property(struct drm_device *dev)
 EXPORT_SYMBOL(drm_mode_create_dithering_property);
 
 /**
+ * drm_mode_create_dirty_property - create dirty property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_dirty_info_property(struct drm_device *dev)
+{
+       struct drm_property *dirty_info;
+       int i;
+
+       if (dev->mode_config.dirty_info_property)
+               return 0;
+
+       dirty_info =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "dirty",
+                                   ARRAY_SIZE(drm_dirty_info_enum_list));
+       for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
+               drm_property_add_enum(dirty_info, i,
+                                     drm_dirty_info_enum_list[i].type,
+                                     drm_dirty_info_enum_list[i].name);
+       dev->mode_config.dirty_info_property = dirty_info;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
+
+/**
  * drm_mode_config_init - initialize DRM mode_configuration structure
  * @dev: DRM device
  *
@@ -1753,6 +1792,71 @@ out:
        return ret;
 }
 
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_clip_rect __user *clips_ptr;
+       struct drm_clip_rect *clips = NULL;
+       struct drm_mode_fb_dirty_cmd *r = data;
+       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb;
+       unsigned flags;
+       int num_clips;
+       int ret = 0;
+
+       mutex_lock(&dev->mode_config.mutex);
+       obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               DRM_ERROR("invalid framebuffer id\n");
+               ret = -EINVAL;
+               goto out_err1;
+       }
+       fb = obj_to_fb(obj);
+
+       num_clips = r->num_clips;
+       clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr;
+
+       if (!num_clips != !clips_ptr) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
+
+       /* If userspace annotates copy, clips must come in pairs */
+       if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       if (num_clips && clips_ptr) {
+               clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+               if (!clips) {
+                       ret = -ENOMEM;
+                       goto out_err1;
+               }
+
+               ret = copy_from_user(clips, clips_ptr,
+                                    num_clips * sizeof(*clips));
+               if (ret)
+                       goto out_err2;
+       }
+
+       if (fb->funcs->dirty) {
+               ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips);
+       } else {
+               ret = -ENOSYS;
+               goto out_err2;
+       }
+
+out_err2:
+       kfree(clips);
+out_err1:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+
 /**
  * drm_fb_release - remove and free the FBs on this file
  * @filp: file * from the ioctl
index bfaf59b..ff2f104 100644 (file)
@@ -146,6 +146,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW)
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
index 0114ac9..43a35b0 100644 (file)
@@ -697,6 +697,7 @@ struct drm_gem_open {
 #define DRM_IOCTL_MODE_ADDFB           DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_RMFB            DRM_IOWR(0xAF, unsigned int)
 #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)
 
 /**
  * Device specific ioctls should only be in their respective headers
index 938f327..219f075 100644 (file)
@@ -242,6 +242,21 @@ struct drm_framebuffer_funcs {
        int (*create_handle)(struct drm_framebuffer *fb,
                             struct drm_file *file_priv,
                             unsigned int *handle);
+       /**
+        * Optinal callback for the dirty fb ioctl.
+        *
+        * Userspace can notify the driver via this callback
+        * that a area of the framebuffer has changed and should
+        * be flushed to the display hardware.
+        *
+        * See documentation in drm_mode.h for the struct
+        * drm_mode_fb_dirty_cmd for more information as all
+        * the semantics and arguments have a one to one mapping
+        * on this function.
+        */
+       int (*dirty)(struct drm_framebuffer *framebuffer, unsigned flags,
+                    unsigned color, struct drm_clip_rect *clips,
+                    unsigned num_clips);
 };
 
 struct drm_framebuffer {
@@ -610,6 +625,7 @@ struct drm_mode_config {
        /* Optional properties */
        struct drm_property *scaling_mode_property;
        struct drm_property *dithering_mode_property;
+       struct drm_property *dirty_info_property;
 };
 
 #define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
@@ -718,6 +734,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
                                     char *formats[]);
 extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_dithering_property(struct drm_device *dev);
+extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
 extern char *drm_get_encoder_name(struct drm_encoder *encoder);
 
 extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
@@ -745,6 +762,8 @@ extern int drm_mode_rmfb(struct drm_device *dev,
                         void *data, struct drm_file *file_priv);
 extern int drm_mode_getfb(struct drm_device *dev,
                          void *data, struct drm_file *file_priv);
+extern int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                                 void *data, struct drm_file *file_priv);
 extern int drm_mode_addmode_ioctl(struct drm_device *dev,
                                  void *data, struct drm_file *file_priv);
 extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
index 09ca6ad..43009bc 100644 (file)
 #define DRM_MODE_DITHERING_OFF 0
 #define DRM_MODE_DITHERING_ON  1
 
+/* Dirty info options */
+#define DRM_MODE_DIRTY_OFF      0
+#define DRM_MODE_DIRTY_ON       1
+#define DRM_MODE_DIRTY_ANNOTATE 2
+
 struct drm_mode_modeinfo {
        __u32 clock;
        __u16 hdisplay, hsync_start, hsync_end, htotal, hskew;
@@ -222,6 +227,45 @@ struct drm_mode_fb_cmd {
        __u32 handle;
 };
 
+#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01
+#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02
+#define DRM_MODE_FB_DIRTY_FLAGS         0x03
+
+/*
+ * Mark a region of a framebuffer as dirty.
+ *
+ * Some hardware does not automatically update display contents
+ * as a hardware or software draw to a framebuffer. This ioctl
+ * allows userspace to tell the kernel and the hardware what
+ * regions of the framebuffer have changed.
+ *
+ * The kernel or hardware is free to update more then just the
+ * region specified by the clip rects. The kernel or hardware
+ * may also delay and/or coalesce several calls to dirty into a
+ * single update.
+ *
+ * Userspace may annotate the updates, the annotates are a
+ * promise made by the caller that the change is either a copy
+ * of pixels or a fill of a single color in the region specified.
+ *
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then
+ * the number of updated regions are half of num_clips given,
+ * where the clip rects are paired in src and dst. The width and
+ * height of each one of the pairs must match.
+ *
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller
+ * promises that the region specified of the clip rects is filled
+ * completely with a single color as given in the color argument.
+ */
+
+struct drm_mode_fb_dirty_cmd {
+       __u32 fb_id;
+       __u32 flags;
+       __u32 color;
+       __u32 num_clips;
+       __u64 clips_ptr;
+};
+
 struct drm_mode_mode_cmd {
        __u32 connector_id;
        struct drm_mode_modeinfo mode;