All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Armada DRM stuff
@ 2013-10-06 22:07 ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-06 22:07 UTC (permalink / raw)
  To: linux-arm-kernel

The Armada DRM drivers again, along with the TDA998x HDMI output support,
and an illustration to show how to add Armada 610 support (and others.)

Rob has looked at this a couple of times since its last posting, and
has provided additional useful feedback which has been incorporated.
I believe all the major issues have been addressed now.

 drivers/gpu/drm/Kconfig                 |    2 +
 drivers/gpu/drm/Makefile                |    1 +
 drivers/gpu/drm/armada/Kconfig          |   24 +
 drivers/gpu/drm/armada/Makefile         |    8 +
 drivers/gpu/drm/armada/armada_510.c     |   87 +++
 drivers/gpu/drm/armada/armada_610.c     |   49 ++
 drivers/gpu/drm/armada/armada_crtc.c    | 1099 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   83 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++
 drivers/gpu/drm/armada/armada_drm.h     |  114 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  428 ++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 +++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++
 drivers/gpu/drm/armada/armada_gem.c     |  616 +++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  326 +++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 +++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 ++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 ++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 drivers/gpu/drm/i2c/tda998x_drv.c       |    2 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 26 files changed, 4389 insertions(+), 0 deletions(-)

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

* [PATCH 0/5] Armada DRM stuff
@ 2013-10-06 22:07 ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-06 22:07 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter,
	Sebastian Hesselbarth

The Armada DRM drivers again, along with the TDA998x HDMI output support,
and an illustration to show how to add Armada 610 support (and others.)

Rob has looked at this a couple of times since its last posting, and
has provided additional useful feedback which has been incorporated.
I believe all the major issues have been addressed now.

 drivers/gpu/drm/Kconfig                 |    2 +
 drivers/gpu/drm/Makefile                |    1 +
 drivers/gpu/drm/armada/Kconfig          |   24 +
 drivers/gpu/drm/armada/Makefile         |    8 +
 drivers/gpu/drm/armada/armada_510.c     |   87 +++
 drivers/gpu/drm/armada/armada_610.c     |   49 ++
 drivers/gpu/drm/armada/armada_crtc.c    | 1099 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   83 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++
 drivers/gpu/drm/armada/armada_drm.h     |  114 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  428 ++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 +++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++
 drivers/gpu/drm/armada/armada_gem.c     |  616 +++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  326 +++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 +++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 ++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 ++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 drivers/gpu/drm/i2c/tda998x_drv.c       |    2 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 26 files changed, 4389 insertions(+), 0 deletions(-)

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

* [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-06 22:07   ` Russell King
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:07 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 |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 60e8404..704040b5 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
 	buf[HB(0)] = 0x82;
 	buf[HB(1)] = 0x02;
 	buf[HB(2)] = 13;
+	buf[PB(1)] = 2; /* underscanned display */
+	buf[PB(3)] = 2 << 2; /* full range */
 	buf[PB(4)] = drm_match_cea_mode(mode);
 
 	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
-- 
1.7.4.4

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

* [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
@ 2013-10-06 22:07   ` Russell King
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:07 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter,
	Sebastian Hesselbarth

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

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 60e8404..704040b5 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
 	buf[HB(0)] = 0x82;
 	buf[HB(1)] = 0x02;
 	buf[HB(2)] = 13;
+	buf[PB(1)] = 2; /* underscanned display */
+	buf[PB(3)] = 2 << 2; /* full range */
 	buf[PB(4)] = drm_match_cea_mode(mode);
 
 	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
-- 
1.7.4.4

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-06 22:08   ` Russell King
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:08 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the pair of LCD controllers on the Marvell
Armada 510 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 via DRM planes
- page flipping of the main scanout buffers
- DRM prime for buffer export/import

This driver is trivial to extend to other Armada SoCs.

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/armada/Kconfig          |   15 +
 drivers/gpu/drm/armada/Makefile         |    7 +
 drivers/gpu/drm/armada/armada_510.c     |   86 +++
 drivers/gpu/drm/armada/armada_crtc.c    |  861 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   74 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++++
 drivers/gpu/drm/armada/armada_drm.h     |  112 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  380 ++++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 ++++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++++
 drivers/gpu/drm/armada/armada_gem.c     |  616 ++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  316 +++++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 ++++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 +++++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 +++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 24 files changed, 4020 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/Kconfig
 create mode 100644 drivers/gpu/drm/armada/Makefile
 create mode 100644 drivers/gpu/drm/armada/armada_510.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
 create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
 create mode 100644 drivers/gpu/drm/armada/armada_drm.h
 create mode 100644 drivers/gpu/drm/armada/armada_drv.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.h
 create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.h
 create mode 100644 drivers/gpu/drm/armada/armada_hw.h
 create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
 create mode 100644 drivers/gpu/drm/armada/armada_output.c
 create mode 100644 drivers/gpu/drm/armada/armada_output.h
 create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.h
 create mode 100644 include/uapi/drm/armada_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d..3b5176d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -225,6 +225,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/armada/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adf..385f460 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_ARMADA) += armada/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
new file mode 100644
index 0000000..c7a0a94
--- /dev/null
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -0,0 +1,15 @@
+config DRM_ARMADA
+	tristate "DRM support for Marvell Armada SoCs"
+	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
+	  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.
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
new file mode 100644
index 0000000..d6f43e0
--- /dev/null
+++ b/drivers/gpu/drm/armada/Makefile
@@ -0,0 +1,7 @@
+armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
+		   armada_gem.o armada_output.o armada_overlay.o \
+		   armada_slave.o
+armada-y	+= armada_510.o
+armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
+
+obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
new file mode 100644
index 0000000..a016888
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ *
+ * Armada 510 (aka Dove) variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int armada510_init(struct armada_private *priv, struct device *dev)
+{
+	priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+
+	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
+		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+	return PTR_RET(priv->extclk[0]);
+}
+
+static int armada510_crtc_init(struct armada_crtc *dcrtc)
+{
+	/* Lower the watermark so to eliminate jitter at higher bandwidths */
+	armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+	return 0;
+}
+
+/*
+ * Armada510 specific SCLK register selection.
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ *
+ * We currently are pretty rudimentary here, always selecting
+ * EXT_REF_CLK_1 for LCD0 and erroring LCD1.  This needs improvement!
+ */
+static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
+	const struct drm_display_mode *mode, uint32_t *sclk)
+{
+	struct armada_private *priv = dcrtc->crtc.dev->dev_private;
+	struct clk *clk = priv->extclk[0];
+	int ret;
+
+	if (dcrtc->num == 1)
+		return -EINVAL;
+
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	if (dcrtc->clk != clk) {
+		ret = clk_prepare_enable(clk);
+		if (ret)
+			return ret;
+		dcrtc->clk = clk;
+	}
+
+	if (sclk) {
+		uint32_t rate, ref, div;
+
+		rate = mode->clock * 1000;
+		ref = clk_round_rate(clk, rate);
+		div = DIV_ROUND_UP(ref, rate);
+		if (div < 1)
+			div = 1;
+
+		clk_set_rate(clk, ref);
+		*sclk = div | SCLK_510_EXTCLK1;
+	}
+
+	return 0;
+}
+
+const struct armada_variant armada510_ops = {
+	.has_spu_adv_reg = true,
+	.init = armada510_init,
+	.crtc_init = armada510_crtc_init,
+	.crtc_compute_clock = armada510_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
new file mode 100644
index 0000000..7b379fd
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -0,0 +1,861 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+struct armada_frame_work {
+	struct drm_pending_vblank_event *event;
+	struct armada_regs regs[4];
+	struct drm_framebuffer *old_fb;
+};
+
+enum csc_mode {
+	CSC_AUTO = 0,
+	CSC_YUV_CCIR601 = 1,
+	CSC_YUV_CCIR709 = 2,
+	CSC_RGB_COMPUTER = 1,
+	CSC_RGB_STUDIO = 2,
+};
+
+/*
+ * 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 Armada 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.)
+ */
+
+void
+armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_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 armada_drm_crtc_update(struct armada_crtc *dcrtc)
+{
+	uint32_t dumb_ctrl;
+
+	dumb_ctrl = dcrtc->cfg_dumb_ctrl;
+
+	if (!dpms_blanked(dcrtc->dpms))
+		dumb_ctrl |= CFG_DUMB_ENA;
+
+	/*
+	 * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might
+	 * be using SPI or GPIO.  If we set this to DUMB_BLANK, we will
+	 * force LCD_D[23:0] to output blank color, overriding the GPIO or
+	 * SPI usage.  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 documentation doesn't indicate what the normal state of
+	 * the sync signals are.  Sebastian Hesselbart kindly probed
+	 * these signals on his board to determine their state.
+	 *
+	 * The non-inverted state of the sync signals is active high.
+	 * Setting these bits makes the appropriate signal active low.
+	 */
+	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 armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
+	int x, int y, struct armada_regs *regs, bool interlaced)
+{
+	struct armada_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 */
+	armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
+	armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
+	armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
+
+	return i;
+}
+
+static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
+	struct armada_frame_work *work)
+{
+	struct drm_device *dev = dcrtc->crtc.dev;
+	unsigned long flags;
+	int ret;
+
+	ret = drm_vblank_get(dev, dcrtc->num);
+	if (ret) {
+		DRM_ERROR("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (!dcrtc->frame_work)
+		dcrtc->frame_work = work;
+	else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	if (ret)
+		drm_vblank_put(dev, dcrtc->num);
+
+	return ret;
+}
+
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
+{
+	struct drm_device *dev = dcrtc->crtc.dev;
+	struct armada_frame_work *work = dcrtc->frame_work;
+
+	dcrtc->frame_work = NULL;
+
+	armada_drm_crtc_update_regs(dcrtc, work->regs);
+
+	if (work->event)
+		drm_send_vblank_event(dev, dcrtc->num, work->event);
+
+	drm_vblank_put(dev, dcrtc->num);
+
+	/* Finally, queue the process-half of the cleanup. */
+	__armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
+	kfree(work);
+}
+
+static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
+	struct drm_framebuffer *fb, bool force)
+{
+	struct armada_frame_work *work;
+
+	if (!fb)
+		return;
+
+	if (force) {
+		/* Display is disabled, so just drop the old fb */
+		drm_framebuffer_unreference(fb);
+		return;
+	}
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (work) {
+		int i = 0;
+		work->event = NULL;
+		work->old_fb = fb;
+		armada_reg_queue_end(work->regs, i);
+
+		if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0)
+			return;
+
+		kfree(work);
+	}
+
+	/*
+	 * Oops - just drop the reference immediately and hope for
+	 * the best.  The worst that will happen is the buffer gets
+	 * reused before it has finished being displayed.
+	 */
+	drm_framebuffer_unreference(fb);
+}
+
+static void armada_drm_vblank_off(struct armada_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->frame_work)
+		armada_drm_crtc_complete_frame_work(dcrtc);
+	spin_unlock_irq(&dev->event_lock);
+}
+
+void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
+	int idx)
+{
+}
+
+void armada_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 armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	if (dcrtc->dpms != dpms) {
+		dcrtc->dpms = dpms;
+		armada_drm_crtc_update(dcrtc);
+		if (dpms_blanked(dpms))
+			armada_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 armada_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct drm_plane *plane;
+
+	/*
+	 * If we have an overlay plane associated with this CRTC, disable
+	 * it before the modeset to avoid its coordinates being outside
+	 * the new mode parameters.  DRM doesn't provide help with this.
+	 */
+	plane = dcrtc->plane;
+	if (plane) {
+		struct drm_framebuffer *fb = plane->fb;
+
+		plane->funcs->disable_plane(plane);
+		plane->fb = NULL;
+		plane->crtc = NULL;
+		drm_framebuffer_unreference(fb);
+	}
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	if (dcrtc->dpms != DRM_MODE_DPMS_ON) {
+		dcrtc->dpms = DRM_MODE_DPMS_ON;
+		armada_drm_crtc_update(dcrtc);
+	}
+}
+
+/* The mode_config.mutex will be held for this call */
+static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+	const struct drm_display_mode *mode, struct drm_display_mode *adj)
+{
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	int ret;
+
+	/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
+	if (!priv->variant->has_spu_adv_reg &&
+	    adj->flags & DRM_MODE_FLAG_INTERLACE)
+		return false;
+
+	/* Check whether the display mode is possible */
+	ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
+	if (ret)
+		return false;
+
+	return true;
+}
+
+void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+{
+	struct armada_vbl_event *e, *n;
+	void __iomem *base = dcrtc->base;
+
+	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);
+
+	if (stat & VSYNC_IRQ)
+		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) {
+		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 &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
+		val |= dcrtc->v[i].spu_adv_reg;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
+	}
+	spin_unlock(&dcrtc->irq_lock);
+
+	if (stat & GRA_FRAME_IRQ) {
+		struct drm_device *dev = dcrtc->crtc.dev;
+
+		spin_lock(&dev->event_lock);
+		if (dcrtc->frame_work)
+			armada_drm_crtc_complete_frame_work(dcrtc);
+		spin_unlock(&dev->event_lock);
+
+		wake_up(&dcrtc->frame_wait);
+	}
+}
+
+/* These are locked by dev->vbl_lock */
+void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
+{
+	if (dcrtc->irq_ena & mask) {
+		dcrtc->irq_ena &= ~mask;
+		writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+	}
+}
+
+void armada_drm_crtc_enable_irq(struct armada_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);
+	}
+}
+
+static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
+{
+	struct drm_display_mode *adj = &dcrtc->crtc.mode;
+	uint32_t val = 0;
+
+	if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709)
+		val |= CFG_CSC_YUV_CCIR709;
+	if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO)
+		val |= CFG_CSC_RGB_STUDIO;
+
+	/*
+	 * In auto mode, set the colorimetry, based upon the HDMI spec.
+	 * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use
+	 * ITU601.  It may be more appropriate to set this depending on
+	 * the source - but what if the graphic frame is YUV and the
+	 * video frame is RGB?
+	 */
+	if ((adj->hdisplay == 1280 && adj->vdisplay == 720 &&
+	     !(adj->flags & DRM_MODE_FLAG_INTERLACE)) ||
+	    (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
+		if (dcrtc->csc_yuv_mode == CSC_AUTO)
+			val |= CFG_CSC_YUV_CCIR709;
+	}
+
+	/*
+	 * We assume we're connected to a TV-like device, so the YUV->RGB
+	 * conversion should produce a limited range.  We should set this
+	 * depending on the connectors attached to this CRTC, and what
+	 * kind of device they report being connected.
+	 */
+	if (dcrtc->csc_rgb_mode == CSC_AUTO)
+		val |= CFG_CSC_RGB_STUDIO;
+
+	return val;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_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 armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_regs regs[17];
+	uint32_t lm, rm, tm, bm, val, sclk;
+	unsigned long flags;
+	unsigned i;
+	bool interlaced;
+
+	drm_framebuffer_reference(crtc->fb);
+
+	interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
+
+	i = armada_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->frame_work);
+
+	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);
+	}
+
+	/* Now compute the divider for real */
+	priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+
+	/* Ensure graphic fifo is enabled */
+	armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
+	armada_reg_queue_set(regs, i, 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 | ADV_VSYNCOFFEN;
+
+	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 | ADV_VSYNCOFFEN;
+	} else {
+		dcrtc->v[0] = dcrtc->v[1];
+	}
+
+	val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay;
+
+	armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
+	armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
+	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
+	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
+			   LCD_SPUT_V_H_TOTAL);
+
+	if (priv->variant->has_spu_adv_reg)
+		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
+				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
+				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
+
+	val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
+	val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
+	val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
+
+	if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
+		val |= CFG_PALETTE_ENA;
+
+	if (interlaced)
+		val |= CFG_GRA_FTOGGLE;
+
+	armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT |
+			     CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
+					 CFG_SWAPYU | CFG_YUV2RGB) |
+			     CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
+			     LCD_SPU_DMA_CTRL0);
+
+	val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
+	armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
+
+	val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL);
+	armada_reg_queue_end(regs, i);
+
+	armada_drm_crtc_update_regs(dcrtc, regs);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+
+	armada_drm_crtc_update(dcrtc);
+
+	drm_vblank_post_modeset(crtc->dev, dcrtc->num);
+	armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+	return 0;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+	struct drm_framebuffer *old_fb)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_regs regs[4];
+	unsigned i;
+
+	i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
+				    dcrtc->interlaced);
+	armada_reg_queue_end(regs, i);
+
+	/* Wait for pending flips to complete */
+	wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+
+	/* Take a reference to the new fb as we're using it */
+	drm_framebuffer_reference(crtc->fb);
+
+	/* Update the base in the CRTC */
+	armada_drm_crtc_update_regs(dcrtc, regs);
+
+	/* Drop our previously held reference */
+	armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+	return 0;
+}
+
+static void armada_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+	armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
+
+	/* Power down most RAMs and FIFOs */
+	writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+		       CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+		       CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+}
+
+static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
+	.dpms		= armada_drm_crtc_dpms,
+	.prepare	= armada_drm_crtc_prepare,
+	.commit		= armada_drm_crtc_commit,
+	.mode_fixup	= armada_drm_crtc_mode_fixup,
+	.mode_set	= armada_drm_crtc_mode_set,
+	.mode_set_base	= armada_drm_crtc_mode_set_base,
+	.load_lut	= armada_drm_crtc_load_lut,
+	.disable	= armada_drm_crtc_disable,
+};
+
+static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+
+	priv->dcrtc[dcrtc->num] = NULL;
+	drm_crtc_cleanup(&dcrtc->crtc);
+
+	if (!IS_ERR(dcrtc->clk))
+		clk_disable_unprepare(dcrtc->clk);
+
+	kfree(dcrtc);
+}
+
+/*
+ * The mode_config lock is held here, to prevent races between this
+ * and a mode_set.
+ */
+static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
+	struct drm_framebuffer *fb, struct drm_pending_vblank_event *event)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_frame_work *work;
+	struct drm_device *dev = crtc->dev;
+	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;
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work)
+		return -ENOMEM;
+
+	work->event = event;
+	work->old_fb = dcrtc->crtc.fb;
+
+	i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
+				    dcrtc->interlaced);
+	armada_reg_queue_end(work->regs, i);
+
+	/*
+	 * Hold the old framebuffer for the work - DRM appears to drop our
+	 * reference to the old framebuffer in drm_mode_page_flip_ioctl().
+	 */
+	drm_framebuffer_reference(work->old_fb);
+
+	ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
+	if (ret) {
+		/*
+		 * Undo our reference above; DRM does not drop the reference
+		 * to this object on error, so that's okay.
+		 */
+		drm_framebuffer_unreference(work->old_fb);
+		kfree(work);
+		return ret;
+	}
+
+	/*
+	 * Don't take a reference on the new framebuffer;
+	 * drm_mode_page_flip_ioctl() has already grabbed a reference and
+	 * will _not_ drop that reference on successful return from this
+	 * function.  Simply mark this new framebuffer as the current one.
+	 */
+	dcrtc->crtc.fb = fb;
+
+	/*
+	 * Finally, if the display is blanked, we won't receive an
+	 * interrupt, so complete it now.
+	 */
+	if (dpms_blanked(dcrtc->dpms)) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		if (dcrtc->frame_work)
+			armada_drm_crtc_complete_frame_work(dcrtc);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+
+	return 0;
+}
+
+static int
+armada_drm_crtc_set_property(struct drm_crtc *crtc,
+	struct drm_property *property, uint64_t val)
+{
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	bool update_csc = false;
+
+	if (property == priv->csc_yuv_prop) {
+		dcrtc->csc_yuv_mode = val;
+		update_csc = true;
+	} else if (property == priv->csc_rgb_prop) {
+		dcrtc->csc_rgb_mode = val;
+		update_csc = true;
+	}
+
+	if (update_csc) {
+		uint32_t val;
+
+		val = dcrtc->spu_iopad_ctrl |
+		      armada_drm_crtc_calculate_csc(dcrtc);
+		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+	}
+
+	return 0;
+}
+
+static struct drm_crtc_funcs armada_crtc_funcs = {
+	.destroy	= armada_drm_crtc_destroy,
+	.set_config	= drm_crtc_helper_set_config,
+	.page_flip	= armada_drm_crtc_page_flip,
+	.set_property	= armada_drm_crtc_set_property,
+};
+
+static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
+	{ CSC_AUTO,        "Auto" },
+	{ CSC_YUV_CCIR601, "CCIR601" },
+	{ CSC_YUV_CCIR709, "CCIR709" },
+};
+
+static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = {
+	{ CSC_AUTO,         "Auto" },
+	{ CSC_RGB_COMPUTER, "Computer system" },
+	{ CSC_RGB_STUDIO,   "Studio" },
+};
+
+static int armada_drm_crtc_create_properties(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	if (priv->csc_yuv_prop)
+		return 0;
+
+	priv->csc_yuv_prop = drm_property_create_enum(dev, 0,
+				"CSC_YUV", armada_drm_csc_yuv_enum_list,
+				ARRAY_SIZE(armada_drm_csc_yuv_enum_list));
+	priv->csc_rgb_prop = drm_property_create_enum(dev, 0,
+				"CSC_RGB", armada_drm_csc_rgb_enum_list,
+				ARRAY_SIZE(armada_drm_csc_rgb_enum_list));
+
+	if (!priv->csc_yuv_prop || !priv->csc_rgb_prop)
+		return -ENOMEM;
+
+	return 0;
+}
+
+int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
+	struct resource *res)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc;
+	void __iomem *base;
+	int ret;
+
+	ret = armada_drm_crtc_create_properties(dev);
+	if (ret)
+		return ret;
+
+	base = devm_request_and_ioremap(dev->dev, res);
+	if (!base) {
+		DRM_ERROR("failed to ioremap register\n");
+		return -ENOMEM;
+	}
+
+	dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
+	if (!dcrtc) {
+		DRM_ERROR("failed to allocate Armada crtc\n");
+		return -ENOMEM;
+	}
+
+	dcrtc->base = base;
+	dcrtc->num = num;
+	dcrtc->clk = ERR_PTR(-EINVAL);
+	dcrtc->csc_yuv_mode = CSC_AUTO;
+	dcrtc->csc_rgb_mode = CSC_AUTO;
+	dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
+	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
+	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(dcrtc->spu_iopad_ctrl,
+		       dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+	writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
+	writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+		       CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+		       CFG_PDWN64x66, 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);
+
+	if (priv->variant->crtc_init) {
+		ret = priv->variant->crtc_init(dcrtc);
+		if (ret) {
+			kfree(dcrtc);
+			return ret;
+		}
+	}
+
+	/* Ensure AXI pipeline is enabled */
+	armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
+
+	priv->dcrtc[dcrtc->num] = dcrtc;
+
+	drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
+	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
+
+	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
+				   dcrtc->csc_yuv_mode);
+	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
+				   dcrtc->csc_rgb_mode);
+
+	return armada_overlay_plane_create(dev, 1 << dcrtc->num);
+}
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
new file mode 100644
index 0000000..972da53
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -0,0 +1,74 @@
+/*
+ * 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 ARMADA_CRTC_H
+#define ARMADA_CRTC_H
+
+struct armada_gem_object;
+
+struct armada_regs {
+	uint32_t offset;
+	uint32_t mask;
+	uint32_t val;
+};
+
+#define armada_reg_queue_mod(_r, _i, _v, _m, _o)	\
+	do {					\
+		struct armada_regs *__reg = _r;	\
+		__reg[_i].offset = _o;		\
+		__reg[_i].mask = ~(_m);		\
+		__reg[_i].val = _v;		\
+		_i++;				\
+	} while (0)
+
+#define armada_reg_queue_set(_r, _i, _v, _o)	\
+	armada_reg_queue_mod(_r, _i, _v, ~0, _o)
+
+#define armada_reg_queue_end(_r, _i)		\
+	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
+
+struct armada_frame_work;
+
+struct armada_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;
+	uint8_t			csc_yuv_mode;
+	uint8_t			csc_rgb_mode;
+
+	struct drm_plane	*plane;
+
+	int			dpms;
+	uint32_t		cfg_dumb_ctrl;
+	uint32_t		dumb_ctrl;
+	uint32_t		spu_iopad_ctrl;
+
+	wait_queue_head_t	frame_wait;
+	struct armada_frame_work *frame_work;
+
+	spinlock_t		irq_lock;
+	uint32_t		irq_ena;
+	struct list_head	vbl_list;
+};
+#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
+
+int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
+void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
+void armada_drm_crtc_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
new file mode 100644
index 0000000..612f375
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_debugfs.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+
+static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct armada_private *priv = dev->dev_private;
+	int ret;
+
+	mutex_lock(&dev->struct_mutex);
+	ret = drm_mm_dump_table(m, &priv->linear);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+static int armada_debugfs_reg_show(struct seq_file *m, void *data)
+{
+	struct drm_device *dev = m->private;
+	struct armada_private *priv = dev->dev_private;
+	int n, i;
+
+	if (priv) {
+		for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+			struct armada_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 armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, armada_debugfs_reg_show, inode->i_private);
+}
+
+static const struct file_operations fops_reg_r = {
+	.owner = THIS_MODULE,
+	.open = armada_debugfs_reg_r_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int armada_debugfs_write(struct file *file, const char __user *ptr,
+	size_t len, loff_t *off)
+{
+	struct drm_device *dev = file->private_data;
+	struct armada_private *priv = dev->dev_private;
+	struct armada_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 armada_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 = armada_debugfs_reg_w_open,
+	.write = armada_debugfs_write,
+	.llseek = noop_llseek,
+};
+
+static struct drm_info_list armada_debugfs_list[] = {
+	{ "gem_linear", armada_debugfs_gem_linear_show, 0 },
+};
+#define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_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 armada_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 armada_drm_debugfs_init(struct drm_minor *minor)
+{
+	int ret;
+
+	ret = drm_debugfs_create_files(armada_debugfs_list,
+				       ARMADA_DEBUGFS_ENTRIES,
+				       minor->debugfs_root, minor);
+	if (ret)
+		return ret;
+
+	ret = armada_debugfs_create(minor->debugfs_root, minor,
+				   "reg", S_IFREG | S_IRUSR, &fops_reg_r);
+	if (ret)
+		goto err_1;
+
+	ret = armada_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(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+				 minor);
+	return ret;
+}
+
+void armada_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(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+				 minor);
+}
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
new file mode 100644
index 0000000..e8c4f80
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -0,0 +1,112 @@
+/*
+ * 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 ARMADA_DRM_H
+#define ARMADA_DRM_H
+
+#include <linux/kfifo.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+#include <drm/drmP.h>
+
+struct armada_crtc;
+struct armada_gem_object;
+struct clk;
+struct drm_fb_helper;
+
+static inline void
+armada_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 inline uint32_t armada_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 armada_vbl_event {
+	struct list_head	node;
+	void			*data;
+	void			(*fn)(struct armada_crtc *, void *);
+};
+void armada_drm_vbl_event_add(struct armada_crtc *,
+	struct armada_vbl_event *);
+void armada_drm_vbl_event_remove(struct armada_crtc *,
+	struct armada_vbl_event *);
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
+	struct armada_vbl_event *);
+#define armada_drm_vbl_event_init(_e, _f, _d) do {	\
+	struct armada_vbl_event *__e = _e;		\
+	INIT_LIST_HEAD(&__e->node);			\
+	__e->data = _d;					\
+	__e->fn = _f;					\
+} while (0)
+
+
+struct armada_private;
+
+struct armada_variant {
+	bool	has_spu_adv_reg;
+	int (*init)(struct armada_private *, struct device *);
+	int (*crtc_init)(struct armada_crtc *);
+	int (*crtc_compute_clock)(struct armada_crtc *,
+				  const struct drm_display_mode *,
+				  uint32_t *);
+};
+
+/* Variant ops */
+extern const struct armada_variant armada510_ops;
+
+struct armada_private {
+	const struct armada_variant *variant;
+	struct work_struct	fb_unref_work;
+	DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
+	struct drm_fb_helper	*fbdev;
+	struct armada_crtc	*dcrtc[2];
+	struct drm_mm		linear;
+	struct clk		*extclk[2];
+	struct drm_property	*csc_yuv_prop;
+	struct drm_property	*csc_rgb_prop;
+	struct drm_property	*colorkey_prop;
+	struct drm_property	*colorkey_min_prop;
+	struct drm_property	*colorkey_max_prop;
+	struct drm_property	*colorkey_val_prop;
+	struct drm_property	*colorkey_alpha_prop;
+	struct drm_property	*colorkey_mode_prop;
+	struct drm_property	*brightness_prop;
+	struct drm_property	*contrast_prop;
+	struct drm_property	*saturation_prop;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry		*de;
+#endif
+};
+
+void __armada_drm_queue_unref_work(struct drm_device *,
+	struct drm_framebuffer *);
+void armada_drm_queue_unref_work(struct drm_device *,
+	struct drm_framebuffer *);
+
+extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
+
+int armada_fbdev_init(struct drm_device *);
+void armada_fbdev_fini(struct drm_device *);
+
+int armada_overlay_plane_create(struct drm_device *, unsigned long);
+
+int armada_drm_debugfs_init(struct drm_minor *);
+void armada_drm_debugfs_cleanup(struct drm_minor *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
new file mode 100644
index 0000000..7bfab9a
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -0,0 +1,380 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+static void armada_drm_unref_work(struct work_struct *work)
+{
+	struct armada_private *priv =
+		container_of(work, struct armada_private, fb_unref_work);
+	struct drm_framebuffer *fb;
+
+	while (kfifo_get(&priv->fb_unref, &fb))
+		drm_framebuffer_unreference(fb);
+}
+
+/* Must be called with dev->event_lock held */
+void __armada_drm_queue_unref_work(struct drm_device *dev,
+	struct drm_framebuffer *fb)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	/*
+	 * Yes, we really must jump through these hoops just to store a
+	 * _pointer_ to something into the kfifo.  This is utterly insane
+	 * and idiotic, because it kfifo requires the _data_ pointed to by
+	 * the pointer const, not the pointer itself.  Not only that, but
+	 * you have to pass a pointer _to_ the pointer you want stored.
+	 */
+	const struct drm_framebuffer *silly_api_alert = fb;
+	WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
+	schedule_work(&priv->fb_unref_work);
+}
+
+void armada_drm_queue_unref_work(struct drm_device *dev,
+	struct drm_framebuffer *fb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	__armada_drm_queue_unref_work(dev, fb);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int armada_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	const struct platform_device_id *id;
+	struct armada_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;
+
+		/* Resources above 64K are graphics memory */
+		if (resource_size(r) > SZ_64K)
+			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), "armada-drm"))
+		return -EBUSY;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		DRM_ERROR("failed to allocate private\n");
+		return -ENOMEM;
+	}
+
+	dev->dev_private = priv;
+
+	/* Get the implementation specific driver data. */
+	id = platform_get_device_id(dev->platformdev);
+	if (!id)
+		return -ENXIO;
+
+	priv->variant = (struct armada_variant *)id->driver_data;
+
+	ret = priv->variant->init(priv, dev->dev);
+	if (ret)
+		return ret;
+
+	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
+	INIT_KFIFO(priv->fb_unref);
+
+	/* Mode setting support */
+	drm_mode_config_init(dev);
+	dev->mode_config.min_width = 320;
+	dev->mode_config.min_height = 200;
+
+	/*
+	 * With vscale enabled, the maximum width is 1920 due to the
+	 * 1920 by 3 lines RAM
+	 */
+	dev->mode_config.max_width = 1920;
+	dev->mode_config.max_height = 2048;
+
+	dev->mode_config.preferred_depth = 24;
+	dev->mode_config.funcs = &armada_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++) {
+		if (!res[n])
+			break;
+
+		ret = armada_drm_crtc_create(dev, n, res[n]);
+		if (ret)
+			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 = armada_fbdev_init(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);
+	flush_work(&priv->fb_unref_work);
+
+	return ret;
+}
+
+static int armada_drm_unload(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	drm_kms_helper_poll_fini(dev);
+	armada_fbdev_fini(dev);
+	drm_irq_uninstall(dev);
+	drm_mode_config_cleanup(dev);
+	drm_mm_takedown(&priv->linear);
+	flush_work(&priv->fb_unref_work);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
+	struct armada_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 armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	if (!list_empty(&evt->node)) {
+		list_del_init(&evt->node);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+	}
+}
+
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dcrtc->irq_lock, flags);
+	armada_drm_vbl_event_remove(dcrtc, evt);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+/* These are called under the vbl_lock. */
+static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
+{
+	struct armada_private *priv = dev->dev_private;
+	armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+	return 0;
+}
+
+static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
+{
+	struct armada_private *priv = dev->dev_private;
+	armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+}
+
+static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
+{
+	struct drm_device *dev = arg;
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc = priv->dcrtc[0];
+	uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+	irqreturn_t handled = IRQ_NONE;
+
+	/*
+	 * 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|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+		armada_drm_crtc_irq(dcrtc, stat);
+		handled = IRQ_HANDLED;
+	}
+
+	return handled;
+}
+
+static int armada_drm_irq_postinstall(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_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 armada_drm_irq_uninstall(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc = priv->dcrtc[0];
+
+	writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+}
+
+static struct drm_ioctl_desc armada_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
+		DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
+		DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
+		DRM_UNLOCKED),
+};
+
+static const struct file_operations armada_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,
+};
+
+static struct drm_driver armada_drm_driver = {
+	.load			= armada_drm_load,
+	.open			= NULL,
+	.preclose		= NULL,
+	.postclose		= NULL,
+	.lastclose		= NULL,
+	.unload			= armada_drm_unload,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= armada_drm_enable_vblank,
+	.disable_vblank		= armada_drm_disable_vblank,
+	.irq_handler		= armada_drm_irq_handler,
+	.irq_postinstall	= armada_drm_irq_postinstall,
+	.irq_uninstall		= armada_drm_irq_uninstall,
+#ifdef CONFIG_DEBUG_FS
+	.debugfs_init		= armada_drm_debugfs_init,
+	.debugfs_cleanup	= armada_drm_debugfs_cleanup,
+#endif
+	.gem_init_object	= NULL,
+	.gem_free_object	= armada_gem_free_object,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_export	= armada_gem_prime_export,
+	.gem_prime_import	= armada_gem_prime_import,
+	.dumb_create		= armada_gem_dumb_create,
+	.dumb_map_offset	= armada_gem_dumb_map_offset,
+	.dumb_destroy		= armada_gem_dumb_destroy,
+	.gem_vm_ops		= &armada_gem_vm_ops,
+	.major			= 1,
+	.minor			= 0,
+	.name			= "armada-drm",
+	.desc			= "Armada SoC DRM",
+	.date			= "20120730",
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
+				  DRIVER_HAVE_IRQ | DRIVER_PRIME,
+	.ioctls			= armada_ioctls,
+	.fops			= &armada_drm_fops,
+};
+
+static int armada_drm_probe(struct platform_device *pdev)
+{
+	return drm_platform_init(&armada_drm_driver, pdev);
+}
+
+static int armada_drm_remove(struct platform_device *pdev)
+{
+	drm_platform_exit(&armada_drm_driver, pdev);
+	return 0;
+}
+
+static const struct platform_device_id armada_drm_platform_ids[] = {
+	{
+		.name		= "armada-drm",
+		.driver_data	= (unsigned long)&armada510_ops,
+	}, {
+		.name		= "armada-510-drm",
+		.driver_data	= (unsigned long)&armada510_ops,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
+
+static struct platform_driver armada_drm_platform_driver = {
+	.probe	= armada_drm_probe,
+	.remove	= armada_drm_remove,
+	.driver	= {
+		.name	= "armada-drm",
+		.owner	= THIS_MODULE,
+	},
+	.id_table = armada_drm_platform_ids,
+};
+
+static int __init armada_drm_init(void)
+{
+	armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
+	return platform_driver_register(&armada_drm_platform_driver);
+}
+module_init(armada_drm_init);
+
+static void __exit armada_drm_exit(void)
+{
+	platform_driver_unregister(&armada_drm_platform_driver);
+}
+module_exit(armada_drm_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Armada DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:armada-drm");
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c
new file mode 100644
index 0000000..1c90969
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+static void armada_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+
+	drm_framebuffer_cleanup(&dfb->fb);
+	drm_gem_object_unreference_unlocked(&dfb->obj->obj);
+	kfree(dfb);
+}
+
+static int armada_fb_create_handle(struct drm_framebuffer *fb,
+	struct drm_file *dfile, unsigned int *handle)
+{
+	struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+	return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
+}
+
+static const struct drm_framebuffer_funcs armada_fb_funcs = {
+	.destroy	= armada_fb_destroy,
+	.create_handle	= armada_fb_create_handle,
+};
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
+	struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
+{
+	struct armada_framebuffer *dfb;
+	uint8_t format, config;
+	int ret;
+
+	switch (mode->pixel_format) {
+#define FMT(drm, fmt, mod)		\
+	case DRM_FORMAT_##drm:		\
+		format = CFG_##fmt;	\
+		config = mod;		\
+		break
+	FMT(RGB565,	565,		CFG_SWAPRB);
+	FMT(BGR565,	565,		0);
+	FMT(ARGB1555,	1555,		CFG_SWAPRB);
+	FMT(ABGR1555,	1555,		0);
+	FMT(RGB888,	888PACK,	CFG_SWAPRB);
+	FMT(BGR888,	888PACK,	0);
+	FMT(XRGB8888,	X888,		CFG_SWAPRB);
+	FMT(XBGR8888,	X888,		0);
+	FMT(ARGB8888,	8888,		CFG_SWAPRB);
+	FMT(ABGR8888,	8888,		0);
+	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
+	FMT(UYVY,	422PACK,	CFG_YUV2RGB);
+	FMT(VYUY,	422PACK,	CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(YVYU,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU);
+	FMT(YUV422,	422,		CFG_YUV2RGB);
+	FMT(YVU422,	422,		CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(YUV420,	420,		CFG_YUV2RGB);
+	FMT(YVU420,	420,		CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(C8,		PSEUDO8,	0);
+#undef FMT
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
+	dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
+	if (!dfb) {
+		DRM_ERROR("failed to allocate Armada fb object\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dfb->fmt = format;
+	dfb->mod = config;
+	dfb->obj = obj;
+
+	drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
+
+	ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
+	if (ret) {
+		kfree(dfb);
+		return ERR_PTR(ret);
+	}
+
+	/*
+	 * Take a reference on our object as we're successful - the
+	 * caller already holds a reference, which keeps us safe for
+	 * the above call, but the caller will drop their reference
+	 * to it.  Hence we need to take our own reference.
+	 */
+	drm_gem_object_reference(&obj->obj);
+
+	return dfb;
+}
+
+static struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
+	struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
+{
+	struct armada_gem_object *obj;
+	struct armada_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 &&
+	    (mode->handles[0] != mode->handles[1] ||
+	     mode->handles[0] != mode->handles[2])) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]);
+	if (!obj) {
+		ret = -ENOENT;
+		goto err;
+	}
+
+	if (obj->obj.import_attach && !obj->sgt) {
+		ret = armada_gem_map_import(obj);
+		if (ret)
+			goto err_unref;
+	}
+
+	/* Framebuffer objects must have a valid device address for scanout */
+	if (obj->dev_addr == DMA_ERROR_CODE) {
+		ret = -EINVAL;
+		goto err_unref;
+	}
+
+	dfb = armada_framebuffer_create(dev, mode, obj);
+	if (IS_ERR(dfb)) {
+		ret = PTR_ERR(dfb);
+		goto err;
+	}
+
+	drm_gem_object_unreference_unlocked(&obj->obj);
+
+	return &dfb->fb;
+
+ err_unref:
+	drm_gem_object_unreference_unlocked(&obj->obj);
+ err:
+	DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
+	return ERR_PTR(ret);
+}
+
+static void armada_output_poll_changed(struct drm_device *dev)
+{
+	struct armada_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 armada_drm_mode_config_funcs = {
+	.fb_create		= armada_fb_create,
+	.output_poll_changed	= armada_output_poll_changed,
+};
diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h
new file mode 100644
index 0000000..ce3f12e
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.h
@@ -0,0 +1,24 @@
+/*
+ * 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 ARMADA_FB_H
+#define ARMADA_FB_H
+
+struct armada_framebuffer {
+	struct drm_framebuffer	fb;
+	struct armada_gem_object *obj;
+	uint8_t			fmt;
+	uint8_t			mod;
+};
+#define drm_fb_to_armada_fb(dfb) \
+	container_of(dfb, struct armada_framebuffer, fb)
+#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *,
+	struct drm_mode_fb_cmd2 *, struct armada_gem_object *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
new file mode 100644
index 0000000..dd5ea77
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -0,0 +1,202 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+
+static /*const*/ struct fb_ops armada_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 armada_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 armada_framebuffer *dfb;
+	struct armada_gem_object *obj;
+	struct fb_info *info;
+	int size, ret;
+	void *ptr;
+
+	memset(&mode, 0, sizeof(mode));
+	mode.width = sizes->surface_width;
+	mode.height = sizes->surface_height;
+	mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
+	mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+					sizes->surface_depth);
+
+	size = mode.pitches[0] * mode.height;
+	obj = armada_gem_alloc_private_object(dev, size);
+	if (!obj) {
+		DRM_ERROR("failed to allocate fb memory\n");
+		return -ENOMEM;
+	}
+
+	ret = armada_gem_linear_back(dev, obj);
+	if (ret) {
+		drm_gem_object_unreference_unlocked(&obj->obj);
+		return ret;
+	}
+
+	ptr = armada_gem_map_object(dev, obj);
+	if (!ptr) {
+		drm_gem_object_unreference_unlocked(&obj->obj);
+		return -ENOMEM;
+	}
+
+	dfb = armada_framebuffer_create(dev, &mode, obj);
+
+	/*
+	 * A reference is now held by the framebuffer object if
+	 * successful, otherwise this drops the ref for the error path.
+	 */
+	drm_gem_object_unreference_unlocked(&obj->obj);
+
+	if (IS_ERR(dfb))
+		return PTR_ERR(dfb);
+
+	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, "armada-drmfb", sizeof(info->fix.id));
+	info->par = fbh;
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+	info->fbops = &armada_fb_ops;
+	info->fix.smem_start = obj->phys_addr;
+	info->fix.smem_len = obj->obj.size;
+	info->screen_size = obj->obj.size;
+	info->screen_base = ptr;
+	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);
+
+	return 0;
+
+ err_fbcmap:
+	framebuffer_release(info);
+ err_fballoc:
+	dfb->fb.funcs->destroy(&dfb->fb);
+	return ret;
+}
+
+static int armada_fb_probe(struct drm_fb_helper *fbh,
+	struct drm_fb_helper_surface_size *sizes)
+{
+	int ret = 0;
+
+	if (!fbh->fb) {
+		ret = armada_fb_create(fbh, sizes);
+		if (ret == 0)
+			ret = 1;
+	}
+	return ret;
+}
+
+static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+	.gamma_set	= armada_drm_crtc_gamma_set,
+	.gamma_get	= armada_drm_crtc_gamma_get,
+	.fb_probe	= armada_fb_probe,
+};
+
+int armada_fbdev_init(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_fb_helper *fbh;
+	int ret;
+
+	fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
+	if (!fbh)
+		return -ENOMEM;
+
+	priv->fbdev = fbh;
+
+	fbh->funcs = &armada_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:
+	priv->fbdev = NULL;
+	return ret;
+}
+
+void armada_fbdev_fini(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_fb_helper *fbh = priv->fbdev;
+
+	if (fbh) {
+		struct fb_info *info = fbh->fbdev;
+
+		if (info) {
+			unregister_framebuffer(info);
+			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);
+
+		priv->fbdev = NULL;
+	}
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
new file mode 100644
index 0000000..c865a9a
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -0,0 +1,616 @@
+/*
+ * 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/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/shmem_fs.h>
+#include <drm/drmP.h>
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct armada_gem_object *obj = drm_to_armada_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:
+	case -EBUSY:
+		return VM_FAULT_NOPAGE;
+	case -ENOMEM:
+		return VM_FAULT_OOM;
+	default:
+		return VM_FAULT_SIGBUS;
+	}
+}
+
+const struct vm_operations_struct armada_gem_vm_ops = {
+	.fault	= armada_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);
+}
+
+/* dev->struct_mutex is held here */
+void armada_gem_free_object(struct drm_gem_object *obj)
+{
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+
+	DRM_DEBUG_DRIVER("release obj %p\n", dobj);
+
+	drm_gem_free_mmap_offset(&dobj->obj);
+
+	if (dobj->page) {
+		/* page backed memory */
+		unsigned int order = get_order(dobj->obj.size);
+		__free_pages(dobj->page, order);
+	} else if (dobj->linear) {
+		/* linear backed memory */
+		drm_mm_remove_node(dobj->linear);
+		kfree(dobj->linear);
+		if (dobj->addr)
+			iounmap(dobj->addr);
+	}
+
+	if (dobj->obj.import_attach) {
+		/* We only ever display imported data */
+		dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
+					 DMA_TO_DEVICE);
+		drm_prime_gem_destroy(&dobj->obj, NULL);
+	}
+
+	drm_gem_object_release(&dobj->obj);
+
+	kfree(dobj);
+}
+
+int
+armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
+{
+	struct armada_private *priv = dev->dev_private;
+	size_t size = obj->obj.size;
+
+	if (obj->page || obj->linear)
+		return 0;
+
+	/*
+	 * If it is a small allocation (typically cursor, which will
+	 * be 32x64 or 64x32 ARGB pixels) try to get it from the system.
+	 * Framebuffers will never be this small (our minimum size for
+	 * framebuffers is larger than this anyway.)  Such objects are
+	 * only accessed by the CPU so we don't need any special handing
+	 * here.
+	 */
+	if (size <= 8192) {
+		unsigned int order = get_order(size);
+		struct page *p = alloc_pages(GFP_KERNEL, order);
+
+		if (p) {
+			obj->addr = page_address(p);
+			obj->phys_addr = page_to_phys(p);
+			obj->page = p;
+
+			memset(obj->addr, 0, PAGE_ALIGN(size));
+		}
+	}
+
+	/*
+	 * We could grab something from CMA if it's enabled, but that
+	 * involves building in a problem:
+	 *
+	 * CMA's interface uses dma_alloc_coherent(), which provides us
+	 * with an CPU virtual address and a device address.
+	 *
+	 * The CPU virtual address may be either an address in the kernel
+	 * direct mapped region (for example, as it would be on x86) or
+	 * it may be remapped into another part of kernel memory space
+	 * (eg, as it would be on ARM.)  This means virt_to_phys() on the
+	 * returned virtual address is invalid depending on the architecture
+	 * implementation.
+	 *
+	 * The device address may also not be a physical address; it may
+	 * be that there is some kind of remapping between the device and
+	 * system RAM, which makes the use of the device address also
+	 * unsafe to re-use as a physical address.
+	 *
+	 * This makes DRM usage of dma_alloc_coherent() in a generic way
+	 * at best very questionable and unsafe.
+	 */
+
+	/* Otherwise, grab it from our linear allocation */
+	if (!obj->page) {
+		struct drm_mm_node *node;
+		unsigned align = min_t(unsigned, size, SZ_2M);
+		void __iomem *ptr;
+		int ret;
+
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!node)
+			return -ENOSPC;
+
+		mutex_lock(&dev->struct_mutex);
+		ret = drm_mm_insert_node(&priv->linear, node, size, align,
+					 DRM_MM_SEARCH_DEFAULT);
+		mutex_unlock(&dev->struct_mutex);
+		if (ret) {
+			kfree(node);
+			return ret;
+		}
+
+		obj->linear = node;
+
+		/* Ensure that the memory we're returning is cleared. */
+		ptr = ioremap_wc(obj->linear->start, size);
+		if (!ptr) {
+			mutex_lock(&dev->struct_mutex);
+			drm_mm_remove_node(obj->linear);
+			mutex_unlock(&dev->struct_mutex);
+			kfree(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;
+}
+
+void *
+armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj)
+{
+	/* only linear objects need to be ioremap'd */
+	if (!dobj->addr && dobj->linear)
+		dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size);
+	return dobj->addr;
+}
+
+struct armada_gem_object *
+armada_gem_alloc_private_object(struct drm_device *dev, size_t size)
+{
+	struct armada_gem_object *obj;
+
+	size = roundup_gem_size(size);
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return NULL;
+
+	drm_gem_private_object_init(dev, &obj->obj, size);
+	obj->dev_addr = DMA_ERROR_CODE;
+
+	DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
+
+	return obj;
+}
+
+struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev,
+	size_t size)
+{
+	struct armada_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;
+	}
+
+	obj->dev_addr = DMA_ERROR_CODE;
+
+	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 armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+	struct drm_mode_create_dumb *args)
+{
+	struct armada_gem_object *dobj;
+	u32 handle;
+	size_t size;
+	int ret;
+
+	args->pitch = armada_pitch(args->width, args->bpp);
+	args->size = size = args->pitch * args->height;
+
+	dobj = armada_gem_alloc_private_object(dev, size);
+	if (dobj == NULL)
+		return -ENOMEM;
+
+	ret = armada_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 armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+	uint32_t handle, uint64_t *offset)
+{
+	struct armada_gem_object *obj;
+	int ret = 0;
+
+	mutex_lock(&dev->struct_mutex);
+	obj = armada_gem_object_lookup(dev, file, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object\n");
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	/* Don't allow imported objects to be mapped */
+	if (obj->obj.import_attach) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	ret = drm_gem_create_mmap_offset(&obj->obj);
+	if (ret == 0) {
+		*offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
+		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 armada_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 armada_gem_create_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_create *args = data;
+	struct armada_gem_object *dobj;
+	size_t size;
+	u32 handle;
+	int ret;
+
+	if (args->size == 0)
+		return -ENOMEM;
+
+	size = args->size;
+
+	dobj = armada_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;
+}
+
+/* Map a shmem-backed object into process memory space */
+int armada_gem_mmap_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_mmap *args = data;
+	struct armada_gem_object *dobj;
+	unsigned long addr;
+
+	dobj = armada_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 armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_pwrite *args = data;
+	struct armada_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 = armada_gem_object_lookup(dev, file, args->handle);
+	if (dobj == NULL)
+		return -ENOENT;
+
+	/* Must be a kernel-mapped object */
+	if (!dobj->addr)
+		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;
+	}
+
+	if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) {
+		ret = -EFAULT;
+	} else if (dobj->update) {
+		dobj->update(dobj->update_data);
+		ret = 0;
+	}
+
+ unref:
+	drm_gem_object_unreference_unlocked(&dobj->obj);
+	return ret;
+}
+
+/* Prime support */
+struct sg_table *
+armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
+	enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = attach->dmabuf->priv;
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+	struct scatterlist *sg;
+	struct sg_table *sgt;
+	int i, num;
+
+	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt)
+		return NULL;
+
+	if (dobj->obj.filp) {
+		struct address_space *mapping;
+		gfp_t gfp;
+		int count;
+
+		count = dobj->obj.size / PAGE_SIZE;
+		if (sg_alloc_table(sgt, count, GFP_KERNEL))
+			goto free_sgt;
+
+		mapping = file_inode(dobj->obj.filp)->i_mapping;
+		gfp = mapping_gfp_mask(mapping);
+
+		for_each_sg(sgt->sgl, sg, count, i) {
+			struct page *page;
+
+			page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+			if (IS_ERR(page)) {
+				num = i;
+				goto release;
+			}
+
+			sg_set_page(sg, page, PAGE_SIZE, 0);
+		}
+
+		if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
+			num = sgt->nents;
+			goto release;
+		}
+	} else if (dobj->page) {
+		/* Single contiguous page */
+		if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+			goto free_sgt;
+
+		sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
+
+		if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
+			goto free_table;
+	} else if (dobj->linear) {
+		/* Single contiguous physical region - no struct page */
+		if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+			goto free_sgt;
+		sg_dma_address(sgt->sgl) = dobj->dev_addr;
+		sg_dma_len(sgt->sgl) = dobj->obj.size;
+	} else {
+		goto free_sgt;
+	}
+	return sgt;
+
+ release:
+	for_each_sg(sgt->sgl, sg, num, i)
+		page_cache_release(sg_page(sg));
+ free_table:
+	sg_free_table(sgt);
+ free_sgt:
+	kfree(sgt);
+	return NULL;
+}
+
+static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
+	struct sg_table *sgt, enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = attach->dmabuf->priv;
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+	int i;
+
+	if (!dobj->linear)
+		dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+
+	if (dobj->obj.filp) {
+		struct scatterlist *sg;
+		for_each_sg(sgt->sgl, sg, sgt->nents, i)
+			page_cache_release(sg_page(sg));
+	}
+
+	sg_free_table(sgt);
+	kfree(sgt);
+}
+
+static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n)
+{
+	return NULL;
+}
+
+static void
+armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr)
+{
+}
+
+static int
+armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+	return -EINVAL;
+}
+
+static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
+	.map_dma_buf	= armada_gem_prime_map_dma_buf,
+	.unmap_dma_buf	= armada_gem_prime_unmap_dma_buf,
+	.release	= drm_gem_dmabuf_release,
+	.kmap_atomic	= armada_gem_dmabuf_no_kmap,
+	.kunmap_atomic	= armada_gem_dmabuf_no_kunmap,
+	.kmap		= armada_gem_dmabuf_no_kmap,
+	.kunmap		= armada_gem_dmabuf_no_kunmap,
+	.mmap		= armada_gem_dmabuf_mmap,
+};
+
+struct dma_buf *
+armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
+	int flags)
+{
+	return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
+			      O_RDWR);
+}
+
+struct drm_gem_object *
+armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf)
+{
+	struct dma_buf_attachment *attach;
+	struct armada_gem_object *dobj;
+
+	if (buf->ops == &armada_gem_prime_dmabuf_ops) {
+		struct drm_gem_object *obj = buf->priv;
+		if (obj->dev == dev) {
+			/*
+			 * Importing our own dmabuf(s) increases the
+			 * refcount on the gem object itself.
+			 */
+			drm_gem_object_reference(obj);
+			dma_buf_put(buf);
+			return obj;
+		}
+	}
+
+	attach = dma_buf_attach(buf, dev->dev);
+	if (IS_ERR(attach))
+		return ERR_CAST(attach);
+
+	dobj = armada_gem_alloc_private_object(dev, buf->size);
+	if (!dobj) {
+		dma_buf_detach(buf, attach);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dobj->obj.import_attach = attach;
+
+	/*
+	 * Don't call dma_buf_map_attachment() here - it maps the
+	 * scatterlist immediately for DMA, and this is not always
+	 * an appropriate thing to do.
+	 */
+	return &dobj->obj;
+}
+
+int armada_gem_map_import(struct armada_gem_object *dobj)
+{
+	int ret;
+
+	dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach,
+					  DMA_TO_DEVICE);
+	if (!dobj->sgt) {
+		DRM_ERROR("dma_buf_map_attachment() returned NULL\n");
+		return -EINVAL;
+	}
+	if (IS_ERR(dobj->sgt)) {
+		ret = PTR_ERR(dobj->sgt);
+		dobj->sgt = NULL;
+		DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret);
+		return ret;
+	}
+	if (dobj->sgt->nents > 1) {
+		DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n");
+		return -EINVAL;
+	}
+	if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) {
+		DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n");
+		return -EINVAL;
+	}
+	dobj->dev_addr = sg_dma_address(dobj->sgt->sgl);
+	return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h
new file mode 100644
index 0000000..00b6cd4
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.h
@@ -0,0 +1,52 @@
+/*
+ * 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 ARMADA_GEM_H
+#define ARMADA_GEM_H
+
+/* GEM */
+struct armada_gem_object {
+	struct drm_gem_object	obj;
+	void			*addr;
+	phys_addr_t		phys_addr;
+	resource_size_t		dev_addr;
+	struct drm_mm_node	*linear;	/* for linear backed */
+	struct page		*page;		/* for page backed */
+	struct sg_table		*sgt;		/* for imported */
+	void			(*update)(void *);
+	void			*update_data;
+};
+
+extern const struct vm_operations_struct armada_gem_vm_ops;
+
+#define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj)
+
+void armada_gem_free_object(struct drm_gem_object *);
+int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *);
+void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *);
+struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
+	size_t);
+int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
+	struct drm_mode_create_dumb *);
+int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
+	uint32_t, uint64_t *);
+int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
+	uint32_t);
+struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
+	struct drm_gem_object *obj, int flags);
+struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
+	struct dma_buf *);
+int armada_gem_map_import(struct armada_gem_object *);
+
+static inline struct armada_gem_object *armada_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_armada_gem(obj) : NULL;
+}
+#endif
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
new file mode 100644
index 0000000..216a4b5
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -0,0 +1,316 @@
+/*
+ * 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 ARMADA_HW_H
+#define ARMADA_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,	/* Armada 510 */
+	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,	/* Armada 510 */
+	LCD_SPU_SPI_RXDATA		= 0x0140,
+	LCD_SPU_ISA_RXDATA		= 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,	/* Armada 510 */
+	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,
+};
+
+enum {
+	CFG_565		= 0,
+	CFG_1555	= 1,
+	CFG_888PACK	= 2,
+	CFG_X888	= 3,
+	CFG_8888	= 4,
+	CFG_422PACK	= 5,
+	CFG_422		= 6,
+	CFG_420		= 7,
+	CFG_PSEUDO4	= 9,
+	CFG_PSEUDO8	= 10,
+	CFG_SWAPRB	= 1 << 4,
+	CFG_SWAPUV	= 1 << 3,
+	CFG_SWAPYU	= 1 << 2,
+	CFG_YUV2RGB	= 1 << 1,
+};
+
+/* 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,
+#define	CFG_DMA_FMT(x)	((x) << 20)
+	CFG_GRAFORMAT	= 0xf << 16,
+#define	CFG_GRA_FMT(x)	((x) << 16)
+#define CFG_GRA_MOD(x)	((x) << 8)
+	CFG_GRA_FTOGGLE	= 1 << 15,
+	CFG_GRA_HSMOOTH	= 1 << 14,
+	CFG_GRA_TSTMODE	= 1 << 13,
+	CFG_GRA_ENA	= 1 << 8,
+#define CFG_DMA_MOD(x)	((x) << 0)
+	CFG_DMA_FTOGGLE	= 1 << 7,
+	CFG_DMA_HSMOOTH	= 1 << 6,
+	CFG_DMA_TSTMODE	= 1 << 5,
+	CFG_DMA_ENA	= 1 << 0,
+};
+
+enum {
+	CKMODE_DISABLE	= 0,
+	CKMODE_Y	= 1,
+	CKMODE_U	= 2,
+	CKMODE_RGB	= 3,
+	CKMODE_V	= 4,
+	CKMODE_R	= 5,
+	CKMODE_G	= 6,
+	CKMODE_B	= 7,
+};
+
+/* For LCD_SPU_DMA_CTRL1 */
+enum {
+	CFG_FRAME_TRIG		= 1 << 31,
+	CFG_VSYNC_INV		= 1 << 27,
+	CFG_CKMODE_MASK		= 0x7 << 24,
+#define CFG_CKMODE(x)		((x) << 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_CTRL */
+enum {
+	SRAM_READ	= 0 << 14,
+	SRAM_WRITE	= 2 << 14,
+	SRAM_INIT	= 3 << 14,
+	SRAM_HWC32_RAMR	= 0xc << 8,
+	SRAM_HWC32_RAMG	= 0xd << 8,
+	SRAM_HWC32_RAMB	= 0xe << 8,
+	SRAM_HWC32_TRAN	= 0xf << 8,
+	SRAM_HWC	= 0xf << 8,
+};
+
+/* For LCD_SPU_SRAM_PARA1 */
+enum {
+	CFG_CSB_256x32	= 1 << 15,	/* cursor */
+	CFG_CSB_256x24	= 1 << 14,	/* palette */
+	CFG_CSB_256x8	= 1 << 13,	/* gamma */
+	CFG_PDWN1920x32	= 1 << 8,	/* Armada 510: power down vscale ram */
+	CFG_PDWN256x32	= 1 << 7,	/* power down cursor */
+	CFG_PDWN256x24	= 1 << 6,	/* power down palette */
+	CFG_PDWN256x8	= 1 << 5,	/* power down gamma */
+	CFG_PDWNHWC	= 1 << 4,	/* Armada 510: power down all hwc ram */
+	CFG_PDWN32x32	= 1 << 3,	/* power down slave->smart ram */
+	CFG_PDWN16x66	= 1 << 2,	/* power down UV fifo */
+	CFG_PDWN32x66	= 1 << 1,	/* power down Y fifo */
+	CFG_PDWN64x66	= 1 << 0,	/* power down graphic fifo */
+};
+
+/* For LCD_CFG_SCLK_DIV */
+enum {
+	/* Armada 510 */
+	SCLK_510_AXI		= 0x0 << 30,
+	SCLK_510_EXTCLK0	= 0x1 << 30,
+	SCLK_510_PLL		= 0x2 << 30,
+	SCLK_510_EXTCLK1	= 0x3 << 30,
+	SCLK_510_DIV_CHANGE	= 1 << 29,
+	SCLK_510_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_510_INT_DIV_MASK	= 0xffff << 0,
+
+	/* Armada 16x */
+	SCLK_16X_AHB		= 0x0 << 28,
+	SCLK_16X_PCLK		= 0x1 << 28,
+	SCLK_16X_AXI		= 0x4 << 28,
+	SCLK_16X_PLL		= 0x8 << 28,
+	SCLK_16X_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_16X_INT_DIV_MASK	= 0xffff << 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,	/* Normally active high */
+	CFG_INV_HENA	= 1 << 4,
+	CFG_INV_VSYNC	= 1 << 3,	/* Normally active high */
+	CFG_INV_HSYNC	= 1 << 2,	/* Normally active high */
+	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_YUV_CCIR709	= 1 << 9,
+	CFG_CSC_YUV_CCIR601	= 0 << 9,
+	CFG_CSC_RGB_STUDIO	= 1 << 8,
+	CFG_CSC_RGB_COMPUTER	= 0 << 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/armada/armada_ioctlP.h b/drivers/gpu/drm/armada/armada_ioctlP.h
new file mode 100644
index 0000000..bd8c456
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_ioctlP.h
@@ -0,0 +1,18 @@
+/*
+ * 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 ARMADA_IOCTLP_H
+#define ARMADA_IOCTLP_H
+
+#define ARMADA_IOCTL_PROTO(name)\
+extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
+
+ARMADA_IOCTL_PROTO(gem_create);
+ARMADA_IOCTL_PROTO(gem_mmap);
+ARMADA_IOCTL_PROTO(gem_pwrite);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
new file mode 100644
index 0000000..d685a54
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -0,0 +1,158 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_output.h"
+#include "armada_drm.h"
+
+struct armada_connector {
+	struct drm_connector conn;
+	const struct armada_output_type *type;
+};
+
+#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
+{
+	struct drm_encoder *enc = conn->encoder;
+
+	return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
+}
+
+static enum drm_connector_status armada_drm_connector_detect(
+	struct drm_connector *conn, bool force)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+	enum drm_connector_status status = connector_status_disconnected;
+
+	if (dconn->type->detect) {
+		status = dconn->type->detect(conn, force);
+	} else {
+		struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+
+		if (enc)
+			status = encoder_helper_funcs(enc)->detect(enc, conn);
+	}
+
+	return status;
+}
+
+static void armada_drm_connector_destroy(struct drm_connector *conn)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+	drm_sysfs_connector_remove(conn);
+	drm_connector_cleanup(conn);
+	kfree(dconn);
+}
+
+static int armada_drm_connector_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+	if (!dconn->type->set_property)
+		return -EINVAL;
+
+	return dconn->type->set_property(conn, property, value);
+}
+
+static const struct drm_connector_funcs armada_drm_conn_funcs = {
+	.dpms		= drm_helper_connector_dpms,
+	.fill_modes	= drm_helper_probe_single_connector_modes,
+	.detect		= armada_drm_connector_detect,
+	.destroy	= armada_drm_connector_destroy,
+	.set_property	= armada_drm_connector_set_property,
+};
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void armada_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+	return true;
+}
+
+/* Shouldn't this be a generic helper function? */
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+	int valid = MODE_BAD;
+
+	if (encoder) {
+		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+		valid = slave->slave_funcs->mode_valid(encoder, mode);
+	}
+	return valid;
+}
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+	int rc = -EINVAL;
+
+	if (encoder) {
+		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+		rc = slave->slave_funcs->set_property(encoder, conn, property,
+						      value);
+	}
+	return rc;
+}
+
+int armada_output_create(struct drm_device *dev,
+	const struct armada_output_type *type, const void *data)
+{
+	struct armada_connector *dconn;
+	int ret;
+
+	dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
+	if (!dconn)
+		return -ENOMEM;
+
+	dconn->type = type;
+
+	ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
+				 type->connector_type);
+	if (ret) {
+		DRM_ERROR("unable to init connector\n");
+		goto err_destroy_dconn;
+	}
+
+	ret = type->create(&dconn->conn, data);
+	if (ret)
+		goto err_conn;
+
+	ret = drm_sysfs_connector_add(&dconn->conn);
+	if (ret)
+		goto err_sysfs;
+
+	return 0;
+
+ err_sysfs:
+	if (dconn->conn.encoder)
+		dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
+ err_conn:
+	drm_connector_cleanup(&dconn->conn);
+ err_destroy_dconn:
+	kfree(dconn);
+	return ret;
+}
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h
new file mode 100644
index 0000000..4126d43
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.h
@@ -0,0 +1,39 @@
+/*
+ * 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 ARMADA_CONNETOR_H
+#define ARMADA_CONNETOR_H
+
+#define encoder_helper_funcs(encoder) \
+	((struct drm_encoder_helper_funcs *)encoder->helper_private)
+
+struct armada_output_type {
+	int connector_type;
+	enum drm_connector_status (*detect)(struct drm_connector *, bool);
+	int (*create)(struct drm_connector *, const void *);
+	int (*set_property)(struct drm_connector *, struct drm_property *,
+			    uint64_t);
+};
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder);
+void armada_drm_encoder_commit(struct drm_encoder *encoder);
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adj);
+
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode);
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value);
+
+int armada_output_create(struct drm_device *dev,
+	const struct armada_output_type *type, const void *data);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
new file mode 100644
index 0000000..c5b06fd
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -0,0 +1,477 @@
+/*
+ * 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 <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+struct armada_plane_properties {
+	uint32_t colorkey_yr;
+	uint32_t colorkey_ug;
+	uint32_t colorkey_vb;
+#define K2R(val) (((val) >> 0) & 0xff)
+#define K2G(val) (((val) >> 8) & 0xff)
+#define K2B(val) (((val) >> 16) & 0xff)
+	int16_t  brightness;
+	uint16_t contrast;
+	uint16_t saturation;
+	uint32_t colorkey_mode;
+};
+
+struct armada_plane {
+	struct drm_plane base;
+	spinlock_t lock;
+	struct drm_framebuffer *old_fb;
+	uint32_t src_hw;
+	uint32_t dst_hw;
+	uint32_t dst_yx;
+	uint32_t ctrl0;
+	struct {
+		struct armada_vbl_event update;
+		struct armada_regs regs[13];
+		wait_queue_head_t wait;
+	} vbl;
+	struct armada_plane_properties prop;
+};
+#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
+
+
+static void
+armada_ovl_update_attr(struct armada_plane_properties *prop,
+	struct armada_crtc *dcrtc)
+{
+	writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
+	writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
+	writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
+
+	writel_relaxed(prop->brightness << 16 | prop->contrast,
+		       dcrtc->base + LCD_SPU_CONTRAST);
+	/* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
+	writel_relaxed(prop->saturation << 16,
+		       dcrtc->base + LCD_SPU_SATURATION);
+	writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
+
+	spin_lock_irq(&dcrtc->irq_lock);
+	armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
+		     CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
+		     dcrtc->base + LCD_SPU_DMA_CTRL1);
+
+	armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
+	spin_unlock_irq(&dcrtc->irq_lock);
+}
+
+/* === Plane support === */
+static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
+{
+	struct armada_plane *dplane = data;
+	struct drm_framebuffer *fb;
+
+	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
+
+	spin_lock(&dplane->lock);
+	fb = dplane->old_fb;
+	dplane->old_fb = NULL;
+	spin_unlock(&dplane->lock);
+
+	if (fb)
+		armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
+}
+
+static unsigned armada_limit(int start, unsigned size, unsigned max)
+{
+	int end = start + size;
+	if (end < 0)
+		return 0;
+	if (start < 0)
+		start = 0;
+	return (unsigned)end > max ? max - start : end - start;
+}
+
+static int
+armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+	struct drm_framebuffer *fb,
+	int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
+	uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	uint32_t val, ctrl0;
+	unsigned idx = 0;
+	int ret;
+
+	crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
+	crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
+	ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
+		CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
+		CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
+
+	/* Does the position/size result in nothing to display? */
+	if (crtc_w == 0 || crtc_h == 0) {
+		ctrl0 &= ~CFG_DMA_ENA;
+	}
+
+	/*
+	 * FIXME: if the starting point is off screen, we need to
+	 * adjust src_x, src_y, src_w, src_h appropriately, and
+	 * according to the scale.
+	 */
+
+	if (!dcrtc->plane) {
+		dcrtc->plane = plane;
+		armada_ovl_update_attr(&dplane->prop, dcrtc);
+	}
+
+	/* FIXME: overlay on an interlaced display */
+	/* Just updating the position/size? */
+	if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
+		val = (src_h & 0xffff0000) | src_w >> 16;
+		dplane->src_hw = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
+		val = crtc_h << 16 | crtc_w;
+		dplane->dst_hw = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
+		val = crtc_y << 16 | crtc_x;
+		dplane->dst_yx = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
+		return 0;
+	} else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
+		/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
+		armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
+			       dcrtc->base + LCD_SPU_SRAM_PARA1);
+	}
+
+	ret = wait_event_timeout(dplane->vbl.wait,
+				 list_empty(&dplane->vbl.update.node),
+				 HZ/25);
+	if (ret < 0)
+		return ret;
+
+	if (plane->fb != fb) {
+		struct armada_gem_object *obj = drm_fb_obj(fb);
+		uint32_t sy, su, sv;
+
+		/*
+		 * Take a reference on the new framebuffer - we want to
+		 * hold on to it while the hardware is displaying it.
+		 */
+		drm_framebuffer_reference(fb);
+
+		if (plane->fb) {
+			struct drm_framebuffer *older_fb;
+
+			spin_lock_irq(&dplane->lock);
+			older_fb = dplane->old_fb;
+			dplane->old_fb = plane->fb;
+			spin_unlock_irq(&dplane->lock);
+			if (older_fb)
+				armada_drm_queue_unref_work(dcrtc->crtc.dev,
+							    older_fb);
+		}
+
+		src_y >>= 16;
+		src_x >>= 16;
+		sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
+			src_x * fb->bits_per_pixel / 8;
+		su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
+			src_x;
+		sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
+			src_x;
+
+		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+				     LCD_SPU_DMA_START_ADDR_Y0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, su,
+				     LCD_SPU_DMA_START_ADDR_U0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+				     LCD_SPU_DMA_START_ADDR_V0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+				     LCD_SPU_DMA_START_ADDR_Y1);
+		armada_reg_queue_set(dplane->vbl.regs, idx, su,
+				     LCD_SPU_DMA_START_ADDR_U1);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+				     LCD_SPU_DMA_START_ADDR_V1);
+
+		val = fb->pitches[0] << 16 | fb->pitches[0];
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_PITCH_YC);
+		val = fb->pitches[1] << 16 | fb->pitches[2];
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_PITCH_UV);
+	}
+
+	val = (src_h & 0xffff0000) | src_w >> 16;
+	if (dplane->src_hw != val) {
+		dplane->src_hw = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_HPXL_VLN);
+	}
+	val = crtc_h << 16 | crtc_w;
+	if (dplane->dst_hw != val) {
+		dplane->dst_hw = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DZM_HPXL_VLN);
+	}
+	val = crtc_y << 16 | crtc_x;
+	if (dplane->dst_yx != val) {
+		dplane->dst_yx = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_OVSA_HPXL_VLN);
+	}
+	if (dplane->ctrl0 != ctrl0) {
+		dplane->ctrl0 = ctrl0;
+		armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
+			CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
+			CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
+			CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
+			CFG_YUV2RGB) | CFG_DMA_ENA,
+			LCD_SPU_DMA_CTRL0);
+	}
+	if (idx) {
+		armada_reg_queue_end(dplane->vbl.regs, idx);
+		armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
+	}
+	return 0;
+}
+
+static int armada_plane_disable(struct drm_plane *plane)
+{
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct drm_framebuffer *fb;
+	struct armada_crtc *dcrtc;
+
+	if (!dplane->base.crtc)
+		return 0;
+
+	dcrtc = drm_to_armada_crtc(dplane->base.crtc);
+	dcrtc->plane = NULL;
+
+	spin_lock_irq(&dcrtc->irq_lock);
+	armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
+	armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+	dplane->ctrl0 = 0;
+	spin_unlock_irq(&dcrtc->irq_lock);
+
+	/* Power down the Y/U/V FIFOs */
+	armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
+		       dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+	if (plane->fb)
+		drm_framebuffer_unreference(plane->fb);
+
+	spin_lock_irq(&dplane->lock);
+	fb = dplane->old_fb;
+	dplane->old_fb = NULL;
+	spin_unlock_irq(&dplane->lock);
+	if (fb)
+		drm_framebuffer_unreference(fb);
+
+	return 0;
+}
+
+static void armada_plane_destroy(struct drm_plane *plane)
+{
+	kfree(plane);
+}
+
+static int armada_plane_set_property(struct drm_plane *plane,
+	struct drm_property *property, uint64_t val)
+{
+	struct armada_private *priv = plane->dev->dev_private;
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	bool update_attr = false;
+
+	if (property == priv->colorkey_prop) {
+#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
+		dplane->prop.colorkey_yr = CCC(K2R(val));
+		dplane->prop.colorkey_ug = CCC(K2G(val));
+		dplane->prop.colorkey_vb = CCC(K2B(val));
+#undef CCC
+		update_attr = true;
+	} else if (property == priv->colorkey_min_prop) {
+		dplane->prop.colorkey_yr &= ~0x00ff0000;
+		dplane->prop.colorkey_yr |= K2R(val) << 16;
+		dplane->prop.colorkey_ug &= ~0x00ff0000;
+		dplane->prop.colorkey_ug |= K2G(val) << 16;
+		dplane->prop.colorkey_vb &= ~0x00ff0000;
+		dplane->prop.colorkey_vb |= K2B(val) << 16;
+		update_attr = true;
+	} else if (property == priv->colorkey_max_prop) {
+		dplane->prop.colorkey_yr &= ~0xff000000;
+		dplane->prop.colorkey_yr |= K2R(val) << 24;
+		dplane->prop.colorkey_ug &= ~0xff000000;
+		dplane->prop.colorkey_ug |= K2G(val) << 24;
+		dplane->prop.colorkey_vb &= ~0xff000000;
+		dplane->prop.colorkey_vb |= K2B(val) << 24;
+		update_attr = true;
+	} else if (property == priv->colorkey_val_prop) {
+		dplane->prop.colorkey_yr &= ~0x0000ff00;
+		dplane->prop.colorkey_yr |= K2R(val) << 8;
+		dplane->prop.colorkey_ug &= ~0x0000ff00;
+		dplane->prop.colorkey_ug |= K2G(val) << 8;
+		dplane->prop.colorkey_vb &= ~0x0000ff00;
+		dplane->prop.colorkey_vb |= K2B(val) << 8;
+		update_attr = true;
+	} else if (property == priv->colorkey_alpha_prop) {
+		dplane->prop.colorkey_yr &= ~0x000000ff;
+		dplane->prop.colorkey_yr |= K2R(val);
+		dplane->prop.colorkey_ug &= ~0x000000ff;
+		dplane->prop.colorkey_ug |= K2G(val);
+		dplane->prop.colorkey_vb &= ~0x000000ff;
+		dplane->prop.colorkey_vb |= K2B(val);
+		update_attr = true;
+	} else if (property == priv->colorkey_mode_prop) {
+		dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
+		dplane->prop.colorkey_mode |= CFG_CKMODE(val);
+		update_attr = true;
+	} else if (property == priv->brightness_prop) {
+		dplane->prop.brightness = val - 256;
+		update_attr = true;
+	} else if (property == priv->contrast_prop) {
+		dplane->prop.contrast = val;
+		update_attr = true;
+	} else if (property == priv->saturation_prop) {
+		dplane->prop.saturation = val;
+		update_attr = true;
+	}
+
+	if (update_attr && dplane->base.crtc)
+		armada_ovl_update_attr(&dplane->prop,
+				       drm_to_armada_crtc(dplane->base.crtc));
+
+	return 0;
+}
+
+static const struct drm_plane_funcs armada_plane_funcs = {
+	.update_plane	= armada_plane_update,
+	.disable_plane	= armada_plane_disable,
+	.destroy	= armada_plane_destroy,
+	.set_property	= armada_plane_set_property,
+};
+
+static const uint32_t armada_formats[] = {
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YVU422,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
+static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
+	{ CKMODE_DISABLE, "disabled" },
+	{ CKMODE_Y,       "Y component" },
+	{ CKMODE_U,       "U component" },
+	{ CKMODE_V,       "V component" },
+	{ CKMODE_RGB,     "RGB" },
+	{ CKMODE_R,       "R component" },
+	{ CKMODE_G,       "G component" },
+	{ CKMODE_B,       "B component" },
+};
+
+static int armada_overlay_create_properties(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	if (priv->colorkey_prop)
+		return 0;
+
+	priv->colorkey_prop = drm_property_create_range(dev, 0,
+				"colorkey", 0, 0xffffff);
+	priv->colorkey_min_prop = drm_property_create_range(dev, 0,
+				"colorkey_min", 0, 0xffffff);
+	priv->colorkey_max_prop = drm_property_create_range(dev, 0,
+				"colorkey_max", 0, 0xffffff);
+	priv->colorkey_val_prop = drm_property_create_range(dev, 0,
+				"colorkey_val", 0, 0xffffff);
+	priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
+				"colorkey_alpha", 0, 0xffffff);
+	priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
+				"colorkey_mode",
+				armada_drm_colorkey_enum_list,
+				ARRAY_SIZE(armada_drm_colorkey_enum_list));
+	priv->brightness_prop = drm_property_create_range(dev, 0,
+				"brightness", 0, 256 + 255);
+	priv->contrast_prop = drm_property_create_range(dev, 0,
+				"contrast", 0, 0x7fff);
+	priv->saturation_prop = drm_property_create_range(dev, 0,
+				"saturation", 0, 0x7fff);
+
+	if (!priv->colorkey_prop)
+		return -ENOMEM;
+
+	return 0;
+}
+
+int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_mode_object *mobj;
+	struct armada_plane *dplane;
+	int ret;
+
+	ret = armada_overlay_create_properties(dev);
+	if (ret)
+		return ret;
+
+	dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
+	if (!dplane)
+		return -ENOMEM;
+
+	spin_lock_init(&dplane->lock);
+	init_waitqueue_head(&dplane->vbl.wait);
+	armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
+				  dplane);
+
+	drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
+		       armada_formats, ARRAY_SIZE(armada_formats), false);
+
+	dplane->prop.colorkey_yr = 0xfefefe00;
+	dplane->prop.colorkey_ug = 0x01010100;
+	dplane->prop.colorkey_vb = 0x01010100;
+	dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
+	dplane->prop.brightness = 0;
+	dplane->prop.contrast = 0x4000;
+	dplane->prop.saturation = 0x4000;
+
+	mobj = &dplane->base.base;
+	drm_object_attach_property(mobj, priv->colorkey_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_min_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_max_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_val_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
+				   0x000000);
+	drm_object_attach_property(mobj, priv->colorkey_mode_prop,
+				   CKMODE_RGB);
+	drm_object_attach_property(mobj, priv->brightness_prop, 256);
+	drm_object_attach_property(mobj, priv->contrast_prop,
+				   dplane->prop.contrast);
+	drm_object_attach_property(mobj, priv->saturation_prop,
+				   dplane->prop.saturation);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c
new file mode 100644
index 0000000..00d0fac
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.c
@@ -0,0 +1,139 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_drm.h"
+#include "armada_output.h"
+#include "armada_slave.h"
+
+static int armada_drm_slave_get_modes(struct drm_connector *conn)
+{
+	struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+	int count = 0;
+
+	if (enc) {
+		struct drm_encoder_slave *slave = to_encoder_slave(enc);
+
+		count = slave->slave_funcs->get_modes(enc, conn);
+	}
+
+	return count;
+}
+
+static void armada_drm_slave_destroy(struct drm_encoder *enc)
+{
+	struct drm_encoder_slave *slave = to_encoder_slave(enc);
+	struct i2c_client *client = drm_i2c_encoder_get_client(enc);
+
+	if (slave->slave_funcs)
+		slave->slave_funcs->destroy(enc);
+	if (client)
+		i2c_put_adapter(client->adapter);
+
+	drm_encoder_cleanup(&slave->base);
+	kfree(slave);
+}
+
+static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
+	.destroy	= armada_drm_slave_destroy,
+};
+
+static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
+	.get_modes	= armada_drm_slave_get_modes,
+	.mode_valid	= armada_drm_slave_encoder_mode_valid,
+	.best_encoder	= armada_drm_connector_encoder,
+};
+
+static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
+	.dpms = drm_i2c_encoder_dpms,
+	.save = drm_i2c_encoder_save,
+	.restore = drm_i2c_encoder_restore,
+	.mode_fixup = drm_i2c_encoder_mode_fixup,
+	.prepare = drm_i2c_encoder_prepare,
+	.commit = drm_i2c_encoder_commit,
+	.mode_set = drm_i2c_encoder_mode_set,
+	.detect = drm_i2c_encoder_detect,
+};
+
+static int
+armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
+{
+	const struct armada_drm_slave_config *config = data;
+	struct drm_encoder_slave *slave;
+	struct i2c_adapter *adap;
+	int ret;
+
+	conn->interlace_allowed = config->interlace_allowed;
+	conn->doublescan_allowed = config->doublescan_allowed;
+	conn->polled = config->polled;
+
+	drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
+
+	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+	if (!slave)
+		return -ENOMEM;
+
+	slave->base.possible_crtcs = config->crtcs;
+
+	adap = i2c_get_adapter(config->i2c_adapter_id);
+	if (!adap) {
+		kfree(slave);
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_encoder_init(conn->dev, &slave->base,
+			       &armada_drm_slave_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_ERROR("unable to init encoder\n");
+		i2c_put_adapter(adap);
+		kfree(slave);
+		return ret;
+	}
+
+	ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
+	i2c_put_adapter(adap);
+	if (ret) {
+		DRM_ERROR("unable to init encoder slave\n");
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
+
+	ret = slave->slave_funcs->create_resources(&slave->base, conn);
+	if (ret) {
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	ret = drm_mode_connector_attach_encoder(conn, &slave->base);
+	if (ret) {
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	conn->encoder = &slave->base;
+
+	return ret;
+}
+
+static const struct armada_output_type armada_drm_conn_slave = {
+	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
+	.create		= armada_drm_conn_slave_create,
+	.set_property	= armada_drm_slave_encoder_set_property,
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+	const struct armada_drm_slave_config *config)
+{
+	return armada_output_create(dev, &armada_drm_conn_slave, config);
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h
new file mode 100644
index 0000000..bf2374c
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.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 ARMADA_SLAVE_H
+#define ARMADA_SLAVE_H
+
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+
+struct armada_drm_slave_config {
+	int i2c_adapter_id;
+	uint32_t crtcs;
+	uint8_t polled;
+	bool interlace_allowed;
+	bool doublescan_allowed;
+	struct i2c_board_info info;
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+	const struct armada_drm_slave_config *);
+
+#endif
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 24f4995..e5e6e91 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1135,4 +1135,21 @@ extern int drm_format_horz_chroma_subsampling(uint32_t format);
 extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
+/* Helpers */
+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 /* __DRM_CRTC_H__ */
diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
new file mode 100644
index 0000000..8dec3fd
--- /dev/null
+++ b/include/uapi/drm/armada_drm.h
@@ -0,0 +1,45 @@
+/*
+ * 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_ARMADA_IOCTL_H
+#define DRM_ARMADA_IOCTL_H
+
+#define DRM_ARMADA_GEM_CREATE		0x00
+#define DRM_ARMADA_GEM_MMAP		0x02
+#define DRM_ARMADA_GEM_PWRITE		0x03
+
+#define ARMADA_IOCTL(dir, name, str) \
+	DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
+
+struct drm_armada_gem_create {
+	uint32_t handle;
+	uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_CREATE \
+	ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
+
+struct drm_armada_gem_mmap {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+	uint64_t size;
+	uint64_t addr;
+};
+#define DRM_IOCTL_ARMADA_GEM_MMAP \
+	ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
+
+struct drm_armada_gem_pwrite {
+	uint64_t ptr;
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_PWRITE \
+	ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
+
+#endif
-- 
1.7.4.4

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-06 22:08   ` Russell King
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:08 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel; +Cc: Jason Cooper, Sebastian Hesselbarth

This patch adds support for the pair of LCD controllers on the Marvell
Armada 510 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 via DRM planes
- page flipping of the main scanout buffers
- DRM prime for buffer export/import

This driver is trivial to extend to other Armada SoCs.

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/armada/Kconfig          |   15 +
 drivers/gpu/drm/armada/Makefile         |    7 +
 drivers/gpu/drm/armada/armada_510.c     |   86 +++
 drivers/gpu/drm/armada/armada_crtc.c    |  861 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   74 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++++
 drivers/gpu/drm/armada/armada_drm.h     |  112 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  380 ++++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 ++++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++++
 drivers/gpu/drm/armada/armada_gem.c     |  616 ++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  316 +++++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 ++++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 +++++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 +++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 24 files changed, 4020 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/Kconfig
 create mode 100644 drivers/gpu/drm/armada/Makefile
 create mode 100644 drivers/gpu/drm/armada/armada_510.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
 create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
 create mode 100644 drivers/gpu/drm/armada/armada_drm.h
 create mode 100644 drivers/gpu/drm/armada/armada_drv.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.h
 create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.h
 create mode 100644 drivers/gpu/drm/armada/armada_hw.h
 create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
 create mode 100644 drivers/gpu/drm/armada/armada_output.c
 create mode 100644 drivers/gpu/drm/armada/armada_output.h
 create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.h
 create mode 100644 include/uapi/drm/armada_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d..3b5176d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -225,6 +225,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/armada/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adf..385f460 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_ARMADA) += armada/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
new file mode 100644
index 0000000..c7a0a94
--- /dev/null
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -0,0 +1,15 @@
+config DRM_ARMADA
+	tristate "DRM support for Marvell Armada SoCs"
+	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
+	  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.
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
new file mode 100644
index 0000000..d6f43e0
--- /dev/null
+++ b/drivers/gpu/drm/armada/Makefile
@@ -0,0 +1,7 @@
+armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
+		   armada_gem.o armada_output.o armada_overlay.o \
+		   armada_slave.o
+armada-y	+= armada_510.o
+armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
+
+obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
new file mode 100644
index 0000000..a016888
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ *
+ * Armada 510 (aka Dove) variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int armada510_init(struct armada_private *priv, struct device *dev)
+{
+	priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+
+	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
+		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+	return PTR_RET(priv->extclk[0]);
+}
+
+static int armada510_crtc_init(struct armada_crtc *dcrtc)
+{
+	/* Lower the watermark so to eliminate jitter at higher bandwidths */
+	armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+	return 0;
+}
+
+/*
+ * Armada510 specific SCLK register selection.
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ *
+ * We currently are pretty rudimentary here, always selecting
+ * EXT_REF_CLK_1 for LCD0 and erroring LCD1.  This needs improvement!
+ */
+static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
+	const struct drm_display_mode *mode, uint32_t *sclk)
+{
+	struct armada_private *priv = dcrtc->crtc.dev->dev_private;
+	struct clk *clk = priv->extclk[0];
+	int ret;
+
+	if (dcrtc->num == 1)
+		return -EINVAL;
+
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	if (dcrtc->clk != clk) {
+		ret = clk_prepare_enable(clk);
+		if (ret)
+			return ret;
+		dcrtc->clk = clk;
+	}
+
+	if (sclk) {
+		uint32_t rate, ref, div;
+
+		rate = mode->clock * 1000;
+		ref = clk_round_rate(clk, rate);
+		div = DIV_ROUND_UP(ref, rate);
+		if (div < 1)
+			div = 1;
+
+		clk_set_rate(clk, ref);
+		*sclk = div | SCLK_510_EXTCLK1;
+	}
+
+	return 0;
+}
+
+const struct armada_variant armada510_ops = {
+	.has_spu_adv_reg = true,
+	.init = armada510_init,
+	.crtc_init = armada510_crtc_init,
+	.crtc_compute_clock = armada510_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
new file mode 100644
index 0000000..7b379fd
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -0,0 +1,861 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+struct armada_frame_work {
+	struct drm_pending_vblank_event *event;
+	struct armada_regs regs[4];
+	struct drm_framebuffer *old_fb;
+};
+
+enum csc_mode {
+	CSC_AUTO = 0,
+	CSC_YUV_CCIR601 = 1,
+	CSC_YUV_CCIR709 = 2,
+	CSC_RGB_COMPUTER = 1,
+	CSC_RGB_STUDIO = 2,
+};
+
+/*
+ * 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 Armada 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.)
+ */
+
+void
+armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_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 armada_drm_crtc_update(struct armada_crtc *dcrtc)
+{
+	uint32_t dumb_ctrl;
+
+	dumb_ctrl = dcrtc->cfg_dumb_ctrl;
+
+	if (!dpms_blanked(dcrtc->dpms))
+		dumb_ctrl |= CFG_DUMB_ENA;
+
+	/*
+	 * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might
+	 * be using SPI or GPIO.  If we set this to DUMB_BLANK, we will
+	 * force LCD_D[23:0] to output blank color, overriding the GPIO or
+	 * SPI usage.  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 documentation doesn't indicate what the normal state of
+	 * the sync signals are.  Sebastian Hesselbart kindly probed
+	 * these signals on his board to determine their state.
+	 *
+	 * The non-inverted state of the sync signals is active high.
+	 * Setting these bits makes the appropriate signal active low.
+	 */
+	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 armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
+	int x, int y, struct armada_regs *regs, bool interlaced)
+{
+	struct armada_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 */
+	armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
+	armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
+	armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
+
+	return i;
+}
+
+static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
+	struct armada_frame_work *work)
+{
+	struct drm_device *dev = dcrtc->crtc.dev;
+	unsigned long flags;
+	int ret;
+
+	ret = drm_vblank_get(dev, dcrtc->num);
+	if (ret) {
+		DRM_ERROR("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (!dcrtc->frame_work)
+		dcrtc->frame_work = work;
+	else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	if (ret)
+		drm_vblank_put(dev, dcrtc->num);
+
+	return ret;
+}
+
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
+{
+	struct drm_device *dev = dcrtc->crtc.dev;
+	struct armada_frame_work *work = dcrtc->frame_work;
+
+	dcrtc->frame_work = NULL;
+
+	armada_drm_crtc_update_regs(dcrtc, work->regs);
+
+	if (work->event)
+		drm_send_vblank_event(dev, dcrtc->num, work->event);
+
+	drm_vblank_put(dev, dcrtc->num);
+
+	/* Finally, queue the process-half of the cleanup. */
+	__armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
+	kfree(work);
+}
+
+static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
+	struct drm_framebuffer *fb, bool force)
+{
+	struct armada_frame_work *work;
+
+	if (!fb)
+		return;
+
+	if (force) {
+		/* Display is disabled, so just drop the old fb */
+		drm_framebuffer_unreference(fb);
+		return;
+	}
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (work) {
+		int i = 0;
+		work->event = NULL;
+		work->old_fb = fb;
+		armada_reg_queue_end(work->regs, i);
+
+		if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0)
+			return;
+
+		kfree(work);
+	}
+
+	/*
+	 * Oops - just drop the reference immediately and hope for
+	 * the best.  The worst that will happen is the buffer gets
+	 * reused before it has finished being displayed.
+	 */
+	drm_framebuffer_unreference(fb);
+}
+
+static void armada_drm_vblank_off(struct armada_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->frame_work)
+		armada_drm_crtc_complete_frame_work(dcrtc);
+	spin_unlock_irq(&dev->event_lock);
+}
+
+void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
+	int idx)
+{
+}
+
+void armada_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 armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	if (dcrtc->dpms != dpms) {
+		dcrtc->dpms = dpms;
+		armada_drm_crtc_update(dcrtc);
+		if (dpms_blanked(dpms))
+			armada_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 armada_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct drm_plane *plane;
+
+	/*
+	 * If we have an overlay plane associated with this CRTC, disable
+	 * it before the modeset to avoid its coordinates being outside
+	 * the new mode parameters.  DRM doesn't provide help with this.
+	 */
+	plane = dcrtc->plane;
+	if (plane) {
+		struct drm_framebuffer *fb = plane->fb;
+
+		plane->funcs->disable_plane(plane);
+		plane->fb = NULL;
+		plane->crtc = NULL;
+		drm_framebuffer_unreference(fb);
+	}
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	if (dcrtc->dpms != DRM_MODE_DPMS_ON) {
+		dcrtc->dpms = DRM_MODE_DPMS_ON;
+		armada_drm_crtc_update(dcrtc);
+	}
+}
+
+/* The mode_config.mutex will be held for this call */
+static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+	const struct drm_display_mode *mode, struct drm_display_mode *adj)
+{
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	int ret;
+
+	/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
+	if (!priv->variant->has_spu_adv_reg &&
+	    adj->flags & DRM_MODE_FLAG_INTERLACE)
+		return false;
+
+	/* Check whether the display mode is possible */
+	ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
+	if (ret)
+		return false;
+
+	return true;
+}
+
+void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+{
+	struct armada_vbl_event *e, *n;
+	void __iomem *base = dcrtc->base;
+
+	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);
+
+	if (stat & VSYNC_IRQ)
+		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) {
+		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 &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
+		val |= dcrtc->v[i].spu_adv_reg;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
+	}
+	spin_unlock(&dcrtc->irq_lock);
+
+	if (stat & GRA_FRAME_IRQ) {
+		struct drm_device *dev = dcrtc->crtc.dev;
+
+		spin_lock(&dev->event_lock);
+		if (dcrtc->frame_work)
+			armada_drm_crtc_complete_frame_work(dcrtc);
+		spin_unlock(&dev->event_lock);
+
+		wake_up(&dcrtc->frame_wait);
+	}
+}
+
+/* These are locked by dev->vbl_lock */
+void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
+{
+	if (dcrtc->irq_ena & mask) {
+		dcrtc->irq_ena &= ~mask;
+		writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+	}
+}
+
+void armada_drm_crtc_enable_irq(struct armada_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);
+	}
+}
+
+static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
+{
+	struct drm_display_mode *adj = &dcrtc->crtc.mode;
+	uint32_t val = 0;
+
+	if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709)
+		val |= CFG_CSC_YUV_CCIR709;
+	if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO)
+		val |= CFG_CSC_RGB_STUDIO;
+
+	/*
+	 * In auto mode, set the colorimetry, based upon the HDMI spec.
+	 * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use
+	 * ITU601.  It may be more appropriate to set this depending on
+	 * the source - but what if the graphic frame is YUV and the
+	 * video frame is RGB?
+	 */
+	if ((adj->hdisplay == 1280 && adj->vdisplay == 720 &&
+	     !(adj->flags & DRM_MODE_FLAG_INTERLACE)) ||
+	    (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
+		if (dcrtc->csc_yuv_mode == CSC_AUTO)
+			val |= CFG_CSC_YUV_CCIR709;
+	}
+
+	/*
+	 * We assume we're connected to a TV-like device, so the YUV->RGB
+	 * conversion should produce a limited range.  We should set this
+	 * depending on the connectors attached to this CRTC, and what
+	 * kind of device they report being connected.
+	 */
+	if (dcrtc->csc_rgb_mode == CSC_AUTO)
+		val |= CFG_CSC_RGB_STUDIO;
+
+	return val;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_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 armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_regs regs[17];
+	uint32_t lm, rm, tm, bm, val, sclk;
+	unsigned long flags;
+	unsigned i;
+	bool interlaced;
+
+	drm_framebuffer_reference(crtc->fb);
+
+	interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
+
+	i = armada_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->frame_work);
+
+	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);
+	}
+
+	/* Now compute the divider for real */
+	priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+
+	/* Ensure graphic fifo is enabled */
+	armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
+	armada_reg_queue_set(regs, i, 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 | ADV_VSYNCOFFEN;
+
+	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 | ADV_VSYNCOFFEN;
+	} else {
+		dcrtc->v[0] = dcrtc->v[1];
+	}
+
+	val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay;
+
+	armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
+	armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
+	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
+	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
+			   LCD_SPUT_V_H_TOTAL);
+
+	if (priv->variant->has_spu_adv_reg)
+		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
+				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
+				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
+
+	val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
+	val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
+	val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
+
+	if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
+		val |= CFG_PALETTE_ENA;
+
+	if (interlaced)
+		val |= CFG_GRA_FTOGGLE;
+
+	armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT |
+			     CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
+					 CFG_SWAPYU | CFG_YUV2RGB) |
+			     CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
+			     LCD_SPU_DMA_CTRL0);
+
+	val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
+	armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
+
+	val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc);
+	armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL);
+	armada_reg_queue_end(regs, i);
+
+	armada_drm_crtc_update_regs(dcrtc, regs);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+
+	armada_drm_crtc_update(dcrtc);
+
+	drm_vblank_post_modeset(crtc->dev, dcrtc->num);
+	armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+	return 0;
+}
+
+/* The mode_config.mutex will be held for this call */
+static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+	struct drm_framebuffer *old_fb)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_regs regs[4];
+	unsigned i;
+
+	i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
+				    dcrtc->interlaced);
+	armada_reg_queue_end(regs, i);
+
+	/* Wait for pending flips to complete */
+	wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+
+	/* Take a reference to the new fb as we're using it */
+	drm_framebuffer_reference(crtc->fb);
+
+	/* Update the base in the CRTC */
+	armada_drm_crtc_update_regs(dcrtc, regs);
+
+	/* Drop our previously held reference */
+	armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
+
+	return 0;
+}
+
+static void armada_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+/* The mode_config.mutex will be held for this call */
+static void armada_drm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+
+	armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+	armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
+
+	/* Power down most RAMs and FIFOs */
+	writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+		       CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+		       CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+}
+
+static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
+	.dpms		= armada_drm_crtc_dpms,
+	.prepare	= armada_drm_crtc_prepare,
+	.commit		= armada_drm_crtc_commit,
+	.mode_fixup	= armada_drm_crtc_mode_fixup,
+	.mode_set	= armada_drm_crtc_mode_set,
+	.mode_set_base	= armada_drm_crtc_mode_set_base,
+	.load_lut	= armada_drm_crtc_load_lut,
+	.disable	= armada_drm_crtc_disable,
+};
+
+static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+
+	priv->dcrtc[dcrtc->num] = NULL;
+	drm_crtc_cleanup(&dcrtc->crtc);
+
+	if (!IS_ERR(dcrtc->clk))
+		clk_disable_unprepare(dcrtc->clk);
+
+	kfree(dcrtc);
+}
+
+/*
+ * The mode_config lock is held here, to prevent races between this
+ * and a mode_set.
+ */
+static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
+	struct drm_framebuffer *fb, struct drm_pending_vblank_event *event)
+{
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_frame_work *work;
+	struct drm_device *dev = crtc->dev;
+	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;
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work)
+		return -ENOMEM;
+
+	work->event = event;
+	work->old_fb = dcrtc->crtc.fb;
+
+	i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
+				    dcrtc->interlaced);
+	armada_reg_queue_end(work->regs, i);
+
+	/*
+	 * Hold the old framebuffer for the work - DRM appears to drop our
+	 * reference to the old framebuffer in drm_mode_page_flip_ioctl().
+	 */
+	drm_framebuffer_reference(work->old_fb);
+
+	ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
+	if (ret) {
+		/*
+		 * Undo our reference above; DRM does not drop the reference
+		 * to this object on error, so that's okay.
+		 */
+		drm_framebuffer_unreference(work->old_fb);
+		kfree(work);
+		return ret;
+	}
+
+	/*
+	 * Don't take a reference on the new framebuffer;
+	 * drm_mode_page_flip_ioctl() has already grabbed a reference and
+	 * will _not_ drop that reference on successful return from this
+	 * function.  Simply mark this new framebuffer as the current one.
+	 */
+	dcrtc->crtc.fb = fb;
+
+	/*
+	 * Finally, if the display is blanked, we won't receive an
+	 * interrupt, so complete it now.
+	 */
+	if (dpms_blanked(dcrtc->dpms)) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		if (dcrtc->frame_work)
+			armada_drm_crtc_complete_frame_work(dcrtc);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+
+	return 0;
+}
+
+static int
+armada_drm_crtc_set_property(struct drm_crtc *crtc,
+	struct drm_property *property, uint64_t val)
+{
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	bool update_csc = false;
+
+	if (property == priv->csc_yuv_prop) {
+		dcrtc->csc_yuv_mode = val;
+		update_csc = true;
+	} else if (property == priv->csc_rgb_prop) {
+		dcrtc->csc_rgb_mode = val;
+		update_csc = true;
+	}
+
+	if (update_csc) {
+		uint32_t val;
+
+		val = dcrtc->spu_iopad_ctrl |
+		      armada_drm_crtc_calculate_csc(dcrtc);
+		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+	}
+
+	return 0;
+}
+
+static struct drm_crtc_funcs armada_crtc_funcs = {
+	.destroy	= armada_drm_crtc_destroy,
+	.set_config	= drm_crtc_helper_set_config,
+	.page_flip	= armada_drm_crtc_page_flip,
+	.set_property	= armada_drm_crtc_set_property,
+};
+
+static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
+	{ CSC_AUTO,        "Auto" },
+	{ CSC_YUV_CCIR601, "CCIR601" },
+	{ CSC_YUV_CCIR709, "CCIR709" },
+};
+
+static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = {
+	{ CSC_AUTO,         "Auto" },
+	{ CSC_RGB_COMPUTER, "Computer system" },
+	{ CSC_RGB_STUDIO,   "Studio" },
+};
+
+static int armada_drm_crtc_create_properties(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	if (priv->csc_yuv_prop)
+		return 0;
+
+	priv->csc_yuv_prop = drm_property_create_enum(dev, 0,
+				"CSC_YUV", armada_drm_csc_yuv_enum_list,
+				ARRAY_SIZE(armada_drm_csc_yuv_enum_list));
+	priv->csc_rgb_prop = drm_property_create_enum(dev, 0,
+				"CSC_RGB", armada_drm_csc_rgb_enum_list,
+				ARRAY_SIZE(armada_drm_csc_rgb_enum_list));
+
+	if (!priv->csc_yuv_prop || !priv->csc_rgb_prop)
+		return -ENOMEM;
+
+	return 0;
+}
+
+int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
+	struct resource *res)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc;
+	void __iomem *base;
+	int ret;
+
+	ret = armada_drm_crtc_create_properties(dev);
+	if (ret)
+		return ret;
+
+	base = devm_request_and_ioremap(dev->dev, res);
+	if (!base) {
+		DRM_ERROR("failed to ioremap register\n");
+		return -ENOMEM;
+	}
+
+	dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
+	if (!dcrtc) {
+		DRM_ERROR("failed to allocate Armada crtc\n");
+		return -ENOMEM;
+	}
+
+	dcrtc->base = base;
+	dcrtc->num = num;
+	dcrtc->clk = ERR_PTR(-EINVAL);
+	dcrtc->csc_yuv_mode = CSC_AUTO;
+	dcrtc->csc_rgb_mode = CSC_AUTO;
+	dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
+	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
+	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(dcrtc->spu_iopad_ctrl,
+		       dcrtc->base + LCD_SPU_IOPAD_CONTROL);
+	writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
+	writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+		       CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
+		       CFG_PDWN64x66, 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);
+
+	if (priv->variant->crtc_init) {
+		ret = priv->variant->crtc_init(dcrtc);
+		if (ret) {
+			kfree(dcrtc);
+			return ret;
+		}
+	}
+
+	/* Ensure AXI pipeline is enabled */
+	armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
+
+	priv->dcrtc[dcrtc->num] = dcrtc;
+
+	drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
+	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
+
+	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
+				   dcrtc->csc_yuv_mode);
+	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
+				   dcrtc->csc_rgb_mode);
+
+	return armada_overlay_plane_create(dev, 1 << dcrtc->num);
+}
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
new file mode 100644
index 0000000..972da53
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -0,0 +1,74 @@
+/*
+ * 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 ARMADA_CRTC_H
+#define ARMADA_CRTC_H
+
+struct armada_gem_object;
+
+struct armada_regs {
+	uint32_t offset;
+	uint32_t mask;
+	uint32_t val;
+};
+
+#define armada_reg_queue_mod(_r, _i, _v, _m, _o)	\
+	do {					\
+		struct armada_regs *__reg = _r;	\
+		__reg[_i].offset = _o;		\
+		__reg[_i].mask = ~(_m);		\
+		__reg[_i].val = _v;		\
+		_i++;				\
+	} while (0)
+
+#define armada_reg_queue_set(_r, _i, _v, _o)	\
+	armada_reg_queue_mod(_r, _i, _v, ~0, _o)
+
+#define armada_reg_queue_end(_r, _i)		\
+	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
+
+struct armada_frame_work;
+
+struct armada_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;
+	uint8_t			csc_yuv_mode;
+	uint8_t			csc_rgb_mode;
+
+	struct drm_plane	*plane;
+
+	int			dpms;
+	uint32_t		cfg_dumb_ctrl;
+	uint32_t		dumb_ctrl;
+	uint32_t		spu_iopad_ctrl;
+
+	wait_queue_head_t	frame_wait;
+	struct armada_frame_work *frame_work;
+
+	spinlock_t		irq_lock;
+	uint32_t		irq_ena;
+	struct list_head	vbl_list;
+};
+#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
+
+int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
+void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
+void armada_drm_crtc_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
+void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
new file mode 100644
index 0000000..612f375
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_debugfs.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+
+static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct armada_private *priv = dev->dev_private;
+	int ret;
+
+	mutex_lock(&dev->struct_mutex);
+	ret = drm_mm_dump_table(m, &priv->linear);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+static int armada_debugfs_reg_show(struct seq_file *m, void *data)
+{
+	struct drm_device *dev = m->private;
+	struct armada_private *priv = dev->dev_private;
+	int n, i;
+
+	if (priv) {
+		for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+			struct armada_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 armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, armada_debugfs_reg_show, inode->i_private);
+}
+
+static const struct file_operations fops_reg_r = {
+	.owner = THIS_MODULE,
+	.open = armada_debugfs_reg_r_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int armada_debugfs_write(struct file *file, const char __user *ptr,
+	size_t len, loff_t *off)
+{
+	struct drm_device *dev = file->private_data;
+	struct armada_private *priv = dev->dev_private;
+	struct armada_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 armada_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 = armada_debugfs_reg_w_open,
+	.write = armada_debugfs_write,
+	.llseek = noop_llseek,
+};
+
+static struct drm_info_list armada_debugfs_list[] = {
+	{ "gem_linear", armada_debugfs_gem_linear_show, 0 },
+};
+#define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_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 armada_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 armada_drm_debugfs_init(struct drm_minor *minor)
+{
+	int ret;
+
+	ret = drm_debugfs_create_files(armada_debugfs_list,
+				       ARMADA_DEBUGFS_ENTRIES,
+				       minor->debugfs_root, minor);
+	if (ret)
+		return ret;
+
+	ret = armada_debugfs_create(minor->debugfs_root, minor,
+				   "reg", S_IFREG | S_IRUSR, &fops_reg_r);
+	if (ret)
+		goto err_1;
+
+	ret = armada_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(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+				 minor);
+	return ret;
+}
+
+void armada_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(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
+				 minor);
+}
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
new file mode 100644
index 0000000..e8c4f80
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -0,0 +1,112 @@
+/*
+ * 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 ARMADA_DRM_H
+#define ARMADA_DRM_H
+
+#include <linux/kfifo.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+#include <drm/drmP.h>
+
+struct armada_crtc;
+struct armada_gem_object;
+struct clk;
+struct drm_fb_helper;
+
+static inline void
+armada_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 inline uint32_t armada_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 armada_vbl_event {
+	struct list_head	node;
+	void			*data;
+	void			(*fn)(struct armada_crtc *, void *);
+};
+void armada_drm_vbl_event_add(struct armada_crtc *,
+	struct armada_vbl_event *);
+void armada_drm_vbl_event_remove(struct armada_crtc *,
+	struct armada_vbl_event *);
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
+	struct armada_vbl_event *);
+#define armada_drm_vbl_event_init(_e, _f, _d) do {	\
+	struct armada_vbl_event *__e = _e;		\
+	INIT_LIST_HEAD(&__e->node);			\
+	__e->data = _d;					\
+	__e->fn = _f;					\
+} while (0)
+
+
+struct armada_private;
+
+struct armada_variant {
+	bool	has_spu_adv_reg;
+	int (*init)(struct armada_private *, struct device *);
+	int (*crtc_init)(struct armada_crtc *);
+	int (*crtc_compute_clock)(struct armada_crtc *,
+				  const struct drm_display_mode *,
+				  uint32_t *);
+};
+
+/* Variant ops */
+extern const struct armada_variant armada510_ops;
+
+struct armada_private {
+	const struct armada_variant *variant;
+	struct work_struct	fb_unref_work;
+	DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
+	struct drm_fb_helper	*fbdev;
+	struct armada_crtc	*dcrtc[2];
+	struct drm_mm		linear;
+	struct clk		*extclk[2];
+	struct drm_property	*csc_yuv_prop;
+	struct drm_property	*csc_rgb_prop;
+	struct drm_property	*colorkey_prop;
+	struct drm_property	*colorkey_min_prop;
+	struct drm_property	*colorkey_max_prop;
+	struct drm_property	*colorkey_val_prop;
+	struct drm_property	*colorkey_alpha_prop;
+	struct drm_property	*colorkey_mode_prop;
+	struct drm_property	*brightness_prop;
+	struct drm_property	*contrast_prop;
+	struct drm_property	*saturation_prop;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry		*de;
+#endif
+};
+
+void __armada_drm_queue_unref_work(struct drm_device *,
+	struct drm_framebuffer *);
+void armada_drm_queue_unref_work(struct drm_device *,
+	struct drm_framebuffer *);
+
+extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
+
+int armada_fbdev_init(struct drm_device *);
+void armada_fbdev_fini(struct drm_device *);
+
+int armada_overlay_plane_create(struct drm_device *, unsigned long);
+
+int armada_drm_debugfs_init(struct drm_minor *);
+void armada_drm_debugfs_cleanup(struct drm_minor *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
new file mode 100644
index 0000000..7bfab9a
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -0,0 +1,380 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+static void armada_drm_unref_work(struct work_struct *work)
+{
+	struct armada_private *priv =
+		container_of(work, struct armada_private, fb_unref_work);
+	struct drm_framebuffer *fb;
+
+	while (kfifo_get(&priv->fb_unref, &fb))
+		drm_framebuffer_unreference(fb);
+}
+
+/* Must be called with dev->event_lock held */
+void __armada_drm_queue_unref_work(struct drm_device *dev,
+	struct drm_framebuffer *fb)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	/*
+	 * Yes, we really must jump through these hoops just to store a
+	 * _pointer_ to something into the kfifo.  This is utterly insane
+	 * and idiotic, because it kfifo requires the _data_ pointed to by
+	 * the pointer const, not the pointer itself.  Not only that, but
+	 * you have to pass a pointer _to_ the pointer you want stored.
+	 */
+	const struct drm_framebuffer *silly_api_alert = fb;
+	WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
+	schedule_work(&priv->fb_unref_work);
+}
+
+void armada_drm_queue_unref_work(struct drm_device *dev,
+	struct drm_framebuffer *fb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	__armada_drm_queue_unref_work(dev, fb);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int armada_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	const struct platform_device_id *id;
+	struct armada_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;
+
+		/* Resources above 64K are graphics memory */
+		if (resource_size(r) > SZ_64K)
+			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), "armada-drm"))
+		return -EBUSY;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		DRM_ERROR("failed to allocate private\n");
+		return -ENOMEM;
+	}
+
+	dev->dev_private = priv;
+
+	/* Get the implementation specific driver data. */
+	id = platform_get_device_id(dev->platformdev);
+	if (!id)
+		return -ENXIO;
+
+	priv->variant = (struct armada_variant *)id->driver_data;
+
+	ret = priv->variant->init(priv, dev->dev);
+	if (ret)
+		return ret;
+
+	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
+	INIT_KFIFO(priv->fb_unref);
+
+	/* Mode setting support */
+	drm_mode_config_init(dev);
+	dev->mode_config.min_width = 320;
+	dev->mode_config.min_height = 200;
+
+	/*
+	 * With vscale enabled, the maximum width is 1920 due to the
+	 * 1920 by 3 lines RAM
+	 */
+	dev->mode_config.max_width = 1920;
+	dev->mode_config.max_height = 2048;
+
+	dev->mode_config.preferred_depth = 24;
+	dev->mode_config.funcs = &armada_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++) {
+		if (!res[n])
+			break;
+
+		ret = armada_drm_crtc_create(dev, n, res[n]);
+		if (ret)
+			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 = armada_fbdev_init(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);
+	flush_work(&priv->fb_unref_work);
+
+	return ret;
+}
+
+static int armada_drm_unload(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	drm_kms_helper_poll_fini(dev);
+	armada_fbdev_fini(dev);
+	drm_irq_uninstall(dev);
+	drm_mode_config_cleanup(dev);
+	drm_mm_takedown(&priv->linear);
+	flush_work(&priv->fb_unref_work);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
+	struct armada_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 armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	if (!list_empty(&evt->node)) {
+		list_del_init(&evt->node);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+	}
+}
+
+void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dcrtc->irq_lock, flags);
+	armada_drm_vbl_event_remove(dcrtc, evt);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+}
+
+/* These are called under the vbl_lock. */
+static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
+{
+	struct armada_private *priv = dev->dev_private;
+	armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+	return 0;
+}
+
+static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
+{
+	struct armada_private *priv = dev->dev_private;
+	armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
+}
+
+static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
+{
+	struct drm_device *dev = arg;
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc = priv->dcrtc[0];
+	uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+	irqreturn_t handled = IRQ_NONE;
+
+	/*
+	 * 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|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+		armada_drm_crtc_irq(dcrtc, stat);
+		handled = IRQ_HANDLED;
+	}
+
+	return handled;
+}
+
+static int armada_drm_irq_postinstall(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_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 armada_drm_irq_uninstall(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct armada_crtc *dcrtc = priv->dcrtc[0];
+
+	writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+}
+
+static struct drm_ioctl_desc armada_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
+		DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
+		DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
+		DRM_UNLOCKED),
+};
+
+static const struct file_operations armada_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,
+};
+
+static struct drm_driver armada_drm_driver = {
+	.load			= armada_drm_load,
+	.open			= NULL,
+	.preclose		= NULL,
+	.postclose		= NULL,
+	.lastclose		= NULL,
+	.unload			= armada_drm_unload,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= armada_drm_enable_vblank,
+	.disable_vblank		= armada_drm_disable_vblank,
+	.irq_handler		= armada_drm_irq_handler,
+	.irq_postinstall	= armada_drm_irq_postinstall,
+	.irq_uninstall		= armada_drm_irq_uninstall,
+#ifdef CONFIG_DEBUG_FS
+	.debugfs_init		= armada_drm_debugfs_init,
+	.debugfs_cleanup	= armada_drm_debugfs_cleanup,
+#endif
+	.gem_init_object	= NULL,
+	.gem_free_object	= armada_gem_free_object,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_export	= armada_gem_prime_export,
+	.gem_prime_import	= armada_gem_prime_import,
+	.dumb_create		= armada_gem_dumb_create,
+	.dumb_map_offset	= armada_gem_dumb_map_offset,
+	.dumb_destroy		= armada_gem_dumb_destroy,
+	.gem_vm_ops		= &armada_gem_vm_ops,
+	.major			= 1,
+	.minor			= 0,
+	.name			= "armada-drm",
+	.desc			= "Armada SoC DRM",
+	.date			= "20120730",
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
+				  DRIVER_HAVE_IRQ | DRIVER_PRIME,
+	.ioctls			= armada_ioctls,
+	.fops			= &armada_drm_fops,
+};
+
+static int armada_drm_probe(struct platform_device *pdev)
+{
+	return drm_platform_init(&armada_drm_driver, pdev);
+}
+
+static int armada_drm_remove(struct platform_device *pdev)
+{
+	drm_platform_exit(&armada_drm_driver, pdev);
+	return 0;
+}
+
+static const struct platform_device_id armada_drm_platform_ids[] = {
+	{
+		.name		= "armada-drm",
+		.driver_data	= (unsigned long)&armada510_ops,
+	}, {
+		.name		= "armada-510-drm",
+		.driver_data	= (unsigned long)&armada510_ops,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
+
+static struct platform_driver armada_drm_platform_driver = {
+	.probe	= armada_drm_probe,
+	.remove	= armada_drm_remove,
+	.driver	= {
+		.name	= "armada-drm",
+		.owner	= THIS_MODULE,
+	},
+	.id_table = armada_drm_platform_ids,
+};
+
+static int __init armada_drm_init(void)
+{
+	armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
+	return platform_driver_register(&armada_drm_platform_driver);
+}
+module_init(armada_drm_init);
+
+static void __exit armada_drm_exit(void)
+{
+	platform_driver_unregister(&armada_drm_platform_driver);
+}
+module_exit(armada_drm_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Armada DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:armada-drm");
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c
new file mode 100644
index 0000000..1c90969
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+
+static void armada_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+
+	drm_framebuffer_cleanup(&dfb->fb);
+	drm_gem_object_unreference_unlocked(&dfb->obj->obj);
+	kfree(dfb);
+}
+
+static int armada_fb_create_handle(struct drm_framebuffer *fb,
+	struct drm_file *dfile, unsigned int *handle)
+{
+	struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
+	return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
+}
+
+static const struct drm_framebuffer_funcs armada_fb_funcs = {
+	.destroy	= armada_fb_destroy,
+	.create_handle	= armada_fb_create_handle,
+};
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
+	struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
+{
+	struct armada_framebuffer *dfb;
+	uint8_t format, config;
+	int ret;
+
+	switch (mode->pixel_format) {
+#define FMT(drm, fmt, mod)		\
+	case DRM_FORMAT_##drm:		\
+		format = CFG_##fmt;	\
+		config = mod;		\
+		break
+	FMT(RGB565,	565,		CFG_SWAPRB);
+	FMT(BGR565,	565,		0);
+	FMT(ARGB1555,	1555,		CFG_SWAPRB);
+	FMT(ABGR1555,	1555,		0);
+	FMT(RGB888,	888PACK,	CFG_SWAPRB);
+	FMT(BGR888,	888PACK,	0);
+	FMT(XRGB8888,	X888,		CFG_SWAPRB);
+	FMT(XBGR8888,	X888,		0);
+	FMT(ARGB8888,	8888,		CFG_SWAPRB);
+	FMT(ABGR8888,	8888,		0);
+	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
+	FMT(UYVY,	422PACK,	CFG_YUV2RGB);
+	FMT(VYUY,	422PACK,	CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(YVYU,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU);
+	FMT(YUV422,	422,		CFG_YUV2RGB);
+	FMT(YVU422,	422,		CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(YUV420,	420,		CFG_YUV2RGB);
+	FMT(YVU420,	420,		CFG_YUV2RGB | CFG_SWAPUV);
+	FMT(C8,		PSEUDO8,	0);
+#undef FMT
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
+	dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
+	if (!dfb) {
+		DRM_ERROR("failed to allocate Armada fb object\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dfb->fmt = format;
+	dfb->mod = config;
+	dfb->obj = obj;
+
+	drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
+
+	ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
+	if (ret) {
+		kfree(dfb);
+		return ERR_PTR(ret);
+	}
+
+	/*
+	 * Take a reference on our object as we're successful - the
+	 * caller already holds a reference, which keeps us safe for
+	 * the above call, but the caller will drop their reference
+	 * to it.  Hence we need to take our own reference.
+	 */
+	drm_gem_object_reference(&obj->obj);
+
+	return dfb;
+}
+
+static struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
+	struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
+{
+	struct armada_gem_object *obj;
+	struct armada_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 &&
+	    (mode->handles[0] != mode->handles[1] ||
+	     mode->handles[0] != mode->handles[2])) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]);
+	if (!obj) {
+		ret = -ENOENT;
+		goto err;
+	}
+
+	if (obj->obj.import_attach && !obj->sgt) {
+		ret = armada_gem_map_import(obj);
+		if (ret)
+			goto err_unref;
+	}
+
+	/* Framebuffer objects must have a valid device address for scanout */
+	if (obj->dev_addr == DMA_ERROR_CODE) {
+		ret = -EINVAL;
+		goto err_unref;
+	}
+
+	dfb = armada_framebuffer_create(dev, mode, obj);
+	if (IS_ERR(dfb)) {
+		ret = PTR_ERR(dfb);
+		goto err;
+	}
+
+	drm_gem_object_unreference_unlocked(&obj->obj);
+
+	return &dfb->fb;
+
+ err_unref:
+	drm_gem_object_unreference_unlocked(&obj->obj);
+ err:
+	DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
+	return ERR_PTR(ret);
+}
+
+static void armada_output_poll_changed(struct drm_device *dev)
+{
+	struct armada_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 armada_drm_mode_config_funcs = {
+	.fb_create		= armada_fb_create,
+	.output_poll_changed	= armada_output_poll_changed,
+};
diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h
new file mode 100644
index 0000000..ce3f12e
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.h
@@ -0,0 +1,24 @@
+/*
+ * 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 ARMADA_FB_H
+#define ARMADA_FB_H
+
+struct armada_framebuffer {
+	struct drm_framebuffer	fb;
+	struct armada_gem_object *obj;
+	uint8_t			fmt;
+	uint8_t			mod;
+};
+#define drm_fb_to_armada_fb(dfb) \
+	container_of(dfb, struct armada_framebuffer, fb)
+#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj
+
+struct armada_framebuffer *armada_framebuffer_create(struct drm_device *,
+	struct drm_mode_fb_cmd2 *, struct armada_gem_object *);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
new file mode 100644
index 0000000..dd5ea77
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -0,0 +1,202 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+
+static /*const*/ struct fb_ops armada_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 armada_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 armada_framebuffer *dfb;
+	struct armada_gem_object *obj;
+	struct fb_info *info;
+	int size, ret;
+	void *ptr;
+
+	memset(&mode, 0, sizeof(mode));
+	mode.width = sizes->surface_width;
+	mode.height = sizes->surface_height;
+	mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
+	mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+					sizes->surface_depth);
+
+	size = mode.pitches[0] * mode.height;
+	obj = armada_gem_alloc_private_object(dev, size);
+	if (!obj) {
+		DRM_ERROR("failed to allocate fb memory\n");
+		return -ENOMEM;
+	}
+
+	ret = armada_gem_linear_back(dev, obj);
+	if (ret) {
+		drm_gem_object_unreference_unlocked(&obj->obj);
+		return ret;
+	}
+
+	ptr = armada_gem_map_object(dev, obj);
+	if (!ptr) {
+		drm_gem_object_unreference_unlocked(&obj->obj);
+		return -ENOMEM;
+	}
+
+	dfb = armada_framebuffer_create(dev, &mode, obj);
+
+	/*
+	 * A reference is now held by the framebuffer object if
+	 * successful, otherwise this drops the ref for the error path.
+	 */
+	drm_gem_object_unreference_unlocked(&obj->obj);
+
+	if (IS_ERR(dfb))
+		return PTR_ERR(dfb);
+
+	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, "armada-drmfb", sizeof(info->fix.id));
+	info->par = fbh;
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+	info->fbops = &armada_fb_ops;
+	info->fix.smem_start = obj->phys_addr;
+	info->fix.smem_len = obj->obj.size;
+	info->screen_size = obj->obj.size;
+	info->screen_base = ptr;
+	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);
+
+	return 0;
+
+ err_fbcmap:
+	framebuffer_release(info);
+ err_fballoc:
+	dfb->fb.funcs->destroy(&dfb->fb);
+	return ret;
+}
+
+static int armada_fb_probe(struct drm_fb_helper *fbh,
+	struct drm_fb_helper_surface_size *sizes)
+{
+	int ret = 0;
+
+	if (!fbh->fb) {
+		ret = armada_fb_create(fbh, sizes);
+		if (ret == 0)
+			ret = 1;
+	}
+	return ret;
+}
+
+static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+	.gamma_set	= armada_drm_crtc_gamma_set,
+	.gamma_get	= armada_drm_crtc_gamma_get,
+	.fb_probe	= armada_fb_probe,
+};
+
+int armada_fbdev_init(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_fb_helper *fbh;
+	int ret;
+
+	fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
+	if (!fbh)
+		return -ENOMEM;
+
+	priv->fbdev = fbh;
+
+	fbh->funcs = &armada_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:
+	priv->fbdev = NULL;
+	return ret;
+}
+
+void armada_fbdev_fini(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_fb_helper *fbh = priv->fbdev;
+
+	if (fbh) {
+		struct fb_info *info = fbh->fbdev;
+
+		if (info) {
+			unregister_framebuffer(info);
+			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);
+
+		priv->fbdev = NULL;
+	}
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
new file mode 100644
index 0000000..c865a9a
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -0,0 +1,616 @@
+/*
+ * 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/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/shmem_fs.h>
+#include <drm/drmP.h>
+#include "armada_drm.h"
+#include "armada_gem.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct armada_gem_object *obj = drm_to_armada_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:
+	case -EBUSY:
+		return VM_FAULT_NOPAGE;
+	case -ENOMEM:
+		return VM_FAULT_OOM;
+	default:
+		return VM_FAULT_SIGBUS;
+	}
+}
+
+const struct vm_operations_struct armada_gem_vm_ops = {
+	.fault	= armada_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);
+}
+
+/* dev->struct_mutex is held here */
+void armada_gem_free_object(struct drm_gem_object *obj)
+{
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+
+	DRM_DEBUG_DRIVER("release obj %p\n", dobj);
+
+	drm_gem_free_mmap_offset(&dobj->obj);
+
+	if (dobj->page) {
+		/* page backed memory */
+		unsigned int order = get_order(dobj->obj.size);
+		__free_pages(dobj->page, order);
+	} else if (dobj->linear) {
+		/* linear backed memory */
+		drm_mm_remove_node(dobj->linear);
+		kfree(dobj->linear);
+		if (dobj->addr)
+			iounmap(dobj->addr);
+	}
+
+	if (dobj->obj.import_attach) {
+		/* We only ever display imported data */
+		dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
+					 DMA_TO_DEVICE);
+		drm_prime_gem_destroy(&dobj->obj, NULL);
+	}
+
+	drm_gem_object_release(&dobj->obj);
+
+	kfree(dobj);
+}
+
+int
+armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
+{
+	struct armada_private *priv = dev->dev_private;
+	size_t size = obj->obj.size;
+
+	if (obj->page || obj->linear)
+		return 0;
+
+	/*
+	 * If it is a small allocation (typically cursor, which will
+	 * be 32x64 or 64x32 ARGB pixels) try to get it from the system.
+	 * Framebuffers will never be this small (our minimum size for
+	 * framebuffers is larger than this anyway.)  Such objects are
+	 * only accessed by the CPU so we don't need any special handing
+	 * here.
+	 */
+	if (size <= 8192) {
+		unsigned int order = get_order(size);
+		struct page *p = alloc_pages(GFP_KERNEL, order);
+
+		if (p) {
+			obj->addr = page_address(p);
+			obj->phys_addr = page_to_phys(p);
+			obj->page = p;
+
+			memset(obj->addr, 0, PAGE_ALIGN(size));
+		}
+	}
+
+	/*
+	 * We could grab something from CMA if it's enabled, but that
+	 * involves building in a problem:
+	 *
+	 * CMA's interface uses dma_alloc_coherent(), which provides us
+	 * with an CPU virtual address and a device address.
+	 *
+	 * The CPU virtual address may be either an address in the kernel
+	 * direct mapped region (for example, as it would be on x86) or
+	 * it may be remapped into another part of kernel memory space
+	 * (eg, as it would be on ARM.)  This means virt_to_phys() on the
+	 * returned virtual address is invalid depending on the architecture
+	 * implementation.
+	 *
+	 * The device address may also not be a physical address; it may
+	 * be that there is some kind of remapping between the device and
+	 * system RAM, which makes the use of the device address also
+	 * unsafe to re-use as a physical address.
+	 *
+	 * This makes DRM usage of dma_alloc_coherent() in a generic way
+	 * at best very questionable and unsafe.
+	 */
+
+	/* Otherwise, grab it from our linear allocation */
+	if (!obj->page) {
+		struct drm_mm_node *node;
+		unsigned align = min_t(unsigned, size, SZ_2M);
+		void __iomem *ptr;
+		int ret;
+
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!node)
+			return -ENOSPC;
+
+		mutex_lock(&dev->struct_mutex);
+		ret = drm_mm_insert_node(&priv->linear, node, size, align,
+					 DRM_MM_SEARCH_DEFAULT);
+		mutex_unlock(&dev->struct_mutex);
+		if (ret) {
+			kfree(node);
+			return ret;
+		}
+
+		obj->linear = node;
+
+		/* Ensure that the memory we're returning is cleared. */
+		ptr = ioremap_wc(obj->linear->start, size);
+		if (!ptr) {
+			mutex_lock(&dev->struct_mutex);
+			drm_mm_remove_node(obj->linear);
+			mutex_unlock(&dev->struct_mutex);
+			kfree(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;
+}
+
+void *
+armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj)
+{
+	/* only linear objects need to be ioremap'd */
+	if (!dobj->addr && dobj->linear)
+		dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size);
+	return dobj->addr;
+}
+
+struct armada_gem_object *
+armada_gem_alloc_private_object(struct drm_device *dev, size_t size)
+{
+	struct armada_gem_object *obj;
+
+	size = roundup_gem_size(size);
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return NULL;
+
+	drm_gem_private_object_init(dev, &obj->obj, size);
+	obj->dev_addr = DMA_ERROR_CODE;
+
+	DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
+
+	return obj;
+}
+
+struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev,
+	size_t size)
+{
+	struct armada_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;
+	}
+
+	obj->dev_addr = DMA_ERROR_CODE;
+
+	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 armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+	struct drm_mode_create_dumb *args)
+{
+	struct armada_gem_object *dobj;
+	u32 handle;
+	size_t size;
+	int ret;
+
+	args->pitch = armada_pitch(args->width, args->bpp);
+	args->size = size = args->pitch * args->height;
+
+	dobj = armada_gem_alloc_private_object(dev, size);
+	if (dobj == NULL)
+		return -ENOMEM;
+
+	ret = armada_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 armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+	uint32_t handle, uint64_t *offset)
+{
+	struct armada_gem_object *obj;
+	int ret = 0;
+
+	mutex_lock(&dev->struct_mutex);
+	obj = armada_gem_object_lookup(dev, file, handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object\n");
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	/* Don't allow imported objects to be mapped */
+	if (obj->obj.import_attach) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	ret = drm_gem_create_mmap_offset(&obj->obj);
+	if (ret == 0) {
+		*offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
+		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 armada_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 armada_gem_create_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_create *args = data;
+	struct armada_gem_object *dobj;
+	size_t size;
+	u32 handle;
+	int ret;
+
+	if (args->size == 0)
+		return -ENOMEM;
+
+	size = args->size;
+
+	dobj = armada_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;
+}
+
+/* Map a shmem-backed object into process memory space */
+int armada_gem_mmap_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_mmap *args = data;
+	struct armada_gem_object *dobj;
+	unsigned long addr;
+
+	dobj = armada_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 armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_armada_gem_pwrite *args = data;
+	struct armada_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 = armada_gem_object_lookup(dev, file, args->handle);
+	if (dobj == NULL)
+		return -ENOENT;
+
+	/* Must be a kernel-mapped object */
+	if (!dobj->addr)
+		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;
+	}
+
+	if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) {
+		ret = -EFAULT;
+	} else if (dobj->update) {
+		dobj->update(dobj->update_data);
+		ret = 0;
+	}
+
+ unref:
+	drm_gem_object_unreference_unlocked(&dobj->obj);
+	return ret;
+}
+
+/* Prime support */
+struct sg_table *
+armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
+	enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = attach->dmabuf->priv;
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+	struct scatterlist *sg;
+	struct sg_table *sgt;
+	int i, num;
+
+	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt)
+		return NULL;
+
+	if (dobj->obj.filp) {
+		struct address_space *mapping;
+		gfp_t gfp;
+		int count;
+
+		count = dobj->obj.size / PAGE_SIZE;
+		if (sg_alloc_table(sgt, count, GFP_KERNEL))
+			goto free_sgt;
+
+		mapping = file_inode(dobj->obj.filp)->i_mapping;
+		gfp = mapping_gfp_mask(mapping);
+
+		for_each_sg(sgt->sgl, sg, count, i) {
+			struct page *page;
+
+			page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+			if (IS_ERR(page)) {
+				num = i;
+				goto release;
+			}
+
+			sg_set_page(sg, page, PAGE_SIZE, 0);
+		}
+
+		if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
+			num = sgt->nents;
+			goto release;
+		}
+	} else if (dobj->page) {
+		/* Single contiguous page */
+		if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+			goto free_sgt;
+
+		sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
+
+		if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
+			goto free_table;
+	} else if (dobj->linear) {
+		/* Single contiguous physical region - no struct page */
+		if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+			goto free_sgt;
+		sg_dma_address(sgt->sgl) = dobj->dev_addr;
+		sg_dma_len(sgt->sgl) = dobj->obj.size;
+	} else {
+		goto free_sgt;
+	}
+	return sgt;
+
+ release:
+	for_each_sg(sgt->sgl, sg, num, i)
+		page_cache_release(sg_page(sg));
+ free_table:
+	sg_free_table(sgt);
+ free_sgt:
+	kfree(sgt);
+	return NULL;
+}
+
+static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
+	struct sg_table *sgt, enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = attach->dmabuf->priv;
+	struct armada_gem_object *dobj = drm_to_armada_gem(obj);
+	int i;
+
+	if (!dobj->linear)
+		dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+
+	if (dobj->obj.filp) {
+		struct scatterlist *sg;
+		for_each_sg(sgt->sgl, sg, sgt->nents, i)
+			page_cache_release(sg_page(sg));
+	}
+
+	sg_free_table(sgt);
+	kfree(sgt);
+}
+
+static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n)
+{
+	return NULL;
+}
+
+static void
+armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr)
+{
+}
+
+static int
+armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+	return -EINVAL;
+}
+
+static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
+	.map_dma_buf	= armada_gem_prime_map_dma_buf,
+	.unmap_dma_buf	= armada_gem_prime_unmap_dma_buf,
+	.release	= drm_gem_dmabuf_release,
+	.kmap_atomic	= armada_gem_dmabuf_no_kmap,
+	.kunmap_atomic	= armada_gem_dmabuf_no_kunmap,
+	.kmap		= armada_gem_dmabuf_no_kmap,
+	.kunmap		= armada_gem_dmabuf_no_kunmap,
+	.mmap		= armada_gem_dmabuf_mmap,
+};
+
+struct dma_buf *
+armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
+	int flags)
+{
+	return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
+			      O_RDWR);
+}
+
+struct drm_gem_object *
+armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf)
+{
+	struct dma_buf_attachment *attach;
+	struct armada_gem_object *dobj;
+
+	if (buf->ops == &armada_gem_prime_dmabuf_ops) {
+		struct drm_gem_object *obj = buf->priv;
+		if (obj->dev == dev) {
+			/*
+			 * Importing our own dmabuf(s) increases the
+			 * refcount on the gem object itself.
+			 */
+			drm_gem_object_reference(obj);
+			dma_buf_put(buf);
+			return obj;
+		}
+	}
+
+	attach = dma_buf_attach(buf, dev->dev);
+	if (IS_ERR(attach))
+		return ERR_CAST(attach);
+
+	dobj = armada_gem_alloc_private_object(dev, buf->size);
+	if (!dobj) {
+		dma_buf_detach(buf, attach);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dobj->obj.import_attach = attach;
+
+	/*
+	 * Don't call dma_buf_map_attachment() here - it maps the
+	 * scatterlist immediately for DMA, and this is not always
+	 * an appropriate thing to do.
+	 */
+	return &dobj->obj;
+}
+
+int armada_gem_map_import(struct armada_gem_object *dobj)
+{
+	int ret;
+
+	dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach,
+					  DMA_TO_DEVICE);
+	if (!dobj->sgt) {
+		DRM_ERROR("dma_buf_map_attachment() returned NULL\n");
+		return -EINVAL;
+	}
+	if (IS_ERR(dobj->sgt)) {
+		ret = PTR_ERR(dobj->sgt);
+		dobj->sgt = NULL;
+		DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret);
+		return ret;
+	}
+	if (dobj->sgt->nents > 1) {
+		DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n");
+		return -EINVAL;
+	}
+	if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) {
+		DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n");
+		return -EINVAL;
+	}
+	dobj->dev_addr = sg_dma_address(dobj->sgt->sgl);
+	return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h
new file mode 100644
index 0000000..00b6cd4
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.h
@@ -0,0 +1,52 @@
+/*
+ * 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 ARMADA_GEM_H
+#define ARMADA_GEM_H
+
+/* GEM */
+struct armada_gem_object {
+	struct drm_gem_object	obj;
+	void			*addr;
+	phys_addr_t		phys_addr;
+	resource_size_t		dev_addr;
+	struct drm_mm_node	*linear;	/* for linear backed */
+	struct page		*page;		/* for page backed */
+	struct sg_table		*sgt;		/* for imported */
+	void			(*update)(void *);
+	void			*update_data;
+};
+
+extern const struct vm_operations_struct armada_gem_vm_ops;
+
+#define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj)
+
+void armada_gem_free_object(struct drm_gem_object *);
+int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *);
+void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *);
+struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
+	size_t);
+int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
+	struct drm_mode_create_dumb *);
+int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
+	uint32_t, uint64_t *);
+int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
+	uint32_t);
+struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
+	struct drm_gem_object *obj, int flags);
+struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
+	struct dma_buf *);
+int armada_gem_map_import(struct armada_gem_object *);
+
+static inline struct armada_gem_object *armada_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_armada_gem(obj) : NULL;
+}
+#endif
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
new file mode 100644
index 0000000..216a4b5
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -0,0 +1,316 @@
+/*
+ * 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 ARMADA_HW_H
+#define ARMADA_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,	/* Armada 510 */
+	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,	/* Armada 510 */
+	LCD_SPU_SPI_RXDATA		= 0x0140,
+	LCD_SPU_ISA_RXDATA		= 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,	/* Armada 510 */
+	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,
+};
+
+enum {
+	CFG_565		= 0,
+	CFG_1555	= 1,
+	CFG_888PACK	= 2,
+	CFG_X888	= 3,
+	CFG_8888	= 4,
+	CFG_422PACK	= 5,
+	CFG_422		= 6,
+	CFG_420		= 7,
+	CFG_PSEUDO4	= 9,
+	CFG_PSEUDO8	= 10,
+	CFG_SWAPRB	= 1 << 4,
+	CFG_SWAPUV	= 1 << 3,
+	CFG_SWAPYU	= 1 << 2,
+	CFG_YUV2RGB	= 1 << 1,
+};
+
+/* 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,
+#define	CFG_DMA_FMT(x)	((x) << 20)
+	CFG_GRAFORMAT	= 0xf << 16,
+#define	CFG_GRA_FMT(x)	((x) << 16)
+#define CFG_GRA_MOD(x)	((x) << 8)
+	CFG_GRA_FTOGGLE	= 1 << 15,
+	CFG_GRA_HSMOOTH	= 1 << 14,
+	CFG_GRA_TSTMODE	= 1 << 13,
+	CFG_GRA_ENA	= 1 << 8,
+#define CFG_DMA_MOD(x)	((x) << 0)
+	CFG_DMA_FTOGGLE	= 1 << 7,
+	CFG_DMA_HSMOOTH	= 1 << 6,
+	CFG_DMA_TSTMODE	= 1 << 5,
+	CFG_DMA_ENA	= 1 << 0,
+};
+
+enum {
+	CKMODE_DISABLE	= 0,
+	CKMODE_Y	= 1,
+	CKMODE_U	= 2,
+	CKMODE_RGB	= 3,
+	CKMODE_V	= 4,
+	CKMODE_R	= 5,
+	CKMODE_G	= 6,
+	CKMODE_B	= 7,
+};
+
+/* For LCD_SPU_DMA_CTRL1 */
+enum {
+	CFG_FRAME_TRIG		= 1 << 31,
+	CFG_VSYNC_INV		= 1 << 27,
+	CFG_CKMODE_MASK		= 0x7 << 24,
+#define CFG_CKMODE(x)		((x) << 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_CTRL */
+enum {
+	SRAM_READ	= 0 << 14,
+	SRAM_WRITE	= 2 << 14,
+	SRAM_INIT	= 3 << 14,
+	SRAM_HWC32_RAMR	= 0xc << 8,
+	SRAM_HWC32_RAMG	= 0xd << 8,
+	SRAM_HWC32_RAMB	= 0xe << 8,
+	SRAM_HWC32_TRAN	= 0xf << 8,
+	SRAM_HWC	= 0xf << 8,
+};
+
+/* For LCD_SPU_SRAM_PARA1 */
+enum {
+	CFG_CSB_256x32	= 1 << 15,	/* cursor */
+	CFG_CSB_256x24	= 1 << 14,	/* palette */
+	CFG_CSB_256x8	= 1 << 13,	/* gamma */
+	CFG_PDWN1920x32	= 1 << 8,	/* Armada 510: power down vscale ram */
+	CFG_PDWN256x32	= 1 << 7,	/* power down cursor */
+	CFG_PDWN256x24	= 1 << 6,	/* power down palette */
+	CFG_PDWN256x8	= 1 << 5,	/* power down gamma */
+	CFG_PDWNHWC	= 1 << 4,	/* Armada 510: power down all hwc ram */
+	CFG_PDWN32x32	= 1 << 3,	/* power down slave->smart ram */
+	CFG_PDWN16x66	= 1 << 2,	/* power down UV fifo */
+	CFG_PDWN32x66	= 1 << 1,	/* power down Y fifo */
+	CFG_PDWN64x66	= 1 << 0,	/* power down graphic fifo */
+};
+
+/* For LCD_CFG_SCLK_DIV */
+enum {
+	/* Armada 510 */
+	SCLK_510_AXI		= 0x0 << 30,
+	SCLK_510_EXTCLK0	= 0x1 << 30,
+	SCLK_510_PLL		= 0x2 << 30,
+	SCLK_510_EXTCLK1	= 0x3 << 30,
+	SCLK_510_DIV_CHANGE	= 1 << 29,
+	SCLK_510_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_510_INT_DIV_MASK	= 0xffff << 0,
+
+	/* Armada 16x */
+	SCLK_16X_AHB		= 0x0 << 28,
+	SCLK_16X_PCLK		= 0x1 << 28,
+	SCLK_16X_AXI		= 0x4 << 28,
+	SCLK_16X_PLL		= 0x8 << 28,
+	SCLK_16X_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_16X_INT_DIV_MASK	= 0xffff << 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,	/* Normally active high */
+	CFG_INV_HENA	= 1 << 4,
+	CFG_INV_VSYNC	= 1 << 3,	/* Normally active high */
+	CFG_INV_HSYNC	= 1 << 2,	/* Normally active high */
+	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_YUV_CCIR709	= 1 << 9,
+	CFG_CSC_YUV_CCIR601	= 0 << 9,
+	CFG_CSC_RGB_STUDIO	= 1 << 8,
+	CFG_CSC_RGB_COMPUTER	= 0 << 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/armada/armada_ioctlP.h b/drivers/gpu/drm/armada/armada_ioctlP.h
new file mode 100644
index 0000000..bd8c456
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_ioctlP.h
@@ -0,0 +1,18 @@
+/*
+ * 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 ARMADA_IOCTLP_H
+#define ARMADA_IOCTLP_H
+
+#define ARMADA_IOCTL_PROTO(name)\
+extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
+
+ARMADA_IOCTL_PROTO(gem_create);
+ARMADA_IOCTL_PROTO(gem_mmap);
+ARMADA_IOCTL_PROTO(gem_pwrite);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
new file mode 100644
index 0000000..d685a54
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -0,0 +1,158 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_output.h"
+#include "armada_drm.h"
+
+struct armada_connector {
+	struct drm_connector conn;
+	const struct armada_output_type *type;
+};
+
+#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
+{
+	struct drm_encoder *enc = conn->encoder;
+
+	return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
+}
+
+static enum drm_connector_status armada_drm_connector_detect(
+	struct drm_connector *conn, bool force)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+	enum drm_connector_status status = connector_status_disconnected;
+
+	if (dconn->type->detect) {
+		status = dconn->type->detect(conn, force);
+	} else {
+		struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+
+		if (enc)
+			status = encoder_helper_funcs(enc)->detect(enc, conn);
+	}
+
+	return status;
+}
+
+static void armada_drm_connector_destroy(struct drm_connector *conn)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+	drm_sysfs_connector_remove(conn);
+	drm_connector_cleanup(conn);
+	kfree(dconn);
+}
+
+static int armada_drm_connector_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct armada_connector *dconn = drm_to_armada_conn(conn);
+
+	if (!dconn->type->set_property)
+		return -EINVAL;
+
+	return dconn->type->set_property(conn, property, value);
+}
+
+static const struct drm_connector_funcs armada_drm_conn_funcs = {
+	.dpms		= drm_helper_connector_dpms,
+	.fill_modes	= drm_helper_probe_single_connector_modes,
+	.detect		= armada_drm_connector_detect,
+	.destroy	= armada_drm_connector_destroy,
+	.set_property	= armada_drm_connector_set_property,
+};
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void armada_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+	return true;
+}
+
+/* Shouldn't this be a generic helper function? */
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode)
+{
+	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+	int valid = MODE_BAD;
+
+	if (encoder) {
+		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+		valid = slave->slave_funcs->mode_valid(encoder, mode);
+	}
+	return valid;
+}
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value)
+{
+	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
+	int rc = -EINVAL;
+
+	if (encoder) {
+		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
+
+		rc = slave->slave_funcs->set_property(encoder, conn, property,
+						      value);
+	}
+	return rc;
+}
+
+int armada_output_create(struct drm_device *dev,
+	const struct armada_output_type *type, const void *data)
+{
+	struct armada_connector *dconn;
+	int ret;
+
+	dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
+	if (!dconn)
+		return -ENOMEM;
+
+	dconn->type = type;
+
+	ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
+				 type->connector_type);
+	if (ret) {
+		DRM_ERROR("unable to init connector\n");
+		goto err_destroy_dconn;
+	}
+
+	ret = type->create(&dconn->conn, data);
+	if (ret)
+		goto err_conn;
+
+	ret = drm_sysfs_connector_add(&dconn->conn);
+	if (ret)
+		goto err_sysfs;
+
+	return 0;
+
+ err_sysfs:
+	if (dconn->conn.encoder)
+		dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
+ err_conn:
+	drm_connector_cleanup(&dconn->conn);
+ err_destroy_dconn:
+	kfree(dconn);
+	return ret;
+}
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h
new file mode 100644
index 0000000..4126d43
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.h
@@ -0,0 +1,39 @@
+/*
+ * 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 ARMADA_CONNETOR_H
+#define ARMADA_CONNETOR_H
+
+#define encoder_helper_funcs(encoder) \
+	((struct drm_encoder_helper_funcs *)encoder->helper_private)
+
+struct armada_output_type {
+	int connector_type;
+	enum drm_connector_status (*detect)(struct drm_connector *, bool);
+	int (*create)(struct drm_connector *, const void *);
+	int (*set_property)(struct drm_connector *, struct drm_property *,
+			    uint64_t);
+};
+
+struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
+
+void armada_drm_encoder_prepare(struct drm_encoder *encoder);
+void armada_drm_encoder_commit(struct drm_encoder *encoder);
+
+bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+	const struct drm_display_mode *mode, struct drm_display_mode *adj);
+
+int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
+	struct drm_display_mode *mode);
+
+int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
+	struct drm_property *property, uint64_t value);
+
+int armada_output_create(struct drm_device *dev,
+	const struct armada_output_type *type, const void *data);
+
+#endif
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
new file mode 100644
index 0000000..c5b06fd
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -0,0 +1,477 @@
+/*
+ * 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 <drm/drmP.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_fb.h"
+#include "armada_gem.h"
+#include "armada_hw.h"
+#include <drm/armada_drm.h>
+#include "armada_ioctlP.h"
+
+struct armada_plane_properties {
+	uint32_t colorkey_yr;
+	uint32_t colorkey_ug;
+	uint32_t colorkey_vb;
+#define K2R(val) (((val) >> 0) & 0xff)
+#define K2G(val) (((val) >> 8) & 0xff)
+#define K2B(val) (((val) >> 16) & 0xff)
+	int16_t  brightness;
+	uint16_t contrast;
+	uint16_t saturation;
+	uint32_t colorkey_mode;
+};
+
+struct armada_plane {
+	struct drm_plane base;
+	spinlock_t lock;
+	struct drm_framebuffer *old_fb;
+	uint32_t src_hw;
+	uint32_t dst_hw;
+	uint32_t dst_yx;
+	uint32_t ctrl0;
+	struct {
+		struct armada_vbl_event update;
+		struct armada_regs regs[13];
+		wait_queue_head_t wait;
+	} vbl;
+	struct armada_plane_properties prop;
+};
+#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
+
+
+static void
+armada_ovl_update_attr(struct armada_plane_properties *prop,
+	struct armada_crtc *dcrtc)
+{
+	writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
+	writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
+	writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
+
+	writel_relaxed(prop->brightness << 16 | prop->contrast,
+		       dcrtc->base + LCD_SPU_CONTRAST);
+	/* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
+	writel_relaxed(prop->saturation << 16,
+		       dcrtc->base + LCD_SPU_SATURATION);
+	writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
+
+	spin_lock_irq(&dcrtc->irq_lock);
+	armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
+		     CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
+		     dcrtc->base + LCD_SPU_DMA_CTRL1);
+
+	armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
+	spin_unlock_irq(&dcrtc->irq_lock);
+}
+
+/* === Plane support === */
+static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
+{
+	struct armada_plane *dplane = data;
+	struct drm_framebuffer *fb;
+
+	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
+
+	spin_lock(&dplane->lock);
+	fb = dplane->old_fb;
+	dplane->old_fb = NULL;
+	spin_unlock(&dplane->lock);
+
+	if (fb)
+		armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
+}
+
+static unsigned armada_limit(int start, unsigned size, unsigned max)
+{
+	int end = start + size;
+	if (end < 0)
+		return 0;
+	if (start < 0)
+		start = 0;
+	return (unsigned)end > max ? max - start : end - start;
+}
+
+static int
+armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+	struct drm_framebuffer *fb,
+	int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
+	uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	uint32_t val, ctrl0;
+	unsigned idx = 0;
+	int ret;
+
+	crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
+	crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
+	ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
+		CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
+		CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
+
+	/* Does the position/size result in nothing to display? */
+	if (crtc_w == 0 || crtc_h == 0) {
+		ctrl0 &= ~CFG_DMA_ENA;
+	}
+
+	/*
+	 * FIXME: if the starting point is off screen, we need to
+	 * adjust src_x, src_y, src_w, src_h appropriately, and
+	 * according to the scale.
+	 */
+
+	if (!dcrtc->plane) {
+		dcrtc->plane = plane;
+		armada_ovl_update_attr(&dplane->prop, dcrtc);
+	}
+
+	/* FIXME: overlay on an interlaced display */
+	/* Just updating the position/size? */
+	if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
+		val = (src_h & 0xffff0000) | src_w >> 16;
+		dplane->src_hw = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
+		val = crtc_h << 16 | crtc_w;
+		dplane->dst_hw = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
+		val = crtc_y << 16 | crtc_x;
+		dplane->dst_yx = val;
+		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
+		return 0;
+	} else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
+		/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
+		armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
+			       dcrtc->base + LCD_SPU_SRAM_PARA1);
+	}
+
+	ret = wait_event_timeout(dplane->vbl.wait,
+				 list_empty(&dplane->vbl.update.node),
+				 HZ/25);
+	if (ret < 0)
+		return ret;
+
+	if (plane->fb != fb) {
+		struct armada_gem_object *obj = drm_fb_obj(fb);
+		uint32_t sy, su, sv;
+
+		/*
+		 * Take a reference on the new framebuffer - we want to
+		 * hold on to it while the hardware is displaying it.
+		 */
+		drm_framebuffer_reference(fb);
+
+		if (plane->fb) {
+			struct drm_framebuffer *older_fb;
+
+			spin_lock_irq(&dplane->lock);
+			older_fb = dplane->old_fb;
+			dplane->old_fb = plane->fb;
+			spin_unlock_irq(&dplane->lock);
+			if (older_fb)
+				armada_drm_queue_unref_work(dcrtc->crtc.dev,
+							    older_fb);
+		}
+
+		src_y >>= 16;
+		src_x >>= 16;
+		sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
+			src_x * fb->bits_per_pixel / 8;
+		su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
+			src_x;
+		sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
+			src_x;
+
+		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+				     LCD_SPU_DMA_START_ADDR_Y0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, su,
+				     LCD_SPU_DMA_START_ADDR_U0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+				     LCD_SPU_DMA_START_ADDR_V0);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sy,
+				     LCD_SPU_DMA_START_ADDR_Y1);
+		armada_reg_queue_set(dplane->vbl.regs, idx, su,
+				     LCD_SPU_DMA_START_ADDR_U1);
+		armada_reg_queue_set(dplane->vbl.regs, idx, sv,
+				     LCD_SPU_DMA_START_ADDR_V1);
+
+		val = fb->pitches[0] << 16 | fb->pitches[0];
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_PITCH_YC);
+		val = fb->pitches[1] << 16 | fb->pitches[2];
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_PITCH_UV);
+	}
+
+	val = (src_h & 0xffff0000) | src_w >> 16;
+	if (dplane->src_hw != val) {
+		dplane->src_hw = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_HPXL_VLN);
+	}
+	val = crtc_h << 16 | crtc_w;
+	if (dplane->dst_hw != val) {
+		dplane->dst_hw = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DZM_HPXL_VLN);
+	}
+	val = crtc_y << 16 | crtc_x;
+	if (dplane->dst_yx != val) {
+		dplane->dst_yx = val;
+		armada_reg_queue_set(dplane->vbl.regs, idx, val,
+				     LCD_SPU_DMA_OVSA_HPXL_VLN);
+	}
+	if (dplane->ctrl0 != ctrl0) {
+		dplane->ctrl0 = ctrl0;
+		armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
+			CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
+			CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
+			CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
+			CFG_YUV2RGB) | CFG_DMA_ENA,
+			LCD_SPU_DMA_CTRL0);
+	}
+	if (idx) {
+		armada_reg_queue_end(dplane->vbl.regs, idx);
+		armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
+	}
+	return 0;
+}
+
+static int armada_plane_disable(struct drm_plane *plane)
+{
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct drm_framebuffer *fb;
+	struct armada_crtc *dcrtc;
+
+	if (!dplane->base.crtc)
+		return 0;
+
+	dcrtc = drm_to_armada_crtc(dplane->base.crtc);
+	dcrtc->plane = NULL;
+
+	spin_lock_irq(&dcrtc->irq_lock);
+	armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
+	armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+	dplane->ctrl0 = 0;
+	spin_unlock_irq(&dcrtc->irq_lock);
+
+	/* Power down the Y/U/V FIFOs */
+	armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
+		       dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+	if (plane->fb)
+		drm_framebuffer_unreference(plane->fb);
+
+	spin_lock_irq(&dplane->lock);
+	fb = dplane->old_fb;
+	dplane->old_fb = NULL;
+	spin_unlock_irq(&dplane->lock);
+	if (fb)
+		drm_framebuffer_unreference(fb);
+
+	return 0;
+}
+
+static void armada_plane_destroy(struct drm_plane *plane)
+{
+	kfree(plane);
+}
+
+static int armada_plane_set_property(struct drm_plane *plane,
+	struct drm_property *property, uint64_t val)
+{
+	struct armada_private *priv = plane->dev->dev_private;
+	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	bool update_attr = false;
+
+	if (property == priv->colorkey_prop) {
+#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
+		dplane->prop.colorkey_yr = CCC(K2R(val));
+		dplane->prop.colorkey_ug = CCC(K2G(val));
+		dplane->prop.colorkey_vb = CCC(K2B(val));
+#undef CCC
+		update_attr = true;
+	} else if (property == priv->colorkey_min_prop) {
+		dplane->prop.colorkey_yr &= ~0x00ff0000;
+		dplane->prop.colorkey_yr |= K2R(val) << 16;
+		dplane->prop.colorkey_ug &= ~0x00ff0000;
+		dplane->prop.colorkey_ug |= K2G(val) << 16;
+		dplane->prop.colorkey_vb &= ~0x00ff0000;
+		dplane->prop.colorkey_vb |= K2B(val) << 16;
+		update_attr = true;
+	} else if (property == priv->colorkey_max_prop) {
+		dplane->prop.colorkey_yr &= ~0xff000000;
+		dplane->prop.colorkey_yr |= K2R(val) << 24;
+		dplane->prop.colorkey_ug &= ~0xff000000;
+		dplane->prop.colorkey_ug |= K2G(val) << 24;
+		dplane->prop.colorkey_vb &= ~0xff000000;
+		dplane->prop.colorkey_vb |= K2B(val) << 24;
+		update_attr = true;
+	} else if (property == priv->colorkey_val_prop) {
+		dplane->prop.colorkey_yr &= ~0x0000ff00;
+		dplane->prop.colorkey_yr |= K2R(val) << 8;
+		dplane->prop.colorkey_ug &= ~0x0000ff00;
+		dplane->prop.colorkey_ug |= K2G(val) << 8;
+		dplane->prop.colorkey_vb &= ~0x0000ff00;
+		dplane->prop.colorkey_vb |= K2B(val) << 8;
+		update_attr = true;
+	} else if (property == priv->colorkey_alpha_prop) {
+		dplane->prop.colorkey_yr &= ~0x000000ff;
+		dplane->prop.colorkey_yr |= K2R(val);
+		dplane->prop.colorkey_ug &= ~0x000000ff;
+		dplane->prop.colorkey_ug |= K2G(val);
+		dplane->prop.colorkey_vb &= ~0x000000ff;
+		dplane->prop.colorkey_vb |= K2B(val);
+		update_attr = true;
+	} else if (property == priv->colorkey_mode_prop) {
+		dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
+		dplane->prop.colorkey_mode |= CFG_CKMODE(val);
+		update_attr = true;
+	} else if (property == priv->brightness_prop) {
+		dplane->prop.brightness = val - 256;
+		update_attr = true;
+	} else if (property == priv->contrast_prop) {
+		dplane->prop.contrast = val;
+		update_attr = true;
+	} else if (property == priv->saturation_prop) {
+		dplane->prop.saturation = val;
+		update_attr = true;
+	}
+
+	if (update_attr && dplane->base.crtc)
+		armada_ovl_update_attr(&dplane->prop,
+				       drm_to_armada_crtc(dplane->base.crtc));
+
+	return 0;
+}
+
+static const struct drm_plane_funcs armada_plane_funcs = {
+	.update_plane	= armada_plane_update,
+	.disable_plane	= armada_plane_disable,
+	.destroy	= armada_plane_destroy,
+	.set_property	= armada_plane_set_property,
+};
+
+static const uint32_t armada_formats[] = {
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YVU422,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
+static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
+	{ CKMODE_DISABLE, "disabled" },
+	{ CKMODE_Y,       "Y component" },
+	{ CKMODE_U,       "U component" },
+	{ CKMODE_V,       "V component" },
+	{ CKMODE_RGB,     "RGB" },
+	{ CKMODE_R,       "R component" },
+	{ CKMODE_G,       "G component" },
+	{ CKMODE_B,       "B component" },
+};
+
+static int armada_overlay_create_properties(struct drm_device *dev)
+{
+	struct armada_private *priv = dev->dev_private;
+
+	if (priv->colorkey_prop)
+		return 0;
+
+	priv->colorkey_prop = drm_property_create_range(dev, 0,
+				"colorkey", 0, 0xffffff);
+	priv->colorkey_min_prop = drm_property_create_range(dev, 0,
+				"colorkey_min", 0, 0xffffff);
+	priv->colorkey_max_prop = drm_property_create_range(dev, 0,
+				"colorkey_max", 0, 0xffffff);
+	priv->colorkey_val_prop = drm_property_create_range(dev, 0,
+				"colorkey_val", 0, 0xffffff);
+	priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
+				"colorkey_alpha", 0, 0xffffff);
+	priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
+				"colorkey_mode",
+				armada_drm_colorkey_enum_list,
+				ARRAY_SIZE(armada_drm_colorkey_enum_list));
+	priv->brightness_prop = drm_property_create_range(dev, 0,
+				"brightness", 0, 256 + 255);
+	priv->contrast_prop = drm_property_create_range(dev, 0,
+				"contrast", 0, 0x7fff);
+	priv->saturation_prop = drm_property_create_range(dev, 0,
+				"saturation", 0, 0x7fff);
+
+	if (!priv->colorkey_prop)
+		return -ENOMEM;
+
+	return 0;
+}
+
+int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
+{
+	struct armada_private *priv = dev->dev_private;
+	struct drm_mode_object *mobj;
+	struct armada_plane *dplane;
+	int ret;
+
+	ret = armada_overlay_create_properties(dev);
+	if (ret)
+		return ret;
+
+	dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
+	if (!dplane)
+		return -ENOMEM;
+
+	spin_lock_init(&dplane->lock);
+	init_waitqueue_head(&dplane->vbl.wait);
+	armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
+				  dplane);
+
+	drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
+		       armada_formats, ARRAY_SIZE(armada_formats), false);
+
+	dplane->prop.colorkey_yr = 0xfefefe00;
+	dplane->prop.colorkey_ug = 0x01010100;
+	dplane->prop.colorkey_vb = 0x01010100;
+	dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
+	dplane->prop.brightness = 0;
+	dplane->prop.contrast = 0x4000;
+	dplane->prop.saturation = 0x4000;
+
+	mobj = &dplane->base.base;
+	drm_object_attach_property(mobj, priv->colorkey_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_min_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_max_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_val_prop,
+				   0x0101fe);
+	drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
+				   0x000000);
+	drm_object_attach_property(mobj, priv->colorkey_mode_prop,
+				   CKMODE_RGB);
+	drm_object_attach_property(mobj, priv->brightness_prop, 256);
+	drm_object_attach_property(mobj, priv->contrast_prop,
+				   dplane->prop.contrast);
+	drm_object_attach_property(mobj, priv->saturation_prop,
+				   dplane->prop.saturation);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c
new file mode 100644
index 0000000..00d0fac
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.c
@@ -0,0 +1,139 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include "armada_drm.h"
+#include "armada_output.h"
+#include "armada_slave.h"
+
+static int armada_drm_slave_get_modes(struct drm_connector *conn)
+{
+	struct drm_encoder *enc = armada_drm_connector_encoder(conn);
+	int count = 0;
+
+	if (enc) {
+		struct drm_encoder_slave *slave = to_encoder_slave(enc);
+
+		count = slave->slave_funcs->get_modes(enc, conn);
+	}
+
+	return count;
+}
+
+static void armada_drm_slave_destroy(struct drm_encoder *enc)
+{
+	struct drm_encoder_slave *slave = to_encoder_slave(enc);
+	struct i2c_client *client = drm_i2c_encoder_get_client(enc);
+
+	if (slave->slave_funcs)
+		slave->slave_funcs->destroy(enc);
+	if (client)
+		i2c_put_adapter(client->adapter);
+
+	drm_encoder_cleanup(&slave->base);
+	kfree(slave);
+}
+
+static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
+	.destroy	= armada_drm_slave_destroy,
+};
+
+static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
+	.get_modes	= armada_drm_slave_get_modes,
+	.mode_valid	= armada_drm_slave_encoder_mode_valid,
+	.best_encoder	= armada_drm_connector_encoder,
+};
+
+static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
+	.dpms = drm_i2c_encoder_dpms,
+	.save = drm_i2c_encoder_save,
+	.restore = drm_i2c_encoder_restore,
+	.mode_fixup = drm_i2c_encoder_mode_fixup,
+	.prepare = drm_i2c_encoder_prepare,
+	.commit = drm_i2c_encoder_commit,
+	.mode_set = drm_i2c_encoder_mode_set,
+	.detect = drm_i2c_encoder_detect,
+};
+
+static int
+armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
+{
+	const struct armada_drm_slave_config *config = data;
+	struct drm_encoder_slave *slave;
+	struct i2c_adapter *adap;
+	int ret;
+
+	conn->interlace_allowed = config->interlace_allowed;
+	conn->doublescan_allowed = config->doublescan_allowed;
+	conn->polled = config->polled;
+
+	drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
+
+	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+	if (!slave)
+		return -ENOMEM;
+
+	slave->base.possible_crtcs = config->crtcs;
+
+	adap = i2c_get_adapter(config->i2c_adapter_id);
+	if (!adap) {
+		kfree(slave);
+		return -EPROBE_DEFER;
+	}
+
+	ret = drm_encoder_init(conn->dev, &slave->base,
+			       &armada_drm_slave_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_ERROR("unable to init encoder\n");
+		i2c_put_adapter(adap);
+		kfree(slave);
+		return ret;
+	}
+
+	ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
+	i2c_put_adapter(adap);
+	if (ret) {
+		DRM_ERROR("unable to init encoder slave\n");
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
+
+	ret = slave->slave_funcs->create_resources(&slave->base, conn);
+	if (ret) {
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	ret = drm_mode_connector_attach_encoder(conn, &slave->base);
+	if (ret) {
+		armada_drm_slave_destroy(&slave->base);
+		return ret;
+	}
+
+	conn->encoder = &slave->base;
+
+	return ret;
+}
+
+static const struct armada_output_type armada_drm_conn_slave = {
+	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
+	.create		= armada_drm_conn_slave_create,
+	.set_property	= armada_drm_slave_encoder_set_property,
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+	const struct armada_drm_slave_config *config)
+{
+	return armada_output_create(dev, &armada_drm_conn_slave, config);
+}
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h
new file mode 100644
index 0000000..bf2374c
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.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 ARMADA_SLAVE_H
+#define ARMADA_SLAVE_H
+
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+
+struct armada_drm_slave_config {
+	int i2c_adapter_id;
+	uint32_t crtcs;
+	uint8_t polled;
+	bool interlace_allowed;
+	bool doublescan_allowed;
+	struct i2c_board_info info;
+};
+
+int armada_drm_connector_slave_create(struct drm_device *dev,
+	const struct armada_drm_slave_config *);
+
+#endif
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 24f4995..e5e6e91 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1135,4 +1135,21 @@ extern int drm_format_horz_chroma_subsampling(uint32_t format);
 extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
+/* Helpers */
+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 /* __DRM_CRTC_H__ */
diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
new file mode 100644
index 0000000..8dec3fd
--- /dev/null
+++ b/include/uapi/drm/armada_drm.h
@@ -0,0 +1,45 @@
+/*
+ * 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_ARMADA_IOCTL_H
+#define DRM_ARMADA_IOCTL_H
+
+#define DRM_ARMADA_GEM_CREATE		0x00
+#define DRM_ARMADA_GEM_MMAP		0x02
+#define DRM_ARMADA_GEM_PWRITE		0x03
+
+#define ARMADA_IOCTL(dir, name, str) \
+	DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
+
+struct drm_armada_gem_create {
+	uint32_t handle;
+	uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_CREATE \
+	ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
+
+struct drm_armada_gem_mmap {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+	uint64_t size;
+	uint64_t addr;
+};
+#define DRM_IOCTL_ARMADA_GEM_MMAP \
+	ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
+
+struct drm_armada_gem_pwrite {
+	uint64_t ptr;
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_PWRITE \
+	ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
+
+#endif
-- 
1.7.4.4

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-06 22:09   ` Russell King
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:09 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds ARGB hardware cursor support to the DRM driver for the
Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
64x32 resolutions.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/armada_510.c  |    1 +
 drivers/gpu/drm/armada/armada_crtc.c |  245 +++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/armada/armada_crtc.h |    9 ++
 drivers/gpu/drm/armada/armada_drm.h  |    1 +
 drivers/gpu/drm/armada/armada_hw.h   |    6 +-
 5 files changed, 256 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
index a016888..59948ef 100644
--- a/drivers/gpu/drm/armada/armada_510.c
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -80,6 +80,7 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
 
 const struct armada_variant armada510_ops = {
 	.has_spu_adv_reg = true,
+	.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
 	.init = armada510_init,
 	.crtc_init = armada510_crtc_init,
 	.crtc_compute_clock = armada510_crtc_compute_clock,
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 7b379fd..e8605bf 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -381,8 +381,21 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		val = readl_relaxed(base + LCD_SPU_ADV_REG);
 		val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
 		val |= dcrtc->v[i].spu_adv_reg;
-		writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
+		writel_relaxed(val, base + LCD_SPU_ADV_REG);
 	}
+
+	if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) {
+		writel_relaxed(dcrtc->cursor_hw_pos,
+			       base + LCD_SPU_HWC_OVSA_HPXL_VLN);
+		writel_relaxed(dcrtc->cursor_hw_sz,
+			       base + LCD_SPU_HWC_HPXL_VLN);
+		armada_updatel(CFG_HWC_ENA,
+			       CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
+			       base + LCD_SPU_DMA_CTRL0);
+		dcrtc->cursor_update = false;
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+	}
+
 	spin_unlock(&dcrtc->irq_lock);
 
 	if (stat & GRA_FRAME_IRQ) {
@@ -522,7 +535,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 				    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 | ADV_VSYNCOFFEN;
+	dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+		priv->variant->spu_adv_reg;
 
 	if (interlaced) {
 		/* Odd interlaced frame */
@@ -530,7 +544,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 						(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 | ADV_VSYNCOFFEN;
+		dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+			priv->variant->spu_adv_reg;
 	} else {
 		dcrtc->v[0] = dcrtc->v[1];
 	}
@@ -545,10 +560,11 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
 			   LCD_SPUT_V_H_TOTAL);
 
-	if (priv->variant->has_spu_adv_reg)
+	if (priv->variant->has_spu_adv_reg) {
 		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
 				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
 				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
+	}
 
 	val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
 	val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
@@ -640,11 +656,230 @@ static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
 	.disable	= armada_drm_crtc_disable,
 };
 
+static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
+	unsigned stride, unsigned width, unsigned height)
+{
+	uint32_t addr;
+	unsigned y;
+
+	addr = SRAM_HWC32_RAM1;
+	for (y = 0; y < height; y++) {
+		uint32_t *p = &pix[y * stride];
+		unsigned x;
+
+		for (x = 0; x < width; x++, p++) {
+			uint32_t val = *p;
+
+			val = (val & 0xff00ff00) |
+			      (val & 0x000000ff) << 16 |
+			      (val & 0x00ff0000) >> 16;
+
+			writel_relaxed(val,
+				       base + LCD_SPU_SRAM_WRDAT);
+			writel_relaxed(addr | SRAM_WRITE,
+				       base + LCD_SPU_SRAM_CTRL);
+			addr += 1;
+			if ((addr & 0x00ff) == 0)
+				addr += 0xf00;
+			if ((addr & 0x30ff) == 0)
+				addr = SRAM_HWC32_RAM2;
+		}
+	}
+}
+
+static void armada_drm_crtc_cursor_tran(void __iomem *base)
+{
+	unsigned addr;
+
+	for (addr = 0; addr < 256; addr++) {
+		/* write the default value */
+		writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT);
+		writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN,
+			       base + LCD_SPU_SRAM_CTRL);
+	}
+}
+
+static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
+{
+	uint32_t xoff, xscr, w = dcrtc->cursor_w, s;
+	uint32_t yoff, yscr, h = dcrtc->cursor_h;
+	uint32_t para1;
+
+	/*
+	 * 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.vdisplay - dcrtc->cursor_y, 0);
+	} else {
+		yoff = 0;
+		yscr = dcrtc->cursor_y;
+	}
+
+	/* On interlaced modes, the vertical cursor size must be halved */
+	s = dcrtc->cursor_w;
+	if (dcrtc->interlaced) {
+		s *= 2;
+		yscr /= 2;
+		h /= 2;
+	}
+
+	if (!dcrtc->cursor_obj || !h || !w) {
+		spin_lock_irq(&dcrtc->irq_lock);
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+		dcrtc->cursor_update = false;
+		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+		spin_unlock_irq(&dcrtc->irq_lock);
+		return 0;
+	}
+
+	para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1);
+	armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32,
+		       dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+	/*
+	 * Initialize the transparency if the SRAM was powered down.
+	 * We must also reload the cursor data as well.
+	 */
+	if (!(para1 & CFG_CSB_256x32)) {
+		armada_drm_crtc_cursor_tran(dcrtc->base);
+		reload = true;
+	}
+
+	if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
+		spin_lock_irq(&dcrtc->irq_lock);
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+		dcrtc->cursor_update = false;
+		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+		spin_unlock_irq(&dcrtc->irq_lock);
+		reload = true;
+	}
+	if (reload) {
+		struct armada_gem_object *obj = dcrtc->cursor_obj;
+		uint32_t *pix;
+		/* Set the top-left corner of the cursor image */
+		pix = obj->addr;
+		pix += yoff * s + xoff;
+		armada_load_cursor_argb(dcrtc->base, pix, s, w, h);
+	}
+
+	/* Reload the cursor position, size and enable in the IRQ handler */
+	spin_lock_irq(&dcrtc->irq_lock);
+	dcrtc->cursor_hw_pos = yscr << 16 | xscr;
+	dcrtc->cursor_hw_sz = h << 16 | w;
+	dcrtc->cursor_update = true;
+	armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+	spin_unlock_irq(&dcrtc->irq_lock);
+
+	return 0;
+}
+
+static void cursor_update(void *data)
+{
+	armada_drm_crtc_cursor_update(data, true);
+}
+
+static int armada_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 armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_gem_object *obj = NULL;
+	int ret;
+
+	/* If no cursor support, replicate drm's return value */
+	if (!priv->variant->has_spu_adv_reg)
+		return -ENXIO;
+
+	if (handle && w > 0 && h > 0) {
+		/* maximum size is 64x32 or 32x64 */
+		if (w > 64 || h > 64 || (w > 32 && h > 32))
+			return -ENOMEM;
+
+		obj = armada_gem_object_lookup(dev, file, handle);
+		if (!obj)
+			return -ENOENT;
+
+		/* Must be a kernel-mapped object */
+		if (!obj->addr) {
+			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 = armada_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 armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct drm_device *dev = crtc->dev;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+	int ret;
+
+	/* If no cursor support, replicate drm's return value */
+	if (!priv->variant->has_spu_adv_reg)
+		return -EFAULT;
+
+	mutex_lock(&dev->struct_mutex);
+	dcrtc->cursor_x = x;
+	dcrtc->cursor_y = y;
+	ret = armada_drm_crtc_cursor_update(dcrtc, false);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
 static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_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);
 
@@ -750,6 +985,8 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 }
 
 static struct drm_crtc_funcs armada_crtc_funcs = {
+	.cursor_set	= armada_drm_crtc_cursor_set,
+	.cursor_move	= armada_drm_crtc_cursor_move,
 	.destroy	= armada_drm_crtc_destroy,
 	.set_config	= drm_crtc_helper_set_config,
 	.page_flip	= armada_drm_crtc_page_flip,
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 972da53..9c10a07 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -44,11 +44,20 @@ struct armada_crtc {
 		uint32_t	spu_adv_reg;
 	} v[2];
 	bool			interlaced;
+	bool			cursor_update;
 	uint8_t			csc_yuv_mode;
 	uint8_t			csc_rgb_mode;
 
 	struct drm_plane	*plane;
 
+	struct armada_gem_object	*cursor_obj;
+	int			cursor_x;
+	int			cursor_y;
+	uint32_t		cursor_hw_pos;
+	uint32_t		cursor_hw_sz;
+	uint32_t		cursor_w;
+	uint32_t		cursor_h;
+
 	int			dpms;
 	uint32_t		cfg_dumb_ctrl;
 	uint32_t		dumb_ctrl;
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index e8c4f80..eef09ec 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -60,6 +60,7 @@ struct armada_private;
 
 struct armada_variant {
 	bool	has_spu_adv_reg;
+	uint32_t spu_adv_reg;
 	int (*init)(struct armada_private *, struct device *);
 	int (*crtc_init)(struct armada_crtc *);
 	int (*crtc_compute_clock)(struct armada_crtc *,
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
index 216a4b5..27319a8 100644
--- a/drivers/gpu/drm/armada/armada_hw.h
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -168,8 +168,10 @@ enum {
 	SRAM_READ	= 0 << 14,
 	SRAM_WRITE	= 2 << 14,
 	SRAM_INIT	= 3 << 14,
-	SRAM_HWC32_RAMR	= 0xc << 8,
-	SRAM_HWC32_RAMG	= 0xd << 8,
+	SRAM_HWC32_RAM1	= 0xc << 8,
+	SRAM_HWC32_RAM2	= 0xd << 8,
+	SRAM_HWC32_RAMR	= SRAM_HWC32_RAM1,
+	SRAM_HWC32_RAMG	= SRAM_HWC32_RAM2,
 	SRAM_HWC32_RAMB	= 0xe << 8,
 	SRAM_HWC32_TRAN	= 0xf << 8,
 	SRAM_HWC	= 0xf << 8,
-- 
1.7.4.4

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-06 22:09   ` Russell King
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:09 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter,
	Sebastian Hesselbarth

This patch adds ARGB hardware cursor support to the DRM driver for the
Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
64x32 resolutions.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/armada_510.c  |    1 +
 drivers/gpu/drm/armada/armada_crtc.c |  245 +++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/armada/armada_crtc.h |    9 ++
 drivers/gpu/drm/armada/armada_drm.h  |    1 +
 drivers/gpu/drm/armada/armada_hw.h   |    6 +-
 5 files changed, 256 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
index a016888..59948ef 100644
--- a/drivers/gpu/drm/armada/armada_510.c
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -80,6 +80,7 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
 
 const struct armada_variant armada510_ops = {
 	.has_spu_adv_reg = true,
+	.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
 	.init = armada510_init,
 	.crtc_init = armada510_crtc_init,
 	.crtc_compute_clock = armada510_crtc_compute_clock,
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 7b379fd..e8605bf 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -381,8 +381,21 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		val = readl_relaxed(base + LCD_SPU_ADV_REG);
 		val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
 		val |= dcrtc->v[i].spu_adv_reg;
-		writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
+		writel_relaxed(val, base + LCD_SPU_ADV_REG);
 	}
+
+	if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) {
+		writel_relaxed(dcrtc->cursor_hw_pos,
+			       base + LCD_SPU_HWC_OVSA_HPXL_VLN);
+		writel_relaxed(dcrtc->cursor_hw_sz,
+			       base + LCD_SPU_HWC_HPXL_VLN);
+		armada_updatel(CFG_HWC_ENA,
+			       CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
+			       base + LCD_SPU_DMA_CTRL0);
+		dcrtc->cursor_update = false;
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+	}
+
 	spin_unlock(&dcrtc->irq_lock);
 
 	if (stat & GRA_FRAME_IRQ) {
@@ -522,7 +535,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 				    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 | ADV_VSYNCOFFEN;
+	dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+		priv->variant->spu_adv_reg;
 
 	if (interlaced) {
 		/* Odd interlaced frame */
@@ -530,7 +544,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 						(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 | ADV_VSYNCOFFEN;
+		dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
+			priv->variant->spu_adv_reg;
 	} else {
 		dcrtc->v[0] = dcrtc->v[1];
 	}
@@ -545,10 +560,11 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
 			   LCD_SPUT_V_H_TOTAL);
 
-	if (priv->variant->has_spu_adv_reg)
+	if (priv->variant->has_spu_adv_reg) {
 		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
 				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
 				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
+	}
 
 	val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
 	val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
@@ -640,11 +656,230 @@ static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
 	.disable	= armada_drm_crtc_disable,
 };
 
+static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
+	unsigned stride, unsigned width, unsigned height)
+{
+	uint32_t addr;
+	unsigned y;
+
+	addr = SRAM_HWC32_RAM1;
+	for (y = 0; y < height; y++) {
+		uint32_t *p = &pix[y * stride];
+		unsigned x;
+
+		for (x = 0; x < width; x++, p++) {
+			uint32_t val = *p;
+
+			val = (val & 0xff00ff00) |
+			      (val & 0x000000ff) << 16 |
+			      (val & 0x00ff0000) >> 16;
+
+			writel_relaxed(val,
+				       base + LCD_SPU_SRAM_WRDAT);
+			writel_relaxed(addr | SRAM_WRITE,
+				       base + LCD_SPU_SRAM_CTRL);
+			addr += 1;
+			if ((addr & 0x00ff) == 0)
+				addr += 0xf00;
+			if ((addr & 0x30ff) == 0)
+				addr = SRAM_HWC32_RAM2;
+		}
+	}
+}
+
+static void armada_drm_crtc_cursor_tran(void __iomem *base)
+{
+	unsigned addr;
+
+	for (addr = 0; addr < 256; addr++) {
+		/* write the default value */
+		writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT);
+		writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN,
+			       base + LCD_SPU_SRAM_CTRL);
+	}
+}
+
+static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
+{
+	uint32_t xoff, xscr, w = dcrtc->cursor_w, s;
+	uint32_t yoff, yscr, h = dcrtc->cursor_h;
+	uint32_t para1;
+
+	/*
+	 * 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.vdisplay - dcrtc->cursor_y, 0);
+	} else {
+		yoff = 0;
+		yscr = dcrtc->cursor_y;
+	}
+
+	/* On interlaced modes, the vertical cursor size must be halved */
+	s = dcrtc->cursor_w;
+	if (dcrtc->interlaced) {
+		s *= 2;
+		yscr /= 2;
+		h /= 2;
+	}
+
+	if (!dcrtc->cursor_obj || !h || !w) {
+		spin_lock_irq(&dcrtc->irq_lock);
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+		dcrtc->cursor_update = false;
+		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+		spin_unlock_irq(&dcrtc->irq_lock);
+		return 0;
+	}
+
+	para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1);
+	armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32,
+		       dcrtc->base + LCD_SPU_SRAM_PARA1);
+
+	/*
+	 * Initialize the transparency if the SRAM was powered down.
+	 * We must also reload the cursor data as well.
+	 */
+	if (!(para1 & CFG_CSB_256x32)) {
+		armada_drm_crtc_cursor_tran(dcrtc->base);
+		reload = true;
+	}
+
+	if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
+		spin_lock_irq(&dcrtc->irq_lock);
+		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+		dcrtc->cursor_update = false;
+		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
+		spin_unlock_irq(&dcrtc->irq_lock);
+		reload = true;
+	}
+	if (reload) {
+		struct armada_gem_object *obj = dcrtc->cursor_obj;
+		uint32_t *pix;
+		/* Set the top-left corner of the cursor image */
+		pix = obj->addr;
+		pix += yoff * s + xoff;
+		armada_load_cursor_argb(dcrtc->base, pix, s, w, h);
+	}
+
+	/* Reload the cursor position, size and enable in the IRQ handler */
+	spin_lock_irq(&dcrtc->irq_lock);
+	dcrtc->cursor_hw_pos = yscr << 16 | xscr;
+	dcrtc->cursor_hw_sz = h << 16 | w;
+	dcrtc->cursor_update = true;
+	armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+	spin_unlock_irq(&dcrtc->irq_lock);
+
+	return 0;
+}
+
+static void cursor_update(void *data)
+{
+	armada_drm_crtc_cursor_update(data, true);
+}
+
+static int armada_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 armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+	struct armada_gem_object *obj = NULL;
+	int ret;
+
+	/* If no cursor support, replicate drm's return value */
+	if (!priv->variant->has_spu_adv_reg)
+		return -ENXIO;
+
+	if (handle && w > 0 && h > 0) {
+		/* maximum size is 64x32 or 32x64 */
+		if (w > 64 || h > 64 || (w > 32 && h > 32))
+			return -ENOMEM;
+
+		obj = armada_gem_object_lookup(dev, file, handle);
+		if (!obj)
+			return -ENOENT;
+
+		/* Must be a kernel-mapped object */
+		if (!obj->addr) {
+			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 = armada_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 armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct drm_device *dev = crtc->dev;
+	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct armada_private *priv = crtc->dev->dev_private;
+	int ret;
+
+	/* If no cursor support, replicate drm's return value */
+	if (!priv->variant->has_spu_adv_reg)
+		return -EFAULT;
+
+	mutex_lock(&dev->struct_mutex);
+	dcrtc->cursor_x = x;
+	dcrtc->cursor_y = y;
+	ret = armada_drm_crtc_cursor_update(dcrtc, false);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
 static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_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);
 
@@ -750,6 +985,8 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 }
 
 static struct drm_crtc_funcs armada_crtc_funcs = {
+	.cursor_set	= armada_drm_crtc_cursor_set,
+	.cursor_move	= armada_drm_crtc_cursor_move,
 	.destroy	= armada_drm_crtc_destroy,
 	.set_config	= drm_crtc_helper_set_config,
 	.page_flip	= armada_drm_crtc_page_flip,
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 972da53..9c10a07 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -44,11 +44,20 @@ struct armada_crtc {
 		uint32_t	spu_adv_reg;
 	} v[2];
 	bool			interlaced;
+	bool			cursor_update;
 	uint8_t			csc_yuv_mode;
 	uint8_t			csc_rgb_mode;
 
 	struct drm_plane	*plane;
 
+	struct armada_gem_object	*cursor_obj;
+	int			cursor_x;
+	int			cursor_y;
+	uint32_t		cursor_hw_pos;
+	uint32_t		cursor_hw_sz;
+	uint32_t		cursor_w;
+	uint32_t		cursor_h;
+
 	int			dpms;
 	uint32_t		cfg_dumb_ctrl;
 	uint32_t		dumb_ctrl;
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index e8c4f80..eef09ec 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -60,6 +60,7 @@ struct armada_private;
 
 struct armada_variant {
 	bool	has_spu_adv_reg;
+	uint32_t spu_adv_reg;
 	int (*init)(struct armada_private *, struct device *);
 	int (*crtc_init)(struct armada_crtc *);
 	int (*crtc_compute_clock)(struct armada_crtc *,
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
index 216a4b5..27319a8 100644
--- a/drivers/gpu/drm/armada/armada_hw.h
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -168,8 +168,10 @@ enum {
 	SRAM_READ	= 0 << 14,
 	SRAM_WRITE	= 2 << 14,
 	SRAM_INIT	= 3 << 14,
-	SRAM_HWC32_RAMR	= 0xc << 8,
-	SRAM_HWC32_RAMG	= 0xd << 8,
+	SRAM_HWC32_RAM1	= 0xc << 8,
+	SRAM_HWC32_RAM2	= 0xd << 8,
+	SRAM_HWC32_RAMR	= SRAM_HWC32_RAM1,
+	SRAM_HWC32_RAMG	= SRAM_HWC32_RAM2,
 	SRAM_HWC32_RAMB	= 0xe << 8,
 	SRAM_HWC32_TRAN	= 0xf << 8,
 	SRAM_HWC	= 0xf << 8,
-- 
1.7.4.4

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

* [PATCH 4/5] DRM: Armada: start of MMP2/MMP3 support
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-06 22:10   ` Russell King
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:10 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/Makefile      |    1 +
 drivers/gpu/drm/armada/armada_610.c  |   49 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.c |    1 +
 drivers/gpu/drm/armada/armada_drm.h  |    1 +
 drivers/gpu/drm/armada/armada_drv.c  |    6 ++++
 drivers/gpu/drm/armada/armada_hw.h   |    8 +++++
 6 files changed, 66 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/armada_610.c

diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
index d6f43e0..088db4a 100644
--- a/drivers/gpu/drm/armada/Makefile
+++ b/drivers/gpu/drm/armada/Makefile
@@ -2,6 +2,7 @@ armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
 		   armada_gem.o armada_output.o armada_overlay.o \
 		   armada_slave.o
 armada-y	+= armada_510.o
+armada-y	+= armada_610.o
 armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
 
 obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_610.c b/drivers/gpu/drm/armada/armada_610.c
new file mode 100644
index 0000000..36a10e3
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_610.c
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * Armada 610/MMP2/MMP3 variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int mmp23_init(struct armada_private *priv, struct device *dev)
+{
+	priv->extclk[0] = devm_clk_get(dev, NULL);
+
+	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
+		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+	return PTR_RET(priv->extclk[0]);
+}
+
+/*
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ */
+static int mmp23_crtc_compute_clock(struct armada_crtc *dcrtc,
+	const struct drm_display_mode *mode, uint32_t *sclk)
+{
+	/*
+	 * on MMP3 bits 31:29 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 * on MMP2 bits 31:30 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 */
+	*sclk = 0x20001100; // FIXME hardcoded mmp3 value
+
+	return 0;
+}
+
+const struct armada_variant mmp23_ops = {
+	.init = mmp23_init,
+	.crtc_compute_clock = mmp23_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index e8605bf..b5877ee 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -1054,6 +1054,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
+	/* FIXME: MMP2/MMP3: OLPC panel is RGB666 */
 	dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
 	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
 	spin_lock_init(&dcrtc->irq_lock);
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index eef09ec..f52fccb 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -70,6 +70,7 @@ struct armada_variant {
 
 /* Variant ops */
 extern const struct armada_variant armada510_ops;
+extern const struct armada_variant mmp23_ops;
 
 struct armada_private {
 	const struct armada_variant *variant;
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 7bfab9a..db62f1b 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -344,6 +344,12 @@ static const struct platform_device_id armada_drm_platform_ids[] = {
 		.name		= "armada-drm",
 		.driver_data	= (unsigned long)&armada510_ops,
 	}, {
+		.name		= "armada-610-drm", /* aka MMP2 */
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
+		.name		= "mmp3-drm",
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
 		.name		= "armada-510-drm",
 		.driver_data	= (unsigned long)&armada510_ops,
 	},
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
index 27319a8..1688716 100644
--- a/drivers/gpu/drm/armada/armada_hw.h
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -74,6 +74,7 @@ enum {
 	LCD_SPU_IOPAD_CONTROL		= 0x01bc,
 	LCD_SPU_IRQ_ENA			= 0x01c0,
 	LCD_SPU_IRQ_ISR			= 0x01c4,
+	LCD_MISC_CNTL			= 0x01c8,	/* Armada 168 */
 };
 
 /* For LCD_SPU_ADV_REG */
@@ -211,6 +212,13 @@ enum {
 	SCLK_16X_PLL		= 0x8 << 28,
 	SCLK_16X_FRAC_DIV_MASK	= 0xfff << 16,
 	SCLK_16X_INT_DIV_MASK	= 0xffff << 0,
+
+	/* PXA910 / MMP2 (Armada 610) / MMP3 / PXA988 */
+	SCLK_MMP_SRC_SEL	= 1 << 31,
+	SCLK_MMP_DISABLE	= 1 << 28,
+	SCLK_MMP_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_MMP_DSI_DIV_MASK	= 0xf << 8,
+	SCLK_MMP_INT_DIV_MASK	= 0xff << 0,
 };
 
 /* For LCD_SPU_DUMB_CTRL */
-- 
1.7.4.4

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

* [PATCH 4/5] DRM: Armada: start of MMP2/MMP3 support
@ 2013-10-06 22:10   ` Russell King
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:10 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter,
	Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/Makefile      |    1 +
 drivers/gpu/drm/armada/armada_610.c  |   49 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.c |    1 +
 drivers/gpu/drm/armada/armada_drm.h  |    1 +
 drivers/gpu/drm/armada/armada_drv.c  |    6 ++++
 drivers/gpu/drm/armada/armada_hw.h   |    8 +++++
 6 files changed, 66 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/armada_610.c

diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
index d6f43e0..088db4a 100644
--- a/drivers/gpu/drm/armada/Makefile
+++ b/drivers/gpu/drm/armada/Makefile
@@ -2,6 +2,7 @@ armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
 		   armada_gem.o armada_output.o armada_overlay.o \
 		   armada_slave.o
 armada-y	+= armada_510.o
+armada-y	+= armada_610.o
 armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
 
 obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_610.c b/drivers/gpu/drm/armada/armada_610.c
new file mode 100644
index 0000000..36a10e3
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_610.c
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * Armada 610/MMP2/MMP3 variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int mmp23_init(struct armada_private *priv, struct device *dev)
+{
+	priv->extclk[0] = devm_clk_get(dev, NULL);
+
+	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
+		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+	return PTR_RET(priv->extclk[0]);
+}
+
+/*
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ */
+static int mmp23_crtc_compute_clock(struct armada_crtc *dcrtc,
+	const struct drm_display_mode *mode, uint32_t *sclk)
+{
+	/*
+	 * on MMP3 bits 31:29 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 * on MMP2 bits 31:30 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 */
+	*sclk = 0x20001100; // FIXME hardcoded mmp3 value
+
+	return 0;
+}
+
+const struct armada_variant mmp23_ops = {
+	.init = mmp23_init,
+	.crtc_compute_clock = mmp23_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index e8605bf..b5877ee 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -1054,6 +1054,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
+	/* FIXME: MMP2/MMP3: OLPC panel is RGB666 */
 	dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
 	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
 	spin_lock_init(&dcrtc->irq_lock);
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index eef09ec..f52fccb 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -70,6 +70,7 @@ struct armada_variant {
 
 /* Variant ops */
 extern const struct armada_variant armada510_ops;
+extern const struct armada_variant mmp23_ops;
 
 struct armada_private {
 	const struct armada_variant *variant;
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 7bfab9a..db62f1b 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -344,6 +344,12 @@ static const struct platform_device_id armada_drm_platform_ids[] = {
 		.name		= "armada-drm",
 		.driver_data	= (unsigned long)&armada510_ops,
 	}, {
+		.name		= "armada-610-drm", /* aka MMP2 */
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
+		.name		= "mmp3-drm",
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
 		.name		= "armada-510-drm",
 		.driver_data	= (unsigned long)&armada510_ops,
 	},
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
index 27319a8..1688716 100644
--- a/drivers/gpu/drm/armada/armada_hw.h
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -74,6 +74,7 @@ enum {
 	LCD_SPU_IOPAD_CONTROL		= 0x01bc,
 	LCD_SPU_IRQ_ENA			= 0x01c0,
 	LCD_SPU_IRQ_ISR			= 0x01c4,
+	LCD_MISC_CNTL			= 0x01c8,	/* Armada 168 */
 };
 
 /* For LCD_SPU_ADV_REG */
@@ -211,6 +212,13 @@ enum {
 	SCLK_16X_PLL		= 0x8 << 28,
 	SCLK_16X_FRAC_DIV_MASK	= 0xfff << 16,
 	SCLK_16X_INT_DIV_MASK	= 0xffff << 0,
+
+	/* PXA910 / MMP2 (Armada 610) / MMP3 / PXA988 */
+	SCLK_MMP_SRC_SEL	= 1 << 31,
+	SCLK_MMP_DISABLE	= 1 << 28,
+	SCLK_MMP_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_MMP_DSI_DIV_MASK	= 0xf << 8,
+	SCLK_MMP_INT_DIV_MASK	= 0xff << 0,
 };
 
 /* For LCD_SPU_DUMB_CTRL */
-- 
1.7.4.4

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-06 22:11   ` Russell King
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:11 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/Kconfig      |    9 +++++++
 drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
index c7a0a94..87e62dd 100644
--- a/drivers/gpu/drm/armada/Kconfig
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -13,3 +13,12 @@ config DRM_ARMADA
 	  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.
+
+config DRM_ARMADA_TDA1998X
+	bool "Support TDA1998X HDMI output"
+	depends on DRM_ARMADA != n
+	depends on I2C && DRM_I2C_NXP_TDA998X = y
+	default y
+	help
+	  Support the TDA1998x HDMI output device found on the Solid-Run
+	  CuBox.
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index db62f1b..69517cf 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -16,6 +16,42 @@
 #include <drm/armada_drm.h>
 #include "armada_ioctlP.h"
 
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+#include <drm/i2c/tda998x.h>
+#include "armada_slave.h"
+
+static struct tda998x_encoder_params params = {
+	/* 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,
+	.audio_cfg = BIT(2),
+	.audio_frame[1] = 1,
+	.audio_format = AFMT_SPDIF,
+	.audio_sample_rate = 44100,
+};
+
+static const struct armada_drm_slave_config tda19988_config = {
+	.i2c_adapter_id = 0,
+	.crtcs = 1 << 0, /* Only LCD0@the moment */
+	.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
+	.interlace_allowed = true,
+	.info = {
+		.type = "tda998x",
+		.addr = 0x70,
+		.platform_data = &params,
+	},
+};
+#endif
+
 static void armada_drm_unref_work(struct work_struct *work)
 {
 	struct armada_private *priv =
@@ -134,6 +170,12 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 			goto err_kms;
 	}
 
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+	ret = armada_drm_connector_slave_create(dev, &tda19988_config);
+	if (ret)
+		goto err_kms;
+#endif
+
 	ret = drm_vblank_init(dev, n);
 	if (ret)
 		goto err_kms;
-- 
1.7.4.4

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-06 22:11   ` Russell King
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King @ 2013-10-06 22:11 UTC (permalink / raw)
  To: dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter,
	Sebastian Hesselbarth

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/armada/Kconfig      |    9 +++++++
 drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
index c7a0a94..87e62dd 100644
--- a/drivers/gpu/drm/armada/Kconfig
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -13,3 +13,12 @@ config DRM_ARMADA
 	  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.
+
+config DRM_ARMADA_TDA1998X
+	bool "Support TDA1998X HDMI output"
+	depends on DRM_ARMADA != n
+	depends on I2C && DRM_I2C_NXP_TDA998X = y
+	default y
+	help
+	  Support the TDA1998x HDMI output device found on the Solid-Run
+	  CuBox.
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index db62f1b..69517cf 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -16,6 +16,42 @@
 #include <drm/armada_drm.h>
 #include "armada_ioctlP.h"
 
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+#include <drm/i2c/tda998x.h>
+#include "armada_slave.h"
+
+static struct tda998x_encoder_params params = {
+	/* 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,
+	.audio_cfg = BIT(2),
+	.audio_frame[1] = 1,
+	.audio_format = AFMT_SPDIF,
+	.audio_sample_rate = 44100,
+};
+
+static const struct armada_drm_slave_config tda19988_config = {
+	.i2c_adapter_id = 0,
+	.crtcs = 1 << 0, /* Only LCD0 at the moment */
+	.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
+	.interlace_allowed = true,
+	.info = {
+		.type = "tda998x",
+		.addr = 0x70,
+		.platform_data = &params,
+	},
+};
+#endif
+
 static void armada_drm_unref_work(struct work_struct *work)
 {
 	struct armada_private *priv =
@@ -134,6 +170,12 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 			goto err_kms;
 	}
 
+#ifdef CONFIG_DRM_ARMADA_TDA1998X
+	ret = armada_drm_connector_slave_create(dev, &tda19988_config);
+	if (ret)
+		goto err_kms;
+#endif
+
 	ret = drm_vblank_init(dev, n);
 	if (ret)
 		goto err_kms;
-- 
1.7.4.4

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

* [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
  2013-10-06 22:07   ` Russell King
@ 2013-10-07  8:59     ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 06 Oct 2013 23:07:55 +0100
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 |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index 60e8404..704040b5 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
>  	buf[HB(0)] = 0x82;
>  	buf[HB(1)] = 0x02;
>  	buf[HB(2)] = 13;
> +	buf[PB(1)] = 2; /* underscanned display */
> +	buf[PB(3)] = 2 << 2; /* full range */
>  	buf[PB(4)] = drm_match_cea_mode(mode);
>  
>  	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,

You should use the 'hdmi.h' constants which are meaningful enough:

	buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
	buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;

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

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

* Re: [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
@ 2013-10-07  8:59     ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  8:59 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, 06 Oct 2013 23:07:55 +0100
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 |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index 60e8404..704040b5 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
>  	buf[HB(0)] = 0x82;
>  	buf[HB(1)] = 0x02;
>  	buf[HB(2)] = 13;
> +	buf[PB(1)] = 2; /* underscanned display */
> +	buf[PB(3)] = 2 << 2; /* full range */
>  	buf[PB(4)] = drm_match_cea_mode(mode);
>  
>  	tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,

You should use the 'hdmi.h' constants which are meaningful enough:

	buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
	buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;

-- 
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] 86+ messages in thread

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-06 22:09   ` Russell King
@ 2013-10-07  9:01     ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  9:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 06 Oct 2013 23:09:56 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> This patch adds ARGB hardware cursor support to the DRM driver for the
> Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> 64x32 resolutions.
	[snip]

I don't see the interest of such cursors. Actually, most often, the
cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
supports 64x64 cursors with transparency, it would be more useful to
implement these ones...

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

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07  9:01     ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  9:01 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, 06 Oct 2013 23:09:56 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> This patch adds ARGB hardware cursor support to the DRM driver for the
> Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> 64x32 resolutions.
	[snip]

I don't see the interest of such cursors. Actually, most often, the
cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
supports 64x64 cursors with transparency, it would be more useful to
implement these 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] 86+ messages in thread

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-06 22:11   ` Russell King
@ 2013-10-07  9:18     ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  9:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 06 Oct 2013 23:11:56 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/armada/Kconfig      |    9 +++++++
>  drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
>  2 files changed, 51 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
> index c7a0a94..87e62dd 100644
> --- a/drivers/gpu/drm/armada/Kconfig
> +++ b/drivers/gpu/drm/armada/Kconfig
> @@ -13,3 +13,12 @@ config DRM_ARMADA
>  	  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.
> +
> +config DRM_ARMADA_TDA1998X
> +	bool "Support TDA1998X HDMI output"
> +	depends on DRM_ARMADA != n
> +	depends on I2C && DRM_I2C_NXP_TDA998X = y
> +	default y
> +	help
> +	  Support the TDA1998x HDMI output device found on the Solid-Run
> +	  CuBox.

It seems we are going backwards: as the Armada based boards will soon
move to full DT (mvebu), you are making an exception for the Cubox, so
that there should be Cubox specific kernels. I don't like that...

> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index db62f1b..69517cf 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -16,6 +16,42 @@
>  #include <drm/armada_drm.h>
>  #include "armada_ioctlP.h"
>  
> +#ifdef CONFIG_DRM_ARMADA_TDA1998X
> +#include <drm/i2c/tda998x.h>
> +#include "armada_slave.h"
> +
> +static struct tda998x_encoder_params params = {
> +	/* 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,

I still don't agree. You don't need to invert R <-> B for the Cubox at
the tda998x level: this may be done setting as it should be the
CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.

> +	.audio_cfg = BIT(2),
> +	.audio_frame[1] = 1,
> +	.audio_format = AFMT_SPDIF,
> +	.audio_sample_rate = 44100,

These values are rather mysterious!

Looking at the tda998x driver, I found that:
- audio_cfg can be 0x03 for i2s input and 0x04 for spdif input
- audio_frame[1] is the (channel count - 1)
- audio_format and audio_sample_rate are hardcoding the audio input to
  spdif and the sample rate to 44.1kHz

I don't think that these parameters are needed:

- the tda998x must be prepared to get either i2s or spdif as the audio
  input at any time. The choice of this input results from the audio
  subsystem constraints (codec) found at audio streaming start time.

- the channel count is always 2 for spdif. Do we need to accept four
  channels for i2s?

- audio on hdmi works fine for me at any input rate setting the tda998x
  sample rate to 96 kHz. Anyway, in the actual tda998x driver, this
  audio_sample_rate value is just used to set an approximate value of
  ACR. But this value is not used because an optimal value is computed
  by the hardware thanks to the flag AIP_CNTRL_0_ACR_MAN!

	[snip]

The remaining patch is Cubox specific.

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07  9:18     ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07  9:18 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, 06 Oct 2013 23:11:56 +0100
Russell King <rmk+kernel@arm.linux.org.uk> wrote:

> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/armada/Kconfig      |    9 +++++++
>  drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
>  2 files changed, 51 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
> index c7a0a94..87e62dd 100644
> --- a/drivers/gpu/drm/armada/Kconfig
> +++ b/drivers/gpu/drm/armada/Kconfig
> @@ -13,3 +13,12 @@ config DRM_ARMADA
>  	  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.
> +
> +config DRM_ARMADA_TDA1998X
> +	bool "Support TDA1998X HDMI output"
> +	depends on DRM_ARMADA != n
> +	depends on I2C && DRM_I2C_NXP_TDA998X = y
> +	default y
> +	help
> +	  Support the TDA1998x HDMI output device found on the Solid-Run
> +	  CuBox.

It seems we are going backwards: as the Armada based boards will soon
move to full DT (mvebu), you are making an exception for the Cubox, so
that there should be Cubox specific kernels. I don't like that...

> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index db62f1b..69517cf 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -16,6 +16,42 @@
>  #include <drm/armada_drm.h>
>  #include "armada_ioctlP.h"
>  
> +#ifdef CONFIG_DRM_ARMADA_TDA1998X
> +#include <drm/i2c/tda998x.h>
> +#include "armada_slave.h"
> +
> +static struct tda998x_encoder_params params = {
> +	/* 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,

I still don't agree. You don't need to invert R <-> B for the Cubox at
the tda998x level: this may be done setting as it should be the
CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.

> +	.audio_cfg = BIT(2),
> +	.audio_frame[1] = 1,
> +	.audio_format = AFMT_SPDIF,
> +	.audio_sample_rate = 44100,

These values are rather mysterious!

Looking at the tda998x driver, I found that:
- audio_cfg can be 0x03 for i2s input and 0x04 for spdif input
- audio_frame[1] is the (channel count - 1)
- audio_format and audio_sample_rate are hardcoding the audio input to
  spdif and the sample rate to 44.1kHz

I don't think that these parameters are needed:

- the tda998x must be prepared to get either i2s or spdif as the audio
  input at any time. The choice of this input results from the audio
  subsystem constraints (codec) found at audio streaming start time.

- the channel count is always 2 for spdif. Do we need to accept four
  channels for i2s?

- audio on hdmi works fine for me at any input rate setting the tda998x
  sample rate to 96 kHz. Anyway, in the actual tda998x driver, this
  audio_sample_rate value is just used to set an approximate value of
  ACR. But this value is not used because an optimal value is computed
  by the hardware thanks to the flag AIP_CNTRL_0_ACR_MAN!

	[snip]

The remaining patch is Cubox specific.

-- 
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] 86+ messages in thread

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07  9:01     ` Jean-Francois Moine
@ 2013-10-07  9:40       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 11:01:41AM +0200, Jean-Francois Moine wrote:
> On Sun, 06 Oct 2013 23:09:56 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > This patch adds ARGB hardware cursor support to the DRM driver for the
> > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > 64x32 resolutions.
> 	[snip]
> 
> I don't see the interest of such cursors. Actually, most often, the
> cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> supports 64x64 cursors with transparency, it would be more useful to
> implement these ones...

Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
This patch is a result of trialling each of the Armada's cursor options
and this is the only one which results in a reasonable looking cursor.

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07  9:40       ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07  9:40 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 11:01:41AM +0200, Jean-Francois Moine wrote:
> On Sun, 06 Oct 2013 23:09:56 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > This patch adds ARGB hardware cursor support to the DRM driver for the
> > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > 64x32 resolutions.
> 	[snip]
> 
> I don't see the interest of such cursors. Actually, most often, the
> cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> supports 64x64 cursors with transparency, it would be more useful to
> implement these ones...

Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
This patch is a result of trialling each of the Armada's cursor options
and this is the only one which results in a reasonable looking cursor.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07  9:18     ` Jean-Francois Moine
@ 2013-10-07  9:44       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07  9:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
> On Sun, 06 Oct 2013 23:11:56 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/armada/Kconfig      |    9 +++++++
> >  drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
> >  2 files changed, 51 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
> > index c7a0a94..87e62dd 100644
> > --- a/drivers/gpu/drm/armada/Kconfig
> > +++ b/drivers/gpu/drm/armada/Kconfig
> > @@ -13,3 +13,12 @@ config DRM_ARMADA
> >  	  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.
> > +
> > +config DRM_ARMADA_TDA1998X
> > +	bool "Support TDA1998X HDMI output"
> > +	depends on DRM_ARMADA != n
> > +	depends on I2C && DRM_I2C_NXP_TDA998X = y
> > +	default y
> > +	help
> > +	  Support the TDA1998x HDMI output device found on the Solid-Run
> > +	  CuBox.
> 
> It seems we are going backwards: as the Armada based boards will soon
> move to full DT (mvebu), you are making an exception for the Cubox, so
> that there should be Cubox specific kernels. I don't like that...

*Ignored*.  You know why.

> > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> > index db62f1b..69517cf 100644
> > --- a/drivers/gpu/drm/armada/armada_drv.c
> > +++ b/drivers/gpu/drm/armada/armada_drv.c
> > @@ -16,6 +16,42 @@
> >  #include <drm/armada_drm.h>
> >  #include "armada_ioctlP.h"
> >  
> > +#ifdef CONFIG_DRM_ARMADA_TDA1998X
> > +#include <drm/i2c/tda998x.h>
> > +#include "armada_slave.h"
> > +
> > +static struct tda998x_encoder_params params = {
> > +	/* 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,
> 
> I still don't agree. You don't need to invert R <-> B for the Cubox at
> the tda998x level: this may be done setting as it should be the
> CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.

You are totally and utterly wrong there.  We need R and B presented on
their correct lanes to the TDA998x so that the Armadas YUV->RGB
conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
to match, neither does setting any of the other bits.

CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
nothing to do at all with how the output is wired.

> > +	.audio_cfg = BIT(2),
> > +	.audio_frame[1] = 1,
> > +	.audio_format = AFMT_SPDIF,
> > +	.audio_sample_rate = 44100,
> 
> These values are rather mysterious!

Also I'm going to ignore this comment, because quite honestly, I think
this is worthless.  You haven't investigated how the TDA998x actually
gets setup by Rabeeh.

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07  9:44       ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07  9:44 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
> On Sun, 06 Oct 2013 23:11:56 +0100
> Russell King <rmk+kernel@arm.linux.org.uk> wrote:
> 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/armada/Kconfig      |    9 +++++++
> >  drivers/gpu/drm/armada/armada_drv.c |   42 +++++++++++++++++++++++++++++++++++
> >  2 files changed, 51 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
> > index c7a0a94..87e62dd 100644
> > --- a/drivers/gpu/drm/armada/Kconfig
> > +++ b/drivers/gpu/drm/armada/Kconfig
> > @@ -13,3 +13,12 @@ config DRM_ARMADA
> >  	  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.
> > +
> > +config DRM_ARMADA_TDA1998X
> > +	bool "Support TDA1998X HDMI output"
> > +	depends on DRM_ARMADA != n
> > +	depends on I2C && DRM_I2C_NXP_TDA998X = y
> > +	default y
> > +	help
> > +	  Support the TDA1998x HDMI output device found on the Solid-Run
> > +	  CuBox.
> 
> It seems we are going backwards: as the Armada based boards will soon
> move to full DT (mvebu), you are making an exception for the Cubox, so
> that there should be Cubox specific kernels. I don't like that...

*Ignored*.  You know why.

> > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> > index db62f1b..69517cf 100644
> > --- a/drivers/gpu/drm/armada/armada_drv.c
> > +++ b/drivers/gpu/drm/armada/armada_drv.c
> > @@ -16,6 +16,42 @@
> >  #include <drm/armada_drm.h>
> >  #include "armada_ioctlP.h"
> >  
> > +#ifdef CONFIG_DRM_ARMADA_TDA1998X
> > +#include <drm/i2c/tda998x.h>
> > +#include "armada_slave.h"
> > +
> > +static struct tda998x_encoder_params params = {
> > +	/* 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,
> 
> I still don't agree. You don't need to invert R <-> B for the Cubox at
> the tda998x level: this may be done setting as it should be the
> CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.

You are totally and utterly wrong there.  We need R and B presented on
their correct lanes to the TDA998x so that the Armadas YUV->RGB
conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
to match, neither does setting any of the other bits.

CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
nothing to do at all with how the output is wired.

> > +	.audio_cfg = BIT(2),
> > +	.audio_frame[1] = 1,
> > +	.audio_format = AFMT_SPDIF,
> > +	.audio_sample_rate = 44100,
> 
> These values are rather mysterious!

Also I'm going to ignore this comment, because quite honestly, I think
this is worthless.  You haven't investigated how the TDA998x actually
gets setup by Rabeeh.

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07  9:40       ` Russell King - ARM Linux
@ 2013-10-07 10:09         ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 10:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 7 Oct 2013 10:40:08 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > 64x32 resolutions.  
> > 	[snip]
> > 
> > I don't see the interest of such cursors. Actually, most often, the
> > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > supports 64x64 cursors with transparency, it would be more useful to
> > implement these ones...  
> 
> Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> This patch is a result of trialling each of the Armada's cursor options
> and this is the only one which results in a reasonable looking cursor.

Strange. I am using the 64x64 cursor with transparency of the 510 for
many months and I never saw any problem.

If you absolutely want to have all transparency shades, you should
accept 64x64 cursors and test if they may be rendered as 64x32 or 32x64,
i.e. test that there is only pure transparency in the non-rendered
rectangles...

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

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07 10:09         ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 10:09 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, 7 Oct 2013 10:40:08 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > 64x32 resolutions.  
> > 	[snip]
> > 
> > I don't see the interest of such cursors. Actually, most often, the
> > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > supports 64x64 cursors with transparency, it would be more useful to
> > implement these ones...  
> 
> Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> This patch is a result of trialling each of the Armada's cursor options
> and this is the only one which results in a reasonable looking cursor.

Strange. I am using the 64x64 cursor with transparency of the 510 for
many months and I never saw any problem.

If you absolutely want to have all transparency shades, you should
accept 64x64 cursors and test if they may be rendered as 64x32 or 32x64,
i.e. test that there is only pure transparency in the non-rendered
rectangles...

-- 
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] 86+ messages in thread

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07 10:09         ` Jean-Francois Moine
@ 2013-10-07 10:32           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 10:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 12:09:22PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 10:40:08 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > > 64x32 resolutions.  
> > > 	[snip]
> > > 
> > > I don't see the interest of such cursors. Actually, most often, the
> > > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > > supports 64x64 cursors with transparency, it would be more useful to
> > > implement these ones...  
> > 
> > Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> > This patch is a result of trialling each of the Armada's cursor options
> > and this is the only one which results in a reasonable looking cursor.
> 
> Strange. I am using the 64x64 cursor with transparency of the 510 for
> many months and I never saw any problem.

When I tried it, all cursors had variable alpha, and converting the
alpha to a single transparency bit made the cursor look rubbish against
certain backgrounds.

> If you absolutely want to have all transparency shades, you should
> accept 64x64 cursors and test if they may be rendered as 64x32 or
> 32x64, i.e. test that there is only pure transparency in the non-
> rendered rectangles...

That is policy, and that is something which can be done by the X server
rather than the kernel.  The kernel exposes the hardware capabilities,
which are to allow a 64x32 or a 32x64 cursor.  Userspace can make the
decision dynamically which it wishes to use.

However, what you're suggesting is dangerous.  What do you do when you're
presented with a cursor which is 64x64 ?  Do you:

(a) not display it
(b) crash the X server

There isn't a fallback to using software cursors available in the X server
because there's no error reporting for a failed hardware cursor update.

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07 10:32           ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 10:32 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 12:09:22PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 10:40:08 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > > 64x32 resolutions.  
> > > 	[snip]
> > > 
> > > I don't see the interest of such cursors. Actually, most often, the
> > > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > > supports 64x64 cursors with transparency, it would be more useful to
> > > implement these ones...  
> > 
> > Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> > This patch is a result of trialling each of the Armada's cursor options
> > and this is the only one which results in a reasonable looking cursor.
> 
> Strange. I am using the 64x64 cursor with transparency of the 510 for
> many months and I never saw any problem.

When I tried it, all cursors had variable alpha, and converting the
alpha to a single transparency bit made the cursor look rubbish against
certain backgrounds.

> If you absolutely want to have all transparency shades, you should
> accept 64x64 cursors and test if they may be rendered as 64x32 or
> 32x64, i.e. test that there is only pure transparency in the non-
> rendered rectangles...

That is policy, and that is something which can be done by the X server
rather than the kernel.  The kernel exposes the hardware capabilities,
which are to allow a 64x32 or a 32x64 cursor.  Userspace can make the
decision dynamically which it wishes to use.

However, what you're suggesting is dangerous.  What do you do when you're
presented with a cursor which is 64x64 ?  Do you:

(a) not display it
(b) crash the X server

There isn't a fallback to using software cursors available in the X server
because there's no error reporting for a failed hardware cursor update.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07  9:44       ` Russell King - ARM Linux
@ 2013-10-07 10:48         ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 10:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 7 Oct 2013 10:44:04 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
	[snip]
> > It seems we are going backwards: as the Armada based boards will soon
> > move to full DT (mvebu), you are making an exception for the Cubox, so
> > that there should be Cubox specific kernels. I don't like that...
> 
> *Ignored*.  You know why.

Sorry. I don't see why. May you explain again?

> > > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
	[snip]
> > > +static struct tda998x_encoder_params params = {
> > > +	/* 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,
> > 
> > I still don't agree. You don't need to invert R <-> B for the Cubox at
> > the tda998x level: this may be done setting as it should be the
> > CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.
> 
> You are totally and utterly wrong there.  We need R and B presented on
> their correct lanes to the TDA998x so that the Armadas YUV->RGB
> conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
> to match, neither does setting any of the other bits.
> 
> CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
> nothing to do at all with how the output is wired.

Then, may you explain why you must swap R/B on simple RGB output?

>From your [PATCH 2/5] DRM: Armada: Add Armada DRM driver:

+	FMT(RGB888,	888PACK,	CFG_SWAPRB);
+	FMT(BGR888,	888PACK,	0);

+	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);

> > > +	.audio_cfg = BIT(2),
> > > +	.audio_frame[1] = 1,
> > > +	.audio_format = AFMT_SPDIF,
> > > +	.audio_sample_rate = 44100,
> > 
> > These values are rather mysterious!
> 
> Also I'm going to ignore this comment, because quite honestly, I think
> this is worthless.  You haven't investigated how the TDA998x actually
> gets setup by Rabeeh.

Rabeeh did the most he could to have a working Cubox. He used bad
written drivers and he had not the time to think about how the drivers
could be enhanced.

Here is a small story about i2s/spdif: once, I set the tda998x to use
the spdif input, and at this time, I was using the dummy codec.
This codec accepts the format 32_LE, as does the audio device, but the
output cannot go to spdif. Result: no hdmi sound.

Should we restrict the hdmi transmitter to spdif (thus 'no 32 bits
stream') or to i2s (thus 'no compressed stream') only, or accept both
i2s and spdif and allow a full range of supported audio streams?

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 10:48         ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 10:48 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Mon, 7 Oct 2013 10:44:04 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
	[snip]
> > It seems we are going backwards: as the Armada based boards will soon
> > move to full DT (mvebu), you are making an exception for the Cubox, so
> > that there should be Cubox specific kernels. I don't like that...
> 
> *Ignored*.  You know why.

Sorry. I don't see why. May you explain again?

> > > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
	[snip]
> > > +static struct tda998x_encoder_params params = {
> > > +	/* 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,
> > 
> > I still don't agree. You don't need to invert R <-> B for the Cubox at
> > the tda998x level: this may be done setting as it should be the
> > CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.
> 
> You are totally and utterly wrong there.  We need R and B presented on
> their correct lanes to the TDA998x so that the Armadas YUV->RGB
> conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
> to match, neither does setting any of the other bits.
> 
> CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
> nothing to do at all with how the output is wired.

Then, may you explain why you must swap R/B on simple RGB output?

From your [PATCH 2/5] DRM: Armada: Add Armada DRM driver:

+	FMT(RGB888,	888PACK,	CFG_SWAPRB);
+	FMT(BGR888,	888PACK,	0);

+	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);

> > > +	.audio_cfg = BIT(2),
> > > +	.audio_frame[1] = 1,
> > > +	.audio_format = AFMT_SPDIF,
> > > +	.audio_sample_rate = 44100,
> > 
> > These values are rather mysterious!
> 
> Also I'm going to ignore this comment, because quite honestly, I think
> this is worthless.  You haven't investigated how the TDA998x actually
> gets setup by Rabeeh.

Rabeeh did the most he could to have a working Cubox. He used bad
written drivers and he had not the time to think about how the drivers
could be enhanced.

Here is a small story about i2s/spdif: once, I set the tda998x to use
the spdif input, and at this time, I was using the dummy codec.
This codec accepts the format 32_LE, as does the audio device, but the
output cannot go to spdif. Result: no hdmi sound.

Should we restrict the hdmi transmitter to spdif (thus 'no 32 bits
stream') or to i2s (thus 'no compressed stream') only, or accept both
i2s and spdif and allow a full range of supported audio streams?

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/
+
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 10:48         ` Jean-Francois Moine
@ 2013-10-07 11:09           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 11:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 10:44:04 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
> 	[snip]
> > > It seems we are going backwards: as the Armada based boards will soon
> > > move to full DT (mvebu), you are making an exception for the Cubox, so
> > > that there should be Cubox specific kernels. I don't like that...
> > 
> > *Ignored*.  You know why.
> 
> Sorry. I don't see why. May you explain again?

I don't run DT because DT lacks most of the features I require on the
cubox.  Therefore I can't develop for DT.  Simple.

> > > > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> 	[snip]
> > > > +static struct tda998x_encoder_params params = {
> > > > +	/* 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,
> > > 
> > > I still don't agree. You don't need to invert R <-> B for the Cubox at
> > > the tda998x level: this may be done setting as it should be the
> > > CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.
> > 
> > You are totally and utterly wrong there.  We need R and B presented on
> > their correct lanes to the TDA998x so that the Armadas YUV->RGB
> > conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
> > to match, neither does setting any of the other bits.
> > 
> > CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
> > nothing to do at all with how the output is wired.
> 
> Then, may you explain why you must swap R/B on simple RGB output?
> 
> From your [PATCH 2/5] DRM: Armada: Add Armada DRM driver:
> 
> +	FMT(RGB888,	888PACK,	CFG_SWAPRB);
> +	FMT(BGR888,	888PACK,	0);

I think I explained above.  This is to do with the *graphics* *framebuffer*
*format* in *memory*.  This has nothing to do with how the hardware is
wired up.

With no swapping, the 888 format is defined by the documentation to be:

	[7:0] = red
	[15:8] = green
	[23:16] = blue

Now, if you look this up in the drm_fourcc.h header file, this corresponds
with this entry:

#define DRM_FORMAT_BGR888       fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */

Hence, this is the BGR888 format with no swapping.  Therefore, my second
line is correct.

With CFG_SWAPRB set, the format in hardware becomes:

	[7:0] = blue
	[15:8] = green
	[23:16] = red

Again, if you look this up in the drm_fourcc.h header, you get:

#define DRM_FORMAT_RGB888       fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */

So, the two entries you quote above are 100% correct.

> +	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);

Where is the RGB swap there?  Again, this is to do with swapping the
format to match the hardware.  Please read the documentation on the
framebuffer formats.

The native 422PACK format is:
	[7:0] = U
	[15:8] = Y0
	[23:16] = V
	[31:24] = Y1

and we need to set both CFG_SWAPYU and CFG_SWAPUV to get this to be:
	[7:0] = V
	[15:8] = Y1
	[23:16] = U
	[31:24] = Y0

which is what the YUYV format wants.

> Rabeeh did the most he could to have a working Cubox. He used bad
> written drivers and he had not the time to think about how the drivers
> could be enhanced.

What does I2S give us in terms of "enhancement" which SPDIF does not,
remembering that audio is transmitted over HDMI in a format which closely
resembles SPDIF (but with a 28-bit subframe size.)

> Here is a small story about i2s/spdif: once, I set the tda998x to use
> the spdif input, and at this time, I was using the dummy codec.
> This codec accepts the format 32_LE, as does the audio device, but the
> output cannot go to spdif. Result: no hdmi sound.

How ASoC works in this regard is that the capabilities are the _union_ of
the codec and the cpu DAIs.

The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
32.  Quite simply, that's because the SPDIF format does not allow 32-bits
per sample.  Therefore, 32_LE is not available for use with audio output,
and userspace must convert down to something which the hardware does
support.

"Because I2S can support 32-bit audio" is a poor reason, because you can't
pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
to 24 bits for the sample and 4 bits of control/status information.

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 11:09           ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 11:09 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 10:44:04 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
> 	[snip]
> > > It seems we are going backwards: as the Armada based boards will soon
> > > move to full DT (mvebu), you are making an exception for the Cubox, so
> > > that there should be Cubox specific kernels. I don't like that...
> > 
> > *Ignored*.  You know why.
> 
> Sorry. I don't see why. May you explain again?

I don't run DT because DT lacks most of the features I require on the
cubox.  Therefore I can't develop for DT.  Simple.

> > > > diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> 	[snip]
> > > > +static struct tda998x_encoder_params params = {
> > > > +	/* 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,
> > > 
> > > I still don't agree. You don't need to invert R <-> B for the Cubox at
> > > the tda998x level: this may be done setting as it should be the
> > > CFG_GRA_SWAPRB flag of the lcd register LCD_SPU_DMA_CTRL0.
> > 
> > You are totally and utterly wrong there.  We need R and B presented on
> > their correct lanes to the TDA998x so that the Armadas YUV->RGB
> > conversion works.  Setting CFG_GRA_SWAPRB does not swap the YUV output
> > to match, neither does setting any of the other bits.
> > 
> > CFG_GRA_SWAPRB is all about the _graphics_ _framebuffer_ format, it's got
> > nothing to do at all with how the output is wired.
> 
> Then, may you explain why you must swap R/B on simple RGB output?
> 
> From your [PATCH 2/5] DRM: Armada: Add Armada DRM driver:
> 
> +	FMT(RGB888,	888PACK,	CFG_SWAPRB);
> +	FMT(BGR888,	888PACK,	0);

I think I explained above.  This is to do with the *graphics* *framebuffer*
*format* in *memory*.  This has nothing to do with how the hardware is
wired up.

With no swapping, the 888 format is defined by the documentation to be:

	[7:0] = red
	[15:8] = green
	[23:16] = blue

Now, if you look this up in the drm_fourcc.h header file, this corresponds
with this entry:

#define DRM_FORMAT_BGR888       fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */

Hence, this is the BGR888 format with no swapping.  Therefore, my second
line is correct.

With CFG_SWAPRB set, the format in hardware becomes:

	[7:0] = blue
	[15:8] = green
	[23:16] = red

Again, if you look this up in the drm_fourcc.h header, you get:

#define DRM_FORMAT_RGB888       fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */

So, the two entries you quote above are 100% correct.

> +	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);

Where is the RGB swap there?  Again, this is to do with swapping the
format to match the hardware.  Please read the documentation on the
framebuffer formats.

The native 422PACK format is:
	[7:0] = U
	[15:8] = Y0
	[23:16] = V
	[31:24] = Y1

and we need to set both CFG_SWAPYU and CFG_SWAPUV to get this to be:
	[7:0] = V
	[15:8] = Y1
	[23:16] = U
	[31:24] = Y0

which is what the YUYV format wants.

> Rabeeh did the most he could to have a working Cubox. He used bad
> written drivers and he had not the time to think about how the drivers
> could be enhanced.

What does I2S give us in terms of "enhancement" which SPDIF does not,
remembering that audio is transmitted over HDMI in a format which closely
resembles SPDIF (but with a 28-bit subframe size.)

> Here is a small story about i2s/spdif: once, I set the tda998x to use
> the spdif input, and at this time, I was using the dummy codec.
> This codec accepts the format 32_LE, as does the audio device, but the
> output cannot go to spdif. Result: no hdmi sound.

How ASoC works in this regard is that the capabilities are the _union_ of
the codec and the cpu DAIs.

The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
32.  Quite simply, that's because the SPDIF format does not allow 32-bits
per sample.  Therefore, 32_LE is not available for use with audio output,
and userspace must convert down to something which the hardware does
support.

"Because I2S can support 32-bit audio" is a poor reason, because you can't
pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
to 24 bits for the sample and 4 bits of control/status information.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 11:09           ` Russell King - ARM Linux
@ 2013-10-07 11:29             ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 11:29 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/07/2013 01:09 PM, Russell King - ARM Linux wrote:
> On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
>> On Mon, 7 Oct 2013 10:44:04 +0100
>> Rabeeh did the most he could to have a working Cubox. He used bad
>> written drivers and he had not the time to think about how the drivers
>> could be enhanced.
>
> What does I2S give us in terms of "enhancement" which SPDIF does not,
> remembering that audio is transmitted over HDMI in a format which closely
> resembles SPDIF (but with a 28-bit subframe size.)

With respect to what _most_ users want, we should use SPDIF and ignore
I2S. Consumer audio fits well into SPDIF and we get pass-through, which
we loose on I2S.

I2S _can_ support more than two channels, but only if you wire up more
DATA lines. Those are not available on Dove, so its I2S is limited to
two channel audio.

The CuBox is simply not designed for HBR or multi-channel PCM, it is a
_consumer_ settop box replacement.

Sebastian

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 11:29             ` Sebastian Hesselbarth
  0 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 11:29 UTC (permalink / raw)
  To: Russell King - ARM Linux, Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel

On 10/07/2013 01:09 PM, Russell King - ARM Linux wrote:
> On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
>> On Mon, 7 Oct 2013 10:44:04 +0100
>> Rabeeh did the most he could to have a working Cubox. He used bad
>> written drivers and he had not the time to think about how the drivers
>> could be enhanced.
>
> What does I2S give us in terms of "enhancement" which SPDIF does not,
> remembering that audio is transmitted over HDMI in a format which closely
> resembles SPDIF (but with a 28-bit subframe size.)

With respect to what _most_ users want, we should use SPDIF and ignore
I2S. Consumer audio fits well into SPDIF and we get pass-through, which
we loose on I2S.

I2S _can_ support more than two channels, but only if you wire up more
DATA lines. Those are not available on Dove, so its I2S is limited to
two channel audio.

The CuBox is simply not designed for HBR or multi-channel PCM, it is a
_consumer_ settop box replacement.

Sebastian

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 11:09           ` Russell King - ARM Linux
@ 2013-10-07 12:03             ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 12:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 7 Oct 2013 12:09:02 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > Here is a small story about i2s/spdif: once, I set the tda998x to use
> > the spdif input, and at this time, I was using the dummy codec.
> > This codec accepts the format 32_LE, as does the audio device, but the
> > output cannot go to spdif. Result: no hdmi sound.  
> 
> How ASoC works in this regard is that the capabilities are the _union_ of
> the codec and the cpu DAIs.
> 
> The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
> 32.  Quite simply, that's because the SPDIF format does not allow 32-bits
> per sample.  Therefore, 32_LE is not available for use with audio output,
> and userspace must convert down to something which the hardware does
> support.
> 
> "Because I2S can support 32-bit audio" is a poor reason, because you can't
> pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
> to 24 bits for the sample and 4 bits of control/status information.

OK. So, to resume, if both i2s and spdif are furnished by the audio
subsystem, the tda998x must use spdif only (with the spdif codec).

Then, the declaration of the tda998x in the DT will include an
optional audio property, say 'audio-input' with the values "i2s" or
"spdif" which corresponds to your parameter 'audio_format'.

Also, from your video swap explanation, the DT will contain some video
property(ies) TBD.

The remaining question is: do we need more audio parameters?

- the audio_cfg value is defined by i2s/spdif (3 or 4)
- the audio_frame[1] value is always 1 (2 channels)
- audio_sample_rate is useless

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 12:03             ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-07 12:03 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, 7 Oct 2013 12:09:02 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> > Here is a small story about i2s/spdif: once, I set the tda998x to use
> > the spdif input, and at this time, I was using the dummy codec.
> > This codec accepts the format 32_LE, as does the audio device, but the
> > output cannot go to spdif. Result: no hdmi sound.  
> 
> How ASoC works in this regard is that the capabilities are the _union_ of
> the codec and the cpu DAIs.
> 
> The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
> 32.  Quite simply, that's because the SPDIF format does not allow 32-bits
> per sample.  Therefore, 32_LE is not available for use with audio output,
> and userspace must convert down to something which the hardware does
> support.
> 
> "Because I2S can support 32-bit audio" is a poor reason, because you can't
> pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
> to 24 bits for the sample and 4 bits of control/status information.

OK. So, to resume, if both i2s and spdif are furnished by the audio
subsystem, the tda998x must use spdif only (with the spdif codec).

Then, the declaration of the tda998x in the DT will include an
optional audio property, say 'audio-input' with the values "i2s" or
"spdif" which corresponds to your parameter 'audio_format'.

Also, from your video swap explanation, the DT will contain some video
property(ies) TBD.

The remaining question is: do we need more audio parameters?

- the audio_cfg value is defined by i2s/spdif (3 or 4)
- the audio_frame[1] value is always 1 (2 channels)
- audio_sample_rate is useless

-- 
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] 86+ messages in thread

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07 10:32           ` Russell King - ARM Linux
@ 2013-10-07 12:29             ` Siarhei Siamashka
  -1 siblings, 0 replies; 86+ messages in thread
From: Siarhei Siamashka @ 2013-10-07 12:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 7 Oct 2013 11:32:50 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Mon, Oct 07, 2013 at 12:09:22PM +0200, Jean-Francois Moine wrote:
> > On Mon, 7 Oct 2013 10:40:08 +0100
> > Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > 
> > > > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > > > 64x32 resolutions.  
> > > > 	[snip]
> > > > 
> > > > I don't see the interest of such cursors. Actually, most often, the
> > > > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > > > supports 64x64 cursors with transparency, it would be more useful to
> > > > implement these ones...  
> > > 
> > > Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> > > This patch is a result of trialling each of the Armada's cursor options
> > > and this is the only one which results in a reasonable looking cursor.
> > 
> > Strange. I am using the 64x64 cursor with transparency of the 510 for
> > many months and I never saw any problem.
> 
> When I tried it, all cursors had variable alpha, and converting the
> alpha to a single transparency bit made the cursor look rubbish against
> certain backgrounds.
> 
> > If you absolutely want to have all transparency shades, you should
> > accept 64x64 cursors and test if they may be rendered as 64x32 or
> > 32x64, i.e. test that there is only pure transparency in the non-
> > rendered rectangles...
> 
> That is policy, and that is something which can be done by the X server
> rather than the kernel.  The kernel exposes the hardware capabilities,
> which are to allow a 64x32 or a 32x64 cursor.  Userspace can make the
> decision dynamically which it wishes to use.
> 
> However, what you're suggesting is dangerous.  What do you do when you're
> presented with a cursor which is 64x64 ?  Do you:
> 
> (a) not display it
> (b) crash the X server
> 
> There isn't a fallback to using software cursors available in the X server
> because there's no error reporting for a failed hardware cursor update.

Hmm, maybe I'm missing something, but doesn't returning FALSE from
UseHWCursorARGB just make the X server fallback to using a software
cursor?

https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72

I'm just doing this. And also have hooks to notify the DRI2 code about
the status of the cursor. In the case if the hardware cursor is not
enabled, hardware overlays can't be safely used with the software
cursor and we need to fallback to CPU/GPU copy instead of zero-copy
buffers swapping.

And I fully agree that it is the responsibility of the kernel to expose
as much of the hardware capabilities as possible (without compromising
reliability and/or security).

-- 
Best regards,
Siarhei Siamashka

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07 12:29             ` Siarhei Siamashka
  0 siblings, 0 replies; 86+ messages in thread
From: Siarhei Siamashka @ 2013-10-07 12:29 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Mon, 7 Oct 2013 11:32:50 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Mon, Oct 07, 2013 at 12:09:22PM +0200, Jean-Francois Moine wrote:
> > On Mon, 7 Oct 2013 10:40:08 +0100
> > Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > 
> > > > > This patch adds ARGB hardware cursor support to the DRM driver for the
> > > > > Marvell Armada SoCs.  ARGB cursors are supported at either 32x64 or
> > > > > 64x32 resolutions.  
> > > > 	[snip]
> > > > 
> > > > I don't see the interest of such cursors. Actually, most often, the
> > > > cursors are 64x64 and 'A' is either 0 or 0xff. As the Armada 510
> > > > supports 64x64 cursors with transparency, it would be more useful to
> > > > implement these ones...  
> > > 
> > > Sorry, you're completely wrong there.  Xorg uses an alphablended cursor.
> > > This patch is a result of trialling each of the Armada's cursor options
> > > and this is the only one which results in a reasonable looking cursor.
> > 
> > Strange. I am using the 64x64 cursor with transparency of the 510 for
> > many months and I never saw any problem.
> 
> When I tried it, all cursors had variable alpha, and converting the
> alpha to a single transparency bit made the cursor look rubbish against
> certain backgrounds.
> 
> > If you absolutely want to have all transparency shades, you should
> > accept 64x64 cursors and test if they may be rendered as 64x32 or
> > 32x64, i.e. test that there is only pure transparency in the non-
> > rendered rectangles...
> 
> That is policy, and that is something which can be done by the X server
> rather than the kernel.  The kernel exposes the hardware capabilities,
> which are to allow a 64x32 or a 32x64 cursor.  Userspace can make the
> decision dynamically which it wishes to use.
> 
> However, what you're suggesting is dangerous.  What do you do when you're
> presented with a cursor which is 64x64 ?  Do you:
> 
> (a) not display it
> (b) crash the X server
> 
> There isn't a fallback to using software cursors available in the X server
> because there's no error reporting for a failed hardware cursor update.

Hmm, maybe I'm missing something, but doesn't returning FALSE from
UseHWCursorARGB just make the X server fallback to using a software
cursor?

https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72

I'm just doing this. And also have hooks to notify the DRI2 code about
the status of the cursor. In the case if the hardware cursor is not
enabled, hardware overlays can't be safely used with the software
cursor and we need to fallback to CPU/GPU copy instead of zero-copy
buffers swapping.

And I fully agree that it is the responsibility of the kernel to expose
as much of the hardware capabilities as possible (without compromising
reliability and/or security).

-- 
Best regards,
Siarhei Siamashka

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 12:03             ` Jean-Francois Moine
@ 2013-10-07 12:36               ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 02:03:27PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 12:09:02 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > > Here is a small story about i2s/spdif: once, I set the tda998x to use
> > > the spdif input, and at this time, I was using the dummy codec.
> > > This codec accepts the format 32_LE, as does the audio device, but the
> > > output cannot go to spdif. Result: no hdmi sound.  
> > 
> > How ASoC works in this regard is that the capabilities are the _union_ of
> > the codec and the cpu DAIs.
> > 
> > The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
> > 32.  Quite simply, that's because the SPDIF format does not allow 32-bits
> > per sample.  Therefore, 32_LE is not available for use with audio output,
> > and userspace must convert down to something which the hardware does
> > support.
> > 
> > "Because I2S can support 32-bit audio" is a poor reason, because you can't
> > pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
> > to 24 bits for the sample and 4 bits of control/status information.
> 
> OK. So, to resume, if both i2s and spdif are furnished by the audio
> subsystem, the tda998x must use spdif only (with the spdif codec).

This is going off-topic because the TDA998x bindings are a subject for
the TDA998x driver, not the Armada driver.  This data structure merely
exists to provide the required data for the TDA998x in absence of DT
information.

To do the job properly - which is what should be done _whenever_ DT
bindings are being considered - you have to look at the hardware with
an overview of its capabilities, never at the implementation.

So, the TDA998x family needs to be looked at in isolation, and its DT
bindings need to be considered on its own merit.

The TDA998x can accept a SPDIF signal on a single pin (on AP6), with or
without a clock (on AP5), or it can accept I2S: I2S clock in APCLK, I2S
word select on AP0, and up to four I2S streams on AP1..4 and auxillary
("internal test") data in AP7.  AP7 should therefore not be used.

The TDA19988 is slightly different, because it doesn't have soo many
AP pins.  Here, SPDIF can be supplied on either AP1 or AP2 with the
optional clock on AP3.  I2S is similar to the above but only AP1 and
AP2 accept streams.

I think the I2S is a special case - I'm not aware of anything which
outputs _four_ synchronised I2S data streams with a common word select.
Nevertheless, it's something that needs to be considered when creating
DT bindings.

So, we have some quite different capabilities depending on the device.
For I2S, we can get away with this:

	tda998x {
		compatible = "nxp,tda19988", "nxp,tda998x";
		i2s-source = <&i2s_source N>;
	};

where N is the number of coherent I2S streams supplied.  For the TDA998x,
N can be 1 to 4.  For TDA19988, N can be 1 or 2 but no more.

For SPDIF, the situation is more complex.  The TDA19988 could in theory
have two streams connected, and one of those streams can be selected.
So, maybe:

	tda998x {
		compatible = "nxp,tda998x";
		spdif-source = <&spdif_source>;
		spdif-has-mclk;
	};

and for the 19988:

	tda19988 {
		compatible = "nxp,tda19988";
		spdif0-source = <&spdif_source0>;
		spdif0-has-mclk;
		spdif1-source = <&spdif_source1>;
		spdif1-has-mclk;
	};

> Also, from your video swap explanation, the DT will contain some video
> property(ies) TBD.

The TDA998x needs some properties to define how it is connected to the
rest of the hardware - which pins are connected to what in terms of the
colour channel information.  As I hope I've made clear to you, this has
no bearing on the settings in the Dove for the SWAPRB bits.  This is
purely only to do with the TDA998x.

So, this also needs to be presented as properties for the TDA998x driver.

> The remaining question is: do we need more audio parameters?
> 
> - the audio_cfg value is defined by i2s/spdif (3 or 4)

This should be discovered from the DT binding information, because which
AP pins get used depends on the signals being supplied to it.

> - the audio_frame[1] value is always 1 (2 channels)
> - audio_sample_rate is useless

audio_sample_rate comes in if we wish to use a fixed sample rate and
have coherent video and audio clocks derived from a common source.  That
allows fixed CTS and N values to be used based on the audio sample rate
being supplied as the clocks have a fixed known relationship (and aren't
going to drift.)

However, you are right that this should not be specified in DT - it
should come from the audio subsystem.  However, that's something which
is a very very long way away from being possible (if ever) with Linux
since I don't see the DPCM issues ever getting resolved.

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 12:36               ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 12:36 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 02:03:27PM +0200, Jean-Francois Moine wrote:
> On Mon, 7 Oct 2013 12:09:02 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> 
> > > Here is a small story about i2s/spdif: once, I set the tda998x to use
> > > the spdif input, and at this time, I was using the dummy codec.
> > > This codec accepts the format 32_LE, as does the audio device, but the
> > > output cannot go to spdif. Result: no hdmi sound.  
> > 
> > How ASoC works in this regard is that the capabilities are the _union_ of
> > the codec and the cpu DAIs.
> > 
> > The SPDIF transmitter codec supports 16, 20 and 24 bits per sample but not
> > 32.  Quite simply, that's because the SPDIF format does not allow 32-bits
> > per sample.  Therefore, 32_LE is not available for use with audio output,
> > and userspace must convert down to something which the hardware does
> > support.
> > 
> > "Because I2S can support 32-bit audio" is a poor reason, because you can't
> > pass 32-bit audio via HDMI due to a subframe being limited to 28 bits - up
> > to 24 bits for the sample and 4 bits of control/status information.
> 
> OK. So, to resume, if both i2s and spdif are furnished by the audio
> subsystem, the tda998x must use spdif only (with the spdif codec).

This is going off-topic because the TDA998x bindings are a subject for
the TDA998x driver, not the Armada driver.  This data structure merely
exists to provide the required data for the TDA998x in absence of DT
information.

To do the job properly - which is what should be done _whenever_ DT
bindings are being considered - you have to look at the hardware with
an overview of its capabilities, never at the implementation.

So, the TDA998x family needs to be looked at in isolation, and its DT
bindings need to be considered on its own merit.

The TDA998x can accept a SPDIF signal on a single pin (on AP6), with or
without a clock (on AP5), or it can accept I2S: I2S clock in APCLK, I2S
word select on AP0, and up to four I2S streams on AP1..4 and auxillary
("internal test") data in AP7.  AP7 should therefore not be used.

The TDA19988 is slightly different, because it doesn't have soo many
AP pins.  Here, SPDIF can be supplied on either AP1 or AP2 with the
optional clock on AP3.  I2S is similar to the above but only AP1 and
AP2 accept streams.

I think the I2S is a special case - I'm not aware of anything which
outputs _four_ synchronised I2S data streams with a common word select.
Nevertheless, it's something that needs to be considered when creating
DT bindings.

So, we have some quite different capabilities depending on the device.
For I2S, we can get away with this:

	tda998x {
		compatible = "nxp,tda19988", "nxp,tda998x";
		i2s-source = <&i2s_source N>;
	};

where N is the number of coherent I2S streams supplied.  For the TDA998x,
N can be 1 to 4.  For TDA19988, N can be 1 or 2 but no more.

For SPDIF, the situation is more complex.  The TDA19988 could in theory
have two streams connected, and one of those streams can be selected.
So, maybe:

	tda998x {
		compatible = "nxp,tda998x";
		spdif-source = <&spdif_source>;
		spdif-has-mclk;
	};

and for the 19988:

	tda19988 {
		compatible = "nxp,tda19988";
		spdif0-source = <&spdif_source0>;
		spdif0-has-mclk;
		spdif1-source = <&spdif_source1>;
		spdif1-has-mclk;
	};

> Also, from your video swap explanation, the DT will contain some video
> property(ies) TBD.

The TDA998x needs some properties to define how it is connected to the
rest of the hardware - which pins are connected to what in terms of the
colour channel information.  As I hope I've made clear to you, this has
no bearing on the settings in the Dove for the SWAPRB bits.  This is
purely only to do with the TDA998x.

So, this also needs to be presented as properties for the TDA998x driver.

> The remaining question is: do we need more audio parameters?
> 
> - the audio_cfg value is defined by i2s/spdif (3 or 4)

This should be discovered from the DT binding information, because which
AP pins get used depends on the signals being supplied to it.

> - the audio_frame[1] value is always 1 (2 channels)
> - audio_sample_rate is useless

audio_sample_rate comes in if we wish to use a fixed sample rate and
have coherent video and audio clocks derived from a common source.  That
allows fixed CTS and N values to be used based on the audio sample rate
being supplied as the clocks have a fixed known relationship (and aren't
going to drift.)

However, you are right that this should not be specified in DT - it
should come from the audio subsystem.  However, that's something which
is a very very long way away from being possible (if ever) with Linux
since I don't see the DPCM issues ever getting resolved.

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07 12:29             ` Siarhei Siamashka
@ 2013-10-07 12:50               ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 12:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
> On Mon, 7 Oct 2013 11:32:50 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > However, what you're suggesting is dangerous.  What do you do when you're
> > presented with a cursor which is 64x64 ?  Do you:
> > 
> > (a) not display it
> > (b) crash the X server
> > 
> > There isn't a fallback to using software cursors available in the X server
> > because there's no error reporting for a failed hardware cursor update.
> 
> Hmm, maybe I'm missing something, but doesn't returning FALSE from
> UseHWCursorARGB just make the X server fallback to using a software
> cursor?
> 
> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
> 
> I'm just doing this. And also have hooks to notify the DRI2 code about
> the status of the cursor. In the case if the hardware cursor is not
> enabled, hardware overlays can't be safely used with the software
> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
> buffers swapping.
> 
> And I fully agree that it is the responsibility of the kernel to expose
> as much of the hardware capabilities as possible (without compromising
> reliability and/or security).

If you wish to go in below the level presented by
hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
this, and it's something which maybe should be looked into.  You have a
good reason to do this (due to your hardware cursor being incompatible
with overlays).

We have no such restriction - the only issue here is the "odd" size of
cursors.  Practically, choosing one or other of 64x32 or 32x64 works
fine for the cases I've seen.  I haven't seen any cursors which are
64x64 yet.  I'm not saying they don't exist though!  I can imagine that
some accessibility options might want to display a large cursor.

Now, the way I handle this is that the kernel allows _either_ a
(1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
height larger than 32 gets rejected with -EINVAL:

+               /* maximum size is 64x32 or 32x64 */
+               if (w > 64 || h > 64 || (w > 32 && h > 32))
+                       return -ENOMEM;

which is quite reasonable for this hardware.  However, there is no way
to export this from DRM - adding maximum cursor size properties wouldn't
really represent this.

The only question is whether DRM should export some capabilities to
indicate what's possible with the cursor, so that X knows about this
quirk of this hardware.  Overall, I suspect that it's just rare and
not worth the effort.  I think it's just easier to pick one of these
and stick with it, and if we happen to encounter a cursor larger than
our chosen dimentions, XFree86 will already automatically fall back
to s/w cursor.

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-07 12:50               ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-07 12:50 UTC (permalink / raw)
  To: Siarhei Siamashka
  Cc: Jean-Francois Moine, Jason Cooper, David Airlie, dri-devel,
	Rob Clark, Daniel Vetter, linux-arm-kernel,
	Sebastian Hesselbarth

On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
> On Mon, 7 Oct 2013 11:32:50 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> > However, what you're suggesting is dangerous.  What do you do when you're
> > presented with a cursor which is 64x64 ?  Do you:
> > 
> > (a) not display it
> > (b) crash the X server
> > 
> > There isn't a fallback to using software cursors available in the X server
> > because there's no error reporting for a failed hardware cursor update.
> 
> Hmm, maybe I'm missing something, but doesn't returning FALSE from
> UseHWCursorARGB just make the X server fallback to using a software
> cursor?
> 
> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
> 
> I'm just doing this. And also have hooks to notify the DRI2 code about
> the status of the cursor. In the case if the hardware cursor is not
> enabled, hardware overlays can't be safely used with the software
> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
> buffers swapping.
> 
> And I fully agree that it is the responsibility of the kernel to expose
> as much of the hardware capabilities as possible (without compromising
> reliability and/or security).

If you wish to go in below the level presented by
hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
this, and it's something which maybe should be looked into.  You have a
good reason to do this (due to your hardware cursor being incompatible
with overlays).

We have no such restriction - the only issue here is the "odd" size of
cursors.  Practically, choosing one or other of 64x32 or 32x64 works
fine for the cases I've seen.  I haven't seen any cursors which are
64x64 yet.  I'm not saying they don't exist though!  I can imagine that
some accessibility options might want to display a large cursor.

Now, the way I handle this is that the kernel allows _either_ a
(1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
height larger than 32 gets rejected with -EINVAL:

+               /* maximum size is 64x32 or 32x64 */
+               if (w > 64 || h > 64 || (w > 32 && h > 32))
+                       return -ENOMEM;

which is quite reasonable for this hardware.  However, there is no way
to export this from DRM - adding maximum cursor size properties wouldn't
really represent this.

The only question is whether DRM should export some capabilities to
indicate what's possible with the cursor, so that X knows about this
quirk of this hardware.  Overall, I suspect that it's just rare and
not worth the effort.  I think it's just easier to pick one of these
and stick with it, and if we happen to encounter a cursor larger than
our chosen dimentions, XFree86 will already automatically fall back
to s/w cursor.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 11:09           ` Russell King - ARM Linux
@ 2013-10-07 14:59             ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-07 14:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 7, 2013 at 7:09 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
>> On Mon, 7 Oct 2013 10:44:04 +0100
>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>>
>> > On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
>>       [snip]
>> > > It seems we are going backwards: as the Armada based boards will soon
>> > > move to full DT (mvebu), you are making an exception for the Cubox, so
>> > > that there should be Cubox specific kernels. I don't like that...
>> >
>> > *Ignored*.  You know why.
>>
>> Sorry. I don't see why. May you explain again?
>
> I don't run DT because DT lacks most of the features I require on the
> cubox.  Therefore I can't develop for DT.  Simple.

Jean-Fran?ois, just as an aside, I really don't think code that can be
shared, like tda998x, should encode a DT requirement.. there are
plenty of platforms that don't use DT (arm isn't everything, and last
I heard aarch64 was going to be ACPI).

Beyond that, it is a driver decision whether or not to support only-DT
or DT + other.. and as long as there is a common board which can use
the driver but which is not DT, there is probably a compelling reason
to still support the non-DT case.

BR,
-R

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 14:59             ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-07 14:59 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jean-Francois Moine, Jason Cooper, David Airlie, dri-devel,
	Daniel Vetter, linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 7, 2013 at 7:09 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 12:48:20PM +0200, Jean-Francois Moine wrote:
>> On Mon, 7 Oct 2013 10:44:04 +0100
>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>>
>> > On Mon, Oct 07, 2013 at 11:18:07AM +0200, Jean-Francois Moine wrote:
>>       [snip]
>> > > It seems we are going backwards: as the Armada based boards will soon
>> > > move to full DT (mvebu), you are making an exception for the Cubox, so
>> > > that there should be Cubox specific kernels. I don't like that...
>> >
>> > *Ignored*.  You know why.
>>
>> Sorry. I don't see why. May you explain again?
>
> I don't run DT because DT lacks most of the features I require on the
> cubox.  Therefore I can't develop for DT.  Simple.

Jean-François, just as an aside, I really don't think code that can be
shared, like tda998x, should encode a DT requirement.. there are
plenty of platforms that don't use DT (arm isn't everything, and last
I heard aarch64 was going to be ACPI).

Beyond that, it is a driver decision whether or not to support only-DT
or DT + other.. and as long as there is a common board which can use
the driver but which is not DT, there is probably a compelling reason
to still support the non-DT case.

BR,
-R

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 11:29             ` Sebastian Hesselbarth
@ 2013-10-07 15:53               ` Mark Brown
  -1 siblings, 0 replies; 86+ messages in thread
From: Mark Brown @ 2013-10-07 15:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 01:29:30PM +0200, Sebastian Hesselbarth wrote:

> I2S _can_ support more than two channels, but only if you wire up more
> DATA lines. Those are not available on Dove, so its I2S is limited to
> two channel audio.

A lot of devices implement extra channels with TDM rather than with
extra wires - you get all the left channels and all the right channels
grouped together (like with DSP/PCM modes except the channels are
ordered differently).  I'm never sure that's actually useful, though,
since it's not something that just works with most hosts that don't
understand it in hardware and most of those can do DSP/PCM anyway.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131007/68e92cf3/attachment.sig>

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 15:53               ` Mark Brown
  0 siblings, 0 replies; 86+ messages in thread
From: Mark Brown @ 2013-10-07 15:53 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King - ARM Linux, Jason Cooper,
	David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 649 bytes --]

On Mon, Oct 07, 2013 at 01:29:30PM +0200, Sebastian Hesselbarth wrote:

> I2S _can_ support more than two channels, but only if you wire up more
> DATA lines. Those are not available on Dove, so its I2S is limited to
> two channel audio.

A lot of devices implement extra channels with TDM rather than with
extra wires - you get all the left channels and all the right channels
grouped together (like with DSP/PCM modes except the channels are
ordered differently).  I'm never sure that's actually useful, though,
since it's not something that just works with most hosts that don't
understand it in hardware and most of those can do DSP/PCM anyway.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
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] 86+ messages in thread

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 15:53               ` Mark Brown
@ 2013-10-07 16:08                 ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/07/2013 05:53 PM, Mark Brown wrote:
> On Mon, Oct 07, 2013 at 01:29:30PM +0200, Sebastian Hesselbarth wrote:
>
>> I2S _can_ support more than two channels, but only if you wire up more
>> DATA lines. Those are not available on Dove, so its I2S is limited to
>> two channel audio.
>
> A lot of devices implement extra channels with TDM rather than with
> extra wires - you get all the left channels and all the right channels
> grouped together (like with DSP/PCM modes except the channels are
> ordered differently).  I'm never sure that's actually useful, though,
> since it's not something that just works with most hosts that don't
> understand it in hardware and most of those can do DSP/PCM anyway.

True, I didn't mention TDM modes squeezing multiple channels into one
L/R frame. Neither Dove nor TDA998x support this, but thanks for the
clarification.

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 16:08                 ` Sebastian Hesselbarth
  0 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 16:08 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jean-Francois Moine, Russell King - ARM Linux, Jason Cooper,
	David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel

On 10/07/2013 05:53 PM, Mark Brown wrote:
> On Mon, Oct 07, 2013 at 01:29:30PM +0200, Sebastian Hesselbarth wrote:
>
>> I2S _can_ support more than two channels, but only if you wire up more
>> DATA lines. Those are not available on Dove, so its I2S is limited to
>> two channel audio.
>
> A lot of devices implement extra channels with TDM rather than with
> extra wires - you get all the left channels and all the right channels
> grouped together (like with DSP/PCM modes except the channels are
> ordered differently).  I'm never sure that's actually useful, though,
> since it's not something that just works with most hosts that don't
> understand it in hardware and most of those can do DSP/PCM anyway.

True, I didn't mention TDM modes squeezing multiple channels into one
L/R frame. Neither Dove nor TDA998x support this, but thanks for the
clarification.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 16:08                 ` Sebastian Hesselbarth
@ 2013-10-07 17:05                   ` Mark Brown
  -1 siblings, 0 replies; 86+ messages in thread
From: Mark Brown @ 2013-10-07 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 06:08:12PM +0200, Sebastian Hesselbarth wrote:

> True, I didn't mention TDM modes squeezing multiple channels into one
> L/R frame. Neither Dove nor TDA998x support this, but thanks for the
> clarification.

Depending on how the DMA and so on is done you can sometimes still do it
on the CPU side by lying to the CPU about the data format (eg, tell it
that two 16 bit samples are actually a 32 bit one) though it gets messy
for I2S since you need to be able to gather all the left samples and all
the right samples.  Though given that your CODEC doesn't support it it's
still all moot anyway as you say.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131007/bd99b77f/attachment.sig>

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-07 17:05                   ` Mark Brown
  0 siblings, 0 replies; 86+ messages in thread
From: Mark Brown @ 2013-10-07 17:05 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jean-Francois Moine, Russell King - ARM Linux, Jason Cooper,
	David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 629 bytes --]

On Mon, Oct 07, 2013 at 06:08:12PM +0200, Sebastian Hesselbarth wrote:

> True, I didn't mention TDM modes squeezing multiple channels into one
> L/R frame. Neither Dove nor TDA998x support this, but thanks for the
> clarification.

Depending on how the DMA and so on is done you can sometimes still do it
on the CPU side by lying to the CPU about the data format (eg, tell it
that two 16 bit samples are actually a 32 bit one) though it gets messy
for I2S since you need to be able to gather all the left samples and all
the right samples.  Though given that your CODEC doesn't support it it's
still all moot anyway as you say.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
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] 86+ messages in thread

* [PATCH 0/5] Armada DRM stuff
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-07 21:47   ` Sebastian Hesselbarth
  -1 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 21:47 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
> The Armada DRM drivers again, along with the TDA998x HDMI output support,
> and an illustration to show how to add Armada 610 support (and others.)
>
> Rob has looked at this a couple of times since its last posting, and
> has provided additional useful feedback which has been incorporated.
> I believe all the major issues have been addressed now.

Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>

on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.

Let's please get this driver mainlined and start working on proper DT
support for it. Thanks for driver and the patience to work out all
issues!

Sebastian

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

* Re: [PATCH 0/5] Armada DRM stuff
@ 2013-10-07 21:47   ` Sebastian Hesselbarth
  0 siblings, 0 replies; 86+ messages in thread
From: Sebastian Hesselbarth @ 2013-10-07 21:47 UTC (permalink / raw)
  To: Russell King - ARM Linux, dri-devel, linux-arm-kernel
  Cc: David Airlie, Rob Clark, Jason Cooper, Daniel Vetter

On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
> The Armada DRM drivers again, along with the TDA998x HDMI output support,
> and an illustration to show how to add Armada 610 support (and others.)
>
> Rob has looked at this a couple of times since its last posting, and
> has provided additional useful feedback which has been incorporated.
> I believe all the major issues have been addressed now.

Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>

on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.

Let's please get this driver mainlined and start working on proper DT
support for it. Thanks for driver and the patience to work out all
issues!

Sebastian

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-07 14:59             ` Rob Clark
@ 2013-10-08  9:19               ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-08  9:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 7 Oct 2013 10:59:39 -0400
Rob Clark <robdclark@gmail.com> wrote:

> Jean-Fran?ois, just as an aside, I really don't think code that can be
> shared, like tda998x, should encode a DT requirement.. there are
> plenty of platforms that don't use DT (arm isn't everything, and last
> I heard aarch64 was going to be ACPI).
> 
> Beyond that, it is a driver decision whether or not to support only-DT
> or DT + other.. and as long as there is a common board which can use
> the driver but which is not DT, there is probably a compelling reason
> to still support the non-DT case.

Rob,

The Cubox is an open platform, and I use it just like a desktop PC.
When its required drivers will be in the mainline, I will do the same
as I do with PCs: I will not recompile a specific kernel each time
there are kernel bugs or security issues. Instead, I will just upgrade
my system from my distributor (Debian), and, in the packages, there
will be a generic mvebu kernel as there is already one for Marvell
Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
But, for that, all the Cubox specific stuff must be described in a DT.

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-08  9:19               ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-08  9:19 UTC (permalink / raw)
  To: Rob Clark
  Cc: Russell King - ARM Linux, Jason Cooper, David Airlie, dri-devel,
	Daniel Vetter, linux-arm-kernel, Sebastian Hesselbarth

On Mon, 7 Oct 2013 10:59:39 -0400
Rob Clark <robdclark@gmail.com> wrote:

> Jean-François, just as an aside, I really don't think code that can be
> shared, like tda998x, should encode a DT requirement.. there are
> plenty of platforms that don't use DT (arm isn't everything, and last
> I heard aarch64 was going to be ACPI).
> 
> Beyond that, it is a driver decision whether or not to support only-DT
> or DT + other.. and as long as there is a common board which can use
> the driver but which is not DT, there is probably a compelling reason
> to still support the non-DT case.

Rob,

The Cubox is an open platform, and I use it just like a desktop PC.
When its required drivers will be in the mainline, I will do the same
as I do with PCs: I will not recompile a specific kernel each time
there are kernel bugs or security issues. Instead, I will just upgrade
my system from my distributor (Debian), and, in the packages, there
will be a generic mvebu kernel as there is already one for Marvell
Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
But, for that, all the Cubox specific stuff must be described in a DT.

-- 
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] 86+ messages in thread

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-08  9:19               ` Jean-Francois Moine
@ 2013-10-08  9:49                 ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-08  9:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
> The Cubox is an open platform, and I use it just like a desktop PC.
> When its required drivers will be in the mainline, I will do the same
> as I do with PCs: I will not recompile a specific kernel each time
> there are kernel bugs or security issues. Instead, I will just upgrade
> my system from my distributor (Debian), and, in the packages, there
> will be a generic mvebu kernel as there is already one for Marvell
> Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> But, for that, all the Cubox specific stuff must be described in a DT.

Which scenario is better:

1. To have something in mainline which is capable of driving the hardware,
   but may need some additional work to make it useful for DT based setups.

or

2. To have nothing.

Now, you may prefer to have nothing, but personally, I prefer there to be
forward progress.  Forward progress means getting some kind of DRM driver
into mainline.  Yes, it may not work with DT setups yet, but - as per
the discussions that have happened _endlessly_ on this topic, it's
something that can be resolved at a later date.

We _still_ haven't worked out how to properly deal with the TDA998x
driver (or indeed any DRM based outputs) in a DT based setup, and all
the time that problem exists, it won't be possible to write a proper
stable DT binding for this.

So please, get off your hobby horse about this and allow us to make some
modicum of progress.

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-08  9:49                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-08  9:49 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
> The Cubox is an open platform, and I use it just like a desktop PC.
> When its required drivers will be in the mainline, I will do the same
> as I do with PCs: I will not recompile a specific kernel each time
> there are kernel bugs or security issues. Instead, I will just upgrade
> my system from my distributor (Debian), and, in the packages, there
> will be a generic mvebu kernel as there is already one for Marvell
> Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> But, for that, all the Cubox specific stuff must be described in a DT.

Which scenario is better:

1. To have something in mainline which is capable of driving the hardware,
   but may need some additional work to make it useful for DT based setups.

or

2. To have nothing.

Now, you may prefer to have nothing, but personally, I prefer there to be
forward progress.  Forward progress means getting some kind of DRM driver
into mainline.  Yes, it may not work with DT setups yet, but - as per
the discussions that have happened _endlessly_ on this topic, it's
something that can be resolved at a later date.

We _still_ haven't worked out how to properly deal with the TDA998x
driver (or indeed any DRM based outputs) in a DT based setup, and all
the time that problem exists, it won't be possible to write a proper
stable DT binding for this.

So please, get off your hobby horse about this and allow us to make some
modicum of progress.

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-08  9:19               ` Jean-Francois Moine
@ 2013-10-08 12:07                 ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-08 12:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 8, 2013 at 5:19 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Mon, 7 Oct 2013 10:59:39 -0400
> Rob Clark <robdclark@gmail.com> wrote:
>
>> Jean-Fran?ois, just as an aside, I really don't think code that can be
>> shared, like tda998x, should encode a DT requirement.. there are
>> plenty of platforms that don't use DT (arm isn't everything, and last
>> I heard aarch64 was going to be ACPI).
>>
>> Beyond that, it is a driver decision whether or not to support only-DT
>> or DT + other.. and as long as there is a common board which can use
>> the driver but which is not DT, there is probably a compelling reason
>> to still support the non-DT case.
>
> Rob,
>
> The Cubox is an open platform, and I use it just like a desktop PC.
> When its required drivers will be in the mainline, I will do the same
> as I do with PCs: I will not recompile a specific kernel each time
> there are kernel bugs or security issues. Instead, I will just upgrade
> my system from my distributor (Debian), and, in the packages, there
> will be a generic mvebu kernel as there is already one for Marvell
> Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> But, for that, all the Cubox specific stuff must be described in a DT.

sure, I'm not saying *not* to support DT (send patches if need be).
I'm just saying that it should be ok to also support non-DT case and
that shared components (like tda998x) should also still work in non-DT
case.

BR,
-R

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-08 12:07                 ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-08 12:07 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King - ARM Linux, Jason Cooper, David Airlie, dri-devel,
	Daniel Vetter, linux-arm-kernel, Sebastian Hesselbarth

On Tue, Oct 8, 2013 at 5:19 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Mon, 7 Oct 2013 10:59:39 -0400
> Rob Clark <robdclark@gmail.com> wrote:
>
>> Jean-François, just as an aside, I really don't think code that can be
>> shared, like tda998x, should encode a DT requirement.. there are
>> plenty of platforms that don't use DT (arm isn't everything, and last
>> I heard aarch64 was going to be ACPI).
>>
>> Beyond that, it is a driver decision whether or not to support only-DT
>> or DT + other.. and as long as there is a common board which can use
>> the driver but which is not DT, there is probably a compelling reason
>> to still support the non-DT case.
>
> Rob,
>
> The Cubox is an open platform, and I use it just like a desktop PC.
> When its required drivers will be in the mainline, I will do the same
> as I do with PCs: I will not recompile a specific kernel each time
> there are kernel bugs or security issues. Instead, I will just upgrade
> my system from my distributor (Debian), and, in the packages, there
> will be a generic mvebu kernel as there is already one for Marvell
> Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> But, for that, all the Cubox specific stuff must be described in a DT.

sure, I'm not saying *not* to support DT (send patches if need be).
I'm just saying that it should be ok to also support non-DT case and
that shared components (like tda998x) should also still work in non-DT
case.

BR,
-R

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

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-08  9:49                 ` Russell King - ARM Linux
@ 2013-10-08 15:34                   ` Jean-Francois Moine
  -1 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-08 15:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 8 Oct 2013 10:49:39 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
> > The Cubox is an open platform, and I use it just like a desktop PC.
> > When its required drivers will be in the mainline, I will do the same
> > as I do with PCs: I will not recompile a specific kernel each time
> > there are kernel bugs or security issues. Instead, I will just upgrade
> > my system from my distributor (Debian), and, in the packages, there
> > will be a generic mvebu kernel as there is already one for Marvell
> > Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> > But, for that, all the Cubox specific stuff must be described in a DT.
> 
> Which scenario is better:
> 
> 1. To have something in mainline which is capable of driving the hardware,
>    but may need some additional work to make it useful for DT based setups.
> 
> or
> 
> 2. To have nothing.
> 
> Now, you may prefer to have nothing, but personally, I prefer there to be
> forward progress.  Forward progress means getting some kind of DRM driver
> into mainline.  Yes, it may not work with DT setups yet, but - as per
> the discussions that have happened _endlessly_ on this topic, it's
> something that can be resolved at a later date.
> 
> We _still_ haven't worked out how to properly deal with the TDA998x
> driver (or indeed any DRM based outputs) in a DT based setup, and all
> the time that problem exists, it won't be possible to write a proper
> stable DT binding for this.
> 
> So please, get off your hobby horse about this and allow us to make some
> modicum of progress.

You forgot:

3. To have all patches ready for submission and have a working DT driven
   Cubox kernel.

I will submit a patch to add DT to the tda998x driver as soon as I have
checked the new audio properties we talked about yesterday.

Normally, this should have no impact on your Armada drm driver, and,
yes, I will add DT to your driver as soon as it will be accepted
(sorry to not ack it now: I had no time yet to have a look at it).

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

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-08 15:34                   ` Jean-Francois Moine
  0 siblings, 0 replies; 86+ messages in thread
From: Jean-Francois Moine @ 2013-10-08 15:34 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Tue, 8 Oct 2013 10:49:39 +0100
Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:

> On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
> > The Cubox is an open platform, and I use it just like a desktop PC.
> > When its required drivers will be in the mainline, I will do the same
> > as I do with PCs: I will not recompile a specific kernel each time
> > there are kernel bugs or security issues. Instead, I will just upgrade
> > my system from my distributor (Debian), and, in the packages, there
> > will be a generic mvebu kernel as there is already one for Marvell
> > Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
> > But, for that, all the Cubox specific stuff must be described in a DT.
> 
> Which scenario is better:
> 
> 1. To have something in mainline which is capable of driving the hardware,
>    but may need some additional work to make it useful for DT based setups.
> 
> or
> 
> 2. To have nothing.
> 
> Now, you may prefer to have nothing, but personally, I prefer there to be
> forward progress.  Forward progress means getting some kind of DRM driver
> into mainline.  Yes, it may not work with DT setups yet, but - as per
> the discussions that have happened _endlessly_ on this topic, it's
> something that can be resolved at a later date.
> 
> We _still_ haven't worked out how to properly deal with the TDA998x
> driver (or indeed any DRM based outputs) in a DT based setup, and all
> the time that problem exists, it won't be possible to write a proper
> stable DT binding for this.
> 
> So please, get off your hobby horse about this and allow us to make some
> modicum of progress.

You forgot:

3. To have all patches ready for submission and have a working DT driven
   Cubox kernel.

I will submit a patch to add DT to the tda998x driver as soon as I have
checked the new audio properties we talked about yesterday.

Normally, this should have no impact on your Armada drm driver, and,
yes, I will add DT to your driver as soon as it will be accepted
(sorry to not ack it now: I had no time yet to have a look at it).

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 0/5] Armada DRM stuff
  2013-10-07 21:47   ` Sebastian Hesselbarth
@ 2013-10-09 14:31     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-09 14:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 07, 2013 at 11:47:55PM +0200, Sebastian Hesselbarth wrote:
> On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
>> The Armada DRM drivers again, along with the TDA998x HDMI output support,
>> and an illustration to show how to add Armada 610 support (and others.)
>>
>> Rob has looked at this a couple of times since its last posting, and
>> has provided additional useful feedback which has been incorporated.
>> I believe all the major issues have been addressed now.
>
> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>
> on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.
>
> Let's please get this driver mainlined and start working on proper DT
> support for it. Thanks for driver and the patience to work out all
> issues!

Sebastian,

I've added your tested-by now, and also updated the TDA998x patch to use
the HDMI definitions that Jean-Francois pointed out (which is a really
small delta, so isn't worth resending the patch set just for that).
Thanks.

All the points brought up in previous reviews concerning the physical
address exposure, and the location of the DRM helpers have all been
addressed, so it is now ready to be merged into mainline.  So now the
question is how to proceed with it.

However, I see no response from any DRM developers, even though they are
around - they've been responding on a different thread about driver
model/sysfs abuse by DRM.

I'm going to pull it into my nightly build tree so it can have some
testing with non-dove builds, and if there's still no response from
DRM people next week, I'll put it in to linux-next via my tree so at
least it can get some exposure and integration validation there.

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

* Re: [PATCH 0/5] Armada DRM stuff
@ 2013-10-09 14:31     ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-09 14:31 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: Jason Cooper, David Airlie, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel

On Mon, Oct 07, 2013 at 11:47:55PM +0200, Sebastian Hesselbarth wrote:
> On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
>> The Armada DRM drivers again, along with the TDA998x HDMI output support,
>> and an illustration to show how to add Armada 610 support (and others.)
>>
>> Rob has looked at this a couple of times since its last posting, and
>> has provided additional useful feedback which has been incorporated.
>> I believe all the major issues have been addressed now.
>
> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>
> on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.
>
> Let's please get this driver mainlined and start working on proper DT
> support for it. Thanks for driver and the patience to work out all
> issues!

Sebastian,

I've added your tested-by now, and also updated the TDA998x patch to use
the HDMI definitions that Jean-Francois pointed out (which is a really
small delta, so isn't worth resending the patch set just for that).
Thanks.

All the points brought up in previous reviews concerning the physical
address exposure, and the location of the DRM helpers have all been
addressed, so it is now ready to be merged into mainline.  So now the
question is how to proceed with it.

However, I see no response from any DRM developers, even though they are
around - they've been responding on a different thread about driver
model/sysfs abuse by DRM.

I'm going to pull it into my nightly build tree so it can have some
testing with non-dove builds, and if there's still no response from
DRM people next week, I'll put it in to linux-next via my tree so at
least it can get some exposure and integration validation there.

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

* [PATCH 0/5] Armada DRM stuff
  2013-10-09 14:31     ` Russell King - ARM Linux
@ 2013-10-09 14:48       ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-09 14:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Oct 9, 2013 at 10:31 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 11:47:55PM +0200, Sebastian Hesselbarth wrote:
>> On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
>>> The Armada DRM drivers again, along with the TDA998x HDMI output support,
>>> and an illustration to show how to add Armada 610 support (and others.)
>>>
>>> Rob has looked at this a couple of times since its last posting, and
>>> has provided additional useful feedback which has been incorporated.
>>> I believe all the major issues have been addressed now.
>>
>> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>>
>> on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.
>>
>> Let's please get this driver mainlined and start working on proper DT
>> support for it. Thanks for driver and the patience to work out all
>> issues!
>
> Sebastian,
>
> I've added your tested-by now, and also updated the TDA998x patch to use
> the HDMI definitions that Jean-Francois pointed out (which is a really
> small delta, so isn't worth resending the patch set just for that).
> Thanks.
>
> All the points brought up in previous reviews concerning the physical
> address exposure, and the location of the DRM helpers have all been
> addressed, so it is now ready to be merged into mainline.  So now the
> question is how to proceed with it.
>
> However, I see no response from any DRM developers, even though they are
> around - they've been responding on a different thread about driver
> model/sysfs abuse by DRM.
>
> I'm going to pull it into my nightly build tree so it can have some
> testing with non-dove builds, and if there's still no response from
> DRM people next week, I'll put it in to linux-next via my tree so at
> least it can get some exposure and integration validation there.

fyi, I think this is in pretty good shape.. I've reviewed earlier
versions, and had an initial look at this version which addresses my
concerns, although I still intend to give a closer review of the final
version (still on my TODO list, but we have a bit of time before 3.13
merge window.. I should at least get to it by the weekend)

BR,
-R

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

* Re: [PATCH 0/5] Armada DRM stuff
@ 2013-10-09 14:48       ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-09 14:48 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Wed, Oct 9, 2013 at 10:31 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 11:47:55PM +0200, Sebastian Hesselbarth wrote:
>> On 10/07/2013 12:07 AM, Russell King - ARM Linux wrote:
>>> The Armada DRM drivers again, along with the TDA998x HDMI output support,
>>> and an illustration to show how to add Armada 610 support (and others.)
>>>
>>> Rob has looked at this a couple of times since its last posting, and
>>> has provided additional useful feedback which has been incorporated.
>>> I believe all the major issues have been addressed now.
>>
>> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>>
>> on Marvell Armada 510, SolidRun CuBox with quick-hack DT support added.
>>
>> Let's please get this driver mainlined and start working on proper DT
>> support for it. Thanks for driver and the patience to work out all
>> issues!
>
> Sebastian,
>
> I've added your tested-by now, and also updated the TDA998x patch to use
> the HDMI definitions that Jean-Francois pointed out (which is a really
> small delta, so isn't worth resending the patch set just for that).
> Thanks.
>
> All the points brought up in previous reviews concerning the physical
> address exposure, and the location of the DRM helpers have all been
> addressed, so it is now ready to be merged into mainline.  So now the
> question is how to proceed with it.
>
> However, I see no response from any DRM developers, even though they are
> around - they've been responding on a different thread about driver
> model/sysfs abuse by DRM.
>
> I'm going to pull it into my nightly build tree so it can have some
> testing with non-dove builds, and if there's still no response from
> DRM people next week, I'll put it in to linux-next via my tree so at
> least it can get some exposure and integration validation there.

fyi, I think this is in pretty good shape.. I've reviewed earlier
versions, and had an initial look at this version which addresses my
concerns, although I still intend to give a closer review of the final
version (still on my TODO list, but we have a bit of time before 3.13
merge window.. I should at least get to it by the weekend)

BR,
-R

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-06 22:08   ` Russell King
@ 2013-10-10 21:25     ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-10 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Oct 6, 2013 at 6:08 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> This patch adds support for the pair of LCD controllers on the Marvell
> Armada 510 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 via DRM planes
> - page flipping of the main scanout buffers
> - DRM prime for buffer export/import
>
> This driver is trivial to extend to other Armada SoCs.
>
> Included in this commit is the core driver with no output support; output
> support is platform and encoder driver dependent.

w/ a couple minor comments below:

Reviewed-by: Rob Clark <robdclark@gmail.com>


> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/Kconfig                 |    2 +
>  drivers/gpu/drm/Makefile                |    1 +
>  drivers/gpu/drm/armada/Kconfig          |   15 +
>  drivers/gpu/drm/armada/Makefile         |    7 +
>  drivers/gpu/drm/armada/armada_510.c     |   86 +++
>  drivers/gpu/drm/armada/armada_crtc.c    |  861 +++++++++++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_crtc.h    |   74 +++
>  drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++++
>  drivers/gpu/drm/armada/armada_drm.h     |  112 ++++
>  drivers/gpu/drm/armada/armada_drv.c     |  380 ++++++++++++++
>  drivers/gpu/drm/armada/armada_fb.c      |  170 ++++++
>  drivers/gpu/drm/armada/armada_fb.h      |   24 +
>  drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++++
>  drivers/gpu/drm/armada/armada_gem.c     |  616 ++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_gem.h     |   52 ++
>  drivers/gpu/drm/armada/armada_hw.h      |  316 +++++++++++
>  drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
>  drivers/gpu/drm/armada/armada_output.c  |  158 ++++++
>  drivers/gpu/drm/armada/armada_output.h  |   39 ++
>  drivers/gpu/drm/armada/armada_overlay.c |  477 +++++++++++++++++
>  drivers/gpu/drm/armada/armada_slave.c   |  139 +++++
>  drivers/gpu/drm/armada/armada_slave.h   |   26 +
>  include/drm/drm_crtc.h                  |   17 +
>  include/uapi/drm/armada_drm.h           |   45 ++
>  24 files changed, 4020 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpu/drm/armada/Kconfig
>  create mode 100644 drivers/gpu/drm/armada/Makefile
>  create mode 100644 drivers/gpu/drm/armada/armada_510.c
>  create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
>  create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
>  create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
>  create mode 100644 drivers/gpu/drm/armada/armada_drm.h
>  create mode 100644 drivers/gpu/drm/armada/armada_drv.c
>  create mode 100644 drivers/gpu/drm/armada/armada_fb.c
>  create mode 100644 drivers/gpu/drm/armada/armada_fb.h
>  create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
>  create mode 100644 drivers/gpu/drm/armada/armada_gem.c
>  create mode 100644 drivers/gpu/drm/armada/armada_gem.h
>  create mode 100644 drivers/gpu/drm/armada/armada_hw.h
>  create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
>  create mode 100644 drivers/gpu/drm/armada/armada_output.c
>  create mode 100644 drivers/gpu/drm/armada/armada_output.h
>  create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
>  create mode 100644 drivers/gpu/drm/armada/armada_slave.c
>  create mode 100644 drivers/gpu/drm/armada/armada_slave.h
>  create mode 100644 include/uapi/drm/armada_drm.h
>
[snip]
> +/*
> + * 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 armada_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
> +       struct drm_plane *plane;
> +
> +       /*
> +        * If we have an overlay plane associated with this CRTC, disable
> +        * it before the modeset to avoid its coordinates being outside
> +        * the new mode parameters.  DRM doesn't provide help with this.
> +        */

Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
things by having crtc helpers disable planes on setcrtc (rather than
only doing this on fbdev/lastclose restore).  I
don't know of any userspace that this would cause problems for.  But
not really sure if it would be considered an interface break or just
"fixing a bug".. since we have different behavior on different
drivers, I'd lean towards the latter.

> +       plane = dcrtc->plane;
> +       if (plane) {
> +               struct drm_framebuffer *fb = plane->fb;
> +
> +               plane->funcs->disable_plane(plane);
> +               plane->fb = NULL;
> +               plane->crtc = NULL;
> +               drm_framebuffer_unreference(fb);
> +       }
> +}


> diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
> new file mode 100644
> index 0000000..c865a9a
> --- /dev/null
> +++ b/drivers/gpu/drm/armada/armada_gem.c
> @@ -0,0 +1,616 @@
> +/*
> + * 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/dma-buf.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/shmem_fs.h>
> +#include <drm/drmP.h>
> +#include "armada_drm.h"
> +#include "armada_gem.h"
> +#include <drm/armada_drm.h>
> +#include "armada_ioctlP.h"
> +
> +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +       struct armada_gem_object *obj = drm_to_armada_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();

probably this thread is applicable here too?

https://lkml.org/lkml/2013/9/12/417

(although.. we have at least a few  slightly differet variants on the
same errno -> VM_FAULT_x switch in different drivers.. maybe we should
do something better)

> +       case 0:
> +       case -ERESTARTSYS:
> +       case -EINTR:
> +       case -EBUSY:
> +               return VM_FAULT_NOPAGE;
> +       case -ENOMEM:
> +               return VM_FAULT_OOM;
> +       default:
> +               return VM_FAULT_SIGBUS;
> +       }
> +}
> +

[snip]

> +/* Prime support */
> +struct sg_table *
> +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
> +       enum dma_data_direction dir)
> +{
> +       struct drm_gem_object *obj = attach->dmabuf->priv;
> +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
> +       struct scatterlist *sg;
> +       struct sg_table *sgt;
> +       int i, num;
> +
> +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
> +       if (!sgt)
> +               return NULL;
> +
> +       if (dobj->obj.filp) {
> +               struct address_space *mapping;
> +               gfp_t gfp;
> +               int count;
> +
> +               count = dobj->obj.size / PAGE_SIZE;
> +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
> +                       goto free_sgt;
> +
> +               mapping = file_inode(dobj->obj.filp)->i_mapping;
> +               gfp = mapping_gfp_mask(mapping);
> +
> +               for_each_sg(sgt->sgl, sg, count, i) {
> +                       struct page *page;
> +
> +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);

you could almost use drm_gem_get_pages().. although I guess you
otherwise have no need for the page[] array?

(the page array would be handy if you supported mmap on shmem backed
files.. which as long as they are cached should be the easy case)

> +                       if (IS_ERR(page)) {
> +                               num = i;
> +                               goto release;
> +                       }
> +
> +                       sg_set_page(sg, page, PAGE_SIZE, 0);
> +               }
> +
> +               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
> +                       num = sgt->nents;
> +                       goto release;
> +               }
> +       } else if (dobj->page) {
> +               /* Single contiguous page */
> +               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
> +                       goto free_sgt;
> +
> +               sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
> +
> +               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
> +                       goto free_table;
> +       } else if (dobj->linear) {
> +               /* Single contiguous physical region - no struct page */
> +               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
> +                       goto free_sgt;
> +               sg_dma_address(sgt->sgl) = dobj->dev_addr;
> +               sg_dma_len(sgt->sgl) = dobj->obj.size;
> +       } else {
> +               goto free_sgt;
> +       }
> +       return sgt;
> +
> + release:
> +       for_each_sg(sgt->sgl, sg, num, i)
> +               page_cache_release(sg_page(sg));
> + free_table:
> +       sg_free_table(sgt);
> + free_sgt:
> +       kfree(sgt);
> +       return NULL;
> +}
[snip]
> +> diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
> new file mode 100644
> index 0000000..8dec3fd
> --- /dev/null
> +++ b/include/uapi/drm/armada_drm.h
> @@ -0,0 +1,45 @@
> +/*
> + * 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_ARMADA_IOCTL_H
> +#define DRM_ARMADA_IOCTL_H
> +
> +#define DRM_ARMADA_GEM_CREATE          0x00
> +#define DRM_ARMADA_GEM_MMAP            0x02
> +#define DRM_ARMADA_GEM_PWRITE          0x03
> +
> +#define ARMADA_IOCTL(dir, name, str) \
> +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
> +
> +struct drm_armada_gem_create {

Any reason not to throw in a 'uint32_t flags' field?  At least then
you could indicate what sort of backing memory you want, and do things
like allocate linear scanout buffer w/ your gem_create rather than
having to use dumb_create.  Maybe not something you need now, but
seems like eventually you'll come up with some reason or another to
want to pass some flags into gem_create.

> +       uint32_t handle;
> +       uint32_t size;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_CREATE \
> +       ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
> +
> +struct drm_armada_gem_mmap {
> +       uint32_t handle;
> +       uint32_t pad;
> +       uint64_t offset;
> +       uint64_t size;
> +       uint64_t addr;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_MMAP \
> +       ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
> +
> +struct drm_armada_gem_pwrite {
> +       uint64_t ptr;
> +       uint32_t handle;
> +       uint32_t offset;
> +       uint32_t size;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_PWRITE \
> +       ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
> +
> +#endif
> --
> 1.7.4.4
>

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

* Re: [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-10 21:25     ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-10 21:25 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, Oct 6, 2013 at 6:08 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> This patch adds support for the pair of LCD controllers on the Marvell
> Armada 510 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 via DRM planes
> - page flipping of the main scanout buffers
> - DRM prime for buffer export/import
>
> This driver is trivial to extend to other Armada SoCs.
>
> Included in this commit is the core driver with no output support; output
> support is platform and encoder driver dependent.

w/ a couple minor comments below:

Reviewed-by: Rob Clark <robdclark@gmail.com>


> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/Kconfig                 |    2 +
>  drivers/gpu/drm/Makefile                |    1 +
>  drivers/gpu/drm/armada/Kconfig          |   15 +
>  drivers/gpu/drm/armada/Makefile         |    7 +
>  drivers/gpu/drm/armada/armada_510.c     |   86 +++
>  drivers/gpu/drm/armada/armada_crtc.c    |  861 +++++++++++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_crtc.h    |   74 +++
>  drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++++
>  drivers/gpu/drm/armada/armada_drm.h     |  112 ++++
>  drivers/gpu/drm/armada/armada_drv.c     |  380 ++++++++++++++
>  drivers/gpu/drm/armada/armada_fb.c      |  170 ++++++
>  drivers/gpu/drm/armada/armada_fb.h      |   24 +
>  drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++++
>  drivers/gpu/drm/armada/armada_gem.c     |  616 ++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_gem.h     |   52 ++
>  drivers/gpu/drm/armada/armada_hw.h      |  316 +++++++++++
>  drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
>  drivers/gpu/drm/armada/armada_output.c  |  158 ++++++
>  drivers/gpu/drm/armada/armada_output.h  |   39 ++
>  drivers/gpu/drm/armada/armada_overlay.c |  477 +++++++++++++++++
>  drivers/gpu/drm/armada/armada_slave.c   |  139 +++++
>  drivers/gpu/drm/armada/armada_slave.h   |   26 +
>  include/drm/drm_crtc.h                  |   17 +
>  include/uapi/drm/armada_drm.h           |   45 ++
>  24 files changed, 4020 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpu/drm/armada/Kconfig
>  create mode 100644 drivers/gpu/drm/armada/Makefile
>  create mode 100644 drivers/gpu/drm/armada/armada_510.c
>  create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
>  create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
>  create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
>  create mode 100644 drivers/gpu/drm/armada/armada_drm.h
>  create mode 100644 drivers/gpu/drm/armada/armada_drv.c
>  create mode 100644 drivers/gpu/drm/armada/armada_fb.c
>  create mode 100644 drivers/gpu/drm/armada/armada_fb.h
>  create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
>  create mode 100644 drivers/gpu/drm/armada/armada_gem.c
>  create mode 100644 drivers/gpu/drm/armada/armada_gem.h
>  create mode 100644 drivers/gpu/drm/armada/armada_hw.h
>  create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
>  create mode 100644 drivers/gpu/drm/armada/armada_output.c
>  create mode 100644 drivers/gpu/drm/armada/armada_output.h
>  create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
>  create mode 100644 drivers/gpu/drm/armada/armada_slave.c
>  create mode 100644 drivers/gpu/drm/armada/armada_slave.h
>  create mode 100644 include/uapi/drm/armada_drm.h
>
[snip]
> +/*
> + * 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 armada_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
> +       struct drm_plane *plane;
> +
> +       /*
> +        * If we have an overlay plane associated with this CRTC, disable
> +        * it before the modeset to avoid its coordinates being outside
> +        * the new mode parameters.  DRM doesn't provide help with this.
> +        */

Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
things by having crtc helpers disable planes on setcrtc (rather than
only doing this on fbdev/lastclose restore).  I
don't know of any userspace that this would cause problems for.  But
not really sure if it would be considered an interface break or just
"fixing a bug".. since we have different behavior on different
drivers, I'd lean towards the latter.

> +       plane = dcrtc->plane;
> +       if (plane) {
> +               struct drm_framebuffer *fb = plane->fb;
> +
> +               plane->funcs->disable_plane(plane);
> +               plane->fb = NULL;
> +               plane->crtc = NULL;
> +               drm_framebuffer_unreference(fb);
> +       }
> +}


> diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
> new file mode 100644
> index 0000000..c865a9a
> --- /dev/null
> +++ b/drivers/gpu/drm/armada/armada_gem.c
> @@ -0,0 +1,616 @@
> +/*
> + * 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/dma-buf.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/shmem_fs.h>
> +#include <drm/drmP.h>
> +#include "armada_drm.h"
> +#include "armada_gem.h"
> +#include <drm/armada_drm.h>
> +#include "armada_ioctlP.h"
> +
> +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +       struct armada_gem_object *obj = drm_to_armada_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();

probably this thread is applicable here too?

https://lkml.org/lkml/2013/9/12/417

(although.. we have at least a few  slightly differet variants on the
same errno -> VM_FAULT_x switch in different drivers.. maybe we should
do something better)

> +       case 0:
> +       case -ERESTARTSYS:
> +       case -EINTR:
> +       case -EBUSY:
> +               return VM_FAULT_NOPAGE;
> +       case -ENOMEM:
> +               return VM_FAULT_OOM;
> +       default:
> +               return VM_FAULT_SIGBUS;
> +       }
> +}
> +

[snip]

> +/* Prime support */
> +struct sg_table *
> +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
> +       enum dma_data_direction dir)
> +{
> +       struct drm_gem_object *obj = attach->dmabuf->priv;
> +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
> +       struct scatterlist *sg;
> +       struct sg_table *sgt;
> +       int i, num;
> +
> +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
> +       if (!sgt)
> +               return NULL;
> +
> +       if (dobj->obj.filp) {
> +               struct address_space *mapping;
> +               gfp_t gfp;
> +               int count;
> +
> +               count = dobj->obj.size / PAGE_SIZE;
> +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
> +                       goto free_sgt;
> +
> +               mapping = file_inode(dobj->obj.filp)->i_mapping;
> +               gfp = mapping_gfp_mask(mapping);
> +
> +               for_each_sg(sgt->sgl, sg, count, i) {
> +                       struct page *page;
> +
> +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);

you could almost use drm_gem_get_pages().. although I guess you
otherwise have no need for the page[] array?

(the page array would be handy if you supported mmap on shmem backed
files.. which as long as they are cached should be the easy case)

> +                       if (IS_ERR(page)) {
> +                               num = i;
> +                               goto release;
> +                       }
> +
> +                       sg_set_page(sg, page, PAGE_SIZE, 0);
> +               }
> +
> +               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
> +                       num = sgt->nents;
> +                       goto release;
> +               }
> +       } else if (dobj->page) {
> +               /* Single contiguous page */
> +               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
> +                       goto free_sgt;
> +
> +               sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
> +
> +               if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
> +                       goto free_table;
> +       } else if (dobj->linear) {
> +               /* Single contiguous physical region - no struct page */
> +               if (sg_alloc_table(sgt, 1, GFP_KERNEL))
> +                       goto free_sgt;
> +               sg_dma_address(sgt->sgl) = dobj->dev_addr;
> +               sg_dma_len(sgt->sgl) = dobj->obj.size;
> +       } else {
> +               goto free_sgt;
> +       }
> +       return sgt;
> +
> + release:
> +       for_each_sg(sgt->sgl, sg, num, i)
> +               page_cache_release(sg_page(sg));
> + free_table:
> +       sg_free_table(sgt);
> + free_sgt:
> +       kfree(sgt);
> +       return NULL;
> +}
[snip]
> +> diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
> new file mode 100644
> index 0000000..8dec3fd
> --- /dev/null
> +++ b/include/uapi/drm/armada_drm.h
> @@ -0,0 +1,45 @@
> +/*
> + * 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_ARMADA_IOCTL_H
> +#define DRM_ARMADA_IOCTL_H
> +
> +#define DRM_ARMADA_GEM_CREATE          0x00
> +#define DRM_ARMADA_GEM_MMAP            0x02
> +#define DRM_ARMADA_GEM_PWRITE          0x03
> +
> +#define ARMADA_IOCTL(dir, name, str) \
> +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
> +
> +struct drm_armada_gem_create {

Any reason not to throw in a 'uint32_t flags' field?  At least then
you could indicate what sort of backing memory you want, and do things
like allocate linear scanout buffer w/ your gem_create rather than
having to use dumb_create.  Maybe not something you need now, but
seems like eventually you'll come up with some reason or another to
want to pass some flags into gem_create.

> +       uint32_t handle;
> +       uint32_t size;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_CREATE \
> +       ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
> +
> +struct drm_armada_gem_mmap {
> +       uint32_t handle;
> +       uint32_t pad;
> +       uint64_t offset;
> +       uint64_t size;
> +       uint64_t addr;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_MMAP \
> +       ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
> +
> +struct drm_armada_gem_pwrite {
> +       uint64_t ptr;
> +       uint32_t handle;
> +       uint32_t offset;
> +       uint32_t size;
> +};
> +#define DRM_IOCTL_ARMADA_GEM_PWRITE \
> +       ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
> +
> +#endif
> --
> 1.7.4.4
>

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-10 21:25     ` Rob Clark
@ 2013-10-10 21:59       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-10 21:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
> On Sun, Oct 6, 2013 at 6:08 PM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
> > +       /*
> > +        * If we have an overlay plane associated with this CRTC, disable
> > +        * it before the modeset to avoid its coordinates being outside
> > +        * the new mode parameters.  DRM doesn't provide help with this.
> > +        */
> 
> Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
> things by having crtc helpers disable planes on setcrtc (rather than
> only doing this on fbdev/lastclose restore).  I
> don't know of any userspace that this would cause problems for.  But
> not really sure if it would be considered an interface break or just
> "fixing a bug".. since we have different behavior on different
> drivers, I'd lean towards the latter.

The reasoning here is if the overlay plane is larger than the mode being
set, we will end up having the hardware programmed in an inconsistent
state - the overlay plane position/width/height can be outside of the
active region, which is invalid for the hardware.  So, it's safer to
disable the plane and wait for the next plane to be sent and have that
validated against the new mode.

It's not like you would be able to see the plane immediately, because
most monitors blank while they retrain to the new mode settings.

> > +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> > +{
> > +       struct armada_gem_object *obj = drm_to_armada_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();
> 
> probably this thread is applicable here too?
> 
> https://lkml.org/lkml/2013/9/12/417
> 
> (although.. we have at least a few  slightly differet variants on the
> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
> do something better)

Hmm.  It seems today's vm_insert_pfn() has the following error return
values:

-EFAULT - virtual address outside vma
-EINVAL - track_pfn_insert() failure
-ENOMEM - failed to get locked pte
-EBUSY - entry already exists in pte

So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
redundant and can be removed.  I'm not sure if it makes sense for this
to be generic - it looks like it's only Intel, gma500 and me who use
this, and Intel handles more error codes (due to it calling other
functions.)

I'll respin dropping those four unnecessary error codes, and post the
delta for this.

> > +/* Prime support */
> > +struct sg_table *
> > +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
> > +       enum dma_data_direction dir)
> > +{
> > +       struct drm_gem_object *obj = attach->dmabuf->priv;
> > +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
> > +       struct scatterlist *sg;
> > +       struct sg_table *sgt;
> > +       int i, num;
> > +
> > +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
> > +       if (!sgt)
> > +               return NULL;
> > +
> > +       if (dobj->obj.filp) {
> > +               struct address_space *mapping;
> > +               gfp_t gfp;
> > +               int count;
> > +
> > +               count = dobj->obj.size / PAGE_SIZE;
> > +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
> > +                       goto free_sgt;
> > +
> > +               mapping = file_inode(dobj->obj.filp)->i_mapping;
> > +               gfp = mapping_gfp_mask(mapping);
> > +
> > +               for_each_sg(sgt->sgl, sg, count, i) {
> > +                       struct page *page;
> > +
> > +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
> 
> you could almost use drm_gem_get_pages().. although I guess you
> otherwise have no need for the page[] array?

Correct.  The page[] array would just be an additional unnecessary
allocation, and therefore would be an additional unnecessary point of
failure.

> > +#define DRM_ARMADA_GEM_CREATE          0x00
> > +#define DRM_ARMADA_GEM_MMAP            0x02
> > +#define DRM_ARMADA_GEM_PWRITE          0x03
> > +
> > +#define ARMADA_IOCTL(dir, name, str) \
> > +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
> > +
> > +struct drm_armada_gem_create {
> 
> Any reason not to throw in a 'uint32_t flags' field?  At least then
> you could indicate what sort of backing memory you want, and do things
> like allocate linear scanout buffer w/ your gem_create rather than
> having to use dumb_create.  Maybe not something you need now, but
> seems like eventually you'll come up with some reason or another to
> want to pass some flags into gem_create.

I thought one of the points of the dumb_create stuff was so that there
is a standard API for dumb scanout buffers across all DRM drivers.  It
has that advantage, and as I understand it, a generic KMS driver for X
is on the cards.

Thanks for the review.

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

* Re: [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-10 21:59       ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-10 21:59 UTC (permalink / raw)
  To: Rob Clark
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
> On Sun, Oct 6, 2013 at 6:08 PM, Russell King
> <rmk+kernel@arm.linux.org.uk> wrote:
> > +       /*
> > +        * If we have an overlay plane associated with this CRTC, disable
> > +        * it before the modeset to avoid its coordinates being outside
> > +        * the new mode parameters.  DRM doesn't provide help with this.
> > +        */
> 
> Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
> things by having crtc helpers disable planes on setcrtc (rather than
> only doing this on fbdev/lastclose restore).  I
> don't know of any userspace that this would cause problems for.  But
> not really sure if it would be considered an interface break or just
> "fixing a bug".. since we have different behavior on different
> drivers, I'd lean towards the latter.

The reasoning here is if the overlay plane is larger than the mode being
set, we will end up having the hardware programmed in an inconsistent
state - the overlay plane position/width/height can be outside of the
active region, which is invalid for the hardware.  So, it's safer to
disable the plane and wait for the next plane to be sent and have that
validated against the new mode.

It's not like you would be able to see the plane immediately, because
most monitors blank while they retrain to the new mode settings.

> > +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> > +{
> > +       struct armada_gem_object *obj = drm_to_armada_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();
> 
> probably this thread is applicable here too?
> 
> https://lkml.org/lkml/2013/9/12/417
> 
> (although.. we have at least a few  slightly differet variants on the
> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
> do something better)

Hmm.  It seems today's vm_insert_pfn() has the following error return
values:

-EFAULT - virtual address outside vma
-EINVAL - track_pfn_insert() failure
-ENOMEM - failed to get locked pte
-EBUSY - entry already exists in pte

So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
redundant and can be removed.  I'm not sure if it makes sense for this
to be generic - it looks like it's only Intel, gma500 and me who use
this, and Intel handles more error codes (due to it calling other
functions.)

I'll respin dropping those four unnecessary error codes, and post the
delta for this.

> > +/* Prime support */
> > +struct sg_table *
> > +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
> > +       enum dma_data_direction dir)
> > +{
> > +       struct drm_gem_object *obj = attach->dmabuf->priv;
> > +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
> > +       struct scatterlist *sg;
> > +       struct sg_table *sgt;
> > +       int i, num;
> > +
> > +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
> > +       if (!sgt)
> > +               return NULL;
> > +
> > +       if (dobj->obj.filp) {
> > +               struct address_space *mapping;
> > +               gfp_t gfp;
> > +               int count;
> > +
> > +               count = dobj->obj.size / PAGE_SIZE;
> > +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
> > +                       goto free_sgt;
> > +
> > +               mapping = file_inode(dobj->obj.filp)->i_mapping;
> > +               gfp = mapping_gfp_mask(mapping);
> > +
> > +               for_each_sg(sgt->sgl, sg, count, i) {
> > +                       struct page *page;
> > +
> > +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
> 
> you could almost use drm_gem_get_pages().. although I guess you
> otherwise have no need for the page[] array?

Correct.  The page[] array would just be an additional unnecessary
allocation, and therefore would be an additional unnecessary point of
failure.

> > +#define DRM_ARMADA_GEM_CREATE          0x00
> > +#define DRM_ARMADA_GEM_MMAP            0x02
> > +#define DRM_ARMADA_GEM_PWRITE          0x03
> > +
> > +#define ARMADA_IOCTL(dir, name, str) \
> > +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
> > +
> > +struct drm_armada_gem_create {
> 
> Any reason not to throw in a 'uint32_t flags' field?  At least then
> you could indicate what sort of backing memory you want, and do things
> like allocate linear scanout buffer w/ your gem_create rather than
> having to use dumb_create.  Maybe not something you need now, but
> seems like eventually you'll come up with some reason or another to
> want to pass some flags into gem_create.

I thought one of the points of the dumb_create stuff was so that there
is a standard API for dumb scanout buffers across all DRM drivers.  It
has that advantage, and as I understand it, a generic KMS driver for X
is on the cards.

Thanks for the review.

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-10 21:59       ` Russell King - ARM Linux
@ 2013-10-10 22:23         ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-10 22:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
>> On Sun, Oct 6, 2013 at 6:08 PM, Russell King
>> <rmk+kernel@arm.linux.org.uk> wrote:
>> > +       /*
>> > +        * If we have an overlay plane associated with this CRTC, disable
>> > +        * it before the modeset to avoid its coordinates being outside
>> > +        * the new mode parameters.  DRM doesn't provide help with this.
>> > +        */
>>
>> Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
>> things by having crtc helpers disable planes on setcrtc (rather than
>> only doing this on fbdev/lastclose restore).  I
>> don't know of any userspace that this would cause problems for.  But
>> not really sure if it would be considered an interface break or just
>> "fixing a bug".. since we have different behavior on different
>> drivers, I'd lean towards the latter.
>
> The reasoning here is if the overlay plane is larger than the mode being
> set, we will end up having the hardware programmed in an inconsistent
> state - the overlay plane position/width/height can be outside of the
> active region, which is invalid for the hardware.  So, it's safer to
> disable the plane and wait for the next plane to be sent and have that
> validated against the new mode.

right.  I think disabling the planes is the correct behavior.  So
nothing to do here for armada.  I mostly just wanted to get some
opinions about making the crtc helpers do this automatically for us.

> It's not like you would be able to see the plane immediately, because
> most monitors blank while they retrain to the new mode settings.
>
>> > +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>> > +{
>> > +       struct armada_gem_object *obj = drm_to_armada_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();
>>
>> probably this thread is applicable here too?
>>
>> https://lkml.org/lkml/2013/9/12/417
>>
>> (although.. we have at least a few  slightly differet variants on the
>> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
>> do something better)
>
> Hmm.  It seems today's vm_insert_pfn() has the following error return
> values:
>
> -EFAULT - virtual address outside vma
> -EINVAL - track_pfn_insert() failure
> -ENOMEM - failed to get locked pte
> -EBUSY - entry already exists in pte
>
> So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
> redundant and can be removed.  I'm not sure if it makes sense for this
> to be generic - it looks like it's only Intel, gma500 and me who use
> this, and Intel handles more error codes (due to it calling other
> functions.)

I just noticed msm and omapdrm are missing the -EBUSY case (have some
patches I need to send), which was why I mentioned this.  They do have
other error cases from other fxns, so maybe something generic/common
doesn't make sense..  but I realized i915 fixed the same issue a while
back, so somewhere common has the advantage of somehow not forgetting
to fix other drivers ;-)

> I'll respin dropping those four unnecessary error codes, and post the
> delta for this.
>
>> > +/* Prime support */
>> > +struct sg_table *
>> > +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
>> > +       enum dma_data_direction dir)
>> > +{
>> > +       struct drm_gem_object *obj = attach->dmabuf->priv;
>> > +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
>> > +       struct scatterlist *sg;
>> > +       struct sg_table *sgt;
>> > +       int i, num;
>> > +
>> > +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
>> > +       if (!sgt)
>> > +               return NULL;
>> > +
>> > +       if (dobj->obj.filp) {
>> > +               struct address_space *mapping;
>> > +               gfp_t gfp;
>> > +               int count;
>> > +
>> > +               count = dobj->obj.size / PAGE_SIZE;
>> > +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
>> > +                       goto free_sgt;
>> > +
>> > +               mapping = file_inode(dobj->obj.filp)->i_mapping;
>> > +               gfp = mapping_gfp_mask(mapping);
>> > +
>> > +               for_each_sg(sgt->sgl, sg, count, i) {
>> > +                       struct page *page;
>> > +
>> > +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
>>
>> you could almost use drm_gem_get_pages().. although I guess you
>> otherwise have no need for the page[] array?
>
> Correct.  The page[] array would just be an additional unnecessary
> allocation, and therefore would be an additional unnecessary point of
> failure.
>
>> > +#define DRM_ARMADA_GEM_CREATE          0x00
>> > +#define DRM_ARMADA_GEM_MMAP            0x02
>> > +#define DRM_ARMADA_GEM_PWRITE          0x03
>> > +
>> > +#define ARMADA_IOCTL(dir, name, str) \
>> > +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
>> > +
>> > +struct drm_armada_gem_create {
>>
>> Any reason not to throw in a 'uint32_t flags' field?  At least then
>> you could indicate what sort of backing memory you want, and do things
>> like allocate linear scanout buffer w/ your gem_create rather than
>> having to use dumb_create.  Maybe not something you need now, but
>> seems like eventually you'll come up with some reason or another to
>> want to pass some flags into gem_create.
>
> I thought one of the points of the dumb_create stuff was so that there
> is a standard API for dumb scanout buffers across all DRM drivers.  It
> has that advantage, and as I understand it, a generic KMS driver for X
> is on the cards.

Yeah, xf86-video-modesetting currently uses dumb allocation.  So you
definitely want to continue to support the dumb buffer path.

I can't really think of any immediate need for 'flags'..  but seems
like sooner or later someone will come up with some need for it, so it
seemed like a convenient placeholder to have.

Well, technically you can get away w/ adding new fields to the end of
the ioctl struct, and drm_ioctl() will take care of zero'ing that out
for you with an old userspace.  So I guess you could just rely on that
if you ever need to add 'flags' or some other fields.

> Thanks for the review.

no prob

BR,
-R

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

* Re: [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-10 22:23         ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-10 22:23 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
>> On Sun, Oct 6, 2013 at 6:08 PM, Russell King
>> <rmk+kernel@arm.linux.org.uk> wrote:
>> > +       /*
>> > +        * If we have an overlay plane associated with this CRTC, disable
>> > +        * it before the modeset to avoid its coordinates being outside
>> > +        * the new mode parameters.  DRM doesn't provide help with this.
>> > +        */
>>
>> Getting a bit off topic, but yeah.. I'd be in favor of "clarifying"
>> things by having crtc helpers disable planes on setcrtc (rather than
>> only doing this on fbdev/lastclose restore).  I
>> don't know of any userspace that this would cause problems for.  But
>> not really sure if it would be considered an interface break or just
>> "fixing a bug".. since we have different behavior on different
>> drivers, I'd lean towards the latter.
>
> The reasoning here is if the overlay plane is larger than the mode being
> set, we will end up having the hardware programmed in an inconsistent
> state - the overlay plane position/width/height can be outside of the
> active region, which is invalid for the hardware.  So, it's safer to
> disable the plane and wait for the next plane to be sent and have that
> validated against the new mode.

right.  I think disabling the planes is the correct behavior.  So
nothing to do here for armada.  I mostly just wanted to get some
opinions about making the crtc helpers do this automatically for us.

> It's not like you would be able to see the plane immediately, because
> most monitors blank while they retrain to the new mode settings.
>
>> > +static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>> > +{
>> > +       struct armada_gem_object *obj = drm_to_armada_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();
>>
>> probably this thread is applicable here too?
>>
>> https://lkml.org/lkml/2013/9/12/417
>>
>> (although.. we have at least a few  slightly differet variants on the
>> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
>> do something better)
>
> Hmm.  It seems today's vm_insert_pfn() has the following error return
> values:
>
> -EFAULT - virtual address outside vma
> -EINVAL - track_pfn_insert() failure
> -ENOMEM - failed to get locked pte
> -EBUSY - entry already exists in pte
>
> So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
> redundant and can be removed.  I'm not sure if it makes sense for this
> to be generic - it looks like it's only Intel, gma500 and me who use
> this, and Intel handles more error codes (due to it calling other
> functions.)

I just noticed msm and omapdrm are missing the -EBUSY case (have some
patches I need to send), which was why I mentioned this.  They do have
other error cases from other fxns, so maybe something generic/common
doesn't make sense..  but I realized i915 fixed the same issue a while
back, so somewhere common has the advantage of somehow not forgetting
to fix other drivers ;-)

> I'll respin dropping those four unnecessary error codes, and post the
> delta for this.
>
>> > +/* Prime support */
>> > +struct sg_table *
>> > +armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
>> > +       enum dma_data_direction dir)
>> > +{
>> > +       struct drm_gem_object *obj = attach->dmabuf->priv;
>> > +       struct armada_gem_object *dobj = drm_to_armada_gem(obj);
>> > +       struct scatterlist *sg;
>> > +       struct sg_table *sgt;
>> > +       int i, num;
>> > +
>> > +       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
>> > +       if (!sgt)
>> > +               return NULL;
>> > +
>> > +       if (dobj->obj.filp) {
>> > +               struct address_space *mapping;
>> > +               gfp_t gfp;
>> > +               int count;
>> > +
>> > +               count = dobj->obj.size / PAGE_SIZE;
>> > +               if (sg_alloc_table(sgt, count, GFP_KERNEL))
>> > +                       goto free_sgt;
>> > +
>> > +               mapping = file_inode(dobj->obj.filp)->i_mapping;
>> > +               gfp = mapping_gfp_mask(mapping);
>> > +
>> > +               for_each_sg(sgt->sgl, sg, count, i) {
>> > +                       struct page *page;
>> > +
>> > +                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
>>
>> you could almost use drm_gem_get_pages().. although I guess you
>> otherwise have no need for the page[] array?
>
> Correct.  The page[] array would just be an additional unnecessary
> allocation, and therefore would be an additional unnecessary point of
> failure.
>
>> > +#define DRM_ARMADA_GEM_CREATE          0x00
>> > +#define DRM_ARMADA_GEM_MMAP            0x02
>> > +#define DRM_ARMADA_GEM_PWRITE          0x03
>> > +
>> > +#define ARMADA_IOCTL(dir, name, str) \
>> > +       DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
>> > +
>> > +struct drm_armada_gem_create {
>>
>> Any reason not to throw in a 'uint32_t flags' field?  At least then
>> you could indicate what sort of backing memory you want, and do things
>> like allocate linear scanout buffer w/ your gem_create rather than
>> having to use dumb_create.  Maybe not something you need now, but
>> seems like eventually you'll come up with some reason or another to
>> want to pass some flags into gem_create.
>
> I thought one of the points of the dumb_create stuff was so that there
> is a standard API for dumb scanout buffers across all DRM drivers.  It
> has that advantage, and as I understand it, a generic KMS driver for X
> is on the cards.

Yeah, xf86-video-modesetting currently uses dumb allocation.  So you
definitely want to continue to support the dumb buffer path.

I can't really think of any immediate need for 'flags'..  but seems
like sooner or later someone will come up with some need for it, so it
seemed like a convenient placeholder to have.

Well, technically you can get away w/ adding new fields to the end of
the ioctl struct, and drm_ioctl() will take care of zero'ing that out
for you with an old userspace.  So I guess you could just rely on that
if you ever need to add 'flags' or some other fields.

> Thanks for the review.

no prob

BR,
-R

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-10 22:23         ` Rob Clark
@ 2013-10-10 22:53           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-10 22:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 10, 2013 at 06:23:26PM -0400, Rob Clark wrote:
> On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
> >> probably this thread is applicable here too?
> >>
> >> https://lkml.org/lkml/2013/9/12/417
> >>
> >> (although.. we have at least a few  slightly differet variants on the
> >> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
> >> do something better)
> >
> > Hmm.  It seems today's vm_insert_pfn() has the following error return
> > values:
> >
> > -EFAULT - virtual address outside vma
> > -EINVAL - track_pfn_insert() failure
> > -ENOMEM - failed to get locked pte
> > -EBUSY - entry already exists in pte
> >
> > So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
> > redundant and can be removed.  I'm not sure if it makes sense for this
> > to be generic - it looks like it's only Intel, gma500 and me who use
> > this, and Intel handles more error codes (due to it calling other
> > functions.)
> 
> I just noticed msm and omapdrm are missing the -EBUSY case (have some
> patches I need to send), which was why I mentioned this.  They do have
> other error cases from other fxns, so maybe something generic/common
> doesn't make sense..  but I realized i915 fixed the same issue a while
> back, so somewhere common has the advantage of somehow not forgetting
> to fix other drivers ;-)

Here's the promised delta:

 drivers/gpu/drm/armada/armada_gem.c |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index 02de7a4..1e74f70 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -25,12 +25,7 @@ static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	ret = vm_insert_pfn(vma, addr, pfn);
 
 	switch (ret) {
-	case -EIO:
-	case -EAGAIN:
-		set_need_resched();
 	case 0:
-	case -ERESTARTSYS:
-	case -EINTR:
 	case -EBUSY:
 		return VM_FAULT_NOPAGE;
 	case -ENOMEM:

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

* Re: [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-10 22:53           ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-10 22:53 UTC (permalink / raw)
  To: Rob Clark
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Thu, Oct 10, 2013 at 06:23:26PM -0400, Rob Clark wrote:
> On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
> >> probably this thread is applicable here too?
> >>
> >> https://lkml.org/lkml/2013/9/12/417
> >>
> >> (although.. we have at least a few  slightly differet variants on the
> >> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
> >> do something better)
> >
> > Hmm.  It seems today's vm_insert_pfn() has the following error return
> > values:
> >
> > -EFAULT - virtual address outside vma
> > -EINVAL - track_pfn_insert() failure
> > -ENOMEM - failed to get locked pte
> > -EBUSY - entry already exists in pte
> >
> > So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
> > redundant and can be removed.  I'm not sure if it makes sense for this
> > to be generic - it looks like it's only Intel, gma500 and me who use
> > this, and Intel handles more error codes (due to it calling other
> > functions.)
> 
> I just noticed msm and omapdrm are missing the -EBUSY case (have some
> patches I need to send), which was why I mentioned this.  They do have
> other error cases from other fxns, so maybe something generic/common
> doesn't make sense..  but I realized i915 fixed the same issue a while
> back, so somewhere common has the advantage of somehow not forgetting
> to fix other drivers ;-)

Here's the promised delta:

 drivers/gpu/drm/armada/armada_gem.c |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index 02de7a4..1e74f70 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -25,12 +25,7 @@ static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	ret = vm_insert_pfn(vma, addr, pfn);
 
 	switch (ret) {
-	case -EIO:
-	case -EAGAIN:
-		set_need_resched();
 	case 0:
-	case -ERESTARTSYS:
-	case -EINTR:
 	case -EBUSY:
 		return VM_FAULT_NOPAGE;
 	case -ENOMEM:

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

* [PATCH 2/5] DRM: Armada: Add Armada DRM driver
  2013-10-10 22:53           ` Russell King - ARM Linux
@ 2013-10-11  0:10             ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-11  0:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 10, 2013 at 6:53 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Thu, Oct 10, 2013 at 06:23:26PM -0400, Rob Clark wrote:
>> On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
>> >> probably this thread is applicable here too?
>> >>
>> >> https://lkml.org/lkml/2013/9/12/417
>> >>
>> >> (although.. we have at least a few  slightly differet variants on the
>> >> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
>> >> do something better)
>> >
>> > Hmm.  It seems today's vm_insert_pfn() has the following error return
>> > values:
>> >
>> > -EFAULT - virtual address outside vma
>> > -EINVAL - track_pfn_insert() failure
>> > -ENOMEM - failed to get locked pte
>> > -EBUSY - entry already exists in pte
>> >
>> > So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
>> > redundant and can be removed.  I'm not sure if it makes sense for this
>> > to be generic - it looks like it's only Intel, gma500 and me who use
>> > this, and Intel handles more error codes (due to it calling other
>> > functions.)
>>
>> I just noticed msm and omapdrm are missing the -EBUSY case (have some
>> patches I need to send), which was why I mentioned this.  They do have
>> other error cases from other fxns, so maybe something generic/common
>> doesn't make sense..  but I realized i915 fixed the same issue a while
>> back, so somewhere common has the advantage of somehow not forgetting
>> to fix other drivers ;-)
>
> Here's the promised delta:

looks good.. I'll try to get to reviewing the rest of the series
tomorrow, or at least before the end of the weekend

Reviewed-by: Rob Clark <robdclark@gmail.com>

BR,
-R

>  drivers/gpu/drm/armada/armada_gem.c |    5 -----
>  1 files changed, 0 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
> index 02de7a4..1e74f70 100644
> --- a/drivers/gpu/drm/armada/armada_gem.c
> +++ b/drivers/gpu/drm/armada/armada_gem.c
> @@ -25,12 +25,7 @@ static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>         ret = vm_insert_pfn(vma, addr, pfn);
>
>         switch (ret) {
> -       case -EIO:
> -       case -EAGAIN:
> -               set_need_resched();
>         case 0:
> -       case -ERESTARTSYS:
> -       case -EINTR:
>         case -EBUSY:
>                 return VM_FAULT_NOPAGE;
>         case -ENOMEM:
>

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

* Re: [PATCH 2/5] DRM: Armada: Add Armada DRM driver
@ 2013-10-11  0:10             ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-11  0:10 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Jason Cooper, dri-devel, linux-arm-kernel, Sebastian Hesselbarth

On Thu, Oct 10, 2013 at 6:53 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Thu, Oct 10, 2013 at 06:23:26PM -0400, Rob Clark wrote:
>> On Thu, Oct 10, 2013 at 5:59 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > On Thu, Oct 10, 2013 at 05:25:15PM -0400, Rob Clark wrote:
>> >> probably this thread is applicable here too?
>> >>
>> >> https://lkml.org/lkml/2013/9/12/417
>> >>
>> >> (although.. we have at least a few  slightly differet variants on the
>> >> same errno -> VM_FAULT_x switch in different drivers.. maybe we should
>> >> do something better)
>> >
>> > Hmm.  It seems today's vm_insert_pfn() has the following error return
>> > values:
>> >
>> > -EFAULT - virtual address outside vma
>> > -EINVAL - track_pfn_insert() failure
>> > -ENOMEM - failed to get locked pte
>> > -EBUSY - entry already exists in pte
>> >
>> > So it seems my handling -EIO, -EAGAIN, -ERESTARTSYS and -EINTR are all
>> > redundant and can be removed.  I'm not sure if it makes sense for this
>> > to be generic - it looks like it's only Intel, gma500 and me who use
>> > this, and Intel handles more error codes (due to it calling other
>> > functions.)
>>
>> I just noticed msm and omapdrm are missing the -EBUSY case (have some
>> patches I need to send), which was why I mentioned this.  They do have
>> other error cases from other fxns, so maybe something generic/common
>> doesn't make sense..  but I realized i915 fixed the same issue a while
>> back, so somewhere common has the advantage of somehow not forgetting
>> to fix other drivers ;-)
>
> Here's the promised delta:

looks good.. I'll try to get to reviewing the rest of the series
tomorrow, or at least before the end of the weekend

Reviewed-by: Rob Clark <robdclark@gmail.com>

BR,
-R

>  drivers/gpu/drm/armada/armada_gem.c |    5 -----
>  1 files changed, 0 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
> index 02de7a4..1e74f70 100644
> --- a/drivers/gpu/drm/armada/armada_gem.c
> +++ b/drivers/gpu/drm/armada/armada_gem.c
> @@ -25,12 +25,7 @@ static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>         ret = vm_insert_pfn(vma, addr, pfn);
>
>         switch (ret) {
> -       case -EIO:
> -       case -EAGAIN:
> -               set_need_resched();
>         case 0:
> -       case -ERESTARTSYS:
> -       case -EINTR:
>         case -EBUSY:
>                 return VM_FAULT_NOPAGE;
>         case -ENOMEM:
>

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-07 12:50               ` Russell King - ARM Linux
@ 2013-10-17 23:58                 ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-17 23:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 7, 2013 at 8:50 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
>> On Mon, 7 Oct 2013 11:32:50 +0100
>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>> > However, what you're suggesting is dangerous.  What do you do when you're
>> > presented with a cursor which is 64x64 ?  Do you:
>> >
>> > (a) not display it
>> > (b) crash the X server
>> >
>> > There isn't a fallback to using software cursors available in the X server
>> > because there's no error reporting for a failed hardware cursor update.
>>
>> Hmm, maybe I'm missing something, but doesn't returning FALSE from
>> UseHWCursorARGB just make the X server fallback to using a software
>> cursor?
>>
>> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
>>
>> I'm just doing this. And also have hooks to notify the DRI2 code about
>> the status of the cursor. In the case if the hardware cursor is not
>> enabled, hardware overlays can't be safely used with the software
>> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
>> buffers swapping.
>>
>> And I fully agree that it is the responsibility of the kernel to expose
>> as much of the hardware capabilities as possible (without compromising
>> reliability and/or security).
>
> If you wish to go in below the level presented by
> hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
> this, and it's something which maybe should be looked into.  You have a
> good reason to do this (due to your hardware cursor being incompatible
> with overlays).
>
> We have no such restriction - the only issue here is the "odd" size of
> cursors.  Practically, choosing one or other of 64x32 or 32x64 works
> fine for the cases I've seen.  I haven't seen any cursors which are
> 64x64 yet.  I'm not saying they don't exist though!  I can imagine that
> some accessibility options might want to display a large cursor.
>
> Now, the way I handle this is that the kernel allows _either_ a
> (1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
> height larger than 32 gets rejected with -EINVAL:
>
> +               /* maximum size is 64x32 or 32x64 */
> +               if (w > 64 || h > 64 || (w > 32 && h > 32))
> +                       return -ENOMEM;
>
> which is quite reasonable for this hardware.  However, there is no way
> to export this from DRM - adding maximum cursor size properties wouldn't
> really represent this.

Until relatively recently, it had always been a device specific ddx
which would somehow know what w/h values to pass to
xf86_cursors_init().  I think for xf86-video-modesetting and wayland
compositors, we probably need to come up with something better.
Although off the top of my head I can't think of a good way to express
64x32 or 32x64.. that is a weird restriction.

Anyways, right now I don't think there is anything different that I'd
do in the kernel part.  I *suppose* you could try and figure out that
all the alpha values are either 0x00 or 0xff and support 64x64 in that
special case.  I'm not sure that is actually better.. it at least
makes the failure vs success cases more confusing.  And unless you are
rockin' the twm+xterm+xcalc setup, I doubt you will see many cursors
with only 0x00 or 0xff alpha.

So,

Signed-off-by: Rob Clark <robdclark@gmail.com>


> The only question is whether DRM should export some capabilities to
> indicate what's possible with the cursor, so that X knows about this
> quirk of this hardware.  Overall, I suspect that it's just rare and
> not worth the effort.  I think it's just easier to pick one of these
> and stick with it, and if we happen to encounter a cursor larger than
> our chosen dimentions, XFree86 will already automatically fall back
> to s/w cursor.

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-17 23:58                 ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-17 23:58 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Siarhei Siamashka, Jason Cooper, Jean-Francois Moine,
	David Airlie, dri-devel, Daniel Vetter, linux-arm-kernel,
	Sebastian Hesselbarth

On Mon, Oct 7, 2013 at 8:50 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
>> On Mon, 7 Oct 2013 11:32:50 +0100
>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>> > However, what you're suggesting is dangerous.  What do you do when you're
>> > presented with a cursor which is 64x64 ?  Do you:
>> >
>> > (a) not display it
>> > (b) crash the X server
>> >
>> > There isn't a fallback to using software cursors available in the X server
>> > because there's no error reporting for a failed hardware cursor update.
>>
>> Hmm, maybe I'm missing something, but doesn't returning FALSE from
>> UseHWCursorARGB just make the X server fallback to using a software
>> cursor?
>>
>> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
>>
>> I'm just doing this. And also have hooks to notify the DRI2 code about
>> the status of the cursor. In the case if the hardware cursor is not
>> enabled, hardware overlays can't be safely used with the software
>> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
>> buffers swapping.
>>
>> And I fully agree that it is the responsibility of the kernel to expose
>> as much of the hardware capabilities as possible (without compromising
>> reliability and/or security).
>
> If you wish to go in below the level presented by
> hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
> this, and it's something which maybe should be looked into.  You have a
> good reason to do this (due to your hardware cursor being incompatible
> with overlays).
>
> We have no such restriction - the only issue here is the "odd" size of
> cursors.  Practically, choosing one or other of 64x32 or 32x64 works
> fine for the cases I've seen.  I haven't seen any cursors which are
> 64x64 yet.  I'm not saying they don't exist though!  I can imagine that
> some accessibility options might want to display a large cursor.
>
> Now, the way I handle this is that the kernel allows _either_ a
> (1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
> height larger than 32 gets rejected with -EINVAL:
>
> +               /* maximum size is 64x32 or 32x64 */
> +               if (w > 64 || h > 64 || (w > 32 && h > 32))
> +                       return -ENOMEM;
>
> which is quite reasonable for this hardware.  However, there is no way
> to export this from DRM - adding maximum cursor size properties wouldn't
> really represent this.

Until relatively recently, it had always been a device specific ddx
which would somehow know what w/h values to pass to
xf86_cursors_init().  I think for xf86-video-modesetting and wayland
compositors, we probably need to come up with something better.
Although off the top of my head I can't think of a good way to express
64x32 or 32x64.. that is a weird restriction.

Anyways, right now I don't think there is anything different that I'd
do in the kernel part.  I *suppose* you could try and figure out that
all the alpha values are either 0x00 or 0xff and support 64x64 in that
special case.  I'm not sure that is actually better.. it at least
makes the failure vs success cases more confusing.  And unless you are
rockin' the twm+xterm+xcalc setup, I doubt you will see many cursors
with only 0x00 or 0xff alpha.

So,

Signed-off-by: Rob Clark <robdclark@gmail.com>


> The only question is whether DRM should export some capabilities to
> indicate what's possible with the cursor, so that X knows about this
> quirk of this hardware.  Overall, I suspect that it's just rare and
> not worth the effort.  I think it's just easier to pick one of these
> and stick with it, and if we happen to encounter a cursor larger than
> our chosen dimentions, XFree86 will already automatically fall back
> to s/w cursor.

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

* [PATCH 4/5] DRM: Armada: start of MMP2/MMP3 support
  2013-10-06 22:10   ` Russell King
@ 2013-10-18  0:11     ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18  0:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Oct 6, 2013 at 6:10 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/armada/Makefile      |    1 +
>  drivers/gpu/drm/armada/armada_610.c  |   49 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_crtc.c |    1 +
>  drivers/gpu/drm/armada/armada_drm.h  |    1 +
>  drivers/gpu/drm/armada/armada_drv.c  |    6 ++++
>  drivers/gpu/drm/armada/armada_hw.h   |    8 +++++
>  6 files changed, 66 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpu/drm/armada/armada_610.c
>
> diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
> index d6f43e0..088db4a 100644
> --- a/drivers/gpu/drm/armada/Makefile
> +++ b/drivers/gpu/drm/armada/Makefile
> @@ -2,6 +2,7 @@ armada-y        := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
>                    armada_gem.o armada_output.o armada_overlay.o \
>                    armada_slave.o
>  armada-y       += armada_510.o
> +armada-y       += armada_610.o
>  armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
>
>  obj-$(CONFIG_DRM_ARMADA) := armada.o
> diff --git a/drivers/gpu/drm/armada/armada_610.c b/drivers/gpu/drm/armada/armada_610.c
> new file mode 100644
> index 0000000..36a10e3
> --- /dev/null
> +++ b/drivers/gpu/drm/armada/armada_610.c
> @@ -0,0 +1,49 @@
> +/*
> + * 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.
> + *
> + * Armada 610/MMP2/MMP3 variant support
> + */
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "armada_crtc.h"
> +#include "armada_drm.h"
> +#include "armada_hw.h"
> +
> +static int mmp23_init(struct armada_private *priv, struct device *dev)
> +{
> +       priv->extclk[0] = devm_clk_get(dev, NULL);
> +
> +       if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
> +               priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
> +
> +       return PTR_RET(priv->extclk[0]);
> +}
> +
> +/*
> + * This gets called with sclk = NULL to test whether the mode is
> + * supportable, and again with sclk != NULL to set the clocks up for
> + * that.  The former can return an error, but the latter is expected
> + * not to.
> + */
> +static int mmp23_crtc_compute_clock(struct armada_crtc *dcrtc,
> +       const struct drm_display_mode *mode, uint32_t *sclk)
> +{
> +       /*
> +        * on MMP3 bits 31:29 select the clock, OLPC wants 0x1 here, LCD clock 1
> +        * on MMP2 bits 31:30 select the clock, OLPC wants 0x1 here, LCD clock 1
> +        */
> +       *sclk = 0x20001100; // FIXME hardcoded mmp3 value
> +

maybe fix this?  Or comment out for now mmp2 below in armada_drm_platform_ids?

BR,
-R

> +       return 0;
> +}
> +
> +const struct armada_variant mmp23_ops = {
> +       .init = mmp23_init,
> +       .crtc_compute_clock = mmp23_crtc_compute_clock,
> +};
> diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
> index e8605bf..b5877ee 100644
> --- a/drivers/gpu/drm/armada/armada_crtc.c
> +++ b/drivers/gpu/drm/armada/armada_crtc.c
> @@ -1054,6 +1054,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
>         dcrtc->clk = ERR_PTR(-EINVAL);
>         dcrtc->csc_yuv_mode = CSC_AUTO;
>         dcrtc->csc_rgb_mode = CSC_AUTO;
> +       /* FIXME: MMP2/MMP3: OLPC panel is RGB666 */
>         dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
>         dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
>         spin_lock_init(&dcrtc->irq_lock);
> diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
> index eef09ec..f52fccb 100644
> --- a/drivers/gpu/drm/armada/armada_drm.h
> +++ b/drivers/gpu/drm/armada/armada_drm.h
> @@ -70,6 +70,7 @@ struct armada_variant {
>
>  /* Variant ops */
>  extern const struct armada_variant armada510_ops;
> +extern const struct armada_variant mmp23_ops;
>
>  struct armada_private {
>         const struct armada_variant *variant;
> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index 7bfab9a..db62f1b 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -344,6 +344,12 @@ static const struct platform_device_id armada_drm_platform_ids[] = {
>                 .name           = "armada-drm",
>                 .driver_data    = (unsigned long)&armada510_ops,
>         }, {
> +               .name           = "armada-610-drm", /* aka MMP2 */
> +               .driver_data    = (unsigned long)&mmp23_ops,
> +       }, {
> +               .name           = "mmp3-drm",
> +               .driver_data    = (unsigned long)&mmp23_ops,
> +       }, {
>                 .name           = "armada-510-drm",
>                 .driver_data    = (unsigned long)&armada510_ops,
>         },
> diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
> index 27319a8..1688716 100644
> --- a/drivers/gpu/drm/armada/armada_hw.h
> +++ b/drivers/gpu/drm/armada/armada_hw.h
> @@ -74,6 +74,7 @@ enum {
>         LCD_SPU_IOPAD_CONTROL           = 0x01bc,
>         LCD_SPU_IRQ_ENA                 = 0x01c0,
>         LCD_SPU_IRQ_ISR                 = 0x01c4,
> +       LCD_MISC_CNTL                   = 0x01c8,       /* Armada 168 */
>  };
>
>  /* For LCD_SPU_ADV_REG */
> @@ -211,6 +212,13 @@ enum {
>         SCLK_16X_PLL            = 0x8 << 28,
>         SCLK_16X_FRAC_DIV_MASK  = 0xfff << 16,
>         SCLK_16X_INT_DIV_MASK   = 0xffff << 0,
> +
> +       /* PXA910 / MMP2 (Armada 610) / MMP3 / PXA988 */
> +       SCLK_MMP_SRC_SEL        = 1 << 31,
> +       SCLK_MMP_DISABLE        = 1 << 28,
> +       SCLK_MMP_FRAC_DIV_MASK  = 0xfff << 16,
> +       SCLK_MMP_DSI_DIV_MASK   = 0xf << 8,
> +       SCLK_MMP_INT_DIV_MASK   = 0xff << 0,
>  };
>
>  /* For LCD_SPU_DUMB_CTRL */
> --
> 1.7.4.4
>

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

* Re: [PATCH 4/5] DRM: Armada: start of MMP2/MMP3 support
@ 2013-10-18  0:11     ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18  0:11 UTC (permalink / raw)
  To: Russell King
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Sun, Oct 6, 2013 at 6:10 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/armada/Makefile      |    1 +
>  drivers/gpu/drm/armada/armada_610.c  |   49 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/armada/armada_crtc.c |    1 +
>  drivers/gpu/drm/armada/armada_drm.h  |    1 +
>  drivers/gpu/drm/armada/armada_drv.c  |    6 ++++
>  drivers/gpu/drm/armada/armada_hw.h   |    8 +++++
>  6 files changed, 66 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpu/drm/armada/armada_610.c
>
> diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
> index d6f43e0..088db4a 100644
> --- a/drivers/gpu/drm/armada/Makefile
> +++ b/drivers/gpu/drm/armada/Makefile
> @@ -2,6 +2,7 @@ armada-y        := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
>                    armada_gem.o armada_output.o armada_overlay.o \
>                    armada_slave.o
>  armada-y       += armada_510.o
> +armada-y       += armada_610.o
>  armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
>
>  obj-$(CONFIG_DRM_ARMADA) := armada.o
> diff --git a/drivers/gpu/drm/armada/armada_610.c b/drivers/gpu/drm/armada/armada_610.c
> new file mode 100644
> index 0000000..36a10e3
> --- /dev/null
> +++ b/drivers/gpu/drm/armada/armada_610.c
> @@ -0,0 +1,49 @@
> +/*
> + * 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.
> + *
> + * Armada 610/MMP2/MMP3 variant support
> + */
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "armada_crtc.h"
> +#include "armada_drm.h"
> +#include "armada_hw.h"
> +
> +static int mmp23_init(struct armada_private *priv, struct device *dev)
> +{
> +       priv->extclk[0] = devm_clk_get(dev, NULL);
> +
> +       if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
> +               priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
> +
> +       return PTR_RET(priv->extclk[0]);
> +}
> +
> +/*
> + * This gets called with sclk = NULL to test whether the mode is
> + * supportable, and again with sclk != NULL to set the clocks up for
> + * that.  The former can return an error, but the latter is expected
> + * not to.
> + */
> +static int mmp23_crtc_compute_clock(struct armada_crtc *dcrtc,
> +       const struct drm_display_mode *mode, uint32_t *sclk)
> +{
> +       /*
> +        * on MMP3 bits 31:29 select the clock, OLPC wants 0x1 here, LCD clock 1
> +        * on MMP2 bits 31:30 select the clock, OLPC wants 0x1 here, LCD clock 1
> +        */
> +       *sclk = 0x20001100; // FIXME hardcoded mmp3 value
> +

maybe fix this?  Or comment out for now mmp2 below in armada_drm_platform_ids?

BR,
-R

> +       return 0;
> +}
> +
> +const struct armada_variant mmp23_ops = {
> +       .init = mmp23_init,
> +       .crtc_compute_clock = mmp23_crtc_compute_clock,
> +};
> diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
> index e8605bf..b5877ee 100644
> --- a/drivers/gpu/drm/armada/armada_crtc.c
> +++ b/drivers/gpu/drm/armada/armada_crtc.c
> @@ -1054,6 +1054,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
>         dcrtc->clk = ERR_PTR(-EINVAL);
>         dcrtc->csc_yuv_mode = CSC_AUTO;
>         dcrtc->csc_rgb_mode = CSC_AUTO;
> +       /* FIXME: MMP2/MMP3: OLPC panel is RGB666 */
>         dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
>         dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
>         spin_lock_init(&dcrtc->irq_lock);
> diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
> index eef09ec..f52fccb 100644
> --- a/drivers/gpu/drm/armada/armada_drm.h
> +++ b/drivers/gpu/drm/armada/armada_drm.h
> @@ -70,6 +70,7 @@ struct armada_variant {
>
>  /* Variant ops */
>  extern const struct armada_variant armada510_ops;
> +extern const struct armada_variant mmp23_ops;
>
>  struct armada_private {
>         const struct armada_variant *variant;
> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index 7bfab9a..db62f1b 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -344,6 +344,12 @@ static const struct platform_device_id armada_drm_platform_ids[] = {
>                 .name           = "armada-drm",
>                 .driver_data    = (unsigned long)&armada510_ops,
>         }, {
> +               .name           = "armada-610-drm", /* aka MMP2 */
> +               .driver_data    = (unsigned long)&mmp23_ops,
> +       }, {
> +               .name           = "mmp3-drm",
> +               .driver_data    = (unsigned long)&mmp23_ops,
> +       }, {
>                 .name           = "armada-510-drm",
>                 .driver_data    = (unsigned long)&armada510_ops,
>         },
> diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
> index 27319a8..1688716 100644
> --- a/drivers/gpu/drm/armada/armada_hw.h
> +++ b/drivers/gpu/drm/armada/armada_hw.h
> @@ -74,6 +74,7 @@ enum {
>         LCD_SPU_IOPAD_CONTROL           = 0x01bc,
>         LCD_SPU_IRQ_ENA                 = 0x01c0,
>         LCD_SPU_IRQ_ISR                 = 0x01c4,
> +       LCD_MISC_CNTL                   = 0x01c8,       /* Armada 168 */
>  };
>
>  /* For LCD_SPU_ADV_REG */
> @@ -211,6 +212,13 @@ enum {
>         SCLK_16X_PLL            = 0x8 << 28,
>         SCLK_16X_FRAC_DIV_MASK  = 0xfff << 16,
>         SCLK_16X_INT_DIV_MASK   = 0xffff << 0,
> +
> +       /* PXA910 / MMP2 (Armada 610) / MMP3 / PXA988 */
> +       SCLK_MMP_SRC_SEL        = 1 << 31,
> +       SCLK_MMP_DISABLE        = 1 << 28,
> +       SCLK_MMP_FRAC_DIV_MASK  = 0xfff << 16,
> +       SCLK_MMP_DSI_DIV_MASK   = 0xf << 8,
> +       SCLK_MMP_INT_DIV_MASK   = 0xff << 0,
>  };
>
>  /* For LCD_SPU_DUMB_CTRL */
> --
> 1.7.4.4
>

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

* [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
  2013-10-08 15:34                   ` Jean-Francois Moine
@ 2013-10-18  0:20                     ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18  0:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Oct 8, 2013 at 11:34 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Tue, 8 Oct 2013 10:49:39 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>
>> On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
>> > The Cubox is an open platform, and I use it just like a desktop PC.
>> > When its required drivers will be in the mainline, I will do the same
>> > as I do with PCs: I will not recompile a specific kernel each time
>> > there are kernel bugs or security issues. Instead, I will just upgrade
>> > my system from my distributor (Debian), and, in the packages, there
>> > will be a generic mvebu kernel as there is already one for Marvell
>> > Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
>> > But, for that, all the Cubox specific stuff must be described in a DT.
>>
>> Which scenario is better:
>>
>> 1. To have something in mainline which is capable of driving the hardware,
>>    but may need some additional work to make it useful for DT based setups.
>>
>> or
>>
>> 2. To have nothing.
>>
>> Now, you may prefer to have nothing, but personally, I prefer there to be
>> forward progress.  Forward progress means getting some kind of DRM driver
>> into mainline.  Yes, it may not work with DT setups yet, but - as per
>> the discussions that have happened _endlessly_ on this topic, it's
>> something that can be resolved at a later date.
>>
>> We _still_ haven't worked out how to properly deal with the TDA998x
>> driver (or indeed any DRM based outputs) in a DT based setup, and all
>> the time that problem exists, it won't be possible to write a proper
>> stable DT binding for this.
>>
>> So please, get off your hobby horse about this and allow us to make some
>> modicum of progress.
>
> You forgot:
>
> 3. To have all patches ready for submission and have a working DT driven
>    Cubox kernel.

No need to block this patch on DT support for all platforms that can
use this driver.  I don't see an issue with removing non-DT support
later if it is no longer needed, or adding DT support in a future
patch for platforms which can use it.  So this patch looks fine to me.

Signed-off-by: Rob Clark <robdclark@gmail.com


> I will submit a patch to add DT to the tda998x driver as soon as I have
> checked the new audio properties we talked about yesterday.
>
> Normally, this should have no impact on your Armada drm driver, and,
> yes, I will add DT to your driver as soon as it will be accepted
> (sorry to not ack it now: I had no time yet to have a look at it).
>
> --
> Ken ar c'henta? |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/

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

* Re: [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver
@ 2013-10-18  0:20                     ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18  0:20 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Russell King - ARM Linux, Jason Cooper, David Airlie, dri-devel,
	Daniel Vetter, linux-arm-kernel, Sebastian Hesselbarth

On Tue, Oct 8, 2013 at 11:34 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Tue, 8 Oct 2013 10:49:39 +0100
> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>
>> On Tue, Oct 08, 2013 at 11:19:13AM +0200, Jean-Francois Moine wrote:
>> > The Cubox is an open platform, and I use it just like a desktop PC.
>> > When its required drivers will be in the mainline, I will do the same
>> > as I do with PCs: I will not recompile a specific kernel each time
>> > there are kernel bugs or security issues. Instead, I will just upgrade
>> > my system from my distributor (Debian), and, in the packages, there
>> > will be a generic mvebu kernel as there is already one for Marvell
>> > Armada 370/xp, Freescale iMX5x/iMX6 (linux-image-3.10-3-armmp).
>> > But, for that, all the Cubox specific stuff must be described in a DT.
>>
>> Which scenario is better:
>>
>> 1. To have something in mainline which is capable of driving the hardware,
>>    but may need some additional work to make it useful for DT based setups.
>>
>> or
>>
>> 2. To have nothing.
>>
>> Now, you may prefer to have nothing, but personally, I prefer there to be
>> forward progress.  Forward progress means getting some kind of DRM driver
>> into mainline.  Yes, it may not work with DT setups yet, but - as per
>> the discussions that have happened _endlessly_ on this topic, it's
>> something that can be resolved at a later date.
>>
>> We _still_ haven't worked out how to properly deal with the TDA998x
>> driver (or indeed any DRM based outputs) in a DT based setup, and all
>> the time that problem exists, it won't be possible to write a proper
>> stable DT binding for this.
>>
>> So please, get off your hobby horse about this and allow us to make some
>> modicum of progress.
>
> You forgot:
>
> 3. To have all patches ready for submission and have a working DT driven
>    Cubox kernel.

No need to block this patch on DT support for all platforms that can
use this driver.  I don't see an issue with removing non-DT support
later if it is no longer needed, or adding DT support in a future
patch for platforms which can use it.  So this patch looks fine to me.

Signed-off-by: Rob Clark <robdclark@gmail.com


> I will submit a patch to add DT to the tda998x driver as soon as I have
> checked the new audio properties we talked about yesterday.
>
> Normally, this should have no impact on your Armada drm driver, and,
> yes, I will add DT to your driver as soon as it will be accepted
> (sorry to not ack it now: I had no time yet to have a look at it).
>
> --
> Ken ar c'hentañ |             ** Breizh ha Linux atav! **
> Jef             |               http://moinejf.free.fr/

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

* [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
  2013-10-17 23:58                 ` Rob Clark
@ 2013-10-18 14:31                   ` Alex Deucher
  -1 siblings, 0 replies; 86+ messages in thread
From: Alex Deucher @ 2013-10-18 14:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 17, 2013 at 7:58 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Mon, Oct 7, 2013 at 8:50 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
>> On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
>>> On Mon, 7 Oct 2013 11:32:50 +0100
>>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>>> > However, what you're suggesting is dangerous.  What do you do when you're
>>> > presented with a cursor which is 64x64 ?  Do you:
>>> >
>>> > (a) not display it
>>> > (b) crash the X server
>>> >
>>> > There isn't a fallback to using software cursors available in the X server
>>> > because there's no error reporting for a failed hardware cursor update.
>>>
>>> Hmm, maybe I'm missing something, but doesn't returning FALSE from
>>> UseHWCursorARGB just make the X server fallback to using a software
>>> cursor?
>>>
>>> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
>>>
>>> I'm just doing this. And also have hooks to notify the DRI2 code about
>>> the status of the cursor. In the case if the hardware cursor is not
>>> enabled, hardware overlays can't be safely used with the software
>>> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
>>> buffers swapping.
>>>
>>> And I fully agree that it is the responsibility of the kernel to expose
>>> as much of the hardware capabilities as possible (without compromising
>>> reliability and/or security).
>>
>> If you wish to go in below the level presented by
>> hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
>> this, and it's something which maybe should be looked into.  You have a
>> good reason to do this (due to your hardware cursor being incompatible
>> with overlays).
>>
>> We have no such restriction - the only issue here is the "odd" size of
>> cursors.  Practically, choosing one or other of 64x32 or 32x64 works
>> fine for the cases I've seen.  I haven't seen any cursors which are
>> 64x64 yet.  I'm not saying they don't exist though!  I can imagine that
>> some accessibility options might want to display a large cursor.
>>
>> Now, the way I handle this is that the kernel allows _either_ a
>> (1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
>> height larger than 32 gets rejected with -EINVAL:
>>
>> +               /* maximum size is 64x32 or 32x64 */
>> +               if (w > 64 || h > 64 || (w > 32 && h > 32))
>> +                       return -ENOMEM;
>>
>> which is quite reasonable for this hardware.  However, there is no way
>> to export this from DRM - adding maximum cursor size properties wouldn't
>> really represent this.
>
> Until relatively recently, it had always been a device specific ddx
> which would somehow know what w/h values to pass to
> xf86_cursors_init().  I think for xf86-video-modesetting and wayland
> compositors, we probably need to come up with something better.
> Although off the top of my head I can't think of a good way to express
> 64x32 or 32x64.. that is a weird restriction.
>

We have a similar problem on our newer asics.  They require a 128x128
cursors so when you use xf86-video-modesetting, you end up with messed
up cursors because it assumes 64x64.  We could add cursor size to drm
caps.

Alex

> Anyways, right now I don't think there is anything different that I'd
> do in the kernel part.  I *suppose* you could try and figure out that
> all the alpha values are either 0x00 or 0xff and support 64x64 in that
> special case.  I'm not sure that is actually better.. it at least
> makes the failure vs success cases more confusing.  And unless you are
> rockin' the twm+xterm+xcalc setup, I doubt you will see many cursors
> with only 0x00 or 0xff alpha.
>
> So,
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
>
>> The only question is whether DRM should export some capabilities to
>> indicate what's possible with the cursor, so that X knows about this
>> quirk of this hardware.  Overall, I suspect that it's just rare and
>> not worth the effort.  I think it's just easier to pick one of these
>> and stick with it, and if we happen to encounter a cursor larger than
>> our chosen dimentions, XFree86 will already automatically fall back
>> to s/w cursor.
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
@ 2013-10-18 14:31                   ` Alex Deucher
  0 siblings, 0 replies; 86+ messages in thread
From: Alex Deucher @ 2013-10-18 14:31 UTC (permalink / raw)
  To: Rob Clark
  Cc: linux-arm-kernel, Russell King - ARM Linux, Jason Cooper,
	dri-devel, Sebastian Hesselbarth

On Thu, Oct 17, 2013 at 7:58 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Mon, Oct 7, 2013 at 8:50 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
>> On Mon, Oct 07, 2013 at 03:29:15PM +0300, Siarhei Siamashka wrote:
>>> On Mon, 7 Oct 2013 11:32:50 +0100
>>> Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
>>> > However, what you're suggesting is dangerous.  What do you do when you're
>>> > presented with a cursor which is 64x64 ?  Do you:
>>> >
>>> > (a) not display it
>>> > (b) crash the X server
>>> >
>>> > There isn't a fallback to using software cursors available in the X server
>>> > because there's no error reporting for a failed hardware cursor update.
>>>
>>> Hmm, maybe I'm missing something, but doesn't returning FALSE from
>>> UseHWCursorARGB just make the X server fallback to using a software
>>> cursor?
>>>
>>> https://github.com/ssvb/xf86-video-fbturbo/blob/689c08256555/src/sunxi_disp_hwcursor.c#L72
>>>
>>> I'm just doing this. And also have hooks to notify the DRI2 code about
>>> the status of the cursor. In the case if the hardware cursor is not
>>> enabled, hardware overlays can't be safely used with the software
>>> cursor and we need to fallback to CPU/GPU copy instead of zero-copy
>>> buffers swapping.
>>>
>>> And I fully agree that it is the responsibility of the kernel to expose
>>> as much of the hardware capabilities as possible (without compromising
>>> reliability and/or security).
>>
>> If you wish to go in below the level presented by
>> hw/xfree86/modes/xf86Cursors.c (as you are doing), then yes you can do
>> this, and it's something which maybe should be looked into.  You have a
>> good reason to do this (due to your hardware cursor being incompatible
>> with overlays).
>>
>> We have no such restriction - the only issue here is the "odd" size of
>> cursors.  Practically, choosing one or other of 64x32 or 32x64 works
>> fine for the cases I've seen.  I haven't seen any cursors which are
>> 64x64 yet.  I'm not saying they don't exist though!  I can imagine that
>> some accessibility options might want to display a large cursor.
>>
>> Now, the way I handle this is that the kernel allows _either_ a
>> (1 to 32)x(1 to 64) or (1 to 64)x(1 to 32) cursor.  Both width and
>> height larger than 32 gets rejected with -EINVAL:
>>
>> +               /* maximum size is 64x32 or 32x64 */
>> +               if (w > 64 || h > 64 || (w > 32 && h > 32))
>> +                       return -ENOMEM;
>>
>> which is quite reasonable for this hardware.  However, there is no way
>> to export this from DRM - adding maximum cursor size properties wouldn't
>> really represent this.
>
> Until relatively recently, it had always been a device specific ddx
> which would somehow know what w/h values to pass to
> xf86_cursors_init().  I think for xf86-video-modesetting and wayland
> compositors, we probably need to come up with something better.
> Although off the top of my head I can't think of a good way to express
> 64x32 or 32x64.. that is a weird restriction.
>

We have a similar problem on our newer asics.  They require a 128x128
cursors so when you use xf86-video-modesetting, you end up with messed
up cursors because it assumes 64x64.  We could add cursor size to drm
caps.

Alex

> Anyways, right now I don't think there is anything different that I'd
> do in the kernel part.  I *suppose* you could try and figure out that
> all the alpha values are either 0x00 or 0xff and support 64x64 in that
> special case.  I'm not sure that is actually better.. it at least
> makes the failure vs success cases more confusing.  And unless you are
> rockin' the twm+xterm+xcalc setup, I doubt you will see many cursors
> with only 0x00 or 0xff alpha.
>
> So,
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
>
>> The only question is whether DRM should export some capabilities to
>> indicate what's possible with the cursor, so that X knows about this
>> quirk of this hardware.  Overall, I suspect that it's just rare and
>> not worth the effort.  I think it's just easier to pick one of these
>> and stick with it, and if we happen to encounter a cursor larger than
>> our chosen dimentions, XFree86 will already automatically fall back
>> to s/w cursor.
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
  2013-10-07  8:59     ` Jean-Francois Moine
@ 2013-10-18 15:00       ` Rob Clark
  -1 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18 15:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 7, 2013 at 4:59 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sun, 06 Oct 2013 23:07:55 +0100
> 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 |    2 ++
>>  1 files changed, 2 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
>> index 60e8404..704040b5 100644
>> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
>> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
>> @@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
>>       buf[HB(0)] = 0x82;
>>       buf[HB(1)] = 0x02;
>>       buf[HB(2)] = 13;
>> +     buf[PB(1)] = 2; /* underscanned display */
>> +     buf[PB(3)] = 2 << 2; /* full range */
>>       buf[PB(4)] = drm_match_cea_mode(mode);
>>
>>       tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
>
> You should use the 'hdmi.h' constants which are meaningful enough:
>
>         buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
>         buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;
>

with the update to use hdmi.h const, this has my

Reviewed-by: Rob Clark <robdclark@gmail.com>

(and other ones I replied last night, s/s-o-b/r-b/)

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

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

* Re: [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display
@ 2013-10-18 15:00       ` Rob Clark
  0 siblings, 0 replies; 86+ messages in thread
From: Rob Clark @ 2013-10-18 15:00 UTC (permalink / raw)
  To: Jean-Francois Moine
  Cc: Jason Cooper, David Airlie, dri-devel, Daniel Vetter,
	Russell King, linux-arm-kernel, Sebastian Hesselbarth

On Mon, Oct 7, 2013 at 4:59 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> On Sun, 06 Oct 2013 23:07:55 +0100
> 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 |    2 ++
>>  1 files changed, 2 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
>> index 60e8404..704040b5 100644
>> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
>> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
>> @@ -549,6 +549,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
>>       buf[HB(0)] = 0x82;
>>       buf[HB(1)] = 0x02;
>>       buf[HB(2)] = 13;
>> +     buf[PB(1)] = 2; /* underscanned display */
>> +     buf[PB(3)] = 2 << 2; /* full range */
>>       buf[PB(4)] = drm_match_cea_mode(mode);
>>
>>       tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
>
> You should use the 'hdmi.h' constants which are meaningful enough:
>
>         buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
>         buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;
>

with the update to use hdmi.h const, this has my

Reviewed-by: Rob Clark <robdclark@gmail.com>

(and other ones I replied last night, s/s-o-b/r-b/)

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

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

* [GIT PULL] Armada DRM support
  2013-10-06 22:07 ` Russell King - ARM Linux
@ 2013-10-18 15:15   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-18 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

David,

Rob Clark has now reviewed this and has given his blessing for it to be
pulled for the coming merge window.

This adds support for the Armada 510 display subsystem found on the
Marvell Dove devices.  This IP is re-used across several different Marvell
SoCs with various tweaks, and this driver has been structured to allow
the other IPs to re-use the bulk of this code; further work in this area
is expected from interested parties.

This has been extensively tested on the SolidRun Cubox platform and
appears to work well there.

Please pull this driver for the next merge window.  Thanks.

The following changes since commit 15c03dd4859ab16f9212238f29dd315654aa94f6:

  Linux 3.12-rc3 (2013-09-29 15:02:38 -0700)

are available in the git repository at:

  git://ftp.arm.linux.org.uk/~rmk/linux-cubox.git drm-tda998x-3.12

which is commit: 585b691e2180e1501637050290292f77f5c30c7b

Russell King (5):
      DRM: Armada: Add Armada DRM driver
      DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
      drm/i2c: tda998x: set VIF for full range, underscanned display
      Merge branches 'drm-3.12' and 'tda998x-3.12' into drm-tda998x-3.12
      DRM: Armada: add support for drm tda19988 driver

 drivers/gpu/drm/Kconfig                 |    2 +
 drivers/gpu/drm/Makefile                |    1 +
 drivers/gpu/drm/armada/Kconfig          |   24 +
 drivers/gpu/drm/armada/Makefile         |    7 +
 drivers/gpu/drm/armada/armada_510.c     |   87 +++
 drivers/gpu/drm/armada/armada_crtc.c    | 1098 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   83 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++
 drivers/gpu/drm/armada/armada_drm.h     |  113 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  422 ++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 +++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++
 drivers/gpu/drm/armada/armada_gem.c     |  611 +++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  318 +++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 +++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 ++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 ++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 drivers/gpu/drm/i2c/tda998x_drv.c       |    3 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 25 files changed, 4319 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/Kconfig
 create mode 100644 drivers/gpu/drm/armada/Makefile
 create mode 100644 drivers/gpu/drm/armada/armada_510.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
 create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
 create mode 100644 drivers/gpu/drm/armada/armada_drm.h
 create mode 100644 drivers/gpu/drm/armada/armada_drv.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.h
 create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.h
 create mode 100644 drivers/gpu/drm/armada/armada_hw.h
 create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
 create mode 100644 drivers/gpu/drm/armada/armada_output.c
 create mode 100644 drivers/gpu/drm/armada/armada_output.h
 create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.h
 create mode 100644 include/uapi/drm/armada_drm.h

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

* [GIT PULL] Armada DRM support
@ 2013-10-18 15:15   ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-18 15:15 UTC (permalink / raw)
  To: David Airlie
  Cc: Jason Cooper, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

David,

Rob Clark has now reviewed this and has given his blessing for it to be
pulled for the coming merge window.

This adds support for the Armada 510 display subsystem found on the
Marvell Dove devices.  This IP is re-used across several different Marvell
SoCs with various tweaks, and this driver has been structured to allow
the other IPs to re-use the bulk of this code; further work in this area
is expected from interested parties.

This has been extensively tested on the SolidRun Cubox platform and
appears to work well there.

Please pull this driver for the next merge window.  Thanks.

The following changes since commit 15c03dd4859ab16f9212238f29dd315654aa94f6:

  Linux 3.12-rc3 (2013-09-29 15:02:38 -0700)

are available in the git repository at:

  git://ftp.arm.linux.org.uk/~rmk/linux-cubox.git drm-tda998x-3.12

which is commit: 585b691e2180e1501637050290292f77f5c30c7b

Russell King (5):
      DRM: Armada: Add Armada DRM driver
      DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors
      drm/i2c: tda998x: set VIF for full range, underscanned display
      Merge branches 'drm-3.12' and 'tda998x-3.12' into drm-tda998x-3.12
      DRM: Armada: add support for drm tda19988 driver

 drivers/gpu/drm/Kconfig                 |    2 +
 drivers/gpu/drm/Makefile                |    1 +
 drivers/gpu/drm/armada/Kconfig          |   24 +
 drivers/gpu/drm/armada/Makefile         |    7 +
 drivers/gpu/drm/armada/armada_510.c     |   87 +++
 drivers/gpu/drm/armada/armada_crtc.c    | 1098 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.h    |   83 +++
 drivers/gpu/drm/armada/armada_debugfs.c |  183 +++++
 drivers/gpu/drm/armada/armada_drm.h     |  113 ++++
 drivers/gpu/drm/armada/armada_drv.c     |  422 ++++++++++++
 drivers/gpu/drm/armada/armada_fb.c      |  170 +++++
 drivers/gpu/drm/armada/armada_fb.h      |   24 +
 drivers/gpu/drm/armada/armada_fbdev.c   |  202 ++++++
 drivers/gpu/drm/armada/armada_gem.c     |  611 +++++++++++++++++
 drivers/gpu/drm/armada/armada_gem.h     |   52 ++
 drivers/gpu/drm/armada/armada_hw.h      |  318 +++++++++
 drivers/gpu/drm/armada/armada_ioctlP.h  |   18 +
 drivers/gpu/drm/armada/armada_output.c  |  158 +++++
 drivers/gpu/drm/armada/armada_output.h  |   39 ++
 drivers/gpu/drm/armada/armada_overlay.c |  477 ++++++++++++++
 drivers/gpu/drm/armada/armada_slave.c   |  139 ++++
 drivers/gpu/drm/armada/armada_slave.h   |   26 +
 drivers/gpu/drm/i2c/tda998x_drv.c       |    3 +
 include/drm/drm_crtc.h                  |   17 +
 include/uapi/drm/armada_drm.h           |   45 ++
 25 files changed, 4319 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/armada/Kconfig
 create mode 100644 drivers/gpu/drm/armada/Makefile
 create mode 100644 drivers/gpu/drm/armada/armada_510.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.c
 create mode 100644 drivers/gpu/drm/armada/armada_crtc.h
 create mode 100644 drivers/gpu/drm/armada/armada_debugfs.c
 create mode 100644 drivers/gpu/drm/armada/armada_drm.h
 create mode 100644 drivers/gpu/drm/armada/armada_drv.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.c
 create mode 100644 drivers/gpu/drm/armada/armada_fb.h
 create mode 100644 drivers/gpu/drm/armada/armada_fbdev.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.c
 create mode 100644 drivers/gpu/drm/armada/armada_gem.h
 create mode 100644 drivers/gpu/drm/armada/armada_hw.h
 create mode 100644 drivers/gpu/drm/armada/armada_ioctlP.h
 create mode 100644 drivers/gpu/drm/armada/armada_output.c
 create mode 100644 drivers/gpu/drm/armada/armada_output.h
 create mode 100644 drivers/gpu/drm/armada/armada_overlay.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.c
 create mode 100644 drivers/gpu/drm/armada/armada_slave.h
 create mode 100644 include/uapi/drm/armada_drm.h

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

* [GIT PULL] Armada DRM support
  2013-10-18 15:15   ` Russell King - ARM Linux
@ 2013-10-22 13:36     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-22 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Oct 18, 2013 at 04:15:57PM +0100, Russell King - ARM Linux wrote:
> David,
> 
> Rob Clark has now reviewed this and has given his blessing for it to be
> pulled for the coming merge window.
> 
> This adds support for the Armada 510 display subsystem found on the
> Marvell Dove devices.  This IP is re-used across several different Marvell
> SoCs with various tweaks, and this driver has been structured to allow
> the other IPs to re-use the bulk of this code; further work in this area
> is expected from interested parties.
> 
> This has been extensively tested on the SolidRun Cubox platform and
> appears to work well there.

David,

The zero day tester found a problem with this building on some non-ARM.
Please add this fix to it, which Rob Clark has acked.  It's a small patch
to add a dependency on ARM.

Thanks.

The following changes since commit 585b691e2180e1501637050290292f77f5c30c7b:

  DRM: Armada: add support for drm tda19988 driver (2013-10-18 16:00:33 +0100)

are available in the git repository at:
  git://ftp.arm.linux.org.uk/~rmk/linux-cubox.git drm-tda998x-3.12-fixes

Russell King (1):
      DRM: Armada: depend on ARM

 drivers/gpu/drm/armada/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

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

* Re: [GIT PULL] Armada DRM support
@ 2013-10-22 13:36     ` Russell King - ARM Linux
  0 siblings, 0 replies; 86+ messages in thread
From: Russell King - ARM Linux @ 2013-10-22 13:36 UTC (permalink / raw)
  To: David Airlie
  Cc: Jason Cooper, dri-devel, Rob Clark, Daniel Vetter,
	linux-arm-kernel, Sebastian Hesselbarth

On Fri, Oct 18, 2013 at 04:15:57PM +0100, Russell King - ARM Linux wrote:
> David,
> 
> Rob Clark has now reviewed this and has given his blessing for it to be
> pulled for the coming merge window.
> 
> This adds support for the Armada 510 display subsystem found on the
> Marvell Dove devices.  This IP is re-used across several different Marvell
> SoCs with various tweaks, and this driver has been structured to allow
> the other IPs to re-use the bulk of this code; further work in this area
> is expected from interested parties.
> 
> This has been extensively tested on the SolidRun Cubox platform and
> appears to work well there.

David,

The zero day tester found a problem with this building on some non-ARM.
Please add this fix to it, which Rob Clark has acked.  It's a small patch
to add a dependency on ARM.

Thanks.

The following changes since commit 585b691e2180e1501637050290292f77f5c30c7b:

  DRM: Armada: add support for drm tda19988 driver (2013-10-18 16:00:33 +0100)

are available in the git repository at:
  git://ftp.arm.linux.org.uk/~rmk/linux-cubox.git drm-tda998x-3.12-fixes

Russell King (1):
      DRM: Armada: depend on ARM

 drivers/gpu/drm/armada/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

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

end of thread, other threads:[~2013-10-22 13:36 UTC | newest]

Thread overview: 86+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-06 22:07 [PATCH 0/5] Armada DRM stuff Russell King - ARM Linux
2013-10-06 22:07 ` Russell King - ARM Linux
2013-10-06 22:07 ` [PATCH 1/5] drm/i2c: tda998x: set VIF for full range, underscanned display Russell King
2013-10-06 22:07   ` Russell King
2013-10-07  8:59   ` Jean-Francois Moine
2013-10-07  8:59     ` Jean-Francois Moine
2013-10-18 15:00     ` Rob Clark
2013-10-18 15:00       ` Rob Clark
2013-10-06 22:08 ` [PATCH 2/5] DRM: Armada: Add Armada DRM driver Russell King
2013-10-06 22:08   ` Russell King
2013-10-10 21:25   ` Rob Clark
2013-10-10 21:25     ` Rob Clark
2013-10-10 21:59     ` Russell King - ARM Linux
2013-10-10 21:59       ` Russell King - ARM Linux
2013-10-10 22:23       ` Rob Clark
2013-10-10 22:23         ` Rob Clark
2013-10-10 22:53         ` Russell King - ARM Linux
2013-10-10 22:53           ` Russell King - ARM Linux
2013-10-11  0:10           ` Rob Clark
2013-10-11  0:10             ` Rob Clark
2013-10-06 22:09 ` [PATCH 3/5] DRM: Armada: Add support for ARGB 32x64 or 64x32 hardware cursors Russell King
2013-10-06 22:09   ` Russell King
2013-10-07  9:01   ` Jean-Francois Moine
2013-10-07  9:01     ` Jean-Francois Moine
2013-10-07  9:40     ` Russell King - ARM Linux
2013-10-07  9:40       ` Russell King - ARM Linux
2013-10-07 10:09       ` Jean-Francois Moine
2013-10-07 10:09         ` Jean-Francois Moine
2013-10-07 10:32         ` Russell King - ARM Linux
2013-10-07 10:32           ` Russell King - ARM Linux
2013-10-07 12:29           ` Siarhei Siamashka
2013-10-07 12:29             ` Siarhei Siamashka
2013-10-07 12:50             ` Russell King - ARM Linux
2013-10-07 12:50               ` Russell King - ARM Linux
2013-10-17 23:58               ` Rob Clark
2013-10-17 23:58                 ` Rob Clark
2013-10-18 14:31                 ` Alex Deucher
2013-10-18 14:31                   ` Alex Deucher
2013-10-06 22:10 ` [PATCH 4/5] DRM: Armada: start of MMP2/MMP3 support Russell King
2013-10-06 22:10   ` Russell King
2013-10-18  0:11   ` Rob Clark
2013-10-18  0:11     ` Rob Clark
2013-10-06 22:11 ` [PATCH 5/5] DRM: Armada: add support for drm tda19988 driver Russell King
2013-10-06 22:11   ` Russell King
2013-10-07  9:18   ` Jean-Francois Moine
2013-10-07  9:18     ` Jean-Francois Moine
2013-10-07  9:44     ` Russell King - ARM Linux
2013-10-07  9:44       ` Russell King - ARM Linux
2013-10-07 10:48       ` Jean-Francois Moine
2013-10-07 10:48         ` Jean-Francois Moine
2013-10-07 11:09         ` Russell King - ARM Linux
2013-10-07 11:09           ` Russell King - ARM Linux
2013-10-07 11:29           ` Sebastian Hesselbarth
2013-10-07 11:29             ` Sebastian Hesselbarth
2013-10-07 15:53             ` Mark Brown
2013-10-07 15:53               ` Mark Brown
2013-10-07 16:08               ` Sebastian Hesselbarth
2013-10-07 16:08                 ` Sebastian Hesselbarth
2013-10-07 17:05                 ` Mark Brown
2013-10-07 17:05                   ` Mark Brown
2013-10-07 12:03           ` Jean-Francois Moine
2013-10-07 12:03             ` Jean-Francois Moine
2013-10-07 12:36             ` Russell King - ARM Linux
2013-10-07 12:36               ` Russell King - ARM Linux
2013-10-07 14:59           ` Rob Clark
2013-10-07 14:59             ` Rob Clark
2013-10-08  9:19             ` Jean-Francois Moine
2013-10-08  9:19               ` Jean-Francois Moine
2013-10-08  9:49               ` Russell King - ARM Linux
2013-10-08  9:49                 ` Russell King - ARM Linux
2013-10-08 15:34                 ` Jean-Francois Moine
2013-10-08 15:34                   ` Jean-Francois Moine
2013-10-18  0:20                   ` Rob Clark
2013-10-18  0:20                     ` Rob Clark
2013-10-08 12:07               ` Rob Clark
2013-10-08 12:07                 ` Rob Clark
2013-10-07 21:47 ` [PATCH 0/5] Armada DRM stuff Sebastian Hesselbarth
2013-10-07 21:47   ` Sebastian Hesselbarth
2013-10-09 14:31   ` Russell King - ARM Linux
2013-10-09 14:31     ` Russell King - ARM Linux
2013-10-09 14:48     ` Rob Clark
2013-10-09 14:48       ` Rob Clark
2013-10-18 15:15 ` [GIT PULL] Armada DRM support Russell King - ARM Linux
2013-10-18 15:15   ` Russell King - ARM Linux
2013-10-22 13:36   ` Russell King - ARM Linux
2013-10-22 13:36     ` Russell King - ARM Linux

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.