diff options
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_crtc.c | 78 | ||||
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_drv.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_irq.c | 4 |
3 files changed, 79 insertions, 8 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index c086f72e488d..1076bc0a7f78 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -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); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index cf1b37e5374b..e81fbc07ea9b 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -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"); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 803aff0db768..249c0330d6ce 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -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) |