MLK-17371 gpu: imx: dpu: framegen: Use better timeout value to wait for ENSTS
authorLiu Ying <victor.liu@nxp.com>
Fri, 12 Jan 2018 09:42:21 +0000 (17:42 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Tue, 20 Mar 2018 19:52:51 +0000 (14:52 -0500)
The DPU spec tells us that we need to wait for all pending frames to
be completed when a display stream is disabled.  It turns out
that the hardcoded 60-microsecond timeout value is not enough for
some low refresh rate video modes, e.g., 1920x1080@24, which makes
the display stream be disabled incorrectly(leave the hardware an
incorrect machine status).  The SoC design indicates that there are
two pending frames to complete in the worst case.  This patch waits
for at most three frame duration(which is enough for sure) so that
the hardware may flush out all the pending frames.  In case the clock
subsystem provides us a pixel clock with wrong rate and causes the
timeout value be unreasonably long, we truncate it to wait for at
most three seconds.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
drivers/gpu/drm/imx/dpu/dpu-crtc.c
drivers/gpu/imx/dpu/dpu-framegen.c
include/video/dpu.h

index b075b39..cf96dfa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 NXP
+ * Copyright 2017-2018 NXP
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -137,12 +137,13 @@ static void dpu_crtc_enable(struct drm_crtc *crtc)
        tcon_set_operation_mode(dpu_crtc->tcon);
 }
 
-static void dpu_crtc_disable(struct drm_crtc *crtc)
+static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
+                                   struct drm_crtc_state *old_crtc_state)
 {
        struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 
        framegen_disable(dpu_crtc->fg);
-       framegen_wait_done(dpu_crtc->fg);
+       framegen_wait_done(dpu_crtc->fg, &old_crtc_state->adjusted_mode);
        framegen_disable_clock(dpu_crtc->fg);
 
        WARN_ON(!crtc->state->event);
@@ -512,7 +513,7 @@ static const struct drm_crtc_helper_funcs dpu_helper_funcs = {
        .atomic_begin = dpu_crtc_atomic_begin,
        .atomic_flush = dpu_crtc_atomic_flush,
        .enable = dpu_crtc_enable,
-       .disable = dpu_crtc_disable,
+       .atomic_disable = dpu_crtc_atomic_disable,
 };
 
 static int dpu_enable_vblank(struct drm_crtc *crtc)
index ffbea66..fb4ff10 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2016 Freescale Semiconductor, Inc.
- * Copyright 2017 NXP
+ * Copyright 2017-2018 NXP
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -96,6 +96,8 @@ typedef enum {
 #define FGSREPD                        0x90
 #define FGSRFTD                        0x94
 
+#define KHZ                    1000
+
 struct dpu_framegen {
        void __iomem *base;
        struct clk *clk_pll;
@@ -348,17 +350,47 @@ void framegen_panic_displaymode(struct dpu_framegen *fg, fgdm_t mode)
 }
 EXPORT_SYMBOL_GPL(framegen_panic_displaymode);
 
-void framegen_wait_done(struct dpu_framegen *fg)
+void framegen_wait_done(struct dpu_framegen *fg, struct drm_display_mode *m)
 {
-       unsigned long timeout = jiffies + msecs_to_jiffies(60);
+       unsigned long timeout, pending_framedur_jiffies;
+       int frame_size = m->crtc_htotal * m->crtc_vtotal;
+       int dotclock, pending_framedur_ns;
        u32 val;
 
+       dotclock = clk_get_rate(fg->clk_disp) / KHZ;
+       if (dotclock == 0) {
+               /* fall back to display mode's clock */
+               dotclock = m->crtc_clock;
+
+               dev_warn(fg->dpu->dev,
+                               "pixel clock for FrameGen%d is zero\n", fg->id);
+       }
+
+       /*
+        * The SoC designer indicates that there are two pending frames
+        * to complete in the worst case.
+        * So, three pending frames are enough for sure.
+        */
+       pending_framedur_ns = div_u64((u64) 3 * frame_size * 1000000, dotclock);
+       pending_framedur_jiffies = nsecs_to_jiffies(pending_framedur_ns);
+       if (pending_framedur_jiffies > (3 * HZ)) {
+               pending_framedur_jiffies = 3 * HZ;
+
+               dev_warn(fg->dpu->dev,
+                        "truncate FrameGen%d pending frame duration to 3sec\n",
+                        fg->id);
+       }
+       timeout = jiffies + pending_framedur_jiffies;
+
        mutex_lock(&fg->mutex);
        do {
                val = dpu_fg_read(fg, FGENSTS);
        } while ((val & ENSTS) && time_before(jiffies, timeout));
        mutex_unlock(&fg->mutex);
 
+       dev_dbg(fg->dpu->dev, "FrameGen%d pending frame duration is %ums\n",
+                        fg->id, jiffies_to_msecs(pending_framedur_jiffies));
+
        if (val & ENSTS)
                dev_err(fg->dpu->dev, "failed to wait for FrameGen%d done\n",
                        fg->id);
index aacb399..f28b2f2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2016 Freescale Semiconductor, Inc.
- * Copyright 2017 NXP
+ * Copyright 2017-2018 NXP
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -602,7 +602,7 @@ void framegen_pkickconfig(struct dpu_framegen *fg, bool enable);
 void framegen_sacfg(struct dpu_framegen *fg, unsigned int x, unsigned int y);
 void framegen_displaymode(struct dpu_framegen *fg, fgdm_t mode);
 void framegen_panic_displaymode(struct dpu_framegen *fg, fgdm_t mode);
-void framegen_wait_done(struct dpu_framegen *fg);
+void framegen_wait_done(struct dpu_framegen *fg, struct drm_display_mode *m);
 void framegen_read_timestamp(struct dpu_framegen *fg,
                             u32 *frame_index, u32 *line_index);
 void framegen_wait_for_frame_counter_moving(struct dpu_framegen *fg);