diff options
author | Keith Packard <keithp@keithp.com> | 2014-01-22 11:32:35 -0800 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2014-01-22 11:32:35 -0800 |
commit | 409e8e29fbe16122ba5a4249256fc56e2e68ea93 (patch) | |
tree | 6f9620a6956ecc6a13c1ada6bda0c0879ee6bc8f | |
parent | 457bc83549e58bb87de96bed02988db3275a7611 (diff) | |
parent | d6c8d7509727060b8e2358b9ed1c0e17b2ec3401 (diff) |
Merge remote-tracking branch 'dlespiau/20131216-4k'
-rw-r--r-- | hw/xfree86/ddc/interpret_edid.c | 137 | ||||
-rw-r--r-- | hw/xfree86/ddc/xf86DDC.h | 2 | ||||
-rw-r--r-- | hw/xfree86/modes/xf86Crtc.c | 11 |
3 files changed, 105 insertions, 45 deletions
diff --git a/hw/xfree86/ddc/interpret_edid.c b/hw/xfree86/ddc/interpret_edid.c index 882a6b201..17a8f81c0 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) { @@ -666,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; } 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 diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c index b2eb72e8e..42db8d79f 100644 --- a/hw/xfree86/modes/xf86Crtc.c +++ b/hw/xfree86/modes/xf86Crtc.c @@ -1673,6 +1673,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)) @@ -1685,6 +1686,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, |