aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2015-08-12 19:40:52 +0100
committerAndrey Konovalov <andrey.konovalov@linaro.org>2015-10-16 19:11:49 +0300
commit684e5801778f6446b34ea963f508a0d226bf99a1 (patch)
tree46d24462323e8341cc4d74746b5819d090baea2d
parent412310eee6498dc9d650989ddd8df0a41df4e1ec (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);