From: Koji Matsuoka <koji.matsuoka.xm@xxxxxxxxxxx> The workaround of DPLLCR2 register is required at the time of H3(WS1.0) and H3(WS1.1). This patch adds procedure to apply the workaround by revision. Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@xxxxxxxxxxx> [uli: replace PRR hack with soc_device_match()] Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@xxxxxxxxx> --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 88 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 8 +++ drivers/gpu/drm/rcar-du/rcar_du_drv.c | 5 ++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 +- drivers/gpu/drm/rcar-du/rcar_du_plane.h | 7 ++- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 21 +++++++- 6 files changed, 128 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 7316fc7..85e3c53 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -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> @@ -106,14 +107,70 @@ 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 const struct soc_device_attribute r8a7795es1[] = { + { .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; + 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; + + dpll = kzalloc(sizeof(*dpll), GFP_KERNEL); + if (dpll == NULL) + return; /* Compute the clock divisor and select the internal or external dot * clock based on the requested frequency. @@ -130,6 +187,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 +206,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 (soc_device_match(r8a7795es1)) + 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 4d0ae8a..d968fa9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -55,6 +55,7 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { }, }, .num_lvds = 0, + .dpll_ch = 0, }; static const struct rcar_du_device_info rcar_du_r8a7790_info = { @@ -84,6 +85,7 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { }, }, .num_lvds = 2, + .dpll_ch = 0, }; /* M2-W (r8a7791) and M2-N (r8a7793) are identical */ @@ -108,6 +110,7 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = { }, }, .num_lvds = 1, + .dpll_ch = 0, }; static const struct rcar_du_device_info rcar_du_r8a7794_info = { @@ -131,6 +134,7 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = { }, }, .num_lvds = 0, + .dpll_ch = 0, }; static const struct rcar_du_device_info rcar_du_r8a7795_info = { @@ -165,6 +169,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 c9db610..6366d24 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -1,7 +1,7 @@ /* * rcar_du_drv.h -- R-Car Display Unit DRM driver * - * Copyright (C) 2013-2015 Renesas Electronics Corporation + * Copyright (C) 2013-2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -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 8b91dd3..c1de338 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-2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -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..513a716 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -1,7 +1,7 @@ /* * rcar_du_regs.h -- R-Car Display Unit Registers Definitions * - * Copyright (C) 2013-2015 Renesas Electronics Corporation + * Copyright (C) 2013-2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) * @@ -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