All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-16 19:25 ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel

What follows is my DRM driver for Dove, which I've been working on with
the Solid-run Cubox, which only offers HDMI output via the TDA19988
chip.

Not everything is finished off perfectly in this driver; there's quite
a number of ragged edges (particularly with page flipping, which is
completely untested.)

I have most of the Xorg driver complete - none of the DRI interfaces
checked yet (partly because that involves quite an amount of work with
Mesa).

However, graphics, video overlay, interlacing, accelerated GPU via the
original vivante support all works through this driver, with this driver
providing the X pixmaps.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-16 19:25 ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

What follows is my DRM driver for Dove, which I've been working on with
the Solid-run Cubox, which only offers HDMI output via the TDA19988
chip.

Not everything is finished off perfectly in this driver; there's quite
a number of ragged edges (particularly with page flipping, which is
completely untested.)

I have most of the Xorg driver complete - none of the DRI interfaces
checked yet (partly because that involves quite an amount of work with
Mesa).

However, graphics, video overlay, interlacing, accelerated GPU via the
original vivante support all works through this driver, with this driver
providing the X pixmaps.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 1/8] DRM: Add Dove DRM driver
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:25   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel

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@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@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

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 1/8] DRM: Add Dove DRM driver
@ 2013-05-16 19:25   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

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@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@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

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 2/8] drm/i2c: nxp-tda998x: fix EDID reading on TDA19988 devices
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:25   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel

TDA19988 devices need their RAM enabled in order to read EDID
information.  Add support for this.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index e68b58a..d71c408 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -229,6 +229,8 @@ struct tda998x_priv {
 
 /* Page 12h: HDCP and OTP */
 #define REG_TX3                   REG(0x12, 0x9a)     /* read/write */
+#define REG_TX4                   REG(0x12, 0x9b)     /* read/write */
+# define TX4_PD_RAM               (1 << 1)
 #define REG_TX33                  REG(0x12, 0xb8)     /* read/write */
 # define TX33_HDMI                (1 << 1)
 
@@ -673,6 +675,7 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
 static uint8_t *
 do_get_edid(struct drm_encoder *encoder)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
 	int j = 0, valid_extensions = 0;
 	uint8_t *block, *new;
 	bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -680,6 +683,9 @@ do_get_edid(struct drm_encoder *encoder)
 	if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
 		return NULL;
 
+	if (priv->rev == TDA19988)
+		reg_clear(encoder, REG_TX4, TX4_PD_RAM);
+
 	/* base block fetch */
 	if (read_edid_block(encoder, block, 0))
 		goto fail;
@@ -689,7 +695,7 @@ do_get_edid(struct drm_encoder *encoder)
 
 	/* if there's no extensions, we're done */
 	if (block[0x7e] == 0)
-		return block;
+		goto done;
 
 	new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
 	if (!new)
@@ -716,9 +722,15 @@ do_get_edid(struct drm_encoder *encoder)
 		block = new;
 	}
 
+done:
+	if (priv->rev == TDA19988)
+		reg_set(encoder, REG_TX4, TX4_PD_RAM);
+
 	return block;
 
 fail:
+	if (priv->rev == TDA19988)
+		reg_set(encoder, REG_TX4, TX4_PD_RAM);
 	dev_warn(encoder->dev->dev, "failed to read EDID\n");
 	kfree(block);
 	return NULL;
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 2/8] drm/i2c: nxp-tda998x: fix EDID reading on TDA19988 devices
@ 2013-05-16 19:25   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:25 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

TDA19988 devices need their RAM enabled in order to read EDID
information.  Add support for this.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index e68b58a..d71c408 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -229,6 +229,8 @@ struct tda998x_priv {
 
 /* Page 12h: HDCP and OTP */
 #define REG_TX3                   REG(0x12, 0x9a)     /* read/write */
+#define REG_TX4                   REG(0x12, 0x9b)     /* read/write */
+# define TX4_PD_RAM               (1 << 1)
 #define REG_TX33                  REG(0x12, 0xb8)     /* read/write */
 # define TX33_HDMI                (1 << 1)
 
@@ -673,6 +675,7 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
 static uint8_t *
 do_get_edid(struct drm_encoder *encoder)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
 	int j = 0, valid_extensions = 0;
 	uint8_t *block, *new;
 	bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -680,6 +683,9 @@ do_get_edid(struct drm_encoder *encoder)
 	if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
 		return NULL;
 
+	if (priv->rev == TDA19988)
+		reg_clear(encoder, REG_TX4, TX4_PD_RAM);
+
 	/* base block fetch */
 	if (read_edid_block(encoder, block, 0))
 		goto fail;
@@ -689,7 +695,7 @@ do_get_edid(struct drm_encoder *encoder)
 
 	/* if there's no extensions, we're done */
 	if (block[0x7e] == 0)
-		return block;
+		goto done;
 
 	new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
 	if (!new)
@@ -716,9 +722,15 @@ do_get_edid(struct drm_encoder *encoder)
 		block = new;
 	}
 
+done:
+	if (priv->rev == TDA19988)
+		reg_set(encoder, REG_TX4, TX4_PD_RAM);
+
 	return block;
 
 fail:
+	if (priv->rev == TDA19988)
+		reg_set(encoder, REG_TX4, TX4_PD_RAM);
 	dev_warn(encoder->dev->dev, "failed to read EDID\n");
 	kfree(block);
 	return NULL;
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:26   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel

When switching between various drivers for this device, it's possible
that some critical registers are left containing values which affect
the device operation.  One such case encountered is the VIP output
mux register.  This defaults to 0x24 on powerup, but other drivers may
set this to 0x12.  This results in incorrect colours.

Fix this by ensuring that the register is always set to the power on
default setting.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d71c408..4b4db95 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -110,6 +110,7 @@ struct tda998x_priv {
 #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
 # define VIP_CNTRL_5_CKCASE       (1 << 0)
 # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
+#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
 #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
 # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
 # define MAT_CONTRL_MAT_BP        (1 << 2)
@@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 
 	switch (mode) {
 	case DRM_MODE_DPMS_ON:
+		/* Write the default value MUX register */
+		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
 		/* enable audio and video ports */
 		reg_write(encoder, REG_ENA_AP, 0xff);
 		reg_write(encoder, REG_ENA_VP_0, 0xff);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
@ 2013-05-16 19:26   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

When switching between various drivers for this device, it's possible
that some critical registers are left containing values which affect
the device operation.  One such case encountered is the VIP output
mux register.  This defaults to 0x24 on powerup, but other drivers may
set this to 0x12.  This results in incorrect colours.

Fix this by ensuring that the register is always set to the power on
default setting.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d71c408..4b4db95 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -110,6 +110,7 @@ struct tda998x_priv {
 #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
 # define VIP_CNTRL_5_CKCASE       (1 << 0)
 # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
+#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
 #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
 # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
 # define MAT_CONTRL_MAT_BP        (1 << 2)
@@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 
 	switch (mode) {
 	case DRM_MODE_DPMS_ON:
+		/* Write the default value MUX register */
+		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
 		/* enable audio and video ports */
 		reg_write(encoder, REG_ENA_AP, 0xff);
 		reg_write(encoder, REG_ENA_VP_0, 0xff);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 4/8] drm/i2c: nxp-tda998x: fix npix/nline programming
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:26   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel

The npix/nline registers are supposed to be programmed with the total
number of pixels/lines, not the displayed pixels/lines, and not minus
one either.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 4b4db95..da2917b 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -586,8 +586,8 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
 		reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_H_TGL);
 
 	reg_write(encoder, REG_VIDFORMAT, 0x00);
-	reg_write16(encoder, REG_NPIX_MSB, mode->hdisplay - 1);
-	reg_write16(encoder, REG_NLINE_MSB, mode->vdisplay - 1);
+	reg_write16(encoder, REG_NPIX_MSB, mode->htotal);
+	reg_write16(encoder, REG_NLINE_MSB, mode->vtotal);
 	reg_write16(encoder, REG_VS_LINE_STRT_1_MSB, line_start);
 	reg_write16(encoder, REG_VS_LINE_END_1_MSB, line_end);
 	reg_write16(encoder, REG_VS_PIX_STRT_1_MSB, hs_start);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 4/8] drm/i2c: nxp-tda998x: fix npix/nline programming
@ 2013-05-16 19:26   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

The npix/nline registers are supposed to be programmed with the total
number of pixels/lines, not the displayed pixels/lines, and not minus
one either.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 4b4db95..da2917b 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -586,8 +586,8 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
 		reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_H_TGL);
 
 	reg_write(encoder, REG_VIDFORMAT, 0x00);
-	reg_write16(encoder, REG_NPIX_MSB, mode->hdisplay - 1);
-	reg_write16(encoder, REG_NLINE_MSB, mode->vdisplay - 1);
+	reg_write16(encoder, REG_NPIX_MSB, mode->htotal);
+	reg_write16(encoder, REG_NLINE_MSB, mode->vtotal);
 	reg_write16(encoder, REG_VS_LINE_STRT_1_MSB, line_start);
 	reg_write16(encoder, REG_VS_LINE_END_1_MSB, line_end);
 	reg_write16(encoder, REG_VS_PIX_STRT_1_MSB, hs_start);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 5/8] drm/i2c: nxp-tda998x: prepare for video input configuration
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:26   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |   16 ++++++++++------
 1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index da2917b..d6afd02 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -32,6 +32,9 @@ struct tda998x_priv {
 	uint16_t rev;
 	uint8_t current_page;
 	int dpms;
+	u8 vip_cntrl_0;
+	u8 vip_cntrl_1;
+	u8 vip_cntrl_2;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -447,12 +450,9 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		reg_write(encoder, REG_ENA_VP_1, 0xff);
 		reg_write(encoder, REG_ENA_VP_2, 0xff);
 		/* set muxing after enabling ports: */
-		reg_write(encoder, REG_VIP_CNTRL_0,
-				VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3));
-		reg_write(encoder, REG_VIP_CNTRL_1,
-				VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1));
-		reg_write(encoder, REG_VIP_CNTRL_2,
-				VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5));
+		reg_write(encoder, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
+		reg_write(encoder, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
+		reg_write(encoder, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
 		break;
 	case DRM_MODE_DPMS_OFF:
 		/* disable audio and video ports */
@@ -822,6 +822,10 @@ tda998x_encoder_init(struct i2c_client *client,
 	if (!priv)
 		return -ENOMEM;
 
+	priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
+	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
+	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
+
 	priv->current_page = 0;
 	priv->cec = i2c_new_dummy(client->adapter, 0x34);
 	priv->dpms = DRM_MODE_DPMS_OFF;
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 5/8] drm/i2c: nxp-tda998x: prepare for video input configuration
@ 2013-05-16 19:26   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:26 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |   16 ++++++++++------
 1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index da2917b..d6afd02 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -32,6 +32,9 @@ struct tda998x_priv {
 	uint16_t rev;
 	uint8_t current_page;
 	int dpms;
+	u8 vip_cntrl_0;
+	u8 vip_cntrl_1;
+	u8 vip_cntrl_2;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -447,12 +450,9 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		reg_write(encoder, REG_ENA_VP_1, 0xff);
 		reg_write(encoder, REG_ENA_VP_2, 0xff);
 		/* set muxing after enabling ports: */
-		reg_write(encoder, REG_VIP_CNTRL_0,
-				VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3));
-		reg_write(encoder, REG_VIP_CNTRL_1,
-				VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1));
-		reg_write(encoder, REG_VIP_CNTRL_2,
-				VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5));
+		reg_write(encoder, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
+		reg_write(encoder, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
+		reg_write(encoder, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
 		break;
 	case DRM_MODE_DPMS_OFF:
 		/* disable audio and video ports */
@@ -822,6 +822,10 @@ tda998x_encoder_init(struct i2c_client *client,
 	if (!priv)
 		return -ENOMEM;
 
+	priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
+	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
+	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
+
 	priv->current_page = 0;
 	priv->cec = i2c_new_dummy(client->adapter, 0x34);
 	priv->dpms = DRM_MODE_DPMS_OFF;
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 6/8] drm/i2c: nxp-tda998x: add video and audio input configuration
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:27   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |  308 ++++++++++++++++++++++++++++++++++++-
 1 files changed, 301 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d6afd02..8ffb844 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -23,7 +23,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_encoder_slave.h>
 #include <drm/drm_edid.h>
-
+#include <drm/i2c/tda998x.h>
 
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
@@ -32,9 +32,11 @@ struct tda998x_priv {
 	uint16_t rev;
 	uint8_t current_page;
 	int dpms;
+	bool is_hdmi_sink;
 	u8 vip_cntrl_0;
 	u8 vip_cntrl_1;
 	u8 vip_cntrl_2;
+	struct tda998x_encoder_params params;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -71,10 +73,13 @@ struct tda998x_priv {
 # define I2C_MASTER_DIS_MM        (1 << 0)
 # define I2C_MASTER_DIS_FILT      (1 << 1)
 # define I2C_MASTER_APP_STRT_LAT  (1 << 2)
+#define REG_FEAT_POWERDOWN        REG(0x00, 0x0e)     /* read/write */
+# define FEAT_POWERDOWN_SPDIF     (1 << 3)
 #define REG_INT_FLAGS_0           REG(0x00, 0x0f)     /* read/write */
 #define REG_INT_FLAGS_1           REG(0x00, 0x10)     /* read/write */
 #define REG_INT_FLAGS_2           REG(0x00, 0x11)     /* read/write */
 # define INT_FLAGS_2_EDID_BLK_RD  (1 << 1)
+#define REG_ENA_ACLK              REG(0x00, 0x16)     /* read/write */
 #define REG_ENA_VP_0              REG(0x00, 0x18)     /* read/write */
 #define REG_ENA_VP_1              REG(0x00, 0x19)     /* read/write */
 #define REG_ENA_VP_2              REG(0x00, 0x1a)     /* read/write */
@@ -113,6 +118,7 @@ struct tda998x_priv {
 #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
 # define VIP_CNTRL_5_CKCASE       (1 << 0)
 # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
+#define REG_MUX_AP                REG(0x00, 0x26)     /* read/write */
 #define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
 #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
 # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
@@ -175,6 +181,12 @@ struct tda998x_priv {
 # define HVF_CNTRL_1_PAD(x)       (((x) & 3) << 4)
 # define HVF_CNTRL_1_SEMI_PLANAR  (1 << 6)
 #define REG_RPT_CNTRL             REG(0x00, 0xf0)     /* write */
+#define REG_I2S_FORMAT            REG(0x00, 0xfc)     /* read/write */
+# define I2S_FORMAT(x)            (((x) & 3) << 0)
+#define REG_AIP_CLKSEL            REG(0x00, 0xfd)     /* write */
+# define AIP_CLKSEL_FS(x)         (((x) & 3) << 0)
+# define AIP_CLKSEL_CLK_POL(x)    (((x) & 1) << 2)
+# define AIP_CLKSEL_AIP(x)        (((x) & 7) << 3)
 
 
 /* Page 02h: PLL settings */
@@ -216,6 +228,11 @@ struct tda998x_priv {
 
 
 /* Page 10h: information frames and packets */
+#define REG_IF1_HB0               REG(0x10, 0x20)     /* read/write */
+#define REG_IF2_HB0               REG(0x10, 0x40)     /* read/write */
+#define REG_IF3_HB0               REG(0x10, 0x60)     /* read/write */
+#define REG_IF4_HB0               REG(0x10, 0x80)     /* read/write */
+#define REG_IF5_HB0               REG(0x10, 0xa0)     /* read/write */
 
 
 /* Page 11h: audio settings and content info packets */
@@ -225,10 +242,33 @@ struct tda998x_priv {
 # define AIP_CNTRL_0_LAYOUT       (1 << 2)
 # define AIP_CNTRL_0_ACR_MAN      (1 << 5)
 # define AIP_CNTRL_0_RST_CTS      (1 << 6)
+#define REG_CA_I2S                REG(0x11, 0x01)     /* read/write */
+# define CA_I2S_CA_I2S(x)         (((x) & 31) << 0)
+# define CA_I2S_HBR_CHSTAT        (1 << 6)
+#define REG_LATENCY_RD            REG(0x11, 0x04)     /* read/write */
+#define REG_ACR_CTS_0             REG(0x11, 0x05)     /* read/write */
+#define REG_ACR_CTS_1             REG(0x11, 0x06)     /* read/write */
+#define REG_ACR_CTS_2             REG(0x11, 0x07)     /* read/write */
+#define REG_ACR_N_0               REG(0x11, 0x08)     /* read/write */
+#define REG_ACR_N_1               REG(0x11, 0x09)     /* read/write */
+#define REG_ACR_N_2               REG(0x11, 0x0a)     /* read/write */
+#define REG_CTS_N                 REG(0x11, 0x0c)     /* read/write */
+# define CTS_N_K(x)               (((x) & 7) << 0)
+# define CTS_N_M(x)               (((x) & 3) << 4)
 #define REG_ENC_CNTRL             REG(0x11, 0x0d)     /* read/write */
 # define ENC_CNTRL_RST_ENC        (1 << 0)
 # define ENC_CNTRL_RST_SEL        (1 << 1)
 # define ENC_CNTRL_CTL_CODE(x)    (((x) & 3) << 2)
+#define REG_DIP_FLAGS             REG(0x11, 0x0e)     /* read/write */
+# define DIP_FLAGS_ACR            (1 << 0)
+# define DIP_FLAGS_GC             (1 << 1)
+#define REG_DIP_IF_FLAGS          REG(0x11, 0x0f)     /* read/write */
+# define DIP_IF_FLAGS_IF1         (1 << 1)
+# define DIP_IF_FLAGS_IF2         (1 << 2)
+# define DIP_IF_FLAGS_IF3         (1 << 3)
+# define DIP_IF_FLAGS_IF4         (1 << 4)
+# define DIP_IF_FLAGS_IF5         (1 << 5)
+#define REG_CH_STAT_B(x)          REG(0x11, 0x14 + (x)) /* read/write */
 
 
 /* Page 12h: HDCP and OTP */
@@ -344,6 +384,23 @@ reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt)
 	return ret;
 }
 
+static void
+reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	uint8_t buf[cnt+1];
+	int ret;
+
+	buf[0] = REG2ADDR(reg);
+	memcpy(&buf[1], p, cnt);
+
+	set_page(encoder, reg);
+
+	ret = i2c_master_send(client, buf, cnt + 1);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
+}
+
 static uint8_t
 reg_read(struct drm_encoder *encoder, uint16_t reg)
 {
@@ -421,11 +478,192 @@ tda998x_reset(struct drm_encoder *encoder)
 	reg_write(encoder, REG_PLL_SCG2,     0x10);
 }
 
+static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
+{
+	uint8_t sum = 0;
+
+	while (bytes--)
+		sum += *buf++;
+	return (255 - sum) + 1;
+}
+
+#define HB(x) (x)
+#define PB(x) (HB(2) + 1 + (x))
+
+static void
+tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr,
+		 uint8_t *buf, size_t size)
+{
+	buf[PB(0)] = tda998x_cksum(buf, size);
+
+	reg_clear(encoder, REG_DIP_IF_FLAGS, bit);
+	reg_write_range(encoder, addr, buf, size);
+	reg_set(encoder, REG_DIP_IF_FLAGS, bit);
+}
+
+static void
+tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
+{
+	uint8_t buf[PB(5) + 1];
+
+	buf[HB(0)] = 0x84;
+	buf[HB(1)] = 0x01;
+	buf[HB(2)] = 10;
+	buf[PB(0)] = 0;
+	buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
+	buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
+	buf[PB(4)] = p->audio_frame[4];
+	buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
+
+	tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
+			 sizeof(buf));
+}
+
+static void
+tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+	uint8_t buf[PB(13) + 1];
+
+	memset(buf, 0, sizeof(buf));
+	buf[HB(0)] = 0x82;
+	buf[HB(1)] = 0x02;
+	buf[HB(2)] = 13;
+	buf[PB(4)] = drm_match_cea_mode(mode);
+
+	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
+			 sizeof(buf));
+}
+
+static void tda998x_audio_mute(struct drm_encoder *encoder, bool on)
+{
+	if (on) {
+		reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
+		reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
+		reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+	} else {
+		reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+	}
+}
+
+static void
+tda998x_configure_audio(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
+{
+	uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n;
+	uint32_t n;
+
+	/* SetAudioPortConfig */
+	reg_write(encoder, REG_ENA_AP, p->audio_cfg);
+	/* SetAudioClockPortConfig */
+	reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg);
+
+	/*
+	 * layout = channelAllocation ? 1 : 0;
+	 * AudioInSetConfig(format, i2sFormat, channelAllocation,
+	 *   HDMITX_CHAN_NO_CHANGE, HDMITX_CLKPOLDSD_NO_CHANGE,
+	 *   HDMITX_SWAPDSD_NO_CHANGE, layout, HDMITX_LATENCY_CURRENT,
+	 *   dstRate)
+	 */
+	switch (p->audio_format) {
+	case AFMT_SPDIF:
+		reg_write(encoder, REG_MUX_AP, 0x40);
+		clksel_aip = AIP_CLKSEL_AIP(0);
+		/* FS64SPDIF */
+		clksel_fs = AIP_CLKSEL_FS(2);
+		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		ca_i2s = 0;
+		break;
+
+	case AFMT_I2S:
+		reg_write(encoder, REG_MUX_AP, 0x64);
+		clksel_aip = AIP_CLKSEL_AIP(1);
+		/* ACLK */
+		clksel_fs = AIP_CLKSEL_FS(0);
+		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		ca_i2s = CA_I2S_CA_I2S(0 /* channel allocation */);
+		break;
+	}
+
+	reg_write(encoder, REG_AIP_CLKSEL, clksel_aip);
+//	reg_write(encoder, REG_CA_I2S, ca_i2s);
+//	reg_write(encoder, REG_I2S_FORMAT, I2S_FORMAT(0 /* i2s format */));
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT);
+	/* latency? */
+
+	/* get video format */
+
+	/*
+	 * ctsRef = HDMITX_CTSREF_FS64SPDIF, uCtsX = HDMITX_CTSX_64
+	 * AudioInSetCts(ctsRef, rate, VidFmt, vOutFreq,
+	 *   HDMITX_CTS_AUTO, uCtsX, HDMITX_CTSK_USE_CTSX,
+	 *   HDMITX_CTSMTS_USE_CTSX, dstRate)
+	 */
+	/* Auto CTS */
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN);
+	reg_write(encoder, REG_CTS_N, cts_n);
+	reg_write(encoder, REG_AUDIO_DIV, 3);
+
+	/*
+	 * This is the approximate value of N, which happens to be
+	 * the recommended values for non-coherent clocks.
+	 */
+	n = 128 * p->audio_sample_rate / 1000;
+
+	/* Write the CTS and N values */
+	buf[0] = 0x44;
+	buf[1] = 0x42;
+	buf[2] = 0x01;
+	buf[3] = n;
+	buf[4] = n >> 8;
+	buf[5] = n >> 16;
+	reg_write_range(encoder, REG_ACR_CTS_0, buf, 6);
+
+	/* Set CTS clock reference */
+	reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
+
+	/* Reset CTS generator */
+	reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+
+	/* Write the channel status */
+	buf[0] = 0x04;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0xf1;
+	reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4);
+
+	tda998x_audio_mute(encoder, true);
+	mdelay(20);
+	tda998x_audio_mute(encoder, false);
+
+	/* Write the audio information packet */
+	tda998x_write_aif(encoder, p);
+}
+
 /* DRM encoder functions */
 
 static void
 tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
+	struct tda998x_encoder_params *p = params;
+
+	priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
+			    (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
+			    VIP_CNTRL_0_SWAP_B(p->swap_b) |
+			    (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
+	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
+			    (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
+			    VIP_CNTRL_1_SWAP_D(p->swap_d) |
+			    (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
+	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
+			    (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
+			    VIP_CNTRL_2_SWAP_F(p->swap_f) |
+			    (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
+
+	priv->params = *p;
+
+	if (p->audio_cfg)
+		tda998x_configure_audio(encoder, p);
 }
 
 static void
@@ -445,7 +683,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		/* Write the default value MUX register */
 		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
 		/* enable audio and video ports */
-		reg_write(encoder, REG_ENA_AP, 0xff);
+//		reg_write(encoder, REG_ENA_AP, priv->ena_ap);
+//		reg_write(encoder, REG_ENA_ACLK, priv->ena_aclk);
 		reg_write(encoder, REG_ENA_VP_0, 0xff);
 		reg_write(encoder, REG_ENA_VP_1, 0xff);
 		reg_write(encoder, REG_ENA_VP_2, 0xff);
@@ -456,7 +695,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		break;
 	case DRM_MODE_DPMS_OFF:
 		/* disable audio and video ports */
-		reg_write(encoder, REG_ENA_AP, 0x00);
+//		reg_write(encoder, REG_ENA_AP, 0x00);
 		reg_write(encoder, REG_ENA_VP_0, 0x00);
 		reg_write(encoder, REG_ENA_VP_1, 0x00);
 		reg_write(encoder, REG_ENA_VP_2, 0x00);
@@ -607,17 +846,31 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
 	reg_write16(encoder, REG_REFPIX_MSB, ref_pix);
 	reg_write16(encoder, REG_REFLINE_MSB, ref_line);
 
-	reg = TBG_CNTRL_1_VHX_EXT_DE |
-			TBG_CNTRL_1_VHX_EXT_HS |
-			TBG_CNTRL_1_VHX_EXT_VS |
-			TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
+	reg = TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
 			TBG_CNTRL_1_VH_TGL_2;
+	/*
+	 * It is questionable whether this is correct - the nxp driver
+	 * does not set VH_TGL_2 and the below for all display modes.
+	 */
 	if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC))
 		reg |= TBG_CNTRL_1_VH_TGL_0;
 	reg_set(encoder, REG_TBG_CNTRL_1, reg);
 
 	/* must be last register set: */
 	reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+
+	/* Only setup the info frames if the sink is HDMI */
+	if (priv->is_hdmi_sink) {
+		/* We need to turn HDMI HDCP stuff on to get audio through */
+		reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+		reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
+		reg_set(encoder, REG_TX33, TX33_HDMI);
+
+		tda998x_write_avi(encoder, adjusted_mode);
+
+		if (priv->params.audio_cfg)
+			tda998x_configure_audio(encoder, &priv->params);
+	}
 }
 
 static enum drm_connector_status
@@ -743,12 +996,14 @@ static int
 tda998x_encoder_get_modes(struct drm_encoder *encoder,
 			 struct drm_connector *connector)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
 	struct edid *edid = (struct edid *)do_get_edid(encoder);
 	int n = 0;
 
 	if (edid) {
 		drm_mode_connector_update_edid_property(connector, edid);
 		n = drm_add_edid_modes(connector, edid);
+		priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
 		kfree(edid);
 	}
 
@@ -810,6 +1065,40 @@ tda998x_remove(struct i2c_client *client)
 	return 0;
 }
 
+static ssize_t i2c_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct drm_encoder *encoder = dev_get_drvdata(dev);
+	unsigned int page, addr;
+	unsigned char val;
+
+	sscanf(buf, "%x %x", &page, &addr);
+
+	val = reg_read(encoder, REG(page, addr));
+
+	printk("i2c read %02x @ page:%02x address:%02x\n", val, page, addr);
+	return size;
+}
+
+static ssize_t i2c_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct drm_encoder *encoder = dev_get_drvdata(dev);
+	unsigned int page, addr, mask, val;
+	unsigned char rval;
+
+	sscanf(buf, "%x %x %x %x", &page, &addr, &mask, &val);
+
+	rval = reg_read(encoder, REG(page, addr));
+	rval &= ~mask;
+	rval |= val & mask;
+	reg_write(encoder, REG(page, addr), rval);
+
+	printk("i2c write %02x @ page:%02x address:%02x\n", rval, page, addr);
+	return size;
+}
+
+static DEVICE_ATTR(i2c_read, S_IWUSR, NULL, i2c_read_store);
+static DEVICE_ATTR(i2c_write, S_IWUSR, NULL, i2c_write_store);
+
 static int
 tda998x_encoder_init(struct i2c_client *client,
 		    struct drm_device *dev,
@@ -817,6 +1106,11 @@ tda998x_encoder_init(struct i2c_client *client,
 {
 	struct drm_encoder *encoder = &encoder_slave->base;
 	struct tda998x_priv *priv;
+/* debug */
+	device_create_file(&client->dev, &dev_attr_i2c_read);
+	device_create_file(&client->dev, &dev_attr_i2c_write);
+	dev_set_drvdata(&client->dev, encoder);
+/* debug end */
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 6/8] drm/i2c: nxp-tda998x: add video and audio input configuration
@ 2013-05-16 19:27   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/i2c/tda998x_drv.c |  308 ++++++++++++++++++++++++++++++++++++-
 1 files changed, 301 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d6afd02..8ffb844 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -23,7 +23,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_encoder_slave.h>
 #include <drm/drm_edid.h>
-
+#include <drm/i2c/tda998x.h>
 
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
@@ -32,9 +32,11 @@ struct tda998x_priv {
 	uint16_t rev;
 	uint8_t current_page;
 	int dpms;
+	bool is_hdmi_sink;
 	u8 vip_cntrl_0;
 	u8 vip_cntrl_1;
 	u8 vip_cntrl_2;
+	struct tda998x_encoder_params params;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -71,10 +73,13 @@ struct tda998x_priv {
 # define I2C_MASTER_DIS_MM        (1 << 0)
 # define I2C_MASTER_DIS_FILT      (1 << 1)
 # define I2C_MASTER_APP_STRT_LAT  (1 << 2)
+#define REG_FEAT_POWERDOWN        REG(0x00, 0x0e)     /* read/write */
+# define FEAT_POWERDOWN_SPDIF     (1 << 3)
 #define REG_INT_FLAGS_0           REG(0x00, 0x0f)     /* read/write */
 #define REG_INT_FLAGS_1           REG(0x00, 0x10)     /* read/write */
 #define REG_INT_FLAGS_2           REG(0x00, 0x11)     /* read/write */
 # define INT_FLAGS_2_EDID_BLK_RD  (1 << 1)
+#define REG_ENA_ACLK              REG(0x00, 0x16)     /* read/write */
 #define REG_ENA_VP_0              REG(0x00, 0x18)     /* read/write */
 #define REG_ENA_VP_1              REG(0x00, 0x19)     /* read/write */
 #define REG_ENA_VP_2              REG(0x00, 0x1a)     /* read/write */
@@ -113,6 +118,7 @@ struct tda998x_priv {
 #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
 # define VIP_CNTRL_5_CKCASE       (1 << 0)
 # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
+#define REG_MUX_AP                REG(0x00, 0x26)     /* read/write */
 #define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
 #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
 # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
@@ -175,6 +181,12 @@ struct tda998x_priv {
 # define HVF_CNTRL_1_PAD(x)       (((x) & 3) << 4)
 # define HVF_CNTRL_1_SEMI_PLANAR  (1 << 6)
 #define REG_RPT_CNTRL             REG(0x00, 0xf0)     /* write */
+#define REG_I2S_FORMAT            REG(0x00, 0xfc)     /* read/write */
+# define I2S_FORMAT(x)            (((x) & 3) << 0)
+#define REG_AIP_CLKSEL            REG(0x00, 0xfd)     /* write */
+# define AIP_CLKSEL_FS(x)         (((x) & 3) << 0)
+# define AIP_CLKSEL_CLK_POL(x)    (((x) & 1) << 2)
+# define AIP_CLKSEL_AIP(x)        (((x) & 7) << 3)
 
 
 /* Page 02h: PLL settings */
@@ -216,6 +228,11 @@ struct tda998x_priv {
 
 
 /* Page 10h: information frames and packets */
+#define REG_IF1_HB0               REG(0x10, 0x20)     /* read/write */
+#define REG_IF2_HB0               REG(0x10, 0x40)     /* read/write */
+#define REG_IF3_HB0               REG(0x10, 0x60)     /* read/write */
+#define REG_IF4_HB0               REG(0x10, 0x80)     /* read/write */
+#define REG_IF5_HB0               REG(0x10, 0xa0)     /* read/write */
 
 
 /* Page 11h: audio settings and content info packets */
@@ -225,10 +242,33 @@ struct tda998x_priv {
 # define AIP_CNTRL_0_LAYOUT       (1 << 2)
 # define AIP_CNTRL_0_ACR_MAN      (1 << 5)
 # define AIP_CNTRL_0_RST_CTS      (1 << 6)
+#define REG_CA_I2S                REG(0x11, 0x01)     /* read/write */
+# define CA_I2S_CA_I2S(x)         (((x) & 31) << 0)
+# define CA_I2S_HBR_CHSTAT        (1 << 6)
+#define REG_LATENCY_RD            REG(0x11, 0x04)     /* read/write */
+#define REG_ACR_CTS_0             REG(0x11, 0x05)     /* read/write */
+#define REG_ACR_CTS_1             REG(0x11, 0x06)     /* read/write */
+#define REG_ACR_CTS_2             REG(0x11, 0x07)     /* read/write */
+#define REG_ACR_N_0               REG(0x11, 0x08)     /* read/write */
+#define REG_ACR_N_1               REG(0x11, 0x09)     /* read/write */
+#define REG_ACR_N_2               REG(0x11, 0x0a)     /* read/write */
+#define REG_CTS_N                 REG(0x11, 0x0c)     /* read/write */
+# define CTS_N_K(x)               (((x) & 7) << 0)
+# define CTS_N_M(x)               (((x) & 3) << 4)
 #define REG_ENC_CNTRL             REG(0x11, 0x0d)     /* read/write */
 # define ENC_CNTRL_RST_ENC        (1 << 0)
 # define ENC_CNTRL_RST_SEL        (1 << 1)
 # define ENC_CNTRL_CTL_CODE(x)    (((x) & 3) << 2)
+#define REG_DIP_FLAGS             REG(0x11, 0x0e)     /* read/write */
+# define DIP_FLAGS_ACR            (1 << 0)
+# define DIP_FLAGS_GC             (1 << 1)
+#define REG_DIP_IF_FLAGS          REG(0x11, 0x0f)     /* read/write */
+# define DIP_IF_FLAGS_IF1         (1 << 1)
+# define DIP_IF_FLAGS_IF2         (1 << 2)
+# define DIP_IF_FLAGS_IF3         (1 << 3)
+# define DIP_IF_FLAGS_IF4         (1 << 4)
+# define DIP_IF_FLAGS_IF5         (1 << 5)
+#define REG_CH_STAT_B(x)          REG(0x11, 0x14 + (x)) /* read/write */
 
 
 /* Page 12h: HDCP and OTP */
@@ -344,6 +384,23 @@ reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt)
 	return ret;
 }
 
+static void
+reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	uint8_t buf[cnt+1];
+	int ret;
+
+	buf[0] = REG2ADDR(reg);
+	memcpy(&buf[1], p, cnt);
+
+	set_page(encoder, reg);
+
+	ret = i2c_master_send(client, buf, cnt + 1);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
+}
+
 static uint8_t
 reg_read(struct drm_encoder *encoder, uint16_t reg)
 {
@@ -421,11 +478,192 @@ tda998x_reset(struct drm_encoder *encoder)
 	reg_write(encoder, REG_PLL_SCG2,     0x10);
 }
 
+static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
+{
+	uint8_t sum = 0;
+
+	while (bytes--)
+		sum += *buf++;
+	return (255 - sum) + 1;
+}
+
+#define HB(x) (x)
+#define PB(x) (HB(2) + 1 + (x))
+
+static void
+tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr,
+		 uint8_t *buf, size_t size)
+{
+	buf[PB(0)] = tda998x_cksum(buf, size);
+
+	reg_clear(encoder, REG_DIP_IF_FLAGS, bit);
+	reg_write_range(encoder, addr, buf, size);
+	reg_set(encoder, REG_DIP_IF_FLAGS, bit);
+}
+
+static void
+tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
+{
+	uint8_t buf[PB(5) + 1];
+
+	buf[HB(0)] = 0x84;
+	buf[HB(1)] = 0x01;
+	buf[HB(2)] = 10;
+	buf[PB(0)] = 0;
+	buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
+	buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
+	buf[PB(4)] = p->audio_frame[4];
+	buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
+
+	tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
+			 sizeof(buf));
+}
+
+static void
+tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+	uint8_t buf[PB(13) + 1];
+
+	memset(buf, 0, sizeof(buf));
+	buf[HB(0)] = 0x82;
+	buf[HB(1)] = 0x02;
+	buf[HB(2)] = 13;
+	buf[PB(4)] = drm_match_cea_mode(mode);
+
+	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
+			 sizeof(buf));
+}
+
+static void tda998x_audio_mute(struct drm_encoder *encoder, bool on)
+{
+	if (on) {
+		reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
+		reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
+		reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+	} else {
+		reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+	}
+}
+
+static void
+tda998x_configure_audio(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
+{
+	uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n;
+	uint32_t n;
+
+	/* SetAudioPortConfig */
+	reg_write(encoder, REG_ENA_AP, p->audio_cfg);
+	/* SetAudioClockPortConfig */
+	reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg);
+
+	/*
+	 * layout = channelAllocation ? 1 : 0;
+	 * AudioInSetConfig(format, i2sFormat, channelAllocation,
+	 *   HDMITX_CHAN_NO_CHANGE, HDMITX_CLKPOLDSD_NO_CHANGE,
+	 *   HDMITX_SWAPDSD_NO_CHANGE, layout, HDMITX_LATENCY_CURRENT,
+	 *   dstRate)
+	 */
+	switch (p->audio_format) {
+	case AFMT_SPDIF:
+		reg_write(encoder, REG_MUX_AP, 0x40);
+		clksel_aip = AIP_CLKSEL_AIP(0);
+		/* FS64SPDIF */
+		clksel_fs = AIP_CLKSEL_FS(2);
+		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		ca_i2s = 0;
+		break;
+
+	case AFMT_I2S:
+		reg_write(encoder, REG_MUX_AP, 0x64);
+		clksel_aip = AIP_CLKSEL_AIP(1);
+		/* ACLK */
+		clksel_fs = AIP_CLKSEL_FS(0);
+		cts_n = CTS_N_M(3) | CTS_N_K(3);
+		ca_i2s = CA_I2S_CA_I2S(0 /* channel allocation */);
+		break;
+	}
+
+	reg_write(encoder, REG_AIP_CLKSEL, clksel_aip);
+//	reg_write(encoder, REG_CA_I2S, ca_i2s);
+//	reg_write(encoder, REG_I2S_FORMAT, I2S_FORMAT(0 /* i2s format */));
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT);
+	/* latency? */
+
+	/* get video format */
+
+	/*
+	 * ctsRef = HDMITX_CTSREF_FS64SPDIF, uCtsX = HDMITX_CTSX_64
+	 * AudioInSetCts(ctsRef, rate, VidFmt, vOutFreq,
+	 *   HDMITX_CTS_AUTO, uCtsX, HDMITX_CTSK_USE_CTSX,
+	 *   HDMITX_CTSMTS_USE_CTSX, dstRate)
+	 */
+	/* Auto CTS */
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN);
+	reg_write(encoder, REG_CTS_N, cts_n);
+	reg_write(encoder, REG_AUDIO_DIV, 3);
+
+	/*
+	 * This is the approximate value of N, which happens to be
+	 * the recommended values for non-coherent clocks.
+	 */
+	n = 128 * p->audio_sample_rate / 1000;
+
+	/* Write the CTS and N values */
+	buf[0] = 0x44;
+	buf[1] = 0x42;
+	buf[2] = 0x01;
+	buf[3] = n;
+	buf[4] = n >> 8;
+	buf[5] = n >> 16;
+	reg_write_range(encoder, REG_ACR_CTS_0, buf, 6);
+
+	/* Set CTS clock reference */
+	reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
+
+	/* Reset CTS generator */
+	reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+	reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+
+	/* Write the channel status */
+	buf[0] = 0x04;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0xf1;
+	reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4);
+
+	tda998x_audio_mute(encoder, true);
+	mdelay(20);
+	tda998x_audio_mute(encoder, false);
+
+	/* Write the audio information packet */
+	tda998x_write_aif(encoder, p);
+}
+
 /* DRM encoder functions */
 
 static void
 tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
+	struct tda998x_encoder_params *p = params;
+
+	priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
+			    (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
+			    VIP_CNTRL_0_SWAP_B(p->swap_b) |
+			    (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
+	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
+			    (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
+			    VIP_CNTRL_1_SWAP_D(p->swap_d) |
+			    (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
+	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
+			    (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
+			    VIP_CNTRL_2_SWAP_F(p->swap_f) |
+			    (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
+
+	priv->params = *p;
+
+	if (p->audio_cfg)
+		tda998x_configure_audio(encoder, p);
 }
 
 static void
@@ -445,7 +683,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		/* Write the default value MUX register */
 		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
 		/* enable audio and video ports */
-		reg_write(encoder, REG_ENA_AP, 0xff);
+//		reg_write(encoder, REG_ENA_AP, priv->ena_ap);
+//		reg_write(encoder, REG_ENA_ACLK, priv->ena_aclk);
 		reg_write(encoder, REG_ENA_VP_0, 0xff);
 		reg_write(encoder, REG_ENA_VP_1, 0xff);
 		reg_write(encoder, REG_ENA_VP_2, 0xff);
@@ -456,7 +695,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 		break;
 	case DRM_MODE_DPMS_OFF:
 		/* disable audio and video ports */
-		reg_write(encoder, REG_ENA_AP, 0x00);
+//		reg_write(encoder, REG_ENA_AP, 0x00);
 		reg_write(encoder, REG_ENA_VP_0, 0x00);
 		reg_write(encoder, REG_ENA_VP_1, 0x00);
 		reg_write(encoder, REG_ENA_VP_2, 0x00);
@@ -607,17 +846,31 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
 	reg_write16(encoder, REG_REFPIX_MSB, ref_pix);
 	reg_write16(encoder, REG_REFLINE_MSB, ref_line);
 
-	reg = TBG_CNTRL_1_VHX_EXT_DE |
-			TBG_CNTRL_1_VHX_EXT_HS |
-			TBG_CNTRL_1_VHX_EXT_VS |
-			TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
+	reg = TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
 			TBG_CNTRL_1_VH_TGL_2;
+	/*
+	 * It is questionable whether this is correct - the nxp driver
+	 * does not set VH_TGL_2 and the below for all display modes.
+	 */
 	if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC))
 		reg |= TBG_CNTRL_1_VH_TGL_0;
 	reg_set(encoder, REG_TBG_CNTRL_1, reg);
 
 	/* must be last register set: */
 	reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+
+	/* Only setup the info frames if the sink is HDMI */
+	if (priv->is_hdmi_sink) {
+		/* We need to turn HDMI HDCP stuff on to get audio through */
+		reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+		reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
+		reg_set(encoder, REG_TX33, TX33_HDMI);
+
+		tda998x_write_avi(encoder, adjusted_mode);
+
+		if (priv->params.audio_cfg)
+			tda998x_configure_audio(encoder, &priv->params);
+	}
 }
 
 static enum drm_connector_status
@@ -743,12 +996,14 @@ static int
 tda998x_encoder_get_modes(struct drm_encoder *encoder,
 			 struct drm_connector *connector)
 {
+	struct tda998x_priv *priv = to_tda998x_priv(encoder);
 	struct edid *edid = (struct edid *)do_get_edid(encoder);
 	int n = 0;
 
 	if (edid) {
 		drm_mode_connector_update_edid_property(connector, edid);
 		n = drm_add_edid_modes(connector, edid);
+		priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
 		kfree(edid);
 	}
 
@@ -810,6 +1065,40 @@ tda998x_remove(struct i2c_client *client)
 	return 0;
 }
 
+static ssize_t i2c_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct drm_encoder *encoder = dev_get_drvdata(dev);
+	unsigned int page, addr;
+	unsigned char val;
+
+	sscanf(buf, "%x %x", &page, &addr);
+
+	val = reg_read(encoder, REG(page, addr));
+
+	printk("i2c read %02x @ page:%02x address:%02x\n", val, page, addr);
+	return size;
+}
+
+static ssize_t i2c_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct drm_encoder *encoder = dev_get_drvdata(dev);
+	unsigned int page, addr, mask, val;
+	unsigned char rval;
+
+	sscanf(buf, "%x %x %x %x", &page, &addr, &mask, &val);
+
+	rval = reg_read(encoder, REG(page, addr));
+	rval &= ~mask;
+	rval |= val & mask;
+	reg_write(encoder, REG(page, addr), rval);
+
+	printk("i2c write %02x @ page:%02x address:%02x\n", rval, page, addr);
+	return size;
+}
+
+static DEVICE_ATTR(i2c_read, S_IWUSR, NULL, i2c_read_store);
+static DEVICE_ATTR(i2c_write, S_IWUSR, NULL, i2c_write_store);
+
 static int
 tda998x_encoder_init(struct i2c_client *client,
 		    struct drm_device *dev,
@@ -817,6 +1106,11 @@ tda998x_encoder_init(struct i2c_client *client,
 {
 	struct drm_encoder *encoder = &encoder_slave->base;
 	struct tda998x_priv *priv;
+/* debug */
+	device_create_file(&client->dev, &dev_attr_i2c_read);
+	device_create_file(&client->dev, &dev_attr_i2c_write);
+	dev_set_drvdata(&client->dev, encoder);
+/* debug end */
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 7/8] DRM: Dove: add support for drm tda19988 driver
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:27   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/dove/Kconfig         |    9 ++
 drivers/gpu/drm/dove/Makefile        |    2 +
 drivers/gpu/drm/dove/dove_drm.h      |    1 +
 drivers/gpu/drm/dove/dove_drv.c      |    4 +
 drivers/gpu/drm/dove/dove_tda19988.c |  249 ++++++++++++++++++++++++++++++++++
 5 files changed, 265 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/dove/dove_tda19988.c

diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
index 24844b6..718d3c5 100644
--- a/drivers/gpu/drm/dove/Kconfig
+++ b/drivers/gpu/drm/dove/Kconfig
@@ -16,6 +16,15 @@ config DRM_DOVE
 
 if DRM_DOVE != n
 
+config DRM_DOVE_TDA1998X
+	bool "Support TDA1998X HDMI output"
+	depends on I2C
+	depends on DRM_I2C_NXP_TDA998X = y
+	default y
+	help
+	  Support the TDA1998x HDMI output device found on the Solid-Run
+	  CuBox.
+
 config DRM_DOVE_CURSOR
 	bool "Enable Dove DRM hardware cursor support"
 
diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
index a2326c4..65c701e 100644
--- a/drivers/gpu/drm/dove/Makefile
+++ b/drivers/gpu/drm/dove/Makefile
@@ -4,4 +4,6 @@ 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
 
+dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o
+
 obj-$(CONFIG_DRM_DOVE) := dove.o
diff --git a/drivers/gpu/drm/dove/dove_drm.h b/drivers/gpu/drm/dove/dove_drm.h
index d00a9d6..825c553 100644
--- a/drivers/gpu/drm/dove/dove_drm.h
+++ b/drivers/gpu/drm/dove/dove_drm.h
@@ -57,6 +57,7 @@ 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);
+struct drm_connector *dove_drm_tda19988_nxp_create(struct drm_device *dev);
 
 int dove_drm_debugfs_init(struct drm_minor *);
 void dove_drm_debugfs_cleanup(struct drm_minor *);
diff --git a/drivers/gpu/drm/dove/dove_drv.c b/drivers/gpu/drm/dove/dove_drv.c
index 98cb25f..1792f00 100644
--- a/drivers/gpu/drm/dove/dove_drv.c
+++ b/drivers/gpu/drm/dove/dove_drv.c
@@ -88,6 +88,10 @@ static int dove_drm_load(struct drm_device *dev, unsigned long flags)
 		}
 	}
 
+#ifdef CONFIG_DRM_DOVE_TDA1998X
+	dove_drm_tda19988_create(dev);
+#endif
+
 	ret = drm_vblank_init(dev, n);
 	if (ret)
 		goto err_kms;
diff --git a/drivers/gpu/drm/dove/dove_tda19988.c b/drivers/gpu/drm/dove/dove_tda19988.c
new file mode 100644
index 0000000..26ef2a2
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_tda19988.c
@@ -0,0 +1,249 @@
+/*
+ * 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_edid.h"
+#include "drm_crtc_helper.h"
+#include "dove_output.h"
+#include "dove_drm.h"
+#include <drm/drm_encoder_slave.h>
+#include <drm/i2c/tda998x.h>
+
+struct dove_drm_tda19988_encoder {
+	struct drm_encoder_slave slave;
+	struct drm_encoder_helper_funcs encoder_helpers;
+};
+#define to_tda19988_encoder(enc) container_of(to_encoder_slave(enc), struct dove_drm_tda19988_encoder, slave)
+
+static int dove_drm_connector_tda19988_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *enc = conn->encoder;
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+
+	return tenc->slave.slave_funcs->mode_valid(enc, mode);
+}
+
+static int dove_drm_connector_tda19988_get_modes(struct drm_connector *conn)
+{
+	struct drm_display_mode *mode;
+	struct drm_encoder *enc = conn->encoder;
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+	int count = tenc->slave.slave_funcs->get_modes(enc, conn);
+
+	if (count)
+		return count;
+
+	mode = drm_mode_create(conn->dev);
+	mode->type = DRM_MODE_TYPE_DRIVER;
+	mode->clock = 74250;
+	mode->vrefresh = 60;
+	mode->hdisplay = 1280;
+	mode->hsync_start = mode->hdisplay + 110;
+	mode->hsync_end = mode->hsync_start + 40;
+	mode->htotal = mode->hsync_end + 220;
+	mode->vdisplay = 720;
+	mode->vsync_start = mode->vdisplay + 5;
+	mode->vsync_end = mode->vsync_start + 5;
+	mode->vtotal = mode->vsync_end + 20;
+	mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(conn, mode);
+
+	return 1;
+}
+
+static bool dove_drm_tda19988_enc_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+	adjusted->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
+			     DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC |
+			     DRM_MODE_FLAG_PCSYNC | DRM_MODE_FLAG_NCSYNC);
+
+	/* The TDA19988 always requires negative VSYNC? */
+	adjusted->flags |= DRM_MODE_FLAG_NVSYNC;
+
+	/* The TDA19988 requires positive HSYNC on 1080p or 720p */
+	if ((adjusted->hdisplay == 1920 && adjusted->vdisplay == 1080) ||
+	    (adjusted->hdisplay == 1280 && adjusted->vdisplay == 720))
+		adjusted->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		adjusted->flags |= DRM_MODE_FLAG_NHSYNC;
+
+	return true;
+}
+
+static void dove_drm_tda19988_enc_destroy(struct drm_encoder *enc)
+{
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+
+	if (tenc->slave.slave_funcs)
+		tenc->slave.slave_funcs->destroy(enc);
+
+	drm_encoder_cleanup(&tenc->slave.base);
+	kfree(tenc);
+}
+
+static const struct drm_encoder_funcs dove_drm_tda19988_enc_funcs = {
+	.destroy	= dove_drm_tda19988_enc_destroy,
+};
+
+static const struct drm_connector_helper_funcs dove_drm_conn_tda19988_helper_funcs = {
+	.get_modes	= dove_drm_connector_tda19988_get_modes,
+	.mode_valid	= dove_drm_connector_tda19988_mode_valid,
+	.best_encoder	= dove_drm_connector_best_encoder,
+};
+
+static enum drm_connector_status dove_drm_conn_tda19988_detect(
+	struct drm_connector *conn, bool force)
+{
+	struct drm_encoder *enc = conn->encoder;
+	struct drm_encoder_helper_funcs *funcs = enc->helper_private;
+	enum drm_connector_status status = funcs->detect(enc, conn);
+
+	/*
+	 * Treat all disconnected status as unknown, otherwise we fail
+	 * to initialize when we have no "connected" output devices.
+	 */
+	if (status == connector_status_disconnected)
+		status = connector_status_unknown;
+
+	return status;
+}
+
+static struct i2c_board_info info[] = {
+	{
+		.type = "tda998x",
+		.addr = 0x70,
+	},
+	{ }
+};
+
+static struct tda998x_encoder_params params = {
+#if 0
+	/* This is with a post mux value of 0x12, which is what the nxp driver uses
+	    VIP_MUX_G_B | VIP_MUX_B_R | VIP_MUX_R_G = 0x00 | 0x02 | 0x10
+	LCD out	Pins	VIP	Int VP
+	R:7:0	VPC7:0	23:16	7:0[R]
+	G:15:8	VPB7:0	15:8	23:16[G]
+	B:23:16	VPA7:0	7:0	15:8[B]
+	*/
+	.swap_a = 0,
+	.swap_b = 1,
+	.swap_c = 2,
+	.swap_d = 3,
+	.swap_e = 4,
+	.swap_f = 5,
+#else
+	/* With 0x24, there is no translation between vp_out and int_vp
+	FB	LCD out	Pins	VIP 	Int Vp
+	R:23:16	R:7:0	VPC7:0	7:0	7:0[R]
+	G:15:8	G:15:8	VPB7:0	23:16	23:16[G]
+	B:7:0	B:23:16	VPA7:0	15:8	15:8[B]
+	*/
+	.swap_a = 2,
+	.swap_b = 3,
+	.swap_c = 4,
+	.swap_d = 5,
+	.swap_e = 0,
+	.swap_f = 1,
+#endif
+	.audio_cfg = BIT(2),
+	.audio_frame[1] = 1,
+	.audio_format = AFMT_SPDIF,
+	.audio_sample_rate = 44100,
+};
+
+static int dove_drm_conn_tda19988_create(struct drm_connector *conn,
+	struct drm_encoder **enc_ret)
+{
+	struct dove_drm_tda19988_encoder *tenc;
+	struct drm_encoder_slave_funcs *sfuncs;
+	struct i2c_adapter *adap;
+	int ret;
+
+	drm_connector_helper_add(conn, &dove_drm_conn_tda19988_helper_funcs);
+
+	tenc = kzalloc(sizeof(*tenc), GFP_KERNEL);
+	if (!tenc)
+		return -ENOMEM;
+
+	tenc->slave.base.possible_crtcs = 1 << 0;
+
+	adap = i2c_get_adapter(0);
+	if (!adap) {
+		kfree(tenc);
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_encoder_init(conn->dev, &tenc->slave.base,
+			       &dove_drm_tda19988_enc_funcs,
+			       DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_ERROR("unable to init encoder\n");
+		i2c_put_adapter(adap);
+		kfree(tenc);
+		return ret;
+	}
+
+	ret = drm_i2c_encoder_init(conn->dev, &tenc->slave, adap, info);
+	i2c_put_adapter(adap);
+	if (ret) {
+		DRM_ERROR("unable to init encoder slave\n");
+		dove_drm_tda19988_enc_destroy(&tenc->slave.base);
+		return ret;
+	}
+
+	sfuncs = tenc->slave.slave_funcs;
+	tenc->encoder_helpers.dpms = sfuncs->dpms;
+	tenc->encoder_helpers.save = sfuncs->save;
+	tenc->encoder_helpers.restore = sfuncs->restore;
+	tenc->encoder_helpers.mode_fixup = dove_drm_tda19988_enc_mode_fixup;
+	tenc->encoder_helpers.prepare = dove_drm_encoder_prepare;
+	tenc->encoder_helpers.commit = dove_drm_encoder_commit;
+	tenc->encoder_helpers.mode_set = sfuncs->mode_set;
+	tenc->encoder_helpers.detect = sfuncs->detect;
+
+	drm_encoder_helper_add(&tenc->slave.base, &tenc->encoder_helpers);
+	ret = sfuncs->create_resources(&tenc->slave.base, conn);
+	if (ret) {
+		dove_drm_tda19988_enc_destroy(&tenc->slave.base);
+		return ret;
+	}
+
+	sfuncs->set_config(&tenc->slave.base, &params);
+
+	conn->encoder = &tenc->slave.base;
+	*enc_ret = &tenc->slave.base;
+
+	return ret;
+}
+
+static int dove_drm_conn_tda19988_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct dove_drm_tda19988_encoder *tenc =
+		to_tda19988_encoder(conn->encoder);
+	struct drm_encoder_slave_funcs *sfuncs = tenc->slave.slave_funcs;
+
+	return sfuncs->set_property(&tenc->slave.base, conn, property, value);
+}
+
+static const struct dove_output_type dove_drm_conn_tda19988 = {
+	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
+	.polled		= DRM_CONNECTOR_POLL_HPD,
+	.detect		= dove_drm_conn_tda19988_detect,
+	.create		= dove_drm_conn_tda19988_create,
+	.set_property	= dove_drm_conn_tda19988_set_property,
+};
+
+struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev)
+{
+	return dove_output_create(dev, &dove_drm_conn_tda19988);
+}
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 7/8] DRM: Dove: add support for drm tda19988 driver
@ 2013-05-16 19:27   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/dove/Kconfig         |    9 ++
 drivers/gpu/drm/dove/Makefile        |    2 +
 drivers/gpu/drm/dove/dove_drm.h      |    1 +
 drivers/gpu/drm/dove/dove_drv.c      |    4 +
 drivers/gpu/drm/dove/dove_tda19988.c |  249 ++++++++++++++++++++++++++++++++++
 5 files changed, 265 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/dove/dove_tda19988.c

diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
index 24844b6..718d3c5 100644
--- a/drivers/gpu/drm/dove/Kconfig
+++ b/drivers/gpu/drm/dove/Kconfig
@@ -16,6 +16,15 @@ config DRM_DOVE
 
 if DRM_DOVE != n
 
+config DRM_DOVE_TDA1998X
+	bool "Support TDA1998X HDMI output"
+	depends on I2C
+	depends on DRM_I2C_NXP_TDA998X = y
+	default y
+	help
+	  Support the TDA1998x HDMI output device found on the Solid-Run
+	  CuBox.
+
 config DRM_DOVE_CURSOR
 	bool "Enable Dove DRM hardware cursor support"
 
diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
index a2326c4..65c701e 100644
--- a/drivers/gpu/drm/dove/Makefile
+++ b/drivers/gpu/drm/dove/Makefile
@@ -4,4 +4,6 @@ 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
 
+dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o
+
 obj-$(CONFIG_DRM_DOVE) := dove.o
diff --git a/drivers/gpu/drm/dove/dove_drm.h b/drivers/gpu/drm/dove/dove_drm.h
index d00a9d6..825c553 100644
--- a/drivers/gpu/drm/dove/dove_drm.h
+++ b/drivers/gpu/drm/dove/dove_drm.h
@@ -57,6 +57,7 @@ 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);
+struct drm_connector *dove_drm_tda19988_nxp_create(struct drm_device *dev);
 
 int dove_drm_debugfs_init(struct drm_minor *);
 void dove_drm_debugfs_cleanup(struct drm_minor *);
diff --git a/drivers/gpu/drm/dove/dove_drv.c b/drivers/gpu/drm/dove/dove_drv.c
index 98cb25f..1792f00 100644
--- a/drivers/gpu/drm/dove/dove_drv.c
+++ b/drivers/gpu/drm/dove/dove_drv.c
@@ -88,6 +88,10 @@ static int dove_drm_load(struct drm_device *dev, unsigned long flags)
 		}
 	}
 
+#ifdef CONFIG_DRM_DOVE_TDA1998X
+	dove_drm_tda19988_create(dev);
+#endif
+
 	ret = drm_vblank_init(dev, n);
 	if (ret)
 		goto err_kms;
diff --git a/drivers/gpu/drm/dove/dove_tda19988.c b/drivers/gpu/drm/dove/dove_tda19988.c
new file mode 100644
index 0000000..26ef2a2
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_tda19988.c
@@ -0,0 +1,249 @@
+/*
+ * 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_edid.h"
+#include "drm_crtc_helper.h"
+#include "dove_output.h"
+#include "dove_drm.h"
+#include <drm/drm_encoder_slave.h>
+#include <drm/i2c/tda998x.h>
+
+struct dove_drm_tda19988_encoder {
+	struct drm_encoder_slave slave;
+	struct drm_encoder_helper_funcs encoder_helpers;
+};
+#define to_tda19988_encoder(enc) container_of(to_encoder_slave(enc), struct dove_drm_tda19988_encoder, slave)
+
+static int dove_drm_connector_tda19988_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *enc = conn->encoder;
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+
+	return tenc->slave.slave_funcs->mode_valid(enc, mode);
+}
+
+static int dove_drm_connector_tda19988_get_modes(struct drm_connector *conn)
+{
+	struct drm_display_mode *mode;
+	struct drm_encoder *enc = conn->encoder;
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+	int count = tenc->slave.slave_funcs->get_modes(enc, conn);
+
+	if (count)
+		return count;
+
+	mode = drm_mode_create(conn->dev);
+	mode->type = DRM_MODE_TYPE_DRIVER;
+	mode->clock = 74250;
+	mode->vrefresh = 60;
+	mode->hdisplay = 1280;
+	mode->hsync_start = mode->hdisplay + 110;
+	mode->hsync_end = mode->hsync_start + 40;
+	mode->htotal = mode->hsync_end + 220;
+	mode->vdisplay = 720;
+	mode->vsync_start = mode->vdisplay + 5;
+	mode->vsync_end = mode->vsync_start + 5;
+	mode->vtotal = mode->vsync_end + 20;
+	mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(conn, mode);
+
+	return 1;
+}
+
+static bool dove_drm_tda19988_enc_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+	adjusted->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
+			     DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC |
+			     DRM_MODE_FLAG_PCSYNC | DRM_MODE_FLAG_NCSYNC);
+
+	/* The TDA19988 always requires negative VSYNC? */
+	adjusted->flags |= DRM_MODE_FLAG_NVSYNC;
+
+	/* The TDA19988 requires positive HSYNC on 1080p or 720p */
+	if ((adjusted->hdisplay == 1920 && adjusted->vdisplay == 1080) ||
+	    (adjusted->hdisplay == 1280 && adjusted->vdisplay == 720))
+		adjusted->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		adjusted->flags |= DRM_MODE_FLAG_NHSYNC;
+
+	return true;
+}
+
+static void dove_drm_tda19988_enc_destroy(struct drm_encoder *enc)
+{
+	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
+
+	if (tenc->slave.slave_funcs)
+		tenc->slave.slave_funcs->destroy(enc);
+
+	drm_encoder_cleanup(&tenc->slave.base);
+	kfree(tenc);
+}
+
+static const struct drm_encoder_funcs dove_drm_tda19988_enc_funcs = {
+	.destroy	= dove_drm_tda19988_enc_destroy,
+};
+
+static const struct drm_connector_helper_funcs dove_drm_conn_tda19988_helper_funcs = {
+	.get_modes	= dove_drm_connector_tda19988_get_modes,
+	.mode_valid	= dove_drm_connector_tda19988_mode_valid,
+	.best_encoder	= dove_drm_connector_best_encoder,
+};
+
+static enum drm_connector_status dove_drm_conn_tda19988_detect(
+	struct drm_connector *conn, bool force)
+{
+	struct drm_encoder *enc = conn->encoder;
+	struct drm_encoder_helper_funcs *funcs = enc->helper_private;
+	enum drm_connector_status status = funcs->detect(enc, conn);
+
+	/*
+	 * Treat all disconnected status as unknown, otherwise we fail
+	 * to initialize when we have no "connected" output devices.
+	 */
+	if (status == connector_status_disconnected)
+		status = connector_status_unknown;
+
+	return status;
+}
+
+static struct i2c_board_info info[] = {
+	{
+		.type = "tda998x",
+		.addr = 0x70,
+	},
+	{ }
+};
+
+static struct tda998x_encoder_params params = {
+#if 0
+	/* This is with a post mux value of 0x12, which is what the nxp driver uses
+	    VIP_MUX_G_B | VIP_MUX_B_R | VIP_MUX_R_G = 0x00 | 0x02 | 0x10
+	LCD out	Pins	VIP	Int VP
+	R:7:0	VPC7:0	23:16	7:0[R]
+	G:15:8	VPB7:0	15:8	23:16[G]
+	B:23:16	VPA7:0	7:0	15:8[B]
+	*/
+	.swap_a = 0,
+	.swap_b = 1,
+	.swap_c = 2,
+	.swap_d = 3,
+	.swap_e = 4,
+	.swap_f = 5,
+#else
+	/* With 0x24, there is no translation between vp_out and int_vp
+	FB	LCD out	Pins	VIP 	Int Vp
+	R:23:16	R:7:0	VPC7:0	7:0	7:0[R]
+	G:15:8	G:15:8	VPB7:0	23:16	23:16[G]
+	B:7:0	B:23:16	VPA7:0	15:8	15:8[B]
+	*/
+	.swap_a = 2,
+	.swap_b = 3,
+	.swap_c = 4,
+	.swap_d = 5,
+	.swap_e = 0,
+	.swap_f = 1,
+#endif
+	.audio_cfg = BIT(2),
+	.audio_frame[1] = 1,
+	.audio_format = AFMT_SPDIF,
+	.audio_sample_rate = 44100,
+};
+
+static int dove_drm_conn_tda19988_create(struct drm_connector *conn,
+	struct drm_encoder **enc_ret)
+{
+	struct dove_drm_tda19988_encoder *tenc;
+	struct drm_encoder_slave_funcs *sfuncs;
+	struct i2c_adapter *adap;
+	int ret;
+
+	drm_connector_helper_add(conn, &dove_drm_conn_tda19988_helper_funcs);
+
+	tenc = kzalloc(sizeof(*tenc), GFP_KERNEL);
+	if (!tenc)
+		return -ENOMEM;
+
+	tenc->slave.base.possible_crtcs = 1 << 0;
+
+	adap = i2c_get_adapter(0);
+	if (!adap) {
+		kfree(tenc);
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_encoder_init(conn->dev, &tenc->slave.base,
+			       &dove_drm_tda19988_enc_funcs,
+			       DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_ERROR("unable to init encoder\n");
+		i2c_put_adapter(adap);
+		kfree(tenc);
+		return ret;
+	}
+
+	ret = drm_i2c_encoder_init(conn->dev, &tenc->slave, adap, info);
+	i2c_put_adapter(adap);
+	if (ret) {
+		DRM_ERROR("unable to init encoder slave\n");
+		dove_drm_tda19988_enc_destroy(&tenc->slave.base);
+		return ret;
+	}
+
+	sfuncs = tenc->slave.slave_funcs;
+	tenc->encoder_helpers.dpms = sfuncs->dpms;
+	tenc->encoder_helpers.save = sfuncs->save;
+	tenc->encoder_helpers.restore = sfuncs->restore;
+	tenc->encoder_helpers.mode_fixup = dove_drm_tda19988_enc_mode_fixup;
+	tenc->encoder_helpers.prepare = dove_drm_encoder_prepare;
+	tenc->encoder_helpers.commit = dove_drm_encoder_commit;
+	tenc->encoder_helpers.mode_set = sfuncs->mode_set;
+	tenc->encoder_helpers.detect = sfuncs->detect;
+
+	drm_encoder_helper_add(&tenc->slave.base, &tenc->encoder_helpers);
+	ret = sfuncs->create_resources(&tenc->slave.base, conn);
+	if (ret) {
+		dove_drm_tda19988_enc_destroy(&tenc->slave.base);
+		return ret;
+	}
+
+	sfuncs->set_config(&tenc->slave.base, &params);
+
+	conn->encoder = &tenc->slave.base;
+	*enc_ret = &tenc->slave.base;
+
+	return ret;
+}
+
+static int dove_drm_conn_tda19988_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct dove_drm_tda19988_encoder *tenc =
+		to_tda19988_encoder(conn->encoder);
+	struct drm_encoder_slave_funcs *sfuncs = tenc->slave.slave_funcs;
+
+	return sfuncs->set_property(&tenc->slave.base, conn, property, value);
+}
+
+static const struct dove_output_type dove_drm_conn_tda19988 = {
+	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
+	.polled		= DRM_CONNECTOR_POLL_HPD,
+	.detect		= dove_drm_conn_tda19988_detect,
+	.create		= dove_drm_conn_tda19988_create,
+	.set_property	= dove_drm_conn_tda19988_set_property,
+};
+
+struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev)
+{
+	return dove_output_create(dev, &dove_drm_conn_tda19988);
+}
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 8/8] DRM: dove: provide a couple of generic slave encoder helpers
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-16 19:27   ` Russell King
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/dove/dove_output.c   |   20 ++++++++++++++++++++
 drivers/gpu/drm/dove/dove_output.h   |    6 ++++++
 drivers/gpu/drm/dove/dove_tda19988.c |   23 ++---------------------
 3 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/dove/dove_output.c b/drivers/gpu/drm/dove/dove_output.c
index 7cd7d35..470ce80 100644
--- a/drivers/gpu/drm/dove/dove_output.c
+++ b/drivers/gpu/drm/dove/dove_output.c
@@ -9,6 +9,7 @@
 #include "drm_crtc_helper.h"
 #include "drm_edid.h"
 #include "drm_helper.h"
+#include "drm_encoder_slave.h"
 #include "dove_output.h"
 #include "dove_drm.h"
 
@@ -76,6 +77,25 @@ void dove_drm_encoder_commit(struct drm_encoder *encoder)
 	funcs->dpms(encoder, DRM_MODE_DPMS_ON);
 }
 
+/* Shouldn't this be a generic helper function? */
+int dove_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *encoder = conn->encoder;
+	struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+	return slave->slave_funcs->mode_valid(encoder, mode);
+}
+
+int dove_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct drm_encoder *encoder = conn->encoder;
+	struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+	return slave->slave_funcs->set_property(encoder, conn, property, value);
+}
+
 struct drm_connector *dove_output_create(struct drm_device *dev,
 	const struct dove_output_type *type)
 {
diff --git a/drivers/gpu/drm/dove/dove_output.h b/drivers/gpu/drm/dove/dove_output.h
index 1b12890..d504ef0 100644
--- a/drivers/gpu/drm/dove/dove_output.h
+++ b/drivers/gpu/drm/dove/dove_output.h
@@ -20,6 +20,12 @@ 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);
 
+int dove_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode);
+
+int dove_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value);
+
 struct drm_connector *dove_output_create(struct drm_device *dev,
 	const struct dove_output_type *);
 
diff --git a/drivers/gpu/drm/dove/dove_tda19988.c b/drivers/gpu/drm/dove/dove_tda19988.c
index 26ef2a2..0b179d7 100644
--- a/drivers/gpu/drm/dove/dove_tda19988.c
+++ b/drivers/gpu/drm/dove/dove_tda19988.c
@@ -20,15 +20,6 @@ struct dove_drm_tda19988_encoder {
 };
 #define to_tda19988_encoder(enc) container_of(to_encoder_slave(enc), struct dove_drm_tda19988_encoder, slave)
 
-static int dove_drm_connector_tda19988_mode_valid(struct drm_connector *conn,
-	struct drm_display_mode *mode)
-{
-	struct drm_encoder *enc = conn->encoder;
-	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
-
-	return tenc->slave.slave_funcs->mode_valid(enc, mode);
-}
-
 static int dove_drm_connector_tda19988_get_modes(struct drm_connector *conn)
 {
 	struct drm_display_mode *mode;
@@ -96,7 +87,7 @@ static const struct drm_encoder_funcs dove_drm_tda19988_enc_funcs = {
 
 static const struct drm_connector_helper_funcs dove_drm_conn_tda19988_helper_funcs = {
 	.get_modes	= dove_drm_connector_tda19988_get_modes,
-	.mode_valid	= dove_drm_connector_tda19988_mode_valid,
+	.mode_valid	= dove_drm_slave_encoder_mode_valid,
 	.best_encoder	= dove_drm_connector_best_encoder,
 };
 
@@ -225,22 +216,12 @@ static int dove_drm_conn_tda19988_create(struct drm_connector *conn,
 	return ret;
 }
 
-static int dove_drm_conn_tda19988_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
-{
-	struct dove_drm_tda19988_encoder *tenc =
-		to_tda19988_encoder(conn->encoder);
-	struct drm_encoder_slave_funcs *sfuncs = tenc->slave.slave_funcs;
-
-	return sfuncs->set_property(&tenc->slave.base, conn, property, value);
-}
-
 static const struct dove_output_type dove_drm_conn_tda19988 = {
 	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
 	.polled		= DRM_CONNECTOR_POLL_HPD,
 	.detect		= dove_drm_conn_tda19988_detect,
 	.create		= dove_drm_conn_tda19988_create,
-	.set_property	= dove_drm_conn_tda19988_set_property,
+	.set_property	= dove_drm_slave_encoder_set_property,
 };
 
 struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev)
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 8/8] DRM: dove: provide a couple of generic slave encoder helpers
@ 2013-05-16 19:27   ` Russell King
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King @ 2013-05-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/dove/dove_output.c   |   20 ++++++++++++++++++++
 drivers/gpu/drm/dove/dove_output.h   |    6 ++++++
 drivers/gpu/drm/dove/dove_tda19988.c |   23 ++---------------------
 3 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/dove/dove_output.c b/drivers/gpu/drm/dove/dove_output.c
index 7cd7d35..470ce80 100644
--- a/drivers/gpu/drm/dove/dove_output.c
+++ b/drivers/gpu/drm/dove/dove_output.c
@@ -9,6 +9,7 @@
 #include "drm_crtc_helper.h"
 #include "drm_edid.h"
 #include "drm_helper.h"
+#include "drm_encoder_slave.h"
 #include "dove_output.h"
 #include "dove_drm.h"
 
@@ -76,6 +77,25 @@ void dove_drm_encoder_commit(struct drm_encoder *encoder)
 	funcs->dpms(encoder, DRM_MODE_DPMS_ON);
 }
 
+/* Shouldn't this be a generic helper function? */
+int dove_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *encoder = conn->encoder;
+	struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+	return slave->slave_funcs->mode_valid(encoder, mode);
+}
+
+int dove_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct drm_encoder *encoder = conn->encoder;
+	struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+	return slave->slave_funcs->set_property(encoder, conn, property, value);
+}
+
 struct drm_connector *dove_output_create(struct drm_device *dev,
 	const struct dove_output_type *type)
 {
diff --git a/drivers/gpu/drm/dove/dove_output.h b/drivers/gpu/drm/dove/dove_output.h
index 1b12890..d504ef0 100644
--- a/drivers/gpu/drm/dove/dove_output.h
+++ b/drivers/gpu/drm/dove/dove_output.h
@@ -20,6 +20,12 @@ 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);
 
+int dove_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode);
+
+int dove_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value);
+
 struct drm_connector *dove_output_create(struct drm_device *dev,
 	const struct dove_output_type *);
 
diff --git a/drivers/gpu/drm/dove/dove_tda19988.c b/drivers/gpu/drm/dove/dove_tda19988.c
index 26ef2a2..0b179d7 100644
--- a/drivers/gpu/drm/dove/dove_tda19988.c
+++ b/drivers/gpu/drm/dove/dove_tda19988.c
@@ -20,15 +20,6 @@ struct dove_drm_tda19988_encoder {
 };
 #define to_tda19988_encoder(enc) container_of(to_encoder_slave(enc), struct dove_drm_tda19988_encoder, slave)
 
-static int dove_drm_connector_tda19988_mode_valid(struct drm_connector *conn,
-	struct drm_display_mode *mode)
-{
-	struct drm_encoder *enc = conn->encoder;
-	struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc);
-
-	return tenc->slave.slave_funcs->mode_valid(enc, mode);
-}
-
 static int dove_drm_connector_tda19988_get_modes(struct drm_connector *conn)
 {
 	struct drm_display_mode *mode;
@@ -96,7 +87,7 @@ static const struct drm_encoder_funcs dove_drm_tda19988_enc_funcs = {
 
 static const struct drm_connector_helper_funcs dove_drm_conn_tda19988_helper_funcs = {
 	.get_modes	= dove_drm_connector_tda19988_get_modes,
-	.mode_valid	= dove_drm_connector_tda19988_mode_valid,
+	.mode_valid	= dove_drm_slave_encoder_mode_valid,
 	.best_encoder	= dove_drm_connector_best_encoder,
 };
 
@@ -225,22 +216,12 @@ static int dove_drm_conn_tda19988_create(struct drm_connector *conn,
 	return ret;
 }
 
-static int dove_drm_conn_tda19988_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
-{
-	struct dove_drm_tda19988_encoder *tenc =
-		to_tda19988_encoder(conn->encoder);
-	struct drm_encoder_slave_funcs *sfuncs = tenc->slave.slave_funcs;
-
-	return sfuncs->set_property(&tenc->slave.base, conn, property, value);
-}
-
 static const struct dove_output_type dove_drm_conn_tda19988 = {
 	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
 	.polled		= DRM_CONNECTOR_POLL_HPD,
 	.detect		= dove_drm_conn_tda19988_detect,
 	.create		= dove_drm_conn_tda19988_create,
-	.set_property	= dove_drm_conn_tda19988_set_property,
+	.set_property	= dove_drm_slave_encoder_set_property,
 };
 
 struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev)
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-16 19:25 ` Russell King - ARM Linux
@ 2013-05-17 11:33   ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 11:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 16 May 2013 20:25:10 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> What follows is my DRM driver for Dove, which I've been working on with
> the Solid-run Cubox, which only offers HDMI output via the TDA19988
> chip.
> 
> Not everything is finished off perfectly in this driver; there's quite
> a number of ragged edges (particularly with page flipping, which is
> completely untested.)
> 
> I have most of the Xorg driver complete - none of the DRI interfaces
> checked yet (partly because that involves quite an amount of work with
> Mesa).
> 
> However, graphics, video overlay, interlacing, accelerated GPU via the
> original vivante support all works through this driver, with this driver
> providing the X pixmaps.

Hi Russell,

I quickly compared your dove drm driver and ours (Sebastian and me):

- CMA helper

  You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
  simplify some code.

- device tree

  Our driver depends on the DT and, by this way, it may be used for
  various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
  CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
  connected, while the display controller permits connection of a smart
  panel (port A) and a VGA screen (port B). 
  Also, the driver could be used for different chips as the Armada 160
  which has quite the same LCD controller (but one LCD only and no
  display controller - Sebastian's idea).

- module loading (si5351, tda998x)

  As our driver is loaded by the Cubox DT, and as the auxilliary drivers
  (external clock, HDMI transmitter) may also be modules, a
  synchronization mechanism (inspired by the tegra drm driver) permits
  the driver to start when all the other drivers are also started.

- display controller

  I implemented output port cloning (LCD 0 to port B) but it is not
  tested (I just have a Cubox and I think that Sebastian did not have
  time enough yet to do it).     

- LCD handling

  With our driver, the description of a smart panel (port A) may be
  done by the DT.

- hardware cursor

  Our driver always proposes the HWC 32 (RGBA 64x64).

- LCD clocks

  Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
  clocks). In our driver, the choice of the clock is done on each video
  mode change (Sebastian's idea).

- interlaced modes

  While the code is there, I did not test the interlaced modes.
  Your code may be better.

- video overlay

  Same as above: the code is in our driver (overlay plane), but it is
  not tested.

- private ioctls

  It should be easy to add them in our driver and have an API
  compatible with your X server module. 

- screen rotation (IRE)

  This feature is needed when the Dove SoC is used in a tablet and does
  not exist in both drivers.

- VGA DAC

  This feature is needed to get the VGA screen parameters (mode,
  dimensions) and does not exist in both drivers too.

Otherwise, there are some small differences in, for example,
programming the LCD modes, or treating the vblank events.

What do you think about merging?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 11:33   ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 11:33 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Thu, 16 May 2013 20:25:10 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> What follows is my DRM driver for Dove, which I've been working on with
> the Solid-run Cubox, which only offers HDMI output via the TDA19988
> chip.
> 
> Not everything is finished off perfectly in this driver; there's quite
> a number of ragged edges (particularly with page flipping, which is
> completely untested.)
> 
> I have most of the Xorg driver complete - none of the DRI interfaces
> checked yet (partly because that involves quite an amount of work with
> Mesa).
> 
> However, graphics, video overlay, interlacing, accelerated GPU via the
> original vivante support all works through this driver, with this driver
> providing the X pixmaps.

Hi Russell,

I quickly compared your dove drm driver and ours (Sebastian and me):

- CMA helper

  You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
  simplify some code.

- device tree

  Our driver depends on the DT and, by this way, it may be used for
  various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
  CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
  connected, while the display controller permits connection of a smart
  panel (port A) and a VGA screen (port B). 
  Also, the driver could be used for different chips as the Armada 160
  which has quite the same LCD controller (but one LCD only and no
  display controller - Sebastian's idea).

- module loading (si5351, tda998x)

  As our driver is loaded by the Cubox DT, and as the auxilliary drivers
  (external clock, HDMI transmitter) may also be modules, a
  synchronization mechanism (inspired by the tegra drm driver) permits
  the driver to start when all the other drivers are also started.

- display controller

  I implemented output port cloning (LCD 0 to port B) but it is not
  tested (I just have a Cubox and I think that Sebastian did not have
  time enough yet to do it).     

- LCD handling

  With our driver, the description of a smart panel (port A) may be
  done by the DT.

- hardware cursor

  Our driver always proposes the HWC 32 (RGBA 64x64).

- LCD clocks

  Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
  clocks). In our driver, the choice of the clock is done on each video
  mode change (Sebastian's idea).

- interlaced modes

  While the code is there, I did not test the interlaced modes.
  Your code may be better.

- video overlay

  Same as above: the code is in our driver (overlay plane), but it is
  not tested.

- private ioctls

  It should be easy to add them in our driver and have an API
  compatible with your X server module. 

- screen rotation (IRE)

  This feature is needed when the Dove SoC is used in a tablet and does
  not exist in both drivers.

- VGA DAC

  This feature is needed to get the VGA screen parameters (mode,
  dimensions) and does not exist in both drivers too.

Otherwise, there are some small differences in, for example,
programming the LCD modes, or treating the vblank events.

What do you think about merging?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 11:33   ` Jean-Francois Moine
@ 2013-05-17 11:58     ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-17 11:58 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/17/13 13:33, Jean-Francois Moine wrote:
> On Thu, 16 May 2013 20:25:10 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>> What follows is my DRM driver for Dove, which I've been working on with
>> the Solid-run Cubox, which only offers HDMI output via the TDA19988
>> chip.
...
> - device tree
>
>    Our driver depends on the DT and, by this way, it may be used for
>    various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
>    CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
>    connected, while the display controller permits connection of a smart
>    panel (port A) and a VGA screen (port B).
>    Also, the driver could be used for different chips as the Armada 160
>    which has quite the same LCD controller (but one LCD only and no
>    display controller - Sebastian's idea).

Jean-Francois,

as Russell already mentioned, he is not on DT yet. It's not a big deal
to add the binding parsing to his driver though. Basically, there is 
only one thing that bothers me with Russell's driver: integrating TDA
HDMI in the driver. But from what I have seen, it's almost equal to
our drm_slave_encoder stuff, so rebranding some functions should be
enough to allow any external encoder on lcdc.

> - module loading (si5351, tda998x)
>
>    As our driver is loaded by the Cubox DT, and as the auxilliary drivers
>    (external clock, HDMI transmitter) may also be modules, a
>    synchronization mechanism (inspired by the tegra drm driver) permits
>    the driver to start when all the other drivers are also started.

I agree on HDMI (when enabled through DT), but disagree on clock.
There is some clocks internally that should be exploited even if there
is no external clock available. This is actually something more general
with common clock as it does not handle dynamic clocks yet.

> - display controller
>
>    I implemented output port cloning (LCD 0 to port B) but it is not
>    tested (I just have a Cubox and I think that Sebastian did not have
>    time enough yet to do it).

Lack of time, sorry.
Would have to test on D2Plug which has both HDMI (also TDA) and VGA.

> - LCD handling
>
>    With our driver, the description of a smart panel (port A) may be
>    done by the DT.

In priciple, DT should support describing all possible panel modes. But
of course, let's start with what we can test. SmartPanel and VGA in
Jean-Francois and mine driver are just place holders. To mainline a
common driver, I suggest to take Russell's driver as a start and add
DT and stuff Russell agrees to.

> - LCD clocks
>
>    Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
>    clocks). In our driver, the choice of the clock is done on each video
>    mode change (Sebastian's idea).

This is because of the dynamic clock restriction above. Reprobing clocks
on each mode change will behave like dynamic clocks and should be
removed if common clock framework supports it someday.

Then there is a heuristic, that tries to find the best matching clock
starting with the current clock as lcdc itself can do integer division.

For Cubox with si5351 clock driver, it should be sufficient to rely on
si5351 providing the clock. That will allow all HD modes, except
1600x1200 at 60 and above which exceed 160MHz max output frequency
restriction of si5351.

> - screen rotation (IRE)
>
>    This feature is needed when the Dove SoC is used in a tablet and does
>    not exist in both drivers.

Again, let's implement what we can test - or is there anybody using
secretary mode on his TV? BTW, IRE is only available on Dove, while lcdc
controller can be reused on some PXAs.. try not to put too much Dove
dependency into lcdc if possible.

> - VGA DAC
>
>    This feature is needed to get the VGA screen parameters (mode,
>    dimensions) and does not exist in both drivers too.

Do I still have a VGA monitor to test? ;)

> What do you think about merging?

As said above, I'd prefer to take Russell's driver as a start and merge
ours (actually yours, as I just added stuff that slowed you down :P)

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 11:58     ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-17 11:58 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King - ARM Linux, Jason Cooper, David Airlie, dri-devel,
	Rob Clark, Darren Etheridge, linux-arm-kernel

On 05/17/13 13:33, Jean-Francois Moine wrote:
> On Thu, 16 May 2013 20:25:10 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>> What follows is my DRM driver for Dove, which I've been working on with
>> the Solid-run Cubox, which only offers HDMI output via the TDA19988
>> chip.
...
> - device tree
>
>    Our driver depends on the DT and, by this way, it may be used for
>    various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
>    CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
>    connected, while the display controller permits connection of a smart
>    panel (port A) and a VGA screen (port B).
>    Also, the driver could be used for different chips as the Armada 160
>    which has quite the same LCD controller (but one LCD only and no
>    display controller - Sebastian's idea).

Jean-Francois,

as Russell already mentioned, he is not on DT yet. It's not a big deal
to add the binding parsing to his driver though. Basically, there is 
only one thing that bothers me with Russell's driver: integrating TDA
HDMI in the driver. But from what I have seen, it's almost equal to
our drm_slave_encoder stuff, so rebranding some functions should be
enough to allow any external encoder on lcdc.

> - module loading (si5351, tda998x)
>
>    As our driver is loaded by the Cubox DT, and as the auxilliary drivers
>    (external clock, HDMI transmitter) may also be modules, a
>    synchronization mechanism (inspired by the tegra drm driver) permits
>    the driver to start when all the other drivers are also started.

I agree on HDMI (when enabled through DT), but disagree on clock.
There is some clocks internally that should be exploited even if there
is no external clock available. This is actually something more general
with common clock as it does not handle dynamic clocks yet.

> - display controller
>
>    I implemented output port cloning (LCD 0 to port B) but it is not
>    tested (I just have a Cubox and I think that Sebastian did not have
>    time enough yet to do it).

Lack of time, sorry.
Would have to test on D2Plug which has both HDMI (also TDA) and VGA.

> - LCD handling
>
>    With our driver, the description of a smart panel (port A) may be
>    done by the DT.

In priciple, DT should support describing all possible panel modes. But
of course, let's start with what we can test. SmartPanel and VGA in
Jean-Francois and mine driver are just place holders. To mainline a
common driver, I suggest to take Russell's driver as a start and add
DT and stuff Russell agrees to.

> - LCD clocks
>
>    Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
>    clocks). In our driver, the choice of the clock is done on each video
>    mode change (Sebastian's idea).

This is because of the dynamic clock restriction above. Reprobing clocks
on each mode change will behave like dynamic clocks and should be
removed if common clock framework supports it someday.

Then there is a heuristic, that tries to find the best matching clock
starting with the current clock as lcdc itself can do integer division.

For Cubox with si5351 clock driver, it should be sufficient to rely on
si5351 providing the clock. That will allow all HD modes, except
1600x1200@60 and above which exceed 160MHz max output frequency
restriction of si5351.

> - screen rotation (IRE)
>
>    This feature is needed when the Dove SoC is used in a tablet and does
>    not exist in both drivers.

Again, let's implement what we can test - or is there anybody using
secretary mode on his TV? BTW, IRE is only available on Dove, while lcdc
controller can be reused on some PXAs.. try not to put too much Dove
dependency into lcdc if possible.

> - VGA DAC
>
>    This feature is needed to get the VGA screen parameters (mode,
>    dimensions) and does not exist in both drivers too.

Do I still have a VGA monitor to test? ;)

> What do you think about merging?

As said above, I'd prefer to take Russell's driver as a start and merge
ours (actually yours, as I just added stuff that slowed you down :P)

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 11:33   ` Jean-Francois Moine
@ 2013-05-17 12:01     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 17, 2013 at 01:33:45PM +0200, Jean-Francois Moine wrote:
> Hi Russell,
> 
> I quickly compared your dove drm driver and ours (Sebastian and me):

I already said - I don't support DT.  I don't run any DT based ARM
devices, so I have no experience with DT.  What I care more about
is a working cubox platform, which afaik DT still can't offer yet.

> - CMA helper
> 
>   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
>   simplify some code.

Possibly, but the biggest win for me is having cacheable gem objects.
CMA will be a retrograde step for those.

> - device tree
> 
>   Our driver depends on the DT and, by this way, it may be used for
>   various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
>   CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
>   connected, while the display controller permits connection of a smart
>   panel (port A) and a VGA screen (port B). 

I only have a Cubox, so I can't test anything but HDMI.

Given that there's big questions about the polarity of signals coming
out of the Armada 510 (with the invert bits 0, are the syncs positive
or negative?) I wouldn't like to lead anyone up the path of thinking
that this driver supports anything but the tested HDMI scenario yet.

As that information is not in any of the Armada 510 TRMs, and can only
be discovered by physically probing the signals with a scope... that's
on my todo list when I feel like dismantling the Cubox and getting the
soldering iron out to separate the two boards so I can get access to
the signals.

I've already tried to explain this to Sebastian on IRC, and kept getting
nonsense back from him.  "You need to program the whole chain".  Yes,
but that makes no sense to the question I was asking.

The reason is this:
+-----------------------------+      +------------------------+
| Armada510                   |      |   TDA19988             |
| LCD controller ---> switch ---------> switch --> processing |
|   VSYNC/HSYNC               |      |                        |
+-----------------------------+      +------------------------+

The issue is that each 'switch' point is capable of inverting the sync
signal.  If you program both identically then you end up with inversion
following another inversion.  Which means no inversion.  That's why
I've found Sebastian's answers to be much less than helpful on this
point.

What I have found with the NXP TDA19988 driver is that you need to get
the input syncs to the TDA19988 set to the correct polarity for the
TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
then has its own processing internally which places the appropriate
sync codes onto the output data stream.

In some cases, getting this wrong shifts the displayed image by a few
pixels/lines.  In other cases you get no output or invalid output which
isn't recognised by the connected device.

>   Also, the driver could be used for different chips as the Armada 160
>   which has quite the same LCD controller (but one LCD only and no
>   display controller - Sebastian's idea).

That's no problem - you just provide my driver with only one IO space
for the LCD controller, and it will only use one.  If you have more
than two, it will cope as well.

> - module loading (si5351, tda998x)
> 
>   As our driver is loaded by the Cubox DT, and as the auxilliary drivers
>   (external clock, HDMI transmitter) may also be modules, a
>   synchronization mechanism (inspired by the tegra drm driver) permits
>   the driver to start when all the other drivers are also started.

The lack of Si5351 already gets tested on every cubox boot I do.  That
causes probe deferal, which allows the driver to properly start when
it becomes ready.

Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
serial8250.0: ttyS0 at MMIO 0xf1012000 (irq = 7) is a 16550A
console [ttyS0] enabled
serial8250.1: ttyS1 at MMIO 0xf1012100 (irq = 8) is a 16550A
[drm] Initialized drm 1.1.0 20060810
[drm:dove_drm_load] *ERROR* failed to get clock
platform dove-drm: Driver dove-drm requests probe deferral
...
si5351 0-0060: registered si5351 i2c client
si5351 0-0060: external clock setup : clkdev = d827f820
cubox_extclk_setup : add alias 'extclk/dovefb.0' to clkout0 w 0
external clock setup done
...
dove-drm dove-drm: found TDA19988
[drm] Supports vblank timestamp caching Rev 1 (10.10.2010).
[drm] No driver support for vblank timestamp query.
dove-drm dove-drm: failed to read EDID
Console: switching to colour frame buffer device 160x45
dove-drm dove-drm: fb0: dove-drmfb frame buffer device
dove-drm dove-drm: registered panic notifier
[drm] Initialized dove-drm 1.0.0 20120730 on minor 0

TDA998x is a different matter - I do need to propagate that error code,
and then make a decision in the tda19988 connector whether to return
-EPROBEDEFER.

(The failure to read EDID is caused by my HDMI switch, which at boot
doesn't have the cubox selected, and so doesn't provide EDID until
the cubox is selected.)

> - display controller
> 
>   I implemented output port cloning (LCD 0 to port B) but it is not
>   tested (I just have a Cubox and I think that Sebastian did not have
>   time enough yet to do it).

I didn't bother with this because - again - no access to port B on my
hardware.

> - LCD handling
> 
>   With our driver, the description of a smart panel (port A) may be
>   done by the DT.

That's simple enough to add - just another 'connector' even though it's
the same physical connection out of the Armada 510.

> - hardware cursor
> 
>   Our driver always proposes the HWC 32 (RGBA 64x64).

Hardware cursor support is pretty useless.

In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
32x64.  This is useless for Xorg's purposes, where it really wants a
64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
any effort and I've a good mind to strip that out from my driver (which
will simplify a few places.)

I've played with the idea of reducing a RGBA cursor down to 2bpp mode
where we can fit the required size in, but that doesn't work very well.

Just because the hardware does something doesn't mean you should write
code for it! :)

> - LCD clocks
> 
>   Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
>   clocks). In our driver, the choice of the clock is done on each video
>   mode change (Sebastian's idea).

Yes, this could be added to mine, probably fairly simply too, but for
the purposes of the cubox...

> - interlaced modes
> 
>   While the code is there, I did not test the interlaced modes.
>   Your code may be better.

I know mine works... I've run it at 1080i with the NXP TDA19988 driver.

> - video overlay
> 
>   Same as above: the code is in our driver (overlay plane), but it is
>   not tested.

We as a family, have watched many hours of video on mine. :)

> - private ioctls
> 
>   It should be easy to add them in our driver and have an API
>   compatible with your X server module. 
> 
> - screen rotation (IRE)
> 
>   This feature is needed when the Dove SoC is used in a tablet and does
>   not exist in both drivers.
> 
> - VGA DAC
> 
>   This feature is needed to get the VGA screen parameters (mode,
>   dimensions) and does not exist in both drivers too.
> 
> Otherwise, there are some small differences in, for example,
> programming the LCD modes, or treating the vblank events.
> 
> What do you think about merging?

Yes, I think merging the two together would probably be sane. :)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 12:01     ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 12:01 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, May 17, 2013 at 01:33:45PM +0200, Jean-Francois Moine wrote:
> Hi Russell,
> 
> I quickly compared your dove drm driver and ours (Sebastian and me):

I already said - I don't support DT.  I don't run any DT based ARM
devices, so I have no experience with DT.  What I care more about
is a working cubox platform, which afaik DT still can't offer yet.

> - CMA helper
> 
>   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
>   simplify some code.

Possibly, but the biggest win for me is having cacheable gem objects.
CMA will be a retrograde step for those.

> - device tree
> 
>   Our driver depends on the DT and, by this way, it may be used for
>   various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
>   CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
>   connected, while the display controller permits connection of a smart
>   panel (port A) and a VGA screen (port B). 

I only have a Cubox, so I can't test anything but HDMI.

Given that there's big questions about the polarity of signals coming
out of the Armada 510 (with the invert bits 0, are the syncs positive
or negative?) I wouldn't like to lead anyone up the path of thinking
that this driver supports anything but the tested HDMI scenario yet.

As that information is not in any of the Armada 510 TRMs, and can only
be discovered by physically probing the signals with a scope... that's
on my todo list when I feel like dismantling the Cubox and getting the
soldering iron out to separate the two boards so I can get access to
the signals.

I've already tried to explain this to Sebastian on IRC, and kept getting
nonsense back from him.  "You need to program the whole chain".  Yes,
but that makes no sense to the question I was asking.

The reason is this:
+-----------------------------+      +------------------------+
| Armada510                   |      |   TDA19988             |
| LCD controller ---> switch ---------> switch --> processing |
|   VSYNC/HSYNC               |      |                        |
+-----------------------------+      +------------------------+

The issue is that each 'switch' point is capable of inverting the sync
signal.  If you program both identically then you end up with inversion
following another inversion.  Which means no inversion.  That's why
I've found Sebastian's answers to be much less than helpful on this
point.

What I have found with the NXP TDA19988 driver is that you need to get
the input syncs to the TDA19988 set to the correct polarity for the
TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
then has its own processing internally which places the appropriate
sync codes onto the output data stream.

In some cases, getting this wrong shifts the displayed image by a few
pixels/lines.  In other cases you get no output or invalid output which
isn't recognised by the connected device.

>   Also, the driver could be used for different chips as the Armada 160
>   which has quite the same LCD controller (but one LCD only and no
>   display controller - Sebastian's idea).

That's no problem - you just provide my driver with only one IO space
for the LCD controller, and it will only use one.  If you have more
than two, it will cope as well.

> - module loading (si5351, tda998x)
> 
>   As our driver is loaded by the Cubox DT, and as the auxilliary drivers
>   (external clock, HDMI transmitter) may also be modules, a
>   synchronization mechanism (inspired by the tegra drm driver) permits
>   the driver to start when all the other drivers are also started.

The lack of Si5351 already gets tested on every cubox boot I do.  That
causes probe deferal, which allows the driver to properly start when
it becomes ready.

Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
serial8250.0: ttyS0 at MMIO 0xf1012000 (irq = 7) is a 16550A
console [ttyS0] enabled
serial8250.1: ttyS1 at MMIO 0xf1012100 (irq = 8) is a 16550A
[drm] Initialized drm 1.1.0 20060810
[drm:dove_drm_load] *ERROR* failed to get clock
platform dove-drm: Driver dove-drm requests probe deferral
...
si5351 0-0060: registered si5351 i2c client
si5351 0-0060: external clock setup : clkdev = d827f820
cubox_extclk_setup : add alias 'extclk/dovefb.0' to clkout0 w 0
external clock setup done
...
dove-drm dove-drm: found TDA19988
[drm] Supports vblank timestamp caching Rev 1 (10.10.2010).
[drm] No driver support for vblank timestamp query.
dove-drm dove-drm: failed to read EDID
Console: switching to colour frame buffer device 160x45
dove-drm dove-drm: fb0: dove-drmfb frame buffer device
dove-drm dove-drm: registered panic notifier
[drm] Initialized dove-drm 1.0.0 20120730 on minor 0

TDA998x is a different matter - I do need to propagate that error code,
and then make a decision in the tda19988 connector whether to return
-EPROBEDEFER.

(The failure to read EDID is caused by my HDMI switch, which at boot
doesn't have the cubox selected, and so doesn't provide EDID until
the cubox is selected.)

> - display controller
> 
>   I implemented output port cloning (LCD 0 to port B) but it is not
>   tested (I just have a Cubox and I think that Sebastian did not have
>   time enough yet to do it).

I didn't bother with this because - again - no access to port B on my
hardware.

> - LCD handling
> 
>   With our driver, the description of a smart panel (port A) may be
>   done by the DT.

That's simple enough to add - just another 'connector' even though it's
the same physical connection out of the Armada 510.

> - hardware cursor
> 
>   Our driver always proposes the HWC 32 (RGBA 64x64).

Hardware cursor support is pretty useless.

In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
32x64.  This is useless for Xorg's purposes, where it really wants a
64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
any effort and I've a good mind to strip that out from my driver (which
will simplify a few places.)

I've played with the idea of reducing a RGBA cursor down to 2bpp mode
where we can fit the required size in, but that doesn't work very well.

Just because the hardware does something doesn't mean you should write
code for it! :)

> - LCD clocks
> 
>   Each LCD may use one clock amongst 4 (AXI, LCD PLL and 2 external
>   clocks). In our driver, the choice of the clock is done on each video
>   mode change (Sebastian's idea).

Yes, this could be added to mine, probably fairly simply too, but for
the purposes of the cubox...

> - interlaced modes
> 
>   While the code is there, I did not test the interlaced modes.
>   Your code may be better.

I know mine works... I've run it at 1080i with the NXP TDA19988 driver.

> - video overlay
> 
>   Same as above: the code is in our driver (overlay plane), but it is
>   not tested.

We as a family, have watched many hours of video on mine. :)

> - private ioctls
> 
>   It should be easy to add them in our driver and have an API
>   compatible with your X server module. 
> 
> - screen rotation (IRE)
> 
>   This feature is needed when the Dove SoC is used in a tablet and does
>   not exist in both drivers.
> 
> - VGA DAC
> 
>   This feature is needed to get the VGA screen parameters (mode,
>   dimensions) and does not exist in both drivers too.
> 
> Otherwise, there are some small differences in, for example,
> programming the LCD modes, or treating the vblank events.
> 
> What do you think about merging?

Yes, I think merging the two together would probably be sane. :)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 12:01     ` Russell King - ARM Linux
@ 2013-05-17 17:40       ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 17:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 17 May 2013 13:01:15 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> I already said - I don't support DT.  I don't run any DT based ARM
> devices, so I have no experience with DT.  What I care more about
> is a working cubox platform, which afaik DT still can't offer yet.

I like the DT concept. All the job I did on the Cubox is based on DT,
even when I was still using the aweful Marvell and NXP drivers. I have
nothing which is Cubox specific in my kernel, but the driver modules.

> > - CMA helper
> > 
> >   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
> >   simplify some code.  
> 
> Possibly, but the biggest win for me is having cacheable gem objects.
> CMA will be a retrograde step for those.

I did not think yet about the possible problems which could be raised
when the output of the video decoding engine, vMeta, will be the LCD
overlay plane.

> > - device tree
> > 
> >   Our driver depends on the DT and, by this way, it may be used for
> >   various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
> >   CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
> >   connected, while the display controller permits connection of a smart
> >   panel (port A) and a VGA screen (port B).   
> 
> I only have a Cubox, so I can't test anything but HDMI.

Sebastian has other Dove based machine(s).

> Given that there's big questions about the polarity of signals coming
> out of the Armada 510 (with the invert bits 0, are the syncs positive
> or negative?) I wouldn't like to lead anyone up the path of thinking
> that this driver supports anything but the tested HDMI scenario yet.
> 
> As that information is not in any of the Armada 510 TRMs, and can only
> be discovered by physically probing the signals with a scope... that's
> on my todo list when I feel like dismantling the Cubox and getting the
> soldering iron out to separate the two boards so I can get access to
> the signals.

Yes, from the beginning, Solidrun did not give us any information about
their hardware..., nor about their software! It seems Rabeeh is only
interested by a closed XBMC.

> I've already tried to explain this to Sebastian on IRC, and kept getting
> nonsense back from him.  "You need to program the whole chain".  Yes,
> but that makes no sense to the question I was asking.
> 
> The reason is this:
> +-----------------------------+      +------------------------+
> | Armada510                   |      |   TDA19988             |
> | LCD controller ---> switch ---------> switch --> processing |
> |   VSYNC/HSYNC               |      |                        |
> +-----------------------------+      +------------------------+
> 
> The issue is that each 'switch' point is capable of inverting the sync
> signal.  If you program both identically then you end up with inversion
> following another inversion.  Which means no inversion.  That's why
> I've found Sebastian's answers to be much less than helpful on this
> point.
> 
> What I have found with the NXP TDA19988 driver is that you need to get
> the input syncs to the TDA19988 set to the correct polarity for the
> TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
> then has its own processing internally which places the appropriate
> sync codes onto the output data stream.
> 
> In some cases, getting this wrong shifts the displayed image by a few
> pixels/lines.  In other cases you get no output or invalid output which
> isn't recognised by the connected device.

Right. But I found that using Rob's tda988x driver gives me a full
image on my TV set while with NXP's driver, I had more than 20 pixels
lost on each side (left, right, top and bottom).

	[snip]
> > - module loading (si5351, tda998x)
> > 
> >   As our driver is loaded by the Cubox DT, and as the auxilliary drivers
> >   (external clock, HDMI transmitter) may also be modules, a
> >   synchronization mechanism (inspired by the tegra drm driver) permits
> >   the driver to start when all the other drivers are also started.  
> 
> The lack of Si5351 already gets tested on every cubox boot I do.  That
> causes probe deferal, which allows the driver to properly start when
> it becomes ready.
	[snip]

That's what we have. I added an escape mechanism in case the si5351 is
not present (I was waiting for the driver to be in the kernel). In this
case, after a first -EPROBEDEFER, the LCD works with either AXI or the
LCD PLL (both work fine with my TV set at 1920x1080p).

	[snip]
> > - hardware cursor
> > 
> >   Our driver always proposes the HWC 32 (RGBA 64x64).  
> 
> Hardware cursor support is pretty useless.
> 
> In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
> 32x64.  This is useless for Xorg's purposes, where it really wants a
> 64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
> cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
> any effort and I've a good mind to strip that out from my driver (which
> will simplify a few places.)
> 
> I've played with the idea of reducing a RGBA cursor down to 2bpp mode
> where we can fit the required size in, but that doesn't work very well.
> 
> Just because the hardware does something doesn't mean you should write
> code for it! :)

Maybe I did not explain correctly: the colored cursor maybe RGB888 +
transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
case. And, yes, I better like a hardware cursor: it asks for less
computation, and I get it immediately at graphic starting time!

	[snip]
> > - video overlay
> > 
> >   Same as above: the code is in our driver (overlay plane), but it is
> >   not tested.  
> 
> We as a family, have watched many hours of video on mine. :)

I wrote the drm driver because there was no easy way to use the video
decoding engine with vlc and the Marvell's drivers. Now that the driver
runs, the next step was to extend the 'modesetting' X server module to
handle the overlay planes (I wonder why nobody did it yet). But, if you
already have something running, I'd be glad to get it...

	[snip]
> > What do you think about merging?  
> 
> Yes, I think merging the two together would probably be sane. :)

OK.

The first step is "DT or not DT"? For me, the DT is more flexible
(one or two LCDs, smart panel definition, display controller or not..)
and permits easy inclusion of out of tree drivers as the private VPU
and GPU ones.

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 17:40       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 17:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, 17 May 2013 13:01:15 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> I already said - I don't support DT.  I don't run any DT based ARM
> devices, so I have no experience with DT.  What I care more about
> is a working cubox platform, which afaik DT still can't offer yet.

I like the DT concept. All the job I did on the Cubox is based on DT,
even when I was still using the aweful Marvell and NXP drivers. I have
nothing which is Cubox specific in my kernel, but the driver modules.

> > - CMA helper
> > 
> >   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
> >   simplify some code.  
> 
> Possibly, but the biggest win for me is having cacheable gem objects.
> CMA will be a retrograde step for those.

I did not think yet about the possible problems which could be raised
when the output of the video decoding engine, vMeta, will be the LCD
overlay plane.

> > - device tree
> > 
> >   Our driver depends on the DT and, by this way, it may be used for
> >   various boards (Cubox, DB-MV88AP510 Development Board, CompuLab
> >   CM-A510 Board..). Especially, in the Cubox, only a HDMI screen may be
> >   connected, while the display controller permits connection of a smart
> >   panel (port A) and a VGA screen (port B).   
> 
> I only have a Cubox, so I can't test anything but HDMI.

Sebastian has other Dove based machine(s).

> Given that there's big questions about the polarity of signals coming
> out of the Armada 510 (with the invert bits 0, are the syncs positive
> or negative?) I wouldn't like to lead anyone up the path of thinking
> that this driver supports anything but the tested HDMI scenario yet.
> 
> As that information is not in any of the Armada 510 TRMs, and can only
> be discovered by physically probing the signals with a scope... that's
> on my todo list when I feel like dismantling the Cubox and getting the
> soldering iron out to separate the two boards so I can get access to
> the signals.

Yes, from the beginning, Solidrun did not give us any information about
their hardware..., nor about their software! It seems Rabeeh is only
interested by a closed XBMC.

> I've already tried to explain this to Sebastian on IRC, and kept getting
> nonsense back from him.  "You need to program the whole chain".  Yes,
> but that makes no sense to the question I was asking.
> 
> The reason is this:
> +-----------------------------+      +------------------------+
> | Armada510                   |      |   TDA19988             |
> | LCD controller ---> switch ---------> switch --> processing |
> |   VSYNC/HSYNC               |      |                        |
> +-----------------------------+      +------------------------+
> 
> The issue is that each 'switch' point is capable of inverting the sync
> signal.  If you program both identically then you end up with inversion
> following another inversion.  Which means no inversion.  That's why
> I've found Sebastian's answers to be much less than helpful on this
> point.
> 
> What I have found with the NXP TDA19988 driver is that you need to get
> the input syncs to the TDA19988 set to the correct polarity for the
> TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
> then has its own processing internally which places the appropriate
> sync codes onto the output data stream.
> 
> In some cases, getting this wrong shifts the displayed image by a few
> pixels/lines.  In other cases you get no output or invalid output which
> isn't recognised by the connected device.

Right. But I found that using Rob's tda988x driver gives me a full
image on my TV set while with NXP's driver, I had more than 20 pixels
lost on each side (left, right, top and bottom).

	[snip]
> > - module loading (si5351, tda998x)
> > 
> >   As our driver is loaded by the Cubox DT, and as the auxilliary drivers
> >   (external clock, HDMI transmitter) may also be modules, a
> >   synchronization mechanism (inspired by the tegra drm driver) permits
> >   the driver to start when all the other drivers are also started.  
> 
> The lack of Si5351 already gets tested on every cubox boot I do.  That
> causes probe deferal, which allows the driver to properly start when
> it becomes ready.
	[snip]

That's what we have. I added an escape mechanism in case the si5351 is
not present (I was waiting for the driver to be in the kernel). In this
case, after a first -EPROBEDEFER, the LCD works with either AXI or the
LCD PLL (both work fine with my TV set at 1920x1080p).

	[snip]
> > - hardware cursor
> > 
> >   Our driver always proposes the HWC 32 (RGBA 64x64).  
> 
> Hardware cursor support is pretty useless.
> 
> In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
> 32x64.  This is useless for Xorg's purposes, where it really wants a
> 64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
> cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
> any effort and I've a good mind to strip that out from my driver (which
> will simplify a few places.)
> 
> I've played with the idea of reducing a RGBA cursor down to 2bpp mode
> where we can fit the required size in, but that doesn't work very well.
> 
> Just because the hardware does something doesn't mean you should write
> code for it! :)

Maybe I did not explain correctly: the colored cursor maybe RGB888 +
transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
case. And, yes, I better like a hardware cursor: it asks for less
computation, and I get it immediately at graphic starting time!

	[snip]
> > - video overlay
> > 
> >   Same as above: the code is in our driver (overlay plane), but it is
> >   not tested.  
> 
> We as a family, have watched many hours of video on mine. :)

I wrote the drm driver because there was no easy way to use the video
decoding engine with vlc and the Marvell's drivers. Now that the driver
runs, the next step was to extend the 'modesetting' X server module to
handle the overlay planes (I wonder why nobody did it yet). But, if you
already have something running, I'd be glad to get it...

	[snip]
> > What do you think about merging?  
> 
> Yes, I think merging the two together would probably be sane. :)

OK.

The first step is "DT or not DT"? For me, the DT is more flexible
(one or two LCDs, smart panel definition, display controller or not..)
and permits easy inclusion of out of tree drivers as the private VPU
and GPU ones.

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 17:40       ` Jean-Francois Moine
@ 2013-05-17 18:00         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 18:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> On Fri, 17 May 2013 13:01:15 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > > - CMA helper
> > > 
> > >   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
> > >   simplify some code.  
> > 
> > Possibly, but the biggest win for me is having cacheable gem objects.
> > CMA will be a retrograde step for those.
> 
> I did not think yet about the possible problems which could be raised
> when the output of the video decoding engine, vMeta, will be the LCD
> overlay plane.

Not talking about vMeta here, but the vivante GPU.  vMeta has entirely
different requirements because vMeta does not have any IOMMU, whereas
the GPU has a built-in MMU.  That alone allows us to deal with SHM-backed
buffer objects in a sensible way.

> > I've already tried to explain this to Sebastian on IRC, and kept getting
> > nonsense back from him.  "You need to program the whole chain".  Yes,
> > but that makes no sense to the question I was asking.
> > 
> > The reason is this:
> > +-----------------------------+      +------------------------+
> > | Armada510                   |      |   TDA19988             |
> > | LCD controller ---> switch ---------> switch --> processing |
> > |   VSYNC/HSYNC               |      |                        |
> > +-----------------------------+      +------------------------+
> > 
> > The issue is that each 'switch' point is capable of inverting the sync
> > signal.  If you program both identically then you end up with inversion
> > following another inversion.  Which means no inversion.  That's why
> > I've found Sebastian's answers to be much less than helpful on this
> > point.
> > 
> > What I have found with the NXP TDA19988 driver is that you need to get
> > the input syncs to the TDA19988 set to the correct polarity for the
> > TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
> > then has its own processing internally which places the appropriate
> > sync codes onto the output data stream.
> > 
> > In some cases, getting this wrong shifts the displayed image by a few
> > pixels/lines.  In other cases you get no output or invalid output which
> > isn't recognised by the connected device.
> 
> Right. But I found that using Rob's tda988x driver gives me a full
> image on my TV set while with NXP's driver, I had more than 20 pixels
> lost on each side (left, right, top and bottom).

At the moment, Rob's driver has many problems, one of them is that it
mis-programs the NPIX and NLINE registers in such a way that it causes
my TV to complain about the input.  Basically, the driver as it stands
in mainline is totally unusable for me.  The TV doesn't recognise any
output.

Yet, program the device with the CEA mode (where it uses its own internal
settings) and lo and behold, picture.  I tracked that down to NPIX and
NLINE being completely wrong.

What you're actually seeing is what's called "overscan" which is something
that TVs do with broadcast images.  The left and right pixels, top and
bottom lines are always "off screen".  However, our computer displays,
we expect the display to be "underscanned" where the top left and bottom
right most pixels correspond with the corners of the display.

There are bits in the HDMI data stream which tell the TV whether the
display should be overscanned or underscanned.  Unfortunately, many
TVs choose to ignore this data (they're permitted to do this by the
standards), and instead decide that if it's a computer resolution, they
will underscan, otherwise if it's a broadcast resolution, they will
overscan.

That's not a bug in the transmission.  That's a bug in the display device.

> > > - hardware cursor
> > > 
> > >   Our driver always proposes the HWC 32 (RGBA 64x64).  
> > 
> > Hardware cursor support is pretty useless.
> > 
> > In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
> > 32x64.  This is useless for Xorg's purposes, where it really wants a
> > 64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
> > cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
> > any effort and I've a good mind to strip that out from my driver (which
> > will simplify a few places.)
> > 
> > I've played with the idea of reducing a RGBA cursor down to 2bpp mode
> > where we can fit the required size in, but that doesn't work very well.
> > 
> > Just because the hardware does something doesn't mean you should write
> > code for it! :)
> 
> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> case. And, yes, I better like a hardware cursor: it asks for less
> computation, and I get it immediately at graphic starting time!

Interesting.  Where did you find the documentation for the transparency?
The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

> 	[snip]
> > > - video overlay
> > > 
> > >   Same as above: the code is in our driver (overlay plane), but it is
> > >   not tested.  
> > 
> > We as a family, have watched many hours of video on mine. :)
> 
> I wrote the drm driver because there was no easy way to use the video
> decoding engine with vlc and the Marvell's drivers. Now that the driver
> runs, the next step was to extend the 'modesetting' X server module to
> handle the overlay planes (I wonder why nobody did it yet). But, if you
> already have something running, I'd be glad to get it...

Ahem.  You're duplicating all my work.  I've had working vlc for mpeg2
and mpeg4 (not h264) since August/September last year!

> 	[snip]
> > > What do you think about merging?  
> > 
> > Yes, I think merging the two together would probably be sane. :)
> 
> OK.
> 
> The first step is "DT or not DT"? For me, the DT is more flexible
> (one or two LCDs, smart panel definition, display controller or not..)
> and permits easy inclusion of out of tree drivers as the private VPU
> and GPU ones.

I'd argue supporting both. :)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 18:00         ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 18:00 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> On Fri, 17 May 2013 13:01:15 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > > - CMA helper
> > > 
> > >   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
> > >   simplify some code.  
> > 
> > Possibly, but the biggest win for me is having cacheable gem objects.
> > CMA will be a retrograde step for those.
> 
> I did not think yet about the possible problems which could be raised
> when the output of the video decoding engine, vMeta, will be the LCD
> overlay plane.

Not talking about vMeta here, but the vivante GPU.  vMeta has entirely
different requirements because vMeta does not have any IOMMU, whereas
the GPU has a built-in MMU.  That alone allows us to deal with SHM-backed
buffer objects in a sensible way.

> > I've already tried to explain this to Sebastian on IRC, and kept getting
> > nonsense back from him.  "You need to program the whole chain".  Yes,
> > but that makes no sense to the question I was asking.
> > 
> > The reason is this:
> > +-----------------------------+      +------------------------+
> > | Armada510                   |      |   TDA19988             |
> > | LCD controller ---> switch ---------> switch --> processing |
> > |   VSYNC/HSYNC               |      |                        |
> > +-----------------------------+      +------------------------+
> > 
> > The issue is that each 'switch' point is capable of inverting the sync
> > signal.  If you program both identically then you end up with inversion
> > following another inversion.  Which means no inversion.  That's why
> > I've found Sebastian's answers to be much less than helpful on this
> > point.
> > 
> > What I have found with the NXP TDA19988 driver is that you need to get
> > the input syncs to the TDA19988 set to the correct polarity for the
> > TDA19988, which _isn't_ what is advertised in the EDID.  The TDA19988
> > then has its own processing internally which places the appropriate
> > sync codes onto the output data stream.
> > 
> > In some cases, getting this wrong shifts the displayed image by a few
> > pixels/lines.  In other cases you get no output or invalid output which
> > isn't recognised by the connected device.
> 
> Right. But I found that using Rob's tda988x driver gives me a full
> image on my TV set while with NXP's driver, I had more than 20 pixels
> lost on each side (left, right, top and bottom).

At the moment, Rob's driver has many problems, one of them is that it
mis-programs the NPIX and NLINE registers in such a way that it causes
my TV to complain about the input.  Basically, the driver as it stands
in mainline is totally unusable for me.  The TV doesn't recognise any
output.

Yet, program the device with the CEA mode (where it uses its own internal
settings) and lo and behold, picture.  I tracked that down to NPIX and
NLINE being completely wrong.

What you're actually seeing is what's called "overscan" which is something
that TVs do with broadcast images.  The left and right pixels, top and
bottom lines are always "off screen".  However, our computer displays,
we expect the display to be "underscanned" where the top left and bottom
right most pixels correspond with the corners of the display.

There are bits in the HDMI data stream which tell the TV whether the
display should be overscanned or underscanned.  Unfortunately, many
TVs choose to ignore this data (they're permitted to do this by the
standards), and instead decide that if it's a computer resolution, they
will underscan, otherwise if it's a broadcast resolution, they will
overscan.

That's not a bug in the transmission.  That's a bug in the display device.

> > > - hardware cursor
> > > 
> > >   Our driver always proposes the HWC 32 (RGBA 64x64).  
> > 
> > Hardware cursor support is pretty useless.
> > 
> > In RGBA mode, it's not 64x64 but you get the choice of either 64x32 or
> > 32x64.  This is useless for Xorg's purposes, where it really wants a
> > 64x64 cursor.  And the XF86 Xorg backend really wants RGBA for hardware
> > cursor, not 2bpp.  So my conclusion is that hardware cursor is not worth
> > any effort and I've a good mind to strip that out from my driver (which
> > will simplify a few places.)
> > 
> > I've played with the idea of reducing a RGBA cursor down to 2bpp mode
> > where we can fit the required size in, but that doesn't work very well.
> > 
> > Just because the hardware does something doesn't mean you should write
> > code for it! :)
> 
> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> case. And, yes, I better like a hardware cursor: it asks for less
> computation, and I get it immediately at graphic starting time!

Interesting.  Where did you find the documentation for the transparency?
The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

> 	[snip]
> > > - video overlay
> > > 
> > >   Same as above: the code is in our driver (overlay plane), but it is
> > >   not tested.  
> > 
> > We as a family, have watched many hours of video on mine. :)
> 
> I wrote the drm driver because there was no easy way to use the video
> decoding engine with vlc and the Marvell's drivers. Now that the driver
> runs, the next step was to extend the 'modesetting' X server module to
> handle the overlay planes (I wonder why nobody did it yet). But, if you
> already have something running, I'd be glad to get it...

Ahem.  You're duplicating all my work.  I've had working vlc for mpeg2
and mpeg4 (not h264) since August/September last year!

> 	[snip]
> > > What do you think about merging?  
> > 
> > Yes, I think merging the two together would probably be sane. :)
> 
> OK.
> 
> The first step is "DT or not DT"? For me, the DT is more flexible
> (one or two LCDs, smart panel definition, display controller or not..)
> and permits easy inclusion of out of tree drivers as the private VPU
> and GPU ones.

I'd argue supporting both. :)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 18:00         ` Russell King - ARM Linux
@ 2013-05-17 18:05           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 18:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 17, 2013 at 07:00:29PM +0100, Russell King - ARM Linux wrote:
> On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> > Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> > transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> > case. And, yes, I better like a hardware cursor: it asks for less
> > computation, and I get it immediately at graphic starting time!
> 
> Interesting.  Where did you find the documentation for the transparency?
> The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

Ignore that, I just found the reference (unfortunately wrapped so evince
couldn't find it with a search.)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 18:05           ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-17 18:05 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, May 17, 2013 at 07:00:29PM +0100, Russell King - ARM Linux wrote:
> On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> > Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> > transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> > case. And, yes, I better like a hardware cursor: it asks for less
> > computation, and I get it immediately at graphic starting time!
> 
> Interesting.  Where did you find the documentation for the transparency?
> The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

Ignore that, I just found the reference (unfortunately wrapped so evince
couldn't find it with a search.)

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 18:00         ` Russell King - ARM Linux
@ 2013-05-17 18:57           ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 18:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 17 May 2013 19:00:29 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> > transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> > case. And, yes, I better like a hardware cursor: it asks for less
> > computation, and I get it immediately at graphic starting time!  
> 
> Interesting.  Where did you find the documentation for the transparency?
> The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

Simply in the ? 11.3.2.1. The HWC32_TRANS_CNTL SRAM is loaded like the
HWC 2bpp, but with 00 transparent / 01 RGB.

> > The first step is "DT or not DT"? For me, the DT is more flexible
> > (one or two LCDs, smart panel definition, display controller or not..)
> > and permits easy inclusion of out of tree drivers as the private VPU
> > and GPU ones.  
> 
> I'd argue supporting both. :)

Not easy!

If you have not yet looked at our driver, here is how it starts:

- in '/', the DT contains

	video {
		compatible = "marvell,dove-video";
	};

  which loads the dove-drm module.

- its module init function registers the lcd driver, the dcon driver
  and the drm driver.

- the lcd probe function tries to get all the resources for the
  specific LCD from the DT, including the clocks and the HDMI
  transmitter.
  If some resource is lacking, it deferes.
  When all resources are there, it says "present" to the drm driver (see
  below).

  The resources of a LCD are declared in the DT by something like:

  &lcd0 {					/* the iomem and irq are declared
						 * in the Dove global DT */
	status = "okay";			/* this LCD is present and usable */
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <&si5351 0>;
							/* 3 usable clocks */
	marvell,port-type = <11>;			/* HDMIA */
	marvell,external-encoder = <&tda998x>;		/* HDMI slave encoder */
  };

- the dcon probe function gets its resources and says "present" to the
  drm driver.
  Its DT declaration is just:

  &dcon { status = "okay"; };			/* iomem and irq in the Dove DT */

- the drm probe function scans all the DT, counting its usuable devices,
  (i.e. the LCDs and the dcon), and decrement the "present" variable
  accordingly.

- when the "present" variable is null, the active devices have all
  their resources, and, then, the drm driver is activated by a call to
  drm_platform_init().

I don't see clearly how to do that with a static initialization, and I
don't want to write a "cubox-setup.c". A kernel CONFIG_CUBOX ?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-17 18:57           ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-17 18:57 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, 17 May 2013 19:00:29 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> > transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> > case. And, yes, I better like a hardware cursor: it asks for less
> > computation, and I get it immediately at graphic starting time!  
> 
> Interesting.  Where did you find the documentation for the transparency?
> The FS lists HWC32_TRANS_CNTL but omits to specify where that gets used.

Simply in the ¶ 11.3.2.1. The HWC32_TRANS_CNTL SRAM is loaded like the
HWC 2bpp, but with 00 transparent / 01 RGB.

> > The first step is "DT or not DT"? For me, the DT is more flexible
> > (one or two LCDs, smart panel definition, display controller or not..)
> > and permits easy inclusion of out of tree drivers as the private VPU
> > and GPU ones.  
> 
> I'd argue supporting both. :)

Not easy!

If you have not yet looked at our driver, here is how it starts:

- in '/', the DT contains

	video {
		compatible = "marvell,dove-video";
	};

  which loads the dove-drm module.

- its module init function registers the lcd driver, the dcon driver
  and the drm driver.

- the lcd probe function tries to get all the resources for the
  specific LCD from the DT, including the clocks and the HDMI
  transmitter.
  If some resource is lacking, it deferes.
  When all resources are there, it says "present" to the drm driver (see
  below).

  The resources of a LCD are declared in the DT by something like:

  &lcd0 {					/* the iomem and irq are declared
						 * in the Dove global DT */
	status = "okay";			/* this LCD is present and usable */
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <&si5351 0>;
							/* 3 usable clocks */
	marvell,port-type = <11>;			/* HDMIA */
	marvell,external-encoder = <&tda998x>;		/* HDMI slave encoder */
  };

- the dcon probe function gets its resources and says "present" to the
  drm driver.
  Its DT declaration is just:

  &dcon { status = "okay"; };			/* iomem and irq in the Dove DT */

- the drm probe function scans all the DT, counting its usuable devices,
  (i.e. the LCDs and the dcon), and decrement the "present" variable
  accordingly.

- when the "present" variable is null, the active devices have all
  their resources, and, then, the drm driver is activated by a call to
  drm_platform_init().

I don't see clearly how to do that with a static initialization, and I
don't want to write a "cubox-setup.c". A kernel CONFIG_CUBOX ?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
  2013-05-16 19:26   ` Russell King
@ 2013-05-18  6:56     ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18  6:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 16 May 2013 20:26:18 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> When switching between various drivers for this device, it's possible
> that some critical registers are left containing values which affect
> the device operation.  One such case encountered is the VIP output
> mux register.  This defaults to 0x24 on powerup, but other drivers may
> set this to 0x12.  This results in incorrect colours.
> 
> Fix this by ensuring that the register is always set to the power on
> default setting.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
>  1 files changed, 3 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index d71c408..4b4db95 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -110,6 +110,7 @@ struct tda998x_priv {
>  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
>  # define VIP_CNTRL_5_CKCASE       (1 << 0)
>  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> +#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
>  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
>  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
>  # define MAT_CONTRL_MAT_BP        (1 << 2)
> @@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>  
>  	switch (mode) {
>  	case DRM_MODE_DPMS_ON:
> +		/* Write the default value MUX register */
> +		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
>  		/* enable audio and video ports */
>  		reg_write(encoder, REG_ENA_AP, 0xff);
>  		reg_write(encoder, REG_ENA_VP_0, 0xff);

This register is never touched. Should not this setting better go at
reset time (in tda998x_reset)?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
@ 2013-05-18  6:56     ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18  6:56 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Thu, 16 May 2013 20:26:18 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> When switching between various drivers for this device, it's possible
> that some critical registers are left containing values which affect
> the device operation.  One such case encountered is the VIP output
> mux register.  This defaults to 0x24 on powerup, but other drivers may
> set this to 0x12.  This results in incorrect colours.
> 
> Fix this by ensuring that the register is always set to the power on
> default setting.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
>  1 files changed, 3 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index d71c408..4b4db95 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -110,6 +110,7 @@ struct tda998x_priv {
>  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
>  # define VIP_CNTRL_5_CKCASE       (1 << 0)
>  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> +#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
>  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
>  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
>  # define MAT_CONTRL_MAT_BP        (1 << 2)
> @@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>  
>  	switch (mode) {
>  	case DRM_MODE_DPMS_ON:
> +		/* Write the default value MUX register */
> +		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
>  		/* enable audio and video ports */
>  		reg_write(encoder, REG_ENA_AP, 0xff);
>  		reg_write(encoder, REG_ENA_VP_0, 0xff);

This register is never touched. Should not this setting better go at
reset time (in tda998x_reset)?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/4] Add DT support to rmk's Dove DRM driver
  2013-05-16 19:25 ` Russell King - ARM Linux
                   ` (9 preceding siblings ...)
  (?)
@ 2013-05-18 17:12 ` Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 1/4] ARM: dove: add lcd controller DT nodes Sebastian Hesselbarth
                     ` (3 more replies)
  -1 siblings, 4 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 17:12 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King, dri-devel, linux-arm-kernel,
	Jason Cooper

This RFC adds DT support to the DRM driver for Marvell Dove SoCs
posted by Russell King recently. For those booting DT with appended
ATAGs, remember to reduce probed memory by passing mem=1008M as
kernel parameter.

There was an include missing in Russell's RFC that is also added.

Sebastian Hesselbarth (4):
  ARM: dove: add lcd controller DT nodes
  ARM: dove: add video card node for SolidRun CuBox
  DRM: add OF support for Dove DRM driver
  DRM: tda998x: add missing include

 arch/arm/boot/dts/dove-cubox.dts |   16 +++++-
 arch/arm/boot/dts/dove.dtsi      |   16 ++++++
 drivers/gpu/drm/dove/Kconfig     |    4 ++
 drivers/gpu/drm/dove/Makefile    |    1 +
 drivers/gpu/drm/dove/dove_card.c |  110 ++++++++++++++++++++++++++++++++++++++
 include/drm/i2c/tda998x.h        |   23 ++++++++
 6 files changed, 169 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/dove/dove_card.c
 create mode 100644 include/drm/i2c/tda998x.h
---
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Cc: dri-devel@lists.freedesktop.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Jean-Francois Moine <moinejf@free.fr>
-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 1/4] ARM: dove: add lcd controller DT nodes
  2013-05-18 17:12 ` [RFC 0/4] Add DT support to rmk's Dove DRM driver Sebastian Hesselbarth
@ 2013-05-18 17:12   ` Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox Sebastian Hesselbarth
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 17:12 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King, dri-devel, linux-arm-kernel,
	Jason Cooper

This adds device tree nodes for the lcd controllers found on Marvell
Dove SoCs. For now, there is no DT documentation and clocks property
should refer to clock connected to extclk0 pin.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Cc: dri-devel@lists.freedesktop.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Jean-Francois Moine <moinejf@free.fr>
---
 arch/arm/boot/dts/dove.dtsi |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 6cab468..2053e86 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -258,5 +258,21 @@
 				dmacap,xor;
 			};
 		};
+
+		lcd0: lcd-controller@820000 {
+			compatible = "marvell,dove-lcd";
+			reg = <0x820000 0x200>;
+			interrupts = <47>;
+			clocks = <0>;
+			status = "disabled";
+		};
+
+		lcd1: lcd-controller@810000 {
+			compatible = "marvell,dove-lcd";
+			reg = <0x810000 0x200>;
+			interrupts = <46>;
+			clocks = <0>;
+			status = "disabled";
+		};
 	};
 };
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox
  2013-05-18 17:12 ` [RFC 0/4] Add DT support to rmk's Dove DRM driver Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 1/4] ARM: dove: add lcd controller DT nodes Sebastian Hesselbarth
@ 2013-05-18 17:12   ` Sebastian Hesselbarth
  2013-05-18 17:33       ` Jean-Francois Moine
  2013-05-18 17:12   ` [RFC 3/4] DRM: add OF support for Dove DRM driver Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 4/4] DRM: tda998x: add missing include Sebastian Hesselbarth
  3 siblings, 1 reply; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 17:12 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King, dri-devel, linux-arm-kernel,
	Jason Cooper

This adds a video card node required for rmk's dove_drm driver. Reg
property matches reserved memory region (currently 16M at top of memory),
clocks property should carry extclk0 for now.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Cc: dri-devel@lists.freedesktop.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Jean-Francois Moine <moinejf@free.fr>
---
 arch/arm/boot/dts/dove-cubox.dts |   16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts
index ed2b7b2..f26d0d2 100644
--- a/arch/arm/boot/dts/dove-cubox.dts
+++ b/arch/arm/boot/dts/dove-cubox.dts
@@ -8,7 +8,7 @@
 
 	memory {
 		device_type = "memory";
-		reg = <0x00000000 0x40000000>;
+		reg = <0x00000000 0x3f000000>;
 	};
 
 	chosen {
@@ -52,10 +52,24 @@
 			#clock-cells = <0>;
 		};
 	};
+
+	video {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		vcard: video-card {
+			compatible = "marvell,dove-video-card";
+			reg = <0x3f000000 0x1000000>;
+			clocks = <&si5351 0>, <&si5351 0>;
+		};
+	};
 };
 
 &uart0 { status = "okay"; };
 &sata0 { status = "okay"; };
+&lcd0 { status = "okay"; };
 
 &i2c0 {
 	status = "okay";
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 17:12 ` [RFC 0/4] Add DT support to rmk's Dove DRM driver Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 1/4] ARM: dove: add lcd controller DT nodes Sebastian Hesselbarth
  2013-05-18 17:12   ` [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox Sebastian Hesselbarth
@ 2013-05-18 17:12   ` Sebastian Hesselbarth
  2013-05-18 17:45       ` Jean-Francois Moine
  2013-05-18 17:12   ` [RFC 4/4] DRM: tda998x: add missing include Sebastian Hesselbarth
  3 siblings, 1 reply; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 17:12 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King, dri-devel, linux-arm-kernel,
	Jason Cooper

This adds OF support for the Dove DRM driver recently posted as RFC by
Russell King.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Cc: dri-devel@lists.freedesktop.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/dove/Kconfig     |    4 ++
 drivers/gpu/drm/dove/Makefile    |    1 +
 drivers/gpu/drm/dove/dove_card.c |  110 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 115 insertions(+)
 create mode 100644 drivers/gpu/drm/dove/dove_card.c

diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
index 718d3c5..a943ea5 100644
--- a/drivers/gpu/drm/dove/Kconfig
+++ b/drivers/gpu/drm/dove/Kconfig
@@ -28,4 +28,8 @@ config DRM_DOVE_TDA1998X
 config DRM_DOVE_CURSOR
 	bool "Enable Dove DRM hardware cursor support"
 
+config DRM_DOVE_OF
+	bool "Enable Dove DRM OF video card"
+	depends on OF
+
 endif
diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
index 65c701e..f0b6eed 100644
--- a/drivers/gpu/drm/dove/Makefile
+++ b/drivers/gpu/drm/dove/Makefile
@@ -5,5 +5,6 @@ dove-y			:= dove_crtc.o dove_drv.o dove_fb.o dove_fbdev.o \
 dove-$(CONFIG_DEBUG_FS) += dove_debugfs.o
 
 dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o
+dove-$(CONFIG_DRM_DOVE_OF) += dove_card.o
 
 obj-$(CONFIG_DRM_DOVE) := dove.o
diff --git a/drivers/gpu/drm/dove/dove_card.c b/drivers/gpu/drm/dove/dove_card.c
new file mode 100644
index 0000000..e4bcb5b
--- /dev/null
+++ b/drivers/gpu/drm/dove/dove_card.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013
+ *   Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define DOVE_LCD0_BASE	0x20000
+#define DOVE_LCD1_BASE	0x10000
+
+static struct resource dove_drm_resources[5];
+static struct platform_device dove_drm_platform_device = {
+	.name = "dove-drm",
+	.id = 0,
+	.dev = { .coherent_dma_mask = ~0, },
+	.resource = dove_drm_resources,
+};
+
+static int dove_card_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *lcdnp;
+	struct resource *res = dove_drm_resources;
+	int ret, n = 0, crtcs = 0;
+
+	/* get video memory resource */
+	if (of_address_to_resource(np, 0, &res[n++])) {
+		dev_err(&pdev->dev, "invalid or missing video memory\n");
+		return -EINVAL;
+	}
+
+	/* get reg and irq resource from each enabled lcdc */
+	for_each_compatible_node(lcdnp, NULL, "marvell,dove-lcd") {
+		struct clk_lookup *cl;
+		struct clk *clk;
+		int lcd;
+
+		if (!of_device_is_available(lcdnp))
+			continue;
+
+		ret = of_address_to_resource(lcdnp, 0, &res[n]);
+		if (ret)
+			return ret;
+		lcd = ((res[n].start & 0xfffff) == DOVE_LCD1_BASE);
+		n++;
+
+		ret = of_irq_to_resource(lcdnp, 0, &res[n]);
+		if (ret < 0)
+			return ret;
+		n++;
+
+		crtcs++;
+
+		clk = clk_get(&pdev->dev, NULL);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			if (ret == -ENOENT)
+				return -EPROBE_DEFER;
+			return ret;
+		}
+
+		/* add clock alias for dovefb.0 */
+		cl = clkdev_alloc(clk, "extclk", "dovefb.0");
+		if (cl)
+			clkdev_add(cl);
+		clk_put(clk);
+	}
+
+	if (!crtcs)
+		return -ENODEV;
+
+	dove_drm_platform_device.num_resources = n;
+	ret = platform_device_register(&dove_drm_platform_device);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register drm device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id dove_card_of_ids[] = {
+	{ .compatible = "marvell,dove-video-card", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dove_card_of_ids);
+
+static struct platform_driver dove_card_driver = {
+	.probe	= dove_card_probe,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "dove-drm-card",
+		.of_match_table = of_match_ptr(dove_card_of_ids),
+	},
+};
+module_platform_driver(dove_card_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
+MODULE_DESCRIPTION("Dove DRM Graphics Card");
+MODULE_LICENSE("GPL");
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 17:12 ` [RFC 0/4] Add DT support to rmk's Dove DRM driver Sebastian Hesselbarth
                     ` (2 preceding siblings ...)
  2013-05-18 17:12   ` [RFC 3/4] DRM: add OF support for Dove DRM driver Sebastian Hesselbarth
@ 2013-05-18 17:12   ` Sebastian Hesselbarth
  2013-05-18 17:46       ` Jean-Francois Moine
  3 siblings, 1 reply; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 17:12 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King, dri-devel, linux-arm-kernel,
	Jason Cooper

The RFC sent by Russell King was missing an include for tda998x. This
is just a compatible clone to remember Russell to add that later.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel@lists.infradead.org
Cc: dri-devel@lists.freedesktop.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Jean-Francois Moine <moinejf@free.fr>
---
 include/drm/i2c/tda998x.h |   23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 include/drm/i2c/tda998x.h

diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
new file mode 100644
index 0000000..41f799f
--- /dev/null
+++ b/include/drm/i2c/tda998x.h
@@ -0,0 +1,23 @@
+#ifndef __TDA998X_H__
+#define __TDA998X_H__
+
+enum tda998x_audio_format {
+	AFMT_I2S,
+	AFMT_SPDIF,
+};
+
+struct tda998x_encoder_params {
+	int audio_cfg;
+	int audio_clk_cfg;
+	enum tda998x_audio_format audio_format;
+	int audio_sample_rate;
+	char audio_frame[6];
+	int swap_a, mirr_a;
+	int swap_b, mirr_b;
+	int swap_c, mirr_c;
+	int swap_d, mirr_d;
+	int swap_e, mirr_e;
+	int swap_f, mirr_f;
+};
+
+#endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 91+ messages in thread

* [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox
  2013-05-18 17:12   ` [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox Sebastian Hesselbarth
@ 2013-05-18 17:33       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 19:12:17 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> This adds a video card node required for rmk's dove_drm driver. Reg
> property matches reserved memory region (currently 16M at top of memory),
> clocks property should carry extclk0 for now.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: dri-devel at lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  arch/arm/boot/dts/dove-cubox.dts |   16 +++++++++++++++-
>  1 file changed, 15 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts
> index ed2b7b2..f26d0d2 100644
> --- a/arch/arm/boot/dts/dove-cubox.dts
> +++ b/arch/arm/boot/dts/dove-cubox.dts
> @@ -8,7 +8,7 @@
>  
>  	memory {
>  		device_type = "memory";
> -		reg = <0x00000000 0x40000000>;
> +		reg = <0x00000000 0x3f000000>;
>  	};
>  
>  	chosen {
> @@ -52,10 +52,24 @@
>  			#clock-cells = <0>;
>  		};
>  	};
> +
> +	video {
> +		compatible = "simple-bus";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		vcard: video-card {
> +			compatible = "marvell,dove-video-card";
> +			reg = <0x3f000000 0x1000000>;
> +			clocks = <&si5351 0>, <&si5351 0>;
> +		};
> +	};
>  };
>  
>  &uart0 { status = "okay"; };
>  &sata0 { status = "okay"; };
> +&lcd0 { status = "okay"; };
>  
>  &i2c0 {
>  	status = "okay";

May you explain a bit more this strange hack?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox
@ 2013-05-18 17:33       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:33 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On Sat, 18 May 2013 19:12:17 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> This adds a video card node required for rmk's dove_drm driver. Reg
> property matches reserved memory region (currently 16M at top of memory),
> clocks property should carry extclk0 for now.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: dri-devel@lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  arch/arm/boot/dts/dove-cubox.dts |   16 +++++++++++++++-
>  1 file changed, 15 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts
> index ed2b7b2..f26d0d2 100644
> --- a/arch/arm/boot/dts/dove-cubox.dts
> +++ b/arch/arm/boot/dts/dove-cubox.dts
> @@ -8,7 +8,7 @@
>  
>  	memory {
>  		device_type = "memory";
> -		reg = <0x00000000 0x40000000>;
> +		reg = <0x00000000 0x3f000000>;
>  	};
>  
>  	chosen {
> @@ -52,10 +52,24 @@
>  			#clock-cells = <0>;
>  		};
>  	};
> +
> +	video {
> +		compatible = "simple-bus";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +
> +		vcard: video-card {
> +			compatible = "marvell,dove-video-card";
> +			reg = <0x3f000000 0x1000000>;
> +			clocks = <&si5351 0>, <&si5351 0>;
> +		};
> +	};
>  };
>  
>  &uart0 { status = "okay"; };
>  &sata0 { status = "okay"; };
> +&lcd0 { status = "okay"; };
>  
>  &i2c0 {
>  	status = "okay";

May you explain a bit more this strange hack?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 17:12   ` [RFC 3/4] DRM: add OF support for Dove DRM driver Sebastian Hesselbarth
@ 2013-05-18 17:45       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 19:12:18 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> This adds OF support for the Dove DRM driver recently posted as RFC by
> Russell King.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: dri-devel at lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  drivers/gpu/drm/dove/Kconfig     |    4 ++
>  drivers/gpu/drm/dove/Makefile    |    1 +
>  drivers/gpu/drm/dove/dove_card.c |  110 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 115 insertions(+)
>  create mode 100644 drivers/gpu/drm/dove/dove_card.c
> 
> diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
> index 718d3c5..a943ea5 100644
> --- a/drivers/gpu/drm/dove/Kconfig
> +++ b/drivers/gpu/drm/dove/Kconfig
> @@ -28,4 +28,8 @@ config DRM_DOVE_TDA1998X
>  config DRM_DOVE_CURSOR
>  	bool "Enable Dove DRM hardware cursor support"
>  
> +config DRM_DOVE_OF
> +	bool "Enable Dove DRM OF video card"
> +	depends on OF
> +
>  endif
> diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
> index 65c701e..f0b6eed 100644
> --- a/drivers/gpu/drm/dove/Makefile
> +++ b/drivers/gpu/drm/dove/Makefile
> @@ -5,5 +5,6 @@ dove-y			:= dove_crtc.o dove_drv.o dove_fb.o dove_fbdev.o \
>  dove-$(CONFIG_DEBUG_FS) += dove_debugfs.o
>  
>  dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o
> +dove-$(CONFIG_DRM_DOVE_OF) += dove_card.o
>  
>  obj-$(CONFIG_DRM_DOVE) := dove.o
> diff --git a/drivers/gpu/drm/dove/dove_card.c b/drivers/gpu/drm/dove/dove_card.c
> new file mode 100644
> index 0000000..e4bcb5b
> --- /dev/null
> +++ b/drivers/gpu/drm/dove/dove_card.c
> @@ -0,0 +1,110 @@
> +/*
> + * Copyright (C) 2013
> + *   Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#define DOVE_LCD0_BASE	0x20000
> +#define DOVE_LCD1_BASE	0x10000
> +
> +static struct resource dove_drm_resources[5];
> +static struct platform_device dove_drm_platform_device = {
> +	.name = "dove-drm",
> +	.id = 0,
> +	.dev = { .coherent_dma_mask = ~0, },
> +	.resource = dove_drm_resources,
> +};
> +
> +static int dove_card_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device_node *lcdnp;
> +	struct resource *res = dove_drm_resources;
> +	int ret, n = 0, crtcs = 0;
> +
> +	/* get video memory resource */
> +	if (of_address_to_resource(np, 0, &res[n++])) {
> +		dev_err(&pdev->dev, "invalid or missing video memory\n");
> +		return -EINVAL;
> +	}
> +
> +	/* get reg and irq resource from each enabled lcdc */
> +	for_each_compatible_node(lcdnp, NULL, "marvell,dove-lcd") {
> +		struct clk_lookup *cl;
> +		struct clk *clk;
> +		int lcd;
> +
> +		if (!of_device_is_available(lcdnp))
> +			continue;
> +
> +		ret = of_address_to_resource(lcdnp, 0, &res[n]);
> +		if (ret)
> +			return ret;
> +		lcd = ((res[n].start & 0xfffff) == DOVE_LCD1_BASE);
> +		n++;
> +
> +		ret = of_irq_to_resource(lcdnp, 0, &res[n]);
> +		if (ret < 0)
> +			return ret;
> +		n++;
> +
> +		crtcs++;
> +
> +		clk = clk_get(&pdev->dev, NULL);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			if (ret == -ENOENT)
> +				return -EPROBE_DEFER;
> +			return ret;
> +		}
> +
> +		/* add clock alias for dovefb.0 */
> +		cl = clkdev_alloc(clk, "extclk", "dovefb.0");
> +		if (cl)
> +			clkdev_add(cl);
> +		clk_put(clk);
> +	}
> +
> +	if (!crtcs)
> +		return -ENODEV;
> +
> +	dove_drm_platform_device.num_resources = n;
> +	ret = platform_device_register(&dove_drm_platform_device);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to register drm device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dove_card_of_ids[] = {
> +	{ .compatible = "marvell,dove-video-card", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, dove_card_of_ids);
> +
> +static struct platform_driver dove_card_driver = {
> +	.probe	= dove_card_probe,
> +	.driver	= {
> +		.owner	= THIS_MODULE,
> +		.name	= "dove-drm-card",
> +		.of_match_table = of_match_ptr(dove_card_of_ids),
> +	},
> +};
> +module_platform_driver(dove_card_driver);
> +
> +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
> +MODULE_DESCRIPTION("Dove DRM Graphics Card");
> +MODULE_LICENSE("GPL");

It seems we are moving backwards:
- what about the display controller?
- how do you clone the lcd 0 output to the port B?
- what occurs when the si5351 and the tda998x are modules?

My driver had the same layout as Russell's when I proposed it to you
and when you insisted to handle the 2 LCDs and the 2 ports as one card.
I spent 2 months to have a nice design and you put it to garbage!
I am not happy...

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/4] DRM: add OF support for Dove DRM driver
@ 2013-05-18 17:45       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:45 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On Sat, 18 May 2013 19:12:18 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> This adds OF support for the Dove DRM driver recently posted as RFC by
> Russell King.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: dri-devel@lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  drivers/gpu/drm/dove/Kconfig     |    4 ++
>  drivers/gpu/drm/dove/Makefile    |    1 +
>  drivers/gpu/drm/dove/dove_card.c |  110 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 115 insertions(+)
>  create mode 100644 drivers/gpu/drm/dove/dove_card.c
> 
> diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig
> index 718d3c5..a943ea5 100644
> --- a/drivers/gpu/drm/dove/Kconfig
> +++ b/drivers/gpu/drm/dove/Kconfig
> @@ -28,4 +28,8 @@ config DRM_DOVE_TDA1998X
>  config DRM_DOVE_CURSOR
>  	bool "Enable Dove DRM hardware cursor support"
>  
> +config DRM_DOVE_OF
> +	bool "Enable Dove DRM OF video card"
> +	depends on OF
> +
>  endif
> diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile
> index 65c701e..f0b6eed 100644
> --- a/drivers/gpu/drm/dove/Makefile
> +++ b/drivers/gpu/drm/dove/Makefile
> @@ -5,5 +5,6 @@ dove-y			:= dove_crtc.o dove_drv.o dove_fb.o dove_fbdev.o \
>  dove-$(CONFIG_DEBUG_FS) += dove_debugfs.o
>  
>  dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o
> +dove-$(CONFIG_DRM_DOVE_OF) += dove_card.o
>  
>  obj-$(CONFIG_DRM_DOVE) := dove.o
> diff --git a/drivers/gpu/drm/dove/dove_card.c b/drivers/gpu/drm/dove/dove_card.c
> new file mode 100644
> index 0000000..e4bcb5b
> --- /dev/null
> +++ b/drivers/gpu/drm/dove/dove_card.c
> @@ -0,0 +1,110 @@
> +/*
> + * Copyright (C) 2013
> + *   Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#define DOVE_LCD0_BASE	0x20000
> +#define DOVE_LCD1_BASE	0x10000
> +
> +static struct resource dove_drm_resources[5];
> +static struct platform_device dove_drm_platform_device = {
> +	.name = "dove-drm",
> +	.id = 0,
> +	.dev = { .coherent_dma_mask = ~0, },
> +	.resource = dove_drm_resources,
> +};
> +
> +static int dove_card_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device_node *lcdnp;
> +	struct resource *res = dove_drm_resources;
> +	int ret, n = 0, crtcs = 0;
> +
> +	/* get video memory resource */
> +	if (of_address_to_resource(np, 0, &res[n++])) {
> +		dev_err(&pdev->dev, "invalid or missing video memory\n");
> +		return -EINVAL;
> +	}
> +
> +	/* get reg and irq resource from each enabled lcdc */
> +	for_each_compatible_node(lcdnp, NULL, "marvell,dove-lcd") {
> +		struct clk_lookup *cl;
> +		struct clk *clk;
> +		int lcd;
> +
> +		if (!of_device_is_available(lcdnp))
> +			continue;
> +
> +		ret = of_address_to_resource(lcdnp, 0, &res[n]);
> +		if (ret)
> +			return ret;
> +		lcd = ((res[n].start & 0xfffff) == DOVE_LCD1_BASE);
> +		n++;
> +
> +		ret = of_irq_to_resource(lcdnp, 0, &res[n]);
> +		if (ret < 0)
> +			return ret;
> +		n++;
> +
> +		crtcs++;
> +
> +		clk = clk_get(&pdev->dev, NULL);
> +		if (IS_ERR(clk)) {
> +			ret = PTR_ERR(clk);
> +			if (ret == -ENOENT)
> +				return -EPROBE_DEFER;
> +			return ret;
> +		}
> +
> +		/* add clock alias for dovefb.0 */
> +		cl = clkdev_alloc(clk, "extclk", "dovefb.0");
> +		if (cl)
> +			clkdev_add(cl);
> +		clk_put(clk);
> +	}
> +
> +	if (!crtcs)
> +		return -ENODEV;
> +
> +	dove_drm_platform_device.num_resources = n;
> +	ret = platform_device_register(&dove_drm_platform_device);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to register drm device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dove_card_of_ids[] = {
> +	{ .compatible = "marvell,dove-video-card", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, dove_card_of_ids);
> +
> +static struct platform_driver dove_card_driver = {
> +	.probe	= dove_card_probe,
> +	.driver	= {
> +		.owner	= THIS_MODULE,
> +		.name	= "dove-drm-card",
> +		.of_match_table = of_match_ptr(dove_card_of_ids),
> +	},
> +};
> +module_platform_driver(dove_card_driver);
> +
> +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
> +MODULE_DESCRIPTION("Dove DRM Graphics Card");
> +MODULE_LICENSE("GPL");

It seems we are moving backwards:
- what about the display controller?
- how do you clone the lcd 0 output to the port B?
- what occurs when the si5351 and the tda998x are modules?

My driver had the same layout as Russell's when I proposed it to you
and when you insisted to handle the 2 LCDs and the 2 ports as one card.
I spent 2 months to have a nice design and you put it to garbage!
I am not happy...

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 17:12   ` [RFC 4/4] DRM: tda998x: add missing include Sebastian Hesselbarth
@ 2013-05-18 17:46       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 19:12:19 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> The RFC sent by Russell King was missing an include for tda998x. This
> is just a compatible clone to remember Russell to add that later.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: dri-devel at lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  include/drm/i2c/tda998x.h |   23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 include/drm/i2c/tda998x.h
> 
> diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
> new file mode 100644
> index 0000000..41f799f
> --- /dev/null
> +++ b/include/drm/i2c/tda998x.h
> @@ -0,0 +1,23 @@
> +#ifndef __TDA998X_H__
> +#define __TDA998X_H__
> +
> +enum tda998x_audio_format {
> +	AFMT_I2S,
> +	AFMT_SPDIF,
> +};
> +
> +struct tda998x_encoder_params {
> +	int audio_cfg;
> +	int audio_clk_cfg;
> +	enum tda998x_audio_format audio_format;
> +	int audio_sample_rate;
> +	char audio_frame[6];
> +	int swap_a, mirr_a;
> +	int swap_b, mirr_b;
> +	int swap_c, mirr_c;
> +	int swap_d, mirr_d;
> +	int swap_e, mirr_e;
> +	int swap_f, mirr_f;
> +};
> +
> +#endif

These parameters should not be there. It seems to me that the DT is the
right place.

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 17:46       ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 17:46 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On Sat, 18 May 2013 19:12:19 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> The RFC sent by Russell King was missing an include for tda998x. This
> is just a compatible clone to remember Russell to add that later.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: dri-devel@lists.freedesktop.org
> Cc: Jason Cooper <jason@lakedaemon.net>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> ---
>  include/drm/i2c/tda998x.h |   23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 include/drm/i2c/tda998x.h
> 
> diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
> new file mode 100644
> index 0000000..41f799f
> --- /dev/null
> +++ b/include/drm/i2c/tda998x.h
> @@ -0,0 +1,23 @@
> +#ifndef __TDA998X_H__
> +#define __TDA998X_H__
> +
> +enum tda998x_audio_format {
> +	AFMT_I2S,
> +	AFMT_SPDIF,
> +};
> +
> +struct tda998x_encoder_params {
> +	int audio_cfg;
> +	int audio_clk_cfg;
> +	enum tda998x_audio_format audio_format;
> +	int audio_sample_rate;
> +	char audio_frame[6];
> +	int swap_a, mirr_a;
> +	int swap_b, mirr_b;
> +	int swap_c, mirr_c;
> +	int swap_d, mirr_d;
> +	int swap_e, mirr_e;
> +	int swap_f, mirr_f;
> +};
> +
> +#endif

These parameters should not be there. It seems to me that the DT is the
right place.

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 17:45       ` Jean-Francois Moine
@ 2013-05-18 18:20         ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2013 07:45 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:18 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> This adds OF support for the Dove DRM driver recently posted as RFC by
>> Russell King.
>>
...

Jean-Francois,

one thing first: It is an RFC! It is to allow you to _test_ rmk's driver
on DT. Nothing more, nothing less.

I will comment on your questions but that all can change for a full
patch set of rmk, you, or me.

The "video-card" node combines all devices that will be available and
active on a specific Dove board. As you may have noticed about rmk's
driver, it is registering crtcs from IORESOURCE_MEM passed with the
platform_device.

To match with this approach we _have to_ recreate that platform_device
from what we see on DT. On DT each bus node gets registered as its own
platform_device. So in the video-card driver we look for node we know
of and put together a platform_device for rmk's driver. We cannot hook
DT upon either an lcd node nor dcon node as they might be disabled and
the driver will get called multiple times.

> It seems we are moving backwards:
> - what about the display controller?

That would be part of probing DT nodes above. I did not take care of
that because rmk doesn't support dcon for now.

> - how do you clone the lcd 0 output to the port B?

Pass properties on video-card node or even better let dcon driver
take care of it when it sees a video-card with more than one crtc.

> - what occurs when the si5351 and the tda998x are modules?

Touche, forgot that part. Feel free to add module support to the
RFC.

> My driver had the same layout as Russell's when I proposed it to you
> and when you insisted to handle the 2 LCDs and the 2 ports as one card.

I still insist to handle 2 LCDs and DCON.

> I spent 2 months to have a nice design and you put it to garbage!
> I am not happy...

I put nothing to garbage. _You_ also agreed to merge with rmk's driver!
We can now put in all features we implemented differently
_step-by-step_.

Merging the drivers starts with adding support for DT - that is what
I provided. You know the HW better than me, why don't you start picking
features from your driver and add them in rmk's driver?

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/4] DRM: add OF support for Dove DRM driver
@ 2013-05-18 18:20         ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:20 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On 05/18/2013 07:45 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:18 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> This adds OF support for the Dove DRM driver recently posted as RFC by
>> Russell King.
>>
...

Jean-Francois,

one thing first: It is an RFC! It is to allow you to _test_ rmk's driver
on DT. Nothing more, nothing less.

I will comment on your questions but that all can change for a full
patch set of rmk, you, or me.

The "video-card" node combines all devices that will be available and
active on a specific Dove board. As you may have noticed about rmk's
driver, it is registering crtcs from IORESOURCE_MEM passed with the
platform_device.

To match with this approach we _have to_ recreate that platform_device
from what we see on DT. On DT each bus node gets registered as its own
platform_device. So in the video-card driver we look for node we know
of and put together a platform_device for rmk's driver. We cannot hook
DT upon either an lcd node nor dcon node as they might be disabled and
the driver will get called multiple times.

> It seems we are moving backwards:
> - what about the display controller?

That would be part of probing DT nodes above. I did not take care of
that because rmk doesn't support dcon for now.

> - how do you clone the lcd 0 output to the port B?

Pass properties on video-card node or even better let dcon driver
take care of it when it sees a video-card with more than one crtc.

> - what occurs when the si5351 and the tda998x are modules?

Touche, forgot that part. Feel free to add module support to the
RFC.

> My driver had the same layout as Russell's when I proposed it to you
> and when you insisted to handle the 2 LCDs and the 2 ports as one card.

I still insist to handle 2 LCDs and DCON.

> I spent 2 months to have a nice design and you put it to garbage!
> I am not happy...

I put nothing to garbage. _You_ also agreed to merge with rmk's driver!
We can now put in all features we implemented differently
_step-by-step_.

Merging the drivers starts with adding support for DT - that is what
I provided. You know the HW better than me, why don't you start picking
features from your driver and add them in rmk's driver?

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 17:46       ` Jean-Francois Moine
@ 2013-05-18 18:21         ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:21 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2013 07:46 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:19 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>
>> The RFC sent by Russell King was missing an include for tda998x. This
>> is just a compatible clone to remember Russell to add that later.
>>
>> Signed-off-by: Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>
...
> These parameters should not be there. It seems to me that the DT is the
> right place.

True, but if you just read the description above:
"RFC sent by Russell King was missing an include for tda998x".

You want to test the RFC, you need that include.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 18:21         ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:21 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On 05/18/2013 07:46 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:19 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>
>> The RFC sent by Russell King was missing an include for tda998x. This
>> is just a compatible clone to remember Russell to add that later.
>>
>> Signed-off-by: Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>
...
> These parameters should not be there. It seems to me that the DT is the
> right place.

True, but if you just read the description above:
"RFC sent by Russell King was missing an include for tda998x".

You want to test the RFC, you need that include.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 17:46       ` Jean-Francois Moine
@ 2013-05-18 18:23         ` Rob Clark
  -1 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-18 18:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 1:46 PM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sat, 18 May 2013 19:12:19 +0200
> Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:
>
>> The RFC sent by Russell King was missing an include for tda998x. This
>> is just a compatible clone to remember Russell to add that later.
>>
>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>> ---
>> Cc: Russell King <linux@arm.linux.org.uk>
>> Cc: linux-arm-kernel at lists.infradead.org
>> Cc: dri-devel at lists.freedesktop.org
>> Cc: Jason Cooper <jason@lakedaemon.net>
>> Cc: Jean-Francois Moine <moinejf@free.fr>
>> ---
>>  include/drm/i2c/tda998x.h |   23 +++++++++++++++++++++++
>>  1 file changed, 23 insertions(+)
>>  create mode 100644 include/drm/i2c/tda998x.h
>>
>> diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
>> new file mode 100644
>> index 0000000..41f799f
>> --- /dev/null
>> +++ b/include/drm/i2c/tda998x.h
>> @@ -0,0 +1,23 @@
>> +#ifndef __TDA998X_H__
>> +#define __TDA998X_H__
>> +
>> +enum tda998x_audio_format {
>> +     AFMT_I2S,
>> +     AFMT_SPDIF,
>> +};
>> +
>> +struct tda998x_encoder_params {
>> +     int audio_cfg;
>> +     int audio_clk_cfg;
>> +     enum tda998x_audio_format audio_format;
>> +     int audio_sample_rate;
>> +     char audio_frame[6];
>> +     int swap_a, mirr_a;
>> +     int swap_b, mirr_b;
>> +     int swap_c, mirr_c;
>> +     int swap_d, mirr_d;
>> +     int swap_e, mirr_e;
>> +     int swap_f, mirr_f;
>> +};
>> +
>> +#endif
>
> These parameters should not be there. It seems to me that the DT is the
> right place.

You might not want to directly have a hard DT dependency in tda998x,
as the encoder could be used on non-DT platforms.  Although a DT to
encoder-params helper might be a nice idea for platforms which do have
DT.

BR,
-R

> --
> Ken ar c'henta? |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 18:23         ` Rob Clark
  0 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-18 18:23 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, Russell King, linux-arm-kernel, dri-devel,
	Sebastian Hesselbarth

On Sat, May 18, 2013 at 1:46 PM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sat, 18 May 2013 19:12:19 +0200
> Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:
>
>> The RFC sent by Russell King was missing an include for tda998x. This
>> is just a compatible clone to remember Russell to add that later.
>>
>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>> ---
>> Cc: Russell King <linux@arm.linux.org.uk>
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: dri-devel@lists.freedesktop.org
>> Cc: Jason Cooper <jason@lakedaemon.net>
>> Cc: Jean-Francois Moine <moinejf@free.fr>
>> ---
>>  include/drm/i2c/tda998x.h |   23 +++++++++++++++++++++++
>>  1 file changed, 23 insertions(+)
>>  create mode 100644 include/drm/i2c/tda998x.h
>>
>> diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h
>> new file mode 100644
>> index 0000000..41f799f
>> --- /dev/null
>> +++ b/include/drm/i2c/tda998x.h
>> @@ -0,0 +1,23 @@
>> +#ifndef __TDA998X_H__
>> +#define __TDA998X_H__
>> +
>> +enum tda998x_audio_format {
>> +     AFMT_I2S,
>> +     AFMT_SPDIF,
>> +};
>> +
>> +struct tda998x_encoder_params {
>> +     int audio_cfg;
>> +     int audio_clk_cfg;
>> +     enum tda998x_audio_format audio_format;
>> +     int audio_sample_rate;
>> +     char audio_frame[6];
>> +     int swap_a, mirr_a;
>> +     int swap_b, mirr_b;
>> +     int swap_c, mirr_c;
>> +     int swap_d, mirr_d;
>> +     int swap_e, mirr_e;
>> +     int swap_f, mirr_f;
>> +};
>> +
>> +#endif
>
> These parameters should not be there. It seems to me that the DT is the
> right place.

You might not want to directly have a hard DT dependency in tda998x,
as the encoder could be used on non-DT platforms.  Although a DT to
encoder-params helper might be a nice idea for platforms which do have
DT.

BR,
-R

> --
> Ken ar c'hentañ |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox
  2013-05-18 17:33       ` Jean-Francois Moine
@ 2013-05-18 18:33         ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2013 07:33 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:17 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> This adds a video card node required for rmk's dove_drm driver. Reg
>> property matches reserved memory region (currently 16M at top of memory),
>> clocks property should carry extclk0 for now.
>>
>> Signed-off-by: Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>
>> ---
...
>> +		vcard: video-card {
>> +			compatible = "marvell,dove-video-card";
>> +			reg =<0x3f000000 0x1000000>;
>> +			clocks =<&si5351 0>,<&si5351 0>;
>> +		};
>> +	};
...
>> +&lcd0 { status = "okay"; };
>
> May you explain a bit more this strange hack?

This "hack" adds the video-card device node that describes the board
dependent part of Dove SoC video. Remember, it is a device tree node
to match Russel's driver!

You have the video memory passed, the clocks property will vanish
later. And you enable lcd0 as you may have noticed that there is
nothing connected on lcd1 on the _CuBox_.

But there is on the D2Plug, and that DT description _will_ enable
lcd0, lcd1 and dcon.

Maybe, there is a misunderstanding in in the concept of DT here.
DT does _not_ describe the driver layout but the HW. And for Linux
this basically means, you replace board/SoC dependent init code
that register some platform_device with a description in DT.

The actual driver does _not_ need to know about non-DT or DT except
that somebody has to parse it and create a platform_device for it.
If you only have standard properties like reg and irq, it all gets
parsed automagically by DT bus probing. But as you already pointed
out, a video card on Dove is a little bit more complex as reg and
irq - so I provided a DT parser for rmk's *RFC* driver as *RFC*!

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox
@ 2013-05-18 18:33         ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 18:33 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On 05/18/2013 07:33 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 19:12:17 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> This adds a video card node required for rmk's dove_drm driver. Reg
>> property matches reserved memory region (currently 16M at top of memory),
>> clocks property should carry extclk0 for now.
>>
>> Signed-off-by: Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>
>> ---
...
>> +		vcard: video-card {
>> +			compatible = "marvell,dove-video-card";
>> +			reg =<0x3f000000 0x1000000>;
>> +			clocks =<&si5351 0>,<&si5351 0>;
>> +		};
>> +	};
...
>> +&lcd0 { status = "okay"; };
>
> May you explain a bit more this strange hack?

This "hack" adds the video-card device node that describes the board
dependent part of Dove SoC video. Remember, it is a device tree node
to match Russel's driver!

You have the video memory passed, the clocks property will vanish
later. And you enable lcd0 as you may have noticed that there is
nothing connected on lcd1 on the _CuBox_.

But there is on the D2Plug, and that DT description _will_ enable
lcd0, lcd1 and dcon.

Maybe, there is a misunderstanding in in the concept of DT here.
DT does _not_ describe the driver layout but the HW. And for Linux
this basically means, you replace board/SoC dependent init code
that register some platform_device with a description in DT.

The actual driver does _not_ need to know about non-DT or DT except
that somebody has to parse it and create a platform_device for it.
If you only have standard properties like reg and irq, it all gets
parsed automagically by DT bus probing. But as you already pointed
out, a video card on Dove is a little bit more complex as reg and
irq - so I provided a DT parser for rmk's *RFC* driver as *RFC*!

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 18:23         ` Rob Clark
@ 2013-05-18 18:58           ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 18:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 14:23:19 -0400
Rob Clark <robdclark@gmail.com> wrote:

> > These parameters should not be there. It seems to me that the DT is the
> > right place.  
> 
> You might not want to directly have a hard DT dependency in tda998x,
> as the encoder could be used on non-DT platforms.  Although a DT to
> encoder-params helper might be a nice idea for platforms which do have
> DT.

If I correctly understand:

- Russell does not use any DT, so his drm driver should be declared in
  some cubox-setup code in mach-dove/

- this code should also declare the tda998x

- the drm driver contains/passes parameters to the tda998x

As the connection Dove LCD <-> tda998x is Cubox specific, the question
is: why are'nt the tda998x parameters in the cubox-setup code?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 18:58           ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 18:58 UTC (permalink / raw)
  To: Rob Clark
  Cc: Jason Cooper, Russell King, linux-arm-kernel, dri-devel,
	Sebastian Hesselbarth

On Sat, 18 May 2013 14:23:19 -0400
Rob Clark <robdclark@gmail.com> wrote:

> > These parameters should not be there. It seems to me that the DT is the
> > right place.  
> 
> You might not want to directly have a hard DT dependency in tda998x,
> as the encoder could be used on non-DT platforms.  Although a DT to
> encoder-params helper might be a nice idea for platforms which do have
> DT.

If I correctly understand:

- Russell does not use any DT, so his drm driver should be declared in
  some cubox-setup code in mach-dove/

- this code should also declare the tda998x

- the drm driver contains/passes parameters to the tda998x

As the connection Dove LCD <-> tda998x is Cubox specific, the question
is: why are'nt the tda998x parameters in the cubox-setup code?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 18:58           ` Jean-Francois Moine
@ 2013-05-18 19:11             ` Rob Clark
  -1 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-18 19:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 2:58 PM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sat, 18 May 2013 14:23:19 -0400
> Rob Clark <robdclark@gmail.com> wrote:
>
>> > These parameters should not be there. It seems to me that the DT is the
>> > right place.
>>
>> You might not want to directly have a hard DT dependency in tda998x,
>> as the encoder could be used on non-DT platforms.  Although a DT to
>> encoder-params helper might be a nice idea for platforms which do have
>> DT.
>
> If I correctly understand:
>
> - Russell does not use any DT, so his drm driver should be declared in
>   some cubox-setup code in mach-dove/
>
> - this code should also declare the tda998x
>
> - the drm driver contains/passes parameters to the tda998x
>
> As the connection Dove LCD <-> tda998x is Cubox specific, the question
> is: why are'nt the tda998x parameters in the cubox-setup code?

ok, maybe I am misunderstanding you.  I think the parameters should be
filled in by the board file on a non-DT setup.  But the part in
drivers/gpu/drm/i2c should not pull them directly out of DT, or should
have an arrangement like

 #ifdef CONFIG_OF
 .. pull params out of DT ..
#else
 .. use params passed in from via params struct, which is populated in
board file ..
#endif

to accommodate non-DT builds.  (Although I think just having a helper
to populate 'struct tda998x_encoder_params' from DT seems cleaner.)


BR,
-R

> --
> Ken ar c'henta? |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 19:11             ` Rob Clark
  0 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-18 19:11 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, Russell King, linux-arm-kernel, dri-devel,
	Sebastian Hesselbarth

On Sat, May 18, 2013 at 2:58 PM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sat, 18 May 2013 14:23:19 -0400
> Rob Clark <robdclark@gmail.com> wrote:
>
>> > These parameters should not be there. It seems to me that the DT is the
>> > right place.
>>
>> You might not want to directly have a hard DT dependency in tda998x,
>> as the encoder could be used on non-DT platforms.  Although a DT to
>> encoder-params helper might be a nice idea for platforms which do have
>> DT.
>
> If I correctly understand:
>
> - Russell does not use any DT, so his drm driver should be declared in
>   some cubox-setup code in mach-dove/
>
> - this code should also declare the tda998x
>
> - the drm driver contains/passes parameters to the tda998x
>
> As the connection Dove LCD <-> tda998x is Cubox specific, the question
> is: why are'nt the tda998x parameters in the cubox-setup code?

ok, maybe I am misunderstanding you.  I think the parameters should be
filled in by the board file on a non-DT setup.  But the part in
drivers/gpu/drm/i2c should not pull them directly out of DT, or should
have an arrangement like

 #ifdef CONFIG_OF
 .. pull params out of DT ..
#else
 .. use params passed in from via params struct, which is populated in
board file ..
#endif

to accommodate non-DT builds.  (Although I think just having a helper
to populate 'struct tda998x_encoder_params' from DT seems cleaner.)


BR,
-R

> --
> Ken ar c'hentañ |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 18:20         ` Sebastian Hesselbarth
@ 2013-05-18 19:18           ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 19:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 20:20:00 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> I put nothing to garbage. _You_ also agreed to merge with rmk's driver!
> We can now put in all features we implemented differently
> _step-by-step_.
> 
> Merging the drivers starts with adding support for DT - that is what
> I provided. You know the HW better than me, why don't you start picking
> features from your driver and add them in rmk's driver?

The general hardware code of both drivers is close enough for merging
may be done starting from anyone. But the general layout is not:

- my driver is DT driven and has one card with 2 CTRC's and 2 connectors.

- Russell's is non-DT, so, with some extra code I am not aware of, with
  any number of cards each one with one CRTC and one connector (no, I
  tried it, you cannot clone a connector of one card to the connector
  of another card).

So, for me, merging means enhance my code from Russell's, but I will
not go to a non-DT kernel.

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/4] DRM: add OF support for Dove DRM driver
@ 2013-05-18 19:18           ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-18 19:18 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Russell King, dri-devel, linux-arm-kernel, Jason Cooper

On Sat, 18 May 2013 20:20:00 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> I put nothing to garbage. _You_ also agreed to merge with rmk's driver!
> We can now put in all features we implemented differently
> _step-by-step_.
> 
> Merging the drivers starts with adding support for DT - that is what
> I provided. You know the HW better than me, why don't you start picking
> features from your driver and add them in rmk's driver?

The general hardware code of both drivers is close enough for merging
may be done starting from anyone. But the general layout is not:

- my driver is DT driven and has one card with 2 CTRC's and 2 connectors.

- Russell's is non-DT, so, with some extra code I am not aware of, with
  any number of cards each one with one CRTC and one connector (no, I
  tried it, you cannot clone a connector of one card to the connector
  of another card).

So, for me, merging means enhance my code from Russell's, but I will
not go to a non-DT kernel.

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 18:58           ` Jean-Francois Moine
@ 2013-05-18 19:30             ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 19:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2013 08:58 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 14:23:19 -0400
> Rob Clark<robdclark@gmail.com>  wrote:
>
>>> These parameters should not be there. It seems to me that the DT is the
>>> right place.
>>
>> You might not want to directly have a hard DT dependency in tda998x,
>> as the encoder could be used on non-DT platforms.  Although a DT to
>> encoder-params helper might be a nice idea for platforms which do have
>> DT.
>
> If I correctly understand:
>
> - Russell does not use any DT, so his drm driver should be declared in
>    some cubox-setup code in mach-dove/

No. The _device_ is declared in some cubox-setup but the _driver_ goes
into drivers/gpu/drm. Reading vendor provided kernel code may be
misleading as they often just put all stuff in arch/arm/mach-something.

> - this code should also declare the tda998x

The device for tda998x yes, but not the driver. Anyway, Russel decided
to have tda998x probed by his drm_driver.

> - the drm driver contains/passes parameters to the tda998x
>
> As the connection Dove LCD<->  tda998x is Cubox specific, the question
> is: why are'nt the tda998x parameters in the cubox-setup code?

The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
as you can find the very same controller on other Marvell SoCs with
little differences.

So in the end, we will have a DT node for the HW controllers found
in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
_how_ lcd controllers, external encoders, clocks, maybe audio, ...
are hooked up on that specific board.

There is so much to take care of like pixel format on lcd pins driving
an external encoder (_not_ only tda998x), what gpio pin is connected to
TDA interrupt line, one or two lcds, ...

The corresponding drivers _will_ take care of it .. but in the future.
All I try to make sure is that driver architecture does not prevent us
from e.g. having two lcds plus dcon later on. Or allows to reuse
dove-drm on pxa where only one lcd but no dcon is available.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 19:30             ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 19:30 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Rob Clark, Russell King, linux-arm-kernel, dri-devel, Jason Cooper

On 05/18/2013 08:58 PM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 14:23:19 -0400
> Rob Clark<robdclark@gmail.com>  wrote:
>
>>> These parameters should not be there. It seems to me that the DT is the
>>> right place.
>>
>> You might not want to directly have a hard DT dependency in tda998x,
>> as the encoder could be used on non-DT platforms.  Although a DT to
>> encoder-params helper might be a nice idea for platforms which do have
>> DT.
>
> If I correctly understand:
>
> - Russell does not use any DT, so his drm driver should be declared in
>    some cubox-setup code in mach-dove/

No. The _device_ is declared in some cubox-setup but the _driver_ goes
into drivers/gpu/drm. Reading vendor provided kernel code may be
misleading as they often just put all stuff in arch/arm/mach-something.

> - this code should also declare the tda998x

The device for tda998x yes, but not the driver. Anyway, Russel decided
to have tda998x probed by his drm_driver.

> - the drm driver contains/passes parameters to the tda998x
>
> As the connection Dove LCD<->  tda998x is Cubox specific, the question
> is: why are'nt the tda998x parameters in the cubox-setup code?

The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
as you can find the very same controller on other Marvell SoCs with
little differences.

So in the end, we will have a DT node for the HW controllers found
in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
_how_ lcd controllers, external encoders, clocks, maybe audio, ...
are hooked up on that specific board.

There is so much to take care of like pixel format on lcd pins driving
an external encoder (_not_ only tda998x), what gpio pin is connected to
TDA interrupt line, one or two lcds, ...

The corresponding drivers _will_ take care of it .. but in the future.
All I try to make sure is that driver architecture does not prevent us
from e.g. having two lcds plus dcon later on. Or allows to reuse
dove-drm on pxa where only one lcd but no dcon is available.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 19:30             ` Sebastian Hesselbarth
@ 2013-05-18 20:26               ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-18 20:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 09:30:09PM +0200, Sebastian Hesselbarth wrote:
> The device for tda998x yes, but not the driver. Anyway, Russel decided
> to have tda998x probed by his drm_driver.

For the simple reason that _that_ is how DRM slave encoders work.
Sometimes, reading the code of the subsystem you're using is well
worth the effort.

If Jean-Francois would like to read drm_encoder_slave.c, then it will
be found that in order to use the TDA998x driver, which is itself a
DRM slave encoder, you must use drm_i2c_encoder_init().  In order to
use that, you must provide the I2C adapter structure, and a board
info structure.

If you don't want to do that, your options are:
(a) you don't use the existing TDA998x DRM slave encoder, and instead
    write your own TDA998x driver, which will likely be justifyable
    rejected, or
(b) you propose a new DRM interface to allow DRM components to be
    registered independently, without reference to a core drm_device
    structure.

> The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
> also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
> as you can find the very same controller on other Marvell SoCs with
> little differences.

Well, to spoil the argument a little, actually, the interconnection
between the two is in no way "standardized".  There's many different
ways to wire the two chips together and have it work - because the
TDA998x chips have a set of input muxes and swaps which allow you to
connect the red, green, blue high/low nibbles in various ways and
still have a correctly working system.  The TDA998x connectivity is
_highly_ configuable.

So, just because one board connects LCD_D0 (red bit 0) to a particular
pin on the TDA998x does not mean that another board does it that way
too.

So Jean-Francois is quite correct that this data needs to be provided
by the board in some manner.  The question is - how to do that sensibly.

One possible stop-gap solution is to provide a default set which just
happens to match the cubox, and allow OF to override it. :)

> There is so much to take care of like pixel format on lcd pins driving
> an external encoder (_not_ only tda998x), what gpio pin is connected to
> TDA interrupt line, one or two lcds, ...

Luckily, drivers/gpu/drm/i2c/tda998x.c does not make use of the IRQ
signal at present - it's fairly basic and it currently operates by
polling.  Eventually, this could change of course. :)

I think people need to keep a sense of perspective here: this is all
entirely "new" stuff which is still being actively developed.  It is
not fully polished.  We've not had a true open source TDA998x driver
before 3.9 (that's when it was introduced.)  It has teething problems
at the moment, but I'm working with the authors to resolve these issues.
I'm also still working on the DRM driver.

For example, I've been playing with the RGB888 cursor support today,
which seems to be suffering from a one pixel error in the hotspot
location.  I've not got to the bottom of it, but that kind of error
_is_ important to understand and resolve, because it means that
things like drawing programmes become unusable.

What I'm starting to suspect is a bug in the X server causing this
and not either my DRM driver or Xorg driver.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 20:26               ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-18 20:26 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Rob Clark, linux-arm-kernel, dri-devel,
	Jason Cooper

On Sat, May 18, 2013 at 09:30:09PM +0200, Sebastian Hesselbarth wrote:
> The device for tda998x yes, but not the driver. Anyway, Russel decided
> to have tda998x probed by his drm_driver.

For the simple reason that _that_ is how DRM slave encoders work.
Sometimes, reading the code of the subsystem you're using is well
worth the effort.

If Jean-Francois would like to read drm_encoder_slave.c, then it will
be found that in order to use the TDA998x driver, which is itself a
DRM slave encoder, you must use drm_i2c_encoder_init().  In order to
use that, you must provide the I2C adapter structure, and a board
info structure.

If you don't want to do that, your options are:
(a) you don't use the existing TDA998x DRM slave encoder, and instead
    write your own TDA998x driver, which will likely be justifyable
    rejected, or
(b) you propose a new DRM interface to allow DRM components to be
    registered independently, without reference to a core drm_device
    structure.

> The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
> also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
> as you can find the very same controller on other Marvell SoCs with
> little differences.

Well, to spoil the argument a little, actually, the interconnection
between the two is in no way "standardized".  There's many different
ways to wire the two chips together and have it work - because the
TDA998x chips have a set of input muxes and swaps which allow you to
connect the red, green, blue high/low nibbles in various ways and
still have a correctly working system.  The TDA998x connectivity is
_highly_ configuable.

So, just because one board connects LCD_D0 (red bit 0) to a particular
pin on the TDA998x does not mean that another board does it that way
too.

So Jean-Francois is quite correct that this data needs to be provided
by the board in some manner.  The question is - how to do that sensibly.

One possible stop-gap solution is to provide a default set which just
happens to match the cubox, and allow OF to override it. :)

> There is so much to take care of like pixel format on lcd pins driving
> an external encoder (_not_ only tda998x), what gpio pin is connected to
> TDA interrupt line, one or two lcds, ...

Luckily, drivers/gpu/drm/i2c/tda998x.c does not make use of the IRQ
signal at present - it's fairly basic and it currently operates by
polling.  Eventually, this could change of course. :)

I think people need to keep a sense of perspective here: this is all
entirely "new" stuff which is still being actively developed.  It is
not fully polished.  We've not had a true open source TDA998x driver
before 3.9 (that's when it was introduced.)  It has teething problems
at the moment, but I'm working with the authors to resolve these issues.
I'm also still working on the DRM driver.

For example, I've been playing with the RGB888 cursor support today,
which seems to be suffering from a one pixel error in the hotspot
location.  I've not got to the bottom of it, but that kind of error
_is_ important to understand and resolve, because it means that
things like drawing programmes become unusable.

What I'm starting to suspect is a bug in the X server causing this
and not either my DRM driver or Xorg driver.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 17:45       ` Jean-Francois Moine
@ 2013-05-18 20:46         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-18 20:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 07:45:02PM +0200, Jean-Francois Moine wrote:
> It seems we are moving backwards:
> - what about the display controller?
> - how do you clone the lcd 0 output to the port B?
> - what occurs when the si5351 and the tda998x are modules?

I've no idea why you keep bringing that last point up.  I've already
told you what happens when the SI5351 is a module.  It is already not
a problem as I have already evidenced by my boot log.  So please get
that and stop repeating this same point which I've already answered,
or I will start ranting at you and we will have a massive falling out.

As for cloning output to the VGA port, that's just a matter of dealing
with the display controller.  That's _not_ difficult.

> My driver had the same layout as Russell's when I proposed it to you
> and when you insisted to handle the 2 LCDs and the 2 ports as one card.

This seems to be a misrepresentation.  So what you're saying is that
your driver originally handled the two LCDs as two separate cards.

That is _not_ the same as my driver.  My driver handles the two LCDs
as two separate CRTs of the same DRM device.  This allows X to drive
the two CRTs together in any manner it desires depending on the
capabilities of the "connectors" associated with each CRTC and the
user preferences.

> I spent 2 months to have a nice design and you put it to garbage!
> I am not happy...

Stop that right now.  If you want to start whinging about the amount of
time you've spent on this, then I can tell you now that if you have only
spent two months on this, you are a total newbie to this and your effort
is utterly insignificant compared to mine.  And you *have* touched a
nerve here by making that statement.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/4] DRM: add OF support for Dove DRM driver
@ 2013-05-18 20:46         ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-18 20:46 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Sat, May 18, 2013 at 07:45:02PM +0200, Jean-Francois Moine wrote:
> It seems we are moving backwards:
> - what about the display controller?
> - how do you clone the lcd 0 output to the port B?
> - what occurs when the si5351 and the tda998x are modules?

I've no idea why you keep bringing that last point up.  I've already
told you what happens when the SI5351 is a module.  It is already not
a problem as I have already evidenced by my boot log.  So please get
that and stop repeating this same point which I've already answered,
or I will start ranting at you and we will have a massive falling out.

As for cloning output to the VGA port, that's just a matter of dealing
with the display controller.  That's _not_ difficult.

> My driver had the same layout as Russell's when I proposed it to you
> and when you insisted to handle the 2 LCDs and the 2 ports as one card.

This seems to be a misrepresentation.  So what you're saying is that
your driver originally handled the two LCDs as two separate cards.

That is _not_ the same as my driver.  My driver handles the two LCDs
as two separate CRTs of the same DRM device.  This allows X to drive
the two CRTs together in any manner it desires depending on the
capabilities of the "connectors" associated with each CRTC and the
user preferences.

> I spent 2 months to have a nice design and you put it to garbage!
> I am not happy...

Stop that right now.  If you want to start whinging about the amount of
time you've spent on this, then I can tell you now that if you have only
spent two months on this, you are a total newbie to this and your effort
is utterly insignificant compared to mine.  And you *have* touched a
nerve here by making that statement.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 20:26               ` Russell King - ARM Linux
@ 2013-05-18 20:50                 ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 20:50 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2013 10:26 PM, Russell King - ARM Linux wrote:
> On Sat, May 18, 2013 at 09:30:09PM +0200, Sebastian Hesselbarth wrote:
>> The device for tda998x yes, but not the driver. Anyway, Russel decided
>> to have tda998x probed by his drm_driver.
>
> For the simple reason that _that_ is how DRM slave encoders work.
> Sometimes, reading the code of the subsystem you're using is well
> worth the effort.

I agree and add that the probing itself doesn't prevent you from using
DT for tda driver at all. You can still have an marvell,external-slave
property pointing to the phandle of tda node. With that you get the
adapter and i2c slave address for what is currently called
dove_tda19989.c and may become e.g. dove_ext_i2c.c. In tda998x_drv you
find the node and get all properties for input config or interrupt
gpio.

I have done that in the drivers before, but DT node parsing here is
_added_ to the driver as it can be used on other non-DT platforms as
well.

>> The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
>> also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
>> as you can find the very same controller on other Marvell SoCs with
>> little differences.
>
> Well, to spoil the argument a little, actually, the interconnection
> between the two is in no way "standardized".  There's many different
> ways to wire the two chips together and have it work - because the
> TDA998x chips have a set of input muxes and swaps which allow you to
> connect the red, green, blue high/low nibbles in various ways and
> still have a correctly working system.  The TDA998x connectivity is
> _highly_ configuable.
>
> So, just because one board connects LCD_D0 (red bit 0) to a particular
> pin on the TDA998x does not mean that another board does it that way
> too.
>
> So Jean-Francois is quite correct that this data needs to be provided
> by the board in some manner.  The question is - how to do that sensibly.
>
> One possible stop-gap solution is to provide a default set which just
> happens to match the cubox, and allow OF to override it. :)

While I agree, Rob may have a different view on that for tda998x ;)

>> There is so much to take care of like pixel format on lcd pins driving
>> an external encoder (_not_ only tda998x), what gpio pin is connected to
>> TDA interrupt line, one or two lcds, ...
>
> Luckily, drivers/gpu/drm/i2c/tda998x.c does not make use of the IRQ
> signal at present - it's fairly basic and it currently operates by
> polling.  Eventually, this could change of course. :)

Again, that is in the driver Jean-Francois has available. Make sure irq
handler runs in a separate thread from get_edid and hpd and you will
be interrupted on hpd. Having said, that should finally lead to the
slave encoder setting .connector_type and .polled as this is where you
know it.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-18 20:50                 ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-18 20:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jean-Francois Moine, Rob Clark, linux-arm-kernel, dri-devel,
	Jason Cooper

On 05/18/2013 10:26 PM, Russell King - ARM Linux wrote:
> On Sat, May 18, 2013 at 09:30:09PM +0200, Sebastian Hesselbarth wrote:
>> The device for tda998x yes, but not the driver. Anyway, Russel decided
>> to have tda998x probed by his drm_driver.
>
> For the simple reason that _that_ is how DRM slave encoders work.
> Sometimes, reading the code of the subsystem you're using is well
> worth the effort.

I agree and add that the probing itself doesn't prevent you from using
DT for tda driver at all. You can still have an marvell,external-slave
property pointing to the phandle of tda node. With that you get the
adapter and i2c slave address for what is currently called
dove_tda19989.c and may become e.g. dove_ext_i2c.c. In tda998x_drv you
find the node and get all properties for input config or interrupt
gpio.

I have done that in the drivers before, but DT node parsing here is
_added_ to the driver as it can be used on other non-DT platforms as
well.

>> The connection of Dove LCD and tda998x is _not_ Cubox specific, it is
>> also on the D2Plug. To be precise, even "Dove LCD" is not Dove specific
>> as you can find the very same controller on other Marvell SoCs with
>> little differences.
>
> Well, to spoil the argument a little, actually, the interconnection
> between the two is in no way "standardized".  There's many different
> ways to wire the two chips together and have it work - because the
> TDA998x chips have a set of input muxes and swaps which allow you to
> connect the red, green, blue high/low nibbles in various ways and
> still have a correctly working system.  The TDA998x connectivity is
> _highly_ configuable.
>
> So, just because one board connects LCD_D0 (red bit 0) to a particular
> pin on the TDA998x does not mean that another board does it that way
> too.
>
> So Jean-Francois is quite correct that this data needs to be provided
> by the board in some manner.  The question is - how to do that sensibly.
>
> One possible stop-gap solution is to provide a default set which just
> happens to match the cubox, and allow OF to override it. :)

While I agree, Rob may have a different view on that for tda998x ;)

>> There is so much to take care of like pixel format on lcd pins driving
>> an external encoder (_not_ only tda998x), what gpio pin is connected to
>> TDA interrupt line, one or two lcds, ...
>
> Luckily, drivers/gpu/drm/i2c/tda998x.c does not make use of the IRQ
> signal at present - it's fairly basic and it currently operates by
> polling.  Eventually, this could change of course. :)

Again, that is in the driver Jean-Francois has available. Make sure irq
handler runs in a separate thread from get_edid and hpd and you will
be interrupted on hpd. Having said, that should finally lead to the
slave encoder setting .connector_type and .polled as this is where you
know it.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-18 19:30             ` Sebastian Hesselbarth
@ 2013-05-19  6:01               ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-19  6:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 18 May 2013 21:30:09 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> So in the end, we will have a DT node for the HW controllers found
> in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
> _how_ lcd controllers, external encoders, clocks, maybe audio, ...
> are hooked up on that specific board.

Here is my dove-cubox.dts. What is wrong with it?

/dts-v1/;

/include/ "dove.dtsi"

/ {
	model = "SolidRun CuBox";
	compatible = "solidrun,cubox", "marvell,dove";

	memory {
		device_type = "memory";
		reg = <0x00000000 0x40000000>;
	};

	chosen {
		bootargs = "console=ttyS0,115200n8 earlyprintk";
	};

	leds {
		compatible = "gpio-leds";
		pinctrl-0 = <&pmx_gpio_18>;
		pinctrl-names = "default";

		power {
			label = "Power";
			gpios = <&gpio0 18 1>;
			linux,default-trigger = "default-on";
		};
	};

	regulators {
		compatible = "simple-bus";
		#address-cells = <1>;
		#size-cells = <0>;

		usb_power: regulator at 1 {
			compatible = "regulator-fixed";
			reg = <1>;
			regulator-name = "USB Power";
			regulator-min-microvolt = <5000000>;
			regulator-max-microvolt = <5000000>;
			enable-active-high;
			regulator-always-on;
			regulator-boot-on;
			gpio = <&gpio0 1 0>;
		};
	};

	clocks {
		/* 25MHz reference crystal */
		ref25: oscillator {
			compatible = "fixed-clock";
			#clock-cells = <0>;
			clock-frequency = <25000000>;
		};

		lcdclk: fixed-clock {
			compatible = "fixed-clock";
			#clock-cells = <0>;
			clock-frequency = <400000000>;
		};
	};

	audio {
		compatible = "marvell,kirkwood-spdif-audio";
		id = <1>;
	};

	video {
		compatible = "marvell,dove-video";
	};
};

&uart0 { status = "okay"; };
&sata0 { status = "okay"; };
&i2c0 {
	status = "okay";
	clock-frequency = <100000>;

	si5351: clock-generator {
		compatible = "silabs,si5351a-msop";
		reg = <0x60>;
		#address-cells = <1>;
		#size-cells = <0>;
		#clock-cells = <1>;

		/* connect xtal input to 25MHz reference */
		clocks = <&ref25>;

		/* connect xtal input as source of pll0 and pll1 */
		silabs,pll-source = <0 0>, <1 0>;

		clkout0 {
			reg = <0>;
			silabs,drive-strength = <8>;
			silabs,multisynth-source = <0>;
			silabs,clock-source = <0>;
			silabs,pll-master;
		};

		clkout1 {
			reg = <1>;
			silabs,drive-strength = <8>;
			silabs,multisynth-source = <1>;
			silabs,clock-source = <0>;
			silabs,pll-master;
		};

		clkout2 {
			reg = <2>;
			silabs,multisynth-source = <1>;
			silabs,clock-source = <0>;
		};
	};

	tda998x: hdmi-encoder {
		compatible = "nxp,tda998x";
		reg = <0x70>;
		interrupt-parent = <&gpio0>;
		interrupts = <27 2>;		/* falling edge */
	};
};

&sdio0 {
	status = "okay";
	/* sdio0 card detect is connected to wrong pin on CuBox */
	cd-gpios = <&gpio0 12 1>;
};

&spi0 {
	status = "okay";

	/* spi0.0: 4M Flash Winbond W25Q32BV */
	spi-flash at 0 {
		compatible = "st,w25q32";
		spi-max-frequency = <20000000>;
		reg = <0>;
	};
};

&pinctrl {
	pinctrl-0 = <&pmx_gpio_1 &pmx_gpio_12 &pmx_gpio_13 &pmx_gpio_camera>;
	pinctrl-names = "default";

	pmx_gpio_1: pmx-gpio-1 {
		marvell,pins = "mpp1";
		marvell,function = "gpio";
	};

	pmx_gpio_12: pmx-gpio-12 {
		marvell,pins = "mpp12";
		marvell,function = "gpio";
	};

/* kirkwood i2s */
	pmx_gpio_13: pmx-gpio-13 {
		marvell,pins = "mpp13";
		marvell,function = "gpio";
	};

	pmx_gpio_18: pmx-gpio-18 {
		marvell,pins = "mpp18";
		marvell,function = "gpio";
	};
/* nxp HDMI irq on pin 27 */
	pmx_gpio_camera: pmx-gpio-camera {
		marvell,pins = "mpp_camera";
		marvell,function = "gpio";
	};
};

&mdio { status = "okay"; };
&ethernet { status = "okay"; };
&lcd0 {
	status = "okay";
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <&si5351 0>;
	marvell,port-type = <11>;		/* HDMIA */
	marvell,external-encoder = <&tda998x>;
};

&i2s1 { status = "okay"; };

/* --- test (not cubox) ---- *
&dcon { status = "okay"; };

&lcd1 {
	status = "okay";
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <0>;
	marvell,port-type = <1>;
	display-timings {
		mode {
			hactive = <1920>;
			vactive = <1080>;
			hfront-porch = <88>;
			hsync-len = <44>;
			hback-porch = <148>;
			vfront-porch = <4>;
			vsync-len = <5>;
			vback-porch = <36>;
			clock = <148500>;
		};
	};
};
 * ---- */

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-19  6:01               ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-19  6:01 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Rob Clark, Russell King, linux-arm-kernel, dri-devel, Jason Cooper

On Sat, 18 May 2013 21:30:09 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:

> So in the end, we will have a DT node for the HW controllers found
> in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
> _how_ lcd controllers, external encoders, clocks, maybe audio, ...
> are hooked up on that specific board.

Here is my dove-cubox.dts. What is wrong with it?

/dts-v1/;

/include/ "dove.dtsi"

/ {
	model = "SolidRun CuBox";
	compatible = "solidrun,cubox", "marvell,dove";

	memory {
		device_type = "memory";
		reg = <0x00000000 0x40000000>;
	};

	chosen {
		bootargs = "console=ttyS0,115200n8 earlyprintk";
	};

	leds {
		compatible = "gpio-leds";
		pinctrl-0 = <&pmx_gpio_18>;
		pinctrl-names = "default";

		power {
			label = "Power";
			gpios = <&gpio0 18 1>;
			linux,default-trigger = "default-on";
		};
	};

	regulators {
		compatible = "simple-bus";
		#address-cells = <1>;
		#size-cells = <0>;

		usb_power: regulator@1 {
			compatible = "regulator-fixed";
			reg = <1>;
			regulator-name = "USB Power";
			regulator-min-microvolt = <5000000>;
			regulator-max-microvolt = <5000000>;
			enable-active-high;
			regulator-always-on;
			regulator-boot-on;
			gpio = <&gpio0 1 0>;
		};
	};

	clocks {
		/* 25MHz reference crystal */
		ref25: oscillator {
			compatible = "fixed-clock";
			#clock-cells = <0>;
			clock-frequency = <25000000>;
		};

		lcdclk: fixed-clock {
			compatible = "fixed-clock";
			#clock-cells = <0>;
			clock-frequency = <400000000>;
		};
	};

	audio {
		compatible = "marvell,kirkwood-spdif-audio";
		id = <1>;
	};

	video {
		compatible = "marvell,dove-video";
	};
};

&uart0 { status = "okay"; };
&sata0 { status = "okay"; };
&i2c0 {
	status = "okay";
	clock-frequency = <100000>;

	si5351: clock-generator {
		compatible = "silabs,si5351a-msop";
		reg = <0x60>;
		#address-cells = <1>;
		#size-cells = <0>;
		#clock-cells = <1>;

		/* connect xtal input to 25MHz reference */
		clocks = <&ref25>;

		/* connect xtal input as source of pll0 and pll1 */
		silabs,pll-source = <0 0>, <1 0>;

		clkout0 {
			reg = <0>;
			silabs,drive-strength = <8>;
			silabs,multisynth-source = <0>;
			silabs,clock-source = <0>;
			silabs,pll-master;
		};

		clkout1 {
			reg = <1>;
			silabs,drive-strength = <8>;
			silabs,multisynth-source = <1>;
			silabs,clock-source = <0>;
			silabs,pll-master;
		};

		clkout2 {
			reg = <2>;
			silabs,multisynth-source = <1>;
			silabs,clock-source = <0>;
		};
	};

	tda998x: hdmi-encoder {
		compatible = "nxp,tda998x";
		reg = <0x70>;
		interrupt-parent = <&gpio0>;
		interrupts = <27 2>;		/* falling edge */
	};
};

&sdio0 {
	status = "okay";
	/* sdio0 card detect is connected to wrong pin on CuBox */
	cd-gpios = <&gpio0 12 1>;
};

&spi0 {
	status = "okay";

	/* spi0.0: 4M Flash Winbond W25Q32BV */
	spi-flash@0 {
		compatible = "st,w25q32";
		spi-max-frequency = <20000000>;
		reg = <0>;
	};
};

&pinctrl {
	pinctrl-0 = <&pmx_gpio_1 &pmx_gpio_12 &pmx_gpio_13 &pmx_gpio_camera>;
	pinctrl-names = "default";

	pmx_gpio_1: pmx-gpio-1 {
		marvell,pins = "mpp1";
		marvell,function = "gpio";
	};

	pmx_gpio_12: pmx-gpio-12 {
		marvell,pins = "mpp12";
		marvell,function = "gpio";
	};

/* kirkwood i2s */
	pmx_gpio_13: pmx-gpio-13 {
		marvell,pins = "mpp13";
		marvell,function = "gpio";
	};

	pmx_gpio_18: pmx-gpio-18 {
		marvell,pins = "mpp18";
		marvell,function = "gpio";
	};
/* nxp HDMI irq on pin 27 */
	pmx_gpio_camera: pmx-gpio-camera {
		marvell,pins = "mpp_camera";
		marvell,function = "gpio";
	};
};

&mdio { status = "okay"; };
&ethernet { status = "okay"; };
&lcd0 {
	status = "okay";
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <&si5351 0>;
	marvell,port-type = <11>;		/* HDMIA */
	marvell,external-encoder = <&tda998x>;
};

&i2s1 { status = "okay"; };

/* --- test (not cubox) ---- *
&dcon { status = "okay"; };

&lcd1 {
	status = "okay";
	clocks = <&core_clk 3>, <0>, <&lcdclk>, <0>;
	marvell,port-type = <1>;
	display-timings {
		mode {
			hactive = <1920>;
			vactive = <1080>;
			hfront-porch = <88>;
			hsync-len = <44>;
			hback-porch = <148>;
			vfront-porch = <4>;
			vsync-len = <5>;
			vback-porch = <36>;
			clock = <148500>;
		};
	};
};
 * ---- */

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-19  6:01               ` Jean-Francois Moine
@ 2013-05-19  8:30                 ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-19  8:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/19/2013 08:01 AM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 21:30:09 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> So in the end, we will have a DT node for the HW controllers found
>> in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
>> _how_ lcd controllers, external encoders, clocks, maybe audio, ...
>> are hooked up on that specific board.
>
> Here is my dove-cubox.dts. What is wrong with it?

Oh come on, seriously?

There is _nothing_ wrong with it, it already reflects what I told you
would be required for generic Dove SoC board video.

> 	video {
> 		compatible = "marvell,dove-video";
> 	};

As you may have noticed, the RFC node I sent yesterday. Is in _no_ way
different from a functional point of view. I added a video-memory
property, I renamed the compatible string. And I moved clocks property
to the video card *but* as I said in the RFC description:

"This adds a video card node required for rmk's dove_drm driver. Reg
property matches reserved memory region (currently 16M at top of 
memory), clocks property should carry extclk0 for now."

It is a node *required for rmk's dove_drm driver* for you to _test_
that very driver.

> &lcd0 {
> 	status = "okay";
> 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<&si5351 0>;
> 	marvell,port-type =<11>;		/* HDMIA */
> 	marvell,external-encoder =<&tda998x>;
> };

Again, there is no DT support in *rmk's driver*! I didn't add those
to the RFC node. I provided a DT node to *test* rmk driver. While
you were busy with complaining about things we already answered,
I did test the driver on both Cubox and D2Plug. Now I can start
with looking into rmk's driver and _suggest_ to fix this or that.

> /* --- test (not cubox) ---- *
> &dcon { status = "okay"; };
>
> &lcd1 {
> 	status = "okay";
> 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<0>;
> 	marvell,port-type =<1>;
> 	display-timings {
> 		mode {
> 			hactive =<1920>;
> 			vactive =<1080>;
> 			hfront-porch =<88>;
> 			hsync-len =<44>;
> 			hback-porch =<148>;
> 			vfront-porch =<4>;
> 			vsync-len =<5>;
> 			vback-porch =<36>;
> 			clock =<148500>;
> 		};
> 	};

I would be surprised if, lcd1 will ever be capable of driving
1080p60 on a *VGA port*!

Seriously, start _reading_ what we say. I want all those
features I already told you for your driver, in mainline driver
too. All I told you was to prevent you from doing dirty little
Cubox specific hacks that I would have to remove for e.g. D2Plug.

*But* if you ask me if we should take Russell's or your driver
as a basis, the answer is Russell's. Colon.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-19  8:30                 ` Sebastian Hesselbarth
  0 siblings, 0 replies; 91+ messages in thread
From: Sebastian Hesselbarth @ 2013-05-19  8:30 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Rob Clark, Russell King, linux-arm-kernel, dri-devel, Jason Cooper

On 05/19/2013 08:01 AM, Jean-Francois Moine wrote:
> On Sat, 18 May 2013 21:30:09 +0200
> Sebastian Hesselbarth<sebastian.hesselbarth@gmail.com>  wrote:
>> So in the end, we will have a DT node for the HW controllers found
>> in Dove SoCs, a node for TDA998x, and a node for the video card, i.e.
>> _how_ lcd controllers, external encoders, clocks, maybe audio, ...
>> are hooked up on that specific board.
>
> Here is my dove-cubox.dts. What is wrong with it?

Oh come on, seriously?

There is _nothing_ wrong with it, it already reflects what I told you
would be required for generic Dove SoC board video.

> 	video {
> 		compatible = "marvell,dove-video";
> 	};

As you may have noticed, the RFC node I sent yesterday. Is in _no_ way
different from a functional point of view. I added a video-memory
property, I renamed the compatible string. And I moved clocks property
to the video card *but* as I said in the RFC description:

"This adds a video card node required for rmk's dove_drm driver. Reg
property matches reserved memory region (currently 16M at top of 
memory), clocks property should carry extclk0 for now."

It is a node *required for rmk's dove_drm driver* for you to _test_
that very driver.

> &lcd0 {
> 	status = "okay";
> 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<&si5351 0>;
> 	marvell,port-type =<11>;		/* HDMIA */
> 	marvell,external-encoder =<&tda998x>;
> };

Again, there is no DT support in *rmk's driver*! I didn't add those
to the RFC node. I provided a DT node to *test* rmk driver. While
you were busy with complaining about things we already answered,
I did test the driver on both Cubox and D2Plug. Now I can start
with looking into rmk's driver and _suggest_ to fix this or that.

> /* --- test (not cubox) ---- *
> &dcon { status = "okay"; };
>
> &lcd1 {
> 	status = "okay";
> 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<0>;
> 	marvell,port-type =<1>;
> 	display-timings {
> 		mode {
> 			hactive =<1920>;
> 			vactive =<1080>;
> 			hfront-porch =<88>;
> 			hsync-len =<44>;
> 			hback-porch =<148>;
> 			vfront-porch =<4>;
> 			vsync-len =<5>;
> 			vback-porch =<36>;
> 			clock =<148500>;
> 		};
> 	};

I would be surprised if, lcd1 will ever be capable of driving
1080p60 on a *VGA port*!

Seriously, start _reading_ what we say. I want all those
features I already told you for your driver, in mainline driver
too. All I told you was to prevent you from doing dirty little
Cubox specific hacks that I would have to remove for e.g. D2Plug.

*But* if you ask me if we should take Russell's or your driver
as a basis, the answer is Russell's. Colon.

Sebastian

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 17:40       ` Jean-Francois Moine
@ 2013-05-19  8:59         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> case. And, yes, I better like a hardware cursor: it asks for less
> computation, and I get it immediately at graphic starting time!

Having looked at this now, using the RGB+transparency is less than ideal
because we're having to reduce an alpha channel down to a simple on/off
transparency.  X cursors really are alphablended components!

So, the options here are:
(a) use "software" rendered cursor
    + correct and expected cursor size
    + correct rendering
    + possible to use the GPU (I believe mine does)
    - maybe time consuming as it has to be removed/replaced on the screen
(b) use RGB+transparency for 64x64 hardware cursor
    + correct and expected cursor size
    + does not have to be removed/replaced when screen contents change
    - incorrect rendering due to reducing the alpha channel to a simple
      on/off transparency mask
    - has to be reloaded when the pointer is close to the edges of the
      screen which is CPU intensive
    - cursor image data passed into DRM is required to be ARGB (I've
      discussed this with David Airlie last night.)  This means we have
      to do translation to RGB+T in the kernel which is *not* nice.
(c) use ARGB 32x64 or 64x32 hardware cursor
    + does not have to be removed/replaced when screen contents change
    + correct rendering of cursor
    - unexpected cursor size; user clients do not expect to be restricted
      to 32 rows or 32 lines of cursor
    - can only select maximum cursor size on initialization of hardware
      cursor; can't dynamically switch between 32x64 and 64x32 sizes
    - has to be reloaded when the pointer is close to the edges of the
      screen which is CPU intensive

While I would like to have hardware cursor support, I don't think it's
worth the effort.  The one which tips it for me is the need to reload
the cursor near the edges - note the restriction in the documentation
that the cursor position + cursor size can't be outside the active
size.  Also, cursors generally have 7 or so transparent pixels to the
left of them (which of course changes depending on the cursor shape.)

It's also less CPU intensive to (probably) allow the GPU to render it
than it is for the CPU to deal with these hardware restrictions.

I would've liked to see hardware cursor support, but I think what I'm
going to be doing is stripping the cursor code out of the main driver
into a separate optional patch which will ultimately be dropped.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-19  8:59         ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19  8:59 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
> case. And, yes, I better like a hardware cursor: it asks for less
> computation, and I get it immediately at graphic starting time!

Having looked at this now, using the RGB+transparency is less than ideal
because we're having to reduce an alpha channel down to a simple on/off
transparency.  X cursors really are alphablended components!

So, the options here are:
(a) use "software" rendered cursor
    + correct and expected cursor size
    + correct rendering
    + possible to use the GPU (I believe mine does)
    - maybe time consuming as it has to be removed/replaced on the screen
(b) use RGB+transparency for 64x64 hardware cursor
    + correct and expected cursor size
    + does not have to be removed/replaced when screen contents change
    - incorrect rendering due to reducing the alpha channel to a simple
      on/off transparency mask
    - has to be reloaded when the pointer is close to the edges of the
      screen which is CPU intensive
    - cursor image data passed into DRM is required to be ARGB (I've
      discussed this with David Airlie last night.)  This means we have
      to do translation to RGB+T in the kernel which is *not* nice.
(c) use ARGB 32x64 or 64x32 hardware cursor
    + does not have to be removed/replaced when screen contents change
    + correct rendering of cursor
    - unexpected cursor size; user clients do not expect to be restricted
      to 32 rows or 32 lines of cursor
    - can only select maximum cursor size on initialization of hardware
      cursor; can't dynamically switch between 32x64 and 64x32 sizes
    - has to be reloaded when the pointer is close to the edges of the
      screen which is CPU intensive

While I would like to have hardware cursor support, I don't think it's
worth the effort.  The one which tips it for me is the need to reload
the cursor near the edges - note the restriction in the documentation
that the cursor position + cursor size can't be outside the active
size.  Also, cursors generally have 7 or so transparent pixels to the
left of them (which of course changes depending on the cursor shape.)

It's also less CPU intensive to (probably) allow the GPU to render it
than it is for the CPU to deal with these hardware restrictions.

I would've liked to see hardware cursor support, but I think what I'm
going to be doing is stripping the cursor code out of the main driver
into a separate optional patch which will ultimately be dropped.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
  2013-05-18  6:56     ` Jean-Francois Moine
@ 2013-05-19 10:30       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19 10:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 08:56:59AM +0200, Jean-Francois Moine wrote:
> On Thu, 16 May 2013 20:26:18 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > When switching between various drivers for this device, it's possible
> > that some critical registers are left containing values which affect
> > the device operation.  One such case encountered is the VIP output
> > mux register.  This defaults to 0x24 on powerup, but other drivers may
> > set this to 0x12.  This results in incorrect colours.
> > 
> > Fix this by ensuring that the register is always set to the power on
> > default setting.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
> >  1 files changed, 3 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> > index d71c408..4b4db95 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> > @@ -110,6 +110,7 @@ struct tda998x_priv {
> >  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
> >  # define VIP_CNTRL_5_CKCASE       (1 << 0)
> >  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> > +#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
> >  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
> >  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
> >  # define MAT_CONTRL_MAT_BP        (1 << 2)
> > @@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
> >  
> >  	switch (mode) {
> >  	case DRM_MODE_DPMS_ON:
> > +		/* Write the default value MUX register */
> > +		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
> >  		/* enable audio and video ports */
> >  		reg_write(encoder, REG_ENA_AP, 0xff);
> >  		reg_write(encoder, REG_ENA_VP_0, 0xff);
> 
> This register is never touched. Should not this setting better go at
> reset time (in tda998x_reset)?

Yes, you're right, it could be done there without any additional side
effects.  I've just moved it there.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set
@ 2013-05-19 10:30       ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19 10:30 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Sat, May 18, 2013 at 08:56:59AM +0200, Jean-Francois Moine wrote:
> On Thu, 16 May 2013 20:26:18 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > When switching between various drivers for this device, it's possible
> > that some critical registers are left containing values which affect
> > the device operation.  One such case encountered is the VIP output
> > mux register.  This defaults to 0x24 on powerup, but other drivers may
> > set this to 0x12.  This results in incorrect colours.
> > 
> > Fix this by ensuring that the register is always set to the power on
> > default setting.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/i2c/tda998x_drv.c |    3 +++
> >  1 files changed, 3 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> > index d71c408..4b4db95 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> > @@ -110,6 +110,7 @@ struct tda998x_priv {
> >  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
> >  # define VIP_CNTRL_5_CKCASE       (1 << 0)
> >  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> > +#define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
> >  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
> >  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
> >  # define MAT_CONTRL_MAT_BP        (1 << 2)
> > @@ -438,6 +439,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
> >  
> >  	switch (mode) {
> >  	case DRM_MODE_DPMS_ON:
> > +		/* Write the default value MUX register */
> > +		reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
> >  		/* enable audio and video ports */
> >  		reg_write(encoder, REG_ENA_AP, 0xff);
> >  		reg_write(encoder, REG_ENA_VP_0, 0xff);
> 
> This register is never touched. Should not this setting better go at
> reset time (in tda998x_reset)?

Yes, you're right, it could be done there without any additional side
effects.  I've just moved it there.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-17 11:33   ` Jean-Francois Moine
@ 2013-05-19 11:25     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 17, 2013 at 01:33:45PM +0200, Jean-Francois Moine wrote:
> I quickly compared your dove drm driver and ours (Sebastian and me):
> 
> - CMA helper
> 
>   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
>   simplify some code.

Looking at the CMA helper code in DRM, it makes some fundamental errors:

1. It assumes the returned DMA address is a physical address.  This
   is not necessarily the case (there are a number of ARM platforms
   where this is most definitely not true.)  So:

        cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
                        &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
...
        ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
                        vma->vm_end - vma->vm_start, vma->vm_page_prot);

   is extremely broken.

2. If you use the CMA helper, then all your GEM objects must be CMA
   objects.  You can't combine different types of objects (eg, SHM-backed
   objects) with CMA objects.  This will be a massive performance
   regression with GPUs which can handle scatter-gathered SHM objects
   such as the Vivante GPU on the Armada 510.

So, for me, the last point especially is a very strong argument not to
use the DRM CMA helper.  Yes, I could switch to using the DMA coherent/
writecombine allocation API and thus make use of CMA, and that's
something I will be looking at.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-19 11:25     ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-19 11:25 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Fri, May 17, 2013 at 01:33:45PM +0200, Jean-Francois Moine wrote:
> I quickly compared your dove drm driver and ours (Sebastian and me):
> 
> - CMA helper
> 
>   You don't use DRM_KMS_CMA_HELPER and DRM_GEM_CMA_HELPER which would
>   simplify some code.

Looking at the CMA helper code in DRM, it makes some fundamental errors:

1. It assumes the returned DMA address is a physical address.  This
   is not necessarily the case (there are a number of ARM platforms
   where this is most definitely not true.)  So:

        cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
                        &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
...
        ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
                        vma->vm_end - vma->vm_start, vma->vm_page_prot);

   is extremely broken.

2. If you use the CMA helper, then all your GEM objects must be CMA
   objects.  You can't combine different types of objects (eg, SHM-backed
   objects) with CMA objects.  This will be a massive performance
   regression with GPUs which can handle scatter-gathered SHM objects
   such as the Vivante GPU on the Armada 510.

So, for me, the last point especially is a very strong argument not to
use the DRM CMA helper.  Yes, I could switch to using the DMA coherent/
writecombine allocation API and thus make use of CMA, and that's
something I will be looking at.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 4/4] DRM: tda998x: add missing include
  2013-05-19  8:30                 ` Sebastian Hesselbarth
@ 2013-05-19 16:49                   ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-19 16:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 19 May 2013 10:30:00 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:
> > /* --- test (not cubox) ---- *
> > &dcon { status = "okay"; };
> > 
> > &lcd1 {
> > 	status = "okay";
> > 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<0>;
> > 	marvell,port-type =<1>;
> > 	display-timings {
> > 		mode {
> > 			hactive =<1920>;
> > 			vactive =<1080>;
> > 			hfront-porch =<88>;
> > 			hsync-len =<44>;
> > 			hback-porch =<148>;
> > 			vfront-porch =<4>;
> > 			vsync-len =<5>;
> > 			vback-porch =<36>;
> > 			clock =<148500>;
> > 		};
> > 	};  
> 
> I would be surprised if, lcd1 will ever be capable of driving
> 1080p60 on a *VGA port*!

As you may see, this sequence is preceded by an open comment: "test".

Now, as the port B of the display controller is only VGA (this is
checked in my driver), as there is no VGA connector on the Cubox, as
there is no VGA DAC code in my driver, and as I had to test the display
controller, I:

- defined the port B as VGA,

- set the timings of the mode I use for my HDMI display.

This does not mean the example must be used in the real word. It is
just a working example for test purpose.

> Seriously, start _reading_ what we say. I want all those
> features I already told you for your driver, in mainline driver
> too. All I told you was to prevent you from doing dirty little
> Cubox specific hacks that I would have to remove for e.g. D2Plug.

There is _NO_ Cubox specific stuff in my driver (I don't even use any
cubox-setup as Russell) and it should work without any change (except
the DT) in your D2plug. Remember, all my drm driver work is in
http://moinejf.free.fr/cubox/ as a big kernel patch (I will add the
I2C: mv64xxx which work fine - thanks Russell).

> *But* if you ask me if we should take Russell's or your driver
> as a basis, the answer is Russell's. Colon.

OK. Do what you want. My driver works fine enough for my usage:
software development. For video, my ISP gives us for free a Atom based
multimedia player with a Blue-Ray reader, and I have a AMD64 double
core on my family TV set for internet video (it will be a long time
till there will be VP8 decoding with vMeta). The only interest I see
in the Cubox is its low power consumption.

Now, I will switch back to my favorite long-distance development...

See you.

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 4/4] DRM: tda998x: add missing include
@ 2013-05-19 16:49                   ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-19 16:49 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Rob Clark, Russell King, linux-arm-kernel, dri-devel, Jason Cooper

On Sun, 19 May 2013 10:30:00 +0200
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> wrote:
> > /* --- test (not cubox) ---- *
> > &dcon { status = "okay"; };
> > 
> > &lcd1 {
> > 	status = "okay";
> > 	clocks =<&core_clk 3>,<0>,<&lcdclk>,<0>;
> > 	marvell,port-type =<1>;
> > 	display-timings {
> > 		mode {
> > 			hactive =<1920>;
> > 			vactive =<1080>;
> > 			hfront-porch =<88>;
> > 			hsync-len =<44>;
> > 			hback-porch =<148>;
> > 			vfront-porch =<4>;
> > 			vsync-len =<5>;
> > 			vback-porch =<36>;
> > 			clock =<148500>;
> > 		};
> > 	};  
> 
> I would be surprised if, lcd1 will ever be capable of driving
> 1080p60 on a *VGA port*!

As you may see, this sequence is preceded by an open comment: "test".

Now, as the port B of the display controller is only VGA (this is
checked in my driver), as there is no VGA connector on the Cubox, as
there is no VGA DAC code in my driver, and as I had to test the display
controller, I:

- defined the port B as VGA,

- set the timings of the mode I use for my HDMI display.

This does not mean the example must be used in the real word. It is
just a working example for test purpose.

> Seriously, start _reading_ what we say. I want all those
> features I already told you for your driver, in mainline driver
> too. All I told you was to prevent you from doing dirty little
> Cubox specific hacks that I would have to remove for e.g. D2Plug.

There is _NO_ Cubox specific stuff in my driver (I don't even use any
cubox-setup as Russell) and it should work without any change (except
the DT) in your D2plug. Remember, all my drm driver work is in
http://moinejf.free.fr/cubox/ as a big kernel patch (I will add the
I2C: mv64xxx which work fine - thanks Russell).

> *But* if you ask me if we should take Russell's or your driver
> as a basis, the answer is Russell's. Colon.

OK. Do what you want. My driver works fine enough for my usage:
software development. For video, my ISP gives us for free a Atom based
multimedia player with a Blue-Ray reader, and I have a AMD64 double
core on my family TV set for internet video (it will be a long time
till there will be VP8 decoding with vMeta). The only interest I see
in the Cubox is its low power consumption.

Now, I will switch back to my favorite long-distance development...

See you.

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 3/4] DRM: add OF support for Dove DRM driver
  2013-05-18 19:18           ` Jean-Francois Moine
@ 2013-05-20 10:16             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-20 10:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, May 18, 2013 at 09:18:46PM +0200, Jean-Francois Moine wrote:
> The general hardware code of both drivers is close enough for merging
> may be done starting from anyone. But the general layout is not:
> 
> - my driver is DT driven and has one card with 2 CTRC's and 2 connectors.
> 
> - Russell's is non-DT, so, with some extra code I am not aware of, with
>   any number of cards each one with one CRTC and one connector (no, I

Utter shit, and I've told you before.  I'm not going to repeat it again,
and I warned you that we would have a falling out.  As you seem to be
immune to my comments provided in a sane manner, I'm now just going to
ignore you in the future because you're clearly not bothering to read
anything I'm telling you.  That makes further discussion with you utterly
pointless.

>   tried it, you cannot clone a connector of one card to the connector
>   of another card).
> 
> So, for me, merging means enhance my code from Russell's, but I will
> not go to a non-DT kernel.

You seem to see DT setups vs non-DT setups as two entirely separate things.
They are not.  We have plenty of drivers which work in both setups just
fine.

So really all of your points above are utter trash to me - really.

As to illustrate the point, my TDA19988 backend is now _just_ a slave
encoder backend, it has nothing TDA19988 specific, nor does it have
anything cubox specific there; the cubox specific part has been moved
up into dove_drv.c as pure data, and that data can be constructed
either from platform data or from OF properties parsed by dove_drv.c.

But why did I just bother to type that.  You're just going to ignore it
and keep repeating your same old claptrap.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 3/4] DRM: add OF support for Dove DRM driver
@ 2013-05-20 10:16             ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-20 10:16 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Sat, May 18, 2013 at 09:18:46PM +0200, Jean-Francois Moine wrote:
> The general hardware code of both drivers is close enough for merging
> may be done starting from anyone. But the general layout is not:
> 
> - my driver is DT driven and has one card with 2 CTRC's and 2 connectors.
> 
> - Russell's is non-DT, so, with some extra code I am not aware of, with
>   any number of cards each one with one CRTC and one connector (no, I

Utter shit, and I've told you before.  I'm not going to repeat it again,
and I warned you that we would have a falling out.  As you seem to be
immune to my comments provided in a sane manner, I'm now just going to
ignore you in the future because you're clearly not bothering to read
anything I'm telling you.  That makes further discussion with you utterly
pointless.

>   tried it, you cannot clone a connector of one card to the connector
>   of another card).
> 
> So, for me, merging means enhance my code from Russell's, but I will
> not go to a non-DT kernel.

You seem to see DT setups vs non-DT setups as two entirely separate things.
They are not.  We have plenty of drivers which work in both setups just
fine.

So really all of your points above are utter trash to me - really.

As to illustrate the point, my TDA19988 backend is now _just_ a slave
encoder backend, it has nothing TDA19988 specific, nor does it have
anything cubox specific there; the cubox specific part has been moved
up into dove_drv.c as pure data, and that data can be constructed
either from platform data or from OF properties parsed by dove_drv.c.

But why did I just bother to type that.  You're just going to ignore it
and keep repeating your same old claptrap.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-19  8:59         ` Russell King - ARM Linux
@ 2013-05-20 13:36           ` Alex Deucher
  -1 siblings, 0 replies; 91+ messages in thread
From: Alex Deucher @ 2013-05-20 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, May 19, 2013 at 4:59 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
>> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
>> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
>> case. And, yes, I better like a hardware cursor: it asks for less
>> computation, and I get it immediately at graphic starting time!
>
> Having looked at this now, using the RGB+transparency is less than ideal
> because we're having to reduce an alpha channel down to a simple on/off
> transparency.  X cursors really are alphablended components!
>
> So, the options here are:
> (a) use "software" rendered cursor
>     + correct and expected cursor size
>     + correct rendering
>     + possible to use the GPU (I believe mine does)
>     - maybe time consuming as it has to be removed/replaced on the screen
> (b) use RGB+transparency for 64x64 hardware cursor
>     + correct and expected cursor size
>     + does not have to be removed/replaced when screen contents change
>     - incorrect rendering due to reducing the alpha channel to a simple
>       on/off transparency mask
>     - has to be reloaded when the pointer is close to the edges of the
>       screen which is CPU intensive
>     - cursor image data passed into DRM is required to be ARGB (I've
>       discussed this with David Airlie last night.)  This means we have
>       to do translation to RGB+T in the kernel which is *not* nice.
> (c) use ARGB 32x64 or 64x32 hardware cursor
>     + does not have to be removed/replaced when screen contents change
>     + correct rendering of cursor
>     - unexpected cursor size; user clients do not expect to be restricted
>       to 32 rows or 32 lines of cursor
>     - can only select maximum cursor size on initialization of hardware
>       cursor; can't dynamically switch between 32x64 and 64x32 sizes
>     - has to be reloaded when the pointer is close to the edges of the
>       screen which is CPU intensive

You can tell the xserver what size cursor you support when you call
xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
hardware with non-64x64 cursors and haven't run into any problems yet.

Alex

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-20 13:36           ` Alex Deucher
  0 siblings, 0 replies; 91+ messages in thread
From: Alex Deucher @ 2013-05-20 13:36 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jean-Francois Moine, Jason Cooper, dri-devel, Darren Etheridge,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, May 19, 2013 at 4:59 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, May 17, 2013 at 07:40:23PM +0200, Jean-Francois Moine wrote:
>> Maybe I did not explain correctly: the colored cursor maybe RGB888 +
>> transparency (64x64) or full ARGB (64x32 or 32x64). I coded the first
>> case. And, yes, I better like a hardware cursor: it asks for less
>> computation, and I get it immediately at graphic starting time!
>
> Having looked at this now, using the RGB+transparency is less than ideal
> because we're having to reduce an alpha channel down to a simple on/off
> transparency.  X cursors really are alphablended components!
>
> So, the options here are:
> (a) use "software" rendered cursor
>     + correct and expected cursor size
>     + correct rendering
>     + possible to use the GPU (I believe mine does)
>     - maybe time consuming as it has to be removed/replaced on the screen
> (b) use RGB+transparency for 64x64 hardware cursor
>     + correct and expected cursor size
>     + does not have to be removed/replaced when screen contents change
>     - incorrect rendering due to reducing the alpha channel to a simple
>       on/off transparency mask
>     - has to be reloaded when the pointer is close to the edges of the
>       screen which is CPU intensive
>     - cursor image data passed into DRM is required to be ARGB (I've
>       discussed this with David Airlie last night.)  This means we have
>       to do translation to RGB+T in the kernel which is *not* nice.
> (c) use ARGB 32x64 or 64x32 hardware cursor
>     + does not have to be removed/replaced when screen contents change
>     + correct rendering of cursor
>     - unexpected cursor size; user clients do not expect to be restricted
>       to 32 rows or 32 lines of cursor
>     - can only select maximum cursor size on initialization of hardware
>       cursor; can't dynamically switch between 32x64 and 64x32 sizes
>     - has to be reloaded when the pointer is close to the edges of the
>       screen which is CPU intensive

You can tell the xserver what size cursor you support when you call
xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
hardware with non-64x64 cursors and haven't run into any problems yet.

Alex

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-20 13:36           ` Alex Deucher
@ 2013-05-20 20:15             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-20 20:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 20, 2013 at 09:36:02AM -0400, Alex Deucher wrote:
> You can tell the xserver what size cursor you support when you call
> xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
> cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
> hardware with non-64x64 cursors and haven't run into any problems yet.

I guess the xf86 backend will fall back to software cursors if it gets
a cursor larger than the hardware supports, so maybe 64x32 will be
fine.

However, my comments concerning the less-than-64x64 size come from
David Airlie who said "X and userside programs expect 64x64 to work.
so its possibly pointless supporting anything less, as in you'd write
code but nobody would end up using it".

Note also that the generic DRM KMS code assumes cursor support at
64x64, and there's no generic way for a driver at present (that I know
of) to inform it of anything different.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-20 20:15             ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2013-05-20 20:15 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Jean-Francois Moine, Jason Cooper, dri-devel, Darren Etheridge,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, May 20, 2013 at 09:36:02AM -0400, Alex Deucher wrote:
> You can tell the xserver what size cursor you support when you call
> xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
> cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
> hardware with non-64x64 cursors and haven't run into any problems yet.

I guess the xf86 backend will fall back to software cursors if it gets
a cursor larger than the hardware supports, so maybe 64x32 will be
fine.

However, my comments concerning the less-than-64x64 size come from
David Airlie who said "X and userside programs expect 64x64 to work.
so its possibly pointless supporting anything less, as in you'd write
code but nobody would end up using it".

Note also that the generic DRM KMS code assumes cursor support at
64x64, and there's no generic way for a driver at present (that I know
of) to inform it of anything different.

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-20 20:15             ` Russell King - ARM Linux
@ 2013-05-20 20:23               ` Alex Deucher
  -1 siblings, 0 replies; 91+ messages in thread
From: Alex Deucher @ 2013-05-20 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 20, 2013 at 4:15 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, May 20, 2013 at 09:36:02AM -0400, Alex Deucher wrote:
>> You can tell the xserver what size cursor you support when you call
>> xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
>> cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
>> hardware with non-64x64 cursors and haven't run into any problems yet.
>
> I guess the xf86 backend will fall back to software cursors if it gets
> a cursor larger than the hardware supports, so maybe 64x32 will be
> fine.
>
> However, my comments concerning the less-than-64x64 size come from
> David Airlie who said "X and userside programs expect 64x64 to work.
> so its possibly pointless supporting anything less, as in you'd write
> code but nobody would end up using it".
>
> Note also that the generic DRM KMS code assumes cursor support at
> 64x64, and there's no generic way for a driver at present (that I know
> of) to inform it of anything different.

It shouldn't be too hard to add a new cap for querying the cursor size
to the drm cap interface.

Alex

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-20 20:23               ` Alex Deucher
  0 siblings, 0 replies; 91+ messages in thread
From: Alex Deucher @ 2013-05-20 20:23 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jean-Francois Moine, Jason Cooper, dri-devel, Darren Etheridge,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, May 20, 2013 at 4:15 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, May 20, 2013 at 09:36:02AM -0400, Alex Deucher wrote:
>> You can tell the xserver what size cursor you support when you call
>> xf86_cursors_init() in the ddx.  Just expose a 32x64 or 64x32 ARGB
>> cursor.  Most apps don't use a 64x64 cursor anyway.  I've used
>> hardware with non-64x64 cursors and haven't run into any problems yet.
>
> I guess the xf86 backend will fall back to software cursors if it gets
> a cursor larger than the hardware supports, so maybe 64x32 will be
> fine.
>
> However, my comments concerning the less-than-64x64 size come from
> David Airlie who said "X and userside programs expect 64x64 to work.
> so its possibly pointless supporting anything less, as in you'd write
> code but nobody would end up using it".
>
> Note also that the generic DRM KMS code assumes cursor support at
> 64x64, and there's no generic way for a driver at present (that I know
> of) to inform it of anything different.

It shouldn't be too hard to add a new cap for querying the cursor size
to the drm cap interface.

Alex

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
  2013-05-20 20:23               ` Alex Deucher
@ 2013-05-21  6:30                 ` Jean-Francois Moine
  -1 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-21  6:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 20 May 2013 16:23:33 -0400
Alex Deucher <alexdeucher@gmail.com> wrote:

> > Note also that the generic DRM KMS code assumes cursor support at
> > 64x64, and there's no generic way for a driver at present (that I know
> > of) to inform it of anything different.  
> 
> It shouldn't be too hard to add a new cap for querying the cursor size
> to the drm cap interface.

There was already a patch request for that, but the functions were
surely useless.

Otherwise, you may do a search on "largest cursor:" (xdpyinfo output).
The results are surprising..

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver
@ 2013-05-21  6:30                 ` Jean-Francois Moine
  0 siblings, 0 replies; 91+ messages in thread
From: Jean-Francois Moine @ 2013-05-21  6:30 UTC (permalink / raw)
  To: Alex Deucher
  Cc: Russell King - ARM Linux, Jason Cooper, dri-devel,
	Darren Etheridge, linux-arm-kernel, Sebastian Hesselbarth

On Mon, 20 May 2013 16:23:33 -0400
Alex Deucher <alexdeucher@gmail.com> wrote:

> > Note also that the generic DRM KMS code assumes cursor support at
> > 64x64, and there's no generic way for a driver at present (that I know
> > of) to inform it of anything different.  
> 
> It shouldn't be too hard to add a new cap for querying the cursor size
> to the drm cap interface.

There was already a patch request for that, but the functions were
surely useless.

Otherwise, you may do a search on "largest cursor:" (xdpyinfo output).
The results are surprising..

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 91+ messages in thread

* [RFC 6/8] drm/i2c: nxp-tda998x: add video and audio input configuration
  2013-05-16 19:27   ` Russell King
@ 2013-05-22 21:08     ` Rob Clark
  -1 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-22 21:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 16, 2013 at 3:27 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/i2c/tda998x_drv.c |  308 ++++++++++++++++++++++++++++++++++++-
>  1 files changed, 301 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index d6afd02..8ffb844 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -23,7 +23,7 @@
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_encoder_slave.h>
>  #include <drm/drm_edid.h>
> -
> +#include <drm/i2c/tda998x.h>
>
>  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
>
> @@ -32,9 +32,11 @@ struct tda998x_priv {
>         uint16_t rev;
>         uint8_t current_page;
>         int dpms;
> +       bool is_hdmi_sink;
>         u8 vip_cntrl_0;
>         u8 vip_cntrl_1;
>         u8 vip_cntrl_2;
> +       struct tda998x_encoder_params params;
>  };
>
>  #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
> @@ -71,10 +73,13 @@ struct tda998x_priv {
>  # define I2C_MASTER_DIS_MM        (1 << 0)
>  # define I2C_MASTER_DIS_FILT      (1 << 1)
>  # define I2C_MASTER_APP_STRT_LAT  (1 << 2)
> +#define REG_FEAT_POWERDOWN        REG(0x00, 0x0e)     /* read/write */
> +# define FEAT_POWERDOWN_SPDIF     (1 << 3)
>  #define REG_INT_FLAGS_0           REG(0x00, 0x0f)     /* read/write */
>  #define REG_INT_FLAGS_1           REG(0x00, 0x10)     /* read/write */
>  #define REG_INT_FLAGS_2           REG(0x00, 0x11)     /* read/write */
>  # define INT_FLAGS_2_EDID_BLK_RD  (1 << 1)
> +#define REG_ENA_ACLK              REG(0x00, 0x16)     /* read/write */
>  #define REG_ENA_VP_0              REG(0x00, 0x18)     /* read/write */
>  #define REG_ENA_VP_1              REG(0x00, 0x19)     /* read/write */
>  #define REG_ENA_VP_2              REG(0x00, 0x1a)     /* read/write */
> @@ -113,6 +118,7 @@ struct tda998x_priv {
>  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
>  # define VIP_CNTRL_5_CKCASE       (1 << 0)
>  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> +#define REG_MUX_AP                REG(0x00, 0x26)     /* read/write */
>  #define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
>  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
>  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
> @@ -175,6 +181,12 @@ struct tda998x_priv {
>  # define HVF_CNTRL_1_PAD(x)       (((x) & 3) << 4)
>  # define HVF_CNTRL_1_SEMI_PLANAR  (1 << 6)
>  #define REG_RPT_CNTRL             REG(0x00, 0xf0)     /* write */
> +#define REG_I2S_FORMAT            REG(0x00, 0xfc)     /* read/write */
> +# define I2S_FORMAT(x)            (((x) & 3) << 0)
> +#define REG_AIP_CLKSEL            REG(0x00, 0xfd)     /* write */
> +# define AIP_CLKSEL_FS(x)         (((x) & 3) << 0)
> +# define AIP_CLKSEL_CLK_POL(x)    (((x) & 1) << 2)
> +# define AIP_CLKSEL_AIP(x)        (((x) & 7) << 3)
>
>
>  /* Page 02h: PLL settings */
> @@ -216,6 +228,11 @@ struct tda998x_priv {
>
>
>  /* Page 10h: information frames and packets */
> +#define REG_IF1_HB0               REG(0x10, 0x20)     /* read/write */
> +#define REG_IF2_HB0               REG(0x10, 0x40)     /* read/write */
> +#define REG_IF3_HB0               REG(0x10, 0x60)     /* read/write */
> +#define REG_IF4_HB0               REG(0x10, 0x80)     /* read/write */
> +#define REG_IF5_HB0               REG(0x10, 0xa0)     /* read/write */
>
>
>  /* Page 11h: audio settings and content info packets */
> @@ -225,10 +242,33 @@ struct tda998x_priv {
>  # define AIP_CNTRL_0_LAYOUT       (1 << 2)
>  # define AIP_CNTRL_0_ACR_MAN      (1 << 5)
>  # define AIP_CNTRL_0_RST_CTS      (1 << 6)
> +#define REG_CA_I2S                REG(0x11, 0x01)     /* read/write */
> +# define CA_I2S_CA_I2S(x)         (((x) & 31) << 0)
> +# define CA_I2S_HBR_CHSTAT        (1 << 6)
> +#define REG_LATENCY_RD            REG(0x11, 0x04)     /* read/write */
> +#define REG_ACR_CTS_0             REG(0x11, 0x05)     /* read/write */
> +#define REG_ACR_CTS_1             REG(0x11, 0x06)     /* read/write */
> +#define REG_ACR_CTS_2             REG(0x11, 0x07)     /* read/write */
> +#define REG_ACR_N_0               REG(0x11, 0x08)     /* read/write */
> +#define REG_ACR_N_1               REG(0x11, 0x09)     /* read/write */
> +#define REG_ACR_N_2               REG(0x11, 0x0a)     /* read/write */
> +#define REG_CTS_N                 REG(0x11, 0x0c)     /* read/write */
> +# define CTS_N_K(x)               (((x) & 7) << 0)
> +# define CTS_N_M(x)               (((x) & 3) << 4)
>  #define REG_ENC_CNTRL             REG(0x11, 0x0d)     /* read/write */
>  # define ENC_CNTRL_RST_ENC        (1 << 0)
>  # define ENC_CNTRL_RST_SEL        (1 << 1)
>  # define ENC_CNTRL_CTL_CODE(x)    (((x) & 3) << 2)
> +#define REG_DIP_FLAGS             REG(0x11, 0x0e)     /* read/write */
> +# define DIP_FLAGS_ACR            (1 << 0)
> +# define DIP_FLAGS_GC             (1 << 1)
> +#define REG_DIP_IF_FLAGS          REG(0x11, 0x0f)     /* read/write */
> +# define DIP_IF_FLAGS_IF1         (1 << 1)
> +# define DIP_IF_FLAGS_IF2         (1 << 2)
> +# define DIP_IF_FLAGS_IF3         (1 << 3)
> +# define DIP_IF_FLAGS_IF4         (1 << 4)
> +# define DIP_IF_FLAGS_IF5         (1 << 5)
> +#define REG_CH_STAT_B(x)          REG(0x11, 0x14 + (x)) /* read/write */
>
>
>  /* Page 12h: HDCP and OTP */
> @@ -344,6 +384,23 @@ reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt)
>         return ret;
>  }
>
> +static void
> +reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt)
> +{
> +       struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
> +       uint8_t buf[cnt+1];
> +       int ret;
> +
> +       buf[0] = REG2ADDR(reg);
> +       memcpy(&buf[1], p, cnt);
> +
> +       set_page(encoder, reg);
> +
> +       ret = i2c_master_send(client, buf, cnt + 1);
> +       if (ret < 0)
> +               dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
> +}
> +
>  static uint8_t
>  reg_read(struct drm_encoder *encoder, uint16_t reg)
>  {
> @@ -421,11 +478,192 @@ tda998x_reset(struct drm_encoder *encoder)
>         reg_write(encoder, REG_PLL_SCG2,     0x10);
>  }
>
> +static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
> +{
> +       uint8_t sum = 0;
> +
> +       while (bytes--)
> +               sum += *buf++;
> +       return (255 - sum) + 1;
> +}
> +
> +#define HB(x) (x)
> +#define PB(x) (HB(2) + 1 + (x))
> +
> +static void
> +tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr,
> +                uint8_t *buf, size_t size)
> +{
> +       buf[PB(0)] = tda998x_cksum(buf, size);
> +
> +       reg_clear(encoder, REG_DIP_IF_FLAGS, bit);
> +       reg_write_range(encoder, addr, buf, size);
> +       reg_set(encoder, REG_DIP_IF_FLAGS, bit);
> +}
> +
> +static void
> +tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
> +{
> +       uint8_t buf[PB(5) + 1];
> +
> +       buf[HB(0)] = 0x84;
> +       buf[HB(1)] = 0x01;
> +       buf[HB(2)] = 10;
> +       buf[PB(0)] = 0;
> +       buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
> +       buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
> +       buf[PB(4)] = p->audio_frame[4];
> +       buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
> +
> +       tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
> +                        sizeof(buf));
> +}
> +
> +static void
> +tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
> +{
> +       uint8_t buf[PB(13) + 1];
> +
> +       memset(buf, 0, sizeof(buf));
> +       buf[HB(0)] = 0x82;
> +       buf[HB(1)] = 0x02;
> +       buf[HB(2)] = 13;
> +       buf[PB(4)] = drm_match_cea_mode(mode);
> +
> +       tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
> +                        sizeof(buf));
> +}

fwiw, I expect you could use the hdmi infoframe helpers for some of
this (drivers/video/hdmi.c).. it is a relatively recent addition, in
case you hadn't seen them already

BR,
-R

> +static void tda998x_audio_mute(struct drm_encoder *encoder, bool on)
> +{
> +       if (on) {
> +               reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
> +               reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
> +               reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
> +       } else {
> +               reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
> +       }
> +}
> +
> +static void
> +tda998x_configure_audio(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
> +{
> +       uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n;
> +       uint32_t n;
> +
> +       /* SetAudioPortConfig */
> +       reg_write(encoder, REG_ENA_AP, p->audio_cfg);
> +       /* SetAudioClockPortConfig */
> +       reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg);
> +
> +       /*
> +        * layout = channelAllocation ? 1 : 0;
> +        * AudioInSetConfig(format, i2sFormat, channelAllocation,
> +        *   HDMITX_CHAN_NO_CHANGE, HDMITX_CLKPOLDSD_NO_CHANGE,
> +        *   HDMITX_SWAPDSD_NO_CHANGE, layout, HDMITX_LATENCY_CURRENT,
> +        *   dstRate)
> +        */
> +       switch (p->audio_format) {
> +       case AFMT_SPDIF:
> +               reg_write(encoder, REG_MUX_AP, 0x40);
> +               clksel_aip = AIP_CLKSEL_AIP(0);
> +               /* FS64SPDIF */
> +               clksel_fs = AIP_CLKSEL_FS(2);
> +               cts_n = CTS_N_M(3) | CTS_N_K(3);
> +               ca_i2s = 0;
> +               break;
> +
> +       case AFMT_I2S:
> +               reg_write(encoder, REG_MUX_AP, 0x64);
> +               clksel_aip = AIP_CLKSEL_AIP(1);
> +               /* ACLK */
> +               clksel_fs = AIP_CLKSEL_FS(0);
> +               cts_n = CTS_N_M(3) | CTS_N_K(3);
> +               ca_i2s = CA_I2S_CA_I2S(0 /* channel allocation */);
> +               break;
> +       }
> +
> +       reg_write(encoder, REG_AIP_CLKSEL, clksel_aip);
> +//     reg_write(encoder, REG_CA_I2S, ca_i2s);
> +//     reg_write(encoder, REG_I2S_FORMAT, I2S_FORMAT(0 /* i2s format */));
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT);
> +       /* latency? */
> +
> +       /* get video format */
> +
> +       /*
> +        * ctsRef = HDMITX_CTSREF_FS64SPDIF, uCtsX = HDMITX_CTSX_64
> +        * AudioInSetCts(ctsRef, rate, VidFmt, vOutFreq,
> +        *   HDMITX_CTS_AUTO, uCtsX, HDMITX_CTSK_USE_CTSX,
> +        *   HDMITX_CTSMTS_USE_CTSX, dstRate)
> +        */
> +       /* Auto CTS */
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN);
> +       reg_write(encoder, REG_CTS_N, cts_n);
> +       reg_write(encoder, REG_AUDIO_DIV, 3);
> +
> +       /*
> +        * This is the approximate value of N, which happens to be
> +        * the recommended values for non-coherent clocks.
> +        */
> +       n = 128 * p->audio_sample_rate / 1000;
> +
> +       /* Write the CTS and N values */
> +       buf[0] = 0x44;
> +       buf[1] = 0x42;
> +       buf[2] = 0x01;
> +       buf[3] = n;
> +       buf[4] = n >> 8;
> +       buf[5] = n >> 16;
> +       reg_write_range(encoder, REG_ACR_CTS_0, buf, 6);
> +
> +       /* Set CTS clock reference */
> +       reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
> +
> +       /* Reset CTS generator */
> +       reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
> +
> +       /* Write the channel status */
> +       buf[0] = 0x04;
> +       buf[1] = 0x00;
> +       buf[2] = 0x00;
> +       buf[3] = 0xf1;
> +       reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4);
> +
> +       tda998x_audio_mute(encoder, true);
> +       mdelay(20);
> +       tda998x_audio_mute(encoder, false);
> +
> +       /* Write the audio information packet */
> +       tda998x_write_aif(encoder, p);
> +}
> +
>  /* DRM encoder functions */
>
>  static void
>  tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
>  {
> +       struct tda998x_priv *priv = to_tda998x_priv(encoder);
> +       struct tda998x_encoder_params *p = params;
> +
> +       priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
> +                           (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
> +                           VIP_CNTRL_0_SWAP_B(p->swap_b) |
> +                           (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
> +       priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
> +                           (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
> +                           VIP_CNTRL_1_SWAP_D(p->swap_d) |
> +                           (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
> +       priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
> +                           (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
> +                           VIP_CNTRL_2_SWAP_F(p->swap_f) |
> +                           (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
> +
> +       priv->params = *p;
> +
> +       if (p->audio_cfg)
> +               tda998x_configure_audio(encoder, p);
>  }
>
>  static void
> @@ -445,7 +683,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>                 /* Write the default value MUX register */
>                 reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
>                 /* enable audio and video ports */
> -               reg_write(encoder, REG_ENA_AP, 0xff);
> +//             reg_write(encoder, REG_ENA_AP, priv->ena_ap);
> +//             reg_write(encoder, REG_ENA_ACLK, priv->ena_aclk);
>                 reg_write(encoder, REG_ENA_VP_0, 0xff);
>                 reg_write(encoder, REG_ENA_VP_1, 0xff);
>                 reg_write(encoder, REG_ENA_VP_2, 0xff);
> @@ -456,7 +695,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>                 break;
>         case DRM_MODE_DPMS_OFF:
>                 /* disable audio and video ports */
> -               reg_write(encoder, REG_ENA_AP, 0x00);
> +//             reg_write(encoder, REG_ENA_AP, 0x00);
>                 reg_write(encoder, REG_ENA_VP_0, 0x00);
>                 reg_write(encoder, REG_ENA_VP_1, 0x00);
>                 reg_write(encoder, REG_ENA_VP_2, 0x00);
> @@ -607,17 +846,31 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
>         reg_write16(encoder, REG_REFPIX_MSB, ref_pix);
>         reg_write16(encoder, REG_REFLINE_MSB, ref_line);
>
> -       reg = TBG_CNTRL_1_VHX_EXT_DE |
> -                       TBG_CNTRL_1_VHX_EXT_HS |
> -                       TBG_CNTRL_1_VHX_EXT_VS |
> -                       TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
> +       reg = TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
>                         TBG_CNTRL_1_VH_TGL_2;
> +       /*
> +        * It is questionable whether this is correct - the nxp driver
> +        * does not set VH_TGL_2 and the below for all display modes.
> +        */
>         if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC))
>                 reg |= TBG_CNTRL_1_VH_TGL_0;
>         reg_set(encoder, REG_TBG_CNTRL_1, reg);
>
>         /* must be last register set: */
>         reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
> +
> +       /* Only setup the info frames if the sink is HDMI */
> +       if (priv->is_hdmi_sink) {
> +               /* We need to turn HDMI HDCP stuff on to get audio through */
> +               reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
> +               reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
> +               reg_set(encoder, REG_TX33, TX33_HDMI);
> +
> +               tda998x_write_avi(encoder, adjusted_mode);
> +
> +               if (priv->params.audio_cfg)
> +                       tda998x_configure_audio(encoder, &priv->params);
> +       }
>  }
>
>  static enum drm_connector_status
> @@ -743,12 +996,14 @@ static int
>  tda998x_encoder_get_modes(struct drm_encoder *encoder,
>                          struct drm_connector *connector)
>  {
> +       struct tda998x_priv *priv = to_tda998x_priv(encoder);
>         struct edid *edid = (struct edid *)do_get_edid(encoder);
>         int n = 0;
>
>         if (edid) {
>                 drm_mode_connector_update_edid_property(connector, edid);
>                 n = drm_add_edid_modes(connector, edid);
> +               priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
>                 kfree(edid);
>         }
>
> @@ -810,6 +1065,40 @@ tda998x_remove(struct i2c_client *client)
>         return 0;
>  }
>
> +static ssize_t i2c_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       struct drm_encoder *encoder = dev_get_drvdata(dev);
> +       unsigned int page, addr;
> +       unsigned char val;
> +
> +       sscanf(buf, "%x %x", &page, &addr);
> +
> +       val = reg_read(encoder, REG(page, addr));
> +
> +       printk("i2c read %02x @ page:%02x address:%02x\n", val, page, addr);
> +       return size;
> +}
> +
> +static ssize_t i2c_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       struct drm_encoder *encoder = dev_get_drvdata(dev);
> +       unsigned int page, addr, mask, val;
> +       unsigned char rval;
> +
> +       sscanf(buf, "%x %x %x %x", &page, &addr, &mask, &val);
> +
> +       rval = reg_read(encoder, REG(page, addr));
> +       rval &= ~mask;
> +       rval |= val & mask;
> +       reg_write(encoder, REG(page, addr), rval);
> +
> +       printk("i2c write %02x @ page:%02x address:%02x\n", rval, page, addr);
> +       return size;
> +}
> +
> +static DEVICE_ATTR(i2c_read, S_IWUSR, NULL, i2c_read_store);
> +static DEVICE_ATTR(i2c_write, S_IWUSR, NULL, i2c_write_store);
> +
>  static int
>  tda998x_encoder_init(struct i2c_client *client,
>                     struct drm_device *dev,
> @@ -817,6 +1106,11 @@ tda998x_encoder_init(struct i2c_client *client,
>  {
>         struct drm_encoder *encoder = &encoder_slave->base;
>         struct tda998x_priv *priv;
> +/* debug */
> +       device_create_file(&client->dev, &dev_attr_i2c_read);
> +       device_create_file(&client->dev, &dev_attr_i2c_write);
> +       dev_set_drvdata(&client->dev, encoder);
> +/* debug end */
>
>         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
>         if (!priv)
> --
> 1.7.4.4
>

^ permalink raw reply	[flat|nested] 91+ messages in thread

* Re: [RFC 6/8] drm/i2c: nxp-tda998x: add video and audio input configuration
@ 2013-05-22 21:08     ` Rob Clark
  0 siblings, 0 replies; 91+ messages in thread
From: Rob Clark @ 2013-05-22 21:08 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Darren Etheridge,
	linux-arm-kernel, Sebastian Hesselbarth

On Thu, May 16, 2013 at 3:27 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/i2c/tda998x_drv.c |  308 ++++++++++++++++++++++++++++++++++++-
>  1 files changed, 301 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index d6afd02..8ffb844 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -23,7 +23,7 @@
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_encoder_slave.h>
>  #include <drm/drm_edid.h>
> -
> +#include <drm/i2c/tda998x.h>
>
>  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
>
> @@ -32,9 +32,11 @@ struct tda998x_priv {
>         uint16_t rev;
>         uint8_t current_page;
>         int dpms;
> +       bool is_hdmi_sink;
>         u8 vip_cntrl_0;
>         u8 vip_cntrl_1;
>         u8 vip_cntrl_2;
> +       struct tda998x_encoder_params params;
>  };
>
>  #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
> @@ -71,10 +73,13 @@ struct tda998x_priv {
>  # define I2C_MASTER_DIS_MM        (1 << 0)
>  # define I2C_MASTER_DIS_FILT      (1 << 1)
>  # define I2C_MASTER_APP_STRT_LAT  (1 << 2)
> +#define REG_FEAT_POWERDOWN        REG(0x00, 0x0e)     /* read/write */
> +# define FEAT_POWERDOWN_SPDIF     (1 << 3)
>  #define REG_INT_FLAGS_0           REG(0x00, 0x0f)     /* read/write */
>  #define REG_INT_FLAGS_1           REG(0x00, 0x10)     /* read/write */
>  #define REG_INT_FLAGS_2           REG(0x00, 0x11)     /* read/write */
>  # define INT_FLAGS_2_EDID_BLK_RD  (1 << 1)
> +#define REG_ENA_ACLK              REG(0x00, 0x16)     /* read/write */
>  #define REG_ENA_VP_0              REG(0x00, 0x18)     /* read/write */
>  #define REG_ENA_VP_1              REG(0x00, 0x19)     /* read/write */
>  #define REG_ENA_VP_2              REG(0x00, 0x1a)     /* read/write */
> @@ -113,6 +118,7 @@ struct tda998x_priv {
>  #define REG_VIP_CNTRL_5           REG(0x00, 0x25)     /* write */
>  # define VIP_CNTRL_5_CKCASE       (1 << 0)
>  # define VIP_CNTRL_5_SP_CNT(x)    (((x) & 3) << 1)
> +#define REG_MUX_AP                REG(0x00, 0x26)     /* read/write */
>  #define REG_MUX_VP_VIP_OUT        REG(0x00, 0x27)     /* read/write */
>  #define REG_MAT_CONTRL            REG(0x00, 0x80)     /* write */
>  # define MAT_CONTRL_MAT_SC(x)     (((x) & 3) << 0)
> @@ -175,6 +181,12 @@ struct tda998x_priv {
>  # define HVF_CNTRL_1_PAD(x)       (((x) & 3) << 4)
>  # define HVF_CNTRL_1_SEMI_PLANAR  (1 << 6)
>  #define REG_RPT_CNTRL             REG(0x00, 0xf0)     /* write */
> +#define REG_I2S_FORMAT            REG(0x00, 0xfc)     /* read/write */
> +# define I2S_FORMAT(x)            (((x) & 3) << 0)
> +#define REG_AIP_CLKSEL            REG(0x00, 0xfd)     /* write */
> +# define AIP_CLKSEL_FS(x)         (((x) & 3) << 0)
> +# define AIP_CLKSEL_CLK_POL(x)    (((x) & 1) << 2)
> +# define AIP_CLKSEL_AIP(x)        (((x) & 7) << 3)
>
>
>  /* Page 02h: PLL settings */
> @@ -216,6 +228,11 @@ struct tda998x_priv {
>
>
>  /* Page 10h: information frames and packets */
> +#define REG_IF1_HB0               REG(0x10, 0x20)     /* read/write */
> +#define REG_IF2_HB0               REG(0x10, 0x40)     /* read/write */
> +#define REG_IF3_HB0               REG(0x10, 0x60)     /* read/write */
> +#define REG_IF4_HB0               REG(0x10, 0x80)     /* read/write */
> +#define REG_IF5_HB0               REG(0x10, 0xa0)     /* read/write */
>
>
>  /* Page 11h: audio settings and content info packets */
> @@ -225,10 +242,33 @@ struct tda998x_priv {
>  # define AIP_CNTRL_0_LAYOUT       (1 << 2)
>  # define AIP_CNTRL_0_ACR_MAN      (1 << 5)
>  # define AIP_CNTRL_0_RST_CTS      (1 << 6)
> +#define REG_CA_I2S                REG(0x11, 0x01)     /* read/write */
> +# define CA_I2S_CA_I2S(x)         (((x) & 31) << 0)
> +# define CA_I2S_HBR_CHSTAT        (1 << 6)
> +#define REG_LATENCY_RD            REG(0x11, 0x04)     /* read/write */
> +#define REG_ACR_CTS_0             REG(0x11, 0x05)     /* read/write */
> +#define REG_ACR_CTS_1             REG(0x11, 0x06)     /* read/write */
> +#define REG_ACR_CTS_2             REG(0x11, 0x07)     /* read/write */
> +#define REG_ACR_N_0               REG(0x11, 0x08)     /* read/write */
> +#define REG_ACR_N_1               REG(0x11, 0x09)     /* read/write */
> +#define REG_ACR_N_2               REG(0x11, 0x0a)     /* read/write */
> +#define REG_CTS_N                 REG(0x11, 0x0c)     /* read/write */
> +# define CTS_N_K(x)               (((x) & 7) << 0)
> +# define CTS_N_M(x)               (((x) & 3) << 4)
>  #define REG_ENC_CNTRL             REG(0x11, 0x0d)     /* read/write */
>  # define ENC_CNTRL_RST_ENC        (1 << 0)
>  # define ENC_CNTRL_RST_SEL        (1 << 1)
>  # define ENC_CNTRL_CTL_CODE(x)    (((x) & 3) << 2)
> +#define REG_DIP_FLAGS             REG(0x11, 0x0e)     /* read/write */
> +# define DIP_FLAGS_ACR            (1 << 0)
> +# define DIP_FLAGS_GC             (1 << 1)
> +#define REG_DIP_IF_FLAGS          REG(0x11, 0x0f)     /* read/write */
> +# define DIP_IF_FLAGS_IF1         (1 << 1)
> +# define DIP_IF_FLAGS_IF2         (1 << 2)
> +# define DIP_IF_FLAGS_IF3         (1 << 3)
> +# define DIP_IF_FLAGS_IF4         (1 << 4)
> +# define DIP_IF_FLAGS_IF5         (1 << 5)
> +#define REG_CH_STAT_B(x)          REG(0x11, 0x14 + (x)) /* read/write */
>
>
>  /* Page 12h: HDCP and OTP */
> @@ -344,6 +384,23 @@ reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt)
>         return ret;
>  }
>
> +static void
> +reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt)
> +{
> +       struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
> +       uint8_t buf[cnt+1];
> +       int ret;
> +
> +       buf[0] = REG2ADDR(reg);
> +       memcpy(&buf[1], p, cnt);
> +
> +       set_page(encoder, reg);
> +
> +       ret = i2c_master_send(client, buf, cnt + 1);
> +       if (ret < 0)
> +               dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
> +}
> +
>  static uint8_t
>  reg_read(struct drm_encoder *encoder, uint16_t reg)
>  {
> @@ -421,11 +478,192 @@ tda998x_reset(struct drm_encoder *encoder)
>         reg_write(encoder, REG_PLL_SCG2,     0x10);
>  }
>
> +static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
> +{
> +       uint8_t sum = 0;
> +
> +       while (bytes--)
> +               sum += *buf++;
> +       return (255 - sum) + 1;
> +}
> +
> +#define HB(x) (x)
> +#define PB(x) (HB(2) + 1 + (x))
> +
> +static void
> +tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr,
> +                uint8_t *buf, size_t size)
> +{
> +       buf[PB(0)] = tda998x_cksum(buf, size);
> +
> +       reg_clear(encoder, REG_DIP_IF_FLAGS, bit);
> +       reg_write_range(encoder, addr, buf, size);
> +       reg_set(encoder, REG_DIP_IF_FLAGS, bit);
> +}
> +
> +static void
> +tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
> +{
> +       uint8_t buf[PB(5) + 1];
> +
> +       buf[HB(0)] = 0x84;
> +       buf[HB(1)] = 0x01;
> +       buf[HB(2)] = 10;
> +       buf[PB(0)] = 0;
> +       buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
> +       buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
> +       buf[PB(4)] = p->audio_frame[4];
> +       buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
> +
> +       tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
> +                        sizeof(buf));
> +}
> +
> +static void
> +tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
> +{
> +       uint8_t buf[PB(13) + 1];
> +
> +       memset(buf, 0, sizeof(buf));
> +       buf[HB(0)] = 0x82;
> +       buf[HB(1)] = 0x02;
> +       buf[HB(2)] = 13;
> +       buf[PB(4)] = drm_match_cea_mode(mode);
> +
> +       tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
> +                        sizeof(buf));
> +}

fwiw, I expect you could use the hdmi infoframe helpers for some of
this (drivers/video/hdmi.c).. it is a relatively recent addition, in
case you hadn't seen them already

BR,
-R

> +static void tda998x_audio_mute(struct drm_encoder *encoder, bool on)
> +{
> +       if (on) {
> +               reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
> +               reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
> +               reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
> +       } else {
> +               reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
> +       }
> +}
> +
> +static void
> +tda998x_configure_audio(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
> +{
> +       uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n;
> +       uint32_t n;
> +
> +       /* SetAudioPortConfig */
> +       reg_write(encoder, REG_ENA_AP, p->audio_cfg);
> +       /* SetAudioClockPortConfig */
> +       reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg);
> +
> +       /*
> +        * layout = channelAllocation ? 1 : 0;
> +        * AudioInSetConfig(format, i2sFormat, channelAllocation,
> +        *   HDMITX_CHAN_NO_CHANGE, HDMITX_CLKPOLDSD_NO_CHANGE,
> +        *   HDMITX_SWAPDSD_NO_CHANGE, layout, HDMITX_LATENCY_CURRENT,
> +        *   dstRate)
> +        */
> +       switch (p->audio_format) {
> +       case AFMT_SPDIF:
> +               reg_write(encoder, REG_MUX_AP, 0x40);
> +               clksel_aip = AIP_CLKSEL_AIP(0);
> +               /* FS64SPDIF */
> +               clksel_fs = AIP_CLKSEL_FS(2);
> +               cts_n = CTS_N_M(3) | CTS_N_K(3);
> +               ca_i2s = 0;
> +               break;
> +
> +       case AFMT_I2S:
> +               reg_write(encoder, REG_MUX_AP, 0x64);
> +               clksel_aip = AIP_CLKSEL_AIP(1);
> +               /* ACLK */
> +               clksel_fs = AIP_CLKSEL_FS(0);
> +               cts_n = CTS_N_M(3) | CTS_N_K(3);
> +               ca_i2s = CA_I2S_CA_I2S(0 /* channel allocation */);
> +               break;
> +       }
> +
> +       reg_write(encoder, REG_AIP_CLKSEL, clksel_aip);
> +//     reg_write(encoder, REG_CA_I2S, ca_i2s);
> +//     reg_write(encoder, REG_I2S_FORMAT, I2S_FORMAT(0 /* i2s format */));
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT);
> +       /* latency? */
> +
> +       /* get video format */
> +
> +       /*
> +        * ctsRef = HDMITX_CTSREF_FS64SPDIF, uCtsX = HDMITX_CTSX_64
> +        * AudioInSetCts(ctsRef, rate, VidFmt, vOutFreq,
> +        *   HDMITX_CTS_AUTO, uCtsX, HDMITX_CTSK_USE_CTSX,
> +        *   HDMITX_CTSMTS_USE_CTSX, dstRate)
> +        */
> +       /* Auto CTS */
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN);
> +       reg_write(encoder, REG_CTS_N, cts_n);
> +       reg_write(encoder, REG_AUDIO_DIV, 3);
> +
> +       /*
> +        * This is the approximate value of N, which happens to be
> +        * the recommended values for non-coherent clocks.
> +        */
> +       n = 128 * p->audio_sample_rate / 1000;
> +
> +       /* Write the CTS and N values */
> +       buf[0] = 0x44;
> +       buf[1] = 0x42;
> +       buf[2] = 0x01;
> +       buf[3] = n;
> +       buf[4] = n >> 8;
> +       buf[5] = n >> 16;
> +       reg_write_range(encoder, REG_ACR_CTS_0, buf, 6);
> +
> +       /* Set CTS clock reference */
> +       reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
> +
> +       /* Reset CTS generator */
> +       reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
> +       reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
> +
> +       /* Write the channel status */
> +       buf[0] = 0x04;
> +       buf[1] = 0x00;
> +       buf[2] = 0x00;
> +       buf[3] = 0xf1;
> +       reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4);
> +
> +       tda998x_audio_mute(encoder, true);
> +       mdelay(20);
> +       tda998x_audio_mute(encoder, false);
> +
> +       /* Write the audio information packet */
> +       tda998x_write_aif(encoder, p);
> +}
> +
>  /* DRM encoder functions */
>
>  static void
>  tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
>  {
> +       struct tda998x_priv *priv = to_tda998x_priv(encoder);
> +       struct tda998x_encoder_params *p = params;
> +
> +       priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
> +                           (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
> +                           VIP_CNTRL_0_SWAP_B(p->swap_b) |
> +                           (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
> +       priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
> +                           (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
> +                           VIP_CNTRL_1_SWAP_D(p->swap_d) |
> +                           (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
> +       priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
> +                           (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
> +                           VIP_CNTRL_2_SWAP_F(p->swap_f) |
> +                           (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
> +
> +       priv->params = *p;
> +
> +       if (p->audio_cfg)
> +               tda998x_configure_audio(encoder, p);
>  }
>
>  static void
> @@ -445,7 +683,8 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>                 /* Write the default value MUX register */
>                 reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
>                 /* enable audio and video ports */
> -               reg_write(encoder, REG_ENA_AP, 0xff);
> +//             reg_write(encoder, REG_ENA_AP, priv->ena_ap);
> +//             reg_write(encoder, REG_ENA_ACLK, priv->ena_aclk);
>                 reg_write(encoder, REG_ENA_VP_0, 0xff);
>                 reg_write(encoder, REG_ENA_VP_1, 0xff);
>                 reg_write(encoder, REG_ENA_VP_2, 0xff);
> @@ -456,7 +695,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
>                 break;
>         case DRM_MODE_DPMS_OFF:
>                 /* disable audio and video ports */
> -               reg_write(encoder, REG_ENA_AP, 0x00);
> +//             reg_write(encoder, REG_ENA_AP, 0x00);
>                 reg_write(encoder, REG_ENA_VP_0, 0x00);
>                 reg_write(encoder, REG_ENA_VP_1, 0x00);
>                 reg_write(encoder, REG_ENA_VP_2, 0x00);
> @@ -607,17 +846,31 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
>         reg_write16(encoder, REG_REFPIX_MSB, ref_pix);
>         reg_write16(encoder, REG_REFLINE_MSB, ref_line);
>
> -       reg = TBG_CNTRL_1_VHX_EXT_DE |
> -                       TBG_CNTRL_1_VHX_EXT_HS |
> -                       TBG_CNTRL_1_VHX_EXT_VS |
> -                       TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
> +       reg = TBG_CNTRL_1_DWIN_DIS | /* HDCP off */
>                         TBG_CNTRL_1_VH_TGL_2;
> +       /*
> +        * It is questionable whether this is correct - the nxp driver
> +        * does not set VH_TGL_2 and the below for all display modes.
> +        */
>         if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC))
>                 reg |= TBG_CNTRL_1_VH_TGL_0;
>         reg_set(encoder, REG_TBG_CNTRL_1, reg);
>
>         /* must be last register set: */
>         reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
> +
> +       /* Only setup the info frames if the sink is HDMI */
> +       if (priv->is_hdmi_sink) {
> +               /* We need to turn HDMI HDCP stuff on to get audio through */
> +               reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
> +               reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
> +               reg_set(encoder, REG_TX33, TX33_HDMI);
> +
> +               tda998x_write_avi(encoder, adjusted_mode);
> +
> +               if (priv->params.audio_cfg)
> +                       tda998x_configure_audio(encoder, &priv->params);
> +       }
>  }
>
>  static enum drm_connector_status
> @@ -743,12 +996,14 @@ static int
>  tda998x_encoder_get_modes(struct drm_encoder *encoder,
>                          struct drm_connector *connector)
>  {
> +       struct tda998x_priv *priv = to_tda998x_priv(encoder);
>         struct edid *edid = (struct edid *)do_get_edid(encoder);
>         int n = 0;
>
>         if (edid) {
>                 drm_mode_connector_update_edid_property(connector, edid);
>                 n = drm_add_edid_modes(connector, edid);
> +               priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
>                 kfree(edid);
>         }
>
> @@ -810,6 +1065,40 @@ tda998x_remove(struct i2c_client *client)
>         return 0;
>  }
>
> +static ssize_t i2c_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       struct drm_encoder *encoder = dev_get_drvdata(dev);
> +       unsigned int page, addr;
> +       unsigned char val;
> +
> +       sscanf(buf, "%x %x", &page, &addr);
> +
> +       val = reg_read(encoder, REG(page, addr));
> +
> +       printk("i2c read %02x @ page:%02x address:%02x\n", val, page, addr);
> +       return size;
> +}
> +
> +static ssize_t i2c_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
> +{
> +       struct drm_encoder *encoder = dev_get_drvdata(dev);
> +       unsigned int page, addr, mask, val;
> +       unsigned char rval;
> +
> +       sscanf(buf, "%x %x %x %x", &page, &addr, &mask, &val);
> +
> +       rval = reg_read(encoder, REG(page, addr));
> +       rval &= ~mask;
> +       rval |= val & mask;
> +       reg_write(encoder, REG(page, addr), rval);
> +
> +       printk("i2c write %02x @ page:%02x address:%02x\n", rval, page, addr);
> +       return size;
> +}
> +
> +static DEVICE_ATTR(i2c_read, S_IWUSR, NULL, i2c_read_store);
> +static DEVICE_ATTR(i2c_write, S_IWUSR, NULL, i2c_write_store);
> +
>  static int
>  tda998x_encoder_init(struct i2c_client *client,
>                     struct drm_device *dev,
> @@ -817,6 +1106,11 @@ tda998x_encoder_init(struct i2c_client *client,
>  {
>         struct drm_encoder *encoder = &encoder_slave->base;
>         struct tda998x_priv *priv;
> +/* debug */
> +       device_create_file(&client->dev, &dev_attr_i2c_read);
> +       device_create_file(&client->dev, &dev_attr_i2c_write);
> +       dev_set_drvdata(&client->dev, encoder);
> +/* debug end */
>
>         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
>         if (!priv)
> --
> 1.7.4.4
>

^ permalink raw reply	[flat|nested] 91+ messages in thread

end of thread, other threads:[~2013-05-22 21:08 UTC | newest]

Thread overview: 91+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-16 19:25 [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver Russell King - ARM Linux
2013-05-16 19:25 ` Russell King - ARM Linux
2013-05-16 19:25 ` [RFC 1/8] DRM: Add Dove DRM driver Russell King
2013-05-16 19:25   ` Russell King
2013-05-16 19:25 ` [RFC 2/8] drm/i2c: nxp-tda998x: fix EDID reading on TDA19988 devices Russell King
2013-05-16 19:25   ` Russell King
2013-05-16 19:26 ` [RFC 3/8] drm/i2c: nxp-tda998x: ensure VIP output mux is properly set Russell King
2013-05-16 19:26   ` Russell King
2013-05-18  6:56   ` Jean-Francois Moine
2013-05-18  6:56     ` Jean-Francois Moine
2013-05-19 10:30     ` Russell King - ARM Linux
2013-05-19 10:30       ` Russell King - ARM Linux
2013-05-16 19:26 ` [RFC 4/8] drm/i2c: nxp-tda998x: fix npix/nline programming Russell King
2013-05-16 19:26   ` Russell King
2013-05-16 19:26 ` [RFC 5/8] drm/i2c: nxp-tda998x: prepare for video input configuration Russell King
2013-05-16 19:26   ` Russell King
2013-05-16 19:27 ` [RFC 6/8] drm/i2c: nxp-tda998x: add video and audio " Russell King
2013-05-16 19:27   ` Russell King
2013-05-22 21:08   ` Rob Clark
2013-05-22 21:08     ` Rob Clark
2013-05-16 19:27 ` [RFC 7/8] DRM: Dove: add support for drm tda19988 driver Russell King
2013-05-16 19:27   ` Russell King
2013-05-16 19:27 ` [RFC 8/8] DRM: dove: provide a couple of generic slave encoder helpers Russell King
2013-05-16 19:27   ` Russell King
2013-05-17 11:33 ` [RFC 0/8] rmk's Dove DRM/TDA19988 Cubox driver Jean-Francois Moine
2013-05-17 11:33   ` Jean-Francois Moine
2013-05-17 11:58   ` Sebastian Hesselbarth
2013-05-17 11:58     ` Sebastian Hesselbarth
2013-05-17 12:01   ` Russell King - ARM Linux
2013-05-17 12:01     ` Russell King - ARM Linux
2013-05-17 17:40     ` Jean-Francois Moine
2013-05-17 17:40       ` Jean-Francois Moine
2013-05-17 18:00       ` Russell King - ARM Linux
2013-05-17 18:00         ` Russell King - ARM Linux
2013-05-17 18:05         ` Russell King - ARM Linux
2013-05-17 18:05           ` Russell King - ARM Linux
2013-05-17 18:57         ` Jean-Francois Moine
2013-05-17 18:57           ` Jean-Francois Moine
2013-05-19  8:59       ` Russell King - ARM Linux
2013-05-19  8:59         ` Russell King - ARM Linux
2013-05-20 13:36         ` Alex Deucher
2013-05-20 13:36           ` Alex Deucher
2013-05-20 20:15           ` Russell King - ARM Linux
2013-05-20 20:15             ` Russell King - ARM Linux
2013-05-20 20:23             ` Alex Deucher
2013-05-20 20:23               ` Alex Deucher
2013-05-21  6:30               ` Jean-Francois Moine
2013-05-21  6:30                 ` Jean-Francois Moine
2013-05-19 11:25   ` Russell King - ARM Linux
2013-05-19 11:25     ` Russell King - ARM Linux
2013-05-18 17:12 ` [RFC 0/4] Add DT support to rmk's Dove DRM driver Sebastian Hesselbarth
2013-05-18 17:12   ` [RFC 1/4] ARM: dove: add lcd controller DT nodes Sebastian Hesselbarth
2013-05-18 17:12   ` [RFC 2/4] ARM: dove: add video card node for SolidRun CuBox Sebastian Hesselbarth
2013-05-18 17:33     ` Jean-Francois Moine
2013-05-18 17:33       ` Jean-Francois Moine
2013-05-18 18:33       ` Sebastian Hesselbarth
2013-05-18 18:33         ` Sebastian Hesselbarth
2013-05-18 17:12   ` [RFC 3/4] DRM: add OF support for Dove DRM driver Sebastian Hesselbarth
2013-05-18 17:45     ` Jean-Francois Moine
2013-05-18 17:45       ` Jean-Francois Moine
2013-05-18 18:20       ` Sebastian Hesselbarth
2013-05-18 18:20         ` Sebastian Hesselbarth
2013-05-18 19:18         ` Jean-Francois Moine
2013-05-18 19:18           ` Jean-Francois Moine
2013-05-20 10:16           ` Russell King - ARM Linux
2013-05-20 10:16             ` Russell King - ARM Linux
2013-05-18 20:46       ` Russell King - ARM Linux
2013-05-18 20:46         ` Russell King - ARM Linux
2013-05-18 17:12   ` [RFC 4/4] DRM: tda998x: add missing include Sebastian Hesselbarth
2013-05-18 17:46     ` Jean-Francois Moine
2013-05-18 17:46       ` Jean-Francois Moine
2013-05-18 18:21       ` Sebastian Hesselbarth
2013-05-18 18:21         ` Sebastian Hesselbarth
2013-05-18 18:23       ` Rob Clark
2013-05-18 18:23         ` Rob Clark
2013-05-18 18:58         ` Jean-Francois Moine
2013-05-18 18:58           ` Jean-Francois Moine
2013-05-18 19:11           ` Rob Clark
2013-05-18 19:11             ` Rob Clark
2013-05-18 19:30           ` Sebastian Hesselbarth
2013-05-18 19:30             ` Sebastian Hesselbarth
2013-05-18 20:26             ` Russell King - ARM Linux
2013-05-18 20:26               ` Russell King - ARM Linux
2013-05-18 20:50               ` Sebastian Hesselbarth
2013-05-18 20:50                 ` Sebastian Hesselbarth
2013-05-19  6:01             ` Jean-Francois Moine
2013-05-19  6:01               ` Jean-Francois Moine
2013-05-19  8:30               ` Sebastian Hesselbarth
2013-05-19  8:30                 ` Sebastian Hesselbarth
2013-05-19 16:49                 ` Jean-Francois Moine
2013-05-19 16:49                   ` Jean-Francois Moine

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.