summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKeith Packard <keithp@mactel.(none)>2006-06-04 00:15:06 -0700
committerKeith Packard <keithp@mactel.(none)>2006-06-04 00:15:06 -0700
commit34f6a8204f1edec015283fc6b5f196e47897e3de (patch)
tree95d6caca2aea6ac7dbbc5a6d28865eb1bb689fe6 /src
parentc1c46f882f9a11c383c8d1d1ce393be8fda55ed0 (diff)
Get sDVO output working on mac mini.
Add lots of register debugging to track delta from BIOS settings. Fix various mode settings to mirror BIOS sDVO values. Disable analog/lvds output on pipe with sDVO. Borrow Dave Airlie's I830xf86ValidateDDCModes code. Fix various sDVO I2C messages to mirror Dave's code.
Diffstat (limited to 'src')
-rw-r--r--src/i810_reg.h3
-rw-r--r--src/i830.h2
-rw-r--r--src/i830_debug.c13
-rw-r--r--src/i830_debug.h1
-rw-r--r--src/i830_display.c30
-rw-r--r--src/i830_driver.c32
-rw-r--r--src/i830_modes.c366
-rw-r--r--src/i830_sdvo.c91
-rw-r--r--src/i830_sdvo_regs.h2
9 files changed, 510 insertions, 30 deletions
diff --git a/src/i810_reg.h b/src/i810_reg.h
index 53c65bbf..92e3342f 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -748,6 +748,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# define TV_HOTPLUG_INT_STATUS (1 << 10)
# define SDVOC_HOTPLUG_INT_STATUS (1 << 7)
# define SDVOB_HOTPLUG_INT_STATUS (1 << 6)
+#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14))
#define SDVOB 0x61140
#define SDVOC 0x61160
@@ -765,7 +766,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SDVOB_PCIE_CONCURRENCY (1 << 3)
#define SDVO_DETECTED (1 << 2)
/* Bits to be preserved when writing */
-#define SDVO_PRESERVE_MASK (1 << 17)
+#define SDVOC_PRESERVE_MASK (1 << 17)
#define I830_HTOTAL_MASK 0xfff0000
#define I830_HACTIVE_MASK 0x7ff
diff --git a/src/i830.h b/src/i830.h
index c60bfbe6..8cf7b711 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -420,6 +420,7 @@ typedef struct _I830Rec {
Bool devicePresence;
OsTimerPtr devicesTimer;
+ int MaxClock;
int ddc2;
int num_outputs;
@@ -589,6 +590,7 @@ extern Rotation I830GetRotation(ScreenPtr pScreen);
extern Bool I830RandRInit(ScreenPtr pScreen, int rotation);
extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg,
char *name);
+int I830xf86ValidateDDCModes(ScrnInfoPtr pScrn1, char **ppModeName);
/*
* 12288 is set as the maximum, chosen because it is enough for
diff --git a/src/i830_debug.c b/src/i830_debug.c
index b5442577..6d43cd60 100644
--- a/src/i830_debug.c
+++ b/src/i830_debug.c
@@ -35,7 +35,7 @@
/* XXX: What was the syntax for sticking quotes around the "reg" argument? */
#define DEFINEREG(reg) \
- { reg, NULL, 0 }
+ { reg, #reg, 0 }
static struct i830SnapshotRec {
int reg;
@@ -129,3 +129,14 @@ void i830CompareRegsToSnapshot(ScrnInfoPtr pScrn)
}
}
}
+
+void i830DumpRegs (ScrnInfoPtr pScrn)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ int i;
+
+ for (i = 0; i < NUM_I830_SNAPSHOTREGS; i++) {
+ xf86DrvMsg (pScrn->scrnIndex, X_WARNING, "%10.10s: 0x%08x\n",
+ i830_snapshot[i].name, (unsigned int) INREG(i830_snapshot[i].reg));
+ }
+}
diff --git a/src/i830_debug.h b/src/i830_debug.h
index 269f03ea..a8e38398 100644
--- a/src/i830_debug.h
+++ b/src/i830_debug.h
@@ -27,3 +27,4 @@
void i830TakeRegSnapshot(ScrnInfoPtr pScrn);
void i830CompareRegsToSnapshot(ScrnInfoPtr pScrn);
+void i830DumpRegs (ScrnInfoPtr pScrn);
diff --git a/src/i830_display.c b/src/i830_display.c
index 95fa936d..d49da1f5 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -34,6 +34,7 @@
#include "i830.h"
#include "i830_bios.h"
#include "i830_display.h"
+#include "i830_debug.h"
/** Returns the pixel clock for the given refclk and divisors. */
static int i830_clock(int refclk, int m1, int m2, int n, int p1, int p2)
@@ -255,7 +256,8 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
int m1 = 0, m2 = 0, n = 0, p1 = 0, p2 = 0;
CARD32 dpll = 0, fp = 0, temp;
CARD32 htot, hblank, hsync, vtot, vblank, vsync, dspcntr;
- CARD32 pipesrc, dspsize, adpa, sdvoc = 0;
+ CARD32 pipesrc, dspsize, adpa;
+ CARD32 sdvob = 0, sdvoc= 0;
Bool ok, is_sdvo;
int refclk, pixel_clock;
int outputs;
@@ -391,13 +393,14 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
ErrorF("DVOB: %08x\nDVOC: %08x\n", (int)INREG(SDVOB), (int)INREG(SDVOC));
- sdvoc = INREG(SDVOC) & SDVO_PRESERVE_MASK;
- sdvoc |= SDVO_ENABLE;
+ sdvob = INREG(SDVOB) & SDVOB_PRESERVE_MASK;
+ sdvoc = INREG(SDVOC) & SDVOC_PRESERVE_MASK;
+ sdvob |= SDVO_ENABLE | (9 << 19) | SDVO_BORDER_ENABLE;
+ sdvoc |= 9 << 19;
if (pipe == 1)
- sdvoc |= SDVO_PIPE_B_SELECT;
- // sdvoc |= SDVO_PHASE_SELECT_DEFAULT;
- sdvoc |= SDVO_BORDER_ENABLE;
+ sdvob |= SDVO_PIPE_B_SELECT;
OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE);
+ OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE);
}
fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2);
@@ -434,7 +437,10 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
FatalError("unknown display bpp\n");
}
- adpa = ADPA_DAC_ENABLE;
+ if (is_sdvo)
+ adpa = ADPA_DAC_DISABLE;
+ else
+ adpa = ADPA_DAC_ENABLE;
if (pMode->Flags & V_PHSYNC)
adpa |= ADPA_HSYNC_ACTIVE_HIGH;
if (pMode->Flags & V_PVSYNC)
@@ -466,9 +472,6 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
OUTREG(FPA0, fp);
OUTREG(DPLL_A, dpll);
- if (is_sdvo)
- OUTREG(SDVOC, sdvoc);
-
OUTREG(HTOTAL_A, htot);
OUTREG(HBLANK_A, hblank);
OUTREG(HSYNC_A, hsync);
@@ -487,6 +490,11 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
/* And then turn the plane on */
OUTREG(DSPACNTR, dspcntr);
+
+ if (is_sdvo) {
+ OUTREG(SDVOB, sdvob);
+ OUTREG(SDVOC, sdvoc);
+ }
} else {
/* Always make sure the LVDS is off before we play with DPLLs and pipe
* configuration.
@@ -637,6 +645,8 @@ done:
I830DRIUnlock(pScrn);
#endif
+ i830DumpRegs (pScrn);
+ I830DumpSDVO (pScrn);
return ok;
}
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 425ddd5c..c91bf432 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -2386,14 +2386,17 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
"Maximum space available for video modes: %d kByte\n", memsize);
+ pI830->MaxClock = 300000;
+
/*
* Setup 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 = 12000; /* XXX: Random number */
- clockRanges->maxClock = 400000; /* XXX: May be lower */
+ /* 25MHz appears to be the smallest that works. */
+ clockRanges->minClock = 25000;
+ clockRanges->maxClock = pI830->MaxClock;
clockRanges->clockIndex = -1; /* programmable */
clockRanges->interlaceAllowed = TRUE; /* XXX check this */
clockRanges->doubleScanAllowed = FALSE; /* XXX check this */
@@ -2412,16 +2415,17 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
}
n = i830ValidateFPModes(pScrn, pScrn->display->modes);
} else {
+ I830xf86ValidateDDCModes(pScrn, pScrn->display->modes);
/* XXX minPitch, minHeight are random numbers. */
n = xf86ValidateModes(pScrn,
pScrn->monitor->Modes, /* availModes */
pScrn->display->modes, /* modeNames */
clockRanges, /* clockRanges */
NULL, /* linePitches */
- 256, /* minPitch */
+ 320, /* minPitch */
MAX_DISPLAY_PITCH, /* maxPitch */
- 64, /* pitchInc */
- pScrn->bitsPerPixel, /* minHeight */
+ 64 * pScrn->bitsPerPixel, /* pitchInc */
+ 200, /* minHeight */
MAX_DISPLAY_HEIGHT, /* maxHeight */
pScrn->display->virtualX, /* virtualX */
pScrn->display->virtualY, /* virtualY */
@@ -2444,6 +2448,24 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags)
xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);
+ /*
+ * Fix up modes to make hblank start at hsync start.
+ * I don't know why the xf86 code mangles this...
+ */
+ {
+ DisplayModePtr p;
+
+ for (p = pScrn->modes; p;) {
+ xf86DrvMsg (pScrn->scrnIndex,
+ X_INFO, "move blank start from %d to %d\n",
+ p->CrtcHBlankStart, p->CrtcHDisplay);
+ p->CrtcHBlankStart = p->CrtcHDisplay;
+ p = p->next;
+ if (p == pScrn->modes)
+ break;
+ }
+ }
+
pScrn->currentMode = pScrn->modes;
#ifndef USE_PITCHES
diff --git a/src/i830_modes.c b/src/i830_modes.c
index 16576bbe..ce86d8ca 100644
--- a/src/i830_modes.c
+++ b/src/i830_modes.c
@@ -44,6 +44,52 @@
#include "xf86.h"
#include "i830.h"
+#include <math.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},
+};
+
extern const int i830refreshes[];
void
@@ -108,3 +154,323 @@ I830PrintModes(ScrnInfoPtr scrp)
p = p->next;
} while (p != NULL && p != scrp->modes);
}
+
+/* This function will sort all modes according to their resolution.
+ * Highest resolution first.
+ */
+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;
+ }
+}
+
+DisplayModePtr I830xf86DDCModes(ScrnInfoPtr pScrn)
+{
+ DisplayModePtr p;
+ DisplayModePtr last = NULL;
+ DisplayModePtr new = NULL;
+ DisplayModePtr first = NULL;
+ int count = 0;
+ int j, tmp;
+ char stmp[32];
+ xf86MonPtr ddc = pScrn->monitor->DDC;
+
+ /* 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;
+ 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;
+ }
+ }
+ }
+ }
+
+ /* Search thru established modes from EDID */
+ tmp = (ddc->timings1.t1 << 8) | ddc->timings1.t2;
+ for (j = 0; j < 16; j++) {
+ if (tmp & (1 << j)) {
+ for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) {
+ if ((est_timings[j].hsize == p->HDisplay) &&
+ (est_timings[j].vsize == p->VDisplay)) {
+ float refresh =
+ (float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
+ float err = (float)est_timings[j].refresh - refresh;
+
+ 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 established timing "
+ "table: %s\n", new->name);
+
+ I830xf86SortModes(&new, &first, &last);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Total of %d mode(s) found.\n", count);
+
+ return first;
+}
+
+/* XFree86's xf86ValidateModes routine doesn't work well with DDC modes,
+ * so here is our own validation routine.
+ */
+int I830xf86ValidateDDCModes(ScrnInfoPtr pScrn1, char **ppModeName)
+{
+ DisplayModePtr p;
+ DisplayModePtr last = NULL;
+ DisplayModePtr first = NULL;
+ DisplayModePtr ddcModes = NULL;
+ int count = 0;
+ int i, width, height;
+ ScrnInfoPtr pScrn = pScrn1;
+
+ pScrn->virtualX = pScrn1->display->virtualX;
+ pScrn->virtualY = pScrn1->display->virtualY;
+
+ if (pScrn->monitor->DDC) {
+ int maxVirtX = pScrn->virtualX;
+ int maxVirtY = pScrn->virtualY;
+
+ /* Collect all of the DDC modes */
+ first = last = ddcModes = I830xf86DDCModes(pScrn);
+
+ for (p = ddcModes; p; p = p->next) {
+
+ maxVirtX = MAX(maxVirtX, p->HDisplay);
+ maxVirtY = MAX(maxVirtY, p->VDisplay);
+ count++;
+
+ last = p;
+ }
+
+ /* Match up modes that are specified in the XF86Config file */
+ if (ppModeName[0]) {
+ DisplayModePtr next;
+
+ /* Reset the max virtual dimensions */
+ maxVirtX = pScrn->virtualX;
+ maxVirtY = pScrn->virtualY;
+
+ /* Reset list */
+ first = last = NULL;
+
+ for (i = 0; ppModeName[i]; i++) {
+ /* FIXME: Use HDisplay and VDisplay instead of mode string */
+ if (sscanf(ppModeName[i], "%dx%d", &width, &height) == 2) {
+ for (p = ddcModes; p; p = next) {
+ next = p->next;
+
+ if (p->HDisplay == width && p->VDisplay == height) {
+ /* We found a DDC mode that matches the one
+ requested in the XF86Config file */
+ p->type |= M_T_USERDEF;
+
+ /* Update the max virtual setttings */
+ maxVirtX = MAX(maxVirtX, width);
+ maxVirtY = MAX(maxVirtY, height);
+
+ /* Unhook from DDC modes */
+ if (p->prev) p->prev->next = p->next;
+ if (p->next) p->next->prev = p->prev;
+ if (p == ddcModes) ddcModes = p->next;
+
+ /* Add to used modes */
+ if (last) {
+ last->next = p;
+ p->prev = last;
+ } else {
+ first = p;
+ p->prev = NULL;
+ }
+ p->next = NULL;
+ last = p;
+
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Add remaining DDC modes if they're smaller than the user
+ * specified modes
+ */
+ for (p = ddcModes; p; p = next) {
+ next = p->next;
+ if (p->HDisplay <= maxVirtX && p->VDisplay <= maxVirtY) {
+ /* Unhook from DDC modes */
+ if (p->prev) p->prev->next = p->next;
+ if (p->next) p->next->prev = p->prev;
+ if (p == ddcModes) ddcModes = p->next;
+
+ /* Add to used modes */
+ if (last) {
+ last->next = p;
+ p->prev = last;
+ } else {
+ first = p;
+ p->prev = NULL;
+ }
+ p->next = NULL;
+ last = p;
+ }
+ }
+
+ /* Delete unused modes */
+ while (ddcModes)
+ xf86DeleteMode(&ddcModes, ddcModes);
+ } else {
+ /*
+ * No modes were configured, so we make the DDC modes
+ * available for the user to cycle through.
+ */
+ for (p = ddcModes; p; p = p->next)
+ p->type |= M_T_USERDEF;
+ }
+
+ pScrn->virtualX = pScrn->display->virtualX = maxVirtX;
+ pScrn->virtualY = pScrn->display->virtualY = maxVirtY;
+ }
+
+ /* Close the doubly-linked mode list, if we found any usable modes */
+ if (last) {
+ DisplayModePtr temp = NULL;
+ /* we should add these to pScrn monitor modes */
+ last->next = pScrn->monitor->Modes;
+ temp = pScrn->monitor->Modes->prev;
+ pScrn->monitor->Modes->prev = first;
+ pScrn->monitor->Modes->prev = last;
+
+ first->prev = temp;
+ if (temp)
+ temp->next = first;
+
+ pScrn->monitor->Modes = first;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Total number of valid DDC mode(s) found: %d\n", count);
+
+ return count;
+}
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index 579f77ab..7d239759 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -139,7 +139,7 @@ I830SDVOSetTargetInput(I830SDVOPtr s, Bool target_1, Bool target_2)
s->sdvo_regs[SDVO_I2C_ARG_0] = target_1;
s->sdvo_regs[SDVO_I2C_ARG_1] = target_2;
- I830SDVOWriteOutputs(s, 1);
+ I830SDVOWriteOutputs(s, 2);
I830SDVOReadInputRegs(s);
@@ -218,7 +218,7 @@ I830SDVOSetTargetOutput(I830SDVOPtr s, Bool target_1, Bool target_2)
s->sdvo_regs[SDVO_I2C_ARG_0] = target_1;
s->sdvo_regs[SDVO_I2C_ARG_1] = target_2;
- I830SDVOWriteOutputs(s, 1);
+ I830SDVOWriteOutputs(s, 2);
I830SDVOReadInputRegs(s);
return TRUE;
@@ -259,7 +259,7 @@ I830SDVOGetTimings(I830SDVOPtr s, i830_sdvo_dtd *dtd, CARD8 cmd)
return TRUE;
}
-/* Fetches either input or output timings to *dtd, depending on cmd. */
+/* Sets either input or output timings to *dtd, depending on cmd. */
Bool
I830SDVOSetTimings(I830SDVOPtr s, i830_sdvo_dtd *dtd, CARD8 cmd)
{
@@ -286,7 +286,7 @@ I830SDVOSetTimings(I830SDVOPtr s, i830_sdvo_dtd *dtd, CARD8 cmd)
s->sdvo_regs[SDVO_I2C_ARG_5] = dtd->sdvo_flags;
s->sdvo_regs[SDVO_I2C_ARG_6] = dtd->v_sync_off_high;
s->sdvo_regs[SDVO_I2C_ARG_7] = dtd->reserved;
- I830SDVOWriteOutputs(s, 8);
+ I830SDVOWriteOutputs(s, 7);
I830SDVOReadInputRegs(s);
return TRUE;
@@ -477,6 +477,7 @@ I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode)
CARD8 c17a[8];
CARD16 out_timings[6];
CARD16 clock_min, clock_max;
+ Bool out1, out2;
/* do some mode translations */
h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart;
@@ -516,30 +517,41 @@ I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode)
out_timings[4] = c17a[5] | ((short)c17a[4] << 8);
out_timings[5] = c17a[3] | ((short)c17a[2] << 8);
- I830SDVOSetTargetInput(s, TRUE, TRUE);
+ I830SDVOSetTargetInput(s, FALSE, FALSE);
I830SDVOGetInputPixelClockRange(s, &clock_min, &clock_max);
ErrorF("clock min/max: %d %d\n", clock_min, clock_max);
+ I830SDVOGetActiveOutputs(s, &out1, &out2);
+
I830SDVOSetActiveOutputs(s, FALSE, FALSE);
- I830SDVOSetTargetOutput(s, TRUE, TRUE);
+ I830SDVOSetTargetOutput(s, TRUE, FALSE);
I830SDVOSetOutputTimingsPart1(s, clock, out_timings[0], out_timings[1],
out_timings[2]);
I830SDVOSetOutputTimingsPart2(s, out_timings[3], out_timings[4],
out_timings[5]);
+ I830SDVOSetTargetInput (s, FALSE, FALSE);
+
I830SDVOCreatePreferredInputTiming(s, clock, width, height);
I830SDVOGetPreferredInputTimingPart1(s);
I830SDVOGetPreferredInputTimingPart2(s);
+
+ I830SDVOSetTargetInput (s, FALSE, FALSE);
+
I830SDVOSetInputTimingsPart1(s, clock, curr_table[0], curr_table[1],
curr_table[2]);
I830SDVOSetInputTimingsPart2(s, curr_table[3], curr_table[4],
out_timings[5]);
- /*if (mode->PrivFlags & I830_MFLAG_DOUBLE)
- I830SDVOSetClockRateMult(s, 0x02);
- else */
- I830SDVOSetClockRateMult(s, 0x01);
+ I830SDVOSetTargetInput (s, FALSE, FALSE);
+
+ if (clock >= 10000)
+ I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_1X);
+ else if (clock >= 5000)
+ I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_2X);
+ else
+ I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_4X);
return TRUE;
}
@@ -548,6 +560,7 @@ Bool
I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode)
{
Bool ret = TRUE;
+ Bool out1, out2;
/* the BIOS writes out 6 commands post mode set */
/* two 03s, 04 05, 10, 1d */
@@ -564,7 +577,9 @@ I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode)
ret = FALSE;
}
- I830SDVOSetActiveOutputs(s, TRUE, TRUE);
+ I830SDVOGetActiveOutputs (s, &out1, &out2);
+ I830SDVOSetActiveOutputs(s, TRUE, FALSE);
+ I830SDVOSetTargetInput (s, FALSE, FALSE);
return ret;
}
@@ -580,7 +595,7 @@ i830SDVOSave(ScrnInfoPtr pScrn, int output_index)
&sdvo->save_sdvo_active_2);
if (sdvo->caps.caps & 0x1) {
- I830SDVOSetTargetInput(sdvo, TRUE, FALSE);
+ I830SDVOSetTargetInput(sdvo, FALSE, FALSE);
I830SDVOGetTimings(sdvo, &sdvo->save_input_dtd_1,
SDVO_CMD_GET_INPUT_TIMINGS_PART1);
}
@@ -622,7 +637,7 @@ i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index)
I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv;
if (sdvo->caps.caps & 0x1) {
- I830SDVOSetTargetInput(sdvo, TRUE, FALSE);
+ I830SDVOSetTargetInput(sdvo, FALSE, FALSE);
I830SDVOSetTimings(sdvo, &sdvo->save_input_dtd_1,
SDVO_CMD_SET_INPUT_TIMINGS_PART1);
}
@@ -830,3 +845,53 @@ I830SDVOInit(ScrnInfoPtr pScrn, int output_index, CARD32 output_device)
return sdvo;
}
+
+static void
+I830DumpSDVOCmd (I830SDVOPtr s, int opcode)
+{
+ memset (s->sdvo_regs, 0, sizeof (s->sdvo_regs));
+ s->sdvo_regs[SDVO_I2C_OPCODE] = opcode;
+ I830SDVOWriteOutputs (s, 0);
+ I830SDVOReadInputRegs (s);
+}
+
+static void
+I830DumpOneSDVO (I830SDVOPtr s)
+{
+ ErrorF ("Dump %s\n", s->d.DevName);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_DEVICE_CAPS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_FIRMWARE_REV);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_TRAINED_INPUTS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_ACTIVE_OUTPUTS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_IN_OUT_MAP);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_ATTACHED_DISPLAYS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_HOT_PLUG_SUPPORT);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_ACTIVE_HOT_PLUG);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_INTR_EVENT_SOURCE);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_INPUT_TIMINGS_PART1);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_INPUT_TIMINGS_PART2);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_OUTPUT_TIMINGS_PART1);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_OUTPUT_TIMINGS_PART2);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_CLOCK_RATE_MULT);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_SUPPORTED_TV_FORMATS);
+ I830DumpSDVOCmd (s, SDVO_CMD_GET_TV_FORMAT);
+}
+
+void
+I830DumpSDVO (ScrnInfoPtr pScrn)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ I830SDVOPtr s;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ s = pI830->output[i].sdvo_drv;
+ if (s)
+ I830DumpOneSDVO (s);
+ }
+}
diff --git a/src/i830_sdvo_regs.h b/src/i830_sdvo_regs.h
index 37cfcf2e..a35d5a4e 100644
--- a/src/i830_sdvo_regs.h
+++ b/src/i830_sdvo_regs.h
@@ -157,7 +157,9 @@
#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_3X (1 << 2)
# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
+# define SDVO_CLOCK_RATE_MULT_5X (1 << 4)
#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27