Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1028 → Rev 1029

/drivers/video/radeonhd/rhd_modes.c
0,0 → 1,1952
/*
* Copyright 2004-2008 Luc Verhaegen <lverhaegen@novell.com>
* Copyright 2007, 2008 Matthias Hopf <mhopf@novell.com>
* Copyright 2007, 2008 Egbert Eich <eich@novell.com>
* 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"
#if HAVE_XF86_ANSIC_H
# include "xf86_ansic.h"
#else
# include <stdio.h>
# include <string.h>
#endif
 
 
#include "rhd.h"
 
#include "edid.h"
#include "xf86DDC.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"
 
/*
* Define a set of own mode errors.
*/
#define RHD_MODE_STATUS 0x51B00
#ifndef MONREC_HAS_REDUCED
#define MODE_NO_REDUCED 0x01 + RHD_MODE_STATUS
#endif
#define MODE_MEM_BW 0x02 + RHD_MODE_STATUS
#define MODE_OUTPUT_UNDEF 0x03 + RHD_MODE_STATUS
#define MODE_NOT_PAL 0x04 + RHD_MODE_STATUS
#define MODE_NOT_NTSC 0x05 + RHD_MODE_STATUS
#define MODE_HTOTAL_WIDE 0x06 + RHD_MODE_STATUS
#define MODE_HDISPLAY_WIDE 0x07 + RHD_MODE_STATUS
#define MODE_HSYNC_RANGE 0x08 + RHD_MODE_STATUS
#define MODE_HBLANK_RANGE 0x09 + RHD_MODE_STATUS
#define MODE_VTOTAL_WIDE 0x0A + RHD_MODE_STATUS
#define MODE_VDISPLAY_WIDE 0x0B + RHD_MODE_STATUS
#define MODE_VSYNC_RANGE 0x0C + RHD_MODE_STATUS
#define MODE_VBLANK_RANGE 0x0D + RHD_MODE_STATUS
#define MODE_PITCH 0x0E + RHD_MODE_STATUS
#define MODE_OFFSET 0x0F + RHD_MODE_STATUS
#define MODE_MINHEIGHT 0x10 + RHD_MODE_STATUS
#define MODE_FIXED 0x11 + RHD_MODE_STATUS
#define MODE_SCALE 0x12 + RHD_MODE_STATUS
 
/*
* 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 = strdup(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)
{
// char *tmp = kmalloc(strlen(*p) + strlen(new) + 2);
 
*p = (char*)realloc(*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, 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 = strdup(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)
{
RHDPtr rhdPtr = RHDPTRI(Crtc);
ScrnInfoPtr pScrn = 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 * xf86ModeStatusToString(ModeStatus status)
{
switch (status) {
case MODE_OK:
return "Mode OK";
case MODE_HSYNC:
return "hsync out of range";
case MODE_VSYNC:
return "vrefresh out of range";
case MODE_H_ILLEGAL:
return "illegal horizontal timings";
case MODE_V_ILLEGAL:
return "illegal vertical timings";
case MODE_BAD_WIDTH:
return "width requires unsupported line pitch";
case MODE_NOMODE:
return "no mode of this name";
case MODE_NO_INTERLACE:
return "interlace mode not supported";
case MODE_NO_DBLESCAN:
return "doublescan mode not supported";
case MODE_NO_VSCAN:
return "multiscan mode not supported";
case MODE_MEM:
return "insufficient memory for mode";
case MODE_VIRTUAL_X:
return "width too large for virtual size";
case MODE_VIRTUAL_Y:
return "height too large for virtual size";
case MODE_MEM_VIRT:
return "insufficient memory given virtual size";
case MODE_NOCLOCK:
return "no clock available for mode";
case MODE_CLOCK_HIGH:
return "mode clock too high";
case MODE_CLOCK_LOW:
return "mode clock too low";
case MODE_CLOCK_RANGE:
return "bad mode clock/interlace/doublescan";
case MODE_BAD_HVALUE:
return "horizontal timing out of range";
case MODE_BAD_VVALUE:
return "vertical timing out of range";
case MODE_BAD_VSCAN:
return "VScan value out of range";
case MODE_HSYNC_NARROW:
return "horizontal sync too narrow";
case MODE_HSYNC_WIDE:
return "horizontal sync too wide";
case MODE_HBLANK_NARROW:
return "horizontal blanking too narrow";
case MODE_HBLANK_WIDE:
return "horizontal blanking too wide";
case MODE_VSYNC_NARROW:
return "vertical sync too narrow";
case MODE_VSYNC_WIDE:
return "vertical sync too wide";
case MODE_VBLANK_NARROW:
return "vertical blanking too narrow";
case MODE_VBLANK_WIDE:
return "vertical blanking too wide";
case MODE_PANEL:
return "exceeds panel dimensions";
case MODE_INTERLACE_WIDTH:
return "width too large for interlaced mode";
case MODE_ONE_WIDTH:
return "all modes must have the same width";
case MODE_ONE_HEIGHT:
return "all modes must have the same height";
case MODE_ONE_SIZE:
return "all modes must have the same resolution";
case MODE_BAD:
return "unknown reason";
case MODE_ERROR:
return "internal error";
default:
return "unknown";
}
}
 
 
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;
}
 
#if 0
/*
* 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;
}
#endif
 
/*
*
*/
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;
}
}
}
}
 
 
/* 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 = NULL; //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;
}
 
/*
*
*/
#if 0
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;
#if 0
/* 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;
#endif
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 0
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;
}
#endif
/* 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;
}
#endif
 
/*
* 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)
{
RHDPtr rhdPtr = (RHDPtr)(scrnIndex);
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);
}
}