# Base git commit: 1860e379875d
# (Linux 3.15)
# 
# Author:    Russell King (Tue 22 Apr 15:24:03 BST 2014)
# Committer: Russell King (Mon 30 Jun 17:26:30 BST 2014)
# 
#   drm/armada: make variant a CRTC thing
# 
#   Move the variant pointer into the armada_crtc structure, and update for
#   the resulting changes.
# 
#   Signed-off-by: Russell King
# 
#   99e9e31f8662e77cbcf4a161287ad4fb73c99fb5
#    drivers/gpu/drm/armada/armada_510.c  |  4 ++--
#    drivers/gpu/drm/armada/armada_crtc.c | 27 ++++++++++++---------------
#    drivers/gpu/drm/armada/armada_crtc.h |  5 ++++-
#    drivers/gpu/drm/armada/armada_drm.h  |  9 ++++-----
#    drivers/gpu/drm/armada/armada_drv.c  |  5 +++--
#    5 files changed, 25 insertions(+), 25 deletions(-)
# 
# Author:    Russell King (Tue 22 Apr 15:21:30 BST 2014)
# Committer: Russell King (Mon 30 Jun 17:26:27 BST 2014)
# 
#   drm/armada: move variant initialisation to CRTC init
# 
#   Move the variant initialisation entirely to the CRTC init function -
#   the variant support is really about the CRTC properties than the whole
#   system, and we want to treat each CRTC individually when we support DT.
# 
#   Signed-off-by: Russell King
# 
#   6a3ab05a2813ea762509860be02c253d16946588
#    drivers/gpu/drm/armada/armada_510.c  | 19 ++++++++-----------
#    drivers/gpu/drm/armada/armada_crtc.c |  2 +-
#    drivers/gpu/drm/armada/armada_crtc.h |  1 +
#    drivers/gpu/drm/armada/armada_drm.h  |  6 ++----
#    drivers/gpu/drm/armada/armada_drv.c  |  4 ----
#    5 files changed, 12 insertions(+), 20 deletions(-)
# 
# Author:    Russell King (Tue 22 Apr 11:02:23 BST 2014)
# Committer: Russell King (Mon 30 Jun 17:26:25 BST 2014)
# 
#   drm/armada: use number of CRTCs registered
# 
#   Use the number of CRTCs registered to size the vblank arrays rather than
#   our own count.  Number CRTCs using this as well.  This permits us to
#   register CRTCs as components in the near future rather than as part of a
#   single device.
# 
#   Signed-off-by: Russell King
# 
#   9469812681a0be57428abdb7e73663c52e164333
#    drivers/gpu/drm/armada/armada_crtc.c | 6 +++---
#    drivers/gpu/drm/armada/armada_crtc.h | 3 +--
#    drivers/gpu/drm/armada/armada_drv.c  | 4 ++--
#    3 files changed, 6 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Sat 26 Apr 15:19:38 BST 2014)
# Committer: Russell King (Mon 30 Jun 17:26:24 BST 2014)
# 
#   drm/armada: move IRQ handling into CRTC
# 
#   Signed-off-by: Russell King
# 
#   479fc4453fd3542327f3db1b57fc1af4ebba71ae
#    drivers/gpu/drm/armada/armada_crtc.c | 36 +++++++++++++++++--
#    drivers/gpu/drm/armada/armada_crtc.h |  4 +--
#    drivers/gpu/drm/armada/armada_drv.c  | 68 +++++-------------------------------
#    3 files changed, 45 insertions(+), 63 deletions(-)
# 
# Author:    Russell King (Sun 15 Jun 11:07:12 BST 2014)
# Committer: Russell King (Mon 30 Jun 17:26:23 BST 2014)
# 
#   drm: add of_graph endpoint helper to find possible CRTCs
# 
#   Add a helper to allow encoders to find their possible CRTCs from the
#   OF graph without having to re-implement this functionality.  We add a
#   device_node to drm_crtc which corresponds with the port node in the
#   DT description of the CRTC device.
# 
#   We can then scan the DRM device list for CRTCs to find their index,
#   matching the appropriate CRTC using the port device_node, thus building
#   up the possible CRTC mask.
# 
#   Signed-off-by: Russell King
# 
#   b77348ce1b97adeda075ab7bc7a1acbb85058854
#    drivers/gpu/drm/Makefile |  1 +
#    drivers/gpu/drm/drm_of.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
#    include/drm/drm_crtc.h   |  2 ++
#    include/drm/drm_of.h     | 18 ++++++++++++++
#    4 files changed, 82 insertions(+)
#    create mode 100644 drivers/gpu/drm/drm_of.c
#    create mode 100644 include/drm/drm_of.h
# 
# Author:    Russell King (Sun 27 Oct 15:27:27 GMT 2013)
# Committer: Russell King (Sun 15 Jun 14:17:17 BST 2014)
# 
#   drm: ensure fbdev helper arrays are appropriately dimensioned
# 
#   If the number of connectors changes, then it is possible for the fbdev
#   helper to overrun/underrun some arrays which it allocates.  It allocates
#   these arrays based on mode_config.num_connector but then walks lists
#   using fb_helper->connector_count to limit the array index.  This can
#   lead to writes off the end of the arrays.
# 
#   Fix this by allocating the arrays using fb_helper->connector_count.
# 
#   A similar thing exists for some of the CRTC arrays.  For these, use
#   fb_helper->crtc_count.
# 
#   Signed-off-by: Russell King
# 
#   f36f5b89d4a7c6fb40933ec1dea81501349b2c65
#    drivers/gpu/drm/drm_fb_helper.c | 14 +++++++-------
#    1 file changed, 7 insertions(+), 7 deletions(-)
# 
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 48e38ba22783..4c8d22e2c55e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 drm-$(CONFIG_PCI) += ati_pcigart.o
 drm-$(CONFIG_DRM_PANEL) += drm_panel.o
+drm-$(CONFIG_OF) += drm_of.o
 
 drm-usb-y   := drm_usb.o
 
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
index 59948eff6095..1bcf1a8c1915 100644
--- a/drivers/gpu/drm/armada/armada_510.c
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -15,20 +15,19 @@
 #include "armada_drm.h"
 #include "armada_hw.h"
 
-static int armada510_init(struct armada_private *priv, struct device *dev)
+static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
 {
-	priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+	struct clk *clk;
 
-	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
-		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+	clk = devm_clk_get(dev, "ext_ref_clk_1");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
 
-	return PTR_RET(priv->extclk[0]);
-}
+	dcrtc->extclk[0] = clk;
 
-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;
 }
 
@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc)
 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];
+	struct clk *clk = dcrtc->extclk[0];
 	int ret;
 
 	if (dcrtc->num == 1)
@@ -81,7 +79,6 @@ 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,
+	.init = armada510_crtc_init,
+	.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 81c34f949dfc..afa497489000 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -332,24 +332,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc)
 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 &&
+	if (!dcrtc->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);
+	ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL);
 	if (ret)
 		return false;
 
 	return true;
 }
 
-void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 {
 	struct armada_vbl_event *e, *n;
 	void __iomem *base = dcrtc->base;
@@ -410,6 +409,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	}
 }
 
+static irqreturn_t armada_drm_irq(int irq, void *arg)
+{
+	struct armada_crtc *dcrtc = arg;
+	u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	/*
+	 * This is rediculous - rather than writing bits to clear, we
+	 * have to set the actual status register value.  This is racy.
+	 */
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	/* Mask out those interrupts we haven't enabled */
+	v = stat & dcrtc->irq_ena;
+
+	if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+		armada_drm_crtc_irq(dcrtc, stat);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
 /* These are locked by dev->vbl_lock */
 void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
 {
@@ -470,7 +490,6 @@ 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;
@@ -515,7 +534,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	}
 
 	/* Now compute the divider for real */
-	priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+	dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
 
 	/* Ensure graphic fifo is enabled */
 	armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
@@ -537,7 +556,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	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 |
-		priv->variant->spu_adv_reg;
+		dcrtc->variant->spu_adv_reg;
 
 	if (interlaced) {
 		/* Odd interlaced frame */
@@ -546,7 +565,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 		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 |
-			priv->variant->spu_adv_reg;
+			dcrtc->variant->spu_adv_reg;
 	} else {
 		dcrtc->v[0] = dcrtc->v[1];
 	}
@@ -561,7 +580,7 @@ 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 (dcrtc->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);
@@ -805,12 +824,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
 {
 	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)
+	if (!dcrtc->variant->has_spu_adv_reg)
 		return -ENXIO;
 
 	if (handle && w > 0 && h > 0) {
@@ -858,11 +876,10 @@ 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)
+	if (!dcrtc->variant->has_spu_adv_reg)
 		return -EFAULT;
 
 	mutex_lock(&dev->struct_mutex);
@@ -888,6 +905,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
 	if (!IS_ERR(dcrtc->clk))
 		clk_disable_unprepare(dcrtc->clk);
 
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+
 	kfree(dcrtc);
 }
 
@@ -1027,8 +1046,8 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
 	return 0;
 }
 
-int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
-	struct resource *res)
+int armada_drm_crtc_create(struct drm_device *dev, struct resource *res,
+	int irq, const struct armada_variant *variant)
 {
 	struct armada_private *priv = dev->dev_private;
 	struct armada_crtc *dcrtc;
@@ -1051,8 +1070,9 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 		return -ENOMEM;
 	}
 
+	dcrtc->variant = variant;
 	dcrtc->base = base;
-	dcrtc->num = num;
+	dcrtc->num = dev->mode_config.num_crtc;
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
@@ -1074,9 +1094,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 		       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);
+	writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
+			       dcrtc);
+	if (ret < 0) {
+		kfree(dcrtc);
+		return ret;
+	}
 
-	if (priv->variant->crtc_init) {
-		ret = priv->variant->crtc_init(dcrtc);
+	if (dcrtc->variant->init) {
+		ret = dcrtc->variant->init(dcrtc, dev->dev);
 		if (ret) {
 			kfree(dcrtc);
 			return ret;
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 9c10a07e7492..73efcfcdf814 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -32,12 +32,15 @@ struct armada_regs {
 	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
 
 struct armada_frame_work;
+struct armada_variant;
 
 struct armada_crtc {
 	struct drm_crtc		crtc;
+	const struct armada_variant *variant;
 	unsigned		num;
 	void __iomem		*base;
 	struct clk		*clk;
+	struct clk		*extclk[2];
 	struct {
 		uint32_t	spu_v_h_total;
 		uint32_t	spu_v_porch;
@@ -72,10 +75,10 @@ struct armada_crtc {
 };
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 
-int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+int armada_drm_crtc_create(struct drm_device *, struct resource *, int,
+	const struct armada_variant *);
 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 *);
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index a72cae03b99b..ea63c6c7c66f 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
 struct armada_private;
 
 struct armada_variant {
-	bool	has_spu_adv_reg;
+	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 *,
-				  const struct drm_display_mode *,
-				  uint32_t *);
+	int (*init)(struct armada_crtc *, struct device *);
+	int (*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;
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 32982da82694..2f61d63d7657 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -85,6 +85,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
 static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 {
 	const struct platform_device_id *id;
+	const struct armada_variant *variant;
 	struct armada_private *priv;
 	struct resource *res[ARRAY_SIZE(priv->dcrtc)];
 	struct resource *mem = NULL;
@@ -128,11 +129,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 	if (!id)
 		return -ENXIO;
 
-	priv->variant = (struct armada_variant *)id->driver_data;
-
-	ret = priv->variant->init(priv, dev->dev);
-	if (ret)
-		return ret;
+	variant = (const struct armada_variant *)id->driver_data;
 
 	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
 	INIT_KFIFO(priv->fb_unref);
@@ -155,10 +152,16 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 
 	/* Create all LCD controllers */
 	for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+		int irq;
+
 		if (!res[n])
 			break;
 
-		ret = armada_drm_crtc_create(dev, n, res[n]);
+		irq = platform_get_irq(dev->platformdev, n);
+		if (irq < 0)
+			goto err_kms;
+
+		ret = armada_drm_crtc_create(dev, res[n], irq, variant);
 		if (ret)
 			goto err_kms;
 	}
@@ -169,11 +172,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 		goto err_kms;
 #endif
 
-	ret = drm_vblank_init(dev, n);
-	if (ret)
-		goto err_kms;
-
-	ret = drm_irq_install(dev);
+	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
 	if (ret)
 		goto err_kms;
 
@@ -181,14 +180,12 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 
 	ret = armada_fbdev_init(dev);
 	if (ret)
-		goto err_irq;
+		goto err_kms;
 
 	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);
@@ -203,7 +200,6 @@ static int armada_drm_unload(struct drm_device *dev)
 
 	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);
@@ -259,52 +255,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
 	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),
@@ -340,9 +290,6 @@ static struct drm_driver armada_drm_driver = {
 	.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,
@@ -362,7 +309,7 @@ static struct drm_driver armada_drm_driver = {
 	.desc			= "Armada SoC DRM",
 	.date			= "20120730",
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
-				  DRIVER_HAVE_IRQ | DRIVER_PRIME,
+				  DRIVER_PRIME,
 	.ioctls			= armada_ioctls,
 	.fops			= &armada_drm_fops,
 };
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 04d3fd3658f3..5665338aa56c 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1375,7 +1375,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 	if (modes[n] == NULL)
 		return best_score;
 
-	crtcs = kzalloc(dev->mode_config.num_connector *
+	crtcs = kcalloc(fb_helper->crtc_count,
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 	if (!crtcs)
 		return best_score;
@@ -1421,7 +1421,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 		if (score > best_score) {
 			best_score = score;
 			memcpy(best_crtcs, crtcs,
-			       dev->mode_config.num_connector *
+			       fb_helper->crtc_count *
 			       sizeof(struct drm_fb_helper_crtc *));
 		}
 	}
@@ -1445,11 +1445,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	width = dev->mode_config.max_width;
 	height = dev->mode_config.max_height;
 
-	crtcs = kcalloc(dev->mode_config.num_connector,
+	crtcs = kcalloc(fb_helper->connector_count,
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
-	modes = kcalloc(dev->mode_config.num_connector,
+	modes = kcalloc(fb_helper->connector_count,
 			sizeof(struct drm_display_mode *), GFP_KERNEL);
-	enabled = kcalloc(dev->mode_config.num_connector,
+	enabled = kcalloc(fb_helper->connector_count,
 			  sizeof(bool), GFP_KERNEL);
 	if (!crtcs || !modes || !enabled) {
 		DRM_ERROR("Memory allocation failed\n");
@@ -1462,8 +1462,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	if (!(fb_helper->funcs->initial_config &&
 	      fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
 					       enabled, width, height))) {
-		memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
-		memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
+		memset(modes, 0, fb_helper->connector_count * sizeof(modes[0]));
+		memset(crtcs, 0, fb_helper->connector_count * sizeof(crtcs[0]));
 
 		if (!drm_target_cloned(fb_helper,
 				       modes, enabled, width, height) &&
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
new file mode 100644
index 000000000000..bbb60981a2f4
--- /dev/null
+++ b/drivers/gpu/drm/drm_of.c
@@ -0,0 +1,61 @@
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/of_graph.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_of.h>
+
+/**
+ * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
+ * @dev: DRM device
+ * @port: port OF node
+ *
+ * Given a port OF node, return the possible mask of the corresponding
+ * CRTC within a device's list of CRTCs.  Returns zero if not found.
+ */
+static uint32_t drm_crtc_port_mask(struct drm_device *dev,
+				   struct device_node *port)
+{
+	unsigned int index = 0;
+	struct drm_crtc *tmp;
+
+	list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+		if (tmp->port == port)
+			return 1 << index;
+
+		index++;
+	}
+
+	return 0;
+}
+
+/**
+ * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
+ * @dev: DRM device
+ * @port: encoder port to scan for endpoints
+ *
+ * Scan all endpoints attached to a port, locate their attached CRTCs,
+ * and generate the DRM mask of CRTCs which may be attached to this
+ * encoder.
+ */
+uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
+				    struct device_node *port)
+{
+	struct device_node *remote_port, *ep = NULL;
+	uint32_t possible_crtcs = 0;
+
+	do {
+		ep = of_graph_get_next_endpoint(port, ep);
+		if (!ep)
+			break;
+
+		remote_port = of_graph_get_remote_port(ep);
+		if (!remote_port)
+			return 0;
+
+		possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
+	} while (1);
+
+	return possible_crtcs;
+}
+EXPORT_SYMBOL(drm_of_find_possible_crtcs);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index e55fccbe7c42..a394cdfac651 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -40,6 +40,7 @@ struct drm_framebuffer;
 struct drm_object_properties;
 struct drm_file;
 struct drm_clip_rect;
+struct device_node;
 
 #define DRM_MODE_OBJECT_CRTC 0xcccccccc
 #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
@@ -294,6 +295,7 @@ struct drm_crtc_funcs {
  */
 struct drm_crtc {
 	struct drm_device *dev;
+	struct device_node *port;
 	struct list_head head;
 
 	/**
diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h
new file mode 100644
index 000000000000..2441f7112074
--- /dev/null
+++ b/include/drm/drm_of.h
@@ -0,0 +1,18 @@
+#ifndef __DRM_OF_H__
+#define __DRM_OF_H__
+
+struct drm_device;
+struct device_node;
+
+#ifdef CONFIG_OF
+extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
+					   struct device_node *port);
+#else
+static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
+						  struct device_node *port)
+{
+	return 0;
+}
+#endif
+
+#endif /* __DRM_OF_H__ */
