[PATCH 17/21] drm/tilcdc: Do not update the next frame buffer close to vertical blank
Jyri Sarha
jsarha at ti.com
Thu Feb 4 09:31:10 UTC 2016
From: Tomi Valkeinen <tomi.valkeinen at ti.com>
Do not update the next frame buffer close to vertical blank. This is
to avoid situation when the frame changes between writing of
LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ti.com>
[Added description to the patch]
Signed-off-by: Jyri Sarha <jsarha at ti.com>
---
drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 61 +++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index 7514d40..7445356 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -21,6 +21,8 @@
#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
+#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000
+
struct tilcdc_crtc {
struct drm_crtc base;
@@ -29,8 +31,12 @@ struct tilcdc_crtc {
int dpms;
wait_queue_head_t frame_done_wq;
bool frame_done;
+ spinlock_t irq_lock;
+
+ ktime_t last_vblank;
struct drm_framebuffer *curr_fb;
+ struct drm_framebuffer *next_fb;
/* for deferred fb unref's: */
struct drm_flip_work unref_work;
@@ -146,6 +152,8 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
int r;
unsigned long flags;
+ s64 tdiff;
+ ktime_t next_vblank;
r = tilcdc_verify_fb(crtc, fb);
if (r)
@@ -162,12 +170,21 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
pm_runtime_get_sync(dev->dev);
+ spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
- set_scanout(crtc, fb);
+ next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
+ 1000000 / crtc->hwmode.vrefresh);
+
+ tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));
+
+ if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US)
+ set_scanout(crtc, fb);
+ else
+ tilcdc_crtc->next_fb = fb;
- spin_lock_irqsave(&dev->event_lock, flags);
tilcdc_crtc->event = event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
pm_runtime_put_sync(dev->dev);
@@ -211,6 +228,12 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
pm_runtime_put_sync(dev->dev);
+ if (tilcdc_crtc->next_fb) {
+ drm_flip_work_queue(&tilcdc_crtc->unref_work,
+ tilcdc_crtc->next_fb);
+ tilcdc_crtc->next_fb = NULL;
+ }
+
if (tilcdc_crtc->curr_fb) {
drm_flip_work_queue(&tilcdc_crtc->unref_work,
tilcdc_crtc->curr_fb);
@@ -651,19 +674,39 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
if (stat & LCDC_END_OF_FRAME0) {
unsigned long flags;
+ bool skip_event = false;
+ ktime_t now;
+
+ now = ktime_get();
drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
+ spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
+
+ tilcdc_crtc->last_vblank = now;
+
+ if (tilcdc_crtc->next_fb) {
+ set_scanout(crtc, tilcdc_crtc->next_fb);
+ tilcdc_crtc->next_fb = NULL;
+ skip_event = true;
+ }
+
+ spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
+
drm_handle_vblank(dev, 0);
- spin_lock_irqsave(&dev->event_lock, flags);
+ if (!skip_event) {
+ struct drm_pending_vblank_event *event;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
- if (tilcdc_crtc->event) {
- drm_send_vblank_event(dev, 0, tilcdc_crtc->event);
+ event = tilcdc_crtc->event;
tilcdc_crtc->event = NULL;
- }
+ if (event)
+ drm_send_vblank_event(dev, 0, event);
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
}
if (priv->rev == 2) {
@@ -716,6 +759,8 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
drm_flip_work_init(&tilcdc_crtc->unref_work,
"unref", unref_worker);
+ spin_lock_init(&tilcdc_crtc->irq_lock);
+
ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs);
if (ret < 0)
goto fail;
--
1.9.1
More information about the dri-devel
mailing list