The DECON channels are not cleared properly as the windows aren't
shadow protected. When accompanied with an IOMMU, it pagefaults, and
the kernel panics.

Implement shadow protect/unprotect, along with a standalone update,
for channel clearing to properly take effect.

Signed-off-by: Kaustabh Chakraborty <kauschl...@disroot.org>
---
 drivers/gpu/drm/exynos/exynos7_drm_decon.c | 55 +++++++++++++++++-------------
 1 file changed, 32 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c 
b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 4e4ced50ff15..7f0985eb216e 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -81,6 +81,28 @@ static const enum drm_plane_type decon_win_types[WINDOWS_NR] 
= {
        DRM_PLANE_TYPE_CURSOR,
 };
 
+/**
+ * decon_shadow_protect_win() - disable updating values from shadow registers 
at vsync
+ *
+ * @ctx: display and enhancement controller context
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void decon_shadow_protect_win(struct decon_context *ctx,
+                                    unsigned int win, bool protect)
+{
+       u32 bits, val;
+
+       bits = SHADOWCON_WINx_PROTECT(win);
+
+       val = readl(ctx->regs + SHADOWCON);
+       if (protect)
+               val |= bits;
+       else
+               val &= ~bits;
+       writel(val, ctx->regs + SHADOWCON);
+}
+
 static void decon_wait_for_vblank(struct decon_context *ctx)
 {
        if (ctx->suspended)
@@ -101,18 +123,27 @@ static void decon_wait_for_vblank(struct decon_context 
*ctx)
 static void decon_clear_channels(struct decon_context *ctx)
 {
        unsigned int win, ch_enabled = 0;
+       u32 val;
 
        /* Check if any channel is enabled. */
        for (win = 0; win < WINDOWS_NR; win++) {
-               u32 val = readl(ctx->regs + WINCON(win));
+               val = readl(ctx->regs + WINCON(win));
 
                if (val & WINCONx_ENWIN) {
+                       decon_shadow_protect_win(ctx, win, true);
+
                        val &= ~WINCONx_ENWIN;
                        writel(val, ctx->regs + WINCON(win));
                        ch_enabled = 1;
+
+                       decon_shadow_protect_win(ctx, win, false);
                }
        }
 
+       val = readl(ctx->regs + DECON_UPDATE);
+       val |= DECON_UPDATE_STANDALONE_F;
+       writel(val, ctx->regs + DECON_UPDATE);
+
        /* Wait for vsync, as disable channel takes effect at next vsync */
        if (ch_enabled)
                decon_wait_for_vblank(ctx);
@@ -340,28 +371,6 @@ static void decon_win_set_colkey(struct decon_context 
*ctx, unsigned int win)
        writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
 }
 
-/**
- * decon_shadow_protect_win() - disable updating values from shadow registers 
at vsync
- *
- * @ctx: display and enhancement controller context
- * @win: window to protect registers for
- * @protect: 1 to protect (disable updates)
- */
-static void decon_shadow_protect_win(struct decon_context *ctx,
-                                    unsigned int win, bool protect)
-{
-       u32 bits, val;
-
-       bits = SHADOWCON_WINx_PROTECT(win);
-
-       val = readl(ctx->regs + SHADOWCON);
-       if (protect)
-               val |= bits;
-       else
-               val &= ~bits;
-       writel(val, ctx->regs + SHADOWCON);
-}
-
 static void decon_atomic_begin(struct exynos_drm_crtc *crtc)
 {
        struct decon_context *ctx = crtc->ctx;

-- 
2.46.1

Reply via email to