Merge tag 'omapdrm-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into drm-next

omapdrm changes for 4.1

* universal plane support
* refactoring to prepare work atomic modesetting work
* a lot of small fixes

* tag 'omapdrm-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (36 commits)
  drm/omap: tiler: add hibernation callback
  drm/omap: add hibernation callbacks
  drm/omap: keep ref to old_fb
  drm/omap: fix race conditon in DMM
  drm/omap: fix race condition with dev->obj_list
  drm/omap: do not use BUG_ON(!spin_is_locked(x))
  drm/omap: only ignore DIGIT SYNC LOST for TV output
  drm/omap: fix race with error_irq
  drm/omap: use DRM_ERROR_RATELIMITED() for error irqs
  drm/omap: stop connector polling during suspend
  drm/omap: remove dummy PM functions
  drm/omap: tiler: fix race condition with engine->async
  drm/omap: fix plane's channel selection
  drm/omap: fix TILER on OMAP5
  drm/omap: handle incompatible buffer stride and pixel size
  drm/omap: fix error handling in omap_framebuffer_create()
  drm/omap: fix operation without fbdev
  drm/omap: add a comment why locking is missing
  drm/omap: add pin refcounting to omap_framebuffer
  drm/omap: clear omap_obj->paddr in omap_gem_put_paddr()
  ...
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index a94b11f7..b41965c 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -271,18 +271,6 @@
 	.best_encoder = omap_connector_attached_encoder,
 };
 
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-void omap_connector_flush(struct drm_connector *connector,
-		int x, int y, int w, int h)
-{
-	struct omap_connector *omap_connector = to_omap_connector(connector);
-
-	/* TODO: enable when supported in dss */
-	VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
-}
-
 /* initialize connector */
 struct drm_connector *omap_connector_init(struct drm_device *dev,
 		int connector_type, struct omap_dss_device *dssdev,
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index b0566a1..f456544 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -28,7 +28,6 @@
 
 struct omap_crtc {
 	struct drm_crtc base;
-	struct drm_plane *plane;
 
 	const char *name;
 	int pipe;
@@ -46,7 +45,6 @@
 
 	struct omap_video_timings timings;
 	bool enabled;
-	bool full_update;
 
 	struct omap_drm_apply apply;
 
@@ -74,8 +72,14 @@
 	 * XXX maybe fold into apply_work??
 	 */
 	struct work_struct page_flip_work;
+
+	bool ignore_digit_sync_lost;
 };
 
+/* -----------------------------------------------------------------------------
+ * Helper Functions
+ */
+
 uint32_t pipe2vbl(struct drm_crtc *crtc)
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@@ -83,6 +87,22 @@
 	return dispc_mgr_get_vsync_irq(omap_crtc->channel);
 }
 
+const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	return &omap_crtc->timings;
+}
+
+enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	return omap_crtc->channel;
+}
+
+/* -----------------------------------------------------------------------------
+ * DSS Manager Functions
+ */
+
 /*
  * Manager-ops, callbacks from output when they need to configure
  * the upstream part of the video pipe.
@@ -122,7 +142,63 @@
 {
 }
 
-static void set_enabled(struct drm_crtc *crtc, bool enable);
+/* Called only from CRTC pre_apply and suspend/resume handlers. */
+static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
+{
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	enum omap_channel channel = omap_crtc->channel;
+	struct omap_irq_wait *wait;
+	u32 framedone_irq, vsync_irq;
+	int ret;
+
+	if (dispc_mgr_is_enabled(channel) == enable)
+		return;
+
+	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+		/*
+		 * Digit output produces some sync lost interrupts during the
+		 * first frame when enabling, so we need to ignore those.
+		 */
+		omap_crtc->ignore_digit_sync_lost = true;
+	}
+
+	framedone_irq = dispc_mgr_get_framedone_irq(channel);
+	vsync_irq = dispc_mgr_get_vsync_irq(channel);
+
+	if (enable) {
+		wait = omap_irq_wait_init(dev, vsync_irq, 1);
+	} else {
+		/*
+		 * When we disable the digit output, we need to wait for
+		 * FRAMEDONE to know that DISPC has finished with the output.
+		 *
+		 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
+		 * that case we need to use vsync interrupt, and wait for both
+		 * even and odd frames.
+		 */
+
+		if (framedone_irq)
+			wait = omap_irq_wait_init(dev, framedone_irq, 1);
+		else
+			wait = omap_irq_wait_init(dev, vsync_irq, 2);
+	}
+
+	dispc_mgr_enable(channel, enable);
+
+	ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
+	if (ret) {
+		dev_err(dev->dev, "%s: timeout waiting for %s\n",
+				omap_crtc->name, enable ? "enable" : "disable");
+	}
+
+	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+		omap_crtc->ignore_digit_sync_lost = false;
+		/* make sure the irq handler sees the value above */
+		mb();
+	}
+}
+
 
 static int omap_crtc_enable(struct omap_overlay_manager *mgr)
 {
@@ -131,7 +207,7 @@
 	dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
 	dispc_mgr_set_timings(omap_crtc->channel,
 			&omap_crtc->timings);
-	set_enabled(&omap_crtc->base, true);
+	omap_crtc_set_enabled(&omap_crtc->base, true);
 
 	return 0;
 }
@@ -140,7 +216,7 @@
 {
 	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
 
-	set_enabled(&omap_crtc->base, false);
+	omap_crtc_set_enabled(&omap_crtc->base, false);
 }
 
 static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
@@ -149,7 +225,6 @@
 	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
 	DBG("%s", omap_crtc->name);
 	omap_crtc->timings = *timings;
-	omap_crtc->full_update = true;
 }
 
 static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
@@ -174,264 +249,33 @@
 }
 
 static const struct dss_mgr_ops mgr_ops = {
-		.connect = omap_crtc_connect,
-		.disconnect = omap_crtc_disconnect,
-		.start_update = omap_crtc_start_update,
-		.enable = omap_crtc_enable,
-		.disable = omap_crtc_disable,
-		.set_timings = omap_crtc_set_timings,
-		.set_lcd_config = omap_crtc_set_lcd_config,
-		.register_framedone_handler = omap_crtc_register_framedone_handler,
-		.unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
+	.connect = omap_crtc_connect,
+	.disconnect = omap_crtc_disconnect,
+	.start_update = omap_crtc_start_update,
+	.enable = omap_crtc_enable,
+	.disable = omap_crtc_disable,
+	.set_timings = omap_crtc_set_timings,
+	.set_lcd_config = omap_crtc_set_lcd_config,
+	.register_framedone_handler = omap_crtc_register_framedone_handler,
+	.unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
 };
 
-/*
- * CRTC funcs:
+/* -----------------------------------------------------------------------------
+ * Apply Logic
  */
 
-static void omap_crtc_destroy(struct drm_crtc *crtc)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-
-	DBG("%s", omap_crtc->name);
-
-	WARN_ON(omap_crtc->apply_irq.registered);
-	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
-
-	drm_crtc_cleanup(crtc);
-
-	kfree(omap_crtc);
-}
-
-static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-	struct omap_drm_private *priv = crtc->dev->dev_private;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	bool enabled = (mode == DRM_MODE_DPMS_ON);
-	int i;
-
-	DBG("%s: %d", omap_crtc->name, mode);
-
-	if (enabled != omap_crtc->enabled) {
-		omap_crtc->enabled = enabled;
-		omap_crtc->full_update = true;
-		omap_crtc_apply(crtc, &omap_crtc->apply);
-
-		/* also enable our private plane: */
-		WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
-
-		/* and any attached overlay planes: */
-		for (i = 0; i < priv->num_planes; i++) {
-			struct drm_plane *plane = priv->planes[i];
-			if (plane->crtc == crtc)
-				WARN_ON(omap_plane_dpms(plane, mode));
-		}
-	}
-}
-
-static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
-		const struct drm_display_mode *mode,
-		struct drm_display_mode *adjusted_mode)
-{
-	return true;
-}
-
-static int omap_crtc_mode_set(struct drm_crtc *crtc,
-		struct drm_display_mode *mode,
-		struct drm_display_mode *adjusted_mode,
-		int x, int y,
-		struct drm_framebuffer *old_fb)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-
-	mode = adjusted_mode;
-
-	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
-			omap_crtc->name, mode->base.id, mode->name,
-			mode->vrefresh, mode->clock,
-			mode->hdisplay, mode->hsync_start,
-			mode->hsync_end, mode->htotal,
-			mode->vdisplay, mode->vsync_start,
-			mode->vsync_end, mode->vtotal,
-			mode->type, mode->flags);
-
-	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
-	omap_crtc->full_update = true;
-
-	return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			x << 16, y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			NULL, NULL);
-}
-
-static void omap_crtc_prepare(struct drm_crtc *crtc)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	DBG("%s", omap_crtc->name);
-	omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-}
-
-static void omap_crtc_commit(struct drm_crtc *crtc)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	DBG("%s", omap_crtc->name);
-	omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-}
-
-static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-		struct drm_framebuffer *old_fb)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct drm_plane *plane = omap_crtc->plane;
-	struct drm_display_mode *mode = &crtc->mode;
-
-	return omap_plane_mode_set(plane, crtc, crtc->primary->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			x << 16, y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			NULL, NULL);
-}
-
-static void vblank_cb(void *arg)
-{
-	struct drm_crtc *crtc = arg;
-	struct drm_device *dev = crtc->dev;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-
-	/* wakeup userspace */
-	if (omap_crtc->event)
-		drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
-
-	omap_crtc->event = NULL;
-	omap_crtc->old_fb = NULL;
-
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-static void page_flip_worker(struct work_struct *work)
-{
-	struct omap_crtc *omap_crtc =
-			container_of(work, struct omap_crtc, page_flip_work);
-	struct drm_crtc *crtc = &omap_crtc->base;
-	struct drm_display_mode *mode = &crtc->mode;
-	struct drm_gem_object *bo;
-
-	drm_modeset_lock(&crtc->mutex, NULL);
-	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			crtc->x << 16, crtc->y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			vblank_cb, crtc);
-	drm_modeset_unlock(&crtc->mutex);
-
-	bo = omap_framebuffer_bo(crtc->primary->fb, 0);
-	drm_gem_object_unreference_unlocked(bo);
-}
-
-static void page_flip_cb(void *arg)
-{
-	struct drm_crtc *crtc = arg;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct omap_drm_private *priv = crtc->dev->dev_private;
-
-	/* avoid assumptions about what ctxt we are called from: */
-	queue_work(priv->wq, &omap_crtc->page_flip_work);
-}
-
-static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
-		 struct drm_framebuffer *fb,
-		 struct drm_pending_vblank_event *event,
-		 uint32_t page_flip_flags)
-{
-	struct drm_device *dev = crtc->dev;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct drm_plane *primary = crtc->primary;
-	struct drm_gem_object *bo;
-	unsigned long flags;
-
-	DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1,
-			fb->base.id, event);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-
-	if (omap_crtc->old_fb) {
-		spin_unlock_irqrestore(&dev->event_lock, flags);
-		dev_err(dev->dev, "already a pending flip\n");
-		return -EINVAL;
-	}
-
-	omap_crtc->event = event;
-	omap_crtc->old_fb = primary->fb = fb;
-
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	/*
-	 * Hold a reference temporarily until the crtc is updated
-	 * and takes the reference to the bo.  This avoids it
-	 * getting freed from under us:
-	 */
-	bo = omap_framebuffer_bo(fb, 0);
-	drm_gem_object_reference(bo);
-
-	omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
-
-	return 0;
-}
-
-static int omap_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct omap_drm_private *priv = crtc->dev->dev_private;
-
-	if (property == priv->rotation_prop) {
-		crtc->invert_dimensions =
-				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
-	}
-
-	return omap_plane_set_property(omap_crtc->plane, property, val);
-}
-
-static const struct drm_crtc_funcs omap_crtc_funcs = {
-	.set_config = drm_crtc_helper_set_config,
-	.destroy = omap_crtc_destroy,
-	.page_flip = omap_crtc_page_flip_locked,
-	.set_property = omap_crtc_set_property,
-};
-
-static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
-	.dpms = omap_crtc_dpms,
-	.mode_fixup = omap_crtc_mode_fixup,
-	.mode_set = omap_crtc_mode_set,
-	.prepare = omap_crtc_prepare,
-	.commit = omap_crtc_commit,
-	.mode_set_base = omap_crtc_mode_set_base,
-};
-
-const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	return &omap_crtc->timings;
-}
-
-enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
-{
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	return omap_crtc->channel;
-}
-
 static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 {
 	struct omap_crtc *omap_crtc =
 			container_of(irq, struct omap_crtc, error_irq);
-	struct drm_crtc *crtc = &omap_crtc->base;
-	DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
-	/* avoid getting in a flood, unregister the irq until next vblank */
-	__omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+
+	if (omap_crtc->ignore_digit_sync_lost) {
+		irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
+		if (!irqstatus)
+			return;
+	}
+
+	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
 }
 
 static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
@@ -440,9 +284,6 @@
 			container_of(irq, struct omap_crtc, apply_irq);
 	struct drm_crtc *crtc = &omap_crtc->base;
 
-	if (!omap_crtc->error_irq.registered)
-		__omap_irq_register(crtc->dev, &omap_crtc->error_irq);
-
 	if (!dispc_mgr_go_busy(omap_crtc->channel)) {
 		struct omap_drm_private *priv =
 				crtc->dev->dev_private;
@@ -501,8 +342,8 @@
 		DBG("%s: GO", omap_crtc->name);
 
 		if (dispc_mgr_is_enabled(channel)) {
-			omap_irq_register(dev, &omap_crtc->apply_irq);
 			dispc_mgr_go(channel);
+			omap_irq_register(dev, &omap_crtc->apply_irq);
 		} else {
 			struct omap_drm_private *priv = dev->dev_private;
 			queue_work(priv->wq, &omap_crtc->apply_work);
@@ -541,75 +382,21 @@
 	return 0;
 }
 
-/* called only from apply */
-static void set_enabled(struct drm_crtc *crtc, bool enable)
-{
-	struct drm_device *dev = crtc->dev;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	enum omap_channel channel = omap_crtc->channel;
-	struct omap_irq_wait *wait;
-	u32 framedone_irq, vsync_irq;
-	int ret;
-
-	if (dispc_mgr_is_enabled(channel) == enable)
-		return;
-
-	/*
-	 * Digit output produces some sync lost interrupts during the first
-	 * frame when enabling, so we need to ignore those.
-	 */
-	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
-
-	framedone_irq = dispc_mgr_get_framedone_irq(channel);
-	vsync_irq = dispc_mgr_get_vsync_irq(channel);
-
-	if (enable) {
-		wait = omap_irq_wait_init(dev, vsync_irq, 1);
-	} else {
-		/*
-		 * When we disable the digit output, we need to wait for
-		 * FRAMEDONE to know that DISPC has finished with the output.
-		 *
-		 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
-		 * that case we need to use vsync interrupt, and wait for both
-		 * even and odd frames.
-		 */
-
-		if (framedone_irq)
-			wait = omap_irq_wait_init(dev, framedone_irq, 1);
-		else
-			wait = omap_irq_wait_init(dev, vsync_irq, 2);
-	}
-
-	dispc_mgr_enable(channel, enable);
-
-	ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
-	if (ret) {
-		dev_err(dev->dev, "%s: timeout waiting for %s\n",
-				omap_crtc->name, enable ? "enable" : "disable");
-	}
-
-	omap_irq_register(crtc->dev, &omap_crtc->error_irq);
-}
-
 static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
 {
 	struct omap_crtc *omap_crtc =
 			container_of(apply, struct omap_crtc, apply);
 	struct drm_crtc *crtc = &omap_crtc->base;
+	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct drm_encoder *encoder = NULL;
+	unsigned int i;
 
-	DBG("%s: enabled=%d, full=%d", omap_crtc->name,
-			omap_crtc->enabled, omap_crtc->full_update);
+	DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled);
 
-	if (omap_crtc->full_update) {
-		struct omap_drm_private *priv = crtc->dev->dev_private;
-		int i;
-		for (i = 0; i < priv->num_encoders; i++) {
-			if (priv->encoders[i]->crtc == crtc) {
-				encoder = priv->encoders[i];
-				break;
-			}
+	for (i = 0; i < priv->num_encoders; i++) {
+		if (priv->encoders[i]->crtc == crtc) {
+			encoder = priv->encoders[i];
+			break;
 		}
 	}
 
@@ -629,8 +416,6 @@
 			omap_encoder_set_enabled(encoder, true);
 		}
 	}
-
-	omap_crtc->full_update = false;
 }
 
 static void omap_crtc_post_apply(struct omap_drm_apply *apply)
@@ -657,11 +442,245 @@
 	}
 }
 
+/* -----------------------------------------------------------------------------
+ * CRTC Functions
+ */
+
+static void omap_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	DBG("%s", omap_crtc->name);
+
+	WARN_ON(omap_crtc->apply_irq.registered);
+	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+
+	drm_crtc_cleanup(crtc);
+
+	kfree(omap_crtc);
+}
+
+static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct omap_drm_private *priv = crtc->dev->dev_private;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	bool enabled = (mode == DRM_MODE_DPMS_ON);
+	int i;
+
+	DBG("%s: %d", omap_crtc->name, mode);
+
+	if (enabled != omap_crtc->enabled) {
+		omap_crtc->enabled = enabled;
+		omap_crtc_apply(crtc, &omap_crtc->apply);
+
+		/* Enable/disable all planes associated with the CRTC. */
+		for (i = 0; i < priv->num_planes; i++) {
+			struct drm_plane *plane = priv->planes[i];
+			if (plane->crtc == crtc)
+				WARN_ON(omap_plane_set_enable(plane, enabled));
+		}
+	}
+}
+
+static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
+		const struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static int omap_crtc_mode_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode,
+		int x, int y,
+		struct drm_framebuffer *old_fb)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	mode = adjusted_mode;
+
+	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+			omap_crtc->name, mode->base.id, mode->name,
+			mode->vrefresh, mode->clock,
+			mode->hdisplay, mode->hsync_start,
+			mode->hsync_end, mode->htotal,
+			mode->vdisplay, mode->vsync_start,
+			mode->vsync_end, mode->vtotal,
+			mode->type, mode->flags);
+
+	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
+
+	/*
+	 * The primary plane CRTC can be reset if the plane is disabled directly
+	 * through the universal plane API. Set it again here.
+	 */
+	crtc->primary->crtc = crtc;
+
+	return omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
+				   0, 0, mode->hdisplay, mode->vdisplay,
+				   x, y, mode->hdisplay, mode->vdisplay,
+				   NULL, NULL);
+}
+
+static void omap_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->name);
+	omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_crtc_commit(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->name);
+	omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+		struct drm_framebuffer *old_fb)
+{
+	struct drm_plane *plane = crtc->primary;
+	struct drm_display_mode *mode = &crtc->mode;
+
+	return omap_plane_mode_set(plane, crtc, crtc->primary->fb,
+				   0, 0, mode->hdisplay, mode->vdisplay,
+				   x, y, mode->hdisplay, mode->vdisplay,
+				   NULL, NULL);
+}
+
+static void vblank_cb(void *arg)
+{
+	struct drm_crtc *crtc = arg;
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	unsigned long flags;
+	struct drm_framebuffer *fb;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/* wakeup userspace */
+	if (omap_crtc->event)
+		drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
+
+	fb = omap_crtc->old_fb;
+
+	omap_crtc->event = NULL;
+	omap_crtc->old_fb = NULL;
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+}
+
+static void page_flip_worker(struct work_struct *work)
+{
+	struct omap_crtc *omap_crtc =
+			container_of(work, struct omap_crtc, page_flip_work);
+	struct drm_crtc *crtc = &omap_crtc->base;
+	struct drm_display_mode *mode = &crtc->mode;
+	struct drm_gem_object *bo;
+
+	drm_modeset_lock(&crtc->mutex, NULL);
+	omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
+			    0, 0, mode->hdisplay, mode->vdisplay,
+			    crtc->x, crtc->y, mode->hdisplay, mode->vdisplay,
+			    vblank_cb, crtc);
+	drm_modeset_unlock(&crtc->mutex);
+
+	bo = omap_framebuffer_bo(crtc->primary->fb, 0);
+	drm_gem_object_unreference_unlocked(bo);
+}
+
+static void page_flip_cb(void *arg)
+{
+	struct drm_crtc *crtc = arg;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct omap_drm_private *priv = crtc->dev->dev_private;
+
+	/* avoid assumptions about what ctxt we are called from: */
+	queue_work(priv->wq, &omap_crtc->page_flip_work);
+}
+
+static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
+		 struct drm_framebuffer *fb,
+		 struct drm_pending_vblank_event *event,
+		 uint32_t page_flip_flags)
+{
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct drm_plane *primary = crtc->primary;
+	struct drm_gem_object *bo;
+	unsigned long flags;
+
+	DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1,
+			fb->base.id, event);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (omap_crtc->old_fb) {
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		dev_err(dev->dev, "already a pending flip\n");
+		return -EBUSY;
+	}
+
+	omap_crtc->event = event;
+	omap_crtc->old_fb = primary->fb = fb;
+	drm_framebuffer_reference(omap_crtc->old_fb);
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	/*
+	 * Hold a reference temporarily until the crtc is updated
+	 * and takes the reference to the bo.  This avoids it
+	 * getting freed from under us:
+	 */
+	bo = omap_framebuffer_bo(fb, 0);
+	drm_gem_object_reference(bo);
+
+	omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
+
+	return 0;
+}
+
+static int omap_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_property *property, uint64_t val)
+{
+	struct omap_drm_private *priv = crtc->dev->dev_private;
+
+	if (property == priv->rotation_prop) {
+		crtc->invert_dimensions =
+				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
+	}
+
+	return omap_plane_set_property(crtc->primary, property, val);
+}
+
+static const struct drm_crtc_funcs omap_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = omap_crtc_destroy,
+	.page_flip = omap_crtc_page_flip_locked,
+	.set_property = omap_crtc_set_property,
+};
+
+static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
+	.dpms = omap_crtc_dpms,
+	.mode_fixup = omap_crtc_mode_fixup,
+	.mode_set = omap_crtc_mode_set,
+	.prepare = omap_crtc_prepare,
+	.commit = omap_crtc_commit,
+	.mode_set_base = omap_crtc_mode_set_base,
+};
+
+/* -----------------------------------------------------------------------------
+ * Init and Cleanup
+ */
+
 static const char *channel_names[] = {
-		[OMAP_DSS_CHANNEL_LCD] = "lcd",
-		[OMAP_DSS_CHANNEL_DIGIT] = "tv",
-		[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
-		[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
+	[OMAP_DSS_CHANNEL_LCD] = "lcd",
+	[OMAP_DSS_CHANNEL_DIGIT] = "tv",
+	[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
+	[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
 };
 
 void omap_crtc_pre_init(void)
@@ -681,12 +700,13 @@
 	struct drm_crtc *crtc = NULL;
 	struct omap_crtc *omap_crtc;
 	struct omap_overlay_manager_info *info;
+	int ret;
 
 	DBG("%s", channel_names[channel]);
 
 	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
 	if (!omap_crtc)
-		goto fail;
+		return NULL;
 
 	crtc = &omap_crtc->base;
 
@@ -700,8 +720,6 @@
 	omap_crtc->apply.post_apply = omap_crtc_post_apply;
 
 	omap_crtc->channel = channel;
-	omap_crtc->plane = plane;
-	omap_crtc->plane->crtc = crtc;
 	omap_crtc->name = channel_names[channel];
 	omap_crtc->pipe = id;
 
@@ -723,18 +741,18 @@
 	info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
 	info->trans_enabled = false;
 
-	drm_crtc_init(dev, crtc, &omap_crtc_funcs);
+	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+					&omap_crtc_funcs);
+	if (ret < 0) {
+		kfree(omap_crtc);
+		return NULL;
+	}
+
 	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
 
-	omap_plane_install_properties(omap_crtc->plane, &crtc->base);
+	omap_plane_install_properties(crtc->primary, &crtc->base);
 
 	omap_crtcs[channel] = omap_crtc;
 
 	return crtc;
-
-fail:
-	if (crtc)
-		omap_crtc_destroy(crtc);
-
-	return NULL;
 }
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
index 58bcd6a..9f32a83 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
@@ -148,11 +148,15 @@
 
 	bool async;
 
-	wait_queue_head_t wait_for_refill;
+	struct completion compl;
 
 	struct list_head idle_node;
 };
 
+struct dmm_platform_data {
+	uint32_t cpu_cache_flags;
+};
+
 struct dmm {
 	struct device *dev;
 	void __iomem *base;
@@ -183,6 +187,8 @@
 
 	/* allocation list and lock */
 	struct list_head alloc_head;
+
+	const struct dmm_platform_data *plat_data;
 };
 
 #endif
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 56c6055..042038e 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -29,6 +29,7 @@
 #include <linux/mm.h>
 #include <linux/time.h>
 #include <linux/list.h>
+#include <linux/completion.h>
 
 #include "omap_dmm_tiler.h"
 #include "omap_dmm_priv.h"
@@ -39,6 +40,10 @@
 static struct tcm *containers[TILFMT_NFORMATS];
 static struct dmm *omap_dmm;
 
+#if defined(CONFIG_OF)
+static const struct of_device_id dmm_of_match[];
+#endif
+
 /* global spinlock for protecting lists */
 static DEFINE_SPINLOCK(list_lock);
 
@@ -58,19 +63,19 @@
 	uint32_t slot_w;	/* width of each slot (in pixels) */
 	uint32_t slot_h;	/* height of each slot (in pixels) */
 } geom[TILFMT_NFORMATS] = {
-		[TILFMT_8BIT]  = GEOM(0, 0, 1),
-		[TILFMT_16BIT] = GEOM(0, 1, 2),
-		[TILFMT_32BIT] = GEOM(1, 1, 4),
-		[TILFMT_PAGE]  = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
+	[TILFMT_8BIT]  = GEOM(0, 0, 1),
+	[TILFMT_16BIT] = GEOM(0, 1, 2),
+	[TILFMT_32BIT] = GEOM(1, 1, 4),
+	[TILFMT_PAGE]  = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
 };
 
 
 /* lookup table for registers w/ per-engine instances */
 static const uint32_t reg[][4] = {
-		[PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
-				DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
-		[PAT_DESCR]  = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
-				DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
+	[PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
+			DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
+	[PAT_DESCR]  = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
+			DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
 };
 
 /* simple allocator to grab next 16 byte aligned memory from txn */
@@ -142,10 +147,10 @@
 
 	for (i = 0; i < dmm->num_engines; i++) {
 		if (status & DMM_IRQSTAT_LST) {
-			wake_up_interruptible(&dmm->engines[i].wait_for_refill);
-
 			if (dmm->engines[i].async)
 				release_engine(&dmm->engines[i]);
+
+			complete(&dmm->engines[i].compl);
 		}
 
 		status >>= 8;
@@ -269,15 +274,17 @@
 
 	/* mark whether it is async to denote list management in IRQ handler */
 	engine->async = wait ? false : true;
+	reinit_completion(&engine->compl);
+	/* verify that the irq handler sees the 'async' and completion value */
+	smp_mb();
 
 	/* kick reload */
 	writel(engine->refill_pa,
 		dmm->base + reg[PAT_DESCR][engine->id]);
 
 	if (wait) {
-		if (wait_event_interruptible_timeout(engine->wait_for_refill,
-				wait_status(engine, DMM_PATSTATUS_READY) == 0,
-				msecs_to_jiffies(1)) <= 0) {
+		if (!wait_for_completion_timeout(&engine->compl,
+				msecs_to_jiffies(1))) {
 			dev_err(dmm->dev, "timed out waiting for done\n");
 			ret = -ETIMEDOUT;
 		}
@@ -529,6 +536,11 @@
 	return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
 }
 
+uint32_t tiler_get_cpu_cache_flags(void)
+{
+	return omap_dmm->plat_data->cpu_cache_flags;
+}
+
 bool dmm_is_available(void)
 {
 	return omap_dmm ? true : false;
@@ -592,6 +604,18 @@
 
 	init_waitqueue_head(&omap_dmm->engine_queue);
 
+	if (dev->dev.of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_node(dmm_of_match, dev->dev.of_node);
+		if (!match) {
+			dev_err(&dev->dev, "failed to find matching device node\n");
+			return -ENODEV;
+		}
+
+		omap_dmm->plat_data = match->data;
+	}
+
 	/* lookup hwmod data - base address and irq */
 	mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
 	if (!mem) {
@@ -696,7 +720,7 @@
 						(REFILL_BUFFER_SIZE * i);
 		omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
 						(REFILL_BUFFER_SIZE * i);
-		init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
+		init_completion(&omap_dmm->engines[i].compl);
 
 		list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
 	}
@@ -941,7 +965,7 @@
 }
 #endif
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int omap_dmm_resume(struct device *dev)
 {
 	struct tcm_area area;
@@ -965,16 +989,28 @@
 
 	return 0;
 }
-
-static const struct dev_pm_ops omap_dmm_pm_ops = {
-	.resume = omap_dmm_resume,
-};
 #endif
 
+static SIMPLE_DEV_PM_OPS(omap_dmm_pm_ops, NULL, omap_dmm_resume);
+
 #if defined(CONFIG_OF)
+static const struct dmm_platform_data dmm_omap4_platform_data = {
+	.cpu_cache_flags = OMAP_BO_WC,
+};
+
+static const struct dmm_platform_data dmm_omap5_platform_data = {
+	.cpu_cache_flags = OMAP_BO_UNCACHED,
+};
+
 static const struct of_device_id dmm_of_match[] = {
-	{ .compatible = "ti,omap4-dmm", },
-	{ .compatible = "ti,omap5-dmm", },
+	{
+		.compatible = "ti,omap4-dmm",
+		.data = &dmm_omap4_platform_data,
+	},
+	{
+		.compatible = "ti,omap5-dmm",
+		.data = &dmm_omap5_platform_data,
+	},
 	{},
 };
 #endif
@@ -986,9 +1022,7 @@
 		.owner = THIS_MODULE,
 		.name = DMM_DRIVER_NAME,
 		.of_match_table = of_match_ptr(dmm_of_match),
-#ifdef CONFIG_PM
 		.pm = &omap_dmm_pm_ops,
-#endif
 	},
 };
 
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
index 4fdd61e..e83c783 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
@@ -106,6 +106,7 @@
 size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
 size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
 void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
+uint32_t tiler_get_cpu_cache_flags(void);
 bool dmm_is_available(void);
 
 extern struct platform_driver omap_dmm_driver;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 8241ed9..94920d4 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -128,6 +128,29 @@
 	return r;
 }
 
+static int omap_modeset_create_crtc(struct drm_device *dev, int id,
+				    enum omap_channel channel)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	struct drm_plane *plane;
+	struct drm_crtc *crtc;
+
+	plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_PRIMARY);
+	if (IS_ERR(plane))
+		return PTR_ERR(plane);
+
+	crtc = omap_crtc_init(dev, plane, channel, id);
+
+	BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
+	priv->crtcs[id] = crtc;
+	priv->num_crtcs++;
+
+	priv->planes[id] = plane;
+	priv->num_planes++;
+
+	return 0;
+}
+
 static int omap_modeset_init(struct drm_device *dev)
 {
 	struct omap_drm_private *priv = dev->dev_private;
@@ -136,6 +159,7 @@
 	int num_mgrs = dss_feat_get_num_mgrs();
 	int num_crtcs;
 	int i, id = 0;
+	int ret;
 
 	drm_mode_config_init(dev);
 
@@ -209,18 +233,13 @@
 		 * allocated crtc, we create a new crtc for it
 		 */
 		if (!channel_used(dev, channel)) {
-			struct drm_plane *plane;
-			struct drm_crtc *crtc;
-
-			plane = omap_plane_init(dev, id, true);
-			crtc = omap_crtc_init(dev, plane, channel, id);
-
-			BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
-			priv->crtcs[id] = crtc;
-			priv->num_crtcs++;
-
-			priv->planes[id] = plane;
-			priv->num_planes++;
+			ret = omap_modeset_create_crtc(dev, id, channel);
+			if (ret < 0) {
+				dev_err(dev->dev,
+					"could not create CRTC (channel %u)\n",
+					channel);
+				return ret;
+			}
 
 			id++;
 		}
@@ -234,26 +253,8 @@
 
 		/* find a free manager for this crtc */
 		for (i = 0; i < num_mgrs; i++) {
-			if (!channel_used(dev, i)) {
-				struct drm_plane *plane;
-				struct drm_crtc *crtc;
-
-				plane = omap_plane_init(dev, id, true);
-				crtc = omap_crtc_init(dev, plane, i, id);
-
-				BUG_ON(priv->num_crtcs >=
-					ARRAY_SIZE(priv->crtcs));
-
-				priv->crtcs[id] = crtc;
-				priv->num_crtcs++;
-
-				priv->planes[id] = plane;
-				priv->num_planes++;
-
+			if (!channel_used(dev, i))
 				break;
-			} else {
-				continue;
-			}
 		}
 
 		if (i == num_mgrs) {
@@ -261,13 +262,24 @@
 			dev_err(dev->dev, "no managers left for crtc\n");
 			return -ENOMEM;
 		}
+
+		ret = omap_modeset_create_crtc(dev, id, i);
+		if (ret < 0) {
+			dev_err(dev->dev,
+				"could not create CRTC (channel %u)\n", i);
+			return ret;
+		}
 	}
 
 	/*
 	 * Create normal planes for the remaining overlays:
 	 */
 	for (; id < num_ovls; id++) {
-		struct drm_plane *plane = omap_plane_init(dev, id, false);
+		struct drm_plane *plane;
+
+		plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_OVERLAY);
+		if (IS_ERR(plane))
+			return PTR_ERR(plane);
 
 		BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
 		priv->planes[priv->num_planes++] = plane;
@@ -286,14 +298,13 @@
 		for (id = 0; id < priv->num_crtcs; id++) {
 			struct drm_crtc *crtc = priv->crtcs[id];
 			enum omap_channel crtc_channel;
-			enum omap_dss_output_id supported_outputs;
 
 			crtc_channel = omap_crtc_channel(crtc);
-			supported_outputs =
-				dss_feat_get_supported_outputs(crtc_channel);
 
-			if (supported_outputs & output->id)
+			if (output->dispc_channel == crtc_channel) {
 				encoder->possible_crtcs |= (1 << id);
+				break;
+			}
 		}
 
 		omap_dss_put_device(output);
@@ -480,6 +491,7 @@
 
 	priv->wq = alloc_ordered_workqueue("omapdrm", 0);
 
+	spin_lock_init(&priv->list_lock);
 	INIT_LIST_HEAD(&priv->obj_list);
 
 	omap_gem_init(dev);
@@ -519,7 +531,8 @@
 
 	drm_kms_helper_poll_fini(dev);
 
-	omap_fbdev_free(dev);
+	if (priv->fbdev)
+		omap_fbdev_free(dev);
 
 	/* flush crtcs so the fbs get released */
 	for (i = 0; i < priv->num_crtcs; i++)
@@ -588,9 +601,11 @@
 		}
 	}
 
-	ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
-	if (ret)
-		DBG("failed to restore crtc mode");
+	if (priv->fbdev) {
+		ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
+		if (ret)
+			DBG("failed to restore crtc mode");
+	}
 }
 
 static void dev_preclose(struct drm_device *dev, struct drm_file *file)
@@ -610,74 +625,57 @@
 };
 
 static const struct file_operations omapdriver_fops = {
-		.owner = THIS_MODULE,
-		.open = drm_open,
-		.unlocked_ioctl = drm_ioctl,
-		.release = drm_release,
-		.mmap = omap_gem_mmap,
-		.poll = drm_poll,
-		.read = drm_read,
-		.llseek = noop_llseek,
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.unlocked_ioctl = drm_ioctl,
+	.release = drm_release,
+	.mmap = omap_gem_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.llseek = noop_llseek,
 };
 
 static struct drm_driver omap_drm_driver = {
-		.driver_features =
-				DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
-		.load = dev_load,
-		.unload = dev_unload,
-		.open = dev_open,
-		.lastclose = dev_lastclose,
-		.preclose = dev_preclose,
-		.postclose = dev_postclose,
-		.set_busid = drm_platform_set_busid,
-		.get_vblank_counter = drm_vblank_count,
-		.enable_vblank = omap_irq_enable_vblank,
-		.disable_vblank = omap_irq_disable_vblank,
-		.irq_preinstall = omap_irq_preinstall,
-		.irq_postinstall = omap_irq_postinstall,
-		.irq_uninstall = omap_irq_uninstall,
-		.irq_handler = omap_irq_handler,
+	.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM
+			 | DRIVER_PRIME,
+	.load = dev_load,
+	.unload = dev_unload,
+	.open = dev_open,
+	.lastclose = dev_lastclose,
+	.preclose = dev_preclose,
+	.postclose = dev_postclose,
+	.set_busid = drm_platform_set_busid,
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = omap_irq_enable_vblank,
+	.disable_vblank = omap_irq_disable_vblank,
+	.irq_preinstall = omap_irq_preinstall,
+	.irq_postinstall = omap_irq_postinstall,
+	.irq_uninstall = omap_irq_uninstall,
+	.irq_handler = omap_irq_handler,
 #ifdef CONFIG_DEBUG_FS
-		.debugfs_init = omap_debugfs_init,
-		.debugfs_cleanup = omap_debugfs_cleanup,
+	.debugfs_init = omap_debugfs_init,
+	.debugfs_cleanup = omap_debugfs_cleanup,
 #endif
-		.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
-		.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
-		.gem_prime_export = omap_gem_prime_export,
-		.gem_prime_import = omap_gem_prime_import,
-		.gem_free_object = omap_gem_free_object,
-		.gem_vm_ops = &omap_gem_vm_ops,
-		.dumb_create = omap_gem_dumb_create,
-		.dumb_map_offset = omap_gem_dumb_map_offset,
-		.dumb_destroy = drm_gem_dumb_destroy,
-		.ioctls = ioctls,
-		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
-		.fops = &omapdriver_fops,
-		.name = DRIVER_NAME,
-		.desc = DRIVER_DESC,
-		.date = DRIVER_DATE,
-		.major = DRIVER_MAJOR,
-		.minor = DRIVER_MINOR,
-		.patchlevel = DRIVER_PATCHLEVEL,
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_export = omap_gem_prime_export,
+	.gem_prime_import = omap_gem_prime_import,
+	.gem_free_object = omap_gem_free_object,
+	.gem_vm_ops = &omap_gem_vm_ops,
+	.dumb_create = omap_gem_dumb_create,
+	.dumb_map_offset = omap_gem_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.ioctls = ioctls,
+	.num_ioctls = DRM_OMAP_NUM_IOCTLS,
+	.fops = &omapdriver_fops,
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
 };
 
-static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
-{
-	DBG("");
-	return 0;
-}
-
-static int pdev_resume(struct platform_device *device)
-{
-	DBG("");
-	return 0;
-}
-
-static void pdev_shutdown(struct platform_device *device)
-{
-	DBG("");
-}
-
 static int pdev_probe(struct platform_device *device)
 {
 	int r;
@@ -709,24 +707,35 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static const struct dev_pm_ops omapdrm_pm_ops = {
-	.resume = omap_gem_resume,
-};
+#ifdef CONFIG_PM_SLEEP
+static int omap_drm_suspend(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	drm_kms_helper_poll_disable(drm_dev);
+
+	return 0;
+}
+
+static int omap_drm_resume(struct device *dev)
+{
+	struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+	drm_kms_helper_poll_enable(drm_dev);
+
+	return omap_gem_resume(dev);
+}
 #endif
 
+static SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume);
+
 static struct platform_driver pdev = {
-		.driver = {
-			.name = DRIVER_NAME,
-#ifdef CONFIG_PM
-			.pm = &omapdrm_pm_ops,
-#endif
-		},
-		.probe = pdev_probe,
-		.remove = pdev_remove,
-		.suspend = pdev_suspend,
-		.resume = pdev_resume,
-		.shutdown = pdev_shutdown,
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = &omapdrm_pm_ops,
+	},
+	.probe = pdev_probe,
+	.remove = pdev_remove,
 };
 
 static int __init omap_drm_init(void)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index 60e47b3..b31c79f 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -105,6 +105,9 @@
 
 	struct workqueue_struct *wq;
 
+	/* lock for obj_list below */
+	spinlock_t list_lock;
+
 	/* list of GEM objects: */
 	struct list_head obj_list;
 
@@ -160,15 +163,15 @@
 void omap_crtc_flush(struct drm_crtc *crtc);
 
 struct drm_plane *omap_plane_init(struct drm_device *dev,
-		int plane_id, bool private_plane);
-int omap_plane_dpms(struct drm_plane *plane, int mode);
+		int id, enum drm_plane_type type);
+int omap_plane_set_enable(struct drm_plane *plane, bool enable);
 int omap_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h,
-		void (*fxn)(void *), void *arg);
+			struct drm_crtc *crtc, struct drm_framebuffer *fb,
+			int crtc_x, int crtc_y,
+			unsigned int crtc_w, unsigned int crtc_h,
+			unsigned int src_x, unsigned int src_y,
+			unsigned int src_w, unsigned int src_h,
+			void (*fxn)(void *), void *arg);
 void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj);
 int omap_plane_set_property(struct drm_plane *plane,
@@ -186,8 +189,6 @@
 		struct drm_encoder *encoder);
 struct drm_encoder *omap_connector_attached_encoder(
 		struct drm_connector *connector);
-void omap_connector_flush(struct drm_connector *connector,
-		int x, int y, int w, int h);
 bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
 
 void copy_timings_omap_to_drm(struct drm_display_mode *mode,
@@ -208,8 +209,6 @@
 		struct omap_drm_window *win, struct omap_overlay_info *info);
 struct drm_connector *omap_framebuffer_get_next_connector(
 		struct drm_framebuffer *fb, struct drm_connector *from);
-void omap_framebuffer_flush(struct drm_framebuffer *fb,
-		int x, int y, int w, int h);
 
 void omap_gem_init(struct drm_device *dev);
 void omap_gem_deinit(struct drm_device *dev);
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index 2a5cacd..b2c1a29 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -86,6 +86,7 @@
 
 struct omap_framebuffer {
 	struct drm_framebuffer base;
+	int pin_count;
 	const struct format *format;
 	struct plane planes[4];
 };
@@ -121,18 +122,6 @@
 		struct drm_file *file_priv, unsigned flags, unsigned color,
 		struct drm_clip_rect *clips, unsigned num_clips)
 {
-	int i;
-
-	drm_modeset_lock_all(fb->dev);
-
-	for (i = 0; i < num_clips; i++) {
-		omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
-					clips[i].x2 - clips[i].x1,
-					clips[i].y2 - clips[i].y1);
-	}
-
-	drm_modeset_unlock_all(fb->dev);
-
 	return 0;
 }
 
@@ -261,6 +250,11 @@
 	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
 	int ret, i, n = drm_format_num_planes(fb->pixel_format);
 
+	if (omap_fb->pin_count > 0) {
+		omap_fb->pin_count++;
+		return 0;
+	}
+
 	for (i = 0; i < n; i++) {
 		struct plane *plane = &omap_fb->planes[i];
 		ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true);
@@ -269,6 +263,8 @@
 		omap_gem_dma_sync(plane->bo, DMA_TO_DEVICE);
 	}
 
+	omap_fb->pin_count++;
+
 	return 0;
 
 fail:
@@ -287,6 +283,11 @@
 	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
 	int ret, i, n = drm_format_num_planes(fb->pixel_format);
 
+	omap_fb->pin_count--;
+
+	if (omap_fb->pin_count > 0)
+		return 0;
+
 	for (i = 0; i < n; i++) {
 		struct plane *plane = &omap_fb->planes[i];
 		ret = omap_gem_put_paddr(plane->bo);
@@ -336,34 +337,6 @@
 	return NULL;
 }
 
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-void omap_framebuffer_flush(struct drm_framebuffer *fb,
-		int x, int y, int w, int h)
-{
-	struct drm_connector *connector = NULL;
-
-	VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
-
-	/* FIXME: This is racy - no protection against modeset config changes. */
-	while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
-		/* only consider connectors that are part of a chain */
-		if (connector->encoder && connector->encoder->crtc) {
-			/* TODO: maybe this should propagate thru the crtc who
-			 * could do the coordinate translation..
-			 */
-			struct drm_crtc *crtc = connector->encoder->crtc;
-			int cx = max(0, x - crtc->x);
-			int cy = max(0, y - crtc->y);
-			int cw = w + (x - crtc->x) - cx;
-			int ch = h + (y - crtc->y) - cy;
-
-			omap_connector_flush(connector, cx, cy, cw, ch);
-		}
-	}
-}
-
 #ifdef CONFIG_DEBUG_FS
 void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
 {
@@ -407,7 +380,7 @@
 struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
 		struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
 {
-	struct omap_framebuffer *omap_fb;
+	struct omap_framebuffer *omap_fb = NULL;
 	struct drm_framebuffer *fb = NULL;
 	const struct format *format = NULL;
 	int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
@@ -450,6 +423,14 @@
 			goto fail;
 		}
 
+		if (pitch % format->planes[i].stride_bpp != 0) {
+			dev_err(dev->dev,
+				"buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n",
+				pitch, format->planes[i].stride_bpp);
+			ret = -EINVAL;
+			goto fail;
+		}
+
 		size = pitch * mode_cmd->height / format->planes[i].sub_y;
 
 		if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
@@ -478,8 +459,7 @@
 	return fb;
 
 fail:
-	if (fb)
-		omap_framebuffer_destroy(fb);
+	kfree(omap_fb);
 
 	return ERR_PTR(ret);
 }
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index d292d24..950cd33 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -42,42 +42,8 @@
 	struct work_struct work;
 };
 
-static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
 static struct drm_fb_helper *get_fb(struct fb_info *fbi);
 
-static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
-		size_t count, loff_t *ppos)
-{
-	ssize_t res;
-
-	res = fb_sys_write(fbi, buf, count, ppos);
-	omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
-
-	return res;
-}
-
-static void omap_fbdev_fillrect(struct fb_info *fbi,
-		const struct fb_fillrect *rect)
-{
-	sys_fillrect(fbi, rect);
-	omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
-}
-
-static void omap_fbdev_copyarea(struct fb_info *fbi,
-		const struct fb_copyarea *area)
-{
-	sys_copyarea(fbi, area);
-	omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
-}
-
-static void omap_fbdev_imageblit(struct fb_info *fbi,
-		const struct fb_image *image)
-{
-	sys_imageblit(fbi, image);
-	omap_fbdev_flush(fbi, image->dx, image->dy,
-				image->width, image->height);
-}
-
 static void pan_worker(struct work_struct *work)
 {
 	struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
@@ -121,10 +87,10 @@
 	 * basic fbdev ops which write to the framebuffer
 	 */
 	.fb_read = fb_sys_read,
-	.fb_write = omap_fbdev_write,
-	.fb_fillrect = omap_fbdev_fillrect,
-	.fb_copyarea = omap_fbdev_copyarea,
-	.fb_imageblit = omap_fbdev_imageblit,
+	.fb_write = fb_sys_write,
+	.fb_fillrect = sys_fillrect,
+	.fb_copyarea = sys_copyarea,
+	.fb_imageblit = sys_imageblit,
 
 	.fb_check_var = drm_fb_helper_check_var,
 	.fb_set_par = drm_fb_helper_set_par,
@@ -294,21 +260,6 @@
 	return fbi->par;
 }
 
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
-{
-	struct drm_fb_helper *helper = get_fb(fbi);
-
-	if (!helper)
-		return;
-
-	VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
-
-	omap_framebuffer_flush(helper->fb, x, y, w, h);
-}
-
 /* initialize fbdev helper */
 struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
 {
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index aeb91ed..e9718b9 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -828,6 +828,7 @@
 				dev_err(obj->dev->dev,
 					"could not release unmap: %d\n", ret);
 			}
+			omap_obj->paddr = 0;
 			omap_obj->block = NULL;
 		}
 	}
@@ -1272,13 +1273,16 @@
 void omap_gem_free_object(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
+	struct omap_drm_private *priv = dev->dev_private;
 	struct omap_gem_object *omap_obj = to_omap_bo(obj);
 
 	evict(obj);
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
+	spin_lock(&priv->list_lock);
 	list_del(&omap_obj->mm_list);
+	spin_unlock(&priv->list_lock);
 
 	drm_gem_free_mmap_offset(obj);
 
@@ -1358,8 +1362,8 @@
 		/* currently don't allow cached buffers.. there is some caching
 		 * stuff that needs to be handled better
 		 */
-		flags &= ~(OMAP_BO_CACHED|OMAP_BO_UNCACHED);
-		flags |= OMAP_BO_WC;
+		flags &= ~(OMAP_BO_CACHED|OMAP_BO_WC|OMAP_BO_UNCACHED);
+		flags |= tiler_get_cpu_cache_flags();
 
 		/* align dimensions to slot boundaries... */
 		tiler_align(gem2fmt(flags),
@@ -1376,7 +1380,9 @@
 	if (!omap_obj)
 		goto fail;
 
+	spin_lock(&priv->list_lock);
 	list_add(&omap_obj->mm_list, &priv->obj_list);
+	spin_unlock(&priv->list_lock);
 
 	obj = &omap_obj->base;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
index a2dbfb1..b46dabd 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
@@ -156,16 +156,16 @@
 }
 
 static struct dma_buf_ops omap_dmabuf_ops = {
-		.map_dma_buf = omap_gem_map_dma_buf,
-		.unmap_dma_buf = omap_gem_unmap_dma_buf,
-		.release = omap_gem_dmabuf_release,
-		.begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
-		.end_cpu_access = omap_gem_dmabuf_end_cpu_access,
-		.kmap_atomic = omap_gem_dmabuf_kmap_atomic,
-		.kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
-		.kmap = omap_gem_dmabuf_kmap,
-		.kunmap = omap_gem_dmabuf_kunmap,
-		.mmap = omap_gem_dmabuf_mmap,
+	.map_dma_buf = omap_gem_map_dma_buf,
+	.unmap_dma_buf = omap_gem_unmap_dma_buf,
+	.release = omap_gem_dmabuf_release,
+	.begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
+	.end_cpu_access = omap_gem_dmabuf_end_cpu_access,
+	.kmap_atomic = omap_gem_dmabuf_kmap_atomic,
+	.kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
+	.kmap = omap_gem_dmabuf_kmap,
+	.kunmap = omap_gem_dmabuf_kunmap,
+	.mmap = omap_gem_dmabuf_mmap,
 };
 
 struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
index f035d2b..3eb097e 100644
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -34,7 +34,7 @@
 	struct omap_drm_irq *irq;
 	uint32_t irqmask = priv->vblank_mask;
 
-	BUG_ON(!spin_is_locked(&list_lock));
+	assert_spin_locked(&list_lock);
 
 	list_for_each_entry(irq, &priv->irq_list, node)
 		irqmask |= irq->irqmask;
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index ee8e2b3..1c6b63f 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -65,12 +65,16 @@
 	struct callback apply_done_cb;
 };
 
-static void unpin_worker(struct drm_flip_work *work, void *val)
+static void omap_plane_unpin_worker(struct drm_flip_work *work, void *val)
 {
 	struct omap_plane *omap_plane =
 			container_of(work, struct omap_plane, unpin_work);
 	struct drm_device *dev = omap_plane->base.dev;
 
+	/*
+	 * omap_framebuffer_pin/unpin are always called from priv->wq,
+	 * so there's no need for locking here.
+	 */
 	omap_framebuffer_unpin(val);
 	mutex_lock(&dev->mode_config.mutex);
 	drm_framebuffer_unreference(val);
@@ -78,7 +82,8 @@
 }
 
 /* update which fb (if any) is pinned for scanout */
-static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+static int omap_plane_update_pin(struct drm_plane *plane,
+				 struct drm_framebuffer *fb)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
@@ -121,13 +126,12 @@
 	struct drm_crtc *crtc = plane->crtc;
 	enum omap_channel channel;
 	bool enabled = omap_plane->enabled && crtc;
-	bool ilace, replication;
 	int ret;
 
 	DBG("%s, enabled=%d", omap_plane->name, enabled);
 
 	/* if fb has changed, pin new fb: */
-	update_pin(plane, enabled ? plane->fb : NULL);
+	omap_plane_update_pin(plane, enabled ? plane->fb : NULL);
 
 	if (!enabled) {
 		dispc_ovl_enable(omap_plane->id, false);
@@ -145,20 +149,17 @@
 	DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
 			&info->paddr, &info->p_uv_addr);
 
-	/* TODO: */
-	ilace = false;
-	replication = false;
+	dispc_ovl_set_channel_out(omap_plane->id, channel);
 
 	/* and finally, update omapdss: */
-	ret = dispc_ovl_setup(omap_plane->id, info,
-			replication, omap_crtc_timings(crtc), false);
+	ret = dispc_ovl_setup(omap_plane->id, info, false,
+			      omap_crtc_timings(crtc), false);
 	if (ret) {
 		dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
 		return;
 	}
 
 	dispc_ovl_enable(omap_plane->id, true);
-	dispc_ovl_set_channel_out(omap_plane->id, channel);
 }
 
 static void omap_plane_post_apply(struct omap_drm_apply *apply)
@@ -167,7 +168,6 @@
 			container_of(apply, struct omap_plane, apply);
 	struct drm_plane *plane = &omap_plane->base;
 	struct omap_drm_private *priv = plane->dev->dev_private;
-	struct omap_overlay_info *info = &omap_plane->info;
 	struct callback cb;
 
 	cb = omap_plane->apply_done_cb;
@@ -177,14 +177,9 @@
 
 	if (cb.fxn)
 		cb.fxn(cb.arg);
-
-	if (omap_plane->enabled) {
-		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
-				info->out_width, info->out_height);
-	}
 }
 
-static int apply(struct drm_plane *plane)
+static int omap_plane_apply(struct drm_plane *plane)
 {
 	if (plane->crtc) {
 		struct omap_plane *omap_plane = to_omap_plane(plane);
@@ -194,12 +189,12 @@
 }
 
 int omap_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h,
-		void (*fxn)(void *), void *arg)
+			struct drm_crtc *crtc, struct drm_framebuffer *fb,
+			int crtc_x, int crtc_y,
+			unsigned int crtc_w, unsigned int crtc_h,
+			unsigned int src_x, unsigned int src_y,
+			unsigned int src_w, unsigned int src_h,
+			void (*fxn)(void *), void *arg)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_window *win = &omap_plane->win;
@@ -209,11 +204,10 @@
 	win->crtc_w = crtc_w;
 	win->crtc_h = crtc_h;
 
-	/* src values are in Q16 fixed point, convert to integer: */
-	win->src_x = src_x >> 16;
-	win->src_y = src_y >> 16;
-	win->src_w = src_w >> 16;
-	win->src_h = src_h >> 16;
+	win->src_x = src_x;
+	win->src_y = src_y;
+	win->src_w = src_w;
+	win->src_h = src_h;
 
 	if (fxn) {
 		/* omap_crtc should ensure that a new page flip
@@ -225,15 +219,7 @@
 		omap_plane->apply_done_cb.arg = arg;
 	}
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
-
-	drm_framebuffer_reference(fb);
-
-	plane->fb = fb;
-	plane->crtc = crtc;
-
-	return apply(plane);
+	return omap_plane_apply(plane);
 }
 
 static int omap_plane_update(struct drm_plane *plane,
@@ -254,17 +240,29 @@
 		break;
 	}
 
+	/*
+	 * We don't need to take a reference to the framebuffer as the DRM core
+	 * has already done so for the purpose of setting plane->fb.
+	 */
+	plane->fb = fb;
+	plane->crtc = crtc;
+
+	/* src values are in Q16 fixed point, convert to integer: */
 	return omap_plane_mode_set(plane, crtc, fb,
 			crtc_x, crtc_y, crtc_w, crtc_h,
-			src_x, src_y, src_w, src_h,
+			src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
 			NULL, NULL);
 }
 
 static int omap_plane_disable(struct drm_plane *plane)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
+
 	omap_plane->win.rotation = BIT(DRM_ROTATE_0);
-	return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+	omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
+				? 0 : omap_plane->id;
+
+	return omap_plane_set_enable(plane, false);
 }
 
 static void omap_plane_destroy(struct drm_plane *plane)
@@ -275,7 +273,6 @@
 
 	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
 
-	omap_plane_disable(plane);
 	drm_plane_cleanup(plane);
 
 	drm_flip_work_cleanup(&omap_plane->unpin_work);
@@ -283,18 +280,15 @@
 	kfree(omap_plane);
 }
 
-int omap_plane_dpms(struct drm_plane *plane, int mode)
+int omap_plane_set_enable(struct drm_plane *plane, bool enable)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
-	bool enabled = (mode == DRM_MODE_DPMS_ON);
-	int ret = 0;
 
-	if (enabled != omap_plane->enabled) {
-		omap_plane->enabled = enabled;
-		ret = apply(plane);
-	}
+	if (enable == omap_plane->enabled)
+		return 0;
 
-	return ret;
+	omap_plane->enabled = enable;
+	return omap_plane_apply(plane);
 }
 
 /* helper to install properties which are common to planes and crtcs */
@@ -342,61 +336,63 @@
 	if (property == priv->rotation_prop) {
 		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->win.rotation = val;
-		ret = apply(plane);
+		ret = omap_plane_apply(plane);
 	} else if (property == priv->zorder_prop) {
 		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->info.zorder = val;
-		ret = apply(plane);
+		ret = omap_plane_apply(plane);
 	}
 
 	return ret;
 }
 
 static const struct drm_plane_funcs omap_plane_funcs = {
-		.update_plane = omap_plane_update,
-		.disable_plane = omap_plane_disable,
-		.destroy = omap_plane_destroy,
-		.set_property = omap_plane_set_property,
+	.update_plane = omap_plane_update,
+	.disable_plane = omap_plane_disable,
+	.destroy = omap_plane_destroy,
+	.set_property = omap_plane_set_property,
 };
 
 static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 {
 	struct omap_plane *omap_plane =
 			container_of(irq, struct omap_plane, error_irq);
-	DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
+	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
+		irqstatus);
 }
 
 static const char *plane_names[] = {
-		[OMAP_DSS_GFX] = "gfx",
-		[OMAP_DSS_VIDEO1] = "vid1",
-		[OMAP_DSS_VIDEO2] = "vid2",
-		[OMAP_DSS_VIDEO3] = "vid3",
+	[OMAP_DSS_GFX] = "gfx",
+	[OMAP_DSS_VIDEO1] = "vid1",
+	[OMAP_DSS_VIDEO2] = "vid2",
+	[OMAP_DSS_VIDEO3] = "vid3",
 };
 
 static const uint32_t error_irqs[] = {
-		[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
-		[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
-		[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
-		[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
+	[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
+	[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
+	[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
+	[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
 };
 
 /* initialize plane */
 struct drm_plane *omap_plane_init(struct drm_device *dev,
-		int id, bool private_plane)
+		int id, enum drm_plane_type type)
 {
 	struct omap_drm_private *priv = dev->dev_private;
-	struct drm_plane *plane = NULL;
+	struct drm_plane *plane;
 	struct omap_plane *omap_plane;
 	struct omap_overlay_info *info;
+	int ret;
 
-	DBG("%s: priv=%d", plane_names[id], private_plane);
+	DBG("%s: type=%d", plane_names[id], type);
 
 	omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
 	if (!omap_plane)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	drm_flip_work_init(&omap_plane->unpin_work,
-			"unpin", unpin_worker);
+			"unpin", omap_plane_unpin_worker);
 
 	omap_plane->nformats = omap_framebuffer_get_formats(
 			omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
@@ -413,8 +409,11 @@
 	omap_plane->error_irq.irq = omap_plane_error_irq;
 	omap_irq_register(dev, &omap_plane->error_irq);
 
-	drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
-			omap_plane->formats, omap_plane->nformats, private_plane);
+	ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1,
+				       &omap_plane_funcs, omap_plane->formats,
+				       omap_plane->nformats, type);
+	if (ret < 0)
+		goto error;
 
 	omap_plane_install_properties(plane, &plane->base);
 
@@ -432,10 +431,15 @@
 	 * TODO add ioctl to give userspace an API to change this.. this
 	 * will come in a subsequent patch.
 	 */
-	if (private_plane)
+	if (type == DRM_PLANE_TYPE_PRIMARY)
 		omap_plane->info.zorder = 0;
 	else
 		omap_plane->info.zorder = id;
 
 	return plane;
+
+error:
+	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
+	kfree(omap_plane);
+	return NULL;
 }