Commit fafcf94e authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: fix hardcoded EDID handling

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: default avatarAlex Deucher <alexdeucher@gmail.com>
Cc: stable@kernel.org
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 2d370f50
...@@ -448,7 +448,7 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, ...@@ -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) bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
{ {
int edid_info; int edid_info, size;
struct edid *edid; struct edid *edid;
unsigned char *raw; unsigned char *raw;
edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE); 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) ...@@ -456,11 +456,12 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
return false; return false;
raw = rdev->bios + edid_info; 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) if (edid == NULL)
return false; return false;
memcpy((unsigned char *)edid, raw, EDID_LENGTH * (raw[0x7e] + 1)); memcpy((unsigned char *)edid, raw, size);
if (!drm_edid_is_valid(edid)) { if (!drm_edid_is_valid(edid)) {
kfree(edid); kfree(edid);
...@@ -468,6 +469,7 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) ...@@ -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 = edid;
rdev->mode_info.bios_hardcoded_edid_size = size;
return true; return true;
} }
...@@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) ...@@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
struct edid * struct edid *
radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) radeon_bios_get_hardcoded_edid(struct radeon_device *rdev)
{ {
if (rdev->mode_info.bios_hardcoded_edid) struct edid *edid;
return rdev->mode_info.bios_hardcoded_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; return NULL;
} }
......
...@@ -629,6 +629,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector, ...@@ -629,6 +629,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector,
static enum drm_connector_status static enum drm_connector_status
radeon_vga_detect(struct drm_connector *connector, bool force) 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 radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_encoder_helper_funcs *encoder_funcs; struct drm_encoder_helper_funcs *encoder_funcs;
...@@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force) ...@@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
if (ret == connector_status_connected) if (ret == connector_status_connected)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); 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); radeon_connector_update_scratch_regs(connector, ret);
return ret; return ret;
} }
...@@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) ...@@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector)
static enum drm_connector_status static enum drm_connector_status
radeon_dvi_detect(struct drm_connector *connector, bool force) 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 radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder = NULL; struct drm_encoder *encoder = NULL;
struct drm_encoder_helper_funcs *encoder_funcs; struct drm_encoder_helper_funcs *encoder_funcs;
...@@ -829,8 +844,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ...@@ -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. * you don't really know what's connected to which port as both are digital.
*/ */
if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { 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 drm_connector *list_connector;
struct radeon_connector *list_radeon_connector; struct radeon_connector *list_radeon_connector;
list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { 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) ...@@ -895,6 +908,19 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); 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: out:
/* updated in get modes as well since we need to know if it's analog or digital */ /* updated in get modes as well since we need to know if it's analog or digital */
radeon_connector_update_scratch_regs(connector, ret); radeon_connector_update_scratch_regs(connector, ret);
......
...@@ -239,6 +239,7 @@ struct radeon_mode_info { ...@@ -239,6 +239,7 @@ struct radeon_mode_info {
struct drm_property *underscan_vborder_property; struct drm_property *underscan_vborder_property;
/* hardcoded DFP edid from BIOS */ /* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid; struct edid *bios_hardcoded_edid;
int bios_hardcoded_edid_size;
/* pointer to fbdev info structure */ /* pointer to fbdev info structure */
struct radeon_fbdev *rfbdev; struct radeon_fbdev *rfbdev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment