diff options
author | Christian König <christian.koenig@amd.com> | 2013-04-29 05:55:02 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2013-05-02 10:09:48 -0400 |
commit | facd112d1395fb6a0b6e460778aefc32197afcfc (patch) | |
tree | 69cc696021ccfe1eb1d78a7aedfb32f984675118 /drivers/gpu/drm/radeon/rv770.c | |
parent | 092fbc4ca29a3d78895673479f794ee162a13ac5 (diff) |
drm/radeon: consolidate UVD clock programming
Instead of duplicating the code over and over again, just use a single
function to handle the clock calculations.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/rv770.c')
-rw-r--r-- | drivers/gpu/drm/radeon/rv770.c | 110 |
1 files changed, 17 insertions, 93 deletions
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 | ||