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: Alex Deucher <alexdeucher@gmail.com> Cc: stable@kernel.org Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
parent
2d370f502a
commit
fafcf94e2b
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user