drm/exynos: wait for vsync before removing FB
authorPrathyush K <prathyush.k@samsung.com>
Tue, 26 Jun 2012 14:36:51 +0000 (20:06 +0530)
committerGerrit <chrome-bot@google.com>
Fri, 29 Jun 2012 19:30:31 +0000 (12:30 -0700)
While removing a framebuffer, we wait for a vsync interrupt. This is
required if a new framebuffer has been just set and has not yet been
updated to the actual registers. In this case, if we remove the older
buffer, we get a page fault, since the older buffer is still being used.
A framebuffer can be safely removed only after receiving a vsync.

This is also required when we try to remove the current framebuffer.
FIMD gets disabled but has to wait for a vsync interrupt before the
disabling takes effect.

BUG=chrome-os-partner:10728, chrome-os-partner:10813
TEST='stop ui/pkill -9 X' and no page fault. Tested on Daisy/Snow.

Change-Id: Ib779526b7d116a8f546178e4fbd1a7d036be2771
Signed-off-by: Prathyush K <prathyush.k@samsung.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/26382
Tested-by: Prathyush Kalashwaram <prathyush@chromium.org>
Reviewed-by: Alim Akhtar <alim.akhtar@samsung.com>
Commit-Ready: Doug Anderson <dianders@chromium.org>
Reviewed-by: Doug Anderson <dianders@chromium.org>
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_fb.c
drivers/gpu/drm/exynos/exynos_drm_fimd.c

index 2c53254..821710d 100644 (file)
@@ -63,6 +63,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
                return -ENOMEM;
        }
 
+       DRM_INIT_WAITQUEUE(&private->wait_vsync_queue);
+       atomic_set(&private->wait_vsync_event, 0);
+
        INIT_LIST_HEAD(&private->pageflip_event_list);
        dev->dev_private = (void *)private;
 
index 982bf2d..28c9b68 100644 (file)
@@ -215,6 +215,14 @@ struct exynos_drm_manager {
 struct exynos_drm_private {
        struct drm_fb_helper *fb_helper;
 
+       /*
+        * wait_vsync_event is set to zero by crtc whenever a VSYNC interrupt
+        * is received. After setting wait_vsync_event to 0, wait_vsync_queue
+        * is woken up.
+        */
+       wait_queue_head_t wait_vsync_queue;
+       atomic_t wait_vsync_event;
+
        /* list head for new event to be added. */
        struct list_head pageflip_event_list;
 
index c38c8f4..cc05e3d 100644 (file)
@@ -48,6 +48,27 @@ struct exynos_drm_fb {
        struct exynos_drm_gem_obj       *exynos_gem_obj[MAX_FB_BUFFER];
 };
 
+static void exynos_drm_wait_for_vsync(struct drm_device *drm_dev)
+{
+       struct exynos_drm_private *dev_priv = drm_dev->dev_private;
+
+       atomic_set(&dev_priv->wait_vsync_event, 1);
+
+       /*
+        * wait for FIMD to signal VSYNC interrupt or return after
+        * timeout which is set to 50ms (refresh rate of 20)
+        * Cannot use DRM_WAIT_ON or wait_event_interruptible_timeout
+        * here since they exit if there is a signal pending. This
+        * happens when X is killed and DRM release is called which
+        * makes these functions return without waiting.
+        */
+       wait_event_timeout(dev_priv->wait_vsync_queue,
+                               !atomic_read(&dev_priv->wait_vsync_event),
+                               DRM_HZ/20);
+
+       /* TODO: Add wait for vsync for HDMI*/
+}
+
 static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
 {
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
@@ -56,6 +77,9 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
 
        drm_framebuffer_cleanup(fb);
 
+       /* wait for vsync from CRTC to safely remove a FB*/
+       exynos_drm_wait_for_vsync(fb->dev);
+
        kfree(exynos_fb);
        exynos_fb = NULL;
 }
index 592fa59..44684f8 100644 (file)
@@ -627,6 +627,10 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
                        drm_vblank_off(drm_dev, crtc);
        }
 
+       /* set wait vsync event to zero and wake up queue. */
+       atomic_set(&dev_priv->wait_vsync_event, 0);
+       DRM_WAKEUP(&dev_priv->wait_vsync_queue);
+
        spin_unlock_irqrestore(&drm_dev->event_lock, flags);
 }