blob: 94ee31258864283fb7af74ca60ef480c2bd27ba5 [file] [log] [blame]
Nico Hubera455f0e2018-01-07 11:40:40 +01001with Ada.Numerics.Discrete_Random;
Nico Huberfda2d6e2017-07-09 16:47:52 +02002with Ada.Unchecked_Conversion;
Nico Huber1d0abe42017-03-05 14:14:09 +01003with Ada.Command_Line;
4with Interfaces.C;
5
Nico Huber3b654a02017-07-15 22:27:14 +02006with HW.Time;
Nico Huber1d0abe42017-03-05 14:14:09 +01007with HW.Debug;
Nico Huberfda2d6e2017-07-09 16:47:52 +02008with HW.PCI.Dev;
9with HW.MMIO_Range;
Nico Huber1d0abe42017-03-05 14:14:09 +010010with HW.GFX.GMA;
Nico Huber3b654a02017-07-15 22:27:14 +020011with HW.GFX.GMA.Config;
Nico Huber1d0abe42017-03-05 14:14:09 +010012with HW.GFX.GMA.Display_Probing;
13
Nico Huberfda2d6e2017-07-09 16:47:52 +020014package body HW.GFX.GMA.GFX_Test
15is
16 pragma Disable_Atomic_Synchronization;
Nico Huber1d0abe42017-03-05 14:14:09 +010017
Nico Hubera455f0e2018-01-07 11:40:40 +010018 Primary_Delay_MS : constant := 8_000;
19 Secondary_Delay_MS : constant := 4_000;
20 Seed : constant := 12345;
21
22 package Rand_P is new Ada.Numerics.Discrete_Random (Pos_Type);
23 Gen : Rand_P.Generator;
24 function Rand return Pos_Type is (Rand_P.Random (Gen));
25
Nico Huber5ef4d602017-12-13 13:56:47 +010026 Start_X : constant := 0;
27 Start_Y : constant := 0;
28
Nico Huberfda2d6e2017-07-09 16:47:52 +020029 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010030
Nico Huber3b654a02017-07-15 22:27:14 +020031 type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
32 type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
33 package GTT is new MMIO_Range
34 (Base_Addr => 0,
35 Element_T => GTT_PTE_Type,
36 Index_T => GTT_Range,
37 Array_T => GTT_Registers_Type);
38
39 GTT_Backup : GTT_Registers_Type;
40
41 procedure Backup_GTT
42 is
43 begin
44 for Idx in GTT_Range loop
45 GTT.Read (GTT_Backup (Idx), Idx);
46 end loop;
47 end Backup_GTT;
48
49 procedure Restore_GTT
50 is
51 begin
52 for Idx in GTT_Range loop
53 GTT.Write (Idx, GTT_Backup (Idx));
54 end loop;
55 end Restore_GTT;
56
Nico Huber1d0abe42017-03-05 14:14:09 +010057 type Pixel_Type is record
58 Red : Byte;
59 Green : Byte;
60 Blue : Byte;
61 Alpha : Byte;
62 end record;
63
64 for Pixel_Type use record
65 Blue at 0 range 0 .. 7;
66 Green at 1 range 0 .. 7;
67 Red at 2 range 0 .. 7;
68 Alpha at 3 range 0 .. 7;
69 end record;
70
Nico Huber244ea7e2017-08-28 11:38:23 +020071 White : constant Pixel_Type := (255, 255, 255, 255);
72 Black : constant Pixel_Type := ( 0, 0, 0, 255);
73 Red : constant Pixel_Type := (255, 0, 0, 255);
74 Green : constant Pixel_Type := ( 0, 255, 0, 255);
75 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
76
Nico Huberfda2d6e2017-07-09 16:47:52 +020077 function Pixel_To_Word (P : Pixel_Type) return Word32
78 with
79 SPARK_Mode => Off
80 is
81 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
82 begin
83 return To_Word (P);
84 end Pixel_To_Word;
85
Nico Huber1d0abe42017-03-05 14:14:09 +010086 Max_W : constant := 4096;
87 Max_H : constant := 2160;
88 FB_Align : constant := 16#0004_0000#;
Nico Huberfda2d6e2017-07-09 16:47:52 +020089 subtype Screen_Index is Natural range
90 0 .. 3 * (Max_W * Max_H + FB_Align / 4) - 1;
91 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +010092
Nico Huber34be6542017-12-13 09:26:24 +010093 function Screen_Offset (FB : Framebuffer_Type) return Natural is
94 (Natural (Phys_Offset (FB) / 4));
95
Nico Huberfda2d6e2017-07-09 16:47:52 +020096 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +010097
Nico Huber3b654a02017-07-15 22:27:14 +020098 Screen_Backup : Screen_Type;
99
100 procedure Backup_Screen (FB : Framebuffer_Type)
101 is
Nico Huber34be6542017-12-13 09:26:24 +0100102 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200103 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
104 begin
105 for Idx in Screen_Index range First .. Last loop
106 Screen.Read (Screen_Backup (Idx), Idx);
107 end loop;
108 end Backup_Screen;
109
110 procedure Restore_Screen (FB : Framebuffer_Type)
111 is
Nico Huber34be6542017-12-13 09:26:24 +0100112 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200113 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
114 begin
115 for Idx in Screen_Index range First .. Last loop
116 Screen.Write (Idx, Screen_Backup (Idx));
117 end loop;
118 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100119
Nico Huber5ef4d602017-12-13 13:56:47 +0100120 function Drawing_Width (FB : Framebuffer_Type) return Natural is
121 (Natural (FB.Width + 2 * Start_X));
122
123 function Drawing_Height (FB : Framebuffer_Type) return Natural is
124 (Natural (FB.Height + 2 * Start_Y));
125
Nico Huber244ea7e2017-08-28 11:38:23 +0200126 function Corner_Fill
127 (X, Y : Natural;
128 FB : Framebuffer_Type;
129 Pipe : Pipe_Index)
130 return Pixel_Type
131 is
132 Xrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100133 (if X < 32 then X else X - (Drawing_Width (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200134 Yrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100135 (if Y < 32 then Y else Y - (Drawing_Height (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200136
137 function Color (Idx : Natural) return Pixel_Type is
138 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
139 when 0 => Blue, when 1 => Black,
140 when 3 => Green, when others => Red);
141 begin
142 return
143 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
144 White
145 elsif Yrel < 16 then
146 (if Xrel < 16 then Color (0) else Color (1))
147 else
148 (if Xrel < 16 then Color (3) else Color (2)));
149 end Corner_Fill;
150
Nico Huber1d0abe42017-03-05 14:14:09 +0100151 function Fill
152 (X, Y : Natural;
153 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200154 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100155 return Pixel_Type
156 is
157 use type HW.Byte;
158
Nico Huber5ef4d602017-12-13 13:56:47 +0100159 Xp : constant Natural := X * 256 / Drawing_Width (Framebuffer);
160 Yp : constant Natural := Y * 256 / Drawing_Height (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100161 Xn : constant Natural := 255 - Xp;
162 Yn : constant Natural := 255 - Yp;
163
164 function Map (X, Y : Natural) return Byte is
165 begin
166 return Byte (X * Y / 255);
167 end Map;
168 begin
169 return
170 (case Pipe is
171 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
172 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
173 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
174 end Fill;
175
176 procedure Test_Screen
177 (Framebuffer : Framebuffer_Type;
178 Pipe : GMA.Pipe_Index)
179 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100180 P : Pixel_Type;
181 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100182 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100183 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200184
185 function Top_Test (X, Y : Natural) return Boolean
186 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100187 C : constant Natural := Drawing_Width (Framebuffer) / 2;
188 S_Y : constant Natural := 3 * (Y - Start_Y) / 2;
Nico Huber9ca69f12017-08-28 14:31:46 +0200189 Left : constant Integer := X - C + S_Y;
190 Right : constant Integer := X - C - S_Y;
191 begin
192 return
Nico Huber5ef4d602017-12-13 13:56:47 +0100193 (Y - Start_Y) < 12 and
Nico Huber9ca69f12017-08-28 14:31:46 +0200194 ((-1 <= Left and Left <= 0) or
195 (0 <= Right and Right <= 1));
196 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100197 begin
Nico Huber5ef4d602017-12-13 13:56:47 +0100198 for Y in 0 .. Drawing_Height (Framebuffer) - 1 loop
Nico Huber1d0abe42017-03-05 14:14:09 +0100199 Offset := Offset_Y;
Nico Huber5ef4d602017-12-13 13:56:47 +0100200 for X in 0 .. Drawing_Width (Framebuffer) - 1 loop
201 if (X < 32 or X >= Drawing_Width (Framebuffer) - 32) and
202 (Y < 32 or Y >= Drawing_Height (Framebuffer) - 32)
Nico Huber244ea7e2017-08-28 11:38:23 +0200203 then
204 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200205 elsif Framebuffer.Rotation /= No_Rotation and then
206 Top_Test (X, Y)
207 then
208 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200209 elsif Y mod 16 = 0 or X mod 16 = 0 then
210 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100211 else
212 P := Fill (X, Y, Framebuffer, Pipe);
213 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200214 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100215 Offset := Offset + 1;
216 end loop;
217 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
218 end loop;
219 end Test_Screen;
220
221 procedure Calc_Framebuffer
222 (FB : out Framebuffer_Type;
223 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200224 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100225 Offset : in out Word32)
226 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100227 Width : constant Width_Type := Width_Type (Mode.H_Visible);
228 Height : constant Height_Type := Height_Type (Mode.V_Visible);
Nico Huber1d0abe42017-03-05 14:14:09 +0100229 begin
230 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200231 if Rotation = Rotated_90 or Rotation = Rotated_270 then
232 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100233 (Width => Width_Type (Height),
234 Height => Height_Type (Width),
235 Start_X => Start_X,
236 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200237 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100238 Stride => Div_Round_Up (Pos32 (Height + 2 * Start_X), 32) * 32,
239 V_Stride => Div_Round_Up (Pos32 (Width + 2 * Start_Y), 32) * 32,
Nico Huber88f3c982017-08-28 13:31:38 +0200240 Tiling => Y_Tiled,
241 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100242 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200243 else
244 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100245 (Width => Width,
246 Height => Height,
247 Start_X => Start_X,
248 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200249 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100250 Stride => Div_Round_Up (Width + 2 * Start_X, 16) * 16,
251 V_Stride => Height + 2 * Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200252 Tiling => Linear,
253 Rotation => Rotation,
254 Offset => Offset);
255 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100256 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100257 end Calc_Framebuffer;
258
Nico Huber3b654a02017-07-15 22:27:14 +0200259 Pipes : GMA.Pipe_Configs;
260
Nico Huber88f3c982017-08-28 13:31:38 +0200261 procedure Prepare_Configs (Rotation : Rotation_Type)
Nico Huber1d0abe42017-03-05 14:14:09 +0100262 is
263 use type HW.GFX.GMA.Port_Type;
264
Nico Huberfda2d6e2017-07-09 16:47:52 +0200265 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200266 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100267 begin
268 GMA.Display_Probing.Scan_Ports (Pipes);
269
270 for Pipe in GMA.Pipe_Index loop
271 if Pipes (Pipe).Port /= GMA.Disabled then
272 Calc_Framebuffer
273 (FB => Pipes (Pipe).Framebuffer,
274 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200275 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100276 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200277 GMA.Setup_Default_FB
278 (FB => Pipes (Pipe).Framebuffer,
279 Clear => False,
280 Success => Success);
281 if not Success then
282 Pipes (Pipe).Port := GMA.Disabled;
283 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100284 end if;
285 end loop;
286
287 GMA.Dump_Configs (Pipes);
288 end Prepare_Configs;
289
Nico Huber3b654a02017-07-15 22:27:14 +0200290 procedure Print_Usage
291 is
292 begin
293 Debug.Put_Line
Nico Huber88f3c982017-08-28 13:31:38 +0200294 ("Usage: " & Ada.Command_Line.Command_Name &
295 " <delay seconds>" &
296 " [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200297 Debug.New_Line;
298 end Print_Usage;
299
Nico Huber1d0abe42017-03-05 14:14:09 +0100300 procedure Main
301 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100302 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200303 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100304 use type Interfaces.C.int;
305
Nico Huberfda2d6e2017-07-09 16:47:52 +0200306 Res_Addr : Word64;
307
Nico Hubera455f0e2018-01-07 11:40:40 +0100308 Delay_MS : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200309 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200310
Nico Huberfda2d6e2017-07-09 16:47:52 +0200311 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100312 Initialized : Boolean;
313
314 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
315 pragma Import (C, iopl, "iopl");
316 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200317 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200318 Print_Usage;
319 return;
320 end if;
321
Nico Hubera455f0e2018-01-07 11:40:40 +0100322 Delay_MS := Natural'Value (Ada.Command_Line.Argument (1)) * 1_000;
Nico Huber3b654a02017-07-15 22:27:14 +0200323
Nico Huber88f3c982017-08-28 13:31:38 +0200324 if Ada.Command_Line.Argument_Count >= 2 then
325 declare
326 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
327 begin
328 if Rotation_Degree = "0" then Rotation := No_Rotation;
329 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
330 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
331 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
332 else Print_Usage; return; end if;
333 end;
334 end if;
335
Nico Huber1d0abe42017-03-05 14:14:09 +0100336 if iopl (3) /= 0 then
337 Debug.Put_Line ("Failed to change i/o privilege level.");
338 return;
339 end if;
340
Nico Huberfda2d6e2017-07-09 16:47:52 +0200341 Dev.Initialize (Dev_Init);
342 if not Dev_Init then
343 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100344 return;
345 end if;
346
Nico Huber3b654a02017-07-15 22:27:14 +0200347 Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
348 if Res_Addr = 0 then
349 Debug.Put_Line ("Failed to map PCI resource0.");
350 return;
351 end if;
352 GTT.Set_Base_Address (Res_Addr);
353
Nico Huberfda2d6e2017-07-09 16:47:52 +0200354 Dev.Map (Res_Addr, PCI.Res2, WC => True);
355 if Res_Addr = 0 then
356 Debug.Put_Line ("Failed to map PCI resource2.");
357 return;
358 end if;
359 Screen.Set_Base_Address (Res_Addr);
360
Nico Huber1d0abe42017-03-05 14:14:09 +0100361 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200362 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100363 Success => Initialized);
364
365 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200366 Backup_GTT;
367
Nico Huber88f3c982017-08-28 13:31:38 +0200368 Prepare_Configs (Rotation);
Nico Huber1d0abe42017-03-05 14:14:09 +0100369
370 GMA.Update_Outputs (Pipes);
371
372 for Pipe in GMA.Pipe_Index loop
373 if Pipes (Pipe).Port /= GMA.Disabled then
Nico Huber3b654a02017-07-15 22:27:14 +0200374 Backup_Screen (Pipes (Pipe).Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100375 Test_Screen
376 (Framebuffer => Pipes (Pipe).Framebuffer,
377 Pipe => Pipe);
378 end if;
379 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200380
Nico Hubera455f0e2018-01-07 11:40:40 +0100381 if Delay_MS >= Primary_Delay_MS + Secondary_Delay_MS then
382 Time.M_Delay (Primary_Delay_MS);
383 Delay_MS := Delay_MS - Primary_Delay_MS;
384 declare
385 New_Pipes : GMA.Pipe_Configs := Pipes;
386
387 function Rand_Div (Num : Pos_Type) return Pos_Type is
388 (case Rand mod 4 is
389 when 3 => Rand mod Num / 3,
390 when 2 => Rand mod Num / 2,
391 when 1 => Rand mod Num,
392 when others => 0);
393 begin
394 Rand_P.Reset (Gen, Seed);
395 while Delay_MS >= Secondary_Delay_MS loop
396 New_Pipes := Pipes;
397 for Pipe in GMA.Pipe_Index loop
398 exit when Pipes (Pipe).Port = Disabled;
399 declare
400 New_FB : Framebuffer_Type renames
401 New_Pipes (Pipe).Framebuffer;
402 Width : constant Width_Type :=
403 Pipes (Pipe).Framebuffer.Width;
404 Height : constant Height_Type :=
405 Pipes (Pipe).Framebuffer.Height;
406 begin
407 New_FB.Start_X := Pos_Type'Min
408 (Width - 320, Rand_Div (Width));
409 New_FB.Start_Y := Pos_Type'Min
410 (Height - 320, Rand_Div (Height));
411 New_FB.Width := Width_Type'Max
412 (320, Width - New_FB.Start_X - Rand_Div (Width));
413 New_FB.Height := Height_Type'Max
414 (320, Height - New_FB.Start_Y - Rand_Div (Height));
415 end;
416 end loop;
417 GMA.Dump_Configs (New_Pipes);
418 GMA.Update_Outputs (New_Pipes);
419 Time.M_Delay (Secondary_Delay_MS);
420 Delay_MS := Delay_MS - Secondary_Delay_MS;
421 end loop;
422 end;
423 end if;
424 Time.M_Delay (Delay_MS);
Nico Huber3b654a02017-07-15 22:27:14 +0200425
426 for Pipe in GMA.Pipe_Index loop
427 if Pipes (Pipe).Port /= GMA.Disabled then
428 Restore_Screen (Pipes (Pipe).Framebuffer);
429 end if;
430 end loop;
431 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100432 end if;
433 end Main;
434
Nico Huberfda2d6e2017-07-09 16:47:52 +0200435end HW.GFX.GMA.GFX_Test;