/* * Copyright 2004-2008 Luc Verhaegen * Copyright 2007, 2008 Matthias Hopf * Copyright 2007, 2008 Egbert Eich * Copyright 2007, 2008 Advanced Micro Devices, Inc. * * 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 COPYRIGHT HOLDER(S) OR 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "xf86.h" #include "xf86DDC.h" #if HAVE_XF86_ANSIC_H # include "xf86_ansic.h" #else # include # include #endif #include "rhd.h" #include "rhd_crtc.h" #include "rhd_pll.h" #include "rhd_connector.h" #include "rhd_output.h" #include "rhd_modes.h" #include "rhd_monitor.h" /* For Acceleration FB validation */ #include "r5xx_accel.h" /* * Don't bother with checking whether X offers this. Just use the internal one * I'm the author of the X side one anyway. */ /* * Generate a CVT standard mode from HDisplay, VDisplay and VRefresh. * * These calculations are stolen from the CVT calculation spreadsheet written * by Graham Loveridge. He seems to be claiming no copyright and there seems to * be no license attached to this. He apparently just wants to see his name * mentioned. * * This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls * * Comments and structure corresponds to the comments and structure of the xls. * This should ease importing of future changes to the standard (not very * likely though). * * About margins; i'm sure that they are to be the bit between HDisplay and * HBlankStart, HBlankEnd and HTotal, VDisplay and VBlankStart, VBlankEnd and * VTotal, where the overscan colour is shown. FB seems to call _all_ blanking * outside sync "margin" for some reason. Since we prefer seeing proper * blanking instead of the overscan colour, and since the Crtc* values will * probably get altered after us, we will disable margins altogether. With * these calculations, Margins will plainly expand H/VDisplay, and we don't * want that. -- libv * */ DisplayModePtr RHDCVTMode(int HDisplay, int VDisplay, float VRefresh, Bool Reduced, Bool Interlaced) { DisplayModeRec *Mode = xnfalloc(sizeof(DisplayModeRec)); /* 1) top/bottom margin size (% of height) - default: 1.8 */ #define CVT_MARGIN_PERCENTAGE 1.8 /* 2) character cell horizontal granularity (pixels) - default 8 */ #define CVT_H_GRANULARITY 1 /* 4) Minimum vertical porch (lines) - default 3 */ #define CVT_MIN_V_PORCH 3 /* 4) Minimum number of vertical back porch lines - default 6 */ #define CVT_MIN_V_BPORCH 6 /* Pixel Clock step (kHz) */ #define CVT_CLOCK_STEP 250 Bool Margins = FALSE; float VFieldRate, HPeriod; int HDisplayRnd, HMargin; int VDisplayRnd, VMargin, VSync; float Interlace; /* Please rename this */ memset(Mode, 0, sizeof(DisplayModeRec)); /* CVT default is 60.0Hz */ if (!VRefresh) VRefresh = 60.0; /* 1. Required field rate */ if (Interlaced) VFieldRate = VRefresh * 2; else VFieldRate = VRefresh; /* 2. Horizontal pixels */ HDisplayRnd = HDisplay - (HDisplay % CVT_H_GRANULARITY); /* 3. Determine left and right borders */ if (Margins) { /* right margin is actually exactly the same as left */ HMargin = (((float) HDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0); HMargin -= HMargin % CVT_H_GRANULARITY; } else HMargin = 0; /* 4. Find total active pixels */ Mode->HDisplay = HDisplayRnd + 2*HMargin; /* 5. Find number of lines per field */ if (Interlaced) VDisplayRnd = VDisplay / 2; else VDisplayRnd = VDisplay; /* 6. Find top and bottom margins */ /* nope. */ if (Margins) /* top and bottom margins are equal again. */ VMargin = (((float) VDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0); else VMargin = 0; Mode->VDisplay = VDisplay + 2*VMargin; /* 7. Interlace */ if (Interlaced) Interlace = 0.5; else Interlace = 0.0; /* Determine VSync Width from aspect ratio */ if (!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay)) VSync = 4; else if (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay)) VSync = 5; else if (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay)) VSync = 6; else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay)) VSync = 7; else if (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay)) VSync = 7; else /* Custom */ VSync = 10; if (!Reduced) { /* simplified GTF calculation */ /* 4) Minimum time of vertical sync + back porch interval (µs) * default 550.0 */ #define CVT_MIN_VSYNC_BP 550.0 /* 3) Nominal HSync width (% of line period) - default 8 */ #define CVT_HSYNC_PERCENTAGE 8 float HBlankPercentage; int VSyncAndBackPorch, VBackPorch; int HBlank; /* 8. Estimated Horizontal period */ HPeriod = ((float) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) / (VDisplayRnd + 2 * VMargin + CVT_MIN_V_PORCH + Interlace); /* 9. Find number of lines in sync + backporch */ if (((int)(CVT_MIN_VSYNC_BP / HPeriod) + 1) < (VSync + CVT_MIN_V_PORCH)) VSyncAndBackPorch = VSync + CVT_MIN_V_PORCH; else VSyncAndBackPorch = (int)(CVT_MIN_VSYNC_BP / HPeriod) + 1; /* 10. Find number of lines in back porch */ VBackPorch = VSyncAndBackPorch - VSync; /* 11. Find total number of lines in vertical field */ Mode->VTotal = VDisplayRnd + 2 * VMargin + VSyncAndBackPorch + Interlace + CVT_MIN_V_PORCH; /* 5) Definition of Horizontal blanking time limitation */ /* Gradient (%/kHz) - default 600 */ #define CVT_M_FACTOR 600 /* Offset (%) - default 40 */ #define CVT_C_FACTOR 40 /* Blanking time scaling factor - default 128 */ #define CVT_K_FACTOR 128 /* Scaling factor weighting - default 20 */ #define CVT_J_FACTOR 20 #define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256 #define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ CVT_J_FACTOR /* 12. Find ideal blanking duty cycle from formula */ HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod/1000.0; /* 13. Blanking time */ if (HBlankPercentage < 20) HBlankPercentage = 20; HBlank = Mode->HDisplay * HBlankPercentage/(100.0 - HBlankPercentage); HBlank -= HBlank % (2*CVT_H_GRANULARITY); /* 14. Find total number of pixels in a line. */ Mode->HTotal = Mode->HDisplay + HBlank; /* Fill in HSync values */ Mode->HSyncEnd = Mode->HDisplay + HBlank / 2; Mode->HSyncStart = Mode->HSyncEnd - (Mode->HTotal * CVT_HSYNC_PERCENTAGE) / 100; Mode->HSyncStart += CVT_H_GRANULARITY - Mode->HSyncStart % CVT_H_GRANULARITY; /* Fill in VSync values */ Mode->VSyncStart = Mode->VDisplay + CVT_MIN_V_PORCH; Mode->VSyncEnd = Mode->VSyncStart + VSync; } else { /* Reduced blanking */ /* Minimum vertical blanking interval time (µs) - default 460 */ #define CVT_RB_MIN_VBLANK 460.0 /* Fixed number of clocks for horizontal sync */ #define CVT_RB_H_SYNC 32.0 /* Fixed number of clocks for horizontal blanking */ #define CVT_RB_H_BLANK 160.0 /* Fixed number of lines for vertical front porch - default 3 */ #define CVT_RB_VFPORCH 3 int VBILines; /* 8. Estimate Horizontal period. */ HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) / (VDisplayRnd + 2*VMargin); /* 9. Find number of lines in vertical blanking */ VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1; /* 10. Check if vertical blanking is sufficient */ if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH)) VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH; /* 11. Find total number of lines in vertical field */ Mode->VTotal = VDisplayRnd + 2 * VMargin + Interlace + VBILines; /* 12. Find total number of pixels in a line */ Mode->HTotal = Mode->HDisplay + CVT_RB_H_BLANK; /* Fill in HSync values */ Mode->HSyncEnd = Mode->HDisplay + CVT_RB_H_BLANK / 2; Mode->HSyncStart = Mode->HSyncEnd - CVT_RB_H_SYNC; /* Fill in VSync values */ Mode->VSyncStart = Mode->VDisplay + CVT_RB_VFPORCH; Mode->VSyncEnd = Mode->VSyncStart + VSync; } /* 15/13. Find pixel clock frequency (kHz for xf86) */ Mode->Clock = Mode->HTotal * 1000.0 / HPeriod; Mode->Clock -= Mode->Clock % CVT_CLOCK_STEP; /* 16/14. Find actual Horizontal Frequency (kHz) */ Mode->HSync = ((float) Mode->Clock) / ((float) Mode->HTotal); /* 17/15. Find actual Field rate */ Mode->VRefresh = (1000.0 * ((float) Mode->Clock)) / ((float) (Mode->HTotal * Mode->VTotal)); /* 18/16. Find actual vertical frame frequency */ /* ignore - just set the mode flag for interlaced */ if (Interlaced) Mode->VTotal *= 2; { char Name[256]; Name[0] = 0; snprintf(Name, 256, "%dx%d", HDisplay, VDisplay); Mode->name = xnfstrdup(Name); } if (Reduced) Mode->Flags |= V_PHSYNC | V_NVSYNC; else Mode->Flags |= V_NHSYNC | V_PVSYNC; if (Interlaced) Mode->Flags |= V_INTERLACE; return Mode; } /* * Temporary. */ static void add(char **p, char *new) { *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2); strcat(*p, " "); strcat(*p, new); } /* * */ Bool rhdModesEqual(DisplayModePtr mode1, DisplayModePtr mode2) { if (mode1->Clock == mode2->Clock && mode1->HDisplay == mode2->HDisplay && mode1->HSyncStart == mode2->HSyncStart && mode1->HSyncEnd == mode2->HSyncEnd && mode1->HTotal == mode2->HTotal && mode1->HSkew == mode2->HSkew && mode1->VDisplay == mode2->VDisplay && mode1->VSyncStart == mode2->VSyncStart && mode1->VSyncEnd == mode2->VSyncEnd && mode1->VTotal == mode2->VTotal && mode1->VScan == mode2->VScan && mode1->Flags == mode2->Flags) return TRUE; return FALSE; } /* * */ void RHDPrintModeline(DisplayModePtr mode) { char tmp[256]; char *flags = xnfcalloc(1, 1); if (mode->HSkew) { snprintf(tmp, 256, "hskew %i", mode->HSkew); add(&flags, tmp); } if (mode->VScan) { snprintf(tmp, 256, "vscan %i", mode->VScan); add(&flags, tmp); } if (mode->Flags & V_INTERLACE) add(&flags, "interlace"); if (mode->Flags & V_CSYNC) add(&flags, "composite"); if (mode->Flags & V_DBLSCAN) add(&flags, "doublescan"); if (mode->Flags & V_BCAST) add(&flags, "bcast"); if (mode->Flags & V_PHSYNC) add(&flags, "+hsync"); if (mode->Flags & V_NHSYNC) add(&flags, "-hsync"); if (mode->Flags & V_PVSYNC) add(&flags, "+vsync"); if (mode->Flags & V_NVSYNC) add(&flags, "-vsync"); if (mode->Flags & V_PCSYNC) add(&flags, "+csync"); if (mode->Flags & V_NCSYNC) add(&flags, "-csync"); #if 0 if (mode->Flags & V_CLKDIV2) add(&flags, "vclk/2"); #endif xf86Msg(X_NONE, "Modeline \"%s\" %6.2f %i %i %i %i %i %i %i %i%s\n", mode->name, ((float)mode->Clock)/1000., mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal, mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal, flags); xfree(flags); } /* * xf86Mode.c should have a some more DisplayModePtr list handling. */ DisplayModePtr RHDModesAdd(DisplayModePtr Modes, DisplayModePtr Additions) { if (!Modes) { if (Additions) return Additions; else return NULL; } if (Additions) { DisplayModePtr Mode = Modes; while (Mode->next) Mode = Mode->next; Mode->next = Additions; Additions->prev = Mode; } return Modes; } /* * */ static DisplayModePtr rhdModeDelete(DisplayModePtr Modes, DisplayModePtr Delete) { DisplayModePtr Next, Previous; if (!Delete) return Modes; if (Modes == Delete) Modes = NULL; if (Delete->next == Delete) Delete->next = NULL; if (Delete->prev == Delete) Delete->next = NULL; Next = Delete->next; Previous = Delete->prev; if (Next) Next->prev = Previous; if (Previous) Previous->next = Next; xfree(Delete->name); xfree(Delete); if (Modes) return Modes; if (Next) return Next; if (Previous) while (Previous->prev) Previous = Previous->prev; return Previous; } /* * */ DisplayModePtr RHDModeCopy(DisplayModePtr Mode) { DisplayModePtr New; if (!Mode) return NULL; New = xnfalloc(sizeof(DisplayModeRec)); memcpy(New, Mode, sizeof(DisplayModeRec)); /* re-use private */ New->name = xnfstrdup(Mode->name); New->prev = NULL; New->next = NULL; New->Private = Mode->Private; New->PrivSize = Mode->PrivSize; return New; } /* * */ static void rhdModesDestroy(DisplayModePtr Modes) { DisplayModePtr mode = Modes, next; while (mode) { next = mode->next; xfree(mode->name); xfree(mode); mode = next; } } /* * Basic sanity checks. */ static int rhdModeSanity(RHDPtr rhdPtr, DisplayModePtr Mode) { /* do we need to bother at all? */ if (Mode->status != MODE_OK) return Mode->status; if (!Mode->name) { xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "Validation found mode without name.\n"); return MODE_ERROR; } if (Mode->Clock <= 0) return MODE_NOCLOCK; if ((Mode->HDisplay <= 0) || (Mode->HSyncStart <= 0) || (Mode->HSyncEnd <= 0) || (Mode->HTotal <= 0)) return MODE_H_ILLEGAL; if ((Mode->HTotal <= Mode->HSyncEnd) || (Mode->HSyncEnd <= Mode->HSyncStart) || (Mode->HSyncStart < Mode->HDisplay)) return MODE_H_ILLEGAL; /* HSkew? */ if ((Mode->VDisplay <= 0) || (Mode->VSyncStart <= 0) || (Mode->VSyncEnd <= 0) || (Mode->VTotal <= 0)) return MODE_V_ILLEGAL; if ((Mode->VTotal <= Mode->VSyncEnd) || (Mode->VSyncEnd <= Mode->VSyncStart) || (Mode->VSyncStart < Mode->VDisplay)) return MODE_V_ILLEGAL; if ((Mode->VScan != 0) && (Mode->VScan != 1)) return MODE_NO_VSCAN; if (Mode->Flags & V_DBLSCAN) return MODE_NO_DBLESCAN; /* Flags */ return MODE_OK; } /* * After we passed the initial sanity check, we need to fill out the CRTC * values. */ static void rhdModeFillOutCrtcValues(DisplayModePtr Mode) { /* do we need to bother at all? */ if (Mode->status != MODE_OK) return; Mode->ClockIndex = -1; /* Always! direct non-programmable support must die. */ if (!Mode->SynthClock) Mode->SynthClock = Mode->Clock; if (!Mode->CrtcHDisplay) Mode->CrtcHDisplay = Mode->HDisplay; if (!Mode->CrtcHBlankStart) Mode->CrtcHBlankStart = Mode->HDisplay; if (!Mode->CrtcHSyncStart) Mode->CrtcHSyncStart = Mode->HSyncStart; if (!Mode->CrtcHSyncEnd) Mode->CrtcHSyncEnd = Mode->HSyncEnd; if (!Mode->CrtcHBlankEnd) Mode->CrtcHBlankEnd = Mode->HTotal; if (!Mode->CrtcHTotal) Mode->CrtcHTotal = Mode->HTotal; if (!Mode->CrtcHSkew) Mode->CrtcHSkew = Mode->HSkew; if (!Mode->CrtcVDisplay) Mode->CrtcVDisplay = Mode->VDisplay; if (!Mode->CrtcVBlankStart) Mode->CrtcVBlankStart = Mode->VDisplay; if (!Mode->CrtcVSyncStart) Mode->CrtcVSyncStart = Mode->VSyncStart; if (!Mode->CrtcVSyncEnd) Mode->CrtcVSyncEnd = Mode->VSyncEnd; if (!Mode->CrtcVBlankEnd) Mode->CrtcVBlankEnd = Mode->VTotal; if (!Mode->CrtcVTotal) Mode->CrtcVTotal = Mode->VTotal; /* Always change these */ Mode->HSync = ((float) Mode->SynthClock) / Mode->CrtcHTotal; Mode->VRefresh = (Mode->SynthClock * 1000.0) / (Mode->CrtcHTotal * Mode->CrtcVTotal); if (Mode->Flags & V_INTERLACE) Mode->VRefresh *= 2.0; if (Mode->Flags & V_DBLSCAN) Mode->VRefresh /= 2.0; /* We're usually first in the chain, right after rhdModeSanity. */ Mode->CrtcHAdjusted = FALSE; Mode->CrtcVAdjusted = FALSE; /* Steer clear of PrivSize, Private and PrivFlags */ } /* * Basic sanity checks. */ static int rhdModeCrtcSanity(DisplayModePtr Mode) { if (Mode->SynthClock <= 0) return MODE_NOCLOCK; if ((Mode->CrtcHDisplay <= 0) || (Mode->CrtcHBlankStart <= 0) || (Mode->CrtcHSyncStart <= 0) || (Mode->CrtcHSyncEnd <= 0) || (Mode->CrtcHBlankEnd <= 0) || (Mode->CrtcHTotal <= 0)) return MODE_H_ILLEGAL; /* there seem to be no alignment constraints on horizontal timing on our hardware here */ if ((Mode->CrtcHTotal < Mode->CrtcHBlankEnd) || (Mode->CrtcHBlankEnd <= Mode->CrtcHSyncEnd) || (Mode->CrtcHSyncEnd <= Mode->CrtcHSyncStart) || (Mode->CrtcHSyncStart < Mode->CrtcHBlankStart) || (Mode->CrtcHBlankStart < Mode->CrtcHDisplay)) return MODE_H_ILLEGAL; /* CrtcHSkew? */ if ((Mode->CrtcVDisplay <= 0) || (Mode->CrtcVBlankStart <= 0) || (Mode->CrtcVSyncStart <= 0) || (Mode->CrtcVSyncEnd <= 0) || (Mode->CrtcVBlankEnd <= 0) || (Mode->CrtcVTotal <= 0)) return MODE_V_ILLEGAL; if ((Mode->CrtcVTotal < Mode->CrtcVBlankEnd) || (Mode->CrtcVBlankEnd <= Mode->CrtcVSyncEnd) || (Mode->CrtcVSyncEnd <= Mode->CrtcVSyncStart) || (Mode->CrtcVSyncStart < Mode->CrtcVBlankStart) || (Mode->CrtcVBlankStart < Mode->CrtcVDisplay)) return MODE_V_ILLEGAL; return MODE_OK; } /* * */ static Bool rhdMonitorFixedValid(struct rhdMonitor *Monitor, DisplayModePtr Mode) { DisplayModePtr Fixed; for (Fixed = Monitor->Modes; Fixed; Fixed = Fixed->next) { if ((Mode->Flags != Fixed->Flags) || (Mode->Clock != Fixed->Clock) || (Mode->SynthClock != Fixed->Clock)) continue; if ((Mode->HDisplay > Fixed->HDisplay) || (Mode->VDisplay > Fixed->VDisplay)) continue; if ((Mode->HSyncStart != Fixed->HSyncStart) || (Mode->HSyncEnd != Fixed->HSyncEnd)) continue; if ((Mode->VSyncStart != Fixed->VSyncStart) || (Mode->VSyncEnd != Fixed->VSyncEnd)) continue; if ((Mode->CrtcHDisplay > Fixed->HDisplay) || (Mode->CrtcVDisplay > Fixed->VDisplay)) continue; if ((Mode->CrtcHBlankStart != Fixed->HDisplay) || (Mode->CrtcHSyncStart != Fixed->HSyncStart) || (Mode->CrtcHSyncEnd != Fixed->HSyncEnd) || (Mode->CrtcHBlankEnd != Fixed->HTotal)) continue; if ((Mode->CrtcVBlankStart != Fixed->VDisplay) || (Mode->CrtcVSyncStart != Fixed->VSyncStart) || (Mode->CrtcVSyncEnd != Fixed->VSyncEnd) || (Mode->CrtcVBlankEnd != Fixed->VTotal)) continue; return TRUE; } return FALSE; } /* * TODO: review fixed modes when doing different modes on both crtcs. */ static int rhdMonitorValid(struct rhdMonitor *Monitor, DisplayModePtr Mode) { int i; Bool isNative = FALSE; if (Monitor->NativeMode && rhdModesEqual(Mode, Monitor->NativeMode)) isNative = TRUE; for (i = 0; i < Monitor->numHSync; i++) if ((Mode->HSync >= (Monitor->HSync[i].lo * (1.0 - SYNC_TOLERANCE))) && (Mode->HSync <= (Monitor->HSync[i].hi * (1.0 + SYNC_TOLERANCE)))) break; if (Monitor->numHSync && (i == Monitor->numHSync)) return MODE_HSYNC; for (i = 0; i < Monitor->numVRefresh; i++) if ((Mode->VRefresh >= (Monitor->VRefresh[i].lo * (1.0 - SYNC_TOLERANCE))) && (Mode->VRefresh <= (Monitor->VRefresh[i].hi * (1.0 + SYNC_TOLERANCE)))) break; if (Monitor->numVRefresh && (i == Monitor->numVRefresh)) return MODE_VSYNC; if (Monitor->Bandwidth && (Mode->SynthClock > (Monitor->Bandwidth * (1 + SYNC_TOLERANCE)))) return MODE_CLOCK_HIGH; if (isNative) { /* if it's this monitor's native mode be less strict on validation */ if (Monitor->ReducedAllowed) { if ((Mode->CrtcHDisplay * 101) > (Mode->CrtcHTotal * 100)) /* 1% */ return MODE_HBLANK_NARROW; } else { /* no reduced blanking */ if ((Mode->CrtcHDisplay * 23) > (Mode->CrtcHTotal * 20)) /* 15% */ return MODE_HBLANK_NARROW; } } else { if (((Mode->CrtcHDisplay * 5 / 4) & ~0x07) > Mode->CrtcHTotal) { /* is this a cvt -r Mode, and only a cvt -r Mode? */ if (((Mode->CrtcHTotal - Mode->CrtcHDisplay) == 160) && ((Mode->CrtcHSyncEnd - Mode->CrtcHDisplay) == 80) && ((Mode->CrtcHSyncEnd - Mode->CrtcHSyncStart) == 32) && ((Mode->CrtcVSyncStart - Mode->CrtcVDisplay) == 3)) { if (!Monitor->ReducedAllowed) return MODE_NO_REDUCED; } else if ((Mode->CrtcHDisplay * 11) > (Mode->CrtcHTotal * 10)) return MODE_HSYNC_NARROW; } } if (Monitor->UseFixedModes && !rhdMonitorFixedValid(Monitor, Mode)) return MODE_FIXED; return MODE_OK; } #define RHD_MODE_VALIDATION_LOOPS 10 enum ValidationKind { VALIDATE_SCALE_NONE, VALIDATE_SCALE_FROM, VALIDATE_SCALE_TO }; /* * */ static int rhdModeValidateCrtc(struct rhdCrtc *Crtc, DisplayModePtr Mode, enum ValidationKind ValidateScaleModeKind) { ScrnInfoPtr pScrn = xf86Screens[Crtc->scrnIndex]; RHDPtr rhdPtr = RHDPTR(pScrn); int Status, i; RHDFUNC(Crtc); Status = rhdModeSanity(rhdPtr, Mode); if (Status != MODE_OK) return Status; rhdModeFillOutCrtcValues(Mode); /* We don't want to loop around this forever */ for (i = 0; i < RHD_MODE_VALIDATION_LOOPS; i++) { struct rhdOutput *Output; Mode->CrtcHAdjusted = FALSE; Mode->CrtcVAdjusted = FALSE; Status = rhdModeCrtcSanity(Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; if (ValidateScaleModeKind != VALIDATE_SCALE_TO) { Status = Crtc->FBValid(Crtc, Mode->CrtcHDisplay, Mode->CrtcVDisplay, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, NULL); if (Status != MODE_OK) return Status; if (Crtc->ScaleValid) { if (ValidateScaleModeKind == VALIDATE_SCALE_NONE) Status = Crtc->ScaleValid(Crtc, RHD_CRTC_SCALE_TYPE_NONE, Mode, NULL); else Status = Crtc->ScaleValid(Crtc, Crtc->ScaleType, Mode, Crtc->ScaledToMode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; } } if (ValidateScaleModeKind != VALIDATE_SCALE_FROM) { Status = Crtc->ModeValid(Crtc, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; if (Crtc->PLL && Crtc->PLL->Valid) { /* RandR may not have PLL filled out. oh well... */ Status = Crtc->PLL->Valid(Crtc->PLL, Mode->Clock); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; } for (Output = rhdPtr->Outputs; Output; Output = Output->Next) if (Output->Active && (Output->Crtc == Crtc)) { /* Check the output */ Status = Output->ModeValid(Output, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) break; /* restart. */ /* Check the monitor attached to this output */ if (Output->Connector && Output->Connector->Monitor) Status = rhdMonitorValid(Output->Connector->Monitor, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) break; /* restart. */ } if (Output) /* We're done. This must be a good mode. */ continue; } return MODE_OK; } /* Mode has been bouncing around for ages, on adjustments */ xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: Mode \"%s\" (%dx%d:%3.1fMhz) was" " thrown around for too long.\n", __func__, Mode->name, Mode->HDisplay, Mode->VDisplay, Mode->Clock/1000.0); return MODE_ERROR; } /* * */ int RHDValidateScaledToMode(struct rhdCrtc *Crtc, DisplayModePtr Mode) { RHDPtr rhdPtr = RHDPTRI(Crtc); int Status; RHDFUNC(Crtc); Status = rhdModeSanity(rhdPtr, Mode); if (Status != MODE_OK) return Status; rhdModeFillOutCrtcValues(Mode); Status = rhdModeValidateCrtc(Crtc, Mode, VALIDATE_SCALE_TO); if (Status != MODE_OK) return Status; /* Do we want to also validate against a configured monitor? */ if (rhdPtr->ConfigMonitor) { Status = rhdMonitorValid(rhdPtr->ConfigMonitor, Mode); if (Status != MODE_OK) return Status; } return MODE_OK; } /* * */ static int rhdModeValidate(ScrnInfoPtr pScrn, DisplayModePtr Mode) { RHDPtr rhdPtr = RHDPTR(pScrn); struct rhdCrtc *Crtc; int Status; int i; Status = rhdModeSanity(rhdPtr, Mode); if (Status != MODE_OK) return Status; rhdModeFillOutCrtcValues(Mode); /* now let our modesetting tree have its say */ for (i = 0; i < 2; i++) { Crtc = rhdPtr->Crtc[i]; if (!Crtc->Active) continue; if (!Crtc->ScaledToMode) { Status = rhdModeValidateCrtc(Crtc, Mode, VALIDATE_SCALE_NONE); if (Status != MODE_OK) return Status; } else { Status = rhdModeValidateCrtc(Crtc, Mode, VALIDATE_SCALE_FROM); if (Status != MODE_OK) return Status; } } /* throw them at the configured monitor, so that the inadequate * conf file at least has some influence. */ if (rhdPtr->ConfigMonitor) { Status = rhdMonitorValid(rhdPtr->ConfigMonitor, Mode); if (Status != MODE_OK) return Status; } /* Did we set up virtual resolution already? */ if ((pScrn->virtualX > 0) && (pScrn->virtualY > 0)) { if (pScrn->virtualX < Mode->CrtcHDisplay) return MODE_VIRTUAL_X; if (pScrn->virtualY < Mode->CrtcVDisplay) return MODE_VIRTUAL_Y; } return MODE_OK; } /* * Wrap the limited xf86 Mode statusses with our own message. */ struct { int Status; char *Message; } rhdModeStatusMessages[] = { { MODE_NO_REDUCED, "Reduced blanking is not supported."}, { MODE_MEM_BW, "Memory bandwidth exceeded."}, { MODE_OUTPUT_UNDEF, "Mode not defined by output device."}, { MODE_NOT_PAL, "This is not a PAL TV mode."}, { MODE_NOT_NTSC, "This is not an NTSC TV mode."}, { MODE_HTOTAL_WIDE, "Horizontal Total is out of range."}, { MODE_HDISPLAY_WIDE, "Mode is too wide."}, { MODE_HSYNC_RANGE, "Horizontal Sync Start is out of range."}, { MODE_HBLANK_RANGE, "Horizontal Blanking Start is out of range."}, { MODE_VTOTAL_WIDE, "Vertical Total is out of range.\n"}, { MODE_VDISPLAY_WIDE, "Mode is too high."}, { MODE_VSYNC_RANGE, "Vertical Sync Start is out of range.\n"}, { MODE_VBLANK_RANGE, "Vertical Blanking Start is out of range."}, { MODE_PITCH, "Scanout buffer Pitch too wide."}, { MODE_OFFSET, "Scanout buffer offset too high in FB."}, { MODE_MINHEIGHT, "Height too low."}, { MODE_FIXED, "Mode not compatible with fixed mode."}, { MODE_SCALE, "Mode cannot be scaled to fixed mode."}, { MODE_NO_ENCODER, "No encoder available for this output."}, { 0, NULL} }; const char * RHDModeStatusToString(int Status) { if ((Status & 0xFFF00) == RHD_MODE_STATUS) { int i; for (i = 0; rhdModeStatusMessages[i].Message; i++) if (rhdModeStatusMessages[i].Status == Status) return rhdModeStatusMessages[i].Message; ErrorF("%s: unhandled Status type: 0x%X\n", __func__, Status); return "Unknown status."; } else return xf86ModeStatusToString(Status); } /* * */ static DisplayModePtr rhdModesGrabOnNameAll(DisplayModePtr *Modes, char *name) { DisplayModePtr Mode, Matched = NULL, Temp; for (Mode = *Modes; Mode; ) { if (!strcmp(Mode->name, name)) { Temp = Mode; Mode = Mode->next; if (Temp->prev) Temp->prev->next = Mode; else *Modes = Mode; if (Mode) Mode->prev = Temp->prev; Temp->prev = NULL; Temp->next = Matched; if (Matched) Matched->prev = Temp; Matched = Temp; } else Mode = Mode->next; } return Matched; } /* * */ static DisplayModePtr rhdModesGrabOnTypeAll(DisplayModePtr *Modes, int Type, int Mask) { DisplayModePtr Mode, Matched = NULL, Temp; for (Mode = *Modes; Mode; ) { if ((Mode->type & Mask) == (Type & Mask)) { Temp = Mode; Mode = Mode->next; if (Temp->prev) Temp->prev->next = Mode; else *Modes = Mode; if (Mode) Mode->prev = Temp->prev; Temp->next = Matched; if (Matched) Matched->prev = Temp; Temp->prev = NULL; Matched = Temp; } else Mode = Mode->next; } return Matched; } /* * */ static DisplayModePtr rhdModesGrabBestRefresh(DisplayModePtr *Modes) { DisplayModePtr Mode, Best = NULL; if (!*Modes) return NULL; Best = *Modes; for (Mode = Best->next; Mode; Mode = Mode->next) if (Best->VRefresh < Mode->VRefresh) Best = Mode; else if (Best->VRefresh == Mode->VRefresh) { /* Same name != same resolution */ if ((Best->HDisplay * Best->VDisplay) < (Mode->HDisplay * Mode->VDisplay)) Best = Mode; else if ((Best->HDisplay * Best->VDisplay) == (Mode->HDisplay * Mode->VDisplay)) { /* Lower bandwidth == better! */ if (Best->Clock > Mode->Clock) Best = Mode; } } if (Best->next) Best->next->prev = Best->prev; if (Best->prev) Best->prev->next = Best->next; if (Best == *Modes) *Modes = (*Modes)->next; Best->next = NULL; Best->prev = NULL; return Best; } /* * */ static DisplayModePtr rhdModesGrabOnHighestType(DisplayModePtr *Modes) { DisplayModePtr Mode; /* User provided, but can also have another source. */ Mode = rhdModesGrabOnTypeAll(Modes, M_T_USERDEF, 0xF0); if (Mode) return Mode; /* Often EDID provided, but can also have another source. */ Mode = rhdModesGrabOnTypeAll(Modes, M_T_DRIVER, 0xF0); if (Mode) return Mode; /* No reason why we should treat built-in and vesa separately */ Mode = *Modes; *Modes = NULL; return Mode; } /* * */ static DisplayModePtr rhdModesSortOnSize(DisplayModePtr Modes) { DisplayModePtr Sorted, Mode, Temp, Next; if (!Modes) return NULL; Sorted = Modes; Modes = Modes->next; Sorted->next = NULL; Sorted->prev = NULL; for (Next = Modes; Next; ) { /* since we're taking modelines from in between */ Mode = Next; Next = Next->next; for (Temp = Sorted; Temp; Temp = Temp->next) { /* nasty ! */ if (((Temp->CrtcHDisplay * Temp->CrtcVDisplay) < (Mode->CrtcHDisplay * Mode->CrtcVDisplay)) || (((Temp->CrtcHDisplay * Temp->CrtcVDisplay) == (Mode->CrtcHDisplay * Mode->CrtcVDisplay)) && ((Temp->VRefresh < Mode->VRefresh) || ((Temp->VRefresh < Mode->VRefresh) && (Temp->SynthClock < Mode->SynthClock))))) { Mode->next = Temp; Mode->prev = Temp->prev; Temp->prev = Mode; if (Mode->prev) Mode->prev->next = Mode; else Sorted = Mode; break; } if (!Temp->next) { Temp->next = Mode; Mode->prev = Temp; Mode->next = NULL; break; } } } return Sorted; } /* * take a modename, try to parse it, if that works, generate the CVT modeline. */ static DisplayModePtr rhdModeCreateFromName(ScrnInfoPtr pScrn, char *name, Bool Silent) { DisplayModePtr Mode; int HDisplay = 0, VDisplay = 0, tmp; float VRefresh = 0; Bool Reduced; int Status; sscanf(name, "%dx%d@%f", &HDisplay, &VDisplay, &VRefresh); if (!HDisplay || !VDisplay) { if (!Silent) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s: Unable to generate " "Modeline for Mode \"%s\"\n", __func__, name); return NULL; } tmp = strlen(name) - 1; if ((name[tmp] == 'r') || (name[tmp] == 'R')) Reduced = TRUE; else Reduced = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Generating Modeline for \"%s\"\n", name); /* First, try a plain CVT mode */ Mode = RHDCVTMode(HDisplay, VDisplay, VRefresh, Reduced, FALSE); xfree(Mode->name); Mode->name = xnfstrdup(name); Mode->type = M_T_USERDEF; Status = rhdModeValidate(pScrn, Mode); if (Status == MODE_OK) return Mode; rhdModesDestroy(Mode); #if 0 /* noscale mode */ /* Now see if we have fixed modes */ for (i = 0; i < 2; i++) { Crtc = rhdPtr->Crtc[i]; if (!Crtc->Active || !Crtc->FixedMode) continue; Mode = RHDModeCopy(Crtc->FixedMode); xfree(Mode->name); Mode->name = xnfstrdup(name); Mode->type = M_T_USERDEF; Mode->HDisplay = HDisplay; Mode->CrtcHDisplay = 0; /* set by validation code */ Mode->VDisplay = VDisplay; Mode->CrtcVDisplay = 0; Status = rhdModeValidate(pScrn, Mode); if (Status == MODE_OK) return Mode; rhdModesDestroy(Mode); } #endif if (!Silent) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Rejected mode \"%s\" " "(%dx%d):\n\t %s\n", name, HDisplay, VDisplay, RHDModeStatusToString(Status)); return NULL; } /* * */ static DisplayModePtr rhdModesListValidateAndCopy(ScrnInfoPtr pScrn, DisplayModePtr Modes, Bool Silent) { DisplayModePtr Keepers = NULL, Check, Mode; int Status; for (Check = Modes; Check; Check = Check->next) { Mode = RHDModeCopy(Check); Status = rhdModeValidate(pScrn, Mode); if (Status == MODE_OK) Keepers = RHDModesAdd(Keepers, Mode); else { if (!Silent) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Rejected mode \"%s\" " "(%dx%d:%3.1fMhz): %s\n", Mode->name, Mode->HDisplay, Mode->VDisplay, Mode->Clock / 1000.0, RHDModeStatusToString(Status)); xfree(Mode->name); xfree(Mode); } } return Keepers; } /* * Create the list of all modes that are currently valid */ static DisplayModePtr rhdCreateModesListAndValidate(ScrnInfoPtr pScrn, Bool Silent) { RHDPtr rhdPtr = RHDPTR(pScrn); DisplayModePtr Keepers = NULL, Modes; struct rhdCrtc *Crtc; struct rhdOutput *Output; int i; RHDFUNC(pScrn); /* Cycle through our monitors list, and find a fixed mode one */ for (i = 0; i < 2; i++) { Crtc = rhdPtr->Crtc[i]; for (Output = rhdPtr->Outputs; Output; Output = Output->Next) { if (Output->Active && (Output->Crtc == Crtc)) { if (Output->Connector && Output->Connector->Monitor && Output->Connector->Monitor->UseFixedModes && !Crtc->ScaledToMode) { Modes = Output->Connector->Monitor->Modes; if (!Silent && Modes) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Validating Fixed" " Modes from Monitor \"%s\"\n\t on Connector" " \"%s\"\n", Output->Connector->Monitor->Name, Output->Connector->Name); Modes = rhdModesListValidateAndCopy(pScrn, Modes, Silent); Keepers = RHDModesAdd(Keepers, Modes); return Keepers; } } } } if (rhdPtr->ConfigMonitor) { /* First Pass, X's own Modes. */ Modes = rhdPtr->ConfigMonitor->Modes; if (!Silent && Modes) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Validating Modes from the " "configured Monitor: \"%s\"\n", pScrn->confScreen->monitor->id); Modes = rhdModesListValidateAndCopy(pScrn, Modes, Silent); Keepers = RHDModesAdd(Keepers, Modes); } /* Cycle through our actual monitors list */ for (i = 0; i < 2; i++) { Crtc = rhdPtr->Crtc[i]; for (Output = rhdPtr->Outputs; Output; Output = Output->Next) { if (Output->Active && (Output->Crtc == Crtc)) { if (Output->Connector && Output->Connector->Monitor) { Modes = Output->Connector->Monitor->Modes; if (!Silent && Modes) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Validating Modes " "from Monitor \"%s\" on \"%s\"\n", Output->Connector->Monitor->Name, Output->Connector->Name); Modes = rhdModesListValidateAndCopy(pScrn, Modes, Silent); Keepers = RHDModesAdd(Keepers, Modes); } } } } return Keepers; } /* * */ DisplayModePtr RHDModesPoolCreate(ScrnInfoPtr pScrn, Bool Silent) { DisplayModePtr Pool = NULL, List, TempList, Temp; char **ModeNames = pScrn->display->modes; int i; RHDFUNC(pScrn); List = rhdCreateModesListAndValidate(pScrn, Silent); if (!List) return List; /* Reduce our list */ if (ModeNames && ModeNames[0]) { /* Find the best matching mode for each name */ for (i = 0; ModeNames[i]; i++) { TempList = rhdModesGrabOnNameAll(&List, ModeNames[i]); if (TempList) { Temp = rhdModesGrabOnHighestType(&TempList); rhdModesDestroy(TempList); TempList = Temp; Temp = rhdModesGrabOnTypeAll(&TempList, M_T_PREFERRED, M_T_PREFERRED); if (Temp) { rhdModesDestroy(TempList); TempList = Temp; } Temp = rhdModesGrabBestRefresh(&TempList); rhdModesDestroy(TempList); } else /* No matching modes found, generate */ Temp = rhdModeCreateFromName(pScrn, ModeNames[i], Silent); if (Temp) Pool = RHDModesAdd(Pool, Temp); } rhdModesDestroy(List); } else { /* No names, just work the list directly */ Temp = rhdModesGrabOnHighestType(&List); rhdModesDestroy(List); List = Temp; while (List) { TempList = rhdModesGrabOnNameAll(&List, List->name); Temp = rhdModesGrabOnTypeAll(&TempList, M_T_PREFERRED, M_T_PREFERRED); if (Temp) { rhdModesDestroy(TempList); TempList = Temp; } Temp = rhdModesGrabBestRefresh(&TempList); rhdModesDestroy(TempList); Pool = RHDModesAdd(Pool, Temp); } /* Sort our list */ TempList = Pool; /* Sort higher priority modes separately */ Pool = rhdModesGrabOnTypeAll(&TempList, M_T_PREFERRED, M_T_PREFERRED); Pool = rhdModesSortOnSize(Pool); TempList = rhdModesSortOnSize(TempList); Pool = RHDModesAdd(Pool, TempList); } return Pool; } /* * */ void RHDModesAttach(ScrnInfoPtr pScrn, DisplayModePtr Modes) { DisplayModePtr Mode = Modes; pScrn->modes = Modes; pScrn->currentMode = Modes; while (Mode->next) { Mode->type = M_T_USERDEF; /* satisfy xf86ZoomViewport */ Mode = Mode->next; } Mode->type = M_T_USERDEF; /* Make our list circular */ Mode->next = pScrn->modes; pScrn->modes->prev = Mode; } /* * */ Bool RHDGetVirtualFromConfig(ScrnInfoPtr pScrn) { RHDPtr rhdPtr = RHDPTR(pScrn); struct rhdCrtc *Crtc1 = rhdPtr->Crtc[0], *Crtc2 = rhdPtr->Crtc[1]; CARD32 VirtualX = pScrn->display->virtualX; CARD32 VirtualY = pScrn->display->virtualY; CARD32 Pitch1, Pitch2; float Ratio = (float) pScrn->display->virtualY / pScrn->display->virtualX; int ret = FALSE; RHDFUNC(pScrn); while (VirtualX && VirtualY) { ret = Crtc1->FBValid(Crtc1, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, &Pitch1); if (ret != MODE_OK) goto shrink; ret = Crtc2->FBValid(Crtc2, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, &Pitch2); if (ret != MODE_OK) goto shrink; if (Pitch1 != Pitch2) goto shrink; /* let 2d acceleration have a say as well */ if (rhdPtr->AccelMethod >= RHD_ACCEL_XAA) if (rhdPtr->ChipSet < RHD_R600) /* badly abstracted, i know */ if (!R5xx2DFBValid(rhdPtr, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, Pitch1)) goto shrink; break; /* must be good then. */ shrink: VirtualX--; VirtualY = Ratio * VirtualX; } if (VirtualX && VirtualY) { pScrn->virtualX = VirtualX; pScrn->virtualY = VirtualY; pScrn->displayWidth = Pitch1; return TRUE; } else return FALSE; } /* * */ void RHDGetVirtualFromModesAndFilter(ScrnInfoPtr pScrn, DisplayModePtr Modes, Bool Silent) { RHDPtr rhdPtr = RHDPTR(pScrn); struct rhdCrtc *Crtc1 = rhdPtr->Crtc[0], *Crtc2 = rhdPtr->Crtc[1]; DisplayModePtr Mode, Next; CARD32 VirtualX = 0; CARD32 VirtualY = 0; CARD32 Pitch1, Pitch2; int ret = FALSE; RHDFUNC(pScrn); /* assert */ if (!Modes) return; Mode = Modes; while (Mode) { if ((Mode->CrtcHDisplay > pScrn->virtualX) || (Mode->CrtcVDisplay > pScrn->virtualY)) { if (Mode->CrtcHDisplay > pScrn->virtualX) VirtualX = Mode->CrtcHDisplay; else VirtualX = pScrn->virtualX; if (Mode->CrtcVDisplay > pScrn->virtualY) VirtualY = Mode->CrtcVDisplay; else VirtualY = pScrn->virtualY; /* Check what Crtc1 thinks this should be. */ ret = Crtc1->FBValid(Crtc1, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, &Pitch1); if (ret != MODE_OK) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s rejected mode \"%s\" " "(%dx%d): %s\n", Crtc1->Name, Mode->name, Mode->HDisplay, Mode->VDisplay, RHDModeStatusToString(ret)); goto rejected; } /* Check what Crtc2 thinks this should be. */ ret = Crtc2->FBValid(Crtc2, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, &Pitch2); if (ret != MODE_OK) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s rejected mode \"%s\" " "(%dx%d): %s\n", Crtc2->Name, Mode->name, Mode->HDisplay, Mode->VDisplay, RHDModeStatusToString(ret)); goto rejected; } /* when needed, check whether this matches our 2D engine as well. */ if (rhdPtr->AccelMethod >= RHD_ACCEL_XAA) if (rhdPtr->ChipSet < RHD_R600) /* badly abstracted, i know */ if (!R5xx2DFBValid(rhdPtr, VirtualX, VirtualY, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, Pitch1)) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D acceleration " "rejected mode \"%s\" (%dx%d).\n", Mode->name, Mode->HDisplay, Mode->VDisplay); goto rejected; } /* mode is perfectly valid FB wise */ Mode = Mode->next; pScrn->virtualX = VirtualX; pScrn->virtualY = VirtualY; pScrn->displayWidth = Pitch1; continue; rejected: Next = Mode->next; Modes = rhdModeDelete(Modes, Mode); Mode = Next; } else Mode = Mode->next; } } /* * RandR entry point: fixup per Crtc and Output (in RandR speech) * Due to misconceptions we might end up fixing *everything* here. */ int RHDRRModeFixup(ScrnInfoPtr pScrn, DisplayModePtr Mode, struct rhdCrtc *Crtc, struct rhdConnector *Connector, struct rhdOutput *Output, struct rhdMonitor *Monitor, Bool ScaledMode) { RHDPtr rhdPtr = RHDPTR(pScrn); int i, Status; ASSERT(Connector); ASSERT(Output); RHDFUNC(Output); Status = rhdModeSanity(rhdPtr, Mode); if (Status != MODE_OK) return Status; rhdModeFillOutCrtcValues(Mode); if (!ScaledMode) { /* We don't want to loop around this forever */ for (i = 0; i < RHD_MODE_VALIDATION_LOOPS; i++) { Mode->CrtcHAdjusted = FALSE; Mode->CrtcVAdjusted = FALSE; /* Sanitize */ Status = rhdModeCrtcSanity(Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; if (Crtc) { /* Check FB */ Status = Crtc->FBValid(Crtc, Mode->CrtcHDisplay, Mode->CrtcVDisplay, pScrn->bitsPerPixel, rhdPtr->FbScanoutStart, rhdPtr->FbScanoutSize, NULL); if (Status != MODE_OK) return Status; if (Crtc->ScaleValid) { Status = Crtc->ScaleValid(Crtc, RHD_CRTC_SCALE_TYPE_NONE, Mode, NULL); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; } /* Check Crtc */ Status = Crtc->ModeValid(Crtc, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; /* Check PLL */ if (Crtc->PLL->Valid) { Status = Crtc->PLL->Valid(Crtc->PLL, Mode->Clock); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; } } /* Check Output */ Status = Output->ModeValid(Output, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; /* Check the monitor attached to this output */ if (Connector->Monitor) Status = rhdMonitorValid(Connector->Monitor, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; /* Seems to be good */ break; } if (i == RHD_MODE_VALIDATION_LOOPS) { /* Mode has been bouncing around for ages, on adjustments */ xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: Mode \"%s\" (%dx%d:%3.1fMhz) was thrown around" " for too long.\n", __func__, Mode->name, Mode->HDisplay, Mode->VDisplay, Mode->Clock/1000.0); return MODE_ERROR; } /* throw them at the configured monitor */ if (Monitor) { Status = rhdMonitorValid(Monitor, Mode); if (Status != MODE_OK) return Status; } } else { if (Crtc) { Status = rhdModeValidateCrtc(Crtc, Mode, VALIDATE_SCALE_FROM); if (Status != MODE_OK) return Status; } } /* Did we set up virtual resolution already? */ if ((pScrn->virtualX > 0) && (pScrn->virtualY > 0)) { if (pScrn->virtualX < Mode->CrtcHDisplay) return MODE_VIRTUAL_X; if (pScrn->virtualY < Mode->CrtcVDisplay) return MODE_VIRTUAL_Y; } return MODE_OK; } /* * RHDRRValidateScaledToMode(): like RHDValidateScaledMode() - but we cannot validate against a CRTC * as this isn't known when this function is called. So at least validate against the 'output' here. */ int RHDRRValidateScaledToMode(struct rhdOutput *Output, DisplayModePtr Mode) { RHDPtr rhdPtr = RHDPTRI(Output); int Status; int i; RHDFUNC(Output); Status = rhdModeSanity(rhdPtr, Mode); if (Status != MODE_OK) return Status; rhdModeFillOutCrtcValues(Mode); for (i = 0; i < RHD_MODE_VALIDATION_LOOPS; i++) { Mode->CrtcHAdjusted = FALSE; Mode->CrtcVAdjusted = FALSE; Status = rhdModeCrtcSanity(Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; /* Check the output */ Status = Output->ModeValid(Output, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; /* restart. */ /* Check the monitor attached to this output */ if (Output->Connector && Output->Connector->Monitor) Status = rhdMonitorValid(Output->Connector->Monitor, Mode); if (Status != MODE_OK) return Status; if (Mode->CrtcHAdjusted || Mode->CrtcVAdjusted) continue; break; } if (i == RHD_MODE_VALIDATION_LOOPS) { /* Mode has been bouncing around for ages, on adjustments */ xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: Mode \"%s\" (%dx%d:%3.1fMhz) was thrown around" " for too long.\n", __func__, Mode->name, Mode->HDisplay, Mode->VDisplay, Mode->Clock/1000.0); return MODE_ERROR; } /* Do we want to also validate against a configured monitor? */ if (rhdPtr->ConfigMonitor) { Status = rhdMonitorValid(rhdPtr->ConfigMonitor, Mode); if (Status != MODE_OK) return Status; } return MODE_OK; } /* * RHDSynthModes(): synthesize CVT modes for well known resolutions. * For now we assume we want reduced modes only. */ void RHDSynthModes(int scrnIndex, DisplayModePtr Mode) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; RHDPtr rhdPtr = RHDPTR(pScrn); DisplayModePtr Tmp; unsigned int i; struct resolution{ int x; int y; } resolution_list[] = { { 320, 200 }, /* CGA */ { 320, 240 }, /* QVGA */ { 640, 480 }, /* VGA */ { 720, 480 }, /* NTSC */ { 854, 480 }, /* WVGA */ { 768, 576 }, /* PAL */ { 800, 600 }, /* SVGA */ { 1024, 768 }, /* XGA */ { 1152, 768 }, { 1280, 720 }, /* HD720 */ { 1280, 960 }, { 1280, 854 }, { 1280, 960 }, { 1280, 1024 }, /* SXGA */ { 1440, 960 }, { 1400, 1050 }, /* SXGA+ */ { 1680, 1050 }, /* WSXGA+ */ { 1600, 1200 }, /* UXGA */ { 1920, 1080 }, /* HD1080 */ { 1920, 1200 }, /* WUXGA */ { 2048, 1536 }, /* QXGA */ { 2560, 1600 }, /* WQXGA */ { 2560, 2048 } /* QSXGA */ }; RHDFUNC(pScrn); for (i = 0; i < (sizeof(resolution_list) / sizeof(struct resolution)); i++) { /* * chances are that the native mode of a display is a CVT mode with 60 Hz. * This will make RandR share the CRTC which is undesireable for scaling. * This we 'tweak' the frequency to be slightly higher. * Don't tell me it's ugly - I know this already. */ Tmp = RHDCVTMode(resolution_list[i].x, resolution_list[i].y, 60.5, TRUE, FALSE); Tmp->status = MODE_OK; rhdModeFillOutCrtcValues(Tmp); xfree(Tmp->name); Tmp->name = xnfalloc(20); snprintf(Tmp->name, 20, "%ix%iScaled",resolution_list[i].x,resolution_list[i].y); Tmp->type = M_T_BUILTIN; if (rhdPtr->verbosity > 6) { xf86DrvMsg(scrnIndex, X_INFO, "%s: Adding Modeline ",__func__); RHDPrintModeline(Tmp); } RHDModesAdd(Mode, Tmp); } } /* * This function may be used to sanitize bogus PANEL modes reported by AtomBIOS. */ void RHDSanitizeModes(int scrnIndex, DisplayModePtr Modes, char *ReportedBy) { while (Modes) { Bool sanitized = FALSE; /* do a little sanitization as some BIOSes seem to report bogus modes */ if (Modes->HTotal <= Modes->HSyncEnd) { Modes->HTotal = Modes->CrtcHTotal = Modes->HSyncEnd + 1; sanitized = TRUE; } if (Modes->VTotal <= Modes->VSyncEnd) { Modes->VTotal = Modes->CrtcVTotal = Modes->VSyncEnd + 1; sanitized = TRUE; } if (Modes->CrtcHBlankEnd <= Modes->CrtcHSyncEnd) { Modes->CrtcHBlankEnd = Modes->CrtcHSyncEnd + 1; sanitized = TRUE; } if (Modes->CrtcVBlankEnd <= Modes->CrtcVSyncEnd) { Modes->CrtcVBlankEnd = Modes->CrtcVSyncEnd + 1; sanitized = TRUE; } if (sanitized) { xf86DrvMsg(scrnIndex, X_WARNING, "Mode %s reported by %s sanitized!\n", Modes->name ? Modes->name : "unnamed",ReportedBy); Modes->HSync = ((float) Modes->Clock) / ((float)Modes->HTotal); Modes->VRefresh = (1000.0 * ((float) Modes->Clock)) / ((float)(((float)Modes->HTotal) * ((float)Modes->VTotal))); } Modes = Modes->next; } }