blob: 41d8461977ad2aa9722508af88c3e13b3a7be6c8 [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 Huber3b654a02017-07-15 22:27:14 +020010with HW.GFX.GMA.Config;
Nico Huber5903f922020-10-05 14:15:57 +020011with HW.GFX.GMA.Registers;
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;
Nico Hubera563ec22019-09-29 19:07:27 +020020 HP_Delay_MS : constant := 500;
Nico Hubera455f0e2018-01-07 11:40:40 +010021 Seed : constant := 12345;
22
Nico Hubera63e8332018-02-01 16:41:30 +010023 package Rand_P is new Ada.Numerics.Discrete_Random (Natural);
Nico Huberd8282b62018-06-18 00:44:55 +020024 function Rand (Gen : Rand_P.Generator)
25 return Int32 is (Int32 (Rand_P.Random (Gen)));
Nico Hubera455f0e2018-01-07 11:40:40 +010026
Nico Huber5ef4d602017-12-13 13:56:47 +010027 Start_X : constant := 0;
28 Start_Y : constant := 0;
29
Nico Huberfda2d6e2017-07-09 16:47:52 +020030 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010031
Nico Huberc76749d2018-06-09 22:04:55 +020032 type GTT_Entry is record
33 Addr : GTT_Address_Type;
34 Valid : Boolean;
35 end record;
36 GTT_Backup : array (GTT_Range) of GTT_Entry;
Arthur Heymans960e2392026-03-03 19:45:24 +010037 GTT_Backup_Count : Natural;
Nico Huber3b654a02017-07-15 22:27:14 +020038
39 procedure Backup_GTT
40 is
41 begin
Arthur Heymans960e2392026-03-03 19:45:24 +010042 GMA.GTT_Entry_Count (GTT_Backup_Count);
43 if GTT_Backup_Count = 0 then
44 Debug.Put_Line ("WARNING: GTT size is 0, skipping GTT backup.");
45 return;
46 end if;
47 for Idx in GTT_Range range 0 .. GTT_Backup_Count - 1 loop
Nico Huberc76749d2018-06-09 22:04:55 +020048 Read_GTT (GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid, Idx);
Nico Huber3b654a02017-07-15 22:27:14 +020049 end loop;
50 end Backup_GTT;
51
52 procedure Restore_GTT
53 is
54 begin
Arthur Heymans960e2392026-03-03 19:45:24 +010055 if GTT_Backup_Count = 0 then
56 return;
57 end if;
58 for Idx in GTT_Range range 0 .. GTT_Backup_Count - 1 loop
Nico Huberc76749d2018-06-09 22:04:55 +020059 Write_GTT (Idx, GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid);
Nico Huber3b654a02017-07-15 22:27:14 +020060 end loop;
61 end Restore_GTT;
62
Nico Huber1d0abe42017-03-05 14:14:09 +010063 type Pixel_Type is record
64 Red : Byte;
65 Green : Byte;
66 Blue : Byte;
67 Alpha : Byte;
68 end record;
69
70 for Pixel_Type use record
71 Blue at 0 range 0 .. 7;
72 Green at 1 range 0 .. 7;
73 Red at 2 range 0 .. 7;
74 Alpha at 3 range 0 .. 7;
75 end record;
76
Nico Huber244ea7e2017-08-28 11:38:23 +020077 White : constant Pixel_Type := (255, 255, 255, 255);
78 Black : constant Pixel_Type := ( 0, 0, 0, 255);
79 Red : constant Pixel_Type := (255, 0, 0, 255);
80 Green : constant Pixel_Type := ( 0, 255, 0, 255);
81 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
82
Nico Huberfda2d6e2017-07-09 16:47:52 +020083 function Pixel_To_Word (P : Pixel_Type) return Word32
84 with
85 SPARK_Mode => Off
86 is
87 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
88 begin
89 return To_Word (P);
90 end Pixel_To_Word;
91
Nico Huber7bb10c62018-01-12 14:07:44 +010092 Max_W : constant := 4096;
93 Max_H : constant := 2160;
94 FB_Align : constant := 16#0004_0000#;
95 Cursor_Align : constant := 16#0001_0000#;
96 Max_Cursor_Wid : constant := 256;
97 subtype Screen_Index is Natural range 0 .. 3 *
98 (Max_W * Max_H + FB_Align / 4 +
99 3 * Max_Cursor_Wid * Max_Cursor_Wid + Cursor_Align / 4)
100 - 1;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200101 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +0100102
Nico Huber34be6542017-12-13 09:26:24 +0100103 function Screen_Offset (FB : Framebuffer_Type) return Natural is
104 (Natural (Phys_Offset (FB) / 4));
105
Nico Huberfda2d6e2017-07-09 16:47:52 +0200106 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +0100107
Nico Huber3b654a02017-07-15 22:27:14 +0200108 Screen_Backup : Screen_Type;
109
110 procedure Backup_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.Read (Screen_Backup (Idx), Idx);
117 end loop;
118 end Backup_Screen;
119
120 procedure Restore_Screen (FB : Framebuffer_Type)
121 is
Nico Huber34be6542017-12-13 09:26:24 +0100122 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200123 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
124 begin
125 for Idx in Screen_Index range First .. Last loop
126 Screen.Write (Idx, Screen_Backup (Idx));
127 end loop;
128 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100129
Nico Huber5ef4d602017-12-13 13:56:47 +0100130 function Drawing_Width (FB : Framebuffer_Type) return Natural is
131 (Natural (FB.Width + 2 * Start_X));
132
133 function Drawing_Height (FB : Framebuffer_Type) return Natural is
134 (Natural (FB.Height + 2 * Start_Y));
135
Nico Huber244ea7e2017-08-28 11:38:23 +0200136 function Corner_Fill
137 (X, Y : Natural;
138 FB : Framebuffer_Type;
139 Pipe : Pipe_Index)
140 return Pixel_Type
141 is
142 Xrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100143 (if X < 32 then X else X - (Drawing_Width (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200144 Yrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100145 (if Y < 32 then Y else Y - (Drawing_Height (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200146
147 function Color (Idx : Natural) return Pixel_Type is
148 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
149 when 0 => Blue, when 1 => Black,
150 when 3 => Green, when others => Red);
151 begin
152 return
153 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
154 White
155 elsif Yrel < 16 then
156 (if Xrel < 16 then Color (0) else Color (1))
157 else
158 (if Xrel < 16 then Color (3) else Color (2)));
159 end Corner_Fill;
160
Nico Huber1d0abe42017-03-05 14:14:09 +0100161 function Fill
162 (X, Y : Natural;
163 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200164 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100165 return Pixel_Type
166 is
167 use type HW.Byte;
168
Nico Huber5ef4d602017-12-13 13:56:47 +0100169 Xp : constant Natural := X * 256 / Drawing_Width (Framebuffer);
170 Yp : constant Natural := Y * 256 / Drawing_Height (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100171 Xn : constant Natural := 255 - Xp;
172 Yn : constant Natural := 255 - Yp;
173
174 function Map (X, Y : Natural) return Byte is
175 begin
176 return Byte (X * Y / 255);
177 end Map;
178 begin
179 return
180 (case Pipe is
181 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
182 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
183 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
184 end Fill;
185
186 procedure Test_Screen
187 (Framebuffer : Framebuffer_Type;
188 Pipe : GMA.Pipe_Index)
189 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100190 P : Pixel_Type;
191 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100192 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100193 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200194
195 function Top_Test (X, Y : Natural) return Boolean
196 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100197 C : constant Natural := Drawing_Width (Framebuffer) / 2;
198 S_Y : constant Natural := 3 * (Y - Start_Y) / 2;
Nico Huber9ca69f12017-08-28 14:31:46 +0200199 Left : constant Integer := X - C + S_Y;
200 Right : constant Integer := X - C - S_Y;
201 begin
202 return
Nico Huber5ef4d602017-12-13 13:56:47 +0100203 (Y - Start_Y) < 12 and
Nico Huber9ca69f12017-08-28 14:31:46 +0200204 ((-1 <= Left and Left <= 0) or
205 (0 <= Right and Right <= 1));
206 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100207 begin
Nico Huber5ef4d602017-12-13 13:56:47 +0100208 for Y in 0 .. Drawing_Height (Framebuffer) - 1 loop
Nico Huber1d0abe42017-03-05 14:14:09 +0100209 Offset := Offset_Y;
Nico Huber5ef4d602017-12-13 13:56:47 +0100210 for X in 0 .. Drawing_Width (Framebuffer) - 1 loop
211 if (X < 32 or X >= Drawing_Width (Framebuffer) - 32) and
212 (Y < 32 or Y >= Drawing_Height (Framebuffer) - 32)
Nico Huber244ea7e2017-08-28 11:38:23 +0200213 then
214 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200215 elsif Framebuffer.Rotation /= No_Rotation and then
216 Top_Test (X, Y)
217 then
218 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200219 elsif Y mod 16 = 0 or X mod 16 = 0 then
220 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100221 else
222 P := Fill (X, Y, Framebuffer, Pipe);
223 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200224 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100225 Offset := Offset + 1;
226 end loop;
227 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
228 end loop;
229 end Test_Screen;
230
Nico Huber7bb10c62018-01-12 14:07:44 +0100231 function Donut (X, Y, Max : Cursor_Pos) return Byte
232 is
233 ZZ : constant Int32 := Max * Max * 2;
234 Dist_Center : constant Int32 := ((X * X + Y * Y) * 255) / ZZ;
235 Dist_Circle : constant Int32 := Dist_Center - 20;
236 begin
237 return Byte (255 - Int32'Min (255, 6 * abs Dist_Circle + 64));
238 end Donut;
239
Nico Huber5903f922020-10-05 14:15:57 +0200240 procedure Draw_Cursor (Pipe : Pipe_Index; Rotation : Rotation_Type; Cursor : Cursor_Type)
Nico Huber7bb10c62018-01-12 14:07:44 +0100241 is
242 use type HW.Byte;
243 Width : constant Width_Type := Cursor_Width (Cursor.Size);
Nico Huber5903f922020-10-05 14:15:57 +0200244 GTT_Offset : constant GTT_Range :=
245 (if Rotation = Rotated_90 or Rotation = Rotated_270 then
246 Cursor.GTT_Offset - GTT_Rotation_Offset
247 else
248 Cursor.GTT_Offset);
249 Screen_Offset : Natural := Natural (Shift_Left (Word32 (GTT_Offset), 12) / 4);
Nico Huber7bb10c62018-01-12 14:07:44 +0100250 begin
251 if Cursor.Mode /= ARGB_Cursor then
252 return;
253 end if;
254 for Y in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
255 for X in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
256 declare
257 D : constant Byte := Donut (X, Y, Width / 2);
258 begin
259 -- Hardware seems to expect pre-multiplied alpha (i.e.
260 -- color components already contain the alpha).
261 Screen.Write
262 (Index => Screen_Offset,
263 Value => Pixel_To_Word (
264 (Red => (if Pipe = Secondary then D / 2 else 0),
265 Green => (if Pipe = Tertiary then D / 2 else 0),
266 Blue => (if Pipe = Primary then D / 2 else 0),
267 Alpha => D)));
268 Screen_Offset := Screen_Offset + 1;
269 end;
270 end loop;
271 end loop;
272 end Draw_Cursor;
273
Nico Huber1d0abe42017-03-05 14:14:09 +0100274 procedure Calc_Framebuffer
275 (FB : out Framebuffer_Type;
276 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200277 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100278 Offset : in out Word32)
279 is
Nico Huberc5c767a2018-06-03 01:09:04 +0200280 Width : constant Width_Type := Mode.H_Visible;
281 Height : constant Height_Type := Mode.V_Visible;
Nico Huber1d0abe42017-03-05 14:14:09 +0100282 begin
283 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200284 if Rotation = Rotated_90 or Rotation = Rotated_270 then
285 FB :=
Nico Huberc5c767a2018-06-03 01:09:04 +0200286 (Width => Height,
287 Height => Width,
Nico Huber5ef4d602017-12-13 13:56:47 +0100288 Start_X => Start_X,
289 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200290 BPC => 8,
Nico Huberc5c767a2018-06-03 01:09:04 +0200291 Stride => Div_Round_Up (Height + 2 * Start_X, 32) * 32,
292 V_Stride => Div_Round_Up (Width + 2 * Start_Y, 32) * 32,
Nico Huber88f3c982017-08-28 13:31:38 +0200293 Tiling => Y_Tiled,
294 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100295 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200296 else
297 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100298 (Width => Width,
299 Height => Height,
300 Start_X => Start_X,
301 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200302 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100303 Stride => Div_Round_Up (Width + 2 * Start_X, 16) * 16,
304 V_Stride => Height + 2 * Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200305 Tiling => Linear,
306 Rotation => Rotation,
307 Offset => Offset);
308 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100309 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100310 end Calc_Framebuffer;
311
Nico Huber7bb10c62018-01-12 14:07:44 +0100312 type Cursor_Array is array (Cursor_Size) of Cursor_Type;
313 Cursors : array (Pipe_Index) of Cursor_Array;
314
315 procedure Prepare_Cursors
316 (Cursors : out Cursor_Array;
Nico Huber5903f922020-10-05 14:15:57 +0200317 Rotation : in Rotation_Type;
Nico Huber7bb10c62018-01-12 14:07:44 +0100318 Offset : in out Word32)
319 is
Nico Huber7bb10c62018-01-12 14:07:44 +0100320 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
321
322 Phys_Base : Word32;
323 Success : Boolean;
324 begin
Nico Huber87719ae2024-06-20 13:06:05 +0000325 if Config.GMA_Base_Is_64bit then
326 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index + 4);
327 if Phys_Base /= 0 then
328 pragma Debug (Debug.Put_Line ("Cannot handle 64-bit DSM yet."));
329 return;
330 end if;
331 end if;
332
333 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index);
Nico Huber7bb10c62018-01-12 14:07:44 +0100334 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
335 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
336 if not Success then
337 Debug.Put_Line ("Failed to read stolen memory base.");
338 return;
339 end if;
340
341 for Size in Cursor_Size loop
342 Offset := (Offset + Cursor_Align - 1) and not (Cursor_Align - 1);
343 declare
344 Width : constant Width_Type := Cursor_Width (Size);
Nico Huber5903f922020-10-05 14:15:57 +0200345 Height : Width_Type renames Width;
346
347 Phys_End : constant Word32 := Offset + Word32 (Width * Height) * 4;
348 GTT_Start : constant GTT_Range := GTT_Range (Shift_Right (Offset, 12));
349 GTT_End : constant GTT_Range := GTT_Range (Shift_Right (Phys_End, 12));
350 -- 90 degree rotations use a special framebuffer mapping w/ GTT_Rotation_Offset:
351 Scanout_Offset : constant GTT_Range :=
352 (if Rotation in Rotated_90 | Rotated_270 then GTT_Rotation_Offset else 0);
Nico Huber7bb10c62018-01-12 14:07:44 +0100353 begin
354 Cursors (Size) :=
355 (Mode => ARGB_Cursor,
356 Size => Size,
357 Center_X => Width,
Nico Huber5903f922020-10-05 14:15:57 +0200358 Center_Y => Height,
359 GTT_Offset => GTT_Start + Scanout_Offset);
360
361 while Offset < Phys_End loop
Nico Huber7bb10c62018-01-12 14:07:44 +0100362 GMA.Write_GTT
363 (GTT_Page => GTT_Range (Offset / GTT_Page_Size),
364 Device_Address => GTT_Address_Type (Phys_Base + Offset),
365 Valid => True);
366 Offset := Offset + GTT_Page_Size;
367 end loop;
Nico Huber5903f922020-10-05 14:15:57 +0200368
369 if Rotation in Rotated_90 | Rotated_270 then
370 -- In case of y-tiled surfaces (needed for 90 degree rotations),
371 -- the fence makes the framebuffer writeable like a linear one.
372 Registers.Add_Fence
373 (First_Page => GTT_Start,
374 Last_Page => GTT_End - 1,
375 Tiling => Y_Tiled,
376 Pitch => Natural (Width / Tile_Width (Y_Tiled)),
377 Success => Success);
378
379 -- Though, for the scanout of the rotated surface, we have to add
380 -- a special, rotated framebuffer mapping. For each linear page
381 -- index we calculate `Phys_Addr` column-wise from bottom to top.
382 declare
383 subtype Rotated_Pages is GTT_Range range
384 GTT_Start + GTT_Rotation_Offset .. GTT_End - 1 + GTT_Rotation_Offset;
385
386 Bytes_Per_Row : constant GTT_Address_Type :=
387 GTT_Address_Type (Tile_Rows (Y_Tiled) * Width * 4);
388 V_Pages : constant GTT_Range := GTT_Range (Height / Tile_Rows (Y_Tiled));
389 V_Bytes : constant GTT_Address_Type :=
390 GTT_Address_Type (V_Pages) * Bytes_Per_Row;
391
392 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_Base + Phys_End);
393 begin
394 for Page in Rotated_Pages loop
395 Phys_Addr := Phys_Addr - Bytes_Per_Row;
396
397 Registers.Write_GTT
398 (GTT_Page => Page,
399 Device_Address => Phys_Addr,
400 Valid => True);
401
402 if (Page - Rotated_Pages'First + 1) mod V_Pages = 0 then
403 Phys_Addr := Phys_Addr + GTT_Page_Size + V_Bytes;
404 end if;
405 end loop;
406 end;
407 end if;
Nico Huber7bb10c62018-01-12 14:07:44 +0100408 end;
409 end loop;
410 end Prepare_Cursors;
411
Nico Huber3b654a02017-07-15 22:27:14 +0200412 Pipes : GMA.Pipe_Configs;
413
Nico Huberd8282b62018-06-18 00:44:55 +0200414 procedure Prepare_Configs (Rotation : Rotation_Type; Gen : Rand_P.Generator)
Nico Huber1d0abe42017-03-05 14:14:09 +0100415 is
416 use type HW.GFX.GMA.Port_Type;
417
Nico Huberfda2d6e2017-07-09 16:47:52 +0200418 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200419 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100420 begin
421 GMA.Display_Probing.Scan_Ports (Pipes);
422
423 for Pipe in GMA.Pipe_Index loop
424 if Pipes (Pipe).Port /= GMA.Disabled then
425 Calc_Framebuffer
426 (FB => Pipes (Pipe).Framebuffer,
427 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200428 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100429 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200430 GMA.Setup_Default_FB
431 (FB => Pipes (Pipe).Framebuffer,
432 Clear => False,
433 Success => Success);
434 if not Success then
435 Pipes (Pipe).Port := GMA.Disabled;
436 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100437 end if;
Nico Huber5903f922020-10-05 14:15:57 +0200438 Prepare_Cursors (Cursors (Pipe), Rotation, Offset);
Nico Huberd8282b62018-06-18 00:44:55 +0200439 Pipes (Pipe).Cursor := Cursors (Pipe) (Cursor_Size'Val (Rand (Gen) mod 3));
Nico Huber1d0abe42017-03-05 14:14:09 +0100440 end loop;
441
442 GMA.Dump_Configs (Pipes);
443 end Prepare_Configs;
444
Nico Hubera63e8332018-02-01 16:41:30 +0100445 procedure Script_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200446 (Pipes : in out GMA.Pipe_Configs;
447 Hotplug_List : out Display_Probing.Port_List;
448 Total_Deadline : in Time.T;
449 Time_MS : in Natural)
Nico Hubera63e8332018-02-01 16:41:30 +0100450 is
451 type Corner is (UL, UR, LR, LL);
452 type Cursor_Script_Entry is record
453 Rel : Corner;
454 X, Y : Int32;
455 end record;
456 Cursor_Script : constant array (Natural range 0 .. 19) of Cursor_Script_Entry :=
457 ((UL, 16, 16), (UL, 16, 16), (UL, 16, 16), (UL, -32, 0), (UL, 16, 16),
458 (UR, -16, 16), (UR, -16, 16), (UR, -16, 16), (UR, 0, -32), (UR, -16, 16),
459 (LR, -16, -16), (LR, -16, -16), (LR, -16, -16), (LR, 32, 0), (LR, -16, -16),
460 (LL, 16, -16), (LL, 16, -16), (LL, 16, -16), (LL, 0, 32), (LL, 16, -16));
461
462 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200463 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100464 Timed_Out : Boolean := False;
465 Cnt : Word32 := 0;
466 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200467 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100468 loop
469 for Pipe in Pipe_Index loop
470 exit when Pipes (Pipe).Port = GMA.Disabled;
471 declare
472 C : Cursor_Type renames Pipes (Pipe).Cursor;
473 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huber3d7c33f2020-10-01 15:14:38 +0200474 Width : constant Width_Type := FB.Width;
475 Height : constant Height_Type := FB.Height;
Nico Hubera63e8332018-02-01 16:41:30 +0100476 CS : Cursor_Script_Entry renames Cursor_Script
477 (Natural (Cnt) mod (Cursor_Script'Last + 1));
478 begin
479 C.Center_X := CS.X;
480 C.Center_Y := CS.Y;
481 case CS.Rel is
482 when UL => null;
483 when UR => C.Center_X := CS.X + Width;
484 when LR => C.Center_X := CS.X + Width;
485 C.Center_Y := CS.Y + Height;
486 when LL => C.Center_Y := CS.Y + Height;
487 end case;
488 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
489 end;
490 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200491
492 Timed_Out := Time.Timed_Out (HP_Deadline);
493 if Timed_Out then
494 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
495 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
496 if Hotplug_List (Hotplug_List'First) /= Disabled then
497 return;
498 end if;
499 end if;
500
501 Timed_Out := Time.Timed_Out (Total_Deadline);
502 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100503 Timed_Out := Time.Timed_Out (Deadline);
504 exit when Timed_Out;
505 Time.M_Delay (160);
506 Cnt := Cnt + 1;
507 end loop;
508 end Script_Cursors;
509
510 type Cursor_Info is record
511 X_Velo, Y_Velo : Int32;
512 X_Acc, Y_Acc : Int32;
513 Color : Pipe_Index;
514 Size : Cursor_Size;
515 end record;
Nico Huberd8282b62018-06-18 00:44:55 +0200516 function Cursor_Rand (Gen : Rand_P.Generator)
517 return Int32 is (Rand (Gen) mod 51 - 25);
518 Cursor_Infos : array (Pipe_Index) of Cursor_Info;
Nico Hubera63e8332018-02-01 16:41:30 +0100519
520 procedure Move_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200521 (Pipes : in out GMA.Pipe_Configs;
522 Hotplug_List : out Display_Probing.Port_List;
523 Total_Deadline : in Time.T;
524 Time_MS : in Natural;
525 Gen : in Rand_P.Generator)
Nico Hubera63e8332018-02-01 16:41:30 +0100526 is
527 procedure Select_New_Cursor
528 (P : in Pipe_Index;
529 C : in out Cursor_Type;
530 CI : in out Cursor_Info)
531 is
532 Old_C : constant Cursor_Type := C;
533 begin
534 -- change either size or color
Nico Huberd8282b62018-06-18 00:44:55 +0200535 if Rand (Gen) mod 2 = 0 then
Nico Hubera63e8332018-02-01 16:41:30 +0100536 CI.Color := Pipe_Index'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200537 ((Pipe_Index'Pos (CI.Color) + 1 + Rand (Gen) mod 2) mod 3);
Nico Hubera63e8332018-02-01 16:41:30 +0100538 else
539 CI.Size := Cursor_Size'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200540 ((Cursor_Size'Pos (CI.Size) + 1 + Rand (Gen) mod 2) mod 3);
Nico Hubera63e8332018-02-01 16:41:30 +0100541 end if;
Nico Huberca2ec162026-07-02 17:41:21 +0000542
543 -- always ensure the cursor fits
544 if Cursor_Width (CI.Size) > Pipes (P).Framebuffer.Width or
545 Cursor_Width (CI.Size) > Pipes (P).Framebuffer.Height
546 then
547 CI.Size := Cursor_64x64;
548 end if;
549
Nico Hubera63e8332018-02-01 16:41:30 +0100550 C := Cursors (CI.Color) (CI.Size);
551 C.Center_X := Old_C.Center_X;
552 C.Center_Y := Old_C.Center_Y;
553 GMA.Update_Cursor (P, C);
554 end Select_New_Cursor;
555
556 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200557 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100558 Timed_Out : Boolean := False;
559 Cnt : Word32 := 0;
560 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200561 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100562 for Pipe in Pipe_Index loop
563 exit when Pipes (Pipe).Port = GMA.Disabled;
564 Select_New_Cursor (Pipe, Pipes (Pipe).Cursor, Cursor_Infos (Pipe));
565 end loop;
566 loop
567 for Pipe in Pipe_Index loop
568 exit when Pipes (Pipe).Port = GMA.Disabled;
569 declare
570 C : Cursor_Type renames Pipes (Pipe).Cursor;
571 CI : Cursor_Info renames Cursor_Infos (Pipe);
572 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huber3d7c33f2020-10-01 15:14:38 +0200573 Width : constant Width_Type := FB.Width;
574 Height : constant Height_Type := FB.Height;
Nico Hubera63e8332018-02-01 16:41:30 +0100575
576 Update : Boolean := False;
577 begin
578 if Cnt mod 16 = 0 then
Nico Huberd8282b62018-06-18 00:44:55 +0200579 CI.X_Acc := Cursor_Rand (Gen);
580 CI.Y_Acc := Cursor_Rand (Gen);
Nico Hubera63e8332018-02-01 16:41:30 +0100581 end if;
582 CI.X_Velo := CI.X_Velo + CI.X_Acc;
583 CI.Y_Velo := CI.Y_Velo + CI.Y_Acc;
584 C.Center_X := C.Center_X + CI.X_Velo / 100;
585 C.Center_Y := C.Center_Y + CI.Y_Velo / 100;
586 if C.Center_X not in 0 .. Width - 1 then
Nico Huberac2a1412026-07-02 10:41:59 +0200587 C.Center_X := Int32'Max (0, Int32'Min (Width, C.Center_X));
588 CI.X_Velo := -CI.X_Velo;
589 Update := True;
Nico Hubera63e8332018-02-01 16:41:30 +0100590 end if;
591 if C.Center_Y not in 0 .. Height - 1 then
Nico Huberac2a1412026-07-02 10:41:59 +0200592 C.Center_Y := Int32'Max (0, Int32'Min (Height, C.Center_Y));
593 CI.Y_Velo := -CI.Y_Velo;
594 Update := True;
Nico Hubera63e8332018-02-01 16:41:30 +0100595 end if;
596 if Update then
597 Select_New_Cursor (Pipe, C, CI);
598 else
599 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
600 end if;
601 end;
602 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200603
604 Timed_Out := Time.Timed_Out (HP_Deadline);
605 if Timed_Out then
606 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
607 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
608 if Hotplug_List (Hotplug_List'First) /= Disabled then
609 return;
610 end if;
611 end if;
612
613 Timed_Out := Time.Timed_Out (Total_Deadline);
614 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100615 Timed_Out := Time.Timed_Out (Deadline);
616 exit when Timed_Out;
617 Time.M_Delay (16); -- ~60 fps
618 Cnt := Cnt + 1;
619 end loop;
620 end Move_Cursors;
621
Nico Huber5903f922020-10-05 14:15:57 +0200622 procedure Run_The_Show
623 (Deadline : Time.T;
624 Gen : Rand_P.Generator;
625 Rotation : Rotation_Type)
Nico Hubera563ec22019-09-29 19:07:27 +0200626 is
627 Timed_Out : Boolean;
628 Hotplug_List : GMA.Display_Probing.Port_List;
629
630 New_Pipes : GMA.Pipe_Configs := Pipes;
631
632 function Rand_Div (Num : Position_Type) return Position_Type is
633 (case Rand (Gen) mod 4 is
634 when 3 => Rand (Gen) mod Num / 3,
635 when 2 => Rand (Gen) mod Num / 2,
636 when 1 => Rand (Gen) mod Num,
637 when others => 0);
638 begin
639 for Pipe in GMA.Pipe_Index loop
640 if Pipes (Pipe).Port /= GMA.Disabled then
641 Test_Screen
642 (Framebuffer => Pipes (Pipe).Framebuffer,
643 Pipe => Pipe);
644 end if;
645 for Size in Cursor_Size loop
Nico Huber5903f922020-10-05 14:15:57 +0200646 Draw_Cursor (Pipe, Rotation, Cursors (Pipe) (Size));
Nico Hubera563ec22019-09-29 19:07:27 +0200647 end loop;
648 end loop;
649
650 Cursor_Infos :=
651 (others =>
652 (Color => Pipe_Index'Val (Rand (Gen) mod 3),
Nico Huberb181b9b2026-07-03 10:42:04 +0000653 Size => Cursor_Size'Val (Rand (Gen) mod 3),
Nico Hubera563ec22019-09-29 19:07:27 +0200654 others => Cursor_Rand (Gen)));
655
656 Script_Cursors (Pipes, Hotplug_List, Deadline, Primary_Delay_MS);
657 if Hotplug_List (Hotplug_List'First) /= Disabled then
658 return;
659 end if;
660 Timed_Out := Time.Timed_Out (Deadline);
661 if Timed_Out then
662 return;
663 end if;
664
665 Rand_P.Reset (Gen, Seed);
666 loop
667 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
668 if Hotplug_List (Hotplug_List'First) /= Disabled then
669 return;
670 end if;
671 New_Pipes := Pipes;
672 for Pipe in GMA.Pipe_Index loop
673 exit when Pipes (Pipe).Port = Disabled;
674 declare
675 New_FB : Framebuffer_Type renames
676 New_Pipes (Pipe).Framebuffer;
677 Cursor : Cursor_Type renames New_Pipes (Pipe).Cursor;
Nico Huberb181b9b2026-07-03 10:42:04 +0000678 CI : Cursor_Info renames Cursor_Infos (Pipe);
Nico Hubera563ec22019-09-29 19:07:27 +0200679 Width : constant Width_Type :=
680 Pipes (Pipe).Framebuffer.Width;
681 Height : constant Height_Type :=
682 Pipes (Pipe).Framebuffer.Height;
683 begin
684 New_FB.Start_X := Position_Type'Min
Nico Huber19d13a52026-04-10 16:21:56 +0000685 (Width - 64, Rand_Div (Width));
Nico Hubera563ec22019-09-29 19:07:27 +0200686 New_FB.Start_Y := Position_Type'Min
Nico Huber19d13a52026-04-10 16:21:56 +0000687 (Height - 64, Rand_Div (Height));
Nico Hubera563ec22019-09-29 19:07:27 +0200688 New_FB.Width := Width_Type'Max
Nico Huber19d13a52026-04-10 16:21:56 +0000689 (64, Width - New_FB.Start_X - Rand_Div (Width));
Nico Hubera563ec22019-09-29 19:07:27 +0200690 New_FB.Height := Height_Type'Max
Nico Huber19d13a52026-04-10 16:21:56 +0000691 (64, Height - New_FB.Start_Y - Rand_Div (Height));
Nico Hubera563ec22019-09-29 19:07:27 +0200692
Nico Huberca2ec162026-07-02 17:41:21 +0000693 Cursor.Mode := No_Cursor;
Nico Huber3d7c33f2020-10-01 15:14:38 +0200694 Cursor.Center_X := New_FB.Width / 2;
695 Cursor.Center_Y := New_FB.Height / 2;
Nico Huberb181b9b2026-07-03 10:42:04 +0000696 CI.X_Velo := 6 * Cursor_Rand (Gen) / (Width / New_FB.Width);
697 CI.Y_Velo := 6 * Cursor_Rand (Gen) / (Height / New_FB.Height);
Nico Hubera563ec22019-09-29 19:07:27 +0200698 end;
699 end loop;
700 GMA.Dump_Configs (New_Pipes);
701 GMA.Update_Outputs (New_Pipes);
702 Move_Cursors
703 (New_Pipes, Hotplug_List, Deadline, Secondary_Delay_MS, Gen);
704 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
705
706 Timed_Out := Time.Timed_Out (Deadline);
707 exit when Timed_Out;
708 end loop;
709 end Run_The_Show;
710
Nico Huber3b654a02017-07-15 22:27:14 +0200711 procedure Print_Usage
712 is
713 begin
Nico Huber30d89712021-06-11 14:13:24 +0200714 Debug.Put ("Usage: ");
715 Debug.Put (Ada.Command_Line.Command_Name);
716 Debug.Put_Line (" <delay seconds> [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200717 Debug.New_Line;
718 end Print_Usage;
719
Nico Huber1d0abe42017-03-05 14:14:09 +0100720 procedure Main
721 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100722 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200723 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100724 use type Interfaces.C.int;
725
Nico Huberfda2d6e2017-07-09 16:47:52 +0200726 Res_Addr : Word64;
727
Nico Hubera455f0e2018-01-07 11:40:40 +0100728 Delay_MS : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200729 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200730
Nico Huberfda2d6e2017-07-09 16:47:52 +0200731 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100732 Initialized : Boolean;
733
Nico Huberd8282b62018-06-18 00:44:55 +0200734 Gen : Rand_P.Generator;
735
Nico Hubera563ec22019-09-29 19:07:27 +0200736 Deadline : Time.T;
737 Timed_Out : Boolean;
738 Hotplug_List : GMA.Display_Probing.Port_List;
739
Nico Huber1d0abe42017-03-05 14:14:09 +0100740 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
741 pragma Import (C, iopl, "iopl");
742 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200743 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200744 Print_Usage;
745 return;
746 end if;
747
Nico Hubera455f0e2018-01-07 11:40:40 +0100748 Delay_MS := Natural'Value (Ada.Command_Line.Argument (1)) * 1_000;
Nico Huber3b654a02017-07-15 22:27:14 +0200749
Nico Huber88f3c982017-08-28 13:31:38 +0200750 if Ada.Command_Line.Argument_Count >= 2 then
751 declare
752 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
753 begin
754 if Rotation_Degree = "0" then Rotation := No_Rotation;
755 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
756 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
757 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
758 else Print_Usage; return; end if;
759 end;
760 end if;
761
Nico Huber1d0abe42017-03-05 14:14:09 +0100762 if iopl (3) /= 0 then
763 Debug.Put_Line ("Failed to change i/o privilege level.");
764 return;
765 end if;
766
Nico Huberfda2d6e2017-07-09 16:47:52 +0200767 Dev.Initialize (Dev_Init);
768 if not Dev_Init then
769 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100770 return;
771 end if;
772
Nico Huberfda2d6e2017-07-09 16:47:52 +0200773 Dev.Map (Res_Addr, PCI.Res2, WC => True);
774 if Res_Addr = 0 then
775 Debug.Put_Line ("Failed to map PCI resource2.");
776 return;
777 end if;
778 Screen.Set_Base_Address (Res_Addr);
779
Nico Huber1d0abe42017-03-05 14:14:09 +0100780 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200781 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100782 Success => Initialized);
783
784 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200785 Backup_GTT;
786
Nico Hubera563ec22019-09-29 19:07:27 +0200787 Deadline := Time.MS_From_Now (Delay_MS);
788 loop
789 Prepare_Configs (Rotation, Gen);
Nico Huber1d0abe42017-03-05 14:14:09 +0100790
Nico Hubera563ec22019-09-29 19:07:27 +0200791 GMA.Update_Outputs (Pipes);
Nico Huber1d0abe42017-03-05 14:14:09 +0100792
Nico Hubera563ec22019-09-29 19:07:27 +0200793 if not (for all P in Pipe_Index => Pipes (P).Port = Disabled) then
794 for Pipe in GMA.Pipe_Index loop
795 if Pipes (Pipe).Port /= GMA.Disabled then
796 Backup_Screen (Pipes (Pipe).Framebuffer);
797 end if;
Nico Hubera455f0e2018-01-07 11:40:40 +0100798 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200799
Nico Huber5903f922020-10-05 14:15:57 +0200800 Run_The_Show (Deadline, Gen, Rotation);
Nico Hubera563ec22019-09-29 19:07:27 +0200801
802 for Pipe in GMA.Pipe_Index loop
803 if Pipes (Pipe).Port /= GMA.Disabled then
804 Restore_Screen (Pipes (Pipe).Framebuffer);
805 end if;
806 end loop;
807 else
808 loop
809 Time.M_Delay (HP_Delay_MS);
810 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
811 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
812
813 Timed_Out := Time.Timed_Out (Deadline);
814 exit when Timed_Out;
815 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200816 end if;
Nico Hubera563ec22019-09-29 19:07:27 +0200817
818 Timed_Out := Time.Timed_Out (Deadline);
819 exit when Timed_Out;
Nico Huber3b654a02017-07-15 22:27:14 +0200820 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200821
Nico Huber3b654a02017-07-15 22:27:14 +0200822 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100823 end if;
824 end Main;
825
Nico Huberfda2d6e2017-07-09 16:47:52 +0200826end HW.GFX.GMA.GFX_Test;