drm: hdlcd: Don't depend on VSYNC interrupt when setting new framebuffer.
authorLiviu Dudau <Liviu.Dudau@arm.com>
Tue, 22 Jul 2014 17:46:29 +0000 (18:46 +0100)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Tue, 22 Jul 2014 17:50:18 +0000 (18:50 +0100)
VSYNC state is influenced by the DRM framework and cannot be relied
on when setting a new framebuffer base address and synchronising with
the hardware. Use the DMA_END interrupt as it is a more reliable way
of signaling the end of frame utilisation.

Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
drivers/gpu/drm/arm/hdlcd_crtc.c
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/hdlcd_drv.h
drivers/gpu/drm/arm/hdlcd_fb.c
drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c

index 17c006ee3522b41e94d4dfd528d7de8cf5f45fbc..791cd3af1786b1dd6f160d763c24ee941e3b25e4 100644 (file)
@@ -47,14 +47,17 @@ void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd, bool wait)
 
        hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
 
-       if (wait) {
+       if (wait && hdlcd->dpms == DRM_MODE_DPMS_ON) {
                drm_vblank_get(fb->dev, 0);
-               reinit_completion(&hdlcd->vsync_completion);
+               reinit_completion(&hdlcd->frame_completion);
                do {
-                       ret = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
-                                                       msecs_to_jiffies(1000));
+                       ret = wait_for_completion_interruptible_timeout(&hdlcd->frame_completion,
+                                                       msecs_to_jiffies(50));
                } while (ret <= 0);
                drm_vblank_put(fb->dev, 0);
+       } else {
+               dev_info(fb->dev->dev, "%s: wait called with DPMS set to %d\n",
+                       __func__, hdlcd->dpms);
        }
 }
 
@@ -76,7 +79,9 @@ static int hdlcd_crtc_page_flip(struct drm_crtc *crtc,
 
        crtc->primary->fb = fb;
 
-       if (hdlcd->dpms != DRM_MODE_DPMS_ON) {
+       if (hdlcd->dpms == DRM_MODE_DPMS_ON) {
+               hdlcd_set_scanout(hdlcd, true);
+       } else {
                unsigned long flags;
 
                /* not active, update registers immediately */
@@ -85,8 +90,6 @@ static int hdlcd_crtc_page_flip(struct drm_crtc *crtc,
                if (event)
                        drm_send_vblank_event(crtc->dev, 0, event);
                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-       } else {
-               hdlcd_set_scanout(hdlcd, true);
        }
 
        return 0;
index a831b8670395828a82d18c5b45d12f0052b0f2bd..e00f9d73e4bb36dc7b80f67e674201710b2c0aec 100644 (file)
@@ -156,7 +156,7 @@ static int hdlcd_load(struct drm_device *dev, unsigned long flags)
                goto fail;
        }
 
-       init_completion(&hdlcd->vsync_completion);
+       init_completion(&hdlcd->frame_completion);
        ret = drm_vblank_init(dev, 1);
        if (ret < 0) {
                dev_err(dev->dev, "failed to initialise vblank\n");
@@ -224,7 +224,10 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
                        drm_vblank_put(dev, 0);
                }
                spin_unlock_irqrestore(&dev->event_lock, flags);
-               complete_all(&hdlcd->vsync_completion);
+       }
+       if (irq_status & HDLCD_INTERRUPT_DMA_END) {
+               // send completion when reading the frame has finished
+               complete_all(&hdlcd->frame_completion);
        }
 
        /* acknowledge interrupt(s) */
@@ -243,15 +246,18 @@ static void hdlcd_irq_preinstall(struct drm_device *dev)
 
 static int hdlcd_irq_postinstall(struct drm_device *dev)
 {
-#ifdef CONFIG_DEBUG_FS
        struct hdlcd_drm_private *hdlcd = dev->dev_private;
-       unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
+       unsigned int irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
 
+#ifdef CONFIG_DEBUG_FS
        /* enable debug interrupts */
        irq_mask |= HDLCD_DEBUG_INT_MASK;
+#endif
 
+       /* enable DMA completion interrupts */
+       irq_mask |= HDLCD_INTERRUPT_DMA_END;
        hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
-#endif
+
        return 0;
 }
 
@@ -259,15 +265,15 @@ static void hdlcd_irq_uninstall(struct drm_device *dev)
 {
        struct hdlcd_drm_private *hdlcd = dev->dev_private;
        /* disable all the interrupts that we might have enabled */
-       unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
+       unsigned int irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
 
 #ifdef CONFIG_DEBUG_FS
        /* disable debug interrupts */
        irq_mask &= ~HDLCD_DEBUG_INT_MASK;
 #endif
 
-       /* disable vsync interrupts */
-       irq_mask &= ~HDLCD_INTERRUPT_VSYNC;
+       /* disable vsync and dma interrupts */
+       irq_mask &= ~(HDLCD_INTERRUPT_VSYNC | HDLCD_INTERRUPT_DMA_END);
 
        hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
 }
index 455da17c250764da19793cc52a54bb483a7b67fd..3fc3bddbb841205296101d7a3bbb83206392de14 100644 (file)
@@ -19,7 +19,7 @@ struct hdlcd_drm_private {
        struct drm_pending_vblank_event *event;
        struct drm_crtc                 crtc;
        struct device_node              *slave_node;
-       struct completion               vsync_completion;
+       struct completion               frame_completion;
 #ifdef CONFIG_DEBUG_FS
        atomic_t buffer_underrun_count;
        atomic_t bus_error_count;
index ca67e6085a921a76e9a6e375e2a37f1bc1d417e7..5165ece55b72195e9b010bd73b69c597d5f78537 100644 (file)
@@ -253,10 +253,10 @@ static int hdlcd_wait_for_vsync(struct fb_info *info)
        int ret;
 
        drm_vblank_get(helper->dev, 0);
-       reinit_completion(&hdlcd->vsync_completion);
+       reinit_completion(&hdlcd->frame_completion);
        do {
-               ret = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
-                                                       msecs_to_jiffies(1000));
+               ret = wait_for_completion_interruptible_timeout(&hdlcd->frame_completion,
+                                                       msecs_to_jiffies(50));
        } while (ret == -ERESTARTSYS);
        drm_vblank_put(helper->dev, 0);
        if (!ret)
index 70a6de8ab262e38bfa681e0a115387f03ab331a3..5f6a4b4ff9f6bfb7ff130215d312de356a1fddf5 100644 (file)
@@ -60,7 +60,7 @@ hdlcd_connector_detect(struct drm_connector *connector, bool force)
 {
        struct drm_encoder_slave *slave;
        if (!connector->encoder)
-               return connector_status_unknown;
+               connector->encoder = hdlcd_connector_best_encoder(connector);
 
        slave = hdlcd_get_slave_encoder(connector);
        if (!slave || !slave->slave_funcs)
@@ -130,6 +130,11 @@ static struct drm_encoder_funcs hdlcd_encoder_funcs = {
        .destroy        = drm_i2c_encoder_destroy,
 };
 
+static void hdlcd_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+       drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
 static struct drm_encoder_helper_funcs hdlcd_encoder_helper_funcs = {
        .dpms           = drm_i2c_encoder_dpms,
        .save           = drm_i2c_encoder_save,
@@ -139,6 +144,7 @@ static struct drm_encoder_helper_funcs hdlcd_encoder_helper_funcs = {
        .commit         = drm_i2c_encoder_commit,
        .mode_set       = drm_i2c_encoder_mode_set,
        .detect         = drm_i2c_encoder_detect,
+       .disable        = hdlcd_hdmi_encoder_disable,
 };
 
 static struct tda998x_encoder_params tda998x_params = {