diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b16c50ee769c..71ca63b79a4f 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -213,6 +213,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/rcar-du/Kconfig"
+
 source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/omapdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 1ecbe5b7312d..801bcafa3028 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
 obj-$(CONFIG_DRM_TILCDC)	+= tilcdc/
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
new file mode 100644
index 000000000000..72887df8dd76
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -0,0 +1,9 @@
+config DRM_RCAR_DU
+	tristate "DRM Support for R-Car Display Unit"
+	depends on DRM && ARM
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	help
+	  Choose this option if you have an R-Car chipset.
+	  If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
new file mode 100644
index 000000000000..7333c0094015
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -0,0 +1,8 @@
+rcar-du-drm-y := rcar_du_crtc.o \
+		 rcar_du_drv.o \
+		 rcar_du_kms.o \
+		 rcar_du_lvds.o \
+		 rcar_du_plane.o \
+		 rcar_du_vga.o
+
+obj-$(CONFIG_DRM_RCAR_DU)	+= rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
new file mode 100644
index 000000000000..24183fb93592
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -0,0 +1,595 @@
+/*
+ * rcar_du_crtc.c  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+#define to_rcar_crtc(c)	container_of(c, struct rcar_du_crtc, crtc)
+
+static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+}
+
+static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
+}
+
+static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+		      rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
+}
+
+static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+		      rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
+}
+
+static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
+				 u32 clr, u32 set)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+	u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+
+	rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
+}
+
+static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_crtc *crtc = &rcrtc->crtc;
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	const struct drm_display_mode *mode = &crtc->mode;
+	unsigned long clk;
+	u32 value;
+	u32 div;
+
+	/* Dot clock */
+	clk = clk_get_rate(rcdu->clock);
+	div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
+	div = clamp(div, 1U, 64U) - 1;
+
+	rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR,
+		      ESCR_DCLKSEL_CLKS | div);
+	rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0);
+
+	/* Signal polarities */
+	value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
+	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+	      | DSMR_DIPM_DE;
+	rcar_du_crtc_write(rcrtc, DSMR, value);
+
+	/* Display timings */
+	rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
+	rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
+					mode->hdisplay - 19);
+	rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
+					mode->hsync_start - 1);
+	rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
+
+	rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
+	rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
+					mode->vdisplay - 2);
+	rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
+					mode->vsync_start - 1);
+	rcar_du_crtc_write(rcrtc, VCR,  mode->vtotal - 1);
+
+	rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start);
+	rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
+}
+
+static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+	u32 dorcr = rcar_du_read(rcdu, DORCR);
+
+	dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
+
+	/* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
+	 * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
+	 * default.
+	 */
+	if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
+		dorcr |= DORCR_PG2D_DS1;
+	else
+		dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
+
+	rcar_du_write(rcdu, DORCR, dorcr);
+}
+
+static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+	rcar_du_write(rcdu, DSYSR,
+		      (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
+		      (start ? DSYSR_DEN : DSYSR_DRES));
+}
+
+static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+	/* Many of the configuration bits are only updated when the display
+	 * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
+	 * of those bits could be pre-configured, but others (especially the
+	 * bits related to plane assignment to display timing controllers) need
+	 * to be modified at runtime.
+	 *
+	 * Restart the display controller if a start is requested. Sorry for the
+	 * flicker. It should be possible to move most of the "DRES-update" bits
+	 * setup to driver initialization time and minimize the number of cases
+	 * when the display controller will have to be restarted.
+	 */
+	if (start) {
+		if (rcdu->used_crtcs++ != 0)
+			__rcar_du_start_stop(rcdu, false);
+		__rcar_du_start_stop(rcdu, true);
+	} else {
+		if (--rcdu->used_crtcs == 0)
+			__rcar_du_start_stop(rcdu, false);
+	}
+}
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	/* Store the route from the CRTC output to the DU output. The DU will be
+	 * configured when starting the CRTC.
+	 */
+	rcrtc->outputs |= 1 << output;
+}
+
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
+{
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+	struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
+	unsigned int num_planes = 0;
+	unsigned int prio = 0;
+	unsigned int i;
+	u32 dptsr = 0;
+	u32 dspr = 0;
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+		struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+		unsigned int j;
+
+		if (plane->crtc != &rcrtc->crtc || !plane->enabled)
+			continue;
+
+		/* Insert the plane in the sorted planes array. */
+		for (j = num_planes++; j > 0; --j) {
+			if (planes[j-1]->zpos <= plane->zpos)
+				break;
+			planes[j] = planes[j-1];
+		}
+
+		planes[j] = plane;
+		prio += plane->format->planes * 4;
+	}
+
+	for (i = 0; i < num_planes; ++i) {
+		struct rcar_du_plane *plane = planes[i];
+		unsigned int index = plane->hwindex;
+
+		prio -= 4;
+		dspr |= (index + 1) << prio;
+		dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+
+		if (plane->format->planes == 2) {
+			index = (index + 1) % 8;
+
+			prio -= 4;
+			dspr |= (index + 1) << prio;
+			dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+		}
+	}
+
+	/* Select display timing and dot clock generator 2 for planes associated
+	 * with superposition controller 2.
+	 */
+	if (rcrtc->index) {
+		u32 value = rcar_du_read(rcdu, DPTSR);
+
+		/* The DPTSR register is updated when the display controller is
+		 * stopped. We thus need to restart the DU. Once again, sorry
+		 * for the flicker. One way to mitigate the issue would be to
+		 * pre-associate planes with CRTCs (either with a fixed 4/4
+		 * split, or through a module parameter). Flicker would then
+		 * occur only if we need to break the pre-association.
+		 */
+		if (value != dptsr) {
+			rcar_du_write(rcdu, DPTSR, dptsr);
+			if (rcdu->used_crtcs) {
+				__rcar_du_start_stop(rcdu, false);
+				__rcar_du_start_stop(rcdu, true);
+			}
+		}
+	}
+
+	rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
+}
+
+static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_crtc *crtc = &rcrtc->crtc;
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	unsigned int i;
+
+	if (rcrtc->started)
+		return;
+
+	if (WARN_ON(rcrtc->plane->format == NULL))
+		return;
+
+	/* Set display off and background to black */
+	rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
+	rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
+
+	/* Configure display timings and output routing */
+	rcar_du_crtc_set_display_timing(rcrtc);
+	rcar_du_crtc_set_routing(rcrtc);
+
+	mutex_lock(&rcdu->planes.lock);
+	rcrtc->plane->enabled = true;
+	rcar_du_crtc_update_planes(crtc);
+	mutex_unlock(&rcdu->planes.lock);
+
+	/* Setup planes. */
+	for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+		struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+		if (plane->crtc != crtc || !plane->enabled)
+			continue;
+
+		rcar_du_plane_setup(plane);
+	}
+
+	/* Select master sync mode. This enables display operation in master
+	 * sync mode (with the HSYNC and VSYNC signals configured as outputs and
+	 * actively driven).
+	 */
+	rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
+
+	rcar_du_start_stop(rcdu, true);
+
+	rcrtc->started = true;
+}
+
+static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_crtc *crtc = &rcrtc->crtc;
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+
+	if (!rcrtc->started)
+		return;
+
+	mutex_lock(&rcdu->planes.lock);
+	rcrtc->plane->enabled = false;
+	rcar_du_crtc_update_planes(crtc);
+	mutex_unlock(&rcdu->planes.lock);
+
+	/* Select switch sync mode. This stops display operation and configures
+	 * the HSYNC and VSYNC signals as inputs.
+	 */
+	rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
+
+	rcar_du_start_stop(rcdu, false);
+
+	rcrtc->started = false;
+}
+
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	rcar_du_crtc_stop(rcrtc);
+	rcar_du_put(rcdu);
+}
+
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
+{
+	struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+	if (rcrtc->dpms != DRM_MODE_DPMS_ON)
+		return;
+
+	rcar_du_get(rcdu);
+	rcar_du_crtc_start(rcrtc);
+}
+
+static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_crtc *crtc = &rcrtc->crtc;
+
+	rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+	rcar_du_plane_update_base(rcrtc->plane);
+}
+
+static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	if (rcrtc->dpms == mode)
+		return;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		rcar_du_get(rcdu);
+		rcar_du_crtc_start(rcrtc);
+	} else {
+		rcar_du_crtc_stop(rcrtc);
+		rcar_du_put(rcdu);
+	}
+
+	rcrtc->dpms = mode;
+}
+
+static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
+				    const struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted_mode)
+{
+	/* TODO Fixup modes */
+	return true;
+}
+
+static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
+{
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	/* We need to access the hardware during mode set, acquire a reference
+	 * to the DU.
+	 */
+	rcar_du_get(rcdu);
+
+	/* Stop the CRTC and release the plane. Force the DPMS mode to off as a
+	 * result.
+	 */
+	rcar_du_crtc_stop(rcrtc);
+	rcar_du_plane_release(rcrtc->plane);
+
+	rcrtc->dpms = DRM_MODE_DPMS_OFF;
+}
+
+static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode,
+				 int x, int y,
+				 struct drm_framebuffer *old_fb)
+{
+	struct rcar_du_device *rcdu = crtc->dev->dev_private;
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+	const struct rcar_du_format_info *format;
+	int ret;
+
+	format = rcar_du_format_info(crtc->fb->pixel_format);
+	if (format == NULL) {
+		dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
+			crtc->fb->pixel_format);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = rcar_du_plane_reserve(rcrtc->plane, format);
+	if (ret < 0)
+		goto error;
+
+	rcrtc->plane->format = format;
+	rcrtc->plane->pitch = crtc->fb->pitches[0];
+
+	rcrtc->plane->src_x = x;
+	rcrtc->plane->src_y = y;
+	rcrtc->plane->width = mode->hdisplay;
+	rcrtc->plane->height = mode->vdisplay;
+
+	rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+
+	rcrtc->outputs = 0;
+
+	return 0;
+
+error:
+	/* There's no rollback/abort operation to clean up in case of error. We
+	 * thus need to release the reference to the DU acquired in prepare()
+	 * here.
+	 */
+	rcar_du_put(rcdu);
+	return ret;
+}
+
+static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	/* We're done, restart the CRTC and set the DPMS mode to on. The
+	 * reference to the DU acquired at prepare() time will thus be released
+	 * by the DPMS handler (possibly called by the disable() handler).
+	 */
+	rcar_du_crtc_start(rcrtc);
+	rcrtc->dpms = DRM_MODE_DPMS_ON;
+}
+
+static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+				      struct drm_framebuffer *old_fb)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	rcrtc->plane->src_x = x;
+	rcrtc->plane->src_y = y;
+
+	rcar_du_crtc_update_base(to_rcar_crtc(crtc));
+
+	return 0;
+}
+
+static void rcar_du_crtc_disable(struct drm_crtc *crtc)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+	rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+	rcar_du_plane_release(rcrtc->plane);
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+	.dpms = rcar_du_crtc_dpms,
+	.mode_fixup = rcar_du_crtc_mode_fixup,
+	.prepare = rcar_du_crtc_mode_prepare,
+	.commit = rcar_du_crtc_mode_commit,
+	.mode_set = rcar_du_crtc_mode_set,
+	.mode_set_base = rcar_du_crtc_mode_set_base,
+	.disable = rcar_du_crtc_disable,
+};
+
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+				   struct drm_file *file)
+{
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+
+	/* Destroy the pending vertical blanking event associated with the
+	 * pending page flip, if any, and disable vertical blanking interrupts.
+	 */
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = rcrtc->event;
+	if (event && event->base.file_priv == file) {
+		rcrtc->event = NULL;
+		event->base.destroy(&event->base);
+		drm_vblank_put(dev, rcrtc->index);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = rcrtc->event;
+	rcrtc->event = NULL;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	if (event == NULL)
+		return;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	drm_send_vblank_event(dev, rcrtc->index, event);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	drm_vblank_put(dev, rcrtc->index);
+}
+
+static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
+				  struct drm_framebuffer *fb,
+				  struct drm_pending_vblank_event *event)
+{
+	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (rcrtc->event != NULL) {
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	crtc->fb = fb;
+	rcar_du_crtc_update_base(rcrtc);
+
+	if (event) {
+		event->pipe = rcrtc->index;
+		drm_vblank_get(dev, rcrtc->index);
+		spin_lock_irqsave(&dev->event_lock, flags);
+		rcrtc->event = event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs crtc_funcs = {
+	.destroy = drm_crtc_cleanup,
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = rcar_du_crtc_page_flip,
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index)
+{
+	struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
+	struct drm_crtc *crtc = &rcrtc->crtc;
+	int ret;
+
+	rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
+	rcrtc->index = index;
+	rcrtc->dpms = DRM_MODE_DPMS_OFF;
+	rcrtc->plane = &rcdu->planes.planes[index];
+
+	rcrtc->plane->crtc = crtc;
+
+	ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
+	if (ret < 0)
+		return ret;
+
+	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+	return 0;
+}
+
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
+{
+	if (enable) {
+		rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
+		rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
+	} else {
+		rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
+	}
+}
+
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc)
+{
+	u32 status;
+
+	status = rcar_du_crtc_read(rcrtc, DSSR);
+	rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
+
+	if (status & DSSR_VBK) {
+		drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
+		rcar_du_crtc_finish_page_flip(rcrtc);
+	}
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
new file mode 100644
index 000000000000..2a0365bcbd14
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -0,0 +1,50 @@
+/*
+ * rcar_du_crtc.h  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_CRTC_H__
+#define __RCAR_DU_CRTC_H__
+
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+struct rcar_du_plane;
+
+struct rcar_du_crtc {
+	struct drm_crtc crtc;
+
+	unsigned int mmio_offset;
+	unsigned int index;
+	bool started;
+
+	struct drm_pending_vblank_event *event;
+	unsigned int outputs;
+	int dpms;
+
+	struct rcar_du_plane *plane;
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index);
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+				   struct drm_file *file);
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
+
+#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
new file mode 100644
index 000000000000..003b34ee38e3
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -0,0 +1,325 @@
+/*
+ * rcar_du_drv.c  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Core device operations
+ */
+
+/*
+ * rcar_du_get - Acquire a reference to the DU
+ *
+ * Acquiring a reference enables the device clock and setup core registers. A
+ * reference must be held before accessing any hardware registers.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ *
+ * Return 0 in case of success or a negative error code otherwise.
+ */
+int rcar_du_get(struct rcar_du_device *rcdu)
+{
+	int ret;
+
+	if (rcdu->use_count)
+		goto done;
+
+	/* Enable clocks before accessing the hardware. */
+	ret = clk_prepare_enable(rcdu->clock);
+	if (ret < 0)
+		return ret;
+
+	/* Enable extended features */
+	rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
+	rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
+	rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
+	rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
+	rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
+
+	/* Use DS1PR and DS2PR to configure planes priorities and connects the
+	 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+	 */
+	rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
+
+done:
+	rcdu->use_count++;
+	return 0;
+}
+
+/*
+ * rcar_du_put - Release a reference to the DU
+ *
+ * Releasing the last reference disables the device clock.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ */
+void rcar_du_put(struct rcar_du_device *rcdu)
+{
+	if (--rcdu->use_count)
+		return;
+
+	clk_disable_unprepare(rcdu->clock);
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+static int rcar_du_unload(struct drm_device *dev)
+{
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+	drm_vblank_cleanup(dev);
+	drm_irq_uninstall(dev);
+
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+static int rcar_du_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
+	struct rcar_du_device *rcdu;
+	struct resource *ioarea;
+	struct resource *mem;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(dev->dev, "no platform data\n");
+		return -ENODEV;
+	}
+
+	rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+	if (rcdu == NULL) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	rcdu->dev = &pdev->dev;
+	rcdu->pdata = pdata;
+	rcdu->ddev = dev;
+	dev->dev_private = rcdu;
+
+	/* I/O resources and clocks */
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem == NULL) {
+		dev_err(&pdev->dev, "failed to get memory resource\n");
+		return -EINVAL;
+	}
+
+	ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+					 resource_size(mem), pdev->name);
+	if (ioarea == NULL) {
+		dev_err(&pdev->dev, "failed to request memory region\n");
+		return -EBUSY;
+	}
+
+	rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
+					  resource_size(ioarea));
+	if (rcdu->mmio == NULL) {
+		dev_err(&pdev->dev, "failed to remap memory resource\n");
+		return -ENOMEM;
+	}
+
+	rcdu->clock = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(rcdu->clock)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return -ENOENT;
+	}
+
+	/* DRM/KMS objects */
+	ret = rcar_du_modeset_init(rcdu);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
+		goto done;
+	}
+
+	/* IRQ and vblank handling */
+	ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to initialize vblank\n");
+		goto done;
+	}
+
+	ret = drm_irq_install(dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to install IRQ handler\n");
+		goto done;
+	}
+
+	platform_set_drvdata(pdev, rcdu);
+
+done:
+	if (ret)
+		rcar_du_unload(dev);
+
+	return ret;
+}
+
+static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct rcar_du_device *rcdu = dev->dev_private;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+		rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
+}
+
+static irqreturn_t rcar_du_irq(int irq, void *arg)
+{
+	struct drm_device *dev = arg;
+	struct rcar_du_device *rcdu = dev->dev_private;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+		rcar_du_crtc_irq(&rcdu->crtcs[i]);
+
+	return IRQ_HANDLED;
+}
+
+static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
+{
+	struct rcar_du_device *rcdu = dev->dev_private;
+
+	rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
+
+	return 0;
+}
+
+static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
+{
+	struct rcar_du_device *rcdu = dev->dev_private;
+
+	rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
+}
+
+static const struct file_operations rcar_du_fops = {
+	.owner		= THIS_MODULE,
+	.open		= drm_open,
+	.release	= drm_release,
+	.unlocked_ioctl	= drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= drm_compat_ioctl,
+#endif
+	.poll		= drm_poll,
+	.read		= drm_read,
+	.fasync		= drm_fasync,
+	.llseek		= no_llseek,
+	.mmap		= drm_gem_cma_mmap,
+};
+
+static struct drm_driver rcar_du_driver = {
+	.driver_features	= DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
+				| DRIVER_PRIME,
+	.load			= rcar_du_load,
+	.unload			= rcar_du_unload,
+	.preclose		= rcar_du_preclose,
+	.irq_handler		= rcar_du_irq,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rcar_du_enable_vblank,
+	.disable_vblank		= rcar_du_disable_vblank,
+	.gem_free_object	= drm_gem_cma_free_object,
+	.gem_vm_ops		= &drm_gem_cma_vm_ops,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import	= drm_gem_cma_dmabuf_import,
+	.gem_prime_export	= drm_gem_cma_dmabuf_export,
+	.dumb_create		= drm_gem_cma_dumb_create,
+	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
+	.dumb_destroy		= drm_gem_cma_dumb_destroy,
+	.fops			= &rcar_du_fops,
+	.name			= "rcar-du",
+	.desc			= "Renesas R-Car Display Unit",
+	.date			= "20130110",
+	.major			= 1,
+	.minor			= 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+#if CONFIG_PM_SLEEP
+static int rcar_du_pm_suspend(struct device *dev)
+{
+	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+	drm_kms_helper_poll_disable(rcdu->ddev);
+	/* TODO Suspend the CRTC */
+
+	return 0;
+}
+
+static int rcar_du_pm_resume(struct device *dev)
+{
+	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+	/* TODO Resume the CRTC */
+
+	drm_kms_helper_poll_enable(rcdu->ddev);
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops rcar_du_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+	return drm_platform_init(&rcar_du_driver, pdev);
+}
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+	drm_platform_exit(&rcar_du_driver, pdev);
+
+	return 0;
+}
+
+static struct platform_driver rcar_du_platform_driver = {
+	.probe		= rcar_du_probe,
+	.remove		= rcar_du_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "rcar-du",
+		.pm	= &rcar_du_pm_ops,
+	},
+};
+
+module_platform_driver(rcar_du_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
new file mode 100644
index 000000000000..193cc59d495c
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -0,0 +1,66 @@
+/*
+ * rcar_du_drv.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_DRV_H__
+#define __RCAR_DU_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/rcar-du.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_plane.h"
+
+struct clk;
+struct device;
+struct drm_device;
+
+struct rcar_du_device {
+	struct device *dev;
+	const struct rcar_du_platform_data *pdata;
+
+	void __iomem *mmio;
+	struct clk *clock;
+	unsigned int use_count;
+
+	struct drm_device *ddev;
+
+	struct rcar_du_crtc crtcs[2];
+	unsigned int used_crtcs;
+	unsigned int num_crtcs;
+
+	struct {
+		struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
+		unsigned int free;
+		struct mutex lock;
+
+		struct drm_property *alpha;
+		struct drm_property *colorkey;
+		struct drm_property *zpos;
+	} planes;
+};
+
+int rcar_du_get(struct rcar_du_device *rcdu);
+void rcar_du_put(struct rcar_du_device *rcdu);
+
+static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
+{
+	return ioread32(rcdu->mmio + reg);
+}
+
+static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
+{
+	iowrite32(data, rcdu->mmio + reg);
+}
+
+#endif /* __RCAR_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
new file mode 100644
index 000000000000..9c63f39658de
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -0,0 +1,245 @@
+/*
+ * rcar_du_kms.c  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct rcar_du_format_info rcar_du_format_infos[] = {
+	{
+		.fourcc = DRM_FORMAT_RGB565,
+		.bpp = 16,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_ARGB1555,
+		.bpp = 16,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_XRGB1555,
+		.bpp = 16,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_XRGB8888,
+		.bpp = 32,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+		.edf = PnDDCR4_EDF_RGB888,
+	}, {
+		.fourcc = DRM_FORMAT_ARGB8888,
+		.bpp = 32,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
+		.edf = PnDDCR4_EDF_ARGB8888,
+	}, {
+		.fourcc = DRM_FORMAT_UYVY,
+		.bpp = 16,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_YUYV,
+		.bpp = 16,
+		.planes = 1,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_NV12,
+		.bpp = 12,
+		.planes = 2,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_NV21,
+		.bpp = 12,
+		.planes = 2,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		/* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
+		.fourcc = DRM_FORMAT_NV16,
+		.bpp = 16,
+		.planes = 2,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	},
+};
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
+		if (rcar_du_format_infos[i].fourcc == fourcc)
+			return &rcar_du_format_infos[i];
+	}
+
+	return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Common connector and encoder functions
+ */
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rcar_du_connector *rcon = to_rcar_connector(connector);
+
+	return &rcon->encoder->encoder;
+}
+
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
+{
+}
+
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode)
+{
+	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+	rcar_du_crtc_route_output(encoder->crtc, renc->output);
+}
+
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
+{
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static struct drm_framebuffer *
+rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+		  struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	const struct rcar_du_format_info *format;
+
+	format = rcar_du_format_info(mode_cmd->pixel_format);
+	if (format == NULL) {
+		dev_dbg(dev->dev, "unsupported pixel format %08x\n",
+			mode_cmd->pixel_format);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) {
+		dev_dbg(dev->dev, "invalid pitch value %u\n",
+			mode_cmd->pitches[0]);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (format->planes == 2) {
+		if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
+			dev_dbg(dev->dev,
+				"luma and chroma pitches do not match\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
+	.fb_create = rcar_du_fb_create,
+};
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu)
+{
+	struct drm_device *dev = rcdu->ddev;
+	struct drm_encoder *encoder;
+	unsigned int i;
+	int ret;
+
+	drm_mode_config_init(rcdu->ddev);
+
+	rcdu->ddev->mode_config.min_width = 0;
+	rcdu->ddev->mode_config.min_height = 0;
+	rcdu->ddev->mode_config.max_width = 4095;
+	rcdu->ddev->mode_config.max_height = 2047;
+	rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
+
+	ret = rcar_du_plane_init(rcdu);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+		rcar_du_crtc_create(rcdu, i);
+
+	rcdu->used_crtcs = 0;
+	rcdu->num_crtcs = i;
+
+	for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+		const struct rcar_du_encoder_data *pdata =
+			&rcdu->pdata->encoders[i];
+
+		if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
+			dev_warn(rcdu->dev,
+				 "encoder %u references unexisting output %u, skipping\n",
+				 i, pdata->output);
+			continue;
+		}
+
+		switch (pdata->encoder) {
+		case RCAR_DU_ENCODER_VGA:
+			rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
+			break;
+
+		case RCAR_DU_ENCODER_LVDS:
+			rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	/* Set the possible CRTCs and possible clones. All encoders can be
+	 * driven by the CRTC associated with the output they're connected to,
+	 * as well as by CRTC 0.
+	 */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+		encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
+		encoder->possible_clones = 1 << 0;
+	}
+
+	ret = rcar_du_plane_register(rcdu);
+	if (ret < 0)
+		return ret;
+
+	drm_kms_helper_poll_init(rcdu->ddev);
+
+	drm_helper_disable_unused_functions(rcdu->ddev);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
new file mode 100644
index 000000000000..e4d8db069a06
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -0,0 +1,59 @@
+/*
+ * rcar_du_kms.h  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_KMS_H__
+#define __RCAR_DU_KMS_H__
+
+#include <linux/types.h>
+
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+
+struct rcar_du_format_info {
+	u32 fourcc;
+	unsigned int bpp;
+	unsigned int planes;
+	unsigned int pnmr;
+	unsigned int edf;
+};
+
+struct rcar_du_encoder {
+	struct drm_encoder encoder;
+	unsigned int output;
+};
+
+#define to_rcar_encoder(e) \
+	container_of(e, struct rcar_du_encoder, encoder)
+
+struct rcar_du_connector {
+	struct drm_connector connector;
+	struct rcar_du_encoder *encoder;
+};
+
+#define to_rcar_connector(c) \
+	container_of(c, struct rcar_du_connector, connector)
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector);
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder);
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode);
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder);
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+
+#endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
new file mode 100644
index 000000000000..7aefe7267e1d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
@@ -0,0 +1,216 @@
+/*
+ * rcar_du_lvds.c  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+
+struct rcar_du_lvds_connector {
+	struct rcar_du_connector connector;
+
+	const struct rcar_du_panel_data *panel;
+};
+
+#define to_rcar_lvds_connector(c) \
+	container_of(c, struct rcar_du_lvds_connector, connector.connector)
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
+{
+	struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector);
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_create(connector->dev);
+	if (mode == NULL)
+		return 0;
+
+	mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+	mode->clock = lvdscon->panel->mode.clock;
+	mode->hdisplay = lvdscon->panel->mode.hdisplay;
+	mode->hsync_start = lvdscon->panel->mode.hsync_start;
+	mode->hsync_end = lvdscon->panel->mode.hsync_end;
+	mode->htotal = lvdscon->panel->mode.htotal;
+	mode->vdisplay = lvdscon->panel->mode.vdisplay;
+	mode->vsync_start = lvdscon->panel->mode.vsync_start;
+	mode->vsync_end = lvdscon->panel->mode.vsync_end;
+	mode->vtotal = lvdscon->panel->mode.vtotal;
+	mode->flags = lvdscon->panel->mode.flags;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
+					    struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = rcar_du_lvds_connector_get_modes,
+	.mode_valid = rcar_du_lvds_connector_mode_valid,
+	.best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rcar_du_lvds_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rcar_du_lvds_connector_destroy,
+};
+
+static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
+				       struct rcar_du_encoder *renc,
+				       const struct rcar_du_panel_data *panel)
+{
+	struct rcar_du_lvds_connector *lvdscon;
+	struct drm_connector *connector;
+	int ret;
+
+	lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
+	if (lvdscon == NULL)
+		return -ENOMEM;
+
+	lvdscon->panel = panel;
+
+	connector = &lvdscon->connector.connector;
+	connector->display_info.width_mm = panel->width_mm;
+	connector->display_info.height_mm = panel->height_mm;
+
+	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+				 DRM_MODE_CONNECTOR_LVDS);
+	if (ret < 0)
+		return ret;
+
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+	ret = drm_sysfs_connector_add(connector);
+	if (ret < 0)
+		return ret;
+
+	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	drm_object_property_set_value(&connector->base,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+	if (ret < 0)
+		return ret;
+
+	connector->encoder = &renc->encoder;
+	lvdscon->connector.encoder = renc;
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+					   const struct drm_display_mode *mode,
+					   struct drm_display_mode *adjusted_mode)
+{
+	const struct drm_display_mode *panel_mode;
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+	bool found = false;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		dev_dbg(dev->dev, "mode_fixup: no connector found\n");
+		return false;
+	}
+
+	if (list_empty(&connector->modes)) {
+		dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
+		return false;
+	}
+
+	panel_mode = list_first_entry(&connector->modes,
+				      struct drm_display_mode, head);
+
+	/* We're not allowed to modify the resolution. */
+	if (mode->hdisplay != panel_mode->hdisplay ||
+	    mode->vdisplay != panel_mode->vdisplay)
+		return false;
+
+	/* The flat panel mode is fixed, just copy it to the adjusted mode. */
+	drm_mode_copy(adjusted_mode, panel_mode);
+
+	return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+	.dpms = rcar_du_lvds_encoder_dpms,
+	.mode_fixup = rcar_du_lvds_encoder_mode_fixup,
+	.prepare = rcar_du_encoder_mode_prepare,
+	.commit = rcar_du_encoder_mode_commit,
+	.mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+		      const struct rcar_du_encoder_lvds_data *data,
+		      unsigned int output)
+{
+	struct rcar_du_encoder *renc;
+	int ret;
+
+	renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+	if (renc == NULL)
+		return -ENOMEM;
+
+	renc->output = output;
+
+	ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret < 0)
+		return ret;
+
+	drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+	return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
new file mode 100644
index 000000000000..b47f8328e103
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_lvds.h  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_LVDS_H__
+#define __RCAR_DU_LVDS_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_lvds_data;
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+		      const struct rcar_du_encoder_lvds_data *data,
+		      unsigned int output);
+
+#endif /* __RCAR_DU_LVDS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
new file mode 100644
index 000000000000..a65f81ddf51d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -0,0 +1,507 @@
+/*
+ * rcar_du_plane.c  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+
+#define RCAR_DU_COLORKEY_NONE		(0 << 24)
+#define RCAR_DU_COLORKEY_SOURCE		(1 << 24)
+#define RCAR_DU_COLORKEY_MASK		(1 << 24)
+
+struct rcar_du_kms_plane {
+	struct drm_plane plane;
+	struct rcar_du_plane *hwplane;
+};
+
+static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
+}
+
+static u32 rcar_du_plane_read(struct rcar_du_device *rcdu,
+			      unsigned int index, u32 reg)
+{
+	return rcar_du_read(rcdu, index * PLANE_OFF + reg);
+}
+
+static void rcar_du_plane_write(struct rcar_du_device *rcdu,
+				unsigned int index, u32 reg, u32 data)
+{
+	rcar_du_write(rcdu, index * PLANE_OFF + reg, data);
+}
+
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+			  const struct rcar_du_format_info *format)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+	unsigned int i;
+	int ret = -EBUSY;
+
+	mutex_lock(&rcdu->planes.lock);
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+		if (!(rcdu->planes.free & (1 << i)))
+			continue;
+
+		if (format->planes == 1 ||
+		    rcdu->planes.free & (1 << ((i + 1) % 8)))
+			break;
+	}
+
+	if (i == ARRAY_SIZE(rcdu->planes.planes))
+		goto done;
+
+	rcdu->planes.free &= ~(1 << i);
+	if (format->planes == 2)
+		rcdu->planes.free &= ~(1 << ((i + 1) % 8));
+
+	plane->hwindex = i;
+
+	ret = 0;
+
+done:
+	mutex_unlock(&rcdu->planes.lock);
+	return ret;
+}
+
+void rcar_du_plane_release(struct rcar_du_plane *plane)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+
+	if (plane->hwindex == -1)
+		return;
+
+	mutex_lock(&rcdu->planes.lock);
+	rcdu->planes.free |= 1 << plane->hwindex;
+	if (plane->format->planes == 2)
+		rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8);
+	mutex_unlock(&rcdu->planes.lock);
+
+	plane->hwindex = -1;
+}
+
+void rcar_du_plane_update_base(struct rcar_du_plane *plane)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+	unsigned int index = plane->hwindex;
+
+	/* According to the datasheet the Y position is expressed in raster line
+	 * units. However, 32bpp formats seem to require a doubled Y position
+	 * value. Similarly, for the second plane, NV12 and NV21 formats seem to
+	 * require a halved Y position value.
+	 */
+	rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+	rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+			    (plane->format->bpp == 32 ? 2 : 1));
+	rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]);
+
+	if (plane->format->planes == 2) {
+		index = (index + 1) % 8;
+
+		rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+		rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+				    (plane->format->bpp == 16 ? 2 : 1) / 2);
+		rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]);
+	}
+}
+
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+				struct drm_framebuffer *fb)
+{
+	struct drm_gem_cma_object *gem;
+
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	plane->dma[0] = gem->paddr + fb->offsets[0];
+
+	if (plane->format->planes == 2) {
+		gem = drm_fb_cma_get_gem_obj(fb, 1);
+		plane->dma[1] = gem->paddr + fb->offsets[1];
+	}
+}
+
+static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
+				     unsigned int index)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+	u32 colorkey;
+	u32 pnmr;
+
+	/* The PnALPHAR register controls alpha-blending in 16bpp formats
+	 * (ARGB1555 and XRGB1555).
+	 *
+	 * For ARGB, set the alpha value to 0, and enable alpha-blending when
+	 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
+	 *
+	 * For XRGB, set the alpha value to the plane-wide alpha value and
+	 * enable alpha-blending regardless of the X bit value.
+	 */
+	if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
+		rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0);
+	else
+		rcar_du_plane_write(rcdu, index, PnALPHAR,
+				    PnALPHAR_ABIT_X | plane->alpha);
+
+	pnmr = PnMR_BM_MD | plane->format->pnmr;
+
+	/* Disable color keying when requested. YUV formats have the
+	 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
+	 * automatically.
+	 */
+	if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
+		pnmr |= PnMR_SPIM_TP_OFF;
+
+	/* For packed YUV formats we need to select the U/V order. */
+	if (plane->format->fourcc == DRM_FORMAT_YUYV)
+		pnmr |= PnMR_YCDF_YUYV;
+
+	rcar_du_plane_write(rcdu, index, PnMR, pnmr);
+
+	switch (plane->format->fourcc) {
+	case DRM_FORMAT_RGB565:
+		colorkey = ((plane->colorkey & 0xf80000) >> 8)
+			 | ((plane->colorkey & 0x00fc00) >> 5)
+			 | ((plane->colorkey & 0x0000f8) >> 3);
+		rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+		break;
+
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		colorkey = ((plane->colorkey & 0xf80000) >> 9)
+			 | ((plane->colorkey & 0x00f800) >> 6)
+			 | ((plane->colorkey & 0x0000f8) >> 3);
+		rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		rcar_du_plane_write(rcdu, index, PnTC3R,
+				    PnTC3R_CODE | (plane->colorkey & 0xffffff));
+		break;
+	}
+}
+
+static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
+				  unsigned int index)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+	u32 ddcr2 = PnDDCR2_CODE;
+	u32 ddcr4;
+	u32 mwr;
+
+	/* Data format
+	 *
+	 * The data format is selected by the DDDF field in PnMR and the EDF
+	 * field in DDCR4.
+	 */
+	ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4);
+	ddcr4 &= ~PnDDCR4_EDF_MASK;
+	ddcr4 |= plane->format->edf | PnDDCR4_CODE;
+
+	rcar_du_plane_setup_mode(plane, index);
+
+	if (plane->format->planes == 2) {
+		if (plane->hwindex != index) {
+			if (plane->format->fourcc == DRM_FORMAT_NV12 ||
+			    plane->format->fourcc == DRM_FORMAT_NV21)
+				ddcr2 |= PnDDCR2_Y420;
+
+			if (plane->format->fourcc == DRM_FORMAT_NV21)
+				ddcr2 |= PnDDCR2_NV21;
+
+			ddcr2 |= PnDDCR2_DIVU;
+		} else {
+			ddcr2 |= PnDDCR2_DIVY;
+		}
+	}
+
+	rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2);
+	rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4);
+
+	/* Memory pitch (expressed in pixels) */
+	if (plane->format->planes == 2)
+		mwr = plane->pitch;
+	else
+		mwr = plane->pitch * 8 / plane->format->bpp;
+
+	rcar_du_plane_write(rcdu, index, PnMWR, mwr);
+
+	/* Destination position and size */
+	rcar_du_plane_write(rcdu, index, PnDSXR, plane->width);
+	rcar_du_plane_write(rcdu, index, PnDSYR, plane->height);
+	rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x);
+	rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y);
+
+	/* Wrap-around and blinking, disabled */
+	rcar_du_plane_write(rcdu, index, PnWASPR, 0);
+	rcar_du_plane_write(rcdu, index, PnWAMWR, 4095);
+	rcar_du_plane_write(rcdu, index, PnBTR, 0);
+	rcar_du_plane_write(rcdu, index, PnMLR, 0);
+}
+
+void rcar_du_plane_setup(struct rcar_du_plane *plane)
+{
+	__rcar_du_plane_setup(plane, plane->hwindex);
+	if (plane->format->planes == 2)
+		__rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
+
+	rcar_du_plane_update_base(plane);
+}
+
+static int
+rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+		       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+		       unsigned int crtc_w, unsigned int crtc_h,
+		       uint32_t src_x, uint32_t src_y,
+		       uint32_t src_w, uint32_t src_h)
+{
+	struct rcar_du_plane *rplane = to_rcar_plane(plane);
+	struct rcar_du_device *rcdu = plane->dev->dev_private;
+	const struct rcar_du_format_info *format;
+	unsigned int nplanes;
+	int ret;
+
+	format = rcar_du_format_info(fb->pixel_format);
+	if (format == NULL) {
+		dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
+			fb->pixel_format);
+		return -EINVAL;
+	}
+
+	if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+		dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	nplanes = rplane->format ? rplane->format->planes : 0;
+
+	/* Reallocate hardware planes if the number of required planes has
+	 * changed.
+	 */
+	if (format->planes != nplanes) {
+		rcar_du_plane_release(rplane);
+		ret = rcar_du_plane_reserve(rplane, format);
+		if (ret < 0)
+			return ret;
+	}
+
+	rplane->crtc = crtc;
+	rplane->format = format;
+	rplane->pitch = fb->pitches[0];
+
+	rplane->src_x = src_x >> 16;
+	rplane->src_y = src_y >> 16;
+	rplane->dst_x = crtc_x;
+	rplane->dst_y = crtc_y;
+	rplane->width = crtc_w;
+	rplane->height = crtc_h;
+
+	rcar_du_plane_compute_base(rplane, fb);
+	rcar_du_plane_setup(rplane);
+
+	mutex_lock(&rcdu->planes.lock);
+	rplane->enabled = true;
+	rcar_du_crtc_update_planes(rplane->crtc);
+	mutex_unlock(&rcdu->planes.lock);
+
+	return 0;
+}
+
+static int rcar_du_plane_disable(struct drm_plane *plane)
+{
+	struct rcar_du_device *rcdu = plane->dev->dev_private;
+	struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+	if (!rplane->enabled)
+		return 0;
+
+	mutex_lock(&rcdu->planes.lock);
+	rplane->enabled = false;
+	rcar_du_crtc_update_planes(rplane->crtc);
+	mutex_unlock(&rcdu->planes.lock);
+
+	rcar_du_plane_release(rplane);
+
+	rplane->crtc = NULL;
+	rplane->format = NULL;
+
+	return 0;
+}
+
+/* Both the .set_property and the .update_plane operations are called with the
+ * mode_config lock held. There is this no need to explicitly protect access to
+ * the alpha and colorkey fields and the mode register.
+ */
+static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
+{
+	if (plane->alpha == alpha)
+		return;
+
+	plane->alpha = alpha;
+	if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
+		return;
+
+	rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
+				       u32 colorkey)
+{
+	if (plane->colorkey == colorkey)
+		return;
+
+	plane->colorkey = colorkey;
+	if (!plane->enabled)
+		return;
+
+	rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
+				   unsigned int zpos)
+{
+	struct rcar_du_device *rcdu = plane->dev;
+
+	mutex_lock(&rcdu->planes.lock);
+	if (plane->zpos == zpos)
+		goto done;
+
+	plane->zpos = zpos;
+	if (!plane->enabled)
+		goto done;
+
+	rcar_du_crtc_update_planes(plane->crtc);
+
+done:
+	mutex_unlock(&rcdu->planes.lock);
+}
+
+static int rcar_du_plane_set_property(struct drm_plane *plane,
+				      struct drm_property *property,
+				      uint64_t value)
+{
+	struct rcar_du_device *rcdu = plane->dev->dev_private;
+	struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+	if (property == rcdu->planes.alpha)
+		rcar_du_plane_set_alpha(rplane, value);
+	else if (property == rcdu->planes.colorkey)
+		rcar_du_plane_set_colorkey(rplane, value);
+	else if (property == rcdu->planes.zpos)
+		rcar_du_plane_set_zpos(rplane, value);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct drm_plane_funcs rcar_du_plane_funcs = {
+	.update_plane = rcar_du_plane_update,
+	.disable_plane = rcar_du_plane_disable,
+	.set_property = rcar_du_plane_set_property,
+	.destroy = drm_plane_cleanup,
+};
+
+static const uint32_t formats[] = {
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu)
+{
+	unsigned int i;
+
+	mutex_init(&rcdu->planes.lock);
+	rcdu->planes.free = 0xff;
+
+	rcdu->planes.alpha =
+		drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
+	if (rcdu->planes.alpha == NULL)
+		return -ENOMEM;
+
+	/* The color key is expressed as an RGB888 triplet stored in a 32-bit
+	 * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
+	 * or enable source color keying (1).
+	 */
+	rcdu->planes.colorkey =
+		drm_property_create_range(rcdu->ddev, 0, "colorkey",
+					  0, 0x01ffffff);
+	if (rcdu->planes.colorkey == NULL)
+		return -ENOMEM;
+
+	rcdu->planes.zpos =
+		drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
+	if (rcdu->planes.zpos == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+		struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+		plane->dev = rcdu;
+		plane->hwindex = -1;
+		plane->alpha = 255;
+		plane->colorkey = RCAR_DU_COLORKEY_NONE;
+		plane->zpos = 0;
+	}
+
+	return 0;
+}
+
+int rcar_du_plane_register(struct rcar_du_device *rcdu)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
+		struct rcar_du_kms_plane *plane;
+
+		plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
+		if (plane == NULL)
+			return -ENOMEM;
+
+		plane->hwplane = &rcdu->planes.planes[i + 2];
+		plane->hwplane->zpos = 1;
+
+		ret = drm_plane_init(rcdu->ddev, &plane->plane,
+				     (1 << rcdu->num_crtcs) - 1,
+				     &rcar_du_plane_funcs, formats,
+				     ARRAY_SIZE(formats), false);
+		if (ret < 0)
+			return ret;
+
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->planes.alpha, 255);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->planes.colorkey,
+					   RCAR_DU_COLORKEY_NONE);
+		drm_object_attach_property(&plane->plane.base,
+					   rcdu->planes.zpos, 1);
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
new file mode 100644
index 000000000000..5397dba2fe57
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -0,0 +1,67 @@
+/*
+ * rcar_du_plane.h  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_PLANE_H__
+#define __RCAR_DU_PLANE_H__
+
+struct drm_crtc;
+struct drm_framebuffer;
+struct rcar_du_device;
+struct rcar_du_format_info;
+
+/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As
+ * using KMS planes requires at least one of the CRTCs being enabled, no more
+ * than 7 KMS planes can be available. We thus create 7 KMS planes and
+ * 9 software planes (one for each KMS planes and one for each CRTC).
+ */
+
+#define RCAR_DU_NUM_KMS_PLANES		7
+#define RCAR_DU_NUM_HW_PLANES		8
+#define RCAR_DU_NUM_SW_PLANES		9
+
+struct rcar_du_plane {
+	struct rcar_du_device *dev;
+	struct drm_crtc *crtc;
+
+	bool enabled;
+
+	int hwindex;		/* 0-based, -1 means unused */
+	unsigned int alpha;
+	unsigned int colorkey;
+	unsigned int zpos;
+
+	const struct rcar_du_format_info *format;
+
+	unsigned long dma[2];
+	unsigned int pitch;
+
+	unsigned int width;
+	unsigned int height;
+
+	unsigned int src_x;
+	unsigned int src_y;
+	unsigned int dst_x;
+	unsigned int dst_y;
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu);
+int rcar_du_plane_register(struct rcar_du_device *rcdu);
+void rcar_du_plane_setup(struct rcar_du_plane *plane);
+void rcar_du_plane_update_base(struct rcar_du_plane *plane);
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+				struct drm_framebuffer *fb);
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+			  const struct rcar_du_format_info *format);
+void rcar_du_plane_release(struct rcar_du_plane *plane);
+
+#endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
new file mode 100644
index 000000000000..69f21f19b51c
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -0,0 +1,445 @@
+/*
+ * rcar_du_regs.h  --  R-Car Display Unit Registers Definitions
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __RCAR_DU_REGS_H__
+#define __RCAR_DU_REGS_H__
+
+#define DISP2_REG_OFFSET	 0x30000
+
+/* -----------------------------------------------------------------------------
+ * Display Control Registers
+ */
+
+#define DSYSR			0x00000	/* display 1 */
+#define D2SYSR			0x30000	/* display 2 */
+#define DSYSR_ILTS		(1 << 29)
+#define DSYSR_DSEC		(1 << 20)
+#define DSYSR_IUPD		(1 << 16)
+#define DSYSR_DRES		(1 << 9)
+#define DSYSR_DEN		(1 << 8)
+#define DSYSR_TVM_MASTER	(0 << 6)
+#define DSYSR_TVM_SWITCH	(1 << 6)
+#define DSYSR_TVM_TVSYNC	(2 << 6)
+#define DSYSR_TVM_MASK		(3 << 6)
+#define DSYSR_SCM_INT_NONE	(0 << 4)
+#define DSYSR_SCM_INT_SYNC	(2 << 4)
+#define DSYSR_SCM_INT_VIDEO	(3 << 4)
+
+#define DSMR			0x00004
+#define D2SMR			0x30004
+#define DSMR_VSPM		(1 << 28)
+#define DSMR_ODPM		(1 << 27)
+#define DSMR_DIPM_DISP		(0 << 25)
+#define DSMR_DIPM_CSYNC		(1 << 25)
+#define DSMR_DIPM_DE		(3 << 25)
+#define DSMR_DIPM_MASK		(3 << 25)
+#define DSMR_CSPM		(1 << 24)
+#define DSMR_DIL		(1 << 19)
+#define DSMR_VSL		(1 << 18)
+#define DSMR_HSL		(1 << 17)
+#define DSMR_DDIS		(1 << 16)
+#define DSMR_CDEL		(1 << 15)
+#define DSMR_CDEM_CDE		(0 << 13)
+#define DSMR_CDEM_LOW		(2 << 13)
+#define DSMR_CDEM_HIGH		(3 << 13)
+#define DSMR_CDEM_MASK		(3 << 13)
+#define DSMR_CDED		(1 << 12)
+#define DSMR_ODEV		(1 << 8)
+#define DSMR_CSY_VH_OR		(0 << 6)
+#define DSMR_CSY_333		(2 << 6)
+#define DSMR_CSY_222		(3 << 6)
+#define DSMR_CSY_MASK		(3 << 6)
+
+#define DSSR			0x00008
+#define D2SSR			0x30008
+#define DSSR_VC1FB_DSA0		(0 << 30)
+#define DSSR_VC1FB_DSA1		(1 << 30)
+#define DSSR_VC1FB_DSA2		(2 << 30)
+#define DSSR_VC1FB_INIT		(3 << 30)
+#define DSSR_VC1FB_MASK		(3 << 30)
+#define DSSR_VC0FB_DSA0		(0 << 28)
+#define DSSR_VC0FB_DSA1		(1 << 28)
+#define DSSR_VC0FB_DSA2		(2 << 28)
+#define DSSR_VC0FB_INIT		(3 << 28)
+#define DSSR_VC0FB_MASK		(3 << 28)
+#define DSSR_DFB(n)		(1 << ((n)+15))
+#define DSSR_TVR		(1 << 15)
+#define DSSR_FRM		(1 << 14)
+#define DSSR_VBK		(1 << 11)
+#define DSSR_RINT		(1 << 9)
+#define DSSR_HBK		(1 << 8)
+#define DSSR_ADC(n)		(1 << ((n)-1))
+
+#define DSRCR			0x0000c
+#define D2SRCR			0x3000c
+#define DSRCR_TVCL		(1 << 15)
+#define DSRCR_FRCL		(1 << 14)
+#define DSRCR_VBCL		(1 << 11)
+#define DSRCR_RICL		(1 << 9)
+#define DSRCR_HBCL		(1 << 8)
+#define DSRCR_ADCL(n)		(1 << ((n)-1))
+#define DSRCR_MASK		0x0000cbff
+
+#define DIER			0x00010
+#define D2IER			0x30010
+#define DIER_TVE		(1 << 15)
+#define DIER_FRE		(1 << 14)
+#define DIER_VBE		(1 << 11)
+#define DIER_RIE		(1 << 9)
+#define DIER_HBE		(1 << 8)
+#define DIER_ADCE(n)		(1 << ((n)-1))
+
+#define CPCR			0x00014
+#define CPCR_CP4CE		(1 << 19)
+#define CPCR_CP3CE		(1 << 18)
+#define CPCR_CP2CE		(1 << 17)
+#define CPCR_CP1CE		(1 << 16)
+
+#define DPPR			0x00018
+#define DPPR_DPE(n)		(1 << ((n)*4-1))
+#define DPPR_DPS(n, p)		(((p)-1) << DPPR_DPS_SHIFT(n))
+#define DPPR_DPS_SHIFT(n)	(((n)-1)*4)
+#define DPPR_BPP16		(DPPR_DPE(8) | DPPR_DPS(8, 1))	/* plane1 */
+#define DPPR_BPP32_P1		(DPPR_DPE(7) | DPPR_DPS(7, 1))
+#define DPPR_BPP32_P2		(DPPR_DPE(8) | DPPR_DPS(8, 2))
+#define DPPR_BPP32		(DPPR_BPP32_P1 | DPPR_BPP32_P2)	/* plane1 & 2 */
+
+#define DEFR			0x00020
+#define D2EFR			0x30020
+#define DEFR_CODE		(0x7773 << 16)
+#define DEFR_EXSL		(1 << 12)
+#define DEFR_EXVL		(1 << 11)
+#define DEFR_EXUP		(1 << 5)
+#define DEFR_VCUP		(1 << 4)
+#define DEFR_DEFE		(1 << 0)
+
+#define DAPCR			0x00024
+#define DAPCR_CODE		(0x7773 << 16)
+#define DAPCR_AP2E		(1 << 4)
+#define DAPCR_AP1E		(1 << 0)
+
+#define DCPCR			0x00028
+#define DCPCR_CODE		(0x7773 << 16)
+#define DCPCR_CA2B		(1 << 13)
+#define DCPCR_CD2F		(1 << 12)
+#define DCPCR_DC2E		(1 << 8)
+#define DCPCR_CAB		(1 << 5)
+#define DCPCR_CDF		(1 << 4)
+#define DCPCR_DCE		(1 << 0)
+
+#define DEFR2			0x00034
+#define D2EFR2			0x30034
+#define DEFR2_CODE		(0x7775 << 16)
+#define DEFR2_DEFE2G		(1 << 0)
+
+#define DEFR3			0x00038
+#define D2EFR3			0x30038
+#define DEFR3_CODE		(0x7776 << 16)
+#define DEFR3_EVDA		(1 << 14)
+#define DEFR3_EVDM_1		(1 << 12)
+#define DEFR3_EVDM_2		(2 << 12)
+#define DEFR3_EVDM_3		(3 << 12)
+#define DEFR3_VMSM2_EMA		(1 << 6)
+#define DEFR3_VMSM1_ENA		(1 << 4)
+#define DEFR3_DEFE3		(1 << 0)
+
+#define DEFR4			0x0003c
+#define D2EFR4			0x3003c
+#define DEFR4_CODE		(0x7777 << 16)
+#define DEFR4_LRUO		(1 << 5)
+#define DEFR4_SPCE		(1 << 4)
+
+#define DVCSR			0x000d0
+#define DVCSR_VCnFB2_DSA0(n)	(0 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA1(n)	(1 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA2(n)	(2 << ((n)*2+16))
+#define DVCSR_VCnFB2_INIT(n)	(3 << ((n)*2+16))
+#define DVCSR_VCnFB2_MASK(n)	(3 << ((n)*2+16))
+#define DVCSR_VCnFB_DSA0(n)	(0 << ((n)*2))
+#define DVCSR_VCnFB_DSA1(n)	(1 << ((n)*2))
+#define DVCSR_VCnFB_DSA2(n)	(2 << ((n)*2))
+#define DVCSR_VCnFB_INIT(n)	(3 << ((n)*2))
+#define DVCSR_VCnFB_MASK(n)	(3 << ((n)*2))
+
+#define DEFR5			0x000e0
+#define DEFR5_CODE		(0x66 << 24)
+#define DEFR5_YCRGB2_DIS	(0 << 14)
+#define DEFR5_YCRGB2_PRI1	(1 << 14)
+#define DEFR5_YCRGB2_PRI2	(2 << 14)
+#define DEFR5_YCRGB2_PRI3	(3 << 14)
+#define DEFR5_YCRGB2_MASK	(3 << 14)
+#define DEFR5_YCRGB1_DIS	(0 << 12)
+#define DEFR5_YCRGB1_PRI1	(1 << 12)
+#define DEFR5_YCRGB1_PRI2	(2 << 12)
+#define DEFR5_YCRGB1_PRI3	(3 << 12)
+#define DEFR5_YCRGB1_MASK	(3 << 12)
+#define DEFR5_DEFE5		(1 << 0)
+
+#define DDLTR			0x000e4
+#define DDLTR_CODE		(0x7766 << 16)
+#define DDLTR_DLAR2		(1 << 6)
+#define DDLTR_DLAY2		(1 << 5)
+#define DDLTR_DLAY1		(1 << 1)
+
+#define DEFR6			0x000e8
+#define DEFR6_CODE		(0x7778 << 16)
+#define DEFR6_ODPM22_D2SMR	(0 << 10)
+#define DEFR6_ODPM22_DISP	(2 << 10)
+#define DEFR6_ODPM22_CDE	(3 << 10)
+#define DEFR6_ODPM22_MASK	(3 << 10)
+#define DEFR6_ODPM12_DSMR	(0 << 8)
+#define DEFR6_ODPM12_DISP	(2 << 8)
+#define DEFR6_ODPM12_CDE	(3 << 8)
+#define DEFR6_ODPM12_MASK	(3 << 8)
+#define DEFR6_TCNE2		(1 << 6)
+#define DEFR6_MLOS1		(1 << 2)
+#define DEFR6_DEFAULT		(DEFR6_CODE | DEFR6_TCNE2)
+
+/* -----------------------------------------------------------------------------
+ * Display Timing Generation Registers
+ */
+
+#define HDSR			0x00040
+#define HDER			0x00044
+#define VDSR			0x00048
+#define VDER			0x0004c
+#define HCR			0x00050
+#define HSWR			0x00054
+#define VCR			0x00058
+#define VSPR			0x0005c
+#define EQWR			0x00060
+#define SPWR			0x00064
+#define CLAMPSR			0x00070
+#define CLAMPWR			0x00074
+#define DESR			0x00078
+#define DEWR			0x0007c
+
+/* -----------------------------------------------------------------------------
+ * Display Attribute Registers
+ */
+
+#define CP1TR			0x00080
+#define CP2TR			0x00084
+#define CP3TR			0x00088
+#define CP4TR			0x0008c
+
+#define DOOR			0x00090
+#define DOOR_RGB(r, g, b)	(((r) << 18) | ((g) << 10) | ((b) << 2))
+#define CDER			0x00094
+#define CDER_RGB(r, g, b)	(((r) << 18) | ((g) << 10) | ((b) << 2))
+#define BPOR			0x00098
+#define BPOR_RGB(r, g, b)	(((r) << 18) | ((g) << 10) | ((b) << 2))
+
+#define RINTOFSR		0x0009c
+
+#define DSHPR			0x000c8
+#define DSHPR_CODE		(0x7776 << 16)
+#define DSHPR_PRIH		(0xa << 4)
+#define DSHPR_PRIL_BPP16	(0x8 << 0)
+#define DSHPR_PRIL_BPP32	(0x9 << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display Plane Registers
+ */
+
+#define PLANE_OFF		0x00100
+
+#define PnMR			0x00100 /* plane 1 */
+#define PnMR_VISL_VIN0		(0 << 26)	/* use Video Input 0 */
+#define PnMR_VISL_VIN1		(1 << 26)	/* use Video Input 1 */
+#define PnMR_VISL_VIN2		(2 << 26)	/* use Video Input 2 */
+#define PnMR_VISL_VIN3		(3 << 26)	/* use Video Input 3 */
+#define PnMR_YCDF_YUYV		(1 << 20)	/* YUYV format */
+#define PnMR_TC_R		(0 << 17)	/* Tranparent color is PnTC1R */
+#define PnMR_TC_CP		(1 << 17)	/* Tranparent color is color palette */
+#define PnMR_WAE		(1 << 16)	/* Wrap around Enable */
+#define PnMR_SPIM_TP		(0 << 12)	/* Transparent Color */
+#define PnMR_SPIM_ALP		(1 << 12)	/* Alpha Blending */
+#define PnMR_SPIM_EOR		(2 << 12)	/* EOR */
+#define PnMR_SPIM_TP_OFF	(1 << 14)	/* No Transparent Color */
+#define PnMR_CPSL_CP1		(0 << 8)	/* Color Palette selected 1 */
+#define PnMR_CPSL_CP2		(1 << 8)	/* Color Palette selected 2 */
+#define PnMR_CPSL_CP3		(2 << 8)	/* Color Palette selected 3 */
+#define PnMR_CPSL_CP4		(3 << 8)	/* Color Palette selected 4 */
+#define PnMR_DC			(1 << 7)	/* Display Area Change */
+#define PnMR_BM_MD		(0 << 4)	/* Manual Display Change Mode */
+#define PnMR_BM_AR		(1 << 4)	/* Auto Rendering Mode */
+#define PnMR_BM_AD		(2 << 4)	/* Auto Display Change Mode */
+#define PnMR_BM_VC		(3 << 4)	/* Video Capture Mode */
+#define PnMR_DDDF_8BPP		(0 << 0)	/* 8bit */
+#define PnMR_DDDF_16BPP		(1 << 0)	/* 16bit or 32bit */
+#define PnMR_DDDF_ARGB		(2 << 0)	/* ARGB */
+#define PnMR_DDDF_YC		(3 << 0)	/* YC */
+#define PnMR_DDDF_MASK		(3 << 0)
+
+#define PnMWR			0x00104
+
+#define PnALPHAR		0x00108
+#define PnALPHAR_ABIT_1		(0 << 12)
+#define PnALPHAR_ABIT_0		(1 << 12)
+#define PnALPHAR_ABIT_X		(2 << 12)
+
+#define PnDSXR			0x00110
+#define PnDSYR			0x00114
+#define PnDPXR			0x00118
+#define PnDPYR			0x0011c
+
+#define PnDSA0R			0x00120
+#define PnDSA1R			0x00124
+#define PnDSA2R			0x00128
+#define PnDSA_MASK		0xfffffff0
+
+#define PnSPXR			0x00130
+#define PnSPYR			0x00134
+#define PnWASPR			0x00138
+#define PnWAMWR			0x0013c
+
+#define PnBTR			0x00140
+
+#define PnTC1R			0x00144
+#define PnTC2R			0x00148
+#define PnTC3R			0x0014c
+#define PnTC3R_CODE		(0x66 << 24)
+
+#define PnMLR			0x00150
+
+#define PnSWAPR			0x00180
+#define PnSWAPR_DIGN		(1 << 4)
+#define PnSWAPR_SPQW		(1 << 3)
+#define PnSWAPR_SPLW		(1 << 2)
+#define PnSWAPR_SPWD		(1 << 1)
+#define PnSWAPR_SPBY		(1 << 0)
+
+#define PnDDCR			0x00184
+#define PnDDCR_CODE		(0x7775 << 16)
+#define PnDDCR_LRGB1		(1 << 11)
+#define PnDDCR_LRGB0		(1 << 10)
+
+#define PnDDCR2			0x00188
+#define PnDDCR2_CODE		(0x7776 << 16)
+#define PnDDCR2_NV21		(1 << 5)
+#define PnDDCR2_Y420		(1 << 4)
+#define PnDDCR2_DIVU		(1 << 1)
+#define PnDDCR2_DIVY		(1 << 0)
+
+#define PnDDCR4			0x00190
+#define PnDDCR4_CODE		(0x7766 << 16)
+#define PnDDCR4_SDFS_RGB	(0 << 4)
+#define PnDDCR4_SDFS_YC		(5 << 4)
+#define PnDDCR4_SDFS_MASK	(7 << 4)
+#define PnDDCR4_EDF_NONE	(0 << 0)
+#define PnDDCR4_EDF_ARGB8888	(1 << 0)
+#define PnDDCR4_EDF_RGB888	(2 << 0)
+#define PnDDCR4_EDF_RGB666	(3 << 0)
+#define PnDDCR4_EDF_MASK	(7 << 0)
+
+#define APnMR			0x0a100
+#define APnMR_WAE		(1 << 16)	/* Wrap around Enable */
+#define APnMR_DC		(1 << 7)	/* Display Area Change */
+#define APnMR_BM_MD		(0 << 4)	/* Manual Display Change Mode */
+#define APnMR_BM_AD		(2 << 4)	/* Auto Display Change Mode */
+
+#define APnMWR			0x0a104
+#define APnDSA0R		0x0a120
+#define APnDSA1R		0x0a124
+#define APnDSA2R		0x0a128
+#define APnMLR			0x0a150
+
+/* -----------------------------------------------------------------------------
+ * Display Capture Registers
+ */
+
+#define DCMWR			0x0c104
+#define DC2MWR			0x0c204
+#define DCSAR			0x0c120
+#define DC2SAR			0x0c220
+#define DCMLR			0x0c150
+#define DC2MLR			0x0c250
+
+/* -----------------------------------------------------------------------------
+ * Color Palette Registers
+ */
+
+#define CP1_000R		0x01000
+#define CP1_255R		0x013fc
+#define CP2_000R		0x02000
+#define CP2_255R		0x023fc
+#define CP3_000R		0x03000
+#define CP3_255R		0x033fc
+#define CP4_000R		0x04000
+#define CP4_255R		0x043fc
+
+/* -----------------------------------------------------------------------------
+ * External Synchronization Control Registers
+ */
+
+#define ESCR			0x10000
+#define ESCR2			0x31000
+#define ESCR_DCLKOINV		(1 << 25)
+#define ESCR_DCLKSEL_DCLKIN	(0 << 20)
+#define ESCR_DCLKSEL_CLKS	(1 << 20)
+#define ESCR_DCLKSEL_MASK	(1 << 20)
+#define ESCR_DCLKDIS		(1 << 16)
+#define ESCR_SYNCSEL_OFF	(0 << 8)
+#define ESCR_SYNCSEL_EXVSYNC	(2 << 8)
+#define ESCR_SYNCSEL_EXHSYNC	(3 << 8)
+#define ESCR_FRQSEL_MASK	(0x3f << 0)
+
+#define OTAR			0x10004
+#define OTAR2			0x31004
+
+/* -----------------------------------------------------------------------------
+ * Dual Display Output Control Registers
+ */
+
+#define DORCR			0x11000
+#define DORCR_PG2T		(1 << 30)
+#define DORCR_DK2S		(1 << 28)
+#define DORCR_PG2D_DS1		(0 << 24)
+#define DORCR_PG2D_DS2		(1 << 24)
+#define DORCR_PG2D_FIX0		(2 << 24)
+#define DORCR_PG2D_DOOR		(3 << 24)
+#define DORCR_PG2D_MASK		(3 << 24)
+#define DORCR_DR1D		(1 << 21)
+#define DORCR_PG1D_DS1		(0 << 16)
+#define DORCR_PG1D_DS2		(1 << 16)
+#define DORCR_PG1D_FIX0		(2 << 16)
+#define DORCR_PG1D_DOOR		(3 << 16)
+#define DORCR_PG1D_MASK		(3 << 16)
+#define DORCR_RGPV		(1 << 4)
+#define DORCR_DPRS		(1 << 0)
+
+#define DPTSR			0x11004
+#define DPTSR_PnDK(n)		(1 << ((n) + 16))
+#define DPTSR_PnTS(n)		(1 << (n))
+
+#define DAPTSR			0x11008
+#define DAPTSR_APnDK(n)		(1 << ((n) + 16))
+#define DAPTSR_APnTS(n)		(1 << (n))
+
+#define DS1PR			0x11020
+#define DS2PR			0x11024
+
+/* -----------------------------------------------------------------------------
+ * YC-RGB Conversion Coefficient Registers
+ */
+
+#define YNCR			0x11080
+#define YNOR			0x11084
+#define CRNOR			0x11088
+#define CBNOR			0x1108c
+#define RCRCR			0x11090
+#define GCRCR			0x11094
+#define GCBCR			0x11098
+#define BCBCR			0x1109c
+
+#endif /* __RCAR_DU_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
new file mode 100644
index 000000000000..327289ec380d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
@@ -0,0 +1,149 @@
+/*
+ * rcar_du_vga.c  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
+{
+	return 0;
+}
+
+static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
+					    struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = rcar_du_vga_connector_get_modes,
+	.mode_valid = rcar_du_vga_connector_mode_valid,
+	.best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rcar_du_vga_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rcar_du_vga_connector_destroy,
+};
+
+static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
+				      struct rcar_du_encoder *renc)
+{
+	struct rcar_du_connector *rcon;
+	struct drm_connector *connector;
+	int ret;
+
+	rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+	if (rcon == NULL)
+		return -ENOMEM;
+
+	connector = &rcon->connector;
+	connector->display_info.width_mm = 0;
+	connector->display_info.height_mm = 0;
+
+	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+				 DRM_MODE_CONNECTOR_VGA);
+	if (ret < 0)
+		return ret;
+
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+	ret = drm_sysfs_connector_add(connector);
+	if (ret < 0)
+		return ret;
+
+	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	drm_object_property_set_value(&connector->base,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+	if (ret < 0)
+		return ret;
+
+	connector->encoder = &renc->encoder;
+	rcon->encoder = renc;
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder,
+					   const struct drm_display_mode *mode,
+					   struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+	.dpms = rcar_du_vga_encoder_dpms,
+	.mode_fixup = rcar_du_vga_encoder_mode_fixup,
+	.prepare = rcar_du_encoder_mode_prepare,
+	.commit = rcar_du_encoder_mode_commit,
+	.mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+		     const struct rcar_du_encoder_vga_data *data,
+		     unsigned int output)
+{
+	struct rcar_du_encoder *renc;
+	int ret;
+
+	renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+	if (renc == NULL)
+		return -ENOMEM;
+
+	renc->output = output;
+
+	ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+			       DRM_MODE_ENCODER_DAC);
+	if (ret < 0)
+		return ret;
+
+	drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+	return rcar_du_vga_connector_init(rcdu, renc);
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
new file mode 100644
index 000000000000..66b4d2d7190d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_vga.h  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_VGA_H__
+#define __RCAR_DU_VGA_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_vga_data;
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+		     const struct rcar_du_encoder_vga_data *data,
+		     unsigned int output);
+
+#endif /* __RCAR_DU_VGA_H__ */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
new file mode 100644
index 000000000000..80587fdbba3e
--- /dev/null
+++ b/include/linux/platform_data/rcar-du.h
@@ -0,0 +1,54 @@
+/*
+ * rcar_du.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_H__
+#define __RCAR_DU_H__
+
+#include <drm/drm_mode.h>
+
+enum rcar_du_encoder_type {
+	RCAR_DU_ENCODER_UNUSED = 0,
+	RCAR_DU_ENCODER_VGA,
+	RCAR_DU_ENCODER_LVDS,
+};
+
+struct rcar_du_panel_data {
+	unsigned int width_mm;		/* Panel width in mm */
+	unsigned int height_mm;		/* Panel height in mm */
+	struct drm_mode_modeinfo mode;
+};
+
+struct rcar_du_encoder_lvds_data {
+	struct rcar_du_panel_data panel;
+};
+
+struct rcar_du_encoder_vga_data {
+	/* TODO: Add DDC information for EDID retrieval */
+};
+
+struct rcar_du_encoder_data {
+	enum rcar_du_encoder_type encoder;
+	unsigned int output;
+
+	union {
+		struct rcar_du_encoder_lvds_data lvds;
+		struct rcar_du_encoder_vga_data vga;
+	} u;
+};
+
+struct rcar_du_platform_data {
+	struct rcar_du_encoder_data *encoders;
+	unsigned int num_encoders;
+};
+
+#endif /* __RCAR_DU_H__ */