Fixup sysfs output registration
[platform/upstream/libdrm.git] / linux-core / drm_crtc.c
index cad7cd8..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
@@ -121,13 +189,6 @@ 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) {
-               mutex_unlock(&dev->mode_config.mutex);
-               DRM_ERROR("Attempt to add multiple framebuffers failed\n");
-               return NULL;
-       }
-
        fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
        if (!fb)
                return NULL;
@@ -280,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
@@ -373,16 +441,15 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
        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);
 
@@ -397,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.
@@ -408,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;
        }
 
@@ -439,7 +511,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                if (output->crtc != crtc)
                        continue;
                
-               DRM_INFO("%s: set mode %s\n", output->name, mode->name);
+               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);
        }
@@ -462,18 +534,17 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
        
        /* 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);
        
@@ -506,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
@@ -558,7 +630,7 @@ EXPORT_SYMBOL(drm_mode_remove);
  */
 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;
 
@@ -569,10 +641,10 @@ struct drm_output *drm_output_create(struct drm_device *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? */
@@ -583,6 +655,10 @@ struct drm_output *drm_output_create(struct drm_device *dev,
        list_add_tail(&output->head, &dev->mode_config.output_list);
        dev->mode_config.num_output++;
 
+       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;
@@ -614,6 +690,9 @@ void drm_output_destroy(struct drm_output *output)
        list_for_each_entry_safe(mode, t, &output->modes, head)
                drm_mode_remove(output, mode);
 
+       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);
@@ -622,35 +701,6 @@ void drm_output_destroy(struct drm_output *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
@@ -695,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
@@ -711,8 +824,17 @@ void drm_mode_config_init(struct drm_device *dev)
        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);
 
@@ -773,6 +895,7 @@ static void drm_pick_crtcs (struct drm_device *dev)
        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;
@@ -793,17 +916,23 @@ static void drm_pick_crtcs (struct drm_device *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->type & DRM_MODE_TYPE_PREFERRED)
+                       if (des_mode->type & DRM_MODE_TYPE_PREFERRED) {
+                               found = 1;
                                break;
+                       }
                }
 
                /* No preferred mode, let's just select the first available */
-               if (!des_mode || !(des_mode->type & DRM_MODE_TYPE_PREFERRED)) {
+               if (!found) {
+                       des_mode = NULL;
                        list_for_each_entry(des_mode, &output->modes, head) {
-                               if (des_mode)
-                                       break;
+                               break;
                        }
                }
 
@@ -839,7 +968,8 @@ static void drm_pick_crtcs (struct drm_device *dev)
                                        list_for_each_entry(modes_equal, &output_equal->modes, head) {
                                                if (drm_mode_equal (modes, modes_equal)) {
                                                        if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) {
-                                                               printk("Cloning %s (0x%lx) to %s (0x%lx)\n",output->name,output->possible_clones,output_equal->name,output_equal->possible_clones);
+                                                               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;
                                                        }
@@ -858,12 +988,12 @@ clone:
                        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
@@ -943,16 +1073,19 @@ 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(mode, mt, &dev->mode_config.usermode_list, head) {
-               drm_mode_destroy(dev, mode);
+       list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) {
+               drm_property_destroy(dev, property);
        }
-               
+
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
+               /* there should only be bo of kernel type left */
                if (fb->bo->type != drm_bo_type_kernel)
                        drm_framebuffer_destroy(fb);
                else
@@ -988,22 +1121,30 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
        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;
@@ -1023,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);
@@ -1043,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
@@ -1061,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;
@@ -1076,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;
 }
@@ -1106,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;
 }
@@ -1136,16 +1355,14 @@ int drm_mode_getresources(struct drm_device *dev,
        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;
 
        mutex_lock(&dev->mode_config.mutex);
 
@@ -1155,33 +1372,25 @@ int drm_mode_getresources(struct drm_device *dev,
        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 (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) {
                copied = 0;
+               fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr;
                list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
-                       card_res->fb_id[copied++] = fb->id;
+                       if (put_user(fb->id, fb_id + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
                }
        }
        card_res->count_fbs = fb_count;
@@ -1189,9 +1398,14 @@ int drm_mode_getresources(struct drm_device *dev,
        /* CRTCs */
        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);
-                       card_res->crtc_id[copied++] = crtc->id;
+                       if (put_user(crtc->id, crtc_id + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
                }
        }
        card_res->count_crtcs = crtc_count;
@@ -1200,36 +1414,23 @@ int drm_mode_getresources(struct drm_device *dev,
        /* Outputs */
        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);
-                       card_res->output_id[copied++] = output->id;
+                       if (put_user(output->id, output_id + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
                }
        }
        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);
-                               card_res->modes[copied++] = u_mode;
-                       }
-               }
-               /* add in user modes */
-               list_for_each_entry(mode, &dev->mode_config.usermode_list, head) {
-                       drm_crtc_convert_to_umode(&u_mode, mode);
-                       card_res->modes[copied++] = u_mode;
-               }
-       }
-       card_res->count_modes = mode_count;
+       DRM_DEBUG("Counted %d %d\n", card_res->count_crtcs,
+                 card_res->count_outputs);
 
-       DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs,
-                 card_res->count_outputs,
-                 card_res->count_modes);
-       
+out:   
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
@@ -1269,19 +1470,25 @@ int drm_mode_getcrtc(struct drm_device *dev,
 
        crtc_resp->x = crtc->x;
        crtc_resp->y = crtc->y;
-       crtc_resp->fb_id = 1;
+
+       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++);
                }
+               
        } else {
-               crtc_resp->mode = 0;
+               crtc_resp->mode_valid = 0;
        }
 
 out:
@@ -1313,9 +1520,16 @@ int drm_mode_getoutput(struct drm_device *dev,
        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;
+
+       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
 
        DRM_DEBUG("output id %d:\n", out_resp->output);
 
@@ -1323,19 +1537,24 @@ int drm_mode_getoutput(struct drm_device *dev,
        output= idr_find(&dev->mode_config.crtc_idr, out_resp->output);
        if (!output || (output->id != out_resp->output)) {
                ret = -EINVAL;
-               goto done;
+               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->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;
@@ -1345,20 +1564,47 @@ int drm_mode_getoutput(struct drm_device *dev,
        else
                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) {
                copied = 0;
+               mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
                list_for_each_entry(mode, &output->modes, head) {
-                       out_resp->modes[copied++] = mode->mode_id;
-               }
-               for (i = 0; i < DRM_OUTPUT_MAX_UMODES; i++) {
-                       if (output->user_mode_ids[i] != 0)
-                               out_resp->modes[copied++] = output->user_mode_ids[i];
-               }
+                       drm_crtc_convert_to_umode(&u_mode, mode);
+                       if (copy_to_user(mode_ptr + copied,
+                                        &u_mode, sizeof(u_mode))) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
                        
+               }
        }
        out_resp->count_modes = mode_count;
 
-done:
+       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 out;
+                               }
+
+                               if (put_user(output->property_values[i], prop_values + copied)) {
+                                       ret = -EFAULT;
+                                       goto out;
+                               }
+                               copied++;
+                       }
+               }
+       }
+       out_resp->count_props = props_count;
+
+out:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
@@ -1384,10 +1630,11 @@ int drm_mode_setcrtc(struct drm_device *dev,
                     void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc *crtc_req = data;
-       struct drm_crtc *crtc;
+       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;
 
@@ -1399,9 +1646,17 @@ int drm_mode_setcrtc(struct drm_device *dev,
                goto out;
        }
 
-       if (crtc_req->mode) {
-               /* if we have a mode we need a framebuffer */
-               if (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);
@@ -1409,26 +1664,11 @@ int drm_mode_setcrtc(struct drm_device *dev,
                                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) {
                DRM_DEBUG("Count outputs is 0 but mode set\n");
@@ -1444,6 +1684,7 @@ int drm_mode_setcrtc(struct drm_device *dev,
 
        if (crtc_req->count_outputs > 0) {
                u32 out_id;
+               /* 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) {
@@ -1452,7 +1693,8 @@ int drm_mode_setcrtc(struct drm_device *dev,
                }
 
                for (i = 0; i < crtc_req->count_outputs; i++) {
-                       if (get_user(out_id, &crtc_req->set_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;
                        }
@@ -1467,39 +1709,96 @@ int drm_mode_setcrtc(struct drm_device *dev,
                        output_set[i] = output;
                }
        }
-               
+
        ret = drm_crtc_set_config(crtc, crtc_req, mode, output_set, fb);
 
 out:
+       kfree(output_set);
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
-/**
- * drm_mode_addfb - add an FB to the graphics configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
- *
- * Add a new FB to the specified CRTC, given a user request.
- *
- * Called by the user via ioctl.
- *
- * RETURNS:
- * Zero on success, errno on failure.
- */
-int drm_mode_addfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv)
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+                       void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_fb_cmd *r = data;
-       struct drm_mode_config *config = &dev->mode_config;
+       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:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+/**
+ * drm_mode_addfb - add an FB to the graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Add a new FB to the specified CRTC, given a user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_fb_cmd *r = data;
+       struct drm_mode_config *config = &dev->mode_config;
        struct drm_framebuffer *fb;
        struct drm_buffer_object *bo;
-       struct drm_crtc *crtc;
        int ret = 0;
 
        if ((config->min_width > r->width) || (r->width > config->max_width)) {
@@ -1515,6 +1814,7 @@ int drm_mode_addfb(struct drm_device *dev,
        /* TODO check limits are okay */
        ret = drm_get_buffer_object(dev, &bo, r->handle);
        if (ret || !bo) {
+               DRM_ERROR("BO handle not valid\n");
                ret = -EINVAL;
                goto out;
        }
@@ -1524,6 +1824,7 @@ int drm_mode_addfb(struct drm_device *dev,
 
        fb = drm_framebuffer_create(dev);
        if (!fb) {
+               DRM_ERROR("could not create framebuffer\n");
                ret = -EINVAL;
                goto out;
        }
@@ -1533,19 +1834,12 @@ int drm_mode_addfb(struct drm_device *dev,
        fb->pitch = r->pitch;
        fb->bits_per_pixel = r->bpp;
        fb->depth = r->depth;
-       fb->offset = bo->offset;
        fb->bo = bo;
 
        r->buffer_id = fb->id;
 
        list_add(&fb->filp_head, &file_priv->fbs);
 
-       /* 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);
-       }
-
 out:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
@@ -1572,8 +1866,10 @@ int drm_mode_rmfb(struct drm_device *dev,
                   void *data, struct drm_file *file_priv)
 {
        struct drm_framebuffer *fb = 0;
+       struct drm_framebuffer *fbl = 0;
        uint32_t *id = data;
        int ret = 0;
+       int found = 0;
 
        mutex_lock(&dev->mode_config.mutex);
        fb = idr_find(&dev->mode_config.crtc_idr, *id);
@@ -1584,15 +1880,24 @@ int drm_mode_rmfb(struct drm_device *dev,
                goto out;
        }
 
-       /* TODO check if we own the buffer */
+       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 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_framebuffer_destroy(fb);
-       else
-               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\n");
+
+       list_del(&fb->filp_head);
+       drm_framebuffer_destroy(fb);
 
 out:
        mutex_unlock(&dev->mode_config.mutex);
@@ -1660,214 +1965,537 @@ out:
 void drm_fb_release(struct file *filp)
 {
        struct drm_file *priv = filp->private_data;
-       struct drm_device *dev = priv->head->dev;
+       struct drm_device *dev = priv->minor->dev;
        struct drm_framebuffer *fb, *tfb;
 
        mutex_lock(&dev->mode_config.mutex);
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
                list_del(&fb->filp_head);
-               if (fb->bo->type != drm_bo_type_kernel)
-                       drm_framebuffer_destroy(fb);
-               else
-                       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);
        }
        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 drm_device *dev,
-                    void *data, struct drm_file *file_priv)
+
+static int drm_mode_attachmode(struct drm_device *dev,
+                              struct drm_output *output,
+                              struct drm_display_mode *mode)
 {
-       struct drm_mode_modeinfo *new_mode = data;
-       struct drm_display_mode *user_mode;
        int ret = 0;
 
-       mutex_lock(&dev->mode_config.mutex);
-       user_mode = drm_mode_create(dev);
-       if (!user_mode) {
-               ret = -ENOMEM;
-               goto out;
-       }
+       list_add_tail(&mode->head, &output->user_modes);
+       return ret;
+}
 
-       drm_crtc_convert_umode(user_mode, new_mode);
-       user_mode->type |= DRM_MODE_TYPE_USERDEF;
+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);
 
-       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 (!found)
+               ret = -EINVAL;
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
        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 drm_device *dev,
-                   void *data, struct drm_file *file_priv)
+int drm_mode_attachmode_ioctl(struct drm_device *dev,
+                             void *data, struct drm_file *file_priv)
 {
-       uint32_t *id = data;
-       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;
 
-       mutex_lock(&dev->mode_config.mutex);    
-       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:
        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 drm_device *dev,
-                       void *data, struct drm_file *file_priv)
+int drm_mode_detachmode_ioctl(struct drm_device *dev,
+                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_mode_cmd *mode_cmd = data;
        struct drm_output *output;
-       struct drm_display_mode *mode;
-       int i, ret = 0;
+       struct drm_display_mode mode;
+       struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+       int ret = 0;
 
        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)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
        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;
+}
+
+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);
 
-       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++;
+       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;
+                       }
+               }
+       }
+
+       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);
+
+int drm_output_property_set_value(struct drm_output *output,
+                                 struct drm_property *property, uint64_t value)
+{
+       int i;
 
-out:
+       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;
+
+       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)
+{
+       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 (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++;
+       }
+
+       value_count = property->num_values;
+
+       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;
 
-/**
- * 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 drm_device *dev,
-                       void *data, struct drm_file *file_priv)
+       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)
 {
-       struct drm_mode_mode_cmd *mode_cmd = data;
-       struct drm_output *output;
-       struct drm_display_mode *mode;
-       int i, found = 0, ret = 0;
+       drm_idr_put(dev, blob->id);
+       list_del(&blob->head);
+       kfree(blob);
+}
 
-       mutex_lock(&dev->mode_config.mutex);
+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;
 
-       mode = idr_find(&dev->mode_config.crtc_idr, mode_cmd->mode_id);
-       if (!mode || (mode->mode_id != mode_cmd->mode_id)) {
+       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:          
+out:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
+