diff options
author | Eric Anholt <eric@anholt.net> | 2006-12-05 10:01:31 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2006-12-05 12:15:34 -0800 |
commit | e777d38ce98d7220621b049b09df1deca5a5df42 (patch) | |
tree | 441a22533d263d5e1a2778f9bc560c99bcc54568 /src | |
parent | 81dde11d419c8f9198ab3502d9813d66d0bc6d6d (diff) |
WIP code to move mode set sequencing to XFree86 handlers.
It compiles. It definitely doesn't run.
Diffstat (limited to 'src')
-rw-r--r-- | src/i830_crt.c | 20 | ||||
-rw-r--r-- | src/i830_display.c | 428 | ||||
-rw-r--r-- | src/i830_display.h | 1 | ||||
-rw-r--r-- | src/i830_driver.c | 67 | ||||
-rw-r--r-- | src/i830_dvo.c | 49 | ||||
-rw-r--r-- | src/i830_lvds.c | 105 | ||||
-rw-r--r-- | src/i830_sdvo.c | 53 | ||||
-rw-r--r-- | src/i830_tv.c | 35 | ||||
-rw-r--r-- | src/i830_xf86Crtc.h | 56 |
9 files changed, 488 insertions, 326 deletions
diff --git a/src/i830_crt.c b/src/i830_crt.c index 7a706d14..ebd83bc4 100644 --- a/src/i830_crt.c +++ b/src/i830_crt.c @@ -93,13 +93,16 @@ i830_crt_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) return MODE_OK; } -static void -i830_crt_pre_set_mode (xf86OutputPtr output, DisplayModePtr pMode) +static Bool +i830_crt_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { + return TRUE; } static void -i830_crt_post_set_mode (xf86OutputPtr output, DisplayModePtr pMode) +i830_crt_mode_set(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { ScrnInfoPtr pScrn = output->scrn; I830Ptr pI830 = I830PTR(pScrn); @@ -122,11 +125,10 @@ i830_crt_post_set_mode (xf86OutputPtr output, DisplayModePtr pMode) OUTREG(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); } - adpa = ADPA_DAC_ENABLE; - - if (pMode->Flags & V_PHSYNC) + adpa = 0; + if (adjusted_mode->Flags & V_PHSYNC) adpa |= ADPA_HSYNC_ACTIVE_HIGH; - if (pMode->Flags & V_PVSYNC) + if (adjusted_mode->Flags & V_PVSYNC) adpa |= ADPA_VSYNC_ACTIVE_HIGH; if (i830_crtc->pipe == 0) @@ -372,8 +374,8 @@ static const xf86OutputFuncsRec i830_crt_output_funcs = { .save = i830_crt_save, .restore = i830_crt_restore, .mode_valid = i830_crt_mode_valid, - .pre_set_mode = i830_crt_pre_set_mode, - .post_set_mode = i830_crt_post_set_mode, + .mode_fixup = i830_crt_mode_fixup, + .mode_set = i830_crt_mode_set, .detect = i830_crt_detect, .get_modes = i830_crt_get_modes, .destroy = i830_crt_destroy diff --git a/src/i830_display.c b/src/i830_display.c index 29b783bf..eab82f6e 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -408,34 +408,94 @@ i830PipeInUse (xf86CrtcPtr crtc) return FALSE; } +static void +i830_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + ScrnInfoPtr pScrn = crtc->scrn; + I830Ptr pI830 = I830PTR(pScrn); + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + CARD32 temp; + + /* XXX: When our outputs are all unaware of DPMS modes other than off and + * on, we should map those modes to DPMSModeOff in the CRTC. + */ + switch (mode) { + case DPMSModeOn: + case DPMSModeStandby: + case DPMSModeSuspend: + /* Enable the DPLL */ + temp = INREG(dpll_reg); + OUTREG(dpll_reg, temp | DPLL_VCO_ENABLE); + + /* Wait for the clocks to stabilize. */ + usleep(150); + + /* Enable the pipe */ + temp = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE); + + /* Enable the plane */ + temp = INREG(dspcntr_reg); + OUTREG(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + break; + case DPMSModeOff: + /* Disable display plane */ + temp = INREG(dspcntr_reg); + OUTREG(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + + /* Disable the VGA plane that we never use */ + OUTREG(VGACNTRL, VGA_DISP_DISABLE); + + if (!IS_I9XX(pI830)) { + /* Wait for vblank for the disable to take effect */ + i830WaitForVblank(pScrn); + } + + /* Next, disable display pipes */ + temp = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + + /* Wait for vblank for the disable to take effect. */ + i830WaitForVblank(pScrn); + + temp = INREG(dpll_reg); + OUTREG(dpll_reg, temp & ~DPLL_VCO_ENABLE); + break; + } +} + +static Bool +i830_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + return TRUE; +} + /** - * Sets the given video mode on the given pipe. + * Sets up registers for the given mode/adjusted_mode pair. * - * Plane A is always output to pipe A, and plane B to pipe B. The plane - * will not be enabled if plane_enable is FALSE, which is used for - * load detection, when something else will be output to the pipe other than - * display data. + * The clocks, CRTCs and outputs attached to this CRTC must be off. + * + * This shouldn't enable any clocks, CRTCs, or outputs, but they should + * be easily turned on/off after this. */ -Bool -i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, - Bool plane_enable) +static void +i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { ScrnInfoPtr pScrn = crtc->scrn; I830Ptr pI830 = I830PTR(pScrn); I830CrtcPrivatePtr intel_crtc = crtc->driver_private; int pipe = intel_crtc->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; - Bool ok, is_sdvo = FALSE, is_dvo = FALSE; - Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE; - int refclk, pixel_clock; - int i; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; int fp_reg = (pipe == 0) ? FPA0 : FPB0; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; @@ -446,38 +506,23 @@ i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - Bool ret = FALSE; -#ifdef XF86DRI - Bool didLock = FALSE; -#endif - - if (xf86ModesEqual(&crtc->curMode, pMode)) - return TRUE; - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Requested pix clock: %d\n", - pMode->Clock); - - crtc->enabled = i830PipeInUse (crtc); - - if (!crtc->enabled) - { - /* XXX disable crtc? */ - return TRUE; - } + int m1 = 0, m2 = 0, n = 0, p1 = 0, p2 = 0; + int i; + int refclk; + CARD32 dpll = 0, fp = 0, temp, dspcntr; + Bool ok, is_sdvo = FALSE, is_dvo = FALSE; + Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE; -#ifdef XF86DRI - didLock = I830DRILock(pScrn); -#endif - - for (i = 0; i < pI830->xf86_config.num_output; i++) - { + /* Set up some convenient bools for what outputs are connected to + * our pipe, used in DPLL setup. + */ + for (i = 0; i < pI830->xf86_config.num_output; i++) { xf86OutputPtr output = pI830->xf86_config.output[i]; - I830OutputPrivatePtr intel_output = output->driver_private; + I830OutputPrivatePtr intel_output = output->driver_private; + if (output->crtc != crtc) continue; - (*output->funcs->pre_set_mode)(output, pMode); - switch (intel_output->type) { case I830_OUTPUT_LVDS: is_lvds = TRUE; @@ -497,90 +542,23 @@ i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, } } - if (is_lvds && (is_sdvo || is_dvo || is_tv || is_crt)) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't enable LVDS and non-LVDS on the same pipe\n"); - goto done; - } - if (is_tv && (is_sdvo || is_dvo || is_crt || is_lvds)) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't enable a TV and any other output on the same " - "pipe\n"); - goto done; - } - if (pipe == 0 && is_lvds) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't support LVDS on pipe A\n"); - goto done; - } - - htot = (pMode->CrtcHDisplay - 1) | ((pMode->CrtcHTotal - 1) << 16); - hblank = (pMode->CrtcHBlankStart - 1) | ((pMode->CrtcHBlankEnd - 1) << 16); - hsync = (pMode->CrtcHSyncStart - 1) | ((pMode->CrtcHSyncEnd - 1) << 16); - vtot = (pMode->CrtcVDisplay - 1) | ((pMode->CrtcVTotal - 1) << 16); - vblank = (pMode->CrtcVBlankStart - 1) | ((pMode->CrtcVBlankEnd - 1) << 16); - vsync = (pMode->CrtcVSyncStart - 1) | ((pMode->CrtcVSyncEnd - 1) << 16); - pipesrc = ((pMode->HDisplay - 1) << 16) | (pMode->VDisplay - 1); - dspsize = ((pMode->VDisplay - 1) << 16) | (pMode->HDisplay - 1); - pixel_clock = pMode->Clock; - - if (is_lvds && pI830->panel_fixed_hactive != 0) { - /* To enable panel fitting, we need to set the pipe timings to that of - * the screen at its full resolution. So, drop the timings from the - * BIOS VBT tables here. - */ - htot = (pI830->panel_fixed_hactive - 1) | - ((pI830->panel_fixed_hactive + pI830->panel_fixed_hblank - 1) - << 16); - hblank = (pI830->panel_fixed_hactive - 1) | - ((pI830->panel_fixed_hactive + pI830->panel_fixed_hblank - 1) - << 16); - hsync = (pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff - 1) | - ((pI830->panel_fixed_hactive + pI830->panel_fixed_hsyncoff + - pI830->panel_fixed_hsyncwidth - 1) << 16); - - vtot = (pI830->panel_fixed_vactive - 1) | - ((pI830->panel_fixed_vactive + pI830->panel_fixed_vblank - 1) - << 16); - vblank = (pI830->panel_fixed_vactive - 1) | - ((pI830->panel_fixed_vactive + pI830->panel_fixed_vblank - 1) - << 16); - vsync = (pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff - 1) | - ((pI830->panel_fixed_vactive + pI830->panel_fixed_vsyncoff + - pI830->panel_fixed_vsyncwidth - 1) << 16); - pixel_clock = pI830->panel_fixed_clock; - - if (pMode->HDisplay <= pI830->panel_fixed_hactive && - pMode->HDisplay <= pI830->panel_fixed_vactive) - { - pipesrc = ((pMode->HDisplay - 1) << 16) | - (pMode->VDisplay - 1); - dspsize = ((pMode->VDisplay - 1) << 16) | - (pMode->HDisplay - 1); - } - } - - /* Adjust the clock for pixel multiplication. - * See DPLL_MD_UDI_MULTIPLIER_MASK. - */ - if (is_sdvo) { - pixel_clock *= i830_sdvo_get_pixel_multiplier(pMode); - } - if (IS_I9XX(pI830)) { refclk = 96000; } else { refclk = 48000; } - ok = i830FindBestPLL(crtc, pixel_clock, refclk, &m1, &m2, &n, + + ok = i830FindBestPLL(crtc, adjusted_mode->Clock, refclk, &m1, &m2, &n, &p1, &p2); - if (!ok) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Couldn't find PLL settings for mode!\n"); - goto done; - } + if (!ok) + FatalError("Couldn't find PLL settings for mode!\n"); - dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS; + fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2); + + i830PrintPll("chosen", refclk, m1, m2, n, p1, p2); + ErrorF("clock regs: 0x%08x, 0x%08x\n", (int)dpll, (int)fp); + + dpll = DPLL_VGA_MODE_DIS; if (IS_I9XX(pI830)) { if (is_lvds) dpll |= DPLLB_MODE_LVDS; @@ -615,33 +593,15 @@ i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; } -#if 0 +#if 0 else if (is_lvds) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; #endif - else + else dpll |= PLL_REF_INPUT_DREFCLK; - fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2); - -#if 1 - ErrorF("hact: %d htot: %d hbstart: %d hbend: %d hsyncstart: %d hsyncend: %d\n", - (int)(htot & 0xffff) + 1, (int)(htot >> 16) + 1, - (int)(hblank & 0xffff) + 1, (int)(hblank >> 16) + 1, - (int)(hsync & 0xffff) + 1, (int)(hsync >> 16) + 1); - ErrorF("vact: %d vtot: %d vbstart: %d vbend: %d vsyncstart: %d vsyncend: %d\n", - (int)(vtot & 0xffff) + 1, (int)(vtot >> 16) + 1, - (int)(vblank & 0xffff) + 1, (int)(vblank >> 16) + 1, - (int)(vsync & 0xffff) + 1, (int)(vsync >> 16) + 1); - ErrorF("pipesrc: %dx%d, dspsize: %dx%d\n", - (int)(pipesrc >> 16) + 1, (int)(pipesrc & 0xffff) + 1, - (int)(dspsize & 0xffff) + 1, (int)(dspsize >> 16) + 1); -#endif - - i830PrintPll("chosen", refclk, m1, m2, n, p1, p2); - ErrorF("clock regs: 0x%08x, 0x%08x\n", (int)dpll, (int)fp); - - dspcntr = DISPLAY_PLANE_ENABLE; + /* Set up the display plane register */ + dspcntr = 0; switch (pScrn->bitsPerPixel) { case 8: dspcntr |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE; @@ -668,61 +628,139 @@ i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, else dspcntr |= DISPPLANE_SEL_PIPE_B; - OUTREG(VGACNTRL, VGA_DISP_DISABLE); + OUTREG(fp_reg, fp); + OUTREG(dpll_reg, dpll); + if (IS_I965G(pI830)) { + /* Set the SDVO multiplier/divider to 1x for the sake of analog output. + * It will be updated by the SDVO code if SDVO had fixed up the clock + * for a higher multiplier. + */ + OUTREG(dpll_md_reg, 0); + } - /* Finally, set the mode. */ - /* First, disable display planes */ - temp = INREG(dspcntr_reg); - OUTREG(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + OUTREG(htot_reg, (adjusted_mode->CrtcHDisplay - 1) | + ((adjusted_mode->CrtcHTotal - 1) << 16)); + OUTREG(hblank_reg, (adjusted_mode->CrtcHBlankStart - 1) | + ((adjusted_mode->CrtcHBlankEnd - 1) << 16)); + OUTREG(hsync_reg, (adjusted_mode->CrtcHSyncStart - 1) | + ((adjusted_mode->CrtcHSyncEnd - 1) << 16)); + OUTREG(vtot_reg, (adjusted_mode->CrtcVDisplay - 1) | + ((adjusted_mode->CrtcVTotal - 1) << 16)); + OUTREG(vblank_reg, (adjusted_mode->CrtcVBlankStart - 1) | + ((adjusted_mode->CrtcVBlankEnd - 1) << 16)); + OUTREG(vsync_reg, (adjusted_mode->CrtcVSyncStart - 1) | + ((adjusted_mode->CrtcVSyncEnd - 1) << 16)); + OUTREG(dspstride_reg, pScrn->displayWidth * pI830->cpp); + /* pipesrc and dspsize control the size that is scaled from, which should + * always be the user's requested size. + */ + OUTREG(dspsize_reg, ((mode->HDisplay - 1) << 16) | (mode->VDisplay - 1)); + OUTREG(dsppos_reg, 0); + i830PipeSetBase(crtc, crtc->x, crtc->y); + OUTREG(pipesrc_reg, ((mode->HDisplay - 1) << 16) | (mode->VDisplay - 1)); + temp = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, temp); + OUTREG(dspcntr_reg, dspcntr); - /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); + /* Disable the panel fitter if it was on our pipe */ + if (!IS_I830(pI830) && ((INREG(PFIT_CONTROL) >> 29) & 0x3) == pipe) + OUTREG(PFIT_CONTROL, 0); +} - /* Next, disable display pipes */ - temp = INREG(pipeconf_reg); - OUTREG(pipeconf_reg, temp & ~PIPEACONF_ENABLE); +/** + * Sets the given video mode on the given pipe. + * + * Plane A is always output to pipe A, and plane B to pipe B. The plane + * will not be enabled if plane_enable is FALSE, which is used for + * load detection, when something else will be output to the pipe other than + * display data. + */ +Bool +i830PipeSetMode(xf86CrtcPtr crtc, DisplayModePtr pMode, + Bool plane_enable) +{ + ScrnInfoPtr pScrn = crtc->scrn; + I830Ptr pI830 = I830PTR(pScrn); + int i; + Bool ret = FALSE; +#ifdef XF86DRI + Bool didLock = FALSE; +#endif + DisplayModePtr adjusted_mode; - OUTREG(fp_reg, fp); - OUTREG(dpll_reg, dpll); + /* XXX: curMode */ + + adjusted_mode = xf86DuplicateMode(pMode); + + crtc->enabled = i830PipeInUse (crtc); + + if (!crtc->enabled) + { + /* XXX disable crtc? */ + return TRUE; + } + +#ifdef XF86DRI + didLock = I830DRILock(pScrn); +#endif - /* - * If the panel fitter is stuck on our pipe, turn it off. - * The LVDS output will set it as necessary in post_set_mode. + /* Pass our mode to the outputs and the CRTC to give them a chance to + * adjust it according to limitations or output properties, and also + * a chance to reject the mode entirely. */ - if (!IS_I830(pI830)) { - if (((INREG(PFIT_CONTROL) >> 29) & 0x3) == pipe) - OUTREG(PFIT_CONTROL, 0); + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr output = pI830->xf86_config.output[i]; + + if (output->crtc != crtc) + continue; + + if (!output->funcs->mode_fixup(output, pMode, adjusted_mode)) { + ret = FALSE; + goto done; + } } + if (!crtc->funcs->mode_fixup(crtc, pMode, adjusted_mode)) { + ret = FALSE; + goto done; + } + + /* Disable the outputs and CRTCs before setting the mode. */ for (i = 0; i < pI830->xf86_config.num_output; i++) { - xf86OutputPtr output = pI830->xf86_config.output[i]; - if (output->crtc == crtc) - (*output->funcs->post_set_mode)(output, pMode); + xf86OutputPtr output = pI830->xf86_config.output[i]; + + if (output->crtc != crtc) + continue; + + /* Disable the output as the first thing we do. */ + output->funcs->dpms(output, DPMSModeOff); } - OUTREG(htot_reg, htot); - OUTREG(hblank_reg, hblank); - OUTREG(hsync_reg, hsync); - OUTREG(vtot_reg, vtot); - OUTREG(vblank_reg, vblank); - OUTREG(vsync_reg, vsync); - OUTREG(dspstride_reg, pScrn->displayWidth * pI830->cpp); - OUTREG(dspsize_reg, dspsize); - OUTREG(dsppos_reg, 0); - i830PipeSetBase(crtc, crtc->x, crtc->y); - OUTREG(pipesrc_reg, pipesrc); + crtc->funcs->dpms(crtc, DPMSModeOff); - /* Then, turn the pipe on first */ - temp = INREG(pipeconf_reg); - OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE); + /* Set up the DPLL and any output state that needs to adjust or depend + * on the DPLL. + */ + crtc->funcs->mode_set(crtc, pMode, adjusted_mode); + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr output = pI830->xf86_config.output[i]; + if (output->crtc == crtc) + output->funcs->mode_set(output, pMode, adjusted_mode); + } - if (plane_enable) { - /* And then turn the plane on */ - OUTREG(dspcntr_reg, dspcntr); + /* Now, enable the clocks, plane, pipe, and outputs that we set up. */ + crtc->funcs->dpms(crtc, DPMSModeOn); + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr output = pI830->xf86_config.output[i]; + if (output->crtc == crtc) + output->funcs->dpms(output, DPMSModeOn); } crtc->curMode = *pMode; + i830DumpRegs(pScrn); + + /* XXX free adjustedmode */ ret = TRUE; done: #ifdef XF86DRI @@ -962,3 +1000,29 @@ i830ReleaseLoadDetectPipe(xf86OutputPtr output) i830DisableUnusedFunctions(pScrn); } } + +static const xf86CrtcFuncsRec i830_crtc_funcs = { + .dpms = i830_crtc_dpms, + .save = NULL, /* XXX */ + .restore = NULL, /* XXX */ + .mode_fixup = i830_crtc_mode_fixup, + .mode_set = i830_crtc_mode_set, + .destroy = NULL, /* XXX */ +}; + +void +i830_crtc_init(ScrnInfoPtr pScrn, int pipe) +{ + xf86CrtcPtr crtc; + I830CrtcPrivatePtr intel_crtc; + + crtc = xf86CrtcCreate (pScrn, &i830_crtc_funcs); + if (crtc == NULL) + return; + + intel_crtc = xnfcalloc (sizeof (I830CrtcPrivateRec), 1); + intel_crtc->pipe = pipe; + + crtc->driver_private = intel_crtc; +} + diff --git a/src/i830_display.h b/src/i830_display.h index c80c3f72..c1725ab1 100644 --- a/src/i830_display.h +++ b/src/i830_display.h @@ -41,6 +41,7 @@ void i830DescribeOutputConfiguration(ScrnInfoPtr pScrn); xf86CrtcPtr i830GetLoadDetectPipe(xf86OutputPtr output); void i830ReleaseLoadDetectPipe(xf86OutputPtr output); Bool i830PipeInUse(xf86CrtcPtr crtc); +void i830_crtc_init(ScrnInfoPtr pScrn, int pipe); /** @{ */ diff --git a/src/i830_driver.c b/src/i830_driver.c index 3aafe3aa..8f017ab2 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -715,28 +715,7 @@ I830SetupOutputs(ScrnInfoPtr pScrn) * Setup the CRTCs */ -static const xf86CrtcFuncsRec i830_crtc_funcs = { -}; - -static void -I830SetupCrtcs(ScrnInfoPtr pScrn, int num_pipe) -{ - int p; - for (p = 0; p < num_pipe; p++) - { - xf86CrtcPtr crtc = xf86CrtcCreate (pScrn, &i830_crtc_funcs); - I830CrtcPrivatePtr intel_crtc; - - if (!crtc) - break; - intel_crtc = xnfcalloc (sizeof (I830CrtcPrivateRec), 1); - intel_crtc->pipe = p; - - crtc->driver_private = intel_crtc; - } -} - static void I830PreInitDDC(ScrnInfoPtr pScrn) { @@ -1340,7 +1319,9 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) I830PreInitDDC(pScrn); I830SetupOutputs(pScrn); - I830SetupCrtcs(pScrn, num_pipe); + for (i = 0; i < num_pipe; i++) { + i830_crtc_init(pScrn, i); + } if (xf86ReturnOptValBool(pI830->Options, OPTION_CLONE, FALSE)) { if (num_pipe == 1) { @@ -2286,7 +2267,13 @@ RestoreHWState(ScrnInfoPtr pScrn) vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS); vgaHWLock(hwp); - /* First, disable display planes */ + /* Disable outputs */ + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr output = pI830->xf86_config.output[i]; + output->funcs->dpms(output, DPMSModeOff); + } + + /* Disable display planes */ temp = INREG(DSPACNTR); OUTREG(DSPACNTR, temp & ~DISPLAY_PLANE_ENABLE); temp = INREG(DSPBCNTR); @@ -2298,12 +2285,6 @@ RestoreHWState(ScrnInfoPtr pScrn) temp = INREG(PIPEBCONF); OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE); - /* Disable outputs if necessary */ - for (i = 0; i < pI830->xf86_config.num_output; i++) { - xf86OutputPtr output = pI830->xf86_config.output[i]; - (*output->funcs->pre_set_mode) (output, NULL); - } - i830WaitForVblank(pScrn); OUTREG(FPA0, pI830->saveFPA0); @@ -2311,6 +2292,16 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(DPLL_A, pI830->saveDPLL_A); if (IS_I965G(pI830)) OUTREG(DPLL_A_MD, pI830->saveDPLL_A_MD); + if(pI830->xf86_config.num_crtc == 2) { + OUTREG(FPB0, pI830->saveFPB0); + OUTREG(FPB1, pI830->saveFPB1); + OUTREG(DPLL_B, pI830->saveDPLL_B); + if (IS_I965G(pI830)) + OUTREG(DPLL_B_MD, pI830->saveDPLL_B_MD); + } + /* Wait for clocks to stabilize */ + usleep(150); + OUTREG(HTOTAL_A, pI830->saveHTOTAL_A); OUTREG(HBLANK_A, pI830->saveHBLANK_A); OUTREG(HSYNC_A, pI830->saveHSYNC_A); @@ -2327,11 +2318,6 @@ RestoreHWState(ScrnInfoPtr pScrn) } if(pI830->xf86_config.num_crtc == 2) { - OUTREG(FPB0, pI830->saveFPB0); - OUTREG(FPB1, pI830->saveFPB1); - OUTREG(DPLL_B, pI830->saveDPLL_B); - if (IS_I965G(pI830)) - OUTREG(DPLL_B_MD, pI830->saveDPLL_B_MD); OUTREG(HTOTAL_B, pI830->saveHTOTAL_B); OUTREG(HBLANK_B, pI830->saveHBLANK_B); OUTREG(HSYNC_B, pI830->saveHSYNC_B); @@ -2348,12 +2334,8 @@ RestoreHWState(ScrnInfoPtr pScrn) } } - OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL); - - for (i = 0; i < pI830->xf86_config.num_output; i++) { - xf86OutputPtr output = pI830->xf86_config.output[i]; - (*output->funcs->restore) (output); - } + if (!IS_I830(pI830) && !IS_845G(pI830)) + OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL); if (IS_I965G(pI830)) { OUTREG(DSPASURF, pI830->saveDSPASURF); @@ -2371,6 +2353,11 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(DSPACNTR, pI830->saveDSPACNTR); OUTREG(DSPBCNTR, pI830->saveDSPBCNTR); + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr output = pI830->xf86_config.output[i]; + (*output->funcs->restore) (output); + } + for(i = 0; i < 7; i++) { OUTREG(SWF0 + (i << 2), pI830->saveSWF[i]); OUTREG(SWF00 + (i << 2), pI830->saveSWF[i+7]); diff --git a/src/i830_dvo.c b/src/i830_dvo.c index 6fe31575..04d20387 100644 --- a/src/i830_dvo.c +++ b/src/i830_dvo.c @@ -59,11 +59,17 @@ struct _I830DVODriver i830_dvo_drivers[] = static void i830_dvo_dpms(xf86OutputPtr output, int mode) { + ScrnInfoPtr pScrn = output->scrn; + I830Ptr pI830 = I830PTR(pScrn); I830OutputPrivatePtr intel_output = output->driver_private; - if (mode == DPMSModeOn) + + if (mode == DPMSModeOn) { + OUTREG(DVOC, INREG(DVOC) | DVO_ENABLE); (*intel_output->i2c_drv->vid_rec->Power)(intel_output->i2c_drv->dev_priv, TRUE); - else + } else { (*intel_output->i2c_drv->vid_rec->Power)(intel_output->i2c_drv->dev_priv, FALSE); + OUTREG(DVOC, INREG(DVOC) & ~DVO_ENABLE); + } } static void @@ -113,48 +119,51 @@ i830_dvo_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) return MODE_BAD; } -static void -i830_dvo_pre_set_mode(xf86OutputPtr output, DisplayModePtr pMode) +static Bool +i830_dvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { - ScrnInfoPtr pScrn = output->scrn; - I830Ptr pI830 = I830PTR(pScrn); - I830OutputPrivatePtr intel_output = output->driver_private; + /* XXX: Hook this up to a DVO driver function */ - (*intel_output->i2c_drv->vid_rec->Mode)(intel_output->i2c_drv->dev_priv, pMode); - - OUTREG(DVOC, INREG(DVOC) & ~DVO_ENABLE); + return TRUE; } static void -i830_dvo_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) +i830_dvo_mode_set(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { ScrnInfoPtr pScrn = output->scrn; I830Ptr pI830 = I830PTR(pScrn); xf86CrtcPtr crtc = output->crtc; I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + I830OutputPrivatePtr intel_output = output->driver_private; int pipe = intel_crtc->pipe; CARD32 dvo; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + intel_output->i2c_drv->vid_rec->Mode(intel_output->i2c_drv->dev_priv, + mode); + /* Save the data order, since I don't know what it should be set to. */ dvo = INREG(DVOC) & (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); - dvo |= DVO_ENABLE; dvo |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | DVO_BLANK_ACTIVE_HIGH; if (pipe == 1) dvo |= DVO_PIPE_B_SELECT; - if (pMode->Flags & V_PHSYNC) + if (adjusted_mode->Flags & V_PHSYNC) dvo |= DVO_HSYNC_ACTIVE_HIGH; - if (pMode->Flags & V_PVSYNC) + if (adjusted_mode->Flags & V_PVSYNC) dvo |= DVO_VSYNC_ACTIVE_HIGH; OUTREG(dpll_reg, INREG(dpll_reg) | DPLL_DVO_HIGH_SPEED); - /*OUTREG(DVOB_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | - (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ - OUTREG(DVOC_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | - (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT)); + /*OUTREG(DVOB_SRCDIM, + (adjusted_mode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ + OUTREG(DVOC_SRCDIM, + (adjusted_mode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT)); /*OUTREG(DVOB, dvo);*/ OUTREG(DVOC, dvo); } @@ -222,8 +231,8 @@ static const xf86OutputFuncsRec i830_dvo_output_funcs = { .save = i830_dvo_save, .restore = i830_dvo_restore, .mode_valid = i830_dvo_mode_valid, - .pre_set_mode = i830_dvo_pre_set_mode, - .post_set_mode = i830_dvo_post_set_mode, + .mode_fixup = i830_dvo_mode_fixup, + .mode_set = i830_dvo_mode_set, .detect = i830_dvo_detect, .get_modes = i830_ddc_get_modes, .destroy = i830_dvo_destroy diff --git a/src/i830_lvds.c b/src/i830_lvds.c index cf709569..a9c0e204 100644 --- a/src/i830_lvds.c +++ b/src/i830_lvds.c @@ -40,7 +40,7 @@ static void i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on) { I830Ptr pI830 = I830PTR(pScrn); - CARD32 pp_status, pp_control; + CARD32 pp_status; CARD32 blc_pwm_ctl; int backlight_duty_cycle; @@ -77,6 +77,8 @@ i830_lvds_dpms (xf86OutputPtr output, int mode) i830SetLVDSPanelPower(pScrn, TRUE); else i830SetLVDSPanelPower(pScrn, FALSE); + + /* XXX: We never power down the LVDS pair. */ } static void @@ -128,28 +130,86 @@ i830_lvds_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) return MODE_OK; } -static void -i830_lvds_pre_set_mode(xf86OutputPtr output, DisplayModePtr pMode) +static Bool +i830_lvds_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { - ScrnInfoPtr pScrn = output->scrn; - /* Always make sure the LVDS is off before we play with DPLLs and pipe - * configuration. We can skip this in some cases (for example, going - * between hi-res modes with automatic panel scaling are fine), but be - * conservative for now. + ScrnInfoPtr pScrn = output->scrn; + I830Ptr pI830 = I830PTR(pScrn); + I830CrtcPrivatePtr intel_crtc = output->crtc->driver_private; + int i; + + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr other_output = pI830->xf86_config.output[i]; + + if (other_output != output && other_output->crtc == output->crtc) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Can't enable LVDS and another output on the same " + "pipe\n"); + return FALSE; + } + } + + if (intel_crtc->pipe == 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Can't support LVDS on pipe A\n"); + return FALSE; + } + + /* If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (pI830->panel_fixed_hactive != 0) { + adjusted_mode->HDisplay = pI830->panel_fixed_hactive; + adjusted_mode->HTotal = adjusted_mode->HDisplay + + pI830->panel_fixed_hblank; + adjusted_mode->HSyncStart = adjusted_mode->HDisplay + + pI830->panel_fixed_hsyncoff; + adjusted_mode->HSyncStart = adjusted_mode->HSyncStart + + pI830->panel_fixed_hsyncwidth; + adjusted_mode->VDisplay = pI830->panel_fixed_vactive; + adjusted_mode->VTotal = adjusted_mode->VDisplay + + pI830->panel_fixed_hblank; + adjusted_mode->VSyncStart = adjusted_mode->VDisplay + + pI830->panel_fixed_hsyncoff; + adjusted_mode->VSyncStart = adjusted_mode->VSyncStart + + pI830->panel_fixed_hsyncwidth; + adjusted_mode->Clock = pI830->panel_fixed_clock; + xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); + } + + /* XXX: if we don't have BIOS fixed timings (or we have + * a preferred mode from DDC, probably), we should use the + * DDC mode as the fixed timing. */ - i830SetLVDSPanelPower(pScrn, FALSE); + + /* XXX: It would be nice to support lower refresh rates on the + * panels to reduce power consumption, and perhaps match the + * user's requested refresh rate. + */ + + return TRUE; } static void -i830_lvds_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) +i830_lvds_mode_set(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { - ScrnInfoPtr pScrn = output->scrn; - I830Ptr pI830 = I830PTR(pScrn); - CARD32 pfit_control; + ScrnInfoPtr pScrn = output->scrn; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 pfit_control; + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + OUTREG(LVDS, LVDS_PORT_EN | LVDS_PIPEB_SELECT); /* Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to - * register description. + * register description and PRM. */ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | @@ -159,19 +219,6 @@ i830_lvds_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) pfit_control |= PANEL_8TO6_DITHER_ENABLE; OUTREG(PFIT_CONTROL, pfit_control); - - /* Disable the PLL before messing with LVDS enable */ - OUTREG(FPB0, INREG(FPB0) & ~DPLL_VCO_ENABLE); - - /* LVDS must be powered on before PLL is enabled and before power - * sequencing the panel. - */ - OUTREG(LVDS, INREG(LVDS) | LVDS_PORT_EN | LVDS_PIPEB_SELECT); - - /* Re-enable the PLL */ - OUTREG(FPB0, INREG(FPB0) | DPLL_VCO_ENABLE); - - i830SetLVDSPanelPower(pScrn, TRUE); } /** @@ -234,8 +281,8 @@ static const xf86OutputFuncsRec i830_lvds_output_funcs = { .save = i830_lvds_save, .restore = i830_lvds_restore, .mode_valid = i830_lvds_mode_valid, - .pre_set_mode = i830_lvds_pre_set_mode, - .post_set_mode = i830_lvds_post_set_mode, + .mode_fixup = i830_lvds_mode_fixup, + .mode_set = i830_lvds_mode_set, .detect = i830_lvds_detect, .get_modes = i830_lvds_get_modes, .destroy = i830_lvds_destroy diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index c75800d4..7755252a 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -534,15 +534,36 @@ i830_sdvo_set_clock_rate_mult(xf86OutputPtr output, CARD8 val) return TRUE; } +static Bool +i830_sdvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO + * device will be told of the multiplier during mode_set. + */ + adjusted_mode->Clock *= i830_sdvo_get_pixel_multiplier(mode); + + return TRUE; +} + static void -i830_sdvo_pre_set_mode(xf86OutputPtr output, DisplayModePtr mode) +i830_sdvo_mode_set(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { ScrnInfoPtr pScrn = output->scrn; I830Ptr pI830 = I830PTR(pScrn); I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; - CARD16 width; - CARD16 height; + xf86CrtcPtr crtc = output->crtc; + I830CrtcPrivatePtr intel_crtc = crtc->driver_private; + Bool input1, input2; + CARD32 dpll, sdvox; + int dpll_reg = (intel_crtc->pipe == 0) ? DPLL_A : DPLL_B; + int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; + int sdvo_pixel_multiply; + int i; + CARD8 status; + CARD16 width, height; CARD16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; CARD16 h_sync_offset, v_sync_offset; struct i830_sdvo_dtd output_dtd; @@ -633,26 +654,6 @@ i830_sdvo_pre_set_mode(xf86OutputPtr output, DisplayModePtr mode) break; } - OUTREG(dev_priv->output_device, INREG(dev_priv->output_device) & ~SDVO_ENABLE); -} - -static void -i830_sdvo_post_set_mode(xf86OutputPtr output, DisplayModePtr mode) -{ - ScrnInfoPtr pScrn = output->scrn; - I830OutputPrivatePtr intel_output = output->driver_private; - struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; - xf86CrtcPtr crtc = output->crtc; - I830CrtcPrivatePtr intel_crtc = crtc->driver_private; - I830Ptr pI830 = I830PTR(pScrn); - Bool input1, input2; - CARD32 dpll, sdvox; - int dpll_reg = (intel_crtc->pipe == 0) ? DPLL_A : DPLL_B; - int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int sdvo_pixel_multiply; - int i; - CARD8 status; - /* Set the SDVO control regs. */ sdvox = INREG(dev_priv->output_device); switch (dev_priv->output_device) { @@ -663,7 +664,7 @@ i830_sdvo_post_set_mode(xf86OutputPtr output, DisplayModePtr mode) sdvox &= SDVOC_PRESERVE_MASK; break; } - sdvox |= SDVO_ENABLE | (9 << 19) | SDVO_BORDER_ENABLE; + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; if (intel_crtc->pipe == 1) sdvox |= SDVO_PIPE_B_SELECT; @@ -1014,8 +1015,8 @@ static const xf86OutputFuncsRec i830_sdvo_output_funcs = { .save = i830_sdvo_save, .restore = i830_sdvo_restore, .mode_valid = i830_sdvo_mode_valid, - .pre_set_mode = i830_sdvo_pre_set_mode, - .post_set_mode = i830_sdvo_post_set_mode, + .mode_fixup = i830_sdvo_mode_fixup, + .mode_set = i830_sdvo_mode_set, .detect = i830_sdvo_detect, .get_modes = i830_ddc_get_modes, .destroy = i830_sdvo_destroy diff --git a/src/i830_tv.c b/src/i830_tv.c index f5716f8b..87aecda6 100644 --- a/src/i830_tv.c +++ b/src/i830_tv.c @@ -295,8 +295,33 @@ static const CARD32 v_chroma[43] = { 0x28003100, 0x28002F00, 0x00003100, }; +static Bool +i830_tv_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + ScrnInfoPtr pScrn = output->scrn; + I830Ptr pI830 = I830PTR(pScrn); + int i; + + for (i = 0; i < pI830->xf86_config.num_output; i++) { + xf86OutputPtr other_output = pI830->xf86_config.output[i]; + + if (other_output != output && other_output->crtc == output->crtc) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Can't enable TV and another output on the same " + "pipe\n"); + return FALSE; + } + } + + /* XXX: fill me in */ + + return TRUE; +} + static void -i830_tv_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) +i830_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { ScrnInfoPtr pScrn = output->scrn; I830Ptr pI830 = I830PTR(pScrn); @@ -359,7 +384,7 @@ i830_tv_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); - tv_ctl = TV_ENC_ENABLE; + tv_ctl = 0; if (intel_crtc->pipe == 1) tv_ctl |= TV_ENC_PIPEB_SELECT; @@ -403,7 +428,7 @@ i830_tv_post_set_mode(xf86OutputPtr output, DisplayModePtr pMode) tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; tv_filter_ctl = TV_AUTO_SCALE; - if (pMode->HDisplay > 1024) + if (mode->HDisplay > 1024) tv_ctl |= TV_V_FILTER_BYPASS; OUTREG(TV_H_CTL_1, hctl1); @@ -635,8 +660,8 @@ static const xf86OutputFuncsRec i830_tv_output_funcs = { .save = i830_tv_save, .restore = i830_tv_restore, .mode_valid = i830_tv_mode_valid, - .pre_set_mode = i830_tv_pre_set_mode, - .post_set_mode = i830_tv_post_set_mode, + .mode_fixup = i830_tv_mode_fixup, + .mode_set = i830_tv_mode_set, .detect = i830_tv_detect, .get_modes = i830_tv_get_modes, .destroy = i830_tv_destroy diff --git a/src/i830_xf86Crtc.h b/src/i830_xf86Crtc.h index 2952c8d5..b5dbe511 100644 --- a/src/i830_xf86Crtc.h +++ b/src/i830_xf86Crtc.h @@ -27,14 +27,15 @@ #include "i830_xf86Modes.h" typedef struct _xf86Crtc xf86CrtcRec, *xf86CrtcPtr; +typedef struct _xf86Output xf86OutputRec, *xf86OutputPtr; typedef struct _xf86CrtcFuncs { /** * Turns the crtc on/off, or sets intermediate power levels if available. * * Unsupported intermediate modes drop to the lower power setting. If the - * mode is DPMSModeOff, the crtc must be disabled, as the DPLL may be - * disabled afterwards. + * mode is DPMSModeOff, the crtc must be disabled sufficiently for it to + * be safe to call mode_set. */ void (*dpms)(xf86CrtcPtr crtc, @@ -52,6 +53,27 @@ typedef struct _xf86CrtcFuncs { void (*restore)(xf86CrtcPtr crtc); + + /** + * Callback to adjust the mode to be set in the CRTC. + * + * This allows a CRTC to adjust the clock or even the entire set of + * timings, which is used for panels with fixed timings or for + * buses with clock limitations. + */ + Bool + (*mode_fixup)(xf86CrtcPtr crtc, + DisplayModePtr mode, + DisplayModePtr adjusted_mode); + + /** + * Callback for setting up a video mode after fixups have been made. + */ + void + (*mode_set)(xf86CrtcPtr crtc, + DisplayModePtr mode, + DisplayModePtr adjusted_mode); + /** * Clean up driver-specific bits of the crtc */ @@ -127,8 +149,6 @@ struct _xf86Crtc { #endif }; -typedef struct _xf86Output xf86OutputRec, *xf86OutputPtr; - typedef struct _xf86OutputFuncs { /** * Turns the output on/off, or sets intermediate power levels if available. @@ -157,7 +177,7 @@ typedef struct _xf86OutputFuncs { * Callback for testing a video mode for a given output. * * This function should only check for cases where a mode can't be supported - * on the pipe specifically, and not represent generic CRTC limitations. + * on the output specifically, and not represent generic CRTC limitations. * * \return MODE_OK if the mode is valid, or another MODE_* otherwise. */ @@ -166,22 +186,28 @@ typedef struct _xf86OutputFuncs { DisplayModePtr pMode); /** - * Callback for setting up a video mode before any crtc/dpll changes. + * Callback to adjust the mode to be set in the CRTC. * - * \param pMode the mode that will be set, or NULL if the mode to be set is - * unknown (such as the restore path of VT switching). + * This allows an output to adjust the clock or even the entire set of + * timings, which is used for panels with fixed timings or for + * buses with clock limitations. */ - void - (*pre_set_mode)(xf86OutputPtr output, - DisplayModePtr pMode); + Bool + (*mode_fixup)(xf86OutputPtr output, + DisplayModePtr mode, + DisplayModePtr adjusted_mode); /** - * Callback for setting up a video mode after the DPLL update but before - * the plane is enabled. + * Callback for setting up a video mode after fixups have been made. + * + * This is only called while the output is disabled. The dpms callback + * must be all that's necessary for the output, to turn the output on + * after this function is called. */ void - (*post_set_mode)(xf86OutputPtr output, - DisplayModePtr pMode); + (*mode_set)(xf86OutputPtr output, + DisplayModePtr mode, + DisplayModePtr adjusted_mode); /** * Probe for a connected output, and return detect_status. |