From a279fb3ff3f3cfc41530aff1d9ff5620279348f6 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 31 Jul 2013 19:11:45 +0100 Subject: xfree86: Add a xf86MonitorFindHDMIBlock() The HDMI CEA vendor specific block has some interesting information, such as the maximum TMDS dot clock. v2: Don't parse CEA blocks with invalid offsets, remove spurious brackets (Chris Wilson) v3: Fix the looping through the CEA data blocks, it had a typo using the wrong variable coming from the code it was ported from. Replace x << 16 + y << 8 + z by x << 16 | y << 8 | z (Chris Wilson) v4: Remove the stray ';' at the end of "if (*end == 0)". (Dominik Behr on IRC) Reviewed-by: Chris Wilson Signed-off-by: Damien Lespiau --- hw/xfree86/ddc/interpret_edid.c | 91 +++++++++++++++++++++++++++++++++++++++++ hw/xfree86/ddc/xf86DDC.h | 2 + 2 files changed, 93 insertions(+) diff --git a/hw/xfree86/ddc/interpret_edid.c b/hw/xfree86/ddc/interpret_edid.c index 882a6b201..6c531b44e 100644 --- a/hw/xfree86/ddc/interpret_edid.c +++ b/hw/xfree86/ddc/interpret_edid.c @@ -332,6 +332,97 @@ xf86ForEachVideoBlock(xf86MonPtr mon, handle_video_fn fn, void *data) } } +static Bool +cea_db_offsets(Uchar *cea, int *start, int *end) +{ + /* Data block offset in CEA extension block */ + *start = CEA_EXT_MIN_DATA_OFFSET; + *end = cea[2]; + if (*end == 0) + *end = CEA_EXT_MAX_DATA_OFFSET; + if (*end < CEA_EXT_MIN_DATA_OFFSET || *end > CEA_EXT_MAX_DATA_OFFSET) + return FALSE; + return TRUE; +} + +static int +cea_db_len(Uchar *db) +{ + return db[0] & 0x1f; +} + +static int +cea_db_tag(Uchar *db) +{ + return db[0] >> 5; +} + +typedef void (*handle_cea_db_fn) (Uchar *, void *); + +static void +cea_for_each_db(xf86MonPtr mon, handle_cea_db_fn fn, void *data) +{ + int i; + + if (!mon) + return; + + if (!(mon->flags & EDID_COMPLETE_RAWDATA)) + return; + + if (!mon->no_sections) + return; + + if (!mon->rawData) + return; + + for (i = 0; i < mon->no_sections; i++) { + int start, end, offset; + Uchar *ext; + + ext = mon->rawData + EDID1_LEN * (i + 1); + if (ext[EXT_TAG] != CEA_EXT) + continue; + + if (!cea_db_offsets(ext, &start, &end)) + continue; + + for (offset = start; + offset < end && offset + cea_db_len(&ext[offset]) < end; + offset += cea_db_len(&ext[offset]) + 1) + fn(&ext[offset], data); + } +} + +struct find_hdmi_block_data { + struct cea_data_block *hdmi; +}; + +static void find_hdmi_block(Uchar *db, void *data) +{ + struct find_hdmi_block_data *result = data; + int oui; + + if (cea_db_tag(db) != CEA_VENDOR_BLK) + return; + + if (cea_db_len(db) < 5) + return; + + oui = (db[3] << 16) | (db[2] << 8) | db[1]; + if (oui == IEEE_ID_HDMI) + result->hdmi = (struct cea_data_block *)db; +} + +struct cea_data_block *xf86MonitorFindHDMIBlock(xf86MonPtr mon) +{ + struct find_hdmi_block_data result = { NULL }; + + cea_for_each_db(mon, find_hdmi_block, &result); + + return result.hdmi; +} + xf86MonPtr xf86InterpretEEDID(int scrnIndex, Uchar * block) { diff --git a/hw/xfree86/ddc/xf86DDC.h b/hw/xfree86/ddc/xf86DDC.h index bdc7648a9..de8e71831 100644 --- a/hw/xfree86/ddc/xf86DDC.h +++ b/hw/xfree86/ddc/xf86DDC.h @@ -98,4 +98,6 @@ typedef void (*handle_video_fn) (struct cea_video_block *, void *); void xf86ForEachVideoBlock(xf86MonPtr, handle_video_fn, void *); +struct cea_data_block *xf86MonitorFindHDMIBlock(xf86MonPtr mon); + #endif -- cgit v1.2.3 From 95c2287465138ac251bf792f354cee3626e76b44 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 7 Aug 2013 15:16:21 +0100 Subject: xfree86: Refactor xf86MonitorIsHDMI() using xf86MonitorFindHDMIBlock() Reviewed-by: Chris Wilson Signed-off-by: Damien Lespiau --- hw/xfree86/ddc/interpret_edid.c | 46 +---------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/hw/xfree86/ddc/interpret_edid.c b/hw/xfree86/ddc/interpret_edid.c index 6c531b44e..17a8f81c0 100644 --- a/hw/xfree86/ddc/interpret_edid.c +++ b/hw/xfree86/ddc/interpret_edid.c @@ -757,49 +757,5 @@ validate_version(int scrnIndex, struct edid_version *r) Bool xf86MonitorIsHDMI(xf86MonPtr mon) { - int i = 0, version, offset; - char *edid = NULL; - - if (!mon) - return FALSE; - - if (!(mon->flags & EDID_COMPLETE_RAWDATA)) - return FALSE; - - if (!mon->no_sections) - return FALSE; - - edid = (char *) mon->rawData; - if (!edid) - return FALSE; - - /* find the CEA extension block */ - for (i = 1; i <= mon->no_sections; i++) - if (edid[i * 128] == 0x02) - break; - if (i == mon->no_sections + 1) - return FALSE; - edid += (i * 128); - - version = edid[1]; - offset = edid[2]; - if (version < 3 || offset < 4) - return FALSE; - - /* walk the cea data blocks */ - for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) { - char *x = edid + i; - - /* find a vendor specific block */ - if ((x[0] & 0xe0) >> 5 == 0x03) { - int oui = (x[3] << 16) + (x[2] << 8) + x[1]; - - /* find the HDMI vendor OUI */ - if (oui == 0x000c03) - return TRUE; - } - } - - /* guess it's not HDMI after all */ - return FALSE; + return xf86MonitorFindHDMIBlock(mon) != NULL; } -- cgit v1.2.3 From d6c8d7509727060b8e2358b9ed1c0e17b2ec3401 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 31 Jul 2013 19:16:45 +0100 Subject: xfree86: Use the TMDS maximum frequency to prune modes Instead of only relying on the Range section, we can do better on HDMI to find out what is the max dot clock the monitor supports. The HDMI CEA vendor block adds a TMDS max freq we can use. This makes X not prune 4k resolutions on HDMI. v2: Replace X_INFO by X_PROBED in the message that prints the max TMDS frequency (Chris Wilson) Reviewed-by: Chris Wilson Signed-off-by: Damien Lespiau --- hw/xfree86/modes/xf86Crtc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c index a441fd16a..42fdad9e2 100644 --- a/hw/xfree86/modes/xf86Crtc.c +++ b/hw/xfree86/modes/xf86Crtc.c @@ -1676,6 +1676,7 @@ xf86ProbeOutputModes(ScrnInfoPtr scrn, int maxX, int maxY) if (edid_monitor) { struct det_monrec_parameter p; struct disp_features *features = &edid_monitor->features; + struct cea_data_block *hdmi_db; /* if display is not continuous-frequency, don't add default modes */ if (!GTF_SUPPORTED(features->msc)) @@ -1688,6 +1689,16 @@ xf86ProbeOutputModes(ScrnInfoPtr scrn, int maxX, int maxY) p.sync_source = &sync_source; xf86ForEachDetailedBlock(edid_monitor, handle_detailed_monrec, &p); + + /* Look at the CEA HDMI vendor block for the max TMDS freq */ + hdmi_db = xf86MonitorFindHDMIBlock(edid_monitor); + if (hdmi_db && hdmi_db->len >= 7) { + int tmds_freq = hdmi_db->u.vendor.hdmi.max_tmds_clock * 5000; + xf86DrvMsg(scrn->scrnIndex, X_PROBED, + "HDMI max TMDS frequency %dKHz\n", tmds_freq); + if (tmds_freq > max_clock) + max_clock = tmds_freq; + } } if (xf86GetOptValFreq(output->options, OPTION_MIN_CLOCK, -- cgit v1.2.3