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 at gmail.com>
Cc: stable at kernel.org
---
 drivers/gpu/drm/radeon/radeon_combios.c    |   21 ++++++++++++++----
 drivers/gpu/drm/radeon/radeon_connectors.c |   30 ++++++++++++++++++++++++++-
 drivers/gpu/drm/radeon/radeon_mode.h       |    1 +
 3 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/radeon/radeon_combios.c 
b/drivers/gpu/drm/radeon/radeon_combios.c
index 6467b0a..8caf546 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -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;
 }

diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c 
b/drivers/gpu/drm/radeon/radeon_connectors.c
index 3f3c9aa..a186673 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -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);
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h 
b/drivers/gpu/drm/radeon/radeon_mode.h
index c74bab8..96285fc 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -263,6 +263,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;
-- 
1.7.1.1

Reply via email to