drm/exynos/decon5433: Fix standalone update code
authorAndrzej Hajda <a.hajda@samsung.com>
Mon, 27 Jun 2016 08:52:16 +0000 (10:52 +0200)
committerInki Dae <inki.dae@samsung.com>
Mon, 4 Jul 2016 02:20:38 +0000 (11:20 +0900)
Documentation is not clear about the subject, but it seems that shadowed
registers should be updated only if STANDALONE_UPDATE_F bit is clear.
After completing update of shadowed registers driver should enable this bit.
Without this logic one can observe flickering, black screens or white noise.

Change-Id: Ia1e54423cd321bf7c8ac9e8cf90d1dc8c8d64daf
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
drivers/gpu/drm/exynos/exynos5433_drm_decon.c

index de234d2..6448471 100644 (file)
@@ -150,6 +150,23 @@ static void decon_setup_trigger(struct decon_context *ctx)
        writel(val, ctx->addr + DECON_TRIGCON);
 }
 
+static void decon_update(struct decon_context *ctx)
+{
+       decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+}
+
+static void decon_wait_for_update(struct decon_context *ctx)
+{
+       /* wait up to duration of 2 frames */
+       unsigned long timeout = jiffies + msecs_to_jiffies(2 * 1000 / 60);
+
+       while (readl(ctx->addr + DECON_UPDATE) & STANDALONE_UPDATE_F) {
+               if (time_is_after_jiffies(timeout))
+                       break;
+               usleep_range(500, 1000);
+       }
+}
+
 static void decon_commit(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;
@@ -157,6 +174,8 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
        bool interlaced = false;
        u32 val;
 
+       decon_wait_for_update(ctx);
+
        decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID, 0);
 
        /* enable clock gate */
@@ -229,6 +248,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
 
        /* enable output and display signal */
        decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, ~0);
+       decon_update(ctx);
 }
 
 #define BIT_VAL(x, e, s)       (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s))
@@ -322,6 +342,8 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
                return;
        }
 
+       decon_wait_for_update(ctx);
+
        decon_shadow_protect_win(ctx, win, true);
 
        /* FIXME: padding? padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width */
@@ -376,10 +398,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
 
        decon_shadow_protect_win(ctx, win, false);
 
-       /* standalone update */
-       val = readl(ctx->addr + DECON_UPDATE);
-       val |= STANDALONE_UPDATE_F;
-       writel(val, ctx->addr + DECON_UPDATE);
+       decon_update(ctx);
 
        if (ctx->i80_if && ctx->trg_type == EXYNOS_DISPLAY_SW_TRIGGER)
                atomic_set(&ctx->win_updated, 1);
@@ -404,6 +423,8 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
                return;
        }
 
+       decon_wait_for_update(ctx);
+
        decon_shadow_protect_win(ctx, win, true);
 
        /* window disable */
@@ -413,10 +434,7 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
 
        decon_shadow_protect_win(ctx, win, false);
 
-       /* standalone update */
-       val = readl(ctx->addr + DECON_UPDATE);
-       val |= STANDALONE_UPDATE_F;
-       writel(val, ctx->addr + DECON_UPDATE);
+       decon_update(ctx);
 
        plane->enabled = false;
 }
@@ -645,6 +663,8 @@ static void decon_clear_channel(struct decon_context *ctx)
                        goto err;
        }
 
+       decon_wait_for_update(ctx);
+
        for (win = 0; win < WINDOWS_NR; win++) {
                /* shadow update disable */
                val = readl(ctx->addr + DECON_SHADOWCON);
@@ -660,13 +680,10 @@ static void decon_clear_channel(struct decon_context *ctx)
                val = readl(ctx->addr + DECON_SHADOWCON);
                val &= ~SHADOWCON_Wx_PROTECT(win);
                writel(val, ctx->addr + DECON_SHADOWCON);
-
-               /* standalone update */
-               val = readl(ctx->addr + DECON_UPDATE);
-               val |= STANDALONE_UPDATE_F;
-               writel(val, ctx->addr + DECON_UPDATE);
        }
 
+       decon_update(ctx);
+
        atomic_set(&ctx->wait_vsync_event, 1);
 
        /*