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