blob: c9ab53270a554b15c24258d6bdadffa624bb57b0 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2015 secunet Security Networks AG
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
6-- the Free Software Foundation; version 2 of the License.
7--
8-- This program is distributed in the hope that it will be useful,
9-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11-- GNU General Public License for more details.
12--
13
14with HW.Time;
15with HW.GFX.GMA.Registers;
16
17package body HW.GFX.GMA.PLLs.WRPLL is
18
19 ----------------------------------------------------------------------------
20 --
21 -- Divider calculation as found in Linux' i915 driver
22 --
23 -- Copyright (C) 2012 Intel Corporation
24 --
25 -- Permission is hereby granted, free of charge, to any person obtaining a
26 -- copy of this software and associated documentation files (the "Software"),
27 -- to deal in the Software without restriction, including without limitation
28 -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 -- and/or sell copies of the Software, and to permit persons to whom the
30 -- Software is furnished to do so, subject to the following conditions:
31 --
32 -- The above copyright notice and this permission notice (including the next
33 -- paragraph) shall be included in all copies or substantial portions of the
34 -- Software.
35 --
36 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
39 -- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
41 -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
42 -- IN THE SOFTWARE.
43 --
44 -- Authors:
45 -- Eugeni Dodonov <eugeni.dodonov@intel.com>
46 --
47
48 LC_FREQ : constant := 2700; -- in MHz
49 LC_FREQ_2K : constant := LC_FREQ * 2000; -- in 500Hz
50
51 P_MIN : constant := 2;
52 P_MAX : constant := 62; -- i915 says 64, but this would overflow 6-bit
53 P_INC : constant := 2;
54
55 -- Constraints for PLL good behavior
56 REF_MIN : constant := 48;
57 REF_MAX : constant := 400;
58 VCO_MIN : constant := 2400;
59 VCO_MAX : constant := 4800;
60
61 type R2_Range is new Natural range 0 .. LC_FREQ * 2 / REF_MIN;
62 type N2_Range is new Natural range 0 .. VCO_MAX * Natural (R2_Range'Last) / LC_FREQ;
63 type P_Range is new Natural range 0 .. P_MAX;
64
65 type RNP is record
66 P : P_Range;
67 N2 : N2_Range;
68 R2 : R2_Range;
69 end record;
70 Invalid_RNP : constant RNP := RNP'(0, 0, 0);
71
72 function Get_Budget_For_Freq
73 (Clock : HW.GFX.Frequency_Type)
74 return Word64
75 is
76 Result : Word64;
77 begin
78 case Clock is
79 when 25175000 |
80 25200000 |
81 27000000 |
82 27027000 |
83 37762500 |
84 37800000 |
85 40500000 |
86 40541000 |
87 54000000 |
88 54054000 |
89 59341000 |
90 59400000 |
91 72000000 |
92 74176000 |
93 74250000 |
94 81000000 |
95 81081000 |
96 89012000 |
97 89100000 |
98 108000000 |
99 108108000 |
100 111264000 |
101 111375000 |
102 148352000 |
103 148500000 |
104 162000000 |
105 162162000 |
106 222525000 |
107 222750000 |
108 296703000 |
109 297000000 =>
110 Result := 0;
111 when 233500000 |
112 245250000 |
113 247750000 |
114 253250000 |
115 298000000 =>
116 Result := 1500;
117 when 169128000 |
118 169500000 |
119 179500000 |
120 202000000 =>
121 Result := 2000;
122 when 256250000 |
123 262500000 |
124 270000000 |
125 272500000 |
126 273750000 |
127 280750000 |
128 281250000 |
129 286000000 |
130 291750000 =>
131 Result := 4000;
132 when 267250000 |
133 268500000 =>
134 Result := 5000;
135 when others =>
136 Result := 1000;
137 end case;
138 return Result;
139 end Get_Budget_For_Freq;
140
141 procedure Update_RNP
142 (Freq_2K : in Word64;
143 Budget : in Word64;
144 R2 : in R2_Range;
145 N2 : in N2_Range;
146 P : in P_Range;
147 Best : in out RNP)
148 with
149 Depends => (Best =>+ (Freq_2K, Budget, R2, N2, P))
150 is
151 use type HW.Word64;
152
153 function Abs_Diff (A, B : Word64) return Word64
154 is
155 Result : Word64;
156 begin
157 if A > B then
158 Result := A - B;
159 else
160 Result := B - A;
161 end if;
162 return Result;
163 end Abs_Diff;
164
165 A, B, C, D, Diff, Diff_Best : Word64;
166 begin
167 -- No best (r,n,p) yet */
168 if Best.P = 0 then
169 Best.P := P;
170 Best.N2 := N2;
171 Best.R2 := R2;
172 else
173 -- Config clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
174 -- freq2k.
175 --
176 -- delta = 1e6 *
177 -- abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
178 -- freq2k;
179 --
180 -- and we would like delta <= budget.
181 --
182 -- If the discrepancy is above the PPM-based budget, always prefer to
183 -- improve upon the previous solution. However, if you're within the
184 -- budget, try to maximize Ref * VCO, that is N / (P * R^2).
185 A := Freq_2K * Budget * Word64 (P) * Word64 (R2);
186 B := Freq_2K * Budget * Word64 (Best.P) * Word64 (Best.R2);
187 Diff := Abs_Diff
188 (Freq_2K * Word64 (P) * Word64 (R2),
189 LC_FREQ_2K * Word64 (N2));
190 Diff_Best := Abs_Diff
191 (Freq_2K * Word64 (Best.P) * Word64 (Best.R2),
192 LC_FREQ_2K * Word64 (Best.N2));
193 C := 1000000 * Diff;
194 D := 1000000 * Diff_Best;
195
196 if A < C and B < D then
197 -- If both are above the Budget, pick the closer
198 if Word64 (Best.P) * Word64 (Best.R2) * Diff
199 < Word64 (P) * Word64 (R2) * Diff_Best
200 then
201 Best.P := P;
202 Best.N2 := N2;
203 Best.R2 := R2;
204 end if;
205 elsif A >= C and B < D then
206 -- If A is below the threshold but B is above it? Update.
207 Best.P := P;
208 Best.N2 := N2;
209 Best.R2 := R2;
210 elsif A >= C and B >= D then
211 -- Both are below the limit, so pick the higher N2/(R2*R2)
212 if Word64 (N2) * Word64 (Best.R2) * Word64 (Best.R2)
213 > Word64 (Best.N2) * Word64 (R2) * Word64 (R2)
214 then
215 Best.P := P;
216 Best.N2 := N2;
217 Best.R2 := R2;
218 end if;
219 end if;
220 -- Otherwise A < C && B >= D, do nothing
221 end if;
222 end Update_RNP;
223
224 procedure Calculate_WRPLL
225 (Clock : in HW.GFX.Frequency_Type;
226 R2_Out : out R2_Range;
227 N2_Out : out N2_Range;
228 P_Out : out P_Range)
229 with
230 Global => null,
231 Pre => True,
232 Post => True
233 is
234 use type HW.Word64;
235
236 Freq_2K : Word64;
237 Budget : Word64;
238 Best : RNP := Invalid_RNP;
239 begin
240 Freq_2K := Word64 (Clock) / 100; -- PLL output should be 5x
241 -- the pixel clock
242 Budget := Get_Budget_For_Freq (Clock);
243
244 -- Special case handling for 540MHz pixel clock: bypass WR PLL entirely
245 -- and directly pass the LC PLL to it. */
246 if Freq_2K = 5400000 then
247 N2_Out := 2;
248 P_Out := 1;
249 R2_Out := 2;
250 else
251 -- Ref = LC_FREQ / R, where Ref is the actual reference input seen by
252 -- the WR PLL.
253 --
254 -- We want R so that REF_MIN <= Ref <= REF_MAX.
255 -- Injecting R2 = 2 * R gives:
256 -- REF_MAX * r2 > LC_FREQ * 2 and
257 -- REF_MIN * r2 < LC_FREQ * 2
258 --
259 -- Which means the desired boundaries for r2 are:
260 -- LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
261 --
262 for R2 in R2_Range range
263 LC_FREQ * 2 / REF_MAX + 1 .. LC_FREQ * 2 / REF_MIN
264 loop
265 -- VCO = N * Ref, that is: VCO = N * LC_FREQ / R
266 --
267 -- Once again we want VCO_MIN <= VCO <= VCO_MAX.
268 -- Injecting R2 = 2 * R and N2 = 2 * N, we get:
269 -- VCO_MAX * r2 > n2 * LC_FREQ and
270 -- VCO_MIN * r2 < n2 * LC_FREQ)
271 --
272 -- Which means the desired boundaries for n2 are:
273 -- VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
274 for N2 in N2_Range range
275 N2_Range (VCO_MIN * Natural (R2) / LC_FREQ + 1)
276 .. N2_Range (VCO_MAX * Natural (R2) / LC_FREQ)
277 loop
278 for P_Fract in Natural range P_MIN / P_INC .. P_MAX / P_INC
279 loop
280 Update_RNP
281 (Freq_2K, Budget, R2, N2, P_Range (P_Fract * P_INC), Best);
282 end loop;
283 end loop;
284 end loop;
285
286 N2_Out := Best.N2;
287 P_Out := Best.P;
288 R2_Out := Best.R2;
289 end if;
290
291 end Calculate_WRPLL;
292
293 --
294 ----------------------------------------------------------------------------
295
296 type Regs is array (WRPLLs) of Registers.Registers_Index;
297
298 WRPLL_CTL : constant Regs := Regs'(Registers.WRPLL_CTL_1, Registers.WRPLL_CTL_2);
299 WRPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
300 WRPLL_CTL_SELECT_LCPLL : constant := 3 * 2 ** 28;
301
302 function WRPLL_CTL_DIVIDER_FEEDBACK (N2 : N2_Range) return Word32
303 is
304 begin
305 return Word32 (N2) * 2 ** 16;
306 end WRPLL_CTL_DIVIDER_FEEDBACK;
307
308 function WRPLL_CTL_DIVIDER_POST (P : P_Range) return Word32
309 is
310 begin
311 return Word32 (P) * 2 ** 8;
312 end WRPLL_CTL_DIVIDER_POST;
313
314 function WRPLL_CTL_DIVIDER_REFERENCE (R2 : R2_Range) return Word32
315 is
316 begin
317 return Word32 (R2) * 2 ** 0;
318 end WRPLL_CTL_DIVIDER_REFERENCE;
319
320 ----------------------------------------------------------------------------
321
322 procedure On
323 (PLL : in WRPLLs;
324 Target_Clock : in Frequency_Type;
325 Success : out Boolean)
326 is
327 R2 : R2_Range;
328 N2 : N2_Range;
329 P : P_Range;
330 begin
331 Calculate_WRPLL (Target_Clock, R2, N2, P);
332 Registers.Write
333 (Register => WRPLL_CTL (PLL),
334 Value => WRPLL_CTL_PLL_ENABLE or
335 WRPLL_CTL_SELECT_LCPLL or
336 WRPLL_CTL_DIVIDER_FEEDBACK (N2) or
337 WRPLL_CTL_DIVIDER_POST (P) or
338 WRPLL_CTL_DIVIDER_REFERENCE (R2));
339 Registers.Posting_Read (WRPLL_CTL (PLL));
340 Time.U_Delay (20);
341
342 Success := True;
343 end On;
344
345 procedure Off (PLL : WRPLLs)
346 is
347 begin
348 Registers.Unset_Mask (WRPLL_CTL (PLL), WRPLL_CTL_PLL_ENABLE);
349 end Off;
350
351end HW.GFX.GMA.PLLs.WRPLL;