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