Port code from radeon driver for panel mode validation, which will hopefully get

the right mode chosen on the VAIO.  Untested.
This commit is contained in:
Eric Anholt 2006-03-27 18:50:53 -08:00 committed by Eric Anholt
parent febdfa967d
commit 33977d2383
3 changed files with 202 additions and 124 deletions

View File

@ -430,6 +430,9 @@ typedef struct _I830Rec {
struct _I830OutputRec output[MAX_OUTPUTS];
I830SDVOPtr sdvo;
/* Panel size pulled from the BIOS */
int PanelXRes, PanelYRes;
/* The BIOS's fixed timings for the LVDS */
int panel_fixed_hactive;
int panel_fixed_hblank;

View File

@ -102,6 +102,7 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
struct bdb_header *bdb;
int vbt_off, bdb_off, bdb_block_off, block_size;
int panel_type = -1;
Bool found_panel_info = FALSE;
if (!i830GetBIOS(pScrn))
return FALSE;
@ -147,9 +148,11 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
timing_ptr = pI830->VBIOS + bdb_off +
lvds2->panels[panel_type].fp_edid_dtd_offset;
pI830->PanelXRes = fpparam->x_res;
pI830->PanelYRes = fpparam->y_res;
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Found panel of size %dx%d in BIOS VBT tables\n",
fpparam->x_res, fpparam->y_res);
pI830->PanelXRes, pI830->PanelYRes);
/* Since lvds_bdb_2_fp_edid_dtd is just an EDID detailed timing
* block, pull the contents out using EDID macros.
@ -163,8 +166,11 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
pI830->panel_fixed_vblank = _V_BLANK(timing_ptr);
pI830->panel_fixed_vsyncoff = _V_SYNC_OFF(timing_ptr);
pI830->panel_fixed_vsyncwidth = _V_SYNC_WIDTH(timing_ptr);
found_panel_info = TRUE;
break;
}
}
return TRUE;
return found_panel_info;
}

View File

@ -183,6 +183,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "i830.h"
#include "i830_display.h"
#include "i830_debug.h"
#include "i830_bios.h"
#ifdef XF86DRI
#include "dri.h"
@ -591,100 +592,6 @@ struct panelid {
char reserved[14];
};
static void
I830InterpretPanelID(int scrnIndex, unsigned char *tmp)
{
ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
struct panelid *block = (struct panelid *)tmp;
#define PANEL_DEFAULT_HZ 60
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"PanelID returned panel resolution : %dx%d\n",
block->hsize, block->vsize);
/* If we get bogus values from this, don't accept it */
if (block->hsize == 0 || block->vsize == 0) {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Bad Panel resolution - ignoring panelID\n");
return;
}
/* If we have monitor timings then don't overwrite them */
if (pScrn->monitor->nHsync > 0 &&
pScrn->monitor->nVrefresh > 0)
return;
/* With panels, we're always assuming a refresh of 60Hz */
pScrn->monitor->nHsync = 1;
pScrn->monitor->nVrefresh = 1;
/* Give a little tolerance for the selected panel */
pScrn->monitor->hsync[0].lo = (float)((PANEL_DEFAULT_HZ/1.05)*block->vsize)/1000;
pScrn->monitor->hsync[0].hi = (float)((PANEL_DEFAULT_HZ/0.95)*block->vsize)/1000;
pScrn->monitor->vrefresh[0].lo = (float)PANEL_DEFAULT_HZ;
pScrn->monitor->vrefresh[0].hi = (float)PANEL_DEFAULT_HZ;
}
/* This should probably go into the VBE layer */
static unsigned char *
vbeReadPanelID(vbeInfoPtr pVbe)
{
int RealOff = pVbe->real_mode_base;
pointer page = pVbe->memory;
unsigned char *tmp = NULL;
int screen = pVbe->pInt10->scrnIndex;
pVbe->pInt10->ax = 0x4F11;
pVbe->pInt10->bx = 0x01;
pVbe->pInt10->cx = 0;
pVbe->pInt10->dx = 0;
pVbe->pInt10->es = SEG_ADDR(RealOff);
pVbe->pInt10->di = SEG_OFF(RealOff);
pVbe->pInt10->num = 0x10;
xf86ExecX86int10(pVbe->pInt10);
if ((pVbe->pInt10->ax & 0xff) != 0x4f) {
xf86DrvMsgVerb(screen,X_INFO,3,"VESA VBE PanelID invalid\n");
goto error;
}
switch (pVbe->pInt10->ax & 0xff00) {
case 0x0:
xf86DrvMsgVerb(screen,X_INFO,3,"VESA VBE PanelID read successfully\n");
tmp = (unsigned char *)xnfalloc(32);
memcpy(tmp,page,32);
break;
case 0x100:
xf86DrvMsgVerb(screen,X_INFO,3,"VESA VBE PanelID read failed\n");
break;
default:
xf86DrvMsgVerb(screen,X_INFO,3,"VESA VBE PanelID unknown failure %i\n",
pVbe->pInt10->ax & 0xff00);
break;
}
error:
return tmp;
}
static void
vbeDoPanelID(vbeInfoPtr pVbe)
{
unsigned char *PanelID_data;
if (!pVbe) return;
PanelID_data = vbeReadPanelID(pVbe);
if (!PanelID_data)
return;
I830InterpretPanelID(pVbe->pInt10->scrnIndex, PanelID_data);
}
int
I830GetBestRefresh(ScrnInfoPtr pScrn, int refresh)
{
@ -2125,6 +2032,166 @@ I830IsPrimary(ScrnInfoPtr pScrn)
return TRUE;
}
static void
i830SetModeToPanelParameters(ScrnInfoPtr pScrn, DisplayModePtr pMode)
{
I830Ptr pI830 = I830PTR(pScrn);
pMode->HTotal = pI830->panel_fixed_hactive;
pMode->HSyncStart = pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff;
pMode->HSyncEnd = pMode->HSyncStart + pI830->panel_fixed_hsyncwidth;
pMode->VTotal = pI830->panel_fixed_vactive;
pMode->VSyncStart = pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff;
pMode->VSyncEnd = pMode->VSyncStart + pI830->panel_fixed_vsyncwidth;
pMode->Clock = 0; /* XXX */
}
/**
* 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;
i830SetModeToPanelParameters(pScrn, new);
new->type = M_T_USERDEF;
pScrn->virtualX = MAX(pScrn->virtualX, pI830->PanelXRes);
pScrn->virtualY = MAX(pScrn->virtualY, pI830->PanelYRes);
pScrn->display->virtualX = pScrn->virtualX;
pScrn->display->virtualY = pScrn->virtualY;
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"No valid mode specified, force to native mode\n");
return new;
}
/* FP mode validation routine for using panel fitting.
*/
static int i830ValidateFPModes(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;
pScrn->virtualX = pScrn->display->virtualX;
pScrn->virtualY = pScrn->display->virtualY;
/* 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 = xnfcalloc(1, sizeof(DisplayModeRec));
new->name = xnfalloc(strlen(ppModeName[i]) + 1);
strcpy(new->name, ppModeName[i]);
new->HDisplay = width;
new->VDisplay = height;
new->type |= M_T_USERDEF;
i830SetModeToPanelParameters(pScrn, new);
new->next = NULL;
new->prev = last;
if (last)
last->next = new;
last = new;
if (!first)
first = new;
pScrn->display->virtualX = pScrn->virtualX = MAX(pScrn->virtualX, width);
pScrn->display->virtualY = pScrn->virtualY = MAX(pScrn->virtualY, height);
count++;
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Valid mode using panel fitting: %s\n", new->name);
}
/* If all else fails, add the native mode */
if (!count) {
first = last = i830FPNativeMode(pScrn);
if (first)
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 = xnfcalloc(1, sizeof(DisplayModeRec));
new->name = xnfalloc(strlen(p->name) + 1);
strcpy(new->name, p->name);
new->HDisplay = p->HDisplay;
new->VDisplay = p->VDisplay;
i830SetModeToPanelParameters(pScrn, new);
new->type |= M_T_DEFAULT;
new->next = NULL;
new->prev = last;
if (last)
last->next = new;
last = new;
if (!first)
first = new;
}
}
}
/* Close the doubly-linked mode list, if we found any usable modes */
if (last) {
last->next = first;
first->prev = last;
pScrn->modes = first;
}
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Total number of valid FP mode(s) found: %d\n", count);
return count;
}
static Bool
I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
{
@ -3151,12 +3218,6 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
SetPipeAccess(pScrn);
/* Check we have an LFP connected, before trying to
* read PanelID information. */
if ( (pI830->pipe == 1 && pI830->operatingDevices & (PIPE_LFP << 8)) ||
(pI830->pipe == 0 && pI830->operatingDevices & PIPE_LFP) )
vbeDoPanelID(pI830->pVbe);
/* XXX Move this to a header. */
#define VIDEO_BIOS_SCRATCH 0x18
@ -3202,28 +3263,36 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
clockRanges->interlaceAllowed = TRUE; /* XXX check this */
clockRanges->doubleScanAllowed = FALSE; /* XXX check this */
/*
* XXX DDC information: There's code in xf86ValidateModes
* (VBEValidateModes) to set monitor defaults based on DDC information
* where available. If we need something that does better than this,
* there's code in vesa/vesa.c.
*/
/* XXX minPitch, minHeight are random numbers. */
n = xf86ValidateModes(pScrn,
pScrn->monitor->Modes, /* availModes */
pScrn->display->modes, /* modeNames */
clockRanges, /* clockRanges */
NULL, /* linePitches */
256, /* minPitch */
MAX_DISPLAY_PITCH, /* maxPitch */
64, /* pitchInc */
pScrn->bitsPerPixel, /* minHeight */
MAX_DISPLAY_HEIGHT, /* maxHeight */
pScrn->display->virtualX, /* virtualX */
pScrn->display->virtualY, /* virtualY */
pI830->FbMapSize, /* apertureSize */
LOOKUP_BEST_REFRESH /* strategy */);
if ( (pI830->pipe == 1 && pI830->operatingDevices & (PIPE_LFP << 8)) ||
(pI830->pipe == 0 && pI830->operatingDevices & PIPE_LFP) ) {
/* If we're outputting to an LFP, use the LFP mode validation that will
* rely on the scaler so that we can display any mode smaller than or the
* same size as the panel.
*/
if (!i830GetLVDSInfoFromBIOS(pScrn)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Unable to locate panel information in BIOS VBT tables\n");
PreInitCleanup(pScrn);
return FALSE;
}
n = i830ValidateFPModes(pScrn, pScrn->display->modes);
} else {
/* XXX minPitch, minHeight are random numbers. */
n = xf86ValidateModes(pScrn,
pScrn->monitor->Modes, /* availModes */
pScrn->display->modes, /* modeNames */
clockRanges, /* clockRanges */
NULL, /* linePitches */
256, /* minPitch */
MAX_DISPLAY_PITCH, /* maxPitch */
64, /* pitchInc */
pScrn->bitsPerPixel, /* minHeight */
MAX_DISPLAY_HEIGHT, /* maxHeight */
pScrn->display->virtualX, /* virtualX */
pScrn->display->virtualY, /* virtualY */
pI830->FbMapSize, /* apertureSize */
LOOKUP_BEST_REFRESH /* strategy */);
}
if (n <= 0) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes.\n");
PreInitCleanup(pScrn);