xf86-video-intel: Branch 'modesetting' - 10 commits - src/i810_reg.h src/i830_crt.c src/i830_display.c src/i830_display.h src/i830_driver.c src/i830_dvo.c src/i830.h src/i830_lvds.c src/i830_modes.c src/i830_sdvo.c src/i830_sdvo.h src/i830_tv.c src/Makefile.am

Eric Anholt anholt at kemper.freedesktop.org
Tue Oct 24 01:15:43 EEST 2006


 src/Makefile.am    |    3 
 src/i810_reg.h     |  563 +++++++++++++++++++++++++++++++++++++++++++++++
 src/i830.h         |   70 +++++
 src/i830_crt.c     |  133 +++++++++++
 src/i830_display.c |  632 ++++++++++++++++++++---------------------------------
 src/i830_display.h |    4 
 src/i830_driver.c  |  267 ++++++----------------
 src/i830_dvo.c     |  136 +++++++++++
 src/i830_lvds.c    |  199 ++++++++++++++++
 src/i830_modes.c   |    8 
 src/i830_sdvo.c    |  333 +++++++++++++++++----------
 src/i830_sdvo.h    |    9 
 src/i830_tv.c      |  430 ++++++++++++++++++++++++++++++++++++
 13 files changed, 2071 insertions(+), 716 deletions(-)

New commits:
diff-tree eec5580cefffc293bf547372ab63b2fedaef4a83 (from parents)
Merge: 53c28b3980d2682de830e8f86553ccad71527ac1 a91c0cbab5de51885bfce7c7dce76f82c1b19553
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 23 14:52:55 2006 -0700

    Merge branch 'overhaul' into modesetting

diff-tree a91c0cbab5de51885bfce7c7dce76f82c1b19553 (from 8149681f2eac0af3b70a9457c5204e17da56142b)
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 23 14:30:38 2006 -0700

    Add work-in-progress integrated TV-out support.
    
    This is the TV connector on board for the 915GM and 945GM.
    
    It is currently not hooked up to output initialization as it's entirely
    untested.  However, I think this is a reasonable starting point for getting
    TV-out actually working.

diff --git a/src/Makefile.am b/src/Makefile.am
index cab6fe9..0fce5e4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -77,6 +77,7 @@ i810_drv_la_SOURCES = \
 	 i830_sdvo.c \
 	 i830_sdvo.h \
 	 i830_sdvo_regs.h \
+	 i830_tv.c \
 	 i830_xf86Modes.h \
 	 i830_xf86Modes.c \
 	 i915_3d.c \
diff --git a/src/i810_reg.h b/src/i810_reg.h
index 4fec65f..a80b66e 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -26,9 +26,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
 **************************************************************************/
 
+/** @file
+ * Register names and fields for Intel graphics.
+ */
+
 /*
  * Authors:
  *   Keith Whitwell <keith at tungstengraphics.com>
+ *   Eric Anholt <eric at anholt.net>
  *
  *   based on the i740 driver by
  *        Kevin E. Martin <kevin at precisioninsight.com> 
@@ -928,6 +933,564 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # define LVDS_CLKA_POWER_DOWN		(0 << 8)
 # define LVDS_CLKA_POWER_UP		(3 << 8)
 
+/** @defgroup TV_CTL
+ * @{
+ */
+#define TV_CTL			0x68000
+/** Enables the TV encoder */
+# define TV_ENC_ENABLE			(1 << 31)
+/** Sources the TV encoder input from pipe B instead of A. */
+# define TV_ENC_PIPEB_SELECT		(1 << 30)
+/** Outputs composite video (DAC A only) */
+# define TV_ENC_OUTPUT_COMPOSITE	(0 << 28)
+/** Outputs SVideo video (DAC B/C) */
+# define TV_ENC_OUTPUT_SVIDEO		(1 << 28)
+/** Outputs Component video (DAC A/B/C) */
+# define TV_ENC_OUTPUT_COMPONENT	(2 << 28)
+# define TV_TRILEVEL_SYNC		(1 << 21)
+/** Enables slow sync generation (945GM only) */
+# define TV_SLOW_SYNC			(1 << 20)
+/** Selects 4x oversampling for 480i and 576p */
+# define TV_OVERSAMPLE_4X		(0 << 18)
+/** Selects 2x oversampling for 720p and 1080i */
+# define TV_OVERSAMPLE_2X		(1 << 18)
+/** Selects no oversampling for 1080p */
+# define TV_OVERSAMPLE_NONE		(2 << 18)
+/** Selects 8x oversampling */
+# define TV_OVERSAMPLE_8X		(3 << 18)
+/** Selects progressive mode rather than interlaced */
+# define TV_PROGRESSIVE			(1 << 17)
+/** Sets the colorburst to PAL mode.  Required for non-M PAL modes. */
+# define TV_PAL_BURST			(1 << 16)
+/** Field for setting delay of Y compared to C */
+# define TV_YC_SKEW_MASK		(7 << 12)
+/** Enables a fix for 480p/576p standard definition modes on the 915GM only */
+# define TV_ENC_SDP_FIX			(1 << 11)
+/**
+ * Enables a fix for the 915GM only.
+ *
+ * Not sure what it does.
+ */
+# define TV_ENC_C0_FIX			(1 << 10)
+/** Bits that must be preserved by software */
+# define TV_CTL_SAVE			((3 << 8) | (3 << 6))
+# define TV_FUSE_STATE_MASK		(3 << 4)
+/** Read-only state that reports all features enabled */
+# define TV_FUSE_STATE_ENABLED		(0 << 4)
+/** Read-only state that reports that Macrovision is disabled in hardware*/
+# define TV_FUSE_STATE_NO_MACROVISION	(1 << 4)
+/** Read-only state that reports that TV-out is disabled in hardware. */
+# define TV_FUSE_STATE_DISABLED		(2 << 4)
+/**
+ * This test mode forces the DACs to 50% of full output.
+ *
+ * This is used for load detection in combination with TVDAC_SENSE_MASK
+ */
+# define TV_TEST_MODE_MONITOR_DETECT	(7 << 0)
+/** @} */
+
+/** @defgroup TV_DAC
+ * @{
+ */
+#define TV_DAC			0x68004
+/**
+ * Reports that DAC state change logic has reported change (RO).
+ *
+ * This gets cleared when TV_DAC_STATE_EN is cleared
+*/
+# define TVDAC_STATE_CHG		(1 << 31)
+# define TVDAC_SENSE_MASK		(7 << 28)
+/** Reports that DAC A voltage is above the detect threshold */
+# define TVDAC_A_SENSE			(1 << 30)
+/** Reports that DAC B voltage is above the detect threshold */
+# define TVDAC_B_SENSE			(1 << 29)
+/** Reports that DAC C voltage is above the detect threshold */
+# define TVDAC_C_SENSE			(1 << 28)
+/**
+ * Enables DAC state detection logic, for load-based TV detection.
+ *
+ * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set
+ * to off, for load detection to work.
+ */
+# define TVDAC_STATE_CHG_EN		(1 << 27)
+/** Sets the DAC A sense value to high */
+# define TVDAC_A_SENSE_CTL		(1 << 26)
+/** Sets the DAC B sense value to high */
+# define TVDAC_B_SENSE_CTL		(1 << 25)
+/** Sets the DAC C sense value to high */
+# define TVDAC_C_SENSE_CTL		(1 << 24)
+/** Overrides the ENC_ENABLE and DAC voltage levels */
+# define DAC_CTL_OVERRIDE		(1 << 7)
+/** Sets the slew rate.  Must be preserved in software */
+# define ENC_TVDAC_SLEW_FAST		(1 << 6)
+# define DAC_A_1_3_V			(0 << 4)
+# define DAC_A_1_1_V			(1 << 4)
+# define DAC_A_0_7_V			(2 << 4)
+# define DAC_A_OFF			(3 << 4)
+# define DAC_B_1_3_V			(0 << 2)
+# define DAC_B_1_1_V			(1 << 2)
+# define DAC_B_0_7_V			(2 << 2)
+# define DAC_B_OFF			(3 << 2)
+# define DAC_C_1_3_V			(0 << 0)
+# define DAC_C_1_1_V			(1 << 0)
+# define DAC_C_0_7_V			(2 << 0)
+# define DAC_C_OFF			(3 << 0)
+/** @} */
+
+/**
+ * CSC coefficients are stored in a floating point format with 9 bits of
+ * mantissa and 2 or 3 bits of exponent.  The exponent is represented as 2**-n,
+ * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with
+ * -1 (0x3) being the only legal negative value.
+ */
+#define TV_CSC_Y		0x68010
+# define TV_RY_MASK			0x07ff0000
+# define TV_RY_SHIFT			16
+# define TV_GY_MASK			0x00000fff
+# define TV_GY_SHIFT			0
+
+#define TV_CSC_Y2		0x68014
+# define TV_BY_MASK			0x07ff0000
+# define TV_BY_SHIFT			16
+/**
+ * Y attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AY_MASK			0x000003ff
+# define TV_AY_SHIFT			0
+
+#define TV_CSC_U		0x68018
+# define TV_RU_MASK			0x07ff0000
+# define TV_RU_SHIFT			16
+# define TV_GU_MASK			0x000007ff
+# define TV_GU_SHIFT			0
+
+#define TV_CSC_U2		0x6801c
+# define TV_BU_MASK			0x07ff0000
+# define TV_BU_SHIFT			16
+/**
+ * U attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AU_MASK			0x000003ff
+# define TV_AU_SHIFT			0
+
+#define TV_CSC_V		0x68020
+# define TV_RV_MASK			0x0fff0000
+# define TV_RV_SHIFT			16
+# define TV_GV_MASK			0x000007ff
+# define TV_GV_SHIFT			0
+
+#define TV_CSC_V2		0x68024
+# define TV_BV_MASK			0x07ff0000
+# define TV_BV_SHIFT			16
+/**
+ * V attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AV_MASK			0x000007ff
+# define TV_AV_SHIFT			0
+
+/** @defgroup TV_CSC_KNOBS
+ * @{
+ */
+#define TV_CLR_KNOBS		0x68028
+/** 2s-complement brightness adjustment */
+# define TV_BRIGHTNESS_MASK		0xff000000
+# define TV_BRIGHTNESS_SHIFT		24
+/** Contrast adjustment, as a 2.6 unsigned floating point number */
+# define TV_CONTRAST_MASK		0x00ff0000
+# define TV_CONTRAST_SHIFT		16
+/** Saturation adjustment, as a 2.6 unsigned floating point number */
+# define TV_SATURATION_MASK		0x0000ff00
+# define TV_SATURATION_SHIFT		8
+/** Hue adjustment, as an integer phase angle in degrees */
+# define TV_HUE_MASK			0x000000ff
+# define TV_HUE_SHIFT			0
+/** @} */
+
+/** @defgroup TV_CLR_LEVEL
+ * @{
+ */
+#define TV_CLR_LEVEL		0x6802c
+/** Controls the DAC level for black */
+# define TV_BLACK_LEVEL_MASK		0x01ff0000
+# define TV_BLACK_LEVEL_SHIFT		16
+/** Controls the DAC level for blanking */
+# define TV_BLANK_LEVEL_MASK		0x000001ff
+# define TV_BLANK_LEVEL_SHIFT		0
+/* @} */
+
+/** @defgroup TV_H_CTL_1
+ * @{
+ */
+#define TV_H_CTL_1		0x68030
+/** Number of pixels in the hsync. */
+# define TV_HSYNC_END_MASK		0x1fff0000
+# define TV_HSYNC_END_SHIFT		16
+/** Total number of pixels minus one in the line (display and blanking). */
+# define TV_HTOTAL_MASK			0x00001fff
+# define TV_HTOTAL_SHIFT		0
+/** @} */
+
+/** @defgroup TV_H_CTL_2
+ * @{
+ */
+#define TV_H_CTL_2		0x68034
+/** Enables the colorburst (needed for non-component color) */
+# define TV_BURST_ENA			(1 << 31)
+/** Offset of the colorburst from the start of hsync, in pixels minus one. */
+# define TV_HBURST_START_SHIFT		16
+# define TV_HBURST_START_MASK		0x1fff0000
+/** Length of the colorburst */
+# define TV_HBURST_LEN_SHIFT		0
+# define TV_HBURST_LEN_MASK		0x0001fff
+/** @} */
+
+/** @defgroup TV_H_CTL_3
+ * @{
+ */
+#define TV_H_CTL_3		0x68038
+/** End of hblank, measured in pixels minus one from start of hsync */
+# define TV_HBLANK_END_SHIFT		16
+# define TV_HBLANK_END_MASK		0x1fff0000
+/** Start of hblank, measured in pixels minus one from start of hsync */
+# define TV_HBLANK_START_SHIFT		0
+# define TV_HBLANK_START_MASK		0x0001fff
+/** @} */
+
+/** @defgroup TV_V_CTL_1
+ * @{
+ */
+#define TV_V_CTL_1		0x6803c
+/** XXX */
+# define TV_NBR_END_SHIFT		16
+# define TV_NBR_END_MASK		0x07ff0000
+/** XXX */
+# define TV_VI_END_F1_SHIFT		8
+# define TV_VI_END_F1_MASK		0x00003f00
+/** XXX */
+# define TV_VI_END_F2_SHIFT		0
+# define TV_VI_END_F2_MASK		0x0000003f
+/** @} */
+
+/** @defgroup TV_V_CTL_2
+ * @{
+ */
+#define TV_V_CTL_2		0x68040
+/** Length of vsync, in half lines */
+# define TV_VSYNC_LEN_MASK		0x07ff0000
+# define TV_VSYNC_LEN_SHIFT		16
+/** Offset of the start of vsync in field 1, measured in one less than the
+ * number of half lines.
+ */
+# define TV_VSYNC_START_F1_MASK		0x00007f00
+# define TV_VSYNC_START_F1_SHIFT	8
+/**
+ * Offset of the start of vsync in field 2, measured in one less than the
+ * number of half lines.
+ */
+# define TV_VSYNC_START_F2_MASK		0x0000007f
+# define TV_VSYNC_START_F2_SHIFT	0
+/** @} */
+
+/** @defgroup TV_V_CTL_3
+ * @{
+ */
+#define TV_V_CTL_3		0x68044
+/** Enables generation of the equalization signal */
+# define TV_EQUAL_ENA			(1 << 31)
+/** Length of vsync, in half lines */
+# define TV_VEQ_LEN_MASK		0x007f0000
+# define TV_VEQ_LEN_SHIFT		16
+/** Offset of the start of equalization in field 1, measured in one less than
+ * the number of half lines.
+ */
+# define TV_VEQ_START_F1_MASK		0x0007f00
+# define TV_VEQ_START_F1_SHIFT		8
+/**
+ * Offset of the start of equalization in field 2, measured in one less than
+ * the number of half lines.
+ */
+# define TV_VEQ_START_F2_MASK		0x000007f
+# define TV_VEQ_START_F2_SHIFT		0
+/** @} */
+
+/** @defgroup TV_V_CTL_4
+ * @{
+ */
+#define TV_V_CTL_4		0x68048
+/**
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F1_MASK	0x003f0000
+# define TV_VBURST_START_F1_SHIFT	16
+/**
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F1_MASK		0x000000ff
+# define TV_VBURST_END_F1_SHIFT		0
+/** @} */
+
+/** @defgroup TV_V_CTL_5
+ * @{
+ */
+#define TV_V_CTL_5		0x6804c
+/**
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F2_MASK	0x003f0000
+# define TV_VBURST_START_F2_SHIFT	16
+/**
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F2_MASK		0x000000ff
+# define TV_VBURST_END_F2_SHIFT		0
+/** @} */
+
+/** @defgroup TV_V_CTL_6
+ * @{
+ */
+#define TV_V_CTL_6		0x68050
+/**
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F3_MASK	0x003f0000
+# define TV_VBURST_START_F3_SHIFT	16
+/**
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F3_MASK		0x000000ff
+# define TV_VBURST_END_F3_SHIFT		0
+/** @} */
+
+/** @defgroup TV_V_CTL_7
+ * @{
+ */
+#define TV_V_CTL_7		0x68054
+/**
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F4_MASK	0x003f0000
+# define TV_VBURST_START_F4_SHIFT	16
+/**
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F4_MASK		0x000000ff
+# define TV_VBURST_END_F4_SHIFT		0
+/** @} */
+
+/** @defgroup TV_SC_CTL_1
+ * @{
+ */
+#define TV_SC_CTL_1		0x68060
+/** Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA1_EN			(1 << 31)
+/** Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA2_EN			(2 << 31)
+/** Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA3_EN			(3 << 31)
+/** Sets the subcarrier DDA to reset frequency every other field */
+# define TV_SC_RESET_EVERY_2		(0 << 24)
+/** Sets the subcarrier DDA to reset frequency every fourth field */
+# define TV_SC_RESET_EVERY_4		(1 << 24)
+/** Sets the subcarrier DDA to reset frequency every eighth field */
+# define TV_SC_RESET_EVERY_8		(2 << 24)
+/** Sets the subcarrier DDA to never reset the frequency */
+# define TV_SC_RESET_NEVER		(3 << 24)
+/** Sets the peak amplitude of the colorburst.*/
+# define TV_BURST_LEVEL_MASK		0x00ff0000
+# define TV_BURST_LEVEL_SHIFT		16
+/** Sets the increment of the first subcarrier phase generation DDA */
+# define TV_SCDDA1_INC_MASK		0x00000fff
+# define TV_SCDDA1_INC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_SC_CTL_2
+ * @{
+ */
+#define TV_SC_CTL_2		0x68068
+/** Sets the rollover for the second subcarrier phase generation DDA */
+# define TV_SCDDA2_SIZE_MASK		0x7fff0000
+# define TV_SCDDA2_SIZE_SHIFT		16
+/** Sets the increent of the second subcarrier phase generation DDA */
+# define TV_SCDDA2_INC_MASK		0x00007fff
+# define TV_SCDDA2_INC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_SC_CTL_3
+ * @{
+ */
+#define TV_SC_CTL_3		0x68068
+/** Sets the rollover for the third subcarrier phase generation DDA */
+# define TV_SCDDA3_SIZE_MASK		0x7fff0000
+# define TV_SCDDA3_SIZE_SHIFT		16
+/** Sets the increent of the third subcarrier phase generation DDA */
+# define TV_SCDDA3_INC_MASK		0x00007fff
+# define TV_SCDDA3_INC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_WIN_POS
+ * @{
+ */
+#define TV_WIN_POS		0x68070
+/** X coordinate of the display from the start of horizontal active */
+# define TV_XPOS_MASK			0x1fff0000
+# define TV_XPOS_SHIFT			16
+/** Y coordinate of the display from the start of vertical active (NBR) */
+# define TV_YPOS_MASK			0x00000fff
+# define TV_YPOS_SHIFT			0
+/** @} */
+
+/** @defgroup TV_WIN_SIZE
+ * @{
+ */
+#define TV_WIN_SIZE		0x68074
+/** Horizontal size of the display window, measured in pixels*/
+# define TV_XSIZE_MASK			0x1fff0000
+# define TV_XSIZE_SHIFT			16
+/**
+ * Vertical size of the display window, measured in pixels.
+ *
+ * Must be even for interlaced modes.
+ */
+# define TV_YSIZE_MASK			0x00000fff
+# define TV_YSIZE_SHIFT			0
+/** @} */
+
+/** @defgroup TV_FILTER_CTL_1
+ * @{
+ */
+#define TV_FILTER_CTL_1		0x68080
+/**
+ * Enables automatic scaling calculation.
+ *
+ * If set, the rest of the registers are ignored, and the calculated values can
+ * be read back from the register.
+ */
+# define TV_AUTO_SCALE			(1 << 31)
+/**
+ * Disables the vertical filter.
+ *
+ * This is required on modes more than 1024 pixels wide */
+# define TV_V_FILTER_BYPASS		(1 << 29)
+/** Enables adaptive vertical filtering */
+# define TV_VADAPT			(1 << 28)
+# define TV_VADAPT_MODE_MASK		(3 << 26)
+/** Selects the least adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_LEAST		(0 << 26)
+/** Selects the moderately adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_MODERATE	(1 << 26)
+/** Selects the most adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_MOST		(3 << 26)
+/**
+ * Sets the horizontal scaling factor.
+ *
+ * This should be the fractional part of the horizontal scaling factor divided
+ * by the oversampling rate.  TV_HSCALE should be less than 1, and set to:
+ *
+ * (src width - 1) / ((oversample * dest width) - 1)
+ */
+# define TV_HSCALE_FRAC_MASK		0x00003fff
+# define TV_HSCALE_FRAC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_FILTER_CTL_2
+ * @{
+ */
+#define TV_FILTER_CTL_2		0x68084
+/**
+ * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1)
+ */
+# define TV_VSCALE_INT_MASK		0x00038000
+# define TV_VSCALE_INT_SHIFT		15
+/**
+ * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * \sa TV_VSCALE_INT_MASK
+ */
+# define TV_VSCALE_FRAC_MASK		0x00007fff
+# define TV_VSCALE_FRAC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_FILTER_CTL_3
+ * @{
+ */
+#define TV_FILTER_CTL_3		0x68088
+/**
+ * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1))
+ *
+ * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
+ */
+# define TV_VSCALE_IP_INT_MASK		0x00038000
+# define TV_VSCALE_IP_INT_SHIFT		15
+/**
+ * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
+ *
+ * \sa TV_VSCALE_IP_INT_MASK
+ */
+# define TV_VSCALE_IP_FRAC_MASK		0x00007fff
+# define TV_VSCALE_IP_FRAC_SHIFT		0
+/** @} */
+
+/** @defgroup TV_CC_CONTROL
+ * @{
+ */
+#define TV_CC_CONTROL		0x68090
+# define TV_CC_ENABLE			(1 << 31)
+/**
+ * Specifies which field to send the CC data in.
+ *
+ * CC data is usually sent in field 0.
+ */
+# define TV_CC_FID_MASK			(1 << 27)
+# define TV_CC_FID_SHIFT		27
+/** Sets the horizontal position of the CC data.  Usually 135. */
+# define TV_CC_HOFF_MASK		0x03ff0000
+# define TV_CC_HOFF_SHIFT		16
+/** Sets the vertical position of the CC data.  Usually 21 */
+# define TV_CC_LINE_MASK		0x0000003f
+# define TV_CC_LINE_SHIFT		0
+/** @} */
+
+/** @defgroup TV_CC_DATA
+ * @{
+ */
+#define TV_CC_DATA		0x68094
+# define TV_CC_RDY			(1 << 31)
+/** Second word of CC data to be transmitted. */
+# define TV_CC_DATA_2_MASK		0x007f0000
+# define TV_CC_DATA_2_SHIFT		16
+/** First word of CC data to be transmitted. */
+# define TV_CC_DATA_1_MASK		0x0000007f
+# define TV_CC_DATA_1_SHIFT		0
+/** @}
+ */
+
+/** @{ */
+#define TV_H_LUMA_0		0x68100
+#define TV_H_LUMA_59		0x681ec
+#define TV_H_CHROMA_0		0x68200
+#define TV_H_CHROMA_59		0x682ec
+/** @} */
+
 #define PIPEACONF 0x70008
 #define PIPEACONF_ENABLE	(1<<31)
 #define PIPEACONF_DISABLE	0
diff --git a/src/i830.h b/src/i830.h
index 90fdcb8..832061c 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -279,6 +279,8 @@ struct _I830OutputRec {
    I2CBusPtr pDDCBus;
    struct _I830DVODriver *i2c_drv;
    I830SDVOPtr sdvo_drv;
+   /** Output-private structure.  Should replace i2c_drv and sdvo_drv */
+   void *dev_priv;
 };
 
 typedef struct _I830Rec {
@@ -693,6 +695,9 @@ Bool I830RandRSetConfig(ScreenPtr pScree
 Rotation I830GetRotation(ScreenPtr pScreen);
 void I830GetOriginalVirtualSize(ScrnInfoPtr pScrn, int *x, int *y);
 
+/* i830_tv.c */
+void i830_tv_init(ScrnInfoPtr pScrn);
+
 /*
  * 12288 is set as the maximum, chosen because it is enough for
  * 1920x1440 at 32bpp with a 2048 pixel line pitch with some to spare.
diff --git a/src/i830_tv.c b/src/i830_tv.c
new file mode 100644
index 0000000..2adbe91
--- /dev/null
+++ b/src/i830_tv.c
@@ -0,0 +1,430 @@
+/*
+ * 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 at anholt.net>
+ *
+ */
+
+/** @file
+ * Integrated TV-out support for the 915GM and 945GM.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "i830.h"
+#include "i830_display.h"
+
+enum tv_type {
+    TV_TYPE_UNKNOWN,
+    TV_TYPE_COMPOSITE,
+    TV_TYPE_SVIDEO,
+    TV_TYPE_COMPONENT
+};
+
+/** Private structure for the integrated TV support */
+struct i830_tv_priv {
+    CARD32 save_TV_H_CTL_1;
+    CARD32 save_TV_H_CTL_2;
+    CARD32 save_TV_H_CTL_3;
+    CARD32 save_TV_V_CTL_1;
+    CARD32 save_TV_V_CTL_2;
+    CARD32 save_TV_V_CTL_3;
+    CARD32 save_TV_V_CTL_4;
+    CARD32 save_TV_V_CTL_5;
+    CARD32 save_TV_V_CTL_6;
+    CARD32 save_TV_V_CTL_7;
+    CARD32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
+    CARD32 save_TV_DAC;
+    CARD32 save_TV_CTL;
+};
+
+enum burst_modes {
+    TV_SC_NTSC_MJ,
+    TV_SC_PAL,
+    TV_SC_PAL_NC,
+    TV_SC_PAL_M,
+    TV_SC_NTSC_443
+};
+
+const struct tv_sc_mode {
+    char *name;
+    int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
+    CARD32 sc_reset;
+    Bool pal_burst;
+} tv_sc_modes[] = {
+    [TV_SC_NTSC_MJ] = {
+	"NTSC M/J",
+	27456, 0, 135, 20800, 0,
+	TV_SC_RESET_EVERY_4,
+	FALSE
+    },
+    [TV_SC_PAL] = {
+	"PAL",
+	27648, 625, 168, 4122, 67,
+	TV_SC_RESET_EVERY_8,
+	TRUE
+    },
+    [TV_SC_PAL_NC] = {
+	"PAL Nc",
+	27648, 625, 135, 23578, 134,
+	TV_SC_RESET_EVERY_8,
+	TRUE
+    },
+    [TV_SC_PAL_M] = {
+	"PAL M",
+	27456, 0, 135, 16704, 0,
+	TV_SC_RESET_EVERY_8,
+	TRUE
+    },
+    [TV_SC_NTSC_443] = {
+	"NTSC-4.43",
+	27456, 525, 168, 4093, 310,
+	TV_SC_RESET_NEVER,
+	FALSE
+    },
+};
+
+/**
+ * Register programming values for TV modes.
+ *
+ * These values account for -1s required.
+ */
+const struct tv_mode {
+    char *name;
+    CARD32 oversample;
+    int hsync_end, hblank_end, hblank_start, htotal;
+    Bool progressive;
+    int vsync_start_f1, vsync_start_f2, vsync_len;
+    Bool veq_ena;
+    int veq_start_f1, veq_start_f2, veq_len;
+    int vi_end_f1, vi_end_f2, nbr_end;
+    Bool burst_ena;
+    int hburst_start, hburst_len;
+    int vburst_start_f1, vburst_end_f1;
+    int vburst_start_f2, vburst_end_f2;
+    int vburst_start_f3, vburst_end_f3;
+    int vburst_start_f4, vburst_end_f4;
+} tv_modes[] = {
+    {
+	"480i",
+	TV_OVERSAMPLE_8X,
+	64, 124, 836, 857,
+	FALSE,
+	6, 7, 6,
+	TRUE, 0, 1, 18,
+	20, 21, 240,
+	TRUE,
+	72, 34, 9, 240, 10, 240, 9, 240, 10, 240
+    }
+};
+
+
+static int
+i830_tv_detect_type(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    CARD32 save_tv_ctl, save_tv_dac;
+    CARD32 tv_ctl, tv_dac;
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    save_tv_ctl = INREG(TV_CTL);
+    save_tv_dac = INREG(TV_DAC);
+
+    /* First, we have to disable the encoder but source from the right pipe,
+     * which is already enabled.
+     */
+    tv_ctl = INREG(TV_CTL) & ~(TV_ENC_ENABLE | TV_ENC_PIPEB_SELECT);
+    if (output->pipe == 1)
+	tv_ctl |= TV_ENC_PIPEB_SELECT;
+    OUTREG(TV_CTL, tv_ctl);
+
+    /* Then set the voltage overrides. */
+    tv_dac = DAC_CTL_OVERRIDE | DAC_A_0_7_V | DAC_B_0_7_V | DAC_C_0_7_V;
+    OUTREG(TV_DAC, tv_dac);
+
+    /* Enable sensing of the load. */
+    tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+    OUTREG(TV_CTL, tv_ctl);
+
+    tv_dac |= TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL | TVDAC_B_SENSE_CTL |
+        TVDAC_C_SENSE_CTL;
+    OUTREG(TV_DAC, tv_dac);
+
+    /* Wait for things to take effect. */
+    i830WaitForVblank(pScrn);
+
+    tv_dac = INREG(TV_DAC);
+
+    OUTREG(TV_DAC, save_tv_dac);
+    OUTREG(TV_CTL, save_tv_ctl);
+
+    if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "Detected Composite TV connection\n");
+	return TV_TYPE_COMPOSITE;
+    } else if ((tv_dac & TVDAC_SENSE_MASK) == TVDAC_A_SENSE) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "Detected S-Video TV connection\n");
+	return TV_TYPE_SVIDEO;
+    } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "Detected Component TV connection\n");
+	return TV_TYPE_COMPONENT;
+    } else {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "Couldn't detect TV connection\n");
+	return TV_TYPE_UNKNOWN;
+    }
+}
+
+static void
+i830_tv_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    switch(mode) {
+    case DPMSModeOn:
+	OUTREG(TV_CTL, INREG(TV_CTL) | TV_ENC_ENABLE);
+	break;
+    case DPMSModeStandby:
+    case DPMSModeSuspend:
+    case DPMSModeOff:
+	OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE);
+	break;
+    }
+}
+
+static void
+i830_tv_save(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    struct i830_tv_priv *dev_priv = output->dev_priv;
+
+    dev_priv->save_TV_H_CTL_1 = INREG(TV_H_CTL_1);
+    dev_priv->save_TV_H_CTL_2 = INREG(TV_H_CTL_2);
+    dev_priv->save_TV_H_CTL_3 = INREG(TV_H_CTL_3);
+    dev_priv->save_TV_V_CTL_1 = INREG(TV_V_CTL_1);
+    dev_priv->save_TV_V_CTL_2 = INREG(TV_V_CTL_2);
+    dev_priv->save_TV_V_CTL_3 = INREG(TV_V_CTL_3);
+    dev_priv->save_TV_V_CTL_4 = INREG(TV_V_CTL_4);
+    dev_priv->save_TV_V_CTL_5 = INREG(TV_V_CTL_5);
+    dev_priv->save_TV_V_CTL_6 = INREG(TV_V_CTL_6);
+    dev_priv->save_TV_V_CTL_7 = INREG(TV_V_CTL_7);
+    dev_priv->save_TV_SC_CTL_1 = INREG(TV_SC_CTL_1);
+    dev_priv->save_TV_SC_CTL_2 = INREG(TV_SC_CTL_2);
+    dev_priv->save_TV_SC_CTL_3 = INREG(TV_SC_CTL_3);
+
+    dev_priv->save_TV_DAC = INREG(TV_DAC);
+    dev_priv->save_TV_CTL = INREG(TV_CTL);
+}
+
+static void
+i830_tv_restore(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    struct i830_tv_priv *dev_priv = output->dev_priv;
+
+    OUTREG(TV_H_CTL_1, dev_priv->save_TV_H_CTL_1);
+    OUTREG(TV_H_CTL_2, dev_priv->save_TV_H_CTL_2);
+    OUTREG(TV_H_CTL_3, dev_priv->save_TV_H_CTL_3);
+    OUTREG(TV_V_CTL_1, dev_priv->save_TV_V_CTL_1);
+    OUTREG(TV_V_CTL_2, dev_priv->save_TV_V_CTL_2);
+    OUTREG(TV_V_CTL_3, dev_priv->save_TV_V_CTL_3);
+    OUTREG(TV_V_CTL_4, dev_priv->save_TV_V_CTL_4);
+    OUTREG(TV_V_CTL_5, dev_priv->save_TV_V_CTL_5);
+    OUTREG(TV_V_CTL_6, dev_priv->save_TV_V_CTL_6);
+    OUTREG(TV_V_CTL_7, dev_priv->save_TV_V_CTL_7);
+    OUTREG(TV_SC_CTL_1, dev_priv->save_TV_SC_CTL_1);
+    OUTREG(TV_SC_CTL_2, dev_priv->save_TV_SC_CTL_2);
+    OUTREG(TV_SC_CTL_3, dev_priv->save_TV_SC_CTL_3);
+
+    OUTREG(TV_DAC, dev_priv->save_TV_DAC);
+    OUTREG(TV_CTL, dev_priv->save_TV_CTL);
+}
+
+static int
+i830_tv_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output,
+		   DisplayModePtr pMode)
+{
+    return MODE_OK;
+}
+
+static void
+i830_tv_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		     DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    /* Disable the encoder while we set up the pipe. */
+    OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE);
+}
+
+static void
+i830_tv_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		      DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    enum tv_type type;
+    const struct tv_mode *tv_mode;
+    const struct tv_sc_mode *sc_mode;
+    CARD32 tv_ctl, tv_filter_ctl;
+    CARD32 hctl1, hctl2, hctl3;
+    CARD32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+    CARD32 scctl1, scctl2, scctl3;
+
+    /* Need to actually choose or construct the appropriate
+     * mode.  For now, just set the first one in the list, with
+     * NTSC format.
+     */
+    tv_mode = &tv_modes[0];
+    sc_mode = &tv_sc_modes[TV_SC_NTSC_MJ];
+
+    type = i830_tv_detect_type(pScrn, output);
+    if (type == TV_TYPE_UNKNOWN) {
+	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Defaulting TV to SVIDEO\n");
+	type = TV_TYPE_SVIDEO;
+    }
+
+    hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+	(tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+    hctl2 = (tv_mode->hburst_start << 16) |
+	(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+    if (tv_mode->burst_ena)
+	hctl2 |= TV_BURST_ENA;
+
+    hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+	(tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+    vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+	(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+	(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+    vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+	(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+	(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+    vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+	(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+	(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+    if (tv_mode->veq_ena)
+	vctl3 |= TV_EQUAL_ENA;
+
+    vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+	(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+    vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+	(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+    vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+	(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+    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;
+    if (output->pipe == 1)
+	tv_ctl |= TV_ENC_PIPEB_SELECT;
+
+    switch (type) {
+    case TV_TYPE_COMPOSITE:
+	tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
+	break;
+    case TV_TYPE_COMPONENT:
+	tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
+	break;
+    default:
+    case TV_TYPE_SVIDEO:
+	tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
+	break;
+    }
+    tv_ctl |= tv_mode->oversample;
+    if (tv_mode->progressive)
+	tv_ctl |= TV_PROGRESSIVE;
+    if (sc_mode->pal_burst)
+	tv_ctl |= TV_PAL_BURST;
+
+    scctl1 = TV_SC_DDA1_EN | TV_SC_DDA1_EN;
+    if (sc_mode->dda3_size != 0)
+	scctl1 |= TV_SC_DDA3_EN;
+    scctl1 |= sc_mode->sc_reset;
+    /* XXX: set the burst level */
+    scctl1 |= sc_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
+
+    scctl2 = sc_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
+	sc_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
+
+    scctl3 = sc_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
+	sc_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
+
+    /* Enable two fixes for the chips that need them. */
+    if (pI830->PciInfo->chipType < PCI_CHIP_I945_G)
+	tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
+
+    tv_filter_ctl = TV_AUTO_SCALE;
+    if (pMode->HDisplay > 1024)
+	tv_ctl |= TV_V_FILTER_BYPASS;
+
+    OUTREG(TV_H_CTL_1, hctl1);
+    OUTREG(TV_H_CTL_2, hctl2);
+    OUTREG(TV_H_CTL_3, hctl3);
+    OUTREG(TV_V_CTL_1, vctl1);
+    OUTREG(TV_V_CTL_2, vctl2);
+    OUTREG(TV_V_CTL_3, vctl3);
+    OUTREG(TV_V_CTL_4, vctl4);
+    OUTREG(TV_V_CTL_5, vctl5);
+    OUTREG(TV_V_CTL_6, vctl6);
+    OUTREG(TV_V_CTL_7, vctl7);
+    OUTREG(TV_SC_CTL_1, scctl1);
+    OUTREG(TV_SC_CTL_2, scctl2);
+    OUTREG(TV_SC_CTL_3, scctl3);
+
+    OUTREG(TV_DAC, 0);
+    OUTREG(TV_CTL, tv_ctl);
+}
+
+void
+i830_tv_init(ScrnInfoPtr pScrn)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    if ((INREG(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+	return;
+
+    pI830->output[pI830->num_outputs].dev_priv =
+	malloc(sizeof(struct i830_tv_priv));
+    if (pI830->output[pI830->num_outputs].dev_priv == NULL)
+	return;
+
+    pI830->output[pI830->num_outputs].type = I830_OUTPUT_ANALOG;
+    pI830->output[pI830->num_outputs].dpms = i830_tv_dpms;
+    pI830->output[pI830->num_outputs].save = i830_tv_save;
+    pI830->output[pI830->num_outputs].restore = i830_tv_restore;
+    pI830->output[pI830->num_outputs].mode_valid = i830_tv_mode_valid;
+    pI830->output[pI830->num_outputs].pre_set_mode = i830_tv_pre_set_mode;
+    pI830->output[pI830->num_outputs].post_set_mode = i830_tv_post_set_mode;
+
+    pI830->num_outputs++;
+}
diff-tree 8149681f2eac0af3b70a9457c5204e17da56142b (from 1838671476875e9f5b3dde235eacf9fb43afb66c)
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 16 16:51:04 2006 -0700

    Add a per-output mode-valid method.
    
    This is currently disconnected, but will be used in more overhaul work.
    This should be where any output limitations, such as clocks, resolution,
    scaling limits, or other options, are validated.  Other limitations, such as
    chipset resolution limits, CRTC clock limits, etc. should be elsewhere.

diff --git a/src/i830.h b/src/i830.h
index c41cb56..90fdcb8 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -211,6 +211,9 @@ typedef struct _I830SDVODriver {
    CARD32 output_device;		/* SDVOB or SDVOC */
 
    i830_sdvo_caps caps;
+
+   CARD16 pixel_clock_min, pixel_clock_max;
+
    int save_sdvo_mult;
    Bool save_sdvo_active_1, save_sdvo_active_2;
    i830_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
@@ -245,6 +248,17 @@ struct _I830OutputRec {
    void (*restore)(ScrnInfoPtr pScrn, I830OutputPtr output);
 
    /**
+    * 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.
+    *
+    * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
+    */
+   int (*mode_valid)(ScrnInfoPtr pScrn, I830OutputPtr output,
+		     DisplayModePtr pMode);
+
+   /**
     * Callback for setting up a video mode before any pipe/dpll changes.
     *
     * \param pMode the mode that will be set, or NULL if the mode to be set is
diff --git a/src/i830_crt.c b/src/i830_crt.c
index 1cf1687..7721a0c 100644
--- a/src/i830_crt.c
+++ b/src/i830_crt.c
@@ -76,6 +76,13 @@ i830_crt_restore(ScrnInfoPtr pScrn, I830
     OUTREG(ADPA, pI830->saveADPA);
 }
 
+static int
+i830_crt_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output,
+		    DisplayModePtr pMode)
+{
+    return MODE_OK;
+}
+
 static void
 i830_crt_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
 		      DisplayModePtr pMode)
@@ -114,6 +121,7 @@ i830_crt_init(ScrnInfoPtr pScrn)
     pI830->output[pI830->num_outputs].dpms = i830_crt_dpms;
     pI830->output[pI830->num_outputs].save = i830_crt_save;
     pI830->output[pI830->num_outputs].restore = i830_crt_restore;
+    pI830->output[pI830->num_outputs].mode_valid = i830_crt_mode_valid;
     pI830->output[pI830->num_outputs].pre_set_mode = i830_crt_pre_set_mode;
     pI830->output[pI830->num_outputs].post_set_mode = i830_crt_post_set_mode;
 
diff --git a/src/i830_dvo.c b/src/i830_dvo.c
index ea74337..01858f2 100644
--- a/src/i830_dvo.c
+++ b/src/i830_dvo.c
@@ -92,15 +92,22 @@ i830_dvo_restore(ScrnInfoPtr pScrn, I830
     output->i2c_drv->vid_rec->RestoreRegs(output->i2c_drv->dev_priv);
 }
 
+static int
+i830_dvo_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output,
+		    DisplayModePtr pMode)
+{
+    if (output->i2c_drv->vid_rec->ModeValid(output->i2c_drv->dev_priv, pMode))
+	return MODE_OK;
+    else
+	return MODE_BAD;
+}
+
 static void
 i830_dvo_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
 		      DisplayModePtr pMode)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
-    if (output->i2c_drv == NULL)
-	return;
-
     output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv, pMode);
 
     OUTREG(DVOC, INREG(DVOC) & ~DVO_ENABLE);
@@ -179,8 +186,9 @@ i830_dvo_init(ScrnInfoPtr pScrn)
     pI830->output[i].dpms = i830_dvo_dpms;
     pI830->output[i].save = i830_dvo_save;
     pI830->output[i].restore = i830_dvo_restore;
-    pI830->output[i].pre_set_mode  = i830_dvo_pre_set_mode ;
-    pI830->output[i].post_set_mode  = i830_dvo_post_set_mode ;
+    pI830->output[i].mode_valid  = i830_dvo_mode_valid;
+    pI830->output[i].pre_set_mode  = i830_dvo_pre_set_mode;
+    pI830->output[i].post_set_mode  = i830_dvo_post_set_mode;
 
     /* Set up the I2C and DDC buses */
     ret = I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E");
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index 186d33b..399324f 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -123,6 +123,13 @@ i830_lvds_restore(ScrnInfoPtr pScrn, I83
 	i830SetLVDSPanelPower(pScrn, FALSE);
 }
 
+static int
+i830_lvds_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output,
+		    DisplayModePtr pMode)
+{
+   return MODE_OK;
+}
+
 static void
 i830_lvds_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
 		       DisplayModePtr pMode)
@@ -178,6 +185,7 @@ i830_lvds_init(ScrnInfoPtr pScrn)
     pI830->output[pI830->num_outputs].dpms = i830_lvds_dpms;
     pI830->output[pI830->num_outputs].save = i830_lvds_save;
     pI830->output[pI830->num_outputs].restore = i830_lvds_restore;
+    pI830->output[pI830->num_outputs].mode_valid = i830_lvds_mode_valid;
     pI830->output[pI830->num_outputs].pre_set_mode = i830_lvds_pre_set_mode;
     pI830->output[pI830->num_outputs].post_set_mode = i830_lvds_post_set_mode;
 
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index 9a1e155..76080df 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -545,7 +545,6 @@ i830_sdvo_pre_set_mode(ScrnInfoPtr pScrn
     CARD8 c16a[8];
     CARD8 c17a[8];
     CARD16 out_timings[6];
-    CARD16 clock_min, clock_max;
     Bool out1, out2;
     I830SDVOPtr s = output->sdvo_drv;
 
@@ -588,8 +587,6 @@ i830_sdvo_pre_set_mode(ScrnInfoPtr pScrn
     out_timings[5] = c17a[3] | ((short)c17a[2] << 8);
 
     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);
     
@@ -775,6 +772,21 @@ i830_sdvo_restore(ScrnInfoPtr pScrn, I83
 			     sdvo->save_sdvo_active_2);
 }
 
+static int
+i830_sdvo_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output,
+		     DisplayModePtr pMode)
+{
+    I830SDVOPtr sdvo = output->sdvo_drv;
+
+    if (sdvo->pixel_clock_min > pMode->Clock)
+	return MODE_CLOCK_HIGH;
+
+    if (sdvo->pixel_clock_max < pMode->Clock)
+	return MODE_CLOCK_LOW;
+
+    return MODE_OK;
+}
+
 static void
 I830SDVOGetCapabilities(I830SDVOPtr s, i830_sdvo_caps *caps)
 {
@@ -954,6 +966,7 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int ou
     pI830->output[pI830->num_outputs].dpms = i830_sdvo_dpms;
     pI830->output[pI830->num_outputs].save = i830_sdvo_save;
     pI830->output[pI830->num_outputs].restore = i830_sdvo_restore;
+    pI830->output[pI830->num_outputs].mode_valid = i830_sdvo_mode_valid;
     pI830->output[pI830->num_outputs].pre_set_mode = i830_sdvo_pre_set_mode;
     pI830->output[pI830->num_outputs].post_set_mode = i830_sdvo_post_set_mode;
 
@@ -1042,6 +1055,9 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int ou
 
     I830SDVOGetCapabilities(sdvo, &sdvo->caps);
 
+    I830SDVOGetInputPixelClockRange(sdvo, &sdvo->pixel_clock_min,
+				    &sdvo->pixel_clock_max);
+
     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 	       "SDVO device VID/DID: %02X:%02X.%02X, %02X,"
 	       "output 1: %c, output 2: %c\n",
diff-tree 1838671476875e9f5b3dde235eacf9fb43afb66c (from 2591c1fcf15608d96031be4760cf08534461ca34)
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Oct 10 12:41:01 2006 -0700

    Only disable the output when marked disabled.
    
    Also, remove a couple of dead variables.

diff --git a/src/i830_display.c b/src/i830_display.c
index 2693ded..9b20d65 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -627,16 +627,13 @@ void
 i830DisableUnusedFunctions(ScrnInfoPtr pScrn)
 {
     I830Ptr pI830 = I830PTR(pScrn);
-    int outputsA, outputsB;
     int i;
 
-    outputsA = pI830->operatingDevices & 0xff;
-    outputsB = (pI830->operatingDevices >> 8) & 0xff;
-
     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling unused functions\n");
 
     for (i = 0; i < pI830->num_outputs; i++) {
-	pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
+	if (pI830->output[i].disabled)
+	    pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
     }
 
     /* Now, any unused plane, pipe, and DPLL (FIXME: except for DVO, i915
diff-tree 2591c1fcf15608d96031be4760cf08534461ca34 (from 09e3d10b0ff69d180467fa9099d12da08e4f681b)
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 9 14:20:49 2006 -0700

    Fix LVDS restore path, and move pipe assignment for outputs to the right place.

diff --git a/src/i830_driver.c b/src/i830_driver.c
index 3a278fb..1f53915 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -1668,45 +1668,6 @@ I830PreInit(ScrnInfoPtr pScrn, int flags
 	 pI830->MonType1 |= PIPE_CRT;
       }
 
-      /* Perform the pipe assignment of outputs.  This code shouldn't exist,
-       * but for now we're supporting the existing MonitorLayout configuration
-       * scheme.
-       */
-      for (i = 0; i < pI830->num_outputs; i++) {
-	 pI830->output[i].disabled = FALSE;
-
-	 switch (pI830->output[i].type) {
-	 case I830_OUTPUT_LVDS:
-	    if (pI830->MonType1 & PIPE_LFP)
-	       pI830->output[i].pipe = 0;
-	    else if (pI830->MonType2 & PIPE_LFP)
-	       pI830->output[i].pipe = 1;
-	    else
-	       pI830->output[i].disabled = TRUE;
-	    break;
-	 case I830_OUTPUT_ANALOG:
-	    if (pI830->MonType1 & PIPE_CRT)
-	       pI830->output[i].pipe = 0;
-	    else if (pI830->MonType2 & PIPE_CRT)
-	       pI830->output[i].pipe = 1;
-	    else
-	       pI830->output[i].disabled = TRUE;
-	    break;
-	 case I830_OUTPUT_DVO:
-	 case I830_OUTPUT_SDVO:
-	    if (pI830->MonType1 & PIPE_DFP)
-	       pI830->output[i].pipe = 0;
-	    else if (pI830->MonType2 & PIPE_DFP)
-	       pI830->output[i].pipe = 1;
-	    else
-	       pI830->output[i].disabled = TRUE;
-	    break;
-	 default:
-	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unhandled output type\n");
-	    break;
-	 }
-      }
-
       /* Check for attached SDVO outputs.  Assume that they're flat panels for
        * now.  Though really, it's just a name at the moment, since we don't
        * treat different SDVO outputs differently.
@@ -1766,6 +1727,48 @@ I830PreInit(ScrnInfoPtr pScrn, int flags
       pI830->Clone = TRUE;
    }
 
+
+   /* Perform the pipe assignment of outputs.  This code shouldn't exist,
+    * but for now we're supporting the existing MonitorLayout configuration
+    * scheme.
+    */
+   for (i = 0; i < pI830->num_outputs; i++) {
+      pI830->output[i].disabled = FALSE;
+
+      switch (pI830->output[i].type) {
+      case I830_OUTPUT_LVDS:
+	 if (pI830->MonType1 & PIPE_LFP)
+	    pI830->output[i].pipe = 0;
+	 else if (pI830->MonType2 & PIPE_LFP)
+	    pI830->output[i].pipe = 1;
+	 else
+	    pI830->output[i].disabled = TRUE;
+	 break;
+      case I830_OUTPUT_ANALOG:
+	 if (pI830->MonType1 & PIPE_CRT)
+	    pI830->output[i].pipe = 0;
+	 else if (pI830->MonType2 & PIPE_CRT)
+	    pI830->output[i].pipe = 1;
+	 else
+	    pI830->output[i].disabled = TRUE;
+	 break;
+      case I830_OUTPUT_DVO:
+      case I830_OUTPUT_SDVO:
+	 if (pI830->MonType1 & PIPE_DFP)
+	    pI830->output[i].pipe = 0;
+	 else if (pI830->MonType2 & PIPE_DFP)
+	    pI830->output[i].pipe = 1;
+	 else
+	    pI830->output[i].disabled = TRUE;
+	 break;
+      default:
+	 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unhandled output type\n");
+	 break;
+      }
+   }
+
+   
+
    pI830->CloneRefresh = 60; /* default to 60Hz */
    if (xf86GetOptValInteger(pI830->Options, OPTION_CLONE_REFRESH,
 			    &(pI830->CloneRefresh))) {
@@ -2703,6 +2706,10 @@ RestoreHWState(ScrnInfoPtr pScrn)
       }
    }
 
+   for (i = 0; i < pI830->num_outputs; i++) {
+      pI830->output[i].restore(pScrn, &pI830->output[i]);
+   }
+
    if (IS_I965G(pI830)) {
       OUTREG(DSPASURF, pI830->saveDSPABASE);
       OUTREG(DSPBSURF, pI830->saveDSPBBASE);
@@ -2719,10 +2726,6 @@ RestoreHWState(ScrnInfoPtr pScrn)
    OUTREG(DSPACNTR, pI830->saveDSPACNTR);
    OUTREG(DSPBCNTR, pI830->saveDSPBCNTR);
 
-   for (i = 0; i < pI830->num_outputs; i++) {
-      pI830->output[i].restore(pScrn, &pI830->output[i]);
-   }
-
    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_lvds.c b/src/i830_lvds.c
index 23fc0a7..186d33b 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -117,6 +117,10 @@ i830_lvds_restore(ScrnInfoPtr pScrn, I83
     OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL);
     OUTREG(LVDS, pI830->saveLVDS);
     OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
+    if (pI830->savePP_CONTROL & POWER_TARGET_ON)
+	i830SetLVDSPanelPower(pScrn, TRUE);
+    else
+	i830SetLVDSPanelPower(pScrn, FALSE);
 }
 
 static void
@@ -159,6 +163,9 @@ i830_lvds_post_set_mode(ScrnInfoPtr pScr
      */
     OUTREG(LVDS, INREG(LVDS) | LVDS_PORT_EN | LVDS_PIPEB_SELECT);
 
+    /* Re-enable the PLL */
+    OUTREG(FPB0, INREG(FPB0) | DPLL_VCO_ENABLE);
+
     i830SetLVDSPanelPower(pScrn, TRUE);
 }
 
diff-tree 09e3d10b0ff69d180467fa9099d12da08e4f681b (from 317cc119c575650c1aa8bf992a0f42bdfffcd7ba)
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 9 13:09:18 2006 -0700

    Add a function for describing the output connection configuration.

diff --git a/src/i830_display.c b/src/i830_display.c
index 32ee6e2..2693ded 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -706,7 +706,6 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
 {
     I830Ptr pI830 = I830PTR(pScrn);
     Bool ok = TRUE;
-    CARD32 planeA, planeB;
 #ifdef XF86DRI
     Bool didLock = FALSE;
 #endif
@@ -777,18 +776,7 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
 
     i830DisableUnusedFunctions(pScrn);
 
-    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");
+    i830DescribeOutputConfiguration(pScrn);
 
 #ifdef XF86DRI
    I830DRISetVBlankInterrupt (pScrn, TRUE);
@@ -804,6 +792,52 @@ done:
     return ok;
 }
 
+void
+i830DescribeOutputConfiguration(ScrnInfoPtr pScrn)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    int i;
+
+    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output configuration:\n");
+
+    for (i = 0; i < pI830->availablePipes; i++) {
+	CARD32 dspcntr = INREG(DSPACNTR + (DSPBCNTR - DSPACNTR) * i);
+
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "  Display plane %c is now %s and connected to pipe %c.\n",
+		   'A' + i,
+		   pI830->planeEnabled[i] ? "enabled" : "disabled",
+		   dspcntr & DISPPLANE_SEL_PIPE_MASK ? 'B' : 'A');
+    }
+
+    for (i = 0; i < pI830->num_outputs; i++) {
+	const char *name = NULL;
+
+	switch (pI830->output[i].type) {
+	case I830_OUTPUT_ANALOG:
+	    name = "CRT";
+	    break;
+	case I830_OUTPUT_LVDS:
+	    name = "LVDS";
+	    break;
+	case I830_OUTPUT_SDVO:
+	    name = "SDVO";
+	    break;
+	case I830_OUTPUT_DVO:
+	    name = "DVO";
+	    break;
+	case I830_OUTPUT_TVOUT:
+	    name = "TV";
+	    break;
+	}
+
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "  Output %s is %sabled and connected to pipe %c\n",
+		   name, pI830->output[i].disabled ? "dis" : "en",
+		   pI830->output[i].pipe == 0 ? 'A' : 'B');
+    }
+}
+
 /**
  * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
  *
diff --git a/src/i830_display.h b/src/i830_display.h
index df8356a..9719406 100644
--- a/src/i830_display.h
+++ b/src/i830_display.h
@@ -33,6 +33,7 @@ Bool i830DetectCRT(ScrnInfoPtr pScrn, Bo
 void i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on);
 void i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y);
 void i830WaitForVblank(ScrnInfoPtr pScrn);
+void i830DescribeOutputConfiguration(ScrnInfoPtr pScrn);
 
 /* i830_sdvo.c */
 Bool I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode);
diff-tree 317cc119c575650c1aa8bf992a0f42bdfffcd7ba (from 9bb7736ab36f172db58703c4664bb1b0cd7f80c3)
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Oct 9 11:49:18 2006 -0700

    Move per-output mode setting code to per-output methods.
    
    This is not a very clean interface, as a number of outputs require tweaks to
    the DPLL registers.  When possible, the DPLLs are just adjusted in the
    per-output post_set_mode, which happens just after the DPLL is enabled.
    However, this seems better than the previous method of having all outputs
    programmed in the same function.

diff --git a/src/i830.h b/src/i830.h
index ae84846..c41cb56 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -222,12 +222,15 @@ extern const char *i830_output_type_name
 
 struct _I830OutputRec {
    int type;
-/*   int pipe;
-   int flags;*/
+   int pipe;
+   Bool disabled;
 
    /**
     * Turns the output on/off, or sets intermediate power levels if available.
-    * Unsupported intermediate modes drop to the lower power setting.
+    *
+    * Unsupported intermediate modes drop to the lower power setting.  If the
+    * mode is DPMSModeOff, the output must be disabled, as the DPLL may be
+    * disabled afterwards.
     */
    void (*dpms)(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
 
@@ -241,6 +244,22 @@ struct _I830OutputRec {
     */
    void (*restore)(ScrnInfoPtr pScrn, I830OutputPtr output);
 
+   /**
+    * Callback for setting up a video mode before any pipe/dpll changes.
+    *
+    * \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).
+    */
+   void (*pre_set_mode)(ScrnInfoPtr pScrn, I830OutputPtr output,
+			DisplayModePtr pMode);
+
+   /**
+    * Callback for setting up a video mode after the DPLL update but before
+    * the plane is enabled.
+    */
+   void (*post_set_mode)(ScrnInfoPtr pScrn, I830OutputPtr output,
+			 DisplayModePtr pMode);
+
    xf86MonPtr MonInfo;
    I2CBusPtr pI2CBus;
    I2CBusPtr pDDCBus;
diff --git a/src/i830_crt.c b/src/i830_crt.c
index 72df6bc..1cf1687 100644
--- a/src/i830_crt.c
+++ b/src/i830_crt.c
@@ -40,15 +40,17 @@ i830_crt_dpms(ScrnInfoPtr pScrn, I830Out
 
     temp = INREG(ADPA);
     temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+    temp &= ~ADPA_DAC_ENABLE;
 
     switch(mode) {
     case DPMSModeOn:
+	temp |= ADPA_DAC_ENABLE;
 	break;
     case DPMSModeStandby:
-	temp |= ADPA_HSYNC_CNTL_DISABLE;
+	temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
 	break;
     case DPMSModeSuspend:
-	temp |= ADPA_VSYNC_CNTL_DISABLE;
+	temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
 	break;
     case DPMSModeOff:
 	temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
@@ -74,6 +76,35 @@ i830_crt_restore(ScrnInfoPtr pScrn, I830
     OUTREG(ADPA, pI830->saveADPA);
 }
 
+static void
+i830_crt_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		      DisplayModePtr pMode)
+{
+}
+
+static void
+i830_crt_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		       DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    CARD32 adpa;
+
+    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 (output->pipe == 0)
+	adpa |= ADPA_PIPE_A_SELECT;
+    else
+	adpa |= ADPA_PIPE_B_SELECT;
+
+    OUTREG(ADPA, adpa);
+}
+
 void
 i830_crt_init(ScrnInfoPtr pScrn)
 {
@@ -83,6 +114,8 @@ i830_crt_init(ScrnInfoPtr pScrn)
     pI830->output[pI830->num_outputs].dpms = i830_crt_dpms;
     pI830->output[pI830->num_outputs].save = i830_crt_save;
     pI830->output[pI830->num_outputs].restore = i830_crt_restore;
+    pI830->output[pI830->num_outputs].pre_set_mode = i830_crt_pre_set_mode;
+    pI830->output[pI830->num_outputs].post_set_mode = i830_crt_post_set_mode;
 
     /* Set up the DDC bus. */
     I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus,
diff --git a/src/i830_display.c b/src/i830_display.c
index e3c3062..32ee6e2 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -226,7 +226,7 @@ i830FindBestPLL(ScrnInfoPtr pScrn, int o
     return (err != target);
 }
 
-static void
+void
 i830WaitForVblank(ScrnInfoPtr pScreen)
 {
     /* Wait for 20ms, i.e. one cycle at 50hz. */
@@ -260,6 +260,91 @@ i830PipeSetBase(ScrnInfoPtr pScrn, int p
 }
 
 /**
+ * In the current world order, there is a list of per-pipe modes, which may or
+ * may not include the mode that was asked to be set by XFree86's mode
+ * selection.  Find the closest one, in the following preference order:
+ *
+ * - Equality
+ * - Closer in size to the requested mode, but no larger
+ * - Closer in refresh rate to the requested mode.
+ */
+static DisplayModePtr
+i830PipeFindClosestMode(ScrnInfoPtr pScrn, int pipe, DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    DisplayModePtr pBest = NULL, pScan;
+
+    /* If the pipe doesn't have any detected modes, just let the system try to
+     * spam the desired mode in.
+     */
+    if (pI830->pipeMon[pipe] == NULL) {
+	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+		   "No pipe mode list for pipe %d,"
+		   "continuing with desired mode\n", pipe);
+	return pMode;
+    }
+
+    assert(pScan->VRefresh != 0.0);
+    for (pScan = pI830->pipeMon[pipe]->Modes; pScan != NULL;
+	 pScan = pScan->next) {
+	/* If there's an exact match, we're done. */
+	if (I830ModesEqual(pScan, pMode)) {
+	    pBest = pMode;
+	    break;
+	}
+
+	/* Reject if it's larger than the desired mode. */
+	if (pScan->HDisplay > pMode->HDisplay ||
+	    pScan->VDisplay > pMode->VDisplay)
+	{
+	    continue;
+	}
+
+	if (pBest == NULL) {
+	    pBest = pScan;
+	    continue;
+	}
+
+	/* Find if it's closer to the right size than the current best
+	 * option.
+	 */
+	if ((pScan->HDisplay > pBest->HDisplay &&
+	     pScan->VDisplay >= pBest->VDisplay) ||
+	    (pScan->HDisplay >= pBest->HDisplay &&
+	     pScan->VDisplay > pBest->VDisplay))
+	{
+	    pBest = pScan;
+	    continue;
+	}
+
+	/* Find if it's still closer to the right refresh than the current
+	 * best resolution.
+	 */
+	if (pScan->HDisplay == pBest->HDisplay &&
+	    pScan->VDisplay == pBest->VDisplay &&
+	    (fabs(pScan->VRefresh - pMode->VRefresh) <
+	     fabs(pBest->VRefresh - pMode->VRefresh))) {
+	    pBest = pScan;
+	}
+    }
+
+    if (pBest == NULL) {
+	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+		   "No suitable mode found to program for the pipe.\n"
+		   "	continuing with desired mode %dx%d@%.1f\n",
+		   pMode->HDisplay, pMode->VDisplay, pMode->VRefresh);
+    } else if (!I830ModesEqual(pBest, pMode)) {
+	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+		   "Choosing pipe %d's mode %dx%d@%.1f instead of xf86 "
+		   "mode %dx%d@%.1f\n", pipe,
+		   pBest->HDisplay, pBest->VDisplay, pBest->VRefresh,
+		   pMode->HDisplay, pMode->VDisplay, pMode->VRefresh);
+	pMode = pBest;
+    }
+    return pMode;
+}
+
+/**
  * 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.
  */
@@ -270,119 +355,76 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
     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;
-    CARD32 sdvob = 0, sdvoc = 0, dvo = 0;
-    Bool ok, is_sdvo, is_dvo;
-    int refclk, pixel_clock, sdvo_pixel_multiply;
-    int outputs;
-    DisplayModePtr pMasterMode = pMode;
-
-    assert(pMode->VRefresh != 0.0);
-    /* If we've got a list of modes probed for the device, find the best match
-     * in there to the requested mode.
-     */
-    if (pI830->pipeMon[pipe] != NULL) {
-	DisplayModePtr pBest = NULL, pScan;
+    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 outputs, 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 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;
+    int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+    int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+    int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+    int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+    int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+    int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+    int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
 
-	assert(pScan->VRefresh != 0.0);
-	for (pScan = pI830->pipeMon[pipe]->Modes; pScan != NULL;
-	     pScan = pScan->next)
-	{
-	    /* If there's an exact match, we're done. */
-	    if (I830ModesEqual(pScan, pMode)) {
-		pBest = pMode;
-		break;
-	    }
-
-	    /* Reject if it's larger than the desired mode. */
-	    if (pScan->HDisplay > pMode->HDisplay ||
-		pScan->VDisplay > pMode->VDisplay)
-	    {
-		continue;
-	    }
-
-	    if (pBest == NULL) {
-		pBest = pScan;
-	        continue;
-	    }
-	    /* Find if it's closer to the right size than the current best
-	     * option.
-	     */
-	    if ((pScan->HDisplay > pBest->HDisplay && 
-		pScan->VDisplay >= pBest->VDisplay) ||
-	        (pScan->HDisplay >= pBest->HDisplay && 
-		pScan->VDisplay > pBest->VDisplay))
-	    {
-		pBest = pScan;
-		continue;
-	    }
-	    /* Find if it's still closer to the right refresh than the current
-	     * best resolution.
-	     */
-	    if (pScan->HDisplay == pBest->HDisplay && 
-		pScan->VDisplay == pBest->VDisplay &&
-		(fabs(pScan->VRefresh - pMode->VRefresh) <
-		fabs(pBest->VRefresh - pMode->VRefresh)))
-	    {
-		pBest = pScan;
-	    }
-	}
-	if (pBest != NULL) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-		       "Choosing pipe %d's mode %dx%d@%.1f instead of xf86 "
-		       "mode %dx%d@%.1f\n", pipe,
-		       pBest->HDisplay, pBest->VDisplay, pBest->VRefresh,
-		       pMode->HDisplay, pMode->VDisplay, pMode->VRefresh);
-	    pMode = pBest;
-	}
-    }
     if (pipe == 0)
 	outputs = pI830->operatingDevices & 0xff;
     else
 	outputs = (pI830->operatingDevices >> 8) & 0xff;
 
-    if (outputs & PIPE_LCD_ACTIVE) {
-	if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMasterMode))
-	    return TRUE;
-    } else {
-	if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMode))
-	    return TRUE;
-    }
+    if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMode))
+	return TRUE;
 
     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Requested pix clock: %d\n",
 	       pMode->Clock);
 
-    if ((outputs & PIPE_LCD_ACTIVE) && (outputs & ~PIPE_LCD_ACTIVE)) {
+    for (i = 0; i < pI830->num_outputs; i++) {
+	if (pI830->output[i].pipe != pipe || pI830->output[i].disabled)
+	    continue;
+
+	switch (pI830->output[i].type) {
+	case I830_OUTPUT_LVDS:
+	    is_lvds = TRUE;
+	    break;
+	case I830_OUTPUT_SDVO:
+	    is_sdvo = TRUE;
+	    break;
+	case I830_OUTPUT_DVO:
+	    is_dvo = TRUE;
+	    break;
+	case I830_OUTPUT_TVOUT:
+	    is_tv = TRUE;
+	    break;
+	case I830_OUTPUT_ANALOG:
+	    is_crt = TRUE;
+	    break;
+	}
+    }
+
+    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");
 	return FALSE;
     }
-    if (((outputs & PIPE_TV_ACTIVE) && (outputs & ~PIPE_TV_ACTIVE)) ||
-	((outputs & PIPE_TV2_ACTIVE) && (outputs & ~PIPE_TV2_ACTIVE))) {
+    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");
+		   "Can't enable a TV and any other output on the same "
+		   "pipe\n");
 	return FALSE;
     }
-    if (pipe == 0 && (outputs & PIPE_LCD_ACTIVE)) {
+    if (pipe == 0 && is_lvds) {
 	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.
-	 */
-	if (IS_I9XX(pI830)) {
-	    is_sdvo = TRUE;
-	    is_dvo = FALSE;
-	} else {
-	    is_dvo = TRUE;
-	    is_sdvo = FALSE;
-	}
-    } else {
-	is_sdvo = FALSE;
-	is_dvo = FALSE;
-    }
 
     htot = (pMode->CrtcHDisplay - 1) | ((pMode->CrtcHTotal - 1) << 16);
     hblank = (pMode->CrtcHBlankStart - 1) | ((pMode->CrtcHBlankEnd - 1) << 16);
@@ -393,8 +435,8 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
     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)
-    {
+
+    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.
@@ -420,30 +462,24 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
 		  pI830->panel_fixed_vsyncwidth - 1) << 16);
 	pixel_clock = pI830->panel_fixed_clock;
 
-	if (pMasterMode->HDisplay <= pI830->panel_fixed_hactive &&
-	    pMasterMode->HDisplay <= pI830->panel_fixed_vactive)
+	if (pMode->HDisplay <= pI830->panel_fixed_hactive &&
+	    pMode->HDisplay <= pI830->panel_fixed_vactive)
 	{
-	    pipesrc = ((pMasterMode->HDisplay - 1) << 16) |
-		       (pMasterMode->VDisplay - 1);
-	    dspsize = ((pMasterMode->VDisplay - 1) << 16) |
-		       (pMasterMode->HDisplay - 1);
+	    pipesrc = ((pMode->HDisplay - 1) << 16) |
+		       (pMode->VDisplay - 1);
+	    dspsize = ((pMode->VDisplay - 1) << 16) |
+		       (pMode->HDisplay - 1);
 	}
     }
 
-    if (pMode->Clock >= 100000)
-	sdvo_pixel_multiply = 1;
-    else if (pMode->Clock >= 50000)
-	sdvo_pixel_multiply = 2;
-    else
-	sdvo_pixel_multiply = 4;
-
     /* In SDVO, we need to keep the clock on the bus between 1Ghz and 2Ghz.
      * The clock on the bus is 10 times the pixel clock normally.  If that
      * would be too low, we run the DPLL at a multiple of the pixel clock, and
-     * tell the SDVO device the multiplier so it can throw away the dummy bytes.
+     * tell the SDVO device the multiplier so it can throw away the dummy
+     * bytes.
      */
     if (is_sdvo) {
-	pixel_clock *= sdvo_pixel_multiply;
+	pixel_clock *= i830_sdvo_get_pixel_multiplier(pMode);
     }
 
     if (IS_I9XX(pI830)) {
@@ -461,7 +497,7 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
 
     dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS;
     if (IS_I9XX(pI830)) {
-	if (outputs & PIPE_LCD_ACTIVE)
+	if (is_lvds)
 	    dpll |= DPLLB_MODE_LVDS;
 	else
 	    dpll |= DPLLB_MODE_DAC_SERIAL;
@@ -486,55 +522,15 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
 	    dpll |= PLL_P2_DIVIDE_BY_4;
     }
 
-    if (outputs & (PIPE_TV_ACTIVE | PIPE_TV2_ACTIVE))
+    if (is_tv)
 	dpll |= PLL_REF_INPUT_TVCLKINBC;
 #if 0    
-    else if (outputs & (PIPE_LCD_ACTIVE))
+    else if (is_lvds)
 	dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
 #endif
     else	
 	dpll |= PLL_REF_INPUT_DREFCLK;
 
-    if (is_dvo) {
-	dpll |= DPLL_DVO_HIGH_SPEED;
-
-	/* 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)
-	    dvo |= DVO_HSYNC_ACTIVE_HIGH;
-	if (pMode->Flags & V_PVSYNC)
-	    dvo |= DVO_VSYNC_ACTIVE_HIGH;
-
-	OUTREG(DVOC, dvo & ~DVO_ENABLE);
-    }
-
-    if (is_sdvo) {
-	dpll |= DPLL_DVO_HIGH_SPEED;
-
-	ErrorF("DVOB: %08x\nDVOC: %08x\n", (int)INREG(SDVOB), (int)INREG(SDVOC));
-
-        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)
-	    sdvob |= SDVO_PIPE_B_SELECT;
-
-	if (IS_I945G(pI830) || IS_I945GM(pI830))
-	    dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-	else
-	    sdvob |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
-
-	OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE);
-	OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE);
-    }
-
     fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2);
 
 #if 1
@@ -576,150 +572,53 @@ i830PipeSetMode(ScrnInfoPtr pScrn, Displ
  	dspcntr |= DISPPLANE_GAMMA_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)
-	adpa |= ADPA_VSYNC_ACTIVE_HIGH;
-    
-    if (pipe == 0) {
+    if (pipe == 0)
 	dspcntr |= DISPPLANE_SEL_PIPE_A;
-	adpa |= ADPA_PIPE_A_SELECT;
-    } else {
+    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);
-
-	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, pI830->pipeX[pipe], pI830->pipeX[pipe]);
-	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);
+    /* Finally, set the mode. */
+    /* First, disable display planes */
+    temp = INREG(dspcntr_reg);
+    OUTREG(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+
+    /* 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);
 
-	/* 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, pI830->pipeX[pipe], pI830->pipeY[pipe]);
-	OUTREG(PIPEBSRC, pipesrc);
+    OUTREG(fp_reg, fp);
+    OUTREG(dpll_reg, dpll);
 
-	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);
-	}
+    for (i = 0; i < pI830->num_outputs; i++) {
+	if (pI830->output[i].pipe == pipe)
+	    pI830->output[i].post_set_mode(pScrn, &pI830->output[i], pMode);
     }
 
-    if (outputs & PIPE_CRT_ACTIVE)
-	OUTREG(ADPA, adpa);
-
-    if (is_dvo) {
-	/*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, dvo);*/
-	OUTREG(DVOC, dvo);
-    }
+    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(pScrn, pipe, pI830->pipeX[pipe], pI830->pipeX[pipe]);
+    OUTREG(pipesrc_reg, pipesrc);
+
+    /* Then, turn the pipe on first */
+    temp = INREG(pipeconf_reg);
+    OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE);
 
-    if (is_sdvo) {
-	OUTREG(SDVOB, sdvob);
-	OUTREG(SDVOC, sdvoc);
-    }
+    /* And then turn the plane on */
+    OUTREG(dspcntr_reg, dspcntr);
 
-    if (outputs & PIPE_LCD_ACTIVE) {
-	pI830->pipeCurMode[pipe] = *pMasterMode;
-    } else {
-	pI830->pipeCurMode[pipe] = *pMode;
-    }
+    pI830->pipeCurMode[pipe] = *pMode;
 
     return TRUE;
 }
@@ -729,47 +628,15 @@ i830DisableUnusedFunctions(ScrnInfoPtr p
 {
     I830Ptr pI830 = I830PTR(pScrn);
     int outputsA, outputsB;
+    int i;
 
     outputsA = pI830->operatingDevices & 0xff;
     outputsB = (pI830->operatingDevices >> 8) & 0xff;
 
     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling unused functions\n");
 
-    /* First, disable the unused outputs */
-    if ((outputsA & PIPE_CRT_ACTIVE) == 0 &&
-	(outputsB & PIPE_CRT_ACTIVE) == 0)
-    {
-	CARD32 adpa = INREG(ADPA);
-
-	if (adpa & ADPA_DAC_ENABLE) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling CRT output\n");
-	    OUTREG(ADPA, adpa & ~ADPA_DAC_ENABLE);
-	}
-    }
-
-    if ((outputsB & PIPE_LCD_ACTIVE) == 0) {
-	CARD32 pp_status = INREG(PP_STATUS);
-
-	if (pp_status & PP_ON) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling LVDS output\n");
-	    i830SetLVDSPanelPower(pScrn, FALSE);
-	}
-    }
-
-    if (IS_I9XX(pI830) && ((outputsA & PIPE_DFP_ACTIVE) == 0 &&
-	(outputsB & PIPE_DFP_ACTIVE) == 0))
-    {
-	CARD32 sdvob = INREG(SDVOB);
-	CARD32 sdvoc = INREG(SDVOC);
-
-	if (sdvob & SDVO_ENABLE) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling SDVOB output\n");
-	    OUTREG(SDVOB, sdvob & ~SDVO_ENABLE);
-	}
-	if (sdvoc & SDVO_ENABLE) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling SDVOC output\n");
-	    OUTREG(SDVOC, sdvoc & ~SDVO_ENABLE);
-	}
+    for (i = 0; i < pI830->num_outputs; i++) {
+	pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
     }
 
     /* Now, any unused plane, pipe, and DPLL (FIXME: except for DVO, i915
@@ -864,30 +731,21 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
     }
 
     for (i = 0; i < pI830->num_outputs; i++) {
-	I830OutputPtr output = &pI830->output[i];
-
-	if (pI830->output[i].type == I830_OUTPUT_SDVO)
-	    pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
-
-	if (pI830->output[i].type == I830_OUTPUT_DVO)
-	    output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv,
-					   pMode);
+	pI830->output[i].pre_set_mode(pScrn, &pI830->output[i], pMode);
     }
 
     if (pI830->planeEnabled[0]) {
-	ok = i830PipeSetMode(pScrn, pMode, 0);
+	ok = i830PipeSetMode(pScrn, i830PipeFindClosestMode(pScrn, 0, pMode),
+			     0);
 	if (!ok)
 	    goto done;
     }
     if (pI830->planeEnabled[1]) {
-	ok = i830PipeSetMode(pScrn, pMode, 1);
+	ok = i830PipeSetMode(pScrn, i830PipeFindClosestMode(pScrn, 1, pMode),
+			     1);
 	if (!ok)
 	    goto done;
     }
-    for (i = 0; i < pI830->num_outputs; i++) {
-	if (pI830->output[i].type == I830_OUTPUT_SDVO)
-	    I830SDVOPostSetMode(pI830->output[i].sdvo_drv, pMode);
-    }
 
     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Mode bandwidth is %d Mpixel/s\n",
 	       (int)(pMode->HDisplay * pMode->VDisplay *
@@ -1092,42 +950,3 @@ i830DetectCRT(ScrnInfoPtr pScrn, Bool al
 
     return FALSE;
 }
-
-/**
- * Sets the power state for the panel.
- */
-void
-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);
-	do {
-	    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 {
-	    pp_status = INREG(PP_STATUS);
-	    pp_control = INREG(PP_CONTROL);
-	} while ((pp_status & PP_ON) || (pp_control & POWER_TARGET_ON));
-    }
-}
diff --git a/src/i830_display.h b/src/i830_display.h
index 91450fe..df8356a 100644
--- a/src/i830_display.h
+++ b/src/i830_display.h
@@ -32,6 +32,7 @@ Bool i830SetMode(ScrnInfoPtr pScrn, Disp
 Bool i830DetectCRT(ScrnInfoPtr pScrn, Bool allow_disturb);
 void i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on);
 void i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y);
+void i830WaitForVblank(ScrnInfoPtr pScrn);
 
 /* i830_sdvo.c */
 Bool I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode);
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 4b55855..3a278fb 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -1528,7 +1528,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags
     * that we might find early in the list.  This hackery will go away when we
     * start doing independent per-head mode selection.
     */
-   for (i = MAX_OUTPUTS - 1; i >= 0; i--) {
+   for (i = pI830->num_outputs - 1; i >= 0; i--) {
      if (pI830->output[i].MonInfo) {
        pScrn->monitor->DDC = pI830->output[i].MonInfo;
        xf86SetDDCproperties(pScrn, pI830->output[i].MonInfo);
@@ -1668,11 +1668,50 @@ I830PreInit(ScrnInfoPtr pScrn, int flags
 	 pI830->MonType1 |= PIPE_CRT;
       }
 
+      /* Perform the pipe assignment of outputs.  This code shouldn't exist,
+       * but for now we're supporting the existing MonitorLayout configuration
+       * scheme.
+       */
+      for (i = 0; i < pI830->num_outputs; i++) {
+	 pI830->output[i].disabled = FALSE;
+
+	 switch (pI830->output[i].type) {
+	 case I830_OUTPUT_LVDS:
+	    if (pI830->MonType1 & PIPE_LFP)
+	       pI830->output[i].pipe = 0;
+	    else if (pI830->MonType2 & PIPE_LFP)
+	       pI830->output[i].pipe = 1;
+	    else
+	       pI830->output[i].disabled = TRUE;
+	    break;
+	 case I830_OUTPUT_ANALOG:
+	    if (pI830->MonType1 & PIPE_CRT)
+	       pI830->output[i].pipe = 0;
+	    else if (pI830->MonType2 & PIPE_CRT)
+	       pI830->output[i].pipe = 1;
+	    else
+	       pI830->output[i].disabled = TRUE;
+	    break;
+	 case I830_OUTPUT_DVO:
+	 case I830_OUTPUT_SDVO:
+	    if (pI830->MonType1 & PIPE_DFP)
+	       pI830->output[i].pipe = 0;
+	    else if (pI830->MonType2 & PIPE_DFP)
+	       pI830->output[i].pipe = 1;
+	    else
+	       pI830->output[i].disabled = TRUE;
+	    break;
+	 default:
+	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unhandled output type\n");
+	    break;
+	 }
+      }
+
       /* Check for attached SDVO outputs.  Assume that they're flat panels for
        * now.  Though really, it's just a name at the moment, since we don't
        * treat different SDVO outputs differently.
        */
-      for (i = 0; i < MAX_OUTPUTS; i++) {
+      for (i = 0; i < pI830->num_outputs; i++) {
 	 if (pI830->output[i].type == I830_OUTPUT_SDVO) {
 	    if (!I830DetectSDVODisplays(pScrn, i))
 	       continue;
@@ -2607,11 +2646,6 @@ RestoreHWState(ScrnInfoPtr pScrn)
    vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS);
    vgaHWLock(hwp);
 
-   /* Disable outputs */
-   for (i = 0; i < pI830->num_outputs; i++) {
-      pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
-   }
-
    /* First, disable display planes */
    temp = INREG(DSPACNTR);
    OUTREG(DSPACNTR, temp & ~DISPLAY_PLANE_ENABLE);
@@ -2624,6 +2658,11 @@ RestoreHWState(ScrnInfoPtr pScrn)
    temp = INREG(PIPEBCONF);
    OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE);
 
+   /* Disable outputs if necessary */
+   for (i = 0; i < pI830->num_outputs; i++) {
+      pI830->output[i].pre_set_mode(pScrn, &pI830->output[i], NULL);
+   }
+
    i830WaitForVblank(pScrn);
 
    OUTREG(FPA0, pI830->saveFPA0);
@@ -4333,7 +4372,7 @@ i830MonitorDetectDebugger(ScrnInfoPtr pS
    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Detected CRT as %s in %dms\n",
 	      found_crt ? "connected" : "disconnected", finish - start);
 
-   for (i = 0; i < MAX_OUTPUTS; i++) {
+   for (i = 0; i < pI830->num_outputs; i++) {
       Bool found_sdvo = TRUE;
 
       if (pI830->output[i].type != I830_OUTPUT_SDVO)
diff --git a/src/i830_dvo.c b/src/i830_dvo.c
index 27f1755..ea74337 100644
--- a/src/i830_dvo.c
+++ b/src/i830_dvo.c
@@ -22,7 +22,8 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN A
 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-**************************************************************************/
+******
+********************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -30,6 +31,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include "xf86.h"
 #include "i830.h"
+#include "i810_reg.h"
 
 #include "sil164/sil164.h"
 #include "ch7xxx/ch7xxx.h"
@@ -90,6 +92,51 @@ i830_dvo_restore(ScrnInfoPtr pScrn, I830
     output->i2c_drv->vid_rec->RestoreRegs(output->i2c_drv->dev_priv);
 }
 
+static void
+i830_dvo_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		      DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    if (output->i2c_drv == NULL)
+	return;
+
+    output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv, pMode);
+
+    OUTREG(DVOC, INREG(DVOC) & ~DVO_ENABLE);
+}
+
+static void
+i830_dvo_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		       DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    CARD32 dvo;
+    int dpll_reg = (output->pipe == 0) ? DPLL_A : DPLL_B;
+
+    /* 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 (output->pipe == 1)
+	dvo |= DVO_PIPE_B_SELECT;
+
+    if (pMode->Flags & V_PHSYNC)
+	dvo |= DVO_HSYNC_ACTIVE_HIGH;
+    if (pMode->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, dvo);*/
+    OUTREG(DVOC, dvo);
+}
+
 static Bool
 I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus,
 			    struct _I830DVODriver **retdrv)
@@ -132,6 +179,8 @@ i830_dvo_init(ScrnInfoPtr pScrn)
     pI830->output[i].dpms = i830_dvo_dpms;
     pI830->output[i].save = i830_dvo_save;
     pI830->output[i].restore = i830_dvo_restore;
+    pI830->output[i].pre_set_mode  = i830_dvo_pre_set_mode ;
+    pI830->output[i].post_set_mode  = i830_dvo_post_set_mode ;
 
     /* Set up the I2C and DDC buses */
     ret = I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E");
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index 3ce8cb6..23fc0a7 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -32,6 +32,45 @@
 #include "xf86.h"
 #include "i830.h"
 
+/**
+ * Sets the power state for the panel.
+ */
+static void
+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);
+	do {
+	    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 {
+	    pp_status = INREG(PP_STATUS);
+	    pp_control = INREG(PP_CONTROL);
+	} while ((pp_status & PP_ON) || (pp_control & POWER_TARGET_ON));
+    }
+}
+
 static void
 i830_lvds_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
@@ -80,6 +119,49 @@ i830_lvds_restore(ScrnInfoPtr pScrn, I83
     OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
 }
 
+static void
+i830_lvds_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+		       DisplayModePtr pMode)
+{
+    /* 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.
+     */
+    i830SetLVDSPanelPower(pScrn, FALSE);
+}
+
+static void
+i830_lvds_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+			DisplayModePtr pMode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    CARD32  pfit_control;
+
+    /* Enable automatic panel scaling so that non-native modes fill the
+     * screen.  Should be enabled before the pipe is enabled, according to
+     * register description.
+     */
+    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);
+
+    /* 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);
+
+    i830SetLVDSPanelPower(pScrn, TRUE);
+}
+
 void
 i830_lvds_init(ScrnInfoPtr pScrn)
 {
@@ -89,6 +171,8 @@ i830_lvds_init(ScrnInfoPtr pScrn)
     pI830->output[pI830->num_outputs].dpms = i830_lvds_dpms;
     pI830->output[pI830->num_outputs].save = i830_lvds_save;
     pI830->output[pI830->num_outputs].restore = i830_lvds_restore;
+    pI830->output[pI830->num_outputs].pre_set_mode = i830_lvds_pre_set_mode;
+    pI830->output[pI830->num_outputs].post_set_mode = i830_lvds_post_set_mode;
 
     /* Set up the LVDS DDC channel.  Most panels won't support it, but it can
      * be useful if available.
diff --git a/src/i830_modes.c b/src/i830_modes.c
index f7d4695..83c6051 100644
--- a/src/i830_modes.c
+++ b/src/i830_modes.c
@@ -750,7 +750,7 @@ I830ReprobePipeModeList(ScrnInfoPtr pScr
     else
 	outputs = (pI830->operatingDevices >> 8) & 0xff;
 
-    for (i = 0; i < MAX_OUTPUTS; i++) {
+    for (i = 0; i < pI830->num_outputs; i++) {
 	switch (pI830->output[i].type) {
 	case I830_OUTPUT_ANALOG:
 	    if (outputs & PIPE_CRT) {
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index dc17af0..9a1e155 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -34,6 +34,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "compiler.h"
 #include "i830.h"
 #include "i830_display.h"
+#include "i810_reg.h"
 #include "i830_sdvo_regs.h"
 
 CARD16 curr_table[6];
@@ -169,6 +170,17 @@ I830SDVOReadInputRegs(I830SDVOPtr s)
     ErrorF("\n");
 }
 
+int
+i830_sdvo_get_pixel_multiplier(DisplayModePtr pMode)
+{
+    if (pMode->Clock >= 100000)
+	return 1;
+    else if (pMode->Clock >= 50000)
+	return 2;
+    else
+	return 4;
+}
+
 /* Sets the control bus switch to either point at one of the DDC buses or the
  * PROM.  It resets from the DDC bus back to internal registers at the next I2C
  * STOP.  PROM access is terminated by accessing an internal register.
@@ -520,9 +532,11 @@ I830SDVOSetClockRateMult(I830SDVOPtr s, 
     return TRUE;
 }
 
-Bool
-I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode)
+static void
+i830_sdvo_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+			DisplayModePtr mode)
 {
+    I830Ptr pI830 = I830PTR(pScrn);
     CARD16 clock = mode->Clock/10, width = mode->CrtcHDisplay;
     CARD16 height = mode->CrtcVDisplay;
     CARD16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
@@ -533,6 +547,7 @@ I830SDVOPreSetMode(I830SDVOPtr s, Displa
     CARD16 out_timings[6];
     CARD16 clock_min, clock_max;
     Bool out1, out2;
+    I830SDVOPtr s = output->sdvo_drv;
 
     /* do some mode translations */
     h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart;
@@ -600,22 +615,34 @@ I830SDVOPreSetMode(I830SDVOPtr s, Displa
 				 out_timings[5]);
 
     I830SDVOSetTargetInput (s, FALSE, FALSE);
-    
-    if (clock >= 10000)
+
+    switch (i830_sdvo_get_pixel_multiplier(mode)) {
+    case 1:
 	I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_1X);
-    else if (clock >= 5000)
+	break;
+    case 2:
 	I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_2X);
-    else
+	break;
+    case 4:
 	I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_4X);
+	break;
+    }
 
-    return TRUE;
+    OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE);
+    OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE);
 }
 
-Bool
-I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode)
+static void
+i830_sdvo_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output,
+			DisplayModePtr mode)
 {
+    I830Ptr pI830 = I830PTR(pScrn);
     Bool ret = TRUE;
     Bool out1, out2;
+    CARD32 dpll, sdvob, sdvoc;
+    int dpll_reg = (output->pipe == 0) ? DPLL_A : DPLL_B;
+    int sdvo_pixel_multiply;
+    I830SDVOPtr s = output->sdvo_drv;
 
     /* the BIOS writes out 6 commands post mode set */
     /* two 03s, 04 05, 10, 1d */
@@ -636,18 +663,41 @@ I830SDVOPostSetMode(I830SDVOPtr s, Displ
     I830SDVOSetActiveOutputs(s, TRUE, FALSE);
     I830SDVOSetTargetInput (s, FALSE, FALSE);
 
-    return ret;
+    /* Set the SDVO control regs. */
+    sdvob = INREG(SDVOB) & SDVOB_PRESERVE_MASK;
+    sdvoc = INREG(SDVOC) & SDVOC_PRESERVE_MASK;
+    sdvob |= SDVO_ENABLE | (9 << 19) | SDVO_BORDER_ENABLE;
+    sdvoc |= 9 << 19;
+    if (output->pipe == 1)
+	sdvob |= SDVO_PIPE_B_SELECT;
+
+    dpll = INREG(dpll_reg);
+
+    sdvo_pixel_multiply = i830_sdvo_get_pixel_multiplier(mode);
+    if (IS_I945G(pI830) || IS_I945GM(pI830))
+	dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+    else
+	sdvob |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+
+    OUTREG(dpll_reg, dpll | DPLL_DVO_HIGH_SPEED);
+
+    OUTREG(SDVOB, sdvob);
+    OUTREG(SDVOC, sdvoc);
 }
 
 static void
 i830_sdvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
+    I830Ptr pI830 = I830PTR(pScrn);
     I830SDVOPtr sdvo = output->sdvo_drv;
 
-    if (mode != DPMSModeOn)
+    if (mode != DPMSModeOn) {
 	I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
-    else
+	OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE);
+    } else {
 	I830SDVOSetActiveOutputs(sdvo, TRUE, FALSE);
+	OUTREG(SDVOB, INREG(SDVOB) | SDVO_ENABLE);
+    }
 }
 
 static void
@@ -857,13 +907,11 @@ 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);
+    for (i = 0; i < pI830->num_outputs; i++) {
+	if (pI830->output[i].type == I830_OUTPUT_SDVO)
+	    I830DumpOneSDVO (pI830->output[i].sdvo_drv);
     }
 }
 
@@ -906,6 +954,8 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int ou
     pI830->output[pI830->num_outputs].dpms = i830_sdvo_dpms;
     pI830->output[pI830->num_outputs].save = i830_sdvo_save;
     pI830->output[pI830->num_outputs].restore = i830_sdvo_restore;
+    pI830->output[pI830->num_outputs].pre_set_mode = i830_sdvo_pre_set_mode;
+    pI830->output[pI830->num_outputs].post_set_mode = i830_sdvo_post_set_mode;
 
     /* Find an existing SDVO I2CBus from another output, or allocate it. */
     for (i = 0; i < pI830->num_outputs; i++) {
@@ -974,7 +1024,7 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int ou
 	return;
     }
 
-    pI830->output[pI830->num_outputs].pI2CBus = ddcbus;
+    pI830->output[pI830->num_outputs].pI2CBus = i2cbus;
     pI830->output[pI830->num_outputs].pDDCBus = ddcbus;
     pI830->output[pI830->num_outputs].sdvo_drv = sdvo;
 
diff --git a/src/i830_sdvo.h b/src/i830_sdvo.h
index 9a3586f..44bbfe4 100644
--- a/src/i830_sdvo.h
+++ b/src/i830_sdvo.h
@@ -58,6 +58,9 @@ typedef struct _i830_sdvo_dtd {
 void
 i830_sdvo_init(ScrnInfoPtr pScrn, int output_device);
 
+int
+i830_sdvo_get_pixel_multiplier(DisplayModePtr pMode);
+
 Bool
 I830DetectSDVODisplays(ScrnInfoPtr pScrn, int output_index);
 
diff-tree 9bb7736ab36f172db58703c4664bb1b0cd7f80c3 (from ada8f62da263d1e93e22df4e0b1149bf1dbe24d4)
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Oct 5 16:02:02 2006 -0700

    Remove checks for output privates in various bits of code.
    
    Now, the output is only set up if it fully initializes, so it will never exist
    if the private it requires doesn't.

diff --git a/src/i830_display.c b/src/i830_display.c
index 5a0fb9d..e3c3062 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -866,10 +866,10 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
     for (i = 0; i < pI830->num_outputs; i++) {
 	I830OutputPtr output = &pI830->output[i];
 
-	if (pI830->output[i].sdvo_drv != NULL)
+	if (pI830->output[i].type == I830_OUTPUT_SDVO)
 	    pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
 
-	if (output->i2c_drv != NULL)
+	if (pI830->output[i].type == I830_OUTPUT_DVO)
 	    output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv,
 					   pMode);
     }
@@ -885,7 +885,7 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
 	    goto done;
     }
     for (i = 0; i < pI830->num_outputs; i++) {
-	if (pI830->output[i].sdvo_drv)
+	if (pI830->output[i].type == I830_OUTPUT_SDVO)
 	    I830SDVOPostSetMode(pI830->output[i].sdvo_drv, pMode);
     }
 
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 5da31a6..4b55855 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -985,14 +985,12 @@ I830DetectMonitors(ScrnInfoPtr pScrn)
 	 xf86PrintEDID(pI830->output[i].MonInfo);
       break;
       case I830_OUTPUT_SDVO:
-	 if (pI830->output[i].sdvo_drv != NULL) {
-	    pI830->output[i].MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex,
-						       pI830->output[i].pDDCBus);
-
-	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DDC SDVO %d, %08lX\n", i,
-		       pI830->output[i].pDDCBus->DriverPrivate.uval);
-	    xf86PrintEDID(pI830->output[i].MonInfo);
-	 }
+	 pI830->output[i].MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex,
+						    pI830->output[i].pDDCBus);
+
+	 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DDC SDVO %d, %08lX\n", i,
+		    pI830->output[i].pDDCBus->DriverPrivate.uval);
+	 xf86PrintEDID(pI830->output[i].MonInfo);
 	 break;
       case I830_OUTPUT_UNUSED:
 	 break;
@@ -1675,8 +1673,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags
        * treat different SDVO outputs differently.
        */
       for (i = 0; i < MAX_OUTPUTS; i++) {
-	 if (pI830->output[i].type == I830_OUTPUT_SDVO &&
-	     pI830->output[i].sdvo_drv != NULL) {
+	 if (pI830->output[i].type == I830_OUTPUT_SDVO) {
 	    if (!I830DetectSDVODisplays(pScrn, i))
 	       continue;
 
@@ -4339,11 +4336,8 @@ i830MonitorDetectDebugger(ScrnInfoPtr pS
    for (i = 0; i < MAX_OUTPUTS; i++) {
       Bool found_sdvo = TRUE;
 
-      if (pI830->output[i].type != I830_OUTPUT_SDVO ||
-	  pI830->output[i].sdvo_drv == NULL)
-      {
+      if (pI830->output[i].type != I830_OUTPUT_SDVO)
 	 continue;
-      }
       start = GetTimeInMillis();
       found_sdvo = I830DetectSDVODisplays(pScrn, i);   
       finish = GetTimeInMillis();
diff --git a/src/i830_dvo.c b/src/i830_dvo.c
index 86d7d73..27f1755 100644
--- a/src/i830_dvo.c
+++ b/src/i830_dvo.c
@@ -57,9 +57,6 @@ struct _I830DVODriver i830_dvo_drivers[]
 static void
 i830_dvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
-    if (output->i2c_drv == NULL)
-	return;
-
     if (mode == DPMSModeOn)
 	output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, TRUE);
     else
@@ -71,9 +68,6 @@ i830_dvo_save(ScrnInfoPtr pScrn, I830Out
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
-    if (output->i2c_drv == NULL)
-	return;
-
     /* Each output should probably just save the registers it touches, but for
      * now, use more overkill.
      */
@@ -89,9 +83,6 @@ i830_dvo_restore(ScrnInfoPtr pScrn, I830
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
-    if (output->i2c_drv == NULL)
-	return;
-
     OUTREG(DVOA, pI830->saveDVOA);
     OUTREG(DVOB, pI830->saveDVOB);
     OUTREG(DVOC, pI830->saveDVOC);
@@ -99,7 +90,7 @@ i830_dvo_restore(ScrnInfoPtr pScrn, I830
     output->i2c_drv->vid_rec->RestoreRegs(output->i2c_drv->dev_priv);
 }
 
-Bool
+static Bool
 I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus,
 			    struct _I830DVODriver **retdrv)
 {
diff --git a/src/i830_modes.c b/src/i830_modes.c
index da1ccde..f7d4695 100644
--- a/src/i830_modes.c
+++ b/src/i830_modes.c
@@ -763,14 +763,12 @@ I830ReprobePipeModeList(ScrnInfoPtr pScr
 	    }
 	    break;
 	case I830_OUTPUT_DVO:
-	    if (outputs & PIPE_DFP && pI830->output[i].i2c_drv != NULL) {
+	    if (outputs & PIPE_DFP) {
 		output_index = i;
 	    }
 	    break;
 	case I830_OUTPUT_SDVO:
-	    if (outputs & PIPE_DFP &&
-		pI830->output[i].sdvo_drv != NULL)
-	    {
+	    if (outputs & PIPE_DFP) {
 		output_index = i;
 	    }
 	    break;
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index 9792904..dc17af0 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -644,9 +644,6 @@ i830_sdvo_dpms(ScrnInfoPtr pScrn, I830Ou
 {
     I830SDVOPtr sdvo = output->sdvo_drv;
 
-    if (sdvo == NULL)
-	return;
-
     if (mode != DPMSModeOn)
 	I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
     else
@@ -659,9 +656,6 @@ i830_sdvo_save(ScrnInfoPtr pScrn, I830Ou
     I830Ptr pI830 = I830PTR(pScrn);
     I830SDVOPtr sdvo = output->sdvo_drv;
 
-    if (sdvo == NULL)
-	return;
-
     sdvo->save_sdvo_mult = I830SDVOGetClockRateMult(sdvo);
     I830SDVOGetActiveOutputs(sdvo, &sdvo->save_sdvo_active_1,
 			     &sdvo->save_sdvo_active_2);
@@ -699,9 +693,6 @@ i830_sdvo_restore(ScrnInfoPtr pScrn, I83
     I830Ptr pI830 = I830PTR(pScrn);
     I830SDVOPtr sdvo = output->sdvo_drv;
 
-    if (sdvo == NULL)
-	return;
-
     if (sdvo->caps.caps & 0x1) {
        I830SDVOSetTargetInput(sdvo, FALSE, FALSE);
        I830SDVOSetTimings(sdvo, &sdvo->save_input_dtd_1,
diff-tree ada8f62da263d1e93e22df4e0b1149bf1dbe24d4 (from 103b4edce7859ddf58f3e1fadeb427a5e85c7acd)
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Oct 5 15:55:07 2006 -0700

    Give each output type an init method in its file, making other methods static.

diff --git a/src/i830.h b/src/i830.h
index f92704b..ae84846 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -633,21 +633,13 @@ extern Bool I830I2CInit(ScrnInfoPtr pScr
 			char *name);
 
 /* i830_crt.c */
-void I830CRTDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
-void I830CRTSave(ScrnInfoPtr pScrn, I830OutputPtr output);
-void I830CRTRestore(ScrnInfoPtr pScrn, I830OutputPtr output);
+void i830_crt_init(ScrnInfoPtr pScrn);
 
 /* i830_dvo.c */
-void I830DVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
-void I830DVOSave(ScrnInfoPtr pScrn, I830OutputPtr output);
-void I830DVORestore(ScrnInfoPtr pScrn, I830OutputPtr output);
-Bool I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus,
-				 struct _I830DVODriver **retdrv);
+void i830_dvo_init(ScrnInfoPtr pScrn);
 
 /* i830_lvds.c */
-void I830LVDSDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
-void I830LVDSSave(ScrnInfoPtr pScrn, I830OutputPtr output);
-void I830LVDSRestore(ScrnInfoPtr pScrn, I830OutputPtr output);
+void i830_lvds_init(ScrnInfoPtr pScrn);
 
 /* i830_memory.c */
 Bool I830BindAGPMemory(ScrnInfoPtr pScrn);
diff --git a/src/i830_crt.c b/src/i830_crt.c
index 1a0dd0b..72df6bc 100644
--- a/src/i830_crt.c
+++ b/src/i830_crt.c
@@ -32,8 +32,8 @@
 #include "xf86.h"
 #include "i830.h"
 
-void
-I830CRTDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+static void
+i830_crt_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
     I830Ptr pI830 = I830PTR(pScrn);
     CARD32 temp;
@@ -58,18 +58,35 @@ I830CRTDPMS(ScrnInfoPtr pScrn, I830Outpu
     OUTREG(ADPA, temp);
 }
 
-void
-I830CRTSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_crt_save(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
     pI830->saveADPA = INREG(ADPA);
 }
 
-void
-I830CRTRestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_crt_restore(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
     OUTREG(ADPA, pI830->saveADPA);
 }
+
+void
+i830_crt_init(ScrnInfoPtr pScrn)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    pI830->output[pI830->num_outputs].type = I830_OUTPUT_ANALOG;
+    pI830->output[pI830->num_outputs].dpms = i830_crt_dpms;
+    pI830->output[pI830->num_outputs].save = i830_crt_save;
+    pI830->output[pI830->num_outputs].restore = i830_crt_restore;
+
+    /* Set up the DDC bus. */
+    I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus,
+		GPIOA, "CRTDDC_A");
+
+    pI830->num_outputs++;
+}
diff --git a/src/i830_display.h b/src/i830_display.h
index 2b808ad..91450fe 100644
--- a/src/i830_display.h
+++ b/src/i830_display.h
@@ -34,7 +34,5 @@ void i830SetLVDSPanelPower(ScrnInfoPtr p
 void i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y);
 
 /* i830_sdvo.c */
-I830SDVOPtr I830SDVOInit(ScrnInfoPtr pScrn, int output_index,
-			 CARD32 output_device);
 Bool I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode);
 Bool I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode);
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 93c7fc4..5da31a6 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -900,71 +900,30 @@ I830UseDDC(ScrnInfoPtr pScrn)
 }
 #endif
 
+/**
+ * Set up the outputs according to what type of chip we are.
+ *
+ * Some outputs may not initialize, due to allocation failure or because a
+ * controller chip isn't found.
+ */
 static void
-I830SetupOutputBusses(ScrnInfoPtr pScrn)
+I830SetupOutputs(ScrnInfoPtr pScrn)
 {
    I830Ptr pI830 = I830PTR(pScrn);
-   int i = 0;
-   Bool ret;
 
    /* everyone has at least a single analog output */
-   pI830->output[i].type = I830_OUTPUT_ANALOG;
-   pI830->output[i].dpms = I830CRTDPMS;
-   pI830->output[i].save = I830CRTSave;
-   pI830->output[i].restore = I830CRTRestore;
-
-   /* setup the DDC bus for the analog output */
-   I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOA, "CRTDDC_A");
-   i++;
-
-   if (IS_MOBILE(pI830) && !IS_I830(pI830)) {
-      /* Set up integrated LVDS */
-      pI830->output[i].type = I830_OUTPUT_LVDS;
-      pI830->output[i].dpms = I830LVDSDPMS;
-      pI830->output[i].save = I830LVDSSave;
-      pI830->output[i].restore = I830LVDSRestore;
-      I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOC, "LVDSDDC_C");
-      i++;
-   }
+   i830_crt_init(pScrn);
 
-   if (IS_I9XX(pI830)) {
-      /* Set up SDVOB */
-      pI830->output[i].type = I830_OUTPUT_SDVO;
-      pI830->output[i].dpms = i830SDVODPMS;
-      pI830->output[i].save = i830SDVOSave;
-      pI830->output[i].restore = i830SDVORestore;
-      I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "SDVOCTRL_E");
-      I830SDVOInit(pScrn, i, SDVOB);
-      i++;
-
-      /* Set up SDVOC */
-      pI830->output[i].type = I830_OUTPUT_SDVO;
-      pI830->output[i].dpms = i830SDVODPMS;
-      pI830->output[i].save = i830SDVOSave;
-      pI830->output[i].restore = i830SDVORestore;
-      pI830->output[i].pI2CBus = pI830->output[i-1].pI2CBus;
-      I830SDVOInit(pScrn, i, SDVOC);
-      i++;
-   } else {
-      /* set up DVO */
-      pI830->output[i].type = I830_OUTPUT_DVO;
-      pI830->output[i].dpms = I830DVODPMS;
-      pI830->output[i].save = I830DVOSave;
-      pI830->output[i].restore = I830DVORestore;
-      I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOD, "DVODDC_D");
-      I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E");
-
-      ret = I830I2CDetectDVOControllers(pScrn, pI830->output[i].pI2CBus,
-					&pI830->output[i].i2c_drv);
-      if (ret == TRUE) {
-	 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found i2c %s on %08lX\n",
-		    pI830->output[i].i2c_drv->modulename,
-		    pI830->output[i].pI2CBus->DriverPrivate.uval);
-      }
+   /* Set up integrated LVDS */
+   if (IS_MOBILE(pI830) && !IS_I830(pI830))
+      i830_lvds_init(pScrn);
 
-      i++;
+   if (IS_I9XX(pI830)) {
+      i830_sdvo_init(pScrn, SDVOB);
+      i830_sdvo_init(pScrn, SDVOC);
+   } else {
+      i830_dvo_init(pScrn);
    }
-   pI830->num_outputs = i;
 }
 
 static void 
@@ -985,7 +944,7 @@ I830PreInitDDC(ScrnInfoPtr pScrn)
       if (xf86LoadSubModule(pScrn, "i2c")) {
 	 xf86LoaderReqSymLists(I810i2cSymbols, NULL);
 
-	 I830SetupOutputBusses(pScrn);
+	 I830SetupOutputs(pScrn);
 
 	 pI830->ddc2 = TRUE;
       } else {
diff --git a/src/i830_dvo.c b/src/i830_dvo.c
index f64a8e8..86d7d73 100644
--- a/src/i830_dvo.c
+++ b/src/i830_dvo.c
@@ -54,8 +54,8 @@ struct _I830DVODriver i830_dvo_drivers[]
 
 #define I830_NUM_DVO_DRIVERS (sizeof(i830_dvo_drivers)/sizeof(struct _I830DVODriver))
 
-void
-I830DVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+static void
+i830_dvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
     if (output->i2c_drv == NULL)
 	return;
@@ -66,8 +66,8 @@ I830DVODPMS(ScrnInfoPtr pScrn, I830Outpu
 	output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, FALSE);
 }
 
-void
-I830DVOSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_dvo_save(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
@@ -84,8 +84,8 @@ I830DVOSave(ScrnInfoPtr pScrn, I830Outpu
     output->i2c_drv->vid_rec->SaveRegs(output->i2c_drv->dev_priv);
 }
 
-void
-I830DVORestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_dvo_restore(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
@@ -129,3 +129,42 @@ I830I2CDetectDVOControllers(ScrnInfoPtr 
     }
     return FALSE;
 }
+
+void
+i830_dvo_init(ScrnInfoPtr pScrn)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    Bool ret;
+    int i = pI830->num_outputs;
+
+    pI830->output[i].type = I830_OUTPUT_DVO;
+    pI830->output[i].dpms = i830_dvo_dpms;
+    pI830->output[i].save = i830_dvo_save;
+    pI830->output[i].restore = i830_dvo_restore;
+
+    /* Set up the I2C and DDC buses */
+    ret = I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E");
+    if (!ret)
+	return;
+
+    ret = I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOD, "DVODDC_D");
+    if (!ret) {
+	xf86DestroyI2CBusRec(pI830->output[i].pI2CBus, TRUE, TRUE);
+	return;
+    }
+
+    /* Now, try to find a controller */
+    ret = I830I2CDetectDVOControllers(pScrn, pI830->output[i].pI2CBus,
+				      &pI830->output[i].i2c_drv);
+    if (ret) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found i2c %s on %08lX\n",
+		   pI830->output[i].i2c_drv->modulename,
+		   pI830->output[i].pI2CBus->DriverPrivate.uval);
+    } else {
+	xf86DestroyI2CBusRec(pI830->output[i].pI2CBus, TRUE, TRUE);
+	xf86DestroyI2CBusRec(pI830->output[i].pDDCBus, TRUE, TRUE);
+	return;
+    }
+
+    pI830->num_outputs++;
+}
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index fcd8fee..3ce8cb6 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -32,8 +32,8 @@
 #include "xf86.h"
 #include "i830.h"
 
-void
-I830LVDSDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+static void
+i830_lvds_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
     if (mode == DPMSModeOn)
 	i830SetLVDSPanelPower(pScrn, TRUE);
@@ -41,8 +41,8 @@ I830LVDSDPMS(ScrnInfoPtr pScrn, I830Outp
 	i830SetLVDSPanelPower(pScrn, FALSE);
 }
 
-void
-I830LVDSSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_lvds_save(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
@@ -66,8 +66,8 @@ I830LVDSSave(ScrnInfoPtr pScrn, I830Outp
     }
 }
 
-void
-I830LVDSRestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_lvds_restore(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
 
@@ -79,3 +79,22 @@ I830LVDSRestore(ScrnInfoPtr pScrn, I830O
     OUTREG(LVDS, pI830->saveLVDS);
     OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
 }
+
+void
+i830_lvds_init(ScrnInfoPtr pScrn)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    pI830->output[pI830->num_outputs].type = I830_OUTPUT_LVDS;
+    pI830->output[pI830->num_outputs].dpms = i830_lvds_dpms;
+    pI830->output[pI830->num_outputs].save = i830_lvds_save;
+    pI830->output[pI830->num_outputs].restore = i830_lvds_restore;
+
+    /* Set up the LVDS DDC channel.  Most panels won't support it, but it can
+     * be useful if available.
+     */
+    I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus,
+		GPIOC, "LVDSDDC_C");
+
+    pI830->num_outputs++;
+}
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index a07e7c9..9792904 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -639,8 +639,22 @@ I830SDVOPostSetMode(I830SDVOPtr s, Displ
     return ret;
 }
 
-void
-i830SDVOSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_sdvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+{
+    I830SDVOPtr sdvo = output->sdvo_drv;
+
+    if (sdvo == NULL)
+	return;
+
+    if (mode != DPMSModeOn)
+	I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
+    else
+	I830SDVOSetActiveOutputs(sdvo, TRUE, FALSE);
+}
+
+static void
+i830_sdvo_save(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
     I830SDVOPtr sdvo = output->sdvo_drv;
@@ -679,23 +693,8 @@ i830SDVOSave(ScrnInfoPtr pScrn, I830Outp
     sdvo->save_SDVOX = INREG(sdvo->output_device);
 }
 
-void
-i830SDVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
-{
-    I830Ptr pI830 = I830PTR(pScrn);
-    I830SDVOPtr sdvo = output->sdvo_drv;
-
-    if (sdvo == NULL)
-	return;
-
-    if (mode != DPMSModeOn)
-	I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
-    else
-	I830SDVOSetActiveOutputs(sdvo, TRUE, FALSE);
-}
-
-void
-i830SDVORestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+static void
+i830_sdvo_restore(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
     I830SDVOPtr sdvo = output->sdvo_drv;
@@ -827,95 +826,6 @@ I830SDVODDCI2CAddress(I2CDevPtr d, I2CSl
     return FALSE;
 }
 
-I830SDVOPtr
-I830SDVOInit(ScrnInfoPtr pScrn, int output_index, CARD32 output_device)
-{
-    I830Ptr pI830 = I830PTR(pScrn);
-    I830SDVOPtr sdvo;
-    int i;
-    unsigned char ch[0x40];
-    I2CBusPtr i2cbus, ddcbus;
-
-    i2cbus = pI830->output[output_index].pI2CBus;
-
-    sdvo = xcalloc(1, sizeof(I830SDVORec));
-    if (sdvo == NULL)
-	return NULL;
-
-    if (output_device == SDVOB) {
-	sdvo->d.DevName = "SDVO Controller B";
-	sdvo->d.SlaveAddr = 0x70;
-    } else {
-	sdvo->d.DevName = "SDVO Controller C";
-	sdvo->d.SlaveAddr = 0x72;
-    }
-    sdvo->d.pI2CBus = i2cbus;
-    sdvo->d.DriverPrivate.ptr = sdvo;
-    sdvo->output_device = output_device;
-
-    if (!xf86I2CDevInit(&sdvo->d)) {
-	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-		   "Failed to initialize SDVO I2C device %s\n",
-		   output_device == SDVOB ? "SDVOB" : "SDVOC");
-	xfree(sdvo);
-	return NULL;
-    }
-
-    /* Set up our wrapper I2C bus for DDC.  It acts just like the regular I2C
-     * bus, except that it does the control bus switch to DDC mode before every
-     * Start.  While we only need to do it at Start after every Stop after a
-     * Start, extra attempts should be harmless.
-     */
-    ddcbus = xf86CreateI2CBusRec();
-    if (ddcbus == NULL) {
-	xf86DestroyI2CDevRec(&sdvo->d, 0);
-	xfree(sdvo);
-	return NULL;
-    }
-    if (output_device == SDVOB)
-        ddcbus->BusName = "SDVOB DDC Bus";
-    else
-        ddcbus->BusName = "SDVOC DDC Bus";
-    ddcbus->scrnIndex = i2cbus->scrnIndex;
-    ddcbus->I2CGetByte = I830SDVODDCI2CGetByte;
-    ddcbus->I2CPutByte = I830SDVODDCI2CPutByte;
-    ddcbus->I2CStart = I830SDVODDCI2CStart;
-    ddcbus->I2CStop = I830SDVODDCI2CStop;
-    ddcbus->I2CAddress = I830SDVODDCI2CAddress;
-    ddcbus->DriverPrivate.ptr = sdvo;
-    if (!xf86I2CBusInit(ddcbus)) {
-	xf86DestroyI2CDevRec(&sdvo->d, 0);
-	xfree(sdvo);
-	return NULL;
-    }
-
-    pI830->output[output_index].pDDCBus = ddcbus;
-
-    /* Read the regs to test if we can talk to the device */
-    for (i = 0; i < 0x40; i++) {
-	if (!sReadByte(sdvo, i, &ch[i])) {
-	    xf86DestroyI2CBusRec(pI830->output[output_index].pDDCBus, FALSE,
-		FALSE);
-	    xf86DestroyI2CDevRec(&sdvo->d, 0);
-	    xfree(sdvo);
-	    return NULL;
-	}
-    }
-
-    pI830->output[output_index].sdvo_drv = sdvo;
-
-    I830SDVOGetCapabilities(sdvo, &sdvo->caps);
-    
-    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
-	       "SDVO device VID/DID: %02X:%02X.%02X, %02X, output 1: %c, output 2: %c\n",
-	       sdvo->caps.vendor_id, sdvo->caps.device_id,
-	       sdvo->caps.device_rev_id, sdvo->caps.caps,
-	       sdvo->caps.output_0_supported ? 'Y' : 'N',
-	       sdvo->caps.output_1_supported ? 'Y' : 'N');
-
-    return sdvo;
-}
-
 static void
 I830DumpSDVOCmd (I830SDVOPtr s, int opcode)
 {
@@ -991,3 +901,113 @@ I830DetectSDVODisplays(ScrnInfoPtr pScrn
     return (s->sdvo_regs[SDVO_I2C_RETURN_0] != 0 ||
 	    s->sdvo_regs[SDVO_I2C_RETURN_1] != 0);
 }
+
+void
+i830_sdvo_init(ScrnInfoPtr pScrn, int output_device)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    I830SDVOPtr sdvo;
+    int i;
+    unsigned char ch[0x40];
+    I2CBusPtr i2cbus = NULL, ddcbus;
+
+    pI830->output[pI830->num_outputs].type = I830_OUTPUT_SDVO;
+    pI830->output[pI830->num_outputs].dpms = i830_sdvo_dpms;
+    pI830->output[pI830->num_outputs].save = i830_sdvo_save;
+    pI830->output[pI830->num_outputs].restore = i830_sdvo_restore;
+
+    /* Find an existing SDVO I2CBus from another output, or allocate it. */
+    for (i = 0; i < pI830->num_outputs; i++) {
+	if (pI830->output[i].type == I830_OUTPUT_SDVO)
+	    i2cbus = pI830->output[i].pI2CBus;
+    }
+    if (i2cbus == NULL)
+	I830I2CInit(pScrn, &i2cbus, GPIOE, "SDVOCTRL_E");
+    if (i2cbus == NULL)
+	return;
+
+    /* Allocate the SDVO output private data */
+    sdvo = xcalloc(1, sizeof(I830SDVORec));
+    if (sdvo == NULL) {
+	xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE);
+	return;
+    }
+
+    if (output_device == SDVOB) {
+	sdvo->d.DevName = "SDVO Controller B";
+	sdvo->d.SlaveAddr = 0x70;
+    } else {
+	sdvo->d.DevName = "SDVO Controller C";
+	sdvo->d.SlaveAddr = 0x72;
+    }
+    sdvo->d.pI2CBus = i2cbus;
+    sdvo->d.DriverPrivate.ptr = sdvo;
+    sdvo->output_device = output_device;
+
+    if (!xf86I2CDevInit(&sdvo->d)) {
+	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+		   "Failed to initialize SDVO I2C device %s\n",
+		   output_device == SDVOB ? "SDVOB" : "SDVOC");
+	xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE);
+	xfree(sdvo);
+	return;
+    }
+
+    /* Set up our wrapper I2C bus for DDC.  It acts just like the regular I2C
+     * bus, except that it does the control bus switch to DDC mode before every
+     * Start.  While we only need to do it at Start after every Stop after a
+     * Start, extra attempts should be harmless.
+     */
+    ddcbus = xf86CreateI2CBusRec();
+    if (ddcbus == NULL) {
+	xf86DestroyI2CDevRec(&sdvo->d, FALSE);
+	xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE);
+	xfree(sdvo);
+	return;
+    }
+    if (output_device == SDVOB)
+        ddcbus->BusName = "SDVOB DDC Bus";
+    else
+        ddcbus->BusName = "SDVOC DDC Bus";
+    ddcbus->scrnIndex = i2cbus->scrnIndex;
+    ddcbus->I2CGetByte = I830SDVODDCI2CGetByte;
+    ddcbus->I2CPutByte = I830SDVODDCI2CPutByte;
+    ddcbus->I2CStart = I830SDVODDCI2CStart;
+    ddcbus->I2CStop = I830SDVODDCI2CStop;
+    ddcbus->I2CAddress = I830SDVODDCI2CAddress;
+    ddcbus->DriverPrivate.ptr = sdvo;
+    if (!xf86I2CBusInit(ddcbus)) {
+	xf86DestroyI2CDevRec(&sdvo->d, FALSE);
+	xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE);
+	xfree(sdvo);
+	return;
+    }
+
+    pI830->output[pI830->num_outputs].pI2CBus = ddcbus;
+    pI830->output[pI830->num_outputs].pDDCBus = ddcbus;
+    pI830->output[pI830->num_outputs].sdvo_drv = sdvo;
+
+    /* Read the regs to test if we can talk to the device */
+    for (i = 0; i < 0x40; i++) {
+	if (!sReadByte(sdvo, i, &ch[i])) {
+	    xf86DestroyI2CBusRec(pI830->output[pI830->num_outputs].pDDCBus,
+				 FALSE, FALSE);
+	    xf86DestroyI2CDevRec(&sdvo->d, FALSE);
+	    xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE);
+	    xfree(sdvo);
+	    return;
+	}
+    }
+
+    I830SDVOGetCapabilities(sdvo, &sdvo->caps);
+
+    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+	       "SDVO device VID/DID: %02X:%02X.%02X, %02X,"
+	       "output 1: %c, output 2: %c\n",
+	       sdvo->caps.vendor_id, sdvo->caps.device_id,
+	       sdvo->caps.device_rev_id, sdvo->caps.caps,
+	       sdvo->caps.output_0_supported ? 'Y' : 'N',
+	       sdvo->caps.output_1_supported ? 'Y' : 'N');
+
+    pI830->num_outputs++;
+}
diff --git a/src/i830_sdvo.h b/src/i830_sdvo.h
index 73ecf32..9a3586f 100644
--- a/src/i830_sdvo.h
+++ b/src/i830_sdvo.h
@@ -56,13 +56,7 @@ typedef struct _i830_sdvo_dtd {
 } __attribute__((packed)) i830_sdvo_dtd;
 
 void
-i830SDVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
-
-void
-i830SDVOSave(ScrnInfoPtr pScrn, I830OutputPtr output);
-
-void
-i830SDVORestore(ScrnInfoPtr pScrn, I830OutputPtr output);
+i830_sdvo_init(ScrnInfoPtr pScrn, int output_device);
 
 Bool
 I830DetectSDVODisplays(ScrnInfoPtr pScrn, int output_index);
diff-tree 103b4edce7859ddf58f3e1fadeb427a5e85c7acd (from 3e6f81f70f65a5ba6b5c3a4e0eeaf67776a5f54d)
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Oct 4 15:00:19 2006 -0700

    Move the save, restore, and DPMS per-output settings to per-output files.

diff --git a/src/Makefile.am b/src/Makefile.am
index 7af2dd7..cab6fe9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,6 +53,7 @@ i810_drv_la_SOURCES = \
          i830_bios.c \
          i830_bios.h \
          i830_common.h \
+	 i830_crt.c \
          i830_cursor.c \
 	 i830_debug.c \
 	 i830_debug.h \
@@ -65,6 +66,7 @@ i810_drv_la_SOURCES = \
          i830_gtf.c \
          i830_i2c.c \
          i830_io.c \
+	 i830_lvds.c \
          i830_memory.c \
          i830_modes.c \
          i830_video.c \
diff --git a/src/i830.h b/src/i830.h
index 7def141..f92704b 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -69,6 +69,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN
 #include "i830_dri.h"
 #endif
 
+typedef struct _I830OutputRec I830OutputRec, *I830OutputPtr;
+
 #include "common.h"
 #include "i830_sdvo.h"
 #include "i2c_vid.h"
@@ -222,6 +224,23 @@ struct _I830OutputRec {
    int type;
 /*   int pipe;
    int flags;*/
+
+   /**
+    * Turns the output on/off, or sets intermediate power levels if available.
+    * Unsupported intermediate modes drop to the lower power setting.
+    */
+   void (*dpms)(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
+
+   /**
+    * Saves the output's state for restoration on VT switch.
+    */
+   void (*save)(ScrnInfoPtr pScrn, I830OutputPtr output);
+
+   /**
+    * Restore's the output's state at VT switch.
+    */
+   void (*restore)(ScrnInfoPtr pScrn, I830OutputPtr output);
+
    xf86MonPtr MonInfo;
    I2CBusPtr pI2CBus;
    I2CBusPtr pDDCBus;
@@ -613,10 +632,23 @@ extern Bool I830FixOffset(ScrnInfoPtr pS
 extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg,
 			char *name);
 
+/* i830_crt.c */
+void I830CRTDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
+void I830CRTSave(ScrnInfoPtr pScrn, I830OutputPtr output);
+void I830CRTRestore(ScrnInfoPtr pScrn, I830OutputPtr output);
+
 /* i830_dvo.c */
+void I830DVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
+void I830DVOSave(ScrnInfoPtr pScrn, I830OutputPtr output);
+void I830DVORestore(ScrnInfoPtr pScrn, I830OutputPtr output);
 Bool I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus,
 				 struct _I830DVODriver **retdrv);
 
+/* i830_lvds.c */
+void I830LVDSDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
+void I830LVDSSave(ScrnInfoPtr pScrn, I830OutputPtr output);
+void I830LVDSRestore(ScrnInfoPtr pScrn, I830OutputPtr output);
+
 /* i830_memory.c */
 Bool I830BindAGPMemory(ScrnInfoPtr pScrn);
 Bool I830UnbindAGPMemory(ScrnInfoPtr pScrn);
diff --git a/src/i830_crt.c b/src/i830_crt.c
new file mode 100644
index 0000000..1a0dd0b
--- /dev/null
+++ b/src/i830_crt.c
@@ -0,0 +1,75 @@
+/*
+ * 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 at anholt.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "i830.h"
+
+void
+I830CRTDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+    CARD32 temp;
+
+    temp = INREG(ADPA);
+    temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+
+    switch(mode) {
+    case DPMSModeOn:
+	break;
+    case DPMSModeStandby:
+	temp |= ADPA_HSYNC_CNTL_DISABLE;
+	break;
+    case DPMSModeSuspend:
+	temp |= ADPA_VSYNC_CNTL_DISABLE;
+	break;
+    case DPMSModeOff:
+	temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+	break;
+    }
+
+    OUTREG(ADPA, temp);
+}
+
+void
+I830CRTSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    pI830->saveADPA = INREG(ADPA);
+}
+
+void
+I830CRTRestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    OUTREG(ADPA, pI830->saveADPA);
+}
diff --git a/src/i830_display.c b/src/i830_display.c
index 2b53128..5a0fb9d 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -864,10 +864,10 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayMo
     }
 
     for (i = 0; i < pI830->num_outputs; i++) {
-	struct _I830OutputRec *output = &pI830->output[i];
+	I830OutputPtr output = &pI830->output[i];
 
-	if (output->sdvo_drv)
-	    I830SDVOPreSetMode(output->sdvo_drv, pMode);
+	if (pI830->output[i].sdvo_drv != NULL)
+	    pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
 
 	if (output->i2c_drv != NULL)
 	    output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv,
diff --git a/src/i830_driver.c b/src/i830_driver.c
index d46fc1b..93c7fc4 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -909,6 +909,9 @@ I830SetupOutputBusses(ScrnInfoPtr pScrn)
 
    /* everyone has at least a single analog output */
    pI830->output[i].type = I830_OUTPUT_ANALOG;
+   pI830->output[i].dpms = I830CRTDPMS;
+   pI830->output[i].save = I830CRTSave;
+   pI830->output[i].restore = I830CRTRestore;
 
    /* setup the DDC bus for the analog output */
    I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOA, "CRTDDC_A");
@@ -917,6 +920,9 @@ I830SetupOutputBusses(ScrnInfoPtr pScrn)
    if (IS_MOBILE(pI830) && !IS_I830(pI830)) {
       /* Set up integrated LVDS */
       pI830->output[i].type = I830_OUTPUT_LVDS;
+      pI830->output[i].dpms = I830LVDSDPMS;
+      pI830->output[i].save = I830LVDSSave;
+      pI830->output[i].restore = I830LVDSRestore;
       I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOC, "LVDSDDC_C");
       i++;
    }
@@ -924,18 +930,27 @@ I830SetupOutputBusses(ScrnInfoPtr pScrn)
    if (IS_I9XX(pI830)) {
       /* Set up SDVOB */
       pI830->output[i].type = I830_OUTPUT_SDVO;
+      pI830->output[i].dpms = i830SDVODPMS;
+      pI830->output[i].save = i830SDVOSave;
+      pI830->output[i].restore = i830SDVORestore;
       I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "SDVOCTRL_E");
       I830SDVOInit(pScrn, i, SDVOB);
       i++;
 
       /* Set up SDVOC */
       pI830->output[i].type = I830_OUTPUT_SDVO;
+      pI830->output[i].dpms = i830SDVODPMS;
+      pI830->output[i].save = i830SDVOSave;
+      pI830->output[i].restore = i830SDVORestore;
       pI830->output[i].pI2CBus = pI830->output[i-1].pI2CBus;
       I830SDVOInit(pScrn, i, SDVOC);
       i++;
    } else {
       /* set up DVO */
       pI830->output[i].type = I830_OUTPUT_DVO;
+      pI830->output[i].dpms = I830DVODPMS;
+      pI830->output[i].save = I830DVOSave;
+      pI830->output[i].restore = I830DVORestore;
       I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOD, "DVODDC_D");
       I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E");
 
@@ -2600,31 +2615,6 @@ SaveHWState(ScrnInfoPtr pScrn)
    pI830->saveVCLK_POST_DIV = INREG(VCLK_POST_DIV);
    pI830->saveVGACNTRL = INREG(VGACNTRL);
 
-   pI830->saveADPA = INREG(ADPA);
-
-   pI830->savePFIT_CONTROL = INREG(PFIT_CONTROL);
-   pI830->savePP_ON = INREG(LVDSPP_ON);
-   pI830->savePP_OFF = INREG(LVDSPP_OFF);
-   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);
-      pI830->saveDVOB = INREG(DVOB);
-      pI830->saveDVOC = INREG(DVOC);
-   }
-
    for(i = 0; i < 7; i++) {
       pI830->saveSWF[i] = INREG(SWF0 + (i << 2));
       pI830->saveSWF[i+7] = INREG(SWF00 + (i << 2));
@@ -2634,17 +2624,8 @@ SaveHWState(ScrnInfoPtr pScrn)
    pI830->saveSWF[16] = INREG(SWF32);
 
    for (i = 0; i < pI830->num_outputs; i++) {
-      if (pI830->output[i].type == I830_OUTPUT_DVO &&
-	  pI830->output[i].i2c_drv != NULL)
-      {
-	 pI830->output[i].i2c_drv->vid_rec->SaveRegs(
-	    pI830->output[i].i2c_drv->dev_priv);
-      }
-      if (pI830->output[i].type == I830_OUTPUT_SDVO &&
-	  pI830->output[i].sdvo_drv != NULL)
-      {
-	 i830SDVOSave(pScrn, i);
-      }
+      if (pI830->output[i].save != NULL)
+	 pI830->output[i].save(pScrn, &pI830->output[i]);
    }
 
    vgaHWUnlock(hwp);
@@ -2670,6 +2651,11 @@ RestoreHWState(ScrnInfoPtr pScrn)
    vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS);
    vgaHWLock(hwp);
 
+   /* Disable outputs */
+   for (i = 0; i < pI830->num_outputs; i++) {
+      pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff);
+   }
+
    /* First, disable display planes */
    temp = INREG(DSPACNTR);
    OUTREG(DSPACNTR, temp & ~DISPLAY_PLANE_ENABLE);
@@ -2682,18 +2668,7 @@ RestoreHWState(ScrnInfoPtr pScrn)
    temp = INREG(PIPEBCONF);
    OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE);
 
-   /* XXX: Wait for a vblank */
-   sleep(1);
-
-   i830SetLVDSPanelPower(pScrn, FALSE);
-
-   for (i = 0; i < pI830->num_outputs; i++) {
-      if (pI830->output[i].type == I830_OUTPUT_SDVO &&
-	  pI830->output[i].sdvo_drv != NULL)
-      {
-	 i830SDVOPreRestore(pScrn, i);
-      }
-   }
+   i830WaitForVblank(pScrn);
 
    OUTREG(FPA0, pI830->saveFPA0);
    OUTREG(FPA1, pI830->saveFPA1);
@@ -2738,12 +2713,6 @@ RestoreHWState(ScrnInfoPtr pScrn)
       OUTREG(DSPBSURF, pI830->saveDSPBBASE);
    }
 
-   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);
-   OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL);
-   
    OUTREG(VCLK_DIVISOR_VGA0, pI830->saveVCLK_DIVISOR_VGA0);
    OUTREG(VCLK_DIVISOR_VGA1, pI830->saveVCLK_DIVISOR_VGA1);
    OUTREG(VCLK_POST_DIV, pI830->saveVCLK_POST_DIV);
@@ -2755,30 +2724,10 @@ RestoreHWState(ScrnInfoPtr pScrn)
    OUTREG(DSPACNTR, pI830->saveDSPACNTR);
    OUTREG(DSPBCNTR, pI830->saveDSPBCNTR);
 
-   OUTREG(ADPA, pI830->saveADPA);
-   OUTREG(LVDS, pI830->saveLVDS);
-   if (!IS_I9XX(pI830)) {
-      OUTREG(DVOA, pI830->saveDVOA);
-      OUTREG(DVOB, pI830->saveDVOB);
-      OUTREG(DVOC, pI830->saveDVOC);
-   }
-
    for (i = 0; i < pI830->num_outputs; i++) {
-      if (pI830->output[i].type == I830_OUTPUT_DVO &&
-	  pI830->output[i].i2c_drv != NULL)
-      {
-	 pI830->output[i].i2c_drv->vid_rec->RestoreRegs(
-	    pI830->output[i].i2c_drv->dev_priv);
-      }
-      if (pI830->output[i].type == I830_OUTPUT_SDVO &&
-	  pI830->output[i].sdvo_drv != NULL)
-      {
-	 i830SDVOPostRestore(pScrn, i);
-      }
+      pI830->output[i].restore(pScrn, &pI830->output[i]);
    }
 
-   OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
-
    for(i = 0; i < 7; i++) {
 	   OUTREG(SWF0 + (i << 2), pI830->saveSWF[i]);
 	   OUTREG(SWF00 + (i << 2), pI830->saveSWF[i+7]);
@@ -4204,39 +4153,6 @@ I830SaveScreen(ScreenPtr pScreen, int mo
    return TRUE;
 }
 
-static void
-I830DPMSCRT(ScrnInfoPtr pScrn, int mode)
-{
-   I830Ptr pI830 = I830PTR(pScrn);
-   CARD32 temp;
-   
-   temp = INREG(ADPA);
-   temp &= ~(ADPA_HSYNC_CNTL_DISABLE|ADPA_VSYNC_CNTL_DISABLE);
-   switch(mode) {
-   case DPMSModeOn:
-      break;
-   case DPMSModeStandby:
-      temp |= ADPA_HSYNC_CNTL_DISABLE;
-      break;
-   case DPMSModeSuspend:
-      temp |= ADPA_VSYNC_CNTL_DISABLE;
-      break;
-   case DPMSModeOff:
-      temp |= ADPA_HSYNC_CNTL_DISABLE|ADPA_VSYNC_CNTL_DISABLE;
-      break;
-   }
-   OUTREG(ADPA, temp);
-}
-
-static void
-I830DPMSLVDS(ScrnInfoPtr pScrn, int mode)
-{
-   if (mode == DPMSModeOn)
-      i830SetLVDSPanelPower(pScrn, TRUE);
-   else
-      i830SetLVDSPanelPower(pScrn, FALSE);
-}
-
 /* Use the VBE version when available. */
 static void
 I830DisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode,
@@ -4246,6 +4162,10 @@ I830DisplayPowerManagementSet(ScrnInfoPt
    int i;
    CARD32 temp, ctrl, base;
 
+   for (i = 0; i < pI830->num_outputs; i++) {
+      pI830->output[i].dpms(pScrn, &pI830->output[i], PowerManagementMode);
+   }
+
    for (i = 0; i < pI830->availablePipes; i++) {
       if (i == 0) {
          ctrl = DSPACNTR;
@@ -4267,22 +4187,6 @@ I830DisplayPowerManagementSet(ScrnInfoPt
       }
    }
 
-   if (pI830->operatingDevices & (PIPE_CRT_ACTIVE | (PIPE_CRT_ACTIVE<<8))) {
-      I830DPMSCRT(pScrn, PowerManagementMode);
-   }
-
-   if (pI830->operatingDevices & (PIPE_LCD_ACTIVE | (PIPE_LCD_ACTIVE<<8))) {
-      I830DPMSLVDS(pScrn, PowerManagementMode);
-   }
-
-   if (pI830->operatingDevices & (PIPE_DFP_ACTIVE | (PIPE_DFP_ACTIVE<<8))) {
-      /* TBD */
-   }
-
-   if (pI830->operatingDevices & (PIPE_DFP2_ACTIVE | (PIPE_DFP2_ACTIVE<<8))) {
-      /* TBD */
-   }
-
    if (pI830->CursorInfoRec && !pI830->SWCursor && pI830->cursorOn) {
       if (PowerManagementMode == DPMSModeOn)
          pI830->CursorInfoRec->ShowCursor(pScrn);
diff --git a/src/i830_dvo.c b/src/i830_dvo.c
index 242e3dd..f64a8e8 100644
--- a/src/i830_dvo.c
+++ b/src/i830_dvo.c
@@ -54,6 +54,51 @@ struct _I830DVODriver i830_dvo_drivers[]
 
 #define I830_NUM_DVO_DRIVERS (sizeof(i830_dvo_drivers)/sizeof(struct _I830DVODriver))
 
+void
+I830DVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+{
+    if (output->i2c_drv == NULL)
+	return;
+
+    if (mode == DPMSModeOn)
+	output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, TRUE);
+    else
+	output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, FALSE);
+}
+
+void
+I830DVOSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    if (output->i2c_drv == NULL)
+	return;
+
+    /* Each output should probably just save the registers it touches, but for
+     * now, use more overkill.
+     */
+    pI830->saveDVOA = INREG(DVOA);
+    pI830->saveDVOB = INREG(DVOB);
+    pI830->saveDVOC = INREG(DVOC);
+
+    output->i2c_drv->vid_rec->SaveRegs(output->i2c_drv->dev_priv);
+}
+
+void
+I830DVORestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    if (output->i2c_drv == NULL)
+	return;
+
+    OUTREG(DVOA, pI830->saveDVOA);
+    OUTREG(DVOB, pI830->saveDVOB);
+    OUTREG(DVOC, pI830->saveDVOC);
+
+    output->i2c_drv->vid_rec->RestoreRegs(output->i2c_drv->dev_priv);
+}
+
 Bool
 I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus,
 			    struct _I830DVODriver **retdrv)
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
new file mode 100644
index 0000000..fcd8fee
--- /dev/null
+++ b/src/i830_lvds.c
@@ -0,0 +1,81 @@
+/*
+ * 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 at anholt.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "i830.h"
+
+void
+I830LVDSDPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
+{
+    if (mode == DPMSModeOn)
+	i830SetLVDSPanelPower(pScrn, TRUE);
+    else
+	i830SetLVDSPanelPower(pScrn, FALSE);
+}
+
+void
+I830LVDSSave(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(pScrn);
+
+    pI830->savePFIT_CONTROL = INREG(PFIT_CONTROL);
+    pI830->savePP_ON = INREG(LVDSPP_ON);
+    pI830->savePP_OFF = INREG(LVDSPP_OFF);
+    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 == 0) {
+	pI830->backlight_duty_cycle =
+	    (pI830->saveBLC_PWM_CTL & BACKLIGHT_MODULATION_FREQ_MASK) >>
+	    BACKLIGHT_MODULATION_FREQ_SHIFT;
+    }
+}
+
+void
+I830LVDSRestore(ScrnInfoPtr pScrn, I830OutputPtr output)
+{
+    I830Ptr pI830 = I830PTR(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);
+    OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL);
+    OUTREG(LVDS, pI830->saveLVDS);
+    OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
+}
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index c762541..a07e7c9 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -640,10 +640,13 @@ I830SDVOPostSetMode(I830SDVOPtr s, Displ
 }
 
 void
-i830SDVOSave(ScrnInfoPtr pScrn, int output_index)
+i830SDVOSave(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
-    I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv;
+    I830SDVOPtr sdvo = output->sdvo_drv;
+
+    if (sdvo == NULL)
+	return;
 
     sdvo->save_sdvo_mult = I830SDVOGetClockRateMult(sdvo);
     I830SDVOGetActiveOutputs(sdvo, &sdvo->save_sdvo_active_1,
@@ -677,19 +680,28 @@ i830SDVOSave(ScrnInfoPtr pScrn, int outp
 }
 
 void
-i830SDVOPreRestore(ScrnInfoPtr pScrn, int output_index)
+i830SDVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode)
 {
     I830Ptr pI830 = I830PTR(pScrn);
-    I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv;
+    I830SDVOPtr sdvo = output->sdvo_drv;
+
+    if (sdvo == NULL)
+	return;
 
-    I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
+    if (mode != DPMSModeOn)
+	I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE);
+    else
+	I830SDVOSetActiveOutputs(sdvo, TRUE, FALSE);
 }
 
 void
-i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index)
+i830SDVORestore(ScrnInfoPtr pScrn, I830OutputPtr output)
 {
     I830Ptr pI830 = I830PTR(pScrn);
-    I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv;
+    I830SDVOPtr sdvo = output->sdvo_drv;
+
+    if (sdvo == NULL)
+	return;
 
     if (sdvo->caps.caps & 0x1) {
        I830SDVOSetTargetInput(sdvo, FALSE, FALSE);
diff --git a/src/i830_sdvo.h b/src/i830_sdvo.h
index 52621e0..73ecf32 100644
--- a/src/i830_sdvo.h
+++ b/src/i830_sdvo.h
@@ -56,13 +56,13 @@ typedef struct _i830_sdvo_dtd {
 } __attribute__((packed)) i830_sdvo_dtd;
 
 void
-i830SDVOSave(ScrnInfoPtr pScrn, int output_index);
+i830SDVODPMS(ScrnInfoPtr pScrn, I830OutputPtr output, int mode);
 
 void
-i830SDVOPreRestore(ScrnInfoPtr pScrn, int output_index);
+i830SDVOSave(ScrnInfoPtr pScrn, I830OutputPtr output);
 
 void
-i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index);
+i830SDVORestore(ScrnInfoPtr pScrn, I830OutputPtr output);
 
 Bool
 I830DetectSDVODisplays(ScrnInfoPtr pScrn, int output_index);



More information about the xorg-commit mailing list