blob: 9f835d221b82d9acd8e2085a610bbc8bdb38e083 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber3be61d42017-01-09 13:58:18 +01002-- Copyright (C) 2014-2017 secunet Security Networks AG
Nico Huber2b6f6992017-07-09 18:11:34 +02003-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
Nico Huber83693c82016-10-08 22:17:55 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02007-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02009--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
Nico Huber2b6f6992017-07-09 18:11:34 +020016with HW.MMIO_Range;
17pragma Elaborate_All (HW.MMIO_Range);
18with HW.PCI.Dev;
19pragma Elaborate_All (HW.PCI.Dev);
20
Nico Huber5374c3a2017-07-15 21:48:06 +020021with HW.GFX.Framebuffer_Filler;
22
Nico Huber83693c82016-10-08 22:17:55 +020023with HW.GFX.GMA.Config;
Nico Huber8c45bcf2016-11-20 17:30:57 +010024with HW.GFX.GMA.Config_Helpers;
Nico Huber83693c82016-10-08 22:17:55 +020025with HW.GFX.GMA.Registers;
26with HW.GFX.GMA.Power_And_Clocks;
27with HW.GFX.GMA.Panel;
28with HW.GFX.GMA.PLLs;
29with HW.GFX.GMA.Port_Detect;
30with HW.GFX.GMA.Connectors;
31with HW.GFX.GMA.Connector_Info;
32with HW.GFX.GMA.Pipe_Setup;
33
Nico Huber83693c82016-10-08 22:17:55 +020034with HW.Debug;
35with GNAT.Source_Info;
36
Nico Huber83693c82016-10-08 22:17:55 +020037use type HW.Int32;
38
39package body HW.GFX.GMA
40 with Refined_State =>
41 (State =>
Nico Huber2b6f6992017-07-09 18:11:34 +020042 (Dev.Address_State,
43 Registers.Address_State,
Nico Huber83693c82016-10-08 22:17:55 +020044 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +010045 Cur_Configs, Allocated_PLLs,
Nico Huberc3f66f62017-07-16 21:39:54 +020046 HPD_Delay, Wait_For_HPD,
47 Linear_FB_Base),
Nico Huber83693c82016-10-08 22:17:55 +020048 Init_State => Initialized,
49 Config_State => Config.Valid_Port_GPU,
50 Device_State =>
Nico Huber2b6f6992017-07-09 18:11:34 +020051 (Dev.PCI_State, Registers.Register_State, Registers.GTT_State))
Nico Huber83693c82016-10-08 22:17:55 +020052is
Nico Huber2b6f6992017-07-09 18:11:34 +020053 pragma Disable_Atomic_Synchronization;
Nico Huber83693c82016-10-08 22:17:55 +020054
55 subtype Port_Name is String (1 .. 8);
56 type Port_Name_Array is array (Port_Type) of Port_Name;
57 Port_Names : constant Port_Name_Array :=
58 (Disabled => "Disabled",
59 Internal => "Internal",
60 DP1 => "DP1 ",
61 DP2 => "DP2 ",
62 DP3 => "DP3 ",
Nico Huber0d454cd2016-11-21 13:33:43 +010063 HDMI1 => "HDMI1 ",
64 HDMI2 => "HDMI2 ",
65 HDMI3 => "HDMI3 ",
Nico Huber83693c82016-10-08 22:17:55 +020066 Analog => "Analog ");
67
Nico Huber2b6f6992017-07-09 18:11:34 +020068 package Dev is new HW.PCI.Dev (PCI.Address'(0, 2, 0));
69
Nico Huber83693c82016-10-08 22:17:55 +020070 package Display_Controller renames Pipe_Setup;
71
Nico Huber99f10f32016-11-20 00:34:05 +010072 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020073
Nico Huber83693c82016-10-08 22:17:55 +020074 type HPD_Type is array (Port_Type) of Boolean;
Nico Huber3be61d42017-01-09 13:58:18 +010075 type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
Nico Huber83693c82016-10-08 22:17:55 +020076
Nico Huber83693c82016-10-08 22:17:55 +020077 Allocated_PLLs : PLLs_Type;
Nico Huber83693c82016-10-08 22:17:55 +020078 HPD_Delay : HPD_Delay_Type;
79 Wait_For_HPD : HPD_Type;
80 Initialized : Boolean := False;
81
Nico Huberc3f66f62017-07-16 21:39:54 +020082 Linear_FB_Base : Word64;
83
Nico Huber83693c82016-10-08 22:17:55 +020084 ----------------------------------------------------------------------------
85
Nico Huberf54d0962016-10-20 14:17:18 +020086 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
87
88 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
89 is
90 begin
91 return Word32 (Freq / 1_000_000);
92 end PCH_RAWCLK_FREQ;
93
94 ----------------------------------------------------------------------------
95
Nico Huber43370ba2017-01-09 15:26:19 +010096 procedure Enable_Output
97 (Pipe : in Pipe_Index;
98 Pipe_Cfg : in Pipe_Config;
99 Success : out Boolean)
100 is
101 Port_Cfg : Port_Config;
102 begin
Nico Huber3be61d42017-01-09 13:58:18 +0100103 pragma Debug (Debug.New_Line);
104 pragma Debug (Debug.Put_Line
105 ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
106
Nico Huber43370ba2017-01-09 15:26:19 +0100107 Config_Helpers.Fill_Port_Config
108 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
109
110 if Success then
111 Success := Config_Helpers.Validate_Config
112 (Pipe_Cfg.Framebuffer, Port_Cfg, Pipe);
113 end if;
114
Nico Huber43370ba2017-01-09 15:26:19 +0100115 if Success then
Nico Huber43370ba2017-01-09 15:26:19 +0100116 Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
117 end if;
118
119 -- loop over all possible DP-lane configurations
120 -- (non-DP ports use a single fake configuration)
121 while Success loop
122 pragma Loop_Invariant
123 (Pipe_Cfg.Port in Active_Port_Type and
124 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
125
126 PLLs.Alloc
127 (Port_Cfg => Port_Cfg,
128 PLL => Allocated_PLLs (Pipe),
129 Success => Success);
130
131 if Success then
132 -- try each DP-lane configuration twice
133 for Try in 1 .. 2 loop
134 pragma Loop_Invariant
135 (Pipe_Cfg.Port in Active_Port_Type);
136
Nico Huber4798c662017-01-11 12:44:48 +0100137 -- Clear pending hot-plug events before every try
138 Port_Detect.Clear_Hotplug_Detect (Pipe_Cfg.Port);
139
Nico Huber43370ba2017-01-09 15:26:19 +0100140 Connectors.Pre_On
141 (Pipe => Pipe,
142 Port_Cfg => Port_Cfg,
143 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
144 Success => Success);
145
146 if Success then
147 Display_Controller.On
148 (Pipe => Pipe,
149 Port_Cfg => Port_Cfg,
150 Framebuffer => Pipe_Cfg.Framebuffer);
151
152 Connectors.Post_On
153 (Port_Cfg => Port_Cfg,
154 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
155 Success => Success);
156
157 if not Success then
158 Display_Controller.Off (Pipe);
159 Connectors.Post_Off (Port_Cfg);
160 end if;
161 end if;
162
163 exit when Success;
164 end loop;
165 exit when Success; -- connection established => stop loop
166
167 -- connection failed
168 PLLs.Free (Allocated_PLLs (Pipe));
169 end if;
170
171 Connector_Info.Next_Link_Setting (Port_Cfg, Success);
172 end loop;
173
174 if Success then
175 pragma Debug (Debug.Put_Line
176 ("Enabled port " & Port_Names (Pipe_Cfg.Port)));
177 else
178 Wait_For_HPD (Pipe_Cfg.Port) := True;
179 if Pipe_Cfg.Port = Internal then
180 Panel.Off;
181 end if;
182 end if;
183 end Enable_Output;
184
Nico Huber3be61d42017-01-09 13:58:18 +0100185 procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
186 is
187 Port_Cfg : Port_Config;
188 Success : Boolean;
189 begin
190 Config_Helpers.Fill_Port_Config
191 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
192 if Success then
193 pragma Debug (Debug.New_Line);
194 pragma Debug (Debug.Put_Line
195 ("Disabling port " & Port_Names (Pipe_Cfg.Port)));
196 pragma Debug (Debug.New_Line);
197
198 Connectors.Pre_Off (Port_Cfg);
199 Display_Controller.Off (Pipe);
200 Connectors.Post_Off (Port_Cfg);
201
202 PLLs.Free (Allocated_PLLs (Pipe));
203 end if;
204 end Disable_Output;
205
Nico Huber99f10f32016-11-20 00:34:05 +0100206 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200207 is
Nico Huber3be61d42017-01-09 13:58:18 +0100208 procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
209 is
210 HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
211 begin
212 if HPD_Delay_Over then
213 Port_Detect.Hotplug_Detect (Port, Detected);
214 HPD_Delay (Port) := Time.MS_From_Now (333);
215 else
216 Detected := False;
217 end if;
218 end Check_HPD;
Nico Huberb56b9c52017-01-11 15:12:23 +0100219
Nico Huber564103f2017-01-11 15:33:07 +0100220 Power_Changed : Boolean := False;
Nico Huberb56b9c52017-01-11 15:12:23 +0100221 Old_Configs : Pipe_Configs;
Nico Huber564103f2017-01-11 15:33:07 +0100222
223 -- Only called when we actually tried to change something
224 -- so we don't congest the log with unnecessary messages.
225 procedure Update_Power
226 is
227 begin
228 if not Power_Changed then
229 Power_And_Clocks.Power_Up (Old_Configs, Configs);
230 Power_Changed := True;
231 end if;
232 end Update_Power;
Nico Huber83693c82016-10-08 22:17:55 +0200233 begin
234 Old_Configs := Cur_Configs;
235
Nico Huberb56b9c52017-01-11 15:12:23 +0100236 -- disable all pipes that changed or had a hot-plug event
237 for Pipe in Pipe_Index loop
238 declare
239 Unplug_Detected : Boolean;
240 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
241 New_Config : Pipe_Config renames Configs (Pipe);
242 begin
243 if Cur_Config.Port /= Disabled then
244 Check_HPD (Cur_Config.Port, Unplug_Detected);
Nico Huber83693c82016-10-08 22:17:55 +0200245
Nico Huberb56b9c52017-01-11 15:12:23 +0100246 if Cur_Config.Port /= New_Config.Port or
247 Cur_Config.Mode /= New_Config.Mode or
248 Unplug_Detected
249 then
250 Disable_Output (Pipe, Cur_Config);
251 Cur_Config.Port := Disabled;
Nico Huber564103f2017-01-11 15:33:07 +0100252 Update_Power;
Nico Huberb56b9c52017-01-11 15:12:23 +0100253 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200254 end if;
Nico Huberb56b9c52017-01-11 15:12:23 +0100255 end;
256 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200257
Nico Huberb56b9c52017-01-11 15:12:23 +0100258 -- enable all pipes that changed and should be active
259 for Pipe in Pipe_Index loop
260 declare
261 Success : Boolean;
262 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
263 New_Config : Pipe_Config renames Configs (Pipe);
264 begin
265 if New_Config.Port /= Disabled and then
266 (Cur_Config.Port /= New_Config.Port or
267 Cur_Config.Mode /= New_Config.Mode)
268 then
Nico Huber3be61d42017-01-09 13:58:18 +0100269 if Wait_For_HPD (New_Config.Port) then
270 Check_HPD (New_Config.Port, Success);
271 Wait_For_HPD (New_Config.Port) := not Success;
272 else
273 Success := True;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100274 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100275
Nico Huber3be61d42017-01-09 13:58:18 +0100276 if Success then
Nico Huber564103f2017-01-11 15:33:07 +0100277 Update_Power;
Nico Huberb56b9c52017-01-11 15:12:23 +0100278 Enable_Output (Pipe, New_Config, Success);
Nico Huber3be61d42017-01-09 13:58:18 +0100279 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200280
281 if Success then
Nico Huberb56b9c52017-01-11 15:12:23 +0100282 Cur_Config := New_Config;
Nico Huber83693c82016-10-08 22:17:55 +0200283 end if;
Nico Huber3be61d42017-01-09 13:58:18 +0100284
Nico Huberb56b9c52017-01-11 15:12:23 +0100285 -- update framebuffer offset only
286 elsif New_Config.Port /= Disabled and
287 Cur_Config.Framebuffer /= New_Config.Framebuffer
288 then
289 Display_Controller.Update_Offset (Pipe, New_Config.Framebuffer);
290 Cur_Config := New_Config;
291 end if;
292 end;
Nico Huber83693c82016-10-08 22:17:55 +0200293 end loop;
294
Nico Huber564103f2017-01-11 15:33:07 +0100295 if Power_Changed then
Nico Huber83693c82016-10-08 22:17:55 +0200296 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
297 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200298 end Update_Outputs;
299
300 ----------------------------------------------------------------------------
301
302 procedure Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200303 (Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100304 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200305 Success : out Boolean)
306 with
307 Refined_Global =>
308 (In_Out =>
Nico Hubere015e822017-08-25 20:12:09 +0200309 (Config.Valid_Port_GPU, Dev.PCI_State,
Nico Huber83693c82016-10-08 22:17:55 +0200310 Registers.Register_State, Port_IO.State),
311 Input =>
312 (Time.State),
313 Output =>
Nico Huber2b6f6992017-07-09 18:11:34 +0200314 (Dev.Address_State,
315 Registers.Address_State,
Nico Huber83693c82016-10-08 22:17:55 +0200316 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +0100317 Cur_Configs, Allocated_PLLs,
Nico Huberc3f66f62017-07-16 21:39:54 +0200318 HPD_Delay, Wait_For_HPD,
319 Linear_FB_Base, Initialized))
Nico Huber83693c82016-10-08 22:17:55 +0200320 is
321 use type HW.Word64;
322
Nico Huber2b6f6992017-07-09 18:11:34 +0200323 PCI_MMIO_Base, PCI_GTT_Base : Word64;
324
Nico Huber83693c82016-10-08 22:17:55 +0200325 Now : constant Time.T := Time.Now;
326
327 procedure Check_Platform (Success : out Boolean)
328 is
329 Audio_VID_DID : Word32;
330 begin
331 case Config.CPU is
332 when Haswell .. Skylake =>
333 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
334 when Ironlake .. Ivybridge =>
335 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
336 end case;
337 Success :=
338 (case Config.CPU is
Nico Huber21da5742017-01-20 14:00:53 +0100339 when Broxton => Audio_VID_DID = 16#8086_280a#,
Nico Huber83693c82016-10-08 22:17:55 +0200340 when Skylake => Audio_VID_DID = 16#8086_2809#,
341 when Broadwell => Audio_VID_DID = 16#8086_2808#,
342 when Haswell => Audio_VID_DID = 16#8086_2807#,
343 when Ivybridge |
344 Sandybridge => Audio_VID_DID = 16#8086_2806# or
345 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200346 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200347 end Check_Platform;
348 begin
Nico Huber83693c82016-10-08 22:17:55 +0200349 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
350
351 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
352
Nico Huberc3f66f62017-07-16 21:39:54 +0200353 Linear_FB_Base := 0;
Nico Huber83693c82016-10-08 22:17:55 +0200354 Wait_For_HPD := HPD_Type'(others => False);
355 HPD_Delay := HPD_Delay_Type'(others => Now);
Nico Huber83693c82016-10-08 22:17:55 +0200356 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100357 Cur_Configs := Pipe_Configs'
358 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200359 (Port => Disabled,
360 Framebuffer => HW.GFX.Default_FB,
361 Mode => HW.GFX.Invalid_Mode));
Nico Huber83693c82016-10-08 22:17:55 +0200362 PLLs.Initialize;
363
Nico Huber2b6f6992017-07-09 18:11:34 +0200364 Dev.Initialize (Success);
365
366 if Success then
367 Dev.Map (PCI_MMIO_Base, PCI.Res0, Length => Config.GTT_Offset);
368 Dev.Map (PCI_GTT_Base, PCI.Res0, Offset => Config.GTT_Offset);
369 if PCI_MMIO_Base /= 0 and PCI_GTT_Base /= 0 then
370 Registers.Set_Register_Base (PCI_MMIO_Base, PCI_GTT_Base);
371 else
372 pragma Debug (Debug.Put_Line
373 ("ERROR: Couldn't map resoure0."));
374 Registers.Set_Register_Base (Config.Default_MMIO_Base);
375 Success := Config.Default_MMIO_Base_Set;
376 end if;
377 else
378 pragma Debug (Debug.Put_Line
379 ("WARNING: Couldn't initialize PCI dev."));
380 Registers.Set_Register_Base (Config.Default_MMIO_Base);
381 Success := Config.Default_MMIO_Base_Set;
382 end if;
383
384 if Success then
385 Check_Platform (Success);
386 end if;
387
Nico Huber83693c82016-10-08 22:17:55 +0200388 if not Success then
389 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
390
391 Panel.Static_Init; -- for flow analysis
392
393 Initialized := False;
394 return;
395 end if;
396
397 Panel.Setup_PP_Sequencer;
398 Port_Detect.Initialize;
Nico Huber0923b792017-06-09 15:28:41 +0200399 Connectors.Initialize;
Nico Huber83693c82016-10-08 22:17:55 +0200400
Nico Huber793a8d42016-11-21 18:57:03 +0100401 if Clean_State then
402 Power_And_Clocks.Pre_All_Off;
403 Connectors.Pre_All_Off;
404 Display_Controller.All_Off;
405 Connectors.Post_All_Off;
406 PLLs.All_Off;
407 Power_And_Clocks.Post_All_Off;
Nico Huber17d64b62017-07-15 20:51:25 +0200408 Registers.Clear_Fences;
Nico Huber33912aa2016-12-06 20:36:23 +0100409 else
410 -- According to PRMs, VGA plane is the only thing
Nico Huber3a0e2a02017-07-19 14:41:46 +0200411 -- that's enabled by default after reset...
Nico Huber33912aa2016-12-06 20:36:23 +0100412 Display_Controller.Legacy_VGA_Off;
Nico Huber3a0e2a02017-07-19 14:41:46 +0200413 -- ... along with some DDI port bits since Skylake.
414 Connectors.Post_Reset_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100415 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200416
417 -------------------- Now restart from a clean state ---------------------
418 Power_And_Clocks.Initialize;
419
Nico Huber1c3b9282017-02-09 13:57:04 +0100420 if Config.Has_PCH then
421 Registers.Unset_And_Set_Mask
422 (Register => Registers.PCH_RAWCLK_FREQ,
423 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
424 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
425 end if;
Nico Huberf54d0962016-10-20 14:17:18 +0200426
Nico Huber83693c82016-10-08 22:17:55 +0200427 Initialized := True;
428
429 end Initialize;
430
431 function Is_Initialized return Boolean
432 with
433 Refined_Post => Is_Initialized'Result = Initialized
434 is
435 begin
436 return Initialized;
437 end Is_Initialized;
438
439 ----------------------------------------------------------------------------
440
Nico Huber42fb2d02017-09-01 17:01:51 +0200441 procedure Power_Up_VGA
442 is
443 Fake_Config : constant Pipe_Configs :=
444 (Primary =>
445 (Port => Analog,
446 Framebuffer => HW.GFX.Default_FB,
447 Mode => HW.GFX.Invalid_Mode),
448 others =>
449 (Port => Disabled,
450 Framebuffer => HW.GFX.Default_FB,
451 Mode => HW.GFX.Invalid_Mode));
452 begin
453 Power_And_Clocks.Power_Up (Cur_Configs, Fake_Config);
454 end Power_Up_VGA;
455
456 ----------------------------------------------------------------------------
457
Nico Huber5374c3a2017-07-15 21:48:06 +0200458 function FB_First_Page (FB : Framebuffer_Type) return Natural is
459 (Natural (FB.Offset / GTT_Page_Size));
460 function FB_Pages (FB : Framebuffer_Type) return Natural is
461 (Natural (Div_Round_Up (FB_Size (FB), GTT_Page_Size)));
462 function FB_Last_Page (FB : Framebuffer_Type) return Natural is
463 (FB_First_Page (FB) + FB_Pages (FB) - 1);
464
465 -- Check basics and that it fits in GTT
466 function Valid_FB (FB : Framebuffer_Type) return Boolean is
467 (FB.Width <= FB.Stride and FB_Last_Page (FB) <= GTT_Range'Last);
468
469 -- Also check that we don't overflow the GTT's 39-bit space
470 -- (always true with a 32-bit base)
471 function Valid_Phys_FB (FB : Framebuffer_Type; Phys_Base : Word32)
472 return Boolean is
473 (Valid_FB (FB) and
474 Int64 (Phys_Base) + Int64 (FB.Offset) + Int64 (FB_Size (FB)) <=
475 Int64 (GTT_Address_Type'Last))
476 with
477 Ghost;
478
Nico Huber83693c82016-10-08 22:17:55 +0200479 procedure Write_GTT
480 (GTT_Page : GTT_Range;
481 Device_Address : GTT_Address_Type;
Nico Huber5374c3a2017-07-15 21:48:06 +0200482 Valid : Boolean)
483 is
Nico Huber83693c82016-10-08 22:17:55 +0200484 begin
485 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
486 end Write_GTT;
487
Nico Huber194e57e2017-07-15 21:15:46 +0200488 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
Nico Huber5374c3a2017-07-15 21:48:06 +0200489 with
490 Pre => Is_Initialized and Valid_Phys_FB (FB, Phys_Base)
Nico Huber83693c82016-10-08 22:17:55 +0200491 is
Nico Huber194e57e2017-07-15 21:15:46 +0200492 Phys_Addr : GTT_Address_Type :=
493 GTT_Address_Type (Phys_Base) + GTT_Address_Type (FB.Offset);
Nico Huber83693c82016-10-08 22:17:55 +0200494 begin
Nico Huber194e57e2017-07-15 21:15:46 +0200495 for Idx in FB_First_Page (FB) .. FB_Last_Page (FB) loop
Nico Huber83693c82016-10-08 22:17:55 +0200496 Registers.Write_GTT
497 (GTT_Page => Idx,
498 Device_Address => Phys_Addr,
499 Valid => True);
Nico Huber194e57e2017-07-15 21:15:46 +0200500 Phys_Addr := Phys_Addr + GTT_Page_Size;
Nico Huber83693c82016-10-08 22:17:55 +0200501 end loop;
502 end Setup_Default_GTT;
503
504 ----------------------------------------------------------------------------
505
Nico Hubereedde882017-07-16 02:54:39 +0200506 use type HW.Word16;
507 subtype Stolen_Size_Range is Int64 range 0 .. 2 ** 33;
508
509 function GGMS_Gen4 (GGC : Word16) return Natural is
510 (Natural (Shift_Right (GGC, 8) and 16#07#));
511 function GTT_Size_Gen4 (GGC : Word16) return Natural is
512 (if GGMS_Gen4 (GGC) in 1 .. 3 then
513 (GGMS_Gen4 (GGC) + 1) * 2 ** 19 else 0);
514
515 function GMS_Gen4 (GGC : Word16) return Natural is
516 (Natural (Shift_Right (GGC, 4) and 16#0f#));
517 Valid_Stolen_Size_Gen4 : constant
518 array (Natural range 1 .. 13) of Stolen_Size_Range :=
519 (1, 4, 8, 16, 32, 48, 64, 128, 256, 96, 160, 224, 352);
520 function Stolen_Size_Gen4 (GGC : Word16) return Stolen_Size_Range is
521 (if GMS_Gen4 (GGC) in Valid_Stolen_Size_Gen4'Range then
Arthur Heymans5fd9a312017-09-12 12:45:18 +0200522 Valid_Stolen_Size_Gen4 (GMS_Gen4 (GGC)) * 2 ** 20 else 0);
Nico Hubereedde882017-07-16 02:54:39 +0200523
524 function GTT_Size_Gen6 (GGC : Word16) return Natural is
525 (Natural (Shift_Right (GGC, 8) and 16#03#) * 2 ** 20);
526
527 function Stolen_Size_Gen6 (GGC : Word16) return Stolen_Size_Range is
528 (Stolen_Size_Range (Shift_Right (GGC, 3) and 16#1f#) * 32 * 2 ** 20);
529
530 function GTT_Size_Gen8 (GGC : Word16) return Natural is
531 (Natural (Shift_Right (GGC, 6) and 16#03#) * 2 ** 20);
532
533 function GMS_Gen8 (GGC : Word16) return Stolen_Size_Range is
534 (Stolen_Size_Range (Shift_Right (GGC, 8) and 16#ff#));
535 function Stolen_Size_Gen8 (GGC : Word16) return Stolen_Size_Range is
536 (GMS_Gen8 (GGC) * 32 * 2 ** 20);
537
538 function Stolen_Size_Gen9 (GGC : Word16) return Stolen_Size_Range is
539 (if GMS_Gen8 (GGC) < 16#f0# then
540 Stolen_Size_Gen8 (GGC)
541 else
542 (GMS_Gen8 (GGC) - 16#f0# + 1) * 4 * 2 ** 20);
543
544 procedure Decode_Stolen
545 (GTT_Size : out Natural;
546 Stolen_Size : out Stolen_Size_Range)
547 with
548 Pre => Is_Initialized
549 is
550 GGC_Reg : constant :=
551 (case Config.CPU is
552 when Ironlake => 16#52#,
553 when Sandybridge .. Skylake => 16#50#);
554 GGC : Word16;
555 begin
556 Dev.Read16 (GGC, GGC_Reg);
557 case Config.CPU is
558 when Ironlake =>
559 GTT_Size := GTT_Size_Gen4 (GGC);
560 Stolen_Size := Stolen_Size_Gen4 (GGC);
561 when Sandybridge .. Haswell =>
562 GTT_Size := GTT_Size_Gen6 (GGC);
563 Stolen_Size := Stolen_Size_Gen6 (GGC);
564 when Broadwell =>
565 GTT_Size := GTT_Size_Gen8 (GGC);
566 Stolen_Size := Stolen_Size_Gen8 (GGC);
567 when Broxton .. Skylake =>
568 GTT_Size := GTT_Size_Gen8 (GGC);
569 Stolen_Size := Stolen_Size_Gen9 (GGC);
570 end case;
571 end Decode_Stolen;
572
573 -- Additional runtime validation that FB fits stolen memory and aperture.
574 procedure Validate_FB (FB : Framebuffer_Type; Valid : out Boolean)
575 with
576 Pre => Is_Initialized,
577 Post => (if Valid then Valid_FB (FB))
578 is
579 GTT_Size, Aperture_Size : Natural;
580 Stolen_Size : Stolen_Size_Range;
581 begin
582 Valid := Valid_FB (FB);
583
584 if Valid then
585 Decode_Stolen (GTT_Size, Stolen_Size);
586 Dev.Resource_Size (Aperture_Size, PCI.Res2);
587 Valid :=
588 FB_Last_Page (FB) < GTT_Size / Config.GTT_PTE_Size and
589 FB_Last_Page (FB) < Natural (Stolen_Size / GTT_Page_Size) and
590 FB_Last_Page (FB) < Aperture_Size / GTT_Page_Size;
591 pragma Debug (not Valid, Debug.Put
592 ("Stolen memory too small to hold framebuffer."));
593 end if;
594 end Validate_FB;
595
Nico Huber5374c3a2017-07-15 21:48:06 +0200596 procedure Setup_Default_FB
597 (FB : in Framebuffer_Type;
598 Clear : in Boolean := True;
599 Success : out Boolean)
600 is
601 GMA_Phys_Base : constant PCI.Index := 16#5c#;
602 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
603
604 Phys_Base : Word32;
605 begin
Nico Hubereedde882017-07-16 02:54:39 +0200606 Validate_FB (FB, Success);
Nico Huber5374c3a2017-07-15 21:48:06 +0200607
608 if Success then
609 Dev.Read32 (Phys_Base, GMA_Phys_Base);
610 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
611 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
612 pragma Debug (not Success, Debug.Put_Line
613 ("Failed to read stolen memory base."));
614 if Success then
615 Setup_Default_GTT (FB, Phys_Base);
616 end if;
617 end if;
618
619 if Success and then Clear then
620 declare
621 use type HW.Word64;
622 Linear_FB : Word64;
623 begin
Nico Huberc3f66f62017-07-16 21:39:54 +0200624 Map_Linear_FB (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +0200625 if Linear_FB /= 0 then
Nico Huberc3f66f62017-07-16 21:39:54 +0200626 Framebuffer_Filler.Fill (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +0200627 end if;
Nico Huber5374c3a2017-07-15 21:48:06 +0200628 end;
629 end if;
630 end Setup_Default_FB;
631
Nico Huberc3f66f62017-07-16 21:39:54 +0200632 procedure Map_Linear_FB (Linear_FB : out Word64; FB : in Framebuffer_Type)
633 is
634 use type HW.Word64;
635
636 Valid : Boolean;
637 begin
638 Linear_FB := 0;
639
640 if Linear_FB_Base = 0 then
641 Dev.Map (Linear_FB_Base, PCI.Res2);
642 pragma Debug
643 (Linear_FB_Base = 0, Debug.Put_Line ("Failed to map resource2."));
644 end if;
645
646 if Linear_FB_Base /= 0 then
647 Validate_FB (FB, Valid);
648 if Valid then
649 Linear_FB := Linear_FB_Base + Word64 (FB.Offset);
650 end if;
651 end if;
652 end Map_Linear_FB;
653
Nico Huber5374c3a2017-07-15 21:48:06 +0200654 ----------------------------------------------------------------------------
655
Nico Huber99f10f32016-11-20 00:34:05 +0100656 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200657 is
658 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100659 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200660 Pipe_Names : constant Pipe_Name_Array :=
661 (Primary => "Primary ",
662 Secondary => "Secondary",
663 Tertiary => "Tertiary ");
664 begin
665 Debug.New_Line;
Paul Menzelb83107c2017-05-04 09:02:33 +0200666 Debug.Put_Line ("CONFIG =>");
Nico Huber99f10f32016-11-20 00:34:05 +0100667 for Pipe in Pipe_Index loop
668 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200669 Debug.Put (" (");
670 else
671 Debug.Put (" ");
672 end if;
673 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
674 Debug.Put_Line
675 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
676 Debug.Put_Line (" Framebuffer =>");
677 Debug.Put (" (Width => ");
678 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
679 Debug.Put_Line (",");
680 Debug.Put (" Height => ");
681 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
682 Debug.Put_Line (",");
683 Debug.Put (" Stride => ");
684 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
685 Debug.Put_Line (",");
686 Debug.Put (" Offset => ");
687 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
688 Debug.Put_Line (",");
689 Debug.Put (" BPC => ");
690 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
691 Debug.Put_Line ("),");
692 Debug.Put_Line (" Mode =>");
693 Debug.Put (" (Dotclock => ");
694 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
695 Debug.Put_Line (",");
696 Debug.Put (" H_Visible => ");
697 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
698 Debug.Put_Line (",");
699 Debug.Put (" H_Sync_Begin => ");
700 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
701 Debug.Put_Line (",");
702 Debug.Put (" H_Sync_End => ");
703 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
704 Debug.Put_Line (",");
705 Debug.Put (" H_Total => ");
706 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
707 Debug.Put_Line (",");
708 Debug.Put (" V_Visible => ");
709 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
710 Debug.Put_Line (",");
711 Debug.Put (" V_Sync_Begin => ");
712 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
713 Debug.Put_Line (",");
714 Debug.Put (" V_Sync_End => ");
715 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
716 Debug.Put_Line (",");
717 Debug.Put (" V_Total => ");
718 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
719 Debug.Put_Line (",");
720 Debug.Put_Line (" H_Sync_Active_High => " &
721 (if Configs (Pipe).Mode.H_Sync_Active_High
722 then "True,"
723 else "False,"));
724 Debug.Put_Line (" V_Sync_Active_High => " &
725 (if Configs (Pipe).Mode.V_Sync_Active_High
726 then "True,"
727 else "False,"));
728 Debug.Put (" BPC => ");
729 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100730 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200731 Debug.Put_Line (")),");
732 else
733 Debug.Put_Line (")));");
734 end if;
735 end loop;
736 end Dump_Configs;
737
738end HW.GFX.GMA;