This patch adds support for the pair of LCD controllers on the Marvell
Armada 510 (aka Dove) SoCs.  This driver supports:
- multiple contiguous scanout buffers for video and graphics
- shm backed cacheable buffer objects for X pixmaps for Vivante GPU
  acceleration
- dual lcd0 and lcd1 crt operation
- video overlay on each LCD crt
- page flipping of the main scanout buffers

Included in this commit is the core driver with no output support; output
support is platform and encoder driver dependent.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/gpu/drm/Kconfig             |    2 +
 drivers/gpu/drm/Makefile            |    1 +
 drivers/gpu/drm/dove/Kconfig        |   22 +
 drivers/gpu/drm/dove/Makefile       |    7 +
 drivers/gpu/drm/dove/dove_crtc.c    |  917 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/dove/dove_crtc.h    |   87 ++++
 drivers/gpu/drm/dove/dove_debugfs.c |  186 +++++++
 drivers/gpu/drm/dove/dove_drm.h     |   64 +++
 drivers/gpu/drm/dove/dove_drv.c     |  323 ++++++++++++
 drivers/gpu/drm/dove/dove_fb.c      |  156 ++++++
 drivers/gpu/drm/dove/dove_fb.h      |   21 +
 drivers/gpu/drm/dove/dove_fbdev.c   |  210 ++++++++
 drivers/gpu/drm/dove/dove_gem.c     |  420 ++++++++++++++++
 drivers/gpu/drm/dove/dove_gem.h     |   41 ++
 drivers/gpu/drm/dove/dove_hw.h      |  283 +++++++++++
 drivers/gpu/drm/dove/dove_ioctl.h   |  128 +++++
 drivers/gpu/drm/dove/dove_ioctlP.h  |   22 +
 drivers/gpu/drm/dove/dove_output.c  |  124 +++++
 drivers/gpu/drm/dove/dove_output.h  |   26 +
 drivers/gpu/drm/dove/dove_overlay.c |  514 ++++++++++++++++++++
 drivers/gpu/drm/dove/drm_helper.h   |   31 ++
 21 files changed, 3585 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/dove/Kconfig
 create mode 100644 drivers/gpu/drm/dove/Makefile
 create mode 100644 drivers/gpu/drm/dove/dove_crtc.c
 create mode 100644 drivers/gpu/drm/dove/dove_crtc.h
 create mode 100644 drivers/gpu/drm/dove/dove_debugfs.c
 create mode 100644 drivers/gpu/drm/dove/dove_drm.h
 create mode 100644 drivers/gpu/drm/dove/dove_drv.c
 create mode 100644 drivers/gpu/drm/dove/dove_fb.c
 create mode 100644 drivers/gpu/drm/dove/dove_fb.h
 create mode 100644 drivers/gpu/drm/dove/dove_fbdev.c
 create mode 100644 drivers/gpu/drm/dove/dove_gem.c
 create mode 100644 drivers/gpu/drm/dove/dove_gem.h
 create mode 100644 drivers/gpu/drm/dove/dove_hw.h
 create mode 100644 drivers/gpu/drm/dove/dove_ioctl.h
 create mode 100644 drivers/gpu/drm/dove/dove_ioctlP.h
 create mode 100644 drivers/gpu/drm/dove/dove_output.c
 create mode 100644 drivers/gpu/drm/dove/dove_output.h
 create mode 100644 drivers/gpu/drm/dove/dove_overlay.c
 create mode 100644 drivers/gpu/drm/dove/drm_helper.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 1e82882..db0a607 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/dove/Kconfig"
+
 source "drivers/gpu/drm/shmobile/Kconfig"

 source "drivers/gpu/drm/tegra/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0d59b24..2d2e593 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_DOVE) += dove/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-$(CONFIG_DRM_OMAP) += omapdrm/
diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
new file mode 100644
index 0000000..24844b6
--- /dev/null
+++ b/drivers/gpu/drm/dove/Kconfig
@@ -0,0 +1,22 @@
+config DRM_DOVE
+       tristate "DRM support for Marvell Dove"
+       depends on DRM && HAVE_CLK
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       select DRM_KMS_HELPER
+       help
+         Support the "LCD" controllers found on the Marvell Armada 510
+         Dove devices.  There are two controllers on the device, each
+         controller supports graphics and video overlays.
+
+         This driver provides no built-in acceleration; acceleration is
+         performed by other IP found on the SoC.  This driver provides
+         kernel mode setting and buffer management to userspace.
+
+if DRM_DOVE != n
+
+config DRM_DOVE_CURSOR
+       bool "Enable Dove DRM hardware cursor support"
+
+endif
diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
new file mode 100644
index 0000000..a2326c4
--- /dev/null
+++ b/drivers/gpu/drm/dove/Makefile
@@ -0,0 +1,7 @@
+ccflags-y := -Iinclude/drm
+
+dove-y                 := dove_crtc.o dove_drv.o dove_fb.o dove_fbdev.o \
+                          dove_gem.o dove_output.o dove_overlay.o
+dove-$(CONFIG_DEBUG_FS) += dove_debugfs.o
+
+obj-$(CONFIG_DRM_DOVE) := dove.o
diff --git a/drivers/gpu/drm/dove/dove_crtc.c b/drivers/gpu/drm/dove/dove_crtc.c
new file mode 100644
index 0000000..4c590d5
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_crtc.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "dove_crtc.h"
+#include "dove_drm.h"
+#include "dove_fb.h"
+#include "dove_gem.h"
+#include "dove_hw.h"
+
+/*
+ * A note about interlacing.  Let's consider HDMI 1920x1080i.
+ * The timing parameters we have from X are:
+ *  Hact HsyA HsyI Htot  Vact VsyA VsyI Vtot
+ *  1920 2448 2492 2640  1080 1084 1094 1125
+ * Which get translated to:
+ *  Hact HsyA HsyI Htot  Vact VsyA VsyI Vtot
+ *  1920 2448 2492 2640   540  542  547  562
+ *
+ * This is how it is defined by CEA-861-D - line and pixel numbers are
+ * referenced to the rising edge of VSYNC and HSYNC.  Total clocks per
+ * line: 2640.  The odd frame, the first active line is at line 21, and
+ * the even frame, the first active line is 584.
+ *
+ * LN:    560     561     562     563             567     568    569
+ * DE:    ~~~|____________________________//__________________________
+ * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
+ * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________
+ *  22 blanking lines.  VSYNC at 1320 (referenced to the HSYNC rising edge).
+ *
+ * LN:    1123   1124    1125      1               5       6      7
+ * DE:    ~~~|____________________________//__________________________
+ * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
+ * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________
+ *  23 blanking lines
+ *
+ * The Dove LCD Controller line and pixel numbers are, like X timings,
+ * referenced to the top left of the active frame.
+ *
+ * So, translating these to our LCD controller:
+ *  Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128.
+ *  Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448.
+ * Note: Vsync front porch remains constant!
+ *
+ * if (odd_frame) {
+ *   vtotal = mode->crtc_vtotal + 1;
+ *   vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1;
+ *   vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2
+ * } else {
+ *   vtotal = mode->crtc_vtotal;
+ *   vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay;
+ *   vhorizpos = mode->crtc_hsync_start;
+ * }
+ * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end;
+ *
+ * So, we need to reprogram these registers on each vsync event:
+ *  LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
+ *
+ * Note: we do not use the frame done interrupts because these appear
+ * to happen too early, and lead to jitter on the display (presumably
+ * they occur at the end of the last active line, before the vsync back
+ * porch, which we're reprogramming.)
+ */
+
+static void dove_updatel(uint32_t val, uint32_t mask, void __iomem *ptr)
+{
+       uint32_t ov, v;
+
+       ov = v = readl_relaxed(ptr);
+       v = (v & ~mask) | val;
+       if (ov != v)
+               writel_relaxed(v, ptr);
+}
+
+void dove_drm_crtc_update_regs(struct dove_crtc *dcrtc, struct dove_regs *regs)
+{
+       while (regs->offset != ~0) {
+               void __iomem *reg = dcrtc->base + regs->offset;
+               uint32_t val;
+
+               val = regs->mask;
+               if (val != 0)
+                       val &= readl_relaxed(reg);
+               writel_relaxed(val | regs->val, reg);
+               ++regs;
+       }
+}
+
+#define dpms_blanked(dpms)     ((dpms) != DRM_MODE_DPMS_ON)
+
+static void dove_drm_crtc_update(struct dove_crtc *dcrtc)
+{
+       uint32_t dumb_ctrl;
+
+       dumb_ctrl = dcrtc->cfg_dumb_ctrl;
+
+       if (!dpms_blanked(dcrtc->dpms))
+               dumb_ctrl |= CFG_DUMB_ENA;
+
+       /*
+        * When a dumb interface isn't under 24bit, it might be
+        * under SPI or GPIO.  If set to 7, this will force
+        * LCD_D[23:0] to output blank color and damage GPIO
+        * and SPI behaviour.  So leave it as-is unless in
+        * DUMB24_RGB888_0 mode.
+        */
+       if (dpms_blanked(dcrtc->dpms) &&
+           (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) {
+               dumb_ctrl &= ~DUMB_MASK;
+               dumb_ctrl |= DUMB_BLANK;
+       }
+
+       /*
+        * The spec is unclear about the polarities of the syncs.
+        * We assume their non-inverted state is active high.
+        */
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC)
+               dumb_ctrl |= CFG_INV_CSYNC;
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC)
+               dumb_ctrl |= CFG_INV_HSYNC;
+       if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC)
+               dumb_ctrl |= CFG_INV_VSYNC;
+
+       if (dcrtc->dumb_ctrl != dumb_ctrl) {
+               dcrtc->dumb_ctrl = dumb_ctrl;
+               writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL);
+       }
+}
+
+static unsigned dove_drm_crtc_calc_fb(struct drm_framebuffer *fb, int x, int y,
+       struct dove_regs *regs, bool interlaced)
+{
+       struct dove_gem_object *obj = drm_fb_obj(fb);
+       unsigned pitch = fb->pitches[0];
+       unsigned offset = y * pitch + x * fb->bits_per_pixel / 8;
+       uint32_t addr_odd, addr_even;
+       unsigned i = 0;
+
+       DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n",
+               pitch, x, y, fb->bits_per_pixel);
+
+       addr_odd = addr_even = obj->dev_addr + offset;
+
+       if (interlaced) {
+               addr_even += pitch;
+               pitch *= 2;
+       }
+
+       /* write offset, base, and pitch */
+       dove_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
+       dove_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
+       dove_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
+
+       return i;
+}
+
+static void dove_drm_crtc_finish_fb(struct dove_crtc *dcrtc,
+       struct drm_framebuffer *fb, bool force)
+{
+       struct dove_gem_object *obj;
+
+       if (!fb)
+               return;
+
+       obj = drm_fb_obj(fb);
+//     if (force || dpms_blanked(dcrtc->dpms)) {
+               /* Display is disabled, so just drop the old fb */
+               drm_gem_object_unreference_unlocked(&obj->obj);
+//     } else {
+//fixme                dcrtc->old_fb_obj = obj;
+//     }
+}
+
+static void dove_drm_crtc_complete_flip(struct dove_crtc *dcrtc,
+       struct timeval *now)
+{
+       struct drm_device *dev = dcrtc->crtc.dev;
+       struct dove_page_flip *flip = dcrtc->flip;
+       struct drm_pending_vblank_event *e = flip->event;
+       struct timeval tvbl;
+       unsigned seq;
+
+       dcrtc->flip = NULL;
+
+       drm_vblank_put(dev, dcrtc->num);
+       dove_drm_crtc_update_regs(dcrtc, flip->regs);
+       kfree(flip);
+
+       if (!e)
+               return;
+
+       seq = drm_vblank_count_and_time(dev, dcrtc->num, &tvbl);
+
+       /* Just like the i915 driver */
+       if (now) {
+               s64 ns_vbl, ns_now;
+
+               ns_vbl = timeval_to_ns(&tvbl);
+               ns_now = timeval_to_ns(now);
+
+               if (10 * (ns_now - ns_vbl) > 9 * dcrtc->crtc.framedur_ns) {
+                       seq++;
+                       tvbl = ns_to_timeval(ns_vbl + dcrtc->crtc.framedur_ns);
+               }
+       }
+
+       e->event.sequence = seq;
+       e->event.tv_sec = tvbl.tv_sec;
+       e->event.tv_usec = tvbl.tv_usec;
+
+       list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+       wake_up_interruptible(&e->base.file_priv->event_wait);
+}
+
+static void dove_drm_vblank_off(struct dove_crtc *dcrtc)
+{
+       struct drm_device *dev = dcrtc->crtc.dev;
+
+       /*
+        * Tell the DRM core that vblank IRQs aren't going to happen for
+        * a while.  This cleans up any pending vblank events for us.
+        */
+       drm_vblank_off(dev, dcrtc->num);
+
+       /* Handle any pending flip event. */
+       spin_lock_irq(&dev->event_lock);
+       if (dcrtc->flip)
+               dove_drm_crtc_complete_flip(dcrtc, NULL);
+       spin_unlock_irq(&dev->event_lock);
+}
+
+void dove_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, int 
idx)
+{
+}
+
+void dove_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, 
int idx)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void dove_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+
+       if (dcrtc->dpms != dpms) {
+               dcrtc->dpms = dpms;
+               dove_drm_crtc_update(dcrtc);
+               if (dpms_blanked(dpms))
+                       dove_drm_vblank_off(dcrtc);
+       }
+}
+
+/*
+ * Prepare for a mode set.  Turn off overlay to ensure that we don't end
+ * up with the overlay size being bigger than the active screen size.
+ * We rely upon X refreshing this state after the mode set has completed.
+ *
+ * The mode_config.mutex will be held for this call
+ */
+static void dove_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+
+       mutex_lock(&dev->struct_mutex);
+       if (dcrtc->doverlay)
+               dove_drm_overlay_off(dev, dcrtc->doverlay);
+       mutex_unlock(&dev->struct_mutex);
+}
+
+/* The mode_config.mutex will be held for this call */
+static void dove_drm_crtc_commit(struct drm_crtc *crtc)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+
+       if (dcrtc->dpms != DRM_MODE_DPMS_ON) {
+               dcrtc->dpms = DRM_MODE_DPMS_ON;
+               dove_drm_crtc_update(dcrtc);
+       }
+}
+
+/* The mode_config.mutex will be held for this call */
+static bool dove_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+       const struct drm_display_mode *mode, struct drm_display_mode *adj)
+{
+       return true;
+}
+
+void dove_drm_crtc_irq(struct dove_crtc *dcrtc, u32 stat)
+{
+       struct dove_vbl_event *e, *n;
+       struct timeval now;
+
+       do_gettimeofday(&now);
+
+       if (stat & DMA_FF_UNDERFLOW)
+               DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
+       if (stat & GRA_FF_UNDERFLOW)
+               DRM_ERROR("graphics underflow on crtc %u\n", dcrtc->num);
+
+       drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
+
+       spin_lock(&dcrtc->irq_lock);
+
+       list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
+               list_del_init(&e->node);
+               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+               e->fn(dcrtc, e->data);
+       }
+
+       if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
+               void __iomem *base = dcrtc->base;
+               int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
+               uint32_t val;
+
+               writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH);
+               writel_relaxed(dcrtc->v[i].spu_v_h_total, base + 
LCD_SPUT_V_H_TOTAL);
+
+               val = readl_relaxed(base + LCD_SPU_ADV_REG);
+               val &= ~(0xfff << 20 | 0xfff);
+               val |= dcrtc->v[i].spu_adv_reg;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
+       }
+       spin_unlock(&dcrtc->irq_lock);
+
+       /* Only on frame 0 IRQs (start of progressive / odd frame) */
+       if (stat & GRA_FRAME_IRQ0) {
+               struct drm_device *dev = dcrtc->crtc.dev;
+
+               spin_lock(&dev->event_lock);
+               if (dcrtc->flip)
+                       dove_drm_crtc_complete_flip(dcrtc, &now);
+
+//             if (dcrtc->old_fb_obj) {
+//             }
+               spin_unlock(&dev->event_lock);
+
+               wake_up(&dcrtc->frame_wait);
+       }
+
+}
+
+/* These are locked by dev->vbl_lock */
+void dove_drm_crtc_disable_irq(struct dove_crtc *dcrtc, u32 mask)
+{
+       if (dcrtc->irq_ena & mask) {
+               dcrtc->irq_ena &= ~mask;
+               writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+       }
+}
+
+void dove_drm_crtc_enable_irq(struct dove_crtc *dcrtc, u32 mask)
+{
+       if ((dcrtc->irq_ena & mask) != mask) {
+               dcrtc->irq_ena |= mask;
+               writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+               if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask)
+                       writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+       }
+}
+
+/* The mode_config.mutex will be held for this call */
+static int dove_drm_crtc_mode_set(struct drm_crtc *crtc,
+       struct drm_display_mode *mode, struct drm_display_mode *adj,
+       int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       struct dove_regs regs[16];
+       uint32_t lm, rm, tm, bm, val, rate, ref, div;
+       unsigned long flags;
+       unsigned i;
+       bool interlaced;
+
+       drm_gem_object_reference(&drm_fb_obj(crtc->fb)->obj);
+
+       interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
+
+       i = dove_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
+
+       rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
+       lm = adj->crtc_htotal - adj->crtc_hsync_end;
+       bm = adj->crtc_vsync_start - adj->crtc_vdisplay;
+       tm = adj->crtc_vtotal - adj->crtc_vsync_end;
+
+       DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n",
+               adj->crtc_hdisplay,
+               adj->crtc_hsync_start,
+               adj->crtc_hsync_end,
+               adj->crtc_htotal, lm, rm);
+       DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n",
+               adj->crtc_vdisplay,
+               adj->crtc_vsync_start,
+               adj->crtc_vsync_end,
+               adj->crtc_vtotal, tm, bm);
+
+       /* Wait for pending flips to complete */
+       wait_event(dcrtc->frame_wait, !dcrtc->flip);
+
+       drm_vblank_pre_modeset(crtc->dev, dcrtc->num);
+
+       crtc->mode = *adj;
+
+       val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA;
+       if (val != dcrtc->dumb_ctrl) {
+               dcrtc->dumb_ctrl = val;
+               writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
+       }
+
+       rate = adj->clock * 1000;
+       clk_set_rate(dcrtc->clk, rate);
+       ref = clk_get_rate(dcrtc->clk);
+       div = DIV_ROUND_UP(ref, rate);
+       if (div < 1)
+               div = 1;
+       dove_reg_queue_set(regs, i, div | dcrtc->cfg_sclk, LCD_CFG_SCLK_DIV);
+
+       if (interlaced ^ dcrtc->interlaced) {
+               if (adj->flags & DRM_MODE_FLAG_INTERLACE)
+                       drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+               else
+                       drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+               dcrtc->interlaced = interlaced;
+       }
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+
+       /* Even interlaced/progressive frame */
+       dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
+                                   adj->crtc_htotal;
+       dcrtc->v[1].spu_v_porch = tm << 16 | bm;
+       val = adj->crtc_hsync_start;
+       dcrtc->v[1].spu_adv_reg = val << 20 | val;
+
+       if (interlaced) {
+               /* Odd interlaced frame */
+               dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total +
+                                               (1 << 16);
+               dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
+               val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
+               dcrtc->v[0].spu_adv_reg = val << 20 | val;
+       } else {
+               dcrtc->v[0] = dcrtc->v[1];
+       }
+
+       val = (adj->crtc_vdisplay << 16) | adj->crtc_hdisplay;
+
+       dove_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
+       dove_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
+       dove_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
+       dove_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
+       dove_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
+       dove_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
+                          LCD_SPUT_V_H_TOTAL);
+       dove_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg | ADV_VSYNCOFFEN,
+                          (0xfff << 20 | 0xfff), LCD_SPU_ADV_REG);
+
+       val = dcrtc->cfg_dma_ctrl0;
+
+       switch (crtc->fb->pixel_format) {
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_ARGB1555:
+               val ^= CFG_GRA_SWAPRB;
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_ABGR1555:
+               val |= CFG_GRA_1555;
+               break;
+       case DRM_FORMAT_RGB565:
+               val ^= CFG_GRA_SWAPRB;
+       case DRM_FORMAT_BGR565:
+               val |= CFG_GRA_565;
+               break;
+       case DRM_FORMAT_RGB888:
+               val ^= CFG_GRA_SWAPRB;
+       case DRM_FORMAT_BGR888:
+               val |= CFG_GRA_888PACK;
+               break;
+       case DRM_FORMAT_XRGB8888:
+               val ^= CFG_GRA_SWAPRB;
+       case DRM_FORMAT_XBGR8888:
+               val |= CFG_GRA_X888;
+               break;
+       case DRM_FORMAT_ARGB8888:
+               val ^= CFG_GRA_SWAPRB;
+       case DRM_FORMAT_ABGR8888:
+               val |= CFG_GRA_8888;
+               break;
+       case DRM_FORMAT_C8:
+               val |= CFG_GRA_PSEUDO8 | CFG_PALETTE_ENA;
+               break;
+       }
+       if (interlaced)
+               val |= CFG_GRA_FTOGGLE;
+       dove_reg_queue_mod(regs, i, val, CFG_GRAFORMAT | CFG_GRA_SWAPRB |
+                          CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
+                          LCD_SPU_DMA_CTRL0);
+
+       val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
+       dove_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
+
+       /*
+        * Set the colorimetry, based upon the HDMI spec.
+        * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others
+        * use ITU601.  In any case, we select professional.
+        */
+       if ((adj->hdisplay == 1280 && adj->vdisplay == 720 && !interlaced) ||
+           (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
+               val = CFG_CSC_PROF | CFG_CSC_CCIR709;
+       } else {
+               val = CFG_CSC_PROF;
+       }
+       dove_reg_queue_mod(regs, i, val, CFG_CSC_MASK, LCD_SPU_IOPAD_CONTROL);
+       dove_reg_queue_end(regs, i);
+
+       dove_drm_crtc_update_regs(dcrtc, regs);
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+
+       dove_drm_crtc_update(dcrtc);
+
+       drm_vblank_post_modeset(crtc->dev, dcrtc->num);
+       dove_drm_crtc_finish_fb(dcrtc, old_fb, false);
+
+       return 0;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int dove_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+       struct drm_framebuffer *old_fb)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       struct dove_regs regs[4];
+       unsigned i;
+
+       i = dove_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs, 
dcrtc->interlaced);
+       dove_reg_queue_end(regs, i);
+
+       /* Wait for pending flips to complete */
+       wait_event(dcrtc->frame_wait, !dcrtc->flip);
+
+       drm_gem_object_reference(&drm_fb_obj(crtc->fb)->obj);
+       dove_drm_crtc_update_regs(dcrtc, regs);
+       dove_drm_crtc_finish_fb(dcrtc, old_fb, false);
+
+       return 0;
+}
+
+static void dove_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void dove_drm_crtc_disable(struct drm_crtc *crtc)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+
+       if (dcrtc->dpms != DRM_MODE_DPMS_OFF) {
+               dcrtc->dpms = DRM_MODE_DPMS_OFF;
+               dove_drm_crtc_update(dcrtc);
+       }
+       dove_drm_vblank_off(dcrtc);
+       dove_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
+}
+
+static const struct drm_crtc_helper_funcs dove_crtc_helper_funcs = {
+       .dpms           = dove_drm_crtc_dpms,
+       .prepare        = dove_drm_crtc_prepare,
+       .commit         = dove_drm_crtc_commit,
+       .mode_fixup     = dove_drm_crtc_mode_fixup,
+       .mode_set       = dove_drm_crtc_mode_set,
+       .mode_set_base  = dove_drm_crtc_mode_set_base,
+       .load_lut       = dove_drm_crtc_load_lut,
+       .disable        = dove_drm_crtc_disable,
+};
+
+#ifdef CONFIG_DRM_DOVE_CURSOR
+static int dove_drm_crtc_cursor_update(struct dove_crtc *dcrtc, bool reload)
+{
+       uint32_t xoff, xscr, w = dcrtc->cursor_w, s = w;
+       uint32_t yoff, yscr, h = dcrtc->cursor_h;
+
+       /*
+        * Calculate the visible width and height of the cursor,
+        * screen position, and the position in the cursor bitmap.
+        */
+       if (dcrtc->cursor_x < 0) {
+               xoff = -dcrtc->cursor_x;
+               xscr = 0;
+               w -= min(xoff, w);
+       } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) {
+               xoff = 0;
+               xscr = dcrtc->cursor_x;
+               w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0);
+       } else {
+               xoff = 0;
+               xscr = dcrtc->cursor_x;
+       }
+
+       if (dcrtc->cursor_y < 0) {
+               yoff = -dcrtc->cursor_y;
+               yscr = 0;
+               h -= min(yoff, h);
+       } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) {
+               yoff = 0;
+               yscr = dcrtc->cursor_y;
+               h = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_y, 0);
+       } else {
+               yoff = 0;
+               yscr = dcrtc->cursor_y;
+       }
+
+       if (dcrtc->interlaced) {
+               s *= 2;
+               yscr /= 2;
+               h /= 2;
+       }
+
+       if (!dcrtc->cursor_obj || !h || !w) {
+               spin_lock_irq(&dcrtc->irq_lock);
+               dove_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+               spin_unlock_irq(&dcrtc->irq_lock);
+               return 0;
+       }
+
+       dove_updatel(CFG_CSB_256x32, CFG_PDWN256x32,
+                    dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+       if (dcrtc->cursor_lw != w || dcrtc->cursor_lh != h || reload) {
+               struct dove_gem_object *obj = dcrtc->cursor_obj;
+               uint32_t *pix, *p, col2 = 0, col3 = 0;
+               unsigned x, y, d, n, a;
+
+               dcrtc->cursor_lw = w;
+               dcrtc->cursor_lh = h;
+
+               pix = phys_to_virt(obj->phys_addr);
+
+               /* Set the top-left corner of the cursor image */
+               pix += yoff * s + xoff;
+
+               a = 2 << 14 | 15 << 8;
+               for (d = n = y = 0; y < h; y++) {
+                       for (x = 0, p = &pix[y * s]; x < w; x++, p++) {
+                               uint32_t v = *p;
+                               unsigned b;
+
+                               if ((v & 0xff000000) != 0xff000000) {
+                                       b = 0;  /* transparent */
+                               } else if (col2 == v) {
+                                       b = 2;  /* color 2 */
+                               } else if (col3 == v) {
+                                       b = 3;  /* color 3 */
+                               } else if (col2 == 0) {
+                                       col2 = v;
+                                       b = 2;  /* alloc color 2 */
+                               } else if (col3 == 0) {
+                                       col3 = v;
+                                       b = 3;  /* alloc color 3 */
+                               } else {
+                                       /* fail */
+                                       b = 1;  /* inverse (!) */
+                               }
+
+                               d |= b << n;
+                               n += 2;
+
+                               if (n == 32) {
+                                       writel_relaxed(d, dcrtc->base + 
LCD_SPU_SRAM_WRDAT);
+                                       writel_relaxed(a, dcrtc->base + 
LCD_SPU_SRAM_CTRL);
+                                       a++;
+                                       d = n = 0;
+                               }
+                       }
+               }
+
+               if (n) {
+                       writel_relaxed(d, dcrtc->base + LCD_SPU_SRAM_WRDAT);
+                       writel_relaxed(a, dcrtc->base + LCD_SPU_SRAM_CTRL);
+               }
+
+               writel_relaxed(col2, dcrtc->base + LCD_SPU_ALPHA_COLOR1);
+               writel_relaxed(col3, dcrtc->base + LCD_SPU_ALPHA_COLOR2);
+               writel_relaxed(h << 16 | w, dcrtc->base + LCD_SPU_HWC_HPXL_VLN);
+       }
+
+       writel_relaxed(yscr << 16 | xscr, dcrtc->base + 
LCD_SPU_HWC_OVSA_HPXL_VLN);
+
+       spin_lock_irq(&dcrtc->irq_lock);
+       dove_updatel(CFG_HWC_ENA,
+                    CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
+                    dcrtc->base + LCD_SPU_DMA_CTRL0);
+       spin_unlock_irq(&dcrtc->irq_lock);
+
+       return 0;
+}
+
+static void cursor_update(void *data)
+{
+       dove_drm_crtc_cursor_update(data, true);
+}
+
+static int dove_drm_crtc_cursor_set(struct drm_crtc *crtc,
+       struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h)
+{
+       struct drm_device *dev = crtc->dev;
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       struct dove_gem_object *obj = NULL;
+       int ret;
+
+       if (handle && w > 0 && h > 0) {
+               /* maximum size is 64x64 */
+               if (w > 64 || h > 64)
+                       return -ENOMEM;
+
+               obj = dove_gem_object_lookup(dev, file, handle);
+               if (!obj)
+                       return -ENOENT;
+
+               /* Don't allow cursor to be in drm linear memory */
+               if (obj->linear) {
+                       drm_gem_object_unreference_unlocked(&obj->obj);
+                       return -EINVAL;
+               }
+
+               if (obj->obj.size < w * h * 4) {
+                       DRM_ERROR("buffer is too small\n");
+                       drm_gem_object_unreference_unlocked(&obj->obj);
+                       return -ENOMEM;
+               }
+       }
+
+       mutex_lock(&dev->struct_mutex);
+       if (dcrtc->cursor_obj) {
+               dcrtc->cursor_obj->update = NULL;
+               dcrtc->cursor_obj->update_data = NULL;
+               drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
+       }
+       dcrtc->cursor_obj = obj;
+       dcrtc->cursor_w = w;
+       dcrtc->cursor_h = h;
+       ret = dove_drm_crtc_cursor_update(dcrtc, true);
+       if (obj) {
+               obj->update_data = dcrtc;
+               obj->update = cursor_update;
+       }
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+static int dove_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+       struct drm_device *dev = crtc->dev;
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       dcrtc->cursor_x = x;
+       dcrtc->cursor_y = y;
+       ret = dove_drm_crtc_cursor_update(dcrtc, false);
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+#else
+#define dove_drm_crtc_cursor_set NULL
+#define dove_drm_crtc_cursor_move NULL
+#endif
+
+static void dove_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       struct dove_private *priv = crtc->dev->dev_private;
+
+       if (dcrtc->cursor_obj)
+               drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
+
+       priv->dcrtc[dcrtc->num] = NULL;
+       drm_crtc_cleanup(&dcrtc->crtc);
+       clk_disable_unprepare(dcrtc->clk);
+       clk_put(dcrtc->clk);
+       kfree(dcrtc);
+}
+
+/*
+ * The mode_config lock is held here, to prevent races between this
+ * and a mode_set.
+ */
+static int dove_drm_crtc_page_flip(struct drm_crtc *crtc,
+       struct drm_framebuffer *fb, struct drm_pending_vblank_event *event)
+{
+       struct dove_crtc *dcrtc = drm_to_dove_crtc(crtc);
+       struct dove_page_flip *flip;
+       struct drm_device *dev = crtc->dev;
+       struct drm_framebuffer *old_fb;
+       unsigned long flags;
+       unsigned i;
+       int ret;
+
+       /* We don't support changing the pixel format */
+       if (fb->pixel_format != crtc->fb->pixel_format)
+               return -EINVAL;
+
+       flip = kmalloc(sizeof(*flip), GFP_KERNEL);
+       if (!flip)
+               return -ENOMEM;
+
+       flip->event = event;
+       i = dove_drm_crtc_calc_fb(fb, crtc->x, crtc->y, flip->regs, 
dcrtc->interlaced);
+       dove_reg_queue_end(flip->regs, i);
+
+       ret = drm_vblank_get(dev, dcrtc->num);
+       if (ret) {
+               DRM_ERROR("failed to acquire vblank counter\n");
+               kfree(flip);
+               return ret;
+       }
+
+       drm_gem_object_reference(&drm_fb_obj(fb)->obj);
+
+       old_fb = dcrtc->crtc.fb;
+       dcrtc->crtc.fb = fb;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (!dcrtc->flip) {
+               dcrtc->flip = flip;
+       } else {
+               ret = -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (ret) {
+               dcrtc->crtc.fb = old_fb;
+               drm_gem_object_unreference_unlocked(&drm_fb_obj(fb)->obj);
+               drm_vblank_put(dev, dcrtc->num);
+               kfree(flip);
+       } else {
+               dove_drm_crtc_finish_fb(dcrtc, old_fb, false);
+       }
+
+       return 0;
+}
+
+static struct drm_crtc_funcs dove_crtc_funcs = {
+       .cursor_set     = dove_drm_crtc_cursor_set,
+       .cursor_move    = dove_drm_crtc_cursor_move,
+       .destroy        = dove_drm_crtc_destroy,
+       .page_flip      = dove_drm_crtc_page_flip,
+       .set_config     = drm_crtc_helper_set_config,
+};
+
+int dove_drm_crtc_create(struct drm_device *dev, unsigned num,
+       struct resource *res, struct clk *clk)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct dove_crtc *dcrtc;
+       void __iomem *base;
+       int ret;
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               DRM_ERROR("clk would not prepare and enable\n");
+               return ret;
+       }
+
+       base = devm_request_and_ioremap(dev->dev, res);
+       if (!base) {
+               DRM_ERROR("failed to ioremap register\n");
+               clk_disable_unprepare(clk);
+               return -ENOMEM;
+       }
+
+       dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
+       if (!dcrtc) {
+               DRM_ERROR("failed to allocate dove crtc\n");
+               clk_disable_unprepare(clk);
+               return -ENOMEM;
+       }
+
+       dcrtc->base = base;
+       dcrtc->num = num;
+       dcrtc->clk = clk;
+       dcrtc->cfg_sclk = 0xc0000000;
+       dcrtc->cfg_dma_ctrl0 = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
+       dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
+       spin_lock_init(&dcrtc->irq_lock);
+       dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
+       INIT_LIST_HEAD(&dcrtc->vbl_list);
+       init_waitqueue_head(&dcrtc->frame_wait);
+
+       /* Initialize some registers which we don't otherwise set */
+       writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR);
+       writel_relaxed(CFG_IOPAD_DUMB24, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
+       writel_relaxed(0x0000e000, dcrtc->base + LCD_SPU_SRAM_PARA1);
+       writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
+       writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+
+       /* Lower the watermark so to eliminate jitter at higher bandwidths */
+       dove_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+
+       /* Ensure AXI pipeline is enabled */
+       dove_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
+
+       priv->dcrtc[dcrtc->num] = dcrtc;
+
+       drm_crtc_init(dev, &dcrtc->crtc, &dove_crtc_funcs);
+       drm_crtc_helper_add(&dcrtc->crtc, &dove_crtc_helper_funcs);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/dove/dove_crtc.h b/drivers/gpu/drm/dove/dove_crtc.h
new file mode 100644
index 0000000..766e7ea
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_crtc.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_CRTC_H
+#define DOVE_CRTC_H
+
+struct dove_overlay;
+struct dove_gem_object;
+
+struct dove_regs {
+       uint32_t offset;
+       uint32_t mask;
+       uint32_t val;
+};
+
+#define dove_reg_queue_mod(_r, _i, _v, _m, _o) \
+       do {                                    \
+               struct dove_regs *__reg = _r;   \
+               __reg[_i].offset = _o;          \
+               __reg[_i].mask = ~(_m);         \
+               __reg[_i].val = _v;             \
+               _i++;                           \
+       } while (0)
+
+#define dove_reg_queue_set(_r, _i, _v, _o)     \
+       dove_reg_queue_mod(_r, _i, _v, ~0, _o)
+
+#define dove_reg_queue_end(_r, _i)             \
+       dove_reg_queue_mod(_r, _i, 0, 0, ~0)
+
+struct dove_page_flip {
+       struct drm_pending_vblank_event *event;
+       struct dove_regs                regs[4];
+};
+
+struct dove_crtc {
+       struct drm_crtc         crtc;
+       unsigned                num;
+       void __iomem            *base;
+       struct clk              *clk;
+       struct {
+               uint32_t        spu_v_h_total;
+               uint32_t        spu_v_porch;
+               uint32_t        spu_adv_reg;
+       } v[2];
+       bool                    interlaced;
+
+       struct dove_overlay     *doverlay;
+
+       struct dove_gem_object  *cursor_obj;
+       int                     cursor_x;
+       int                     cursor_y;
+       uint32_t                cursor_w;
+       uint32_t                cursor_h;
+       uint32_t                cursor_lw;
+       uint32_t                cursor_lh;
+
+       int                     dpms;
+       uint32_t                cfg_sclk;
+       uint32_t                cfg_dma_ctrl0;
+       uint32_t                cfg_dumb_ctrl;
+       uint32_t                dumb_ctrl;
+
+       wait_queue_head_t       frame_wait;
+       struct dove_page_flip   *flip;
+       struct dove_page_flip   flip_pending;
+
+       spinlock_t              irq_lock;
+       uint32_t                irq_ena;
+       struct list_head        vbl_list;
+};
+#define drm_to_dove_crtc(c) container_of(c, struct dove_crtc, crtc)
+
+int dove_drm_crtc_create(struct drm_device *, unsigned, struct resource *,
+       struct clk *);
+void dove_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
+void dove_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
+void dove_drm_crtc_irq(struct dove_crtc *, u32);
+void dove_drm_crtc_disable_irq(struct dove_crtc *, u32);
+void dove_drm_crtc_enable_irq(struct dove_crtc *, u32);
+void dove_drm_crtc_update_regs(struct dove_crtc *, struct dove_regs *);
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_debugfs.c 
b/drivers/gpu/drm/dove/dove_debugfs.c
new file mode 100644
index 0000000..6cb1b1d
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_debugfs.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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.
+ */
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include "drmP.h"
+#include "dove_crtc.h"
+#include "dove_drm.h"
+
+static int dove_debugfs_gem_offs_show(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct drm_gem_mm *mm = dev->mm_private;
+
+       return drm_mm_dump_table(m, &mm->offset_manager);
+}
+
+static int dove_debugfs_gem_linear_show(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = m->private;
+       struct dove_private *priv = node->minor->dev->dev_private;
+
+       return drm_mm_dump_table(m, &priv->linear);
+}
+
+static int dove_debugfs_reg_show(struct seq_file *m, void *data)
+{
+       struct drm_device *dev = m->private;
+       struct dove_private *priv = dev->dev_private;
+       int n, i;
+
+       if (priv) {
+               for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+                       struct dove_crtc *dcrtc = priv->dcrtc[n];
+                       if (!dcrtc)
+                               continue;
+
+                       for (i = 0x84; i <= 0x1c4; i += 4) {
+                               uint32_t v = readl_relaxed(dcrtc->base + i);
+                               seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int dove_debugfs_reg_r_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dove_debugfs_reg_show, inode->i_private);
+}
+
+static const struct file_operations fops_reg_r = {
+       .owner = THIS_MODULE,
+       .open = dove_debugfs_reg_r_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int dove_debugfs_write(struct file *file, const char __user *ptr,
+       size_t len, loff_t *off)
+{
+       struct drm_device *dev = file->private_data;
+       struct dove_private *priv = dev->dev_private;
+       struct dove_crtc *dcrtc = priv->dcrtc[0];
+       char buf[32], *p;
+       uint32_t reg, val;
+       int ret;
+
+       if (*off != 0)
+               return 0;
+
+       if (len > sizeof(buf) - 1)
+               len = sizeof(buf) - 1;
+
+       ret = strncpy_from_user(buf, ptr, len);
+       if (ret < 0)
+               return ret;
+       buf[len] = '\0';
+
+       reg = simple_strtoul(buf, &p, 16);
+       if (!isspace(*p))
+               return -EINVAL;
+       val = simple_strtoul(p + 1, NULL, 16);
+
+       if (reg >= 0x84 && reg <= 0x1c4)
+               writel(val, dcrtc->base + reg);
+
+       return len;
+}
+
+static int dove_debugfs_reg_w_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static const struct file_operations fops_reg_w = {
+       .owner = THIS_MODULE,
+       .open = dove_debugfs_reg_w_open,
+       .write = dove_debugfs_write,
+       .llseek = noop_llseek,
+};
+
+static struct drm_info_list dove_debugfs_list[] = {
+       { "gem_linear", dove_debugfs_gem_linear_show, 0 },
+       { "gem_offset", dove_debugfs_gem_offs_show, 0 },
+};
+#define DOVE_DEBUGFS_ENTRIES ARRAY_SIZE(dove_debugfs_list)
+
+static int drm_add_fake_info_node(struct drm_minor *minor, struct dentry *ent,
+       const void *key)
+{
+       struct drm_info_node *node;
+
+       node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
+       if (node == NULL) {
+               debugfs_remove(ent);
+               return -ENOMEM;
+       }
+
+       node->minor = minor;
+       node->dent = ent;
+       node->info_ent = (void *) key;
+
+       mutex_lock(&minor->debugfs_lock);
+       list_add(&node->list, &minor->debugfs_list);
+       mutex_unlock(&minor->debugfs_lock);
+
+       return 0;
+}
+
+static int dove_debugfs_create(struct dentry *root, struct drm_minor *minor,
+       const char *name, umode_t mode, const struct file_operations *fops)
+{
+       struct dentry *de;
+
+       de = debugfs_create_file(name, mode, root, minor->dev, fops);
+
+       return drm_add_fake_info_node(minor, de, fops);
+}
+
+int dove_drm_debugfs_init(struct drm_minor *minor)
+{
+       int ret;
+
+       ret = drm_debugfs_create_files(dove_debugfs_list, DOVE_DEBUGFS_ENTRIES,
+                                      minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+
+       ret = dove_debugfs_create(minor->debugfs_root, minor,
+                                  "reg", S_IFREG | S_IRUSR, &fops_reg_r);
+       if (ret)
+               goto err_1;
+
+       ret = dove_debugfs_create(minor->debugfs_root, minor,
+                               "reg_wr", S_IFREG | S_IWUSR, &fops_reg_w);
+       if (ret)
+               goto err_2;
+       return ret;
+
+ err_2:
+       drm_debugfs_remove_files((struct drm_info_list *) &fops_reg_r, 1, 
minor);
+ err_1:
+       drm_debugfs_remove_files(dove_debugfs_list, DOVE_DEBUGFS_ENTRIES,
+                                minor);
+       return ret;
+}
+
+void dove_drm_debugfs_cleanup(struct drm_minor *minor)
+{
+       drm_debugfs_remove_files((struct drm_info_list *) &fops_reg_w, 1, 
minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &fops_reg_r, 1, 
minor);
+       drm_debugfs_remove_files(dove_debugfs_list, DOVE_DEBUGFS_ENTRIES,
+                                minor);
+}
diff --git a/drivers/gpu/drm/dove/dove_drm.h b/drivers/gpu/drm/dove/dove_drm.h
new file mode 100644
index 0000000..d00a9d6
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_drm.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_DRM_H
+#define DOVE_DRM_H
+
+struct dove_crtc;
+struct dove_gem_object;
+struct dove_overlay;
+struct clk;
+
+static inline uint32_t dove_pitch(uint32_t width, uint32_t bpp)
+{
+       uint32_t pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
+
+       /* 88AP510 spec recommends pitch be a multiple of 128 */
+       return ALIGN(pitch, 128);
+}
+
+struct dove_vbl_event {
+       struct list_head        node;
+       void                    *data;
+       void                    (*fn)(struct dove_crtc *, void *);
+};
+void dove_drm_vbl_event_add(struct dove_crtc *, struct dove_vbl_event *);
+void dove_drm_vbl_event_remove(struct dove_crtc *, struct dove_vbl_event *);
+void dove_drm_vbl_event_remove_unlocked(struct dove_crtc *, struct 
dove_vbl_event *);
+#define dove_drm_vbl_event_init(_e,_f,_d) do { \
+       struct dove_vbl_event *__e = _e;        \
+       INIT_LIST_HEAD(&__e->node);             \
+       __e->data = _d;                         \
+       __e->fn = _f;                           \
+} while (0)
+
+
+struct dove_private {
+       struct drm_fb_helper    *fbdev;
+       struct dove_crtc        *dcrtc[2];
+       struct dove_overlay     *doverlay;
+       struct drm_mm           linear;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry           *de;
+#endif
+};
+
+extern const struct drm_mode_config_funcs dove_drm_mode_config_funcs;
+
+int dove_fbdev_init(struct drm_device *);
+void dove_fbdev_fini(struct drm_device *);
+
+int dove_overlay_create(struct drm_device *);
+void dove_overlay_destroy(struct drm_device *);
+void dove_drm_overlay_off(struct drm_device *, struct dove_overlay *);
+
+struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev);
+
+int dove_drm_debugfs_init(struct drm_minor *);
+void dove_drm_debugfs_cleanup(struct drm_minor *);
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_drv.c b/drivers/gpu/drm/dove/dove_drv.c
new file mode 100644
index 0000000..98cb25f
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_drv.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "dove_crtc.h"
+#include "dove_drm.h"
+#include "dove_gem.h"
+#include "dove_hw.h"
+#include "dove_ioctl.h"
+#include "dove_ioctlP.h"
+
+static int dove_drm_load(struct drm_device *dev, unsigned long flags)
+{
+       struct dove_private *priv;
+       struct resource *res[ARRAY_SIZE(priv->dcrtc)];
+       struct resource *mem = NULL;
+       int ret, n, i;
+
+       memset(res, 0, sizeof(res));
+
+       for (n = i = 0; ; n++) {
+               struct resource *r = platform_get_resource(dev->platformdev,
+                                                          IORESOURCE_MEM, n);
+               if (!r)
+                       break;
+
+               if (resource_size(r) > SZ_4K)
+                       mem = r;
+               else if (i < ARRAY_SIZE(priv->dcrtc))
+                       res[i++] = r;
+               else
+                       return -EINVAL;
+       }
+
+       if (!res[0] || !mem)
+               return -ENXIO;
+
+       if (!devm_request_mem_region(dev->dev, mem->start,
+                       resource_size(mem), "dove-drm"))
+               return -EBUSY;
+
+       priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               DRM_ERROR("failed to allocate private\n");
+               ret = -ENOMEM;
+               goto err_buf;
+       }
+
+       dev->dev_private = priv;
+
+       /* Mode setting support */
+       drm_mode_config_init(dev);
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+       dev->mode_config.max_width = 2048;
+       dev->mode_config.max_height = 2048;
+       dev->mode_config.preferred_depth = 24;
+       dev->mode_config.funcs = &dove_drm_mode_config_funcs;
+       drm_mm_init(&priv->linear, mem->start, resource_size(mem));
+
+       /* Create all LCD controllers */
+       for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+               struct clk *clk;
+
+               if (!res[n])
+                       break;
+
+               clk = clk_get_sys("dovefb.0", "extclk");
+               if (IS_ERR(clk)) {
+                       DRM_ERROR("failed to get clock\n");
+                       ret = PTR_ERR(clk);
+                       if (ret == -ENOENT)
+                               ret = -EPROBE_DEFER;
+                       goto err_kms;
+               }
+
+               ret = dove_drm_crtc_create(dev, n, res[n], clk);
+               if (ret) {
+                       clk_put(clk);
+                       goto err_kms;
+               }
+       }
+
+       ret = drm_vblank_init(dev, n);
+       if (ret)
+               goto err_kms;
+
+       ret = drm_irq_install(dev);
+       if (ret)
+               goto err_kms;
+
+       dev->vblank_disable_allowed = 1;
+
+       ret = dove_fbdev_init(dev);
+       if (ret)
+               goto err_irq;
+
+       ret = dove_overlay_create(dev);
+       if (ret)
+               goto err_irq;
+
+       drm_kms_helper_poll_init(dev);
+
+       return 0;
+
+ err_irq:
+       drm_irq_uninstall(dev);
+ err_kms:
+       drm_mode_config_cleanup(dev);
+       drm_mm_takedown(&priv->linear);
+ err_buf:
+
+       return ret;
+}
+
+static int dove_drm_unload(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+
+       drm_kms_helper_poll_fini(dev);
+       dove_overlay_destroy(dev);
+       dove_fbdev_fini(dev);
+       drm_irq_uninstall(dev);
+       drm_mode_config_cleanup(dev);
+       drm_mm_takedown(&priv->linear);
+       dev->dev_private = NULL;
+
+       return 0;
+}
+
+void dove_drm_vbl_event_add(struct dove_crtc *dcrtc, struct dove_vbl_event 
*evt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+       if (list_empty(&evt->node)) {
+               list_add_tail(&evt->node, &dcrtc->vbl_list);
+
+               drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+       }
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+void dove_drm_vbl_event_remove(struct dove_crtc *dcrtc,
+       struct dove_vbl_event *evt)
+{
+       if (!list_empty(&evt->node)) {
+               list_del_init(&evt->node);
+               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+       }
+}
+
+void dove_drm_vbl_event_remove_unlocked(struct dove_crtc *dcrtc, struct 
dove_vbl_event *evt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dcrtc->irq_lock, flags);
+       dove_drm_vbl_event_remove(dcrtc, evt);
+       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+/* These are called under the vbl_lock. */
+static int dove_drm_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct dove_private *priv = dev->dev_private;
+       dove_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+       return 0;
+}
+
+static void dove_drm_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct dove_private *priv = dev->dev_private;
+       dove_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+}
+
+static irqreturn_t dove_drm_irq_handler(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct dove_private *priv = dev->dev_private;
+       struct dove_crtc *dcrtc = priv->dcrtc[0];
+       uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+
+       /*
+        * This is rediculous - rather than writing bits to clear, we
+        * have to set the actual status register value.  This is racy.
+        */
+       writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+       /* Mask out those interrupts we haven't enabled */
+       v = stat & dcrtc->irq_ena;
+
+       if (v & VSYNC_IRQ)
+               dove_drm_crtc_irq(dcrtc, stat);
+
+       return (stat & VSYNC_IRQ) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int dove_drm_irq_postinstall(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct dove_crtc *dcrtc = priv->dcrtc[0];
+
+       spin_lock_irq(&dev->vbl_lock);
+       writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+       writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+       spin_unlock_irq(&dev->vbl_lock);
+
+       return 0;
+}
+
+static void dove_drm_irq_uninstall(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct dove_crtc *dcrtc = priv->dcrtc[0];
+
+       writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+}
+
+static struct drm_ioctl_desc dove_ioctls[] = {
+       DRM_IOCTL_DEF_DRV(DOVE_GEM_CREATE, dove_gem_create_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_GEM_CREATE_PHYS, dove_gem_create_phys_ioctl, 
DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_GEM_MMAP, dove_gem_mmap_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_GEM_PROP, dove_gem_prop_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_GEM_PWRITE, dove_gem_pwrite_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_OVERLAY_PUT_IMAGE, dove_overlay_put_image_ioctl, 
DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(DOVE_OVERLAY_ATTRS, dove_overlay_attrs_ioctl, 
DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+};
+
+static const struct file_operations dove_drm_fops = {
+       .owner                  = THIS_MODULE,
+       .llseek                 = no_llseek,
+       .read                   = drm_read,
+       .poll                   = drm_poll,
+       .unlocked_ioctl         = drm_ioctl,
+       .mmap                   = drm_gem_mmap,
+       .open                   = drm_open,
+       .release                = drm_release,
+       .fasync                 = drm_fasync,
+};
+
+extern const struct vm_operations_struct dove_gem_vm_ops;
+
+static struct drm_driver dove_drm_driver = {
+       .load                   = dove_drm_load,
+       .open                   = NULL,
+       .preclose               = NULL,
+       .postclose              = NULL,
+       .lastclose              = NULL,
+       .unload                 = dove_drm_unload,
+       .get_vblank_counter     = drm_vblank_count,
+       .enable_vblank          = dove_drm_enable_vblank,
+       .disable_vblank         = dove_drm_disable_vblank,
+       .irq_handler            = dove_drm_irq_handler,
+       .irq_postinstall        = dove_drm_irq_postinstall,
+       .irq_uninstall          = dove_drm_irq_uninstall,
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_init           = dove_drm_debugfs_init,
+       .debugfs_cleanup        = dove_drm_debugfs_cleanup,
+#endif
+       .gem_init_object        = NULL,
+       .gem_free_object        = dove_gem_free_object,
+       .prime_handle_to_fd     = NULL,
+       .prime_fd_to_handle     = NULL,
+       .gem_prime_export       = NULL,
+       .gem_prime_import       = NULL,
+       .dumb_create            = dove_gem_dumb_create,
+       .dumb_map_offset        = dove_gem_dumb_map_offset,
+       .dumb_destroy           = dove_gem_dumb_destroy,
+       .gem_vm_ops             = &dove_gem_vm_ops,
+       .major                  = 1,
+       .minor                  = 0,
+       .name                   = "dove-drm",
+       .desc                   = "Dove SoC DRM",
+       .date                   = "20120730",
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_HAVE_IRQ,
+       .ioctls                 = dove_ioctls,
+       .fops                   = &dove_drm_fops,
+};
+
+static int dove_drm_probe(struct platform_device *pdev)
+{
+       return drm_platform_init(&dove_drm_driver, pdev);
+}
+
+static int dove_drm_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&dove_drm_driver, pdev);
+       return 0;
+}
+
+static struct platform_driver dove_drm_platform_driver = {
+       .probe  = dove_drm_probe,
+       .remove = dove_drm_remove,
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "dove-drm",
+       },
+};
+
+static int __init dove_drm_init(void)
+{
+       dove_drm_driver.num_ioctls = DRM_ARRAY_SIZE(dove_ioctls);
+       return platform_driver_register(&dove_drm_platform_driver);
+}
+module_init(dove_drm_init);
+
+static void __exit dove_drm_exit(void)
+{
+       platform_driver_unregister(&dove_drm_platform_driver);
+}
+module_exit(dove_drm_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel at arm.linux.org.uk>");
+MODULE_DESCRIPTION("Dove DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dove-drm");
diff --git a/drivers/gpu/drm/dove/dove_fb.c b/drivers/gpu/drm/dove/dove_fb.c
new file mode 100644
index 0000000..1286b4b
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_fb.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ */
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
+#include "dove_drm.h"
+#include "dove_fb.h"
+#include "dove_gem.h"
+
+static void dove_fb_destroy(struct drm_framebuffer *fb)
+{
+       struct dove_framebuffer *dfb = drm_fb_to_dove_fb(fb);
+
+       drm_framebuffer_cleanup(&dfb->fb);
+       if (dfb->obj)
+               drm_gem_object_unreference_unlocked(&dfb->obj->obj);
+       kfree(dfb);
+}
+
+static int dove_fb_create_handle(struct drm_framebuffer *fb,
+       struct drm_file *dfile, unsigned int *handle)
+{
+       struct dove_framebuffer *dfb = drm_fb_to_dove_fb(fb);
+       return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
+}
+
+static const struct drm_framebuffer_funcs dove_fb_funcs = {
+       .destroy        = dove_fb_destroy,
+       .create_handle  = dove_fb_create_handle,
+};
+
+/*
+ * Supported pixel formats:
+ *             T       R       G       B       BPP
+ * RGB565              [15:11] [10:5]  [4:0]   16      DRM_FORMAT_RGB565
+ * BGR565              [4:0]   [10:5]  [15:11] 16      DRM_FORMAT_BGR565
+ * RGB1555     15      [14:10] [9:5]   [4:0]   16      DRM_FORMAT_ARGB1555
+ * BGR1555     15      [4:0]   [9:5]   [14:10] 16      DRM_FORMAT_ABGR1555
+ * RGB888PACK          [23:16] [15:8]  [7:0]   24      DRM_FORMAT_RGB888
+ * BGR888PACK          [7:0]   [15:8]  [23:16] 24      DRM_FORMAT_BGR888
+ * RGB888UNPACK                [23:16] [15:8]  [7:0]   32      
DRM_FORMAT_XRGB8888
+ * BGR888UNPACK                [7:0]   [15:8]  [23:16] 32      
DRM_FORMAT_XBGR8888
+ * RGBA888     [31:24] [23:16] [15:8]  [7:0]   32      DRM_FORMAT_ARGB8888
+ * BGRA888     [31:24] [7:0]   [15:8]  [23:16] 32      DRM_FORMAT_ABGR8888
+ *
+ * We don't currently support (note that the YUV pixel fields
+ * are incorrect, coming from the dovefb driver):
+ *                     Y       U       V       BPP
+ * YUV422PACK          [15:8]  [7:4]   [3:0]   16
+ * YVU422PACK          [7:0]   [11:8]  [15:12] 16
+ * YUV422PLANAR                [15:8]  [7:4]   [3:0]   16
+ * YVU422PLANAR                [7:0]   [11:8]  [15:12] 16
+ * YUV420PLANAR                [11:4]  [3:2]   [1:0]   12
+ * YVU420PLANAR                [7:0]   [9:8]   [11:10] 12
+ * PSEUDOCOLOR                                 8
+ * UYVY422PACK         [11:4]  [15:12] [3:0]   16
+ */
+int dove_framebuffer_create(struct drm_device *dev,
+       struct dove_framebuffer **dfbp, struct drm_mode_fb_cmd2 *mode,
+       struct dove_gem_object *obj)
+{
+       struct dove_framebuffer *dfb;
+       int ret;
+
+       switch (mode->pixel_format) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_BGR565:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_BGR888:
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_ABGR8888:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
+       if (!dfb) {
+               DRM_ERROR("failed to allocate dove fb object\n");
+               return -ENOMEM;
+       }
+
+       ret = drm_framebuffer_init(dev, &dfb->fb, &dove_fb_funcs);
+       if (ret) {
+               kfree(dfb);
+               return ret;
+       }
+
+       drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
+
+       /*
+        * Take a reference on our object - the caller is expected
+        * to drop their reference to it.
+        */
+       drm_gem_object_reference(&obj->obj);
+       dfb->obj = obj;
+       *dfbp = dfb;
+
+       return 0;
+}
+
+static struct drm_framebuffer *dove_fb_create(struct drm_device *dev,
+       struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
+{
+       struct dove_gem_object *obj;
+       struct dove_framebuffer *dfb;
+       int ret;
+
+       DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n",
+               mode->width, mode->height, mode->pixel_format,
+               mode->flags, mode->pitches[0], mode->pitches[1],
+               mode->pitches[2]);
+
+       /* We can only handle a single plane at the moment */
+       if (drm_format_num_planes(mode->pixel_format) > 1)
+               return ERR_PTR(-EINVAL);
+
+       obj = dove_gem_object_lookup(dev, dfile, mode->handles[0]);
+       if (!obj) {
+               DRM_ERROR("failed to lookup gem object\n");
+               return ERR_PTR(-ENOENT);
+       }
+
+       ret = dove_framebuffer_create(dev, &dfb, mode, obj);
+       drm_gem_object_unreference_unlocked(&obj->obj);
+
+       if (ret) {
+               DRM_ERROR("failed to initialize framebuffer\n");
+               return ERR_PTR(ret);
+       }
+
+       return &dfb->fb;
+}
+
+static void dove_output_poll_changed(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh = priv->fbdev;
+
+       if (fbh)
+               drm_fb_helper_hotplug_event(fbh);
+}
+
+const struct drm_mode_config_funcs dove_drm_mode_config_funcs = {
+       .fb_create              = dove_fb_create,
+       .output_poll_changed    = dove_output_poll_changed,
+};
diff --git a/drivers/gpu/drm/dove/dove_fb.h b/drivers/gpu/drm/dove/dove_fb.h
new file mode 100644
index 0000000..69eb274
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_fb.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_FB_H
+#define DOVE_FB_H
+
+struct dove_framebuffer {
+       struct drm_framebuffer  fb;
+       struct dove_gem_object  *obj;
+};
+#define drm_fb_to_dove_fb(dfb) container_of(dfb, struct dove_framebuffer, fb)
+#define drm_fb_obj(fb) drm_fb_to_dove_fb(fb)->obj
+
+int dove_framebuffer_create(struct drm_device *, struct dove_framebuffer **,
+       struct drm_mode_fb_cmd2 *, struct dove_gem_object *);
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_fbdev.c 
b/drivers/gpu/drm/dove/dove_fbdev.c
new file mode 100644
index 0000000..35b676c
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_fbdev.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Written from the i915 driver.
+ *
+ * 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.
+ */
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "drmP.h"
+#include "drm_fb_helper.h"
+#include "dove_crtc.h"
+#include "dove_drm.h"
+#include "dove_fb.h"
+#include "dove_gem.h"
+
+static /*const*/ struct fb_ops dove_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = drm_fb_helper_check_var,
+       .fb_set_par     = drm_fb_helper_set_par,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_blank       = drm_fb_helper_blank,
+       .fb_setcmap     = drm_fb_helper_setcmap,
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int dove_fb_create(struct drm_fb_helper *fbh,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_device *dev = fbh->dev;
+       struct drm_mode_fb_cmd2 mode;
+       struct dove_framebuffer *dfb;
+       struct dove_gem_object *obj;
+       struct fb_info *info;
+       int size, ret;
+
+       memset(&mode, 0, sizeof(mode));
+       mode.width = sizes->surface_width;
+       mode.height = sizes->surface_height;
+       mode.pitches[0] = dove_pitch(mode.width, sizes->surface_bpp);
+       mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+                                       sizes->surface_depth);
+
+       size = ALIGN(mode.pitches[0] * mode.height, PAGE_SIZE);
+       obj = dove_gem_alloc_private_object(dev, size);
+       if (!obj) {
+               DRM_ERROR("failed to allocate fb memory\n");
+               return -ENOMEM;
+       }
+
+       ret = dove_gem_linear_back(dev, obj);
+       if (ret) {
+               drm_gem_object_unreference_unlocked(&obj->obj);
+               return ret;
+       }
+
+       ret = dove_framebuffer_create(dev, &dfb, &mode, obj);
+       if (ret)
+               goto err_fbcreate;
+
+       mutex_lock(&dev->struct_mutex);
+
+       info = framebuffer_alloc(0, dev->dev);
+       if (!info) {
+               ret = -ENOMEM;
+               goto err_fballoc;
+       }
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret) {
+               ret = -ENOMEM;
+               goto err_fbcmap;
+       }
+
+       strlcpy(info->fix.id, "dove-drmfb", sizeof(info->fix.id));
+       info->par = fbh;
+       info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+       info->fbops = &dove_fb_ops;
+       info->fix.smem_start = obj->phys_addr;
+       info->fix.smem_len = size;
+       info->screen_size = size;
+       info->screen_base = ioremap_wc(info->fix.smem_start, size);
+       if (!info->screen_base) {
+               ret = -ENOMEM;
+               goto err_ioremap;
+       }
+
+       fbh->fb = &dfb->fb;
+       fbh->fbdev = info;
+       drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
+       drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
+
+       DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08x\n",
+               dfb->fb.width, dfb->fb.height,
+               dfb->fb.bits_per_pixel, obj->phys_addr);
+
+       drm_gem_object_unreference(&obj->obj);
+       mutex_unlock(&dev->struct_mutex);
+
+       return 0;
+
+ err_ioremap:
+       fb_dealloc_cmap(&info->cmap);
+ err_fbcmap:
+       framebuffer_release(info);
+ err_fballoc:
+       mutex_unlock(&dev->struct_mutex);
+       dfb->fb.funcs->destroy(&dfb->fb);
+ err_fbcreate:
+       drm_gem_object_unreference_unlocked(&obj->obj);
+       return ret;
+}
+
+static void dove_fb_destroy(struct drm_fb_helper *fbh)
+{
+       struct fb_info *info = fbh->fbdev;
+
+       if (info) {
+               unregister_framebuffer(info);
+               iounmap(info->screen_base);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+               framebuffer_release(info);
+       }
+
+       if (fbh->fb)
+               fbh->fb->funcs->destroy(fbh->fb);
+
+       drm_fb_helper_fini(fbh);
+}
+
+static int dove_fb_probe(struct drm_fb_helper *fbh,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       int ret = 0;
+
+       if (!fbh->fb) {
+               ret = dove_fb_create(fbh, sizes);
+               if (ret == 0)
+                       ret = 1;
+       }
+       return ret;
+}
+
+static struct drm_fb_helper_funcs dove_fb_helper_funcs = {
+       .gamma_set      = dove_drm_crtc_gamma_set,
+       .gamma_get      = dove_drm_crtc_gamma_get,
+       .fb_probe       = dove_fb_probe,
+};
+
+int dove_fbdev_init(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh;
+       int ret;
+
+       fbh = kzalloc(sizeof(*fbh), GFP_KERNEL);
+       if (!fbh)
+               return -ENOMEM;
+
+       priv->fbdev = fbh;
+
+       fbh->funcs = &dove_fb_helper_funcs;
+
+       ret = drm_fb_helper_init(dev, fbh, 1, 1);
+       if (ret) {
+               DRM_ERROR("failed to initialize drm fb helper\n");
+               goto err_fb_helper;
+       }
+
+       ret = drm_fb_helper_single_add_all_connectors(fbh);
+       if (ret) {
+               DRM_ERROR("failed to add fb connectors\n");
+               goto err_fb_setup;
+       }
+
+       ret = drm_fb_helper_initial_config(fbh, 32);
+       if (ret) {
+               DRM_ERROR("failed to set initial config\n");
+               goto err_fb_setup;
+       }
+
+       return 0;
+ err_fb_setup:
+       drm_fb_helper_fini(fbh);
+ err_fb_helper:
+       kfree(fbh);
+       priv->fbdev = NULL;
+       return ret;
+}
+
+void dove_fbdev_fini(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct drm_fb_helper *fbh = priv->fbdev;
+
+       if (fbh) {
+               dove_fb_destroy(fbh);
+               kfree(fbh);
+               priv->fbdev = NULL;
+       }
+}
diff --git a/drivers/gpu/drm/dove/dove_gem.c b/drivers/gpu/drm/dove/dove_gem.c
new file mode 100644
index 0000000..2aee511
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_gem.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ */
+#include <linux/shmem_fs.h>
+#include "drmP.h"
+#include "dove_drm.h"
+#include "dove_gem.h"
+#include "dove_ioctl.h"
+#include "dove_ioctlP.h"
+
+static int dove_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct dove_gem_object *obj = drm_to_dove_gem(vma->vm_private_data);
+       unsigned long addr = (unsigned long)vmf->virtual_address;
+       unsigned long pfn = obj->phys_addr >> PAGE_SHIFT;
+       int ret;
+
+       pfn += (addr - vma->vm_start) >> PAGE_SHIFT;
+       ret = vm_insert_pfn(vma, addr, pfn);
+
+       switch (ret) {
+       case -EIO:
+       case -EAGAIN:
+               set_need_resched();
+       case 0:
+       case -ERESTARTSYS:
+       case -EINTR:
+               return VM_FAULT_NOPAGE;
+       case -ENOMEM:
+               return VM_FAULT_OOM;
+       default:
+               return VM_FAULT_SIGBUS;
+       }
+}
+
+const struct vm_operations_struct dove_gem_vm_ops = {
+       .fault  = dove_gem_vm_fault,
+       .open   = drm_gem_vm_open,
+       .close  = drm_gem_vm_close,
+};
+
+static size_t roundup_gem_size(size_t size)
+{
+       return roundup(size, PAGE_SIZE);
+}
+
+void dove_gem_free_object(struct drm_gem_object *obj)
+{
+       struct dove_gem_object *dobj = drm_to_dove_gem(obj);
+
+       DRM_DEBUG_DRIVER("release obj %p\n", dobj);
+
+       if (dobj->linear)
+               drm_mm_put_block(dobj->linear);
+
+       if (dobj->obj.map_list.map)
+               drm_gem_free_mmap_offset(&dobj->obj);
+
+       if (dobj->page) {
+               unsigned int order = get_order(dobj->obj.size);
+               __free_pages(dobj->page, order);
+       }
+
+       drm_gem_object_release(&dobj->obj);
+
+       kfree(dobj);
+}
+
+int dove_gem_linear_back(struct drm_device *dev, struct dove_gem_object *obj)
+{
+       struct dove_private *priv = dev->dev_private;
+       size_t size = obj->obj.size;
+
+       if (obj->page || obj->linear)
+               return 0;
+
+       /* If it is a small allocation, try to get it from the system */
+       if (size < 1048576) {
+               unsigned int order = get_order(size);
+               struct page *p = alloc_pages(GFP_KERNEL, order);
+
+               if (p) {
+                       unsigned sz = (size + 31) & ~31;
+                       uintptr_t ptr;
+
+                       obj->phys_addr = page_to_phys(p);
+                       obj->dev_addr = obj->phys_addr;
+
+                       /* FIXME: Hack around dirty cache */
+                       ptr = (uintptr_t)phys_to_virt(obj->phys_addr);
+                       memset((void *)ptr, 0, PAGE_ALIGN(size));
+                       while (sz) {
+                               asm volatile("mcr p15, 0, %0, c7, c14, 1" : : 
"r" (ptr));
+                               ptr += 32;
+                               sz -= 32;
+                       }
+                       dsb();
+
+                       obj->page = p;
+               }
+       }
+
+       /* Otherwise, grab it from our linear allocation */
+       if (!obj->page) {
+               struct drm_mm_node *free;
+               unsigned align = min_t(unsigned, size, SZ_2M);
+               void __iomem *ptr;
+
+               free = drm_mm_search_free(&priv->linear, size, align, 0);
+               if (free)
+                       obj->linear = drm_mm_get_block(free, size, align);
+               if (!obj->linear)
+                       return -ENOSPC;
+
+               /* Ensure that the memory we're returning is cleared. */
+               ptr = ioremap_wc(obj->linear->start, size);
+               if (!ptr) {
+                       drm_mm_put_block(obj->linear);
+                       obj->linear = NULL;
+                       return -ENOMEM;
+               }
+
+               memset_io(ptr, 0, size);
+               iounmap(ptr);
+
+               obj->phys_addr = obj->linear->start;
+               obj->dev_addr = obj->linear->start;
+       }
+
+       DRM_DEBUG_DRIVER("obj %p phys %#x dev %#x\n",
+                        obj, obj->phys_addr, obj->dev_addr);
+
+       return 0;
+}
+
+struct dove_gem_object *dove_gem_alloc_private_object(struct drm_device *dev,
+       size_t size)
+{
+       struct dove_gem_object *obj;
+
+       size = roundup_gem_size(size);
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return NULL;
+
+       if (drm_gem_private_object_init(dev, &obj->obj, size)) {
+               kfree(obj);
+               return NULL;
+       }
+
+       DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
+
+       return obj;
+}
+
+struct dove_gem_object *dove_gem_alloc_object(struct drm_device *dev,
+       size_t size)
+{
+       struct dove_gem_object *obj;
+       struct address_space *mapping;
+
+       size = roundup_gem_size(size);
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               return NULL;
+
+       if (drm_gem_object_init(dev, &obj->obj, size)) {
+               kfree(obj);
+               return NULL;
+       }
+
+       mapping = obj->obj.filp->f_path.dentry->d_inode->i_mapping;
+       mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE);
+
+       DRM_DEBUG_DRIVER("alloc obj %p size %zu\n", obj, size);
+
+       return obj;
+}
+
+/* Dumb alloc support */
+int dove_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+       struct drm_mode_create_dumb *args)
+{
+       struct dove_gem_object *dobj;
+       u32 handle;
+       size_t size;
+       int ret;
+
+       args->pitch = dove_pitch(args->width, args->bpp);
+       args->size = size = args->pitch * args->height;
+
+       dobj = dove_gem_alloc_private_object(dev, size);
+       if (dobj == NULL)
+               return -ENOMEM;
+
+       ret = dove_gem_linear_back(dev, dobj);
+       if (ret)
+               goto err;
+
+       ret = drm_gem_handle_create(file, &dobj->obj, &handle);
+       if (ret)
+               goto err;
+
+       args->handle = handle;
+
+       /* drop reference from allocate - handle holds it now */
+       DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
+ err:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
+
+int dove_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+       uint32_t handle, uint64_t *offset)
+{
+       struct dove_gem_object *obj;
+       int ret = 0;
+
+       mutex_lock(&dev->struct_mutex);
+       obj = dove_gem_object_lookup(dev, file, handle);
+       if (!obj) {
+               DRM_ERROR("failed to lookup gem object\n");
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+
+       if (!obj->obj.map_list.map)
+               ret = drm_gem_create_mmap_offset(&obj->obj);
+
+       if (ret == 0) {
+               *offset = (u64)obj->obj.map_list.hash.key << PAGE_SHIFT;
+               DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
+       }
+
+       drm_gem_object_unreference(&obj->obj);
+ err_unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+int dove_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+       uint32_t handle)
+{
+       return drm_gem_handle_delete(file, handle);
+}
+
+/* Private driver gem ioctls */
+int dove_gem_create_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_gem_create *args = data;
+       struct dove_gem_object *dobj;
+       size_t size;
+       u32 handle;
+       int ret;
+
+       if (args->size == 0) {
+               args->pitch = dove_pitch(args->width, args->bpp);
+               args->size = size = args->pitch * args->height;
+       } else {
+               size = args->size;
+       }
+
+       dobj = dove_gem_alloc_object(dev, size);
+       if (dobj == NULL)
+               return -ENOMEM;
+
+       ret = drm_gem_handle_create(file, &dobj->obj, &handle);
+       if (ret)
+               goto err;
+
+       args->handle = handle;
+
+       /* drop reference from allocate - handle holds it now */
+       DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
+ err:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
+
+int dove_gem_create_phys_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_gem_create_phys *arg = data;
+       struct dove_gem_object *dobj;
+       u32 handle;
+       int ret;
+
+       dobj = dove_gem_alloc_private_object(dev, arg->size);
+       if (dobj) {
+               dobj->phys_addr = arg->phys;
+               dobj->dev_addr = arg->phys;
+       }
+
+       ret = drm_gem_handle_create(file, &dobj->obj, &handle);
+       if (ret) {
+               drm_gem_object_release(&dobj->obj);
+               return ret;
+       }
+
+       /* drop reference from allocate - handle holds it now */
+       drm_gem_object_unreference(&dobj->obj);
+
+       arg->handle = handle;
+
+       return 0;
+}
+
+/* Map a shmem-backed object into process memory space */
+int dove_gem_mmap_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_gem_mmap *args = data;
+       struct dove_gem_object *dobj;
+       unsigned long addr;
+
+       dobj = dove_gem_object_lookup(dev, file, args->handle);
+       if (dobj == NULL)
+               return -ENOENT;
+
+       if (!dobj->obj.filp) {
+               drm_gem_object_unreference(&dobj->obj);
+               return -EINVAL;
+       }
+
+       addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE,
+                      MAP_SHARED, args->offset);
+       drm_gem_object_unreference(&dobj->obj);
+       if (IS_ERR_VALUE(addr))
+               return addr;
+
+       args->addr = addr;
+
+       return 0;
+}
+
+int dove_gem_prop_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_gem_prop *arg = data;
+       struct dove_gem_object *dobj;
+       int ret;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       dobj = dove_gem_object_lookup(dev, file, arg->handle);
+       if (dobj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+
+       arg->phys = dobj->phys_addr;
+       ret = 0;
+       drm_gem_object_unreference(&dobj->obj);
+
+ unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+}
+
+int dove_gem_pwrite_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_gem_pwrite *args = data;
+       struct dove_gem_object *dobj;
+       char __user *ptr;
+       int ret;
+
+       DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n",
+               args->handle, args->offset, args->size, args->ptr);
+
+       if (args->size == 0)
+               return 0;
+
+       ptr = (char __user *)(uintptr_t)args->ptr;
+
+       if (!access_ok(VERIFY_READ, ptr, args->size))
+               return -EFAULT;
+
+       ret = fault_in_multipages_readable(ptr, args->size);
+       if (ret)
+               return ret;
+
+       dobj = dove_gem_object_lookup(dev, file, args->handle);
+       if (dobj == NULL)
+               return -ENOENT;
+
+       /* Don't allow pwrite to drm linear backed objects */
+       if (dobj->linear)
+               return -EINVAL;
+
+       if (args->offset > dobj->obj.size ||
+           args->size > dobj->obj.size - args->offset) {
+               DRM_ERROR("invalid size: object size %u\n", dobj->obj.size);
+               ret = -EINVAL;
+               goto unref;
+       }
+
+       {
+               void *data = phys_to_virt(dobj->phys_addr) + args->offset;
+               ret = copy_from_user(data, ptr, args->size) ? -EFAULT : 0;
+
+               if (ret == 0 && dobj->update)
+                       dobj->update(dobj->update_data);
+       }
+
+  unref:
+       drm_gem_object_unreference_unlocked(&dobj->obj);
+       return ret;
+}
diff --git a/drivers/gpu/drm/dove/dove_gem.h b/drivers/gpu/drm/dove/dove_gem.h
new file mode 100644
index 0000000..324f23d
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_gem.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_GEM_H
+#define DOVE_GEM_H
+
+/* GEM */
+struct dove_gem_object {
+       struct drm_gem_object   obj;
+       struct drm_mm_node      *linear;
+       phys_addr_t             phys_addr;
+       resource_size_t         dev_addr;
+       struct page             *page;
+       void                    (*update)(void *);
+       void                    *update_data;
+};
+
+#define drm_to_dove_gem(o) container_of(o, struct dove_gem_object, obj)
+
+void dove_gem_free_object(struct drm_gem_object *);
+int dove_gem_linear_back(struct drm_device *, struct dove_gem_object *);
+struct dove_gem_object *dove_gem_alloc_private_object(struct drm_device *, 
size_t);
+int dove_gem_dumb_create(struct drm_file *, struct drm_device *,
+       struct drm_mode_create_dumb *);
+int dove_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
+       uint32_t, uint64_t *);
+int dove_gem_dumb_destroy(struct drm_file *, struct drm_device *,
+       uint32_t);
+
+static inline struct dove_gem_object *dove_gem_object_lookup(
+       struct drm_device *dev, struct drm_file *dfile, unsigned handle)
+{
+       struct drm_gem_object *obj = drm_gem_object_lookup(dev, dfile, handle);
+
+       return obj ? drm_to_dove_gem(obj) : NULL;
+}
+#endif
diff --git a/drivers/gpu/drm/dove/dove_hw.h b/drivers/gpu/drm/dove/dove_hw.h
new file mode 100644
index 0000000..91e022f
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_hw.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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 DOVE_HW_H
+#define DOVE_HW_H
+
+/*
+ * Note: the following registers are written from IRQ context:
+ *  LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
+ *  LCD_SPU_DMA_START_ADDR_[YUV][01], LCD_SPU_DMA_PITCH_YC,
+ *  LCD_SPU_DMA_PITCH_UV, LCD_SPU_DMA_OVSA_HPXL_VLN,
+ *  LCD_SPU_DMA_HPXL_VLN, LCD_SPU_DZM_HPXL_VLN, LCD_SPU_DMA_CTRL0
+ */
+enum {
+       LCD_SPU_ADV_REG                 = 0x0084,
+       LCD_SPU_DMA_START_ADDR_Y0       = 0x00c0,
+       LCD_SPU_DMA_START_ADDR_U0       = 0x00c4,
+       LCD_SPU_DMA_START_ADDR_V0       = 0x00c8,
+       LCD_CFG_DMA_START_ADDR_0        = 0x00cc,
+       LCD_SPU_DMA_START_ADDR_Y1       = 0x00d0,
+       LCD_SPU_DMA_START_ADDR_U1       = 0x00d4,
+       LCD_SPU_DMA_START_ADDR_V1       = 0x00d8,
+       LCD_CFG_DMA_START_ADDR_1        = 0x00dc,
+       LCD_SPU_DMA_PITCH_YC            = 0x00e0,
+       LCD_SPU_DMA_PITCH_UV            = 0x00e4,
+       LCD_SPU_DMA_OVSA_HPXL_VLN       = 0x00e8,
+       LCD_SPU_DMA_HPXL_VLN            = 0x00ec,
+       LCD_SPU_DZM_HPXL_VLN            = 0x00f0,
+       LCD_CFG_GRA_START_ADDR0         = 0x00f4,
+       LCD_CFG_GRA_START_ADDR1         = 0x00f8,
+       LCD_CFG_GRA_PITCH               = 0x00fc,
+       LCD_SPU_GRA_OVSA_HPXL_VLN       = 0x0100,
+       LCD_SPU_GRA_HPXL_VLN            = 0x0104,
+       LCD_SPU_GZM_HPXL_VLN            = 0x0108,
+       LCD_SPU_HWC_OVSA_HPXL_VLN       = 0x010c,
+       LCD_SPU_HWC_HPXL_VLN            = 0x0110,
+       LCD_SPUT_V_H_TOTAL              = 0x0114,
+       LCD_SPU_V_H_ACTIVE              = 0x0118,
+       LCD_SPU_H_PORCH                 = 0x011c,
+       LCD_SPU_V_PORCH                 = 0x0120,
+       LCD_SPU_BLANKCOLOR              = 0x0124,
+       LCD_SPU_ALPHA_COLOR1            = 0x0128,
+       LCD_SPU_ALPHA_COLOR2            = 0x012c,
+       LCD_SPU_COLORKEY_Y              = 0x0130,
+       LCD_SPU_COLORKEY_U              = 0x0134,
+       LCD_SPU_COLORKEY_V              = 0x0138,
+       LCD_CFG_RDREG4F                 = 0x013c,
+       LCD_SPU_SPI_RXDATA              = 0x0140,
+       LCD_SPU_ISA_RSDATA              = 0x0144,
+       LCD_SPU_HWC_RDDAT               = 0x0158,
+       LCD_SPU_GAMMA_RDDAT             = 0x015c,
+       LCD_SPU_PALETTE_RDDAT           = 0x0160,
+       LCD_SPU_IOPAD_IN                = 0x0178,
+       LCD_CFG_RDREG5F                 = 0x017c,
+       LCD_SPU_SPI_CTRL                = 0x0180,
+       LCD_SPU_SPI_TXDATA              = 0x0184,
+       LCD_SPU_SMPN_CTRL               = 0x0188,
+       LCD_SPU_DMA_CTRL0               = 0x0190,
+       LCD_SPU_DMA_CTRL1               = 0x0194,
+       LCD_SPU_SRAM_CTRL               = 0x0198,
+       LCD_SPU_SRAM_WRDAT              = 0x019c,
+       LCD_SPU_SRAM_PARA0              = 0x01a0,
+       LCD_SPU_SRAM_PARA1              = 0x01a4,
+       LCD_CFG_SCLK_DIV                = 0x01a8,
+       LCD_SPU_CONTRAST                = 0x01ac,
+       LCD_SPU_SATURATION              = 0x01b0,
+       LCD_SPU_CBSH_HUE                = 0x01b4,
+       LCD_SPU_DUMB_CTRL               = 0x01b8,
+       LCD_SPU_IOPAD_CONTROL           = 0x01bc,
+       LCD_SPU_IRQ_ENA                 = 0x01c0,
+       LCD_SPU_IRQ_ISR                 = 0x01c4,
+};
+
+/* For LCD_SPU_ADV_REG */
+enum {
+       ADV_VSYNC_L_OFF = 0xfff << 20,
+       ADV_GRACOLORKEY = 1 << 19,
+       ADV_VIDCOLORKEY = 1 << 18,
+       ADV_HWC32BLEND  = 1 << 15,
+       ADV_HWC32ARGB   = 1 << 14,
+       ADV_HWC32ENABLE = 1 << 13,
+       ADV_VSYNCOFFEN  = 1 << 12,
+       ADV_VSYNC_H_OFF = 0xfff << 0,
+};
+
+/* For LCD_SPU_DMA_CTRL0 */
+enum {
+       CFG_NOBLENDING  = 1 << 31,
+       CFG_GAMMA_ENA   = 1 << 30,
+       CFG_CBSH_ENA    = 1 << 29,
+       CFG_PALETTE_ENA = 1 << 28,
+       CFG_ARBFAST_ENA = 1 << 27,
+       CFG_HWC_1BITMOD = 1 << 26,
+       CFG_HWC_1BITENA = 1 << 25,
+       CFG_HWC_ENA     = 1 << 24,
+       CFG_DMAFORMAT   = 0xf << 20,
+       CFG_DMA_565     = 0x0 << 20,
+       CFG_DMA_1555    = 0x1 << 20,
+       CFG_DMA_888PACK = 0x2 << 20,
+       CFG_DMA_X888    = 0x3 << 20,
+       CFG_DMA_8888    = 0x4 << 20,
+       CFG_DMA_422PACK = 0x5 << 20,
+       CFG_DMA_422     = 0x6 << 20,
+       CFG_DMA_420     = 0x7 << 20,
+       CFG_DMA_PSEUDO4 = 0x9 << 20,
+       CFG_DMA_PSEUDO8 = 0xa << 20,
+       CFG_GRAFORMAT   = 0xf << 16,
+       CFG_GRA_565     = 0x0 << 16,
+       CFG_GRA_1555    = 0x1 << 16,
+       CFG_GRA_888PACK = 0x2 << 16,
+       CFG_GRA_X888    = 0x3 << 16,
+       CFG_GRA_8888    = 0x4 << 16,
+       CFG_GRA_422PACK = 0x5 << 16,
+       CFG_GRA_422     = 0x6 << 16,
+       CFG_GRA_420     = 0x7 << 16,
+       CFG_GRA_PSEUDO4 = 0x9 << 16,
+       CFG_GRA_PSEUDO8 = 0xa << 16,
+       CFG_GRA_FTOGGLE = 1 << 15,
+       CFG_GRA_HSMOOTH = 1 << 14,
+       CFG_GRA_TSTMODE = 1 << 13,
+       CFG_GRA_SWAPRB  = 1 << 12,
+       CFG_GRA_SWAPUV  = 1 << 11,
+       CFG_GRA_SWAPYU  = 1 << 10,
+       CFG_YUV2RGB_GRA = 1 << 9,
+       CFG_GRA_ENA     = 1 << 8,
+       CFG_DMA_FTOGGLE = 1 << 7,
+       CFG_DMA_HSMOOTH = 1 << 6,
+       CFG_DMA_TSTMODE = 1 << 5,
+       CFG_DMA_SWAPRB  = 1 << 4,
+       CFG_DMA_SWAPUV  = 1 << 3,
+       CFG_DMA_SWAPYU  = 1 << 2,
+       CFG_YUV2RGB_DMA = 1 << 1,
+       CFG_DMA_ENA     = 1 << 0,
+};
+
+/* For LCD_SPU_DMA_CTRL1 */
+enum {
+       CFG_FRAME_TRIG  = 1 << 31,
+       CFG_VSYNC_INV   = 1 << 27,
+       CFG_CKMODE_MASK = 0x7 << 24,
+       CFG_CKMODE_DIS  = 0x0 << 24,
+       CFG_CKMODE_Y    = 0x1 << 24,
+       CFG_CKMODE_U    = 0x2 << 24,
+       CFG_CKMODE_RGB  = 0x3 << 24,
+       CFG_CKMODE_V    = 0x4 << 24,
+       CFG_CKMODE_R    = 0x5 << 24,
+       CFG_CKMODE_G    = 0x6 << 24,
+       CFG_CKMODE_B    = 0x7 << 24,
+       CFG_CARRY       = 1 << 23,
+       CFG_GATED_CLK   = 1 << 21,
+       CFG_PWRDN_ENA   = 1 << 20,
+       CFG_DSCALE_MASK = 0x3 << 18,
+       CFG_DSCALE_NONE = 0x0 << 18,
+       CFG_DSCALE_HALF = 0x1 << 18,
+       CFG_DSCALE_QUAR = 0x2 << 18,
+       CFG_ALPHAM_MASK = 0x3 << 16,
+       CFG_ALPHAM_VIDEO= 0x0 << 16,
+       CFG_ALPHAM_GRA  = 0x1 << 16,
+       CFG_ALPHAM_CFG  = 0x2 << 16,
+       CFG_ALPHA_MASK  = 0xff << 8,
+       CFG_PIXCMD_MASK = 0xff,
+};
+
+/* For LCD_SPU_SRAM_PARA1 */
+enum {
+       CFG_CSB_256x32  = 1 << 15,
+       CFG_CSB_256x24  = 1 << 14,
+       CFG_CSB_256x8   = 1 << 13,
+       CFG_PDWN256x32  = 1 << 7,
+       CFG_PDWN256x24  = 1 << 6,
+       CFG_PDWN256x8   = 1 << 5,
+       CFG_PDWN32x32   = 1 << 3,
+       CFG_PDWN16x66   = 1 << 2,
+       CFG_PDWN32x66   = 1 << 1,
+       CFG_PDWN64x66   = 1 << 0,
+};
+
+/* For LCD_SPU_DUMB_CTRL */
+enum {
+       DUMB16_RGB565_0 = 0x0 << 28,
+       DUMB16_RGB565_1 = 0x1 << 28,
+       DUMB18_RGB666_0 = 0x2 << 28,
+       DUMB18_RGB666_1 = 0x3 << 28,
+       DUMB12_RGB444_0 = 0x4 << 28,
+       DUMB12_RGB444_1 = 0x5 << 28,
+       DUMB24_RGB888_0 = 0x6 << 28,
+       DUMB_BLANK      = 0x7 << 28,
+       DUMB_MASK       = 0xf << 28,
+       CFG_BIAS_OUT    = 1 << 8,
+       CFG_REV_RGB     = 1 << 7,
+       CFG_INV_CBLANK  = 1 << 6,
+       CFG_INV_CSYNC   = 1 << 5,
+       CFG_INV_HENA    = 1 << 4,
+       CFG_INV_VSYNC   = 1 << 3,
+       CFG_INV_HSYNC   = 1 << 2,
+       CFG_INV_PCLK    = 1 << 1,
+       CFG_DUMB_ENA    = 1 << 0,
+};
+
+/* For LCD_SPU_IOPAD_CONTROL */
+enum {
+       CFG_VSCALE_LN_EN        = 3 << 18,
+       CFG_GRA_VM_ENA          = 1 << 15,
+       CFG_DMA_VM_ENA          = 1 << 13,
+       CFG_CMD_VM_ENA          = 1 << 11,
+       CFG_CSC_MASK            = 3 << 8,
+       CFG_CSC_CCIR709         = 1 << 9,
+       CFG_CSC_PROF            = 1 << 8,
+       CFG_IOPAD_MASK          = 0xf << 0,
+       CFG_IOPAD_DUMB24        = 0x0 << 0,
+       CFG_IOPAD_DUMB18SPI     = 0x1 << 0,
+       CFG_IOPAD_DUMB18GPIO    = 0x2 << 0,
+       CFG_IOPAD_DUMB16SPI     = 0x3 << 0,
+       CFG_IOPAD_DUMB16GPIO    = 0x4 << 0,
+       CFG_IOPAD_DUMB12GPIO    = 0x5 << 0,
+       CFG_IOPAD_SMART18       = 0x6 << 0,
+       CFG_IOPAD_SMART16       = 0x7 << 0,
+       CFG_IOPAD_SMART8        = 0x8 << 0,
+};
+
+#define IOPAD_DUMB24                0x0
+
+/* For LCD_SPU_IRQ_ENA */
+enum {
+       DMA_FRAME_IRQ0_ENA      = 1 << 31,
+       DMA_FRAME_IRQ1_ENA      = 1 << 30,
+       DMA_FRAME_IRQ_ENA       = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA,
+       DMA_FF_UNDERFLOW_ENA    = 1 << 29,
+       GRA_FRAME_IRQ0_ENA      = 1 << 27,
+       GRA_FRAME_IRQ1_ENA      = 1 << 26,
+       GRA_FRAME_IRQ_ENA       = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA,
+       GRA_FF_UNDERFLOW_ENA    = 1 << 25,
+       VSYNC_IRQ_ENA           = 1 << 23,
+       DUMB_FRAMEDONE_ENA      = 1 << 22,
+       TWC_FRAMEDONE_ENA       = 1 << 21,
+       HWC_FRAMEDONE_ENA       = 1 << 20,
+       SLV_IRQ_ENA             = 1 << 19,
+       SPI_IRQ_ENA             = 1 << 18,
+       PWRDN_IRQ_ENA           = 1 << 17,
+       ERR_IRQ_ENA             = 1 << 16,
+       CLEAN_SPU_IRQ_ISR       = 0xffff,
+};
+
+/* For LCD_SPU_IRQ_ISR */
+enum {
+       DMA_FRAME_IRQ0          = 1 << 31,
+       DMA_FRAME_IRQ1          = 1 << 30,
+       DMA_FRAME_IRQ           = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1,
+       DMA_FF_UNDERFLOW        = 1 << 29,
+       GRA_FRAME_IRQ0          = 1 << 27,
+       GRA_FRAME_IRQ1          = 1 << 26,
+       GRA_FRAME_IRQ           = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1,
+       GRA_FF_UNDERFLOW        = 1 << 25,
+       VSYNC_IRQ               = 1 << 23,
+       DUMB_FRAMEDONE          = 1 << 22,
+       TWC_FRAMEDONE           = 1 << 21,
+       HWC_FRAMEDONE           = 1 << 20,
+       SLV_IRQ                 = 1 << 19,
+       SPI_IRQ                 = 1 << 18,
+       PWRDN_IRQ               = 1 << 17,
+       ERR_IRQ                 = 1 << 16,
+       DMA_FRAME_IRQ0_LEVEL    = 1 << 15,
+       DMA_FRAME_IRQ1_LEVEL    = 1 << 14,
+       DMA_FRAME_CNT_ISR       = 3 << 12,
+       GRA_FRAME_IRQ0_LEVEL    = 1 << 11,
+       GRA_FRAME_IRQ1_LEVEL    = 1 << 10,
+       GRA_FRAME_CNT_ISR       = 3 << 8,
+       VSYNC_IRQ_LEVEL         = 1 << 7,
+       DUMB_FRAMEDONE_LEVEL    = 1 << 6,
+       TWC_FRAMEDONE_LEVEL     = 1 << 5,
+       HWC_FRAMEDONE_LEVEL     = 1 << 4,
+       SLV_FF_EMPTY            = 1 << 3,
+       DMA_FF_ALLEMPTY         = 1 << 2,
+       GRA_FF_ALLEMPTY         = 1 << 1,
+       PWRDN_IRQ_LEVEL         = 1 << 0,
+};
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_ioctl.h 
b/drivers/gpu/drm/dove/dove_ioctl.h
new file mode 100644
index 0000000..c1dcbaa
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_ioctl.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  With inspiration from the i915 driver
+ *
+ * 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 DRM_DOVE_IOCTL_H
+#define DRM_DOVE_IOCTL_H
+
+#define DRM_DOVE_GEM_CREATE            0x00
+#define DRM_DOVE_GEM_CREATE_PHYS       0x01
+#define DRM_DOVE_GEM_MMAP              0x02
+#define DRM_DOVE_GEM_PWRITE            0x03
+#define DRM_DOVE_GEM_PROP              0x04
+#define DRM_DOVE_OVERLAY_PUT_IMAGE     0x06
+#define DRM_DOVE_OVERLAY_ATTRS         0x07
+
+#define DOVE_IOCTL(dir,name,str) \
+       DRM_##dir(DRM_COMMAND_BASE + DRM_DOVE_##name, struct drm_dove_##str)
+
+struct drm_dove_gem_create {
+       uint32_t height;
+       uint32_t width;
+       uint32_t bpp;
+       uint32_t handle;
+       uint32_t pitch;
+       uint32_t size;
+};
+#define DRM_IOCTL_DOVE_GEM_CREATE \
+       DOVE_IOCTL(IOWR, GEM_CREATE, gem_create)
+
+struct drm_dove_gem_create_phys {
+       uint32_t size;
+       uint32_t handle;
+       uint64_t phys;
+};
+#define DRM_IOCTL_DOVE_GEM_CREATE_PHYS \
+       DOVE_IOCTL(IOWR, GEM_CREATE_PHYS, gem_create_phys)
+
+struct drm_dove_gem_mmap {
+       uint32_t handle;
+       uint32_t pad;
+       uint64_t offset;
+       uint64_t size;
+       uint64_t addr;
+};
+#define DRM_IOCTL_DOVE_GEM_MMAP \
+       DOVE_IOCTL(IOWR, GEM_MMAP, gem_mmap)
+
+struct drm_dove_gem_pwrite {
+       uint32_t handle;
+       uint32_t offset;
+       uint32_t size;
+       uint64_t ptr;
+};
+#define DRM_IOCTL_DOVE_GEM_PWRITE \
+       DOVE_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
+
+struct drm_dove_gem_prop {
+       uint64_t phys;
+       uint32_t handle;
+};
+#define DRM_IOCTL_DOVE_GEM_PROP \
+       DOVE_IOCTL(IOWR, GEM_PROP, gem_prop)
+
+/* Same as Intel I915 */
+struct drm_dove_overlay_put_image {
+       uint32_t flags;
+#define DOVE_OVERLAY_TYPE_MASK          0x000000ff
+#define DOVE_OVERLAY_YUV_PLANAR         0x00000001
+#define DOVE_OVERLAY_YUV_PACKED         0x00000002
+#define DOVE_OVERLAY_RGB                0x00000003
+#define DOVE_OVERLAY_DEPTH_MASK                0x0000ff00
+#define DOVE_OVERLAY_RGB24             0x00001000
+#define DOVE_OVERLAY_RGB16             0x00002000
+#define DOVE_OVERLAY_RGB15             0x00003000
+#define DOVE_OVERLAY_YUV422            0x00000100
+#define DOVE_OVERLAY_YUV411            0x00000200
+#define DOVE_OVERLAY_YUV420            0x00000300
+#define DOVE_OVERLAY_YUV410            0x00000400
+#define DOVE_OVERLAY_SWAP_MASK         0x00ff0000
+#define DOVE_OVERLAY_NO_SWAP           0x00000000
+#define DOVE_OVERLAY_UV_SWAP           0x00010000
+#define DOVE_OVERLAY_Y_SWAP            0x00020000
+#define DOVE_OVERLAY_Y_AND_UV_SWAP     0x00030000
+#define DOVE_OVERLAY_FLAGS_MASK                0xff000000
+#define DOVE_OVERLAY_ENABLE            0x01000000
+       uint32_t bo_handle;
+       uint16_t stride_Y;
+       uint16_t stride_UV;
+       uint32_t offset_Y;
+       uint32_t offset_U;
+       uint32_t offset_V;
+       uint16_t src_width;
+       uint16_t src_height;
+       uint16_t src_scan_width;
+       uint16_t src_scan_height;
+       uint32_t crtc_id;
+       uint16_t dst_x;
+       uint16_t dst_y;
+       uint16_t dst_width;
+       uint16_t dst_height;
+};
+#define DRM_IOCTL_DOVE_OVERLAY_PUT_IMAGE \
+       DOVE_IOCTL(IOW, OVERLAY_PUT_IMAGE, overlay_put_image)
+
+/* Same as Intel I915 */
+struct drm_dove_overlay_attrs {
+       uint32_t flags;
+#define DOVE_OVERLAY_UPDATE_ATTRS      (1<<0)
+#define DOVE_OVERLAY_UPDATE_GAMMA      (1<<1)
+       uint32_t color_key;
+       int32_t brightness;
+       uint32_t contrast;
+       uint32_t saturation;
+       uint32_t gamma0;
+       uint32_t gamma1;
+       uint32_t gamma2;
+       uint32_t gamma3;
+       uint32_t gamma4;
+       uint32_t gamma5;
+};
+#define DRM_IOCTL_DOVE_OVERLAY_ATTRS \
+       DOVE_IOCTL(IOWR, OVERLAY_ATTRS, overlay_attrs)
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_ioctlP.h 
b/drivers/gpu/drm/dove/dove_ioctlP.h
new file mode 100644
index 0000000..7791b55
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_ioctlP.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_IOCTLP_H
+#define DOVE_IOCTLP_H
+
+#define DOVE_IOCTL_PROTO(name)\
+extern int dove_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
+
+DOVE_IOCTL_PROTO(gem_create);
+DOVE_IOCTL_PROTO(gem_create_phys);
+DOVE_IOCTL_PROTO(gem_mmap);
+DOVE_IOCTL_PROTO(gem_prop);
+DOVE_IOCTL_PROTO(gem_pwrite);
+DOVE_IOCTL_PROTO(overlay_put_image);
+DOVE_IOCTL_PROTO(overlay_attrs);
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_output.c 
b/drivers/gpu/drm/dove/dove_output.c
new file mode 100644
index 0000000..7cd7d35
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_output.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ */
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+#include "drm_helper.h"
+#include "dove_output.h"
+#include "dove_drm.h"
+
+struct dove_connector {
+       struct drm_connector conn;
+       const struct dove_output_type *type;
+};
+
+#define drm_to_dove_conn(c) container_of(c, struct dove_connector, conn)
+
+struct drm_encoder *dove_drm_connector_best_encoder(struct drm_connector *conn)
+{
+       uint32_t id = conn->encoder_ids[0];
+
+       return id ? drm_encoder_find(conn->dev, id) : NULL;
+}
+
+static enum drm_connector_status dove_drm_connector_detect(
+       struct drm_connector *conn, bool force)
+{
+       struct dove_connector *dconn = drm_to_dove_conn(conn);
+
+       return dconn->type->detect(conn, force);
+}
+
+static void dove_drm_connector_destroy(struct drm_connector *conn)
+{
+       struct dove_connector *dconn = drm_to_dove_conn(conn);
+
+       drm_sysfs_connector_remove(conn);
+       drm_connector_cleanup(conn);
+       kfree(dconn);
+}
+
+static int dove_drm_connector_set_property(struct drm_connector *conn,
+       struct drm_property *property, uint64_t value)
+{
+       struct dove_connector *dconn = drm_to_dove_conn(conn);
+
+       if (!dconn->type->set_property)
+               return -EINVAL;
+
+       return dconn->type->set_property(conn, property, value);
+}
+
+static const struct drm_connector_funcs dove_drm_conn_funcs = {
+       .dpms           = drm_helper_connector_dpms,
+       .fill_modes     = drm_helper_probe_single_connector_modes,
+       .detect         = dove_drm_connector_detect,
+       .destroy        = dove_drm_connector_destroy,
+       .set_property   = dove_drm_connector_set_property,
+};
+
+void dove_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+       struct drm_encoder_helper_funcs *funcs = encoder->helper_private;
+
+       funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void dove_drm_encoder_commit(struct drm_encoder *encoder)
+{
+       struct drm_encoder_helper_funcs *funcs = encoder->helper_private;
+
+       funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+struct drm_connector *dove_output_create(struct drm_device *dev,
+       const struct dove_output_type *type)
+{
+       struct dove_connector *dconn;
+       struct drm_encoder *enc;
+       int ret;
+
+       dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
+       if (!dconn)
+               return NULL;
+
+       dconn->conn.interlace_allowed = true;
+       dconn->conn.polled = type->polled;
+       dconn->type = type;
+
+       ret = drm_connector_init(dev, &dconn->conn, &dove_drm_conn_funcs,
+                                type->connector_type);
+       if (ret) {
+               DRM_ERROR("unable to init connector\n");
+               goto err_destroy_dconn;
+       }
+
+       ret = type->create(&dconn->conn, &enc);
+       if (ret)
+               goto err_conn;
+
+       ret = drm_sysfs_connector_add(&dconn->conn);
+       if (ret)
+               goto err_sysfs;
+
+       ret = drm_mode_connector_attach_encoder(&dconn->conn, enc);
+       if (ret)
+               goto err_attach;
+
+       return &dconn->conn;
+
+ err_attach:
+       drm_sysfs_connector_remove(&dconn->conn);
+ err_sysfs:
+       enc->funcs->destroy(enc);
+ err_conn:
+       drm_connector_cleanup(&dconn->conn);
+ err_destroy_dconn:
+       kfree(dconn);
+       return NULL;
+}
diff --git a/drivers/gpu/drm/dove/dove_output.h 
b/drivers/gpu/drm/dove/dove_output.h
new file mode 100644
index 0000000..1b12890
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_output.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DOVE_CONNETOR_H
+#define DOVE_CONNETOR_H
+
+struct dove_output_type {
+       int connector_type;
+       int polled;
+       enum drm_connector_status (*detect)(struct drm_connector *, bool);
+       int (*create)(struct drm_connector *, struct drm_encoder **);
+       int (*set_property)(struct drm_connector *, struct drm_property *, 
uint64_t);
+};
+
+struct drm_encoder *dove_drm_connector_best_encoder(struct drm_connector 
*conn);
+void dove_drm_encoder_prepare(struct drm_encoder *encoder);
+void dove_drm_encoder_commit(struct drm_encoder *encoder);
+
+struct drm_connector *dove_output_create(struct drm_device *dev,
+       const struct dove_output_type *);
+
+#endif
diff --git a/drivers/gpu/drm/dove/dove_overlay.c 
b/drivers/gpu/drm/dove/dove_overlay.c
new file mode 100644
index 0000000..39c081b
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_overlay.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *  Rewritten from the dovefb driver, and Armada510 manuals.
+ *
+ * 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.
+ */
+#include "drmP.h"
+#include "drm_helper.h"
+#include "dove_crtc.h"
+#include "dove_drm.h"
+#include "dove_gem.h"
+#include "dove_hw.h"
+#include "dove_ioctl.h"
+#include "dove_ioctlP.h"
+
+struct dove_overlay {
+       struct dove_gem_object *obj;
+       struct dove_gem_object *old_obj;
+       struct dove_crtc *dcrtc;
+       uint32_t crtc_id;
+       uint32_t cached_flags;
+       uint32_t cached_cfg;
+
+       uint32_t stride_yc;
+       uint32_t stride_uv;
+       uint32_t dst_yx;
+       uint32_t src_hw;
+       uint32_t dst_hw;
+       uint32_t cfg;
+
+       uint32_t color_key;
+       int32_t brightness;
+       uint32_t contrast;
+       uint32_t saturation;
+
+       struct dove_vbl_event vbl_update;
+
+       struct dove_regs update[13];
+
+       wait_queue_head_t vbl_wait;
+};
+
+static void dove_updatel(uint32_t val, uint32_t mask, void __iomem *ptr)
+{
+       uint32_t ov, v;
+
+       ov = v = readl_relaxed(ptr);
+       v = (v & ~mask) | val;
+       if (ov != v)
+               writel_relaxed(v, ptr);
+}
+
+static void dove_ovl_update(struct dove_crtc *dcrtc, void *data)
+{
+       struct dove_overlay *ovl = container_of(data, struct dove_overlay, 
update);
+       dove_drm_crtc_update_regs(dcrtc, data);
+       wake_up(&ovl->vbl_wait);
+}
+
+static void dove_ovl_update_attr(struct dove_overlay *ovl)
+{
+       struct dove_crtc *dcrtc = ovl->dcrtc;
+       uint32_t key = ovl->color_key;
+        uint32_t r, g, b;
+
+        r = (key & 0x0000ff);
+        g = (key & 0x00ff00) >> 8;
+        b = (key & 0xff0000) >> 16;
+
+       writel_relaxed(r << 8 | r << 16 | r << 24, dcrtc->base + 
LCD_SPU_COLORKEY_Y);
+       writel_relaxed(g << 8 | g << 16 | g << 24, dcrtc->base + 
LCD_SPU_COLORKEY_U);
+       writel_relaxed(b << 8 | b << 16 | b << 24, dcrtc->base + 
LCD_SPU_COLORKEY_V);
+
+       writel_relaxed(0x00004000, dcrtc->base + LCD_SPU_CONTRAST);
+       writel_relaxed(0x40000000, dcrtc->base + LCD_SPU_SATURATION);
+       writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
+
+       spin_lock_irq(&dcrtc->irq_lock);
+       dove_updatel(CFG_CKMODE_RGB | CFG_ALPHAM_GRA,
+                    CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
+                    dcrtc->base + LCD_SPU_DMA_CTRL1);
+
+       dove_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
+       spin_unlock_irq(&dcrtc->irq_lock);
+}
+
+static int dove_check_planar(struct drm_dove_overlay_put_image *args,
+       struct dove_gem_object *obj)
+{
+       uint32_t tmp;
+
+       DRM_DEBUG_DRIVER("stride Y%#x UV%#x offset Y%#x U%#x V%#x obj %#x\n",
+               args->stride_Y, args->stride_UV,
+               args->offset_Y, args->offset_U, args->offset_V,
+               obj->obj.size);
+
+       if (args->src_scan_width     > args->stride_Y ||
+           args->src_scan_width / 2 > args->stride_UV)
+               return -EINVAL;
+
+       tmp = args->stride_Y * args->src_height;
+       if (tmp > obj->obj.size ||
+           args->offset_Y > obj->obj.size - tmp)
+               return -EINVAL;
+
+       tmp = args->stride_UV * args->src_height;
+       if (tmp > obj->obj.size ||
+           args->offset_U > obj->obj.size - tmp ||
+           args->offset_V > obj->obj.size - tmp)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int dove_check_packed(struct drm_dove_overlay_put_image *args,
+       struct dove_gem_object *obj)
+{
+       uint32_t tmp = args->stride_Y * args->src_height;
+
+       if (args->src_scan_width * 2 > args->stride_Y ||
+           tmp > obj->obj.size ||
+           args->offset_Y > obj->obj.size - tmp)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct dove_crtc *dove_crtc_lookup(struct drm_device *dev, uint32_t id)
+{
+       struct drm_crtc *crtc = drm_crtc_find(dev, id);
+       return crtc ? drm_to_dove_crtc(crtc) : NULL;
+}
+
+static void dove_ovl_release_old(struct dove_overlay *ovl)
+{
+       if (ovl->old_obj) {
+               drm_gem_object_unreference(&ovl->old_obj->obj);
+               ovl->old_obj = NULL;
+       }
+}
+
+/*
+ * This should be called with both dev->struct_mutex and
+ * the mode_config mutexes held.
+ */
+void dove_drm_overlay_off(struct drm_device *dev, struct dove_overlay *ovl)
+{
+       struct dove_crtc *dcrtc = ovl->dcrtc;
+
+       ovl->cfg = 0;
+
+       if (dcrtc) {
+               /* Disable overlay */
+               dove_drm_vbl_event_remove_unlocked(dcrtc, &ovl->vbl_update);
+
+               spin_lock_irq(&dcrtc->irq_lock);
+               dove_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+               spin_unlock_irq(&dcrtc->irq_lock);
+
+               ovl->dcrtc->doverlay = NULL;
+               ovl->dcrtc = NULL;
+               ovl->crtc_id = ~0;
+       }
+
+       dove_ovl_release_old(ovl);
+
+       if (ovl->obj) {
+               drm_gem_object_unreference(&ovl->obj->obj);
+               ovl->obj = NULL;
+       }
+}
+
+static int dove_ovl_check_dst(const struct drm_dove_overlay_put_image *args,
+       const struct drm_display_mode *mode)
+{
+       if (args->dst_x < mode->hdisplay &&
+           args->dst_width <= mode->hdisplay - args->dst_x &&
+           args->dst_y < mode->vdisplay &&
+           args->dst_height <= mode->vdisplay - args->dst_y)
+               return 0;
+       return -EINVAL;
+}
+
+static int dove_ovl_compute_cfg(struct dove_overlay *ovl, uint32_t flags)
+{
+       uint32_t cfg = CFG_DMA_HSMOOTH | CFG_CBSH_ENA;
+
+       switch (flags & DOVE_OVERLAY_TYPE_MASK) {
+       case DOVE_OVERLAY_YUV_PLANAR:
+               switch (flags & DOVE_OVERLAY_DEPTH_MASK) {
+               case DOVE_OVERLAY_YUV422:       /* Planar YUV422 */
+                       cfg |= CFG_DMA_422;
+                       break;
+               case DOVE_OVERLAY_YUV420:       /* Planar YUV420 */
+                       cfg |= CFG_DMA_420;
+                       break;
+               default:
+                       DRM_ERROR("bad planar depth\n");
+                       return -EINVAL;
+               }
+               /* Planar formats have no swaps */
+               if (flags & DOVE_OVERLAY_SWAP_MASK) {
+                       DRM_ERROR("planar and requested swap\n");
+                       return -EINVAL;
+               }
+               cfg |= CFG_YUV2RGB_DMA;
+               break;
+
+       case DOVE_OVERLAY_YUV_PACKED:
+               switch (flags & DOVE_OVERLAY_DEPTH_MASK) {
+               case DOVE_OVERLAY_YUV422:
+                       cfg |= CFG_DMA_422PACK;
+                       break;
+               default:
+                       DRM_ERROR("bad packed depth\n");
+                       return -EINVAL;
+               }
+               if (flags & (DOVE_OVERLAY_SWAP_MASK & 
~DOVE_OVERLAY_Y_AND_UV_SWAP)) {
+                       DRM_ERROR("bad overlay swap\n");
+                       return -EINVAL;
+               }
+               /*
+                *                            [0:7] [8:15] [16:23] [24:31]
+                * 0:                   YUY2:   Y     U       Y       V
+                * UV_SWAP:             YVYU:   Y     V       Y       U
+                * Y_SWAP:              UYVY:   U     Y       V       Y
+                * Y_SWAP | UV_SWAP:    VYUY:   V     Y       U       Y
+                *
+                * Default ordering in memory:  U     Y0      V       Y1
+                *
+                * Image fourcc 59565955 UYVY flags 01020102 -> correct
+                * Image fourcc 32595559 YUY2 flags 01000102 -> wrong U/V 
swapped
+                */
+               if (flags & DOVE_OVERLAY_UV_SWAP)
+                       cfg |= CFG_DMA_SWAPUV;
+               if (!(flags & DOVE_OVERLAY_Y_SWAP))
+                       cfg ^= CFG_DMA_SWAPYU | CFG_DMA_SWAPUV;
+               cfg |= CFG_YUV2RGB_DMA;
+               break;
+
+       case DOVE_OVERLAY_RGB:
+               switch (flags & DOVE_OVERLAY_DEPTH_MASK) {
+               case DOVE_OVERLAY_RGB24:        /* 3 byte RGB */
+                       cfg |= CFG_DMA_888PACK;
+                       break;
+               case DOVE_OVERLAY_RGB16:        /* 2 byte RGB */
+                       cfg |= CFG_DMA_565;
+                       break;
+               case DOVE_OVERLAY_RGB15:        /* 2 byte RGB */
+                       cfg |= CFG_DMA_1555;
+                       break;
+               default:
+                       DRM_ERROR("bad RGB depth\n");
+                       return -EINVAL;
+               }
+               /* Planar formats have no swaps */
+               if (flags & DOVE_OVERLAY_SWAP_MASK) {
+                       DRM_ERROR("RGB and requested swap\n");
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               DRM_ERROR("bad overlay type\n");
+               return -EINVAL;
+       }
+
+       cfg |= CFG_DMA_ENA;
+
+       ovl->cached_flags = flags;
+       ovl->cached_cfg = cfg;
+       return 0;
+}
+
+int dove_overlay_put_image_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_overlay_put_image *args = data;
+       struct dove_private *priv = dev->dev_private;
+       struct dove_overlay *ovl = priv->doverlay;
+       struct dove_gem_object *obj;
+       struct dove_crtc *dcrtc;
+       uint32_t stride_uv, stride_yc, src_hw, dst_hw, dst_yx;
+       bool planar = false;
+       int ret, idx;
+
+       if (!ovl) {
+               DRM_DEBUG_DRIVER("no overlay");
+               return -ENODEV;
+       }
+
+       if (!(args->flags & DOVE_OVERLAY_ENABLE)) {
+               mutex_lock(&dev->mode_config.mutex);
+               mutex_lock(&dev->struct_mutex);
+
+               dove_drm_overlay_off(dev, ovl);
+
+               mutex_unlock(&dev->struct_mutex);
+               mutex_unlock(&dev->mode_config.mutex);
+
+               return 0;
+       }
+
+//     DRM_DEBUG_DRIVER("flags %x handle %x src %dx%d dst %dx%d+%d+%d\n",
+//             args->flags, args->bo_handle, args->src_scan_width, 
args->src_scan_height,
+//             args->dst_width, args->dst_height, args->dst_x, args->dst_y);
+
+       if (!ovl->dcrtc || ovl->crtc_id != args->crtc_id) {
+               dcrtc = dove_crtc_lookup(dev, args->crtc_id);
+               if (!dcrtc)
+                       return -ENOENT;
+       } else {
+               dcrtc = ovl->dcrtc;
+       }
+
+       if (args->flags != ovl->cached_flags) {
+               ret = dove_ovl_compute_cfg(ovl, args->flags);
+               if (ret)
+                       return ret;
+       }
+
+       obj = dove_gem_object_lookup(dev, file, args->bo_handle);
+       if (!obj)
+               return -ENOENT;
+
+       ret = wait_event_timeout(ovl->vbl_wait, 
list_empty(&ovl->vbl_update.node), HZ/25);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&dev->mode_config.mutex);
+       mutex_lock(&dev->struct_mutex);
+
+       /* Prevent updates */
+       if (ovl->dcrtc)
+               dove_drm_vbl_event_remove_unlocked(ovl->dcrtc, 
&ovl->vbl_update);
+
+       if (ovl->dcrtc != dcrtc) {
+               dove_drm_overlay_off(dev, ovl);
+
+               ovl->crtc_id = args->crtc_id;
+               ovl->dcrtc = dcrtc;
+               dcrtc->doverlay = ovl;
+
+               dove_ovl_update_attr(ovl);
+       }
+
+       ret = dove_ovl_check_dst(args, &ovl->dcrtc->crtc.mode);
+       if (ret)
+               goto err_unref;
+
+       planar = (args->flags & DOVE_OVERLAY_TYPE_MASK) == 
DOVE_OVERLAY_YUV_PLANAR;
+       if (planar)
+               ret = dove_check_planar(args, obj);
+       else
+               ret = dove_check_packed(args, obj);
+       if (ret)
+               goto err_unref;
+
+       if (ovl->dcrtc->interlaced) {
+               args->dst_y /= 2;
+               args->dst_height /= 2;
+       }
+
+       idx = 0;
+       if (ovl->obj != obj) {
+               uint32_t start_y, start_u, start_v;
+
+               /* switch to new object */
+               dove_ovl_release_old(ovl);
+               ovl->old_obj = ovl->obj;
+               ovl->obj = obj;
+
+               start_y = obj->dev_addr + args->offset_Y;
+               if (planar) {
+                       start_u = obj->dev_addr + args->offset_U;
+                       start_v = obj->dev_addr + args->offset_V;
+               } else {
+                       start_u = start_y;
+                       start_v = start_y;
+               }
+
+               dove_reg_queue_set(ovl->update, idx, start_y, 
LCD_SPU_DMA_START_ADDR_Y0);
+               dove_reg_queue_set(ovl->update, idx, start_u, 
LCD_SPU_DMA_START_ADDR_U0);
+               dove_reg_queue_set(ovl->update, idx, start_v, 
LCD_SPU_DMA_START_ADDR_V0);
+               dove_reg_queue_set(ovl->update, idx, start_y, 
LCD_SPU_DMA_START_ADDR_Y1);
+               dove_reg_queue_set(ovl->update, idx, start_u, 
LCD_SPU_DMA_START_ADDR_U1);
+               dove_reg_queue_set(ovl->update, idx, start_v, 
LCD_SPU_DMA_START_ADDR_V1);
+       } else {
+               drm_gem_object_unreference(&obj->obj);
+       }
+
+       stride_yc = args->stride_Y << 16 | args->stride_Y;
+       stride_uv = args->stride_UV << 16 | args->stride_UV;
+
+       if (ovl->stride_yc != stride_yc || ovl->stride_uv != stride_uv) {
+               ovl->stride_yc = stride_yc;
+               ovl->stride_uv = stride_uv;
+               dove_reg_queue_set(ovl->update, idx, stride_yc, 
LCD_SPU_DMA_PITCH_YC);
+               dove_reg_queue_set(ovl->update, idx, stride_uv, 
LCD_SPU_DMA_PITCH_UV);
+       }
+
+       src_hw = args->src_scan_height << 16 | args->src_scan_width;
+       dst_hw = args->dst_height << 16 | args->dst_width;
+       if (ovl->src_hw != src_hw || ovl->dst_hw != dst_hw) {
+               ovl->src_hw = src_hw;
+               ovl->dst_hw = dst_hw;
+
+               dove_reg_queue_set(ovl->update, idx, dst_hw, 
LCD_SPU_DZM_HPXL_VLN);
+               dove_reg_queue_set(ovl->update, idx, src_hw, 
LCD_SPU_DMA_HPXL_VLN);
+       }
+
+       dst_yx = args->dst_y << 16 | args->dst_x;
+       if (ovl->dst_yx != dst_yx) {
+               ovl->dst_yx = dst_yx;
+               dove_reg_queue_set(ovl->update, idx, dst_yx, 
LCD_SPU_DMA_OVSA_HPXL_VLN);
+       }
+
+       /* Update overlay DMA settings */
+       if (ovl->cfg != ovl->cached_cfg) {
+               ovl->cfg = ovl->cached_cfg;
+               dove_reg_queue_mod(ovl->update, idx, ovl->cached_cfg,
+                       CFG_DMAFORMAT | CFG_DMA_FTOGGLE | CFG_DMA_HSMOOTH |
+                       CFG_DMA_TSTMODE | CFG_DMA_SWAPRB | CFG_DMA_SWAPUV |
+                       CFG_DMA_SWAPYU | CFG_YUV2RGB_DMA | CFG_DMA_ENA,
+                       LCD_SPU_DMA_CTRL0);
+       }
+       if (idx) {
+               dove_reg_queue_end(ovl->update, idx);
+               dove_drm_vbl_event_add(dcrtc, &ovl->vbl_update);
+       }
+
+ err_unref:
+       if (ret)
+               drm_gem_object_unreference(&obj->obj);
+       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
+int dove_overlay_attrs_ioctl(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct drm_dove_overlay_attrs *args = data;
+        struct dove_private *priv = dev->dev_private;
+       struct dove_overlay *ovl = priv->doverlay;
+
+       if (!ovl) {
+               DRM_DEBUG_DRIVER("no overlay");
+               return -ENODEV;
+       }
+
+       if (args->flags & DOVE_OVERLAY_UPDATE_ATTRS) {
+               if (args->brightness < -128 || args->brightness > 127 ||
+                   args->contrast > 255 || args->saturation > 1023)
+                       return -EINVAL;
+
+               ovl->color_key = args->color_key;
+               ovl->brightness = args->brightness;
+               ovl->contrast = args->contrast;
+               ovl->saturation = args->saturation;
+
+               mutex_lock(&dev->mode_config.mutex);
+               if (ovl->dcrtc)
+                       dove_ovl_update_attr(ovl);
+               mutex_unlock(&dev->mode_config.mutex);
+       } else {
+               args->color_key = ovl->color_key;
+               args->brightness = ovl->brightness;
+               args->contrast = ovl->contrast;
+               args->saturation = ovl->saturation;
+       }
+
+       if (args->flags & DOVE_OVERLAY_UPDATE_GAMMA) {
+               /* args->gamma0..5 */
+       }
+
+       return 0;
+}
+
+int dove_overlay_create(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct dove_overlay *ovl;
+
+       ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
+       if (!ovl)
+               return -ENOMEM;
+
+       priv->doverlay = ovl;
+
+       init_waitqueue_head(&ovl->vbl_wait);
+       dove_drm_vbl_event_init(&ovl->vbl_update, dove_ovl_update, ovl->update);
+
+       ovl->color_key = 0x0101fe;
+       ovl->brightness = -19;
+       ovl->contrast = 75;
+       ovl->saturation = 146;
+
+       return 0;
+}
+
+void dove_overlay_destroy(struct drm_device *dev)
+{
+       struct dove_private *priv = dev->dev_private;
+       struct dove_overlay *ovl = priv->doverlay;
+
+       if (ovl->dcrtc)
+               dove_drm_vbl_event_remove_unlocked(ovl->dcrtc, 
&ovl->vbl_update);
+
+       kfree(ovl);
+}
diff --git a/drivers/gpu/drm/dove/drm_helper.h 
b/drivers/gpu/drm/dove/drm_helper.h
new file mode 100644
index 0000000..0753b3f
--- /dev/null
+++ b/drivers/gpu/drm/dove/drm_helper.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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 DRM_HELPER_H
+#define DRM_HELPER_H
+
+/* Useful helpers I wish the DRM core would provide */
+
+#include "drmP.h"
+
+static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
+       uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CRTC);
+       return mo ? obj_to_crtc(mo) : NULL;
+}
+
+static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
+       uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+       return mo ? obj_to_encoder(mo) : NULL;
+}
+
+#endif
-- 
1.7.4.4

Reply via email to