Commit 15d02e92 authored by Laurent Pinchart's avatar Laurent Pinchart

drm: omapdrm: Rework page flip handling

To implement proper vblank control the driver will need to wait for page
flip completion before disabling the vblank interrupt. This is made
complex by the page flip implementation which queues and submits page
flips to the hardware in two separate steps between which DRM locks are
released. We thus need to avoid waiting on a page flip that has been
queued but not submitted as submission and wait are covered by the same
lock.

Rework page flip handling as a first step by splitting the flip_pending
boolean variable into an enumerated state and moving between states
based on flip queue, submission and completion. The CANCELLED state will
be used in a second step.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
parent 1d5e5ea1
...@@ -28,6 +28,13 @@ ...@@ -28,6 +28,13 @@
#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) #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 omap_crtc {
struct drm_crtc base; struct drm_crtc base;
...@@ -55,16 +62,19 @@ struct omap_crtc { ...@@ -55,16 +62,19 @@ struct omap_crtc {
struct list_head pending_unpins; struct list_head pending_unpins;
/* /*
* The flip_pending flag indicates if a page flip has been queued and * flip_state flag indicates the current page flap state: IDLE if no
* hasn't completed yet. The flip event, if any, is stored in * page queue has been submitted, WAIT when waiting for GEM async
* flip_event. * 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 * The flip_work work queue handles page flip requests without caring
* about what context the GEM async callback is called from. Possibly we * 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 * should just make omap_gem always call the cb from the worker so we
* don't have to care about this. * 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 drm_pending_vblank_event *flip_event;
struct work_struct flip_work; struct work_struct flip_work;
...@@ -285,6 +295,22 @@ void omap_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) ...@@ -285,6 +295,22 @@ void omap_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
spin_unlock_irqrestore(&dev->event_lock, flags); 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) static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{ {
struct omap_crtc *omap_crtc = struct omap_crtc *omap_crtc =
...@@ -312,16 +338,9 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) ...@@ -312,16 +338,9 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
DBG("%s: apply done", omap_crtc->name); DBG("%s: apply done", omap_crtc->name);
__omap_irq_unregister(dev, &omap_crtc->vblank_irq); __omap_irq_unregister(dev, &omap_crtc->vblank_irq);
spin_lock_irqsave(&dev->event_lock, flags);
/* wakeup userspace */ /* wakeup userspace */
if (omap_crtc->flip_event) spin_lock_irqsave(&dev->event_lock, flags);
drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc_complete_page_flip(&omap_crtc->base, OMAP_PAGE_FLIP_IDLE);
omap_crtc->flip_event);
omap_crtc->flip_event = NULL;
omap_crtc->flip_pending = false;
spin_unlock_irqrestore(&dev->event_lock, flags); spin_unlock_irqrestore(&dev->event_lock, flags);
complete(&omap_crtc->completion); complete(&omap_crtc->completion);
...@@ -533,16 +552,41 @@ static void page_flip_worker(struct work_struct *work) ...@@ -533,16 +552,41 @@ static void page_flip_worker(struct work_struct *work)
container_of(work, struct omap_crtc, flip_work); container_of(work, struct omap_crtc, flip_work);
struct drm_crtc *crtc = &omap_crtc->base; struct drm_crtc *crtc = &omap_crtc->base;
struct drm_display_mode *mode = &crtc->mode; struct drm_display_mode *mode = &crtc->mode;
struct drm_device *dev = crtc->dev;
struct drm_framebuffer *fb;
struct drm_gem_object *bo; struct drm_gem_object *bo;
unsigned long flags;
bool queue_flip;
drm_modeset_lock(&crtc->mutex, NULL); drm_modeset_lock(&crtc->mutex, NULL);
omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay, spin_lock_irqsave(&dev->event_lock, flags);
crtc->x, crtc->y, mode->hdisplay, mode->vdisplay); /*
omap_crtc_flush(crtc); * 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); 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_gem_object_unreference_unlocked(bo);
drm_framebuffer_unreference(crtc->primary->fb); drm_framebuffer_unreference(crtc->primary->fb);
} }
...@@ -573,14 +617,14 @@ static int omap_crtc_page_flip(struct drm_crtc *crtc, ...@@ -573,14 +617,14 @@ static int omap_crtc_page_flip(struct drm_crtc *crtc,
spin_lock_irqsave(&dev->event_lock, flags); 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); spin_unlock_irqrestore(&dev->event_lock, flags);
dev_err(dev->dev, "already a pending flip\n"); dev_err(dev->dev, "already a pending flip\n");
return -EBUSY; return -EBUSY;
} }
omap_crtc->flip_event = event; omap_crtc->flip_event = event;
omap_crtc->flip_pending = true; omap_crtc->flip_state = OMAP_PAGE_FLIP_WAIT;
primary->fb = fb; primary->fb = fb;
drm_framebuffer_reference(fb); drm_framebuffer_reference(fb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment