--- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -36,12 +36,15 @@ struct omap_crtc { struct videomode vm; - bool ignore_digit_sync_lost; + bool ignore_sync_lost; bool enabled; bool pending; wait_queue_head_t pending_wait; struct drm_pending_vblank_event *event; + + struct work_struct error_work; + u32 error_channels; }; /* ----------------------------------------------------------------------------- @@ -157,7 +160,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) * Digit output produces some sync lost interrupts during the * first frame when enabling, so we need to ignore those. */ - omap_crtc->ignore_digit_sync_lost = true; + omap_crtc->ignore_sync_lost = true; } framedone_irq = priv->dispc_ops->mgr_get_framedone_irq(channel); @@ -191,7 +194,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) } if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { - omap_crtc->ignore_digit_sync_lost = false; + omap_crtc->ignore_sync_lost = false; /* make sure the irq handler sees the value above */ mb(); } @@ -263,17 +266,65 @@ static const struct dss_mgr_ops mgr_ops = { * Setup, Flush and Page Flip */ +static void omap_crtc_error_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = container_of(work, struct omap_crtc, error_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = omap_crtc->base.dev; + struct omap_drm_private *priv = dev->dev_private; + + drm_modeset_lock(&crtc->mutex, NULL); + + dev_warn(dev->dev, "sync lost on %s, enabling & disabling...\n", + omap_crtc->name); + + priv->dispc_ops->mgr_enable(omap_crtc->channel, false); + + msleep(50); + dev_warn(dev->dev, "sync lost enabling %s\n", + omap_crtc->name); + + priv->dispc_ops->mgr_enable(omap_crtc->channel, true); + + msleep(50); + + dev_warn(dev->dev, "sync lost recovery done on on %s\n", + omap_crtc->name); + + omap_crtc->ignore_sync_lost = false; + /* make sure the irq handler sees the value above */ + mb(); + + drm_modeset_unlock(&crtc->mutex); +} + void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = omap_crtc->base.dev; + struct omap_drm_private *priv = dev->dev_private; + enum omap_channel channel = omap_crtc_channel(crtc); + u32 sync_lost_irq; + bool sync_lost; + + sync_lost_irq = priv->dispc_ops->mgr_get_sync_lost_irq(channel); - if (omap_crtc->ignore_digit_sync_lost) { - irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; - if (!irqstatus) - return; + sync_lost = irqstatus & sync_lost_irq; + + if (sync_lost) { + if (omap_crtc->ignore_sync_lost) { + irqstatus &= ~sync_lost_irq; + } else { + /* error worker will set this to false */ + omap_crtc->ignore_sync_lost = true; + schedule_work(&omap_crtc->error_work); + } } - DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); + if (!irqstatus) + return; + + printk("%s: errors: %08x\n", omap_crtc->name, irqstatus); } void omap_crtc_vblank_irq(struct drm_crtc *crtc) @@ -612,6 +663,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, init_waitqueue_head(&omap_crtc->pending_wait); + INIT_WORK(&omap_crtc->error_work, omap_crtc_error_worker); + omap_crtc->channel = channel; omap_crtc->name = channel_names[channel];