diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2011-03-23 04:10:10 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-03-24 06:24:32 -0400 |
commit | fafcf94e2b5732d1e13b440291c53115d2b172e9 (patch) | |
tree | 349e6eccfdd57b619aa8e7acca5468a689f03f58 /drivers | |
parent | 2d370f502ac872dc9b4cd05f9922b260e2874ec1 (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_combios.c | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 30 | ||||
-rw-r--r-- | 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 cf7c8d5b4ec2..cf602e2d0718 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, | |||
448 | 448 | ||
449 | bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) | 449 | bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) |
450 | { | 450 | { |
451 | int edid_info; | 451 | int edid_info, size; |
452 | struct edid *edid; | 452 | struct edid *edid; |
453 | unsigned char *raw; | 453 | unsigned char *raw; |
454 | edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE); | 454 | 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 | return false; | 456 | return false; |
457 | 457 | ||
458 | raw = rdev->bios + edid_info; | 458 | raw = rdev->bios + edid_info; |
459 | edid = kmalloc(EDID_LENGTH * (raw[0x7e] + 1), GFP_KERNEL); | 459 | size = EDID_LENGTH * (raw[0x7e] + 1); |
460 | edid = kmalloc(size, GFP_KERNEL); | ||
460 | if (edid == NULL) | 461 | if (edid == NULL) |
461 | return false; | 462 | return false; |
462 | 463 | ||
463 | memcpy((unsigned char *)edid, raw, EDID_LENGTH * (raw[0x7e] + 1)); | 464 | memcpy((unsigned char *)edid, raw, size); |
464 | 465 | ||
465 | if (!drm_edid_is_valid(edid)) { | 466 | if (!drm_edid_is_valid(edid)) { |
466 | kfree(edid); | 467 | kfree(edid); |
@@ -468,6 +469,7 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) | |||
468 | } | 469 | } |
469 | 470 | ||
470 | rdev->mode_info.bios_hardcoded_edid = edid; | 471 | rdev->mode_info.bios_hardcoded_edid = edid; |
472 | rdev->mode_info.bios_hardcoded_edid_size = size; | ||
471 | return true; | 473 | return true; |
472 | } | 474 | } |
473 | 475 | ||
@@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) | |||
475 | struct edid * | 477 | struct edid * |
476 | radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) | 478 | radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) |
477 | { | 479 | { |
478 | if (rdev->mode_info.bios_hardcoded_edid) | 480 | struct edid *edid; |
479 | return rdev->mode_info.bios_hardcoded_edid; | 481 | |
482 | if (rdev->mode_info.bios_hardcoded_edid) { | ||
483 | edid = kmalloc(rdev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL); | ||
484 | if (edid) { | ||
485 | memcpy((unsigned char *)edid, | ||
486 | (unsigned char *)rdev->mode_info.bios_hardcoded_edid, | ||
487 | rdev->mode_info.bios_hardcoded_edid_size); | ||
488 | return edid; | ||
489 | } | ||
490 | } | ||
480 | return NULL; | 491 | return NULL; |
481 | } | 492 | } |
482 | 493 | ||
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 3f3c9aac46cc..a1866738a657 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, | |||
629 | static enum drm_connector_status | 629 | static enum drm_connector_status |
630 | radeon_vga_detect(struct drm_connector *connector, bool force) | 630 | radeon_vga_detect(struct drm_connector *connector, bool force) |
631 | { | 631 | { |
632 | struct drm_device *dev = connector->dev; | ||
633 | struct radeon_device *rdev = dev->dev_private; | ||
632 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | 634 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
633 | struct drm_encoder *encoder; | 635 | struct drm_encoder *encoder; |
634 | struct drm_encoder_helper_funcs *encoder_funcs; | 636 | struct drm_encoder_helper_funcs *encoder_funcs; |
@@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force) | |||
679 | 681 | ||
680 | if (ret == connector_status_connected) | 682 | if (ret == connector_status_connected) |
681 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); | 683 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); |
684 | |||
685 | /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the | ||
686 | * vbios to deal with KVMs. If we have one and are not able to detect a monitor | ||
687 | * by other means, assume the CRT is connected and use that EDID. | ||
688 | */ | ||
689 | if ((!rdev->is_atom_bios) && | ||
690 | (ret == connector_status_disconnected) && | ||
691 | rdev->mode_info.bios_hardcoded_edid_size) { | ||
692 | ret = connector_status_connected; | ||
693 | } | ||
694 | |||
682 | radeon_connector_update_scratch_regs(connector, ret); | 695 | radeon_connector_update_scratch_regs(connector, ret); |
683 | return ret; | 696 | return ret; |
684 | } | 697 | } |
@@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) | |||
790 | static enum drm_connector_status | 803 | static enum drm_connector_status |
791 | radeon_dvi_detect(struct drm_connector *connector, bool force) | 804 | radeon_dvi_detect(struct drm_connector *connector, bool force) |
792 | { | 805 | { |
806 | struct drm_device *dev = connector->dev; | ||
807 | struct radeon_device *rdev = dev->dev_private; | ||
793 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | 808 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
794 | struct drm_encoder *encoder = NULL; | 809 | struct drm_encoder *encoder = NULL; |
795 | struct drm_encoder_helper_funcs *encoder_funcs; | 810 | struct drm_encoder_helper_funcs *encoder_funcs; |
@@ -829,8 +844,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) | |||
829 | * you don't really know what's connected to which port as both are digital. | 844 | * you don't really know what's connected to which port as both are digital. |
830 | */ | 845 | */ |
831 | if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { | 846 | if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { |
832 | struct drm_device *dev = connector->dev; | ||
833 | struct radeon_device *rdev = dev->dev_private; | ||
834 | struct drm_connector *list_connector; | 847 | struct drm_connector *list_connector; |
835 | struct radeon_connector *list_radeon_connector; | 848 | struct radeon_connector *list_radeon_connector; |
836 | list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { | 849 | 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 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); | 908 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); |
896 | } | 909 | } |
897 | 910 | ||
911 | /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the | ||
912 | * vbios to deal with KVMs. If we have one and are not able to detect a monitor | ||
913 | * by other means, assume the DFP is connected and use that EDID. In most | ||
914 | * cases the DVI port is actually a virtual KVM port connected to the service | ||
915 | * processor. | ||
916 | */ | ||
917 | if ((!rdev->is_atom_bios) && | ||
918 | (ret == connector_status_disconnected) && | ||
919 | rdev->mode_info.bios_hardcoded_edid_size) { | ||
920 | radeon_connector->use_digital = true; | ||
921 | ret = connector_status_connected; | ||
922 | } | ||
923 | |||
898 | out: | 924 | out: |
899 | /* updated in get modes as well since we need to know if it's analog or digital */ | 925 | /* updated in get modes as well since we need to know if it's analog or digital */ |
900 | radeon_connector_update_scratch_regs(connector, ret); | 926 | 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 5067d18d0009..1ae6846de827 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h | |||
@@ -239,6 +239,7 @@ struct radeon_mode_info { | |||
239 | struct drm_property *underscan_vborder_property; | 239 | struct drm_property *underscan_vborder_property; |
240 | /* hardcoded DFP edid from BIOS */ | 240 | /* hardcoded DFP edid from BIOS */ |
241 | struct edid *bios_hardcoded_edid; | 241 | struct edid *bios_hardcoded_edid; |
242 | int bios_hardcoded_edid_size; | ||
242 | 243 | ||
243 | /* pointer to fbdev info structure */ | 244 | /* pointer to fbdev info structure */ |
244 | struct radeon_fbdev *rfbdev; | 245 | struct radeon_fbdev *rfbdev; |