aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorJacopo Mondi <jacopo@jmondi.org>2018-08-20 11:26:17 -0400
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2018-09-15 10:28:26 -0400
commit8c74c4561f05f57fca2957b1d98676a0454df1ca (patch)
tree7e234939fdbe894baa09f33a57bb73d92b284fe2 /drivers/gpu
parent7281e6c6a5bdbde9cae6eb3c6d2bf2706b94807d (diff)
drm: rcar-du: Improve non-DPLL clock selection
DU channels not equipped with a DPLL use an SoC internal (provided by the CPG) or external clock source combined with a DU internal divider to generate the desired output dot clock frequency. The current clock selection procedure does not fully exploit the ability of external clock sources to generate the exact dot clock frequency by themselves, but relies instead on tuning the internal DU clock divider only, resulting in a less precise clock generation process. When possible, and desirable, ask the external clock source for the exact output dot clock frequency, and select the clock source that produces the frequency closest to the desired output dot clock. This patch specifically targets platforms (like Salvator-X[S] and ULCBs) where the DU's input dotclock.in is generated by the versaclock VC5 clock source, which is capable of generating the exact rate the DU needs as pixel clock output. This patch fixes higher resolution modes which requires an high pixel clock output currently not working on non-HDMI DU channel (such as 1920x1080@60Hz on the VGA output). Fixes: 1b30dbde8596 ("drm: rcar-du: Add support for external pixel clock") Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> [Factor out code to a helper function] Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Acked-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c85
1 files changed, 54 insertions, 31 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 687e8129adbd..eadf3814228f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -194,6 +194,47 @@ done:
194 best_diff); 194 best_diff);
195} 195}
196 196
197struct du_clk_params {
198 struct clk *clk;
199 unsigned long rate;
200 unsigned long diff;
201 u32 escr;
202};
203
204static void rcar_du_escr_divider(struct clk *clk, unsigned long target,
205 u32 escr, struct du_clk_params *params)
206{
207 unsigned long rate;
208 unsigned long diff;
209 u32 div;
210
211 /*
212 * If the target rate has already been achieved perfectly we can't do
213 * better.
214 */
215 if (params->diff == 0)
216 return;
217
218 /*
219 * Compute the input clock rate and internal divisor values to obtain
220 * the clock rate closest to the target frequency.
221 */
222 rate = clk_round_rate(clk, target);
223 div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1;
224 diff = abs(rate / (div + 1) - target);
225
226 /*
227 * Store the parameters if the resulting frequency is better than any
228 * previously calculated value.
229 */
230 if (diff < params->diff) {
231 params->clk = clk;
232 params->rate = rate;
233 params->diff = diff;
234 params->escr = escr | div;
235 }
236}
237
197static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { 238static const struct soc_device_attribute rcar_du_r8a7795_es1[] = {
198 { .soc_id = "r8a7795", .revision = "ES1.*" }, 239 { .soc_id = "r8a7795", .revision = "ES1.*" },
199 { /* sentinel */ } 240 { /* sentinel */ }
@@ -254,42 +295,24 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
254 295
255 escr = ESCR_DCLKSEL_DCLKIN | div; 296 escr = ESCR_DCLKSEL_DCLKIN | div;
256 } else { 297 } else {
257 unsigned long clk; 298 struct du_clk_params params = { .diff = (unsigned long)-1 };
258 u32 div;
259
260 /*
261 * Compute the clock divisor and select the internal or external
262 * dot clock based on the requested frequency.
263 */
264 clk = clk_get_rate(rcrtc->clock);
265 div = DIV_ROUND_CLOSEST(clk, mode_clock);
266 div = clamp(div, 1U, 64U) - 1;
267
268 escr = ESCR_DCLKSEL_CLKS | div;
269
270 if (rcrtc->extclock) {
271 unsigned long extclk;
272 unsigned long extrate;
273 unsigned long rate;
274 u32 extdiv;
275 299
276 extclk = clk_get_rate(rcrtc->extclock); 300 rcar_du_escr_divider(rcrtc->clock, mode_clock,
277 extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); 301 ESCR_DCLKSEL_CLKS, &params);
278 extdiv = clamp(extdiv, 1U, 64U) - 1; 302 if (rcrtc->extclock)
303 rcar_du_escr_divider(rcrtc->extclock, mode_clock,
304 ESCR_DCLKSEL_DCLKIN, &params);
279 305
280 extrate = extclk / (extdiv + 1); 306 dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n",
281 rate = clk / (div + 1); 307 mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext",
308 params.rate);
282 309
283 if (abs((long)extrate - (long)mode_clock) < 310 clk_set_rate(params.clk, params.rate);
284 abs((long)rate - (long)mode_clock)) 311 escr = params.escr;
285 escr = ESCR_DCLKSEL_DCLKIN | extdiv;
286
287 dev_dbg(rcrtc->group->dev->dev,
288 "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n",
289 mode_clock, extrate, rate, escr);
290 }
291 } 312 }
292 313
314 dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr);
315
293 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, 316 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
294 escr); 317 escr);
295 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); 318 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);