From: Koji Matsuoka <koji.matsuoka...@renesas.com>

Signed-off-by: Koji Matsuoka <koji.matsuoka.xm at renesas.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas at glider.be>
---
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c  | 97 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h  |  8 +++
 drivers/gpu/drm/rcar-du/rcar_du_drv.c   |  1 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.h   |  1 +
 drivers/gpu/drm/rcar-du/rcar_du_plane.h |  7 ++-
 drivers/gpu/drm/rcar-du/rcar_du_regs.h  | 19 +++++++
 6 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c 
b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 0d8bdda..e10943b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -30,6 +30,12 @@
 #include "rcar_du_regs.h"
 #include "rcar_du_vsp.h"

+#define PRODUCT_REG    0xfff00044
+#define PRODUCT_H3_BIT (0x4f << 8)
+#define PRODUCT_MASK   (0x7f << 8)
+#define CUT_ES1                (0x00)
+#define CUT_ES1_MASK   (0x000000ff)
+
 static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
 {
        struct rcar_du_device *rcdu = rcrtc->group->dev;
@@ -106,14 +112,74 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
  * Hardware Setup
  */

+static void rcar_du_dpll_divider(struct dpll_info *dpll, unsigned int extclk,
+                                unsigned int mode_clock)
+{
+       unsigned long dpllclk;
+       unsigned long diff;
+       unsigned long n, m, fdpll;
+       bool match_flag = false;
+       bool clk_diff_set = true;
+
+       for (n = 39; n < 120; n++) {
+               for (m = 0; m < 4; m++) {
+                       for (fdpll = 1; fdpll < 32; fdpll++) {
+                               /* 1/2 (FRQSEL=1) for duty rate 50% */
+                               dpllclk = extclk * (n + 1) / (m + 1)
+                                                / (fdpll + 1) / 2;
+                               if (dpllclk >= 400000000)
+                                       continue;
+
+                               diff = abs((long)dpllclk - (long)mode_clock);
+                               if (clk_diff_set ||
+                                       ((diff == 0) || (dpll->diff > diff))) {
+                                       dpll->diff = diff;
+                                       dpll->n = n;
+                                       dpll->m = m;
+                                       dpll->fdpll = fdpll;
+                                       dpll->dpllclk = dpllclk;
+
+                                       if (clk_diff_set)
+                                               clk_diff_set = false;
+
+                                       if (diff == 0) {
+                                               match_flag = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (match_flag)
+                               break;
+               }
+               if (match_flag)
+                       break;
+       }
+}
+
 static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 {
        const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
        unsigned long mode_clock = mode->clock * 1000;
        unsigned long clk;
        u32 value;
        u32 escr;
        u32 div;
+       u32 dpll_reg = 0;
+       struct dpll_info *dpll;
+       void __iomem *product_reg;
+       bool h3_es1_workaround = false;
+
+       dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+       if (dpll == NULL)
+               return;
+
+       /* DU2 DPLL Clock Select bit workaround in R-Car H3(ES1.0) */
+       product_reg = ioremap(PRODUCT_REG, 0x04);
+       if (((readl(product_reg) & PRODUCT_MASK) == PRODUCT_H3_BIT)
+               && ((readl(product_reg) & CUT_ES1_MASK) == CUT_ES1))
+               h3_es1_workaround = true;
+       iounmap(product_reg);

        /* Compute the clock divisor and select the internal or external dot
         * clock based on the requested frequency.
@@ -130,6 +196,15 @@ static void rcar_du_crtc_set_display_timing(struct 
rcar_du_crtc *rcrtc)
                u32 extdiv;

                extclk = clk_get_rate(rcrtc->extclock);
+
+               if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
+                       rcar_du_dpll_divider(dpll, extclk, mode_clock);
+                       extclk = dpll->dpllclk;
+                       dev_dbg(rcrtc->group->dev->dev,
+                               "dpllclk:%d, fdpll:%d, n:%d, m:%d, diff:%d\n",
+                                dpll->dpllclk, dpll->fdpll, dpll->n, dpll->m,
+                                dpll->diff);
+               }
                extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
                extdiv = clamp(extdiv, 1U, 64U) - 1;

@@ -140,7 +215,27 @@ static void rcar_du_crtc_set_display_timing(struct 
rcar_du_crtc *rcrtc)
                    abs((long)rate - (long)mode_clock)) {
                        dev_dbg(rcrtc->group->dev->dev,
                                "crtc%u: using external clock\n", rcrtc->index);
-                       escr = extdiv | ESCR_DCLKSEL_DCLKIN;
+                       if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
+                               escr = ESCR_DCLKSEL_DCLKIN | 0x01;
+                               dpll_reg =  DPLLCR_CODE | DPLLCR_M(dpll->m) |
+                                       DPLLCR_FDPLL(dpll->fdpll) |
+                                       DPLLCR_CLKE | DPLLCR_N(dpll->n) |
+                                       DPLLCR_STBY;
+
+                               if (rcrtc->index == DU_CH_1)
+                                       dpll_reg |= (DPLLCR_PLCS1 |
+                                                DPLLCR_INCS_DPLL01_DOTCLKIN13);
+                               if (rcrtc->index == DU_CH_2) {
+                                       dpll_reg |= (DPLLCR_PLCS0 |
+                                                DPLLCR_INCS_DPLL01_DOTCLKIN02);
+                                       if (h3_es1_workaround)
+                                               dpll_reg |=  (0x01 << 21);
+                               }
+
+                               rcar_du_group_write(rcrtc->group, DPLLCR,
+                                                                 dpll_reg);
+                       } else
+                               escr = extdiv | ESCR_DCLKSEL_DCLKIN;
                }
        }

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h 
b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 459e539..9a56cc7 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -54,6 +54,14 @@ struct rcar_du_crtc {
        struct rcar_du_vsp *vsp;
 };

+struct dpll_info {
+       unsigned int dpllclk;
+       unsigned int diff;
+       unsigned int fdpll;
+       unsigned int n;
+       unsigned int m;
+};
+
 #define to_rcar_crtc(c)        container_of(c, struct rcar_du_crtc, crtc)

 enum rcar_du_output {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c 
b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 0a93d2a..5ed0d61 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -165,6 +165,7 @@ static const struct rcar_du_device_info 
rcar_du_r8a7795_info = {
                },
        },
        .num_lvds = 1,
+       .dpll_ch =  BIT(1) | BIT(2),
 };

 static const struct of_device_id rcar_du_of_table[] = {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h 
b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index d1d1d8d..790829b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -69,6 +69,7 @@ struct rcar_du_device_info {
        unsigned int num_crtcs;
        struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
        unsigned int num_lvds;
+       unsigned int dpll_ch;
 };

 #define RCAR_DU_MAX_CRTCS              4
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h 
b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
index b18b7b2..47fad23 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_plane.h  --  R-Car Display Unit Planes
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart at ideasonboard.com)
  *
@@ -34,6 +34,11 @@ enum rcar_du_plane_source {
        RCAR_DU_PLANE_VSPD1,
 };

+#define DU_CH_0                0
+#define DU_CH_1                1
+#define DU_CH_2                2
+#define DU_CH_3                3
+
 struct rcar_du_plane {
        struct drm_plane plane;
        struct rcar_du_group *group;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h 
b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index fedb016..7a34bf3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -277,6 +277,25 @@
 #define DEFR10_TSEL_H3_TCON1   (0 << 1) /* DEFR102 register only (DU2/DU3) */
 #define DEFR10_DEFE10          (1 << 0)

+#define DPLLCR                 0x20044
+#define DPLLCR_CODE            (0x95 << 24)
+#define DPLLCR_PLCS1           (1 << 23)
+#define DPLLCR_PLCS0           (1 << 20)
+#define DPLLCR_CLKE            (1 << 18)
+#define DPLLCR_FDPLL(n)                ((n) << 12)     /* n=0 Setting 
prohibited */
+/* H'00 to H'26, H'78 to H'7F: Setting prohibited.*/
+#define DPLLCR_N(n)            ((n) << 5)
+#define DPLLCR_M(n)            ((n) << 3)
+#define DPLLCR_STBY            (1 << 2)
+#define DPLLCR_INCS_DPLL01_DOTCLKIN02  (0 << 0)
+#define DPLLCR_INCS_DPLL01_DOTCLKIN13  (1 << 1)
+
+#define DPLLC2R                        0x20048
+#define DPLLC2R_CODE           (0x95 << 24)
+#define DPLLC2R_SELC           (1 << 12)
+#define DPLLC2R_M(n)           ((n) << 8)
+#define DPLLC2R_FDPLL(n)       ((n) << 0)      /* n=0 Setting prohibited */
+
 /* 
-----------------------------------------------------------------------------
  * Display Timing Generation Registers
  */
-- 
2.7.4

Reply via email to