Fixup sysfs output registration
[platform/upstream/libdrm.git] / linux-core / drm_crtc.c
index fef2700..f54ceb7 100644 (file)
@@ -38,14 +38,30 @@ struct drm_prop_enum_list {
        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[] = 
-{ { ConnectorVGA, "VGA" },
+{ { ConnectorUnknown, "Unknown" },
+  { ConnectorVGA, "VGA" },
   { ConnectorDVII, "DVI-I" },
   { ConnectorDVID, "DVI-D" },
   { ConnectorDVIA, "DVI-A" },
@@ -75,6 +91,16 @@ char *drm_get_output_name(struct drm_output *output)
        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
@@ -163,12 +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) {
-               DRM_ERROR("Attempt to add multiple framebuffers failed\n");
-               return NULL;
-       }
-
        fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
        if (!fb)
                return NULL;
@@ -421,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);
 
@@ -445,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.
@@ -456,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;
        }
 
@@ -510,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);
        
@@ -726,27 +749,62 @@ 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", 4);
-
+               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", 10);
+                       "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);
+                       "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;
 }
 
@@ -771,7 +829,12 @@ void drm_mode_config_init(struct drm_device *dev)
        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);
 
@@ -832,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;
@@ -852,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;
                        }
                }
 
@@ -899,6 +969,7 @@ static void drm_pick_crtcs (struct drm_device *dev)
                                                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",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;
                                                        }
@@ -1005,6 +1076,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        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);
        }
 
@@ -1013,6 +1085,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        }
 
        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
@@ -1049,21 +1122,29 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
        struct drm_crtc **save_crtcs, *new_crtc;
        bool save_enabled = crtc->enabled;
        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 && !drm_mode_equal(new_mode, &crtc->mode))
+       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;
@@ -1083,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);
@@ -1103,7 +1188,12 @@ 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;
 }
@@ -1114,17 +1204,26 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
  * @output hotpluged output
  *
  * LOCKING.
- * Caller must hold mode config lock, function might grap struct lock.
+ * 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)
+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;
@@ -1140,7 +1239,7 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output)
                return 1;
        }
 
-       /* We should realy check if there is a fb using this crtc */
+       /* We should really check if there is a fb using this crtc */
        if (!has_config)
                dev->driver->fb_probe(dev, output->crtc);
        else {
@@ -1150,12 +1249,24 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output)
                        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
@@ -1519,13 +1630,13 @@ 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_framebuffer *fb = NULL;
        struct drm_display_mode *mode = NULL;
+       uint32_t __user *set_outputs_ptr;
        int ret = 0;
        int i;
-       uint32_t __user *set_outputs_ptr;
 
        mutex_lock(&dev->mode_config.mutex);
        crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req->crtc_id);
@@ -1536,8 +1647,16 @@ int drm_mode_setcrtc(struct drm_device *dev,
        }
 
        if (crtc_req->mode_valid) {
-               /* if we have a mode we need a framebuffer */
-               if (crtc_req->fb_id) {
+               /* 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);
@@ -1548,6 +1667,7 @@ int drm_mode_setcrtc(struct drm_device *dev,
 
                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) {
@@ -1564,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) {
@@ -1592,6 +1713,64 @@ int drm_mode_setcrtc(struct drm_device *dev,
        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:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
@@ -1620,7 +1799,6 @@ int drm_mode_addfb(struct drm_device *dev,
        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)) {
@@ -1636,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;
        }
@@ -1645,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;
        }
@@ -1654,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;
@@ -1693,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);
@@ -1705,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);
@@ -1781,16 +1965,16 @@ 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);
 }
@@ -1952,7 +2136,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
 {
        struct drm_property *property = NULL;
 
-       property = kzalloc(sizeof(struct drm_output), GFP_KERNEL);
+       property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
        if (!property)
                return NULL;
 
@@ -2064,6 +2248,24 @@ int drm_output_property_set_value(struct drm_output *output,
 }
 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)
 {
@@ -2224,7 +2426,7 @@ done:
        return ret;
 }
 
-int drm_mode_output_update_edid_property(struct drm_output *output, unsigned char *edid)
+int drm_mode_output_update_edid_property(struct drm_output *output, struct edid *edid)
 {
        struct drm_device *dev = output->dev;
        int ret = 0;