[PATCH 51/72] imx-drm: Move page flip handling to plane driver

Steve Longerbeam slongerbeam at gmail.com
Fri Oct 31 15:54:34 PDT 2014


Move page flip handling and associated vblank handling to the
plane driver. This paves the way to allow page flipping in not
just the primary plane but the overlay plane as well.

To do this the primary and overlay planes are assigned a pipe value
suitable for passing to the drm core vblank methods (drm_vblank_get,
put,handle).

We modify imx-drm core slightly to make room for primary and overlay
pipe id's when assigning the crtc pipe. So crtc 0 has a primary pipe
value of 0 and overlay pipe value 1, crtc 1 has primary pipe value 2
and overlay pipe value 3, etc. imx_drm_crtc_id() returns the primary
pipe divided by 2 as an id value to form possible crtc masks.

The drm core vblank sees these planes as additional crtcs (pipes) and
does not need significant changes.

The plane driver implements page flip using idmac double-buffering.
Without double-buffering, a page flip operation changes the idmac
channel buffer base address while the buffer is still active. This
is not recommended according to the reference manual.

With double-buffering, a page-flip can set the base of the inactive
buffer before switching to it using buffer-ready control.

Note that for the synchronous display channels, the buffer ready signal can
be driven either by the ARM core writing to the buffer ready registers,
or by the DI.

When the ARM core does not drive ping-pong buffer ready selection at
every EOF, the DI will drive buffer ready, triggered by a timer located
in the DI. The IPU's FSU unit will quickly settle on a single buffer and
then remain at that buffer, the DI driving buffer ready at that buffer.

If the ARM core (the plane driver) then selects the other (inactive)
buffer, the FSU switches to it and then remains at this buffer, the DI
again taking over buffer ready signalling at the new buffer, until the
ARM core makes the next buffer selection.

So we take advantage of this behaviour and use buffer selection to
switch to the other buffer when we want to flip to another DRM
framebuffer for page-flip.

Signed-off-by: Steve Longerbeam <steve_longerbeam at mentor.com>
---
 drivers/staging/imx-drm/imx-drm-core.c |   86 +++++++++--------
 drivers/staging/imx-drm/imx-drm.h      |   10 +-
 drivers/staging/imx-drm/ipuv3-crtc.c   |  111 ++++++++--------------
 drivers/staging/imx-drm/ipuv3-plane.c  |  158 ++++++++++++++++++++++++++++----
 drivers/staging/imx-drm/ipuv3-plane.h  |   21 ++++-
 5 files changed, 252 insertions(+), 134 deletions(-)

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



More information about the dri-devel mailing list