diff --git a/src/common.h b/src/common.h index 9a3e0ac..d93c37a 100644 --- a/src/common.h +++ b/src/common.h @@ -169,6 +169,11 @@ static inline void memcpy_volatile(volatile void *dst, const void *src, } \ } while (0) +enum pipe { + PIPE_A = 0, + PIPE_B, +}; + /* To remove all debugging, make sure I810_DEBUG is defined as a * preprocessor symbol, and equal to zero. */ @@ -180,6 +185,21 @@ static inline void memcpy_volatile(volatile void *dst, const void *src, extern int I810_DEBUG; #endif +#include + +static __inline__ void intel_backtrace(void) +{ + void *array[32]; /* deeper nesting than this means something's wrong */ + size_t size, i; + char **strings; + ErrorF("\nBacktrace:\n"); + size = backtrace(array, 32); + strings = backtrace_symbols(array, size); + for (i = 0; i < size; i++) + ErrorF("%d: %s\n", i, strings[i]); + free(strings); +} + #define DEBUG_VERBOSE_ACCEL 0x1 #define DEBUG_VERBOSE_SYNC 0x2 #define DEBUG_VERBOSE_VGA 0x4 diff --git a/src/i810_reg.h b/src/i810_reg.h index 834b948..97f2c08 100644 --- a/src/i810_reg.h +++ b/src/i810_reg.h @@ -2022,6 +2022,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** @} */ #define PIPEA_DSL 0x70000 +#define DSL_LINEMASK 0xfff #define PIPEACONF 0x70008 #define PIPEACONF_ENABLE (1<<31) diff --git a/src/i830_cursor.c b/src/i830_cursor.c index 43a65cb..b304ffd 100644 --- a/src/i830_cursor.c +++ b/src/i830_cursor.c @@ -233,6 +233,7 @@ i830_crtc_show_cursor (xf86CrtcPtr crtc) /* Need to set mode, then address. */ OUTREG(cursor_control, temp); I830SetPipeCursorBase (crtc); + i830WaitForVblank(crtc); } void diff --git a/src/i830_display.c b/src/i830_display.c index 1122721..6353f14 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -369,11 +369,99 @@ i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk, intel_clock_t *best_cl return (err != target); } +/* + * If we're waiting for pipe disable, we need to use this function + * instead of the one that uses VBLANK_INT_STATUS. + */ +void +i830WaitForVblankScanline(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + I830Ptr pI830 = I830PTR(pScrn); + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + int pipedsl = (pipe == 0 ? PIPEA_DSL : PIPEB_DSL); + uint32_t last_line; + int start_time, elapsed_time; + + /* Wait for display line to match or 30ms timeout to elapse */ + start_time = GetTimeInMillis(); + do { + elapsed_time = GetTimeInMillis() - start_time; + last_line = INREG(pipedsl) & DSL_LINEMASK; + usleep(1000); + } while (((INREG(pipedsl) & DSL_LINEMASK) != last_line) && + elapsed_time < 30); + + if (elapsed_time >= 30) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Timeout waiting on vblank (pipe %d), hope it was enough.\n", + pipe); + intel_backtrace(); + } +} + void -i830WaitForVblank(ScrnInfoPtr pScreen) +i830WaitForVblankScanlinePipe(ScrnInfoPtr pScrn, enum pipe pipe) { - /* Wait for 20ms, i.e. one cycle at 50hz. */ - usleep(30000); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + + if (intel_crtc->pipe == pipe) + return i830WaitForVblankScanline(crtc); + } + + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Tried to wait on vblank for non-existent pipe %d.\n", pipe); +} + +void +i830WaitForVblank(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + I830Ptr pI830 = I830PTR(pScrn); + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + int pipestat = (pipe == 0 ? PIPEASTAT : PIPEBSTAT); + int start_time, elapsed_time; + + /* Clear any outstaning vblank event */ + OUTREG(pipestat, INREG(pipestat) & ~VBLANK_INT_STATUS); + start_time = GetTimeInMillis(); + + /* Wait for vblank to have occurred or 30ms timeout to elapse */ + do { + elapsed_time = GetTimeInMillis() - start_time; + } while (!(INREG(pipestat) & VBLANK_INT_STATUS) && elapsed_time < 30); + + if (elapsed_time >= 30) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Timeout waiting on vblank (pipe %d), hope it was enough.\n", + pipe); + intel_backtrace(); + } +} + +void +i830WaitForVblankPipe(ScrnInfoPtr pScrn, enum pipe pipe) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + + if (intel_crtc->pipe == pipe) + return i830WaitForVblank(crtc); + } + + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Tried to wait on vblank for non-existent pipe %d.\n", pipe); } void @@ -572,7 +660,7 @@ i830_enable_fb_compression_8xx(xf86CrtcPtr crtc) /* Wait for compressing bit to clear */ while (INREG(FBC_STATUS) & FBC_STAT_COMPRESSING) ; /* nothing */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); OUTREG(FBC_CFB_BASE, pI830->compressed_front_buffer->bus_addr); OUTREG(FBC_LL_BASE, pI830->compressed_ll_buffer->bus_addr + 6); OUTREG(FBC_CONTROL2, FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | @@ -625,7 +713,7 @@ i830_disable_fb_compression2(xf86CrtcPtr crtc) dpfc_ctl = INREG(DPFC_CONTROL); dpfc_ctl &= ~DPFC_CTL_EN; OUTREG(DPFC_CONTROL, dpfc_ctl); - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); } static void @@ -823,7 +911,7 @@ i830_crtc_dpms(xf86CrtcPtr crtc, int mode) if (!IS_I9XX(pI830)) { /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); } /* Next, disable display pipes */ @@ -834,7 +922,7 @@ i830_crtc_dpms(xf86CrtcPtr crtc, int mode) } /* Wait for vblank for the disable to take effect. */ - i830WaitForVblank(pScrn); + i830WaitForVblankScanline(crtc); temp = INREG(dpll_reg); if ((temp & DPLL_VCO_ENABLE) != 0) { @@ -1366,7 +1454,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, OUTREG(pipesrc_reg, ((mode->HDisplay - 1) << 16) | (mode->VDisplay - 1)); OUTREG(pipeconf_reg, pipeconf); POSTING_READ(pipeconf_reg); - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); OUTREG(dspcntr_reg, dspcntr); /* Flush the plane changes */ @@ -1375,7 +1463,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, I830DRISetVBlankInterrupt (pScrn, TRUE); #endif - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); } @@ -1651,7 +1739,7 @@ i830GetLoadDetectPipe(xf86OutputPtr output, DisplayModePtr mode, int *dpms_mode) output->funcs->commit (output); } /* let the output get through one full cycle before testing */ - i830WaitForVblank (pScrn); + i830WaitForVblank(crtc); return crtc; } diff --git a/src/i830_display.h b/src/i830_display.h index 1eeb7f1..69a02aa 100644 --- a/src/i830_display.h +++ b/src/i830_display.h @@ -29,7 +29,10 @@ /* i830_display.c */ void i830PipeSetBase(xf86CrtcPtr crtc, int x, int y); -void i830WaitForVblank(ScrnInfoPtr pScrn); +void i830WaitForVblank(xf86CrtcPtr crtc); +void i830WaitForVblankPipe(ScrnInfoPtr pScrn, enum pipe pipe); +void i830WaitForVblankScanline(xf86CrtcPtr crtc); +void i830WaitForVblankScanlinePipe(ScrnInfoPtr pScrn, enum pipe pipe); void i830DescribeOutputConfiguration(ScrnInfoPtr pScrn); xf86CrtcPtr i830GetLoadDetectPipe(xf86OutputPtr output, DisplayModePtr mode, int *dpms_mode); diff --git a/src/i830_driver.c b/src/i830_driver.c index b776ff6..45288d4 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -1915,11 +1915,6 @@ SetHWOperatingState(ScrnInfoPtr pScrn) I830InitHWCursor(pScrn); } -enum pipe { - PIPE_A = 0, - PIPE_B, -}; - static Bool i830_pipe_enabled(I830Ptr pI830, enum pipe pipe) { @@ -2087,14 +2082,12 @@ RestoreHWState(ScrnInfoPtr pScrn) xf86OutputPtr output = xf86_config->output[i]; output->funcs->dpms(output, DPMSModeOff); } - i830WaitForVblank(pScrn); /* Disable pipes */ for (i = 0; i < xf86_config->num_crtc; i++) { xf86CrtcPtr crtc = xf86_config->crtc[i]; crtc->funcs->dpms(crtc, DPMSModeOff); } - i830WaitForVblank(pScrn); if (IS_MOBILE(pI830) && !IS_I830(pI830)) OUTREG(LVDS, pI830->saveLVDS); @@ -2134,7 +2127,17 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(DPLL_A, pI830->saveDPLL_A); i830_dpll_settle(); - /* Restore mode config */ + /* + * Restore mode config + * Ordering rules: + * - LVDS must be enabled before DPLLs are programmed + * - pipe timing regs must be valid before pipes are enabled + * - pipes must be enabled before corresponding planes + * - planes are enabled/disabled at next vblank after "trigger" register + * is written (either DSP*CNTR enable bit if 0->1 or DSP*BASE reg) + * - if VGA plane is active, corresponding bit in DSP*CNTR enable + * bit should be off + */ OUTREG(HTOTAL_A, pI830->saveHTOTAL_A); OUTREG(HBLANK_A, pI830->saveHBLANK_A); OUTREG(HSYNC_A, pI830->saveHSYNC_A); @@ -2142,21 +2145,23 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(VBLANK_A, pI830->saveVBLANK_A); OUTREG(VSYNC_A, pI830->saveVSYNC_A); OUTREG(BCLRPAT_A, pI830->saveBCLRPAT_A); - + OUTREG(PIPEASRC, pI830->savePIPEASRC); + OUTREG(PIPEACONF, pI830->savePIPEACONF); + if (pI830->savePIPEACONF & PIPEACONF_ENABLE) + i830WaitForVblankPipe(pScrn, PIPE_A); /* wait for pipe update */ + else + i830WaitForVblankScanlinePipe(pScrn, PIPE_A); + OUTREG(DSPASTRIDE, pI830->saveDSPASTRIDE); OUTREG(DSPASIZE, pI830->saveDSPASIZE); OUTREG(DSPAPOS, pI830->saveDSPAPOS); - OUTREG(PIPEASRC, pI830->savePIPEASRC); - OUTREG(DSPABASE, pI830->saveDSPABASE); if (IS_I965G(pI830)) { OUTREG(DSPASURF, pI830->saveDSPASURF); OUTREG(DSPATILEOFF, pI830->saveDSPATILEOFF); } - OUTREG(PIPEACONF, pI830->savePIPEACONF); - i830WaitForVblank(pScrn); - + OUTREG(DSPABASE, pI830->saveDSPABASE); /* * Program Pipe A's plane * The corresponding display plane may be disabled, and should only be @@ -2167,14 +2172,14 @@ RestoreHWState(ScrnInfoPtr pScrn) DISPPLANE_SEL_PIPE_A) { OUTREG(DSPACNTR, pI830->saveDSPACNTR); OUTREG(DSPABASE, INREG(DSPABASE)); - i830WaitForVblank(pScrn); } if ((pI830->saveDSPBCNTR & DISPPLANE_SEL_PIPE_MASK) == DISPPLANE_SEL_PIPE_A) { OUTREG(DSPBCNTR, pI830->saveDSPBCNTR); OUTREG(DSPBBASE, INREG(DSPBBASE)); - i830WaitForVblank(pScrn); } + if (pI830->savePIPEACONF & PIPEACONF_ENABLE) + i830WaitForVblankPipe(pScrn, PIPE_A); /* wait for plane update */ /* See note about pipe programming above */ if(xf86_config->num_crtc == 2) @@ -2203,20 +2208,24 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(VBLANK_B, pI830->saveVBLANK_B); OUTREG(VSYNC_B, pI830->saveVSYNC_B); OUTREG(BCLRPAT_B, pI830->saveBCLRPAT_B); + OUTREG(PIPEBSRC, pI830->savePIPEBSRC); + OUTREG(PIPEBCONF, pI830->savePIPEBCONF); + if (pI830->savePIPEBCONF & PIPEBCONF_ENABLE) + i830WaitForVblankPipe(pScrn, PIPE_B); /* wait for pipe update */ + else + i830WaitForVblankScanlinePipe(pScrn, PIPE_B); + OUTREG(DSPBSTRIDE, pI830->saveDSPBSTRIDE); OUTREG(DSPBSIZE, pI830->saveDSPBSIZE); OUTREG(DSPBPOS, pI830->saveDSPBPOS); - OUTREG(PIPEBSRC, pI830->savePIPEBSRC); - OUTREG(DSPBBASE, pI830->saveDSPBBASE); if (IS_I965G(pI830)) { OUTREG(DSPBSURF, pI830->saveDSPBSURF); OUTREG(DSPBTILEOFF, pI830->saveDSPBTILEOFF); } - OUTREG(PIPEBCONF, pI830->savePIPEBCONF); - i830WaitForVblank(pScrn); + OUTREG(DSPBBASE, pI830->saveDSPBBASE); /* * Program Pipe B's plane * Note that pipe B may be disabled, and in that case, the plane @@ -2226,14 +2235,14 @@ RestoreHWState(ScrnInfoPtr pScrn) DISPPLANE_SEL_PIPE_B) { OUTREG(DSPACNTR, pI830->saveDSPACNTR); OUTREG(DSPABASE, INREG(DSPABASE)); - i830WaitForVblank(pScrn); } if ((pI830->saveDSPBCNTR & DISPPLANE_SEL_PIPE_MASK) == DISPPLANE_SEL_PIPE_B) { OUTREG(DSPBCNTR, pI830->saveDSPBCNTR); OUTREG(DSPBBASE, INREG(DSPBBASE)); - i830WaitForVblank(pScrn); } + if (pI830->savePIPEBCONF & PIPEBCONF_ENABLE) + i830WaitForVblankPipe(pScrn, PIPE_B); /* wait for plane update */ } OUTREG(VGACNTRL, pI830->saveVGACNTRL); diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index 2379127..4650848 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -906,6 +906,7 @@ i830_sdvo_dpms(xf86OutputPtr output, int mode) { ScrnInfoPtr pScrn = output->scrn; I830OutputPrivatePtr intel_output = output->driver_private; + xf86CrtcPtr crtc = output->crtc; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; I830Ptr pI830 = I830PTR(pScrn); uint32_t temp; @@ -930,7 +931,7 @@ i830_sdvo_dpms(xf86OutputPtr output, int mode) if ((temp & SDVO_ENABLE) == 0) i830_sdvo_write_sdvox(output, temp | SDVO_ENABLE); for (i = 0; i < 2; i++) - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); status = i830_sdvo_get_trained_inputs(output, &input1, &input2); @@ -989,6 +990,7 @@ static void i830_sdvo_restore(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; + xf86CrtcPtr crtc = output->crtc; I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; int o; @@ -1027,7 +1029,7 @@ i830_sdvo_restore(xf86OutputPtr output) if (dev_priv->save_SDVOX & SDVO_ENABLE) { for (i = 0; i < 2; i++) - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); status = i830_sdvo_get_trained_inputs(output, &input1, &input2); if (status == SDVO_CMD_STATUS_SUCCESS && !input1) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, diff --git a/src/i830_tv.c b/src/i830_tv.c index 6adb9f2..b2ccdd4 100644 --- a/src/i830_tv.c +++ b/src/i830_tv.c @@ -890,12 +890,12 @@ i830_tv_restore(xf86OutputPtr output) if (!IS_I9XX(pI830)) { /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); } OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); /* Wait for vblank for the disable to take effect. */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); /* Filter ctl must be set before TV_WIN_SIZE */ OUTREG(TV_FILTER_CTL_1, dev_priv->save_TV_FILTER_CTL_1); @@ -1196,12 +1196,12 @@ i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, if (!IS_I9XX(pI830)) { /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); } OUTREG(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); /* Wait for vblank for the disable to take effect. */ - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); /* Filter ctl must be set before TV_WIN_SIZE */ OUTREG(TV_FILTER_CTL_1, TV_AUTO_SCALE); @@ -1299,7 +1299,7 @@ i830_tv_detect_type (xf86CrtcPtr crtc, DAC_C_0_7_V); OUTREG(TV_CTL, tv_ctl); OUTREG(TV_DAC, tv_dac); - i830WaitForVblank(pScrn); + i830WaitForVblank(crtc); tv_dac = INREG(TV_DAC); OUTREG(TV_DAC, save_tv_dac); OUTREG(TV_CTL, save_tv_ctl);