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

drm/tilcdc: add a workaround for failed clk_set_rate()

Some architectures don't use the common clock framework and don't
implement all the clk interfaces for every clock. This is the case
for da850-lcdk where clk_set_rate() only works for PLL0 and PLL1.

Trying to set the clock rate for the LCDC clock results in -EINVAL
being returned.

As a workaround for that: if the call to clk_set_rate() fails, fall
back to adjusting the clock divider instead. Proper divider value is
calculated by dividing the current clock rate by the required pixel
clock rate in HZ.

This code is based on a hack initially developed internally for
baylibre by Karl Beldan <kbeldan@baylibre.com>.

Tested with a da850-lcdk with an LCD display connected over VGA.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Jyri Sarha <jsarha@ti.com>

authored by

Bartosz Golaszewski and committed by
Jyri Sarha
cb42e20e 7625e052

+51 -6
+51 -6
drivers/gpu/drm/tilcdc/tilcdc_crtc.c
··· 318 318 return true; 319 319 } 320 320 321 + /* 322 + * Calculate the percentage difference between the requested pixel clock rate 323 + * and the effective rate resulting from calculating the clock divider value. 324 + */ 325 + static unsigned int tilcdc_pclk_diff(unsigned long rate, 326 + unsigned long real_rate) 327 + { 328 + int r = rate / 100, rr = real_rate / 100; 329 + 330 + return (unsigned int)(abs(((rr - r) * 100) / r)); 331 + } 332 + 321 333 static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) 322 334 { 323 335 struct drm_device *dev = crtc->dev; 324 336 struct tilcdc_drm_private *priv = dev->dev_private; 325 337 struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); 326 - const unsigned clkdiv = 2; /* using a fixed divider of 2 */ 338 + unsigned long clk_rate, real_rate, req_rate; 339 + unsigned int clkdiv; 327 340 int ret; 328 341 342 + clkdiv = 2; /* first try using a standard divider of 2 */ 343 + 329 344 /* mode.clock is in KHz, set_rate wants parameter in Hz */ 330 - ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); 345 + req_rate = crtc->mode.clock * 1000; 346 + 347 + ret = clk_set_rate(priv->clk, req_rate * clkdiv); 348 + clk_rate = clk_get_rate(priv->clk); 331 349 if (ret < 0) { 332 - dev_err(dev->dev, "failed to set display clock rate to: %d\n", 333 - crtc->mode.clock); 334 - return; 350 + /* 351 + * If we fail to set the clock rate (some architectures don't 352 + * use the common clock framework yet and may not implement 353 + * all the clk API calls for every clock), try the next best 354 + * thing: adjusting the clock divider, unless clk_get_rate() 355 + * failed as well. 356 + */ 357 + if (!clk_rate) { 358 + /* Nothing more we can do. Just bail out. */ 359 + dev_err(dev->dev, 360 + "failed to set the pixel clock - unable to read current lcdc clock rate\n"); 361 + return; 362 + } 363 + 364 + clkdiv = DIV_ROUND_CLOSEST(clk_rate, req_rate); 365 + 366 + /* 367 + * Emit a warning if the real clock rate resulting from the 368 + * calculated divider differs much from the requested rate. 369 + * 370 + * 5% is an arbitrary value - LCDs are usually quite tolerant 371 + * about pixel clock rates. 372 + */ 373 + real_rate = clkdiv * req_rate; 374 + 375 + if (tilcdc_pclk_diff(clk_rate, real_rate) > 5) { 376 + dev_warn(dev->dev, 377 + "effective pixel clock rate (%luHz) differs from the calculated rate (%luHz)\n", 378 + clk_rate, real_rate); 379 + } 335 380 } 336 381 337 - tilcdc_crtc->lcd_fck_rate = clk_get_rate(priv->clk); 382 + tilcdc_crtc->lcd_fck_rate = clk_rate; 338 383 339 384 DBG("lcd_clk=%u, mode clock=%d, div=%u", 340 385 tilcdc_crtc->lcd_fck_rate, crtc->mode.clock, clkdiv);