[Nouveau] [PATCHv2 05/10] drm/nouveau: Restructure the nv04 modesetting code.

Francisco Jerez currojerez at riseup.net
Thu Aug 13 05:01:32 PDT 2009


* Delay the hardware state synchronization to crtc->commit(). This way
  the encoder-specific code can adjust the CRTC regs on mode_set()
  before they're written out.

* Don't abuse adjusted_mode to mean the output side mode, this makes
  impossible for the encoder code to fix up the actual CRTC mode.

* Split the digital from the analog stuff, as they have almost no
  common code and IMHO this will substantially improve consistency
  once the TVDAC implementation is imported.

Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
 drivers/gpu/drm/nouveau/Makefile       |    4 +-
 drivers/gpu/drm/nouveau/nouveau_bios.c |   26 +-
 drivers/gpu/drm/nouveau/nouveau_drv.h  |   33 ++-
 drivers/gpu/drm/nouveau/nouveau_hw.c   |   10 +
 drivers/gpu/drm/nouveau/nv04_crtc.c    |  262 ++---------
 drivers/gpu/drm/nouveau/nv04_dac.c     |  497 ++++++++++++++++++++
 drivers/gpu/drm/nouveau/nv04_dfp.c     |  611 ++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nv04_display.c |   29 +-
 drivers/gpu/drm/nouveau/nv04_output.c  |  797 --------------------------------
 drivers/gpu/drm/nouveau/nvreg.h        |   14 +-
 10 files changed, 1235 insertions(+), 1048 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nv04_dac.c
 create mode 100644 drivers/gpu/drm/nouveau/nv04_dfp.c
 delete mode 100644 drivers/gpu/drm/nouveau/nv04_output.c

diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 2ba1e45..4965e00 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -18,8 +18,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
              nv04_instmem.o nv50_instmem.o \
              nv50_crtc.o nv50_dac.o nv50_sor.o \
              nv50_cursor.o nv50_display.o nv50_fbcon.o \
-             nv04_display.o nv04_output.o nv04_crtc.o nv04_cursor.o \
-             nv04_fbcon.o
+             nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
+             nv04_dac.o nv04_dfp.o
 
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
 nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index db52f25..e9e1e8f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -2698,30 +2698,6 @@ static void parse_init_tables(struct drm_device *dev, struct nvbios *bios)
 	}
 }
 
-static void link_head_and_output(struct drm_device *dev, struct dcb_entry *dcbent, int head, bool dl)
-{
-	/* The BIOS scripts don't do this for us, sadly
-	 * Luckily we do know the values ;-)
-	 *
-	 * head < 0 indicates we wish to force a setting with the overrideval
-	 * (for VT restore etc.)
-	 */
-
-	int ramdac = (dcbent->or & OUTPUT_C) >> 2;
-	uint8_t tmds04 = 0x80;
-
-	if (head != ramdac)
-		tmds04 = 0x88;
-
-	if (dcbent->type == OUTPUT_LVDS)
-		tmds04 |= 0x01;
-
-	nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
-
-	if (dl)	/* dual link */
-		nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
-}
-
 static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk)
 {
 	int compare_record_len, i = 0;
@@ -2762,7 +2738,7 @@ static void run_digital_op_script(struct drm_device *dev, uint16_t scriptptr, st
 	NVWriteVgaCrtc5758(dev, head, 0, dcbent->index);
 	parse_init_table(dev, bios, scriptptr, &iexec);
 
-	link_head_and_output(dev, dcbent, head, dl);
+	nv04_dfp_bind_head(dev, dcbent, head, dl);
 }
 
 static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 1197c68..22d08bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -331,6 +331,16 @@ struct nouveau_pll_vals {
 	int refclk;
 };
 
+enum nv04_fp_display_regs {
+	FP_DISPLAY_END,
+	FP_TOTAL,
+	FP_CRTC,
+	FP_SYNC_START,
+	FP_SYNC_END,
+	FP_VALID_START,
+	FP_VALID_END
+};
+
 struct nv04_crtc_reg {
 	unsigned char MiscOutReg;     /* */
 	uint8_t CRTC[0x9f];
@@ -364,6 +374,8 @@ struct nv04_crtc_reg {
 	uint32_t fp_debug_0;
 	uint32_t fp_debug_1;
 	uint32_t fp_debug_2;
+	uint32_t fp_margin_color;
+	uint32_t ramdac_8c0;
 	uint32_t ramdac_a20;
 	uint32_t ramdac_a24;
 	uint32_t ramdac_a34;
@@ -525,6 +537,7 @@ struct drm_nouveau_private {
 	struct nv04_mode_state saved_reg;
 	uint32_t saved_vga_font[4][16384];
 	uint32_t crtc_owner;
+	uint32_t dac_users[4];
 
 	struct nouveau_suspend_resume {
 		uint32_t fifo_mode;
@@ -893,16 +906,26 @@ extern void nv04_timer_takedown(struct drm_device *);
 extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
 				 unsigned long arg);
 
+/* nv04_dac.c */
+extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
+extern enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+						 struct drm_connector *connector);
+extern int nv04_dac_output_offset(struct drm_encoder *encoder);
+extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
+
+/* nv04_dfp.c */
+extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
+extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
+extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+			       int head, bool dl);
+extern void nv04_dfp_disable(struct drm_device *dev, int head);
+extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
+
 /* nv04_display.c */
 extern int nv04_display_create(struct drm_device *);
 extern void nv04_display_destroy(struct drm_device *);
 extern void nv04_display_restore(struct drm_device *);
 
-/* nv04_output.c */
-extern int nv04_encoder_create(struct drm_device *, struct dcb_entry *);
-extern int nv04_connector_create(struct drm_device *, int i2c_index,
-				 uint16_t encoders, int type);
-
 /* nv04_crtc.c */
 extern int nv04_crtc_create(struct drm_device *, int index);
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
index d270d6f..5f49aed 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -698,6 +698,11 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
 	regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1);
 	regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2);
 
+	regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR);
+
+	if (nv_arch(dev) >= NV_30)
+		regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
+
 	if (nv_arch(dev) == NV_40) {
 		regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
 		regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
@@ -751,6 +756,11 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1);
 	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2);
 
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color);
+
+	if (nv_arch(dev) >= NV_30)
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
+
 	if (nv_arch(dev) == NV_40) {
 		NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
 		NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 103e28c..802b290 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -74,6 +74,18 @@ static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level)
 	NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634);
 }
 
+#define PLLSEL_VPLL1_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL	\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
+#define PLLSEL_VPLL2_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
+#define PLLSEL_TV_MASK					\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2	\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
 /* NV4x 0x40.. pll notes:
  * gpu pll: 0x4000 + 0x4004
  * ?gpu? pll: 0x4008 + 0x400c
@@ -122,17 +134,16 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
 	if (!(vclk = nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv)))
 		return;
 
+	state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
+
 	/* The blob uses this always, so let's do the same */
 	if (nv_arch(dev) == NV_40)
-		state->pllsel |= NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE;
+		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
 	/* again nv40 and some nv43 act more like nv3x as described above */
 	if (dev_priv->chipset < 0x41)
 		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
 				 NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
-	state->pllsel |= (nv_crtc->index ? NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2 |
-					  NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2 :
-					  NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL |
-					  NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2);
+	state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
 
 	if (pv->NM2)
 		NV_TRACE(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
@@ -218,8 +229,7 @@ nv_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode * mode,
 }
 
 static void
-nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode,
-		     struct drm_display_mode *adjusted_mode)
+nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -449,7 +459,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
 	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
 	struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
 	struct drm_encoder *encoder;
-	bool lvds_output = false, tmds_output = false, off_chip_digital = false;
+	bool lvds_output = false, tmds_output = false, tv_output = false,
+		off_chip_digital = false;
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -460,6 +471,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
 
 		if (nv_encoder->dcb->type == OUTPUT_LVDS)
 			digital = lvds_output = true;
+		if (nv_encoder->dcb->type == OUTPUT_TV)
+			tv_output = true;
 		if (nv_encoder->dcb->type == OUTPUT_TMDS)
 			digital = tmds_output = true;
 		if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
@@ -550,7 +563,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
 
 	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
 	/* Enable slaved mode (called MODE_TV in nv4ref.h) */
-	if (lvds_output || tmds_output)
+	if (lvds_output || tmds_output || tv_output)
 		regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
 
 	/* Generic PRAMDAC regs */
@@ -572,187 +585,12 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
 	nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
 
 	/* Some values the blob sets */
+	regp->ramdac_8c0 = 0x100;
 	regp->ramdac_a20 = 0x0;
 	regp->ramdac_a24 = 0xfffff;
 	regp->ramdac_a34 = 0x1;
 }
 
-enum fp_display_regs {
-	FP_DISPLAY_END,
-	FP_TOTAL,
-	FP_CRTC,
-	FP_SYNC_START,
-	FP_SYNC_END,
-	FP_VALID_START,
-	FP_VALID_END
-};
-
-/* this could be set in nv_output, but would require some rework of load/save */
-static void
-nv_crtc_mode_set_fp_regs(struct drm_crtc *crtc, struct drm_display_mode *mode,
-			 struct drm_display_mode * adjusted_mode)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
-	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
-	struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
-	struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
-	struct nouveau_encoder *nv_encoder = NULL;
-	struct drm_encoder *encoder;
-	uint32_t mode_ratio, panel_ratio;
-
-	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-		/* assuming one fp output per crtc seems ok */
-		if (encoder->crtc != crtc)
-			continue;
-		nv_encoder = nouveau_encoder(encoder);
-
-		if (nv_encoder->dcb->type == OUTPUT_LVDS ||
-		    nv_encoder->dcb->type == OUTPUT_TMDS)
-			break;
-		nv_encoder = NULL;
-	}
-
-	if (!nv_encoder)
-		return;
-
-	regp->fp_horiz_regs[FP_DISPLAY_END] = adjusted_mode->hdisplay - 1;
-	regp->fp_horiz_regs[FP_TOTAL] = adjusted_mode->htotal - 1;
-	if (!nv_gf4_disp_arch(dev) ||
-	    (adjusted_mode->hsync_start - adjusted_mode->hdisplay) >=
-					dev_priv->vbios->digital_min_front_porch)
-		regp->fp_horiz_regs[FP_CRTC] = adjusted_mode->hdisplay;
-	else
-		regp->fp_horiz_regs[FP_CRTC] = adjusted_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
-	regp->fp_horiz_regs[FP_SYNC_START] = adjusted_mode->hsync_start - 1;
-	regp->fp_horiz_regs[FP_SYNC_END] = adjusted_mode->hsync_end - 1;
-	regp->fp_horiz_regs[FP_VALID_START] = adjusted_mode->hskew;
-	regp->fp_horiz_regs[FP_VALID_END] = adjusted_mode->hdisplay - 1;
-
-	regp->fp_vert_regs[FP_DISPLAY_END] = adjusted_mode->vdisplay - 1;
-	regp->fp_vert_regs[FP_TOTAL] = adjusted_mode->vtotal - 1;
-	regp->fp_vert_regs[FP_CRTC] = adjusted_mode->vtotal - 5 - 1;
-	regp->fp_vert_regs[FP_SYNC_START] = adjusted_mode->vsync_start - 1;
-	regp->fp_vert_regs[FP_SYNC_END] = adjusted_mode->vsync_end - 1;
-	regp->fp_vert_regs[FP_VALID_START] = 0;
-	regp->fp_vert_regs[FP_VALID_END] = adjusted_mode->vdisplay - 1;
-
-	/* bit26: a bit seen on some g7x, no as yet discernable purpose */
-	regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
-			   (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
-	/* Deal with vsync/hsync polarity */
-	/* LVDS screens do set this, but modes with +ve syncs are very rare */
-	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
-	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
-	/* panel scaling first, as native would get set otherwise */
-	if (nv_connector->scaling_mode == DRM_MODE_SCALE_NON_GPU ||
-	    nv_connector->scaling_mode == DRM_MODE_SCALE_NO_SCALE)	/* panel handles it */
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
-	else if (mode->hdisplay == adjusted_mode->hdisplay &&
-		 mode->vdisplay == adjusted_mode->vdisplay) /* native mode */
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
-	else /* gpu needs to scale */
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
-	if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
-		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
-	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
-	    nv_connector->native_mode->clock > 165000)
-		regp->fp_control |= (2 << 24);
-	if (nv_encoder->dcb->type == OUTPUT_LVDS) {
-		bool duallink, dummy;
-
-		nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
-					      clock, &duallink, &dummy);
-		if (duallink)
-			regp->fp_control |= (8 << 28);
-	} else
-	if (nv_connector->native_mode->clock > 165000)
-		regp->fp_control |= (8 << 28);
-
-	regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
-			   NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
-			   NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
-			   NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
-			   NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
-			   NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
-			   NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
-
-	/* We want automatic scaling */
-	regp->fp_debug_1 = 0;
-	/* This can override HTOTAL and VTOTAL */
-	regp->fp_debug_2 = 0;
-
-	/* Use 20.12 fixed point format to avoid floats */
-	mode_ratio = (1 << 12) * mode->hdisplay / mode->vdisplay;
-	panel_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
-	/* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
-	 * get treated the same as SCALE_FULLSCREEN */
-	if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
-	    mode_ratio != panel_ratio) {
-		uint32_t diff, scale;
-		bool divide_by_2 = nv_gf4_disp_arch(dev);
-
-		if (mode_ratio < panel_ratio) {
-			/* vertical needs to expand to glass size (automatic)
-			 * horizontal needs to be scaled at vertical scale factor
-			 * to maintain aspect */
-
-			scale = (1 << 12) * mode->vdisplay / adjusted_mode->vdisplay;
-			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
-					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
-
-			/* restrict area of screen used, horizontally */
-			diff = adjusted_mode->hdisplay -
-			       adjusted_mode->vdisplay * mode_ratio / (1 << 12);
-			regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
-			regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
-		}
-
-		if (mode_ratio > panel_ratio) {
-			/* horizontal needs to expand to glass size (automatic)
-			 * vertical needs to be scaled at horizontal scale factor
-			 * to maintain aspect */
-
-			scale = (1 << 12) * mode->hdisplay / adjusted_mode->hdisplay;
-			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
-					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
-
-			/* restrict area of screen used, vertically */
-			diff = adjusted_mode->vdisplay -
-			       (1 << 12) * adjusted_mode->hdisplay / mode_ratio;
-			regp->fp_vert_regs[FP_VALID_START] += diff / 2;
-			regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
-		}
-	}
-
-	/* Output property. */
-	if (nv_connector && nv_connector->use_dithering) {
-		if (dev_priv->chipset == 0x11)
-			regp->dither = savep->dither | 0x00010000;
-		else {
-			int i;
-			regp->dither = savep->dither | 0x00000001;
-			for (i = 0; i < 3; i++) {
-				regp->dither_regs[i] = 0xe4e4e4e4;
-				regp->dither_regs[i + 3] = 0x44444444;
-			}
-		}
-	} else {
-		if (dev_priv->chipset != 0x11) {
-			/* reset them */
-			int i;
-			for (i = 0; i < 3; i++) {
-				regp->dither_regs[i] = savep->dither_regs[i];
-				regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
-			}
-		}
-		regp->dither = savep->dither;
-	}
-}
-
 /**
  * Sets up registers for the given mode/adjusted_mode pair.
  *
@@ -770,35 +608,18 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 
-	NV_TRACE(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
-	drm_mode_debug_printmodeline(mode);
-	NV_TRACE(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
-	drm_mode_debug_printmodeline(mode);
+	NV_DEBUG(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
+	drm_mode_debug_printmodeline(adjusted_mode);
 
 	/* unlock must come after turning off FP_TG_CONTROL in output_prepare */
 	nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
 
-	nv_crtc_mode_set_vga(crtc, mode, adjusted_mode);
-	/* calculated in output_prepare, nv40 needs it written before calculating PLLs */
+	nv_crtc_mode_set_vga(crtc, adjusted_mode);
+	/* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
 	if (nv_arch(dev) == NV_40)
 		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
-	nv_crtc_mode_set_regs(crtc, mode);
-	nv_crtc_mode_set_fp_regs(crtc, mode, adjusted_mode);
+	nv_crtc_mode_set_regs(crtc, adjusted_mode);
 	nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
-
-	nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
-
-	nv04_crtc_mode_set_base(crtc, x, y, NULL);
-
-#ifdef __BIG_ENDIAN
-	/* turn on LFB swapping */
-	{
-		uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
-		tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
-		NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
-	}
-#endif
-
 	return 0;
 }
 
@@ -806,15 +627,21 @@ static void nv_crtc_save(struct drm_crtc *crtc)
 {
 	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
 	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
+	struct nv04_mode_state *saved = &dev_priv->saved_reg;
+	struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
 
 	if (nv_two_heads(crtc->dev))
 		NVSetOwner(crtc->dev, nv_crtc->index);
 
-	nouveau_hw_save_state(crtc->dev, nv_crtc->index, &dev_priv->saved_reg);
+	nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
 
 	/* init some state to saved value */
-	dev_priv->mode_reg.sel_clk = dev_priv->saved_reg.sel_clk & ~(0x5 << 16);
-	dev_priv->mode_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX] = dev_priv->saved_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX];
+	state->sel_clk = saved->sel_clk & ~(0x5 << 16);
+	crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
+	state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
+	crtc_state->gpio_ext = crtc_saved->gpio_ext;
 }
 
 static void nv_crtc_restore(struct drm_crtc *crtc)
@@ -856,7 +683,22 @@ static void nv_crtc_prepare(struct drm_crtc *crtc)
 
 static void nv_crtc_commit(struct drm_crtc *crtc)
 {
+	struct drm_device *dev = crtc->dev;
 	struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
+	nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
+
+#ifdef __BIG_ENDIAN
+	/* turn on LFB swapping */
+	{
+		uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
+		tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
+		NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
+	}
+#endif
 
 	funcs->dpms(crtc, DRM_MODE_DPMS_ON);
 }
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
new file mode 100644
index 0000000..05fc393
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * 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.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+int nv04_dac_output_offset(struct drm_encoder *encoder)
+{
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+	int offset = 0;
+
+	if (dcb->or & (8 | OUTPUT_C))
+		offset += 0x68;
+	if (dcb->or & (8 | OUTPUT_B))
+		offset += 0x2000;
+
+	return offset;
+}
+
+/*
+ * arbitrary limit to number of sense oscillations tolerated in one sample
+ * period (observed to be at least 13 in "nvidia")
+ */
+#define MAX_HBLANK_OSC 20
+
+/*
+ * arbitrary limit to number of conflicting sample pairs to tolerate at a
+ * voltage step (observed to be at least 5 in "nvidia")
+ */
+#define MAX_SAMPLE_PAIRS 10
+
+static int sample_load_twice(struct drm_device *dev, bool sense[2])
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		bool sense_a, sense_b, sense_b_prime;
+		int j = 0;
+
+		/*
+		 * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
+		 * then wait for transition 0x4->0x5->0x4: enter hblank, leave
+		 * hblank again
+		 * use a 10ms timeout (guards against crtc being inactive, in
+		 * which case blank state would never change)
+		 */
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000000))
+			return -EBUSY;
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000001))
+			return -EBUSY;
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000000))
+			return -EBUSY;
+
+		udelay(100);
+		/* when level triggers, sense is _LO_ */
+		sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+
+		/* take another reading until it agrees with sense_a... */
+		do {
+			udelay(100);
+			sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+			if (sense_a != sense_b) {
+				sense_b_prime =
+					nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+				if (sense_b == sense_b_prime) {
+					/* ... unless two consecutive subsequent
+					 * samples agree; sense_a is replaced */
+					sense_a = sense_b;
+					/* force mis-match so we loop */
+					sense_b = !sense_a;
+				}
+			}
+		} while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
+
+		if (j == MAX_HBLANK_OSC)
+			/* with so much oscillation, default to sense:LO */
+			sense[i] = false;
+		else
+			sense[i] = sense_a;
+	}
+
+	return 0;
+}
+
+static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
+						 struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	uint8_t saved_seq1, saved_pi, saved_rpc1;
+	uint8_t saved_palette0[3], saved_palette_mask;
+	uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
+	int i;
+	uint8_t blue;
+	bool sense = true;
+
+	/*
+	 * for this detection to work, there needs to be a mode set up on the
+	 * CRTC.  this is presumed to be the case
+	 */
+
+	if (nv_two_heads(dev))
+		/* only implemented for head A for now */
+		NVSetOwner(dev, 0);
+
+	saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
+
+	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
+		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+	msleep(10);
+
+	saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
+		       saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
+	saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
+
+	nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
+	for (i = 0; i < 3; i++)
+		saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
+	saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
+
+	saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
+		      (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
+					   NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
+		      NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
+
+	blue = 8;	/* start of test range */
+
+	do {
+		bool sense_pair[2];
+
+		nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+		/* testing blue won't find monochrome monitors.  I don't care */
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
+
+		i = 0;
+		/* take sample pairs until both samples in the pair agree */
+		do {
+			if (sample_load_twice(dev, sense_pair))
+				goto out;
+		} while ((sense_pair[0] != sense_pair[1]) &&
+							++i < MAX_SAMPLE_PAIRS);
+
+		if (i == MAX_SAMPLE_PAIRS)
+			/* too much oscillation defaults to LO */
+			sense = false;
+		else
+			sense = sense_pair[0];
+
+	/*
+	 * if sense goes LO before blue ramps to 0x18, monitor is not connected.
+	 * ergo, if blue gets to 0x18, monitor must be connected
+	 */
+	} while (++blue < 0x18 && sense);
+
+out:
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
+	nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+	for (i = 0; i < 3; i++)
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
+
+	if (blue == 0x18) {
+		NV_TRACE(dev, "Load detected on head A\n");
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+					  struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+	uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
+	uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
+		saved_rtest_ctrl, temp, routput;
+	int head, present = 0;
+
+#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20)
+	testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+
+	if (dev_priv->vbios->dactestval)
+		testval = dev_priv->vbios->dactestval;
+
+	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
+		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+	saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
+
+	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
+	if (regoffset == 0x68) {
+		saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
+	}
+
+	msleep(4);
+
+	saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+	head = (saved_routput & 0x100) >> 8;
+#if 0
+	/* if there's a spare crtc, using it will minimise flicker for the case
+	 * where the in-use crtc is in use by an off-chip tmds encoder */
+	if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
+		head ^= 1;
+#endif
+	/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
+	routput = (saved_routput & 0xfffffece) | head << 8;
+
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
+	msleep(1);
+
+	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
+		      NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
+	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+		      temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+	msleep(5);
+
+	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+
+	present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
+
+	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+		      temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
+
+	/* bios does something more complex for restoring, but I think this is good enough */
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
+	if (regoffset == 0x68)
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
+	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
+
+	if (present) {
+		NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or));
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+
+static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void nv04_dac_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_disable(dev, head);
+
+	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+	 * at LCD__INDEX which we don't alter
+	 */
+	if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
+		crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+}
+
+
+static void nv04_dac_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+
+	NV_TRACE(dev, "%s called for encoder %d\n", __func__,
+		      nv_encoder->dcb->index);
+
+	if (nv_gf4_disp_arch(dev)) {
+		struct drm_encoder *rebind;
+		uint32_t dac_offset = nv04_dac_output_offset(encoder);
+		uint32_t otherdac;
+
+		/* bit 16-19 are bits that are set on some G70 cards,
+		 * but don't seem to have much effect */
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+			      head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+		/* force any other vga encoders to bind to the other crtc */
+		list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
+			if (rebind == encoder
+			    || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
+				continue;
+
+			dac_offset = nv04_dac_output_offset(rebind);
+			otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
+			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+				      (otherdac & ~0x0100) | (head ^ 1) << 8);
+		}
+	}
+
+	/* This could use refinement for flatpanels, but it should work this way */
+	if (dev_priv->chipset < 0x44)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
+	else
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+}
+
+static void nv04_dac_commit(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+
+	if (nv_gf4_disp_arch(dev)) {
+		uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
+		int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
+		uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
+
+		if (enable) {
+			*dac_users |= 1 << dcb->index;
+			NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+
+		} else if (!(*dac_users &= ~(1 << dcb->index)))
+			NVWriteRAMDAC(dev, 0, dacclk_off, dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
+	}
+}
+
+static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+static void nv04_dac_save(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_gf4_disp_arch(dev))
+		nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
+							  nv04_dac_output_offset(encoder));
+}
+
+static void nv04_dac_restore(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_gf4_disp_arch(dev))
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
+			      nv_encoder->restore.output);
+
+	nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dac_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
+	.dpms = nv04_dac_dpms,
+	.save = nv04_dac_save,
+	.restore = nv04_dac_restore,
+	.mode_fixup = nv04_dac_mode_fixup,
+	.prepare = nv04_dac_prepare,
+	.commit = nv04_dac_commit,
+	.mode_set = nv04_dac_mode_set,
+	.detect = nv04_dac_detect
+};
+
+static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
+	.dpms = nv04_dac_dpms,
+	.save = nv04_dac_save,
+	.restore = nv04_dac_restore,
+	.mode_fixup = nv04_dac_mode_fixup,
+	.prepare = nv04_dac_prepare,
+	.commit = nv04_dac_commit,
+	.mode_set = nv04_dac_mode_set,
+	.detect = nv17_dac_detect
+};
+
+static const struct drm_encoder_funcs nv04_dac_funcs = {
+	.destroy = nv04_dac_destroy,
+};
+
+int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	const struct drm_encoder_helper_funcs *helper;
+	struct drm_encoder *encoder;
+	struct nouveau_encoder *nv_encoder = NULL;
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	if (nv_gf4_disp_arch(dev))
+		helper = &nv17_dac_helper_funcs;
+	else
+		helper = &nv04_dac_helper_funcs;
+
+	drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
+	drm_encoder_helper_add(encoder, helper);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
new file mode 100644
index 0000000..7913e5f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * 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.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+#define FP_TG_CONTROL_ON  (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |		\
+			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
+#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
+
+static inline bool is_fpc_off(uint32_t fpc)
+{
+	return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
+			FP_TG_CONTROL_OFF);
+}
+
+int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
+{
+	/* special case of nv_read_tmds to find crtc associated with an output.
+	 * this does not give a correct answer for off-chip dvi, but there's no
+	 * use for such an answer anyway
+	 */
+	int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+
+	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
+	NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
+	return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac;
+}
+
+void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+			int head, bool dl)
+{
+	/* The BIOS scripts don't do this for us, sadly
+	 * Luckily we do know the values ;-)
+	 *
+	 * head < 0 indicates we wish to force a setting with the overrideval
+	 * (for VT restore etc.)
+	 */
+
+	int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+	uint8_t tmds04 = 0x80;
+
+	if (head != ramdac)
+		tmds04 = 0x88;
+
+	if (dcbent->type == OUTPUT_LVDS)
+		tmds04 |= 0x01;
+
+	nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
+
+	if (dl)	/* dual link */
+		nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
+}
+
+void nv04_dfp_disable(struct drm_device *dev, int head)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+	if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
+	    FP_TG_CONTROL_ON) {
+		/* digital remnants must be cleaned before new crtc
+		 * values programmed.  delay is time for the vga stuff
+		 * to realise it's in control again
+		 */
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
+			      FP_TG_CONTROL_OFF);
+		msleep(50);
+	}
+	/* don't inadvertently turn it on when state written later */
+	crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
+}
+
+void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct nouveau_crtc *nv_crtc;
+	uint32_t *fpc;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		nv_crtc = nouveau_crtc(encoder->crtc);
+		fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+		if (is_fpc_off(*fpc)) {
+			/* using saved value is ok, as (is_digital && dpms_on &&
+			 * fp_control==OFF) is (at present) *only* true when
+			 * fpc's most recent change was by below "off" code
+			 */
+			*fpc = nv_crtc->dpms_saved_fp_control;
+		}
+
+		nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
+		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+	} else {
+		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+			nv_crtc = nouveau_crtc(crtc);
+			fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+			nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
+			if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
+				nv_crtc->dpms_saved_fp_control = *fpc;
+				/* cut the FP output */
+				*fpc &= ~FP_TG_CONTROL_ON;
+				*fpc |= FP_TG_CONTROL_OFF;
+				NVWriteRAMDAC(dev, nv_crtc->index,
+					      NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+			}
+		}
+	}
+}
+
+static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
+
+	/* For internal panels and gpu scaling on DVI we need the native mode */
+	if (nv_connector->scaling_mode != DRM_MODE_SCALE_NON_GPU) {
+		nv_encoder->mode = *nv_connector->native_mode;
+		adjusted_mode->clock = nv_connector->native_mode->clock;
+	} else {
+		nv_encoder->mode = *adjusted_mode;
+	}
+
+	return true;
+}
+
+static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
+				     struct nouveau_encoder *nv_encoder, int head)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
+
+	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
+		return;
+
+	/* SEL_CLK is only used on the primary ramdac
+	 * It toggles spread spectrum PLL output and sets the bindings of PLLs
+	 * to heads on digital outputs
+	 */
+	if (head)
+		state->sel_clk |= bits1618;
+	else
+		state->sel_clk &= ~bits1618;
+
+	/* nv30:
+	 *	bit 0		NVClk spread spectrum on/off
+	 *	bit 2		MemClk spread spectrum on/off
+	 * 	bit 4		PixClk1 spread spectrum on/off toggle
+	 * 	bit 6		PixClk2 spread spectrum on/off toggle
+	 *
+	 * nv40 (observations from bios behaviour and mmio traces):
+	 * 	bits 4&6	as for nv30
+	 * 	bits 5&7	head dependent as for bits 4&6, but do not appear with 4&6;
+	 * 			maybe a different spread mode
+	 * 	bits 8&10	seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
+	 * 	The logic behind turning spread spectrum on/off in the first place,
+	 * 	and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
+	 * 	entry has the necessary info)
+	 */
+	if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
+		int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
+
+		state->sel_clk &= ~0xf0;
+		state->sel_clk |= (head ? 0x40 : 0x10) << shift;
+	}
+}
+
+static void nv04_dfp_prepare(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+	uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
+	uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
+
+	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+	 * at LCD__INDEX which we don't alter
+	 */
+	if (!(*cr_lcd & 0x44)) {
+		*cr_lcd = 0x3;
+
+		if (nv_two_heads(dev)) {
+			if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
+				*cr_lcd |= head ? 0x0 : 0x8;
+			else {
+				*cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
+				if (nv_encoder->dcb->type == OUTPUT_LVDS)
+					*cr_lcd |= 0x30;
+				if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
+					/* avoid being connected to both crtcs */
+					*cr_lcd_oth &= ~0x30;
+					NVWriteVgaCrtc(dev, head ^ 1,
+						       NV_CIO_CRE_LCD__INDEX,
+						       *cr_lcd_oth);
+				}
+			}
+		}
+	}
+}
+
+
+static void nv04_dfp_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
+	struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_display_mode *output_mode = &nv_encoder->mode;
+	uint32_t mode_ratio, panel_ratio;
+
+	NV_DEBUG(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
+	drm_mode_debug_printmodeline(output_mode);
+
+	/* Initialize the FP registers in this CRTC. */
+	regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
+	regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+	if (!nv_gf4_disp_arch(dev) ||
+	    (output_mode->hsync_start - output_mode->hdisplay) >=
+					dev_priv->vbios->digital_min_front_porch)
+		regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
+	else
+		regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
+	regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
+	regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+	regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
+	regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
+
+	regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
+	regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
+	regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
+	regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
+	regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
+	regp->fp_vert_regs[FP_VALID_START] = 0;
+	regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
+
+	/* bit26: a bit seen on some g7x, no as yet discernable purpose */
+	regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
+			   (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
+	/* Deal with vsync/hsync polarity */
+	/* LVDS screens do set this, but modes with +ve syncs are very rare */
+	if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
+	if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
+	/* panel scaling first, as native would get set otherwise */
+	if (nv_connector->scaling_mode == DRM_MODE_SCALE_NON_GPU ||
+	    nv_connector->scaling_mode == DRM_MODE_SCALE_NO_SCALE)	/* panel handles it */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
+	else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
+		 adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
+	else /* gpu needs to scale */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
+	if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
+	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
+	    nv_connector->native_mode->clock > 165000)
+		regp->fp_control |= (2 << 24);
+	if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+		bool duallink, dummy;
+
+		nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
+					      clock, &duallink, &dummy);
+		if (duallink)
+			regp->fp_control |= (8 << 28);
+	} else
+	if (nv_connector->native_mode->clock > 165000)
+		regp->fp_control |= (8 << 28);
+
+	regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
+			   NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
+			   NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
+			   NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
+			   NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
+			   NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
+			   NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
+
+	/* We want automatic scaling */
+	regp->fp_debug_1 = 0;
+	/* This can override HTOTAL and VTOTAL */
+	regp->fp_debug_2 = 0;
+
+	/* Use 20.12 fixed point format to avoid floats */
+	mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
+	panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay;
+	/* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
+	 * get treated the same as SCALE_FULLSCREEN */
+	if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
+	    mode_ratio != panel_ratio) {
+		uint32_t diff, scale;
+		bool divide_by_2 = nv_gf4_disp_arch(dev);
+
+		if (mode_ratio < panel_ratio) {
+			/* vertical needs to expand to glass size (automatic)
+			 * horizontal needs to be scaled at vertical scale factor
+			 * to maintain aspect */
+
+			scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay;
+			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
+					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
+
+			/* restrict area of screen used, horizontally */
+			diff = output_mode->hdisplay -
+			       output_mode->vdisplay * mode_ratio / (1 << 12);
+			regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
+			regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
+		}
+
+		if (mode_ratio > panel_ratio) {
+			/* horizontal needs to expand to glass size (automatic)
+			 * vertical needs to be scaled at horizontal scale factor
+			 * to maintain aspect */
+
+			scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay;
+			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
+					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
+
+			/* restrict area of screen used, vertically */
+			diff = output_mode->vdisplay -
+			       (1 << 12) * output_mode->hdisplay / mode_ratio;
+			regp->fp_vert_regs[FP_VALID_START] += diff / 2;
+			regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
+		}
+	}
+
+	/* Output property. */
+	if (nv_connector->use_dithering) {
+		if (dev_priv->chipset == 0x11)
+			regp->dither = savep->dither | 0x00010000;
+		else {
+			int i;
+			regp->dither = savep->dither | 0x00000001;
+			for (i = 0; i < 3; i++) {
+				regp->dither_regs[i] = 0xe4e4e4e4;
+				regp->dither_regs[i + 3] = 0x44444444;
+			}
+		}
+	} else {
+		if (dev_priv->chipset != 0x11) {
+			/* reset them */
+			int i;
+			for (i = 0; i < 3; i++) {
+				regp->dither_regs[i] = savep->dither_regs[i];
+				regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
+			}
+		}
+		regp->dither = savep->dither;
+	}
+
+	regp->fp_margin_color = 0;
+}
+
+static void nv04_dfp_commit(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct dcb_entry *dcbe = nv_encoder->dcb;
+	int head = nouveau_crtc(encoder->crtc)->index;
+
+	NV_TRACE(dev, "%s called for encoder %d\n", __func__, nv_encoder->dcb->index);
+
+	if (dcbe->type == OUTPUT_TMDS)
+		run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
+	else if (dcbe->type == OUTPUT_LVDS)
+		call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
+
+	/* update fp_control state for any changes made by scripts,
+	 * so correct value is written at DPMS on */
+	dev_priv->mode_reg.crtc_reg[head].fp_control =
+		NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
+
+	/* This could use refinement for flatpanels, but it should work this way */
+	if (dev_priv->chipset < 0x44)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
+	else
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+static inline bool is_powersaving_dpms(int mode)
+{
+	return (mode != DRM_MODE_DPMS_ON);
+}
+
+static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	if (was_powersaving && is_powersaving_dpms(mode))
+		return;
+
+	if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
+		struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
+
+		/* when removing an output, crtc may not be set, but PANEL_OFF
+		 * must still be run
+		 */
+		int head = crtc ? nouveau_crtc(crtc)->index :
+			   nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
+
+		if (mode == DRM_MODE_DPMS_ON)
+			call_lvds_script(dev, nv_encoder->dcb, head,
+					 LVDS_PANEL_ON, nv_connector->native_mode->clock);
+		else
+			/* pxclk of 0 is fine for PANEL_OFF, and for a
+			 * disconnected LVDS encoder there is no native_mode
+			 */
+			call_lvds_script(dev, nv_encoder->dcb, head,
+					 LVDS_PANEL_OFF, 0);
+	}
+
+	nv04_dfp_update_fp_control(encoder, mode);
+
+	if (mode == DRM_MODE_DPMS_ON)
+		nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
+	else {
+		dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
+		dev_priv->mode_reg.sel_clk &= ~0xf0;
+	}
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
+}
+
+static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	nv04_dfp_update_fp_control(encoder, mode);
+}
+
+static void nv04_dfp_save(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_two_heads(dev))
+		nv_encoder->restore.head =
+			nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
+}
+
+static void nv04_dfp_restore(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nv_encoder->restore.head;
+
+	if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+		call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
+				 nouveau_encoder_connector_get(nv_encoder)->native_mode->clock);
+
+	} else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
+		int clock = nouveau_hw_pllvals_to_clk
+					(&dev_priv->saved_reg.crtc_reg[head].pllvals);
+
+		run_tmds_table(dev, nv_encoder->dcb, head, clock);
+	}
+
+	nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dfp_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
+	.dpms = nv04_lvds_dpms,
+	.save = nv04_dfp_save,
+	.restore = nv04_dfp_restore,
+	.mode_fixup = nv04_dfp_mode_fixup,
+	.prepare = nv04_dfp_prepare,
+	.commit = nv04_dfp_commit,
+	.mode_set = nv04_dfp_mode_set,
+	.detect = NULL,
+};
+
+static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
+	.dpms = nv04_tmds_dpms,
+	.save = nv04_dfp_save,
+	.restore = nv04_dfp_restore,
+	.mode_fixup = nv04_dfp_mode_fixup,
+	.prepare = nv04_dfp_prepare,
+	.commit = nv04_dfp_commit,
+	.mode_set = nv04_dfp_mode_set,
+	.detect = NULL,
+};
+
+static const struct drm_encoder_funcs nv04_dfp_funcs = {
+	.destroy = nv04_dfp_destroy,
+};
+
+int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	const struct drm_encoder_helper_funcs *helper;
+	struct drm_encoder *encoder;
+	struct nouveau_encoder *nv_encoder = NULL;
+	int type;
+
+	switch (entry->type) {
+	case OUTPUT_TMDS:
+		type = DRM_MODE_ENCODER_TMDS;
+		helper = &nv04_tmds_helper_funcs;
+		break;
+	case OUTPUT_LVDS:
+		type = DRM_MODE_ENCODER_LVDS;
+		helper = &nv04_lvds_helper_funcs;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
+	drm_encoder_helper_add(encoder, helper);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
index ee50e34..4826e13 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -96,7 +96,7 @@ nv04_display_create(struct drm_device *dev)
 	struct drm_encoder *encoder;
 	struct drm_crtc *crtc;
 	uint16_t connectors[16] = { 0 };
-	int i;
+	int i, ret;
 
 	NV_DEBUG(dev, "\n");
 
@@ -130,16 +130,25 @@ nv04_display_create(struct drm_device *dev)
 	for (i = 0; i < dcb->entries; i++) {
 		struct dcb_entry *dcbent = &dcb->entry[i];
 
-		if (dcbent->type == OUTPUT_TV)
+		switch (dcbent->type) {
+		case OUTPUT_ANALOG:
+			ret = nv04_dac_create(dev, dcbent);
+			break;
+		case OUTPUT_LVDS:
+		case OUTPUT_TMDS:
+			ret = nv04_dfp_create(dev, dcbent);
+			break;
+		case OUTPUT_TV:
 			continue;
-		if (dcbent->type > 3) {
+		default:
 			NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
 			continue;
 		}
 
-		connectors[dcbent->i2c_index] |= 1 << i;
+		if (ret)
+			continue;
 
-		nv04_encoder_create(dev, dcbent);
+		connectors[dcbent->i2c_index] |= 1 << i;
 	}
 
 	for (i = 0; i < dcb->entries; i++) {
@@ -171,13 +180,21 @@ nv04_display_create(struct drm_device *dev)
 			    dev_priv->vbios->fp_no_ddc)
 				i2c_index = 0xf;
 			break;
+		case OUTPUT_TV:
+			type = DRM_MODE_CONNECTOR_TV;
+			break;
 		default:
 			type = DRM_MODE_CONNECTOR_Unknown;
 			continue;
 		}
 
+		if (i2c_index == 0xf)
+			encoders = 1 << dcbent->index; /* allow multiple connectors with the
+							  same dummy i2c index */
+		else
+			connectors[i2c_index] = 0; /* avoid connectors being added multiply */
+
 		nouveau_connector_create(dev, i2c_index, type);
-		connectors[i2c_index] = 0; /* avoid connectors being added multiply */
 	}
 
 	/* Save previous state */
diff --git a/drivers/gpu/drm/nouveau/nv04_output.c b/drivers/gpu/drm/nouveau/nv04_output.c
deleted file mode 100644
index ba57dcf..0000000
--- a/drivers/gpu/drm/nouveau/nv04_output.c
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
- * Copyright 2003 NVIDIA, Corporation
- * Copyright 2006 Dave Airlie
- * Copyright 2007 Maarten Maathuis
- * Copyright 2007-2009 Stuart Bennett
- *
- * 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.
- */
-
-#include "drmP.h"
-#include "drm_crtc_helper.h"
-
-#include "nouveau_drv.h"
-#include "nouveau_encoder.h"
-#include "nouveau_connector.h"
-#include "nouveau_crtc.h"
-#include "nouveau_hw.h"
-#include "nvreg.h"
-
-static int
-nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder)
-{
-	int offset = 0;
-
-	if (nv_encoder->dcb->or & (8 | OUTPUT_C))
-		offset += 0x68;
-	if (nv_encoder->dcb->or & (8 | OUTPUT_B))
-		offset += 0x2000;
-
-	return offset;
-}
-
-static int
-nv_get_digital_bound_head(struct drm_device *dev, int or)
-{
-	/* special case of nv_read_tmds to find crtc associated with an output.
-	 * this does not give a correct answer for off-chip dvi, but there's no
-	 * use for such an answer anyway
-	 */
-	int ramdac = (or & OUTPUT_C) >> 2;
-
-	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
-	NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
-	return (((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac);
-}
-
-/*
- * arbitrary limit to number of sense oscillations tolerated in one sample
- * period (observed to be at least 13 in "nvidia")
- */
-#define MAX_HBLANK_OSC 20
-
-/*
- * arbitrary limit to number of conflicting sample pairs to tolerate at a
- * voltage step (observed to be at least 5 in "nvidia")
- */
-#define MAX_SAMPLE_PAIRS 10
-
-static int sample_load_twice(struct drm_device *dev, bool sense[2])
-{
-	int i;
-
-	for (i = 0; i < 2; i++) {
-		bool sense_a, sense_b, sense_b_prime;
-		int j = 0;
-
-		/*
-		 * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
-		 * then wait for transition 0x4->0x5->0x4: enter hblank, leave
-		 * hblank again
-		 * use a 10ms timeout (guards against crtc being inactive, in
-		 * which case blank state would never change)
-		 */
-		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
-					0x00000001, 0x00000000))
-			return -EBUSY;
-		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
-					0x00000001, 0x00000001))
-			return -EBUSY;
-		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
-					0x00000001, 0x00000000))
-			return -EBUSY;
-
-		udelay(100);
-		/* when level triggers, sense is _LO_ */
-		sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
-
-		/* take another reading until it agrees with sense_a... */
-		do {
-			udelay(100);
-			sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
-			if (sense_a != sense_b) {
-				sense_b_prime =
-					nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
-				if (sense_b == sense_b_prime) {
-					/* ... unless two consecutive subsequent
-					 * samples agree; sense_a is replaced */
-					sense_a = sense_b;
-					/* force mis-match so we loop */
-					sense_b = !sense_a;
-				}
-			}
-		} while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
-
-		if (j == MAX_HBLANK_OSC)
-			/* with so much oscillation, default to sense:LO */
-			sense[i] = false;
-		else
-			sense[i] = sense_a;
-	}
-
-	return 0;
-}
-
-static enum drm_connector_status
-nv04_dac_load_detect(struct drm_encoder *encoder,
-		     struct drm_connector *connector)
-{
-	struct drm_device *dev = encoder->dev;
-	uint8_t saved_seq1, saved_pi, saved_rpc1;
-	uint8_t saved_palette0[3], saved_palette_mask;
-	uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
-	int i;
-	uint8_t blue;
-	bool sense = true;
-
-	/*
-	 * for this detection to work, there needs to be a mode set up on the
-	 * CRTC.  this is presumed to be the case
-	 */
-
-	if (nv_two_heads(dev))
-		/* only implemented for head A for now */
-		NVSetOwner(dev, 0);
-
-	saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
-	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
-
-	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
-		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
-
-	msleep(10);
-
-	saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
-	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
-		       saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
-	saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
-	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
-
-	nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
-	for (i = 0; i < 3; i++)
-		saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
-	saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
-	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
-
-	saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
-		      (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
-					   NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
-		      NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
-
-	blue = 8;	/* start of test range */
-
-	do {
-		bool sense_pair[2];
-
-		nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
-		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
-		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
-		/* testing blue won't find monochrome monitors.  I don't care */
-		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
-
-		i = 0;
-		/* take sample pairs until both samples in the pair agree */
-		do {
-			if (sample_load_twice(dev, sense_pair))
-				goto out;
-		} while ((sense_pair[0] != sense_pair[1]) &&
-							++i < MAX_SAMPLE_PAIRS);
-
-		if (i == MAX_SAMPLE_PAIRS)
-			/* too much oscillation defaults to LO */
-			sense = false;
-		else
-			sense = sense_pair[0];
-
-	/*
-	 * if sense goes LO before blue ramps to 0x18, monitor is not connected.
-	 * ergo, if blue gets to 0x18, monitor must be connected
-	 */
-	} while (++blue < 0x18 && sense);
-
-out:
-	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
-	nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
-	for (i = 0; i < 3; i++)
-		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
-	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
-	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
-	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
-
-	if (blue == 0x18) {
-		NV_TRACE(dev, "Load detected on head A\n");
-		return connector_status_connected;
-	}
-
-	return connector_status_disconnected;
-}
-
-static enum drm_connector_status
-nv17_dac_load_detect(struct drm_encoder *encoder,
-		     struct drm_connector *connector)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	uint32_t testval, regoffset = nv_output_ramdac_offset(nv_encoder);
-	uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, saved_rtest_ctrl, temp;
-	int head, present = 0;
-
-#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20)
-	testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
-	if (dev_priv->vbios->dactestval)
-		testval = dev_priv->vbios->dactestval;
-
-	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
-		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
-
-	saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
-
-	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
-	if (regoffset == 0x68) {
-		saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
-		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
-	}
-
-	msleep(4);
-
-	saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
-	head = (saved_routput & 0x100) >> 8;
-#if 0
-	/* if there's a spare crtc, using it will minimise flicker for the case
-	 * where the in-use crtc is in use by an off-chip tmds encoder */
-	if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
-		head ^= 1;
-#endif
-	/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
-		      (saved_routput & 0xfffffece) | head << 8);
-	msleep(1);
-
-	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
-
-	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
-		      NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
-	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
-	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
-		      temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
-	msleep(1);
-
-	present = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) &
-			NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
-
-	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
-	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
-		      temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
-	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
-
-	/* bios does something more complex for restoring, but I think this is good enough */
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
-	if (regoffset == 0x68)
-		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
-	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
-
-	if (present) {
-		NV_INFO(dev, "Load detected on output %c\n",
-			     '@' + ffs(nv_encoder->dcb->or));
-		return connector_status_connected;
-	}
-
-	return connector_status_disconnected;
-}
-
-static bool
-nv_output_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
-		     struct drm_display_mode *adjusted_mode)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct nouveau_connector *nv_connector;
-
-	nv_connector = nouveau_encoder_connector_get(nv_encoder);
-	if (!nv_connector)
-		return false;
-
-	/* For internal panels and gpu scaling on DVI we need the native mode */
-	if (nv_encoder->dcb->type == OUTPUT_LVDS ||
-	    (nv_encoder->dcb->type == OUTPUT_TMDS && nv_connector->scaling_mode != DRM_MODE_SCALE_NON_GPU)) {
-		int id = adjusted_mode->base.id;
-		*adjusted_mode = *nv_connector->native_mode;
-		adjusted_mode->base.id = id;
-	}
-
-	return true;
-}
-
-static void
-nv_digital_output_prepare_sel_clk(struct drm_device *dev,
-				  struct nouveau_encoder *nv_encoder, int head)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_mode_state *state = &dev_priv->mode_reg;
-	uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
-
-	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
-		return;
-
-	/* SEL_CLK is only used on the primary ramdac
-	 * It toggles spread spectrum PLL output and sets the bindings of PLLs
-	 * to heads on digital outputs
-	 */
-	if (head)
-		state->sel_clk |= bits1618;
-	else
-		state->sel_clk &= ~bits1618;
-
-	/* nv30:
-	 *	bit 0		NVClk spread spectrum on/off
-	 *	bit 2		MemClk spread spectrum on/off
-	 * 	bit 4		PixClk1 spread spectrum on/off toggle
-	 * 	bit 6		PixClk2 spread spectrum on/off toggle
-	 *
-	 * nv40 (observations from bios behaviour and mmio traces):
-	 * 	bits 4&6	as for nv30
-	 * 	bits 5&7	head dependent as for bits 4&6, but do not appear with 4&6;
-	 * 			maybe a different spread mode
-	 * 	bits 8&10	seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
-	 * 	The logic behind turning spread spectrum on/off in the first place,
-	 * 	and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
-	 * 	entry has the necessary info)
-	 */
-	if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
-		int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
-
-		state->sel_clk &= ~0xf0;
-		state->sel_clk |= (head ? 0x40 : 0x10) << shift;
-	}
-}
-
-#define FP_TG_CONTROL_ON  (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |	\
-			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |		\
-			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
-#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE |	\
-			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE |	\
-			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
-
-static inline bool is_fpc_off(uint32_t fpc)
-{
-	return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
-			FP_TG_CONTROL_OFF);
-}
-
-static void
-nv_output_prepare(struct drm_encoder *encoder)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
-	struct drm_device *dev = encoder->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int head = nouveau_crtc(encoder->crtc)->index;
-	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
-	uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
-	uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
-	bool digital_op = nv_encoder->dcb->type == OUTPUT_LVDS ||
-			  nv_encoder->dcb->type == OUTPUT_TMDS;
-
-	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
-
-	if (nv_encoder->dcb->type == OUTPUT_ANALOG) {
-		if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
-							FP_TG_CONTROL_ON) {
-			/* digital remnants must be cleaned before new crtc
-			 * values programmed.  delay is time for the vga stuff
-			 * to realise it's in control again
-			 */
-			NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
-				      FP_TG_CONTROL_OFF);
-			msleep(50);
-		}
-		/* don't inadvertently turn it on when state written later */
-		crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
-	}
-
-	/* calculate some output specific CRTC regs now, so that they can be
-	 * written in nv_crtc_set_mode
-	 */
-
-	if (digital_op)
-		nv_digital_output_prepare_sel_clk(dev, nv_encoder, head);
-
-	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
-	 * at LCD__INDEX which we don't alter
-	 */
-	if (!(*cr_lcd & 0x44)) {
-		*cr_lcd = digital_op ? 0x3 : 0x0;
-		if (digital_op && nv_two_heads(dev)) {
-			if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
-				*cr_lcd |= head ? 0x0 : 0x8;
-			else {
-				*cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
-				if (nv_encoder->dcb->type == OUTPUT_LVDS)
-					*cr_lcd |= 0x30;
-				if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
-					/* avoid being connected to both crtcs */
-					*cr_lcd_oth &= ~0x30;
-					NVWriteVgaCrtc(dev, head ^ 1,
-						       NV_CIO_CRE_LCD__INDEX,
-						       *cr_lcd_oth);
-				}
-			}
-		}
-	}
-}
-
-static void
-nv_output_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
-		   struct drm_display_mode *adjusted_mode)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct dcb_entry *dcbe = nv_encoder->dcb;
-	int head = nouveau_crtc(encoder->crtc)->index;
-
-	NV_TRACE(dev, "%s called for encoder %d\n", __func__,
-		      nv_encoder->dcb->index);
-
-	if (nv_gf4_disp_arch(dev) && dcbe->type == OUTPUT_ANALOG) {
-		struct drm_encoder *rebind;
-		uint32_t dac_offset = nv_output_ramdac_offset(nv_encoder);
-		uint32_t otherdac;
-
-		/* bit 16-19 are bits that are set on some G70 cards,
-		 * but don't seem to have much effect */
-		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
-			      head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
-		/* force any other vga encoders to bind to the other crtc */
-		list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
-			struct nouveau_encoder *nv_rebind = nouveau_encoder(rebind);
-
-			if (nv_rebind == nv_encoder || nv_rebind->dcb->type != OUTPUT_ANALOG)
-				continue;
-
-			dac_offset = nv_output_ramdac_offset(nv_rebind);
-			otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
-			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
-				      (otherdac & ~0x0100) | (head ^ 1) << 8);
-		}
-	}
-	if (dcbe->type == OUTPUT_TMDS)
-		run_tmds_table(dev, dcbe, head, adjusted_mode->clock);
-	else if (dcbe->type == OUTPUT_LVDS)
-		call_lvds_script(dev, dcbe, head, LVDS_RESET, adjusted_mode->clock);
-	if (dcbe->type == OUTPUT_LVDS || dcbe->type == OUTPUT_TMDS)
-		/* update fp_control state for any changes made by scripts,
-		 * so correct value is written at DPMS on */
-		dev_priv->mode_reg.crtc_reg[head].fp_control =
-			NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
-
-	/* This could use refinement for flatpanels, but it should work this way */
-	if (dev_priv->chipset < 0x44)
-		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0xf0000000);
-	else
-		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0x00100000);
-}
-
-static void
-nv_output_commit(struct drm_encoder *encoder)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
-	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
-
-	helper->dpms(encoder, DRM_MODE_DPMS_ON);
-
-	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
-		      drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
-		      '@' + ffs(nv_encoder->dcb->or));
-}
-
-static void
-dpms_update_fp_control(struct drm_device *dev,
-		       struct nouveau_encoder *nv_encoder, struct drm_crtc * crtc,
-		       int mode)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_crtc *nv_crtc;
-	uint32_t *fpc;
-
-	if (mode == DRM_MODE_DPMS_ON) {
-		nv_crtc = nouveau_crtc(crtc);
-		fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
-
-		if (is_fpc_off(*fpc)) {
-			/* using saved value is ok, as (is_digital && dpms_on &&
-			 * fp_control==OFF) is (at present) *only* true when
-			 * fpc's most recent change was by below "off" code
-			 */
-			*fpc = nv_crtc->dpms_saved_fp_control;
-		}
-
-		nv_crtc->fp_users |= 1 << nv_encoder->dcb->index;
-		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
-	} else {
-		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-			nv_crtc = nouveau_crtc(crtc);
-			fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
-
-			nv_crtc->fp_users &= ~(1 << nv_encoder->dcb->index);
-			if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
-				nv_crtc->dpms_saved_fp_control = *fpc;
-				/* cut the FP output */
-				*fpc &= ~FP_TG_CONTROL_ON;
-				*fpc |= FP_TG_CONTROL_OFF;
-				NVWriteRAMDAC(dev, nv_crtc->index,
-					      NV_PRAMDAC_FP_TG_CONTROL, *fpc);
-			}
-		}
-	}
-}
-
-static inline bool is_powersaving_dpms(int mode)
-{
-	return (mode != DRM_MODE_DPMS_ON);
-}
-
-static void
-lvds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
-		  struct drm_crtc * crtc, int mode)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
-
-	if (nv_encoder->last_dpms == mode)
-		return;
-	nv_encoder->last_dpms = mode;
-
-	NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
-		     mode, nv_encoder->dcb->index);
-
-	if (was_powersaving && is_powersaving_dpms(mode))
-		return;
-
-	if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
-		struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
-
-		/* when removing an output, crtc may not be set, but PANEL_OFF
-		 * must still be run
-		 */
-		int head = crtc ? nouveau_crtc(crtc)->index :
-			   nv_get_digital_bound_head(dev, nv_encoder->dcb->or);
-
-		if (mode == DRM_MODE_DPMS_ON)
-			call_lvds_script(dev, nv_encoder->dcb, head,
-					 LVDS_PANEL_ON, nv_connector->native_mode->clock);
-		else
-			/* pxclk of 0 is fine for PANEL_OFF, and for a
-			 * disconnected LVDS encoder there is no native_mode
-			 */
-			call_lvds_script(dev, nv_encoder->dcb, head,
-					 LVDS_PANEL_OFF, 0);
-	}
-
-	dpms_update_fp_control(dev, nv_encoder, crtc, mode);
-
-	if (mode == DRM_MODE_DPMS_ON)
-		nv_digital_output_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
-	else {
-		dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
-		dev_priv->mode_reg.sel_clk &= ~0xf0;
-	}
-	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
-}
-
-static void
-vga_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
-		 struct drm_crtc * crtc, int mode)
-{
-	if (nv_encoder->last_dpms == mode)
-		return;
-	nv_encoder->last_dpms = mode;
-
-	NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
-		     mode, nv_encoder->dcb->index);
-
-	if (nv_gf4_disp_arch(dev)) {
-		uint32_t outputval = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder));
-
-		if (mode == DRM_MODE_DPMS_OFF)
-			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder),
-				      outputval & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
-		else if (mode == DRM_MODE_DPMS_ON)
-			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder),
-				      outputval | NV_PRAMDAC_DACCLK_SEL_DACCLK);
-	}
-}
-
-static void
-tmds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
-		  struct drm_crtc * crtc, int mode)
-{
-	if (nv_encoder->last_dpms == mode)
-		return;
-	nv_encoder->last_dpms = mode;
-
-	NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
-		     mode, nv_encoder->dcb->index);
-
-	dpms_update_fp_control(dev, nv_encoder, crtc, mode);
-}
-
-static void
-nv_output_dpms(struct drm_encoder *encoder, int mode)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct drm_crtc *crtc = encoder->crtc;
-	void (* const encoder_dpms[4])(struct drm_device *, struct nouveau_encoder *, struct drm_crtc *, int) =
-		/* index matches DCB type */
-		{ vga_encoder_dpms, NULL, tmds_encoder_dpms, lvds_encoder_dpms };
-
-	encoder_dpms[nv_encoder->dcb->type](dev, nv_encoder, crtc, mode);
-}
-
-static void
-nv04_encoder_save(struct drm_encoder *encoder)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-
-	if (!nv_encoder->dcb)	/* uninitialised encoder */
-		return;
-
-	if (nv_gf4_disp_arch(dev) && nv_encoder->dcb->type == OUTPUT_ANALOG)
-		nv_encoder->restore.output =
-			NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
-				nv_output_ramdac_offset(nv_encoder));
-	if (nv_two_heads(dev) && (nv_encoder->dcb->type == OUTPUT_LVDS ||
-			      nv_encoder->dcb->type == OUTPUT_TMDS))
-		nv_encoder->restore.head =
-			nv_get_digital_bound_head(dev, nv_encoder->dcb->or);
-}
-
-static void
-nv04_encoder_restore(struct drm_encoder *encoder)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int head = nv_encoder->restore.head;
-
-	if (!nv_encoder->dcb)	/* uninitialised encoder */
-		return;
-
-	if (nv_gf4_disp_arch(dev) && nv_encoder->dcb->type == OUTPUT_ANALOG)
-		NVWriteRAMDAC(dev, 0,
-			      NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder),
-			      nv_encoder->restore.output);
-	if (nv_encoder->dcb->type == OUTPUT_LVDS)
-		call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
-				 nouveau_encoder_connector_get(nv_encoder)->native_mode->clock);
-	if (nv_encoder->dcb->type == OUTPUT_TMDS) {
-		int clock = nouveau_hw_pllvals_to_clk
-					(&dev_priv->saved_reg.crtc_reg[head].pllvals);
-
-		run_tmds_table(dev, nv_encoder->dcb, head, clock);
-	}
-
-	nv_encoder->last_dpms = NV_DPMS_CLEARED;
-}
-
-static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
-	.dpms = nv_output_dpms,
-	.save = nv04_encoder_save,
-	.restore = nv04_encoder_restore,
-	.mode_fixup = nv_output_mode_fixup,
-	.prepare = nv_output_prepare,
-	.commit = nv_output_commit,
-	.mode_set = nv_output_mode_set,
-	.detect = nv04_dac_load_detect
-};
-
-static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
-	.dpms = nv_output_dpms,
-	.save = nv04_encoder_save,
-	.restore = nv04_encoder_restore,
-	.mode_fixup = nv_output_mode_fixup,
-	.prepare = nv_output_prepare,
-	.commit = nv_output_commit,
-	.mode_set = nv_output_mode_set,
-	.detect = nv17_dac_load_detect
-};
-
-static const struct drm_encoder_helper_funcs nv04_encoder_helper_funcs = {
-	.dpms = nv_output_dpms,
-	.save = nv04_encoder_save,
-	.restore = nv04_encoder_restore,
-	.mode_fixup = nv_output_mode_fixup,
-	.prepare = nv_output_prepare,
-	.commit = nv_output_commit,
-	.mode_set = nv_output_mode_set,
-	.detect = NULL
-};
-
-static void
-nv04_encoder_destroy(struct drm_encoder *encoder)
-{
-	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
-	NV_DEBUG(encoder->dev, "\n");
-
-	drm_encoder_cleanup(encoder);
-	kfree(nv_encoder);
-}
-
-static const struct drm_encoder_funcs nv04_encoder_funcs = {
-	.destroy = nv04_encoder_destroy,
-};
-
-int
-nv04_encoder_create(struct drm_device *dev, struct dcb_entry *entry)
-{
-	const struct drm_encoder_helper_funcs *helper;
-	struct nouveau_encoder *nv_encoder = NULL;
-	int type;
-
-	switch (entry->type) {
-	case OUTPUT_TMDS:
-		type = DRM_MODE_ENCODER_TMDS;
-		break;
-	case OUTPUT_LVDS:
-		type = DRM_MODE_ENCODER_LVDS;
-		break;
-	case OUTPUT_ANALOG:
-		type = DRM_MODE_ENCODER_DAC;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
-	if (!nv_encoder)
-		return -ENOMEM;
-
-	nv_encoder->dcb = entry;
-	nv_encoder->or = ffs(entry->or) - 1;
-
-	if (entry->type == OUTPUT_ANALOG) {
-		if (nv_gf4_disp_arch(dev))
-			helper = &nv17_dac_helper_funcs;
-		else
-			helper = &nv04_dac_helper_funcs;
-	} else
-		helper = &nv04_encoder_helper_funcs;
-
-	drm_encoder_init(dev, to_drm_encoder(nv_encoder), &nv04_encoder_funcs, type);
-	drm_encoder_helper_add(to_drm_encoder(nv_encoder), helper);
-
-	to_drm_encoder(nv_encoder)->possible_crtcs = entry->heads;
-	to_drm_encoder(nv_encoder)->possible_clones = 0;
-
-	return 0;
-}
-
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
index 90623b0..bc960b9 100644
--- a/drivers/gpu/drm/nouveau/nvreg.h
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -316,13 +316,18 @@
 #	define NV30_RAMDAC_ENABLE_VCO2				(8 << 4)
 
 #define NV_PRAMDAC_PLL_COEFF_SELECT			0x0068050c
-#	define NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE		(4 << 0)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE	(4 << 0)
 #	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL	(1 << 8)
 #	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL	(2 << 8)
 #	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL	(4 << 8)
-#	define NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2		(8 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2	(8 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		(1 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1		(2 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2		(4 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2		(8 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP	(1 << 20)
 #	define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2	(1 << 28)
-#	define NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2		(2 << 28)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2	(2 << 28)
 
 #define NV_PRAMDAC_PLL_SETUP_CONTROL			0x00680510
 #define NV_RAMDAC_VPLL2					0x00680520
@@ -384,6 +389,7 @@
 #	define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12		(1 << 24)
 #	define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS		(1 << 28)
 #	define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE		(2 << 28)
+#define NV_PRAMDAC_FP_MARGIN_COLOR			0x0068084c
 #define NV_PRAMDAC_850					0x00680850
 #define NV_PRAMDAC_85C					0x0068085c
 #define NV_PRAMDAC_FP_DEBUG_0				0x00680880
@@ -409,6 +415,8 @@
 #	define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE		(1 << 16)
 #define NV_PRAMDAC_FP_TMDS_DATA				0x006808b4
 
+#define NV_PRAMDAC_8C0                                  0x006808c0
+
 /* Some kind of switch */
 #define NV_PRAMDAC_900					0x00680900
 #define NV_PRAMDAC_A20					0x00680A20
-- 
1.6.3.3



More information about the Nouveau mailing list