| 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) |
| |
| Signed-off-by: Damien Lespiau <damien.lespiau at intel.com> |
| --- |
| 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 e6b4d5b..71c605b 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 bdc7648..de8e718 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 a441fd1..42fdad9 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, |
| -- |
| 1.8.3.1 |
| |