diff --git a/src/Makefile.am b/src/Makefile.am index da48149e..8970db1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,6 +55,7 @@ i810_drv_la_SOURCES = \ i830_display.h \ i830_driver.c \ i830.h \ + i830_gtf.c \ i830_i2c.c \ i830_io.c \ i830_memory.c \ diff --git a/src/i830.h b/src/i830.h index 26a0063e..59f563fc 100644 --- a/src/i830.h +++ b/src/i830.h @@ -399,9 +399,10 @@ typedef struct _I830Rec { /* [0] is Pipe A, [1] is Pipe B. */ int availablePipes; - int pipeDevices[MAX_DISPLAY_PIPES]; /* [0] is display plane A, [1] is display plane B. */ int planeEnabled[MAX_DISPLAY_PIPES]; + xf86MonPtr pipeMon[MAX_DISPLAY_PIPES]; + DisplayModePtr pipeModes[MAX_DISPLAY_PIPES]; /* Driver phase/state information */ Bool preinit; @@ -593,8 +594,12 @@ extern Rotation I830GetRotation(ScreenPtr pScreen); extern Bool I830RandRInit(ScreenPtr pScreen, int rotation); extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name); -int I830xf86ValidateDDCModes(ScrnInfoPtr pScrn1, char **ppModeName); -int i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName); +/* i830_modes.c */ +int I830ValidateXF86ModeList(ScrnInfoPtr pScrn); + +/* i830_gtf.c */ +DisplayModePtr i830GetGTF(int h_pixels, int v_lines, float freq, + int interlaced, int margins); /* * 12288 is set as the maximum, chosen because it is enough for diff --git a/src/i830_driver.c b/src/i830_driver.c index f3cb186a..75f9e4b7 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -1258,7 +1258,6 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags) int flags24; int i, n; char *s; - ClockRangePtr clockRanges; pointer pVBEModule = NULL; Bool enable, has_lvds; const char *chipname; @@ -2218,50 +2217,7 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags) pI830->MaxClock = 300000; - /* - * Setup the ClockRanges, which describe what clock ranges are available, - * and what sort of modes they can be used for. - */ - clockRanges = xnfcalloc(sizeof(ClockRange), 1); - clockRanges->next = NULL; - /* 25MHz appears to be the smallest that works. */ - clockRanges->minClock = 25000; - clockRanges->maxClock = pI830->MaxClock; - clockRanges->clockIndex = -1; /* programmable */ - clockRanges->interlaceAllowed = TRUE; /* XXX check this */ - clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ - - if ( (pI830->pipe == 1 && pI830->operatingDevices & (PIPE_LFP << 8)) || - (pI830->pipe == 0 && pI830->operatingDevices & PIPE_LFP) ) { - /* If we're outputting to an LFP, use the LFP mode validation that will - * rely on the scaler so that we can display any mode smaller than or the - * same size as the panel. - */ - if (!has_lvds) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Unable to locate panel information in BIOS VBT tables\n"); - PreInitCleanup(pScrn); - return FALSE; - } - n = i830ValidateFPModes(pScrn, pScrn->display->modes); - } else { - I830xf86ValidateDDCModes(pScrn, pScrn->display->modes); - /* XXX minPitch, minHeight are random numbers. */ - n = xf86ValidateModes(pScrn, - pScrn->monitor->Modes, /* availModes */ - pScrn->display->modes, /* modeNames */ - clockRanges, /* clockRanges */ - 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 */ - pI830->FbMapSize, /* apertureSize */ - LOOKUP_BEST_REFRESH /* strategy */); - } + n = I830ValidateXF86ModeList(pScrn); if (n <= 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes.\n"); PreInitCleanup(pScrn); diff --git a/src/i830_gtf.c b/src/i830_gtf.c new file mode 100644 index 00000000..2eff46a9 --- /dev/null +++ b/src/i830_gtf.c @@ -0,0 +1,356 @@ +#define DEBUG_VERB 2 +/* + * Copyright © 2002 David Dawes + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the author(s) shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * the author(s). + * + * Authors: David Dawes + * + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/vbe/vbeModes.c,v 1.6 2002/11/02 01:38:25 dawes Exp $ + */ +/* + * Modified by Alan Hourihane + * to support extended BIOS modes for the Intel chipsets + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "xf86.h" +#include "vbe.h" +#include "vbeModes.h" +#include "i830.h" + +#include + +#define rint(x) floor(x) + +#define MARGIN_PERCENT 1.8 /* % of active vertical image */ +#define CELL_GRAN 8.0 /* assumed character cell granularity */ +#define MIN_PORCH 1 /* minimum front porch */ +#define V_SYNC_RQD 3 /* width of vsync in lines */ +#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */ +#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */ +#define M 600.0 /* blanking formula gradient */ +#define C 40.0 /* blanking formula offset */ +#define K 128.0 /* blanking formula scaling factor */ +#define J 20.0 /* blanking formula scaling factor */ + +/* C' and M' are part of the Blanking Duty Cycle computation */ + +#define C_PRIME (((C - J) * K/256.0) + J) +#define M_PRIME (K/256.0 * M) + +extern const int i830refreshes[]; + +DisplayModePtr +i830GetGTF(int h_pixels, int v_lines, float freq, int interlaced, int margins) +{ + float h_pixels_rnd; + float v_lines_rnd; + float v_field_rate_rqd; + float top_margin; + float bottom_margin; + float interlace; + float h_period_est; + float vsync_plus_bp; + float v_back_porch; + float total_v_lines; + float v_field_rate_est; + float h_period; + float v_field_rate; + float v_frame_rate; + float left_margin; + float right_margin; + float total_active_pixels; + float ideal_duty_cycle; + float h_blank; + float total_pixels; + float pixel_freq; + float h_freq; + + float h_sync; + float h_front_porch; + float v_odd_front_porch_lines; + char modename[20]; + DisplayModePtr m; + + m = xnfcalloc(sizeof(DisplayModeRec), 1); + + + /* 1. In order to give correct results, the number of horizontal + * pixels requested is first processed to ensure that it is divisible + * by the character size, by rounding it to the nearest character + * cell boundary: + * + * [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND]) + */ + + h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN; + + + /* 2. If interlace is requested, the number of vertical lines assumed + * by the calculation must be halved, as the computation calculates + * the number of vertical lines per field. In either case, the + * number of lines is rounded to the nearest integer. + * + * [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0), + * ROUND([V LINES],0)) + */ + + v_lines_rnd = interlaced ? + rint((float) v_lines) / 2.0 : + rint((float) v_lines); + + /* 3. Find the frame rate required: + * + * [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2, + * [I/P FREQ RQD]) + */ + + v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq); + + /* 4. Find number of lines in Top margin: + * + * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y", + * ROUND(([MARGIN%]/100*[V LINES RND]),0), + * 0) + */ + + top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0); + + /* 5. Find number of lines in Bottom margin: + * + * [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y", + * ROUND(([MARGIN%]/100*[V LINES RND]),0), + * 0) + */ + + bottom_margin = margins ? rint(MARGIN_PERCENT/100.0 * v_lines_rnd) : (0.0); + + /* 6. If interlace is required, then set variable [INTERLACE]=0.5: + * + * [INTERLACE]=(IF([INT RQD?]="y",0.5,0)) + */ + + interlace = interlaced ? 0.5 : 0.0; + + /* 7. Estimate the Horizontal period + * + * [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) / + * ([V LINES RND] + (2*[TOP MARGIN (LINES)]) + + * [MIN PORCH RND]+[INTERLACE]) * 1000000 + */ + + h_period_est = (((1.0/v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP/1000000.0)) + / (v_lines_rnd + (2*top_margin) + MIN_PORCH + interlace) + * 1000000.0); + + /* 8. Find the number of lines in V sync + back porch: + * + * [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0) + */ + + vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP/h_period_est); + + /* 9. Find the number of lines in V back porch alone: + * + * [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND] + * + * XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]? + */ + + v_back_porch = vsync_plus_bp - V_SYNC_RQD; + + /* 10. Find the total number of lines in Vertical field period: + * + * [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] + + * [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] + + * [MIN PORCH RND] + */ + + total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp + + interlace + MIN_PORCH; + + /* 11. Estimate the Vertical field frequency: + * + * [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000 + */ + + v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0; + + /* 12. Find the actual horizontal period: + * + * [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST]) + */ + + h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est); + + /* 13. Find the actual Vertical field frequency: + * + * [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000 + */ + + v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0; + + /* 14. Find the Vertical frame frequency: + * + * [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE])) + */ + + v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate; + + /* 15. Find number of pixels in left margin: + * + * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", + * (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 / + * [CELL GRAN RND]),0)) * [CELL GRAN RND], + * 0)) + */ + + left_margin = margins ? + rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : + 0.0; + + /* 16. Find number of pixels in right margin: + * + * [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", + * (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 / + * [CELL GRAN RND]),0)) * [CELL GRAN RND], + * 0)) + */ + + right_margin = margins ? + rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : + 0.0; + + /* 17. Find total number of active pixels in image and left and right + * margins: + * + * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] + + * [RIGHT MARGIN (PIXELS)] + */ + + total_active_pixels = h_pixels_rnd + left_margin + right_margin; + + /* 18. Find the ideal blanking duty cycle from the blanking duty cycle + * equation: + * + * [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000) + */ + + ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0); + + /* 19. Find the number of pixels in the blanking time to the nearest + * double character cell: + * + * [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] * + * [IDEAL DUTY CYCLE] / + * (100-[IDEAL DUTY CYCLE]) / + * (2*[CELL GRAN RND])), 0)) + * * (2*[CELL GRAN RND]) + */ + + h_blank = rint(total_active_pixels * + ideal_duty_cycle / + (100.0 - ideal_duty_cycle) / + (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN); + + /* 20. Find total number of pixels: + * + * [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)] + */ + + total_pixels = total_active_pixels + h_blank; + + /* 21. Find pixel clock frequency: + * + * [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD] + */ + + pixel_freq = total_pixels / h_period; + + /* 22. Find horizontal frequency: + * + * [H FREQ] = 1000 / [H PERIOD] + */ + + h_freq = 1000.0 / h_period; + + + /* Stage 1 computations are now complete; I should really pass + the results to another function and do the Stage 2 + computations, but I only need a few more values so I'll just + append the computations here for now */ + + + + /* 17. Find the number of pixels in the horizontal sync period: + * + * [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] / + * [CELL GRAN RND]),0))*[CELL GRAN RND] + */ + + h_sync = rint(H_SYNC_PERCENT/100.0 * total_pixels / CELL_GRAN) * CELL_GRAN; + + /* 18. Find the number of pixels in the horizontal front porch period: + * + * [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)] + */ + + h_front_porch = (h_blank / 2.0) - h_sync; + + /* 36. Find the number of lines in the odd front porch period: + * + * [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE]) + */ + + v_odd_front_porch_lines = MIN_PORCH + interlace; + + /* finally, pack the results in the DisplayMode struct */ + + m->HDisplay = (int) (h_pixels_rnd); + m->HSyncStart = (int) (h_pixels_rnd + h_front_porch); + m->HSyncEnd = (int) (h_pixels_rnd + h_front_porch + h_sync); + m->HTotal = (int) (total_pixels); + + m->VDisplay = (int) (v_lines_rnd); + m->VSyncStart = (int) (v_lines_rnd + v_odd_front_porch_lines); + m->VSyncEnd = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD); + m->VTotal = (int) (total_v_lines); + + m->Clock = (int)(pixel_freq * 1000); + m->SynthClock = m->Clock; + m->HSync = h_freq; + m->VRefresh = v_frame_rate /* freq */; + + snprintf(modename, sizeof(modename), "%dx%d", m->HDisplay,m->VDisplay); + m->name = xnfstrdup(modename); + + return (m); +} diff --git a/src/i830_modes.c b/src/i830_modes.c index c0c258c2..6f878a5e 100644 --- a/src/i830_modes.c +++ b/src/i830_modes.c @@ -197,7 +197,7 @@ I830xf86SortModes(DisplayModePtr *new, DisplayModePtr *first, } } -DisplayModePtr I830xf86DDCModes(ScrnInfoPtr pScrn) +DisplayModePtr I830GetDDCModes(ScrnInfoPtr pScrn, xf86MonPtr ddc) { DisplayModePtr p; DisplayModePtr last = NULL; @@ -206,7 +206,9 @@ DisplayModePtr I830xf86DDCModes(ScrnInfoPtr pScrn) int count = 0; int j, tmp; char stmp[32]; - xf86MonPtr ddc = pScrn->monitor->DDC; + + if (ddc == NULL) + return NULL; /* Go thru detailed timing table first */ for (j = 0; j < 4; j++) { @@ -327,30 +329,17 @@ DisplayModePtr I830xf86DDCModes(ScrnInfoPtr pScrn) } xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Total of %d mode(s) found.\n", count); + "Total of %d DDC mode(s) found.\n", count); return first; } -static void -i830SetModeToPanelParameters(ScrnInfoPtr pScrn, DisplayModePtr pMode) -{ - I830Ptr pI830 = I830PTR(pScrn); - - pMode->HTotal = pI830->panel_fixed_hactive; - pMode->HSyncStart = pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff; - pMode->HSyncEnd = pMode->HSyncStart + pI830->panel_fixed_hsyncwidth; - pMode->VTotal = pI830->panel_fixed_vactive; - pMode->VSyncStart = pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff; - pMode->VSyncEnd = pMode->VSyncStart + pI830->panel_fixed_vsyncwidth; - pMode->Clock = pI830->panel_fixed_clock; -} - /** * This function returns a default mode for flat panels using the timing * information provided by the BIOS. */ -static DisplayModePtr i830FPNativeMode(ScrnInfoPtr pScrn) +static DisplayModePtr +i830FPNativeMode(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); DisplayModePtr new; @@ -366,7 +355,14 @@ static DisplayModePtr i830FPNativeMode(ScrnInfoPtr pScrn) strcpy(new->name, stmp); new->HDisplay = pI830->PanelXRes; new->VDisplay = pI830->PanelYRes; - i830SetModeToPanelParameters(pScrn, new); + new->HSyncStart = pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff; + new->HSyncEnd = new->HSyncStart + pI830->panel_fixed_hsyncwidth; + new->HTotal = new->HSyncEnd + 1; + new->VSyncStart = pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff; + new->VSyncEnd = new->VSyncStart + pI830->panel_fixed_vsyncwidth; + new->VTotal = new->VSyncEnd + 1; + new->Clock = pI830->panel_fixed_clock; + new->type = M_T_USERDEF; pScrn->virtualX = MAX(pScrn->virtualX, pI830->PanelXRes); @@ -380,10 +376,21 @@ static DisplayModePtr i830FPNativeMode(ScrnInfoPtr pScrn) return new; } -/* FP mode validation routine for using panel fitting. +/** + * FP automatic modelist creation routine for using panel fitting. + * + * Constructs modes for any resolution less than the panel size specified by the + * user, with the user flag set, plus standard VESA mode sizes without the user + * flag set (for randr). + * + * Modes will be faked to use GTF parameters, even though at the time of being + * programmed into the LVDS they'll end up being forced to the panel's fixed + * mode. + * + * \return doubly-linked list of modes. */ -int -i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) +DisplayModePtr +i830GetLVDSModes(ScrnInfoPtr pScrn, char **ppModeName) { I830Ptr pI830 = I830PTR(pScrn); DisplayModePtr last = NULL; @@ -419,14 +426,8 @@ i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) continue; } - new = xnfcalloc(1, sizeof(DisplayModeRec)); - new->name = xnfalloc(strlen(ppModeName[i]) + 1); - strcpy(new->name, ppModeName[i]); - new->HDisplay = width; - new->VDisplay = height; - new->type |= M_T_USERDEF; - - i830SetModeToPanelParameters(pScrn, new); + new = i830GetGTF(width, height, 60.0, FALSE, FALSE); + new->type |= M_T_USERDEF; new->next = NULL; new->prev = last; @@ -437,21 +438,19 @@ i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) if (!first) first = new; - pScrn->display->virtualX = pScrn->virtualX = MAX(pScrn->virtualX, width); - pScrn->display->virtualY = pScrn->virtualY = MAX(pScrn->virtualY, height); count++; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid mode using panel fitting: %s\n", new->name); } - /* If all else fails, add the native mode */ + /* If the user hasn't specified modes, add the native mode */ if (!count) { first = last = i830FPNativeMode(pScrn); if (first) count = 1; } - /* add in all default vesa modes smaller than panel size, used for randr*/ + /* add in all default vesa modes smaller than panel size, used for randr */ for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) { if ((p->HDisplay <= pI830->PanelXRes) && (p->VDisplay <= pI830->PanelYRes)) { tmp = first; @@ -460,13 +459,11 @@ i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) tmp = tmp->next; } if (!tmp) { - new = xnfcalloc(1, sizeof(DisplayModeRec)); - new->name = xnfalloc(strlen(p->name) + 1); - strcpy(new->name, p->name); - new->HDisplay = p->HDisplay; - new->VDisplay = p->VDisplay; - i830SetModeToPanelParameters(pScrn, new); - new->type |= M_T_DEFAULT; + new = i830GetGTF(p->HDisplay, p->VDisplay, 60.0, FALSE, FALSE); + if (ppModeName[i] == NULL) + new->type |= M_T_USERDEF; + else + new->type |= M_T_DEFAULT; new->next = NULL; new->prev = last; @@ -476,166 +473,202 @@ i830ValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) last = new; if (!first) first = new; + + count++; } } } - /* Close the doubly-linked mode list, if we found any usable modes */ - if (last) { - last->next = first; - first->prev = last; - pScrn->modes = first; - } - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Total number of valid FP mode(s) found: %d\n", count); - /* Adjust the display pitch to fit the modes we've come up with. */ - pScrn->displayWidth = MAX(pScrn->displayWidth, pScrn->virtualX); - pScrn->displayWidth = (pScrn->displayWidth + 63) & ~63; - - return count; + return first; } -/* XFree86's xf86ValidateModes routine doesn't work well with DDC modes, - * so here is our own validation routine. +/** + * Allocates and returns a copy of pMode, including pointers within pMode. */ -int I830xf86ValidateDDCModes(ScrnInfoPtr pScrn1, char **ppModeName) +static DisplayModePtr +I830DuplicateMode(DisplayModePtr pMode) { - DisplayModePtr p; - DisplayModePtr last = NULL; - DisplayModePtr first = NULL; - DisplayModePtr ddcModes = NULL; - int count = 0; - int i, width, height; - ScrnInfoPtr pScrn = pScrn1; + DisplayModePtr pNew; - pScrn->virtualX = pScrn1->display->virtualX; - pScrn->virtualY = pScrn1->display->virtualY; + pNew = xnfalloc(sizeof(DisplayModeRec)); + *pNew = *pMode; + pNew->next = NULL; + pNew->prev = NULL; + pNew->name = xnfstrdup(pMode->name); - if (pScrn->monitor->DDC) { - int maxVirtX = pScrn->virtualX; - int maxVirtY = pScrn->virtualY; + return pNew; +} - /* Collect all of the DDC modes */ - first = last = ddcModes = I830xf86DDCModes(pScrn); +/** + * Injects a list of probed modes into another mode list. + * + * Take the doubly-linked list of modes we've probed for the device, and injects + * it into the doubly-linked modeList. We don't need to filter, because the + * eventual call to xf86ValidateModes will do this for us. I think. + */ +int +I830InjectProbedModes(ScrnInfoPtr pScrn, DisplayModePtr modeList, + char **ppModeName, DisplayModePtr addModes) +{ + DisplayModePtr last = modeList; + DisplayModePtr first = modeList; + DisplayModePtr addMode; + int count = 0; - for (p = ddcModes; p; p = p->next) { + for (addMode = addModes; addMode != NULL; addMode = addMode->next) { + DisplayModePtr pNew; - maxVirtX = MAX(maxVirtX, p->HDisplay); - maxVirtY = MAX(maxVirtY, p->VDisplay); - count++; + /* XXX: Do we need to check if modeList already contains the same mode? + */ - last = p; + pNew = I830DuplicateMode(addMode); + /* If the user didn't specify any modes, mark all modes as M_T_USERDEF + * so that we can cycle through them, etc. XXX: really need to? + */ + if (ppModeName[0] == NULL) { + pNew->type |= M_T_USERDEF; } - /* Match up modes that are specified in the XF86Config file */ - if (ppModeName[0]) { - DisplayModePtr next; - - /* Reset the max virtual dimensions */ - maxVirtX = pScrn->virtualX; - maxVirtY = pScrn->virtualY; - - /* Reset list */ - first = last = NULL; - - for (i = 0; ppModeName[i]; i++) { - /* FIXME: Use HDisplay and VDisplay instead of mode string */ - if (sscanf(ppModeName[i], "%dx%d", &width, &height) == 2) { - for (p = ddcModes; p; p = next) { - next = p->next; - - if (p->HDisplay == width && p->VDisplay == height) { - /* We found a DDC mode that matches the one - requested in the XF86Config file */ - p->type |= M_T_USERDEF; - - /* Update the max virtual setttings */ - maxVirtX = MAX(maxVirtX, width); - maxVirtY = MAX(maxVirtY, height); - - /* Unhook from DDC modes */ - if (p->prev) p->prev->next = p->next; - if (p->next) p->next->prev = p->prev; - if (p == ddcModes) ddcModes = p->next; - - /* Add to used modes */ - if (last) { - last->next = p; - p->prev = last; - } else { - first = p; - p->prev = NULL; - } - p->next = NULL; - last = p; - - break; - } - } - } - } - - /* - * Add remaining DDC modes if they're smaller than the user - * specified modes - */ - for (p = ddcModes; p; p = next) { - next = p->next; - if (p->HDisplay <= maxVirtX && p->VDisplay <= maxVirtY) { - /* Unhook from DDC modes */ - if (p->prev) p->prev->next = p->next; - if (p->next) p->next->prev = p->prev; - if (p == ddcModes) ddcModes = p->next; - - /* Add to used modes */ - if (last) { - last->next = p; - p->prev = last; - } else { - first = p; - p->prev = NULL; - } - p->next = NULL; - last = p; - } - } - - /* Delete unused modes */ - while (ddcModes) - xf86DeleteMode(&ddcModes, ddcModes); + /* Insert pNew into modeList */ + if (last) { + last->next = pNew; + pNew->prev = last; } else { - /* - * No modes were configured, so we make the DDC modes - * available for the user to cycle through. - */ - for (p = ddcModes; p; p = p->next) - p->type |= M_T_USERDEF; + first = pNew; + pNew->prev = NULL; } + pNew->next = NULL; + last = pNew; - pScrn->virtualX = pScrn->display->virtualX = maxVirtX; - pScrn->virtualY = pScrn->display->virtualY = maxVirtY; - } - - /* Close the doubly-linked mode list, if we found any usable modes */ - if (last) { - DisplayModePtr temp = NULL; - /* we should add these to pScrn monitor modes */ - last->next = pScrn->monitor->Modes; - temp = pScrn->monitor->Modes->prev; - pScrn->monitor->Modes->prev = first; - pScrn->monitor->Modes->prev = last; - - first->prev = temp; - if (temp) - temp->next = first; - - pScrn->monitor->Modes = first; + count++; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Total number of valid DDC mode(s) found: %d\n", count); + "Injected %d modes detected from the monitor\n", count); return count; } + +/** + * Performs probing of modes available on the output connected to the given + * pipe. + * + * We do not support multiple outputs per pipe (since the cases for that are + * sufficiently rare we can't imagine the complexity being worth it), so + * the pipe is a sufficient specifier. + */ +static void +I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) +{ + I830Ptr pI830 = I830PTR(pScrn); + int output_index = -1; + int i; + int outputs; + + while (pI830->pipeModes[pipe] != NULL) + xf86DeleteMode(&pI830->pipeModes[pipe], pI830->pipeModes[pipe]); + + if (pipe == 0) + outputs = pI830->operatingDevices & 0xff; + else + outputs = (pI830->operatingDevices >> 8) & 0xff; + + for (i = 0; i < MAX_OUTPUTS; i++) { + switch (pI830->output[i].type) { + case I830_OUTPUT_ANALOG: + if (outputs & PIPE_CRT) { + output_index = i; + } + break; + case I830_OUTPUT_LVDS: + if (outputs & PIPE_LFP) { + output_index = i; + } + break; + case I830_OUTPUT_SDVO: + if (outputs & PIPE_DFP) { + output_index = i; + } + break; + } + } + /* XXX: If there's no output associated with the pipe, bail for now. */ + if (output_index == -1) + return; + + if (outputs & PIPE_LFP) { + pI830->pipeMon[pipe] = NULL; /* XXX */ + pI830->pipeModes[pipe] = i830GetLVDSModes(pScrn, + pScrn->display->modes); + } else if (pI830->output[output_index].pDDCBus != NULL) { + /* XXX: Free the mon */ + pI830->pipeMon[pipe] = xf86DoEDID_DDC2(pScrn->scrnIndex, + pI830->output[output_index].pDDCBus); + pI830->pipeModes[pipe] = I830GetDDCModes(pScrn, + pI830->pipeMon[pipe]); + } else { + ErrorF("don't know how to get modes for this device.\n"); + } +} + +/** + * Probes for video modes on attached otuputs, and assembles a list to insert + * into pScrn. + */ +int +I830ValidateXF86ModeList(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + ClockRangePtr clockRanges; + int n, pipe; + + for (pipe = 0; pipe < MAX_DISPLAY_PIPES; pipe++) { + I830ReprobePipeModeList(pScrn, pipe); + } + + /* XXX: Clean out modes previously injected by our driver */ + + if (pI830->pipeModes[0] != NULL) { + I830InjectProbedModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, pI830->pipeModes[0]); + } + if (pI830->pipeModes[1] != NULL) { + I830InjectProbedModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, pI830->pipeModes[1]); + } + + /* + * Set up the ClockRanges, which describe what clock ranges are available, + * and what sort of modes they can be used for. + */ + clockRanges = xnfcalloc(sizeof(ClockRange), 1); + clockRanges->next = NULL; + clockRanges->minClock = 25000; + clockRanges->maxClock = pI830->MaxClock; + clockRanges->clockIndex = -1; /* programmable */ + clockRanges->interlaceAllowed = TRUE; /* XXX check this */ + clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ + + /* Take the pScrn->monitor->Modes we've accumulated and validate them into + * pScrn->modes. + */ + n = xf86ValidateModes(pScrn, + pScrn->monitor->Modes, /* availModes */ + pScrn->display->modes, /* modeNames */ + clockRanges, /* clockRanges */ + 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 */ + pI830->FbMapSize, /* apertureSize */ + LOOKUP_BEST_REFRESH /* strategy */); + + return n; +}