diff options
-rw-r--r-- | src/i810_reg.h | 10 | ||||
-rw-r--r-- | src/i830.h | 6 | ||||
-rw-r--r-- | src/i830_common.h | 9 | ||||
-rw-r--r-- | src/i830_debug.c | 13 | ||||
-rw-r--r-- | src/i830_debug.h | 1 | ||||
-rw-r--r-- | src/i830_display.c | 46 | ||||
-rw-r--r-- | src/i830_dri.c | 25 | ||||
-rw-r--r-- | src/i830_driver.c | 47 | ||||
-rw-r--r-- | src/i830_modes.c | 366 | ||||
-rw-r--r-- | src/i830_sdvo.c | 91 | ||||
-rw-r--r-- | src/i830_sdvo_regs.h | 2 |
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 @@ -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 |