7c0a5de1e7
git-svn-id: svn://kolibrios.org@1407 a494cfbc-eb01-0410-851d-a64ba20cac60
1953 lines
54 KiB
C
1953 lines
54 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|