@@ -76,6 +76,7 @@ struct omap_crtc {
*/
enum omap_page_flip_state flip_state;
struct drm_pending_vblank_event *flip_event;
+ wait_queue_head_t flip_wait;
struct work_struct flip_work;
struct completion completion;
@@ -309,6 +310,61 @@ static void omap_crtc_complete_page_flip(struct drm_crtc *crtc,
}
omap_crtc->flip_state = state;
+
+ if (state == OMAP_PAGE_FLIP_IDLE)
+ wake_up(&omap_crtc->flip_wait);
+}
+
+static bool omap_crtc_page_flip_pending(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ unsigned long flags;
+ bool pending;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ pending = omap_crtc->flip_state != OMAP_PAGE_FLIP_IDLE;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ return pending;
+}
+
+static void omap_crtc_wait_page_flip(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ bool cancelled = false;
+ unsigned long flags;
+
+ /*
+ * If we're still waiting for the GEM async operation to complete just
+ * cancel the page flip, as we're holding the CRTC mutex preventing the
+ * page flip work handler from queueing the page flip.
+ *
+ * We can't release the reference to the frame buffer here as the async
+ * operation doesn't keep its own reference to the buffer. We'll just
+ * let the page flip work queue handle that.
+ */
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (omap_crtc->flip_state == OMAP_PAGE_FLIP_WAIT) {
+ omap_crtc_complete_page_flip(crtc, OMAP_PAGE_FLIP_CANCELLED);
+ cancelled = true;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ if (cancelled)
+ return;
+
+ if (wait_event_timeout(omap_crtc->flip_wait,
+ !omap_crtc_page_flip_pending(crtc),
+ msecs_to_jiffies(50)))
+ return;
+
+ dev_warn(crtc->dev->dev, "page flip timeout!\n");
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ omap_crtc_complete_page_flip(crtc, OMAP_PAGE_FLIP_IDLE);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
}
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
@@ -455,26 +511,39 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct omap_drm_private *priv = crtc->dev->dev_private;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- bool enabled = (mode == DRM_MODE_DPMS_ON);
+ bool enable = (mode == DRM_MODE_DPMS_ON);
int i;
DBG("%s: %d", omap_crtc->name, mode);
- if (enabled == omap_crtc->enabled)
+ if (enable == omap_crtc->enabled)
return;
+ if (!enable) {
+ omap_crtc_wait_page_flip(crtc);
+ dispc_runtime_get();
+ drm_crtc_vblank_off(crtc);
+ dispc_runtime_put();
+ }
+
/* Enable/disable all planes associated with the CRTC. */
for (i = 0; i < priv->num_planes; i++) {
struct drm_plane *plane = priv->planes[i];
if (plane->crtc == crtc)
- WARN_ON(omap_plane_set_enable(plane, enabled));
+ WARN_ON(omap_plane_set_enable(plane, enable));
}
- omap_crtc->enabled = enabled;
+ omap_crtc->enabled = enable;
omap_crtc_setup(crtc);
omap_crtc_flush(crtc);
+
+ if (enable) {
+ dispc_runtime_get();
+ drm_crtc_vblank_on(crtc);
+ dispc_runtime_put();
+ }
}
static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -709,6 +778,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
crtc = &omap_crtc->base;
INIT_WORK(&omap_crtc->flip_work, page_flip_worker);
+ init_waitqueue_head(&omap_crtc->flip_wait);
INIT_LIST_HEAD(&omap_crtc->pending_unpins);
@@ -502,6 +502,7 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
{
struct omap_drm_platform_data *pdata = dev->dev->platform_data;
struct omap_drm_private *priv;
+ unsigned int i;
int ret;
DBG("load: dev=%p", dev);
@@ -529,10 +530,14 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
return ret;
}
+ /* Initialize vblank handling, start with all CRTCs disabled. */
ret = drm_vblank_init(dev, priv->num_crtcs);
if (ret)
dev_warn(dev->dev, "could not init vblank\n");
+ for (i = 0; i < priv->num_crtcs; i++)
+ drm_crtc_vblank_off(priv->crtcs[i]);
+
priv->fbdev = omap_fbdev_init(dev);
if (!priv->fbdev) {
dev_warn(dev->dev, "omap_fbdev_init failed\n");
@@ -152,12 +152,10 @@ int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id)
DBG("dev=%p, crtc=%d", dev, crtc_id);
- dispc_runtime_get();
spin_lock_irqsave(&list_lock, flags);
priv->vblank_mask |= pipe2vbl(crtc);
omap_irq_update(dev);
spin_unlock_irqrestore(&list_lock, flags);
- dispc_runtime_put();
return 0;
}
@@ -179,12 +177,10 @@ void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id)
DBG("dev=%p, crtc=%d", dev, crtc_id);
- dispc_runtime_get();
spin_lock_irqsave(&list_lock, flags);
priv->vblank_mask &= ~pipe2vbl(crtc);
omap_irq_update(dev);
spin_unlock_irqrestore(&list_lock, flags);
- dispc_runtime_put();
}
static irqreturn_t omap_irq_handler(int irq, void *arg)