Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -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;
}

+28 −2
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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;