[Openchrome-devel] drm-openchrome: 3 commits - drivers/gpu/drm

James Simmons jsimmons at kemper.freedesktop.org
Thu Dec 20 16:54:16 PST 2012


 drivers/gpu/drm/ttm/ttm_bo.c       |    2 
 drivers/gpu/drm/via/Makefile       |    2 
 drivers/gpu/drm/via/via_analog.c   |   10 
 drivers/gpu/drm/via/via_crtc.c     |  431 ++++++++++++++++++++-
 drivers/gpu/drm/via/via_disp_reg.h |   20 
 drivers/gpu/drm/via/via_display.c  |   85 ++++
 drivers/gpu/drm/via/via_display.h  |   23 +
 drivers/gpu/drm/via/via_drv.h      |    1 
 drivers/gpu/drm/via/via_lvds.c     |  746 +++++++++++++++++++++++++++++++++++++
 9 files changed, 1289 insertions(+), 31 deletions(-)

New commits:
commit c45cef24757677199a1ae3f77ae4b2f6a76f253e
Merge: 15220435 0953e76
Author: James Simmons <jsimmons at infradead.org>
Date:   Thu Dec 20 19:53:24 2012 -0500

    Merge branch 'drm-next'

commit 15220435da3c2ebfc7c6ac86ba325be86e10e8f3
Author: James Simmons <jsimmons at infradead.org>
Date:   Thu Dec 20 19:51:30 2012 -0500

    Initial LVDS support. You can resize but xrandr scale is still broken. Will fix scaling at a latter time but in general internal LVDS is supported.

diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile
index 6eb45fa..2912e41 100644
--- a/drivers/gpu/drm/via/Makefile
+++ b/drivers/gpu/drm/via/Makefile
@@ -7,6 +7,6 @@ via-y    := via_drv.o via_pm.o via_i2c.o via_irq.o via_verifier.o via_ioc32.o \
 		init_ttm.o ttm_gem.o via_ttm.o via_fb.o via_sgdma.o \
 		via_h1_dma.o via_h1_cmdbuf.o via_video.o \
 		via_display.o via_crtc.o crtc_hw.o via_clocks.o \
-		via_analog.o
+		via_analog.o via_lvds.o
 
 obj-$(CONFIG_DRM_VIA)	+= via.o
diff --git a/drivers/gpu/drm/via/via_analog.c b/drivers/gpu/drm/via/via_analog.c
index 286ec51..1ea889d 100644
--- a/drivers/gpu/drm/via/via_analog.c
+++ b/drivers/gpu/drm/via/via_analog.c
@@ -69,19 +69,11 @@ via_dac_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static void
-via_dac_prepare(struct drm_encoder *encoder)
-{
-	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-
-	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
-}
-
 static const struct drm_encoder_helper_funcs via_dac_enc_helper_funcs = {
 	.dpms = via_dac_dpms,
 	.mode_fixup = via_dac_mode_fixup,
-	.prepare = via_dac_prepare,
 	.mode_set = via_set_sync_polarity,
+	.prepare = via_encoder_prepare,
 	.commit = via_encoder_commit,
 	.disable = via_encoder_disable,
 };
diff --git a/drivers/gpu/drm/via/via_crtc.c b/drivers/gpu/drm/via/via_crtc.c
index 8222ea1..864a69d 100644
--- a/drivers/gpu/drm/via/via_crtc.c
+++ b/drivers/gpu/drm/via/via_crtc.c
@@ -475,6 +475,328 @@ void via_load_crtc_timing(struct via_crtc *iga, struct drm_display_mode *mode)
 	}
 }
 
+/*
+ * This function changes the destination of scaling up/down
+ * and CRTC timing registers
+ * crtc : which IGA
+ * scale_type : upscaling(VIA_EXPAND) or downscaling(VIA_SHRINK)
+ */
+void via_set_scale_path(struct drm_crtc *crtc, u32 scale_type)
+{
+	struct via_crtc *iga = container_of(crtc, struct via_crtc, base);
+	struct drm_via_private *dev_priv = crtc->dev->dev_private;
+	u8 reg_cr_fd = vga_rcrt(VGABASE, 0xFD);
+	struct drm_device *dev = crtc->dev;
+
+	if (!iga->index)
+		/* register reuse: select IGA1 path */
+		reg_cr_fd |= BIT(7);
+	else
+		/* register reuse: select IGA2 path */
+		reg_cr_fd &= ~BIT(7);
+
+	/* only IGA1 up scaling need to clear this bit CRFD.5. */
+	if (dev->pci_device == PCI_DEVICE_ID_VIA_VX900) {
+		if (!iga->index && ((VIA_HOR_EXPAND & scale_type) ||
+			(VIA_VER_EXPAND & scale_type)))
+			reg_cr_fd &= ~BIT(5);
+	}
+
+	/* CRFD.0 = 0 : common IGA2, = 1 : downscaling IGA */
+	switch (scale_type) {
+	case VIA_NO_SCALING:
+	case VIA_EXPAND:
+	case VIA_HOR_EXPAND:
+	case VIA_VER_EXPAND:
+		/* register reuse: as common IGA2 */
+		reg_cr_fd &= ~BIT(0);
+		break;
+
+	case VIA_SHRINK:
+		/* register reuse: as downscaling IGA */
+		reg_cr_fd |= BIT(0);
+		break;
+
+	default:
+		break;
+	}
+	vga_wcrt(VGABASE, 0xFD, reg_cr_fd);
+}
+
+/* disable IGA scaling */
+static void
+via_disable_iga_scaling(struct drm_crtc *crtc)
+{
+	struct via_crtc *iga = container_of(crtc, struct via_crtc, base);
+	struct drm_via_private *dev_priv = crtc->dev->dev_private;
+
+	if (iga->index) {
+		/* IGA2 scalings disable */
+		via_set_scale_path(crtc, VIA_SHRINK);
+		/* disable IGA down scaling and buffer sharing. */
+		svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0));
+		/* Horizontal and Vertical scaling disable */
+		svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3));
+
+		/* Disable scale up as well */
+		via_set_scale_path(crtc, VIA_EXPAND);
+		/* disable IGA up scaling */
+		svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0));
+		/* Horizontal and Vertical scaling disable */
+		svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3));
+	} else {
+		/* IGA1 scalings disable */
+		via_set_scale_path(crtc, VIA_SHRINK);
+		/* disable IGA down scaling and buffer sharing. */
+		svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0));
+		/* Horizontal and Vertical scaling disable */
+		svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3));
+
+		/* Disable scale up as well */
+		via_set_scale_path(crtc, VIA_EXPAND);
+		/* disable IGA up scaling */
+		svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0));
+		/* Horizontal and Vertical scaling disable */
+		svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3));
+	}
+}
+
+/*
+ * Enable IGA scale functions.
+ *
+ * input : iga_path =	IGA1 or IGA2 or
+ *			IGA1+IGA2
+ *
+ * scale_type	=	VIA_HOR_EXPAND or VIA_VER_EXPAND or VIA_EXPAND or
+ *			VIA_SHRINK or VIA_SHRINK + VIA_EXPAND
+ */
+bool
+via_set_iga_scale_function(struct drm_crtc *crtc, u32 scale_type)
+{
+	struct via_crtc *iga = container_of(crtc, struct via_crtc, base);
+	struct drm_via_private *dev_priv = crtc->dev->dev_private;
+
+	if (!(scale_type & (VIA_SHRINK + VIA_EXPAND)))
+		return false;
+
+	if (iga->index) {
+		/* IGA2 scalings enable */
+		if (VIA_SHRINK & scale_type) {
+			via_set_scale_path(crtc, VIA_SHRINK);
+			/* Horizontal and Vertical scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2,
+					BIT(7) | BIT(3), BIT(7) | BIT(3));
+			/* enable IGA down scaling */
+			svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0));
+			/* hor and ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79,
+					BIT(2) | BIT(1), BIT(2) | BIT(1));
+		}
+
+		if (VIA_EXPAND & scale_type) {
+			via_set_scale_path(crtc, VIA_EXPAND);
+			/* enable IGA up scaling */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0));
+		}
+
+		if ((VIA_EXPAND & scale_type) == VIA_EXPAND) {
+			/* Horizontal and Vertical scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3),
+					BIT(7) | BIT(3));
+			/* hor and ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1),
+					BIT(2) | BIT(1));
+		} else if (VIA_HOR_EXPAND & scale_type) {
+			/* Horizontal scaling disable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7));
+			/* hor scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1));
+		} else if (VIA_VER_EXPAND & scale_type) {
+			/* Vertical scaling disable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3));
+			/* ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2));
+		}
+	} else {
+		/* IGA1 scalings enable */
+		if (VIA_SHRINK & scale_type) {
+			via_set_scale_path(crtc, VIA_SHRINK);
+
+			/* Horizontal and Vertical scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2,
+					BIT(7) | BIT(3), BIT(7) | BIT(3));
+			/* enable IGA down scaling */
+			svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0));
+			/* hor and ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79,
+					BIT(2) | BIT(1), BIT(2) | BIT(1));
+		}
+
+		if (VIA_EXPAND & scale_type) {
+			via_set_scale_path(crtc, VIA_EXPAND);
+			/* enable IGA up scaling */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0));
+		}
+
+		if ((VIA_EXPAND & scale_type) == VIA_EXPAND) {
+			/* Horizontal and Vertical scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2,
+					BIT(7) | BIT(3), BIT(7) | BIT(3));
+			/* hor and ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79,
+					BIT(2) | BIT(1), BIT(2) | BIT(1));
+		} else if (VIA_HOR_EXPAND & scale_type) {
+			/* Horizontal scaling disable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7));
+			/* hor scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1));
+		} else if (VIA_VER_EXPAND & scale_type) {
+			/* Vertical scaling disable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3));
+			/* ver scaling : Interpolation */
+			svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2));
+		}
+	}
+	return true;
+}
+
+/*
+ * 1. get scale factors from source and dest H & V size
+ * 2. load scale factors into registers
+ * 3. enable H or V scale ( set CRA2 bit7 or bit3 )
+ */
+bool
+via_load_iga_scale_factor_regs(struct drm_via_private *dev_priv,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode,
+				u32 scale_type, u32 is_hor_or_ver)
+{
+	u32 dst_hor_regs = adjusted_mode->crtc_hdisplay;
+	u32 dst_ver_regs = adjusted_mode->crtc_vdisplay;
+	u32 src_hor_regs = mode->crtc_hdisplay;
+	u32 src_ver_regs = mode->crtc_vdisplay;
+	u32 hor_factor = 0, ver_factor = 0;
+	struct vga_registers reg;
+
+	if ((0 == src_hor_regs) || (0 == src_ver_regs) ||
+		(0 == dst_hor_regs) || (0 == dst_ver_regs))
+		return false;
+
+	if (VIA_EXPAND == scale_type) {
+		if (HOR_SCALE & is_hor_or_ver) {
+			hor_factor = ((src_hor_regs - 1) * 4096) /
+				(dst_hor_regs - 1);
+			reg.count = ARRAY_SIZE(lcd_hor_scaling);
+			reg.regs = lcd_hor_scaling;
+			load_value_to_registers(VGABASE, &reg, hor_factor);
+			/* Horizontal scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7));
+		}
+
+		if (VER_SCALE & is_hor_or_ver) {
+			ver_factor = ((src_ver_regs - 1) * 2048) /
+				(dst_ver_regs - 1);
+			reg.count = ARRAY_SIZE(lcd_ver_scaling);
+			reg.regs = lcd_ver_scaling;
+			load_value_to_registers(VGABASE, &reg, ver_factor);
+			/* Vertical scaling enable */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3));
+		}
+
+	} else if (VIA_SHRINK == scale_type) {
+
+		if (src_hor_regs > dst_hor_regs)
+			hor_factor =
+				((src_hor_regs - dst_hor_regs) * 4096) /
+				dst_hor_regs;
+
+		if (src_ver_regs > dst_ver_regs)
+			ver_factor =
+				((src_ver_regs - dst_ver_regs) * 2048) /
+				dst_ver_regs;
+
+		reg.count = ARRAY_SIZE(lcd_hor_scaling);
+		reg.regs = lcd_hor_scaling;
+		load_value_to_registers(VGABASE, &reg, hor_factor);
+
+		reg.count = ARRAY_SIZE(lcd_ver_scaling);
+		reg.regs = lcd_ver_scaling;
+		load_value_to_registers(VGABASE, &reg, ver_factor);
+
+		/* set buffer sharing enable bit . */
+		if (hor_factor || ver_factor) {
+			if (dst_hor_regs > 1024)
+				svga_wcrt_mask(VGABASE, 0x89,
+					BIT(7), BIT(7));
+			else
+				svga_wcrt_mask(VGABASE, 0x89,
+					0x00, BIT(7));
+		}
+
+		if (hor_factor)
+			/* CRA2[7]:1 Enable Hor scaling
+			CRA2[6]:1 Linear Mode */
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(6),
+				BIT(7) | BIT(6));
+		else
+			svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(7));
+
+		if (ver_factor)
+			svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3));
+		else
+			svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(3));
+	}
+	return true;
+}
+
+void
+via_set_iga2_downscale_source_timing(struct drm_crtc *crtc,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	unsigned int viewx = adjusted_mode->hdisplay, viewy = adjusted_mode->vdisplay;
+	unsigned int srcx = mode->crtc_hdisplay, srcy = mode->crtc_vdisplay;
+	struct via_crtc *iga = container_of(crtc, struct via_crtc, base);
+	struct drm_display_mode *src_timing;
+
+	src_timing = drm_mode_duplicate(crtc->dev, adjusted_mode);
+	/* derived source timing */
+	if (srcx <= viewx) {
+		src_timing->crtc_htotal = adjusted_mode->crtc_htotal;
+		src_timing->crtc_hdisplay = adjusted_mode->crtc_hdisplay;
+	} else {
+		unsigned int htotal = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hdisplay;
+
+		src_timing->crtc_htotal = htotal + srcx;
+		src_timing->crtc_hdisplay = srcx;
+	}
+	src_timing->crtc_hblank_start = src_timing->crtc_hdisplay;
+	src_timing->crtc_hblank_end = src_timing->crtc_htotal;
+	src_timing->crtc_hsync_start = src_timing->crtc_hdisplay + 2;
+	src_timing->crtc_hsync_end = src_timing->crtc_hsync_start + 1;
+
+	if (srcy <= viewy) {
+		src_timing->crtc_vtotal = adjusted_mode->crtc_vtotal;
+		src_timing->crtc_vdisplay = adjusted_mode->crtc_vdisplay;
+	} else {
+		unsigned int vtotal = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vdisplay;
+
+		src_timing->crtc_vtotal = vtotal + srcy;
+		src_timing->crtc_vdisplay = srcy;
+	}
+	src_timing->crtc_vblank_start = src_timing->crtc_vdisplay;
+	src_timing->crtc_vblank_end = src_timing->crtc_vtotal;
+	src_timing->crtc_vsync_start = src_timing->crtc_vdisplay + 2;
+	src_timing->crtc_vsync_end = src_timing->crtc_vsync_start + 1;
+
+	via_set_scale_path(crtc, VIA_NO_SCALING);
+	/* load src timing */
+	via_load_crtc_timing(iga, src_timing);
+
+	/* Cleanup up source timings */
+	drm_mode_destroy(crtc->dev, src_timing);
+}
+
 static void
 drm_mode_crtc_load_lut(struct drm_crtc *crtc)
 {
@@ -585,28 +907,97 @@ via_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 		svga_wcrt_mask(VGABASE, 0x11, 0x00, BIT(6));
 	}
 
-	/* Write CRTC */
-	via_load_crtc_timing(iga, adjusted_mode);
+	/* disable IGA scales first */
+	via_disable_iga_scaling(crtc);
 
-	/* Patch the IGA1 ECK clock */
-	if (!iga->index) {
-		u8 reg_value = 0;
+	/* Load crtc timing and IGA scaling */
+	if (iga->scaling_mode & VIA_SHRINK) {
+		/* I. down scaling */
+		if (iga->index) {
+			/* enable IGA2 down scaling and set Interpolation */
+			via_set_iga_scale_function(crtc, VIA_SHRINK);
+
+			/* load hor and ver downscaling factor */
+			/**
+			 * interlace modes scaling support(example 1080I):
+			 * we should use mode->crtc_vdisplay here,
+			 * because crtc_vdisplay=540, vdisplay=1080,
+			 * we need 540 here, not 1080.
+			 */
+			via_load_iga_scale_factor_regs(dev_priv, mode,
+							adjusted_mode,
+							VIA_SHRINK,
+							HOR_VER_SCALE);
+			/* load src timing to timing registers */
+			/**
+			 * interlace modes scaling support(example 1080I):
+			 * we should use mode->crtc_vdisplay here,
+			 * because crtc_vdisplay=540, vdisplay=1080,
+			 * we need 540 here, not 1080.
+			 */
+			via_set_iga2_downscale_source_timing(crtc, mode,
+								adjusted_mode);
+
+			/* Download dst timing */
+			via_set_scale_path(crtc, VIA_SHRINK);
+			via_load_crtc_timing(iga, adjusted_mode);
+			/* very necessary to set IGA to none scaling status */
+			/* need to fix why so need. */
+			via_set_scale_path(crtc, VIA_NO_SCALING);
+		} else {
+			/* IGA1 downscaling not supported */
+		}
+	} else {
+		/* when not down scaling, we only need load one timing. */
+		via_load_crtc_timing(iga, adjusted_mode);
+
+		/* Patch the IGA1 ECK clock */
+		if (!iga->index) {
+			u8 reg_value = 0;
+
+			switch (adjusted_mode->crtc_htotal % 8) {
+			case 0:
+			default:
+				break;
+			case 2:
+				reg_value = BIT(7);
+				break;
+			case 4:
+				reg_value = BIT(6);
+				break;
+			case 6:
+				reg_value = BIT(3);
+				break;
+			}
+			svga_wcrt_mask(VGABASE, 0x47, reg_value, BIT(7) | BIT(6) | BIT(3));
+		}
 
-		switch (adjusted_mode->crtc_htotal % 8) {
-		case 0:
-		default:
-			break;
-		case 2:
-			reg_value = BIT(7);
-			break;
-		case 4:
-			reg_value = BIT(6);
-			break;
-		case 6:
-			reg_value = BIT(3);
-			break;
+		/* II. up scaling */
+		if (iga->scaling_mode & VIA_EXPAND) {
+			if (iga->index) {
+				/* Horizontal scaling */
+				if (iga->scaling_mode & VIA_HOR_EXPAND) {
+					via_set_iga_scale_function(crtc,
+								VIA_HOR_EXPAND);
+					via_load_iga_scale_factor_regs(dev_priv,
+							mode, adjusted_mode,
+							VIA_EXPAND, HOR_SCALE);
+				}
+
+				/* Vertical scaling */
+				if (iga->scaling_mode & VIA_VER_EXPAND) {
+					via_set_iga_scale_function(crtc,
+								VIA_VER_EXPAND);
+					via_load_iga_scale_factor_regs(dev_priv,
+							mode, adjusted_mode,
+							VIA_EXPAND, VER_SCALE);
+				}
+			} else {
+				/* IGA1 Upscaling not supported */
+			}
+		} else {
+			/* III. no scaling */
 		}
-		svga_wcrt_mask(VGABASE, 0x47, reg_value, BIT(7) | BIT(6) | BIT(3));
 	}
 
 	/* Relock */
@@ -641,6 +1032,8 @@ via_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	if (adjusted_mode->clock) {
 		u32 clock = adjusted_mode->clock * 1000, pll_regs;
 
+		if (iga->scaling_mode & VIA_SHRINK)
+			clock *= 2;
 		pll_regs = via_get_clk_value(crtc->dev, clock);
 		via_set_vclock(crtc, pll_regs);
 	}
diff --git a/drivers/gpu/drm/via/via_disp_reg.h b/drivers/gpu/drm/via/via_disp_reg.h
index 71ccf35..ebe65fe 100644
--- a/drivers/gpu/drm/via/via_disp_reg.h
+++ b/drivers/gpu/drm/via/via_disp_reg.h
@@ -214,6 +214,24 @@ static struct vga_regset iga2_fetch_count[] __devinitdata = {
 	{ VGA_CRT_IC, 0x67, 2, 3 }
 };
 
+/************************************************/
+/*********** IGA Scaling Factor Registers *******/
+/************************************************/
+#define LCD_HOR_SCALE_FACTOR_FORMULA(x, y)	(((x - 1) * 4096) / (y - 1))
+#define LCD_VER_SCALE_FACTOR_FORMULA(x, y)	(((x - 1) * 2048) / (y - 1))
+
+static struct vga_regset lcd_hor_scaling[] = {
+	{ VGA_CRT_IC, 0x9F, 0, 1 },
+	{ VGA_CRT_IC, 0x77, 0, 7 },
+	{ VGA_CRT_IC, 0x79, 4, 5 }
+};
+
+static struct vga_regset lcd_ver_scaling[] = {
+	{ VGA_CRT_IC, 0x79, 3, 3 },
+	{ VGA_CRT_IC, 0x78, 0, 7 },
+	{ VGA_CRT_IC, 0x79, 6, 7 }
+};
+
 /***********************************************/
 /*********** CRTC timing register **************/
 /***********************************************/
@@ -269,7 +287,7 @@ static struct vga_regset iga2_fetch_count[] __devinitdata = {
 #define IGA1_PIXELTIMING_VER_SYNC_START_FORMULA(x)	(x - 1)
 #define IGA1_PIXELTIMING_VER_SYNC_END_FORMULA(x)	(x - 1)
 
-#define IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA(x,y)	\
+#define IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA(x, y)	\
 	((x / 2) - 1 - (x - y))
 
 /************************************************/
diff --git a/drivers/gpu/drm/via/via_display.c b/drivers/gpu/drm/via/via_display.c
index 2446aa6..6b257c6 100644
--- a/drivers/gpu/drm/via/via_display.c
+++ b/drivers/gpu/drm/via/via_display.c
@@ -244,6 +244,14 @@ via_set_sync_polarity(struct drm_encoder *encoder, struct drm_display_mode *mode
 	}
 }
 
+void
+via_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
 struct drm_encoder *
 via_best_encoder(struct drm_connector *connector)
 {
@@ -281,6 +289,61 @@ via_connector_destroy(struct drm_connector *connector)
 	kfree(con);
 }
 
+/* Power sequence relations */
+struct td_timer {
+	struct vga_regset tdRegs[2];
+};
+
+static struct td_timer td_timer_regs[] = {
+	/* td_timer0 */
+	{ { { VGA_CRT_IC, 0x8B, 0, 7 }, { VGA_CRT_IC, 0x8F, 0, 3 } } },
+	/* td_timer1 */
+	{ { { VGA_CRT_IC, 0x8C, 0, 7 }, { VGA_CRT_IC, 0x8F, 4, 7 } } },
+	/* td_timer2 */
+	{ { { VGA_CRT_IC, 0x8D, 0, 7 }, { VGA_CRT_IC, 0x90, 0, 3 } } },
+	/* td_timer3 */
+	{ { { VGA_CRT_IC, 0x8E, 0, 7 }, { VGA_CRT_IC, 0x90, 4, 7 } } }
+};
+
+/*
+ * Function Name:  via_init_td_timing_regs
+ * Description: Init TD timing register (power sequence)
+ */
+static void
+via_init_td_timing_regs(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	unsigned int td_timer[4] = { 500, 50, 0, 510 }, i;
+	struct vga_registers timings;
+	u32 reg_value;
+
+	/* Fill primary power sequence */
+	for (i = 0; i < 4; i++) {
+		/* Calculate TD Timer, every step is 572.1uSec */
+		reg_value = td_timer[i] * 10000 / 5721;
+
+		timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs);
+		timings.regs = td_timer_regs[i].tdRegs;
+		load_value_to_registers(VGABASE, &timings, reg_value);
+	}
+
+	/* Note: VT3353 have two hardware power sequences
+	 * other chips only have one hardware power sequence */
+	if (dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) {
+		/* set CRD4[0] to "1" to select 2nd LCD power sequence. */
+		svga_wcrt_mask(VGABASE, 0xD4, BIT(0), BIT(0));
+		/* Fill secondary power sequence */
+		for (i = 0; i < 4; i++) {
+			/* Calculate TD Timer, every step is 572.1uSec */
+			reg_value = td_timer[i] * 10000 / 5721;
+
+			timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs);
+			timings.regs = td_timer_regs[i].tdRegs;
+			load_value_to_registers(VGABASE, &timings, reg_value);
+		}
+	}
+}
+
 static void
 via_i2c_reg_init(struct drm_via_private *dev_priv)
 {
@@ -355,10 +418,30 @@ static void
 via_display_init(struct drm_device *dev)
 {
 	struct drm_via_private *dev_priv = dev->dev_private;
+	u8 index = 0x3D, value;
+
+	/* Check if spread spectrum is enable */
+	if (dev->pci_device == PCI_DEVICE_ID_VIA_VX900)
+		index = 0x2C;
+
+	value = vga_rseq(VGABASE, 0x1E);
+	if (value & BIT(3)) {
+		value = vga_rseq(VGABASE, index);
+		vga_wseq(VGABASE, index, value & 0xFE);
+
+		value = vga_rseq(VGABASE, 0x1E);
+		vga_wseq(VGABASE, 0x1E, value & 0xF7);
+
+		dev_priv->spread_spectrum = true;
+	} else
+		dev_priv->spread_spectrum = false;
 
 	/* load fixed CRTC timing registers */
 	via_init_crtc_regs(dev);
 
+	/* 3. Init TD timing register (power sequence) */
+	via_init_td_timing_regs(dev);
+
 	/* I/O address bit to be 1. Enable access */
 	/* to frame buffer at A0000-BFFFFh */
 	svga_wmisc_mask(VGABASE, BIT(0), BIT(0));
@@ -392,6 +475,8 @@ via_modeset_init(struct drm_device *dev)
 	    (dev_priv->revision == CX700_REVISION_700M))
 		via_analog_init(dev);
 
+	via_lvds_init(dev);
+
 	/*
 	 * Set up the framebuffer device
 	 */
diff --git a/drivers/gpu/drm/via/via_display.h b/drivers/gpu/drm/via/via_display.h
index 6f5128a..f233ad2 100644
--- a/drivers/gpu/drm/via/via_display.h
+++ b/drivers/gpu/drm/via/via_display.h
@@ -30,6 +30,27 @@
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
 
+/* IGA Scaling disable */
+#define VIA_NO_SCALING	0
+
+/* IGA Scaling down */
+#define VIA_HOR_SHRINK	BIT(0)
+#define VIA_VER_SHRINK	BIT(1)
+#define VIA_SHRINK	(BIT(0) | BIT(1))
+
+/* IGA Scaling up */
+#define VIA_HOR_EXPAND	BIT(2)
+#define VIA_VER_EXPAND	BIT(3)
+#define VIA_EXPAND	(BIT(2) | BIT(3))
+
+/* Define IGA Scaling up/down status :  Horizontal or Vertical  */
+/* Is IGA Hor scaling up/down status */
+#define	HOR_SCALE	BIT(0)
+/* Is IGA Ver scaling up/down status */
+#define	VER_SCALE	BIT(1)
+/* Is IGA Hor and Ver scaling up/down status */
+#define	HOR_VER_SCALE	(BIT(0) | BIT(1))
+
 struct via_crtc {
 	struct drm_crtc base;
 	struct ttm_bo_kmap_obj cursor_kmap;
@@ -44,6 +65,7 @@ struct via_crtc {
 	struct vga_registers fifo_depth;
 	struct vga_registers offset;
 	struct vga_registers fetch;
+	int scaling_mode;
 	uint8_t index;
 };
 
@@ -121,6 +143,7 @@ extern void via_set_sync_polarity(struct drm_encoder *encoder,
 				struct drm_display_mode *mode,
 				struct drm_display_mode *adjusted_mode);
 extern struct drm_encoder* via_best_encoder(struct drm_connector *connector);
+extern void via_encoder_prepare(struct drm_encoder *encoder);
 extern void via_encoder_disable(struct drm_encoder *encoder);
 extern void via_encoder_commit(struct drm_encoder *encoder);
 
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h
index b4d4d01..2faa473 100644
--- a/drivers/gpu/drm/via/via_drv.h
+++ b/drivers/gpu/drm/via/via_drv.h
@@ -141,6 +141,7 @@ struct drm_via_private {
 	wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS];
 
 	struct via_crtc iga[2];
+	bool spread_spectrum;
 
 	drm_via_sarea_t *sarea_priv;
 	drm_local_map_t *sarea;
diff --git a/drivers/gpu/drm/via/via_lvds.c b/drivers/gpu/drm/via/via_lvds.c
new file mode 100644
index 0000000..ea5c847
--- /dev/null
+++ b/drivers/gpu/drm/via/via_lvds.c
@@ -0,0 +1,746 @@
+/*
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) 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 <linux/dmi.h>
+#include <asm/olpc.h>
+
+#include "via_drv.h"
+
+/* Encoder flags for LVDS */
+#define LVDS_DUAL_CHANNEL	1
+
+/* caculate the cetering timing using mode and adjusted_mode */
+static void
+via_centering_timing(const struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode)
+{
+	int panel_hsync_time = adjusted_mode->hsync_end -
+		adjusted_mode->hsync_start;
+	int panel_vsync_time = adjusted_mode->vsync_end -
+		adjusted_mode->vsync_start;
+	int panel_hblank_start = adjusted_mode->hdisplay;
+	int panel_vbank_start = adjusted_mode->vdisplay;
+	int hborder = (adjusted_mode->hdisplay - mode->hdisplay) / 2;
+	int vborder = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
+	int new_hblank_start = hborder + mode->hdisplay;
+	int new_vblank_start = vborder + mode->vdisplay;
+
+	adjusted_mode->hdisplay = mode->hdisplay;
+	adjusted_mode->hsync_start = (adjusted_mode->hsync_start -
+		panel_hblank_start) + new_hblank_start;
+	adjusted_mode->hsync_end = adjusted_mode->hsync_start +
+		panel_hsync_time;
+	adjusted_mode->vdisplay = mode->vdisplay;
+	adjusted_mode->vsync_start = (adjusted_mode->vsync_start -
+		panel_vbank_start) + new_vblank_start;
+	adjusted_mode->vsync_end = adjusted_mode->vsync_start +
+		panel_vsync_time;
+	/* Adjust Crtc H and V */
+	adjusted_mode->crtc_hdisplay = adjusted_mode->hdisplay;
+	adjusted_mode->crtc_hblank_start = new_hblank_start;
+	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal - hborder;
+	adjusted_mode->crtc_hsync_start = adjusted_mode->hsync_start;
+	adjusted_mode->crtc_hsync_end = adjusted_mode->hsync_end;
+	adjusted_mode->crtc_vdisplay = adjusted_mode->vdisplay;
+	adjusted_mode->crtc_vblank_start = new_vblank_start;
+	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal - vborder;
+	adjusted_mode->crtc_vsync_start = adjusted_mode->vsync_start;
+	adjusted_mode->crtc_vsync_end = adjusted_mode->vsync_end;
+}
+
+static void
+via_enable_internal_lvds(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+
+	/* Turn on LCD panel */
+	if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
+		/* Software control power sequence */
+		if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) ||
+		    (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) {
+			/* Software control power sequence ON */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7));
+			svga_wcrt_mask(VGABASE, 0x91, BIT(0), BIT(0));
+			/* Delay td0 msec. */
+			mdelay(200);
+			/* VDD ON */
+			svga_wcrt_mask(VGABASE, 0x91, BIT(4), BIT(4));
+			/* Delay td1 msec. */
+			mdelay(25);
+			/* DATA ON */
+			svga_wcrt_mask(VGABASE, 0x91, BIT(3), BIT(3));
+			/* VEE ON (unused on vt3353) */
+			svga_wcrt_mask(VGABASE, 0x91, BIT(2), BIT(2));
+			/* Delay td3 msec. */
+			mdelay(250);
+			/* Back-Light ON */
+			svga_wcrt_mask(VGABASE, 0x91, BIT(1), BIT(1));
+		} else {
+			/* Use first power sequence control: *
+			 * Use hardware control power sequence. */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(0));
+			/* Turn on back light and panel path. */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7) | BIT(6));
+			/* Turn on hardware power sequence. */
+			svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
+		}
+	}
+
+	if (enc->diPort & DISP_DI_DFPH) {
+		if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) ||
+		    (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) {
+			/* Software control power sequence ON */
+			svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
+			svga_wcrt_mask(VGABASE, 0xD3, BIT(0), BIT(0));
+			/* Delay td0 msec. */
+			mdelay(200);
+			/* VDD ON */
+			svga_wcrt_mask(VGABASE, 0xD3, BIT(4), BIT(4));
+			/* Delay td1 msec. */
+			mdelay(25);
+			/* DATA ON */
+			svga_wcrt_mask(VGABASE, 0xD3, BIT(3), BIT(3));
+			/* VEE ON (unused on vt3353) */
+			svga_wcrt_mask(VGABASE, 0xD3, BIT(2), BIT(2));
+			/* Delay td3 msec. */
+			mdelay(250);
+			/* Back-Light ON */
+			svga_wcrt_mask(VGABASE, 0xD3, BIT(1), BIT(1));
+		} else {
+			/* Use hardware control power sequence. */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(0));
+			/* Turn on back light and panel path. */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(7) | BIT(6));
+			/* Turn on hardware power sequence. */
+			svga_wcrt_mask(VGABASE, 0xD4, BIT(1), BIT(1));
+		}
+	}
+
+	/* Power on LVDS channel. */
+	if (enc->flags & LVDS_DUAL_CHANNEL) {
+		/* For high resolution LCD (internal),
+		 * power on both LVDS0 and LVDS1 */
+		svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7) | BIT(6));
+	} else {
+		if (enc->diPort & DISP_DI_DFPL)
+			svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7));
+		else if (enc->diPort & DISP_DI_DFPH)
+			svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(6));
+		else
+			DRM_ERROR("invalid diPort\n");
+	}
+}
+
+static void
+via_disable_internal_lvds(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+
+	/* Turn off LCD panel */
+	if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
+		/* Set LCD software power sequence off */
+		if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) ||
+		    (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) {
+			/* Back-Light OFF */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(1));
+			/* Delay td3 msec. */
+			mdelay(250);
+			/* VEE OFF (unused on vt3353) */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(2));
+			/* DATA OFF */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(3));
+			/* Delay td1 msec. */
+			mdelay(25);
+			/* VDD OFF */
+			svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(4));
+		} else {
+			/* Use first power sequence control: *
+			 * Turn off power sequence. */
+			svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(3));
+
+			/* Turn off back light and panel path. */
+			svga_wcrt_mask(VGABASE, 0x91, 0xC0, BIT(7) | BIT(6));
+		}
+	}
+
+	if (enc->diPort & DISP_DI_DFPH) {
+		/* Set LCD software power sequence off */
+		if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) ||
+		    (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) {
+			/* Back-Light OFF */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(1));
+			/* Delay td3 msec. */
+			mdelay(250);
+			/* VEE OFF */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(2));
+			/* DATA OFF */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(3));
+			/* Delay td1 msec. */
+			mdelay(25);
+			/* VDD OFF */
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(4));
+		} else {
+			/* Use second power sequence control: *
+			 * Turn off power sequence. */
+			svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
+			/* Turn off back light and panel path. */
+			svga_wcrt_mask(VGABASE, 0xD3, 0xC0, BIT(7) | BIT(6));
+		}
+	}
+
+	/* Power off LVDS channel. */
+	if (enc->flags & LVDS_DUAL_CHANNEL) {
+		/* For high resolution LCD (internal) we
+		 * power off both LVDS0 and LVDS1 */
+		svga_wcrt_mask(VGABASE, 0xD2, 0xC0, BIT(7) | BIT(6));
+	} else {
+		if (enc->diPort & DISP_DI_DFPL)
+			svga_wcrt_mask(VGABASE, 0xD2, BIT(7), BIT(7));
+		else if (enc->diPort & DISP_DI_DFPH)
+			svga_wcrt_mask(VGABASE, 0xD2, BIT(6), BIT(6));
+		else
+			DRM_ERROR("invalid diPort\n");
+	}
+}
+
+static void
+via_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct via_crtc *iga = NULL;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (encoder->crtc == NULL)
+			return;
+		iga = container_of(encoder->crtc, struct via_crtc, base);
+
+		/* when using the EPIA-EX board, if we do not set this bit,
+		 * light LCD will failed in nonRandR structure,
+		 * So, when light LCD this bit is always setted */
+		svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
+
+		if (dev_priv->spread_spectrum) {
+			if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) ||
+			    (dev->pci_device == PCI_DEVICE_ID_VIA_VX875) ||
+			    (dev->pci_device == PCI_DEVICE_ID_VIA_VX900)) {
+				/* GPIO-4/5 are used for spread spectrum,
+				 * we must clear SR3D[7:6] to disable
+				 * GPIO-4/5 output */
+				svga_wseq_mask(VGABASE, 0x3D, BIT(0), 0xC1);
+			} else {
+				svga_wseq_mask(VGABASE, 0x2C, BIT(0), BIT(0));
+			}
+			svga_wseq_mask(VGABASE, 0x1E, BIT(3), BIT(3));
+		}
+		via_enable_internal_lvds(encoder);
+		break;
+
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		via_disable_internal_lvds(encoder);
+		break;
+	}
+}
+
+static bool
+via_lvds_mode_fixup(struct drm_encoder *encoder,
+			const struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode)
+{
+	struct drm_property *prop = encoder->dev->mode_config.scaling_mode_property;
+	struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base);
+	struct drm_display_mode *native_mode = NULL, *tmp, *t;
+	struct drm_connector *connector = NULL, *con;
+	u64 scale_mode = DRM_MODE_SCALE_CENTER;
+	struct drm_device *dev = encoder->dev;
+
+	list_for_each_entry(con, &dev->mode_config.connector_list, head) {
+		if (encoder ==  con->encoder) {
+			connector = con;
+			break;
+		}
+	}
+
+	if (!connector) {
+		DRM_INFO("LVDS encoder is not used by any connector\n");
+		return false;
+	}
+
+	list_for_each_entry_safe(tmp, t, &connector->modes, head) {
+		if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) {
+			native_mode = tmp;
+			break;
+		}
+	}
+
+	if (!native_mode) {
+		DRM_INFO("No native mode for LVDS\n");
+		return false;
+	}
+
+	drm_object_property_get_value(&connector->base, prop, &scale_mode);
+
+	if ((mode->hdisplay != native_mode->hdisplay) ||
+	    (mode->vdisplay != native_mode->vdisplay)) {
+		if (scale_mode == DRM_MODE_SCALE_NONE)
+			return false;
+		drm_mode_copy(adjusted_mode, native_mode);
+	}
+	drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+	iga->scaling_mode = VIA_NO_SCALING;
+	/* Take care of 410 downscaling */
+	if ((mode->hdisplay > native_mode->hdisplay) ||
+	    (mode->vdisplay > native_mode->vdisplay)) {
+		iga->scaling_mode = VIA_SHRINK;
+	} else {
+		if (!iga->index || scale_mode == DRM_MODE_SCALE_CENTER) {
+			/* Do centering according to mode and adjusted_mode */
+			via_centering_timing(mode, adjusted_mode);
+		} else {
+			if (mode->hdisplay < native_mode->hdisplay)
+				iga->scaling_mode |= VIA_HOR_EXPAND;
+			if (mode->vdisplay < native_mode->vdisplay)
+				iga->scaling_mode |= VIA_VER_EXPAND;
+		}
+	}
+	return true;
+}
+
+const struct drm_encoder_helper_funcs via_lvds_helper_funcs = {
+	.dpms = via_lvds_dpms,
+	.mode_fixup = via_lvds_mode_fixup,
+	.mode_set = via_set_sync_polarity,
+	.prepare = via_encoder_prepare,
+	.commit = via_encoder_commit,
+	.disable = via_encoder_disable,
+};
+
+const struct drm_encoder_funcs via_lvds_enc_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+/* detect this connector connect status */
+static enum drm_connector_status
+via_lcd_detect(struct drm_connector *connector,  bool force)
+{
+	struct via_connector *con = container_of(connector, struct via_connector, base);
+	struct edid *edid = drm_get_edid(&con->base, con->ddc_bus);
+
+	if (edid) {
+		drm_mode_connector_update_edid_property(&con->base, edid);
+		kfree(edid);
+		return connector_status_connected;
+	} else {
+		if (!list_empty(&connector->probed_modes))
+			return connector_status_connected;
+	}
+	return connector_status_disconnected;
+}
+
+static int
+via_lcd_set_property(struct drm_connector *connector,
+			struct drm_property *property, uint64_t value)
+{
+	struct drm_via_private *dev_priv = connector->dev->dev_private;
+	struct drm_device *dev = connector->dev;
+	uint64_t orig;
+	int ret;
+
+	ret = drm_object_property_get_value(&connector->base, property, &orig);
+	if (!ret && (orig != value)) {
+		if (property == dev->mode_config.dithering_mode_property) {
+			u8 reg_value;
+
+			switch (value) {
+			case DRM_MODE_DITHERING_AUTO:
+			case DRM_MODE_DITHERING_ON:
+				reg_value = BIT(0);
+				break;
+
+			case DRM_MODE_DITHERING_OFF:
+				reg_value = 0x00;
+				break;
+
+			default:
+				return -EINVAL;
+			}
+			svga_wcrt_mask(VGABASE, 0x88, reg_value, BIT(0));
+
+		} else if (property == dev->mode_config.scaling_mode_property) {
+			switch (value) {
+			case DRM_MODE_SCALE_NONE:
+				break;
+
+			case DRM_MODE_SCALE_CENTER:
+				break;
+
+			case DRM_MODE_SCALE_ASPECT:
+				break;
+
+			case DRM_MODE_SCALE_FULLSCREEN:
+				break;
+
+			default:
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
+struct drm_connector_funcs via_lcd_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = via_lcd_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.set_property = via_lcd_set_property,
+	.destroy = via_connector_destroy,
+};
+
+int via_lcd_get_modes(struct drm_connector *connector)
+{
+	int count = via_get_edid_modes(connector);
+
+	if (!count) {
+		struct drm_display_mode *tmp, *t;
+
+		list_for_each_entry_safe(tmp, t, &connector->probed_modes, head)
+			count++;
+	}
+	return count;
+}
+
+int via_lcd_mode_valid(struct drm_connector *connector,
+                struct drm_display_mode *mode)
+{
+	struct drm_property *prop = connector->dev->mode_config.scaling_mode_property;
+	struct drm_display_mode *native_mode = NULL, *tmp, *t;
+	struct drm_device *dev = connector->dev;
+	u64 scale_mode = DRM_MODE_SCALE_CENTER;
+
+	list_for_each_entry_safe(tmp, t, &connector->modes, head) {
+		if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) {
+			native_mode = tmp;
+			break;
+		}
+	}
+
+	drm_object_property_get_value(&connector->base, prop, &scale_mode);
+
+	if ((scale_mode == DRM_MODE_SCALE_NONE) &&
+	    ((mode->hdisplay != native_mode->hdisplay) ||
+	     (mode->vdisplay != native_mode->vdisplay)))
+		return MODE_PANEL;
+
+	/* Don't support mode larger than physical size */
+	if (dev->pci_device != PCI_DEVICE_ID_VIA_VX900) {
+		if (mode->hdisplay > native_mode->hdisplay)
+			return MODE_PANEL;
+		if (mode->vdisplay > native_mode->vdisplay)
+			return MODE_PANEL;
+	} else {
+		/* HW limitation 410 only can
+		 * do <= 1.33 scaling */
+		if (mode->hdisplay * 100 > native_mode->hdisplay * 133)
+			return MODE_PANEL;
+		if (mode->vdisplay * 100 > native_mode->vdisplay * 133)
+			return MODE_PANEL;
+
+		/* Now we can not support H V different scale */
+		if ((mode->hdisplay > native_mode->hdisplay) &&
+		    (mode->vdisplay < native_mode->vdisplay))
+			return MODE_PANEL;
+		if ((mode->hdisplay < native_mode->hdisplay) &&
+		    (mode->vdisplay > native_mode->vdisplay))
+			return MODE_PANEL;
+	}
+	return MODE_OK;
+}
+
+struct drm_connector_helper_funcs via_lcd_connector_helper_funcs = {
+	.get_modes = via_lcd_get_modes,
+	.mode_valid = via_lcd_mode_valid,
+	.best_encoder = via_best_encoder,
+};
+
+static int __init via_ttl_lvds_dmi_callback(const struct dmi_system_id *id)
+{
+	DRM_INFO("LVDS is TTL type for %s\n", id->ident);
+	return 1;
+}
+
+static const struct dmi_system_id via_ttl_lvds[] = {
+	{
+		.callback = via_ttl_lvds_dmi_callback,
+		.ident = "VIA Quanta Netbook",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "QCI"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "VT6413A"),
+		},
+	},
+
+	{ }
+};
+
+void
+via_lvds_init(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	bool dual_channel = false, is_msb = false;
+	uint64_t dither = DRM_MODE_DITHERING_OFF;
+	struct via_connector *con;
+	struct via_encoder *enc;
+	struct edid *edid;
+	u8 reg_value;
+
+	con = kzalloc(sizeof(*con), GFP_KERNEL);
+	if (!con) {
+		DRM_INFO("allocate the connector error\n");
+		return;
+	}
+	con->base.interlace_allowed = false;
+	con->base.doublescan_allowed = false;
+	drm_connector_init(dev, &con->base, &via_lcd_connector_funcs,
+				DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&con->base, &via_lcd_connector_helper_funcs);
+
+	switch (dev->pci_device) {
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900:
+		con->ddc_bus = via_find_ddc_bus(0x2C);
+		break;
+	default:
+		con->ddc_bus = via_find_ddc_bus(0x31);
+		break;
+	}
+
+	edid = drm_get_edid(&con->base, con->ddc_bus);
+	if (!edid) {
+		struct drm_display_mode *native_mode = NULL;
+
+		/* OLPC is very special */
+		if (machine_is_olpc()) {
+			native_mode = drm_mode_create(dev);
+
+			native_mode->clock = 56519;
+			native_mode->hdisplay = 1200;
+			native_mode->hsync_start = 1211;
+			native_mode->hsync_end = 1243;
+			native_mode->htotal = 1264;
+			native_mode->hskew = 0;
+			native_mode->vdisplay = 900;
+			native_mode->vsync_start = 901;
+			native_mode->vsync_end = 911;
+			native_mode->vtotal = 912;
+			native_mode->vscan = 0;
+			native_mode->vrefresh = 50;
+			native_mode->hsync = 0;
+		} else {
+			int hdisplay = 0, vdisplay = 0;
+
+			/* First we have to make sure a LVDS is present */
+			reg_value = (vga_rcrt(VGABASE, 0x3B) & BIT(1));
+			if (!reg_value)
+				goto no_device;
+
+			/* If no edid then we detect the mode using
+			 * the scratch pad registers. */
+			reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F);
+
+			DRM_DEBUG("panel index %x detected\n", reg_value);
+			switch (reg_value) {
+			case 0x00:
+				hdisplay = 640;
+				vdisplay = 480;
+				break;
+
+			case 0x01:
+				hdisplay = 800;
+				vdisplay = 600;
+				break;
+
+			case 0x02:
+				hdisplay = 1024;
+				vdisplay = 768;
+				break;
+
+			case 0x03:
+				hdisplay = 1152;
+				vdisplay = 864;
+				break;
+
+			case 0x04:
+				dual_channel = true;
+				hdisplay = 1280;
+				vdisplay = 1024;
+				break;
+
+			case 0x05:
+				dual_channel = true;
+				hdisplay = 1400;
+				vdisplay = 1050;
+				break;
+
+			case 0x06:
+				dual_channel = true;
+				hdisplay = 1600;
+				vdisplay = 1200;
+				break;
+
+			case 0x08:
+				hdisplay = 800;
+				vdisplay = 480;
+				break;
+
+			case 0x09:
+				dual_channel = true;
+				hdisplay = 1024;
+				vdisplay = 768;
+				break;
+
+			case 0x0A:
+				hdisplay = 1368;
+				vdisplay = 768;
+				break;
+
+			case 0x0B:
+				dual_channel = true;
+				hdisplay = 1024;
+				vdisplay = 768;
+				break;
+
+			case 0x0D:
+				dual_channel = true;
+				hdisplay = 1280;
+				vdisplay = 1024;
+				break;
+
+			case 0x0E:
+				dual_channel = true;
+				hdisplay = 1400;
+				vdisplay = 1050;
+				break;
+
+			case 0x0F:
+				dual_channel = true;
+				hdisplay = 1600;
+				vdisplay = 1200;
+				break;
+
+			default:
+				break;
+			}
+
+			if (hdisplay && vdisplay)
+				native_mode = drm_cvt_mode(dev, hdisplay, vdisplay,
+							60, false, false, false);
+
+			if (reg_value < 0x0A)
+				dither = DRM_MODE_DITHERING_ON;
+		}
+		if (!native_mode)
+			goto no_device;
+
+		native_mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+		drm_mode_set_name(native_mode);
+		drm_mode_probed_add(&con->base, native_mode);
+	} else {
+		drm_mode_connector_update_edid_property(&con->base, edid);
+		kfree(edid);
+
+		/* 00 LVDS1 + LVDS2  10 = Dual channel. Other are reserved */
+		if ((vga_rseq(VGABASE, 0x13) >> 6) == 2)
+			dual_channel = true;
+	}
+
+	drm_mode_create_scaling_mode_property(dev);
+	drm_object_attach_property(&con->base.base,
+					dev->mode_config.scaling_mode_property,
+					DRM_MODE_SCALE_CENTER);
+
+	drm_mode_create_dithering_property(dev);
+	drm_object_attach_property(&con->base.base,
+					dev->mode_config.dithering_mode_property,
+					dither);
+	via_lcd_set_property(&con->base, dev->mode_config.dithering_mode_property,
+				dither);
+
+	drm_sysfs_connector_add(&con->base);
+
+	/* Now setup the encoder */
+	enc = kzalloc(sizeof(*enc), GFP_KERNEL);
+	if (!enc) {
+		DRM_INFO("allocate the encoder error\n");
+		goto no_device;
+	}
+	enc->base.possible_crtcs = BIT(1) | BIT(0);
+
+	switch (dev->pci_device) {
+	case PCI_DEVICE_ID_VIA_CLE266:
+		enc->diPort = DISP_DI_DVP1;
+		break;
+
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900:
+		enc->diPort = DISP_DI_DFPL;
+		break;
+
+	default:
+		enc->diPort = DISP_DI_DFPH;
+		break;
+	}
+
+	/* There has to be a way to detect TTL LVDS
+	 * For now we use the DMI to handle this */
+	if (dmi_check_system(via_ttl_lvds))
+		enc->diPort = DISP_DI_DFPL | DISP_DI_DVP1;
+
+	reg_value = 0x00;
+	if (enc->diPort == DISP_DI_DFPH) {
+		if (!is_msb)
+			reg_value = BIT(0);
+		svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(0));
+	} else if (enc->diPort == DISP_DI_DFPL) {
+		if (!is_msb)
+			reg_value = BIT(1);
+		svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(1));
+	}
+
+	if (dual_channel)
+		enc->flags |= LVDS_DUAL_CHANNEL;
+
+	drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs,
+				DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&con->base, &enc->base);
+	return;
+
+no_device:
+	drm_connector_cleanup(&con->base);
+	kfree(con);
+}
commit 0953e76e91f4b6206cef50bd680696dc6bf1ef99
Author: Maarten Lankhorst <maarten.lankhorst at canonical.com>
Date:   Wed Dec 19 18:21:10 2012 +0100

    drm/ttm: fix delayed ttm_bo_cleanup_refs_and_unlock delayed handling
    
    Fix regression introduced by 85b144f860176
    "drm/ttm: call ttm_bo_cleanup_refs with reservation and lru lock held, v3"
    
    Slowpath ttm_bo_cleanup_refs_and_unlock accidentally tried to increase
    refcount on &bo->sync_obj instead of bo->sync_obj.
    
    The compiler didn't complain since sync_obj_ref takes a void pointer,
    so it was still valid c.
    
    This could result in lockups, memory corruptions, and warnings like
    these when graphics card VRAM usage is high:
    
    ------------[ cut here ]------------
    WARNING: at include/linux/kref.h:42 radeon_fence_ref+0x2c/0x40()
    Hardware name: System Product Name
    Pid: 157, comm: X Not tainted 3.7.0-rc7-00520-g85b144f-dirty #174
    Call Trace:
    [<ffffffff81058c84>] ? warn_slowpath_common+0x74/0xb0
    [<ffffffff8129273c>] ? radeon_fence_ref+0x2c/0x40
    [<ffffffff8125e95c>] ? ttm_bo_cleanup_refs_and_unlock+0x18c/0x2d0
    [<ffffffff8125f17c>] ? ttm_mem_evict_first+0x1dc/0x2a0
    [<ffffffff81264452>] ? ttm_bo_man_get_node+0x62/0xb0
    [<ffffffff8125f4ce>] ? ttm_bo_mem_space+0x28e/0x340
    [<ffffffff8125fb0c>] ? ttm_bo_move_buffer+0xfc/0x170
    [<ffffffff810de172>] ? kmem_cache_alloc+0xb2/0xc0
    [<ffffffff8125fc15>] ? ttm_bo_validate+0x95/0x110
    [<ffffffff8125ff7c>] ? ttm_bo_init+0x2ec/0x3b0
    [<ffffffff8129419a>] ? radeon_bo_create+0x18a/0x200
    [<ffffffff81293e80>] ? radeon_bo_clear_va+0x40/0x40
    [<ffffffff812a5342>] ? radeon_gem_object_create+0x92/0x160
    [<ffffffff812a575c>] ? radeon_gem_create_ioctl+0x6c/0x150
    [<ffffffff812a529f>] ? radeon_gem_object_free+0x2f/0x40
    [<ffffffff81246b60>] ? drm_ioctl+0x420/0x4f0
    [<ffffffff812a56f0>] ? radeon_gem_pwrite_ioctl+0x20/0x20
    [<ffffffff810f53a4>] ? do_vfs_ioctl+0x2e4/0x4e0
    [<ffffffff810e5588>] ? vfs_read+0x118/0x160
    [<ffffffff810f55ec>] ? sys_ioctl+0x4c/0xa0
    [<ffffffff810e5851>] ? sys_read+0x51/0xa0
    [<ffffffff814b0612>] ? system_call_fastpath+0x16/0x1b
    
    Signed-off-by: Maarten Lankhorst <maarten.lankhorst at canonical.com>
    Reported-by: Markus Trippelsdorf <markus at trippelsdorf.de>
    Acked-by: Paul Menzel <paulepanter at users.sourceforge.net>
    Signed-off-by: Dave Airlie <airlied at redhat.com>

diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index a915133..33d20be 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -579,7 +579,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
 		 * at this point the buffer should be dead, so
 		 * no new sync objects can be attached.
 		 */
-		sync_obj = driver->sync_obj_ref(&bo->sync_obj);
+		sync_obj = driver->sync_obj_ref(bo->sync_obj);
 		spin_unlock(&bdev->fence_lock);
 
 		atomic_set(&bo->reserved, 0);


More information about the Openchrome-devel mailing list