diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c index 170c921..60562df 100644 --- a/hw/xfree86/modes/xf86Crtc.c +++ b/hw/xfree86/modes/xf86Crtc.c @@ -2132,8 +2132,12 @@ xf86MonPtr xf86OutputGetEDID (xf86OutputPtr output, I2CBusPtr pDDCBus) { ScrnInfoPtr scrn = output->scrn; + xf86MonPtr mon; - return xf86DoEDID_DDC2 (scrn->scrnIndex, pDDCBus); + mon = xf86DoEDID_DDC2 (scrn->scrnIndex, pDDCBus); + xf86DDCApplyQuirks (scrn->scrnIndex, pDDCBus); + + return mon; } static char *_xf86ConnectorNames[] = { "None", "VGA", "DVI-I", "DVI-D", diff --git a/hw/xfree86/modes/xf86EdidModes.c b/hw/xfree86/modes/xf86EdidModes.c index 908593b..24f93fe 100644 --- a/hw/xfree86/modes/xf86EdidModes.c +++ b/hw/xfree86/modes/xf86EdidModes.c @@ -54,6 +54,16 @@ typedef enum { 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, + /* Detailed timing descriptors incorrectly give values in cm instead of + * mm + */ + DDC_QUIRK_DETAILED_SIZE_IN_CM = 1 << 3, + /* Detailed timing descriptors have bogus size values, so just take the + * maximum size and use that. + */ + DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE = 1 << 4 } ddc_quirk_t; static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC) @@ -81,6 +91,36 @@ static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC) return FALSE; } +static Bool quirk_prefer_large_75 (int scrnIndex, xf86MonPtr DDC) +{ + /* Bug #11603: Funai Electronics PM36B */ + if (memcmp (DDC->vendor.name, "FCM", 4) == 0 && + DDC->vendor.prod_id == 13600) + return TRUE; + + return FALSE; +} + +static Bool quirk_detailed_size_in_cm (int scrnIndex, xf86MonPtr DDC) +{ + /* Bug #11603: Funai Electronics PM36B */ + if (memcmp (DDC->vendor.name, "FCM", 4) == 0 && + DDC->vendor.prod_id == 13600) + return TRUE; + + return FALSE; +} + +static Bool quirk_detailed_use_maximum_size (int scrnIndex, xf86MonPtr DDC) +{ + /* Bug #10304: LGPhilipsLCD LP154W01-A5 */ + if (memcmp (DDC->vendor.name, "LPL", 4) == 0 && + DDC->vendor.prod_id == 0) + return TRUE; + + return FALSE; +} + static Bool quirk_135_clock_too_high (int scrnIndex, xf86MonPtr DDC) { /* Envision Peripherals, Inc. EN-7100e. See bug #9550. */ @@ -106,6 +146,18 @@ static const ddc_quirk_map_t ddc_quirks[] = { quirk_135_clock_too_high, DDC_QUIRK_135_CLOCK_TOO_HIGH, "Recommended 135MHz pixel clock is too high" }, + { + quirk_prefer_large_75, DDC_QUIRK_PREFER_LARGE_75, + "Detailed timing is not preferred, use largest mode at 75Hz" + }, + { + quirk_detailed_size_in_cm, DDC_QUIRK_DETAILED_SIZE_IN_CM, + "Detailed timings give sizes in cm." + }, + { + quirk_detailed_use_maximum_size, DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE, + "Detailed timings give sizes in cm." + }, { NULL, DDC_QUIRK_NONE, "No known quirks" @@ -303,27 +355,113 @@ DDCGuessRangesFromModes(int scrnIndex, MonPtr Monitor, DisplayModePtr Modes) } } -DisplayModePtr -xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) +static ddc_quirk_t +xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC) { - int preferred, i; - DisplayModePtr Modes = NULL, Mode; - ddc_quirk_t quirks; + ddc_quirk_t quirks; + int i; - xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n", - DDC->vendor.name, DDC->vendor.prod_id); quirks = DDC_QUIRK_NONE; - for (i = 0; ddc_quirks[i].detect; i++) + for (i = 0; ddc_quirks[i].detect; i++) { if (ddc_quirks[i].detect (scrnIndex, DDC)) { xf86DrvMsg (scrnIndex, X_INFO, " EDID quirk: %s\n", ddc_quirks[i].description); quirks |= ddc_quirks[i].quirk; } - + } + + return quirks; +} + +/** + * Applies monitor-specific quirks to the decoded EDID information. + * + * Note that some quirks applying to the mode list are still implemented in + * xf86DDCGetModes. + */ +void +xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC) +{ + ddc_quirk_t quirks = xf86DDCDetectQuirks (scrnIndex, DDC); + int i; + + if (quirks & DDC_QUIRK_DETAILED_SIZE_IN_CM) { + for (i = 0; i < DET_TIMINGS; i++) { + struct detailed_monitor_section *det_mon = &DDC->det_mon[i]; + + if (det_mon->type != DT) + continue; + + det_mon->section.d_timings.h_size *= 10; + det_mon->section.d_timings.v_size *= 10; + } + } + + if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { + for (i = 0; i < DET_TIMINGS; i++) { + struct detailed_monitor_section *det_mon = &DDC->det_mon[i]; + + if (det_mon->type != DT) + continue; + + det_mon->section.d_timings.h_size = 10 * DDC->features.hsize; + det_mon->section.d_timings.v_size = 10 * DDC->features.vsize; + } + } +} + +/** + * Walks the modes list, finding the mode with the largest area which is + * closest to the target refresh rate, and marks it as the only preferred mode. + */ +static void +xf86DDCSetPreferredRefresh(int scrnIndex, DisplayModePtr modes, + float target_refresh) +{ + DisplayModePtr mode, best = modes; + + for (mode = modes; mode; mode = mode->next) + { + mode->type &= ~M_T_PREFERRED; + + if (mode == best) continue; + + if (mode->HDisplay * mode->VDisplay > + best->HDisplay * best->VDisplay) + { + best = mode; + continue; + } + if (mode->HDisplay * mode->VDisplay == + best->HDisplay * best->VDisplay) + { + double mode_refresh = xf86ModeVRefresh (mode); + double best_refresh = xf86ModeVRefresh (best); + double mode_dist = fabs(mode_refresh - target_refresh); + double best_dist = fabs(best_refresh - target_refresh); + if (mode_dist < best_dist) + { + best = mode; + continue; + } + } + } + if (best) + best->type |= M_T_PREFERRED; +} + +DisplayModePtr +xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) +{ + int preferred, i; + DisplayModePtr Modes = NULL, Mode; + ddc_quirk_t quirks = xf86DDCDetectQuirks (scrnIndex, DDC); + + xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n", + DDC->vendor.name, DDC->vendor.prod_id); + preferred = PREFERRED_TIMING_MODE(DDC->features.msc); - if (quirks & DDC_QUIRK_PREFER_LARGE_60) - preferred = 0; for (i = 0; i < DET_TIMINGS; i++) { struct detailed_monitor_section *det_mon = &DDC->det_mon[i]; @@ -357,32 +495,11 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) Modes = xf86ModesAdd(Modes, Mode); if (quirks & DDC_QUIRK_PREFER_LARGE_60) - { - DisplayModePtr best = Modes; - for (Mode = Modes; Mode; Mode = Mode->next) - { - if (Mode == best) continue; - if (Mode->HDisplay * Mode->VDisplay > best->HDisplay * best->VDisplay) - { - best = Mode; - continue; - } - if (Mode->HDisplay * Mode->VDisplay == best->HDisplay * best->VDisplay) - { - double mode_refresh = xf86ModeVRefresh (Mode); - double best_refresh = xf86ModeVRefresh (best); - double mode_dist = fabs(mode_refresh - 60.0); - double best_dist = fabs(best_refresh - 60.0); - if (mode_dist < best_dist) - { - best = Mode; - continue; - } - } - } - if (best) - best->type |= M_T_PREFERRED; - } + xf86DDCSetPreferredRefresh(scrnIndex, Modes, 60); + + if (quirks & DDC_QUIRK_PREFER_LARGE_75) + xf86DDCSetPreferredRefresh(scrnIndex, Modes, 60); + return Modes; } diff --git a/hw/xfree86/modes/xf86Modes.h b/hw/xfree86/modes/xf86Modes.h index 2bd4ede..3722d25 100644 --- a/hw/xfree86/modes/xf86Modes.h +++ b/hw/xfree86/modes/xf86Modes.h @@ -95,4 +95,7 @@ xf86GetMonitorModes (ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor); DisplayModePtr xf86GetDefaultModes (Bool interlaceAllowed, Bool doubleScanAllowed); +void +xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC); + #endif /* _XF86MODES_H_ */