blob: 449f7b36502a8cb7be1a8aa3f3426a4e777f94b3 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber9a4c4c32019-09-16 22:05:11 +02002-- Copyright (C) 2014-2019 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 Huber83693c82016-10-08 22:17:55 +020021with HW.GFX.GMA.Config;
Nico Huber8c45bcf2016-11-20 17:30:57 +010022with HW.GFX.GMA.Config_Helpers;
Nico Huber83693c82016-10-08 22:17:55 +020023with HW.GFX.GMA.Registers;
Nico Huber312433c2019-09-28 03:15:48 +020024with HW.GFX.GMA.PCode;
Nico Huber83693c82016-10-08 22:17:55 +020025with HW.GFX.GMA.Power_And_Clocks;
26with HW.GFX.GMA.Panel;
27with HW.GFX.GMA.PLLs;
28with HW.GFX.GMA.Port_Detect;
29with HW.GFX.GMA.Connectors;
30with HW.GFX.GMA.Connector_Info;
31with HW.GFX.GMA.Pipe_Setup;
32
Nico Huber83693c82016-10-08 22:17:55 +020033with HW.Debug;
34with GNAT.Source_Info;
35
Nico Huber83693c82016-10-08 22:17:55 +020036
37package body HW.GFX.GMA
38 with Refined_State =>
39 (State =>
Nico Hubere317e9c2019-09-29 03:03:18 +020040 (Config.Variable,
Nico Huberc5c66ec2019-09-28 23:59:45 +020041 Dev.Address_State,
Nico Huber2b6f6992017-07-09 18:11:34 +020042 Registers.Address_State,
Nico Huber312433c2019-09-28 03:15:48 +020043 PCode.Mailbox_Ready,
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,
Nico Huber83693c82016-10-08 22:17:55 +020049 Device_State =>
Nico Huber2b6f6992017-07-09 18:11:34 +020050 (Dev.PCI_State, Registers.Register_State, Registers.GTT_State))
Nico Huber83693c82016-10-08 22:17:55 +020051is
Nico Huber2b6f6992017-07-09 18:11:34 +020052 pragma Disable_Atomic_Synchronization;
Nico Huber83693c82016-10-08 22:17:55 +020053
Nico Huberad096092024-07-02 18:45:44 +020054 subtype Port_Name is String (1 .. 8);
Nico Huber83693c82016-10-08 22:17:55 +020055 type Port_Name_Array is array (Port_Type) of Port_Name;
56 Port_Names : constant Port_Name_Array :=
Nico Huberad096092024-07-02 18:45:44 +020057 (Disabled => "Disabled",
58 LVDS => "LVDS ",
59 eDP => "eDP ",
60 DP1 => "DP1 ",
61 DP2 => "DP2 ",
62 DP3 => "DP3 ",
63 DP_TC1 => "DP_TC1 ",
64 DP_TC2 => "DP_TC2 ",
65 DP_TC3 => "DP_TC3 ",
66 DP_TC4 => "DP_TC4 ",
67 HDMI1 => "HDMI1 ",
68 HDMI2 => "HDMI2 ",
69 HDMI3 => "HDMI3 ",
70 HDMI_TC1 => "HDMI_TC1",
71 HDMI_TC2 => "HDMI_TC2",
72 HDMI_TC3 => "HDMI_TC3",
73 HDMI_TC4 => "HDMI_TC4",
74 Analog => "Analog ",
75 USBC1 => "USBC1 ",
76 USBC2 => "USBC2 ",
77 USBC3 => "USBC3 ",
78 USBC4 => "USBC4 ");
Nico Huber83693c82016-10-08 22:17:55 +020079
Nico Huber4cafa4a2020-06-10 16:14:36 +020080 package Dev is new HW.PCI.Dev
81 (Dev => Config.PCI_Dev,
82 MMConf_Base => Config.Override_MMConf_Base);
Nico Huber2b6f6992017-07-09 18:11:34 +020083
Nico Huber83693c82016-10-08 22:17:55 +020084 package Display_Controller renames Pipe_Setup;
85
Nico Huber99f10f32016-11-20 00:34:05 +010086 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020087
Nico Huber83693c82016-10-08 22:17:55 +020088 type HPD_Type is array (Port_Type) of Boolean;
Nico Huber3be61d42017-01-09 13:58:18 +010089 type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
Nico Huber83693c82016-10-08 22:17:55 +020090
Nico Huber83693c82016-10-08 22:17:55 +020091 Allocated_PLLs : PLLs_Type;
Nico Huber83693c82016-10-08 22:17:55 +020092 HPD_Delay : HPD_Delay_Type;
93 Wait_For_HPD : HPD_Type;
94 Initialized : Boolean := False;
95
Nico Huberc3f66f62017-07-16 21:39:54 +020096 Linear_FB_Base : Word64;
97
Nico Huber83693c82016-10-08 22:17:55 +020098 ----------------------------------------------------------------------------
99
Tim Wawrzynczakfc49b602022-09-09 10:29:24 -0600100 ICP_RAWCLK_NUM : constant := 1 * 2 ** 11;
101
102 function PCH_RAWCLK_FREQ_MASK return Word32 is
103 Mask : Word32;
104 begin
105 if Config.Need_Rawclk_Numerator then
106 Mask := 16#ffff_ffff#;
107 elsif Config.Has_Fractional_RawClk then
108 Mask := 16#3fff# * 2 ** 16;
109 else
110 Mask := 16#3ff# * 2 ** 0;
111 end if;
112 return Mask;
113 end PCH_RAWCLK_FREQ_MASK;
Nico Huberf54d0962016-10-20 14:17:18 +0200114
115 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
116 is
117 begin
Nico Huberdde06302020-12-20 02:18:30 +0100118 if Config.Has_Fractional_RawClk then
119 declare
120 Fraction_K : constant Int64 := Freq / 1_000 mod 1_000;
121 Freq32 : Word32 := Shift_Left (Word32 (Freq / 1_000_000), 16);
122 begin
123 if Fraction_K /= 0 then
124 Freq32 := Freq32 or Shift_Left
125 (Word32 (Div_Round_Closest (1_000, Fraction_K) - 1), 26);
126 end if;
Tim Wawrzynczakfc49b602022-09-09 10:29:24 -0600127
128 if Config.Need_Rawclk_Numerator then
129 Freq32 := Freq32 or ICP_RAWCLK_NUM;
130 end if;
Nico Huberdde06302020-12-20 02:18:30 +0100131 return Freq32;
132 end;
133 else
134 return Word32 (Freq / 1_000_000);
135 end if;
Nico Huberf54d0962016-10-20 14:17:18 +0200136 end PCH_RAWCLK_FREQ;
137
138 ----------------------------------------------------------------------------
139
Nico Huber43370ba2017-01-09 15:26:19 +0100140 procedure Enable_Output
141 (Pipe : in Pipe_Index;
142 Pipe_Cfg : in Pipe_Config;
143 Success : out Boolean)
Nico Huber8a5a3b52018-06-04 14:42:13 +0200144 with
Nico Huber9a4c4c32019-09-16 22:05:11 +0200145 Pre =>
146 Pipe_Cfg.Port in Active_Port_Type and
147 Config_Helpers.Valid_FB (Pipe_Cfg.Framebuffer, Pipe_Cfg.Mode)
Nico Huber43370ba2017-01-09 15:26:19 +0100148 is
149 Port_Cfg : Port_Config;
150 begin
Nico Huber3be61d42017-01-09 13:58:18 +0100151 pragma Debug (Debug.New_Line);
152 pragma Debug (Debug.Put_Line
153 ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
154
Nico Huber43370ba2017-01-09 15:26:19 +0100155 Config_Helpers.Fill_Port_Config
156 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
157
158 if Success then
Nico Huber41e86742024-07-17 17:10:28 +0200159 Power_And_Clocks.Power_Up (Pipe_Cfg.Port, Success);
160 end if;
161
162 if Success then
Nico Huber43370ba2017-01-09 15:26:19 +0100163 Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
164 end if;
165
Nico Hubera8254482024-07-03 12:23:00 +0200166 if Success then
167 Connectors.Prepare (Pipe_Cfg.Port, Port_Cfg, Success);
168 end if;
169
Nico Huber43370ba2017-01-09 15:26:19 +0100170 -- loop over all possible DP-lane configurations
171 -- (non-DP ports use a single fake configuration)
172 while Success loop
173 pragma Loop_Invariant
174 (Pipe_Cfg.Port in Active_Port_Type and
175 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
176
177 PLLs.Alloc
178 (Port_Cfg => Port_Cfg,
179 PLL => Allocated_PLLs (Pipe),
180 Success => Success);
181
182 if Success then
183 -- try each DP-lane configuration twice
184 for Try in 1 .. 2 loop
185 pragma Loop_Invariant
186 (Pipe_Cfg.Port in Active_Port_Type);
187
Nico Huber4798c662017-01-11 12:44:48 +0100188 -- Clear pending hot-plug events before every try
189 Port_Detect.Clear_Hotplug_Detect (Pipe_Cfg.Port);
190
Nico Huber43370ba2017-01-09 15:26:19 +0100191 Connectors.Pre_On
192 (Pipe => Pipe,
193 Port_Cfg => Port_Cfg,
194 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
195 Success => Success);
196
197 if Success then
198 Display_Controller.On
199 (Pipe => Pipe,
Nico Huber4d592b42026-06-23 12:21:37 +0000200 Pipe_Cfg => Pipe_Cfg,
201 Port_Cfg => Port_Cfg);
Nico Huber43370ba2017-01-09 15:26:19 +0100202
203 Connectors.Post_On
Arthur Heymans60d0e5f2018-03-28 17:08:27 +0200204 (Pipe => Pipe,
205 Port_Cfg => Port_Cfg,
Nico Huber43370ba2017-01-09 15:26:19 +0100206 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
207 Success => Success);
208
209 if not Success then
210 Display_Controller.Off (Pipe);
Nico Huberbfea6a32024-03-07 15:22:36 +0000211 Connectors.Post_Off (Pipe, Port_Cfg);
Nico Huber43370ba2017-01-09 15:26:19 +0100212 end if;
213 end if;
214
215 exit when Success;
216 end loop;
217 exit when Success; -- connection established => stop loop
218
219 -- connection failed
220 PLLs.Free (Allocated_PLLs (Pipe));
221 end if;
222
223 Connector_Info.Next_Link_Setting (Port_Cfg, Success);
Nico Hubera8254482024-07-03 12:23:00 +0200224 exit when not Success;
225
226 Connectors.Prepare (Pipe_Cfg.Port, Port_Cfg, Success);
Nico Huber43370ba2017-01-09 15:26:19 +0100227 end loop;
228
229 if Success then
230 pragma Debug (Debug.Put_Line
231 ("Enabled port " & Port_Names (Pipe_Cfg.Port)));
232 else
233 Wait_For_HPD (Pipe_Cfg.Port) := True;
Nico Huber2bbd6e72020-01-07 18:22:59 +0100234 Panel.Off (Config_Helpers.To_Panel (Pipe_Cfg.Port));
Nico Huber43370ba2017-01-09 15:26:19 +0100235 end if;
236 end Enable_Output;
237
Nico Huber3be61d42017-01-09 13:58:18 +0100238 procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
239 is
240 Port_Cfg : Port_Config;
241 Success : Boolean;
242 begin
243 Config_Helpers.Fill_Port_Config
244 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
245 if Success then
246 pragma Debug (Debug.New_Line);
247 pragma Debug (Debug.Put_Line
248 ("Disabling port " & Port_Names (Pipe_Cfg.Port)));
249 pragma Debug (Debug.New_Line);
250
Jeremy Compostellafe80fbb2023-01-11 14:05:42 -0700251 if Pipe_Cfg.Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET then
252 Display_Controller.Legacy_VGA_Off;
253 end if;
254
Nico Huberbfea6a32024-03-07 15:22:36 +0000255 Connectors.Pre_Off (Pipe, Port_Cfg);
Nico Huber3be61d42017-01-09 13:58:18 +0100256 Display_Controller.Off (Pipe);
Nico Huberbfea6a32024-03-07 15:22:36 +0000257 Connectors.Post_Off (Pipe, Port_Cfg);
Nico Huber3be61d42017-01-09 13:58:18 +0100258
259 PLLs.Free (Allocated_PLLs (Pipe));
260 end if;
261 end Disable_Output;
262
Nico Huber99f10f32016-11-20 00:34:05 +0100263 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200264 is
Nico Huber3be61d42017-01-09 13:58:18 +0100265 procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
266 is
267 HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
268 begin
269 if HPD_Delay_Over then
270 Port_Detect.Hotplug_Detect (Port, Detected);
271 HPD_Delay (Port) := Time.MS_From_Now (333);
272 else
273 Detected := False;
274 end if;
275 end Check_HPD;
Nico Huberb56b9c52017-01-11 15:12:23 +0100276
Nico Huber9a4c4c32019-09-16 22:05:11 +0200277 Scaler_Reservation : Display_Controller.Scaler_Reservation :=
278 Display_Controller.Null_Scaler_Reservation;
Nico Huber564103f2017-01-11 15:33:07 +0100279
Nico Huber9a4c4c32019-09-16 22:05:11 +0200280 Update_Power : Boolean := False;
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200281 Update_CDClk : Boolean;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200282 Old_Configs,
283 New_Configs : Pipe_Configs;
Nico Huber3d06de82018-05-29 01:35:04 +0200284
285 function Full_Update (Cur_Config, New_Config : Pipe_Config) return Boolean
286 is
287 begin
288 return
Nico Huber958c5642018-06-02 16:59:31 +0200289 Cur_Config.Port /= New_Config.Port
290 or else
291 Cur_Config.Mode /= New_Config.Mode
292 or else
Nico Huber3d06de82018-05-29 01:35:04 +0200293 (Config.Use_PDW_For_EDP_Scaling and then
Nico Huber8beafd72020-01-07 14:59:44 +0100294 (Cur_Config.Port = eDP and
Nico Huber958c5642018-06-02 16:59:31 +0200295 Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config)))
296 or else
297 (Config.Has_GMCH_PFIT_CONTROL and then
298 (Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config) or
299 Scaling_Type (Cur_Config) /= Scaling_Type (New_Config)));
Nico Huber3d06de82018-05-29 01:35:04 +0200300 end Full_Update;
Nico Huber83693c82016-10-08 22:17:55 +0200301 begin
302 Old_Configs := Cur_Configs;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200303 New_Configs := Configs;
304
305 -- validate new configs, filter invalid configs and those waiting for HPD
306 for Pipe in Pipe_Index loop
307 declare
308 Success : Boolean := True;
309 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
310 New_Config : Pipe_Config renames New_Configs (Pipe);
311 begin
312 if New_Config.Port /= Disabled then
313 if Wait_For_HPD (New_Config.Port) then
314 Check_HPD (New_Config.Port, Success);
315 Wait_For_HPD (New_Config.Port) := not Success;
316 end if;
317
318 Success := Success and then
319 Config_Helpers.Validate_Config
320 (New_Config.Framebuffer, New_Config.Mode, Pipe);
321
322 if Success and then Requires_Scaling (New_Config) then
323 Display_Controller.Reserve_Scaler
324 (Success, Scaler_Reservation, Pipe);
325 end if;
326
327 if not Success then
328 New_Config.Port := Disabled;
329 end if;
330 end if;
331 end;
332 pragma Loop_Invariant
333 (for all P in Pipe_Index'First .. Pipe =>
334 New_Configs (P).Port = Disabled or
335 Config_Helpers.Valid_FB
336 (New_Configs (P).Framebuffer, New_Configs (P).Mode));
337 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200338
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200339 -- limit dotclocks to maximum CDClk, if we are about
340 -- to switch CDClk, all pipes have to be disabled
341 Power_And_Clocks.Limit_Dotclocks (New_Configs, Update_CDClk);
342
Nico Huberb56b9c52017-01-11 15:12:23 +0100343 -- disable all pipes that changed or had a hot-plug event
344 for Pipe in Pipe_Index loop
345 declare
346 Unplug_Detected : Boolean;
347 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200348 New_Config : Pipe_Config renames New_Configs (Pipe);
Nico Huberb56b9c52017-01-11 15:12:23 +0100349 begin
350 if Cur_Config.Port /= Disabled then
351 Check_HPD (Cur_Config.Port, Unplug_Detected);
Nico Huber83693c82016-10-08 22:17:55 +0200352
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200353 if Update_CDClk or
354 Unplug_Detected or
355 Full_Update (Cur_Config, New_Config)
356 then
Nico Huberb56b9c52017-01-11 15:12:23 +0100357 Disable_Output (Pipe, Cur_Config);
358 Cur_Config.Port := Disabled;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200359 Update_Power := True;
Nico Huberb56b9c52017-01-11 15:12:23 +0100360 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200361 end if;
Nico Huberb56b9c52017-01-11 15:12:23 +0100362 end;
363 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200364
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200365 -- switch CDClk if necessary and possible, limit dotclocks accordingly
366 if Update_CDClk then
367 Power_And_Clocks.Update_CDClk (New_Configs);
368 end if;
369
Nico Huberb56b9c52017-01-11 15:12:23 +0100370 -- enable all pipes that changed and should be active
371 for Pipe in Pipe_Index loop
372 declare
373 Success : Boolean;
374 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200375 New_Config : Pipe_Config renames New_Configs (Pipe);
Nico Huberb56b9c52017-01-11 15:12:23 +0100376 begin
Nico Huber9a4c4c32019-09-16 22:05:11 +0200377 -- full update
Nico Huber3d06de82018-05-29 01:35:04 +0200378 if New_Config.Port /= Disabled and
379 Full_Update (Cur_Config, New_Config)
Nico Huberb56b9c52017-01-11 15:12:23 +0100380 then
Nico Huber9a4c4c32019-09-16 22:05:11 +0200381 Power_And_Clocks.Power_Up (Old_Configs, New_Configs);
382 Update_Power := True;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100383
Nico Huber9a4c4c32019-09-16 22:05:11 +0200384 Enable_Output (Pipe, New_Config, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200385 if Success then
Nico Huberb56b9c52017-01-11 15:12:23 +0100386 Cur_Config := New_Config;
Nico Huber83693c82016-10-08 22:17:55 +0200387 end if;
Nico Huber3be61d42017-01-09 13:58:18 +0100388
Nico Huberb56b9c52017-01-11 15:12:23 +0100389 -- update framebuffer offset only
390 elsif New_Config.Port /= Disabled and
Nico Huberf361ec82018-06-02 18:01:45 +0200391 Cur_Config.Framebuffer /= New_Config.Framebuffer
Nico Huberb56b9c52017-01-11 15:12:23 +0100392 then
Nico Huber9a4c4c32019-09-16 22:05:11 +0200393 Display_Controller.Setup_FB
Nico Huber4d592b42026-06-23 12:21:37 +0000394 (Pipe, New_Config, New_Config.Mode);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200395 Display_Controller.Update_Cursor
396 (Pipe, New_Config.Framebuffer, New_Config.Cursor);
397 Cur_Config := New_Config;
Nico Huberb56b9c52017-01-11 15:12:23 +0100398 end if;
399 end;
Nico Huber83693c82016-10-08 22:17:55 +0200400 end loop;
401
Nico Huber9a4c4c32019-09-16 22:05:11 +0200402 if Update_Power then
403 Power_And_Clocks.Power_Down (Old_Configs, New_Configs, Cur_Configs);
Nico Huber83693c82016-10-08 22:17:55 +0200404 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200405 end Update_Outputs;
406
407 ----------------------------------------------------------------------------
408
Nico Huber15ffc4f2018-01-11 14:44:43 +0100409 procedure Update_Cursor (Pipe : Pipe_Index; Cursor : Cursor_Type)
410 is
411 begin
412 Cur_Configs (Pipe).Cursor := Cursor;
413 Display_Controller.Update_Cursor
414 (Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
415 end Update_Cursor;
416
417 procedure Place_Cursor
418 (Pipe : Pipe_Index;
419 X : Cursor_Pos;
420 Y : Cursor_Pos)
421 is
422 begin
423 Cur_Configs (Pipe).Cursor.Center_X := X;
424 Cur_Configs (Pipe).Cursor.Center_Y := Y;
425 Display_Controller.Place_Cursor
426 (Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
427 end Place_Cursor;
428
429 procedure Move_Cursor
430 (Pipe : Pipe_Index;
431 X : Cursor_Pos;
432 Y : Cursor_Pos)
433 is
434 function Cap_Add (A, B : Cursor_Pos) return Cursor_Pos is
435 (if A + B < 0
436 then Int32'Max (Cursor_Pos'First, A + B)
437 else Int32'Min (Cursor_Pos'Last, A + B));
438 begin
439 Place_Cursor
440 (Pipe => Pipe,
441 X => Cap_Add (Cur_Configs (Pipe).Cursor.Center_X, X),
442 Y => Cap_Add (Cur_Configs (Pipe).Cursor.Center_Y, Y));
443 end Move_Cursor;
444
445 ----------------------------------------------------------------------------
446
Nico Huberbc0588e2020-07-21 12:17:19 +0200447 procedure Backlight_On (Port : Active_Port_Type)
448 with
449 Refined_Global => (In_Out => Registers.Register_State)
450 is
451 begin
452 Panel.Backlight_On (Config_Helpers.To_Panel (Port));
453 end Backlight_On;
454
455 procedure Backlight_Off (Port : Active_Port_Type)
456 with
457 Refined_Global => (In_Out => Registers.Register_State)
458 is
459 begin
460 Panel.Backlight_Off (Config_Helpers.To_Panel (Port));
461 end Backlight_Off;
462
463 procedure Set_Brightness (Port : Active_Port_Type; Level : Word32)
464 with
465 Refined_Global => (In_Out => Registers.Register_State)
466 is
467 begin
468 Panel.Set_Backlight (Config_Helpers.To_Panel (Port), Level);
469 end Set_Brightness;
470
471 procedure Get_Max_Brightness (Port : Active_Port_Type; Level : out Word32)
472 with
473 Refined_Global => (In_Out => Registers.Register_State)
474 is
475 begin
476 Panel.Get_Max_Backlight (Config_Helpers.To_Panel (Port), Level);
477 end Get_Max_Brightness;
478
479 ----------------------------------------------------------------------------
480
Nico Huber793f4f82022-09-04 14:24:00 +0000481 pragma Warnings
482 (GNATprove, Off, """Registers.GTT_State"" * is not modified*",
483 Reason => "The whole, abstract Device_State is modified in certain configurations.");
484 pragma Warnings
485 (GNATprove, Off, "no check message justified*", Reason => "see below");
Nico Huber83693c82016-10-08 22:17:55 +0200486 procedure Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200487 (Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100488 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200489 Success : out Boolean)
490 with
491 Refined_Global =>
Nico Huber27088aa2018-06-10 13:28:05 +0200492 (Input => (Time.State),
Nico Huber793f4f82022-09-04 14:24:00 +0000493 In_Out =>
494 (Dev.PCI_State, Port_IO.State,
495 Registers.Register_State, Registers.GTT_State),
Nico Huber83693c82016-10-08 22:17:55 +0200496 Output =>
Nico Huber4ba49112020-06-10 16:31:36 +0200497 (Config.Variable,
Nico Huber27088aa2018-06-10 13:28:05 +0200498 Dev.Address_State,
Nico Huber2b6f6992017-07-09 18:11:34 +0200499 Registers.Address_State,
Nico Huber312433c2019-09-28 03:15:48 +0200500 PCode.Mailbox_Ready,
Nico Huber83693c82016-10-08 22:17:55 +0200501 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +0100502 Cur_Configs, Allocated_PLLs,
Nico Huberc3f66f62017-07-16 21:39:54 +0200503 HPD_Delay, Wait_For_HPD,
504 Linear_FB_Base, Initialized))
Nico Huber83693c82016-10-08 22:17:55 +0200505 is
506 use type HW.Word64;
507
Nico Huber2b6f6992017-07-09 18:11:34 +0200508 PCI_MMIO_Base, PCI_GTT_Base : Word64;
509
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100510 function Default_GTT_Base return Word64 is
511 (Config.Default_MMIO_Base + Word64 (Config.MMIO_GTT_Offset));
512
Nico Huber83693c82016-10-08 22:17:55 +0200513 Now : constant Time.T := Time.Now;
514
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200515 procedure Check_Platform_PCI (Success : out Boolean)
516 is
517 use type HW.Word16;
518 Vendor, Device : Word16;
519 begin
520 Dev.Read16 (Vendor, PCI.Vendor_Id);
521 Dev.Read16 (Device, PCI.Device_Id);
522
Nico Huber6a996dc2018-06-17 16:30:33 +0200523 Config.Detect_CPU (Device);
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200524 Success := Vendor = 16#8086# and Config.Compatible_GPU (Device);
525 end Check_Platform_PCI;
Nico Huber83693c82016-10-08 22:17:55 +0200526 begin
Nico Huber83693c82016-10-08 22:17:55 +0200527 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
528
529 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
530
Nico Huberc3f66f62017-07-16 21:39:54 +0200531 Linear_FB_Base := 0;
Nico Huber312433c2019-09-28 03:15:48 +0200532 PCode.Mailbox_Ready := False;
Nico Huber83693c82016-10-08 22:17:55 +0200533 Wait_For_HPD := HPD_Type'(others => False);
534 HPD_Delay := HPD_Delay_Type'(others => Now);
Nico Huber83693c82016-10-08 22:17:55 +0200535 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100536 Cur_Configs := Pipe_Configs'
537 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200538 (Port => Disabled,
539 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000540 Alignment => Alignment'First,
541 Scaling => Scaling'First,
Nico Hubera02b2c62018-01-09 15:58:34 +0100542 Cursor => Default_Cursor,
Nico Huber83693c82016-10-08 22:17:55 +0200543 Mode => HW.GFX.Invalid_Mode));
Nico Huber27088aa2018-06-10 13:28:05 +0200544 Config.Variable := Config.Initial_Settings;
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100545 Registers.Set_Register_Base (Config.Default_MMIO_Base, Default_GTT_Base);
Nico Huber83693c82016-10-08 22:17:55 +0200546 PLLs.Initialize;
547
Nico Huber2b6f6992017-07-09 18:11:34 +0200548 Dev.Initialize (Success);
Nico Huber4ba49112020-06-10 16:31:36 +0200549 pragma Debug (not Success, Debug.Put_Line ("ERROR: Couldn't initialize PCI dev."));
Nico Huber2b6f6992017-07-09 18:11:34 +0200550
551 if Success then
Nico Huber6a996dc2018-06-17 16:30:33 +0200552 Check_Platform_PCI (Success);
Nico Huber4ba49112020-06-10 16:31:36 +0200553 pragma Debug (not Success, Debug.Put_Line ("ERROR: Incompatible GPU."));
554 end if;
555
556 if Success then
557 if HW.Config.Dynamic_MMIO then
Arthur Heymans960e2392026-03-03 19:45:24 +0100558 if Config.Has_I945_GTT_BAR then
559 -- i945: MMIO is on BAR0, GTT is on separate BAR3
560 Dev.Map (PCI_MMIO_Base, PCI.Res0);
561 Dev.Map (PCI_GTT_Base, PCI.Res3);
562 else
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100563 Dev.Map
564 (PCI_MMIO_Base, PCI.Res0, Length => Config.MMIO_GTT_Offset);
565 Dev.Map
566 (PCI_GTT_Base, PCI.Res0, Offset => Config.MMIO_GTT_Offset);
Arthur Heymans960e2392026-03-03 19:45:24 +0100567 end if;
Nico Huber4ba49112020-06-10 16:31:36 +0200568
Nico Huber6a996dc2018-06-17 16:30:33 +0200569 if PCI_MMIO_Base /= 0 and PCI_GTT_Base /= 0 then
570 Registers.Set_Register_Base (PCI_MMIO_Base, PCI_GTT_Base);
571 else
Nico Huber4ba49112020-06-10 16:31:36 +0200572 pragma Debug (Debug.Put_Line ("ERROR: Couldn't map resource0."));
573 Success := False;
Nico Huber6a996dc2018-06-17 16:30:33 +0200574 end if;
Nico Huber4ba49112020-06-10 16:31:36 +0200575 else
576 Success := Config.Default_MMIO_Base_Set;
577 pragma Debug (not Success, Debug.Put_Line ("ERROR: MMIO base not set."));
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200578 end if;
Nico Huber2b6f6992017-07-09 18:11:34 +0200579 end if;
580
Nico Huber5dbaf4b2020-01-08 17:24:58 +0100581 Panel.Static_Init; -- early for flow analysis
582
Nico Huber83693c82016-10-08 22:17:55 +0200583 if not Success then
Nico Huber83693c82016-10-08 22:17:55 +0200584 Initialized := False;
585 return;
586 end if;
587
588 Panel.Setup_PP_Sequencer;
Nico Huber83693c82016-10-08 22:17:55 +0200589
Nico Huber793a8d42016-11-21 18:57:03 +0100590 if Clean_State then
591 Power_And_Clocks.Pre_All_Off;
592 Connectors.Pre_All_Off;
593 Display_Controller.All_Off;
594 Connectors.Post_All_Off;
595 PLLs.All_Off;
596 Power_And_Clocks.Post_All_Off;
Nico Huber17d64b62017-07-15 20:51:25 +0200597 Registers.Clear_Fences;
Nico Huber33912aa2016-12-06 20:36:23 +0100598 else
599 -- According to PRMs, VGA plane is the only thing
Nico Huber3a0e2a02017-07-19 14:41:46 +0200600 -- that's enabled by default after reset...
Nico Huber33912aa2016-12-06 20:36:23 +0100601 Display_Controller.Legacy_VGA_Off;
Nico Huber3a0e2a02017-07-19 14:41:46 +0200602 -- ... along with some DDI port bits since Skylake.
603 Connectors.Post_Reset_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100604 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200605
606 -------------------- Now restart from a clean state ---------------------
607 Power_And_Clocks.Initialize;
Nico Huber1b991852024-07-17 16:44:05 +0200608 Port_Detect.Initialize;
609 Connectors.Initialize;
Nico Huber83693c82016-10-08 22:17:55 +0200610
Nico Huber1c3b9282017-02-09 13:57:04 +0100611 if Config.Has_PCH then
612 Registers.Unset_And_Set_Mask
613 (Register => Registers.PCH_RAWCLK_FREQ,
614 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
Nico Huberc9ad9de2020-12-20 02:34:37 +0100615 Mask_Set => PCH_RAWCLK_FREQ (Config.Raw_Clock));
Nico Huber1c3b9282017-02-09 13:57:04 +0100616 end if;
Nico Huberf54d0962016-10-20 14:17:18 +0200617
Nico Huber83693c82016-10-08 22:17:55 +0200618 Initialized := True;
619
620 end Initialize;
Nico Huber793f4f82022-09-04 14:24:00 +0000621 pragma Annotate
622 (GNATprove, Intentional, "unused global",
623 "The whole, abstract Device_State is modified in certain configurations.");
624 pragma Warnings (GNATprove, On, "no check message justified*");
625 pragma Warnings
626 (GNATprove, On, """Registers.GTT_State"" * is not modified*");
Nico Huber83693c82016-10-08 22:17:55 +0200627
628 function Is_Initialized return Boolean
629 with
630 Refined_Post => Is_Initialized'Result = Initialized
631 is
632 begin
633 return Initialized;
634 end Is_Initialized;
635
636 ----------------------------------------------------------------------------
637
Nico Hubercf88f3d2018-06-05 13:27:34 +0200638 pragma Warnings
639 (GNATprove, Off, """Registers.Register_State"" * is not modified*",
Nico Huberadfe11f2018-06-10 14:59:04 +0200640 Reason => "Power_Up_VGA is only effective in certain configurations.");
Nico Huber17b513e2022-09-04 13:36:02 +0200641 pragma Warnings
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700642 (GNATprove, Off, """PCode.Mailbox_Ready"" * is not modified*",
643 Reason => "Pcode is only used in certain configurations.");
644 pragma Warnings
Nico Huber17b513e2022-09-04 13:36:02 +0200645 (GNATprove, Off, "no check message justified*", Reason => "see below");
Nico Huber42fb2d02017-09-01 17:01:51 +0200646 procedure Power_Up_VGA
Nico Hubercf88f3d2018-06-05 13:27:34 +0200647 with
648 Refined_Global =>
Nico Huberadfe11f2018-06-10 14:59:04 +0200649 (Input => (Cur_Configs, Config.Variable, Time.State),
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700650 In_Out => (Registers.Register_State, PCode.Mailbox_Ready),
Nico Hubercf88f3d2018-06-05 13:27:34 +0200651 Proof_In => (Initialized))
Nico Huber42fb2d02017-09-01 17:01:51 +0200652 is
653 Fake_Config : constant Pipe_Configs :=
654 (Primary =>
655 (Port => Analog,
656 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000657 Alignment => Center,
658 Scaling => Fit,
Nico Hubera02b2c62018-01-09 15:58:34 +0100659 Cursor => Default_Cursor,
Nico Huber42fb2d02017-09-01 17:01:51 +0200660 Mode => HW.GFX.Invalid_Mode),
661 others =>
662 (Port => Disabled,
663 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000664 Alignment => Alignment'First,
665 Scaling => Scaling'First,
Nico Hubera02b2c62018-01-09 15:58:34 +0100666 Cursor => Default_Cursor,
Nico Huber42fb2d02017-09-01 17:01:51 +0200667 Mode => HW.GFX.Invalid_Mode));
668 begin
669 Power_And_Clocks.Power_Up (Cur_Configs, Fake_Config);
670 end Power_Up_VGA;
Nico Hubercf88f3d2018-06-05 13:27:34 +0200671 pragma Annotate
672 (GNATprove, Intentional, "unused global",
Nico Huberadfe11f2018-06-10 14:59:04 +0200673 "Power_Up_VGA is only effective in certain configurations.");
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700674 pragma Warnings
675 (GNATprove, On, """PCode.Mailbox_Ready"" * is not modified*");
Nico Hubercf88f3d2018-06-05 13:27:34 +0200676 pragma Warnings (GNATprove, On, "no check message justified*");
677 pragma Warnings
678 (GNATprove, On, """Registers.Register_State"" * is not modified*");
Nico Huber42fb2d02017-09-01 17:01:51 +0200679
680 ----------------------------------------------------------------------------
681
Nico Huber5374c3a2017-07-15 21:48:06 +0200682 function FB_First_Page (FB : Framebuffer_Type) return Natural is
Nico Huber34be6542017-12-13 09:26:24 +0100683 (Natural (Phys_Offset (FB) / GTT_Page_Size));
Nico Huber5374c3a2017-07-15 21:48:06 +0200684 function FB_Pages (FB : Framebuffer_Type) return Natural is
685 (Natural (Div_Round_Up (FB_Size (FB), GTT_Page_Size)));
686 function FB_Last_Page (FB : Framebuffer_Type) return Natural is
687 (FB_First_Page (FB) + FB_Pages (FB) - 1);
688
Nico Huber34be6542017-12-13 09:26:24 +0100689 -- Check basics and that it fits in GTT. For 90 degree rotations,
690 -- the Offset should be above GTT_Rotation_Offset. The latter will
691 -- be subtracted for the aperture mapping.
Nico Huber5374c3a2017-07-15 21:48:06 +0200692 function Valid_FB (FB : Framebuffer_Type) return Boolean is
Nico Huber34be6542017-12-13 09:26:24 +0100693 (Valid_Stride (FB) and
694 FB_First_Page (FB) in GTT_Range and
Nico Huber2e87c0d2020-04-18 00:46:39 +0200695 FB_Last_Page (FB) + 128 in GTT_Range and
Nico Huber34be6542017-12-13 09:26:24 +0100696 (not Rotation_90 (FB) or
Nico Huber2e87c0d2020-04-18 00:46:39 +0200697 (FB_First_Page (FB) mod 64 = 0 and
698 FB_Last_Page (FB) + 128 + GTT_Rotation_Offset in GTT_Range and
Nico Huber34be6542017-12-13 09:26:24 +0100699 FB.Offset >= Word32 (GTT_Rotation_Offset) * GTT_Page_Size)));
Nico Huber5374c3a2017-07-15 21:48:06 +0200700
701 -- Also check that we don't overflow the GTT's 39-bit space
702 -- (always true with a 32-bit base)
703 function Valid_Phys_FB (FB : Framebuffer_Type; Phys_Base : Word32)
704 return Boolean is
705 (Valid_FB (FB) and
Nico Huber34be6542017-12-13 09:26:24 +0100706 Int64 (Phys_Base) + Int64 (Phys_Offset (FB)) + Int64 (FB_Size (FB)) <=
Nico Huber5374c3a2017-07-15 21:48:06 +0200707 Int64 (GTT_Address_Type'Last))
708 with
709 Ghost;
710
Nico Huber83693c82016-10-08 22:17:55 +0200711 procedure Write_GTT
712 (GTT_Page : GTT_Range;
713 Device_Address : GTT_Address_Type;
Nico Huber5374c3a2017-07-15 21:48:06 +0200714 Valid : Boolean)
715 is
Nico Huber83693c82016-10-08 22:17:55 +0200716 begin
717 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
718 end Write_GTT;
719
Nico Huberceda17d2018-06-09 22:00:29 +0200720 procedure Read_GTT
721 (Device_Address : out GTT_Address_Type;
722 Valid : out Boolean;
723 GTT_Page : in GTT_Range)
724 is
725 begin
726 Registers.Read_GTT (Device_Address, Valid, GTT_Page);
727 end Read_GTT;
728
Nico Huber194e57e2017-07-15 21:15:46 +0200729 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
Nico Huber5374c3a2017-07-15 21:48:06 +0200730 with
731 Pre => Is_Initialized and Valid_Phys_FB (FB, Phys_Base)
Nico Huber83693c82016-10-08 22:17:55 +0200732 is
Nico Huber194e57e2017-07-15 21:15:46 +0200733 Phys_Addr : GTT_Address_Type :=
Nico Huber34be6542017-12-13 09:26:24 +0100734 GTT_Address_Type (Phys_Base) + GTT_Address_Type (Phys_Offset (FB));
Nico Huber83693c82016-10-08 22:17:55 +0200735 begin
Nico Huber194e57e2017-07-15 21:15:46 +0200736 for Idx in FB_First_Page (FB) .. FB_Last_Page (FB) loop
Nico Huber83693c82016-10-08 22:17:55 +0200737 Registers.Write_GTT
738 (GTT_Page => Idx,
739 Device_Address => Phys_Addr,
740 Valid => True);
Nico Huber194e57e2017-07-15 21:15:46 +0200741 Phys_Addr := Phys_Addr + GTT_Page_Size;
Nico Huber83693c82016-10-08 22:17:55 +0200742 end loop;
Nico Huber2e87c0d2020-04-18 00:46:39 +0200743 -- Add another 128 dummy pages to work around buggy VT-d
744 for Idx in FB_Last_Page (FB) + 1 .. FB_Last_Page (FB) + 128 loop
745 Registers.Write_GTT (Idx, Phys_Addr, True);
746 end loop;
Nico Huber9b479412017-08-27 11:55:56 +0200747
748 if Rotation_90 (FB) and FB.Tiling = Y_Tiled and FB.V_Stride >= 32 then
749 declare
750 V_Pages : constant Natural := Natural (FB.V_Stride) / 32;
751 Bytes_Per_Row : constant GTT_Address_Type :=
752 GTT_Address_Type (Pixel_To_Bytes (32 * FB.Stride, FB));
753 begin
754 Phys_Addr := GTT_Address_Type (Phys_Base) +
Nico Huber34be6542017-12-13 09:26:24 +0100755 GTT_Address_Type (Phys_Offset (FB)) +
Nico Huber9b479412017-08-27 11:55:56 +0200756 GTT_Address_Type (FB_Size (FB));
757 for Page in FB_First_Page (FB) .. FB_Last_Page (FB) loop
758 Phys_Addr := Phys_Addr - Bytes_Per_Row;
759 Registers.Write_GTT
760 (GTT_Page => GTT_Rotation_Offset + Page,
761 Device_Address => Phys_Addr,
762 Valid => True);
763
764 if (Page - FB_First_Page (FB) + 1) mod V_Pages = 0 then
765 Phys_Addr := Phys_Addr + GTT_Page_Size +
766 GTT_Address_Type (V_Pages) * Bytes_Per_Row;
767 end if;
768 end loop;
769 end;
Nico Huber2e87c0d2020-04-18 00:46:39 +0200770 -- Add another 128 dummy pages to work around buggy VT-d
771 for Idx in FB_Last_Page (FB) + 1 .. FB_Last_Page (FB) + 128 loop
772 Registers.Write_GTT (GTT_Rotation_Offset + Idx, Phys_Addr, True);
773 end loop;
Nico Huber9b479412017-08-27 11:55:56 +0200774 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200775 end Setup_Default_GTT;
776
777 ----------------------------------------------------------------------------
778
Nico Hubereedde882017-07-16 02:54:39 +0200779 use type HW.Word16;
780 subtype Stolen_Size_Range is Int64 range 0 .. 2 ** 33;
781
782 function GGMS_Gen4 (GGC : Word16) return Natural is
783 (Natural (Shift_Right (GGC, 8) and 16#07#));
784 function GTT_Size_Gen4 (GGC : Word16) return Natural is
785 (if GGMS_Gen4 (GGC) in 1 .. 3 then
786 (GGMS_Gen4 (GGC) + 1) * 2 ** 19 else 0);
787
788 function GMS_Gen4 (GGC : Word16) return Natural is
789 (Natural (Shift_Right (GGC, 4) and 16#0f#));
790 Valid_Stolen_Size_Gen4 : constant
791 array (Natural range 1 .. 13) of Stolen_Size_Range :=
792 (1, 4, 8, 16, 32, 48, 64, 128, 256, 96, 160, 224, 352);
793 function Stolen_Size_Gen4 (GGC : Word16) return Stolen_Size_Range is
794 (if GMS_Gen4 (GGC) in Valid_Stolen_Size_Gen4'Range then
Arthur Heymans5fd9a312017-09-12 12:45:18 +0200795 Valid_Stolen_Size_Gen4 (GMS_Gen4 (GGC)) * 2 ** 20 else 0);
Nico Hubereedde882017-07-16 02:54:39 +0200796
797 function GTT_Size_Gen6 (GGC : Word16) return Natural is
798 (Natural (Shift_Right (GGC, 8) and 16#03#) * 2 ** 20);
799
800 function Stolen_Size_Gen6 (GGC : Word16) return Stolen_Size_Range is
801 (Stolen_Size_Range (Shift_Right (GGC, 3) and 16#1f#) * 32 * 2 ** 20);
802
Nico Huberfe7985f2019-10-12 22:19:24 +0200803 function GGMS_Gen8 (GGC : Word16) return Natural is
804 (Natural (Shift_Right (GGC, 6) and 16#03#));
Nico Hubereedde882017-07-16 02:54:39 +0200805 function GTT_Size_Gen8 (GGC : Word16) return Natural is
Nico Huberfe7985f2019-10-12 22:19:24 +0200806 (if GGMS_Gen8 (GGC) /= 0 then
807 Natural (Shift_Left (Word32'(1), 20 + GGMS_Gen8 (GGC))) else 0);
Nico Hubereedde882017-07-16 02:54:39 +0200808
809 function GMS_Gen8 (GGC : Word16) return Stolen_Size_Range is
810 (Stolen_Size_Range (Shift_Right (GGC, 8) and 16#ff#));
811 function Stolen_Size_Gen8 (GGC : Word16) return Stolen_Size_Range is
812 (GMS_Gen8 (GGC) * 32 * 2 ** 20);
813
814 function Stolen_Size_Gen9 (GGC : Word16) return Stolen_Size_Range is
815 (if GMS_Gen8 (GGC) < 16#f0# then
816 Stolen_Size_Gen8 (GGC)
817 else
818 (GMS_Gen8 (GGC) - 16#f0# + 1) * 4 * 2 ** 20);
819
820 procedure Decode_Stolen
821 (GTT_Size : out Natural;
822 Stolen_Size : out Stolen_Size_Range)
823 with
824 Pre => Is_Initialized
825 is
Nico Huber63ec8362018-06-09 17:42:19 +0200826 GGC_Reg : constant PCI.Index :=
Arthur Heymans960e2392026-03-03 19:45:24 +0100827 (if Config.Gen_I945 or Config.Gen_G45 or Config.CPU_Ironlake
828 then 16#52# else 16#50#);
Nico Hubereedde882017-07-16 02:54:39 +0200829 GGC : Word16;
830 begin
831 Dev.Read16 (GGC, GGC_Reg);
Arthur Heymans960e2392026-03-03 19:45:24 +0100832 if Config.Gen_I945 then
833 -- i945 GTT is on a separate BAR3; GGC GGMS encoding differs
834 -- from Gen4+. Match the Linux driver and use the BAR size.
835 Dev.Resource_Size (GTT_Size, PCI.Res3);
836 Stolen_Size := Stolen_Size_Gen4 (GGC);
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100837 elsif Config.GMCH_GM965 then
838 -- GM965 has no GGMS field in GGC. The GTT is a fixed 512 KiB
839 -- region in the upper half of the 1 MiB GTTMMADR BAR.
840 GTT_Size := 512 * 2 ** 10;
841 Stolen_Size := Stolen_Size_Gen4 (GGC);
Arthur Heymans960e2392026-03-03 19:45:24 +0100842 elsif Config.Gen_G45 or Config.CPU_Ironlake then
Nico Huber998ee2b2018-06-12 23:02:17 +0200843 GTT_Size := GTT_Size_Gen4 (GGC);
844 Stolen_Size := Stolen_Size_Gen4 (GGC);
845 elsif Config.CPU_Sandybridge or Config.CPU_Ivybridge or Config.CPU_Haswell
846 then
847 GTT_Size := GTT_Size_Gen6 (GGC);
848 Stolen_Size := Stolen_Size_Gen6 (GGC);
849 elsif Config.CPU_Broadwell then
850 GTT_Size := GTT_Size_Gen8 (GGC);
851 Stolen_Size := Stolen_Size_Gen8 (GGC);
852 else
853 GTT_Size := GTT_Size_Gen8 (GGC);
854 Stolen_Size := Stolen_Size_Gen9 (GGC);
855 end if;
Nico Hubereedde882017-07-16 02:54:39 +0200856 end Decode_Stolen;
857
Arthur Heymans960e2392026-03-03 19:45:24 +0100858 procedure GTT_Entry_Count (Count : out Natural)
859 is
860 GTT_Size : Natural;
861
862 procedure Fake_Config_State_Access
863 with
864 Global => (Input => Config.Variable),
865 Annotate => (GNATprove, Intentional, "unused global",
866 "Used to have a common contract across platforms.");
867 procedure Fake_Config_State_Access is null;
868 begin
869 Fake_Config_State_Access;
870
871 if Config.Has_I945_GTT_BAR then
872 -- i945 GTT is on a separate BAR3; its size is the BAR size.
873 Dev.Resource_Size (GTT_Size, PCI.Res3);
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100874 elsif Config.GMCH_GM965 then
875 -- GM965 has no GGMS field in GGC; its GTT is fixed at 512 KiB.
876 GTT_Size := 512 * 2 ** 10;
Arthur Heymans960e2392026-03-03 19:45:24 +0100877 else
878 -- Gen4+: GTT size is encoded in the GGC register.
879 declare
880 GGC_Reg : constant PCI.Index :=
881 (if Config.Gen_G45 or Config.CPU_Ironlake
882 then 16#52# else 16#50#);
883 GGC : Word16;
884 begin
885 Dev.Read16 (GGC, GGC_Reg);
886 if Config.Gen_G45 or Config.CPU_Ironlake then
887 GTT_Size := GTT_Size_Gen4 (GGC);
888 elsif Config.CPU_Sandybridge or
889 Config.CPU_Ivybridge or
890 Config.CPU_Haswell
891 then
892 GTT_Size := GTT_Size_Gen6 (GGC);
893 else
894 GTT_Size := GTT_Size_Gen8 (GGC);
895 end if;
896 end;
897 end if;
898 Count := Natural'Min (GTT_Size / Config.GTT_PTE_Size, GTT_Range'Last + 1);
899 end GTT_Entry_Count;
900
Nico Hubereedde882017-07-16 02:54:39 +0200901 -- Additional runtime validation that FB fits stolen memory and aperture.
902 procedure Validate_FB (FB : Framebuffer_Type; Valid : out Boolean)
903 with
904 Pre => Is_Initialized,
905 Post => (if Valid then Valid_FB (FB))
906 is
Nico Huber2e87c0d2020-04-18 00:46:39 +0200907 GTT_Off : constant Natural :=
908 (if Rotation_90 (FB) then GTT_Rotation_Offset else 0);
909
Nico Hubereedde882017-07-16 02:54:39 +0200910 GTT_Size, Aperture_Size : Natural;
911 Stolen_Size : Stolen_Size_Range;
912 begin
913 Valid := Valid_FB (FB);
914
915 if Valid then
916 Decode_Stolen (GTT_Size, Stolen_Size);
917 Dev.Resource_Size (Aperture_Size, PCI.Res2);
918 Valid :=
Nico Huber2e87c0d2020-04-18 00:46:39 +0200919 FB_Last_Page (FB) + 128 + GTT_Off < GTT_Size / Config.GTT_PTE_Size
920 and
921 FB_Last_Page (FB) < Natural (Stolen_Size / GTT_Page_Size)
922 and
Nico Hubereedde882017-07-16 02:54:39 +0200923 FB_Last_Page (FB) < Aperture_Size / GTT_Page_Size;
Nico Huber34be6542017-12-13 09:26:24 +0100924 pragma Debug (not Valid, Debug.Put_Line
Nico Hubereedde882017-07-16 02:54:39 +0200925 ("Stolen memory too small to hold framebuffer."));
926 end if;
927 end Validate_FB;
928
Nico Huber5374c3a2017-07-15 21:48:06 +0200929 procedure Setup_Default_FB
930 (FB : in Framebuffer_Type;
931 Clear : in Boolean := True;
932 Success : out Boolean)
933 is
Nico Huber5374c3a2017-07-15 21:48:06 +0200934 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
935
936 Phys_Base : Word32;
937 begin
Nico Hubereedde882017-07-16 02:54:39 +0200938 Validate_FB (FB, Success);
Nico Huber5374c3a2017-07-15 21:48:06 +0200939
940 if Success then
Tim Wawrzynczak1b65b842022-09-09 10:23:06 -0600941 if Config.GMA_Base_Is_64bit then
942 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index + 4);
943 if Phys_Base /= 0 then
944 pragma Debug (Debug.Put_Line ("Cannot handle 64-bit DSM yet."));
945 Success := False;
946 return;
947 end if;
948 end if;
949
950 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index);
Nico Huber5374c3a2017-07-15 21:48:06 +0200951 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
952 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
953 pragma Debug (not Success, Debug.Put_Line
954 ("Failed to read stolen memory base."));
Nico Huber0164b022017-08-24 15:12:51 +0200955
956 if Success then
957 if FB.Tiling in XY_Tiling then
958 Registers.Add_Fence
959 (First_Page => FB_First_Page (FB),
960 Last_Page => FB_Last_Page (FB),
961 Tiling => FB.Tiling,
962 Pitch => FB_Pitch (FB.Stride, FB),
963 Success => Success);
964 end if;
965 pragma Debug (not Success, Debug.Put_Line
966 ("Tiled framebuffer but no fence regs available."));
967 end if;
968
Nico Huber5374c3a2017-07-15 21:48:06 +0200969 if Success then
970 Setup_Default_GTT (FB, Phys_Base);
971 end if;
972 end if;
973
974 if Success and then Clear then
975 declare
976 use type HW.Word64;
977 Linear_FB : Word64;
978 begin
Nico Huberc3f66f62017-07-16 21:39:54 +0200979 Map_Linear_FB (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +0200980 if Linear_FB /= 0 then
Nico Huberc3f66f62017-07-16 21:39:54 +0200981 Framebuffer_Filler.Fill (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +0200982 end if;
Nico Huber5374c3a2017-07-15 21:48:06 +0200983 end;
984 end if;
985 end Setup_Default_FB;
986
Nico Huberc3f66f62017-07-16 21:39:54 +0200987 procedure Map_Linear_FB (Linear_FB : out Word64; FB : in Framebuffer_Type)
988 is
989 use type HW.Word64;
990
991 Valid : Boolean;
992 begin
993 Linear_FB := 0;
994
995 if Linear_FB_Base = 0 then
996 Dev.Map (Linear_FB_Base, PCI.Res2);
997 pragma Debug
998 (Linear_FB_Base = 0, Debug.Put_Line ("Failed to map resource2."));
999 end if;
1000
1001 if Linear_FB_Base /= 0 then
1002 Validate_FB (FB, Valid);
1003 if Valid then
Nico Huber34be6542017-12-13 09:26:24 +01001004 Linear_FB := Linear_FB_Base + Word64 (Phys_Offset (FB));
Nico Huberc3f66f62017-07-16 21:39:54 +02001005 end if;
1006 end if;
1007 end Map_Linear_FB;
1008
Nico Huber5374c3a2017-07-15 21:48:06 +02001009 ----------------------------------------------------------------------------
1010
Nico Huber99f10f32016-11-20 00:34:05 +01001011 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +02001012 is
1013 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +01001014 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +02001015 Pipe_Names : constant Pipe_Name_Array :=
1016 (Primary => "Primary ",
1017 Secondary => "Secondary",
1018 Tertiary => "Tertiary ");
Nico Huber5ef4d602017-12-13 13:56:47 +01001019
1020 subtype Tiling_Name is String (1 .. 7);
1021 type Tiling_Name_Array is array (Tiling_Type) of Tiling_Name;
1022 Tilings : constant Tiling_Name_Array :=
1023 (Linear => "Linear ",
1024 X_Tiled => "X_Tiled",
1025 Y_Tiled => "Y_Tiled");
1026
1027 subtype Rotation_Name is String (1 .. 11);
1028 type Rotation_Name_Array is array (Rotation_Type) of Rotation_Name;
1029 Rotations : constant Rotation_Name_Array :=
1030 (No_Rotation => "No_Rotation",
1031 Rotated_90 => "Rotated_90 ",
1032 Rotated_180 => "Rotated_180",
1033 Rotated_270 => "Rotated_270");
Nico Huber83693c82016-10-08 22:17:55 +02001034 begin
1035 Debug.New_Line;
Paul Menzelb83107c2017-05-04 09:02:33 +02001036 Debug.Put_Line ("CONFIG =>");
Nico Huber99f10f32016-11-20 00:34:05 +01001037 for Pipe in Pipe_Index loop
1038 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +02001039 Debug.Put (" (");
1040 else
1041 Debug.Put (" ");
1042 end if;
Nico Huberfb80da92026-07-02 15:01:13 +02001043 Debug.Put (Pipe_Names (Pipe));
1044 Debug.Put_Line (" =>");
1045 Debug.Put (" (Port => ");
1046 Debug.Put (Port_Names (Configs (Pipe).Port));
1047 Debug.Put_Line (",");
Nico Huber83693c82016-10-08 22:17:55 +02001048 Debug.Put_Line (" Framebuffer =>");
Nico Huber5ef4d602017-12-13 13:56:47 +01001049 Debug.Put (" (Width => ");
Nico Huber83693c82016-10-08 22:17:55 +02001050 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
1051 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001052 Debug.Put (" Height => ");
Nico Huber83693c82016-10-08 22:17:55 +02001053 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
1054 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001055 Debug.Put (" Start_X => ");
1056 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_X);
1057 Debug.Put_Line (",");
1058 Debug.Put (" Start_Y => ");
1059 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_Y);
1060 Debug.Put_Line (",");
1061 Debug.Put (" Stride => ");
Nico Huber83693c82016-10-08 22:17:55 +02001062 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
1063 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001064 Debug.Put (" V_Stride => ");
1065 Debug.Put_Int32 (Configs (Pipe).Framebuffer.V_Stride);
1066 Debug.Put_Line (",");
1067 Debug.Put (" Tiling => ");
Nico Huberfb80da92026-07-02 15:01:13 +02001068 Debug.Put (Tilings (Configs (Pipe).Framebuffer.Tiling));
1069 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001070 Debug.Put (" Rotation => ");
Nico Huberfb80da92026-07-02 15:01:13 +02001071 Debug.Put (Rotations (Configs (Pipe).Framebuffer.Rotation));
1072 Debug.Put_Line (",");
Nico Huber83693c82016-10-08 22:17:55 +02001073 Debug.Put (" Offset => ");
1074 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
1075 Debug.Put_Line (",");
1076 Debug.Put (" BPC => ");
1077 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
1078 Debug.Put_Line ("),");
1079 Debug.Put_Line (" Mode =>");
1080 Debug.Put (" (Dotclock => ");
1081 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
1082 Debug.Put_Line (",");
1083 Debug.Put (" H_Visible => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001084 Debug.Put_Int32 (Configs (Pipe).Mode.H_Visible);
Nico Huber83693c82016-10-08 22:17:55 +02001085 Debug.Put_Line (",");
1086 Debug.Put (" H_Sync_Begin => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001087 Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_Begin);
Nico Huber83693c82016-10-08 22:17:55 +02001088 Debug.Put_Line (",");
1089 Debug.Put (" H_Sync_End => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001090 Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_End);
Nico Huber83693c82016-10-08 22:17:55 +02001091 Debug.Put_Line (",");
1092 Debug.Put (" H_Total => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001093 Debug.Put_Int32 (Configs (Pipe).Mode.H_Total);
Nico Huber83693c82016-10-08 22:17:55 +02001094 Debug.Put_Line (",");
1095 Debug.Put (" V_Visible => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001096 Debug.Put_Int32 (Configs (Pipe).Mode.V_Visible);
Nico Huber83693c82016-10-08 22:17:55 +02001097 Debug.Put_Line (",");
1098 Debug.Put (" V_Sync_Begin => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001099 Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_Begin);
Nico Huber83693c82016-10-08 22:17:55 +02001100 Debug.Put_Line (",");
1101 Debug.Put (" V_Sync_End => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001102 Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_End);
Nico Huber83693c82016-10-08 22:17:55 +02001103 Debug.Put_Line (",");
1104 Debug.Put (" V_Total => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001105 Debug.Put_Int32 (Configs (Pipe).Mode.V_Total);
Nico Huber83693c82016-10-08 22:17:55 +02001106 Debug.Put_Line (",");
Nico Huberfb80da92026-07-02 15:01:13 +02001107 Debug.Put (" H_Sync_Active_High => ");
1108 if Configs (Pipe).Mode.H_Sync_Active_High then
1109 Debug.Put_Line ("True,");
1110 else
1111 Debug.Put_Line ( "False,");
1112 end if;
1113 Debug.Put (" V_Sync_Active_High => ");
1114 if Configs (Pipe).Mode.V_Sync_Active_High then
1115 Debug.Put_Line ("True,");
1116 else
1117 Debug.Put_Line ("False,");
1118 end if;
Nico Huber83693c82016-10-08 22:17:55 +02001119 Debug.Put (" BPC => ");
1120 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +01001121 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +02001122 Debug.Put_Line (")),");
1123 else
1124 Debug.Put_Line (")));");
1125 end if;
1126 end loop;
1127 end Dump_Configs;
1128
Nico Huberc5c66ec2019-09-28 23:59:45 +02001129 ----------------------------------------------------------------------------
1130
1131 procedure PCI_Read16 (Value : out Word16; Offset : HW.PCI.Index) is
1132 begin
1133 Dev.Read16 (Value, Offset);
1134 end PCI_Read16;
1135
Nico Huber83693c82016-10-08 22:17:55 +02001136end HW.GFX.GMA;