Commit 3c537889 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: add support for hardcoded edids in rom (v2)

Some servers hardcode an edid in rom so that they will
work properly with KVMs.  This is a port of the relevant
code from the ddx.

[airlied: reworked to validate edid at boot stage - and
remove special quirk, if there is a valid EDID in the BIOS rom
we'll just try and use it.]
Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 2739d49c
...@@ -60,8 +60,7 @@ ...@@ -60,8 +60,7 @@
#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
/* use +hsync +vsync for detailed mode */ /* use +hsync +vsync for detailed mode */
#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
/* define the number of Extension EDID block */
#define MAX_EDID_EXT_NUM 4
#define LEVEL_DMT 0 #define LEVEL_DMT 0
#define LEVEL_GTF 1 #define LEVEL_GTF 1
...@@ -114,14 +113,14 @@ static const u8 edid_header[] = { ...@@ -114,14 +113,14 @@ static const u8 edid_header[] = {
}; };
/** /**
* edid_is_valid - sanity check EDID data * drm_edid_is_valid - sanity check EDID data
* @edid: EDID data * @edid: EDID data
* *
* Sanity check the EDID block by looking at the header, the version number * Sanity check the EDID block by looking at the header, the version number
* and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
* valid. * valid.
*/ */
static bool edid_is_valid(struct edid *edid) bool drm_edid_is_valid(struct edid *edid)
{ {
int i, score = 0; int i, score = 0;
u8 csum = 0; u8 csum = 0;
...@@ -163,6 +162,7 @@ static bool edid_is_valid(struct edid *edid) ...@@ -163,6 +162,7 @@ static bool edid_is_valid(struct edid *edid)
} }
return 0; return 0;
} }
EXPORT_SYMBOL(drm_edid_is_valid);
/** /**
* edid_vendor - match a string against EDID's obfuscated vendor field * edid_vendor - match a string against EDID's obfuscated vendor field
...@@ -1069,8 +1069,8 @@ static int add_detailed_info_eedid(struct drm_connector *connector, ...@@ -1069,8 +1069,8 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
} }
/* Chose real EDID extension number */ /* Chose real EDID extension number */
edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
MAX_EDID_EXT_NUM : edid->extensions; DRM_MAX_EDID_EXT_NUM : edid->extensions;
/* Find CEA extension */ /* Find CEA extension */
for (i = 0; i < edid_ext_num; i++) { for (i = 0; i < edid_ext_num; i++) {
...@@ -1152,7 +1152,7 @@ static int drm_ddc_read_edid(struct drm_connector *connector, ...@@ -1152,7 +1152,7 @@ static int drm_ddc_read_edid(struct drm_connector *connector,
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if (drm_do_probe_ddc_edid(adapter, buf, len)) if (drm_do_probe_ddc_edid(adapter, buf, len))
return -1; return -1;
if (edid_is_valid((struct edid *)buf)) if (drm_edid_is_valid((struct edid *)buf))
return 0; return 0;
} }
...@@ -1177,7 +1177,7 @@ struct edid *drm_get_edid(struct drm_connector *connector, ...@@ -1177,7 +1177,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
int ret; int ret;
struct edid *edid; struct edid *edid;
edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1), edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
GFP_KERNEL); GFP_KERNEL);
if (edid == NULL) { if (edid == NULL) {
dev_warn(&connector->dev->pdev->dev, dev_warn(&connector->dev->pdev->dev,
...@@ -1195,14 +1195,14 @@ struct edid *drm_get_edid(struct drm_connector *connector, ...@@ -1195,14 +1195,14 @@ struct edid *drm_get_edid(struct drm_connector *connector,
if (edid->extensions != 0) { if (edid->extensions != 0) {
int edid_ext_num = edid->extensions; int edid_ext_num = edid->extensions;
if (edid_ext_num > MAX_EDID_EXT_NUM) { if (edid_ext_num > DRM_MAX_EDID_EXT_NUM) {
dev_warn(&connector->dev->pdev->dev, dev_warn(&connector->dev->pdev->dev,
"The number of extension(%d) is " "The number of extension(%d) is "
"over max (%d), actually read number (%d)\n", "over max (%d), actually read number (%d)\n",
edid_ext_num, MAX_EDID_EXT_NUM, edid_ext_num, DRM_MAX_EDID_EXT_NUM,
MAX_EDID_EXT_NUM); DRM_MAX_EDID_EXT_NUM);
/* Reset EDID extension number to be read */ /* Reset EDID extension number to be read */
edid_ext_num = MAX_EDID_EXT_NUM; edid_ext_num = DRM_MAX_EDID_EXT_NUM;
} }
/* Read EDID including extensions too */ /* Read EDID including extensions too */
ret = drm_ddc_read_edid(connector, adapter, (char *)edid, ret = drm_ddc_read_edid(connector, adapter, (char *)edid,
...@@ -1245,8 +1245,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid) ...@@ -1245,8 +1245,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid)
goto end; goto end;
/* Chose real EDID extension number */ /* Chose real EDID extension number */
edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
MAX_EDID_EXT_NUM : edid->extensions; DRM_MAX_EDID_EXT_NUM : edid->extensions;
/* Find CEA extension */ /* Find CEA extension */
for (i = 0; i < edid_ext_num; i++) { for (i = 0; i < edid_ext_num; i++) {
...@@ -1303,7 +1303,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) ...@@ -1303,7 +1303,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
if (edid == NULL) { if (edid == NULL) {
return 0; return 0;
} }
if (!edid_is_valid(edid)) { if (!drm_edid_is_valid(edid)) {
dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
drm_get_connector_name(connector)); drm_get_connector_name(connector));
return 0; return 0;
......
...@@ -443,6 +443,39 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, ...@@ -443,6 +443,39 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
} }
bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
{
int edid_info;
struct edid *edid;
edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE);
if (!edid_info)
return false;
edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
GFP_KERNEL);
if (edid == NULL)
return false;
memcpy((unsigned char *)edid,
(unsigned char *)(rdev->bios + edid_info), EDID_LENGTH);
if (!drm_edid_is_valid(edid)) {
kfree(edid);
return false;
}
rdev->mode_info.bios_hardcoded_edid = edid;
return true;
}
struct edid *
radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
{
if (rdev->mode_info.bios_hardcoded_edid)
return rdev->mode_info.bios_hardcoded_edid;
return NULL;
}
static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev, static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
int ddc_line) int ddc_line)
{ {
......
...@@ -352,6 +352,8 @@ static bool radeon_setup_enc_conn(struct drm_device *dev) ...@@ -352,6 +352,8 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
{ {
struct drm_device *dev = radeon_connector->base.dev;
struct radeon_device *rdev = dev->dev_private;
int ret = 0; int ret = 0;
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
...@@ -366,7 +368,9 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) ...@@ -366,7 +368,9 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
if (!radeon_connector->edid) { if (!radeon_connector->edid) {
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
} }
/* some servers provide a hardcoded edid in rom for KVMs */
if (!radeon_connector->edid)
radeon_connector->edid = radeon_combios_get_hardcoded_edid(rdev);
if (radeon_connector->edid) { if (radeon_connector->edid) {
drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
...@@ -829,6 +833,12 @@ int radeon_modeset_init(struct radeon_device *rdev) ...@@ -829,6 +833,12 @@ int radeon_modeset_init(struct radeon_device *rdev)
return ret; return ret;
} }
/* check combios for a valid hardcoded EDID - Sun servers */
if (!rdev->is_atom_bios) {
/* check for hardcoded EDID in BIOS */
radeon_combios_check_hardcoded_edid(rdev);
}
if (rdev->flags & RADEON_SINGLE_CRTC) if (rdev->flags & RADEON_SINGLE_CRTC)
num_crtc = 1; num_crtc = 1;
...@@ -850,6 +860,8 @@ int radeon_modeset_init(struct radeon_device *rdev) ...@@ -850,6 +860,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
void radeon_modeset_fini(struct radeon_device *rdev) void radeon_modeset_fini(struct radeon_device *rdev)
{ {
kfree(rdev->mode_info.bios_hardcoded_edid);
if (rdev->mode_info.mode_config_initialized) { if (rdev->mode_info.mode_config_initialized) {
radeon_hpd_fini(rdev); radeon_hpd_fini(rdev);
drm_mode_config_cleanup(rdev->ddev); drm_mode_config_cleanup(rdev->ddev);
......
...@@ -207,7 +207,8 @@ struct radeon_mode_info { ...@@ -207,7 +207,8 @@ struct radeon_mode_info {
struct drm_property *tv_std_property; struct drm_property *tv_std_property;
/* legacy TMDS PLL detect */ /* legacy TMDS PLL detect */
struct drm_property *tmds_pll_property; struct drm_property *tmds_pll_property;
/* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid;
}; };
#define MAX_H_CODE_TIMING_LEN 32 #define MAX_H_CODE_TIMING_LEN 32
...@@ -479,6 +480,9 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc, ...@@ -479,6 +480,9 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
extern int radeon_crtc_cursor_move(struct drm_crtc *crtc, extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
int x, int y); int x, int y);
extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
extern struct edid *
radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
extern bool radeon_atom_get_clock_info(struct drm_device *dev); extern bool radeon_atom_get_clock_info(struct drm_device *dev);
extern bool radeon_combios_get_clock_info(struct drm_device *dev); extern bool radeon_combios_get_clock_info(struct drm_device *dev);
extern struct radeon_encoder_atom_dig * extern struct radeon_encoder_atom_dig *
......
...@@ -801,4 +801,6 @@ extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, ...@@ -801,4 +801,6 @@ extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev,
bool interlaced, int margins); bool interlaced, int margins);
extern int drm_add_modes_noedid(struct drm_connector *connector, extern int drm_add_modes_noedid(struct drm_connector *connector,
int hdisplay, int vdisplay); int hdisplay, int vdisplay);
extern bool drm_edid_is_valid(struct edid *edid);
#endif /* __DRM_CRTC_H__ */ #endif /* __DRM_CRTC_H__ */
...@@ -201,4 +201,7 @@ struct edid { ...@@ -201,4 +201,7 @@ struct edid {
#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
/* define the number of Extension EDID block */
#define DRM_MAX_EDID_EXT_NUM 4
#endif /* __DRM_EDID_H__ */ #endif /* __DRM_EDID_H__ */
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