Fixup sysfs output registration
[platform/upstream/libdrm.git] / linux-core / drm_crtc.c
index e5a4b32..f54ceb7 100644 (file)
 #include "drmP.h"
 #include "drm_crtc.h"
 
+struct drm_prop_enum_list {
+       int type;
+       char *name;
+};
+
+/*
+ * Global properties
+ */
+static struct drm_prop_enum_list drm_dpms_enum_list[] =
+{ { DPMSModeOn, "On" },
+  { DPMSModeStandby, "Standby" },
+  { DPMSModeSuspend, "Suspend" },
+  { DPMSModeOff, "Off" }
+};
+
+char *drm_get_dpms_name(int val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+               if (drm_dpms_enum_list[i].type == val)
+                       return drm_dpms_enum_list[i].name;
+
+       return "unknown";
+}
+
+static struct drm_prop_enum_list drm_conn_enum_list[] = 
+{ { ConnectorUnknown, "Unknown" },
+  { ConnectorVGA, "VGA" },
+  { ConnectorDVII, "DVI-I" },
+  { ConnectorDVID, "DVI-D" },
+  { ConnectorDVIA, "DVI-A" },
+  { ConnectorComposite, "Composite" },
+  { ConnectorSVIDEO, "SVIDEO" },
+  { ConnectorLVDS, "LVDS" },
+  { ConnectorComponent, "Component" },
+  { Connector9PinDIN, "9-pin DIN" },
+  { ConnectorDisplayPort, "DisplayPort" },
+  { ConnectorHDMIA, "HDMI Type A" },
+  { ConnectorHDMIB, "HDMI Type B" },
+};
+static struct drm_prop_enum_list drm_output_enum_list[] =
+{ { DRM_MODE_OUTPUT_NONE, "None" },
+  { DRM_MODE_OUTPUT_DAC, "DAC" },
+  { DRM_MODE_OUTPUT_TMDS, "TMDS" },
+  { DRM_MODE_OUTPUT_LVDS, "LVDS" },
+  { DRM_MODE_OUTPUT_TVDAC, "TV" },
+};
+
+char *drm_get_output_name(struct drm_output *output)
+{
+       static char buf[32];
+
+       snprintf(buf, 32, "%s-%d", drm_output_enum_list[output->output_type].name,
+                output->output_type_id);
+       return buf;
+}
+
+char *drm_get_output_status_name(enum drm_output_status status)
+{
+       if (status == output_status_connected)
+               return "connected";
+       else if (status == output_status_disconnected)
+               return "disconnected";
+       else
+               return "unknown";
+}
+
 /**
  * drm_idr_get - allocate a new identifier
  * @dev: DRM device
@@ -59,10 +127,8 @@ again:
        }
 
        ret = idr_get_new_above(&dev->mode_config.crtc_idr, ptr, 1, &new_id);
-       if (ret == -EAGAIN) {
-               spin_unlock(&dev->mode_config.config_lock);
-               goto again;
-       }       
+       if (ret == -EAGAIN)
+               goto again;     
 
        return new_id;
 }
@@ -119,17 +185,10 @@ struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
  * RETURNS:
  * Pointer to new framebuffer or NULL on error.
  */
-struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev)
+struct drm_framebuffer *drm_framebuffer_create(struct drm_device *dev)
 {
        struct drm_framebuffer *fb;
 
-       /* Limit to single framebuffer for now */
-       if (dev->mode_config.num_fb > 1) {
-               spin_unlock(&dev->mode_config.config_lock);
-               DRM_ERROR("Attempt to add multiple framebuffers failed\n");
-               return NULL;
-       }
-
        fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
        if (!fb)
                return NULL;
@@ -155,7 +214,7 @@ EXPORT_SYMBOL(drm_framebuffer_create);
  */
 void drm_framebuffer_destroy(struct drm_framebuffer *fb)
 {
-       drm_device_t *dev = fb->dev;
+       struct drm_device *dev = fb->dev;
        struct drm_crtc *crtc;
 
        /* remove from any CRTC */
@@ -170,6 +229,7 @@ void drm_framebuffer_destroy(struct drm_framebuffer *fb)
 
        kfree(fb);
 }
+EXPORT_SYMBOL(drm_framebuffer_destroy);
 
 /**
  * drm_crtc_create - create a new CRTC object
@@ -184,7 +244,7 @@ void drm_framebuffer_destroy(struct drm_framebuffer *fb)
  * RETURNS:
  * Pointer to new CRTC object or NULL on error.
  */
-struct drm_crtc *drm_crtc_create(drm_device_t *dev,
+struct drm_crtc *drm_crtc_create(struct drm_device *dev,
                                 const struct drm_crtc_funcs *funcs)
 {
        struct drm_crtc *crtc;
@@ -217,7 +277,7 @@ EXPORT_SYMBOL(drm_crtc_create);
  */
 void drm_crtc_destroy(struct drm_crtc *crtc)
 {
-       drm_device_t *dev = crtc->dev;
+       struct drm_device *dev = crtc->dev;
 
        if (crtc->funcs->cleanup)
                (*crtc->funcs->cleanup)(crtc);
@@ -244,7 +304,7 @@ EXPORT_SYMBOL(drm_crtc_destroy);
 bool drm_crtc_in_use(struct drm_crtc *crtc)
 {
        struct drm_output *output;
-       drm_device_t *dev = crtc->dev;
+       struct drm_device *dev = crtc->dev;
        /* FIXME: Locking around list access? */
        list_for_each_entry(output, &dev->mode_config.output_list, head)
                if (output->crtc == crtc)
@@ -281,75 +341,82 @@ static struct drm_display_mode std_mode[] = {
  *
  * FIXME: take into account monitor limits
  */
-void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
+void drm_crtc_probe_single_output_modes(struct drm_output *output, int maxX, int maxY)
 {
-       struct drm_output *output;
+       struct drm_device *dev = output->dev;
        struct drm_display_mode *mode, *t;
        int ret;
        //if (maxX == 0 || maxY == 0) 
        // TODO
 
-       list_for_each_entry(output, &dev->mode_config.output_list, head) {
-
-               /* set all modes to the unverified state */
-               list_for_each_entry_safe(mode, t, &output->modes, head)
-                       mode->status = MODE_UNVERIFIED;
+       /* set all modes to the unverified state */
+       list_for_each_entry_safe(mode, t, &output->modes, head)
+               mode->status = MODE_UNVERIFIED;
                
-               output->status = (*output->funcs->detect)(output);
-
-               if (output->status == output_status_disconnected) {
-                       DRM_DEBUG("%s is disconnected\n", output->name);
-                       /* TODO set EDID to NULL */
-                       continue;
-               }
-
-               ret = (*output->funcs->get_modes)(output);
-
-               if (ret) {
-                       drm_mode_output_list_update(output);
-               }
-
-               if (maxX && maxY)
-                       drm_mode_validate_size(dev, &output->modes, maxX,
-                                              maxY, 0);
-               list_for_each_entry_safe(mode, t, &output->modes, head) {
-                       if (mode->status == MODE_OK)
-                               mode->status = (*output->funcs->mode_valid)(output,mode);
-               }
+       output->status = (*output->funcs->detect)(output);
+       
+       if (output->status == output_status_disconnected) {
+               DRM_DEBUG("%s is disconnected\n", drm_get_output_name(output));
+               /* TODO set EDID to NULL */
+               return;
+       }
+       
+       ret = (*output->funcs->get_modes)(output);
+       
+       if (ret) {
+               drm_mode_output_list_update(output);
+       }
+       
+       if (maxX && maxY)
+               drm_mode_validate_size(dev, &output->modes, maxX,
+                                      maxY, 0);
+       list_for_each_entry_safe(mode, t, &output->modes, head) {
+               if (mode->status == MODE_OK)
+                       mode->status = (*output->funcs->mode_valid)(output,mode);
+       }
+       
+       
+       drm_mode_prune_invalid(dev, &output->modes, TRUE);
+       
+       if (list_empty(&output->modes)) {
+               struct drm_display_mode *stdmode;
                
+               DRM_DEBUG("No valid modes on %s\n", drm_get_output_name(output));
+               
+               /* Should we do this here ???
+                * When no valid EDID modes are available we end up
+                * here and bailed in the past, now we add a standard
+                * 640x480@60Hz mode and carry on.
+                */
+               stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+               drm_mode_probed_add(output, stdmode);
+               drm_mode_list_concat(&output->probed_modes,
+                                    &output->modes);
+               
+               DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+                         drm_get_output_name(output));
+       }
+       
+       drm_mode_sort(&output->modes);
+       
+       DRM_DEBUG("Probed modes for %s\n", drm_get_output_name(output));
+       list_for_each_entry_safe(mode, t, &output->modes, head) {
+               mode->vrefresh = drm_mode_vrefresh(mode);
+               
+               drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+               drm_mode_debug_printmodeline(dev, mode);
+       }
+}
 
-               drm_mode_prune_invalid(dev, &output->modes, TRUE);
-
-               if (list_empty(&output->modes)) {
-                       struct drm_display_mode *stdmode;
-
-                       DRM_DEBUG("No valid modes on %s\n", output->name);
-
-                       /* Should we do this here ???
-                        * When no valid EDID modes are available we end up
-                        * here and bailed in the past, now we add a standard
-                        * 640x480@60Hz mode and carry on.
-                        */
-                       stdmode = drm_mode_duplicate(dev, &std_mode[0]);
-                       drm_mode_probed_add(output, stdmode);
-                       drm_mode_list_concat(&output->probed_modes,
-                                            &output->modes);
-
-                       DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
-                                                               output->name);
-               }
-
-               drm_mode_sort(&output->modes);
-
-               DRM_DEBUG("Probed modes for %s\n", output->name);
-               list_for_each_entry_safe(mode, t, &output->modes, head) {
-                       mode->vrefresh = drm_mode_vrefresh(mode);
+void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
+{
+       struct drm_output *output;
 
-                       drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-                       drm_mode_debug_printmodeline(dev, mode);
-               }
+       list_for_each_entry(output, &dev->mode_config.output_list, head) {
+               drm_crtc_probe_single_output_modes(output, maxX, maxY);
        }
 }
+EXPORT_SYMBOL(drm_crtc_probe_output_modes);
 
 /**
  * drm_crtc_set_mode - set a mode
@@ -370,20 +437,19 @@ void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
 bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                       int x, int y)
 {
-       drm_device_t *dev = crtc->dev;
+       struct drm_device *dev = crtc->dev;
        struct drm_display_mode *adjusted_mode, saved_mode;
        int saved_x, saved_y;
        bool didLock = false;
-       bool ret = false;
        struct drm_output *output;
+       bool ret = true;
 
        adjusted_mode = drm_mode_duplicate(dev, mode);
 
        crtc->enabled = drm_crtc_in_use(crtc);
 
-       if (!crtc->enabled) {
+       if (!crtc->enabled)
                return true;
-       }
 
        didLock = crtc->funcs->lock(crtc);
 
@@ -398,8 +464,13 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
        crtc->x = x;
        crtc->y = y;
 
-       /* XXX short-circuit changes to base location only */
-       
+       if (drm_mode_equal(&saved_mode, &crtc->mode)) {
+               if (saved_x != crtc->x || saved_y != crtc->y) {
+                       crtc->funcs->mode_set_base(crtc, crtc->x, crtc->y);
+                       goto done;
+               }
+       }
+
        /* Pass our mode to the outputs and the CRTC to give them a chance to
         * adjust it according to limitations or output properties, and also
         * a chance to reject the mode entirely.
@@ -409,12 +480,12 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                if (output->crtc != crtc)
                        continue;
                
-               if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
+               if (!(ret = output->funcs->mode_fixup(output, mode, adjusted_mode))) {
                        goto done;
                }
        }
        
-       if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
+       if (!(ret = crtc->funcs->mode_fixup(crtc, mode, adjusted_mode))) {
                goto done;
        }
 
@@ -434,37 +505,46 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
         * on the DPLL.
         */
        crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y);
+
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
-               if (output->crtc == crtc)
-                       output->funcs->mode_set(output, mode, adjusted_mode);
+
+               if (output->crtc != crtc)
+                       continue;
+               
+               DRM_INFO("%s: set mode %s %x\n", drm_get_output_name(output), mode->name, mode->mode_id);
+
+               output->funcs->mode_set(output, mode, adjusted_mode);
        }
        
        /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
        crtc->funcs->commit(crtc);
+
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
-               if (output->crtc == crtc) {
-                       output->funcs->commit(output);
+
+               if (output->crtc != crtc)
+                       continue;
+               
+               output->funcs->commit(output);
+
 #if 0 // TODO def RANDR_12_INTERFACE
-                       if (output->randr_output)
-                               RRPostPendingProperties (output->randr_output);
+               if (output->randr_output)
+                       RRPostPendingProperties (output->randr_output);
 #endif
-               }
        }
        
        /* XXX free adjustedmode */
        drm_mode_destroy(dev, adjusted_mode);
-       ret = TRUE;
        /* TODO */
 //     if (scrn->pScreen)
 //             drm_crtc_set_screen_sub_pixel_order(dev);
 
 done:
-       if (!ret) {
+       if (!ret) { 
+               crtc->mode = saved_mode;
                crtc->x = saved_x;
                crtc->y = saved_y;
-               crtc->mode = saved_mode;
-       }
-       
+       } 
+
        if (didLock)
                crtc->funcs->unlock (crtc);
        
@@ -497,7 +577,8 @@ void drm_disable_unused_functions(struct drm_device *dev)
                        crtc->funcs->dpms(crtc, DPMSModeOff);
        }
 }
-       
+EXPORT_SYMBOL(drm_disable_unused_functions);
+
 /**
  * drm_mode_probed_add - add a mode to the specified output's probed mode list
  * @output: output the new mode
@@ -547,9 +628,9 @@ EXPORT_SYMBOL(drm_mode_remove);
  * RETURNS:
  * Pointer to the new output or NULL on error.
  */
-struct drm_output *drm_output_create(drm_device_t *dev,
+struct drm_output *drm_output_create(struct drm_device *dev,
                                     const struct drm_output_funcs *funcs,
-                                    const char *name)
+                                    int output_type)
 {
        struct drm_output *output = NULL;
 
@@ -560,21 +641,25 @@ struct drm_output *drm_output_create(drm_device_t *dev,
        output->dev = dev;
        output->funcs = funcs;
        output->id = drm_idr_get(dev, output);
-       if (name)
-               strncpy(output->name, name, DRM_OUTPUT_LEN);
-       output->name[DRM_OUTPUT_LEN - 1] = 0;
+       output->output_type = output_type;
+       output->output_type_id = 1; /* TODO */
        output->subpixel_order = SubPixelUnknown;
+       INIT_LIST_HEAD(&output->user_modes);
        INIT_LIST_HEAD(&output->probed_modes);
        INIT_LIST_HEAD(&output->modes);
        /* randr_output? */
        /* output_set_monitor(output)? */
        /* check for output_ignored(output)? */
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
        list_add_tail(&output->head, &dev->mode_config.output_list);
        dev->mode_config.num_output++;
 
-       spin_unlock(&dev->mode_config.config_lock);
+       drm_output_attach_property(output, dev->mode_config.edid_property, 0);
+
+       drm_output_attach_property(output, dev->mode_config.dpms_property, 0);
+
+       mutex_unlock(&dev->mode_config.mutex);
 
        return output;
 
@@ -605,43 +690,17 @@ void drm_output_destroy(struct drm_output *output)
        list_for_each_entry_safe(mode, t, &output->modes, head)
                drm_mode_remove(output, mode);
 
-       spin_lock(&dev->mode_config.config_lock);
+       list_for_each_entry_safe(mode, t, &output->user_modes, head)
+               drm_mode_remove(output, mode);
+
+       mutex_lock(&dev->mode_config.mutex);
        drm_idr_put(dev, output->id);
        list_del(&output->head);
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        kfree(output);
 }
 EXPORT_SYMBOL(drm_output_destroy);
 
-/**
- * drm_output_rename - rename an output
- * @output: output to rename
- * @name: new user visible name
- *
- * LOCKING:
- * None.
- *
- * Simply stuff a new name into @output's name field, based on @name.
- *
- * RETURNS:
- * True if the name was changed, false otherwise.
- */
-bool drm_output_rename(struct drm_output *output, const char *name)
-{
-       if (!name)
-               return false;
-
-       strncpy(output->name, name, DRM_OUTPUT_LEN);
-       output->name[DRM_OUTPUT_LEN - 1] = 0;
-
-       DRM_DEBUG("Changed name to %s\n", output->name);
-//     drm_output_set_monitor(output);
-//     if (drm_output_ignored(output))
-//             return FALSE;
-
-       return TRUE;
-}
-EXPORT_SYMBOL(drm_output_rename);
 
 /**
  * drm_mode_create - create a new display mode
@@ -686,6 +745,69 @@ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
 }
 EXPORT_SYMBOL(drm_mode_destroy);
 
+static int drm_mode_create_standard_output_properties(struct drm_device *dev)
+{
+       int i;
+
+       /*
+        * Standard properties (apply to all outputs)
+        */
+       dev->mode_config.edid_property =
+               drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE,
+                                   "EDID", 0);
+
+       dev->mode_config.dpms_property =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM, 
+                       "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
+       for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+               drm_property_add_enum(dev->mode_config.dpms_property, i, drm_dpms_enum_list[i].type, drm_dpms_enum_list[i].name);
+
+       dev->mode_config.connector_type_property =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE,
+                       "Connector Type", ARRAY_SIZE(drm_conn_enum_list));
+       for (i = 0; i < ARRAY_SIZE(drm_conn_enum_list); i++)
+               drm_property_add_enum(dev->mode_config.connector_type_property, i, drm_conn_enum_list[i].type, drm_conn_enum_list[i].name);
+
+       dev->mode_config.connector_num_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE | DRM_MODE_PROP_IMMUTABLE,
+                       "Connector ID", 2);
+       dev->mode_config.connector_num_property->values[0] = 0;
+       dev->mode_config.connector_num_property->values[1] = 20;
+
+       /*
+        * TV specific properties
+        */
+       dev->mode_config.tv_left_margin_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "left margin", 2);
+       dev->mode_config.tv_left_margin_property->values[0] = 0;
+       dev->mode_config.tv_left_margin_property->values[1] = 100;
+
+       dev->mode_config.tv_right_margin_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "right margin", 2);
+       dev->mode_config.tv_right_margin_property->values[0] = 0;
+       dev->mode_config.tv_right_margin_property->values[1] = 100;
+
+       dev->mode_config.tv_top_margin_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "top margin", 2);
+       dev->mode_config.tv_top_margin_property->values[0] = 0;
+       dev->mode_config.tv_top_margin_property->values[1] = 100;
+
+       dev->mode_config.tv_bottom_margin_property =
+               drm_property_create(dev, DRM_MODE_PROP_RANGE |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "bottom margin", 2);
+       dev->mode_config.tv_bottom_margin_property->values[0] = 0;
+       dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+
+       return 0;
+}
+
 /**
  * drm_mode_config_init - initialize DRM mode_configuration structure
  * @dev: DRM device
@@ -696,14 +818,23 @@ EXPORT_SYMBOL(drm_mode_destroy);
  * Initialize @dev's mode_config structure, used for tracking the graphics
  * configuration of @dev.
  */
-void drm_mode_config_init(drm_device_t *dev)
+void drm_mode_config_init(struct drm_device *dev)
 {
-       spin_lock_init(&dev->mode_config.config_lock);
+       mutex_init(&dev->mode_config.mutex);
        INIT_LIST_HEAD(&dev->mode_config.fb_list);
        INIT_LIST_HEAD(&dev->mode_config.crtc_list);
        INIT_LIST_HEAD(&dev->mode_config.output_list);
-       INIT_LIST_HEAD(&dev->mode_config.usermode_list);
+       INIT_LIST_HEAD(&dev->mode_config.property_list);
+       INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
        idr_init(&dev->mode_config.crtc_idr);
+
+       drm_mode_create_standard_output_properties(dev);
+
+       /* Just to be sure */
+       dev->mode_config.num_fb = 0;
+       dev->mode_config.num_output = 0;
+       dev->mode_config.num_crtc = 0;
+       dev->mode_config.hotplug_counter = 0;
 }
 EXPORT_SYMBOL(drm_mode_config_init);
 
@@ -722,10 +853,10 @@ EXPORT_SYMBOL(drm_mode_config_init);
  * RETURNS:
  * Zero on success, -EINVAL if the handle couldn't be found.
  */
-static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **bo, unsigned long handle)
+static int drm_get_buffer_object(struct drm_device *dev, struct drm_buffer_object **bo, unsigned long handle)
 {
-       drm_user_object_t *uo;
-       drm_hash_item_t *hash;
+       struct drm_user_object *uo;
+       struct drm_hash_item *hash;
        int ret;
 
        *bo = NULL;
@@ -738,13 +869,13 @@ static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **b
                goto out_err;
        }
 
-       uo = drm_hash_entry(hash, drm_user_object_t, hash);
+       uo = drm_hash_entry(hash, struct drm_user_object, hash);
        if (uo->type != drm_buffer_type) {
                ret = -EINVAL;
                goto out_err;
        }
        
-       *bo = drm_user_object_entry(uo, drm_buffer_object_t, base);
+       *bo = drm_user_object_entry(uo, struct drm_buffer_object, base);
        ret = 0;
 out_err:
        mutex_unlock(&dev->struct_mutex);
@@ -758,12 +889,13 @@ out_err:
  * LOCKING:
  * Caller must hold mode config lock.
  */
-static void drm_pick_crtcs (drm_device_t *dev)
+static void drm_pick_crtcs (struct drm_device *dev)
 {
-       int c, o;
+       int c, o, assigned;
        struct drm_output *output, *output_equal;
        struct drm_crtc   *crtc;
        struct drm_display_mode *des_mode = NULL, *modes, *modes_equal;
+       int found;
 
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
                        output->crtc = NULL;
@@ -784,31 +916,48 @@ static void drm_pick_crtcs (drm_device_t *dev)
                if (output->status != output_status_connected)
                        continue;
 
+               if (list_empty(&output->modes))
+                       continue;
+
                des_mode = NULL;
+               found = 0;
                list_for_each_entry(des_mode, &output->modes, head) {
-                       if (des_mode->flags & DRM_MODE_TYPE_PREFERRED)
+                       if (des_mode->type & DRM_MODE_TYPE_PREFERRED) {
+                               found = 1;
                                break;
+                       }
                }
 
-
-               /* No preferred mode, let's select another which should pick
-                * the default 640x480 if nothing else is here.
-                * 
-                */
-               if (!des_mode || !(des_mode->flags & DRM_MODE_TYPE_PREFERRED)) {
+               /* No preferred mode, let's just select the first available */
+               if (!found) {
+                       des_mode = NULL;
                        list_for_each_entry(des_mode, &output->modes, head) {
-                               if (des_mode->flags & DRM_MODE_TYPE_DEFAULT)
-                                       break;
+                               break;
                        }
                }
 
                c = -1;
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+                       assigned = 0;
+
                        c++;
                        if ((output->possible_crtcs & (1 << c)) == 0)
                                continue;
        
-#if 0 /* should we try and clone ?? - code not tested - FIXME */
+                       list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
+                               if (output->id == output_equal->id)
+                                       continue;
+
+                               /* Find out if crtc has been assigned before */
+                               if (output_equal->crtc == crtc)
+                                       assigned = 1;
+                       }
+
+#if 1 /* continue for now */
+                       if (assigned)
+                               continue;
+#endif
+
                        o = -1;
                        list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
                                o++;
@@ -818,7 +967,10 @@ static void drm_pick_crtcs (drm_device_t *dev)
                                list_for_each_entry(modes, &output->modes, head) {
                                        list_for_each_entry(modes_equal, &output_equal->modes, head) {
                                                if (drm_mode_equal (modes, modes_equal)) {
-                                                       if ((output->possible_clones & (1 << o))) {
+                                                       if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) {
+                                                               printk("Cloning %s (0x%lx) to %s (0x%lx)\n",drm_get_output_name(output),output->possible_clones,drm_get_output_name(output_equal),output_equal->possible_clones);
+                                                               des_mode = modes;
+                                                               assigned = 0;
                                                                goto clone;
                                                        }
                                                }
@@ -827,18 +979,21 @@ static void drm_pick_crtcs (drm_device_t *dev)
                        }
 
 clone:
-#endif
+                       /* crtc has been assigned skip it */
+                       if (assigned)
+                               continue;
+
                        /* Found a CRTC to attach to, do it ! */
                        output->crtc = crtc;
                        output->crtc->desired_mode = des_mode;
                        output->initial_x = 0;
                        output->initial_y = 0;
-                       DRM_DEBUG("Desired mode for CRTC %d is %s\n",c,des_mode->name);
+                       DRM_DEBUG("Desired mode for CRTC %d is 0x%x:%s\n",c,des_mode->mode_id, des_mode->name);
                        break;
                }
        }
 }
-
+EXPORT_SYMBOL(drm_pick_crtcs);
 
 /**
  * drm_initial_config - setup a sane initial output configuration
@@ -855,71 +1010,48 @@ clone:
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
  */
-bool drm_initial_config(drm_device_t *dev, bool can_grow)
+bool drm_initial_config(struct drm_device *dev, bool can_grow)
 {
-       /* do a hardcoded initial configuration here */
-       struct drm_display_mode *des_mode = NULL;
        struct drm_output *output;
-       struct drm_framebuffer *fb;
-       drm_buffer_object_t *fbo;
-       unsigned long size, bytes_per_pixel;
+       struct drm_crtc *crtc;
        int ret = false;
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
 
        drm_crtc_probe_output_modes(dev, 2048, 2048);
 
        drm_pick_crtcs(dev);
 
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+               /* can't setup the crtc if there's no assigned mode */
+               if (!crtc->desired_mode)
+                       continue;
+
+               /* Now setup the fbdev for attached crtcs */
+               dev->driver->fb_probe(dev, crtc);
+       }
+
+       /* This is a little screwy, as we've already walked the outputs 
+        * above, but it's a little bit of magic too. There's the potential
+        * for things not to get setup above if an existing device gets
+        * re-assigned thus confusing the hardware. By walking the outputs
+        * this fixes up their crtc's.
+        */
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
 
-               /* can't setup the output if there's no assigned crtc or mode */
+               /* can't setup the output if there's no assigned mode */
                if (!output->crtc || !output->crtc->desired_mode)
                        continue;
 
-               fb = drm_framebuffer_create(dev);
-               if (!fb) {
-                       DRM_ERROR("failed to allocate fb.\n");
-                       ret = true;
-                       goto out;
-               }
-               output->crtc->fb = fb;
-               des_mode = output->crtc->desired_mode;
-
-               if (des_mode->hdisplay > fb->width)
-                       fb->width = des_mode->hdisplay;
-               if (des_mode->vdisplay > fb->height)
-                       fb->height = des_mode->vdisplay;
-
-               /* FIXME: multiple depths */
-               bytes_per_pixel = 4;
-               fb->bits_per_pixel = 32;
-               fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8);
-               fb->depth = 24;
-               size = fb->width * fb->height * bytes_per_pixel;
-               /* FIXME - what about resizeable objects ??? */
-               ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel,
-                                              DRM_BO_FLAG_READ |
-                                              DRM_BO_FLAG_WRITE |
-                                              DRM_BO_FLAG_MEM_PRIV0 |
-                                              DRM_BO_FLAG_NO_MOVE,
-                                              0, 0, 0,
-                                              &fbo);
-               if (ret) {
-                       printk(KERN_ERR "failed to allocate framebuffer\n");
-                       drm_framebuffer_destroy(fb);
-                       continue;
-               }
-               fb->offset = fbo->offset;
-               fb->bo = fbo;
-               printk("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width,
-                      fb->height, fbo->offset, fbo);
-               dev->driver->fb_probe(dev, output->crtc);
+               /* and needs an attached fb */
+               if (output->crtc->fb)
+                       drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0);
        }
+
        drm_disable_unused_functions(dev);
 
-out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 EXPORT_SYMBOL(drm_initial_config);
@@ -936,34 +1068,34 @@ EXPORT_SYMBOL(drm_initial_config);
  *
  * FIXME: cleanup any dangling user buffer objects too
  */
-void drm_mode_config_cleanup(drm_device_t *dev)
+void drm_mode_config_cleanup(struct drm_device *dev)
 {
        struct drm_output *output, *ot;
        struct drm_crtc *crtc, *ct;
        struct drm_framebuffer *fb, *fbt;
-       struct drm_display_mode *mode, *mt;
+       struct drm_property *property, *pt;
+
        list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) {
+               drm_sysfs_output_remove(output);
                drm_output_destroy(output);
        }
 
-       list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
-               drm_crtc_destroy(crtc);
+       list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) {
+               drm_property_destroy(dev, property);
        }
 
-       list_for_each_entry_safe(mode, mt, &dev->mode_config.usermode_list, head) {
-               drm_mode_destroy(dev, mode);
-       }
-               
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
-               dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb));
-               /* If this FB was the kernel one, free it */
-               if (fb->bo->type == drm_bo_type_kernel) {
-                       mutex_lock(&dev->struct_mutex);
-                       drm_bo_usage_deref_locked(fb->bo);
-                       mutex_unlock(&dev->struct_mutex);
-               }
-               drm_framebuffer_destroy(fb);
+               /* there should only be bo of kernel type left */
+               if (fb->bo->type != drm_bo_type_kernel)
+                       drm_framebuffer_destroy(fb);
+               else
+                       dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb));
        }
+
+       list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
+               drm_crtc_destroy(crtc);
+       }
+
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
 
@@ -986,25 +1118,33 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
  */
 int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set, struct drm_framebuffer *fb)
 {
-       drm_device_t *dev = crtc->dev;
+       struct drm_device *dev = crtc->dev;
        struct drm_crtc **save_crtcs, *new_crtc;
        bool save_enabled = crtc->enabled;
-       bool changed;
+       bool changed = false;
+       bool flip_or_move = false;
        struct drm_output *output;
        int count = 0, ro;
 
-       save_crtcs = kzalloc(dev->mode_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL);
+       /* this is meant to be num_output not num_crtc */
+       save_crtcs = kzalloc(dev->mode_config.num_output * sizeof(struct drm_crtc *), GFP_KERNEL);
        if (!save_crtcs)
                return -ENOMEM;
 
+       /* We should be able to check here if the fb has the same properties
+        * and then just flip_or_move it */
        if (crtc->fb != fb)
-               changed = true;
+               flip_or_move = true;
 
        if (crtc_info->x != crtc->x || crtc_info->y != crtc->y)
-               changed = true;
+               flip_or_move = true;
 
-       if (new_mode && (crtc->mode.mode_id != new_mode->mode_id))
+       if (new_mode && !drm_mode_equal(new_mode, &crtc->mode)) {
+               DRM_DEBUG("modes are different\n");
+               drm_mode_debug_printmodeline(dev, &crtc->mode);
+               drm_mode_debug_printmodeline(dev, new_mode);
                changed = true;
+       }
 
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
                save_crtcs[count++] = output->crtc;
@@ -1024,6 +1164,10 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
                }
        }
 
+       /* mode_set_base is not a required function */
+       if (flip_or_move && !crtc->funcs->mode_set_base)
+               changed = true;
+
        if (changed) {
                crtc->fb = fb;
                crtc->enabled = (new_mode != NULL);
@@ -1044,12 +1188,86 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
                        crtc->desired_mode = new_mode;
                }
                drm_disable_unused_functions(dev);
+       } else if (flip_or_move) {
+               if (crtc->fb != fb)
+                       crtc->fb = fb;
+               crtc->funcs->mode_set_base(crtc, crtc_info->x, crtc_info->y);
        }
+
        kfree(save_crtcs);
        return 0;
 }
 
 /**
+ * drm_hotplug_stage_two
+ * @dev DRM device
+ * @output hotpluged output
+ *
+ * LOCKING.
+ * Caller must hold mode config lock, function might grab struct lock.
+ *
+ * Stage two of a hotplug.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
+                         bool connected)
+{
+       int has_config = 0;
+
+       dev->mode_config.hotplug_counter++;
+
+       /* We might want to do something more here */
+       if (!connected) {
+               DRM_DEBUG("not connected\n");
+               return 0;
+       }
+
+       if (output->crtc && output->crtc->desired_mode) {
+               DRM_DEBUG("drm thinks that the output already has a config\n");
+               has_config = 1;
+       }
+
+       drm_crtc_probe_output_modes(dev, 2048, 2048);
+
+       if (!has_config)
+               drm_pick_crtcs(dev);
+
+       if (!output->crtc || !output->crtc->desired_mode) {
+               DRM_DEBUG("could not find a desired mode or crtc for output\n");
+               return 1;
+       }
+
+       /* We should really check if there is a fb using this crtc */
+       if (!has_config)
+               dev->driver->fb_probe(dev, output->crtc);
+       else {
+               dev->driver->fb_resize(dev, output->crtc);
+
+               if (!drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0))
+                       DRM_ERROR("failed to set mode after hotplug\n");
+       }
+
+       drm_sysfs_hotplug_event(dev);
+
+       drm_disable_unused_functions(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_hotplug_stage_two);
+
+int drm_mode_hotplug_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_hotplug *arg = data;
+
+       arg->counter = dev->mode_config.hotplug_counter;
+
+       return 0;
+}
+
+/**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
  * @in: drm_display_mode to use
@@ -1062,8 +1280,6 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
  */
 void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in)
 {
-
-       out->id = in->mode_id;
        out->clock = in->clock;
        out->hdisplay = in->hdisplay;
        out->hsync_start = in->hsync_start;
@@ -1077,6 +1293,7 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display
        out->vscan = in->vscan;
        out->vrefresh = in->vrefresh;
        out->flags = in->flags;
+       out->type = in->type;
        strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
        out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
 }
@@ -1107,6 +1324,7 @@ void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modein
        out->vscan = in->vscan;
        out->vrefresh = in->vrefresh;
        out->flags = in->flags;
+       out->type = in->type;
        strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
        out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
 }
@@ -1129,29 +1347,24 @@ void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modein
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_getresources(struct inode *inode, struct file *filp,
-                         unsigned int cmd, unsigned long arg)
+int drm_mode_getresources(struct drm_device *dev,
+                         void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_card_res __user *argp = (void __user *)arg;
-       struct drm_mode_card_res card_res;
+       struct drm_mode_card_res *card_res = data;
        struct list_head *lh;
        struct drm_framebuffer *fb;
        struct drm_output *output;
        struct drm_crtc *crtc;
-       struct drm_mode_modeinfo u_mode;
-       struct drm_display_mode *mode;
        int ret = 0;
-       int mode_count= 0;
        int output_count = 0;
        int crtc_count = 0;
        int fb_count = 0;
        int copied = 0;
-       
-       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+       uint32_t __user *fb_id;
+       uint32_t __user *crtc_id;
+       uint32_t __user *output_id;
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
 
        list_for_each(lh, &dev->mode_config.fb_list)
                fb_count++;
@@ -1159,107 +1372,66 @@ int drm_mode_getresources(struct inode *inode, struct file *filp,
        list_for_each(lh, &dev->mode_config.crtc_list)
                crtc_count++;
 
-       list_for_each_entry(output, &dev->mode_config.output_list,
-                           head) {
+       list_for_each(lh, &dev->mode_config.output_list)
                output_count++;
-               list_for_each(lh, &output->modes)
-                       mode_count++;
-       }
-       list_for_each(lh, &dev->mode_config.usermode_list)
-               mode_count++;
-
-       if (copy_from_user(&card_res, argp, sizeof(card_res))) {
-               ret = -EFAULT;
-               goto out_unlock;
-       }
 
-       if (card_res.count_modes == 0) {
-               DRM_DEBUG("probing modes %dx%d\n", dev->mode_config.max_width, dev->mode_config.max_height);
-               drm_crtc_probe_output_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height);
-               mode_count = 0;
-               list_for_each_entry(output, &dev->mode_config.output_list, head) {
-                       list_for_each(lh, &output->modes)
-                               mode_count++;
-               }
-               list_for_each(lh, &dev->mode_config.usermode_list)
-                       mode_count++;
-       }
+       card_res->max_height = dev->mode_config.max_height;
+       card_res->min_height = dev->mode_config.min_height;
+       card_res->max_width = dev->mode_config.max_width;
+       card_res->min_width = dev->mode_config.min_width;
 
        /* handle this in 4 parts */
        /* FBs */
-       if (card_res.count_fbs >= fb_count) {
+       if (card_res->count_fbs >= fb_count) {
                copied = 0;
+               fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr;
                list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
-                       if (put_user(fb->id, &card_res.fb_id[copied++])) {
+                       if (put_user(fb->id, fb_id + copied)) {
                                ret = -EFAULT;
-                               goto done;
+                               goto out;
                        }
+                       copied++;
                }
        }
-       card_res.count_fbs = fb_count;
+       card_res->count_fbs = fb_count;
 
        /* CRTCs */
-       if (card_res.count_crtcs >= crtc_count) {
+       if (card_res->count_crtcs >= crtc_count) {
                copied = 0;
+               crtc_id = (uint32_t *)(unsigned long)card_res->crtc_id_ptr;
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head){
                        DRM_DEBUG("CRTC ID is %d\n", crtc->id);
-                       if (put_user(crtc->id, &card_res.crtc_id[copied++])) {
+                       if (put_user(crtc->id, crtc_id + copied)) {
                                ret = -EFAULT;
-                               goto done;
+                               goto out;
                        }
+                       copied++;
                }
        }
-       card_res.count_crtcs = crtc_count;
+       card_res->count_crtcs = crtc_count;
 
 
        /* Outputs */
-       if (card_res.count_outputs >= output_count) {
+       if (card_res->count_outputs >= output_count) {
                copied = 0;
+               output_id = (uint32_t *)(unsigned long)card_res->output_id_ptr;
                list_for_each_entry(output, &dev->mode_config.output_list,
                                    head) {
                        DRM_DEBUG("OUTPUT ID is %d\n", output->id);
-                       if (put_user(output->id, &card_res.output_id[copied++])) {
+                       if (put_user(output->id, output_id + copied)) {
                                ret = -EFAULT;
-                               goto done;
+                               goto out;
                        }
+                       copied++;
                }
        }
-       card_res.count_outputs = output_count;
+       card_res->count_outputs = output_count;
        
-       /* Modes */
-       if (card_res.count_modes >= mode_count) {
-               copied = 0;
-               list_for_each_entry(output, &dev->mode_config.output_list,
-                                   head) {
-                       list_for_each_entry(mode, &output->modes, head) {
-                               drm_crtc_convert_to_umode(&u_mode, mode);
-                               if (copy_to_user(&card_res.modes[copied++], &u_mode, sizeof(struct drm_mode_modeinfo))) {
-                                       ret = -EFAULT;
-                                       goto done;
-                               }
-                       }
-               }
-               /* add in user modes */
-               list_for_each_entry(mode, &dev->mode_config.usermode_list, head) {
-                       drm_crtc_convert_to_umode(&u_mode, mode);
-                       if (copy_to_user(&card_res.modes[copied++], &u_mode, sizeof(struct drm_mode_modeinfo))) {
-                               ret = -EFAULT;
-                               goto done;
-                       }
-               }
-       }
-       card_res.count_modes = mode_count;
+       DRM_DEBUG("Counted %d %d\n", card_res->count_crtcs,
+                 card_res->count_outputs);
 
-done:
-       DRM_DEBUG("Counted %d %d %d\n", card_res.count_crtcs,
-                 card_res.count_outputs,
-                 card_res.count_modes);
-       
-       if (copy_to_user(argp, &card_res, sizeof(card_res)))
-               ret = -EFAULT;
-
-out_unlock:
-       spin_unlock(&dev->mode_config.config_lock);
+out:   
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1280,50 +1452,47 @@ out_unlock:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_getcrtc(struct inode *inode, struct file *filp,
-                    unsigned int cmd, unsigned long arg)
+int drm_mode_getcrtc(struct drm_device *dev,
+                    void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_crtc __user *argp = (void __user *)arg;
-       struct drm_mode_crtc crtc_resp;
+       struct drm_mode_crtc *crtc_resp = data;
        struct drm_crtc *crtc;
        struct drm_output *output;
        int ocount;
        int ret = 0;
 
-       if (copy_from_user(&crtc_resp, argp, sizeof(crtc_resp)))
-               return -EFAULT;
-
-       spin_lock(&dev->mode_config.config_lock);
-       crtc = idr_find(&dev->mode_config.crtc_idr, crtc_resp.crtc_id);
-       if (!crtc || (crtc->id != crtc_resp.crtc_id)) {
+       mutex_lock(&dev->mode_config.mutex);
+       crtc = idr_find(&dev->mode_config.crtc_idr, crtc_resp->crtc_id);
+       if (!crtc || (crtc->id != crtc_resp->crtc_id)) {
                ret = -EINVAL;
                goto out;
        }
 
-       crtc_resp.x = crtc->x;
-       crtc_resp.y = crtc->y;
-       crtc_resp.fb_id = 1;
+       crtc_resp->x = crtc->x;
+       crtc_resp->y = crtc->y;
 
-       crtc_resp.outputs = 0;
+       if (crtc->fb)
+               crtc_resp->fb_id = crtc->fb->id;
+       else
+               crtc_resp->fb_id = 0;
+
+       crtc_resp->outputs = 0;
        if (crtc->enabled) {
 
-               crtc_resp.mode = crtc->mode.mode_id;
+               drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
+               crtc_resp->mode_valid = 1;
                ocount = 0;
                list_for_each_entry(output, &dev->mode_config.output_list, head) {
                        if (output->crtc == crtc)
-                               crtc_resp.outputs |= 1 << (ocount++);
+                               crtc_resp->outputs |= 1 << (ocount++);
                }
+               
        } else {
-               crtc_resp.mode = 0;
+               crtc_resp->mode_valid = 0;
        }
 
-       if (copy_to_user(argp, &crtc_resp, sizeof(crtc_resp)))
-               ret = -EFAULT;
-
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1344,76 +1513,99 @@ out:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_getoutput(struct inode *inode, struct file *filp,
-                      unsigned int cmd, unsigned long arg)
+int drm_mode_getoutput(struct drm_device *dev,
+                      void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_get_output __user *argp = (void __user *)arg;
-       struct drm_mode_get_output out_resp;
+       struct drm_mode_get_output *out_resp = data;
        struct drm_output *output;
        struct drm_display_mode *mode;
        int mode_count = 0;
+       int props_count = 0;
        int ret = 0;
        int copied = 0;
        int i;
+       struct drm_mode_modeinfo u_mode;
+       struct drm_mode_modeinfo __user *mode_ptr;
+       uint32_t __user *prop_ptr;
+       uint64_t __user *prop_values;
 
-       if (copy_from_user(&out_resp, argp, sizeof(out_resp)))
-               return -EFAULT; 
+       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
 
-       DRM_DEBUG("output id %d:\n", out_resp.output);
+       DRM_DEBUG("output id %d:\n", out_resp->output);
 
-       spin_lock(&dev->mode_config.config_lock);
-       output= idr_find(&dev->mode_config.crtc_idr, out_resp.output);
-       if (!output || (output->id != out_resp.output)) {
+       mutex_lock(&dev->mode_config.mutex);
+       output= idr_find(&dev->mode_config.crtc_idr, out_resp->output);
+       if (!output || (output->id != out_resp->output)) {
                ret = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        list_for_each_entry(mode, &output->modes, head)
                mode_count++;
        
-       for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++)
-               if (output->user_mode_ids[i] != 0)
-                       mode_count++;
+       for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+               if (output->property_ids[i] != 0) {
+                       props_count++;
+               }
+       }
 
-       strncpy(out_resp.name, output->name, DRM_OUTPUT_NAME_LEN);
-       out_resp.name[DRM_OUTPUT_NAME_LEN-1] = 0;
+       if (out_resp->count_modes == 0) {
+               drm_crtc_probe_single_output_modes(output, dev->mode_config.max_width, dev->mode_config.max_height);
+       }
 
-       out_resp.mm_width = output->mm_width;
-       out_resp.mm_height = output->mm_height;
-       out_resp.subpixel = output->subpixel_order;
-       out_resp.connection = output->status;
+       out_resp->output_type = output->output_type;
+       out_resp->output_type_id = output->output_type_id;
+       out_resp->mm_width = output->mm_width;
+       out_resp->mm_height = output->mm_height;
+       out_resp->subpixel = output->subpixel_order;
+       out_resp->connection = output->status;
        if (output->crtc)
-               out_resp.crtc = output->crtc->id;
+               out_resp->crtc = output->crtc->id;
        else
-               out_resp.crtc = 0;
+               out_resp->crtc = 0;
+
+       out_resp->crtcs = output->possible_crtcs;
+       out_resp->clones = output->possible_clones;
 
-       if ((out_resp.count_modes >= mode_count) && mode_count) {
+       if ((out_resp->count_modes >= mode_count) && mode_count) {
                copied = 0;
+               mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
                list_for_each_entry(mode, &output->modes, head) {
-                       if (put_user(mode->mode_id, &out_resp.modes[copied++])) {
+                       drm_crtc_convert_to_umode(&u_mode, mode);
+                       if (copy_to_user(mode_ptr + copied,
+                                        &u_mode, sizeof(u_mode))) {
                                ret = -EFAULT;
-                               goto done;
+                               goto out;
                        }
+                       copied++;
+                       
                }
-               for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) {
-                       if (output->user_mode_ids[i] != 0)
-                               if (put_user(output->user_mode_ids[i], &out_resp.modes[copied++])) {
+       }
+       out_resp->count_modes = mode_count;
+
+       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);
+               for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+                       if (output->property_ids[i] != 0) {
+                               if (put_user(output->property_ids[i], prop_ptr + copied)) {
                                        ret = -EFAULT;
-                                       goto done;
+                                       goto out;
                                }
+
+                               if (put_user(output->property_values[i], prop_values + copied)) {
+                                       ret = -EFAULT;
+                                       goto out;
+                               }
+                               copied++;
+                       }
                }
-                       
        }
-       out_resp.count_modes = mode_count;
+       out_resp->count_props = props_count;
 
-done:
-       if (copy_to_user(argp, &out_resp, sizeof(out_resp)))
-               ret = -EFAULT;
-
-out_unlock:
-       spin_unlock(&dev->mode_config.config_lock);
+out:
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1434,85 +1626,75 @@ out_unlock:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_setcrtc(struct inode *inode, struct file *filp,
-                    unsigned int cmd, unsigned long arg)
+int drm_mode_setcrtc(struct drm_device *dev,
+                    void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_crtc __user *argp = (void __user *)arg;
-       struct drm_mode_crtc crtc_req;
-       struct drm_crtc *crtc;
+       struct drm_mode_crtc *crtc_req = data;
+       struct drm_crtc *crtc, *crtcfb;
        struct drm_output **output_set = NULL, *output;
-       struct drm_display_mode *mode;
        struct drm_framebuffer *fb = NULL;
+       struct drm_display_mode *mode = NULL;
+       uint32_t __user *set_outputs_ptr;
        int ret = 0;
        int i;
 
-       if (copy_from_user(&crtc_req, argp, sizeof(crtc_req)))
-               return -EFAULT;
-
-       spin_lock(&dev->mode_config.config_lock);
-       crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req.crtc_id);
-       if (!crtc || (crtc->id != crtc_req.crtc_id)) {
-               DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req.crtc_id);
+       mutex_lock(&dev->mode_config.mutex);
+       crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req->crtc_id);
+       if (!crtc || (crtc->id != crtc_req->crtc_id)) {
+               DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id);
                ret = -EINVAL;
                goto out;
        }
 
-       if (crtc_req.mode) {
-               /* if we have a mode we need a framebuffer */
-               if (crtc_req.fb_id) {
-                       fb = idr_find(&dev->mode_config.crtc_idr, crtc_req.fb_id);
-                       if (!fb || (fb->id != crtc_req.fb_id)) {
-                               DRM_DEBUG("Unknown FB ID%d\n", crtc_req.fb_id);
+       if (crtc_req->mode_valid) {
+               /* If we have a mode we need a framebuffer. */
+               /* If we pass -1, set the mode with the currently bound fb */
+               if (crtc_req->fb_id == -1) {
+                       list_for_each_entry(crtcfb, &dev->mode_config.crtc_list, head) {
+                               if (crtcfb == crtc) {
+                                       DRM_DEBUG("Using current fb for setmode\n");
+                                       fb = crtc->fb;          
+                               }
+                       }
+               } else {
+                       fb = idr_find(&dev->mode_config.crtc_idr, crtc_req->fb_id);
+                       if (!fb || (fb->id != crtc_req->fb_id)) {
+                               DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id);
                                ret = -EINVAL;
                                goto out;
                        }
                }
-               mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode);
-               if (!mode || (mode->mode_id != crtc_req.mode)) {
-                       struct drm_output *output;
-                       
-                       list_for_each_entry(output, 
-                                           &dev->mode_config.output_list,
-                                           head) {
-                               list_for_each_entry(mode, &output->modes,
-                                                   head) {
-                                       drm_mode_debug_printmodeline(dev, 
-                                                                    mode);
-                               }
-                       }
 
-                       DRM_DEBUG("Unknown mode id %d, %p\n", crtc_req.mode, mode);
-                       ret = -EINVAL;
-                       goto out;
-               }
-       } else
-               mode = NULL;
+               mode = drm_mode_create(dev);
+               drm_crtc_convert_umode(mode, &crtc_req->mode);
+               drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+       }
 
-       if (crtc_req.count_outputs == 0 && mode) {
+       if (crtc_req->count_outputs == 0 && mode) {
                DRM_DEBUG("Count outputs is 0 but mode set\n");
                ret = -EINVAL;
                goto out;
        }
 
-       if (crtc_req.count_outputs > 0 && !mode && !fb) {
-               DRM_DEBUG("Count outputs is %d but no mode or fb set\n", crtc_req.count_outputs);
+       if (crtc_req->count_outputs > 0 && !mode && !fb) {
+               DRM_DEBUG("Count outputs is %d but no mode or fb set\n", crtc_req->count_outputs);
                ret = -EINVAL;
                goto out;
        }
 
-       if (crtc_req.count_outputs > 0) {
+       if (crtc_req->count_outputs > 0) {
                u32 out_id;
-               output_set = kmalloc(crtc_req.count_outputs *
+               /* Maybe we should check that count_outputs is a sensible value. */
+               output_set = kmalloc(crtc_req->count_outputs *
                                     sizeof(struct drm_output *), GFP_KERNEL);
                if (!output_set) {
                        ret = -ENOMEM;
                        goto out;
                }
 
-               for (i = 0; i < crtc_req.count_outputs; i++) {
-                       if (get_user(out_id, &crtc_req.set_outputs[i])) {
+               for (i = 0; i < crtc_req->count_outputs; i++) {
+                       set_outputs_ptr = (uint32_t *)(unsigned long)crtc_req->set_outputs_ptr;
+                       if (get_user(out_id, &set_outputs_ptr[i])) {
                                ret = -EFAULT;
                                goto out;
                        }
@@ -1527,11 +1709,69 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp,
                        output_set[i] = output;
                }
        }
-               
-       ret = drm_crtc_set_config(crtc, &crtc_req, mode, output_set, fb);
 
+       ret = drm_crtc_set_config(crtc, crtc_req, mode, output_set, fb);
+
+out:
+       kfree(output_set);
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+                       void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_cursor *req = data;
+       struct drm_crtc *crtc;
+       struct drm_buffer_object *bo = NULL; /* must be set */
+       int ret = 0;
+
+       DRM_DEBUG("\n");
+
+       if (!req->flags) {
+               DRM_ERROR("no operation set\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&dev->mode_config.mutex);
+       crtc = idr_find(&dev->mode_config.crtc_idr, req->crtc);
+       if (!crtc || (crtc->id != req->crtc)) {
+               DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_BO) {
+               /* Turn of the cursor if handle is 0 */
+               if (req->handle)
+                       ret = drm_get_buffer_object(dev, &bo, req->handle);
+
+               if (ret) {
+                       DRM_ERROR("invalid buffer id\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (crtc->funcs->cursor_set) {
+                       ret = crtc->funcs->cursor_set(crtc, bo, req->width, req->height);
+               } else {
+                       DRM_ERROR("crtc does not support cursor\n");
+                       ret = -EFAULT;
+                       goto out;
+               }
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_MOVE) {
+               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;
+               }
+       }
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1552,35 +1792,29 @@ out:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_addfb(struct inode *inode, struct file *filp,
-                  unsigned int cmd, unsigned long arg)
+int drm_mode_addfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
 {
-       struct drm_file *priv = filp->private_data;
-       struct drm_device *dev = priv->head->dev;
-       struct drm_mode_fb_cmd __user *argp = (void __user *)arg;
-       struct drm_mode_fb_cmd r;
+       struct drm_mode_fb_cmd *r = data;
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_framebuffer *fb;
        struct drm_buffer_object *bo;
-       struct drm_crtc *crtc;
        int ret = 0;
 
-       if (copy_from_user(&r, argp, sizeof(r)))
-               return -EFAULT;
-
-       if ((config->min_width > r.width) || (r.width > config->max_width)) {
+       if ((config->min_width > r->width) || (r->width > config->max_width)) {
                DRM_ERROR("mode new framebuffer width not within limits\n");
                return -EINVAL;
        }
-       if ((config->min_height > r.height) || (r.height > config->max_height)) {
+       if ((config->min_height > r->height) || (r->height > config->max_height)) {
                DRM_ERROR("mode new framebuffer height not within limits\n");
                return -EINVAL;
        }
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
        /* TODO check limits are okay */
-       ret = drm_get_buffer_object(dev, &bo, r.handle);
+       ret = drm_get_buffer_object(dev, &bo, r->handle);
        if (ret || !bo) {
+               DRM_ERROR("BO handle not valid\n");
                ret = -EINVAL;
                goto out;
        }
@@ -1590,35 +1824,24 @@ int drm_mode_addfb(struct inode *inode, struct file *filp,
 
        fb = drm_framebuffer_create(dev);
        if (!fb) {
+               DRM_ERROR("could not create framebuffer\n");
                ret = -EINVAL;
                goto out;
        }
 
-       fb->width = r.width;
-       fb->height = r.height;
-       fb->pitch = r.pitch;
-       fb->bits_per_pixel = r.bpp;
-       fb->depth = r.depth;
-       fb->offset = bo->offset;
+       fb->width = r->width;
+       fb->height = r->height;
+       fb->pitch = r->pitch;
+       fb->bits_per_pixel = r->bpp;
+       fb->depth = r->depth;
        fb->bo = bo;
 
-       r.buffer_id = fb->id;
-
-       list_add(&fb->filp_head, &priv->fbs);
+       r->buffer_id = fb->id;
 
-       if (copy_to_user(argp, &r, sizeof(r))) {
-               ret = -EFAULT;
-               goto out;
-       }
-               
-       /* FIXME: bind the fb to the right crtc */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               crtc->fb = fb;
-               dev->driver->fb_probe(dev, crtc);
-       }
+       list_add(&fb->filp_head, &file_priv->fbs);
 
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1639,35 +1862,45 @@ out:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_rmfb(struct inode *inode, struct file *filp,
-                  unsigned int cmd, unsigned long arg)
+int drm_mode_rmfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
        struct drm_framebuffer *fb = 0;
-       uint32_t id = arg;
+       struct drm_framebuffer *fbl = 0;
+       uint32_t *id = data;
        int ret = 0;
+       int found = 0;
 
-       spin_lock(&dev->mode_config.config_lock);
-       fb = idr_find(&dev->mode_config.crtc_idr, id);
+       mutex_lock(&dev->mode_config.mutex);
+       fb = idr_find(&dev->mode_config.crtc_idr, *id);
        /* TODO check that we realy get a framebuffer back. */
-       if (!fb || (id != fb->id)) {
+       if (!fb || (*id != fb->id)) {
                DRM_ERROR("mode invalid framebuffer id\n");
                ret = -EINVAL;
                goto out;
        }
 
-       dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb));
+       list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+               if (fb == fbl)
+                       found = 1;
+
+       if (!found) {
+               DRM_ERROR("tried to remove a fb that we didn't own\n");
+               ret = -EINVAL;
+               goto out;
+       }
 
-       /* TODO check if we own the buffer */
        /* TODO release all crtc connected to the framebuffer */
-       /* bind the fb to the crtc for now */
        /* TODO unhock the destructor from the buffer object */
 
+       if (fb->bo->type == drm_bo_type_kernel)
+               DRM_ERROR("the bo type should not be of kernel type\n");
+
+       list_del(&fb->filp_head);
        drm_framebuffer_destroy(fb);
 
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1688,39 +1921,30 @@ out:
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_getfb(struct inode *inode, struct file *filp,
-                  unsigned int cmd, unsigned long arg)
+int drm_mode_getfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;    
-       struct drm_mode_fb_cmd __user *argp = (void __user *)arg;
-       struct drm_mode_fb_cmd r;
+       struct drm_mode_fb_cmd *r = data;
        struct drm_framebuffer *fb;
        int ret = 0;
 
-       if (copy_from_user(&r, argp, sizeof(r)))
-               return -EFAULT;
-
-       spin_lock(&dev->mode_config.config_lock);
-       fb = idr_find(&dev->mode_config.crtc_idr, r.buffer_id);
-       if (!fb || (r.buffer_id != fb->id)) {
+       mutex_lock(&dev->mode_config.mutex);
+       fb = idr_find(&dev->mode_config.crtc_idr, r->buffer_id);
+       if (!fb || (r->buffer_id != fb->id)) {
                DRM_ERROR("invalid framebuffer id\n");
                ret = -EINVAL;
                goto out;
        }
 
-       r.height = fb->height;
-       r.width = fb->width;
-       r.depth = fb->depth;
-       r.bpp = fb->bits_per_pixel;
-       r.handle = fb->bo->base.hash.key;
-       r.pitch = fb->pitch;
-
-       if (copy_to_user(argp, &r, sizeof(r)))
-               ret = -EFAULT;
+       r->height = fb->height;
+       r->width = fb->width;
+       r->depth = fb->depth;
+       r->bpp = fb->bits_per_pixel;
+       r->handle = fb->bo->base.hash.key;
+       r->pitch = fb->pitch;
 
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -1740,235 +1964,538 @@ out:
  */
 void drm_fb_release(struct file *filp)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
+       struct drm_file *priv = filp->private_data;
+       struct drm_device *dev = priv->minor->dev;
        struct drm_framebuffer *fb, *tfb;
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
                list_del(&fb->filp_head);
-               dev->driver->fb_remove(dev, drm_crtc_from_fb(dev, fb));
+               if (fb->bo->type == drm_bo_type_kernel)
+                       DRM_ERROR("the bo type should not be of kernel_type, the kernel will probably explode, why Dave\n");
+
                drm_framebuffer_destroy(fb);
        }
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
 }
 
-/**
- * drm_fb_newmode - adds a user defined mode
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * Adds a user specified mode to the kernel.
- *
- * Called by the user via ioctl.
+/*
  *
- * RETURNS:
- * writes new mode id into arg.
- * Zero on success, errno on failure.
  */
-int drm_mode_addmode(struct inode *inode, struct file *filp,
-                    unsigned int cmd, unsigned long arg)
+
+static int drm_mode_attachmode(struct drm_device *dev,
+                              struct drm_output *output,
+                              struct drm_display_mode *mode)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_modeinfo __user *argp = (void __user *)arg;
-       struct drm_mode_modeinfo new_mode;
-       struct drm_display_mode *user_mode;
        int ret = 0;
 
-       if (copy_from_user(&new_mode, argp, sizeof(new_mode)))
-               return -EFAULT;
+       list_add_tail(&mode->head, &output->user_modes);
+       return ret;
+}
 
-       spin_lock(&dev->mode_config.config_lock);
-       user_mode = drm_mode_create(dev);
-       if (!user_mode) {
-               ret = -ENOMEM;
-               goto out;
+int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
+                            struct drm_display_mode *mode)
+{
+       struct drm_output *output;
+       int ret = 0;
+       struct drm_display_mode *dup_mode;
+       int need_dup = 0;
+       list_for_each_entry(output, &dev->mode_config.output_list, head) {
+               if (output->crtc == crtc) {
+                       if (need_dup)
+                               dup_mode = drm_mode_duplicate(dev, mode);
+                       else
+                               dup_mode = mode;
+                       ret = drm_mode_attachmode(dev, output, dup_mode); 
+                       if (ret)
+                               return ret;
+                       need_dup = 1;
+               }
        }
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_attachmode_crtc);
 
-       drm_crtc_convert_umode(user_mode, &new_mode);
-       user_mode->type |= DRM_MODE_TYPE_USERDEF;
-
-       user_mode->output_count = 0;
+static int drm_mode_detachmode(struct drm_device *dev,
+                              struct drm_output *output,
+                              struct drm_display_mode *mode)
+{
+       int found = 0;
+       int ret = 0;
+       struct drm_display_mode *match_mode, *t;
 
-       list_add(&user_mode->head, &dev->mode_config.usermode_list);
+       list_for_each_entry_safe(match_mode, t, &output->user_modes, head) {
+               if (drm_mode_equal(match_mode, mode)) {
+                       list_del(&match_mode->head);
+                       drm_mode_destroy(dev, match_mode);
+                       found = 1;
+                       break;
+               }
+       }
 
-       new_mode.id = user_mode->mode_id;
-       if (copy_to_user(argp, &new_mode, sizeof(new_mode)))
-               ret = -EFAULT;
+       if (!found)
+               ret = -EINVAL;
 
-out:
-       spin_unlock(&dev->mode_config.config_lock);
        return ret;
 }
 
+int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode)
+{
+       struct drm_output *output;
+
+       list_for_each_entry(output, &dev->mode_config.output_list, head) {
+               drm_mode_detachmode(dev, output, mode);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_detachmode_crtc);
+
 /**
- * drm_fb_rmmode - removes a user defined mode
+ * drm_fb_attachmode - Attach a user mode to an output
  * @inode: inode from the ioctl
  * @filp: file * from the ioctl
  * @cmd: cmd from ioctl
  * @arg: arg from ioctl
  *
- * Remove the user defined mode specified by the user.
- *
- * Called by the user via ioctl
+ * This attaches a user specified mode to an output.
+ * Called by the user via ioctl.
  *
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_rmmode(struct inode *inode, struct file *filp,
-                   unsigned int cmd, unsigned long arg)
+int drm_mode_attachmode_ioctl(struct drm_device *dev,
+                             void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       uint32_t id = arg;
-       struct drm_display_mode *mode, *t;
-       int ret = -EINVAL;
+       struct drm_mode_mode_cmd *mode_cmd = data;
+       struct drm_output *output;
+       struct drm_display_mode *mode;
+       struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+       int ret = 0;
 
-       spin_lock(&dev->mode_config.config_lock);       
-       mode = idr_find(&dev->mode_config.crtc_idr, id);
-       if (!mode || (id != mode->mode_id)) {
-               ret = -EINVAL;
-               goto out;
-       }
+       mutex_lock(&dev->mode_config.mutex);
 
-       if (!(mode->type & DRM_MODE_TYPE_USERDEF)) {
+       output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id);
+       if (!output || (output->id != mode_cmd->output_id)) {
                ret = -EINVAL;
                goto out;
        }
 
-       if (mode->output_count) {
-               ret = -EINVAL;
+       mode = drm_mode_create(dev);
+       if (!mode) {
+               ret = -ENOMEM;
                goto out;
        }
+       
+       drm_crtc_convert_umode(mode, umode);
 
-       list_for_each_entry(t, &dev->mode_config.usermode_list, head) {
-               if (t == mode) {
-                       list_del(&mode->head);
-                       drm_mode_destroy(dev, mode);
-                       ret = 0;
-                       break;
-               }
-       }
-
+       ret = drm_mode_attachmode(dev, output, mode);
 out:
-       spin_unlock(&dev->mode_config.config_lock);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
+
 /**
- * drm_fb_attachmode - Attach a user mode to an output
+ * drm_fb_detachmode - Detach a user specified mode from an output
  * @inode: inode from the ioctl
  * @filp: file * from the ioctl
  * @cmd: cmd from ioctl
  * @arg: arg from ioctl
  *
- * This attaches a user specified mode to an output.
  * Called by the user via ioctl.
  *
  * RETURNS:
  * Zero on success, errno on failure.
  */
-int drm_mode_attachmode(struct inode *inode, struct file *filp,
-                       unsigned int cmd, unsigned long arg)
+int drm_mode_detachmode_ioctl(struct drm_device *dev,
+                             void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_mode_cmd __user *argp = (void __user *)arg;
-       struct drm_mode_mode_cmd mode_cmd;
+       struct drm_mode_mode_cmd *mode_cmd = data;
        struct drm_output *output;
-       struct drm_display_mode *mode;
-       int i, ret = 0;
-
-       if (copy_from_user(&mode_cmd, argp, sizeof(mode_cmd)))
-               return -EFAULT;
+       struct drm_display_mode mode;
+       struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+       int ret = 0;
 
-       spin_lock(&dev->mode_config.config_lock);
+       mutex_lock(&dev->mode_config.mutex);
 
-       mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd.mode_id);
-       if (!mode || (mode->mode_id != mode_cmd.mode_id)) {
+       output = idr_find(&dev->mode_config.crtc_idr, mode_cmd->output_id);
+       if (!output || (output->id != mode_cmd->output_id)) {
                ret = -EINVAL;
                goto out;
        }
+       
+       drm_crtc_convert_umode(&mode, umode);
+       ret = drm_mode_detachmode(dev, output, &mode);
+out:          
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
 
-       output = idr_find(&dev->mode_config.crtc_idr, mode_cmd.output_id);
-       if (!output || (output->id != mode_cmd.output_id)) {
-               ret = -EINVAL;
-               goto out;
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+                                        const char *name, int num_values)
+{
+       struct drm_property *property = NULL;
+
+       property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
+       if (!property)
+               return NULL;
+
+       if (num_values) {
+               property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
+               if (!property->values)
+                       goto fail;
+       }
+
+       property->id = drm_idr_get(dev, property);
+       property->flags = flags;
+       property->num_values = num_values;
+       INIT_LIST_HEAD(&property->enum_blob_list);
+
+       if (name)
+               strncpy(property->name, name, DRM_PROP_NAME_LEN);
+
+       list_add_tail(&property->head, &dev->mode_config.property_list);
+       return property;
+fail:
+       kfree(property);
+       return NULL;
+}
+EXPORT_SYMBOL(drm_property_create);
+
+int drm_property_add_enum(struct drm_property *property, int index,
+                         uint64_t value, const char *name)
+{
+       struct drm_property_enum *prop_enum;
+
+       if (!(property->flags & DRM_MODE_PROP_ENUM))
+               return -EINVAL;
+
+       if (!list_empty(&property->enum_blob_list)) {
+               list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+                       if (prop_enum->value == value) {
+                               strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 
+                               prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+                               return 0;
+                       }
+               }
        }
 
-       for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) {
-               if (output->user_mode_ids[i] == 0) {
-                       output->user_mode_ids[i] = mode->mode_id;
-                       mode->output_count++;
+       prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
+       if (!prop_enum)
+               return -ENOMEM;
+
+       strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 
+       prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+       prop_enum->value = value;
+
+       property->values[index] = value;
+       list_add_tail(&prop_enum->head, &property->enum_blob_list);
+       return 0;
+}
+EXPORT_SYMBOL(drm_property_add_enum);
+
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
+{
+       struct drm_property_enum *prop_enum, *pt;
+
+       list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
+               list_del(&prop_enum->head);
+               kfree(prop_enum);
+       }
+
+       if (property->num_values)
+               kfree(property->values);
+       drm_idr_put(dev, property->id);
+       list_del(&property->head);
+       kfree(property);        
+}
+EXPORT_SYMBOL(drm_property_destroy);
+
+int drm_output_attach_property(struct drm_output *output,
+                              struct drm_property *property, uint64_t init_val)
+{
+       int i;
+
+       for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+               if (output->property_ids[i] == 0) {
+                       output->property_ids[i] = property->id;
+                       output->property_values[i] = init_val;
                        break;
                }
        }
 
-       if (i == DRM_OUTPUT_MAX_UMODES)
-               ret = -ENOSPC;
+       if (i == DRM_OUTPUT_MAX_PROPERTY)
+               return -EINVAL;
+       return 0;
+}
+EXPORT_SYMBOL(drm_output_attach_property);
 
-out:
-       spin_unlock(&dev->mode_config.config_lock);
-       return ret;
+int drm_output_property_set_value(struct drm_output *output,
+                                 struct drm_property *property, uint64_t value)
+{
+       int i;
+
+       for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+               if (output->property_ids[i] == property->id) {
+                       output->property_values[i] = value;
+                       break;
+               }
+       }
+
+       if (i == DRM_OUTPUT_MAX_PROPERTY)
+               return -EINVAL;
+       return 0;
 }
+EXPORT_SYMBOL(drm_output_property_set_value);
 
+int drm_output_property_get_value(struct drm_output *output,
+                                 struct drm_property *property, uint64_t *val)
+{
+       int i;
 
-/**
- * drm_fb_detachmode - Detach a user specified mode from an output
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * Called by the user via ioctl.
- *
- * RETURNS:
- * Zero on success, errno on failure.
- */
-int drm_mode_detachmode(struct inode *inode, struct file *filp,
-                       unsigned int cmd, unsigned long arg)
+       for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+               if (output->property_ids[i] == property->id) {
+                       *val = output->property_values[i];
+                       break;
+               }
+       }
+
+       if (i == DRM_OUTPUT_MAX_PROPERTY)
+               return -EINVAL;
+       return 0;
+}
+EXPORT_SYMBOL(drm_output_property_get_value);
+
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+                              void *data, struct drm_file *file_priv)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       struct drm_mode_mode_cmd __user *argp = (void __user *)arg;
-       struct drm_mode_mode_cmd mode_cmd;
-       struct drm_output *output;
-       struct drm_display_mode *mode;
-       int i, found = 0, ret = 0;
+       struct drm_mode_get_property *out_resp = data;
+       struct drm_property *property;
+       int enum_count = 0;
+       int blob_count = 0;
+       int value_count = 0;
+       int ret = 0, i;
+       int copied;
+       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;
+       uint64_t __user *values_ptr;
+       uint32_t __user *blob_length_ptr;
+
+       mutex_lock(&dev->mode_config.mutex);
+       property = idr_find(&dev->mode_config.crtc_idr, out_resp->prop_id);
+       if (!property || (property->id != out_resp->prop_id)) {
+               ret = -EINVAL;
+               goto done;
+       }
 
-       if (copy_from_user(&mode_cmd, argp, sizeof(mode_cmd)))
-               return -EFAULT;
+       if (property->flags & DRM_MODE_PROP_ENUM) {
+               list_for_each_entry(prop_enum, &property->enum_blob_list, head)
+                       enum_count++;
+       } else if (property->flags & DRM_MODE_PROP_BLOB) {
+               list_for_each_entry(prop_blob, &property->enum_blob_list, head)
+                       blob_count++;
+       }
 
-       spin_lock(&dev->mode_config.config_lock);
+       value_count = property->num_values;
 
-       mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd.mode_id);
-       if (!mode || (mode->mode_id != mode_cmd.mode_id)) {
+       strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
+       out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+       out_resp->flags = property->flags;
+
+       if ((out_resp->count_values >= value_count) && value_count) {
+               values_ptr = (uint64_t *)(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;
+                               goto done;
+                       }
+               }
+       }
+       out_resp->count_values = value_count;
+
+       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;
+                       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))) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+                               
+                               if (copy_to_user(&enum_ptr[copied].name,
+                                                &prop_enum->name, DRM_PROP_NAME_LEN)) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+                               copied++;
+                       }
+               }
+               out_resp->count_enum_blobs = enum_count;
+       }
+
+       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;
+                       
+                       list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
+                               if (put_user(prop_blob->id, blob_id_ptr + copied)) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+                               
+                               if (put_user(prop_blob->length, blob_length_ptr + copied)) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+                               
+                               copied++;
+                       }
+               }
+               out_resp->count_enum_blobs = enum_count;
+       }
+done:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
+                                                         void *data)
+{
+       struct drm_property_blob *blob;
+
+       if (!length || !data)
+               return NULL;
+
+       blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+       if (!blob)
+               return NULL;
+
+       blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+       blob->length = length;
+
+       memcpy(blob->data, data, length);
+
+       blob->id = drm_idr_get(dev, blob);
+       
+       list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
+       return blob;
+}
+
+static void drm_property_destroy_blob(struct drm_device *dev,
+                              struct drm_property_blob *blob)
+{
+       drm_idr_put(dev, blob->id);
+       list_del(&blob->head);
+       kfree(blob);
+}
+
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_get_blob *out_resp = data;
+       struct drm_property_blob *blob;
+       int ret = 0;
+       void *blob_ptr;
+
+       mutex_lock(&dev->mode_config.mutex);
+       
+       blob = idr_find(&dev->mode_config.crtc_idr, out_resp->blob_id);
+       if (!blob || (blob->id != out_resp->blob_id)) {
                ret = -EINVAL;
+               goto done;
+       }
+
+       if (out_resp->length == blob->length) {
+               blob_ptr = (void *)(unsigned long)out_resp->data;
+               if (copy_to_user(blob_ptr, blob->data, blob->length)){
+                       ret = -EFAULT;
+                       goto done;
+               }
+       }
+       out_resp->length = blob->length;
+
+done:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+int drm_mode_output_update_edid_property(struct drm_output *output, struct edid *edid)
+{
+       struct drm_device *dev = output->dev;
+       int ret = 0;
+       if (output->edid_blob_ptr)
+               drm_property_destroy_blob(dev, output->edid_blob_ptr);
+
+       output->edid_blob_ptr = drm_property_create_blob(output->dev, 128, edid);
+       
+       ret = drm_output_property_set_value(output, dev->mode_config.edid_property, output->edid_blob_ptr->id);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_output_update_edid_property);
+
+int drm_mode_output_property_set_ioctl(struct drm_device *dev,
+                                      void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_output_set_property *out_resp = data;
+       struct drm_property *property;
+       struct drm_output *output;
+       int ret = -EINVAL;
+       int i;
+
+       mutex_lock(&dev->mode_config.mutex);
+       output = idr_find(&dev->mode_config.crtc_idr, out_resp->output_id);
+       if (!output || (output->id != out_resp->output_id)) {
                goto out;
        }
 
-       output = idr_find(&dev->mode_config.crtc_idr, mode_cmd.output_id);
-       if (!output || (output->id != mode_cmd.output_id)) {
-               ret = -EINVAL;
+       for (i = 0; i < DRM_OUTPUT_MAX_PROPERTY; i++) {
+               if (output->property_ids[i] == out_resp->prop_id)
+                       break;
+       }
+
+       if (i == DRM_OUTPUT_MAX_PROPERTY) {
+               goto out;
+       }
+       
+       property = idr_find(&dev->mode_config.crtc_idr, out_resp->prop_id);
+       if (!property || (property->id != out_resp->prop_id)) {
                goto out;
        }
 
+       if (property->flags & DRM_MODE_PROP_IMMUTABLE)
+               goto out;
 
-       for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) {
-               if (output->user_mode_ids[i] == mode->mode_id) {
-                       output->user_mode_ids[i] = 0;
-                       mode->output_count--;
-                       found = 1;
+       if (property->flags & DRM_MODE_PROP_RANGE) {
+               if (out_resp->value < property->values[0])
+                       goto out;
+
+               if (out_resp->value > property->values[1])
+                       goto out;
+       } else {
+               int found = 0;
+               for (i = 0; i < property->num_values; i++) {
+                       if (property->values[i] == out_resp->value) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found) {
+                       goto out;
                }
        }
 
-       if (!found)
-               ret = -EINVAL;
+       if (output->funcs->set_property)
+               ret = output->funcs->set_property(output, property, out_resp->value);
 
-out:          
-       spin_unlock(&dev->mode_config.config_lock);
+out:
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
+