diff mbox

[2/2] drm/exynos: release fb pended by page flip

Message ID 1352883576-25552-2-git-send-email-inki.dae@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Inki Dae Nov. 14, 2012, 8:59 a.m. UTC
This patch releases the fb pended by page flip after fbdev is
restored propely. And fixes invalid memory access when drm is
released while doing pageflip.

This patch makes fb's refcount to be increased when setcrtc and
pageflip are requested. In other words, it increases fb's refcount
only if dma is going to access memory region to fb and decreases
old fb's because the old fb isn't accessed by dma anymore.

This could guarantee releasing gem buffer to the fb after dma
access to the gem buffer has been completed.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_crtc.c  |   42 ++++++++++++++++++++++++++++-
 drivers/gpu/drm/exynos/exynos_drm_fb.c    |   23 ++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_fb.h    |    6 ++++
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |    9 ++++++
 drivers/gpu/drm/exynos/exynos_drm_plane.c |   18 +++++++++++-
 5 files changed, 96 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 2efa4b0..2a52b7e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -32,6 +32,7 @@ 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_encoder.h"
 #include "exynos_drm_plane.h"
+#include "exynos_drm_fb.h"
 
 #define to_exynos_crtc(x)	container_of(x, struct exynos_drm_crtc,\
 				drm_crtc)
@@ -137,8 +138,26 @@  exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 		return ret;
 
 	plane->crtc = crtc;
+
+	/*
+	 * If old_fb exists, unreference the fb.
+	 *
+	 * This means that memory region to the fb isn't accessed by the dma
+	 * of this plane anymore.
+	 */
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
 	plane->fb = crtc->fb;
 
+	/*
+	 * Take a reference to new fb.
+	 *
+	 * Taking a reference means that this plane's dma is going to access
+	 * memory region to the new fb.
+	 */
+	drm_framebuffer_reference(plane->fb);
+
 	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
 
 	return 0;
@@ -168,6 +187,24 @@  static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 				    x, y, crtc_w, crtc_h);
 	if (ret)
 		return ret;
+	/*
+	 * If old_fb exists, unreference the fb.
+	 *
+	 * This means that memory region to the fb isn't accessed by the dma
+	 * of this plane anymore.
+	 */
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	plane->fb = crtc->fb;
+
+	/*
+	 * Take a reference to new fb.
+	 *
+	 * Taking a reference means that this plane's dma is going to access
+	 * memory region to the new fb.
+	 */
+	drm_framebuffer_reference(plane->fb);
 
 	exynos_drm_crtc_commit(crtc);
 
@@ -243,7 +280,7 @@  static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
 
 		crtc->fb = fb;
 		ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
-						    NULL);
+						    old_fb);
 		if (ret) {
 			crtc->fb = old_fb;
 
@@ -254,6 +291,9 @@  static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
 
 			goto out;
 		}
+
+		exynos_drm_fb_set_pending(old_fb, false);
+		exynos_drm_fb_set_pending(fb, true);
 	}
 out:
 	mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 4ef4cd3..f100035 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -43,11 +43,15 @@ 
  * @fb: drm framebuffer obejct.
  * @buf_cnt: a buffer count to drm framebuffer.
  * @exynos_gem_obj: array of exynos specific gem object containing a gem object.
+ * @pending: indicate whehter a fb was pended by page flip.
+ *	if true, the fb should be released after fbdev is restored to avoid
+ *	accessing invalid memory region.
  */
 struct exynos_drm_fb {
 	struct drm_framebuffer		fb;
 	unsigned int			buf_cnt;
 	struct exynos_drm_gem_obj	*exynos_gem_obj[MAX_FB_BUFFER];
+	bool				pending;
 };
 
 static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
@@ -228,6 +232,25 @@  exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
 	return fb;
 }
 
+void exynos_drm_fb_set_pending(struct drm_framebuffer *fb, bool pending)
+{
+	struct exynos_drm_fb *exynos_fb;
+
+	exynos_fb = to_exynos_fb(fb);
+
+	exynos_fb->pending = pending;
+}
+
+void exynos_drm_release_pended_fb(struct drm_framebuffer *fb)
+{
+	struct exynos_drm_fb *exynos_fb;
+
+	exynos_fb = to_exynos_fb(fb);
+
+	if (exynos_fb->pending)
+		drm_framebuffer_unreference(fb);
+}
+
 struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
 						int index)
 {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h
index 96262e5..6b63bb1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h
@@ -33,6 +33,12 @@  exynos_drm_framebuffer_init(struct drm_device *dev,
 			    struct drm_mode_fb_cmd2 *mode_cmd,
 			    struct drm_gem_object *obj);
 
+/* set fb->pending variable to desired value. */
+void exynos_drm_fb_set_pending(struct drm_framebuffer *fb, bool pending);
+
+/* release a fb pended by page flip. */
+void exynos_drm_release_pended_fb(struct drm_framebuffer *fb);
+
 /* get memory information of a drm framebuffer */
 struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
 						 int index);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index e7466c4..62f3b9e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -314,9 +314,18 @@  void exynos_drm_fbdev_fini(struct drm_device *dev)
 void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
 {
 	struct exynos_drm_private *private = dev->dev_private;
+	struct drm_framebuffer *fb, *fbt;
 
 	if (!private || !private->fb_helper)
 		return;
 
 	drm_fb_helper_restore_fbdev_mode(private->fb_helper);
+
+	mutex_lock(&dev->mode_config.mutex);
+
+	/* Release fb pended by page flip. */
+	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head)
+		exynos_drm_release_pended_fb(fb);
+
+	mutex_unlock(&dev->mode_config.mutex);
 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 862ca1e..37a2f68 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -203,8 +203,24 @@  exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	if (ret < 0)
 		return ret;
 
-	plane->crtc = crtc;
+	/*
+	 * If this plane already has a fb, unreference the fb.
+	 *
+	 * This means that memory region to the fb isn't accessed by the dma
+	 * of this plane anymore.
+	 */
+	if (plane->fb)
+		drm_framebuffer_unreference(plane->fb);
+
+	/*
+	 * Take a reference to new fb.
+	 *
+	 * Taking a reference means that this plane's dma is going to access
+	 * memory region to the new fb.
+	 */
+	drm_framebuffer_reference(fb);
 
+	plane->crtc = crtc;
 	exynos_plane_commit(plane);
 	exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);