Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: rcar-du: Add DPLL support

The implementation hardcodes a workaround for the H3 ES1.x SoC
regardless of the SoC revision, as the workaround can be safely applied
on all devices in the Gen3 family without any side effect.

Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@renesas.com>
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

authored by

Koji Matsuoka and committed by
Laurent Pinchart
dc4aedbf 4739a0d4

+105 -1
+80 -1
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
··· 106 106 * Hardware Setup 107 107 */ 108 108 109 + struct dpll_info { 110 + unsigned int output; 111 + unsigned int fdpll; 112 + unsigned int n; 113 + unsigned int m; 114 + }; 115 + 116 + static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc, 117 + struct dpll_info *dpll, 118 + unsigned long input, 119 + unsigned long target) 120 + { 121 + unsigned long best_diff = (unsigned long)-1; 122 + unsigned long diff; 123 + unsigned int fdpll; 124 + unsigned int m; 125 + unsigned int n; 126 + 127 + for (n = 39; n < 120; n++) { 128 + for (m = 0; m < 4; m++) { 129 + for (fdpll = 1; fdpll < 32; fdpll++) { 130 + unsigned long output; 131 + 132 + /* 1/2 (FRQSEL=1) for duty rate 50% */ 133 + output = input * (n + 1) / (m + 1) 134 + / (fdpll + 1) / 2; 135 + 136 + if (output >= 400000000) 137 + continue; 138 + 139 + diff = abs((long)output - (long)target); 140 + if (best_diff > diff) { 141 + best_diff = diff; 142 + dpll->n = n; 143 + dpll->m = m; 144 + dpll->fdpll = fdpll; 145 + dpll->output = output; 146 + } 147 + 148 + if (diff == 0) 149 + goto done; 150 + } 151 + } 152 + } 153 + 154 + done: 155 + dev_dbg(rcrtc->group->dev->dev, 156 + "output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n", 157 + dpll->output, dpll->fdpll, dpll->n, dpll->m, 158 + best_diff); 159 + } 160 + 109 161 static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) 110 162 { 111 163 const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; 164 + struct rcar_du_device *rcdu = rcrtc->group->dev; 112 165 unsigned long mode_clock = mode->clock * 1000; 113 166 unsigned long clk; 114 167 u32 value; ··· 177 124 escr = div | ESCR_DCLKSEL_CLKS; 178 125 179 126 if (rcrtc->extclock) { 127 + struct dpll_info dpll = { 0 }; 180 128 unsigned long extclk; 181 129 unsigned long extrate; 182 130 unsigned long rate; 183 131 u32 extdiv; 184 132 185 133 extclk = clk_get_rate(rcrtc->extclock); 134 + if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { 135 + rcar_du_dpll_divider(rcrtc, &dpll, extclk, mode_clock); 136 + extclk = dpll.output; 137 + } 138 + 186 139 extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); 187 140 extdiv = clamp(extdiv, 1U, 64U) - 1; 188 141 ··· 199 140 abs((long)rate - (long)mode_clock)) { 200 141 dev_dbg(rcrtc->group->dev->dev, 201 142 "crtc%u: using external clock\n", rcrtc->index); 202 - escr = extdiv | ESCR_DCLKSEL_DCLKIN; 143 + 144 + if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { 145 + u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE 146 + | DPLLCR_FDPLL(dpll.fdpll) 147 + | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) 148 + | DPLLCR_STBY; 149 + 150 + if (rcrtc->index == 1) 151 + dpllcr |= DPLLCR_PLCS1 152 + | DPLLCR_INCS_DOTCLKIN1; 153 + else 154 + dpllcr |= DPLLCR_PLCS0 155 + | DPLLCR_INCS_DOTCLKIN0; 156 + 157 + rcar_du_group_write(rcrtc->group, DPLLCR, 158 + dpllcr); 159 + 160 + escr = ESCR_DCLKSEL_DCLKIN | 1; 161 + } else { 162 + escr = ESCR_DCLKSEL_DCLKIN | extdiv; 163 + } 203 164 } 204 165 } 205 166
+1
drivers/gpu/drm/rcar-du/rcar_du_drv.c
··· 162 162 }, 163 163 }, 164 164 .num_lvds = 1, 165 + .dpll_ch = BIT(1) | BIT(2), 165 166 }; 166 167 167 168 static const struct rcar_du_device_info rcar_du_r8a7796_info = {
+1
drivers/gpu/drm/rcar-du/rcar_du_drv.h
··· 65 65 unsigned int num_crtcs; 66 66 struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; 67 67 unsigned int num_lvds; 68 + unsigned int dpll_ch; 68 69 }; 69 70 70 71 #define RCAR_DU_MAX_CRTCS 4
+23
drivers/gpu/drm/rcar-du/rcar_du_regs.h
··· 277 277 #define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */ 278 278 #define DEFR10_DEFE10 (1 << 0) 279 279 280 + #define DPLLCR 0x20044 281 + #define DPLLCR_CODE (0x95 << 24) 282 + #define DPLLCR_PLCS1 (1 << 23) 283 + /* 284 + * PLCS0 is bit 21, but H3 ES1.x requires bit 20 to be set as well. As bit 20 285 + * isn't implemented by other SoC in the Gen3 family it can safely be set 286 + * unconditionally. 287 + */ 288 + #define DPLLCR_PLCS0 (3 << 20) 289 + #define DPLLCR_CLKE (1 << 18) 290 + #define DPLLCR_FDPLL(n) ((n) << 12) 291 + #define DPLLCR_N(n) ((n) << 5) 292 + #define DPLLCR_M(n) ((n) << 3) 293 + #define DPLLCR_STBY (1 << 2) 294 + #define DPLLCR_INCS_DOTCLKIN0 (0 << 0) 295 + #define DPLLCR_INCS_DOTCLKIN1 (1 << 1) 296 + 297 + #define DPLLC2R 0x20048 298 + #define DPLLC2R_CODE (0x95 << 24) 299 + #define DPLLC2R_SELC (1 << 12) 300 + #define DPLLC2R_M(n) ((n) << 8) 301 + #define DPLLC2R_FDPLL(n) ((n) << 0) 302 + 280 303 /* ----------------------------------------------------------------------------- 281 304 * Display Timing Generation Registers 282 305 */