drm: rcar-du: Restrict DPLL duty cycle workaround to H3 ES1.x
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tue, 20 Jun 2017 19:35:44 +0000 (22:35 +0300)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Thu, 3 Aug 2017 13:17:23 +0000 (16:17 +0300)
The H3 ES1.x exhibits dot clock duty cycle stability issues. We can work
around them by configuring the DPLL to twice the desired frequency,
coupled with a /2 post-divider. This isn't needed on other SoCs and
breaks HDMI output on M3-W for a currently unknown reason, so restrict
the workaround to H3 ES1.x.

From an implementation point of view, move work around handling outside
of the rcar_du_dpll_divider() function by requesting a x2 DPLL output
frequency explicitly. The existing post-divider calculation mechanism
will then take care of dividing the clock by two automatically.

While at it, print a more useful debugging message to ease debugging
clock rate issues.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
drivers/gpu/drm/rcar-du/rcar_du_crtc.c

index 63c32b3582fbf4aaec68fb8781c644c6b1a1df0c..d82aa67162c61d171cf2dca698e1ce761f51f050 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/clk.h>
 #include <linux/mutex.h>
+#include <linux/sys_soc.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -129,10 +130,8 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
                        for (fdpll = 1; fdpll < 32; fdpll++) {
                                unsigned long output;
 
-                               /* 1/2 (FRQSEL=1) for duty rate 50% */
                                output = input * (n + 1) / (m + 1)
-                                      / (fdpll + 1) / 2;
-
+                                      / (fdpll + 1);
                                if (output >= 400000000)
                                        continue;
 
@@ -158,6 +157,11 @@ done:
                 best_diff);
 }
 
+static const struct soc_device_attribute rcar_du_r8a7795_es1[] = {
+       { .soc_id = "r8a7795", .revision = "ES1.*" },
+       { /* sentinel */ }
+};
+
 static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 {
        const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
@@ -186,7 +190,20 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 
                extclk = clk_get_rate(rcrtc->extclock);
                if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
-                       rcar_du_dpll_divider(rcrtc, &dpll, extclk, mode_clock);
+                       unsigned long target = mode_clock;
+
+                       /*
+                        * The H3 ES1.x exhibits dot clock duty cycle stability
+                        * issues. We can work around them by configuring the
+                        * DPLL to twice the desired frequency, coupled with a
+                        * /2 post-divider. This isn't needed on other SoCs and
+                        * breaks HDMI output on M3-W for a currently unknown
+                        * reason, so restrict the workaround to H3 ES1.x.
+                        */
+                       if (soc_device_match(rcar_du_r8a7795_es1))
+                               target *= 2;
+
+                       rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
                        extclk = dpll.output;
                }
 
@@ -198,8 +215,6 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 
                if (abs((long)extrate - (long)mode_clock) <
                    abs((long)rate - (long)mode_clock)) {
-                       dev_dbg(rcrtc->group->dev->dev,
-                               "crtc%u: using external clock\n", rcrtc->index);
 
                        if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
                                u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
@@ -216,12 +231,14 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 
                                rcar_du_group_write(rcrtc->group, DPLLCR,
                                                    dpllcr);
-
-                               escr = ESCR_DCLKSEL_DCLKIN | 1;
-                       } else {
-                               escr = ESCR_DCLKSEL_DCLKIN | extdiv;
                        }
+
+                       escr = ESCR_DCLKSEL_DCLKIN | extdiv;
                }
+
+               dev_dbg(rcrtc->group->dev->dev,
+                       "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n",
+                       mode_clock, extrate, rate, escr);
        }
 
        rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,