From 516a815514072dbd2be4d3e3af99e119c3b892c1 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 31 Jul 2013 19:11:45 +0100 Subject: [PATCH] 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. Signed-off-by: Damien Lespiau --- hw/xfree86/ddc/interpret_edid.c | 88 +++++++++++++++++++++++++++++++++++++++++ hw/xfree86/ddc/xf86DDC.h | 2 + hw/xfree86/modes/xf86Crtc.c | 11 ++++++ 3 files changed, 101 insertions(+) diff --git a/hw/xfree86/ddc/interpret_edid.c b/hw/xfree86/ddc/interpret_edid.c index e6b4d5b..10c1c3a 100644 --- a/hw/xfree86/ddc/interpret_edid.c +++ b/hw/xfree86/ddc/interpret_edid.c @@ -332,6 +332,94 @@ xf86ForEachVideoBlock(xf86MonPtr mon, handle_video_fn fn, void *data) } } +static void +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) + *end = CEA_EXT_MAX_DATA_OFFSET; + if (*end < CEA_EXT_MIN_DATA_OFFSET) + *end = CEA_EXT_MIN_DATA_OFFSET; +} + +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; + + cea_db_offsets(ext, &start, &end); + for (offset = start; + offset < end && offset + cea_db_len(&(ext)[(i)]) < 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 35845e8..9f30019 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_INFO, "HDMI TMDS Freq %d/%d\n", + max_clock, tmds_freq); + if (tmds_freq > max_clock) + max_clock = tmds_freq; + } } if (xf86GetOptValFreq(output->options, OPTION_MIN_CLOCK, -- 1.8.3.1