diff options
-rw-r--r-- | src/i830.h | 2 | ||||
-rw-r--r-- | src/i830_display.c | 38 | ||||
-rw-r--r-- | src/i830_driver.c | 6 | ||||
-rw-r--r-- | src/i830_modes.c | 264 | ||||
-rw-r--r-- | src/i830_randr.c | 3 |
5 files changed, 303 insertions, 10 deletions
@@ -595,7 +595,7 @@ extern Bool I830RandRInit(ScreenPtr pScreen, int rotation); extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name); /* i830_modes.c */ -int I830ValidateXF86ModeList(ScrnInfoPtr pScrn); +int I830ValidateXF86ModeList(ScrnInfoPtr pScrn, Bool first_time); /* i830_gtf.c */ DisplayModePtr i830GetGTF(int h_pixels, int v_lines, float freq, diff --git a/src/i830_display.c b/src/i830_display.c index 8843f989..50f49402 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -262,6 +262,44 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) int refclk, pixel_clock, sdvo_pixel_multiply; int outputs; + assert(pMode->VRefresh != 0.0); + /* If we've got a list of modes probed for the device, find the best match + * in there to the requested mode. + */ + if (pI830->pipeModes[pipe] != NULL) { + DisplayModePtr pBest = NULL, pScan; + + assert(pScan->VRefresh != 0.0); + for (pScan = pI830->pipeModes[pipe]; pScan != NULL; pScan = pScan->next) + { + /* Reject if it's larger than the desired mode. */ + if (pScan->HDisplay > pMode->HDisplay || + pScan->VDisplay > pMode->VDisplay) + { + continue; + } + if (pBest == NULL) { + pBest = pScan; + continue; + } + /* Find if it's closer than the current best option */ + if (abs(pScan->VRefresh - pMode->VRefresh) > + abs(pBest->VRefresh - pMode->VRefresh)) + { + continue; + } + } + if (pBest != NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Choosing pipe's mode %p (%dx%dx%.1f) instead of xf86 " + "mode %p (%dx%dx%.1f)\n", pBest, + pBest->HDisplay, pBest->VDisplay, pBest->VRefresh, + pMode, + pMode->HDisplay, pMode->VDisplay, pMode->VRefresh); + pMode = pBest; + } + } + ErrorF("Requested pix clock: %d\n", pMode->Clock); if (pipe == 0) diff --git a/src/i830_driver.c b/src/i830_driver.c index 75f9e4b7..32beb8a5 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -2217,23 +2217,19 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags) pI830->MaxClock = 300000; - n = I830ValidateXF86ModeList(pScrn); + n = I830ValidateXF86ModeList(pScrn, TRUE); if (n <= 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes.\n"); PreInitCleanup(pScrn); return FALSE; } - xf86PruneDriverModes(pScrn); - if (pScrn->modes == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n"); PreInitCleanup(pScrn); return FALSE; } - xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V); - /* * Fix up modes to make hblank start at hsync start. * I don't know why the xf86 code mangles this... diff --git a/src/i830_modes.c b/src/i830_modes.c index 6f878a5e..faa843e3 100644 --- a/src/i830_modes.c +++ b/src/i830_modes.c @@ -1,6 +1,7 @@ #define DEBUG_VERB 2 /* * Copyright © 2002 David Dawes + * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -26,6 +27,7 @@ * the author(s). * * Authors: David Dawes <dawes@xfree86.org> + * Eric Anholt <eric.anholt@intel.com> * * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/vbe/vbeModes.c,v 1.6 2002/11/02 01:38:25 dawes Exp $ */ @@ -197,6 +199,30 @@ I830xf86SortModes(DisplayModePtr *new, DisplayModePtr *first, } } +/** + * Calculates the vertical refresh of a mode. + * + * Taken directly from xf86Mode.c, and should be put back there --Eric Anholt + */ +static double +I830ModeVRefresh(DisplayModePtr mode) +{ + double refresh = 0.0; + + if (mode->VRefresh > 0.0) + refresh = mode->VRefresh; + else if (mode->HTotal > 0 && mode->VTotal > 0) { + refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal; + if (mode->Flags & V_INTERLACE) + refresh *= 2.0; + if (mode->Flags & V_DBLSCAN) + refresh /= 2.0; + if (mode->VScan > 1) + refresh /= (float)(mode->VScan); + } + return refresh; +} + DisplayModePtr I830GetDDCModes(ScrnInfoPtr pScrn, xf86MonPtr ddc) { DisplayModePtr p; @@ -552,6 +578,97 @@ I830InjectProbedModes(ScrnInfoPtr pScrn, DisplayModePtr modeList, return count; } +/* + * I830xf86SetModeCrtc + * + * Initialises the Crtc parameters for a mode. The initialisation includes + * adjustments for interlaced and double scan modes. + * + * Taken directly from xf86Mode.c:xf86SetModeCrtc -- Eric Anholt + * (and it should be put back there!) + */ +static void +I830xf86SetModeCrtc(DisplayModePtr p, int adjustFlags) +{ + if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN)) + return; + + p->CrtcHDisplay = p->HDisplay; + p->CrtcHSyncStart = p->HSyncStart; + p->CrtcHSyncEnd = p->HSyncEnd; + p->CrtcHTotal = p->HTotal; + p->CrtcHSkew = p->HSkew; + p->CrtcVDisplay = p->VDisplay; + p->CrtcVSyncStart = p->VSyncStart; + p->CrtcVSyncEnd = p->VSyncEnd; + p->CrtcVTotal = p->VTotal; + if (p->Flags & V_INTERLACE) { + if (adjustFlags & INTERLACE_HALVE_V) { + p->CrtcVDisplay /= 2; + p->CrtcVSyncStart /= 2; + p->CrtcVSyncEnd /= 2; + p->CrtcVTotal /= 2; + } + /* Force interlaced modes to have an odd VTotal */ + /* maybe we should only do this when INTERLACE_HALVE_V is set? */ + p->CrtcVTotal |= 1; + } + + if (p->Flags & V_DBLSCAN) { + p->CrtcVDisplay *= 2; + p->CrtcVSyncStart *= 2; + p->CrtcVSyncEnd *= 2; + p->CrtcVTotal *= 2; + } + if (p->VScan > 1) { + p->CrtcVDisplay *= p->VScan; + p->CrtcVSyncStart *= p->VScan; + p->CrtcVSyncEnd *= p->VScan; + p->CrtcVTotal *= p->VScan; + } + p->CrtcHAdjusted = FALSE; + p->CrtcVAdjusted = FALSE; + + /* + * XXX + * + * The following is taken from VGA, but applies to other cores as well. + */ + p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay); + p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal); + if ((p->CrtcVBlankEnd - p->CrtcVBlankStart) >= 127) { + /* + * V Blanking size must be < 127. + * Moving blank start forward is safer than moving blank end + * back, since monitors clamp just AFTER the sync pulse (or in + * the sync pulse), but never before. + */ + p->CrtcVBlankStart = p->CrtcVBlankEnd - 127; + /* + * If VBlankStart is now > VSyncStart move VBlankStart + * to VSyncStart using the maximum width that fits into + * VTotal. + */ + if (p->CrtcVBlankStart > p->CrtcVSyncStart) { + p->CrtcVBlankStart = p->CrtcVSyncStart; + p->CrtcVBlankEnd = min(p->CrtcHBlankStart + 127, p->CrtcVTotal); + } + } + p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay); + p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal); + + if ((p->CrtcHBlankEnd - p->CrtcHBlankStart) >= 63 * 8) { + /* + * H Blanking size must be < 63*8. Same remark as above. + */ + p->CrtcHBlankStart = p->CrtcHBlankEnd - 63 * 8; + if (p->CrtcHBlankStart > p->CrtcHSyncStart) { + p->CrtcHBlankStart = p->CrtcHSyncStart; + p->CrtcHBlankEnd = min(p->CrtcHBlankStart + 63 * 8, p->CrtcHTotal); + } + } +} + /** * Performs probing of modes available on the output connected to the given * pipe. @@ -567,6 +684,7 @@ I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) int output_index = -1; int i; int outputs; + DisplayModePtr pMode; while (pI830->pipeModes[pipe] != NULL) xf86DeleteMode(&pI830->pipeModes[pipe], pI830->pipeModes[pipe]); @@ -609,26 +727,108 @@ I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) pI830->output[output_index].pDDCBus); pI830->pipeModes[pipe] = I830GetDDCModes(pScrn, pI830->pipeMon[pipe]); + + for (pMode = pI830->pipeModes[pipe]; pMode != NULL; pMode = pMode->next) + { + I830xf86SetModeCrtc(pMode, INTERLACE_HALVE_V); + pMode->VRefresh = I830ModeVRefresh(pMode); + } } else { ErrorF("don't know how to get modes for this device.\n"); } + + /* Set the vertical refresh, which is used by the choose-best-mode-per-pipe + * code later on. + */ + for (pMode = pI830->pipeModes[pipe]; pMode != NULL; pMode = pMode->next) { + pMode->VRefresh = I830ModeVRefresh(pMode); + } } /** + * This function removes a mode from a list of modes. It should probably be + * moved to xf86Mode.c + * + * There are different types of mode lists: + * + * - singly linked linear lists, ending in NULL + * - doubly linked linear lists, starting and ending in NULL + * - doubly linked circular lists + * + */ + +static void +I830xf86DeleteModeFromList(DisplayModePtr *modeList, DisplayModePtr mode) +{ + /* Catch the easy/insane cases */ + if (modeList == NULL || *modeList == NULL || mode == NULL) + return; + + /* If the mode is at the start of the list, move the start of the list */ + if (*modeList == mode) + *modeList = mode->next; + + /* If mode is the only one on the list, set the list to NULL */ + if ((mode == mode->prev) && (mode == mode->next)) { + *modeList = NULL; + } else { + if ((mode->prev != NULL) && (mode->prev->next == mode)) + mode->prev->next = mode->next; + if ((mode->next != NULL) && (mode->next->prev == mode)) + mode->next->prev = mode->prev; + } +} + +/** * Probes for video modes on attached otuputs, and assembles a list to insert * into pScrn. + * + * \param first_time indicates that the memory layout has already been set up, + * so displayWidth, virtualX, and virtualY shouldn't be touched. + * + * A SetMode must follow this call in order for operatingDevices to match the + * hardware's state, in case we detect a new output device. */ int -I830ValidateXF86ModeList(ScrnInfoPtr pScrn) +I830ValidateXF86ModeList(ScrnInfoPtr pScrn, Bool first_time) { I830Ptr pI830 = I830PTR(pScrn); ClockRangePtr clockRanges; int n, pipe; + DisplayModePtr saved_mode; + int saved_virtualX = 0, saved_virtualY = 0, saved_displayWidth = 0; for (pipe = 0; pipe < MAX_DISPLAY_PIPES; pipe++) { I830ReprobePipeModeList(pScrn, pipe); } + /* If we've got a spare pipe, try to detect if a new CRT has been plugged + * in. + */ + if ((pI830->operatingDevices & (PIPE_CRT | (PIPE_CRT << 8))) == 0) { + if ((pI830->operatingDevices & 0xff) == PIPE_NONE) { + pI830->operatingDevices |= PIPE_CRT; + I830ReprobePipeModeList(pScrn, 0); + if (pI830->pipeModes[0] == NULL) { + /* No new output found. */ + pI830->operatingDevices &= ~PIPE_CRT; + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Enabled new CRT on pipe A\n"); + } + } else if (((pI830->operatingDevices >> 8) & 0xff) == PIPE_NONE) { + pI830->operatingDevices |= PIPE_CRT << 8; + I830ReprobePipeModeList(pScrn, 1); + if (pI830->pipeModes[1] == NULL) { + /* No new output found. */ + pI830->operatingDevices &= ~(PIPE_CRT << 8); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Enabled new CRT on pipe B\n"); + } + } + } + /* XXX: Clean out modes previously injected by our driver */ if (pI830->pipeModes[0] != NULL) { @@ -652,23 +852,79 @@ I830ValidateXF86ModeList(ScrnInfoPtr pScrn) clockRanges->interlaceAllowed = TRUE; /* XXX check this */ clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ + /* Remove the current mode from the modelist if we're re-validating, so we + * can find a new mode to map ourselves to afterwards. + */ + saved_mode = pI830->currentMode; + if (saved_mode != NULL) { + I830xf86DeleteModeFromList(&pScrn->modes, saved_mode); + } + + if (!first_time) { + saved_virtualX = pScrn->virtualX; + saved_virtualY = pScrn->virtualY; + saved_displayWidth = pScrn->displayWidth; + } + /* Take the pScrn->monitor->Modes we've accumulated and validate them into * pScrn->modes. + * XXX: Should set up a scrp->monitor->DDC covering the union of the + * capabilities of our pipes. */ n = xf86ValidateModes(pScrn, pScrn->monitor->Modes, /* availModes */ pScrn->display->modes, /* modeNames */ clockRanges, /* clockRanges */ - NULL, /* linePitches */ + !first_time ? &pScrn->displayWidth : NULL, /* linePitches */ 320, /* minPitch */ MAX_DISPLAY_PITCH, /* maxPitch */ 64 * pScrn->bitsPerPixel, /* pitchInc */ 200, /* minHeight */ MAX_DISPLAY_HEIGHT, /* maxHeight */ - pScrn->display->virtualX, /* virtualX */ - pScrn->display->virtualY, /* virtualY */ + pScrn->virtualX, /* virtualX */ + pScrn->virtualY, /* virtualY */ pI830->FbMapSize, /* apertureSize */ LOOKUP_BEST_REFRESH /* strategy */); + if (!first_time) { + /* Restore things that may have been damaged by xf86ValidateModes. */ + pScrn->virtualX = saved_virtualX; + pScrn->virtualY = saved_virtualY; + pScrn->displayWidth = saved_displayWidth; + } + + /* Need to do xf86CrtcForModes so any user-configured modes are valid for + * non-LVDS. + */ + xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V); + + xf86PruneDriverModes(pScrn); + + /* Try to find the closest equivalent of the previous mode pointer to switch + * to. + */ + if (saved_mode != NULL) { + DisplayModePtr pBestMode = NULL, pMode; + + /* XXX: Is finding a matching x/y res enough? probably not. */ + for (pMode = pScrn->modes; ; pMode = pMode->next) { + if (pMode->HDisplay == saved_mode->HDisplay && + pMode->VDisplay == saved_mode->VDisplay) + { + ErrorF("found matching mode %p\n", pMode); + pBestMode = pMode; + } + if (pMode->next == pScrn->modes) + break; + } + + if (pBestMode != NULL) + xf86SwitchMode(pScrn->pScreen, pBestMode); + else + FatalError("No suitable modes after re-probe\n"); + + xfree(saved_mode->name); + xfree(saved_mode); + } return n; } diff --git a/src/i830_randr.c b/src/i830_randr.c index 0311f2b6..93c05198 100644 --- a/src/i830_randr.c +++ b/src/i830_randr.c @@ -79,6 +79,9 @@ I830RandRGetInfo (ScreenPtr pScreen, Rotation *rotations) randrp->virtualY = scrp->virtualY; } + /* Re-probe the outputs for new monitors or modes */ + I830ValidateXF86ModeList(scrp, FALSE); + for (mode = scrp->modes; ; mode = mode->next) { int refresh = I830RandRModeRefresh (mode); |