diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
index 59200ff..e0178d6 100644
--- a/drivers/staging/imx-drm/imx-drm-core.c
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -28,6 +28,7 @@
 #include "imx-drm.h"
 
 #define MAX_CRTC	4
+#define MAX_PIPES	(2 * MAX_CRTC)
 
 struct imx_drm_crtc;
 
@@ -53,12 +54,29 @@ struct imx_drm_crtc {
 static int legacyfb_depth = 16;
 module_param(legacyfb_depth, int, 0444);
 
+static inline int pipe_to_crtc_id(int pipe)
+{
+	return pipe >> 1;
+}
+
 int imx_drm_crtc_id(struct imx_drm_crtc *crtc)
 {
-	return crtc->pipe;
+	return pipe_to_crtc_id(crtc->pipe);
 }
 EXPORT_SYMBOL_GPL(imx_drm_crtc_id);
 
+int imx_drm_primary_plane_pipe(struct imx_drm_crtc *crtc)
+{
+	return crtc->pipe;
+}
+EXPORT_SYMBOL_GPL(imx_drm_primary_plane_pipe);
+
+int imx_drm_overlay_plane_pipe(struct imx_drm_crtc *crtc)
+{
+	return crtc->pipe + 1;
+}
+EXPORT_SYMBOL_GPL(imx_drm_overlay_plane_pipe);
+
 static void imx_drm_driver_lastclose(struct drm_device *drm)
 {
 #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER)
@@ -129,30 +147,16 @@ int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt)
 }
 EXPORT_SYMBOL_GPL(imx_drm_panel_format);
 
-int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
-{
-	return drm_vblank_get(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe);
-}
-EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get);
-
-void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc)
-{
-	drm_vblank_put(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe);
-}
-EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put);
-
-void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc)
-{
-	drm_handle_vblank(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe);
-}
-EXPORT_SYMBOL_GPL(imx_drm_handle_vblank);
-
-static int imx_drm_enable_vblank(struct drm_device *drm, int crtc)
+static int imx_drm_enable_vblank(struct drm_device *drm, int pipe)
 {
 	struct imx_drm_device *imxdrm = drm->dev_private;
-	struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc];
+	struct imx_drm_crtc *imx_drm_crtc;
 	int ret;
 
+	if (pipe >= MAX_PIPES)
+		return -EINVAL;
+
+	imx_drm_crtc = imxdrm->crtc[pipe_to_crtc_id(pipe)];
 	if (!imx_drm_crtc)
 		return -EINVAL;
 
@@ -160,35 +164,40 @@ static int imx_drm_enable_vblank(struct drm_device *drm, int crtc)
 		return -ENOSYS;
 
 	ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank(
-			imx_drm_crtc->crtc);
+		imx_drm_crtc->crtc, pipe);
 
 	return ret;
 }
 
-static void imx_drm_disable_vblank(struct drm_device *drm, int crtc)
+static void imx_drm_disable_vblank(struct drm_device *drm, int pipe)
 {
 	struct imx_drm_device *imxdrm = drm->dev_private;
-	struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc];
+	struct imx_drm_crtc *imx_drm_crtc;
 
+	if (pipe >= MAX_PIPES)
+		return;
+
+	imx_drm_crtc = imxdrm->crtc[pipe_to_crtc_id(pipe)];
 	if (!imx_drm_crtc)
 		return;
 
 	if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank)
 		return;
 
-	imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc);
+	imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(
+		imx_drm_crtc->crtc, pipe);
 }
 
 static void imx_drm_driver_preclose(struct drm_device *drm,
 		struct drm_file *file)
 {
-	int i;
+	int pipe;
 
 	if (!file->is_master)
 		return;
 
-	for (i = 0; i < MAX_CRTC; i++)
-		imx_drm_disable_vblank(drm, i);
+	for (pipe = 0; pipe < MAX_PIPES; pipe++)
+		imx_drm_disable_vblank(drm, pipe);
 }
 
 static const struct file_operations imx_drm_driver_fops = {
@@ -271,7 +280,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
 
 	drm_mode_config_init(drm);
 
-	ret = drm_vblank_init(drm, MAX_CRTC);
+	ret = drm_vblank_init(drm, MAX_PIPES);
 	if (ret)
 		goto err_kms;
 
@@ -349,13 +358,13 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
 {
 	struct imx_drm_device *imxdrm = drm->dev_private;
 	struct imx_drm_crtc *imx_drm_crtc;
-	int ret;
+	int id, ret;
 
 	/*
-	 * The vblank arrays are dimensioned by MAX_CRTC - we can't
+	 * The vblank arrays are dimensioned by MAX_PIPES - we can't
 	 * pass IDs greater than this to those functions.
 	 */
-	if (imxdrm->pipes >= MAX_CRTC)
+	if (imxdrm->pipes >= MAX_PIPES)
 		return -EINVAL;
 
 	if (imxdrm->drm->open_count)
@@ -366,11 +375,15 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
 		return -ENOMEM;
 
 	imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
-	imx_drm_crtc->pipe = imxdrm->pipes++;
+	imx_drm_crtc->pipe = imxdrm->pipes;
 	imx_drm_crtc->port = port;
 	imx_drm_crtc->crtc = crtc;
 
-	imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc;
+	imxdrm->pipes += 2;
+
+	id = pipe_to_crtc_id(imx_drm_crtc->pipe);
+
+	imxdrm->crtc[id] = imx_drm_crtc;
 
 	*new_crtc = imx_drm_crtc;
 
@@ -387,7 +400,7 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
 	return 0;
 
 err_register:
-	imxdrm->crtc[imx_drm_crtc->pipe] = NULL;
+	imxdrm->crtc[id] = NULL;
 	kfree(imx_drm_crtc);
 	return ret;
 }
@@ -399,10 +412,11 @@ EXPORT_SYMBOL_GPL(imx_drm_add_crtc);
 int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc)
 {
 	struct imx_drm_device *imxdrm = imx_drm_crtc->crtc->dev->dev_private;
+	int id = pipe_to_crtc_id(imx_drm_crtc->pipe);
 
 	drm_crtc_cleanup(imx_drm_crtc->crtc);
 
-	imxdrm->crtc[imx_drm_crtc->pipe] = NULL;
+	imxdrm->crtc[id] = NULL;
 
 	kfree(imx_drm_crtc);
 
diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h
index 3a30f16..ec084fe 100644
--- a/drivers/staging/imx-drm/imx-drm.h
+++ b/drivers/staging/imx-drm/imx-drm.h
@@ -13,10 +13,12 @@ struct imx_drm_crtc;
 struct platform_device;
 
 int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
+int imx_drm_primary_plane_pipe(struct imx_drm_crtc *crtc);
+int imx_drm_overlay_plane_pipe(struct imx_drm_crtc *crtc);
 
 struct imx_drm_crtc_helper_funcs {
-	int (*enable_vblank)(struct drm_crtc *crtc);
-	void (*disable_vblank)(struct drm_crtc *crtc);
+	int (*enable_vblank)(struct drm_crtc *crtc, int pipe);
+	void (*disable_vblank)(struct drm_crtc *crtc, int pipe);
 	int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type,
 			u32 pix_fmt, int hsync_pin, int vsync_pin);
 	const struct drm_crtc_helper_funcs *crtc_helper_funcs;
@@ -33,10 +35,6 @@ int imx_drm_init_drm(struct platform_device *pdev,
 		int preferred_bpp);
 int imx_drm_exit_drm(void);
 
-int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc);
-void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc);
-void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc);
-
 void imx_drm_mode_config_init(struct drm_device *drm);
 
 struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
index 593261f..423b004 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -90,9 +90,7 @@ struct ipu_crtc {
 	struct ipu_dc		*dc;
 	struct ipu_di		*di;
 	int			enabled;
-	struct drm_pending_vblank_event *page_flip_event;
-	struct drm_framebuffer	*newfb;
-	int			irq;
+
 	u32			interface_pix_fmt;
 	unsigned long		di_clkflags;
 	int			di_hsync_pin;
@@ -101,6 +99,22 @@ struct ipu_crtc {
 
 #define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
 
+static struct ipu_plane *pipe_to_plane(struct ipu_crtc *ipu_crtc,
+				       int pipe)
+{
+	struct ipu_plane *plane;
+
+	plane = &ipu_crtc->plane[0];
+	if (pipe == plane->pipe)
+		return plane;
+
+	plane = &ipu_crtc->plane[1];
+	if (ipu_crtc->have_overlay && pipe == plane->pipe)
+		return plane;
+
+	return NULL;
+}
+
 static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
 {
 	if (ipu_crtc->enabled)
@@ -149,36 +163,18 @@ static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
 	}
 }
 
-static int ipu_page_flip(struct drm_crtc *crtc,
+static int ipu_crtc_page_flip(struct drm_crtc *crtc,
 		struct drm_framebuffer *fb,
 		struct drm_pending_vblank_event *event,
 		uint32_t page_flip_flags)
 {
-	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
-	int ret;
-
-	if (ipu_crtc->newfb)
-		return -EBUSY;
-
-	ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
-	if (ret) {
-		dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
-		list_del(&event->base.link);
-
-		return ret;
-	}
-
-	ipu_crtc->newfb = fb;
-	ipu_crtc->page_flip_event = event;
-	crtc->primary->fb = fb;
-
-	return 0;
+	return ipu_plane_page_flip(crtc->primary, fb, event, page_flip_flags);
 }
 
 static const struct drm_crtc_funcs ipu_crtc_funcs = {
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = drm_crtc_cleanup,
-	.page_flip = ipu_page_flip,
+	.page_flip = ipu_crtc_page_flip,
 };
 
 static int ipu_crtc_mode_set(struct drm_crtc *crtc,
@@ -248,37 +244,6 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
 				  x, y, mode->hdisplay, mode->vdisplay);
 }
 
-static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
-{
-	unsigned long flags;
-	struct drm_device *drm = ipu_crtc->base.dev;
-
-	spin_lock_irqsave(&drm->event_lock, flags);
-	if (ipu_crtc->page_flip_event)
-		drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event);
-	ipu_crtc->page_flip_event = NULL;
-	imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
-	spin_unlock_irqrestore(&drm->event_lock, flags);
-}
-
-static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
-{
-	struct ipu_crtc *ipu_crtc = dev_id;
-
-	imx_drm_handle_vblank(ipu_crtc->imx_crtc);
-
-	if (ipu_crtc->newfb) {
-		struct ipu_plane *plane = &ipu_crtc->plane[0];
-
-		ipu_crtc->newfb = NULL;
-		ipu_plane_set_base(plane, ipu_crtc->base.primary->fb,
-				   plane->x, plane->y);
-		ipu_crtc_handle_pageflip(ipu_crtc);
-	}
-
-	return IRQ_HANDLED;
-}
-
 static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
 				  const struct drm_display_mode *mode,
 				  struct drm_display_mode *adjusted_mode)
@@ -308,17 +273,26 @@ static struct drm_crtc_helper_funcs ipu_helper_funcs = {
 	.commit = ipu_crtc_commit,
 };
 
-static int ipu_enable_vblank(struct drm_crtc *crtc)
+static int ipu_enable_vblank(struct drm_crtc *crtc, int pipe)
 {
-	return 0;
+	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+	struct ipu_plane *ipu_plane = pipe_to_plane(ipu_crtc, pipe);
+
+	if (!ipu_plane)
+		return -EINVAL;
+
+	return ipu_plane_enable_vblank(ipu_plane);
 }
 
-static void ipu_disable_vblank(struct drm_crtc *crtc)
+static void ipu_disable_vblank(struct drm_crtc *crtc, int pipe)
 {
 	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+	struct ipu_plane *ipu_plane = pipe_to_plane(ipu_crtc, pipe);
+
+	if (!ipu_plane)
+		return;
 
-	ipu_crtc->page_flip_event = NULL;
-	ipu_crtc->newfb = NULL;
+	ipu_plane_disable_vblank(ipu_plane);
 }
 
 static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
@@ -465,8 +439,8 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 			 struct drm_device *drm)
 {
 	struct device_node *np = ipu_crtc->dev->of_node;
+	int id, primary_pipe, overlay_pipe;
 	int ret;
-	int id;
 
 	ret = ipu_get_resources(ipu_crtc, np);
 	if (ret) {
@@ -484,8 +458,11 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 	}
 
 	id = imx_drm_crtc_id(ipu_crtc->imx_crtc);
+	primary_pipe = imx_drm_primary_plane_pipe(ipu_crtc->imx_crtc);
+
 	ret = ipu_plane_init(&ipu_crtc->plane[0], drm,
 			     ipu_crtc->ipu,
+			     primary_pipe,
 			     ipu_crtc->ch->dma[0],
 			     ipu_crtc->ch->dp[0],
 			     BIT(id), true);
@@ -504,8 +481,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 
 	/* If this crtc is using the DP, add an overlay plane */
 	if (ipu_crtc->ch->dp[1] >= 0) {
+		overlay_pipe = imx_drm_overlay_plane_pipe(ipu_crtc->imx_crtc);
 		ret = ipu_plane_init(&ipu_crtc->plane[1], drm,
 				     ipu_crtc->ipu,
+				     overlay_pipe,
 				     ipu_crtc->ch->dma[1],
 				     ipu_crtc->ch->dp[1],
 				     BIT(id), false);
@@ -513,18 +492,8 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 		ipu_crtc->have_overlay = ret ? false : true;
 	}
 
-	ipu_crtc->irq = ipu_plane_irq(&ipu_crtc->plane[0]);
-	ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
-			"imx_drm", ipu_crtc);
-	if (ret < 0) {
-		dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
-		goto err_put_plane_res;
-	}
-
 	return 0;
 
-err_put_plane_res:
-	ipu_plane_put_resources(&ipu_crtc->plane[0]);
 err_remove_crtc:
 	imx_drm_remove_crtc(ipu_crtc->imx_crtc);
 err_put_resources:
diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c
index 51a257a..76ac178 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.c
+++ b/drivers/staging/imx-drm/ipuv3-plane.c
@@ -43,12 +43,6 @@ static const uint32_t ipu_plane_formats[] = {
 	DRM_FORMAT_NV16,
 };
 
-int ipu_plane_irq(struct ipu_plane *ipu_plane)
-{
-	return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
-				     IPU_IRQ_EOF);
-}
-
 static int calc_vref(struct drm_display_mode *mode)
 {
 	unsigned long htotal, vtotal;
@@ -67,8 +61,8 @@ static inline int calc_bandwidth(int width, int height, unsigned int vref)
 	return width * height * vref;
 }
 
-int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
-		       int x, int y)
+static int set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
+		    int bufnum, int x, int y)
 {
 	struct drm_gem_cma_object *cma_obj;
 	unsigned long eba;
@@ -79,15 +73,19 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
 		return -EFAULT;
 	}
 
-	dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
-		&cma_obj->paddr, x, y);
+	DRM_DEBUG_KMS("phys = %pad, x = %d, y = %d", &cma_obj->paddr, x, y);
 
 	ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
 
 	eba = cma_obj->paddr + fb->offsets[0] +
 	      fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
-	ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
-	ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
+
+	/* bufnum < 0 means set both buffer addresses */
+	if (bufnum < 0) {
+		ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
+		ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
+	} else
+		ipu_cpmem_set_buffer(ipu_plane->ipu_ch, bufnum, eba);
 
 	/* cache offsets for subsequent pageflips */
 	ipu_plane->x = x;
@@ -96,6 +94,62 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
 	return 0;
 }
 
+static int page_flip(struct ipu_plane *ipu_plane,
+		     struct drm_framebuffer *fb)
+{
+	int curbuf, ret;
+
+	curbuf = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
+	curbuf ^= 1;
+
+	DRM_DEBUG_KMS("pipe %d: page flip to %d\n", ipu_plane->pipe, curbuf);
+
+	ret = set_base(ipu_plane, fb, curbuf, ipu_plane->x, ipu_plane->y);
+	if (!ret)
+		ipu_idmac_select_buffer(ipu_plane->ipu_ch, curbuf);
+
+	return ret;
+}
+
+static void handle_page_flip_event(struct ipu_plane *ipu_plane)
+{
+	struct drm_device *drm = ipu_plane->base.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+	if (ipu_plane->page_flip_event)
+		drm_send_vblank_event(drm, -1, ipu_plane->page_flip_event);
+	ipu_plane->page_flip_event = NULL;
+	drm_vblank_put(drm, ipu_plane->pipe);
+	spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+static irqreturn_t ipu_plane_irq_handler(int irq, void *dev_id)
+{
+	struct ipu_plane *ipu_plane = dev_id;
+
+	drm_handle_vblank(ipu_plane->base.dev, ipu_plane->pipe);
+
+	if (ipu_plane->newfb) {
+		page_flip(ipu_plane, ipu_plane->newfb);
+		ipu_plane->newfb = NULL;
+		handle_page_flip_event(ipu_plane);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int ipu_plane_enable_vblank(struct ipu_plane *ipu_plane)
+{
+	return 0;
+}
+
+void ipu_plane_disable_vblank(struct ipu_plane *ipu_plane)
+{
+	ipu_plane->page_flip_event = NULL;
+	ipu_plane->newfb = NULL;
+}
+
 int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
 		       struct drm_display_mode *mode,
 		       struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -206,15 +260,31 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
 
 	ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
 
-	ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
+	/* enable double-buffering */
+	ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, true);
+
+	ret = set_base(ipu_plane, fb, -1, src_x, src_y);
 	if (ret < 0)
 		return ret;
 
+	/* cache width/height for subsequent pageflips */
+	ipu_plane->width = src_w;
+	ipu_plane->height = src_h;
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(ipu_plane->ipu_ch, 0);
+	ipu_idmac_select_buffer(ipu_plane->ipu_ch, 1);
+
 	return 0;
 }
 
 void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
 {
+	if (ipu_plane->irq) {
+		devm_free_irq(ipu_plane->base.dev->dev,
+			      ipu_plane->irq, ipu_plane);
+		ipu_plane->irq = 0;
+	}
 	if (!IS_ERR_OR_NULL(ipu_plane->dp))
 		ipu_dp_put(ipu_plane->dp);
 	if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
@@ -250,6 +320,18 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
 		}
 	}
 
+	ipu_plane->irq = ipu_idmac_channel_irq(ipu_plane->ipu,
+					       ipu_plane->ipu_ch,
+					       IPU_IRQ_EOF);
+	ret = devm_request_irq(ipu_plane->base.dev->dev,
+			       ipu_plane->irq, ipu_plane_irq_handler, 0,
+			       "imx_drm", ipu_plane);
+	if (ret < 0) {
+		DRM_ERROR("irq request failed with %d\n", ret);
+		ipu_plane->irq = 0;
+		goto err_out;
+	}
+
 	return 0;
 err_out:
 	ipu_plane_put_resources(ipu_plane);
@@ -267,6 +349,12 @@ void ipu_plane_enable(struct ipu_plane *ipu_plane)
 		ipu_dp_enable_channel(ipu_plane->dp);
 
 	ipu_plane->enabled = true;
+
+	/*
+	 * we need to wait up to one frame time after enabling
+	 * to allow the IPU's FSU to settle on an IDMAC buffer.
+	 */
+	usleep_range(50000, 50001);
 }
 
 void ipu_plane_disable(struct ipu_plane *ipu_plane)
@@ -416,6 +504,43 @@ static int ipu_plane_set_property(struct drm_plane *plane,
 	return ret;
 }
 
+int ipu_plane_page_flip(struct drm_plane *plane,
+			struct drm_framebuffer *fb,
+			struct drm_pending_vblank_event *event,
+			uint32_t flags)
+{
+	struct ipu_plane *ipu_plane = to_ipu_plane(plane);
+	int ret;
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	if (ipu_plane->newfb)
+		return -EBUSY;
+
+	if (ipu_plane->width > fb->width ||
+	    ipu_plane->height > fb->height ||
+	    ipu_plane->x > fb->width - ipu_plane->width ||
+	    ipu_plane->y > fb->height - ipu_plane->height) {
+		DRM_DEBUG_KMS("Invalid fb size %ux%u for plane %ux%u+%d+%d.\n",
+			      fb->width, fb->height,
+			      ipu_plane->width, ipu_plane->height,
+			      ipu_plane->x, ipu_plane->y);
+		return -ENOSPC;
+	}
+
+	ret = drm_vblank_get(ipu_plane->base.dev, ipu_plane->pipe);
+	if (ret) {
+		DRM_DEBUG_KMS("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	ipu_plane->newfb = fb;
+	ipu_plane->page_flip_event = event;
+	plane->fb = fb;
+
+	return 0;
+}
+
 static struct drm_plane_funcs ipu_plane_funcs = {
 	.update_plane	= ipu_update_plane,
 	.disable_plane	= ipu_disable_plane,
@@ -424,15 +549,16 @@ static struct drm_plane_funcs ipu_plane_funcs = {
 };
 
 int ipu_plane_init(struct ipu_plane *ipu_plane, struct drm_device *drm,
-		   struct ipu_soc *ipu, int dma, int dp,
+		   struct ipu_soc *ipu, int pipe, int dma, int dp,
 		   unsigned int possible_crtcs, bool priv)
 {
 	int ret;
 
-	DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
-		      dma, dp, possible_crtcs);
+	DRM_DEBUG_KMS("channel %d, dp flow %d, pipe %d, possible_crtcs=0x%x\n",
+		      dma, dp, pipe, possible_crtcs);
 
 	ipu_plane->ipu = ipu;
+	ipu_plane->pipe = pipe;
 	ipu_plane->dma = dma;
 	ipu_plane->dp_flow = dp;
 
diff --git a/drivers/staging/imx-drm/ipuv3-plane.h b/drivers/staging/imx-drm/ipuv3-plane.h
index 1487a08..4d856e9 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.h
+++ b/drivers/staging/imx-drm/ipuv3-plane.h
@@ -15,6 +15,7 @@ struct ipu_dp;
 
 struct ipu_plane {
 	struct drm_plane	base;
+	int			pipe;
 
 	struct ipu_soc		*ipu;
 	struct ipuv3_channel	*ipu_ch;
@@ -24,6 +25,8 @@ struct ipu_plane {
 	int			dma;
 	int			dp_flow;
 
+	int			width;
+	int			height;
 	int			x;
 	int			y;
 
@@ -34,11 +37,15 @@ struct ipu_plane {
 	bool			colorkey_en;
 	u32			colorkey;
 
+	struct drm_pending_vblank_event *page_flip_event;
+	struct drm_framebuffer	*newfb;
+	int			irq;
+
 	bool			enabled;
 };
 
 int ipu_plane_init(struct ipu_plane *ipu_plane, struct drm_device *drm,
-		   struct ipu_soc *ipu, int dma, int dp,
+		   struct ipu_soc *ipu, int pipe, int dma, int dp,
 		   unsigned int possible_crtcs, bool priv);
 
 /* Init IDMAC, DMFC, DP */
@@ -49,14 +56,18 @@ int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc,
 		       uint32_t src_x, uint32_t src_y, uint32_t src_w,
 		       uint32_t src_h);
 
+int ipu_plane_page_flip(struct drm_plane *plane,
+			struct drm_framebuffer *fb,
+			struct drm_pending_vblank_event *event,
+			uint32_t flags);
+
+int ipu_plane_enable_vblank(struct ipu_plane *ipu_plane);
+void ipu_plane_disable_vblank(struct ipu_plane *ipu_plane);
+
 void ipu_plane_enable(struct ipu_plane *plane);
 void ipu_plane_disable(struct ipu_plane *plane);
-int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb,
-		       int x, int y);
 
 int ipu_plane_get_resources(struct ipu_plane *plane);
 void ipu_plane_put_resources(struct ipu_plane *plane);
 
-int ipu_plane_irq(struct ipu_plane *plane);
-
 #endif
