aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2015-08-12 19:40:52 +0100
committerJon Medhurst <tixy@linaro.org>2015-11-02 09:40:57 +0000
commit1bc1f790eb34b42c494a1e5b105f77c440d3cd28 (patch)
tree694ead3b3b54e33c19baa0a0a22bd4485b7d6893
parentac8ca9d390d6d35a1ef7cfc688a5a4089cd5270a (diff)
drm: hdlcd: Implement a wait for scanout change to be in effect
User-side code doing double-buffering expects this in the situations that ends up using mode_set_base. Also only set scanout as needed, not on every interrupt, as that isn't required and is unsafe if the framebuffer state is part way through being updated. Code adapted from an earlier version of the driver which was written by Liviu Dudau <Liviu.Dudau@arm.com> Signed-off-by: Jon Medhurst <tixy@linaro.org>
-rw-r--r--drivers/gpu/drm/arm/hdlcd_crtc.c23
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.c20
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.h3
3 files changed, 33 insertions, 13 deletions
diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c
index 8db213d27fe2..aa640455f18d 100644
--- a/drivers/gpu/drm/arm/hdlcd_crtc.c
+++ b/drivers/gpu/drm/arm/hdlcd_crtc.c
@@ -38,7 +38,7 @@ static void hdlcd_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
-void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd)
+void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd, bool wait)
{
struct drm_framebuffer *fb = hdlcd->crtc.primary->fb;
struct drm_gem_cma_object *gem;
@@ -52,6 +52,17 @@ void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd)
(hdlcd->crtc.y * fb->pitches[0]) + (hdlcd->crtc.x * bpp/8);
hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
+
+ if (wait && hdlcd->dpms == DRM_MODE_DPMS_ON) {
+ drm_vblank_get(fb->dev, 0);
+ /* Clear any interrupt that may be from before we changed scanout */
+ hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, HDLCD_INTERRUPT_DMA_END);
+ /* Wait for next interrupt so we know scanout change is live */
+ reinit_completion(&hdlcd->frame_completion);
+ wait_for_completion_interruptible(&hdlcd->frame_completion);
+
+ drm_vblank_put(fb->dev, 0);
+ }
}
static int hdlcd_crtc_page_flip(struct drm_crtc *crtc,
@@ -72,11 +83,13 @@ 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 */
- hdlcd_set_scanout(hdlcd);
+ hdlcd_set_scanout(hdlcd, false);
spin_lock_irqsave(&crtc->dev->event_lock, flags);
if (event)
drm_send_vblank_event(crtc->dev, 0, event);
@@ -189,7 +202,7 @@ static int hdlcd_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
- hdlcd_set_scanout(hdlcd);
+ hdlcd_set_scanout(hdlcd, true);
return 0;
}
@@ -248,7 +261,7 @@ static int hdlcd_crtc_mode_set(struct drm_crtc *crtc,
clk_set_rate(hdlcd->clk, mode->crtc_clock * 1000);
clk_enable(hdlcd->clk);
- hdlcd_set_scanout(hdlcd);
+ hdlcd_set_scanout(hdlcd, false);
return 0;
}
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
index b0c53b2c27de..9eaf184d3b5b 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.c
+++ b/drivers/gpu/drm/arm/hdlcd_drv.c
@@ -165,6 +165,9 @@ static int hdlcd_load(struct drm_device *dev, unsigned long flags)
DRM_ERROR("failed to initialise vblank\n");
goto fail;
}
+
+ init_completion(&hdlcd->frame_completion);
+
hdlcd->fbdev = drm_fbdev_cma_init(dev, 32,
dev->mode_config.num_crtc,
dev->mode_config.num_connector);
@@ -243,8 +246,6 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
struct drm_pending_vblank_event *event;
unsigned long flags;
- hdlcd_set_scanout(hdlcd);
-
drm_handle_vblank(dev, 0);
spin_lock_irqsave(&dev->event_lock, flags);
@@ -256,6 +257,10 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+ if (irq_status & HDLCD_INTERRUPT_DMA_END) {
+ // send completion when reading the frame has finished
+ complete_all(&hdlcd->frame_completion);
+ }
/* acknowledge interrupt(s) */
hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, irq_status);
@@ -273,15 +278,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);
+#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;
}
@@ -296,8 +304,8 @@ static void hdlcd_irq_uninstall(struct drm_device *dev)
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 3585adb0fd8b..78c8eb3d1ae3 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.h
+++ b/drivers/gpu/drm/arm/hdlcd_drv.h
@@ -12,6 +12,7 @@ struct hdlcd_drm_private {
struct drm_framebuffer *fb;
struct drm_pending_vblank_event *event;
struct drm_crtc crtc;
+ struct completion frame_completion;
#ifdef CONFIG_DEBUG_FS
atomic_t buffer_underrun_count;
atomic_t bus_error_count;
@@ -62,8 +63,6 @@ static inline int hdlcd_create_virtual_connector(struct drm_device *dev)
}
#endif
-void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd);
-
/* common function used by all connectors */
extern struct drm_encoder *hdlcd_connector_best_encoder(struct drm_connector *con);