xf86EdidModes.c revision 05b261ec
1/*
2 * Copyright 2006 Luc Verhaegen.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24/**
25 * @file This file covers code to convert a xf86MonPtr containing EDID-probed
26 * information into a list of modes, including applying monitor-specific
27 * quirks to fix broken EDID data.
28 */
29#ifdef HAVE_XORG_CONFIG_H
30#include <xorg-config.h>
31#else
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35#endif
36
37#include "xf86.h"
38#include "xf86DDC.h"
39#include <X11/Xatom.h>
40#include "property.h"
41#include "propertyst.h"
42#include "xf86DDC.h"
43#include "xf86Crtc.h"
44#include <string.h>
45#include <math.h>
46
47/*
48 * Quirks to work around broken EDID data from various monitors.
49 */
50
51typedef enum {
52    DDC_QUIRK_NONE = 0,
53    /* First detailed mode is bogus, prefer largest mode at 60hz */
54    DDC_QUIRK_PREFER_LARGE_60 = 1 << 0,
55    /* 135MHz clock is too high, drop a bit */
56    DDC_QUIRK_135_CLOCK_TOO_HIGH = 1 << 1,
57} ddc_quirk_t;
58
59static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC)
60{
61    /* Belinea 10 15 55 */
62    if (memcmp (DDC->vendor.name, "MAX", 4) == 0 &&
63	DDC->vendor.prod_id == 1516)
64	return TRUE;
65
66    /* Acer AL1706 */
67    if (memcmp (DDC->vendor.name, "ACR", 4) == 0 &&
68	DDC->vendor.prod_id == 44358)
69	return TRUE;
70
71    /* Bug #10814: Samsung SyncMaster 225BW */
72    if (memcmp (DDC->vendor.name, "SAM", 4) == 0 &&
73	DDC->vendor.prod_id == 596)
74	return TRUE;
75
76    /* Bug #10545: Samsung SyncMaster 226BW */
77    if (memcmp (DDC->vendor.name, "SAM", 4) == 0 &&
78	DDC->vendor.prod_id == 638)
79	return TRUE;
80
81    return FALSE;
82}
83
84static Bool quirk_135_clock_too_high (int scrnIndex, xf86MonPtr DDC)
85{
86    /* Envision Peripherals, Inc. EN-7100e.  See bug #9550. */
87    if (memcmp (DDC->vendor.name, "EPI", 4) == 0 &&
88	DDC->vendor.prod_id == 59264)
89	return TRUE;
90
91    return FALSE;
92}
93
94typedef struct {
95    Bool	(*detect) (int scrnIndex, xf86MonPtr DDC);
96    ddc_quirk_t	quirk;
97    char	*description;
98} ddc_quirk_map_t;
99
100static const ddc_quirk_map_t ddc_quirks[] = {
101    {
102	quirk_prefer_large_60,   DDC_QUIRK_PREFER_LARGE_60,
103	"Detailed timing is not preferred, use largest mode at 60Hz"
104    },
105    {
106	quirk_135_clock_too_high,   DDC_QUIRK_135_CLOCK_TOO_HIGH,
107	"Recommended 135MHz pixel clock is too high"
108    },
109    {
110	NULL,		DDC_QUIRK_NONE,
111	"No known quirks"
112    },
113};
114
115/*
116 * TODO:
117 *  - for those with access to the VESA DMT standard; review please.
118 */
119#define MODEPREFIX(name) NULL, NULL, name, 0,M_T_DRIVER
120#define MODESUFFIX   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,FALSE,FALSE,0,NULL,0,0.0,0.0
121
122static DisplayModeRec DDCEstablishedModes[17] = {
123    { MODEPREFIX("800x600"),    40000,  800,  840,  968, 1056, 0,  600,  601,  605,  628, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@60Hz */
124    { MODEPREFIX("800x600"),    36000,  800,  824,  896, 1024, 0,  600,  601,  603,  625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@56Hz */
125    { MODEPREFIX("640x480"),    31500,  640,  656,  720,  840, 0,  480,  481,  484,  500, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@75Hz */
126    { MODEPREFIX("640x480"),    31500,  640,  664,  704,  832, 0,  480,  489,  491,  520, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@72Hz */
127    { MODEPREFIX("640x480"),    30240,  640,  704,  768,  864, 0,  480,  483,  486,  525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@67Hz */
128    { MODEPREFIX("640x480"),    25200,  640,  656,  752,  800, 0,  480,  490,  492,  525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@60Hz */
129    { MODEPREFIX("720x400"),    35500,  720,  738,  846,  900, 0,  400,  421,  423,  449, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 720x400@88Hz */
130    { MODEPREFIX("720x400"),    28320,  720,  738,  846,  900, 0,  400,  412,  414,  449, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 720x400@70Hz */
131    { MODEPREFIX("1280x1024"), 135000, 1280, 1296, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x1024@75Hz */
132    { MODEPREFIX("1024x768"),   78800, 1024, 1040, 1136, 1312, 0,  768,  769,  772,  800, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1024x768@75Hz */
133    { MODEPREFIX("1024x768"),   75000, 1024, 1048, 1184, 1328, 0,  768,  771,  777,  806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@70Hz */
134    { MODEPREFIX("1024x768"),   65000, 1024, 1048, 1184, 1344, 0,  768,  771,  777,  806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@60Hz */
135    { MODEPREFIX("1024x768"),   44900, 1024, 1032, 1208, 1264, 0,  768,  768,  776,  817, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* 1024x768@43Hz */
136    { MODEPREFIX("832x624"),    57284,  832,  864,  928, 1152, 0,  624,  625,  628,  667, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 832x624@75Hz */
137    { MODEPREFIX("800x600"),    49500,  800,  816,  896, 1056, 0,  600,  601,  604,  625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@75Hz */
138    { MODEPREFIX("800x600"),    50000,  800,  856,  976, 1040, 0,  600,  637,  643,  666, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@72Hz */
139    { MODEPREFIX("1152x864"),  108000, 1152, 1216, 1344, 1600, 0,  864,  865,  868,  900, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1152x864@75Hz */
140};
141
142static DisplayModePtr
143DDCModesFromEstablished(int scrnIndex, struct established_timings *timing,
144			ddc_quirk_t quirks)
145{
146    DisplayModePtr Modes = NULL, Mode = NULL;
147    CARD32 bits = (timing->t1) | (timing->t2 << 8) |
148        ((timing->t_manu & 0x80) << 9);
149    int i;
150
151    for (i = 0; i < 17; i++) {
152        if (bits & (0x01 << i)) {
153            Mode = xf86DuplicateMode(&DDCEstablishedModes[i]);
154            Modes = xf86ModesAdd(Modes, Mode);
155        }
156    }
157
158    return Modes;
159}
160
161/*
162 *
163 */
164static DisplayModePtr
165DDCModesFromStandardTiming(int scrnIndex, struct std_timings *timing,
166			   ddc_quirk_t quirks)
167{
168    DisplayModePtr Modes = NULL, Mode = NULL;
169    int i;
170
171    for (i = 0; i < STD_TIMINGS; i++) {
172        if (timing[i].hsize && timing[i].vsize && timing[i].refresh) {
173            Mode =  xf86CVTMode(timing[i].hsize, timing[i].vsize,
174                                timing[i].refresh, FALSE, FALSE);
175	    Mode->type = M_T_DRIVER;
176            Modes = xf86ModesAdd(Modes, Mode);
177        }
178    }
179
180    return Modes;
181}
182
183/*
184 *
185 */
186static DisplayModePtr
187DDCModeFromDetailedTiming(int scrnIndex, struct detailed_timings *timing,
188			  int preferred, ddc_quirk_t quirks)
189{
190    DisplayModePtr Mode;
191
192    /*
193     * Refuse to create modes that are insufficiently large.  64 is a random
194     * number, maybe the spec says something about what the minimum is.  In
195     * particular I see this frequently with _old_ EDID, 1.0 or so, so maybe
196     * our parser is just being too aggresive there.
197     */
198    if (timing->h_active < 64 || timing->v_active < 64) {
199	xf86DrvMsg(scrnIndex, X_INFO,
200		   "%s: Ignoring tiny %dx%d mode\n", __func__,
201		   timing->h_active, timing->v_active);
202	return NULL;
203    }
204
205    /* We don't do stereo */
206    if (timing->stereo) {
207        xf86DrvMsg(scrnIndex, X_INFO,
208		   "%s: Ignoring: We don't handle stereo.\n", __func__);
209        return NULL;
210    }
211
212    /* We only do seperate sync currently */
213    if (timing->sync != 0x03) {
214         xf86DrvMsg(scrnIndex, X_INFO,
215		    "%s: %dx%d Warning: We only handle seperate"
216                    " sync.\n", __func__, timing->h_active, timing->v_active);
217    }
218
219    Mode = xnfalloc(sizeof(DisplayModeRec));
220    memset(Mode, 0, sizeof(DisplayModeRec));
221
222    Mode->type = M_T_DRIVER;
223    if (preferred)
224	Mode->type |= M_T_PREFERRED;
225
226    if( ( quirks & DDC_QUIRK_135_CLOCK_TOO_HIGH ) &&
227	timing->clock == 135000000 )
228        Mode->Clock = 108880;
229    else
230        Mode->Clock = timing->clock / 1000.0;
231
232    Mode->HDisplay = timing->h_active;
233    Mode->HSyncStart = timing->h_active + timing->h_sync_off;
234    Mode->HSyncEnd = Mode->HSyncStart + timing->h_sync_width;
235    Mode->HTotal = timing->h_active + timing->h_blanking;
236
237    Mode->VDisplay = timing->v_active;
238    Mode->VSyncStart = timing->v_active + timing->v_sync_off;
239    Mode->VSyncEnd = Mode->VSyncStart + timing->v_sync_width;
240    Mode->VTotal = timing->v_active + timing->v_blanking;
241
242    /* perform basic check on the detail timing */
243    if (Mode->HSyncEnd > Mode->HTotal || Mode->VSyncEnd > Mode->VTotal) {
244	xfree(Mode);
245	return NULL;
246    }
247
248    xf86SetModeDefaultName(Mode);
249
250    /* We ignore h/v_size and h/v_border for now. */
251
252    if (timing->interlaced)
253        Mode->Flags |= V_INTERLACE;
254
255    if (timing->misc & 0x02)
256	Mode->Flags |= V_PVSYNC;
257    else
258	Mode->Flags |= V_NVSYNC;
259
260    if (timing->misc & 0x01)
261	Mode->Flags |= V_PHSYNC;
262    else
263	Mode->Flags |= V_NHSYNC;
264
265    return Mode;
266}
267
268/*
269 *
270 */
271static void
272DDCGuessRangesFromModes(int scrnIndex, MonPtr Monitor, DisplayModePtr Modes)
273{
274    DisplayModePtr Mode = Modes;
275
276    if (!Monitor || !Modes)
277        return;
278
279    /* set up the ranges for scanning through the modes */
280    Monitor->nHsync = 1;
281    Monitor->hsync[0].lo = 1024.0;
282    Monitor->hsync[0].hi = 0.0;
283
284    Monitor->nVrefresh = 1;
285    Monitor->vrefresh[0].lo = 1024.0;
286    Monitor->vrefresh[0].hi = 0.0;
287
288    while (Mode) {
289        if (!Mode->HSync)
290            Mode->HSync = ((float) Mode->Clock ) / ((float) Mode->HTotal);
291
292        if (!Mode->VRefresh)
293            Mode->VRefresh = (1000.0 * ((float) Mode->Clock)) /
294                ((float) (Mode->HTotal * Mode->VTotal));
295
296        if (Mode->HSync < Monitor->hsync[0].lo)
297            Monitor->hsync[0].lo = Mode->HSync;
298
299        if (Mode->HSync > Monitor->hsync[0].hi)
300            Monitor->hsync[0].hi = Mode->HSync;
301
302        if (Mode->VRefresh < Monitor->vrefresh[0].lo)
303            Monitor->vrefresh[0].lo = Mode->VRefresh;
304
305        if (Mode->VRefresh > Monitor->vrefresh[0].hi)
306            Monitor->vrefresh[0].hi = Mode->VRefresh;
307
308        Mode = Mode->next;
309    }
310}
311
312_X_EXPORT DisplayModePtr
313xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
314{
315    int preferred, i;
316    DisplayModePtr  Modes = NULL, Mode;
317    ddc_quirk_t	    quirks;
318
319    xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n",
320		DDC->vendor.name, DDC->vendor.prod_id);
321    quirks = DDC_QUIRK_NONE;
322    for (i = 0; ddc_quirks[i].detect; i++)
323	if (ddc_quirks[i].detect (scrnIndex, DDC))
324	{
325	    xf86DrvMsg (scrnIndex, X_INFO, "    EDID quirk: %s\n",
326			ddc_quirks[i].description);
327	    quirks |= ddc_quirks[i].quirk;
328	}
329
330    preferred = PREFERRED_TIMING_MODE(DDC->features.msc);
331    if (quirks & DDC_QUIRK_PREFER_LARGE_60)
332	preferred = 0;
333
334    for (i = 0; i < DET_TIMINGS; i++) {
335	struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
336
337        switch (det_mon->type) {
338        case DT:
339            Mode = DDCModeFromDetailedTiming(scrnIndex,
340                                             &det_mon->section.d_timings,
341					     preferred,
342					     quirks);
343	    preferred = 0;
344            Modes = xf86ModesAdd(Modes, Mode);
345            break;
346        case DS_STD_TIMINGS:
347            Mode = DDCModesFromStandardTiming(scrnIndex,
348					      det_mon->section.std_t,
349					      quirks);
350            Modes = xf86ModesAdd(Modes, Mode);
351            break;
352        default:
353            break;
354        }
355    }
356
357    /* Add established timings */
358    Mode = DDCModesFromEstablished(scrnIndex, &DDC->timings1, quirks);
359    Modes = xf86ModesAdd(Modes, Mode);
360
361    /* Add standard timings */
362    Mode = DDCModesFromStandardTiming(scrnIndex, DDC->timings2, quirks);
363    Modes = xf86ModesAdd(Modes, Mode);
364
365    if (quirks & DDC_QUIRK_PREFER_LARGE_60)
366    {
367	DisplayModePtr	best = Modes;
368	for (Mode = Modes; Mode; Mode = Mode->next)
369	{
370	    if (Mode == best) continue;
371	    if (Mode->HDisplay * Mode->VDisplay > best->HDisplay * best->VDisplay)
372	    {
373		best = Mode;
374		continue;
375	    }
376	    if (Mode->HDisplay * Mode->VDisplay == best->HDisplay * best->VDisplay)
377	    {
378		double	mode_refresh = xf86ModeVRefresh (Mode);
379		double	best_refresh = xf86ModeVRefresh (best);
380		double	mode_dist = fabs(mode_refresh - 60.0);
381		double	best_dist = fabs(best_refresh - 60.0);
382		if (mode_dist < best_dist)
383		{
384		    best = Mode;
385		    continue;
386		}
387	    }
388	}
389	if (best)
390	    best->type |= M_T_PREFERRED;
391    }
392    return Modes;
393}
394
395/*
396 * Fill out MonPtr with xf86MonPtr information.
397 */
398_X_EXPORT void
399xf86DDCMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC)
400{
401    DisplayModePtr Modes = NULL, Mode;
402    int i, clock;
403    Bool have_hsync = FALSE, have_vrefresh = FALSE, have_maxpixclock = FALSE;
404
405    if (!Monitor || !DDC)
406        return;
407
408    Monitor->DDC = DDC;
409
410    Monitor->widthmm = 10 * DDC->features.hsize;
411    Monitor->heightmm = 10 * DDC->features.vsize;
412
413    /* If this is a digital display, then we can use reduced blanking */
414    if (DDC->features.input_type)
415        Monitor->reducedblanking = TRUE;
416    /* Allow the user to also enable this through config */
417
418    Modes = xf86DDCGetModes(scrnIndex, DDC);
419
420    /* Skip EDID ranges if they were specified in the config file */
421    have_hsync = (Monitor->nHsync != 0);
422    have_vrefresh = (Monitor->nVrefresh != 0);
423    have_maxpixclock = (Monitor->maxPixClock != 0);
424
425    /* Go through the detailed monitor sections */
426    for (i = 0; i < DET_TIMINGS; i++) {
427        switch (DDC->det_mon[i].type) {
428        case DS_RANGES:
429	    if (!have_hsync) {
430		if (!Monitor->nHsync)
431		    xf86DrvMsg(scrnIndex, X_INFO,
432			    "Using EDID range info for horizontal sync\n");
433		Monitor->hsync[Monitor->nHsync].lo =
434		    DDC->det_mon[i].section.ranges.min_h;
435		Monitor->hsync[Monitor->nHsync].hi =
436		    DDC->det_mon[i].section.ranges.max_h;
437		Monitor->nHsync++;
438	    } else {
439		xf86DrvMsg(scrnIndex, X_INFO,
440			"Using hsync ranges from config file\n");
441	    }
442
443	    if (!have_vrefresh) {
444		if (!Monitor->nVrefresh)
445		    xf86DrvMsg(scrnIndex, X_INFO,
446			    "Using EDID range info for vertical refresh\n");
447		Monitor->vrefresh[Monitor->nVrefresh].lo =
448		    DDC->det_mon[i].section.ranges.min_v;
449		Monitor->vrefresh[Monitor->nVrefresh].hi =
450		    DDC->det_mon[i].section.ranges.max_v;
451		Monitor->nVrefresh++;
452	    } else {
453		xf86DrvMsg(scrnIndex, X_INFO,
454			"Using vrefresh ranges from config file\n");
455	    }
456
457	    clock = DDC->det_mon[i].section.ranges.max_clock * 1000;
458	    if (!have_maxpixclock && clock > Monitor->maxPixClock)
459		Monitor->maxPixClock = clock;
460
461            break;
462        default:
463            break;
464        }
465    }
466
467    if (Modes) {
468        /* Print Modes */
469        xf86DrvMsg(scrnIndex, X_INFO, "Printing DDC gathered Modelines:\n");
470
471        Mode = Modes;
472        while (Mode) {
473            xf86PrintModeline(scrnIndex, Mode);
474            Mode = Mode->next;
475        }
476
477        /* Do we still need ranges to be filled in? */
478        if (!Monitor->nHsync || !Monitor->nVrefresh)
479            DDCGuessRangesFromModes(scrnIndex, Monitor, Modes);
480
481        /* look for last Mode */
482        Mode = Modes;
483
484        while (Mode->next)
485            Mode = Mode->next;
486
487        /* add to MonPtr */
488        if (Monitor->Modes) {
489            Monitor->Last->next = Modes;
490            Modes->prev = Monitor->Last;
491            Monitor->Last = Mode;
492        } else {
493            Monitor->Modes = Modes;
494            Monitor->Last = Mode;
495        }
496    }
497}
498