WIP: fimd dithering
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt
index a8bbbde..6e4dd36 100644
--- a/Documentation/devicetree/bindings/video/samsung-fimd.txt
+++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt
@@ -41,6 +41,7 @@
 - power-domains: a phandle to FIMD power domain node.
 - samsung,invert-vden: video enable signal is inverted
 - samsung,invert-vclk: video clock signal is inverted
+- samsung,mie-dithering: enable dither for the Mobile Image Enhancement
 - display-timings: timing settings for FIMD, as described in document [1].
 		Can be used in case timings cannot be provided otherwise
 		or to override timings provided by the panel.
diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 0720caa..4535596 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -267,6 +267,7 @@
 &fimd {
 	status = "okay";
 	samsung,invert-vclk;
+	samsung,mie-dithering;
 };
 
 &hdmi {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index bd75c15..7c7b021 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -85,6 +85,9 @@
 #define LCD_WR_HOLD(x)			((x) << 4)
 #define I80IFEN_ENABLE			(1 << 0)
 
+#define NO_DITHERING	0x00
+#define MIE_DITHERING	0x01
+
 /* FIMD has totally five hardware windows. */
 #define WINDOWS_NR	5
 #define CURSOR_WIN	4
@@ -153,12 +156,14 @@
 	struct clk			*bus_clk;
 	struct clk			*lcd_clk;
 	void __iomem			*regs;
+	void __iomem			*regs_mie;
 	struct regmap			*sysreg;
 	unsigned long			irq_flags;
 	u32				vidcon0;
 	u32				vidcon1;
 	u32				vidout_con;
 	u32				i80ifcon;
+	u32				dither_mode;
 	bool				i80_if;
 	bool				suspended;
 	int				pipe;
@@ -376,6 +381,52 @@
 	return (clkdiv < 0x100) ? clkdiv : 0xff;
 }
 
+static void fimd_enable_dithering(struct exynos_drm_crtc *crtc)
+{
+	struct fimd_context *ctx = crtc->ctx;
+	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
+	int bpd, fpd, sync_len, i;
+
+	DRM_DEBUG("first\n");
+	if (ctx->dither_mode == MIE_DITHERING) {
+		writel(DP_MIE_CLK_DP_ENABLE, ctx->regs + DP_MIE_CLKCON);
+
+		writel(MIE_HRESOL(mode->crtc_hdisplay) |
+		       MIE_VRESOL(mode->crtc_vdisplay) | MIE_MODE_UI,
+		       ctx->regs_mie + MIE_CTRL1);
+
+		writel(MIE_WINHADDR0(0) | MIE_WINHADDR1(mode->crtc_hdisplay),
+				ctx->regs_mie + MIE_WINHADDR);
+
+		writel(MIE_WINVADDR0(0) | MIE_WINVADDR1(mode->crtc_vdisplay),
+			ctx->regs_mie + MIE_WINVADDR);
+
+		writel(PWMCLKCNT(mode->crtc_vtotal * mode->crtc_htotal /
+			(MIE_PWMCLKVAL + 1)), ctx->regs_mie + MIE_PWMCLKCNT);
+
+		bpd = mode->crtc_vtotal - mode->crtc_vsync_end;
+		fpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
+		sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+		writel(MIE_VBPD(bpd) | MIE_VFPD(fpd) | MIE_VSPW(sync_len),
+		       ctx->regs_mie + MIE_PWMVIDTCON1);
+
+		bpd = mode->crtc_htotal - mode->crtc_hsync_end;
+		fpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
+		sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+		writel(MIE_HBPD(bpd) | MIE_HFPD(fpd) | MIE_HSPW(sync_len),
+		       ctx->regs_mie + MIE_PWMVIDTCON2);
+
+		writel(MIE_DITHCON_EN | MIE_RGB6MODE,
+		       ctx->regs_mie + MIE_AUXCON);
+
+		/* Bypass MIE image brightness enhancement */
+		for (i = 0; i <= 0x30; i += 4) {
+			writel(0, ctx->regs_mie + 0x100 + i);
+			writel(0, ctx->regs_mie + 0x200 + i);
+		}
+	}
+}
+
 static void fimd_commit(struct exynos_drm_crtc *crtc)
 {
 	struct fimd_context *ctx = crtc->ctx;
@@ -474,6 +525,8 @@
 		val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
 
 	writel(val, ctx->regs + VIDCON0);
+
+	fimd_enable_dithering(crtc);
 }
 
 
@@ -795,6 +848,9 @@
 
 	writel(0, ctx->regs + VIDCON0);
 
+	if (ctx->dither_mode == MIE_DITHERING)
+		writel(DP_MIE_CLK_DISABLE, ctx->regs + DP_MIE_CLKCON);
+
 	clk_disable_unprepare(ctx->lcd_clk);
 	clk_disable_unprepare(ctx->bus_clk);
 
@@ -862,6 +918,10 @@
 	struct fimd_context *ctx = crtc->ctx;
 	u32 val;
 
+	DRM_DEBUG_KMS("first\n");
+
+	return;
+
 	/*
 	 * Only Exynos 5250, 5260, 5410 and 542x requires enabling DP/MIE
 	 * clock. On these SoCs the bootloader may enable it but any
@@ -1018,6 +1078,8 @@
 		ctx->vidcon1 |= VIDCON1_INV_VDEN;
 	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
 		ctx->vidcon1 |= VIDCON1_INV_VCLK;
+	if (of_property_read_bool(dev->of_node, "samsung,mie-dithering"))
+		ctx->dither_mode = MIE_DITHERING;
 
 	i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
 	if (i80_if_timings) {
@@ -1075,6 +1137,12 @@
 	if (IS_ERR(ctx->regs))
 		return PTR_ERR(ctx->regs);
 
+	if (ctx->dither_mode == MIE_DITHERING) {
+		ctx->regs_mie = devm_ioremap(dev, MIE_BASE_ADDRESS, 0x400);
+		if (!ctx->regs_mie)
+			ctx->dither_mode = NO_DITHERING;
+	}
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
 					   ctx->i80_if ? "lcd_sys" : "vsync");
 	if (!res) {
diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
index d8fc96e..9b79ad4 100644
--- a/include/video/samsung_fimd.h
+++ b/include/video/samsung_fimd.h
@@ -475,3 +475,45 @@
 #define FIMD_V8_VIDTCON2	0x20018
 #define FIMD_V8_VIDTCON3	0x2001C
 #define FIMD_V8_VIDCON1		0x20004
+
+/* MIE registers */
+#define MIE_BASE_ADDRESS		0x14430000
+#define MIE_CTRL1			0x0
+#define MIE_HRESOL_SHIFT		18
+#define MIE_HRESOL(x)			((x & 0xfff) << MIE_HRESOL_SHIFT)
+#define MIE_VRESOL_SHIFT		7
+#define MIE_VRESOL(x)			((x & 0x7ff) << MIE_VRESOL_SHIFT)
+#define MIE_MODE_UI			(1 << 5)
+
+#define MIE_WINHADDR			0x10
+#define MIE_WINHADDR0_SHIFT		0
+#define MIE_WINHADDR1_SHIFT		20
+#define MIE_WINHADDR0(x)		((x & 0xfff) << MIE_WINHADDR0_SHIFT)
+#define MIE_WINHADDR1(x)		(((x-1) & 0xfff)\
+						<< MIE_WINHADDR1_SHIFT)
+
+#define MIE_WINVADDR			0x14
+#define MIE_WINVADDR0_SHIFT		0
+#define MIE_WINVADDR1_SHIFT		21
+#define MIE_WINVADDR0(x)		((x & 0x7ff) << MIE_WINVADDR0_SHIFT)
+#define MIE_WINVADDR1(x)		(((x - 1) & 0x7ff)\
+						<< MIE_WINVADDR1_SHIFT)
+
+#define MIE_PWMCLKCNT			0x20
+#define MIE_PWMCLKVAL			1
+#define PWMCLKCNT(x)			((x & 0x3fffff) << 4)
+
+#define MIE_PWMVIDTCON1			0x38
+#define MIE_VBPD(x)			((x - 1) << 16)
+#define MIE_VFPD(x)			((x - 1) << 8)
+#define MIE_VSPW(x)			(x - 1)
+
+#define MIE_PWMVIDTCON2			0x3c
+#define MIE_HBPD(x)			((x - 1) << 16)
+#define MIE_HFPD(x)			((x - 1) << 8)
+#define MIE_HSPW(x)			(x - 1)
+
+#define MIE_AUXCON			0x300
+#define MIE_DITHCON_EN			0x1
+#define MIE_RGBMODE_SHIFT		1
+#define MIE_RGB6MODE			(0 << MIE_RGBMODE_SHIFT)