#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
+enum omap_page_flip_state {
+ OMAP_PAGE_FLIP_IDLE,
+ OMAP_PAGE_FLIP_WAIT,
+ OMAP_PAGE_FLIP_QUEUED,
+ OMAP_PAGE_FLIP_CANCELLED,
+};
+
struct omap_crtc {
struct drm_crtc base;
struct list_head pending_unpins;
/*
- * The flip_pending flag indicates if a page flip has been queued and
- * hasn't completed yet. The flip event, if any, is stored in
- * flip_event.
+ * flip_state flag indicates the current page flap state: IDLE if no
+ * page queue has been submitted, WAIT when waiting for GEM async
+ * completion, QUEUED when the page flip has been queued to the hardware
+ * or CANCELLED when the CRTC is turned off before the flip gets queued
+ * to the hardware. The flip event, if any, is stored in flip_event. The
+ * flip_wait wait queue is used to wait for page flip completion.
*
* The flip_work work queue handles page flip requests without caring
* about what context the GEM async callback is called from. Possibly we
* should just make omap_gem always call the cb from the worker so we
* don't have to care about this.
*/
- bool flip_pending;
+ enum omap_page_flip_state flip_state;
struct drm_pending_vblank_event *flip_event;
struct work_struct flip_work;
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+/* Must be called with dev->event_lock locked. */
+static void omap_crtc_complete_page_flip(struct drm_crtc *crtc,
+ enum omap_page_flip_state state)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+
+ if (omap_crtc->flip_event) {
+ drm_send_vblank_event(dev, omap_crtc->pipe,
+ omap_crtc->flip_event);
+ omap_crtc->flip_event = NULL;
+ }
+
+ omap_crtc->flip_state = state;
+}
+
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_crtc *omap_crtc =
DBG("%s: apply done", omap_crtc->name);
__omap_irq_unregister(dev, &omap_crtc->vblank_irq);
- spin_lock_irqsave(&dev->event_lock, flags);
-
/* wakeup userspace */
- if (omap_crtc->flip_event)
- drm_send_vblank_event(dev, omap_crtc->pipe,
- omap_crtc->flip_event);
-
- omap_crtc->flip_event = NULL;
- omap_crtc->flip_pending = false;
-
+ spin_lock_irqsave(&dev->event_lock, flags);
+ omap_crtc_complete_page_flip(&omap_crtc->base, OMAP_PAGE_FLIP_IDLE);
spin_unlock_irqrestore(&dev->event_lock, flags);
complete(&omap_crtc->completion);
container_of(work, struct omap_crtc, flip_work);
struct drm_crtc *crtc = &omap_crtc->base;
struct drm_display_mode *mode = &crtc->mode;
+ struct drm_device *dev = crtc->dev;
+ struct drm_framebuffer *fb;
struct drm_gem_object *bo;
+ unsigned long flags;
+ bool queue_flip;
drm_modeset_lock(&crtc->mutex, NULL);
- omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
- 0, 0, mode->hdisplay, mode->vdisplay,
- crtc->x, crtc->y, mode->hdisplay, mode->vdisplay);
- omap_crtc_flush(crtc);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ /*
+ * The page flip could have been cancelled while waiting for the GEM
+ * async operation to complete. Don't queue the flip in that case.
+ */
+ if (omap_crtc->flip_state == OMAP_PAGE_FLIP_WAIT) {
+ omap_crtc->flip_state = OMAP_PAGE_FLIP_QUEUED;
+ queue_flip = true;
+ } else {
+ omap_crtc->flip_state = OMAP_PAGE_FLIP_IDLE;
+ queue_flip = false;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ fb = crtc->primary->fb;
+
+ if (queue_flip) {
+ omap_plane_mode_set(crtc->primary, crtc, fb,
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ crtc->x, crtc->y, mode->hdisplay,
+ mode->vdisplay);
+ omap_crtc_flush(crtc);
+ }
+
drm_modeset_unlock(&crtc->mutex);
- bo = omap_framebuffer_bo(crtc->primary->fb, 0);
+ bo = omap_framebuffer_bo(fb, 0);
drm_gem_object_unreference_unlocked(bo);
drm_framebuffer_unreference(crtc->primary->fb);
}
spin_lock_irqsave(&dev->event_lock, flags);
- if (omap_crtc->flip_pending) {
+ if (omap_crtc->flip_state != OMAP_PAGE_FLIP_IDLE) {
spin_unlock_irqrestore(&dev->event_lock, flags);
dev_err(dev->dev, "already a pending flip\n");
return -EBUSY;
}
omap_crtc->flip_event = event;
- omap_crtc->flip_pending = true;
+ omap_crtc->flip_state = OMAP_PAGE_FLIP_WAIT;
primary->fb = fb;
drm_framebuffer_reference(fb);