blob: 8c87507519bd987758def8a71fcc1b7e3363dabc [file] [log] [blame]
Nico Huber21da5742017-01-20 14:00:53 +01001--
2-- Copyright (C) 2017 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; either version 2 of the License, or
7-- (at your option) any later version.
8--
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.Debug;
16with GNAT.Source_Info;
17
Nico Huber4b0239f2017-02-07 18:26:51 +010018with HW.GFX.GMA.Config;
19with HW.GFX.GMA.Registers;
20
21use HW.GFX.GMA.Registers;
22
Nico Huber21da5742017-01-20 14:00:53 +010023package body HW.GFX.GMA.PLLs
24with
25 Refined_State => (State => null)
26is
27
Nico Huber4b0239f2017-02-07 18:26:51 +010028 -- DPLL clock equation:
29 -- 5 * Target_Dotclock = Ref_Clk * M1 * (M2 / 2^22) / N / (P1 * P2)
30 --
31 -- Where
32 -- M1 = 2,
33 -- Ref_Clk = 100MHz,
34 -- VCO = Ref_Clk * 2 * (M2 / 2^22),
35 -- N = 1 and
36 -- P = P1 * P2.
37
38 Ref_Clk : constant := 100_000_000;
39 M1 : constant := 2;
40 N : constant := 1;
41
42 -- i915 has a fixme for the M2 range. But the VCO range is very
43 -- limited, giving us a narrow range for M2: 24 .. 33.5
44 subtype VCO_Range is Pos64 range 4_800_000_000 .. 6_700_000_000;
45 subtype M2_Range is Pos64 range
46 N * VCO_Range'First * 2 ** 22 / Ref_Clk / M1 ..
47 N * VCO_Range'Last * 2 ** 22 / Ref_Clk / M1;
48
49 subtype N_Range is Pos64 range 1 .. 1;
50
51 subtype P1_Range is Pos64 range 2 .. 4;
52 subtype P2_Range is Pos64 range 1 .. 20;
53
54 subtype Clock_Range is Frequency_Type range
55 Frequency_Type'First .. 540_000_000;
56 subtype HDMI_Clock_Range is Clock_Range range
57 25_000_000 .. Config.HDMI_Max_Clock_24bpp;
58 subtype Clock_Gap is Clock_Range range 223_333_333 + 1 .. 240_000_000 - 1;
59
60 type Clock_Type is record
61 M2 : M2_Range;
62 P1 : P1_Range;
63 P2 : P2_Range;
64 VCO : VCO_Range;
65 Dotclock : Clock_Range;
66 end record;
67
68 Invalid_Clock : constant Clock_Type :=
69 (M2 => M2_Range'Last,
70 P1 => P1_Range'Last,
71 P2 => P2_Range'Last,
72 VCO => VCO_Range'Last,
73 Dotclock => Clock_Range'Last);
74
75 procedure Calculate_Clock_Parameters
76 (Target_Dotclock : in HDMI_Clock_Range;
77 Best_Clock : out Clock_Type;
78 Valid : out Boolean)
79 with
80 Pre => True
81 is
82 Target_Clock : constant Pos64 := 5 * Target_Dotclock;
83
84 M2, VCO, Current_Clock : Pos64;
85 P2 : P2_Range;
86
87 Valid_Clk : Boolean;
88 begin
89 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
90
91 Valid := False;
92 Best_Clock := Invalid_Clock;
93
94 -- reverse loops as hardware prefers higher values
95 for P1 in reverse P1_Range loop
96 -- find the highest P2 that results in valid clock
97 P2 := P2_Range'Last;
98 loop
99 M2 := Div_Round_Closest
100 (Target_Clock * P2 * P1 * N * 2 ** 22, Ref_Clk * M1);
101 VCO := Div_Round_Closest (Ref_Clk * M1 * M2, 2 ** 22 * N);
102 Current_Clock := Div_Round_Closest (VCO, P1 * P2);
103
104 Valid_Clk := M2 in M2_Range and then
105 Div_Round_Closest (Current_Clock, 5) in Clock_Range;
106 if Valid_Clk then
107 -- the error is always below 2^-22, higher P takes precedence
108 if not Valid or P1 * P2 > Best_Clock.P1 * Best_Clock.P2 then
109 Best_Clock := Clock_Type'
110 (M2 => M2,
111 P1 => P1,
112 P2 => P2,
113 VCO => VCO,
114 Dotclock => Div_Round_Closest (Current_Clock, 5));
115 Valid := True;
116 end if;
117 end if;
118
119 -- Prefer higher P2 over marginal lower error. This is
120 -- just an optimization, since lower P2 values would get
121 -- filtered above anyway.
122 exit when Valid_Clk;
123
124 -- If M2 got too low, it won't get any better. Another
125 -- optimization.
126 exit when M2 < M2_Range'First;
127
128 exit when P2 = P2_Range'First;
129
130 if P2 > 10 then
131 P2 := P2 - 2;
132 else
133 P2 := P2 - 1;
134 end if;
135 end loop;
136 end loop;
137
138 pragma Debug (Valid, Debug.Put_Line ("Valid clock found."));
139 pragma Debug (Valid, Debug.Put ("M2 / P1 / P2: "));
140 pragma Debug (Valid, Debug.Put_Word32 (Word32 (Best_Clock.M2)));
141 pragma Debug (Valid, Debug.Put (" / "));
142 pragma Debug (Valid, Debug.Put_Int8 (Pos8 (Best_Clock.P1)));
143 pragma Debug (Valid, Debug.Put (" / "));
144 pragma Debug (Valid, Debug.Put_Int8 (Pos8 (Best_Clock.P2)));
145 pragma Debug (Valid, Debug.New_Line);
146 pragma Debug (Valid, Debug.Put ("Best / Target: "));
147 pragma Debug (Valid, Debug.Put_Int64 (Best_Clock.Dotclock));
148 pragma Debug (Valid, Debug.Put (" / "));
149 pragma Debug (Valid, Debug.Put_Int64 (Target_Dotclock));
150 pragma Debug (Valid, Debug.New_Line);
151 pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
152 end Calculate_Clock_Parameters;
153
154 ----------------------------------------------------------------------------
155
156 subtype Valid_PLLs is T range DPLL_A .. DPLL_C;
157
158 type Port_PLL_Regs is record
159 PLL_ENABLE : Registers_Index;
160 PLL_EBB_0 : Registers_Index;
161 PLL_EBB_4 : Registers_Index;
162 PLL_0 : Registers_Index;
163 PLL_1 : Registers_Index;
164 PLL_2 : Registers_Index;
165 PLL_3 : Registers_Index;
166 PLL_6 : Registers_Index;
167 PLL_8 : Registers_Index;
168 PLL_9 : Registers_Index;
169 PLL_10 : Registers_Index;
170 PCS_DW12_LN01 : Registers_Index;
171 PCS_DW12_GRP : Registers_Index;
172 end record;
173 type Port_PLL_Array is array (Valid_PLLs) of Port_PLL_Regs;
174
175 PORT : constant Port_PLL_Array :=
176 (DPLL_A =>
177 (PLL_ENABLE => BXT_PORT_PLL_ENABLE_A,
178 PLL_EBB_0 => BXT_PORT_PLL_EBB_0_A,
179 PLL_EBB_4 => BXT_PORT_PLL_EBB_4_A,
180 PLL_0 => BXT_PORT_PLL_0_A,
181 PLL_1 => BXT_PORT_PLL_1_A,
182 PLL_2 => BXT_PORT_PLL_2_A,
183 PLL_3 => BXT_PORT_PLL_3_A,
184 PLL_6 => BXT_PORT_PLL_6_A,
185 PLL_8 => BXT_PORT_PLL_8_A,
186 PLL_9 => BXT_PORT_PLL_9_A,
187 PLL_10 => BXT_PORT_PLL_10_A,
188 PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_A,
189 PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_A),
190 DPLL_B =>
191 (PLL_ENABLE => BXT_PORT_PLL_ENABLE_B,
192 PLL_EBB_0 => BXT_PORT_PLL_EBB_0_B,
193 PLL_EBB_4 => BXT_PORT_PLL_EBB_4_B,
194 PLL_0 => BXT_PORT_PLL_0_B,
195 PLL_1 => BXT_PORT_PLL_1_B,
196 PLL_2 => BXT_PORT_PLL_2_B,
197 PLL_3 => BXT_PORT_PLL_3_B,
198 PLL_6 => BXT_PORT_PLL_6_B,
199 PLL_8 => BXT_PORT_PLL_8_B,
200 PLL_9 => BXT_PORT_PLL_9_B,
201 PLL_10 => BXT_PORT_PLL_10_B,
202 PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_B,
203 PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_B),
204 DPLL_C =>
205 (PLL_ENABLE => BXT_PORT_PLL_ENABLE_C,
206 PLL_EBB_0 => BXT_PORT_PLL_EBB_0_C,
207 PLL_EBB_4 => BXT_PORT_PLL_EBB_4_C,
208 PLL_0 => BXT_PORT_PLL_0_C,
209 PLL_1 => BXT_PORT_PLL_1_C,
210 PLL_2 => BXT_PORT_PLL_2_C,
211 PLL_3 => BXT_PORT_PLL_3_C,
212 PLL_6 => BXT_PORT_PLL_6_C,
213 PLL_8 => BXT_PORT_PLL_8_C,
214 PLL_9 => BXT_PORT_PLL_9_C,
215 PLL_10 => BXT_PORT_PLL_10_C,
216 PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_C,
217 PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_C));
218
219 PORT_PLL_ENABLE : constant := 1 * 2 ** 31;
220 PORT_PLL_ENABLE_LOCK : constant := 1 * 2 ** 30;
221 PORT_PLL_ENABLE_REF_SEL : constant := 1 * 2 ** 27;
222
223 PORT_PLL_EBB0_P1_SHIFT : constant := 13;
224 PORT_PLL_EBB0_P1_MASK : constant := 16#07# * 2 ** 13;
225 PORT_PLL_EBB0_P2_SHIFT : constant := 8;
226 PORT_PLL_EBB0_P2_MASK : constant := 16#1f# * 2 ** 8;
227 function PORT_PLL_EBB0_P1 (P1 : P1_Range) return Word32 is
228 begin
229 return Shift_Left (Word32 (P1), PORT_PLL_EBB0_P1_SHIFT);
230 end PORT_PLL_EBB0_P1;
231 function PORT_PLL_EBB0_P2 (P2 : P2_Range) return Word32 is
232 begin
233 return Shift_Left (Word32 (P2), PORT_PLL_EBB0_P2_SHIFT);
234 end PORT_PLL_EBB0_P2;
235
236 PORT_PLL_EBB4_RECALIBRATE : constant := 1 * 2 ** 14;
237 PORT_PLL_EBB4_10BIT_CLK_ENABLE : constant := 1 * 2 ** 13;
238
239 PORT_PLL_0_M2_INT_MASK : constant := 16#ff# * 2 ** 0;
240 function PORT_PLL_0_M2_INT (M2 : M2_Range) return Word32 is
241 begin
242 return Shift_Right (Word32 (M2), 22);
243 end PORT_PLL_0_M2_INT;
244
245 PORT_PLL_1_N_SHIFT : constant := 8;
246 PORT_PLL_1_N_MASK : constant := 16#0f# * 2 ** 8;
247 function PORT_PLL_1_N (N : N_Range) return Word32 is
248 begin
249 return Shift_Left (Word32 (N), PORT_PLL_1_N_SHIFT);
250 end PORT_PLL_1_N;
251
252 PORT_PLL_2_M2_FRAC_MASK : constant := 16#3f_ffff#;
253 function PORT_PLL_2_M2_FRAC (M2 : M2_Range) return Word32 is
254 begin
255 return Word32 (M2) and PORT_PLL_2_M2_FRAC_MASK;
256 end PORT_PLL_2_M2_FRAC;
257
258 PORT_PLL_3_M2_FRAC_EN_MASK : constant := 1 * 2 ** 16;
259 function PORT_PLL_3_M2_FRAC_EN (M2 : M2_Range) return Word32 is
260 begin
261 return
262 (if (Word32 (M2) and PORT_PLL_2_M2_FRAC_MASK) /= 0 then
263 PORT_PLL_3_M2_FRAC_EN_MASK else 0);
264 end PORT_PLL_3_M2_FRAC_EN;
265
266 PORT_PLL_6_GAIN_CTL_SHIFT : constant := 16;
267 PORT_PLL_6_GAIN_CTL_MASK : constant := 16#07# * 2 ** 16;
268 PORT_PLL_6_INT_COEFF_SHIFT : constant := 8;
269 PORT_PLL_6_INT_COEFF_MASK : constant := 16#1f# * 2 ** 8;
270 PORT_PLL_6_PROP_COEFF_MASK : constant := 16#0f# * 2 ** 0;
271 function PORT_PLL_6_GAIN_COEFF (VCO : VCO_Range) return Word32 is
272 begin
273 return
274 (if VCO >= 6_200_000_000 then
275 Shift_Left (Word32'(3), PORT_PLL_6_GAIN_CTL_SHIFT) or
276 Shift_Left (Word32'(9), PORT_PLL_6_INT_COEFF_SHIFT) or
277 Word32'(4)
278 elsif VCO /= 5_400_000_000 then
279 Shift_Left (Word32'(3), PORT_PLL_6_GAIN_CTL_SHIFT) or
280 Shift_Left (Word32'(11), PORT_PLL_6_INT_COEFF_SHIFT) or
281 Word32'(5)
282 else
283 Shift_Left (Word32'(1), PORT_PLL_6_GAIN_CTL_SHIFT) or
284 Shift_Left (Word32'(8), PORT_PLL_6_INT_COEFF_SHIFT) or
285 Word32'(3));
286 end PORT_PLL_6_GAIN_COEFF;
287
288 PORT_PLL_8_TARGET_CNT_MASK : constant := 16#3ff#;
289 function PORT_PLL_8_TARGET_CNT (VCO : VCO_Range) return Word32 is
290 begin
291 return (if VCO >= 6_200_000_000 then 8 else 9);
292 end PORT_PLL_8_TARGET_CNT;
293
294 PORT_PLL_9_LOCK_THRESHOLD_SHIFT : constant := 1;
295 PORT_PLL_9_LOCK_THRESHOLD_MASK : constant := 16#07# * 2 ** 1;
296 function PORT_PLL_9_LOCK_THRESHOLD (Threshold : Natural) return Word32 is
297 begin
298 return
299 Shift_Left (Word32 (Threshold), PORT_PLL_9_LOCK_THRESHOLD_SHIFT) and
300 PORT_PLL_9_LOCK_THRESHOLD_MASK;
301 end PORT_PLL_9_LOCK_THRESHOLD;
302
303 PORT_PLL_10_DCO_AMP_OVR_EN_H : constant := 2 ** 27;
304 PORT_PLL_10_DCO_AMP_SHIFT : constant := 10;
305 PORT_PLL_10_DCO_AMP_MASK : constant := 16#0f# * 2 ** 10;
306 function PORT_PLL_10_DCO_AMP (Amp : Natural) return Word32 is
307 begin
308 return
309 Shift_Left (Word32 (Amp), PORT_PLL_10_DCO_AMP_SHIFT) and
310 PORT_PLL_10_DCO_AMP_MASK;
311 end PORT_PLL_10_DCO_AMP;
312
313 PORT_PCS_LANE_STAGGER_STRAP_OVRD : constant := 2 ** 6;
314 PORT_PCS_LANE_STAGGER_MASK : constant := 16#1f# * 2 ** 0;
315 function PORT_PCS_LANE_STAGGER (Dotclock : Clock_Range) return Word32 is
316 begin
317 return Word32'(PORT_PCS_LANE_STAGGER_STRAP_OVRD) or
318 (if Dotclock > 270_000_000 then
319 16#18#
320 elsif Dotclock > 135_000_000 then
321 16#0d#
322 elsif Dotclock > 67_000_000 then
323 16#07#
324 elsif Dotclock > 33_000_000 then
325 16#04#
326 else
327 16#02#);
328 end PORT_PCS_LANE_STAGGER;
329
330 ----------------------------------------------------------------------------
331
332 procedure Program_DPLL (P : T; Clock : Clock_Type)
333 is
334 PCS : Word32;
335 begin
336 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
337
338 Set_Mask (PORT (P).PLL_ENABLE, PORT_PLL_ENABLE_REF_SEL); -- non-SSC ref
339 Unset_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_10BIT_CLK_ENABLE);
340
341 Unset_And_Set_Mask
342 (Register => PORT (P).PLL_EBB_0,
343 Mask_Unset => PORT_PLL_EBB0_P1_MASK or
344 PORT_PLL_EBB0_P2_MASK,
345 Mask_Set => PORT_PLL_EBB0_P1 (Clock.P1) or
346 PORT_PLL_EBB0_P2 (Clock.P2));
347 Unset_And_Set_Mask
348 (Register => PORT (P).PLL_0,
349 Mask_Unset => PORT_PLL_0_M2_INT_MASK,
350 Mask_Set => PORT_PLL_0_M2_INT (Clock.M2));
351 Unset_And_Set_Mask
352 (Register => PORT (P).PLL_1,
353 Mask_Unset => PORT_PLL_1_N_MASK,
354 Mask_Set => PORT_PLL_1_N (N));
355 Unset_And_Set_Mask
356 (Register => PORT (P).PLL_2,
357 Mask_Unset => PORT_PLL_2_M2_FRAC_MASK,
358 Mask_Set => PORT_PLL_2_M2_FRAC (Clock.M2));
359 Unset_And_Set_Mask
360 (Register => PORT (P).PLL_3,
361 Mask_Unset => PORT_PLL_3_M2_FRAC_EN_MASK,
362 Mask_Set => PORT_PLL_3_M2_FRAC_EN (Clock.M2));
363 Unset_And_Set_Mask
364 (Register => PORT (P).PLL_6,
365 Mask_Unset => PORT_PLL_6_GAIN_CTL_MASK or
366 PORT_PLL_6_INT_COEFF_MASK or
367 PORT_PLL_6_PROP_COEFF_MASK,
368 Mask_Set => PORT_PLL_6_GAIN_COEFF (Clock.VCO));
369 Unset_And_Set_Mask
370 (Register => PORT (P).PLL_8,
371 Mask_Unset => PORT_PLL_8_TARGET_CNT_MASK,
372 Mask_Set => PORT_PLL_8_TARGET_CNT (Clock.VCO));
373 Unset_And_Set_Mask
374 (Register => PORT (P).PLL_9,
375 Mask_Unset => PORT_PLL_9_LOCK_THRESHOLD_MASK,
376 Mask_Set => PORT_PLL_9_LOCK_THRESHOLD (5));
377 Unset_And_Set_Mask
378 (Register => PORT (P).PLL_10,
379 Mask_Unset => PORT_PLL_10_DCO_AMP_MASK,
380 Mask_Set => PORT_PLL_10_DCO_AMP_OVR_EN_H or
381 PORT_PLL_10_DCO_AMP (15));
382
383 Set_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_RECALIBRATE);
384 Set_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_10BIT_CLK_ENABLE);
385
386 Set_Mask (PORT (P).PLL_ENABLE, PORT_PLL_ENABLE);
387 Wait_Set_Mask
388 (Register => PORT (P).PLL_ENABLE,
389 Mask => PORT_PLL_ENABLE_LOCK,
390 TOut_MS => 1); -- 100us
391
392 Read (PORT (P).PCS_DW12_LN01, PCS);
393 PCS := PCS and not PORT_PCS_LANE_STAGGER_MASK;
394 PCS := PCS or PORT_PCS_LANE_STAGGER (Clock.Dotclock);
395 Write (PORT (P).PCS_DW12_GRP, PCS);
396 end Program_DPLL;
397
398 ----------------------------------------------------------------------------
399
Nico Huber21da5742017-01-20 14:00:53 +0100400 procedure Alloc
401 (Port_Cfg : in Port_Config;
402 PLL : out T;
403 Success : out Boolean)
404 is
Nico Huber4b0239f2017-02-07 18:26:51 +0100405 Clock : Clock_Type := Invalid_Clock;
Nico Huber21da5742017-01-20 14:00:53 +0100406 begin
407 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
408
409 case Port_Cfg.Port is
410 when DIGI_A => PLL := DPLL_A;
411 when DIGI_B => PLL := DPLL_B;
412 when DIGI_C => PLL := DPLL_C;
413 when others => PLL := Invalid_PLL;
414 end case;
415
416 Success := PLL /= Invalid_PLL;
Nico Huber4b0239f2017-02-07 18:26:51 +0100417
418 if Success then
419 case Port_Cfg.Display is
420 when DP =>
421 Success := True;
422 -- we use static values for DP
423 case Port_Cfg.DP.Bandwidth is
424 when DP_Bandwidth_1_62 =>
425 Clock.M2 := 32 * 2 ** 22 + 1677722;
426 Clock.P1 := 4;
427 Clock.P2 := 2;
428 Clock.VCO := 6_480_000_019;
429 Clock.Dotclock := 162_000_000;
430 when DP_Bandwidth_2_7 =>
431 Clock.M2 := 27 * 2 ** 22;
432 Clock.P1 := 4;
433 Clock.P2 := 1;
434 Clock.VCO := 5_400_000_000;
435 Clock.Dotclock := 270_000_000;
436 when DP_Bandwidth_5_4 =>
437 Clock.M2 := 27 * 2 ** 22;
438 Clock.P1 := 2;
439 Clock.P2 := 1;
440 Clock.VCO := 5_400_000_000;
441 Clock.Dotclock := 540_000_000;
442 end case;
443 when HDMI =>
444 if Port_Cfg.Mode.Dotclock in HDMI_Clock_Range and
445 (Port_Cfg.Mode.Dotclock * 99 / 100 < Clock_Gap'First or
446 Port_Cfg.Mode.Dotclock * 101 / 100 > Clock_Gap'Last)
447 then
448 Calculate_Clock_Parameters
449 (Target_Dotclock => Port_Cfg.Mode.Dotclock,
450 Best_Clock => Clock,
451 Valid => Success);
452 else
453 Success := False;
454 pragma Debug (Debug.Put_Line
455 ("Mode's dotclock is out of range."));
456 end if;
457 when others =>
458 Success := False;
459 pragma Debug (Debug.Put_Line ("Invalid display type!"));
460 end case;
461 end if;
462
463 if Success then
464 Program_DPLL (PLL, Clock);
465 end if;
Nico Huber21da5742017-01-20 14:00:53 +0100466 end Alloc;
467
468 procedure Free (PLL : T) is
469 begin
Nico Huber4b0239f2017-02-07 18:26:51 +0100470 if PLL in Valid_PLLs then
471 Unset_Mask (PORT (PLL).PLL_ENABLE, PORT_PLL_ENABLE);
472 end if;
Nico Huber21da5742017-01-20 14:00:53 +0100473 end Free;
474
475 procedure All_Off is
476 begin
477 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
478
Nico Huber4b0239f2017-02-07 18:26:51 +0100479 for PLL in Valid_PLLs loop
480 Free (PLL);
481 end loop;
Nico Huber21da5742017-01-20 14:00:53 +0100482 end All_Off;
483
Nico Huber21da5742017-01-20 14:00:53 +0100484end HW.GFX.GMA.PLLs;