blob: fde7f63de271ac16487a17e78a5aa26db5f191d9 [file] [log] [blame]
Arthur Heymans960e2392026-03-03 19:45:24 +01001--
2-- Copyright (C) 2026 Arthur Heymans <arthur@aheymans.xyz>
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 Mode : Mode_Type;
34 end record;
35
36 type PLL_State_Array is array (DPLLs) of PLL_State;
37
38 PLLs : PLL_State_Array;
39
40 ----------------------------------------------------------------------------
41
42 -- i945 PLL limits use "actual" divider values (register_value + 2).
43 -- Linux kernel i9xx limits (register values):
44 -- N: 1..6, M1: 8..18, M2: 3..7
45 -- Actual values (register + 2):
46 -- N: 3..8, M1: 10..20, M2: 5..9
47 -- M = 5 * M1 + M2, range 70..120
48 -- VCO = ref_clk * M / N, range 1400..2800 MHz
49 -- Reference clock: 96 MHz
50
51 subtype N_Range is Int64 range 3 .. 8;
52 subtype M_Range is Int64 range 70 .. 120;
53 subtype M1_Range is Int64 range 10 .. 20;
54 subtype M2_Range is Int64 range 5 .. 9;
55 subtype P_Range is Int64 range 5 .. 98;
56 subtype P1_Range is Int64 range 1 .. 8;
57 subtype P2_Range is Int64 range 5 .. 14;
58 subtype VCO_Range is Int64 range 1400000000 .. 2800000000;
59 subtype Clock_Range is HW.GFX.Frequency_Type;
60
61 type Clock_Type is
62 record
63 N : N_Range;
64 M1 : M1_Range;
65 M2 : M2_Range;
66 P1 : P1_Range;
67 P2 : P2_Range;
68 M : M_Range;
69 P : P_Range;
70 VCO : VCO_Range;
71 Reference_Clock : Clock_Range;
72 Dotclock : Clock_Range;
73 end record;
74
75 Invalid_Clock : constant Clock_Type := Clock_Type'
76 (N => N_Range'Last,
77 M1 => M1_Range'Last,
78 M2 => M2_Range'Last,
79 P1 => P1_Range'Last,
80 P2 => P2_Range'Last,
81 Reference_Clock => Clock_Range'Last,
82 M => M_Range'Last,
83 P => P_Range'Last,
84 VCO => VCO_Range'Last,
85 Dotclock => Clock_Range'Last);
86
87 type Limits_Type is
88 record
89 N_Lower : N_Range;
90 N_Upper : N_Range;
91 M_Lower : M_Range;
92 M_Upper : M_Range;
93 M1_Lower : M1_Range;
94 M1_Upper : M1_Range;
95 M2_Lower : M2_Range;
96 M2_Upper : M2_Range;
97 P_Lower : P_Range;
98 P_Upper : P_Range;
99 P1_Lower : P1_Range;
100 P1_Upper : P1_Range;
101 P2_Fast : P2_Range;
102 P2_Slow : P2_Range;
103 P2_Threshold : Clock_Range;
104 VCO_Lower : VCO_Range;
105 VCO_Upper : VCO_Range;
106 end record;
107
108 -- i9xx LVDS limits (from Linux intel_limits_i9xx_lvds)
109 LVDS_Limits : constant Limits_Type := Limits_Type'
110 (N_Lower => 3, N_Upper => 8,
111 M_Lower => 70, M_Upper => 120,
112 M1_Lower => 10, M1_Upper => 20,
113 M2_Lower => 5, M2_Upper => 9,
114 P_Lower => 7, P_Upper => 98,
115 P1_Lower => 1, P1_Upper => 8,
116 P2_Fast => 7, P2_Slow => 14,
117 P2_Threshold => 112_000_000,
118 VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
119
120 -- i9xx SDVO/DAC limits (from Linux intel_limits_i9xx_sdvo)
121 SDVO_DAC_Limits : constant Limits_Type := Limits_Type'
122 (N_Lower => 3, N_Upper => 8,
123 M_Lower => 70, M_Upper => 120,
124 M1_Lower => 10, M1_Upper => 20,
125 M2_Lower => 5, M2_Upper => 9,
126 P_Lower => 5, P_Upper => 80,
127 P1_Lower => 1, P1_Upper => 8,
128 -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
129 P2_Fast => 5, P2_Slow => 10,
130 P2_Threshold => 200_000_000,
131 VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
132
Arthur Heymans6dc78432026-06-18 09:34:50 +0200133 -- Pineview has a different Gen3 DPLL encoding. Its N divider is encoded
134 -- as a one-hot value in the frequency-parameter (FP) register, M1 is
135 -- reserved as 0, M = M2 + 2, and P1 starts at bit 15.
136
137 subtype PNV_N_Range is Int64 range 3 .. 6;
138 subtype PNV_M_Range is Int64 range 2 .. 256;
139 subtype PNV_M2_Range is Int64 range 0 .. 254;
140 subtype PNV_P_Range is Int64 range 5 .. 112;
141 subtype PNV_P1_Range is Int64 range 1 .. 8;
142 subtype PNV_P2_Range is Int64 range 5 .. 14;
143 subtype PNV_VCO_Range is Int64 range 1700000000 .. 3500000000;
144
145 type PNV_Clock_Type is
146 record
147 N : PNV_N_Range;
148 M2 : PNV_M2_Range;
149 P1 : PNV_P1_Range;
150 P2 : PNV_P2_Range;
151 M : PNV_M_Range;
152 P : PNV_P_Range;
153 VCO : PNV_VCO_Range;
154 Reference_Clock : Clock_Range;
155 Dotclock : Clock_Range;
156 end record;
157
158 Invalid_PNV_Clock : constant PNV_Clock_Type := PNV_Clock_Type'
159 (N => PNV_N_Range'Last,
160 M2 => PNV_M2_Range'Last,
161 P1 => PNV_P1_Range'Last,
162 P2 => PNV_P2_Range'Last,
163 Reference_Clock => Clock_Range'Last,
164 M => PNV_M_Range'Last,
165 P => PNV_P_Range'Last,
166 VCO => PNV_VCO_Range'Last,
167 Dotclock => Clock_Range'Last);
168
169 type PNV_Limits_Type is
170 record
171 M_Lower : PNV_M_Range;
172 M_Upper : PNV_M_Range;
173 M2_Lower : PNV_M2_Range;
174 M2_Upper : PNV_M2_Range;
175 P_Lower : PNV_P_Range;
176 P_Upper : PNV_P_Range;
177 P1_Lower : PNV_P1_Range;
178 P1_Upper : PNV_P1_Range;
179 P2_Fast : PNV_P2_Range;
180 P2_Slow : PNV_P2_Range;
181 P2_Threshold : Clock_Range;
182 end record;
183
184 -- Pineview SDVO/DAC limits (from Linux pnv_limits_sdvo)
185 Pineview_SDVO_DAC_Limits : constant PNV_Limits_Type := PNV_Limits_Type'
186 (M_Lower => 2, M_Upper => 256,
187 M2_Lower => 0, M2_Upper => 254,
188 P_Lower => 5, P_Upper => 80,
189 P1_Lower => 1, P1_Upper => 8,
190 P2_Fast => 5, P2_Slow => 10,
191 P2_Threshold => 200_000_000);
192
193 -- Pineview LVDS limits (from Linux pnv_limits_lvds)
194 Pineview_LVDS_Limits : constant PNV_Limits_Type := PNV_Limits_Type'
195 (M_Lower => 2, M_Upper => 256,
196 M2_Lower => 0, M2_Upper => 254,
197 P_Lower => 7, P_Upper => 112,
198 P1_Lower => 1, P1_Upper => 8,
199 P2_Fast => 14, P2_Slow => 14,
200 P2_Threshold => 112_000_000);
201
Arthur Heymans960e2392026-03-03 19:45:24 +0100202 ----------------------------------------------------------------------------
203
204 type Regs is array (DPLLs) of Registers.Registers_Index;
205
206 DPLL : constant Regs := Regs'(Registers.GMCH_DPLL_A, Registers.GMCH_DPLL_B);
207 DPLL_VCO_ENABLE : constant := 1 * 2 ** 31;
208 DPLL_VGA_MODE_DIS : constant := 1 * 2 ** 28;
209 DPLL_P2_10_OR_14 : constant := 0 * 2 ** 24;
210 DPLL_P2_5_OR_7 : constant := 1 * 2 ** 24;
Arthur Heymans6dc78432026-06-18 09:34:50 +0200211 DPLL_P1_DIVIDER_SHIFT : constant := 16;
212 DPLL_P1_DIVIDER_SHIFT_PINEVIEW : constant := 15;
Arthur Heymans960e2392026-03-03 19:45:24 +0100213 DPLL_SDVOCLK : constant := 2 * 2 ** 13;
214
215 -- i945 does not use DPLL_PULSE_PHASE (bits 12:9, Gen4+ only)
216 -- i945 uses DVO_2X_MODE (bit 30) for SDVO outputs (same bit as
217 -- DPLL_SDVO_HIGH_SPEED on Gen4+)
218 DPLL_DVO_2X_MODE : constant := 1 * 2 ** 30;
219 DPLL_MODE_LVDS : constant := 2 * 2 ** 26;
220 DPLL_MODE_DAC : constant := 1 * 2 ** 26;
221 DPLL_DREFCLK : constant := 0 * 2 ** 13;
222 DPLL_SSC : constant := 3 * 2 ** 13;
223
224 MODE_DPLL_DAC : constant Word32 := Word32'
225 (DPLL_MODE_DAC or DPLL_DREFCLK);
226
227 MODE_DPLL_SDVO : constant Word32 := Word32'
228 (DPLL_MODE_DAC or DPLL_DREFCLK or DPLL_DVO_2X_MODE);
229
230 MODE_DPLL_LVDS : constant Word32 := Word32'
231 (DPLL_MODE_LVDS or DPLL_SSC);
232
233 type DPLL_Mode_Array is array (Display_Type) of Word32;
234
235 DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
236 (LVDS => MODE_DPLL_LVDS,
237 VGA => MODE_DPLL_DAC,
238 HDMI => MODE_DPLL_SDVO, -- SDVO outputs use HDMI display type
Nico Huberac2a1412026-07-02 10:41:59 +0200239 others => MODE_DPLL_SDVO);
Arthur Heymans960e2392026-03-03 19:45:24 +0100240
241 FP0 : constant Regs := Regs'(Registers.GMCH_FPA0, Registers.GMCH_FPB0);
242 FP1 : constant Regs := Regs'(Registers.GMCH_FPA1, Registers.GMCH_FPB1);
243 FP_N_SHIFT : constant := 16;
244 FP_M1_SHIFT : constant := 8;
245 FP_M2_SHIFT : constant := 0;
246
247 ----------------------------------------------------------------------------
248
249 procedure Verify_Parameters
250 (N : in N_Range;
251 M1 : in M1_Range;
252 M2 : in M2_Range;
253 P1 : in P1_Range;
254 P2 : in P2_Range;
255 Reference_Clock : in Clock_Range;
256 Current_Limits : in Limits_Type;
257 Result : out Clock_Type;
258 Valid : out Boolean)
259 with
260 Global => null,
261 Pre => True,
262 Post => True
263 is
264 M : Int64;
265 P : Int64;
266 VCO : Int64;
267 Dotclock : Int64;
268 begin
269 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
270
271 M := 5 * M1 + M2;
272 P := P1 * P2;
273 VCO := (Int64 (Reference_Clock) * M) / N;
274 Dotclock := VCO / P;
275
276 pragma Debug (Debug_Clocks and not (Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
277 pragma Debug (Debug_Clocks and (Current_Limits.P2_Fast /= P2 and P2 /= Current_Limits.P2_Slow ), Debug.Put_Line ("P2 out of range."));
278 pragma Debug (Debug_Clocks and not (Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper ), Debug.Put_Line ("P out of range."));
279 pragma Debug (Debug_Clocks and not (Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
280 pragma Debug (Debug_Clocks and not (Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
281 pragma Debug (Debug_Clocks and not (Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper ), Debug.Put_Line ("N out of range."));
282 pragma Debug (Debug_Clocks and not (Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper ), Debug.Put_Line ("M out of range."));
283 pragma Debug (Debug_Clocks and not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
284
285 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Line ("Dotclock too low."));
286 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Int64 (Dotclock));
287 pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.New_Line);
288
289 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Line ("Dotclock too high."));
290 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Int64 (Dotclock));
291 pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.New_Line);
292
293 Valid :=
294 Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper and
295 (Current_Limits.P2_Fast = P2 or P2 = Current_Limits.P2_Slow) and
296 Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper and
297 Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper and
298 Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper and
299 Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper and
300 Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper and
301 Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper and
302 Int64 (Clock_Range'First) <= Dotclock and
303 Dotclock <= Int64 (Clock_Range'Last);
304
305 if Valid
306 then
307 Result := Clock_Type'
308 (N => N,
309 M1 => M1,
310 M2 => M2,
311 P1 => P1,
312 P2 => P2,
313 Reference_Clock => Reference_Clock,
314 M => M,
315 P => P,
316 VCO => VCO,
317 Dotclock => Clock_Range (Dotclock));
318 else
319 Result := Invalid_Clock;
320 end if;
321
322 end Verify_Parameters;
323
324 procedure Calculate_Clock_Parameters
325 (Display : in Display_Type;
326 Target_Dotclock : in Clock_Range;
327 Reference_Clock : in Clock_Range;
328 Best_Clock : out Clock_Type;
329 Valid : out Boolean)
330 with
331 Global => null,
332 Pre => True,
333 Post => True
334 is
335 Limits : constant Limits_Type :=
336 (case Display is
337 when LVDS => LVDS_Limits,
338 when others => SDVO_DAC_Limits);
339
340 P2 : P2_Range;
341 Best_Delta : Int64 := Int64'Last;
342 Current_Delta : Int64;
343 Current_Clock : Clock_Type;
344 Registers_Valid : Boolean;
345 begin
346 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
347
348 Valid := False;
349 Best_Clock := Invalid_Clock;
350
351 if Target_Dotclock <= Limits.P2_Threshold then
352 P2 := Limits.P2_Slow;
353 else
354 P2 := Limits.P2_Fast;
355 end if;
356
357 for N in N_Range range Limits.N_Lower .. Limits.N_Upper
358 loop
359 -- reverse loops as hardware prefers higher values
360 for M1 in reverse M1_Range range Limits.M1_Lower .. Limits.M1_Upper
361 loop
362 pragma Loop_Invariant (True);
363 for M2 in reverse M2_Range range Limits.M2_Lower .. Int64'Min (Limits.M2_Upper, M1)
364 loop
365 pragma Loop_Invariant (True);
366 for P1 in reverse P1_Range range Limits.P1_Lower .. Limits.P1_Upper
367 loop
368 Verify_Parameters
369 (N => N,
370 M1 => M1,
371 M2 => M2,
372 P1 => P1,
373 P2 => P2,
374 Reference_Clock => Reference_Clock,
375 Current_Limits => Limits,
376 Result => Current_Clock,
377 Valid => Registers_Valid);
378
379 if Registers_Valid
380 then
381 if Current_Clock.Dotclock > Target_Dotclock
382 then
383 Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
384 else
385 Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
386 end if;
387
388 if Current_Delta < Best_Delta
389 then
390 Best_Delta := Current_Delta;
391 Best_Clock := Current_Clock;
392 Valid := True;
393 end if;
394
395 pragma Debug (Debug_Clocks, Debug.Put ("Current/Target/Best_Delta: "));
396 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Current_Clock.Dotclock));
397 pragma Debug (Debug_Clocks, Debug.Put ("/"));
398 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Target_Dotclock));
399 pragma Debug (Debug_Clocks, Debug.Put ("/"));
400 pragma Debug (Debug_Clocks, Debug.Put_Int64 (Best_Delta));
401 pragma Debug (Debug_Clocks, Debug.Put_Line ("."));
402
403 end if;
404 end loop;
405 end loop;
406 end loop;
407 end loop;
408
409 pragma Debug (Valid, Debug.Put_Line ("Valid clock found."));
410 pragma Debug (Valid, Debug.Put ("Best/Target/Delta: "));
411 pragma Debug (Valid, Debug.Put_Int64 (Best_Clock.Dotclock));
412 pragma Debug (Valid, Debug.Put ("/"));
413 pragma Debug (Valid, Debug.Put_Int64 (Target_Dotclock));
414 pragma Debug (Valid, Debug.Put ("/"));
415 pragma Debug (Valid, Debug.Put_Int64 (Best_Delta));
416 pragma Debug (Valid, Debug.Put_Line ("."));
417 pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
418
419 end Calculate_Clock_Parameters;
420
Arthur Heymans6dc78432026-06-18 09:34:50 +0200421 procedure Verify_Pineview_Parameters
422 (N : in PNV_N_Range;
423 M2 : in PNV_M2_Range;
424 P1 : in PNV_P1_Range;
425 P2 : in PNV_P2_Range;
426 Reference_Clock : in Clock_Range;
427 Current_Limits : in PNV_Limits_Type;
428 Result : out PNV_Clock_Type;
429 Valid : out Boolean)
430 with
431 Global => null,
432 Pre => True,
433 Post => True
434 is
435 M : Int64;
436 P : Int64;
437 VCO : Int64;
438 Dotclock : Int64;
439 begin
440 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
441
442 M := M2 + 2;
443 P := P1 * P2;
444 VCO := (Int64 (Reference_Clock) * M) / N;
445 Dotclock := VCO / P;
446
447 Valid :=
448 Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper and
449 (Current_Limits.P2_Fast = P2 or P2 = Current_Limits.P2_Slow) and
450 Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper and
451 Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper and
452 Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper and
453 PNV_VCO_Range'First <= VCO and VCO <= PNV_VCO_Range'Last and
454 Int64 (Clock_Range'First) <= Dotclock and
455 Dotclock <= Int64 (Clock_Range'Last);
456
457 if Valid
458 then
459 Result := PNV_Clock_Type'
460 (N => N,
461 M2 => M2,
462 P1 => P1,
463 P2 => P2,
464 Reference_Clock => Reference_Clock,
465 M => M,
466 P => P,
467 VCO => VCO,
468 Dotclock => Clock_Range (Dotclock));
469 else
470 Result := Invalid_PNV_Clock;
471 end if;
472 end Verify_Pineview_Parameters;
473
474 procedure Calculate_Pineview_Clock_Parameters
475 (Display : in Display_Type;
476 Target_Dotclock : in Clock_Range;
477 Reference_Clock : in Clock_Range;
478 Best_Clock : out PNV_Clock_Type;
479 Valid : out Boolean)
480 with
481 Global => null,
482 Pre => True,
483 Post => True
484 is
485 Limits : constant PNV_Limits_Type :=
486 (case Display is
487 when LVDS => Pineview_LVDS_Limits,
488 when others => Pineview_SDVO_DAC_Limits);
489
490 P2 : PNV_P2_Range;
491 Best_Delta : Int64 := Int64'Last;
492 Current_Delta : Int64;
493 Current_Clock : PNV_Clock_Type;
494 Registers_Valid : Boolean;
495 begin
496 pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
497
498 Valid := False;
499 Best_Clock := Invalid_PNV_Clock;
500
501 if Target_Dotclock <= Limits.P2_Threshold then
502 P2 := Limits.P2_Slow;
503 else
504 P2 := Limits.P2_Fast;
505 end if;
506
507 -- Use the newer libgfxinit search order (N, then M2, then P1) and
508 -- descending M2/P1 loops. Linux's Pineview code iterates M2 outermost
509 -- and ascending; this only affects ties between parameter sets with
510 -- the same delta.
511 for N in PNV_N_Range loop
512 for M2 in reverse PNV_M2_Range range Limits.M2_Lower .. Limits.M2_Upper
513 loop
514 pragma Loop_Invariant (True);
515 for P1 in reverse PNV_P1_Range range Limits.P1_Lower .. Limits.P1_Upper
516 loop
517 Verify_Pineview_Parameters
518 (N => N,
519 M2 => M2,
520 P1 => P1,
521 P2 => P2,
522 Reference_Clock => Reference_Clock,
523 Current_Limits => Limits,
524 Result => Current_Clock,
525 Valid => Registers_Valid);
526
527 if Registers_Valid
528 then
529 if Current_Clock.Dotclock > Target_Dotclock
530 then
531 Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
532 else
533 Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
534 end if;
535
536 if Current_Delta < Best_Delta
537 then
538 Best_Delta := Current_Delta;
539 Best_Clock := Current_Clock;
540 Valid := True;
541 end if;
542 end if;
543 end loop;
544 end loop;
545 end loop;
546 end Calculate_Pineview_Clock_Parameters;
547
Arthur Heymans960e2392026-03-03 19:45:24 +0100548 procedure Program_DPLL
549 (PLL : DPLLs;
550 Display : Display_Type;
551 Clk : Clock_Type)
552 with
553 Global => (In_Out => Registers.Register_State),
554 Pre => True,
555 Post => True
556 is
557 FP, Encoded_P1, Encoded_P2 : Word32;
558 begin
559 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
560
561 -- FP register: N, M1, M2 encoded as (actual_value - 2)
562 FP :=
563 Shift_Left (Word32 (Clk.N - 2), FP_N_SHIFT) or
564 Shift_Left (Word32 (Clk.M1 - 2), FP_M1_SHIFT) or
565 Shift_Left (Word32 (Clk.M2 - 2), FP_M2_SHIFT);
566
567 Registers.Write (FP0 (PLL), FP);
568 Registers.Write (FP1 (PLL), FP);
569
570 -- P1 encoding: bitmask (1 << (P1-1)) shifted into position
571 Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
572
573 if Clk.P2 = 5 or Clk.P2 = 7
574 then
575 Encoded_P2 := DPLL_P2_5_OR_7;
576 else
577 Encoded_P2 := DPLL_P2_10_OR_14;
578 end if;
579
580 -- i945 DPLL register: no HIGH_SPEED bit, no PULSE_PHASE bits
581 Registers.Write
582 (Register => DPLL (PLL),
583 Value => DPLL_Mode (Display) or
584 DPLL_VGA_MODE_DIS or
585 Encoded_P2 or
586 Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT));
587 end Program_DPLL;
588
Arthur Heymans6dc78432026-06-18 09:34:50 +0200589 procedure Program_Pineview_DPLL
590 (PLL : DPLLs;
591 Display : Display_Type;
592 Clk : PNV_Clock_Type)
593 with
594 Global => (In_Out => Registers.Register_State),
595 Pre => True,
596 Post => True
597 is
598 FP, Encoded_P1, Encoded_P2 : Word32;
599 begin
600 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
601
602 -- Pineview frequency-parameter (FP) register: N is one-hot,
603 -- M1 is reserved, M2 is raw.
604 FP :=
605 Shift_Left (Shift_Left (Word32'(1), Natural (Clk.N)), FP_N_SHIFT) or
606 Shift_Left (Word32 (Clk.M2), FP_M2_SHIFT);
607
608 Registers.Write (FP0 (PLL), FP);
609 Registers.Write (FP1 (PLL), FP);
610
611 Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
612
613 if Clk.P2 = 5 or Clk.P2 = 7
614 then
615 Encoded_P2 := DPLL_P2_5_OR_7;
616 else
617 Encoded_P2 := DPLL_P2_10_OR_14;
618 end if;
619
620 Registers.Write
621 (Register => DPLL (PLL),
622 Value => DPLL_Mode (Display) or
623 DPLL_VGA_MODE_DIS or
624 Encoded_P2 or
625 Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT_PINEVIEW));
626 end Program_Pineview_DPLL;
627
Arthur Heymans960e2392026-03-03 19:45:24 +0100628 procedure On
629 (PLL : in T;
630 Port_Cfg : in Port_Config;
631 Success : out Boolean)
632 is
633 Target_Clock : constant Frequency_Type := Port_Cfg.Mode.Dotclock;
Arthur Heymans960e2392026-03-03 19:45:24 +0100634 begin
635 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
636
637 Success := PLL in DPLLs;
Arthur Heymans960e2392026-03-03 19:45:24 +0100638
639 if Success then
640 if Target_Clock <= 400_000_000 then
Arthur Heymans6dc78432026-06-18 09:34:50 +0200641 if Config.CPU_Any_Pineview then
642 declare
643 Clk : PNV_Clock_Type;
644 begin
645 Calculate_Pineview_Clock_Parameters
646 (Display => Port_Cfg.Display,
647 Target_Dotclock => Target_Clock,
648 Reference_Clock => 96_000_000,
649 Best_Clock => Clk,
650 Valid => Success);
651 if Success then
652 Program_Pineview_DPLL (PLL, Port_Cfg.Display, Clk);
653 end if;
654 end;
655 else
656 declare
657 Clk : Clock_Type;
658 begin
659 Calculate_Clock_Parameters
660 (Display => Port_Cfg.Display,
661 Target_Dotclock => Target_Clock,
662 Reference_Clock => 96_000_000,
663 Best_Clock => Clk,
664 Valid => Success);
665 if Success then
666 Program_DPLL (PLL, Port_Cfg.Display, Clk);
667 end if;
668 end;
669 end if;
Arthur Heymans960e2392026-03-03 19:45:24 +0100670 else
671 Success := False;
672 pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
673 pragma Debug (Debug.Put_Int64 (Target_Clock));
674 pragma Debug (Debug.Put (" > "));
675 pragma Debug (Debug.Put_Int32 (400_000_000));
676 pragma Debug (Debug.New_Line);
677 pragma Debug (Debug.New_Line);
678 end if;
679 end if;
680
681 if Success then
Arthur Heymans960e2392026-03-03 19:45:24 +0100682 Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
683 Registers.Posting_Read (DPLL (PLL));
684 Time.U_Delay (150);
685 end if;
686 end On;
687
688 procedure Off (PLL : T)
689 is
690 begin
691 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
692
693 if PLL in DPLLs then
694 Registers.Unset_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
695 end if;
696 end Off;
697
698 ----------------------------------------------------------------------------
699
700 procedure Initialize
701 is
702 begin
703 PLLs :=
704 (DPLLs =>
705 (Use_Count => 0,
706 Mode => Invalid_Mode));
707 end Initialize;
708
709 procedure Alloc_Configurable
710 (Port_Cfg : in Port_Config;
711 PLL : out T;
712 Success : out Boolean)
713 with
714 Pre => True
715 is
716 function Config_Matches (PE : PLL_State) return Boolean
717 is
718 begin
719 return PE.Mode = Port_Cfg.Mode;
720 end Config_Matches;
721 begin
722 -- try to find shareable PLL
723 for P in DPLLs loop
724 Success := PLLs (P).Use_Count /= 0 and
725 PLLs (P).Use_Count /= Count_Range'Last and
726 Config_Matches (PLLs (P));
727 if Success then
728 PLL := P;
729 PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
730 return;
731 end if;
732 end loop;
733
734 -- try to find free PLL
735 for P in DPLLs loop
736 if PLLs (P).Use_Count = 0 then
737 PLL := P;
738 On (PLL, Port_Cfg, Success);
739 if Success then
740 PLLs (PLL) :=
741 (Use_Count => 1,
742 Mode => Port_Cfg.Mode);
743 end if;
744 return;
745 end if;
746 end loop;
747
748 PLL := Invalid;
749 end Alloc_Configurable;
750
751 procedure Alloc
752 (Port_Cfg : in Port_Config;
753 PLL : out T;
754 Success : out Boolean)
755 is
756 -- On i945, DPLL-to-pipe mapping is fixed:
757 -- LVDS -> Pipe B -> DPLL_B, all others -> Pipe A -> DPLL_A
758 Target : constant DPLLs :=
759 (if Port_Cfg.Display = LVDS then DPLL_B else DPLL_A);
760 begin
761 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
762
763 PLL := Target;
764
765 if PLLs (Target).Use_Count /= 0 and
766 PLLs (Target).Use_Count /= Count_Range'Last and
767 PLLs (Target).Mode = Port_Cfg.Mode
768 then
769 -- Share existing PLL with matching mode
770 PLLs (Target).Use_Count := PLLs (Target).Use_Count + 1;
771 Success := True;
772 elsif PLLs (Target).Use_Count = 0 then
773 -- Program the PLL
774 On (Target, Port_Cfg, Success);
775 if Success then
776 PLLs (Target) :=
777 (Use_Count => 1,
778 Mode => Port_Cfg.Mode);
779 end if;
780 else
781 PLL := Invalid;
782 Success := False;
783 end if;
784 end Alloc;
785
786 procedure Free (PLL : T)
787 is
788 begin
789 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
790
791 if PLL in DPLLs then
792 if PLLs (PLL).Use_Count /= 0 then
793 PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
794 if PLLs (PLL).Use_Count = 0 then
795 Off (PLL);
796 end if;
797 end if;
798 end if;
799 end Free;
800
801 procedure All_Off
802 is
803 begin
804 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
805
806 for PLL in DPLLs loop
807 Off (PLL);
808 end loop;
809 end All_Off;
810
811 function Register_Value (PLL : T) return Word32
812 is
813 begin
814 return (if PLL = DPLL_B then 1 else 0);
815 end Register_Value;
816
817end HW.GFX.GMA.PLLs;