summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/i810_reg.h10
-rw-r--r--src/i830.h6
-rw-r--r--src/i830_common.h9
-rw-r--r--src/i830_debug.c13
-rw-r--r--src/i830_debug.h1
-rw-r--r--src/i830_display.c46
-rw-r--r--src/i830_dri.c25
-rw-r--r--src/i830_driver.c47
-rw-r--r--src/i830_modes.c366
-rw-r--r--src/i830_sdvo.c91
-rw-r--r--src/i830_sdvo_regs.h2
11 files changed, 586 insertions, 30 deletions
diff --git a/src/i810_reg.h b/src/i810_reg.h
index c45368df..b95f795b 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -728,6 +728,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# define SDVO_MULTIPLIER_MASK 0x000000ff
# define SDVO_DEFAULT_MULTIPLIER 0x00000003
+#define BLC_PWM_CTL 0x61254
+#define BACKLIGHT_MODULATION_FREQ_SHIFT (17)
+#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17)
+#define BLM_LEGACY_MODE (1 << 16)
+#define BACKLIGHT_DUTY_CYCLE_SHIFT (0)
+#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff)
+
#define FPA0 0x06040
#define FPA1 0x06044
#define FPB0 0x06048
@@ -752,6 +759,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# define CRT_HOTPLUG_MONITOR_NONE (0 << 8)
# 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
@@ -769,7 +777,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 efd9f6d5..d5bb9c6c 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -419,6 +419,7 @@ typedef struct _I830Rec {
Bool devicePresence;
OsTimerPtr devicesTimer;
+ int MaxClock;
int ddc2;
int num_outputs;
@@ -439,6 +440,8 @@ typedef struct _I830Rec {
int panel_fixed_vsyncoff;
int panel_fixed_vsyncwidth;
+ int backlight_duty_cycle; /* restore backlight to this value */
+
Bool panel_wants_dither;
unsigned char *VBIOS;
@@ -492,6 +495,7 @@ typedef struct _I830Rec {
CARD32 savePaletteA[256];
CARD32 savePaletteB[256];
CARD32 saveSWF[17];
+ CARD32 saveBLC_PWM_CTL;
} I830Rec;
#define I830PTR(p) ((I830Ptr)((p)->driverPrivate))
@@ -542,6 +546,7 @@ extern void I830DRIUnmapScreenRegions(ScrnInfoPtr pScrn, drmI830Sarea *sarea);
extern Bool I830DRIMapScreenRegions(ScrnInfoPtr pScrn, drmI830Sarea *sarea);
extern void I830DRIUnlock(ScrnInfoPtr pScrn);
extern Bool I830DRILock(ScrnInfoPtr pScrn);
+extern Bool I830DRISetVBlankInterrupt (ScrnInfoPtr pScrn, Bool on);
#endif
extern Bool I830AccelInit(ScreenPtr pScreen);
@@ -588,6 +593,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_common.h b/src/i830_common.h
index 41b5cc3c..a27bc011 100644
--- a/src/i830_common.h
+++ b/src/i830_common.h
@@ -52,6 +52,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#define DRM_I830_INIT_HEAP 0x0a
#define DRM_I830_CMDBUFFER 0x0b
#define DRM_I830_DESTROY_HEAP 0x0c
+#define DRM_I830_SET_VBLANK_PIPE 0x0d
+#define DRM_I830_GET_VBLANK_PIPE 0x0e
+
typedef struct {
enum {
@@ -193,5 +196,11 @@ typedef struct {
int region;
} drmI830MemDestroyHeap;
+#define DRM_I830_VBLANK_PIPE_A 1
+#define DRM_I830_VBLANK_PIPE_B 2
+
+typedef struct {
+ int pipe;
+} drmI830VBlankPipe;
#endif /* _I830_DRM_H_ */
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 3a4833e2..f1642da3 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);
@@ -437,7 +440,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)
@@ -469,9 +475,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);
@@ -490,6 +493,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.
@@ -634,12 +642,17 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode)
pI830->planeEnabled[1] ? "enabled" : "disabled",
planeB & DISPPLANE_SEL_PIPE_MASK ? "Pipe B" : "Pipe A");
+#ifdef XF86DRI
+ I830DRISetVBlankInterrupt (pScrn, TRUE);
+#endif
done:
#ifdef XF86DRI
if (didLock)
I830DRIUnlock(pScrn);
#endif
+ i830DumpRegs (pScrn);
+ I830DumpSDVO (pScrn);
return ok;
}
@@ -798,7 +811,14 @@ i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on)
{
I830Ptr pI830 = I830PTR(pScrn);
CARD32 pp_status, pp_control;
+ CARD32 blc_pwm_ctl;
+ int backlight_duty_cycle;
+ blc_pwm_ctl = INREG (BLC_PWM_CTL);
+ backlight_duty_cycle = blc_pwm_ctl & BACKLIGHT_DUTY_CYCLE_MASK;
+ if (backlight_duty_cycle)
+ pI830->backlight_duty_cycle = backlight_duty_cycle;
+
if (on) {
OUTREG(PP_STATUS, INREG(PP_STATUS) | PP_ON);
OUTREG(PP_CONTROL, INREG(PP_CONTROL) | POWER_TARGET_ON);
@@ -806,7 +826,13 @@ i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on)
pp_status = INREG(PP_STATUS);
pp_control = INREG(PP_CONTROL);
} while (!(pp_status & PP_ON) && !(pp_control & POWER_TARGET_ON));
+ OUTREG(BLC_PWM_CTL,
+ (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK) |
+ pI830->backlight_duty_cycle);
} else {
+ OUTREG(BLC_PWM_CTL,
+ (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK));
+
OUTREG(PP_STATUS, INREG(PP_STATUS) & ~PP_ON);
OUTREG(PP_CONTROL, INREG(PP_CONTROL) & ~POWER_TARGET_ON);
do {
diff --git a/src/i830_dri.c b/src/i830_dri.c
index 762d3d95..0cd3ed0b 100644
--- a/src/i830_dri.c
+++ b/src/i830_dri.c
@@ -1449,6 +1449,31 @@ I830UpdateDRIBuffers(ScrnInfoPtr pScrn, drmI830Sarea *sarea)
}
Bool
+I830DRISetVBlankInterrupt (ScrnInfoPtr pScrn, Bool on)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ drmI830VBlankPipe pipe;
+
+ if (pI830->directRenderingEnabled && pI830->drmMinor >= 5) {
+ if (on) {
+ if (pI830->planeEnabled[1])
+ pipe.pipe = DRM_I830_VBLANK_PIPE_B;
+ else
+ pipe.pipe = DRM_I830_VBLANK_PIPE_A;
+ } else {
+ pipe.pipe = 0;
+ }
+ if (drmCommandWrite(pI830->drmSubFD, DRM_I830_SET_VBLANK_PIPE,
+ &pipe, sizeof (pipe))) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "I830 Vblank Pipe Setup Failed\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+Bool
I830DRILock(ScrnInfoPtr pScrn)
{
I830Ptr pI830 = I830PTR(pScrn);
diff --git a/src/i830_driver.c b/src/i830_driver.c
index bc375e7b..c0938faa 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -2358,14 +2358,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 */
@@ -2384,16 +2387,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 */
@@ -2416,6 +2420,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
@@ -2897,6 +2919,17 @@ SaveHWState(ScrnInfoPtr pScrn)
pI830->saveLVDS = INREG(LVDS);
pI830->savePP_CONTROL = INREG(PP_CONTROL);
pI830->savePP_CYCLE = INREG(PP_CYCLE);
+ pI830->saveBLC_PWM_CTL = INREG(BLC_PWM_CTL);
+ pI830->backlight_duty_cycle = (pI830->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (!pI830->backlight_duty_cycle)
+ pI830->backlight_duty_cycle = ((pI830->saveBLC_PWM_CTL &
+ BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT);
+
if (!IS_I9XX(pI830)) {
pI830->saveDVOA = INREG(DVOA);
@@ -2937,6 +2970,9 @@ RestoreHWState(ScrnInfoPtr pScrn)
DPRINTF(PFX, "RestoreHWState\n");
+#ifdef XF86DRI
+ I830DRISetVBlankInterrupt (pScrn, FALSE);
+#endif
vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL);
vgaHWLock(hwp);
@@ -3003,6 +3039,7 @@ RestoreHWState(ScrnInfoPtr pScrn)
}
}
+ OUTREG(BLC_PWM_CTL, pI830->saveBLC_PWM_CTL);
OUTREG(LVDSPP_ON, pI830->savePP_ON);
OUTREG(LVDSPP_OFF, pI830->savePP_OFF);
OUTREG(PP_CYCLE, pI830->savePP_CYCLE);
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