drm/radeon/kms: fix hardcoded EDID handling
authorAlex Deucher <alexdeucher@gmail.com>
Wed, 23 Mar 2011 08:10:10 +0000 (08:10 +0000)
committerDave Airlie <airlied@redhat.com>
Thu, 24 Mar 2011 10:24:32 +0000 (20:24 +1000)
On some servers there is a hardcoded EDID provided
in the vbios so that the driver will always see a
display connected even if something like a KVM
prevents traditional means like DDC or load
detection from working properly.  Also most
server boards with DVI are not actually DVI, but
DVO connected to a virtual KVM service processor.
If we fail to detect a monitor via DDC or load
detection and a hardcoded EDID is available, use
it.

Additionally, when using the hardcoded EDID, use
a copy of it rather than the actual one stored
in the driver as the detect() and get_modes()
functions may free it if DDC is successful.

This fixes the virtual KVM on several internal
servers.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Cc: stable@kernel.org
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/radeon/radeon_combios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_mode.h

index cf7c8d5b4ec24a431a81c776c367a1a54e4f29f3..cf602e2d0718ed21539dafd566405c77b3d9a4ac 100644 (file)
@@ -448,7 +448,7 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
 
 bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
 {
-       int edid_info;
+       int edid_info, size;
        struct edid *edid;
        unsigned char *raw;
        edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE);
@@ -456,11 +456,12 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
                return false;
 
        raw = rdev->bios + edid_info;
-       edid = kmalloc(EDID_LENGTH * (raw[0x7e] + 1), GFP_KERNEL);
+       size = EDID_LENGTH * (raw[0x7e] + 1);
+       edid = kmalloc(size, GFP_KERNEL);
        if (edid == NULL)
                return false;
 
-       memcpy((unsigned char *)edid, raw, EDID_LENGTH * (raw[0x7e] + 1));
+       memcpy((unsigned char *)edid, raw, size);
 
        if (!drm_edid_is_valid(edid)) {
                kfree(edid);
@@ -468,6 +469,7 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
        }
 
        rdev->mode_info.bios_hardcoded_edid = edid;
+       rdev->mode_info.bios_hardcoded_edid_size = size;
        return true;
 }
 
@@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
 struct edid *
 radeon_bios_get_hardcoded_edid(struct radeon_device *rdev)
 {
-       if (rdev->mode_info.bios_hardcoded_edid)
-               return rdev->mode_info.bios_hardcoded_edid;
+       struct edid *edid;
+
+       if (rdev->mode_info.bios_hardcoded_edid) {
+               edid = kmalloc(rdev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL);
+               if (edid) {
+                       memcpy((unsigned char *)edid,
+                              (unsigned char *)rdev->mode_info.bios_hardcoded_edid,
+                              rdev->mode_info.bios_hardcoded_edid_size);
+                       return edid;
+               }
+       }
        return NULL;
 }
 
index 3f3c9aac46ccfd4fb2ff4c58a13ecfbfca4f4501..a1866738a65789ae477a6b7ec5864b05706cdabc 100644 (file)
@@ -629,6 +629,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector,
 static enum drm_connector_status
 radeon_vga_detect(struct drm_connector *connector, bool force)
 {
+       struct drm_device *dev = connector->dev;
+       struct radeon_device *rdev = dev->dev_private;
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct drm_encoder *encoder;
        struct drm_encoder_helper_funcs *encoder_funcs;
@@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
 
        if (ret == connector_status_connected)
                ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true);
+
+       /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the
+        * vbios to deal with KVMs. If we have one and are not able to detect a monitor
+        * by other means, assume the CRT is connected and use that EDID.
+        */
+       if ((!rdev->is_atom_bios) &&
+           (ret == connector_status_disconnected) &&
+           rdev->mode_info.bios_hardcoded_edid_size) {
+               ret = connector_status_connected;
+       }
+
        radeon_connector_update_scratch_regs(connector, ret);
        return ret;
 }
@@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector)
 static enum drm_connector_status
 radeon_dvi_detect(struct drm_connector *connector, bool force)
 {
+       struct drm_device *dev = connector->dev;
+       struct radeon_device *rdev = dev->dev_private;
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct drm_encoder *encoder = NULL;
        struct drm_encoder_helper_funcs *encoder_funcs;
@@ -829,8 +844,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
                         * you don't really know what's connected to which port as both are digital.
                         */
                        if (radeon_connector->shared_ddc && (ret == connector_status_connected)) {
-                               struct drm_device *dev = connector->dev;
-                               struct radeon_device *rdev = dev->dev_private;
                                struct drm_connector *list_connector;
                                struct radeon_connector *list_radeon_connector;
                                list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
@@ -895,6 +908,19 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
                ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true);
        }
 
+       /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the
+        * vbios to deal with KVMs. If we have one and are not able to detect a monitor
+        * by other means, assume the DFP is connected and use that EDID.  In most
+        * cases the DVI port is actually a virtual KVM port connected to the service
+        * processor.
+        */
+       if ((!rdev->is_atom_bios) &&
+           (ret == connector_status_disconnected) &&
+           rdev->mode_info.bios_hardcoded_edid_size) {
+               radeon_connector->use_digital = true;
+               ret = connector_status_connected;
+       }
+
 out:
        /* updated in get modes as well since we need to know if it's analog or digital */
        radeon_connector_update_scratch_regs(connector, ret);
index 5067d18d0009c2bff5c326e90ee7fcb3468bfd81..1ae6846de82739523258f1fe59a44b63edf12cb5 100644 (file)
@@ -239,6 +239,7 @@ struct radeon_mode_info {
        struct drm_property *underscan_vborder_property;
        /* hardcoded DFP edid from BIOS */
        struct edid *bios_hardcoded_edid;
+       int bios_hardcoded_edid_size;
 
        /* pointer to fbdev info structure */
        struct radeon_fbdev *rfbdev;