diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2010-02-23 03:24:38 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-02-24 20:38:06 -0500 |
commit | 383be5d1789d9a7a2e77dca1cb0aca89507d069e (patch) | |
tree | e99787c96586748c9ce78fbd10d8859c648fabc0 | |
parent | 939461d59d6ac4e5142f767d24810c9b4b5caa38 (diff) |
drm/radeon/kms: update new pll algo
- add support for pre-avivo chips
- add support for fixed post/ref dividers
- add support for non-fractional fb dividers
By default avivo chips use the new algo and
pre-avivo chips use the old algo. Use the "new_pll"
module option to toggle between them.
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/radeon/atombios_crtc.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_atombios.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 232 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_mode.h | 2 |
6 files changed, 178 insertions, 89 deletions
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 0ec6934c3a26..dd9fdf560611 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c | |||
@@ -438,12 +438,16 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, | |||
438 | 438 | ||
439 | /* select the PLL algo */ | 439 | /* select the PLL algo */ |
440 | if (ASIC_IS_AVIVO(rdev)) { | 440 | if (ASIC_IS_AVIVO(rdev)) { |
441 | if (radeon_new_pll) | 441 | if (radeon_new_pll == 0) |
442 | pll->algo = PLL_ALGO_AVIVO; | 442 | pll->algo = PLL_ALGO_LEGACY; |
443 | else | ||
444 | pll->algo = PLL_ALGO_NEW; | ||
445 | } else { | ||
446 | if (radeon_new_pll == 1) | ||
447 | pll->algo = PLL_ALGO_NEW; | ||
443 | else | 448 | else |
444 | pll->algo = PLL_ALGO_LEGACY; | 449 | pll->algo = PLL_ALGO_LEGACY; |
445 | } else | 450 | } |
446 | pll->algo = PLL_ALGO_LEGACY; | ||
447 | 451 | ||
448 | if (ASIC_IS_AVIVO(rdev)) { | 452 | if (ASIC_IS_AVIVO(rdev)) { |
449 | if ((rdev->family == CHIP_RS600) || | 453 | if ((rdev->family == CHIP_RS600) || |
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 33aed6c2d41a..6f8619cd1a0d 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c | |||
@@ -1191,12 +1191,16 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct | |||
1191 | lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id); | 1191 | lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id); |
1192 | 1192 | ||
1193 | if (ASIC_IS_AVIVO(rdev)) { | 1193 | if (ASIC_IS_AVIVO(rdev)) { |
1194 | if (radeon_new_pll) | 1194 | if (radeon_new_pll == 0) |
1195 | lvds->pll_algo = PLL_ALGO_AVIVO; | 1195 | lvds->pll_algo = PLL_ALGO_LEGACY; |
1196 | else | ||
1197 | lvds->pll_algo = PLL_ALGO_NEW; | ||
1198 | } else { | ||
1199 | if (radeon_new_pll == 1) | ||
1200 | lvds->pll_algo = PLL_ALGO_NEW; | ||
1196 | else | 1201 | else |
1197 | lvds->pll_algo = PLL_ALGO_LEGACY; | 1202 | lvds->pll_algo = PLL_ALGO_LEGACY; |
1198 | } else | 1203 | } |
1199 | lvds->pll_algo = PLL_ALGO_LEGACY; | ||
1200 | 1204 | ||
1201 | /* LVDS quirks */ | 1205 | /* LVDS quirks */ |
1202 | radeon_atom_apply_lvds_quirks(dev, lvds); | 1206 | radeon_atom_apply_lvds_quirks(dev, lvds); |
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 257827806aee..e35cc3da8f22 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c | |||
@@ -603,95 +603,173 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll, | |||
603 | *post_div_p = best_post_div; | 603 | *post_div_p = best_post_div; |
604 | } | 604 | } |
605 | 605 | ||
606 | static void radeon_compute_pll_avivo(struct radeon_pll *pll, | 606 | static bool |
607 | uint64_t freq, | 607 | calc_fb_div(struct radeon_pll *pll, |
608 | uint32_t *dot_clock_p, | 608 | uint32_t freq, |
609 | uint32_t *fb_div_p, | 609 | uint32_t post_div, |
610 | uint32_t *frac_fb_div_p, | 610 | uint32_t ref_div, |
611 | uint32_t *ref_div_p, | 611 | uint32_t *fb_div, |
612 | uint32_t *post_div_p) | 612 | uint32_t *fb_div_frac) |
613 | { | 613 | { |
614 | fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq; | 614 | fixed20_12 feedback_divider, a, b; |
615 | fixed20_12 pll_out_max, pll_out_min; | 615 | u32 vco_freq; |
616 | fixed20_12 pll_in_max, pll_in_min; | 616 | |
617 | fixed20_12 reference_freq; | 617 | vco_freq = freq * post_div; |
618 | fixed20_12 error, ffreq, a, b; | 618 | /* feedback_divider = vco_freq * ref_div / pll->reference_freq; */ |
619 | 619 | a.full = rfixed_const(pll->reference_freq); | |
620 | pll_out_max.full = rfixed_const(pll->pll_out_max); | 620 | feedback_divider.full = rfixed_const(vco_freq); |
621 | pll_out_min.full = rfixed_const(pll->pll_out_min); | 621 | feedback_divider.full = rfixed_div(feedback_divider, a); |
622 | pll_in_max.full = rfixed_const(pll->pll_in_max); | 622 | a.full = rfixed_const(ref_div); |
623 | pll_in_min.full = rfixed_const(pll->pll_in_min); | 623 | feedback_divider.full = rfixed_mul(feedback_divider, a); |
624 | reference_freq.full = rfixed_const(pll->reference_freq); | 624 | |
625 | do_div(freq, 10); | 625 | if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { |
626 | /* feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; */ | ||
627 | a.full = rfixed_const(10); | ||
628 | feedback_divider.full = rfixed_mul(feedback_divider, a); | ||
629 | feedback_divider.full += rfixed_const_half(0); | ||
630 | feedback_divider.full = rfixed_floor(feedback_divider); | ||
631 | feedback_divider.full = rfixed_div(feedback_divider, a); | ||
632 | |||
633 | /* *fb_div = floor(feedback_divider); */ | ||
634 | a.full = rfixed_floor(feedback_divider); | ||
635 | *fb_div = rfixed_trunc(a); | ||
636 | /* *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; */ | ||
637 | a.full = rfixed_const(10); | ||
638 | b.full = rfixed_mul(feedback_divider, a); | ||
639 | |||
640 | feedback_divider.full = rfixed_floor(feedback_divider); | ||
641 | feedback_divider.full = rfixed_mul(feedback_divider, a); | ||
642 | feedback_divider.full = b.full - feedback_divider.full; | ||
643 | *fb_div_frac = rfixed_trunc(feedback_divider); | ||
644 | } else { | ||
645 | /* *fb_div = floor(feedback_divider + 0.5); */ | ||
646 | feedback_divider.full += rfixed_const_half(0); | ||
647 | feedback_divider.full = rfixed_floor(feedback_divider); | ||
648 | |||
649 | *fb_div = rfixed_trunc(feedback_divider); | ||
650 | *fb_div_frac = 0; | ||
651 | } | ||
652 | |||
653 | if (((*fb_div) < pll->min_feedback_div) || ((*fb_div) > pll->max_feedback_div)) | ||
654 | return false; | ||
655 | else | ||
656 | return true; | ||
657 | } | ||
658 | |||
659 | static bool | ||
660 | calc_fb_ref_div(struct radeon_pll *pll, | ||
661 | uint32_t freq, | ||
662 | uint32_t post_div, | ||
663 | uint32_t *fb_div, | ||
664 | uint32_t *fb_div_frac, | ||
665 | uint32_t *ref_div) | ||
666 | { | ||
667 | fixed20_12 ffreq, max_error, error, pll_out, a; | ||
668 | u32 vco; | ||
669 | |||
626 | ffreq.full = rfixed_const(freq); | 670 | ffreq.full = rfixed_const(freq); |
627 | error.full = rfixed_const(100 * 100); | 671 | /* max_error = ffreq * 0.0025; */ |
672 | a.full = rfixed_const(400); | ||
673 | max_error.full = rfixed_div(ffreq, a); | ||
674 | |||
675 | for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) { | ||
676 | if (calc_fb_div(pll, freq, post_div, (*ref_div), fb_div, fb_div_frac)) { | ||
677 | vco = pll->reference_freq * (((*fb_div) * 10) + (*fb_div_frac)); | ||
678 | vco = vco / ((*ref_div) * 10); | ||
679 | |||
680 | if ((vco < pll->pll_out_min) || (vco > pll->pll_out_max)) | ||
681 | continue; | ||
628 | 682 | ||
629 | /* max p */ | 683 | /* pll_out = vco / post_div; */ |
630 | p.full = rfixed_div(pll_out_max, ffreq); | 684 | a.full = rfixed_const(post_div); |
631 | p.full = rfixed_floor(p); | 685 | pll_out.full = rfixed_const(vco); |
686 | pll_out.full = rfixed_div(pll_out, a); | ||
632 | 687 | ||
633 | /* min m */ | 688 | if (pll_out.full >= ffreq.full) { |
634 | m.full = rfixed_div(reference_freq, pll_in_max); | 689 | error.full = pll_out.full - ffreq.full; |
635 | m.full = rfixed_ceil(m); | 690 | if (error.full <= max_error.full) |
691 | return true; | ||
692 | } | ||
693 | } | ||
694 | } | ||
695 | return false; | ||
696 | } | ||
636 | 697 | ||
637 | while (1) { | 698 | static void radeon_compute_pll_new(struct radeon_pll *pll, |
638 | n.full = rfixed_div(ffreq, reference_freq); | 699 | uint64_t freq, |
639 | n.full = rfixed_mul(n, m); | 700 | uint32_t *dot_clock_p, |
640 | n.full = rfixed_mul(n, p); | 701 | uint32_t *fb_div_p, |
702 | uint32_t *frac_fb_div_p, | ||
703 | uint32_t *ref_div_p, | ||
704 | uint32_t *post_div_p) | ||
705 | { | ||
706 | u32 fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0; | ||
707 | u32 best_freq = 0, vco_frequency; | ||
641 | 708 | ||
642 | f_vco.full = rfixed_div(n, m); | 709 | /* freq = freq / 10; */ |
643 | f_vco.full = rfixed_mul(f_vco, reference_freq); | 710 | do_div(freq, 10); |
644 | 711 | ||
645 | f_pclk.full = rfixed_div(f_vco, p); | 712 | if (pll->flags & RADEON_PLL_USE_POST_DIV) { |
713 | post_div = pll->post_div; | ||
714 | if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div)) | ||
715 | goto done; | ||
716 | |||
717 | vco_frequency = freq * post_div; | ||
718 | if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) | ||
719 | goto done; | ||
720 | |||
721 | if (pll->flags & RADEON_PLL_USE_REF_DIV) { | ||
722 | ref_div = pll->reference_div; | ||
723 | if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) | ||
724 | goto done; | ||
725 | if (!calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac)) | ||
726 | goto done; | ||
727 | } | ||
728 | } else { | ||
729 | for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) { | ||
730 | if (pll->flags & RADEON_PLL_LEGACY) { | ||
731 | if ((post_div == 5) || | ||
732 | (post_div == 7) || | ||
733 | (post_div == 9) || | ||
734 | (post_div == 10) || | ||
735 | (post_div == 11)) | ||
736 | continue; | ||
737 | } | ||
646 | 738 | ||
647 | if (f_pclk.full > ffreq.full) | 739 | if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) |
648 | error.full = f_pclk.full - ffreq.full; | 740 | continue; |
649 | else | ||
650 | error.full = ffreq.full - f_pclk.full; | ||
651 | error.full = rfixed_div(error, f_pclk); | ||
652 | a.full = rfixed_const(100 * 100); | ||
653 | error.full = rfixed_mul(error, a); | ||
654 | |||
655 | a.full = rfixed_mul(m, p); | ||
656 | a.full = rfixed_div(n, a); | ||
657 | best_freq.full = rfixed_mul(reference_freq, a); | ||
658 | |||
659 | if (rfixed_trunc(error) < 25) | ||
660 | break; | ||
661 | |||
662 | a.full = rfixed_const(1); | ||
663 | m.full = m.full + a.full; | ||
664 | a.full = rfixed_div(reference_freq, m); | ||
665 | if (a.full >= pll_in_min.full) | ||
666 | continue; | ||
667 | 741 | ||
668 | m.full = rfixed_div(reference_freq, pll_in_max); | 742 | vco_frequency = freq * post_div; |
669 | m.full = rfixed_ceil(m); | 743 | if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) |
670 | a.full= rfixed_const(1); | 744 | continue; |
671 | p.full = p.full - a.full; | 745 | if (pll->flags & RADEON_PLL_USE_REF_DIV) { |
672 | a.full = rfixed_mul(p, ffreq); | 746 | ref_div = pll->reference_div; |
673 | if (a.full >= pll_out_min.full) | 747 | if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) |
674 | continue; | 748 | goto done; |
675 | else { | 749 | if (calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac)) |
676 | DRM_ERROR("Unable to find pll dividers\n"); | 750 | break; |
677 | break; | 751 | } else { |
752 | if (calc_fb_ref_div(pll, freq, post_div, &fb_div, &fb_div_frac, &ref_div)) | ||
753 | break; | ||
754 | } | ||
678 | } | 755 | } |
679 | } | 756 | } |
680 | 757 | ||
681 | a.full = rfixed_const(10); | 758 | best_freq = pll->reference_freq * 10 * fb_div; |
682 | b.full = rfixed_mul(n, a); | 759 | best_freq += pll->reference_freq * fb_div_frac; |
760 | best_freq = best_freq / (ref_div * post_div); | ||
683 | 761 | ||
684 | frac_n.full = rfixed_floor(n); | 762 | done: |
685 | frac_n.full = rfixed_mul(frac_n, a); | 763 | if (best_freq == 0) |
686 | frac_n.full = b.full - frac_n.full; | 764 | DRM_ERROR("Couldn't find valid PLL dividers\n"); |
687 | 765 | ||
688 | *dot_clock_p = rfixed_trunc(best_freq); | 766 | *dot_clock_p = best_freq / 10; |
689 | *fb_div_p = rfixed_trunc(n); | 767 | *fb_div_p = fb_div; |
690 | *frac_fb_div_p = rfixed_trunc(frac_n); | 768 | *frac_fb_div_p = fb_div_frac; |
691 | *ref_div_p = rfixed_trunc(m); | 769 | *ref_div_p = ref_div; |
692 | *post_div_p = rfixed_trunc(p); | 770 | *post_div_p = post_div; |
693 | 771 | ||
694 | DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p); | 772 | DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p); |
695 | } | 773 | } |
696 | 774 | ||
697 | void radeon_compute_pll(struct radeon_pll *pll, | 775 | void radeon_compute_pll(struct radeon_pll *pll, |
@@ -703,9 +781,9 @@ void radeon_compute_pll(struct radeon_pll *pll, | |||
703 | uint32_t *post_div_p) | 781 | uint32_t *post_div_p) |
704 | { | 782 | { |
705 | switch (pll->algo) { | 783 | switch (pll->algo) { |
706 | case PLL_ALGO_AVIVO: | 784 | case PLL_ALGO_NEW: |
707 | radeon_compute_pll_avivo(pll, freq, dot_clock_p, fb_div_p, | 785 | radeon_compute_pll_new(pll, freq, dot_clock_p, fb_div_p, |
708 | frac_fb_div_p, ref_div_p, post_div_p); | 786 | frac_fb_div_p, ref_div_p, post_div_p); |
709 | break; | 787 | break; |
710 | case PLL_ALGO_LEGACY: | 788 | case PLL_ALGO_LEGACY: |
711 | default: | 789 | default: |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index a9572e6d4d64..be99d4e55a34 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -86,7 +86,7 @@ int radeon_benchmarking = 0; | |||
86 | int radeon_testing = 0; | 86 | int radeon_testing = 0; |
87 | int radeon_connector_table = 0; | 87 | int radeon_connector_table = 0; |
88 | int radeon_tv = 1; | 88 | int radeon_tv = 1; |
89 | int radeon_new_pll = 1; | 89 | int radeon_new_pll = -1; |
90 | int radeon_dynpm = -1; | 90 | int radeon_dynpm = -1; |
91 | int radeon_audio = 1; | 91 | int radeon_audio = 1; |
92 | 92 | ||
@@ -123,7 +123,7 @@ module_param_named(connector_table, radeon_connector_table, int, 0444); | |||
123 | MODULE_PARM_DESC(tv, "TV enable (0 = disable)"); | 123 | MODULE_PARM_DESC(tv, "TV enable (0 = disable)"); |
124 | module_param_named(tv, radeon_tv, int, 0444); | 124 | module_param_named(tv, radeon_tv, int, 0444); |
125 | 125 | ||
126 | MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips"); | 126 | MODULE_PARM_DESC(new_pll, "Select new PLL code"); |
127 | module_param_named(new_pll, radeon_new_pll, int, 0444); | 127 | module_param_named(new_pll, radeon_new_pll, int, 0444); |
128 | 128 | ||
129 | MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)"); | 129 | MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)"); |
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 643251719f1c..df23d6a01d02 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c | |||
@@ -703,7 +703,10 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) | |||
703 | pll = &rdev->clock.p1pll; | 703 | pll = &rdev->clock.p1pll; |
704 | 704 | ||
705 | pll->flags = RADEON_PLL_LEGACY; | 705 | pll->flags = RADEON_PLL_LEGACY; |
706 | pll->algo = PLL_ALGO_LEGACY; | 706 | if (radeon_new_pll == 1) |
707 | pll->algo = PLL_ALGO_NEW; | ||
708 | else | ||
709 | pll->algo = PLL_ALGO_LEGACY; | ||
707 | 710 | ||
708 | if (mode->clock > 200000) /* range limits??? */ | 711 | if (mode->clock > 200000) /* range limits??? */ |
709 | pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; | 712 | pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; |
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 8912f2e8e640..1702b820aa4d 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h | |||
@@ -133,7 +133,7 @@ struct radeon_tmds_pll { | |||
133 | /* pll algo */ | 133 | /* pll algo */ |
134 | enum radeon_pll_algo { | 134 | enum radeon_pll_algo { |
135 | PLL_ALGO_LEGACY, | 135 | PLL_ALGO_LEGACY, |
136 | PLL_ALGO_AVIVO | 136 | PLL_ALGO_NEW |
137 | }; | 137 | }; |
138 | 138 | ||
139 | struct radeon_pll { | 139 | struct radeon_pll { |