diff options
| -rw-r--r-- | drivers/gpu/drm/radeon/evergreen.c | 103 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/r600d.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_uvd.c | 137 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/rv770.c | 110 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/si.c | 104 |
6 files changed, 191 insertions, 278 deletions
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 1531f167d152..105bafb6c29d 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c | |||
| @@ -989,62 +989,10 @@ done: | |||
| 989 | return r; | 989 | return r; |
| 990 | } | 990 | } |
| 991 | 991 | ||
| 992 | static int evergreen_uvd_calc_post_div(unsigned target_freq, | ||
| 993 | unsigned vco_freq, | ||
| 994 | unsigned *div) | ||
| 995 | { | ||
| 996 | /* target larger than vco frequency ? */ | ||
| 997 | if (vco_freq < target_freq) | ||
| 998 | return -1; /* forget it */ | ||
| 999 | |||
| 1000 | /* Fclk = Fvco / PDIV */ | ||
| 1001 | *div = vco_freq / target_freq; | ||
| 1002 | |||
| 1003 | /* we alway need a frequency less than or equal the target */ | ||
| 1004 | if ((vco_freq / *div) > target_freq) | ||
| 1005 | *div += 1; | ||
| 1006 | |||
| 1007 | /* dividers above 5 must be even */ | ||
| 1008 | if (*div > 5 && *div % 2) | ||
| 1009 | *div += 1; | ||
| 1010 | |||
| 1011 | /* out of range ? */ | ||
| 1012 | if (*div >= 128) | ||
| 1013 | return -1; /* forget it */ | ||
| 1014 | |||
| 1015 | return vco_freq / *div; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
| 1019 | { | ||
| 1020 | unsigned i; | ||
| 1021 | |||
| 1022 | /* assert UPLL_CTLREQ */ | ||
| 1023 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
| 1024 | |||
| 1025 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
| 1026 | for (i = 0; i < 100; ++i) { | ||
| 1027 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
| 1028 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
| 1029 | break; | ||
| 1030 | mdelay(10); | ||
| 1031 | } | ||
| 1032 | if (i == 100) | ||
| 1033 | return -ETIMEDOUT; | ||
| 1034 | |||
| 1035 | /* deassert UPLL_CTLREQ */ | ||
| 1036 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
| 1037 | |||
| 1038 | return 0; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 992 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
| 1042 | { | 993 | { |
| 1043 | /* start off with something large */ | 994 | /* start off with something large */ |
| 1044 | int optimal_diff_score = 0x7FFFFFF; | 995 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
| 1045 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
| 1046 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
| 1047 | unsigned vco_freq; | ||
| 1048 | int r; | 996 | int r; |
| 1049 | 997 | ||
| 1050 | /* bypass vclk and dclk with bclk */ | 998 | /* bypass vclk and dclk with bclk */ |
| @@ -1061,40 +1009,11 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 1061 | return 0; | 1009 | return 0; |
| 1062 | } | 1010 | } |
| 1063 | 1011 | ||
| 1064 | /* loop through vco from low to high */ | 1012 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 125000, 250000, |
| 1065 | for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { | 1013 | 16384, 0x03FFFFFF, 0, 128, 5, |
| 1066 | unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; | 1014 | &fb_div, &vclk_div, &dclk_div); |
| 1067 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 1015 | if (r) |
| 1068 | unsigned vclk_div, dclk_div; | 1016 | return r; |
| 1069 | |||
| 1070 | /* fb div out of range ? */ | ||
| 1071 | if (fb_div > 0x03FFFFFF) | ||
| 1072 | break; /* it can oly get worse */ | ||
| 1073 | |||
| 1074 | /* calc vclk with current vco freq. */ | ||
| 1075 | calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
| 1076 | if (calc_clk == -1) | ||
| 1077 | break; /* vco is too big, it has to stop. */ | ||
| 1078 | diff_vclk = vclk - calc_clk; | ||
| 1079 | |||
| 1080 | /* calc dclk with current vco freq. */ | ||
| 1081 | calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
| 1082 | if (calc_clk == -1) | ||
| 1083 | break; /* vco is too big, it has to stop. */ | ||
| 1084 | diff_dclk = dclk - calc_clk; | ||
| 1085 | |||
| 1086 | /* determine if this vco setting is better than current optimal settings */ | ||
| 1087 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
| 1088 | if (diff_score < optimal_diff_score) { | ||
| 1089 | optimal_fb_div = fb_div; | ||
| 1090 | optimal_vclk_div = vclk_div; | ||
| 1091 | optimal_dclk_div = dclk_div; | ||
| 1092 | optimal_vco_freq = vco_freq; | ||
| 1093 | optimal_diff_score = diff_score; | ||
| 1094 | if (optimal_diff_score == 0) | ||
| 1095 | break; /* it can't get better than this */ | ||
| 1096 | } | ||
| 1097 | } | ||
| 1098 | 1017 | ||
| 1099 | /* set VCO_MODE to 1 */ | 1018 | /* set VCO_MODE to 1 */ |
| 1100 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); | 1019 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); |
| @@ -1108,7 +1027,7 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 1108 | 1027 | ||
| 1109 | mdelay(1); | 1028 | mdelay(1); |
| 1110 | 1029 | ||
| 1111 | r = evergreen_uvd_send_upll_ctlreq(rdev); | 1030 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 1112 | if (r) | 1031 | if (r) |
| 1113 | return r; | 1032 | return r; |
| 1114 | 1033 | ||
| @@ -1119,19 +1038,19 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 1119 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); | 1038 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); |
| 1120 | 1039 | ||
| 1121 | /* set feedback divider */ | 1040 | /* set feedback divider */ |
| 1122 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); | 1041 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), ~UPLL_FB_DIV_MASK); |
| 1123 | 1042 | ||
| 1124 | /* set ref divider to 0 */ | 1043 | /* set ref divider to 0 */ |
| 1125 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); | 1044 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); |
| 1126 | 1045 | ||
| 1127 | if (optimal_vco_freq < 187500) | 1046 | if (fb_div < 307200) |
| 1128 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); | 1047 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); |
| 1129 | else | 1048 | else |
| 1130 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); | 1049 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); |
| 1131 | 1050 | ||
| 1132 | /* set PDIV_A and PDIV_B */ | 1051 | /* set PDIV_A and PDIV_B */ |
| 1133 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 1052 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
| 1134 | UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), | 1053 | UPLL_PDIV_A(vclk_div) | UPLL_PDIV_B(dclk_div), |
| 1135 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); | 1054 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); |
| 1136 | 1055 | ||
| 1137 | /* give the PLL some time to settle */ | 1056 | /* give the PLL some time to settle */ |
| @@ -1145,7 +1064,7 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 1145 | /* switch from bypass mode to normal mode */ | 1064 | /* switch from bypass mode to normal mode */ |
| 1146 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 1065 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
| 1147 | 1066 | ||
| 1148 | r = evergreen_uvd_send_upll_ctlreq(rdev); | 1067 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 1149 | if (r) | 1068 | if (r) |
| 1150 | return r; | 1069 | return r; |
| 1151 | 1070 | ||
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 6105b25b18c3..acb146c06973 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h | |||
| @@ -1208,6 +1208,10 @@ | |||
| 1208 | 1208 | ||
| 1209 | #define UVD_CONTEXT_ID 0xf6f4 | 1209 | #define UVD_CONTEXT_ID 0xf6f4 |
| 1210 | 1210 | ||
| 1211 | # define UPLL_CTLREQ_MASK 0x00000008 | ||
| 1212 | # define UPLL_CTLACK_MASK 0x40000000 | ||
| 1213 | # define UPLL_CTLACK2_MASK 0x80000000 | ||
| 1214 | |||
| 1211 | /* | 1215 | /* |
| 1212 | * PM4 | 1216 | * PM4 |
| 1213 | */ | 1217 | */ |
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3ef7543a2986..1442ce765d48 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
| @@ -1162,6 +1162,17 @@ void radeon_uvd_free_handles(struct radeon_device *rdev, | |||
| 1162 | struct drm_file *filp); | 1162 | struct drm_file *filp); |
| 1163 | int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); | 1163 | int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); |
| 1164 | void radeon_uvd_note_usage(struct radeon_device *rdev); | 1164 | void radeon_uvd_note_usage(struct radeon_device *rdev); |
| 1165 | int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, | ||
| 1166 | unsigned vclk, unsigned dclk, | ||
| 1167 | unsigned vco_min, unsigned vco_max, | ||
| 1168 | unsigned fb_factor, unsigned fb_mask, | ||
| 1169 | unsigned pd_min, unsigned pd_max, | ||
| 1170 | unsigned pd_even, | ||
| 1171 | unsigned *optimal_fb_div, | ||
| 1172 | unsigned *optimal_vclk_div, | ||
| 1173 | unsigned *optimal_dclk_div); | ||
| 1174 | int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, | ||
| 1175 | unsigned cg_upll_func_cntl); | ||
| 1165 | 1176 | ||
| 1166 | struct r600_audio { | 1177 | struct r600_audio { |
| 1167 | int channels; | 1178 | int channels; |
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 0312a7f4d768..906e5c0ca3b9 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c | |||
| @@ -692,3 +692,140 @@ void radeon_uvd_note_usage(struct radeon_device *rdev) | |||
| 692 | if (set_clocks) | 692 | if (set_clocks) |
| 693 | radeon_set_uvd_clocks(rdev, 53300, 40000); | 693 | radeon_set_uvd_clocks(rdev, 53300, 40000); |
| 694 | } | 694 | } |
| 695 | |||
| 696 | static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq, | ||
| 697 | unsigned target_freq, | ||
| 698 | unsigned pd_min, | ||
| 699 | unsigned pd_even) | ||
| 700 | { | ||
| 701 | unsigned post_div = vco_freq / target_freq; | ||
| 702 | |||
| 703 | /* adjust to post divider minimum value */ | ||
| 704 | if (post_div < pd_min) | ||
| 705 | post_div = pd_min; | ||
| 706 | |||
| 707 | /* we alway need a frequency less than or equal the target */ | ||
| 708 | if ((vco_freq / post_div) > target_freq) | ||
| 709 | post_div += 1; | ||
| 710 | |||
| 711 | /* post dividers above a certain value must be even */ | ||
| 712 | if (post_div > pd_even && post_div % 2) | ||
| 713 | post_div += 1; | ||
| 714 | |||
| 715 | return post_div; | ||
| 716 | } | ||
| 717 | |||
| 718 | /** | ||
| 719 | * radeon_uvd_calc_upll_dividers - calc UPLL clock dividers | ||
| 720 | * | ||
| 721 | * @rdev: radeon_device pointer | ||
| 722 | * @vclk: wanted VCLK | ||
| 723 | * @dclk: wanted DCLK | ||
| 724 | * @vco_min: minimum VCO frequency | ||
| 725 | * @vco_max: maximum VCO frequency | ||
| 726 | * @fb_factor: factor to multiply vco freq with | ||
| 727 | * @fb_mask: limit and bitmask for feedback divider | ||
| 728 | * @pd_min: post divider minimum | ||
| 729 | * @pd_max: post divider maximum | ||
| 730 | * @pd_even: post divider must be even above this value | ||
| 731 | * @optimal_fb_div: resulting feedback divider | ||
| 732 | * @optimal_vclk_div: resulting vclk post divider | ||
| 733 | * @optimal_dclk_div: resulting dclk post divider | ||
| 734 | * | ||
| 735 | * Calculate dividers for UVDs UPLL (R6xx-SI, except APUs). | ||
| 736 | * Returns zero on success -EINVAL on error. | ||
| 737 | */ | ||
| 738 | int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, | ||
| 739 | unsigned vclk, unsigned dclk, | ||
| 740 | unsigned vco_min, unsigned vco_max, | ||
| 741 | unsigned fb_factor, unsigned fb_mask, | ||
| 742 | unsigned pd_min, unsigned pd_max, | ||
| 743 | unsigned pd_even, | ||
| 744 | unsigned *optimal_fb_div, | ||
| 745 | unsigned *optimal_vclk_div, | ||
| 746 | unsigned *optimal_dclk_div) | ||
| 747 | { | ||
| 748 | unsigned vco_freq, ref_freq = rdev->clock.spll.reference_freq; | ||
| 749 | |||
| 750 | /* start off with something large */ | ||
| 751 | unsigned optimal_score = ~0; | ||
| 752 | |||
| 753 | /* loop through vco from low to high */ | ||
| 754 | vco_min = max(max(vco_min, vclk), dclk); | ||
| 755 | for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 100) { | ||
| 756 | |||
| 757 | uint64_t fb_div = (uint64_t)vco_freq * fb_factor; | ||
| 758 | unsigned vclk_div, dclk_div, score; | ||
| 759 | |||
| 760 | do_div(fb_div, ref_freq); | ||
| 761 | |||
| 762 | /* fb div out of range ? */ | ||
| 763 | if (fb_div > fb_mask) | ||
| 764 | break; /* it can oly get worse */ | ||
| 765 | |||
| 766 | fb_div &= fb_mask; | ||
| 767 | |||
| 768 | /* calc vclk divider with current vco freq */ | ||
| 769 | vclk_div = radeon_uvd_calc_upll_post_div(vco_freq, vclk, | ||
| 770 | pd_min, pd_even); | ||
| 771 | if (vclk_div > pd_max) | ||
| 772 | break; /* vco is too big, it has to stop */ | ||
| 773 | |||
| 774 | /* calc dclk divider with current vco freq */ | ||
| 775 | dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk, | ||
| 776 | pd_min, pd_even); | ||
| 777 | if (vclk_div > pd_max) | ||
| 778 | break; /* vco is too big, it has to stop */ | ||
| 779 | |||
| 780 | /* calc score with current vco freq */ | ||
| 781 | score = vclk - (vco_freq / vclk_div) + dclk - (vco_freq / dclk_div); | ||
| 782 | |||
| 783 | /* determine if this vco setting is better than current optimal settings */ | ||
| 784 | if (score < optimal_score) { | ||
| 785 | *optimal_fb_div = fb_div; | ||
| 786 | *optimal_vclk_div = vclk_div; | ||
| 787 | *optimal_dclk_div = dclk_div; | ||
| 788 | optimal_score = score; | ||
| 789 | if (optimal_score == 0) | ||
| 790 | break; /* it can't get better than this */ | ||
| 791 | } | ||
| 792 | } | ||
| 793 | |||
| 794 | /* did we found a valid setup ? */ | ||
| 795 | if (optimal_score == ~0) | ||
| 796 | return -EINVAL; | ||
| 797 | |||
| 798 | return 0; | ||
| 799 | } | ||
| 800 | |||
| 801 | int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, | ||
| 802 | unsigned cg_upll_func_cntl) | ||
| 803 | { | ||
| 804 | unsigned i; | ||
| 805 | |||
| 806 | /* make sure UPLL_CTLREQ is deasserted */ | ||
| 807 | WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); | ||
| 808 | |||
| 809 | mdelay(10); | ||
| 810 | |||
| 811 | /* assert UPLL_CTLREQ */ | ||
| 812 | WREG32_P(cg_upll_func_cntl, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
| 813 | |||
| 814 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
| 815 | for (i = 0; i < 100; ++i) { | ||
| 816 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
| 817 | if ((RREG32(cg_upll_func_cntl) & mask) == mask) | ||
| 818 | break; | ||
| 819 | mdelay(10); | ||
| 820 | } | ||
| 821 | |||
| 822 | /* deassert UPLL_CTLREQ */ | ||
| 823 | WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); | ||
| 824 | |||
| 825 | if (i == 100) { | ||
| 826 | DRM_ERROR("Timeout setting UVD clocks!\n"); | ||
| 827 | return -ETIMEDOUT; | ||
| 828 | } | ||
| 829 | |||
| 830 | return 0; | ||
| 831 | } | ||
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 91530d4c11c4..83f612a9500b 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c | |||
| @@ -44,56 +44,9 @@ void rv770_fini(struct radeon_device *rdev); | |||
| 44 | static void rv770_pcie_gen2_enable(struct radeon_device *rdev); | 44 | static void rv770_pcie_gen2_enable(struct radeon_device *rdev); |
| 45 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); | 45 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); |
| 46 | 46 | ||
| 47 | static int rv770_uvd_calc_post_div(unsigned target_freq, | ||
| 48 | unsigned vco_freq, | ||
| 49 | unsigned *div) | ||
| 50 | { | ||
| 51 | /* Fclk = Fvco / PDIV */ | ||
| 52 | *div = vco_freq / target_freq; | ||
| 53 | |||
| 54 | /* we alway need a frequency less than or equal the target */ | ||
| 55 | if ((vco_freq / *div) > target_freq) | ||
| 56 | *div += 1; | ||
| 57 | |||
| 58 | /* out of range ? */ | ||
| 59 | if (*div > 30) | ||
| 60 | return -1; /* forget it */ | ||
| 61 | |||
| 62 | *div -= 1; | ||
| 63 | return vco_freq / (*div + 1); | ||
| 64 | } | ||
| 65 | |||
| 66 | static int rv770_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
| 67 | { | ||
| 68 | unsigned i; | ||
| 69 | |||
| 70 | /* assert UPLL_CTLREQ */ | ||
| 71 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
| 72 | |||
| 73 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
| 74 | for (i = 0; i < 100; ++i) { | ||
| 75 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
| 76 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
| 77 | break; | ||
| 78 | mdelay(10); | ||
| 79 | } | ||
| 80 | if (i == 100) | ||
| 81 | return -ETIMEDOUT; | ||
| 82 | |||
| 83 | /* deassert UPLL_CTLREQ */ | ||
| 84 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 47 | int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
| 90 | { | 48 | { |
| 91 | /* start off with something large */ | 49 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
| 92 | int optimal_diff_score = 0x7FFFFFF; | ||
| 93 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
| 94 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
| 95 | unsigned vco_freq, vco_min = 50000, vco_max = 160000; | ||
| 96 | unsigned ref_freq = rdev->clock.spll.reference_freq; | ||
| 97 | int r; | 50 | int r; |
| 98 | 51 | ||
| 99 | /* RV740 uses evergreen uvd clk programming */ | 52 | /* RV740 uses evergreen uvd clk programming */ |
| @@ -111,44 +64,15 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 111 | return 0; | 64 | return 0; |
| 112 | } | 65 | } |
| 113 | 66 | ||
| 114 | /* loop through vco from low to high */ | 67 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 50000, 160000, |
| 115 | vco_min = max(max(vco_min, vclk), dclk); | 68 | 43663, 0x03FFFFFE, 1, 30, ~0, |
| 116 | for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 500) { | 69 | &fb_div, &vclk_div, &dclk_div); |
| 117 | uint64_t fb_div = (uint64_t)vco_freq * 43663; | 70 | if (r) |
| 118 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 71 | return r; |
| 119 | unsigned vclk_div, dclk_div; | 72 | |
| 120 | 73 | fb_div |= 1; | |
| 121 | do_div(fb_div, ref_freq); | 74 | vclk_div -= 1; |
| 122 | fb_div |= 1; | 75 | dclk_div -= 1; |
| 123 | |||
| 124 | /* fb div out of range ? */ | ||
| 125 | if (fb_div > 0x03FFFFFF) | ||
| 126 | break; /* it can oly get worse */ | ||
| 127 | |||
| 128 | /* calc vclk with current vco freq. */ | ||
| 129 | calc_clk = rv770_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
| 130 | if (calc_clk == -1) | ||
| 131 | break; /* vco is too big, it has to stop. */ | ||
| 132 | diff_vclk = vclk - calc_clk; | ||
| 133 | |||
| 134 | /* calc dclk with current vco freq. */ | ||
| 135 | calc_clk = rv770_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
| 136 | if (calc_clk == -1) | ||
| 137 | break; /* vco is too big, it has to stop. */ | ||
| 138 | diff_dclk = dclk - calc_clk; | ||
| 139 | |||
| 140 | /* determine if this vco setting is better than current optimal settings */ | ||
| 141 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
| 142 | if (diff_score < optimal_diff_score) { | ||
| 143 | optimal_fb_div = fb_div; | ||
| 144 | optimal_vclk_div = vclk_div; | ||
| 145 | optimal_dclk_div = dclk_div; | ||
| 146 | optimal_vco_freq = vco_freq; | ||
| 147 | optimal_diff_score = diff_score; | ||
| 148 | if (optimal_diff_score == 0) | ||
| 149 | break; /* it can't get better than this */ | ||
| 150 | } | ||
| 151 | } | ||
| 152 | 76 | ||
| 153 | /* set UPLL_FB_DIV to 0x50000 */ | 77 | /* set UPLL_FB_DIV to 0x50000 */ |
| 154 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK); | 78 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK); |
| @@ -160,7 +84,7 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 160 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); | 84 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); |
| 161 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(1), ~UPLL_FB_DIV(1)); | 85 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(1), ~UPLL_FB_DIV(1)); |
| 162 | 86 | ||
| 163 | r = rv770_uvd_send_upll_ctlreq(rdev); | 87 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 164 | if (r) | 88 | if (r) |
| 165 | return r; | 89 | return r; |
| 166 | 90 | ||
| @@ -170,13 +94,13 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 170 | /* set the required FB_DIV, REF_DIV, Post divder values */ | 94 | /* set the required FB_DIV, REF_DIV, Post divder values */ |
| 171 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REF_DIV(1), ~UPLL_REF_DIV_MASK); | 95 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REF_DIV(1), ~UPLL_REF_DIV_MASK); |
| 172 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 96 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
| 173 | UPLL_SW_HILEN(optimal_vclk_div >> 1) | | 97 | UPLL_SW_HILEN(vclk_div >> 1) | |
| 174 | UPLL_SW_LOLEN((optimal_vclk_div >> 1) + (optimal_vclk_div & 1)) | | 98 | UPLL_SW_LOLEN((vclk_div >> 1) + (vclk_div & 1)) | |
| 175 | UPLL_SW_HILEN2(optimal_dclk_div >> 1) | | 99 | UPLL_SW_HILEN2(dclk_div >> 1) | |
| 176 | UPLL_SW_LOLEN2((optimal_dclk_div >> 1) + (optimal_dclk_div & 1)), | 100 | UPLL_SW_LOLEN2((dclk_div >> 1) + (dclk_div & 1)), |
| 177 | ~UPLL_SW_MASK); | 101 | ~UPLL_SW_MASK); |
| 178 | 102 | ||
| 179 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), | 103 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), |
| 180 | ~UPLL_FB_DIV_MASK); | 104 | ~UPLL_FB_DIV_MASK); |
| 181 | 105 | ||
| 182 | /* give the PLL some time to settle */ | 106 | /* give the PLL some time to settle */ |
| @@ -191,7 +115,7 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 191 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 115 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
| 192 | WREG32_P(CG_UPLL_FUNC_CNTL_3, 0, ~UPLL_FB_DIV(1)); | 116 | WREG32_P(CG_UPLL_FUNC_CNTL_3, 0, ~UPLL_FB_DIV(1)); |
| 193 | 117 | ||
| 194 | r = rv770_uvd_send_upll_ctlreq(rdev); | 118 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 195 | if (r) | 119 | if (r) |
| 196 | return r; | 120 | return r; |
| 197 | 121 | ||
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index fe6b14e0021c..f0b6c2f87c4d 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c | |||
| @@ -5415,62 +5415,9 @@ uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev) | |||
| 5415 | return clock; | 5415 | return clock; |
| 5416 | } | 5416 | } |
| 5417 | 5417 | ||
| 5418 | static int si_uvd_calc_post_div(unsigned target_freq, | ||
| 5419 | unsigned vco_freq, | ||
| 5420 | unsigned *div) | ||
| 5421 | { | ||
| 5422 | /* target larger than vco frequency ? */ | ||
| 5423 | if (vco_freq < target_freq) | ||
| 5424 | return -1; /* forget it */ | ||
| 5425 | |||
| 5426 | /* Fclk = Fvco / PDIV */ | ||
| 5427 | *div = vco_freq / target_freq; | ||
| 5428 | |||
| 5429 | /* we alway need a frequency less than or equal the target */ | ||
| 5430 | if ((vco_freq / *div) > target_freq) | ||
| 5431 | *div += 1; | ||
| 5432 | |||
| 5433 | /* dividers above 5 must be even */ | ||
| 5434 | if (*div > 5 && *div % 2) | ||
| 5435 | *div += 1; | ||
| 5436 | |||
| 5437 | /* out of range ? */ | ||
| 5438 | if (*div >= 128) | ||
| 5439 | return -1; /* forget it */ | ||
| 5440 | |||
| 5441 | return vco_freq / *div; | ||
| 5442 | } | ||
| 5443 | |||
| 5444 | static int si_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
| 5445 | { | ||
| 5446 | unsigned i; | ||
| 5447 | |||
| 5448 | /* assert UPLL_CTLREQ */ | ||
| 5449 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
| 5450 | |||
| 5451 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
| 5452 | for (i = 0; i < 100; ++i) { | ||
| 5453 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
| 5454 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
| 5455 | break; | ||
| 5456 | mdelay(10); | ||
| 5457 | } | ||
| 5458 | if (i == 100) | ||
| 5459 | return -ETIMEDOUT; | ||
| 5460 | |||
| 5461 | /* deassert UPLL_CTLREQ */ | ||
| 5462 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
| 5463 | |||
| 5464 | return 0; | ||
| 5465 | } | ||
| 5466 | |||
| 5467 | int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 5418 | int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
| 5468 | { | 5419 | { |
| 5469 | /* start off with something large */ | 5420 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
| 5470 | int optimal_diff_score = 0x7FFFFFF; | ||
| 5471 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
| 5472 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
| 5473 | unsigned vco_freq; | ||
| 5474 | int r; | 5421 | int r; |
| 5475 | 5422 | ||
| 5476 | /* bypass vclk and dclk with bclk */ | 5423 | /* bypass vclk and dclk with bclk */ |
| @@ -5487,40 +5434,11 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 5487 | return 0; | 5434 | return 0; |
| 5488 | } | 5435 | } |
| 5489 | 5436 | ||
| 5490 | /* loop through vco from low to high */ | 5437 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 125000, 250000, |
| 5491 | for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { | 5438 | 16384, 0x03FFFFFF, 0, 128, 5, |
| 5492 | unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; | 5439 | &fb_div, &vclk_div, &dclk_div); |
| 5493 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 5440 | if (r) |
| 5494 | unsigned vclk_div, dclk_div; | 5441 | return r; |
| 5495 | |||
| 5496 | /* fb div out of range ? */ | ||
| 5497 | if (fb_div > 0x03FFFFFF) | ||
| 5498 | break; /* it can oly get worse */ | ||
| 5499 | |||
| 5500 | /* calc vclk with current vco freq. */ | ||
| 5501 | calc_clk = si_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
| 5502 | if (calc_clk == -1) | ||
| 5503 | break; /* vco is too big, it has to stop. */ | ||
| 5504 | diff_vclk = vclk - calc_clk; | ||
| 5505 | |||
| 5506 | /* calc dclk with current vco freq. */ | ||
| 5507 | calc_clk = si_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
| 5508 | if (calc_clk == -1) | ||
| 5509 | break; /* vco is too big, it has to stop. */ | ||
| 5510 | diff_dclk = dclk - calc_clk; | ||
| 5511 | |||
| 5512 | /* determine if this vco setting is better than current optimal settings */ | ||
| 5513 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
| 5514 | if (diff_score < optimal_diff_score) { | ||
| 5515 | optimal_fb_div = fb_div; | ||
| 5516 | optimal_vclk_div = vclk_div; | ||
| 5517 | optimal_dclk_div = dclk_div; | ||
| 5518 | optimal_vco_freq = vco_freq; | ||
| 5519 | optimal_diff_score = diff_score; | ||
| 5520 | if (optimal_diff_score == 0) | ||
| 5521 | break; /* it can't get better than this */ | ||
| 5522 | } | ||
| 5523 | } | ||
| 5524 | 5442 | ||
| 5525 | /* set RESET_ANTI_MUX to 0 */ | 5443 | /* set RESET_ANTI_MUX to 0 */ |
| 5526 | WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK); | 5444 | WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK); |
| @@ -5537,7 +5455,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 5537 | 5455 | ||
| 5538 | mdelay(1); | 5456 | mdelay(1); |
| 5539 | 5457 | ||
| 5540 | r = si_uvd_send_upll_ctlreq(rdev); | 5458 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 5541 | if (r) | 5459 | if (r) |
| 5542 | return r; | 5460 | return r; |
| 5543 | 5461 | ||
| @@ -5548,19 +5466,19 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 5548 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); | 5466 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); |
| 5549 | 5467 | ||
| 5550 | /* set feedback divider */ | 5468 | /* set feedback divider */ |
| 5551 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); | 5469 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), ~UPLL_FB_DIV_MASK); |
| 5552 | 5470 | ||
| 5553 | /* set ref divider to 0 */ | 5471 | /* set ref divider to 0 */ |
| 5554 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); | 5472 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); |
| 5555 | 5473 | ||
| 5556 | if (optimal_vco_freq < 187500) | 5474 | if (fb_div < 307200) |
| 5557 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); | 5475 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); |
| 5558 | else | 5476 | else |
| 5559 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); | 5477 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); |
| 5560 | 5478 | ||
| 5561 | /* set PDIV_A and PDIV_B */ | 5479 | /* set PDIV_A and PDIV_B */ |
| 5562 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 5480 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
| 5563 | UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), | 5481 | UPLL_PDIV_A(vclk_div) | UPLL_PDIV_B(dclk_div), |
| 5564 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); | 5482 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); |
| 5565 | 5483 | ||
| 5566 | /* give the PLL some time to settle */ | 5484 | /* give the PLL some time to settle */ |
| @@ -5574,7 +5492,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
| 5574 | /* switch from bypass mode to normal mode */ | 5492 | /* switch from bypass mode to normal mode */ |
| 5575 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 5493 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
| 5576 | 5494 | ||
| 5577 | r = si_uvd_send_upll_ctlreq(rdev); | 5495 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
| 5578 | if (r) | 5496 | if (r) |
| 5579 | return r; | 5497 | return r; |
| 5580 | 5498 | ||
