summaryrefslogtreecommitdiff
path: root/src/i830_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/i830_display.c')
-rw-r--r--src/i830_display.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/src/i830_display.c b/src/i830_display.c
new file mode 100644
index 00000000..95fa936d
--- /dev/null
+++ b/src/i830_display.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "xf86_ansic.h"
+#include "i830.h"
+#include "i830_bios.h"
+#include "i830_display.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)
+{
+ return refclk * (5 * m1 + m2) / n / (p1 * p2);
+}
+
+static void
+i830PrintPll(char *prefix, int refclk, int m1, int m2, int n, int p1, int p2)
+{
+ int dotclock;
+
+ dotclock = i830_clock(refclk, m1, m2, n, p1, p2);
+
+ ErrorF("%s: dotclock %d ((%d, %d), %d, (%d, %d))\n", prefix, dotclock,
+ m1, m2, n, p1, p2);
+}
+
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given outputs.
+ *
+ * The equation for these divisors would be:
+ * clk = refclk * (5 * m1 + m2) / n / (p1 * p2)
+ */
+static Bool
+i830PllIsValid(ScrnInfoPtr pScrn, int outputs, int refclk, int m1, int m2,
+ int n, int p1, int p2)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ int p, m, vco, dotclock;
+ int min_m1, max_m1, min_m2, max_m2, min_m, max_m, min_n, max_n;
+ int min_p1, max_p1, min_p, max_p, min_vco, max_vco, min_dot, max_dot;
+
+ if (IS_I9XX(pI830)) {
+ min_m1 = 10;
+ max_m1 = 20;
+ min_m2 = 5;
+ max_m2 = 9;
+ min_m = 70;
+ max_m = 120;
+ min_n = 3;
+ max_n = 8;
+ min_p1 = 1;
+ max_p1 = 8;
+ if (outputs & PIPE_LCD_ACTIVE) {
+ min_p = 7;
+ max_p = 98;
+ } else {
+ min_p = 5;
+ max_p = 80;
+ }
+ min_vco = 1400000;
+ max_vco = 2800000;
+ min_dot = 20000;
+ max_dot = 400000;
+ } else {
+ min_m1 = 18;
+ max_m1 = 26;
+ min_m2 = 6;
+ max_m2 = 16;
+ min_m = 96;
+ max_m = 140;
+ min_n = 3;
+ max_n = 16;
+ min_p1 = 2;
+ max_p1 = 18;
+ min_vco = 930000;
+ max_vco = 1400000;
+ min_dot = 20000;
+ max_dot = 350000;
+ min_p = 4;
+ max_p = 128;
+ }
+
+ p = p1 * p2;
+ m = 5 * m1 + m2;
+ vco = refclk * m / n;
+ dotclock = i830_clock(refclk, m1, m2, n, p1, p2);
+
+ if (p1 < min_p1 || p1 > max_p1)
+ return FALSE;
+ if (p < min_p || p > max_p)
+ return FALSE;
+ if (m2 < min_m2 || m2 > max_m2)
+ return FALSE;
+ if (m1 < min_m1 || m1 > max_m1)
+ return FALSE;
+ if (m1 <= m2)
+ return FALSE;
+ if (m < min_m || m > max_m)
+ return FALSE;
+ if (n < min_n || n > max_n)
+ return FALSE;
+ if (vco < min_vco || vco > max_vco)
+ return FALSE;
+ /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+ * output, etc., rather than just a single range.
+ */
+ if (dotclock < min_dot || dotclock > max_dot)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given refclk,
+ * or FALSE. Divisor values are the actual divisors for
+ * clk = refclk * (5 * m1 + m2) / n / (p1 * p2)
+ */
+static Bool
+i830FindBestPLL(ScrnInfoPtr pScrn, int outputs, int target, int refclk,
+ int *outm1, int *outm2, int *outn, int *outp1, int *outp2)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ int m1, m2, n, p1, p2;
+ int err = target;
+ int min_m1, max_m1, min_m2, max_m2, min_n, max_n, min_p1, max_p1;
+
+ if (IS_I9XX(pI830)) {
+ min_m1 = 10;
+ max_m1 = 20;
+ min_m2 = 5;
+ max_m2 = 9;
+ min_n = 3;
+ max_n = 8;
+ min_p1 = 1;
+ max_p1 = 8;
+ if (outputs & PIPE_LCD_ACTIVE) {
+ if (target < 200000) /* XXX: Is this the right cutoff? */
+ p2 = 14;
+ else
+ p2 = 7;
+ } else {
+ if (target < 200000)
+ p2 = 10;
+ else
+ p2 = 5;
+ }
+ } else {
+ min_m1 = 18;
+ max_m1 = 26;
+ min_m2 = 6;
+ max_m2 = 16;
+ min_n = 3;
+ max_n = 16;
+ min_p1 = 2;
+ max_p1 = 18;
+ if (target < 165000)
+ p2 = 4;
+ else
+ p2 = 2;
+ }
+
+
+ for (m1 = min_m1; m1 <= max_m1; m1++) {
+ for (m2 = min_m2; m2 < max_m2; m2++) {
+ for (n = min_n; n <= max_n; n++) {
+ for (p1 = min_p1; p1 <= max_p1; p1++) {
+ int clock, this_err;
+
+ if (!i830PllIsValid(pScrn, outputs, refclk, m1, m2, n,
+ p1, p2)) {
+ continue;
+ }
+
+ clock = i830_clock(refclk, m1, m2, n, p1, p2);
+ this_err = abs(clock - target);
+ if (this_err < err) {
+ *outm1 = m1;
+ *outm2 = m2;
+ *outn = n;
+ *outp1 = p1;
+ *outp2 = p2;
+ err = this_err;
+ }
+ }
+ }
+ }
+ }
+
+ return (err != target);
+}
+
+static void
+i830WaitForVblank(ScrnInfoPtr pScreen)
+{
+ /* Wait for 20ms, i.e. one cycle at 50hz. */
+ usleep(20000);
+}
+
+void
+i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ unsigned long Start;
+
+ if (I830IsPrimary(pScrn))
+ Start = pI830->FrontBuffer.Start;
+ else {
+ I830Ptr pI8301 = I830PTR(pI830->entityPrivate->pScrn_1);
+ Start = pI8301->FrontBuffer2.Start;
+ }
+
+ if (pipe == 0)
+ OUTREG(DSPABASE, Start + ((y * pScrn->displayWidth + x) * pI830->cpp));
+ else
+ OUTREG(DSPBBASE, Start + ((y * pScrn->displayWidth + x) * pI830->cpp));
+}
+
+/**
+ * Sets the given video mode on the given pipe. Assumes that plane A feeds
+ * pipe A, and plane B feeds pipe B. Should not affect the other planes/pipes.
+ */
+static Bool
+i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ 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;
+ Bool ok, is_sdvo;
+ int refclk, pixel_clock;
+ int outputs;
+
+ ErrorF("Requested pix clock: %d\n", pMode->Clock);
+
+ if (pipe == 0)
+ outputs = pI830->operatingDevices & 0xff;
+ else
+ outputs = (pI830->operatingDevices >> 8) & 0xff;
+
+ if ((outputs & PIPE_LCD_ACTIVE) && (outputs & ~PIPE_LCD_ACTIVE)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Can't enable LVDS and non-LVDS on the same pipe\n");
+ return FALSE;
+ }
+ if (((outputs & PIPE_TV_ACTIVE) && (outputs & ~PIPE_TV_ACTIVE)) ||
+ ((outputs & PIPE_TV2_ACTIVE) && (outputs & ~PIPE_TV2_ACTIVE))) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Can't enable a TV and any other output on the same pipe\n");
+ return FALSE;
+ }
+ if (pipe == 0 && (outputs & PIPE_LCD_ACTIVE)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Can't support LVDS on pipe A\n");
+ return FALSE;
+ }
+ if ((outputs & PIPE_DFP_ACTIVE) || (outputs & PIPE_DFP2_ACTIVE)) {
+ /* We'll change how we control outputs soon, but to get the SDVO code up
+ * and running, just check for these two possibilities.
+ */
+ is_sdvo = TRUE;
+ } else {
+ is_sdvo = FALSE;
+ }
+
+ 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 (outputs & PIPE_LCD_ACTIVE && 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);
+ /* Hack until we get better clone-mode modesetting. If the mode to be
+ * programmed is larger than the size of the panel, only display the
+ * size of the panel.
+ */
+ if (pMode->HDisplay > pI830->panel_fixed_hactive ||
+ pMode->VDisplay > pI830->panel_fixed_vactive) {
+ dspsize = ((pI830->panel_fixed_vactive - 1) << 16) |
+ (pI830->panel_fixed_hactive - 1);
+ pipesrc = ((pI830->panel_fixed_hactive - 1) << 16) |
+ (pI830->panel_fixed_vactive - 1);
+ }
+ pixel_clock = pI830->panel_fixed_clock;
+ }
+
+ if (IS_I9XX(pI830)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+ ok = i830FindBestPLL(pScrn, outputs, pixel_clock, refclk, &m1, &m2, &n,
+ &p1, &p2);
+ if (!ok) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Couldn't find PLL settings for mode!\n");
+ return FALSE;
+ }
+
+ dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS;
+ if (IS_I9XX(pI830)) {
+ if (outputs & PIPE_LCD_ACTIVE)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ dpll |= (1 << (p1 - 1)) << 16;
+ switch (p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+ } else {
+ dpll |= (p1 - 2) << 16;
+ if (p2 == 4)
+ dpll |= PLL_P2_DIVIDE_BY_4;
+ }
+
+ if (outputs & (PIPE_TV_ACTIVE | PIPE_TV2_ACTIVE))
+ dpll |= PLL_REF_INPUT_TVCLKINBC;
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+ dpll |= SDVO_DEFAULT_MULTIPLIER;
+
+ if (is_sdvo) {
+ dpll |= DPLL_DVO_HIGH_SPEED;
+
+ ErrorF("DVOB: %08x\nDVOC: %08x\n", (int)INREG(SDVOB), (int)INREG(SDVOC));
+
+ sdvoc = INREG(SDVOC) & SDVO_PRESERVE_MASK;
+ sdvoc |= SDVO_ENABLE;
+ if (pipe == 1)
+ sdvoc |= SDVO_PIPE_B_SELECT;
+ // sdvoc |= SDVO_PHASE_SELECT_DEFAULT;
+ sdvoc |= SDVO_BORDER_ENABLE;
+ OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE);
+ }
+
+ 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);
+#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;
+ switch (pScrn->bitsPerPixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE;
+ break;
+ case 16:
+ if (pScrn->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ FatalError("unknown display bpp\n");
+ }
+
+ adpa = ADPA_DAC_ENABLE;
+ if (pMode->Flags & V_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (pMode->Flags & V_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ if (pipe == 0) {
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ adpa |= ADPA_PIPE_A_SELECT;
+ } else {
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+ adpa |= ADPA_PIPE_B_SELECT;
+ }
+
+ OUTREG(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Set up display timings and PLLs for the pipe. */
+ if (pipe == 0) {
+ /* First, disable display planes */
+ temp = INREG(DSPACNTR);
+ OUTREG(DSPACNTR, temp & ~DISPLAY_PLANE_ENABLE);
+
+ /* Wait for vblank for the disable to take effect */
+ i830WaitForVblank(pScrn);
+
+ /* Next, disable display pipes */
+ temp = INREG(PIPEACONF);
+ OUTREG(PIPEACONF, temp & ~PIPEACONF_ENABLE);
+
+ 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);
+ OUTREG(VTOTAL_A, vtot);
+ OUTREG(VBLANK_A, vblank);
+ OUTREG(VSYNC_A, vsync);
+ OUTREG(DSPASTRIDE, pScrn->displayWidth * pI830->cpp);
+ OUTREG(DSPASIZE, dspsize);
+ OUTREG(DSPAPOS, 0);
+ i830PipeSetBase(pScrn, pipe, pScrn->frameX0, pScrn->frameY0);
+ OUTREG(PIPEASRC, pipesrc);
+
+ /* Then, turn the pipe on first */
+ temp = INREG(PIPEACONF);
+ OUTREG(PIPEACONF, temp | PIPEACONF_ENABLE);
+
+ /* And then turn the plane on */
+ OUTREG(DSPACNTR, dspcntr);
+ } else {
+ /* Always make sure the LVDS is off before we play with DPLLs and pipe
+ * configuration.
+ */
+ i830SetLVDSPanelPower(pScrn, FALSE);
+
+ /* First, disable display planes */
+ temp = INREG(DSPBCNTR);
+ OUTREG(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE);
+
+ /* Wait for vblank for the disable to take effect */
+ i830WaitForVblank(pScrn);
+
+ /* Next, disable display pipes */
+ temp = INREG(PIPEBCONF);
+ OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE);
+
+ if (outputs & PIPE_LCD_ACTIVE) {
+ /* Disable the PLL before messing with LVDS enable */
+ OUTREG(FPB0, fp & ~DPLL_VCO_ENABLE);
+
+ /* LVDS must be powered on before PLL is enabled and before power
+ * sequencing the panel.
+ */
+ temp = INREG(LVDS);
+ OUTREG(LVDS, temp | LVDS_PORT_EN | LVDS_PIPEB_SELECT);
+ }
+
+ OUTREG(FPB0, fp);
+ OUTREG(DPLL_B, dpll);
+ OUTREG(HTOTAL_B, htot);
+ OUTREG(HBLANK_B, hblank);
+ OUTREG(HSYNC_B, hsync);
+ OUTREG(VTOTAL_B, vtot);
+ OUTREG(VBLANK_B, vblank);
+ OUTREG(VSYNC_B, vsync);
+ OUTREG(DSPBSTRIDE, pScrn->displayWidth * pI830->cpp);
+ OUTREG(DSPBSIZE, dspsize);
+ OUTREG(DSPBPOS, 0);
+ i830PipeSetBase(pScrn, pipe, pScrn->frameX0, pScrn->frameY0);
+ OUTREG(PIPEBSRC, pipesrc);
+
+ if (outputs & PIPE_LCD_ACTIVE) {
+ CARD32 pfit_control;
+
+ /* Enable automatic panel scaling so that non-native modes fill the
+ * screen.
+ */
+ /* XXX: Allow (auto-?) enabling of 8-to-6 dithering */
+ pfit_control = (PFIT_ENABLE |
+ VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+ VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+ if (pI830->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ OUTREG(PFIT_CONTROL, pfit_control);
+ }
+
+ /* Then, turn the pipe on first */
+ temp = INREG(PIPEBCONF);
+ OUTREG(PIPEBCONF, temp | PIPEBCONF_ENABLE);
+
+ /* And then turn the plane on */
+ OUTREG(DSPBCNTR, dspcntr);
+
+ if (outputs & PIPE_LCD_ACTIVE) {
+ i830SetLVDSPanelPower(pScrn, TRUE);
+ }
+ }
+
+ if (outputs & PIPE_CRT_ACTIVE)
+ OUTREG(ADPA, adpa);
+
+ return TRUE;
+}
+
+/**
+ * This function sets the given mode on the active pipes.
+ */
+Bool
+i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ Bool ok = TRUE;
+ CARD32 planeA, planeB;
+#ifdef XF86DRI
+ Bool didLock = FALSE;
+#endif
+ int i;
+
+ DPRINTF(PFX, "i830SetMode\n");
+
+#ifdef XF86DRI
+ didLock = I830DRILock(pScrn);
+#endif
+
+ if (pI830->operatingDevices & 0xff) {
+ pI830->planeEnabled[0] = 1;
+ } else {
+ pI830->planeEnabled[0] = 0;
+ }
+
+ if (pI830->operatingDevices & 0xff00) {
+ pI830->planeEnabled[1] = 1;
+ } else {
+ pI830->planeEnabled[1] = 0;
+ }
+
+ for (i = 0; i < pI830->num_outputs; i++) {
+ if (pI830->output[i].sdvo_drv)
+ I830SDVOPreSetMode(pI830->output[i].sdvo_drv, pMode);
+ }
+
+ if (pI830->planeEnabled[0]) {
+ ok = i830PipeSetMode(pScrn, pMode, 0);
+ if (!ok)
+ goto done;
+ }
+ if (pI830->planeEnabled[1]) {
+ ok = i830PipeSetMode(pScrn, pMode, 1);
+ if (!ok)
+ goto done;
+ }
+ for (i = 0; i < pI830->num_outputs; i++) {
+ if (pI830->output[i].sdvo_drv)
+ I830SDVOPostSetMode(pI830->output[i].sdvo_drv, pMode);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Mode bandwidth is %d Mpixel/s\n",
+ (int)(pMode->HDisplay * pMode->VDisplay *
+ pMode->VRefresh / 1000000));
+
+ planeA = INREG(DSPACNTR);
+ planeB = INREG(DSPBCNTR);
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Display plane A is now %s and connected to %s.\n",
+ pI830->planeEnabled[0] ? "enabled" : "disabled",
+ planeA & DISPPLANE_SEL_PIPE_MASK ? "Pipe B" : "Pipe A");
+ if (pI830->availablePipes == 2)
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Display plane B is now %s and connected to %s.\n",
+ pI830->planeEnabled[1] ? "enabled" : "disabled",
+ planeB & DISPPLANE_SEL_PIPE_MASK ? "Pipe B" : "Pipe A");
+
+done:
+#ifdef XF86DRI
+ if (didLock)
+ I830DRIUnlock(pScrn);
+#endif
+
+ return ok;
+}
+
+Bool
+i830DetectCRT(ScrnInfoPtr pScrn)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ CARD32 temp;
+
+ temp = INREG(PORT_HOTPLUG_EN);
+ OUTREG(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT);
+
+ /* Wait for the bit to clear to signal detection finished. */
+ while (INREG(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT)
+ ;
+
+ return ((INREG(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_INT_STATUS));
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+void
+i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ CARD32 pp_status, pp_control;
+
+ if (on) {
+ OUTREG(PP_STATUS, INREG(PP_STATUS) | PP_ON);
+ OUTREG(PP_CONTROL, INREG(PP_CONTROL) | POWER_TARGET_ON);
+ do {
+ pp_status = INREG(PP_STATUS);
+ pp_control = INREG(PP_CONTROL);
+ } while (!(pp_status & PP_ON) && !(pp_control & POWER_TARGET_ON));
+ } else {
+ OUTREG(PP_STATUS, INREG(PP_STATUS) & ~PP_ON);
+ OUTREG(PP_CONTROL, INREG(PP_CONTROL) & ~POWER_TARGET_ON);
+ do {
+ pp_status = INREG(PP_STATUS);
+ pp_control = INREG(PP_CONTROL);
+ } while ((pp_status & PP_ON) || (pp_control & POWER_TARGET_ON));
+ }
+}