1054 lines
30 KiB
C
1054 lines
30 KiB
C
#define DEBUG_VERB 2
|
|
/*
|
|
* Copyright © 2002 David Dawes
|
|
* Copyright © 2006 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of the author(s) shall
|
|
* not be used in advertising or otherwise to promote the sale, use or other
|
|
* dealings in this Software without prior written authorization from
|
|
* the author(s).
|
|
*
|
|
* Authors: David Dawes <dawes@xfree86.org>
|
|
* Eric Anholt <eric.anholt@intel.com>
|
|
*
|
|
* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/vbe/vbeModes.c,v 1.6 2002/11/02 01:38:25 dawes Exp $
|
|
*/
|
|
/*
|
|
* Modified by Alan Hourihane <alanh@tungstengraphics.com>
|
|
* to support extended BIOS modes for the Intel chipsets
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "xf86.h"
|
|
#include "i830.h"
|
|
#include "i830_display.h"
|
|
#include "i830_xf86Modes.h"
|
|
#include <randrstr.h>
|
|
|
|
#define rint(x) floor(x)
|
|
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
#define MARGIN_PERCENT 1.8 /* % of active vertical image */
|
|
#define CELL_GRAN 8.0 /* assumed character cell granularity */
|
|
#define MIN_PORCH 1 /* minimum front porch */
|
|
#define V_SYNC_RQD 3 /* width of vsync in lines */
|
|
#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */
|
|
#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
|
|
#define M 600.0 /* blanking formula gradient */
|
|
#define C 40.0 /* blanking formula offset */
|
|
#define K 128.0 /* blanking formula scaling factor */
|
|
#define J 20.0 /* blanking formula scaling factor */
|
|
|
|
/* C' and M' are part of the Blanking Duty Cycle computation */
|
|
|
|
#define C_PRIME (((C - J) * K/256.0) + J)
|
|
#define M_PRIME (K/256.0 * M)
|
|
/* Established timings from EDID standard */
|
|
static struct
|
|
{
|
|
int hsize;
|
|
int vsize;
|
|
int refresh;
|
|
} est_timings[] = {
|
|
{1280, 1024, 75},
|
|
{1024, 768, 75},
|
|
{1024, 768, 70},
|
|
{1024, 768, 60},
|
|
{1024, 768, 87},
|
|
{832, 624, 75},
|
|
{800, 600, 75},
|
|
{800, 600, 72},
|
|
{800, 600, 60},
|
|
{800, 600, 56},
|
|
{640, 480, 75},
|
|
{640, 480, 72},
|
|
{640, 480, 67},
|
|
{640, 480, 60},
|
|
{720, 400, 88},
|
|
{720, 400, 70},
|
|
};
|
|
|
|
#define DEBUG_REPROBE 1
|
|
|
|
extern const int i830refreshes[];
|
|
|
|
void
|
|
I830PrintModes(ScrnInfoPtr scrp)
|
|
{
|
|
DisplayModePtr p;
|
|
float hsync, refresh = 0;
|
|
char *desc, *desc2, *prefix, *uprefix;
|
|
|
|
if (scrp == NULL)
|
|
return;
|
|
|
|
xf86DrvMsg(scrp->scrnIndex, scrp->virtualFrom, "Virtual size is %dx%d "
|
|
"(pitch %d)\n", scrp->virtualX, scrp->virtualY,
|
|
scrp->displayWidth);
|
|
|
|
p = scrp->modes;
|
|
if (p == NULL)
|
|
return;
|
|
|
|
do {
|
|
desc = desc2 = "";
|
|
if (p->HSync > 0.0)
|
|
hsync = p->HSync;
|
|
else if (p->HTotal > 0)
|
|
hsync = (float)p->Clock / (float)p->HTotal;
|
|
else
|
|
hsync = 0.0;
|
|
if (p->VTotal > 0)
|
|
refresh = hsync * 1000.0 / p->VTotal;
|
|
if (p->Flags & V_INTERLACE) {
|
|
refresh *= 2.0;
|
|
desc = " (I)";
|
|
}
|
|
if (p->Flags & V_DBLSCAN) {
|
|
refresh /= 2.0;
|
|
desc = " (D)";
|
|
}
|
|
if (p->VScan > 1) {
|
|
refresh /= p->VScan;
|
|
desc2 = " (VScan)";
|
|
}
|
|
if (p->VRefresh > 0.0)
|
|
refresh = p->VRefresh;
|
|
if (p->type & M_T_BUILTIN)
|
|
prefix = "Built-in mode";
|
|
else if (p->type & M_T_DEFAULT)
|
|
prefix = "Default mode";
|
|
else
|
|
prefix = "Mode";
|
|
if (p->type & M_T_USERDEF)
|
|
uprefix = "*";
|
|
else
|
|
uprefix = " ";
|
|
if (p->name)
|
|
xf86DrvMsg(scrp->scrnIndex, X_CONFIG,
|
|
"%s%s \"%s\"\n", uprefix, prefix, p->name);
|
|
else
|
|
xf86DrvMsg(scrp->scrnIndex, X_PROBED,
|
|
"%s%s %dx%d (unnamed)\n",
|
|
uprefix, prefix, p->HDisplay, p->VDisplay);
|
|
p = p->next;
|
|
} while (p != NULL && p != scrp->modes);
|
|
}
|
|
|
|
/**
|
|
* Allocates and returns a copy of pMode, including pointers within pMode.
|
|
*/
|
|
static DisplayModePtr
|
|
I830DuplicateMode(DisplayModePtr pMode)
|
|
{
|
|
DisplayModePtr pNew;
|
|
|
|
pNew = xnfalloc(sizeof(DisplayModeRec));
|
|
*pNew = *pMode;
|
|
pNew->next = NULL;
|
|
pNew->prev = NULL;
|
|
pNew->name = xnfstrdup(pMode->name);
|
|
|
|
return pNew;
|
|
}
|
|
|
|
/* This function will sort all modes according to their resolution.
|
|
* Highest resolution first.
|
|
*/
|
|
static void
|
|
I830xf86SortModes(DisplayModePtr new, DisplayModePtr *first,
|
|
DisplayModePtr *last)
|
|
{
|
|
DisplayModePtr p;
|
|
|
|
p = *last;
|
|
while (p) {
|
|
if (((new->HDisplay < p->HDisplay) &&
|
|
(new->VDisplay < p->VDisplay)) ||
|
|
((new->HDisplay == p->HDisplay) &&
|
|
(new->VDisplay == p->VDisplay) &&
|
|
(new->Clock < p->Clock))) {
|
|
|
|
if (p->next)
|
|
p->next->prev = new;
|
|
new->prev = p;
|
|
new->next = p->next;
|
|
p->next = new;
|
|
if (!(new->next))
|
|
*last = new;
|
|
break;
|
|
}
|
|
if (!p->prev) {
|
|
new->prev = NULL;
|
|
new->next = p;
|
|
p->prev = new;
|
|
*first = new;
|
|
break;
|
|
}
|
|
p = p->prev;
|
|
}
|
|
|
|
if (!*first) {
|
|
*first = new;
|
|
new->prev = NULL;
|
|
new->next = NULL;
|
|
*last = new;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a new pointer to a VESA established mode.
|
|
*
|
|
* \param i index into the VESA established modes table.
|
|
*/
|
|
static DisplayModePtr
|
|
I830GetVESAEstablishedMode(ScrnInfoPtr pScrn, int i)
|
|
{
|
|
DisplayModePtr pMode;
|
|
|
|
for (pMode = I830xf86DefaultModes; pMode->name != NULL; pMode++)
|
|
{
|
|
if (pMode->HDisplay == est_timings[i].hsize &&
|
|
pMode->VDisplay == est_timings[i].vsize &&
|
|
fabs(i830xf86ModeVRefresh(pMode) - est_timings[i].refresh) < 1.0)
|
|
{
|
|
DisplayModePtr pNew = I830DuplicateMode(pMode);
|
|
pNew->VRefresh = i830xf86ModeVRefresh(pMode);
|
|
return pNew;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static DisplayModePtr
|
|
i830GetDDCModes(ScrnInfoPtr pScrn, xf86MonPtr ddc)
|
|
{
|
|
DisplayModePtr last = NULL;
|
|
DisplayModePtr new = NULL;
|
|
DisplayModePtr first = NULL;
|
|
int count = 0;
|
|
int j, tmp;
|
|
char stmp[32];
|
|
|
|
if (ddc == NULL)
|
|
return NULL;
|
|
|
|
/* Go thru detailed timing table first */
|
|
for (j = 0; j < 4; j++) {
|
|
if (ddc->det_mon[j].type == 0) {
|
|
struct detailed_timings *d_timings =
|
|
&ddc->det_mon[j].section.d_timings;
|
|
|
|
if (d_timings->h_active == 0 || d_timings->v_active == 0) break;
|
|
|
|
new = xnfcalloc(1, sizeof (DisplayModeRec));
|
|
memset(new, 0, sizeof (DisplayModeRec));
|
|
|
|
new->HDisplay = d_timings->h_active;
|
|
new->VDisplay = d_timings->v_active;
|
|
|
|
sprintf(stmp, "%dx%d", new->HDisplay, new->VDisplay);
|
|
new->name = xnfalloc(strlen(stmp) + 1);
|
|
strcpy(new->name, stmp);
|
|
|
|
new->HTotal = new->HDisplay + d_timings->h_blanking;
|
|
new->HSyncStart = new->HDisplay + d_timings->h_sync_off;
|
|
new->HSyncEnd = new->HSyncStart + d_timings->h_sync_width;
|
|
new->VTotal = new->VDisplay + d_timings->v_blanking;
|
|
new->VSyncStart = new->VDisplay + d_timings->v_sync_off;
|
|
new->VSyncEnd = new->VSyncStart + d_timings->v_sync_width;
|
|
new->Clock = d_timings->clock / 1000;
|
|
new->Flags = (d_timings->interlaced ? V_INTERLACE : 0);
|
|
new->status = MODE_OK;
|
|
new->type = M_T_DEFAULT;
|
|
|
|
if (d_timings->sync == 3) {
|
|
switch (d_timings->misc) {
|
|
case 0: new->Flags |= V_NHSYNC | V_NVSYNC; break;
|
|
case 1: new->Flags |= V_PHSYNC | V_NVSYNC; break;
|
|
case 2: new->Flags |= V_NHSYNC | V_PVSYNC; break;
|
|
case 3: new->Flags |= V_PHSYNC | V_PVSYNC; break;
|
|
}
|
|
}
|
|
count++;
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Valid Mode from Detailed timing table: %s (ht %d hss %d hse %d vt %d vss %d vse %d)\n",
|
|
new->name,
|
|
new->HTotal, new->HSyncStart, new->HSyncEnd,
|
|
new->VTotal, new->VSyncStart, new->VSyncEnd);
|
|
|
|
I830xf86SortModes(new, &first, &last);
|
|
}
|
|
}
|
|
|
|
/* Search thru standard VESA modes from EDID */
|
|
for (j = 0; j < 8; j++) {
|
|
if (ddc->timings2[j].hsize == 0 || ddc->timings2[j].vsize == 0)
|
|
continue;
|
|
#if 1
|
|
new = i830GetGTF(ddc->timings2[j].hsize, ddc->timings2[j].vsize,
|
|
ddc->timings2[j].refresh, FALSE, FALSE);
|
|
new->status = MODE_OK;
|
|
new->type |= M_T_DEFAULT;
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Valid Mode from standard timing table: %s\n",
|
|
new->name);
|
|
|
|
I830xf86SortModes(new, &first, &last);
|
|
#else
|
|
for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) {
|
|
|
|
/* Ignore all double scan modes */
|
|
if ((ddc->timings2[j].hsize == p->HDisplay) &&
|
|
(ddc->timings2[j].vsize == p->VDisplay)) {
|
|
float refresh =
|
|
(float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
|
|
float err = (float)ddc->timings2[j].refresh - refresh;
|
|
|
|
if (err < 0) err = -err;
|
|
if (err < 1.0) {
|
|
/* Is this good enough? */
|
|
new = xnfcalloc(1, sizeof (DisplayModeRec));
|
|
memcpy(new, p, sizeof(DisplayModeRec));
|
|
new->name = xnfalloc(strlen(p->name) + 1);
|
|
strcpy(new->name, p->name);
|
|
new->status = MODE_OK;
|
|
new->type = M_T_DEFAULT;
|
|
|
|
count++;
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Valid Mode from standard timing table: %s\n",
|
|
new->name);
|
|
|
|
I830xf86SortModes(new, &first, &last);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Search thru established modes from EDID */
|
|
tmp = (ddc->timings1.t1 << 8) | ddc->timings1.t2;
|
|
for (j = 0; j < 16; j++) {
|
|
if (tmp & (1 << j)) {
|
|
new = I830GetVESAEstablishedMode(pScrn, j);
|
|
if (new == NULL) {
|
|
ErrorF("Couldn't get established mode %d\n", j);
|
|
continue;
|
|
}
|
|
new->status = MODE_OK;
|
|
new->type = M_T_DEFAULT;
|
|
|
|
count++;
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid Mode from established "
|
|
"timing table: %s\n", new->name);
|
|
|
|
I830xf86SortModes(new, &first, &last);
|
|
}
|
|
}
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Total of %d DDC mode(s) found.\n", count);
|
|
|
|
return first;
|
|
}
|
|
|
|
/**
|
|
* This function returns a default mode for flat panels using the timing
|
|
* information provided by the BIOS.
|
|
*/
|
|
static DisplayModePtr
|
|
i830FPNativeMode(ScrnInfoPtr pScrn)
|
|
{
|
|
I830Ptr pI830 = I830PTR(pScrn);
|
|
DisplayModePtr new;
|
|
char stmp[32];
|
|
|
|
if (pI830->PanelXRes == 0 || pI830->PanelYRes == 0)
|
|
return NULL;
|
|
|
|
/* Add native panel size */
|
|
new = xnfcalloc(1, sizeof (DisplayModeRec));
|
|
sprintf(stmp, "%dx%d", pI830->PanelXRes, pI830->PanelYRes);
|
|
new->name = xnfalloc(strlen(stmp) + 1);
|
|
strcpy(new->name, stmp);
|
|
new->HDisplay = pI830->PanelXRes;
|
|
new->VDisplay = pI830->PanelYRes;
|
|
new->HSyncStart = pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff;
|
|
new->HSyncEnd = new->HSyncStart + pI830->panel_fixed_hsyncwidth;
|
|
new->HTotal = new->HSyncEnd + 1;
|
|
new->VSyncStart = pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff;
|
|
new->VSyncEnd = new->VSyncStart + pI830->panel_fixed_vsyncwidth;
|
|
new->VTotal = new->VSyncEnd + 1;
|
|
new->Clock = pI830->panel_fixed_clock;
|
|
|
|
new->type = M_T_USERDEF;
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"No valid mode specified, force to native mode\n");
|
|
|
|
return new;
|
|
}
|
|
|
|
/**
|
|
* FP automatic modelist creation routine for using panel fitting.
|
|
*
|
|
* Constructs modes for any resolution less than the panel size specified by the
|
|
* user, with the user flag set, plus standard VESA mode sizes without the user
|
|
* flag set (for randr).
|
|
*
|
|
* Modes will be faked to use GTF parameters, even though at the time of being
|
|
* programmed into the LVDS they'll end up being forced to the panel's fixed
|
|
* mode.
|
|
*
|
|
* \return doubly-linked list of modes.
|
|
*/
|
|
static DisplayModePtr
|
|
i830GetLVDSModes(ScrnInfoPtr pScrn, char **ppModeName)
|
|
{
|
|
I830Ptr pI830 = I830PTR(pScrn);
|
|
DisplayModePtr last = NULL;
|
|
DisplayModePtr new = NULL;
|
|
DisplayModePtr first = NULL;
|
|
DisplayModePtr p, tmp;
|
|
int count = 0;
|
|
int i, width, height;
|
|
|
|
/* We have a flat panel connected to the primary display, and we
|
|
* don't have any DDC info.
|
|
*/
|
|
for (i = 0; ppModeName[i] != NULL; i++) {
|
|
|
|
if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2)
|
|
continue;
|
|
|
|
/* Note: We allow all non-standard modes as long as they do not
|
|
* exceed the native resolution of the panel. Since these modes
|
|
* need the internal RMX unit in the video chips (and there is
|
|
* only one per card), this will only apply to the primary head.
|
|
*/
|
|
if (width < 320 || width > pI830->PanelXRes ||
|
|
height < 200 || height > pI830->PanelYRes) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Mode %s is out of range.\n",
|
|
ppModeName[i]);
|
|
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
|
|
"Valid modes must be between 320x200-%dx%d\n",
|
|
pI830->PanelXRes, pI830->PanelYRes);
|
|
continue;
|
|
}
|
|
|
|
new = i830GetGTF(width, height, 60.0, FALSE, FALSE);
|
|
new->type |= M_T_USERDEF;
|
|
|
|
new->next = NULL;
|
|
new->prev = last;
|
|
|
|
if (last)
|
|
last->next = new;
|
|
last = new;
|
|
if (!first)
|
|
first = new;
|
|
|
|
count++;
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Valid mode using panel fitting: %s\n", new->name);
|
|
}
|
|
|
|
/* If the user hasn't specified modes, add the native mode */
|
|
if (!count) {
|
|
new = i830FPNativeMode(pScrn);
|
|
if (first) {
|
|
I830xf86SortModes(new, &first, &last);
|
|
count = 1;
|
|
}
|
|
}
|
|
|
|
/* add in all default vesa modes smaller than panel size, used for randr */
|
|
for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) {
|
|
if ((p->HDisplay <= pI830->PanelXRes) && (p->VDisplay <= pI830->PanelYRes)) {
|
|
tmp = first;
|
|
while (tmp) {
|
|
if ((p->HDisplay == tmp->HDisplay) && (p->VDisplay == tmp->VDisplay)) break;
|
|
tmp = tmp->next;
|
|
}
|
|
if (!tmp) {
|
|
new = i830GetGTF(p->HDisplay, p->VDisplay, 60.0, FALSE, FALSE);
|
|
if (ppModeName[i] == NULL)
|
|
new->type |= M_T_USERDEF;
|
|
else
|
|
new->type |= M_T_DEFAULT;
|
|
|
|
I830xf86SortModes(new, &first, &last);
|
|
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Total number of valid FP mode(s) found: %d\n", count);
|
|
|
|
return first;
|
|
}
|
|
|
|
static DisplayModePtr
|
|
i830GetModeListTail(DisplayModePtr pModeList)
|
|
{
|
|
DisplayModePtr last;
|
|
|
|
if (pModeList == NULL)
|
|
return NULL;
|
|
|
|
for (last = pModeList; last->next != NULL; last = last->next)
|
|
;
|
|
|
|
return last;
|
|
}
|
|
|
|
/**
|
|
* Appends a list of modes to another mode list, without duplication.
|
|
*/
|
|
static void
|
|
i830AppendModes(ScrnInfoPtr pScrn, DisplayModePtr *modeList,
|
|
DisplayModePtr addModes)
|
|
{
|
|
DisplayModePtr first = *modeList;
|
|
DisplayModePtr last = i830GetModeListTail(first);
|
|
|
|
if (first == NULL) {
|
|
*modeList = addModes;
|
|
} else {
|
|
last->next = addModes;
|
|
addModes->prev = last;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Duplicates every mode in the given list and returns a pointer to the first
|
|
* mode.
|
|
*
|
|
* \param modeList doubly-linked mode list
|
|
*/
|
|
static DisplayModePtr
|
|
i830DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList)
|
|
{
|
|
DisplayModePtr first = NULL, last = NULL;
|
|
DisplayModePtr mode;
|
|
|
|
for (mode = modeList; mode != NULL; mode = mode->next) {
|
|
DisplayModePtr new;
|
|
|
|
new = I830DuplicateMode(mode);
|
|
|
|
/* Insert pNew into modeList */
|
|
if (last) {
|
|
last->next = new;
|
|
new->prev = last;
|
|
} else {
|
|
first = new;
|
|
new->prev = NULL;
|
|
}
|
|
new->next = NULL;
|
|
last = new;
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
static MonPtr
|
|
i830GetDDCMonitor(ScrnInfoPtr pScrn, I2CBusPtr pDDCBus)
|
|
{
|
|
xf86MonPtr ddc;
|
|
MonPtr mon;
|
|
DisplayModePtr userModes;
|
|
int i;
|
|
|
|
ddc = xf86DoEDID_DDC2(pScrn->scrnIndex, pDDCBus);
|
|
|
|
if (ddc == NULL)
|
|
return NULL;
|
|
|
|
mon = xnfcalloc(1, sizeof(*mon));
|
|
mon->Modes = i830GetDDCModes(pScrn, ddc);
|
|
mon->DDC = ddc;
|
|
|
|
for (i = 0; i < DET_TIMINGS; i++) {
|
|
struct detailed_monitor_section *det_mon = &ddc->det_mon[i];
|
|
|
|
switch (ddc->det_mon[i].type) {
|
|
case DS_RANGES:
|
|
mon->hsync[mon->nHsync].lo = det_mon->section.ranges.min_h;
|
|
mon->hsync[mon->nHsync].hi = det_mon->section.ranges.max_h;
|
|
mon->nHsync++;
|
|
mon->vrefresh[mon->nVrefresh].lo = det_mon->section.ranges.min_v;
|
|
mon->vrefresh[mon->nVrefresh].hi = det_mon->section.ranges.max_v;
|
|
mon->nVrefresh++;
|
|
break;
|
|
default:
|
|
/* We probably don't care about trying to contruct ranges around
|
|
* modes specified by DDC.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add in VESA standard and user modelines, and do additional validation
|
|
* on them beyond what pipe config will do (x/y/pitch, clocks, flags)
|
|
*/
|
|
userModes = i830DuplicateModes(pScrn, pScrn->monitor->Modes);
|
|
|
|
i830xf86ValidateModesSync(pScrn, userModes, mon);
|
|
if (ddc->features.hsize > 0 && ddc->features.vsize > 0) {
|
|
i830xf86ValidateModesSize(pScrn, userModes, ddc->features.hsize,
|
|
ddc->features.vsize, -1);
|
|
}
|
|
i830xf86PruneInvalidModes(pScrn, &userModes, TRUE);
|
|
|
|
i830AppendModes(pScrn, &mon->Modes, userModes);
|
|
|
|
mon->Last = i830GetModeListTail(mon->Modes);
|
|
|
|
return mon;
|
|
}
|
|
|
|
static MonPtr
|
|
i830GetLVDSMonitor(ScrnInfoPtr pScrn)
|
|
{
|
|
MonPtr mon;
|
|
|
|
mon = xnfcalloc(1, sizeof(*mon));
|
|
mon->Modes = i830GetLVDSModes(pScrn, pScrn->display->modes);
|
|
mon->Last = i830GetModeListTail(mon->Modes);
|
|
|
|
return mon;
|
|
}
|
|
|
|
static MonPtr
|
|
i830GetConfiguredMonitor(ScrnInfoPtr pScrn)
|
|
{
|
|
MonPtr mon;
|
|
DisplayModePtr userModes;
|
|
|
|
mon = xnfcalloc(1, sizeof(*mon));
|
|
memcpy(mon, pScrn->monitor, sizeof(*mon));
|
|
|
|
if (pScrn->monitor->id != NULL)
|
|
mon->id = xnfstrdup(pScrn->monitor->id);
|
|
if (pScrn->monitor->vendor != NULL)
|
|
mon->vendor = xnfstrdup(pScrn->monitor->vendor);
|
|
if (pScrn->monitor->model != NULL)
|
|
mon->model = xnfstrdup(pScrn->monitor->model);
|
|
|
|
/* Add in VESA standard and user modelines, and do additional validation
|
|
* on them beyond what pipe config will do (x/y/pitch, clocks, flags)
|
|
*/
|
|
userModes = i830DuplicateModes(pScrn, pScrn->monitor->Modes);
|
|
i830xf86ValidateModesSync(pScrn, userModes, mon);
|
|
i830xf86PruneInvalidModes(pScrn, &userModes, FALSE);
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "prune 1\n");
|
|
i830AppendModes(pScrn, &mon->Modes, userModes);
|
|
|
|
mon->Last = i830GetModeListTail(mon->Modes);
|
|
|
|
return mon;
|
|
}
|
|
|
|
static void
|
|
i830FreeMonitor(ScrnInfoPtr pScrn, MonPtr mon)
|
|
{
|
|
while (mon->Modes != NULL)
|
|
xf86DeleteMode(&mon->Modes, mon->Modes);
|
|
xfree(mon->id);
|
|
xfree(mon->vendor);
|
|
xfree(mon->model);
|
|
xfree(mon->DDC);
|
|
xfree(mon);
|
|
}
|
|
|
|
/**
|
|
* Performs probing of modes available on the output connected to the given
|
|
* pipe.
|
|
*
|
|
* We do not support multiple outputs per pipe (since the cases for that are
|
|
* sufficiently rare we can't imagine the complexity being worth it), so
|
|
* the pipe is a sufficient specifier.
|
|
*/
|
|
static void
|
|
I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe)
|
|
{
|
|
I830Ptr pI830 = I830PTR(pScrn);
|
|
int output_index = -1;
|
|
int i;
|
|
int outputs;
|
|
DisplayModePtr pMode;
|
|
MonPtr old_mon = pI830->pipeMon[pipe];
|
|
|
|
if (pipe == 0)
|
|
outputs = pI830->operatingDevices & 0xff;
|
|
else
|
|
outputs = (pI830->operatingDevices >> 8) & 0xff;
|
|
|
|
for (i = 0; i < MAX_OUTPUTS; i++) {
|
|
switch (pI830->output[i].type) {
|
|
case I830_OUTPUT_ANALOG:
|
|
if (outputs & PIPE_CRT) {
|
|
output_index = i;
|
|
}
|
|
break;
|
|
case I830_OUTPUT_LVDS:
|
|
if (outputs & PIPE_LFP) {
|
|
output_index = i;
|
|
}
|
|
break;
|
|
case I830_OUTPUT_SDVO:
|
|
if (outputs & PIPE_DFP &&
|
|
pI830->output[i].sdvo_drv != NULL)
|
|
{
|
|
output_index = i;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* XXX: If there's no output associated with the pipe, bail for now. */
|
|
if (output_index == -1)
|
|
return;
|
|
|
|
if (outputs & PIPE_LFP) {
|
|
pI830->pipeMon[pipe] = i830GetLVDSMonitor(pScrn);
|
|
} else if (pI830->output[output_index].pDDCBus != NULL) {
|
|
pI830->pipeMon[pipe] =
|
|
i830GetDDCMonitor(pScrn, pI830->output[output_index].pDDCBus);
|
|
}
|
|
/* If DDC didn't work (or the flat panel equivalent), then see if we can
|
|
* detect if a monitor is at least plugged in. If we can't tell that one
|
|
* is plugged in, then assume that it is.
|
|
*/
|
|
if (pI830->pipeMon[pipe] == NULL) {
|
|
switch (pI830->output[output_index].type) {
|
|
case I830_OUTPUT_SDVO:
|
|
if (I830DetectSDVODisplays(pScrn, output_index))
|
|
pI830->pipeMon[pipe] = i830GetConfiguredMonitor(pScrn);
|
|
break;
|
|
case I830_OUTPUT_ANALOG:
|
|
/* Do a disruptive detect if necessary, since we want to be sure we
|
|
* know if a monitor is attached, and this detect process should be
|
|
* infrequent.
|
|
*/
|
|
if (i830DetectCRT(pScrn, TRUE))
|
|
pI830->pipeMon[pipe] = i830GetConfiguredMonitor(pScrn);
|
|
break;
|
|
default:
|
|
pI830->pipeMon[pipe] = i830GetConfiguredMonitor(pScrn);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_REPROBE
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Printing probed modes for pipe %d\n",
|
|
pipe);
|
|
#endif
|
|
if (pI830->pipeMon[pipe] != NULL) {
|
|
int minclock, maxclock;
|
|
|
|
switch (pI830->output[output_index].type) {
|
|
case I830_OUTPUT_SDVO:
|
|
minclock = 25000;
|
|
maxclock = 165000;
|
|
case I830_OUTPUT_LVDS:
|
|
case I830_OUTPUT_ANALOG:
|
|
default:
|
|
minclock = 25000;
|
|
maxclock = 400000;
|
|
}
|
|
|
|
i830xf86ValidateModesFlags(pScrn, pI830->pipeMon[pipe]->Modes,
|
|
V_INTERLACE);
|
|
i830xf86ValidateModesClocks(pScrn, pI830->pipeMon[pipe]->Modes,
|
|
&minclock, &maxclock, 1);
|
|
|
|
i830xf86PruneInvalidModes(pScrn, &pI830->pipeMon[pipe]->Modes, TRUE);
|
|
|
|
for (pMode = pI830->pipeMon[pipe]->Modes; pMode != NULL;
|
|
pMode = pMode->next)
|
|
{
|
|
/* The code to choose the best mode per pipe later on will require
|
|
* VRefresh to be set.
|
|
*/
|
|
pMode->VRefresh = i830xf86ModeVRefresh(pMode);
|
|
I830xf86SetModeCrtc(pMode, INTERLACE_HALVE_V);
|
|
#ifdef DEBUG_REPROBE
|
|
PrintModeline(pScrn->scrnIndex, pMode);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (old_mon != NULL && pI830->pipeMon[pipe] == NULL) {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Failed to probe output on pipe %d, disabling output at next "
|
|
"mode switch\n", pipe);
|
|
if (pipe == 0)
|
|
pI830->operatingDevices &= ~0x00ff;
|
|
else
|
|
pI830->operatingDevices &= ~0xff00;
|
|
}
|
|
|
|
if (old_mon != NULL)
|
|
i830FreeMonitor(pScrn, old_mon);
|
|
}
|
|
|
|
/**
|
|
* This function removes a mode from a list of modes. It should probably be
|
|
* moved to xf86Mode.c.
|
|
*
|
|
* There are different types of mode lists:
|
|
*
|
|
* - singly linked linear lists, ending in NULL
|
|
* - doubly linked linear lists, starting and ending in NULL
|
|
* - doubly linked circular lists
|
|
*
|
|
*/
|
|
|
|
static void
|
|
I830xf86DeleteModeFromList(DisplayModePtr *modeList, DisplayModePtr mode)
|
|
{
|
|
/* Catch the easy/insane cases */
|
|
if (modeList == NULL || *modeList == NULL || mode == NULL)
|
|
return;
|
|
|
|
/* If the mode is at the start of the list, move the start of the list */
|
|
if (*modeList == mode)
|
|
*modeList = mode->next;
|
|
|
|
/* If mode is the only one on the list, set the list to NULL */
|
|
if ((mode == mode->prev) && (mode == mode->next)) {
|
|
*modeList = NULL;
|
|
} else {
|
|
if ((mode->prev != NULL) && (mode->prev->next == mode))
|
|
mode->prev->next = mode->next;
|
|
if ((mode->next != NULL) && (mode->next->prev == mode))
|
|
mode->next->prev = mode->prev;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Probes for video modes on attached otuputs, and assembles a list to insert
|
|
* into pScrn.
|
|
*
|
|
* \param first_time indicates that the memory layout has already been set up,
|
|
* so displayWidth, virtualX, and virtualY shouldn't be touched.
|
|
*
|
|
* A SetMode must follow this call in order for operatingDevices to match the
|
|
* hardware's state, in case we detect a new output device.
|
|
*/
|
|
int
|
|
I830ValidateXF86ModeList(ScrnInfoPtr pScrn, Bool first_time)
|
|
{
|
|
I830Ptr pI830 = I830PTR(pScrn);
|
|
int pipe;
|
|
DisplayModePtr saved_mode, last;
|
|
Bool pipes_reconfigured = FALSE;
|
|
int originalVirtualX, originalVirtualY;
|
|
|
|
for (pipe = 0; pipe < MAX_DISPLAY_PIPES; pipe++) {
|
|
I830ReprobePipeModeList(pScrn, pipe);
|
|
}
|
|
|
|
/* If we've got a spare pipe, try to detect if a new CRT has been plugged
|
|
* in.
|
|
*/
|
|
if ((pI830->operatingDevices & (PIPE_CRT | (PIPE_CRT << 8))) == 0) {
|
|
if ((pI830->operatingDevices & 0xff) == PIPE_NONE) {
|
|
pI830->operatingDevices |= PIPE_CRT;
|
|
I830ReprobePipeModeList(pScrn, 0);
|
|
if (pI830->pipeMon[0] == NULL) {
|
|
/* No new output found. */
|
|
pI830->operatingDevices &= ~PIPE_CRT;
|
|
} else {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Enabled new CRT on pipe A\n");
|
|
pipes_reconfigured = TRUE;
|
|
/* Clear the current mode, so we reprogram the pipe for sure. */
|
|
memset(&pI830->pipeCurMode[0], 0, sizeof(pI830->pipeCurMode[0]));
|
|
}
|
|
} else if (((pI830->operatingDevices >> 8) & 0xff) == PIPE_NONE) {
|
|
pI830->operatingDevices |= PIPE_CRT << 8;
|
|
I830ReprobePipeModeList(pScrn, 1);
|
|
if (pI830->pipeMon[1] == NULL) {
|
|
/* No new output found. */
|
|
pI830->operatingDevices &= ~(PIPE_CRT << 8);
|
|
} else {
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
"Enabled new CRT on pipe B\n");
|
|
pipes_reconfigured = TRUE;
|
|
/* Clear the current mode, so we reprogram the pipe for sure. */
|
|
memset(&pI830->pipeCurMode[1], 0, sizeof(pI830->pipeCurMode[1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((pI830->pipeMon[0] == NULL || pI830->pipeMon[0]->Modes == NULL) &&
|
|
(pI830->pipeMon[1] == NULL || pI830->pipeMon[1]->Modes == NULL))
|
|
{
|
|
FatalError("No modes found on either pipe\n");
|
|
}
|
|
|
|
if (first_time) {
|
|
int maxX = -1, maxY = -1;
|
|
|
|
/* Set up a virtual size that will cover any clone mode we'd want to set
|
|
* for either of the two pipes.
|
|
*/
|
|
for (pipe = 0; pipe < MAX_DISPLAY_PIPES; pipe++) {
|
|
MonPtr mon = pI830->pipeMon[pipe];
|
|
DisplayModePtr mode;
|
|
|
|
if (mon == NULL)
|
|
continue;
|
|
|
|
for (mode = mon->Modes; mode != NULL; mode = mode->next) {
|
|
if (mode->HDisplay > maxX)
|
|
maxX = mode->HDisplay;
|
|
if (mode->VDisplay > maxY)
|
|
maxY = mode->VDisplay;
|
|
}
|
|
}
|
|
pScrn->virtualX = maxX;
|
|
pScrn->virtualY = maxY;
|
|
pScrn->displayWidth = (maxX + 63) & ~63;
|
|
}
|
|
|
|
I830GetOriginalVirtualSize(pScrn, &originalVirtualX, &originalVirtualY);
|
|
|
|
/* Disable modes that are larger than the virtual size we decided on
|
|
* initially.
|
|
*/
|
|
if (!first_time) {
|
|
for (pipe = 0; pipe < MAX_DISPLAY_PIPES; pipe++) {
|
|
MonPtr mon = pI830->pipeMon[pipe];
|
|
DisplayModePtr mode;
|
|
|
|
if (mon == NULL)
|
|
continue;
|
|
|
|
for (mode = mon->Modes; mode != NULL; mode = mode->next)
|
|
{
|
|
if (mode->HDisplay > originalVirtualX)
|
|
mode->status = MODE_VIRTUAL_X;
|
|
if (mode->VDisplay > originalVirtualY)
|
|
mode->status = MODE_VIRTUAL_Y;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove the current mode from the modelist if we're re-validating, so we
|
|
* can find a new mode to map ourselves to afterwards.
|
|
*/
|
|
saved_mode = pI830->currentMode;
|
|
if (saved_mode != NULL) {
|
|
I830xf86DeleteModeFromList(&pScrn->modes, saved_mode);
|
|
}
|
|
|
|
/* Clear any existing modes from pScrn->modes */
|
|
while (pScrn->modes != NULL)
|
|
xf86DeleteMode(&pScrn->modes, pScrn->modes);
|
|
|
|
/* Set pScrn->modes to the mode list for the an arbitrary head.
|
|
* pScrn->modes should only be used for XF86VidMode now, which we don't
|
|
* care about enough to make some sort of unioned list.
|
|
*/
|
|
if (pI830->pipeMon[1] != NULL) {
|
|
pScrn->modes = i830DuplicateModes(pScrn, pI830->pipeMon[1]->Modes);
|
|
} else {
|
|
pScrn->modes = i830DuplicateModes(pScrn, pI830->pipeMon[0]->Modes);
|
|
}
|
|
if (pScrn->modes == NULL) {
|
|
FatalError("No modes found\n");
|
|
}
|
|
|
|
/* Don't let pScrn->modes have modes larger than the max root window size.
|
|
* We don't really care about the monitors having it, particularly since we
|
|
* eventually want randr to be able to move to those sizes.
|
|
*/
|
|
i830xf86ValidateModesSize(pScrn, pScrn->modes,
|
|
originalVirtualX, originalVirtualY,
|
|
pScrn->displayWidth);
|
|
|
|
/* Strip out anything bad that we threw out for virtualX. */
|
|
i830xf86PruneInvalidModes(pScrn, &pScrn->modes, TRUE);
|
|
|
|
/* For some reason, pScrn->modes is circular, unlike the other mode lists.
|
|
* How great is that?
|
|
*/
|
|
last = i830GetModeListTail(pScrn->modes);
|
|
last->next = pScrn->modes;
|
|
pScrn->modes->prev = last;
|
|
|
|
#if 0
|
|
/* XXX: do I need this any more? Maybe XF86VidMode uses it?
|
|
* Set up the ClockRanges, which describe what clock ranges are available,
|
|
* and what sort of modes they can be used for.
|
|
*/
|
|
clockRanges = xnfcalloc(sizeof(ClockRange), 1);
|
|
clockRanges->next = NULL;
|
|
clockRanges->minClock = 25000;
|
|
clockRanges->maxClock = pI830->MaxClock;
|
|
clockRanges->clockIndex = -1; /* programmable */
|
|
clockRanges->interlaceAllowed = TRUE; /* XXX check this */
|
|
clockRanges->doubleScanAllowed = FALSE; /* XXX check this */
|
|
#endif
|
|
|
|
#if DEBUG_REPROBE
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Modes post revalidate\n");
|
|
do {
|
|
DisplayModePtr pMode;
|
|
|
|
for (pMode = pScrn->modes; ; pMode = pMode->next) {
|
|
PrintModeline(pScrn->scrnIndex, pMode);
|
|
if (pMode->next == pScrn->modes)
|
|
break;
|
|
}
|
|
} while (0);
|
|
#endif
|
|
|
|
/* Save a pointer to the previous current mode. We can't reset
|
|
* pScrn->currentmode, because we rely on xf86SwitchMode's shortcut not
|
|
* happening so we can hot-enable devices at SwitchMode. We'll notice this
|
|
* case at SwitchMode and free the saved mode.
|
|
*/
|
|
pI830->savedCurrentMode = saved_mode;
|
|
|
|
return 1; /* XXX */
|
|
}
|