diff options
-rw-r--r-- | drivers/gpu/drm/tegra/sor.c | 223 |
1 files changed, 213 insertions, 10 deletions
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index f082ea22f32e..4b554908be5e 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c | |||
@@ -40,6 +40,16 @@ struct tegra_sor { | |||
40 | struct dentry *debugfs; | 40 | struct dentry *debugfs; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | struct tegra_sor_config { | ||
44 | u32 bits_per_pixel; | ||
45 | |||
46 | u32 active_polarity; | ||
47 | u32 active_count; | ||
48 | u32 tu_size; | ||
49 | u32 active_frac; | ||
50 | u32 watermark; | ||
51 | }; | ||
52 | |||
43 | static inline struct tegra_sor * | 53 | static inline struct tegra_sor * |
44 | host1x_client_to_sor(struct host1x_client *client) | 54 | host1x_client_to_sor(struct host1x_client *client) |
45 | { | 55 | { |
@@ -293,12 +303,173 @@ static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) | |||
293 | return -ETIMEDOUT; | 303 | return -ETIMEDOUT; |
294 | } | 304 | } |
295 | 305 | ||
306 | struct tegra_sor_params { | ||
307 | /* number of link clocks per line */ | ||
308 | unsigned int num_clocks; | ||
309 | /* ratio between input and output */ | ||
310 | u64 ratio; | ||
311 | /* precision factor */ | ||
312 | u64 precision; | ||
313 | |||
314 | unsigned int active_polarity; | ||
315 | unsigned int active_count; | ||
316 | unsigned int active_frac; | ||
317 | unsigned int tu_size; | ||
318 | unsigned int error; | ||
319 | }; | ||
320 | |||
321 | static int tegra_sor_compute_params(struct tegra_sor *sor, | ||
322 | struct tegra_sor_params *params, | ||
323 | unsigned int tu_size) | ||
324 | { | ||
325 | u64 active_sym, active_count, frac, approx; | ||
326 | u32 active_polarity, active_frac = 0; | ||
327 | const u64 f = params->precision; | ||
328 | s64 error; | ||
329 | |||
330 | active_sym = params->ratio * tu_size; | ||
331 | active_count = div_u64(active_sym, f) * f; | ||
332 | frac = active_sym - active_count; | ||
333 | |||
334 | /* fraction < 0.5 */ | ||
335 | if (frac >= (f / 2)) { | ||
336 | active_polarity = 1; | ||
337 | frac = f - frac; | ||
338 | } else { | ||
339 | active_polarity = 0; | ||
340 | } | ||
341 | |||
342 | if (frac != 0) { | ||
343 | frac = div_u64(f * f, frac); /* 1/fraction */ | ||
344 | if (frac <= (15 * f)) { | ||
345 | active_frac = div_u64(frac, f); | ||
346 | |||
347 | /* round up */ | ||
348 | if (active_polarity) | ||
349 | active_frac++; | ||
350 | } else { | ||
351 | active_frac = active_polarity ? 1 : 15; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | if (active_frac == 1) | ||
356 | active_polarity = 0; | ||
357 | |||
358 | if (active_polarity == 1) { | ||
359 | if (active_frac) { | ||
360 | approx = active_count + (active_frac * (f - 1)) * f; | ||
361 | approx = div_u64(approx, active_frac * f); | ||
362 | } else { | ||
363 | approx = active_count + f; | ||
364 | } | ||
365 | } else { | ||
366 | if (active_frac) | ||
367 | approx = active_count + div_u64(f, active_frac); | ||
368 | else | ||
369 | approx = active_count; | ||
370 | } | ||
371 | |||
372 | error = div_s64(active_sym - approx, tu_size); | ||
373 | error *= params->num_clocks; | ||
374 | |||
375 | if (error <= 0 && abs64(error) < params->error) { | ||
376 | params->active_count = div_u64(active_count, f); | ||
377 | params->active_polarity = active_polarity; | ||
378 | params->active_frac = active_frac; | ||
379 | params->error = abs64(error); | ||
380 | params->tu_size = tu_size; | ||
381 | |||
382 | if (error == 0) | ||
383 | return true; | ||
384 | } | ||
385 | |||
386 | return false; | ||
387 | } | ||
388 | |||
389 | static int tegra_sor_calc_config(struct tegra_sor *sor, | ||
390 | struct drm_display_mode *mode, | ||
391 | struct tegra_sor_config *config, | ||
392 | struct drm_dp_link *link) | ||
393 | { | ||
394 | const u64 f = 100000, link_rate = link->rate * 1000; | ||
395 | const u64 pclk = mode->clock * 1000; | ||
396 | struct tegra_sor_params params; | ||
397 | u64 input, output, watermark; | ||
398 | u32 num_syms_per_line; | ||
399 | unsigned int i; | ||
400 | |||
401 | if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) | ||
402 | return -EINVAL; | ||
403 | |||
404 | output = link_rate * 8 * link->num_lanes; | ||
405 | input = pclk * config->bits_per_pixel; | ||
406 | |||
407 | if (input >= output) | ||
408 | return -ERANGE; | ||
409 | |||
410 | memset(¶ms, 0, sizeof(params)); | ||
411 | params.ratio = div64_u64(input * f, output); | ||
412 | params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk); | ||
413 | params.precision = f; | ||
414 | params.error = 64 * f; | ||
415 | params.tu_size = 64; | ||
416 | |||
417 | for (i = params.tu_size; i >= 32; i--) | ||
418 | if (tegra_sor_compute_params(sor, ¶ms, i)) | ||
419 | break; | ||
420 | |||
421 | if (params.active_frac == 0) { | ||
422 | config->active_polarity = 0; | ||
423 | config->active_count = params.active_count; | ||
424 | |||
425 | if (!params.active_polarity) | ||
426 | config->active_count--; | ||
427 | |||
428 | config->tu_size = params.tu_size; | ||
429 | config->active_frac = 1; | ||
430 | } else { | ||
431 | config->active_polarity = params.active_polarity; | ||
432 | config->active_count = params.active_count; | ||
433 | config->active_frac = params.active_frac; | ||
434 | config->tu_size = params.tu_size; | ||
435 | } | ||
436 | |||
437 | dev_dbg(sor->dev, | ||
438 | "polarity: %d active count: %d tu size: %d active frac: %d\n", | ||
439 | config->active_polarity, config->active_count, | ||
440 | config->tu_size, config->active_frac); | ||
441 | |||
442 | watermark = params.ratio * config->tu_size * (f - params.ratio); | ||
443 | watermark = div_u64(watermark, f); | ||
444 | |||
445 | watermark = div_u64(watermark + params.error, f); | ||
446 | config->watermark = watermark + (config->bits_per_pixel / 8) + 2; | ||
447 | num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * | ||
448 | (link->num_lanes * 8); | ||
449 | |||
450 | if (config->watermark > 30) { | ||
451 | config->watermark = 30; | ||
452 | dev_err(sor->dev, | ||
453 | "unable to compute TU size, forcing watermark to %u\n", | ||
454 | config->watermark); | ||
455 | } else if (config->watermark > num_syms_per_line) { | ||
456 | config->watermark = num_syms_per_line; | ||
457 | dev_err(sor->dev, "watermark too high, forcing to %u\n", | ||
458 | config->watermark); | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
296 | static int tegra_output_sor_enable(struct tegra_output *output) | 464 | static int tegra_output_sor_enable(struct tegra_output *output) |
297 | { | 465 | { |
298 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | 466 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); |
299 | struct drm_display_mode *mode = &dc->base.mode; | 467 | struct drm_display_mode *mode = &dc->base.mode; |
300 | unsigned int vbe, vse, hbe, hse, vbs, hbs, i; | 468 | unsigned int vbe, vse, hbe, hse, vbs, hbs, i; |
301 | struct tegra_sor *sor = to_sor(output); | 469 | struct tegra_sor *sor = to_sor(output); |
470 | struct tegra_sor_config config; | ||
471 | struct drm_dp_link link; | ||
472 | struct drm_dp_aux *aux; | ||
302 | unsigned long value; | 473 | unsigned long value; |
303 | int err = 0; | 474 | int err = 0; |
304 | 475 | ||
@@ -313,16 +484,34 @@ static int tegra_output_sor_enable(struct tegra_output *output) | |||
313 | 484 | ||
314 | reset_control_deassert(sor->rst); | 485 | reset_control_deassert(sor->rst); |
315 | 486 | ||
487 | /* FIXME: properly convert to struct drm_dp_aux */ | ||
488 | aux = (struct drm_dp_aux *)sor->dpaux; | ||
489 | |||
316 | if (sor->dpaux) { | 490 | if (sor->dpaux) { |
317 | err = tegra_dpaux_enable(sor->dpaux); | 491 | err = tegra_dpaux_enable(sor->dpaux); |
318 | if (err < 0) | 492 | if (err < 0) |
319 | dev_err(sor->dev, "failed to enable DP: %d\n", err); | 493 | dev_err(sor->dev, "failed to enable DP: %d\n", err); |
494 | |||
495 | err = drm_dp_link_probe(aux, &link); | ||
496 | if (err < 0) { | ||
497 | dev_err(sor->dev, "failed to probe eDP link: %d\n", | ||
498 | err); | ||
499 | return err; | ||
500 | } | ||
320 | } | 501 | } |
321 | 502 | ||
322 | err = clk_set_parent(sor->clk, sor->clk_safe); | 503 | err = clk_set_parent(sor->clk, sor->clk_safe); |
323 | if (err < 0) | 504 | if (err < 0) |
324 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); | 505 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); |
325 | 506 | ||
507 | memset(&config, 0, sizeof(config)); | ||
508 | config.bits_per_pixel = 24; /* XXX: don't hardcode? */ | ||
509 | |||
510 | err = tegra_sor_calc_config(sor, mode, &config, &link); | ||
511 | if (err < 0) | ||
512 | dev_err(sor->dev, "failed to compute link configuration: %d\n", | ||
513 | err); | ||
514 | |||
326 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | 515 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); |
327 | value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; | 516 | value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; |
328 | value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; | 517 | value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; |
@@ -460,7 +649,7 @@ static int tegra_output_sor_enable(struct tegra_output *output) | |||
460 | value |= SOR_DP_LINKCTL_ENABLE; | 649 | value |= SOR_DP_LINKCTL_ENABLE; |
461 | 650 | ||
462 | value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; | 651 | value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; |
463 | value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */ | 652 | value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size); |
464 | 653 | ||
465 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; | 654 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; |
466 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | 655 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); |
@@ -476,15 +665,18 @@ static int tegra_output_sor_enable(struct tegra_output *output) | |||
476 | 665 | ||
477 | value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); | 666 | value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); |
478 | value &= ~SOR_DP_CONFIG_WATERMARK_MASK; | 667 | value &= ~SOR_DP_CONFIG_WATERMARK_MASK; |
479 | value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */ | 668 | value |= SOR_DP_CONFIG_WATERMARK(config.watermark); |
480 | 669 | ||
481 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; | 670 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; |
482 | value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */ | 671 | value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count); |
483 | 672 | ||
484 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; | 673 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; |
485 | value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */ | 674 | value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac); |
486 | 675 | ||
487 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */ | 676 | if (config.active_polarity) |
677 | value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; | ||
678 | else | ||
679 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; | ||
488 | 680 | ||
489 | value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; | 681 | value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; |
490 | value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */ | 682 | value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */ |
@@ -506,9 +698,6 @@ static int tegra_output_sor_enable(struct tegra_output *output) | |||
506 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | 698 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); |
507 | 699 | ||
508 | if (sor->dpaux) { | 700 | if (sor->dpaux) { |
509 | /* FIXME: properly convert to struct drm_dp_aux */ | ||
510 | struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux; | ||
511 | struct drm_dp_link link; | ||
512 | u8 rate, lanes; | 701 | u8 rate, lanes; |
513 | 702 | ||
514 | err = drm_dp_link_probe(aux, &link); | 703 | err = drm_dp_link_probe(aux, &link); |
@@ -592,12 +781,26 @@ static int tegra_output_sor_enable(struct tegra_output *output) | |||
592 | * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete | 781 | * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete |
593 | * raster, associate with display controller) | 782 | * raster, associate with display controller) |
594 | */ | 783 | */ |
595 | value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 | | 784 | value = SOR_STATE_ASY_VSYNCPOL | |
596 | SOR_STATE_ASY_VSYNCPOL | | ||
597 | SOR_STATE_ASY_HSYNCPOL | | 785 | SOR_STATE_ASY_HSYNCPOL | |
598 | SOR_STATE_ASY_PROTOCOL_DP_A | | 786 | SOR_STATE_ASY_PROTOCOL_DP_A | |
599 | SOR_STATE_ASY_CRC_MODE_COMPLETE | | 787 | SOR_STATE_ASY_CRC_MODE_COMPLETE | |
600 | SOR_STATE_ASY_OWNER(dc->pipe + 1); | 788 | SOR_STATE_ASY_OWNER(dc->pipe + 1); |
789 | |||
790 | switch (config.bits_per_pixel) { | ||
791 | case 24: | ||
792 | value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; | ||
793 | break; | ||
794 | |||
795 | case 18: | ||
796 | value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; | ||
797 | break; | ||
798 | |||
799 | default: | ||
800 | BUG(); | ||
801 | break; | ||
802 | } | ||
803 | |||
601 | tegra_sor_writel(sor, value, SOR_STATE_1); | 804 | tegra_sor_writel(sor, value, SOR_STATE_1); |
602 | 805 | ||
603 | /* | 806 | /* |