[RFC 08/21] drm: rcar-du: Add DPLL support
On 30.05.2016 18:00, Ulrich Hecht wrote: > From: Koji Matsuoka > > Signed-off-by: Koji Matsuoka > Signed-off-by: Geert Uytterhoeven> --- > 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 (0x00ff) NACK for the hard coded register. We've already discussed this in the thread https://www.mail-archive.com/linux-renesas-soc at vger.kernel.org/msg04008.html and found that this isn't ready this way: https://www.mail-archive.com/linux-renesas-soc at vger.kernel.org/msg04079.html Best regards Dirk > 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 >= 4) > + 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 = >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, > +
[RFC 08/21] drm: rcar-du: Add DPLL support
From: Koji MatsuokaSigned-off-by: Koji Matsuoka Signed-off-by: Geert Uytterhoeven --- 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_REG0xfff00044 +#define PRODUCT_H3_BIT (0x4f << 8) +#define PRODUCT_MASK (0x7f << 8) +#define CUT_ES1(0x00) +#define CUT_ES1_MASK (0x00ff) + 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 >= 4) + 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 = >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)