@@ -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);
@@ -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)
{
@@ -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);
@@ -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);
}
@@ -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);