Line data Source code
1 : /*
2 : * Copyright 2014 Advanced Micro Devices, Inc.
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a
5 : * copy of this software and associated documentation files (the "Software"),
6 : * to deal in the Software without restriction, including without limitation
7 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 : * and/or sell copies of the Software, and to permit persons to whom the
9 : * Software is furnished to do so, subject to the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included in
12 : * all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 : * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 : * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 : * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 : * OTHER DEALINGS IN THE SOFTWARE.
21 : *
22 : */
23 :
24 : #include <drm/amdgpu_drm.h>
25 : #include "amdgpu.h"
26 : #include "atom.h"
27 : #include "atombios_encoders.h"
28 : #include "amdgpu_pll.h"
29 : #include <asm/div64.h>
30 : #include <linux/gcd.h>
31 :
32 : /**
33 : * amdgpu_pll_reduce_ratio - fractional number reduction
34 : *
35 : * @nom: nominator
36 : * @den: denominator
37 : * @nom_min: minimum value for nominator
38 : * @den_min: minimum value for denominator
39 : *
40 : * Find the greatest common divisor and apply it on both nominator and
41 : * denominator, but make nominator and denominator are at least as large
42 : * as their minimum values.
43 : */
44 0 : static void amdgpu_pll_reduce_ratio(unsigned *nom, unsigned *den,
45 : unsigned nom_min, unsigned den_min)
46 : {
47 : unsigned tmp;
48 :
49 : /* reduce the numbers to a simpler ratio */
50 0 : tmp = gcd(*nom, *den);
51 0 : *nom /= tmp;
52 0 : *den /= tmp;
53 :
54 : /* make sure nominator is large enough */
55 0 : if (*nom < nom_min) {
56 0 : tmp = DIV_ROUND_UP(nom_min, *nom);
57 0 : *nom *= tmp;
58 0 : *den *= tmp;
59 : }
60 :
61 : /* make sure the denominator is large enough */
62 0 : if (*den < den_min) {
63 0 : tmp = DIV_ROUND_UP(den_min, *den);
64 0 : *nom *= tmp;
65 0 : *den *= tmp;
66 : }
67 0 : }
68 :
69 : /**
70 : * amdgpu_pll_get_fb_ref_div - feedback and ref divider calculation
71 : *
72 : * @adev: amdgpu_device pointer
73 : * @nom: nominator
74 : * @den: denominator
75 : * @post_div: post divider
76 : * @fb_div_max: feedback divider maximum
77 : * @ref_div_max: reference divider maximum
78 : * @fb_div: resulting feedback divider
79 : * @ref_div: resulting reference divider
80 : *
81 : * Calculate feedback and reference divider for a given post divider. Makes
82 : * sure we stay within the limits.
83 : */
84 0 : static void amdgpu_pll_get_fb_ref_div(struct amdgpu_device *adev, unsigned int nom,
85 : unsigned int den, unsigned int post_div,
86 : unsigned int fb_div_max, unsigned int ref_div_max,
87 : unsigned int *fb_div, unsigned int *ref_div)
88 : {
89 :
90 : /* limit reference * post divider to a maximum */
91 0 : if (adev->family == AMDGPU_FAMILY_SI)
92 0 : ref_div_max = min(100 / post_div, ref_div_max);
93 : else
94 0 : ref_div_max = min(128 / post_div, ref_div_max);
95 :
96 : /* get matching reference and feedback divider */
97 0 : *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max);
98 0 : *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den);
99 :
100 : /* limit fb divider to its maximum */
101 0 : if (*fb_div > fb_div_max) {
102 0 : *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div);
103 0 : *fb_div = fb_div_max;
104 : }
105 0 : }
106 :
107 : /**
108 : * amdgpu_pll_compute - compute PLL paramaters
109 : *
110 : * @adev: amdgpu_device pointer
111 : * @pll: information about the PLL
112 : * @freq: requested frequency
113 : * @dot_clock_p: resulting pixel clock
114 : * @fb_div_p: resulting feedback divider
115 : * @frac_fb_div_p: fractional part of the feedback divider
116 : * @ref_div_p: resulting reference divider
117 : * @post_div_p: resulting reference divider
118 : *
119 : * Try to calculate the PLL parameters to generate the given frequency:
120 : * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
121 : */
122 0 : void amdgpu_pll_compute(struct amdgpu_device *adev,
123 : struct amdgpu_pll *pll,
124 : u32 freq,
125 : u32 *dot_clock_p,
126 : u32 *fb_div_p,
127 : u32 *frac_fb_div_p,
128 : u32 *ref_div_p,
129 : u32 *post_div_p)
130 : {
131 0 : unsigned target_clock = pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV ?
132 0 : freq : freq / 10;
133 :
134 : unsigned fb_div_min, fb_div_max, fb_div;
135 : unsigned post_div_min, post_div_max, post_div;
136 : unsigned ref_div_min, ref_div_max, ref_div;
137 : unsigned post_div_best, diff_best;
138 : unsigned nom, den;
139 :
140 : /* determine allowed feedback divider range */
141 0 : fb_div_min = pll->min_feedback_div;
142 0 : fb_div_max = pll->max_feedback_div;
143 :
144 0 : if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
145 0 : fb_div_min *= 10;
146 0 : fb_div_max *= 10;
147 : }
148 :
149 : /* determine allowed ref divider range */
150 0 : if (pll->flags & AMDGPU_PLL_USE_REF_DIV)
151 0 : ref_div_min = pll->reference_div;
152 : else
153 0 : ref_div_min = pll->min_ref_div;
154 :
155 0 : if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV &&
156 : pll->flags & AMDGPU_PLL_USE_REF_DIV)
157 0 : ref_div_max = pll->reference_div;
158 : else
159 0 : ref_div_max = pll->max_ref_div;
160 :
161 : /* determine allowed post divider range */
162 0 : if (pll->flags & AMDGPU_PLL_USE_POST_DIV) {
163 0 : post_div_min = pll->post_div;
164 0 : post_div_max = pll->post_div;
165 : } else {
166 : unsigned vco_min, vco_max;
167 :
168 0 : if (pll->flags & AMDGPU_PLL_IS_LCD) {
169 0 : vco_min = pll->lcd_pll_out_min;
170 0 : vco_max = pll->lcd_pll_out_max;
171 : } else {
172 0 : vco_min = pll->pll_out_min;
173 0 : vco_max = pll->pll_out_max;
174 : }
175 :
176 0 : if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
177 0 : vco_min *= 10;
178 0 : vco_max *= 10;
179 : }
180 :
181 0 : post_div_min = vco_min / target_clock;
182 0 : if ((target_clock * post_div_min) < vco_min)
183 0 : ++post_div_min;
184 0 : if (post_div_min < pll->min_post_div)
185 0 : post_div_min = pll->min_post_div;
186 :
187 0 : post_div_max = vco_max / target_clock;
188 0 : if ((target_clock * post_div_max) > vco_max)
189 0 : --post_div_max;
190 0 : if (post_div_max > pll->max_post_div)
191 0 : post_div_max = pll->max_post_div;
192 : }
193 :
194 : /* represent the searched ratio as fractional number */
195 0 : nom = target_clock;
196 0 : den = pll->reference_freq;
197 :
198 : /* reduce the numbers to a simpler ratio */
199 0 : amdgpu_pll_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
200 :
201 : /* now search for a post divider */
202 0 : if (pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP)
203 : post_div_best = post_div_min;
204 : else
205 0 : post_div_best = post_div_max;
206 : diff_best = ~0;
207 :
208 0 : for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
209 : unsigned diff;
210 0 : amdgpu_pll_get_fb_ref_div(adev, nom, den, post_div, fb_div_max,
211 : ref_div_max, &fb_div, &ref_div);
212 0 : diff = abs(target_clock - (pll->reference_freq * fb_div) /
213 : (ref_div * post_div));
214 :
215 0 : if (diff < diff_best || (diff == diff_best &&
216 0 : !(pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP))) {
217 :
218 0 : post_div_best = post_div;
219 0 : diff_best = diff;
220 : }
221 : }
222 0 : post_div = post_div_best;
223 :
224 : /* get the feedback and reference divider for the optimal value */
225 0 : amdgpu_pll_get_fb_ref_div(adev, nom, den, post_div, fb_div_max, ref_div_max,
226 : &fb_div, &ref_div);
227 :
228 : /* reduce the numbers to a simpler ratio once more */
229 : /* this also makes sure that the reference divider is large enough */
230 0 : amdgpu_pll_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
231 :
232 : /* avoid high jitter with small fractional dividers */
233 0 : if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) {
234 0 : fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 60);
235 0 : if (fb_div < fb_div_min) {
236 0 : unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div);
237 0 : fb_div *= tmp;
238 0 : ref_div *= tmp;
239 : }
240 : }
241 :
242 : /* and finally save the result */
243 0 : if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
244 0 : *fb_div_p = fb_div / 10;
245 0 : *frac_fb_div_p = fb_div % 10;
246 : } else {
247 0 : *fb_div_p = fb_div;
248 0 : *frac_fb_div_p = 0;
249 : }
250 :
251 0 : *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
252 0 : (pll->reference_freq * *frac_fb_div_p)) /
253 0 : (ref_div * post_div * 10);
254 0 : *ref_div_p = ref_div;
255 0 : *post_div_p = post_div;
256 :
257 0 : DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
258 : freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p,
259 : ref_div, post_div);
260 0 : }
261 :
262 : /**
263 : * amdgpu_pll_get_use_mask - look up a mask of which pplls are in use
264 : *
265 : * @crtc: drm crtc
266 : *
267 : * Returns the mask of which PPLLs (Pixel PLLs) are in use.
268 : */
269 0 : u32 amdgpu_pll_get_use_mask(struct drm_crtc *crtc)
270 : {
271 0 : struct drm_device *dev = crtc->dev;
272 : struct drm_crtc *test_crtc;
273 : struct amdgpu_crtc *test_amdgpu_crtc;
274 0 : u32 pll_in_use = 0;
275 :
276 0 : list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
277 0 : if (crtc == test_crtc)
278 0 : continue;
279 :
280 0 : test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
281 0 : if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
282 0 : pll_in_use |= (1 << test_amdgpu_crtc->pll_id);
283 : }
284 0 : return pll_in_use;
285 : }
286 :
287 : /**
288 : * amdgpu_pll_get_shared_dp_ppll - return the PPLL used by another crtc for DP
289 : *
290 : * @crtc: drm crtc
291 : *
292 : * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
293 : * also in DP mode. For DP, a single PPLL can be used for all DP
294 : * crtcs/encoders.
295 : */
296 0 : int amdgpu_pll_get_shared_dp_ppll(struct drm_crtc *crtc)
297 : {
298 0 : struct drm_device *dev = crtc->dev;
299 : struct drm_crtc *test_crtc;
300 : struct amdgpu_crtc *test_amdgpu_crtc;
301 :
302 0 : list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
303 0 : if (crtc == test_crtc)
304 0 : continue;
305 0 : test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
306 0 : if (test_amdgpu_crtc->encoder &&
307 0 : ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
308 : /* for DP use the same PLL for all */
309 0 : if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
310 0 : return test_amdgpu_crtc->pll_id;
311 : }
312 : }
313 : return ATOM_PPLL_INVALID;
314 : }
315 :
316 : /**
317 : * amdgpu_pll_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc
318 : *
319 : * @crtc: drm crtc
320 : *
321 : * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can
322 : * be shared (i.e., same clock).
323 : */
324 0 : int amdgpu_pll_get_shared_nondp_ppll(struct drm_crtc *crtc)
325 : {
326 0 : struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
327 0 : struct drm_device *dev = crtc->dev;
328 : struct drm_crtc *test_crtc;
329 : struct amdgpu_crtc *test_amdgpu_crtc;
330 : u32 adjusted_clock, test_adjusted_clock;
331 :
332 0 : adjusted_clock = amdgpu_crtc->adjusted_clock;
333 :
334 0 : if (adjusted_clock == 0)
335 : return ATOM_PPLL_INVALID;
336 :
337 0 : list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
338 0 : if (crtc == test_crtc)
339 0 : continue;
340 0 : test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
341 0 : if (test_amdgpu_crtc->encoder &&
342 0 : !ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
343 : /* check if we are already driving this connector with another crtc */
344 0 : if (test_amdgpu_crtc->connector == amdgpu_crtc->connector) {
345 : /* if we are, return that pll */
346 0 : if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
347 0 : return test_amdgpu_crtc->pll_id;
348 : }
349 : /* for non-DP check the clock */
350 0 : test_adjusted_clock = test_amdgpu_crtc->adjusted_clock;
351 0 : if ((crtc->mode.clock == test_crtc->mode.clock) &&
352 0 : (adjusted_clock == test_adjusted_clock) &&
353 0 : (amdgpu_crtc->ss_enabled == test_amdgpu_crtc->ss_enabled) &&
354 0 : (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID))
355 0 : return test_amdgpu_crtc->pll_id;
356 : }
357 : }
358 : return ATOM_PPLL_INVALID;
359 : }
|