blob: 0a5b1b51b8d2a74b74bfcfee98812645c31a59c7 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2014-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
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
Nico Huber83693c82016-10-08 22:17:55 +020015with HW.GFX.GMA.Config;
Nico Huber8c45bcf2016-11-20 17:30:57 +010016with HW.GFX.GMA.Config_Helpers;
Nico Huber83693c82016-10-08 22:17:55 +020017with HW.GFX.GMA.Registers;
18with HW.GFX.GMA.Power_And_Clocks;
19with HW.GFX.GMA.Panel;
20with HW.GFX.GMA.PLLs;
21with HW.GFX.GMA.Port_Detect;
22with HW.GFX.GMA.Connectors;
23with HW.GFX.GMA.Connector_Info;
24with HW.GFX.GMA.Pipe_Setup;
25
26with System;
27
28with HW.Debug;
29with GNAT.Source_Info;
30
31use type HW.Word8;
32use type HW.Int32;
33
34package body HW.GFX.GMA
35 with Refined_State =>
36 (State =>
37 (Registers.Address_State,
38 PLLs.State, Panel.Panel_State,
39 Cur_Configs, Allocated_PLLs, DP_Links,
40 HPD_Delay, Wait_For_HPD),
41 Init_State => Initialized,
42 Config_State => Config.Valid_Port_GPU,
43 Device_State =>
44 (Registers.Register_State, Registers.GTT_State))
45is
46
47 subtype Port_Name is String (1 .. 8);
48 type Port_Name_Array is array (Port_Type) of Port_Name;
49 Port_Names : constant Port_Name_Array :=
50 (Disabled => "Disabled",
51 Internal => "Internal",
52 DP1 => "DP1 ",
53 DP2 => "DP2 ",
54 DP3 => "DP3 ",
Nico Huber0d454cd2016-11-21 13:33:43 +010055 HDMI1 => "HDMI1 ",
56 HDMI2 => "HDMI2 ",
57 HDMI3 => "HDMI3 ",
Nico Huber83693c82016-10-08 22:17:55 +020058 Analog => "Analog ");
59
60 package Display_Controller renames Pipe_Setup;
61
Nico Huber99f10f32016-11-20 00:34:05 +010062 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020063
Nico Huber99f10f32016-11-20 00:34:05 +010064 type Links_Type is array (Pipe_Index) of DP_Link;
Nico Huber83693c82016-10-08 22:17:55 +020065
66 type HPD_Type is array (Port_Type) of Boolean;
67 type HPD_Delay_Type is array (Port_Type) of Time.T;
68
Nico Huber83693c82016-10-08 22:17:55 +020069 Allocated_PLLs : PLLs_Type;
70 DP_Links : Links_Type;
71 HPD_Delay : HPD_Delay_Type;
72 Wait_For_HPD : HPD_Type;
73 Initialized : Boolean := False;
74
Nico Huber83693c82016-10-08 22:17:55 +020075 ----------------------------------------------------------------------------
76
Nico Huberf54d0962016-10-20 14:17:18 +020077 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
78
79 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
80 is
81 begin
82 return Word32 (Freq / 1_000_000);
83 end PCH_RAWCLK_FREQ;
84
85 ----------------------------------------------------------------------------
86
Nico Huber83693c82016-10-08 22:17:55 +020087 function To_Controller
Nico Huber99f10f32016-11-20 00:34:05 +010088 (Dsp_Config : Pipe_Index) return Display_Controller.Controller_Type
Nico Huber83693c82016-10-08 22:17:55 +020089 is
90 Result : Display_Controller.Controller_Type;
91 begin
92 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
93
94 case Dsp_Config is
95 when Primary =>
96 Result := Display_Controller.Controllers (Display_Controller.A);
97 when Secondary =>
98 Result := Display_Controller.Controllers (Display_Controller.B);
99 when Tertiary =>
100 Result := Display_Controller.Controllers (Display_Controller.C);
101 end case;
102 return Result;
103 end To_Controller;
104
105 ----------------------------------------------------------------------------
106
107 function To_Head
Nico Huber99f10f32016-11-20 00:34:05 +0100108 (N_Config : Pipe_Index;
Nico Huber83693c82016-10-08 22:17:55 +0200109 Port : Active_Port_Type)
110 return Display_Controller.Head_Type
111 is
112 Result : Display_Controller.Head_Type;
113 begin
114 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
115
116 if Config.Has_EDP_Pipe and then Port = Internal then
117 Result := Display_Controller.Heads (Display_Controller.Head_EDP);
118 else
119 case N_Config is
120 when Primary =>
121 Result := Display_Controller.Heads (Display_Controller.Head_A);
122 when Secondary =>
123 Result := Display_Controller.Heads (Display_Controller.Head_B);
124 when Tertiary =>
125 Result := Display_Controller.Heads (Display_Controller.Head_C);
126 end case;
127 end if;
128 return Result;
129 end To_Head;
130
131 ----------------------------------------------------------------------------
132
133 procedure Legacy_VGA_Off
134 is
135 Reg8 : Word8;
136 begin
137 -- disable legacy VGA plane, taking over control now
138 Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
139 Port_IO.InB (Reg8, VGA_SR_DATA);
140 Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
141 Time.U_Delay (100); -- PRM says 100us, Linux does 300
142 Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
143 end Legacy_VGA_Off;
144
145 ----------------------------------------------------------------------------
146
Nico Huber99f10f32016-11-20 00:34:05 +0100147 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200148 is
149 Did_Power_Up : Boolean := False;
150
151 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +0100152 Old_Config, New_Config : Pipe_Config;
153 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200154 Port_Cfg : Port_Config;
155
156 procedure Check_HPD
157 (Port_Cfg : in Port_Config;
158 Port : in Port_Type;
159 Detected : out Boolean)
160 is
161 begin
162 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
163 if HPD_Delay_Over then
164 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
165 HPD_Delay (Port) := Time.MS_From_Now (333);
166 else
167 Detected := False;
168 end if;
169 end Check_HPD;
170 begin
171 Old_Configs := Cur_Configs;
172
Nico Huber99f10f32016-11-20 00:34:05 +0100173 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200174 HPD := False;
175
176 Old_Config := Cur_Configs (I);
177 New_Config := Configs (I);
178
Nico Huber8c45bcf2016-11-20 17:30:57 +0100179 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100180 (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
Nico Huber8c45bcf2016-11-20 17:30:57 +0100181 Port_Cfg.DP := DP_Links (I);
Nico Huber83693c82016-10-08 22:17:55 +0200182 if Success then
183 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
184 end if;
185
186 -- Connector changed?
187 if (Success and then HPD) or
188 Old_Config.Port /= New_Config.Port or
189 Old_Config.Mode /= New_Config.Mode
190 then
191 if Old_Config.Port /= Disabled then
192 if Success then
193 pragma Debug (Debug.New_Line);
194 pragma Debug (Debug.Put_Line
195 ("Disabling port " & Port_Names (Old_Config.Port)));
196
197 Connectors.Pre_Off (Port_Cfg);
198
199 Display_Controller.Off
200 (To_Controller (I), To_Head (I, Old_Config.Port));
201
202 Connectors.Post_Off (Port_Cfg);
203 end if;
204
205 -- Free PLL
206 PLLs.Free (Allocated_PLLs (I));
207
208 Cur_Configs (I).Port := Disabled;
209 end if;
210
211 if New_Config.Port /= Disabled then
Nico Huber8c45bcf2016-11-20 17:30:57 +0100212 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100213 (Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200214
Nico Huber8c45bcf2016-11-20 17:30:57 +0100215 if Success then
216 Success := Config_Helpers.Validate_Config
217 (New_Config.Framebuffer, Port_Cfg, I);
218 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100219
Nico Huber83693c82016-10-08 22:17:55 +0200220 if Success and then Wait_For_HPD (New_Config.Port) then
221 Check_HPD (Port_Cfg, New_Config.Port, Success);
222 Wait_For_HPD (New_Config.Port) := not Success;
223 end if;
224
225 if Success then
226 pragma Debug (Debug.New_Line);
227 pragma Debug (Debug.Put_Line
228 ("Trying to enable port " & Port_Names (New_Config.Port)));
229
230 if not Did_Power_Up then
231 Power_And_Clocks.Power_Up (Old_Configs, Configs);
232 Did_Power_Up := True;
233 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200234 end if;
235
236 if Success then
237 Connector_Info.Preferred_Link_Setting
238 (Port_Cfg => Port_Cfg,
239 Success => Success);
240 end if;
241
242 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100243 pragma Loop_Invariant
244 (New_Config.Port in Active_Port_Type and
245 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200246
247 PLLs.Alloc
248 (Port_Cfg => Port_Cfg,
249 PLL => Allocated_PLLs (I),
250 Success => Success);
251
252 if Success then
253 for Try in 1 .. 2 loop
254 pragma Loop_Invariant
255 (New_Config.Port in Active_Port_Type);
256
257 Connectors.Pre_On
258 (Port_Cfg => Port_Cfg,
259 PLL_Hint => PLLs.Register_Value
260 (Allocated_PLLs (I)),
261 Pipe_Hint => Display_Controller.Get_Pipe_Hint
262 (To_Head (I, New_Config.Port)),
263 Success => Success);
264
265 if Success then
266 Display_Controller.On
267 (Controller => To_Controller (I),
268 Head => To_Head (I, New_Config.Port),
269 Port_Cfg => Port_Cfg,
270 Framebuffer => New_Config.Framebuffer);
271
272 Connectors.Post_On
273 (Port_Cfg => Port_Cfg,
274 PLL_Hint => PLLs.Register_Value
275 (Allocated_PLLs (I)),
276 Success => Success);
277
278 if not Success then
279 Display_Controller.Off
280 (To_Controller (I),
281 To_Head (I, New_Config.Port));
282 Connectors.Post_Off (Port_Cfg);
283 end if;
284 end if;
285
286 exit when Success;
287 end loop;
288 exit when Success; -- connection established => stop loop
289
290 -- connection failed
291 PLLs.Free (Allocated_PLLs (I));
292 end if;
293
294 Connector_Info.Next_Link_Setting
295 (Port_Cfg => Port_Cfg,
296 Success => Success);
297 end loop;
298
299 if Success then
300 pragma Debug (Debug.Put_Line
301 ("Enabled port " & Port_Names (New_Config.Port)));
302 Cur_Configs (I) := New_Config;
303 DP_Links (I) := Port_Cfg.DP;
304 else
305 Wait_For_HPD (New_Config.Port) := True;
306 if New_Config.Port = Internal then
307 Panel.Off;
308 end if;
309 end if;
310 else
311 Cur_Configs (I) := New_Config;
312 end if;
313 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
314 Old_Config.Port /= Disabled
315 then
316 Display_Controller.Update_Offset
317 (Controller => To_Controller (I),
318 Framebuffer => New_Config.Framebuffer);
319 Cur_Configs (I) := New_Config;
320 end if;
321 end loop;
322
323 if Did_Power_Up then
324 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
325 end if;
326
327 end Update_Outputs;
328
329 ----------------------------------------------------------------------------
330
331 procedure Initialize
332 (MMIO_Base : in Word64 := 0;
333 Write_Delay : in Word64 := 0;
334 Success : out Boolean)
335 with
336 Refined_Global =>
337 (In_Out =>
338 (Config.Valid_Port_GPU,
339 Registers.Register_State, Port_IO.State),
340 Input =>
341 (Time.State),
342 Output =>
343 (Registers.Address_State,
344 PLLs.State, Panel.Panel_State,
345 Cur_Configs, Allocated_PLLs, DP_Links,
346 HPD_Delay, Wait_For_HPD, Initialized))
347 is
348 use type HW.Word64;
349
350 Now : constant Time.T := Time.Now;
351
352 procedure Check_Platform (Success : out Boolean)
353 is
354 Audio_VID_DID : Word32;
355 begin
356 case Config.CPU is
357 when Haswell .. Skylake =>
358 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
359 when Ironlake .. Ivybridge =>
360 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
361 end case;
362 Success :=
363 (case Config.CPU is
364 when Skylake => Audio_VID_DID = 16#8086_2809#,
365 when Broadwell => Audio_VID_DID = 16#8086_2808#,
366 when Haswell => Audio_VID_DID = 16#8086_2807#,
367 when Ivybridge |
368 Sandybridge => Audio_VID_DID = 16#8086_2806# or
369 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200370 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200371 end Check_Platform;
372 begin
373 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
374 Reason => "Write_Delay is used for debugging only");
375
376 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
377
378 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
379
380 Wait_For_HPD := HPD_Type'(others => False);
381 HPD_Delay := HPD_Delay_Type'(others => Now);
382 DP_Links := Links_Type'(others => HW.GFX.Default_DP);
383 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100384 Cur_Configs := Pipe_Configs'
385 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200386 (Port => Disabled,
387 Framebuffer => HW.GFX.Default_FB,
388 Mode => HW.GFX.Invalid_Mode));
389 Registers.Set_Register_Base
390 (if MMIO_Base /= 0 then
391 MMIO_Base
392 else
393 Config.Default_MMIO_Base);
394 PLLs.Initialize;
395
396 Check_Platform (Success);
397 if not Success then
398 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
399
400 Panel.Static_Init; -- for flow analysis
401
402 Initialized := False;
403 return;
404 end if;
405
406 Panel.Setup_PP_Sequencer;
407 Port_Detect.Initialize;
408
409 Power_And_Clocks.Pre_All_Off;
410
411 Legacy_VGA_Off;
412
413 Connectors.Pre_All_Off;
414 Display_Controller.All_Off;
415 Connectors.Post_All_Off;
416 PLLs.All_Off;
417
418 Power_And_Clocks.Post_All_Off;
419
420 -------------------- Now restart from a clean state ---------------------
421 Power_And_Clocks.Initialize;
422
Nico Huberf54d0962016-10-20 14:17:18 +0200423 Registers.Unset_And_Set_Mask
424 (Register => Registers.PCH_RAWCLK_FREQ,
425 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
426 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
427
Nico Huber83693c82016-10-08 22:17:55 +0200428 Initialized := True;
429
430 end Initialize;
431
432 function Is_Initialized return Boolean
433 with
434 Refined_Post => Is_Initialized'Result = Initialized
435 is
436 begin
437 return Initialized;
438 end Is_Initialized;
439
440 ----------------------------------------------------------------------------
441
442 procedure Write_GTT
443 (GTT_Page : GTT_Range;
444 Device_Address : GTT_Address_Type;
445 Valid : Boolean) is
446 begin
447 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
448 end Write_GTT;
449
450 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
451 is
452 FB_Size : constant Pos32 :=
453 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
454 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
455 begin
456 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
457 loop
458 Registers.Write_GTT
459 (GTT_Page => Idx,
460 Device_Address => Phys_Addr,
461 Valid => True);
462 Phys_Addr := Phys_Addr + 4096;
463 end loop;
464 end Setup_Default_GTT;
465
466 ----------------------------------------------------------------------------
467
Nico Huber99f10f32016-11-20 00:34:05 +0100468 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200469 is
470 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100471 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200472 Pipe_Names : constant Pipe_Name_Array :=
473 (Primary => "Primary ",
474 Secondary => "Secondary",
475 Tertiary => "Tertiary ");
476 begin
477 Debug.New_Line;
478 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100479 for Pipe in Pipe_Index loop
480 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200481 Debug.Put (" (");
482 else
483 Debug.Put (" ");
484 end if;
485 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
486 Debug.Put_Line
487 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
488 Debug.Put_Line (" Framebuffer =>");
489 Debug.Put (" (Width => ");
490 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
491 Debug.Put_Line (",");
492 Debug.Put (" Height => ");
493 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
494 Debug.Put_Line (",");
495 Debug.Put (" Stride => ");
496 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
497 Debug.Put_Line (",");
498 Debug.Put (" Offset => ");
499 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
500 Debug.Put_Line (",");
501 Debug.Put (" BPC => ");
502 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
503 Debug.Put_Line ("),");
504 Debug.Put_Line (" Mode =>");
505 Debug.Put (" (Dotclock => ");
506 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
507 Debug.Put_Line (",");
508 Debug.Put (" H_Visible => ");
509 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
510 Debug.Put_Line (",");
511 Debug.Put (" H_Sync_Begin => ");
512 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
513 Debug.Put_Line (",");
514 Debug.Put (" H_Sync_End => ");
515 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
516 Debug.Put_Line (",");
517 Debug.Put (" H_Total => ");
518 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
519 Debug.Put_Line (",");
520 Debug.Put (" V_Visible => ");
521 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
522 Debug.Put_Line (",");
523 Debug.Put (" V_Sync_Begin => ");
524 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
525 Debug.Put_Line (",");
526 Debug.Put (" V_Sync_End => ");
527 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
528 Debug.Put_Line (",");
529 Debug.Put (" V_Total => ");
530 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
531 Debug.Put_Line (",");
532 Debug.Put_Line (" H_Sync_Active_High => " &
533 (if Configs (Pipe).Mode.H_Sync_Active_High
534 then "True,"
535 else "False,"));
536 Debug.Put_Line (" V_Sync_Active_High => " &
537 (if Configs (Pipe).Mode.V_Sync_Active_High
538 then "True,"
539 else "False,"));
540 Debug.Put (" BPC => ");
541 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100542 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200543 Debug.Put_Line (")),");
544 else
545 Debug.Put_Line (")));");
546 end if;
547 end loop;
548 end Dump_Configs;
549
550end HW.GFX.GMA;