Merge tag 'v3.2-rc6' of /home/airlied/devel/kernel/linux-2.6 into drm-core-next
[pandora-kernel.git] / drivers / gpu / drm / drm_crtc.c
index 8323fc3..f259a25 100644 (file)
@@ -36,6 +36,7 @@
 #include "drmP.h"
 #include "drm_crtc.h"
 #include "drm_edid.h"
+#include "drm_fourcc.h"
 
 struct drm_prop_enum_list {
        int type;
@@ -324,6 +325,7 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
 {
        struct drm_device *dev = fb->dev;
        struct drm_crtc *crtc;
+       struct drm_plane *plane;
        struct drm_mode_set set;
        int ret;
 
@@ -340,6 +342,15 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
                }
        }
 
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               if (plane->fb == fb) {
+                       /* should turn off the crtc */
+                       ret = plane->funcs->disable_plane(plane);
+                       if (ret)
+                               DRM_ERROR("failed to disable plane with busy fb\n");
+               }
+       }
+
        drm_mode_object_put(dev, &fb->base);
        list_del(&fb->head);
        dev->mode_config.num_fb--;
@@ -540,6 +551,51 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 }
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
+int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                  unsigned long possible_crtcs,
+                  const struct drm_plane_funcs *funcs,
+                  const uint32_t *formats, uint32_t format_count)
+{
+       mutex_lock(&dev->mode_config.mutex);
+
+       plane->dev = dev;
+       drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+       plane->funcs = funcs;
+       plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
+                                     GFP_KERNEL);
+       if (!plane->format_types) {
+               DRM_DEBUG_KMS("out of memory when allocating plane\n");
+               drm_mode_object_put(dev, &plane->base);
+               mutex_unlock(&dev->mode_config.mutex);
+               return -ENOMEM;
+       }
+
+       memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
+       plane->format_count = format_count;
+       plane->possible_crtcs = possible_crtcs;
+
+       list_add_tail(&plane->head, &dev->mode_config.plane_list);
+       dev->mode_config.num_plane++;
+
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_plane_init);
+
+void drm_plane_cleanup(struct drm_plane *plane)
+{
+       struct drm_device *dev = plane->dev;
+
+       mutex_lock(&dev->mode_config.mutex);
+       kfree(plane->format_types);
+       drm_mode_object_put(dev, &plane->base);
+       list_del(&plane->head);
+       dev->mode_config.num_plane--;
+       mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_plane_cleanup);
+
 /**
  * drm_mode_create - create a new display mode
  * @dev: DRM device
@@ -871,6 +927,7 @@ void drm_mode_config_init(struct drm_device *dev)
        INIT_LIST_HEAD(&dev->mode_config.encoder_list);
        INIT_LIST_HEAD(&dev->mode_config.property_list);
        INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
+       INIT_LIST_HEAD(&dev->mode_config.plane_list);
        idr_init(&dev->mode_config.crtc_idr);
 
        mutex_lock(&dev->mode_config.mutex);
@@ -947,6 +1004,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        struct drm_encoder *encoder, *enct;
        struct drm_framebuffer *fb, *fbt;
        struct drm_property *property, *pt;
+       struct drm_plane *plane, *plt;
 
        list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
                                 head) {
@@ -971,6 +1029,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                crtc->funcs->destroy(crtc);
        }
 
+       list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
+                                head) {
+               plane->funcs->destroy(plane);
+       }
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
 
@@ -1379,7 +1441,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
         */
        if ((out_resp->count_modes >= mode_count) && mode_count) {
                copied = 0;
-               mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
+               mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
                list_for_each_entry(mode, &connector->modes, head) {
                        drm_crtc_convert_to_umode(&u_mode, mode);
                        if (copy_to_user(mode_ptr + copied,
@@ -1394,8 +1456,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
        if ((out_resp->count_props >= props_count) && props_count) {
                copied = 0;
-               prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr);
-               prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr);
+               prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr);
+               prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr);
                for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
                        if (connector->property_ids[i] != 0) {
                                if (put_user(connector->property_ids[i],
@@ -1417,7 +1479,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
        if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
                copied = 0;
-               encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr);
+               encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
                for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
                        if (connector->encoder_ids[i] != 0) {
                                if (put_user(connector->encoder_ids[i],
@@ -1470,6 +1532,245 @@ out:
        return ret;
 }
 
+/**
+ * drm_mode_getplane_res - get plane info
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Return an plane count and set of IDs.
+ */
+int drm_mode_getplane_res(struct drm_device *dev, void *data,
+                           struct drm_file *file_priv)
+{
+       struct drm_mode_get_plane_res *plane_resp = data;
+       struct drm_mode_config *config;
+       struct drm_plane *plane;
+       uint32_t __user *plane_ptr;
+       int copied = 0, ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+       config = &dev->mode_config;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if (config->num_plane &&
+           (plane_resp->count_planes >= config->num_plane)) {
+               plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
+
+               list_for_each_entry(plane, &config->plane_list, head) {
+                       if (put_user(plane->base.id, plane_ptr + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
+       }
+       plane_resp->count_planes = config->num_plane;
+
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+/**
+ * drm_mode_getplane - get plane info
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Return plane info, including formats supported, gamma size, any
+ * current fb, etc.
+ */
+int drm_mode_getplane(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       struct drm_mode_get_plane *plane_resp = data;
+       struct drm_mode_object *obj;
+       struct drm_plane *plane;
+       uint32_t __user *format_ptr;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+       obj = drm_mode_object_find(dev, plane_resp->plane_id,
+                                  DRM_MODE_OBJECT_PLANE);
+       if (!obj) {
+               ret = -ENOENT;
+               goto out;
+       }
+       plane = obj_to_plane(obj);
+
+       if (plane->crtc)
+               plane_resp->crtc_id = plane->crtc->base.id;
+       else
+               plane_resp->crtc_id = 0;
+
+       if (plane->fb)
+               plane_resp->fb_id = plane->fb->base.id;
+       else
+               plane_resp->fb_id = 0;
+
+       plane_resp->plane_id = plane->base.id;
+       plane_resp->possible_crtcs = plane->possible_crtcs;
+       plane_resp->gamma_size = plane->gamma_size;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if (plane->format_count &&
+           (plane_resp->count_format_types >= plane->format_count)) {
+               format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
+               if (copy_to_user(format_ptr,
+                                plane->format_types,
+                                sizeof(uint32_t) * plane->format_count)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+       }
+       plane_resp->count_format_types = plane->format_count;
+
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+/**
+ * drm_mode_setplane - set up or tear down an plane
+ * @dev: DRM device
+ * @data: ioctl data*
+ * @file_prive: DRM file info
+ *
+ * Set plane info, including placement, fb, scaling, and other factors.
+ * Or pass a NULL fb to disable.
+ */
+int drm_mode_setplane(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       struct drm_mode_set_plane *plane_req = data;
+       struct drm_mode_object *obj;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *fb;
+       int ret = 0;
+       unsigned int fb_width, fb_height;
+       int i;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       /*
+        * First, find the plane, crtc, and fb objects.  If not available,
+        * we don't bother to call the driver.
+        */
+       obj = drm_mode_object_find(dev, plane_req->plane_id,
+                                  DRM_MODE_OBJECT_PLANE);
+       if (!obj) {
+               DRM_DEBUG_KMS("Unknown plane ID %d\n",
+                             plane_req->plane_id);
+               ret = -ENOENT;
+               goto out;
+       }
+       plane = obj_to_plane(obj);
+
+       /* No fb means shut it down */
+       if (!plane_req->fb_id) {
+               plane->funcs->disable_plane(plane);
+               plane->crtc = NULL;
+               plane->fb = NULL;
+               goto out;
+       }
+
+       obj = drm_mode_object_find(dev, plane_req->crtc_id,
+                                  DRM_MODE_OBJECT_CRTC);
+       if (!obj) {
+               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
+                             plane_req->crtc_id);
+               ret = -ENOENT;
+               goto out;
+       }
+       crtc = obj_to_crtc(obj);
+
+       obj = drm_mode_object_find(dev, plane_req->fb_id,
+                                  DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
+                             plane_req->fb_id);
+               ret = -ENOENT;
+               goto out;
+       }
+       fb = obj_to_fb(obj);
+
+       /* Check whether this plane supports the fb pixel format. */
+       for (i = 0; i < plane->format_count; i++)
+               if (fb->pixel_format == plane->format_types[i])
+                       break;
+       if (i == plane->format_count) {
+               DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       fb_width = fb->width << 16;
+       fb_height = fb->height << 16;
+
+       /* Make sure source coordinates are inside the fb. */
+       if (plane_req->src_w > fb_width ||
+           plane_req->src_x > fb_width - plane_req->src_w ||
+           plane_req->src_h > fb_height ||
+           plane_req->src_y > fb_height - plane_req->src_h) {
+               DRM_DEBUG_KMS("Invalid source coordinates "
+                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+                             plane_req->src_w >> 16,
+                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
+                             plane_req->src_h >> 16,
+                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
+                             plane_req->src_x >> 16,
+                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
+                             plane_req->src_y >> 16,
+                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       /* Give drivers some help against integer overflows */
+       if (plane_req->crtc_w > INT_MAX ||
+           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
+           plane_req->crtc_h > INT_MAX ||
+           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
+               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+                             plane_req->crtc_w, plane_req->crtc_h,
+                             plane_req->crtc_x, plane_req->crtc_y);
+               ret = -ERANGE;
+               goto out;
+       }
+
+       ret = plane->funcs->update_plane(plane, crtc, fb,
+                                        plane_req->crtc_x, plane_req->crtc_y,
+                                        plane_req->crtc_w, plane_req->crtc_h,
+                                        plane_req->src_x, plane_req->src_y,
+                                        plane_req->src_w, plane_req->src_h);
+       if (!ret) {
+               plane->crtc = crtc;
+               plane->fb = fb;
+       }
+
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
 /**
  * drm_mode_setcrtc - set CRTC configuration
  * @inode: inode from the ioctl
@@ -1576,7 +1877,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                }
 
                for (i = 0; i < crtc_req->count_connectors; i++) {
-                       set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr;
+                       set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
                        if (get_user(out_id, &set_connectors_ptr[i])) {
                                ret = -EFAULT;
                                goto out;
@@ -1625,10 +1926,8 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       if (!req->flags) {
-               DRM_ERROR("no operation set\n");
+       if (!req->flags)
                return -EINVAL;
-       }
 
        mutex_lock(&dev->mode_config.mutex);
        obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
@@ -1641,7 +1940,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
 
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set) {
-                       DRM_ERROR("crtc does not support cursor\n");
                        ret = -ENXIO;
                        goto out;
                }
@@ -1654,7 +1952,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
                if (crtc->funcs->cursor_move) {
                        ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
                } else {
-                       DRM_ERROR("crtc does not support cursor\n");
                        ret = -EFAULT;
                        goto out;
                }
@@ -1664,6 +1961,42 @@ out:
        return ret;
 }
 
+/* Original addfb only supported RGB formats, so figure out which one */
+uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
+{
+       uint32_t fmt;
+
+       switch (bpp) {
+       case 8:
+               fmt = DRM_FORMAT_RGB332;
+               break;
+       case 16:
+               if (depth == 15)
+                       fmt = DRM_FORMAT_XRGB1555;
+               else
+                       fmt = DRM_FORMAT_RGB565;
+               break;
+       case 24:
+               fmt = DRM_FORMAT_RGB888;
+               break;
+       case 32:
+               if (depth == 24)
+                       fmt = DRM_FORMAT_XRGB8888;
+               else if (depth == 30)
+                       fmt = DRM_FORMAT_XRGB2101010;
+               else
+                       fmt = DRM_FORMAT_ARGB8888;
+               break;
+       default:
+               DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
+               fmt = DRM_FORMAT_XRGB8888;
+               break;
+       }
+
+       return fmt;
+}
+EXPORT_SYMBOL(drm_mode_legacy_fb_format);
+
 /**
  * drm_mode_addfb - add an FB to the graphics configuration
  * @inode: inode from the ioctl
@@ -1684,7 +2017,140 @@ out:
 int drm_mode_addfb(struct drm_device *dev,
                   void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_fb_cmd *r = data;
+       struct drm_mode_fb_cmd *or = data;
+       struct drm_mode_fb_cmd2 r = {};
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_framebuffer *fb;
+       int ret = 0;
+
+       /* Use new struct with format internally */
+       r.fb_id = or->fb_id;
+       r.width = or->width;
+       r.height = or->height;
+       r.pitches[0] = or->pitch;
+       r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
+       r.handles[0] = or->handle;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       if ((config->min_width > r.width) || (r.width > config->max_width))
+               return -EINVAL;
+
+       if ((config->min_height > r.height) || (r.height > config->max_height))
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       /* TODO check buffer is sufficiently large */
+       /* TODO setup destructor callback */
+
+       fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
+       if (IS_ERR(fb)) {
+               DRM_ERROR("could not create framebuffer\n");
+               ret = PTR_ERR(fb);
+               goto out;
+       }
+
+       or->fb_id = fb->base.id;
+       list_add(&fb->filp_head, &file_priv->fbs);
+       DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
+
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+static int format_check(struct drm_mode_fb_cmd2 *r)
+{
+       uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
+
+       switch (format) {
+       case DRM_FORMAT_C8:
+       case DRM_FORMAT_RGB332:
+       case DRM_FORMAT_BGR233:
+       case DRM_FORMAT_XRGB4444:
+       case DRM_FORMAT_XBGR4444:
+       case DRM_FORMAT_RGBX4444:
+       case DRM_FORMAT_BGRX4444:
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_ABGR4444:
+       case DRM_FORMAT_RGBA4444:
+       case DRM_FORMAT_BGRA4444:
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_RGBX5551:
+       case DRM_FORMAT_BGRX5551:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_RGBA5551:
+       case DRM_FORMAT_BGRA5551:
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_BGR565:
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_BGR888:
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_RGBX8888:
+       case DRM_FORMAT_BGRX8888:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_BGRA8888:
+       case DRM_FORMAT_XRGB2101010:
+       case DRM_FORMAT_XBGR2101010:
+       case DRM_FORMAT_RGBX1010102:
+       case DRM_FORMAT_BGRX1010102:
+       case DRM_FORMAT_ARGB2101010:
+       case DRM_FORMAT_ABGR2101010:
+       case DRM_FORMAT_RGBA1010102:
+       case DRM_FORMAT_BGRA1010102:
+       case DRM_FORMAT_YUYV:
+       case DRM_FORMAT_YVYU:
+       case DRM_FORMAT_UYVY:
+       case DRM_FORMAT_VYUY:
+       case DRM_FORMAT_AYUV:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_YUV410:
+       case DRM_FORMAT_YVU410:
+       case DRM_FORMAT_YUV411:
+       case DRM_FORMAT_YVU411:
+       case DRM_FORMAT_YUV420:
+       case DRM_FORMAT_YVU420:
+       case DRM_FORMAT_YUV422:
+       case DRM_FORMAT_YVU422:
+       case DRM_FORMAT_YUV444:
+       case DRM_FORMAT_YVU444:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/**
+ * drm_mode_addfb2 - add an FB to the graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Add a new FB to the specified CRTC, given a user request with format.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb2(struct drm_device *dev,
+                   void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_fb_cmd2 *r = data;
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_framebuffer *fb;
        int ret = 0;
@@ -1693,18 +2159,23 @@ int drm_mode_addfb(struct drm_device *dev,
                return -EINVAL;
 
        if ((config->min_width > r->width) || (r->width > config->max_width)) {
-               DRM_ERROR("mode new framebuffer width not within limits\n");
+               DRM_ERROR("bad framebuffer width %d, should be >= %d && <= %d\n",
+                         r->width, config->min_width, config->max_width);
                return -EINVAL;
        }
        if ((config->min_height > r->height) || (r->height > config->max_height)) {
-               DRM_ERROR("mode new framebuffer height not within limits\n");
+               DRM_ERROR("bad framebuffer height %d, should be >= %d && <= %d\n",
+                         r->height, config->min_height, config->max_height);
                return -EINVAL;
        }
 
-       mutex_lock(&dev->mode_config.mutex);
+       ret = format_check(r);
+       if (ret) {
+               DRM_ERROR("bad framebuffer format 0x%08x\n", r->pixel_format);
+               return ret;
+       }
 
-       /* TODO check buffer is sufficiently large */
-       /* TODO setup destructor callback */
+       mutex_lock(&dev->mode_config.mutex);
 
        fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
        if (IS_ERR(fb)) {
@@ -1756,7 +2227,6 @@ int drm_mode_rmfb(struct drm_device *dev,
        obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
        /* TODO check that we really get a framebuffer back. */
        if (!obj) {
-               DRM_ERROR("mode invalid framebuffer id\n");
                ret = -EINVAL;
                goto out;
        }
@@ -1767,7 +2237,6 @@ int drm_mode_rmfb(struct drm_device *dev,
                        found = 1;
 
        if (!found) {
-               DRM_ERROR("tried to remove a fb that we didn't own\n");
                ret = -EINVAL;
                goto out;
        }
@@ -1814,7 +2283,6 @@ int drm_mode_getfb(struct drm_device *dev,
        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;
        }
@@ -1824,7 +2292,7 @@ int drm_mode_getfb(struct drm_device *dev,
        r->width = fb->width;
        r->depth = fb->depth;
        r->bpp = fb->bits_per_pixel;
-       r->pitch = fb->pitch;
+       r->pitch = fb->pitches[0];
        fb->funcs->create_handle(fb, file_priv, &r->handle);
 
 out:
@@ -1850,14 +2318,13 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
        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;
+       clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
 
        if (!num_clips != !clips_ptr) {
                ret = -EINVAL;
@@ -2253,7 +2720,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        struct drm_property_enum *prop_enum;
        struct drm_mode_property_enum __user *enum_ptr;
        struct drm_property_blob *prop_blob;
-       uint32_t *blob_id_ptr;
+       uint32_t __user *blob_id_ptr;
        uint64_t __user *values_ptr;
        uint32_t __user *blob_length_ptr;
 
@@ -2283,7 +2750,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        out_resp->flags = property->flags;
 
        if ((out_resp->count_values >= value_count) && value_count) {
-               values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr;
+               values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
                for (i = 0; i < value_count; i++) {
                        if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
                                ret = -EFAULT;
@@ -2296,7 +2763,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        if (property->flags & DRM_MODE_PROP_ENUM) {
                if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
                        copied = 0;
-                       enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr;
+                       enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
                        list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
 
                                if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
@@ -2318,8 +2785,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        if (property->flags & DRM_MODE_PROP_BLOB) {
                if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
                        copied = 0;
-                       blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr;
-                       blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr;
+                       blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
+                       blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr;
 
                        list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
                                if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
@@ -2380,7 +2847,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
        struct drm_mode_get_blob *out_resp = data;
        struct drm_property_blob *blob;
        int ret = 0;
-       void *blob_ptr;
+       void __user *blob_ptr;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
@@ -2394,7 +2861,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
        blob = obj_to_blob(obj);
 
        if (out_resp->length == blob->length) {
-               blob_ptr = (void *)(unsigned long)out_resp->data;
+               blob_ptr = (void __user *)(unsigned long)out_resp->data;
                if (copy_to_user(blob_ptr, blob->data, blob->length)){
                        ret = -EFAULT;
                        goto done;
@@ -2788,3 +3255,71 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
 
        return dev->driver->dumb_destroy(file_priv, dev, args->handle);
 }
+
+/*
+ * Just need to support RGB formats here for compat with code that doesn't
+ * use pixel formats directly yet.
+ */
+void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
+                         int *bpp)
+{
+       switch (format) {
+       case DRM_FORMAT_RGB332:
+       case DRM_FORMAT_BGR233:
+               *depth = 8;
+               *bpp = 8;
+               break;
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_RGBX5551:
+       case DRM_FORMAT_BGRX5551:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_RGBA5551:
+       case DRM_FORMAT_BGRA5551:
+               *depth = 15;
+               *bpp = 16;
+               break;
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_BGR565:
+               *depth = 16;
+               *bpp = 16;
+               break;
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_BGR888:
+               *depth = 24;
+               *bpp = 24;
+               break;
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_RGBX8888:
+       case DRM_FORMAT_BGRX8888:
+               *depth = 24;
+               *bpp = 32;
+               break;
+       case DRM_FORMAT_XRGB2101010:
+       case DRM_FORMAT_XBGR2101010:
+       case DRM_FORMAT_RGBX1010102:
+       case DRM_FORMAT_BGRX1010102:
+       case DRM_FORMAT_ARGB2101010:
+       case DRM_FORMAT_ABGR2101010:
+       case DRM_FORMAT_RGBA1010102:
+       case DRM_FORMAT_BGRA1010102:
+               *depth = 30;
+               *bpp = 32;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_BGRA8888:
+               *depth = 32;
+               *bpp = 32;
+               break;
+       default:
+               DRM_DEBUG_KMS("unsupported pixel format\n");
+               *depth = 0;
+               *bpp = 0;
+               break;
+       }
+}
+EXPORT_SYMBOL(drm_fb_get_bpp_depth);