summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <anholt@FreeBSD.org>2006-03-27 18:50:53 -0800
committerEric Anholt <anholt@leguin.anholt.net>2006-04-06 15:59:51 -0700
commit33977d23830b5f9bb7d9e2e9c141f91cb127b7de (patch)
tree1ebdaa0b07352965c364d6d5b5ec49714e731874
parentfebdfa967dbe1df487db71ba5f677ef37450bf7a (diff)
Port code from radeon driver for panel mode validation, which will hopefully get
the right mode chosen on the VAIO. Untested.
-rw-r--r--src/i830.h3
-rw-r--r--src/i830_bios.c10
-rw-r--r--src/i830_driver.c313
3 files changed, 202 insertions, 124 deletions
diff --git a/src/i830.h b/src/i830.h
index d41da791..3fba456c 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -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;
diff --git a/src/i830_bios.c b/src/i830_bios.c
index 31e89b59..47c39ec3 100644
--- a/src/i830_bios.c
+++ b/src/i830_bios.c
@@ -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;
}
diff --git a/src/i830_driver.c b/src/i830_driver.c
index ca46ec99..c6b59708 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -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);