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