aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/hw/xfree86/modes
diff options
context:
space:
mode:
authormarha <marha@users.sourceforge.net>2009-11-16 13:46:01 +0000
committermarha <marha@users.sourceforge.net>2009-11-16 13:46:01 +0000
commit578938f1cdd5a06dd6fa28167d575ec980322a5d (patch)
treee31cf77fab7cc6e005b0e726e7951d7eef79550f /xorg-server/hw/xfree86/modes
parent0032f9b66d63a4b1c5222edb8603fb60da379fb0 (diff)
downloadvcxsrv-578938f1cdd5a06dd6fa28167d575ec980322a5d.tar.gz
vcxsrv-578938f1cdd5a06dd6fa28167d575ec980322a5d.tar.bz2
vcxsrv-578938f1cdd5a06dd6fa28167d575ec980322a5d.zip
Update to git master branch of xserver.
Diffstat (limited to 'xorg-server/hw/xfree86/modes')
-rw-r--r--xorg-server/hw/xfree86/modes/xf86Crtc.c176
-rw-r--r--xorg-server/hw/xfree86/modes/xf86EdidModes.c444
-rw-r--r--xorg-server/hw/xfree86/modes/xf86Modes.c42
-rw-r--r--xorg-server/hw/xfree86/modes/xf86Modes.h3
4 files changed, 439 insertions, 226 deletions
diff --git a/xorg-server/hw/xfree86/modes/xf86Crtc.c b/xorg-server/hw/xfree86/modes/xf86Crtc.c
index 506fbb9cb..d015c6a77 100644
--- a/xorg-server/hw/xfree86/modes/xf86Crtc.c
+++ b/xorg-server/hw/xfree86/modes/xf86Crtc.c
@@ -1377,34 +1377,6 @@ xf86InitialPanning (ScrnInfoPtr scrn)
}
}
-/*
- * XXX walk the monitor mode list and prune out duplicates that
- * are inserted by xf86DDCMonitorSet. In an ideal world, that
- * function would do this work by itself.
- */
-
-static void
-xf86PruneDuplicateMonitorModes (MonPtr Monitor)
-{
- DisplayModePtr master, clone, next;
-
- for (master = Monitor->Modes;
- master && master != Monitor->Last;
- master = master->next)
- {
- for (clone = master->next; clone && clone != Monitor->Modes; clone = next)
- {
- next = clone->next;
- if (xf86ModesEqual (master, clone))
- {
- if (Monitor->Last == clone)
- Monitor->Last = clone->prev;
- xf86DeleteMode (&Monitor->Modes, clone);
- }
- }
- }
-}
-
/** Return - 0 + if a should be earlier, same or later than b in list
*/
static int
@@ -1523,6 +1495,42 @@ GuessRangeFromModes(MonPtr mon, DisplayModePtr mode)
mon->vrefresh[0].lo = 58.0;
}
+struct det_monrec_parameter {
+ MonRec *mon_rec;
+ int *max_clock;
+ Bool set_hsync;
+ Bool set_vrefresh;
+ enum { sync_config, sync_edid, sync_default } *sync_source;
+};
+
+static void handle_detailed_monrec(struct detailed_monitor_section *det_mon,
+ void *data)
+{
+ enum { sync_config, sync_edid, sync_default };
+ struct det_monrec_parameter *p;
+ p = (struct det_monrec_parameter *)data;
+
+ if (det_mon->type == DS_RANGES) {
+ struct monitor_ranges *ranges = &det_mon->section.ranges;
+ if (p->set_hsync && ranges->max_h) {
+ p->mon_rec->hsync[p->mon_rec->nHsync].lo = ranges->min_h;
+ p->mon_rec->hsync[p->mon_rec->nHsync].hi = ranges->max_h;
+ p->mon_rec->nHsync++;
+ if (*p->sync_source == sync_default)
+ *p->sync_source = sync_edid;
+ }
+ if (p->set_vrefresh && ranges->max_v) {
+ p->mon_rec->vrefresh[p->mon_rec->nVrefresh].lo = ranges->min_v;
+ p->mon_rec->vrefresh[p->mon_rec->nVrefresh].hi = ranges->max_v;
+ p->mon_rec->nVrefresh++;
+ if (*p->sync_source == sync_default)
+ *p->sync_source = sync_edid;
+ }
+ if (ranges->max_clock * 1000 > *p->max_clock)
+ *p->max_clock = ranges->max_clock * 1000;
+ }
+}
+
void
xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
{
@@ -1539,9 +1547,6 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
maxY = config->maxHeight;
}
- /* Elide duplicate modes before defaulting code uses them */
- xf86PruneDuplicateMonitorModes (scrn->monitor);
-
/* Probe the list of modes for each output. */
for (o = 0; o < config->num_output; o++)
{
@@ -1556,6 +1561,8 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
int max_clock = 0;
double clock;
Bool add_default_modes = TRUE;
+ Bool debug_modes = config->debug_modes ||
+ xf86Initialising;
enum { sync_config, sync_edid, sync_default } sync_source = sync_default;
while (output->probed_modes != NULL)
@@ -1601,42 +1608,24 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
edid_monitor = output->MonInfo;
- if (edid_monitor)
- {
- int i;
- Bool set_hsync = mon_rec.nHsync == 0;
- Bool set_vrefresh = mon_rec.nVrefresh == 0;
- struct disp_features *features = &edid_monitor->features;
+ if (edid_monitor)
+ {
+ struct det_monrec_parameter p;
+ struct disp_features *features = &edid_monitor->features;
/* if display is not continuous-frequency, don't add default modes */
if (!GTF_SUPPORTED(features->msc))
add_default_modes = FALSE;
- for (i = 0; i < sizeof (edid_monitor->det_mon) / sizeof (edid_monitor->det_mon[0]); i++)
- {
- if (edid_monitor->det_mon[i].type == DS_RANGES)
- {
- struct monitor_ranges *ranges = &edid_monitor->det_mon[i].section.ranges;
- if (set_hsync && ranges->max_h)
- {
- mon_rec.hsync[mon_rec.nHsync].lo = ranges->min_h;
- mon_rec.hsync[mon_rec.nHsync].hi = ranges->max_h;
- mon_rec.nHsync++;
- if (sync_source == sync_default)
- sync_source = sync_edid;
- }
- if (set_vrefresh && ranges->max_v)
- {
- mon_rec.vrefresh[mon_rec.nVrefresh].lo = ranges->min_v;
- mon_rec.vrefresh[mon_rec.nVrefresh].hi = ranges->max_v;
- mon_rec.nVrefresh++;
- if (sync_source == sync_default)
- sync_source = sync_edid;
- }
- if (ranges->max_clock * 1000 > max_clock)
- max_clock = ranges->max_clock * 1000;
- }
- }
+ p.mon_rec = &mon_rec;
+ p.max_clock = &max_clock;
+ p.set_hsync = mon_rec.nHsync == 0;
+ p.set_vrefresh = mon_rec.nVrefresh == 0;
+ p.sync_source = &sync_source;
+
+ xf86ForEachDetailedBlock(edid_monitor,
+ handle_detailed_monrec,
+ &p);
}
if (xf86GetOptValFreq (output->options, OPTION_MIN_CLOCK,
@@ -1725,8 +1714,7 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
if (mode->status == MODE_OK)
mode->status = (*output->funcs->mode_valid)(output, mode);
- xf86PruneInvalidModes(scrn, &output->probed_modes,
- config->debug_modes);
+ xf86PruneInvalidModes(scrn, &output->probed_modes, debug_modes);
output->probed_modes = xf86SortModes (output->probed_modes);
@@ -1758,7 +1746,7 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
output->initial_rotation = xf86OutputInitialRotation (output);
- if (config->debug_modes) {
+ if (debug_modes) {
if (output->probed_modes != NULL) {
xf86DrvMsg(scrn->scrnIndex, X_INFO,
"Printing probed modes for output %s\n",
@@ -1777,7 +1765,7 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
mode->VRefresh = xf86ModeVRefresh(mode);
xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
- if (config->debug_modes)
+ if (debug_modes)
xf86PrintModeline(scrn->scrnIndex, mode);
}
}
@@ -2900,6 +2888,35 @@ xf86OutputSetEDIDProperty (xf86OutputPtr output, void *data, int data_len)
#endif
+/* Pull out a phyiscal size from a detailed timing if available. */
+struct det_phySize_parameter {
+ xf86OutputPtr output;
+ ddc_quirk_t quirks;
+ Bool ret;
+};
+
+static void handle_detailed_physical_size(struct detailed_monitor_section
+ *det_mon, void *data)
+{
+ struct det_phySize_parameter *p;
+ p = (struct det_phySize_parameter *)data;
+
+ if (p->ret == TRUE )
+ return ;
+
+ xf86DetTimingApplyQuirks(det_mon, p->quirks,
+ p->output->MonInfo->features.hsize,
+ p->output->MonInfo->features.vsize);
+ if (det_mon->type == DT &&
+ det_mon->section.d_timings.h_size != 0 &&
+ det_mon->section.d_timings.v_size != 0) {
+
+ p->output->mm_width = det_mon->section.d_timings.h_size;
+ p->output->mm_height = det_mon->section.d_timings.v_size;
+ p->ret = TRUE;
+ }
+}
+
/**
* Set the EDID information for the specified output
*/
@@ -2908,7 +2925,7 @@ xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
{
ScrnInfoPtr scrn = output->scrn;
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
- int i;
+ Bool debug_modes = config->debug_modes || xf86Initialising;
#ifdef RANDR_12_INTERFACE
int size;
#endif
@@ -2918,7 +2935,7 @@ xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
output->MonInfo = edid_mon;
- if (config->debug_modes) {
+ if (debug_modes) {
xf86DrvMsg(scrn->scrnIndex, X_INFO, "EDID for output %s\n",
output->name);
xf86PrintEDID(edid_mon);
@@ -2943,20 +2960,15 @@ xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
xf86OutputSetEDIDProperty (output, edid_mon ? edid_mon->rawData : NULL, size);
#endif
- if (edid_mon)
- {
- /* Pull out a phyiscal size from a detailed timing if available. */
- for (i = 0; i < 4; i++) {
- if (edid_mon->det_mon[i].type == DT &&
- edid_mon->det_mon[i].section.d_timings.h_size != 0 &&
- edid_mon->det_mon[i].section.d_timings.v_size != 0)
- {
- output->mm_width = edid_mon->det_mon[i].section.d_timings.h_size;
- output->mm_height = edid_mon->det_mon[i].section.d_timings.v_size;
- break;
- }
- }
-
+ if (edid_mon) {
+
+ struct det_phySize_parameter p;
+ p.output = output;
+ p.quirks = xf86DDCDetectQuirks(scrn->scrnIndex,edid_mon, FALSE);
+ p.ret = FALSE;
+ xf86ForEachDetailedBlock(edid_mon,
+ handle_detailed_physical_size, &p);
+
/* if no mm size is available from a detailed timing, check the max size field */
if ((!output->mm_width || !output->mm_height) &&
(edid_mon->features.hsize && edid_mon->features.vsize))
diff --git a/xorg-server/hw/xfree86/modes/xf86EdidModes.c b/xorg-server/hw/xfree86/modes/xf86EdidModes.c
index 2f80070cf..449078e2a 100644
--- a/xorg-server/hw/xfree86/modes/xf86EdidModes.c
+++ b/xorg-server/hw/xfree86/modes/xf86EdidModes.c
@@ -45,20 +45,23 @@
#include <string.h>
#include <math.h>
+static void handle_detailed_rblank(struct detailed_monitor_section *det_mon,
+ void *data)
+{
+ if (det_mon->type == DS_RANGES)
+ if (det_mon->section.ranges.supported_blanking & CVT_REDUCED)
+ *(Bool*)data = TRUE;
+}
+
static Bool
xf86MonitorSupportsReducedBlanking(xf86MonPtr DDC)
{
/* EDID 1.4 explicitly defines RB support */
if (DDC->ver.revision >= 4) {
- int i;
- for (i = 0; i < DET_TIMINGS; i++) {
- struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
- if (det_mon->type == DS_RANGES)
- if (det_mon->section.ranges.supported_blanking & CVT_REDUCED)
- return TRUE;
- }
-
- return FALSE;
+ Bool ret = FALSE;
+
+ xf86ForEachDetailedBlock(DDC, handle_detailed_rblank, &ret);
+ return ret;
}
/* For anything older, assume digital means RB support. Boo. */
@@ -68,34 +71,6 @@ xf86MonitorSupportsReducedBlanking(xf86MonPtr DDC)
return FALSE;
}
-/*
- * Quirks to work around broken EDID data from various monitors.
- */
-
-typedef enum {
- DDC_QUIRK_NONE = 0,
- /* First detailed mode is bogus, prefer largest mode at 60hz */
- DDC_QUIRK_PREFER_LARGE_60 = 1 << 0,
- /* 135MHz clock is too high, drop a bit */
- DDC_QUIRK_135_CLOCK_TOO_HIGH = 1 << 1,
- /* Prefer the largest mode at 75 Hz */
- DDC_QUIRK_PREFER_LARGE_75 = 1 << 2,
- /* Convert detailed timing's horizontal from units of cm to mm */
- DDC_QUIRK_DETAILED_H_IN_CM = 1 << 3,
- /* Convert detailed timing's vertical from units of cm to mm */
- DDC_QUIRK_DETAILED_V_IN_CM = 1 << 4,
- /* Detailed timing descriptors have bogus size values, so just take the
- * maximum size and use that.
- */
- DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE = 1 << 5,
- /* Monitor forgot to set the first detailed is preferred bit. */
- DDC_QUIRK_FIRST_DETAILED_PREFERRED = 1 << 6,
- /* use +hsync +vsync for detailed mode */
- DDC_QUIRK_DETAILED_SYNC_PP = 1 << 7,
- /* Force single-link DVI bandwidth limit */
- DDC_QUIRK_DVI_SINGLE_LINK = 1 << 8,
-} ddc_quirk_t;
-
static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC)
{
/* Belinea 10 15 55 */
@@ -266,7 +241,7 @@ static const ddc_quirk_map_t ddc_quirks[] = {
},
{
quirk_detailed_use_maximum_size, DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE,
- "Detailed timings give sizes in cm."
+ "Use maximum size instead of detailed timing sizes."
},
{
quirk_first_detailed_preferred, DDC_QUIRK_FIRST_DETAILED_PREFERRED,
@@ -498,8 +473,10 @@ DDCModesFromStandardTiming(struct std_timings *timing, ddc_quirk_t quirks,
vsize = timing[i].vsize;
refresh = timing[i].refresh;
- /* HDTV hack. Hooray. */
- if (hsize == 1360 && vsize == 765 && refresh == 60) {
+ /* HDTV hack, because you can't say 1366 */
+ if (refresh == 60 &&
+ ((hsize == 1360 && vsize == 765) ||
+ (hsize == 1368 && vsize == 769))) {
Mode = xf86CVTMode(1366, 768, 60, FALSE, FALSE);
Mode->HDisplay = 1366;
Mode->VSyncStart--;
@@ -527,6 +504,45 @@ DDCModesFromStandardTiming(struct std_timings *timing, ddc_quirk_t quirks,
return Modes;
}
+static void
+DDCModeDoInterlaceQuirks(DisplayModePtr mode)
+{
+ /*
+ * EDID is delightfully ambiguous about how interlaced modes are to be
+ * encoded. X's internal representation is of frame height, but some
+ * HDTV detailed timings are encoded as field height.
+ *
+ * The format list here is from CEA, in frame size. Technically we
+ * should be checking refresh rate too. Whatever.
+ */
+ static const struct {
+ int w, h;
+ } cea_interlaced[] = {
+ { 1920, 1080 },
+ { 720, 480 },
+ { 1440, 480 },
+ { 2880, 480 },
+ { 720, 576 },
+ { 1440, 576 },
+ { 2880, 576 },
+ };
+ static const int n_modes = sizeof(cea_interlaced)/sizeof(cea_interlaced[0]);
+ int i;
+
+ for (i = 0; i < n_modes; i++) {
+ if ((mode->HDisplay == cea_interlaced[i].w) &&
+ (mode->VDisplay == cea_interlaced[i].h / 2)) {
+ mode->VDisplay *= 2;
+ mode->VSyncStart *= 2;
+ mode->VSyncEnd *= 2;
+ mode->VTotal *= 2;
+ mode->VTotal |= 1;
+ }
+ }
+
+ mode->Flags |= V_INTERLACE;
+}
+
/*
*
*/
@@ -591,12 +607,10 @@ DDCModeFromDetailedTiming(int scrnIndex, struct detailed_timings *timing,
return NULL;
}
- xf86SetModeDefaultName(Mode);
-
/* We ignore h/v_size and h/v_border for now. */
if (timing->interlaced)
- Mode->Flags |= V_INTERLACE;
+ DDCModeDoInterlaceQuirks(Mode);
if (quirks & DDC_QUIRK_DETAILED_SYNC_PP)
Mode->Flags |= V_PVSYNC | V_PHSYNC;
@@ -612,6 +626,8 @@ DDCModeFromDetailedTiming(int scrnIndex, struct detailed_timings *timing,
Mode->Flags |= V_NHSYNC;
}
+ xf86SetModeDefaultName(Mode);
+
return Mode;
}
@@ -774,7 +790,7 @@ DDCGuessRangesFromModes(int scrnIndex, MonPtr Monitor, DisplayModePtr Modes)
}
}
-static ddc_quirk_t
+ddc_quirk_t
xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose)
{
ddc_quirk_t quirks;
@@ -794,6 +810,25 @@ xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose)
return quirks;
}
+void xf86DetTimingApplyQuirks(struct detailed_monitor_section *det_mon,
+ ddc_quirk_t quirks,
+ int hsize, int vsize)
+{
+ if (det_mon->type != DT)
+ return;
+
+ if (quirks & DDC_QUIRK_DETAILED_H_IN_CM)
+ det_mon->section.d_timings.h_size *= 10;
+
+ if (quirks & DDC_QUIRK_DETAILED_V_IN_CM)
+ det_mon->section.d_timings.v_size *= 10;
+
+ if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
+ det_mon->section.d_timings.h_size = 10 * hsize;
+ det_mon->section.d_timings.v_size = 10 * vsize;
+ }
+}
+
/**
* Applies monitor-specific quirks to the decoded EDID information.
*
@@ -807,21 +842,9 @@ xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC)
int i;
for (i = 0; i < DET_TIMINGS; i++) {
- struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
-
- if (det_mon->type != DT)
- continue;
-
- if (quirks & DDC_QUIRK_DETAILED_H_IN_CM)
- det_mon->section.d_timings.h_size *= 10;
-
- if (quirks & DDC_QUIRK_DETAILED_V_IN_CM)
- det_mon->section.d_timings.v_size *= 10;
-
- if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
- det_mon->section.d_timings.h_size = 10 * DDC->features.hsize;
- det_mon->section.d_timings.v_size = 10 * DDC->features.vsize;
- }
+ xf86DetTimingApplyQuirks(DDC->det_mon + i, quirks,
+ DDC->features.hsize,
+ DDC->features.vsize);
}
}
@@ -866,14 +889,156 @@ xf86DDCSetPreferredRefresh(int scrnIndex, DisplayModePtr modes,
best->type |= M_T_PREFERRED;
}
+#define CEA_VIDEO_MODES_NUM 64
+static const DisplayModeRec CEAVideoModes[CEA_VIDEO_MODES_NUM] = {
+ { MODEPREFIX, 25175, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 1:640x480@60Hz */
+ { MODEPREFIX, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 2:720x480@60Hz */
+ { MODEPREFIX, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 3:720x480@60Hz */
+ { MODEPREFIX, 74250, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 4: 1280x720@60Hz */
+ { MODEPREFIX, 74250, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 5:1920x1080i@60Hz */
+ { MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 6:1440x480i@60Hz */
+ { MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 7:1440x480i@60Hz */
+ { MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 8:1440x240@60Hz */
+ { MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 9:1440x240@60Hz */
+ { MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 10:2880x480i@60Hz */
+ { MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 11:2880x480i@60Hz */
+ { MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 12:2880x240@60Hz */
+ { MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 13:2880x240@60Hz */
+ { MODEPREFIX, 54000, 1440, 1472, 1596, 1716, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 14:1440x480@60Hz */
+ { MODEPREFIX, 54000, 1440, 1472, 1596, 1716, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 15:1440x480@60Hz */
+ { MODEPREFIX, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 16:1920x1080@60Hz */
+ { MODEPREFIX, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 17:720x576@50Hz */
+ { MODEPREFIX, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 18:720x576@50Hz */
+ { MODEPREFIX, 74250, 1280, 1720, 1760, 1980, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 19: 1280x720@50Hz */
+ { MODEPREFIX, 74250, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 20:1920x1080i@50Hz */
+ { MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 21:1440x576i@50Hz */
+ { MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 22:1440x576i@50Hz */
+ { MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 23:1440x288@50Hz */
+ { MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 24:1440x288@50Hz */
+ { MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 25:2880x576i@50Hz */
+ { MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 26:2880x576i@50Hz */
+ { MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 27:2880x288@50Hz */
+ { MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 28:2880x288@50Hz */
+ { MODEPREFIX, 54000, 1440, 1464, 1592, 1728, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 29:1440x576@50Hz */
+ { MODEPREFIX, 54000, 1440, 1464, 1592, 1728, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 30:1440x576@50Hz */
+ { MODEPREFIX, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 31:1920x1080@50Hz */
+ { MODEPREFIX, 74250, 1920, 2558, 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 32:1920x1080@24Hz */
+ { MODEPREFIX, 74250, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 33:1920x1080@25Hz */
+ { MODEPREFIX, 74250, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 34:1920x1080@30Hz */
+ { MODEPREFIX, 108000, 2880, 2944, 3192, 3432, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 35:2880x480@60Hz */
+ { MODEPREFIX, 108000, 2880, 2944, 3192, 3432, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 36:2880x480@60Hz */
+ { MODEPREFIX, 108000, 2880, 2928, 3184, 3456, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 37:2880x576@50Hz */
+ { MODEPREFIX, 108000, 2880, 2928, 3184, 3456, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 38:2880x576@50Hz */
+ { MODEPREFIX, 72000, 1920, 1952, 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, V_PHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 39:1920x1080i@50Hz */
+ { MODEPREFIX, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 40:1920x1080i@100Hz */
+ { MODEPREFIX, 148500, 1280, 1720, 1760, 1980, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 41:1280x720@100Hz */
+ { MODEPREFIX, 54000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 42:720x576@100Hz */
+ { MODEPREFIX, 54000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 43:720x576@100Hz */
+ { MODEPREFIX, 54000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 44:1440x576i@100Hz */
+ { MODEPREFIX, 54000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 45:1440x576i@100Hz */
+ { MODEPREFIX, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 46:1920x1080i@120Hz */
+ { MODEPREFIX, 148500, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 47:1280x720@120Hz */
+ { MODEPREFIX, 54000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 48:720x480@120Hz */
+ { MODEPREFIX, 54000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 49:720x480@120Hz */
+ { MODEPREFIX, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 50:1440x480i@120Hz */
+ { MODEPREFIX, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 51:1440x480i@120Hz */
+ { MODEPREFIX, 108000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 52:720x576@200Hz */
+ { MODEPREFIX, 108000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 53:720x576@200Hz */
+ { MODEPREFIX, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 54:1440x576i@200Hz */
+ { MODEPREFIX, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 55:1440x576i@200Hz */
+ { MODEPREFIX, 108000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 56:720x480@240Hz */
+ { MODEPREFIX, 108000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 57:720x480@240Hz */
+ { MODEPREFIX, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 58:1440x480i@240 */
+ { MODEPREFIX, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 59:1440x480i@240 */
+ { MODEPREFIX, 59400, 1280, 3040, 3080, 3300, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 60: 1280x720@24Hz */
+ { MODEPREFIX, 74250, 3700, 3740, 1430, 3960, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 61: 1280x720@25Hz */
+ { MODEPREFIX, 74250, 1280, 3040, 3080, 3300, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 62: 1280x720@30Hz */
+ { MODEPREFIX, 297000, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 63: 1920x1080@120Hz */
+ { MODEPREFIX, 297000, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 64:1920x1080@100Hz */
+};
+
+/* chose mode line by cea short video descriptor*/
+static void handle_cea_svd(struct cea_video_block *video, void *data)
+{
+ DisplayModePtr Mode;
+ DisplayModePtr *Modes = (DisplayModePtr *) data;
+ int vid;
+
+ vid = video ->video_code & 0x7f;
+ if (vid < CEA_VIDEO_MODES_NUM) {
+ Mode = xf86DuplicateMode(CEAVideoModes + vid);
+ *Modes = xf86ModesAdd(*Modes, Mode);
+ }
+}
+
+static DisplayModePtr
+DDCModesFromCEAExtension(int scrnIndex, xf86MonPtr MonPtr)
+{
+ DisplayModePtr Modes = NULL;
+
+ xf86ForEachVideoBlock(MonPtr,
+ handle_cea_svd,
+ &Modes);
+
+ return Modes;
+}
+
+struct det_modes_parameter {
+ xf86MonPtr DDC;
+ ddc_quirk_t quirks;
+ DisplayModePtr Modes;
+ Bool rb;
+ Bool preferred;
+ int timing_level;
+};
+
+static void handle_detailed_modes(struct detailed_monitor_section *det_mon,
+ void *data)
+{
+ DisplayModePtr Mode;
+ struct det_modes_parameter *p = (struct det_modes_parameter *)data;
+
+ xf86DetTimingApplyQuirks(det_mon,p->quirks,
+ p->DDC->features.hsize,
+ p->DDC->features.vsize);
+
+ switch (det_mon->type) {
+ case DT:
+ Mode = DDCModeFromDetailedTiming(p->DDC->scrnIndex,
+ &det_mon->section.d_timings,
+ p->preferred,
+ p->quirks);
+ p->preferred = FALSE;
+ p->Modes = xf86ModesAdd(p->Modes, Mode);
+ break;
+ case DS_STD_TIMINGS:
+ Mode = DDCModesFromStandardTiming(det_mon->section.std_t,
+ p->quirks, p->timing_level,p->rb);
+ p->Modes = xf86ModesAdd(p->Modes, Mode);
+ break;
+#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(7,0,0,0,0)
+ case DS_CVT:
+ Mode = DDCModesFromCVT(p->DDC->scrnIndex, det_mon->section.cvt);
+ p->Modes = xf86ModesAdd(p->Modes, Mode);
+ break;
+#endif
+ case DS_EST_III:
+ Mode = DDCModesFromEstIII(det_mon->section.est_iii);
+ p->Modes = xf86ModesAdd(p->Modes, Mode);
+ break;
+ default:
+ break;
+ }
+}
+
DisplayModePtr
xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
{
- int i;
DisplayModePtr Modes = NULL, Mode;
ddc_quirk_t quirks;
Bool preferred, rb;
int timing_level;
+ struct det_modes_parameter p;
xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n",
DDC->vendor.name, DDC->vendor.prod_id);
@@ -892,35 +1057,14 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
timing_level = MonitorStandardTimingLevel(DDC);
- for (i = 0; i < DET_TIMINGS; i++) {
- struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
-
- Mode = NULL;
- switch (det_mon->type) {
- case DT:
- Mode = DDCModeFromDetailedTiming(scrnIndex,
- &det_mon->section.d_timings,
- preferred,
- quirks);
- preferred = FALSE;
- break;
- case DS_STD_TIMINGS:
- Mode = DDCModesFromStandardTiming(det_mon->section.std_t,
- quirks, timing_level, rb);
- break;
-#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(7,0,0,0,0)
- case DS_CVT:
- Mode = DDCModesFromCVT(scrnIndex, det_mon->section.cvt);
- break;
-#endif
- case DS_EST_III:
- Mode = DDCModesFromEstIII(det_mon->section.est_iii);
- break;
- default:
- break;
- }
- Modes = xf86ModesAdd(Modes, Mode);
- }
+ p.quirks = quirks;
+ p.DDC = DDC;
+ p.Modes = Modes;
+ p.rb = rb;
+ p.preferred = preferred;
+ p.timing_level = timing_level;
+ xf86ForEachDetailedBlock(DDC, handle_detailed_modes, &p);
+ Modes = p.Modes;
/* Add established timings */
Mode = DDCModesFromEstablished(scrnIndex, &DDC->timings1, quirks);
@@ -930,15 +1074,78 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
Mode = DDCModesFromStandardTiming(DDC->timings2, quirks, timing_level, rb);
Modes = xf86ModesAdd(Modes, Mode);
+ /* Add cea-extension mode timings */
+ Mode = DDCModesFromCEAExtension(scrnIndex,DDC);
+ Modes = xf86ModesAdd(Modes, Mode);
+
if (quirks & DDC_QUIRK_PREFER_LARGE_60)
xf86DDCSetPreferredRefresh(scrnIndex, Modes, 60);
if (quirks & DDC_QUIRK_PREFER_LARGE_75)
xf86DDCSetPreferredRefresh(scrnIndex, Modes, 75);
+ Modes = xf86PruneDuplicateModes(Modes);
+
return Modes;
}
+struct det_mon_parameter {
+ MonPtr Monitor;
+ ddc_quirk_t quirks;
+ Bool have_hsync;
+ Bool have_vrefresh;
+ Bool have_maxpixclock;
+};
+
+static void handle_detailed_monset(struct detailed_monitor_section *det_mon,
+ void *data)
+{
+ int clock;
+ struct det_mon_parameter *p = (struct det_mon_parameter *)data;
+ int scrnIndex = ((xf86MonPtr)(p->Monitor->DDC))->scrnIndex;
+
+ switch (det_mon->type) {
+ case DS_RANGES:
+ if (!p->have_hsync) {
+ if (!p->Monitor->nHsync)
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Using EDID range info for horizontal sync\n");
+ p->Monitor->hsync[p->Monitor->nHsync].lo =
+ det_mon->section.ranges.min_h;
+ p->Monitor->hsync[p->Monitor->nHsync].hi =
+ det_mon->section.ranges.max_h;
+ p->Monitor->nHsync++;
+ } else {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Using hsync ranges from config file\n");
+ }
+
+ if (!p->have_vrefresh) {
+ if (!p->Monitor->nVrefresh)
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Using EDID range info for vertical refresh\n");
+ p->Monitor->vrefresh[p->Monitor->nVrefresh].lo =
+ det_mon->section.ranges.min_v;
+ p->Monitor->vrefresh[p->Monitor->nVrefresh].hi =
+ det_mon->section.ranges.max_v;
+ p->Monitor->nVrefresh++;
+ } else {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Using vrefresh ranges from config file\n");
+ }
+
+ clock = det_mon->section.ranges.max_clock * 1000;
+ if (p->quirks & DDC_QUIRK_DVI_SINGLE_LINK)
+ clock = min(clock, 165000);
+ if (!p->have_maxpixclock && clock > p->Monitor->maxPixClock)
+ p->Monitor->maxPixClock = clock;
+
+ break;
+ default:
+ break;
+ }
+}
+
/*
* Fill out MonPtr with xf86MonPtr information.
*/
@@ -946,17 +1153,13 @@ void
xf86EdidMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC)
{
DisplayModePtr Modes = NULL, Mode;
- int i, clock;
- Bool have_hsync = FALSE, have_vrefresh = FALSE, have_maxpixclock = FALSE;
- ddc_quirk_t quirks;
+ struct det_mon_parameter p;
if (!Monitor || !DDC)
return;
Monitor->DDC = DDC;
- quirks = xf86DDCDetectQuirks(scrnIndex, DDC, FALSE);
-
if (Monitor->widthmm <= 0 || Monitor->heightmm <= 0) {
Monitor->widthmm = 10 * DDC->features.hsize;
Monitor->heightmm = 10 * DDC->features.vsize;
@@ -966,54 +1169,13 @@ xf86EdidMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC)
Modes = xf86DDCGetModes(scrnIndex, DDC);
- /* Skip EDID ranges if they were specified in the config file */
- have_hsync = (Monitor->nHsync != 0);
- have_vrefresh = (Monitor->nVrefresh != 0);
- have_maxpixclock = (Monitor->maxPixClock != 0);
-
/* Go through the detailed monitor sections */
- for (i = 0; i < DET_TIMINGS; i++) {
- switch (DDC->det_mon[i].type) {
- case DS_RANGES:
- if (!have_hsync) {
- if (!Monitor->nHsync)
- xf86DrvMsg(scrnIndex, X_INFO,
- "Using EDID range info for horizontal sync\n");
- Monitor->hsync[Monitor->nHsync].lo =
- DDC->det_mon[i].section.ranges.min_h;
- Monitor->hsync[Monitor->nHsync].hi =
- DDC->det_mon[i].section.ranges.max_h;
- Monitor->nHsync++;
- } else {
- xf86DrvMsg(scrnIndex, X_INFO,
- "Using hsync ranges from config file\n");
- }
-
- if (!have_vrefresh) {
- if (!Monitor->nVrefresh)
- xf86DrvMsg(scrnIndex, X_INFO,
- "Using EDID range info for vertical refresh\n");
- Monitor->vrefresh[Monitor->nVrefresh].lo =
- DDC->det_mon[i].section.ranges.min_v;
- Monitor->vrefresh[Monitor->nVrefresh].hi =
- DDC->det_mon[i].section.ranges.max_v;
- Monitor->nVrefresh++;
- } else {
- xf86DrvMsg(scrnIndex, X_INFO,
- "Using vrefresh ranges from config file\n");
- }
-
- clock = DDC->det_mon[i].section.ranges.max_clock * 1000;
- if (quirks & DDC_QUIRK_DVI_SINGLE_LINK)
- clock = min(clock, 165000);
- if (!have_maxpixclock && clock > Monitor->maxPixClock)
- Monitor->maxPixClock = clock;
-
- break;
- default:
- break;
- }
- }
+ p.Monitor = Monitor;
+ p.quirks = xf86DDCDetectQuirks(scrnIndex, Monitor->DDC, FALSE);
+ p.have_hsync = (Monitor->nHsync != 0);
+ p.have_vrefresh = (Monitor->nVrefresh != 0);
+ p.have_maxpixclock = (Monitor->maxPixClock != 0);
+ xf86ForEachDetailedBlock(DDC, handle_detailed_monset, &p);
if (Modes) {
/* Print Modes */
diff --git a/xorg-server/hw/xfree86/modes/xf86Modes.c b/xorg-server/hw/xfree86/modes/xf86Modes.c
index d105b48ab..75aedaa99 100644
--- a/xorg-server/hw/xfree86/modes/xf86Modes.c
+++ b/xorg-server/hw/xfree86/modes/xf86Modes.c
@@ -136,10 +136,12 @@ xf86ModeBandwidth(DisplayModePtr mode, int depth)
void
xf86SetModeDefaultName(DisplayModePtr mode)
{
- if (mode->name != NULL)
- xfree(mode->name);
+ Bool interlaced = !!(mode->Flags & V_INTERLACE);
- mode->name = XNFprintf("%dx%d", mode->HDisplay, mode->VDisplay);
+ xfree(mode->name);
+
+ mode->name = XNFprintf("%dx%d%s", mode->HDisplay, mode->VDisplay,
+ interlaced ? "i" : "");
}
/*
@@ -689,3 +691,37 @@ xf86GetDefaultModes (void)
}
return head;
}
+
+/*
+ * Walk a mode list and prune out duplicates. Will preserve the preferred
+ * mode of an otherwise-duplicate pair.
+ *
+ * Probably best to call this on lists that are all of a single class
+ * (driver, default, user, etc.), otherwise, which mode gets deleted is
+ * not especially well defined.
+ *
+ * Returns the new list.
+ */
+
+DisplayModePtr
+xf86PruneDuplicateModes(DisplayModePtr modes)
+{
+ DisplayModePtr m, n, o;
+
+top:
+ for (m = modes; m; m = m->next) {
+ for (n = m->next; n; n = o) {
+ o = n->next;
+ if (xf86ModesEqual(m, n)) {
+ if (n->type & M_T_PREFERRED) {
+ xf86DeleteMode(&modes, m);
+ goto top;
+ }
+ else
+ xf86DeleteMode(&modes, n);
+ }
+ }
+ }
+
+ return modes;
+}
diff --git a/xorg-server/hw/xfree86/modes/xf86Modes.h b/xorg-server/hw/xfree86/modes/xf86Modes.h
index 908f59b48..38927b128 100644
--- a/xorg-server/hw/xfree86/modes/xf86Modes.h
+++ b/xorg-server/hw/xfree86/modes/xf86Modes.h
@@ -95,6 +95,9 @@ extern _X_EXPORT void
xf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr *modeList,
Bool verbose);
+extern _X_EXPORT DisplayModePtr
+xf86PruneDuplicateModes(DisplayModePtr modes);
+
extern _X_EXPORT void
xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList,
int flags);