From 58134d3cbe97e894b43ac5039cfb50b3f7a1b862 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Tue, 22 Jul 2014 18:46:29 +0100 Subject: [PATCH] drm: hdlcd: Don't depend on VSYNC interrupt when setting new framebuffer. 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 --- drivers/gpu/drm/arm/hdlcd_crtc.c | 17 ++++++++++------- drivers/gpu/drm/arm/hdlcd_drv.c | 22 ++++++++++++++-------- drivers/gpu/drm/arm/hdlcd_drv.h | 2 +- drivers/gpu/drm/arm/hdlcd_fb.c | 6 +++--- drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c | 8 +++++++- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 17c006e..791cd3a 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -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; diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index a831b86..e00f9d7 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -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); } diff --git a/drivers/gpu/drm/arm/hdlcd_drv.h b/drivers/gpu/drm/arm/hdlcd_drv.h index 455da17..3fc3bdd 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.h +++ b/drivers/gpu/drm/arm/hdlcd_drv.h @@ -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; diff --git a/drivers/gpu/drm/arm/hdlcd_fb.c b/drivers/gpu/drm/arm/hdlcd_fb.c index ca67e60..5165ece 100644 --- a/drivers/gpu/drm/arm/hdlcd_fb.c +++ b/drivers/gpu/drm/arm/hdlcd_fb.c @@ -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) diff --git a/drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c b/drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c index 70a6de8..5f6a4b4 100644 --- a/drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c +++ b/drivers/gpu/drm/arm/hdlcd_hdmi_encoder.c @@ -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 = { -- 2.7.4