blob: fcc08ad30f121353e68f211723ed57e3cd29f993 [file] [log] [blame]
Arthur Heymans73ea0322018-03-28 17:17:07 +02001--
2-- Copyright (C) 2015-2016 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.Time;
16with HW.GFX.GMA.Config;
17with HW.GFX.GMA.Registers;
18
19with HW.Debug;
20with GNAT.Source_Info;
21
22package body HW.GFX.GMA.PLLs
23with
24 Refined_State => (State => PLLs)
25is
26
27 Debug_Clocks : constant Boolean := False;
28
29 type Count_Range is new Natural range 0 .. 2;
30
31 type PLL_State is record
32 Use_Count : Count_Range;
33 Used_For_DP : Boolean;
34 Link_Rate : DP_Bandwidth;
35 Mode : Mode_Type;
36 end record;
37
38 type PLL_State_Array is array (DPLLs) of PLL_State;
39
40 PLLs : PLL_State_Array;
41
42 ----------------------------------------------------------------------------
43
44 subtype N_Range is Int64 range 3 .. 8;
45 subtype M_Range is Int64 range 70 .. 138;
46 subtype M1_Range is Int64 range 10 .. 25;
47 subtype M2_Range is Int64 range 4 .. 13;
48 subtype P_Range is Int64 range 5 .. 112;
49 subtype P1_Range is Int64 range 1 .. 8;
50 subtype P2_Range is Int64 range 5 .. 14;
51 subtype VCO_Range is Int64 range 1400000000 .. 3500000000;
52 subtype Clock_Range is HW.GFX.Frequency_Type;
53
54 type Clock_Type is
55 record
56 N : N_Range;
57 M1 : M1_Range;
58 M2 : M2_Range;
59 P1 : P1_Range;
60 P2 : P2_Range;
61 M : M_Range;
62 P : P_Range;
63 VCO : VCO_Range;
64 Reference_Clock : Clock_Range;
65 Dotclock : Clock_Range;
66 end record;
67
68 Invalid_Clock : constant Clock_Type := Clock_Type'
69 (N => N_Range'Last,
70 M1 => M1_Range'Last,
71 M2 => M2_Range'Last,
72 P1 => P1_Range'Last,
73 P2 => P2_Range'Last,
74 Reference_Clock => Clock_Range'Last,
75 M => M_Range'Last,
76 P => P_Range'Last,
77 VCO => VCO_Range'Last,
78 Dotclock => Clock_Range'Last);
79
80 type Limits_Type is
81 record
82 N_Lower : N_Range;
83 N_Upper : N_Range;
84 M_Lower : M_Range;
85 M_Upper : M_Range;
86 M1_Lower : M1_Range;
87 M1_Upper : M1_Range;
88 M2_Lower : M2_Range;
89 M2_Upper : M2_Range;
90 P_Lower : P_Range;
91 P_Upper : P_Range;
92 P1_Lower : P1_Range;
93 P1_Upper : P1_Range;
94 P2_Fast : P2_Range;
95 P2_Slow : P2_Range;
96 P2_Threshold : Clock_Range;
97 VCO_Lower : VCO_Range;
98 VCO_Upper : VCO_Range;
99 end record;
100
101 LVDS_Single_Limits : constant Limits_Type := Limits_Type'
102 (N_Lower => 3, N_Upper => 5,
103 M_Lower => 104, M_Upper => 138,
104 M1_Lower => 19, M1_Upper => 25, -- this is capped by M_Upper >= 5 * M1 + M2_Lower
105 M2_Lower => 7, M2_Upper => 13,
106 P_Lower => 28, P_Upper => 112,
107 P1_Lower => 2, P1_Upper => 8,
108 P2_Fast => 14, P2_Slow => 14,
109 P2_Threshold => Clock_Range'First,
110 VCO_Lower => 1_750_000_000, VCO_Upper => 3_500_000_000);
111 LVDS_Dual_Limits : constant Limits_Type := Limits_Type'
112 (N_Lower => 3, N_Upper => 5,
113 M_Lower => 104, M_Upper => 138,
114 M1_Lower => 19, M1_Upper => 25,
115 M2_Lower => 7, M2_Upper => 13,
116 P_Lower => 14, P_Upper => 56,
117 P1_Lower => 2, P1_Upper => 6,
118 P2_Fast => 7, P2_Slow => 7,
119 P2_Threshold => Clock_Range'First,
120 VCO_Lower => 1_750_000_000, VCO_Upper => 3_500_000_000);
121 SDVO_Limits : constant Limits_Type := Limits_Type'
122 (N_Lower => 3, N_Upper => 6,
123 M_Lower => 104, M_Upper => 138,
124 M1_Lower => 16, M1_Upper => 25,
125 M2_Lower => 7, M2_Upper => 13,
126 P_Lower => 10, P_Upper => 30,
127 P1_Lower => 1, P1_Upper => 3,
128 -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
129 P2_Fast => 5, P2_Slow => 10,
130 P2_Threshold => 270_000_000,
131 VCO_Lower => 1_750_000_000, VCO_Upper => 3_500_000_000);
132 HDMI_Analog_Limits : constant Limits_Type := Limits_Type'
133 (N_Lower => 3, N_Upper => 6,
134 M_Lower => 104, M_Upper => 138,
135 M1_Lower => 18, M1_Upper => 25,
136 M2_Lower => 7, M2_Upper => 13,
137 P_Lower => 5, P_Upper => 80,
138 P1_Lower => 1, P1_Upper => 8,
139 -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
140 P2_Fast => 5, P2_Slow => 10,
141 P2_Threshold => 165_000_000,
142 VCO_Lower => 1_750_000_000, VCO_Upper => 3_500_000_000);
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100143 -- I9XX limits are used by GM965 (Crestline) and older non-G4x chips.
144 I9XX_LVDS_Limits : constant Limits_Type := Limits_Type'
145 (N_Lower => 3, N_Upper => 8,
146 M_Lower => 70, M_Upper => 120,
147 M1_Lower => 10, M1_Upper => 20,
148 M2_Lower => 5, M2_Upper => 9,
149 P_Lower => 7, P_Upper => 98,
150 P1_Lower => 1, P1_Upper => 8,
151 P2_Fast => 7, P2_Slow => 14,
152 P2_Threshold => 112_000_000,
153 VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
154 -- The All_Other/I9XX_SDVO limits match Linux' intel_limits_i9xx_sdvo.
Arthur Heymans73ea0322018-03-28 17:17:07 +0200155 All_Other_Limits : constant Limits_Type := Limits_Type'
156 (N_Lower => 3, N_Upper => 8,
157 M_Lower => 70, M_Upper => 120,
158 M1_Lower => 10, M1_Upper => 20,
159 M2_Lower => 5, M2_Upper => 9,
160 P_Lower => 5, P_Upper => 80,
161 P1_Lower => 1, P1_Upper => 8,
162 -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
163 P2_Fast => 5, P2_Slow => 10,
164 P2_Threshold => 200_000_000,
165 VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
166
167 ----------------------------------------------------------------------------
168
169 type Regs is array (DPLLs) of Registers.Registers_Index;
170
171 DPLL : constant Regs := Regs'(Registers.GMCH_DPLL_A, Registers.GMCH_DPLL_B);
172 DPLL_VCO_ENABLE : constant := 1 * 2 ** 31;
173 DPLL_VGA_MODE_DIS : constant := 1 * 2 ** 28;
174 DPLL_P2_10_OR_14 : constant := 0 * 2 ** 24;
175 DPLL_P2_5_OR_7 : constant := 1 * 2 ** 24;
176 DPLL_P1_DIVIDER_SHIFT : constant := 16;
177 DPLL_SDVOCLK : constant := 2 * 2 ** 13;
178 DPLL_PULSE_PHASE_MASK : constant := 15 * 2 ** 9;
179 DPLL_PULSE_PHASE_6 : constant := 6 * 2 ** 9;
180
181 DPLL_HIGH_SPEED : constant := 1 * 2 ** 30;
182 DPLL_MODE_LVDS : constant := 2 * 2 ** 26;
183 DPLL_MODE_DAC : constant := 1 * 2 ** 26;
184 DPLL_DREFCLK : constant := 0 * 2 ** 13;
185 DPLL_SSC : constant := 3 * 2 ** 13;
186
187 MODE_DPLL_DAC : constant Word32 := Word32'
188 (DPLL_MODE_DAC or DPLL_DREFCLK);
189
190 MODE_DPLL_HDMI : constant Word32 := Word32'
191 (DPLL_MODE_DAC or DPLL_DREFCLK or DPLL_HIGH_SPEED);
192
193 MODE_DPLL_LVDS : constant Word32 := Word32'
194 (DPLL_MODE_LVDS or DPLL_SSC);
195
196 MODE_DPLL_DP : constant Word32 := Word32'
197 (DPLL_MODE_DAC or DPLL_SSC or DPLL_HIGH_SPEED);
198
199 type DPLL_Mode_Array is array (Display_Type) of Word32;
200
201 DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
202 (LVDS => MODE_DPLL_LVDS,
203 DP => MODE_DPLL_DP,
204 HDMI => MODE_DPLL_HDMI,
205 VGA => MODE_DPLL_DAC,
206 Others => MODE_DPLL_HDMI); --TODO Add SDVO
207
208 FP0 : constant Regs := Regs'(Registers.GMCH_FPA0, Registers.GMCH_FPB0);
209 FP1 : constant Regs := Regs'(Registers.GMCH_FPA1, Registers.GMCH_FPB1);
210 FP_DOUBLE_CLOCK : constant := 1 * 2 ** 27;
211 FP_N_SHIFT : constant := 16;
212 FP_M1_SHIFT : constant := 8;
213 FP_M2_SHIFT : constant := 0;
214
215 ----------------------------------------------------------------------------
216
217 procedure Verify_Parameters
218 (N : in N_Range;
219 M1 : in M1_Range;
220 M2 : in M2_Range;
221 P1 : in P1_Range;
222 P2 : in P2_Range;
223 Reference_Clock : in Clock_Range;
224 Current_Limits : in Limits_Type;
225 Result : out Clock_Type;
226 Valid : out Boolean)
227 with
228 Global => null,
229 Pre => True,
230 Post => True
231 is
232 M : Int64;
233 P : Int64;
234 VCO : Int64;
235 Dotclock : Int64;
236 begin
237 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
238
239 M := 5 * M1 + M2;
240 P := P1 * P2;
241 VCO := (Int64 (Reference_Clock) * M) / N;
242 Dotclock := VCO / P;
243
244 pragma Debug (Debug_Clocks and not (Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
245 pragma Debug (Debug_Clocks and (Current_Limits.P2_Fast /= P2 and P2 /= Current_Limits.P2_Slow ), Debug.Put_Line ("P2 out of range."));
246 pragma Debug (Debug_Clocks and not (Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper ), Debug.Put_Line ("P out of range."));
247 pragma Debug (Debug_Clocks and not (Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
248 pragma Debug (Debug_Clocks and not (Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
249 -- pragma Debug (Debug_Clocks and not (M2 <= M1 ), Debug.Put_Line ("M1 greater thant M2."));
250 pragma Debug (Debug_Clocks and not (Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper ), Debug.Put_Line ("N out of range."));
251 pragma Debug (Debug_Clocks and not (Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper ), Debug.Put_Line ("M out of range."));
252 pragma Debug (Debug_Clocks and not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
253
254 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Line ("Dotclock too low."));
255 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Int64 (Dotclock));
256 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.New_Line);
257
258 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Line ("Dotclock too high."));
259 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Int64 (Dotclock));
260 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.New_Line);
261
262 Valid :=
263 Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper and
264 (Current_Limits.P2_Fast = P2 or P2 = Current_Limits.P2_Slow) and
265 Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper and
266 Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper and
267 Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper and
268 Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper and
269 Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper and
270 Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper and
271 Int64 (Clock_Range'First) <= Dotclock and
272 Dotclock <= Int64 (Clock_Range'Last);
273
274 if Valid
275 then
276 Result := Clock_Type'
277 (N => N,
278 M1 => M1,
279 M2 => M2,
280 P1 => P1,
281 P2 => P2,
282 Reference_Clock => Reference_Clock,
283 M => M,
284 P => P,
285 VCO => VCO,
286 Dotclock => Clock_Range (Dotclock));
287 else
288 Result := Invalid_Clock;
289 end if;
290
291 end Verify_Parameters;
292
293 procedure Calculate_Clock_Parameters
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100294 (Target_Dotclock : in Clock_Range;
Arthur Heymans73ea0322018-03-28 17:17:07 +0200295 Reference_Clock : in Clock_Range;
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100296 Limits : in Limits_Type;
Arthur Heymans73ea0322018-03-28 17:17:07 +0200297 Best_Clock : out Clock_Type;
298 Valid : out Boolean)
299 with
300 Global => null,
301 Pre => True,
302 Post => True
303 is
Arthur Heymans73ea0322018-03-28 17:17:07 +0200304 P2 : P2_Range;
305 Best_Delta : Int64 := Int64'Last;
306 Current_Delta : Int64;
307 Current_Clock : Clock_Type;
308 Registers_Valid : Boolean;
309 begin
310 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
311
312 Valid := False;
313 Best_Clock := Invalid_Clock;
314
315 if Target_Dotclock <= Limits.P2_Threshold then
316 P2 := Limits.P2_Slow;
317 else
318 P2 := Limits.P2_Fast;
319 end if;
320
321 for N in N_Range range Limits.N_Lower .. Limits.N_Upper
322 loop
323 -- reverse loops as hardware prefers higher values
324 for M1 in reverse M1_Range range Limits.M1_Lower .. Limits.M1_Upper
325 loop
Nico Huber3652e9c2022-09-04 14:29:30 +0000326 pragma Loop_Invariant (True);
Arthur Heymans73ea0322018-03-28 17:17:07 +0200327 for M2 in reverse M2_Range range Limits.M2_Lower .. Int64'Min (Limits.M2_Upper, M1)
328 loop
Nico Huber3652e9c2022-09-04 14:29:30 +0000329 pragma Loop_Invariant (True);
Arthur Heymans73ea0322018-03-28 17:17:07 +0200330 for P1 in reverse P1_Range range Limits.P1_Lower .. Limits.P1_Upper
331 loop
332 Verify_Parameters
333 (N => N,
334 M1 => M1,
335 M2 => M2,
336 P1 => P1,
337 P2 => P2,
338 Reference_Clock => Reference_Clock,
339 Current_Limits => Limits,
340 Result => Current_Clock,
341 Valid => Registers_Valid);
342
343 if Registers_Valid
344 then
345 if Current_Clock.Dotclock > Target_Dotclock
346 then
347 Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
348 else
349 Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
350 end if;
351
352 if Current_Delta < Best_Delta
353 then
354 Best_Delta := Current_Delta;
355 Best_Clock := Current_Clock;
356 Valid := True;
357 end if;
358
359 pragma Debug (Debug_Clocks, Debug.Put ("Current/Target/Best_Delta: "));
360 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Current_Clock.Dotclock));
361 pragma Debug (Debug_Clocks, Debug.Put ("/"));
362 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Target_Dotclock));
363 pragma Debug (Debug_Clocks, Debug.Put ("/"));
364 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Best_Delta));
365 pragma Debug (Debug_Clocks, Debug.Put_Line ("."));
366
367 end if;
368 end loop;
369 end loop;
370 end loop;
371 end loop;
372
373 pragma Debug (Valid, Debug.Put_Line ("Valid clock found."));
374 pragma Debug (Valid, Debug.Put ("Best/Target/Delta: "));
375 pragma Debug (Valid, Debug.Put_Int64 (Best_Clock.Dotclock));
376 pragma Debug (Valid, Debug.Put ("/"));
377 pragma Debug (Valid, Debug.Put_Int64 (Target_Dotclock));
378 pragma Debug (Valid, Debug.Put ("/"));
379 pragma Debug (Valid, Debug.Put_Int64 (Best_Delta));
380 pragma Debug (Valid, Debug.Put_Line ("."));
381 pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
382
383 end Calculate_Clock_Parameters;
384
385 procedure Program_DPLL
386 (PLL : DPLLs;
387 Display : Display_Type;
388 Clk : Clock_Type)
389 with
390 Global => (In_Out => Registers.Register_State),
391 Pre => True,
392 Post => True
393 is
394 FP, Encoded_P1, Encoded_P2 : Word32;
395 begin
396 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
397
398 FP :=
399 Shift_Left (Word32 (Clk.N - 2), FP_N_SHIFT) or
400 Shift_Left (Word32 (Clk.M1 - 2), FP_M1_SHIFT) or
401 Shift_Left (Word32 (Clk.M2 - 2), FP_M2_SHIFT);
402
403 Registers.Write (FP0 (PLL), FP);
404 Registers.Write (FP1 (PLL), FP);
405
406 Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
407
408 if Clk.P2 = 5 or Clk.P2 = 7
409 then
410 Encoded_P2 := DPLL_P2_5_OR_7;
411 else
412 Encoded_P2 := DPLL_P2_10_OR_14;
413 end if;
414
415 Registers.Write
416 (Register => DPLL (PLL),
417 Value => DPLL_Mode (Display) or
418 DPLL_VGA_MODE_DIS or
419 DPLL_PULSE_PHASE_6 or
420 Encoded_P2 or
421 Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT));
422 end Program_DPLL;
423
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100424 function Select_Limits
425 (Display : Display_Type;
426 Target_Dotclock : Clock_Range)
427 return Limits_Type
428 is
429 begin
430 if Config.GMCH_GM965 then
431 -- i9xx PLL limits for GM965 (Crestline)
432 case Display is
433 when LVDS => return I9XX_LVDS_Limits;
434 when others => return All_Other_Limits;
435 end case;
436 else
437 -- G4x PLL limits for G45/GM45
438 case Display is
439 when LVDS =>
440 if Target_Dotclock >= Config.LVDS_Dual_Threshold then
441 return LVDS_Dual_Limits;
442 else
443 return LVDS_Single_Limits;
444 end if;
445 when HDMI => return HDMI_Analog_Limits;
446 when VGA => return HDMI_Analog_Limits;
447 when others => return All_Other_Limits;
448 end case;
449 end if;
450 end Select_Limits;
451
Arthur Heymans73ea0322018-03-28 17:17:07 +0200452 procedure On
453 (PLL : in T;
454 Port_Cfg : in Port_Config;
455 Success : out Boolean)
456 is
457 Target_Clock : constant Frequency_Type :=
458 (if Port_Cfg.Display = DP then
459 DP_Symbol_Rate (Port_Cfg.DP.Bandwidth)
460 else
461 Port_Cfg.Mode.Dotclock);
462 Clk : Clock_Type;
463 begin
464 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
465
466 Success := PLL in DPLLs;
467 Clk := Invalid_Clock;
468
469 if Success then
470 if Port_Cfg.Display = DP then
471 Success := True;
472 -- we use static values for DP
473 case Port_Cfg.DP.Bandwidth is
474 when DP_Bandwidth_1_62 =>
475 Clk.N := 4;
476 Clk.M1 := 25;
477 Clk.M2 := 10;
478 Clk.P1 := 2;
479 Clk.P2 := 10;
480 when DP_Bandwidth_2_7 =>
481 Clk.N := 3;
482 Clk.M1 := 16;
483 Clk.M2 := 4;
484 Clk.P1 := 1;
485 Clk.P2 := 10;
486 when others =>
487 Success := False;
488 end case;
489 elsif Target_Clock <= 340_000_000 then
490 Calculate_Clock_Parameters
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100491 (Target_Dotclock => Target_Clock,
Arthur Heymans73ea0322018-03-28 17:17:07 +0200492 -- should be, but doesn't has to be always the same:
493 Reference_Clock => 96_000_000,
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100494 Limits => Select_Limits (Port_Cfg.Display, Target_Clock),
Arthur Heymans73ea0322018-03-28 17:17:07 +0200495 Best_Clock => Clk,
496 Valid => Success);
497 else
498 Success := False;
499 pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
500 pragma Debug (Debug.Put_Int64 (Target_Clock));
501 pragma Debug (Debug.Put (" > "));
502 pragma Debug (Debug.Put_Int32 (340_000_000));
503 pragma Debug (Debug.New_Line);
504 pragma Debug (Debug.New_Line);
505 end if;
506 end if;
507
508 if Success then
509 Program_DPLL (PLL, Port_Cfg.Display, Clk);
510
511 Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
512 Registers.Posting_Read (DPLL (PLL));
513 Time.U_Delay (150);
514 end if;
515 end On;
516
517 procedure Off (PLL : T)
518 is
519 begin
520 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
521
522 if PLL in DPLLs then
523 Registers.Unset_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
524 end if;
525 end Off;
526
527 ----------------------------------------------------------------------------
528
529 procedure Initialize
530 is
531 begin
532 PLLs :=
533 (DPLLs =>
534 (Use_Count => 0,
535 Used_For_DP => False,
536 Link_Rate => DP_Bandwidth'First,
537 Mode => Invalid_Mode));
538 end Initialize;
539
540 procedure Alloc_Configurable
541 (Port_Cfg : in Port_Config;
542 PLL : out T;
543 Success : out Boolean)
544 with
545 Pre => True
546 is
547 function Config_Matches (PE : PLL_State) return Boolean
548 is
549 begin
550 return
551 PE.Used_For_DP = (Port_Cfg.Display = DP) and
552 ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
553 (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
554 end Config_Matches;
555 begin
556 -- try to find shareable PLL
557 for P in DPLLs loop
558 Success := PLLs (P).Use_Count /= 0 and
559 PLLs (P).Use_Count /= Count_Range'Last and
560 Config_Matches (PLLs (P));
561 if Success then
562 PLL := P;
563 PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
564 return;
565 end if;
566 end loop;
567
568 -- try to find free PLL
569 for P in DPLLs loop
570 if PLLs (P).Use_Count = 0 then
571 PLL := P;
572 On (PLL, Port_Cfg, Success);
573 if Success then
574 PLLs (PLL) :=
575 (Use_Count => 1,
576 Used_For_DP => Port_Cfg.Display = DP,
577 Link_Rate => Port_Cfg.DP.Bandwidth,
578 Mode => Port_Cfg.Mode);
579 end if;
580 return;
581 end if;
582 end loop;
583
584 PLL := Invalid;
585 end Alloc_Configurable;
586
587 procedure Alloc
588 (Port_Cfg : in Port_Config;
589 PLL : out T;
590 Success : out Boolean)
591 is
592 begin
593 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
594
595 if Port_Cfg.Port = DIGI_A then
596 PLL := Invalid;
597 Success := True;
598 else
599 Alloc_Configurable (Port_Cfg, PLL, Success);
600 end if;
601 end Alloc;
602
603 procedure Free (PLL : T)
604 is
605 begin
606 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
607
608 if PLL in DPLLs then
609 if PLLs (PLL).Use_Count /= 0 then
610 PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
611 if PLLs (PLL).Use_Count = 0 then
612 Off (PLL);
613 end if;
614 end if;
615 end if;
616 end Free;
617
618 procedure All_Off
619 is
620 begin
621 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
622
623 for PLL in DPLLs loop
624 Off (PLL);
625 end loop;
626 end All_Off;
627
628 function Register_Value (PLL : T) return Word32
629 is
630 begin
631 return (if PLL = DPLL_B then 1 else 0);
632 end Register_Value;
633
634end HW.GFX.GMA.PLLs;