blob: 833240fc4c49f6b5f355f0eab9d949fe7c1ff6ef [file] [log] [blame]
Nico Huberfda2d6e2017-07-09 16:47:52 +02001with Ada.Unchecked_Conversion;
Nico Huber1d0abe42017-03-05 14:14:09 +01002with Ada.Command_Line;
3with Interfaces.C;
4
Nico Huber3b654a02017-07-15 22:27:14 +02005with HW.Time;
Nico Huber1d0abe42017-03-05 14:14:09 +01006with HW.Debug;
Nico Huberfda2d6e2017-07-09 16:47:52 +02007with HW.PCI.Dev;
8with HW.MMIO_Range;
Nico Huber1d0abe42017-03-05 14:14:09 +01009with HW.GFX.GMA;
Nico Huber3b654a02017-07-15 22:27:14 +020010with HW.GFX.GMA.Config;
Nico Huber1d0abe42017-03-05 14:14:09 +010011with HW.GFX.GMA.Display_Probing;
12
Nico Huberfda2d6e2017-07-09 16:47:52 +020013package body HW.GFX.GMA.GFX_Test
14is
15 pragma Disable_Atomic_Synchronization;
Nico Huber1d0abe42017-03-05 14:14:09 +010016
Nico Huber5ef4d602017-12-13 13:56:47 +010017 Start_X : constant := 0;
18 Start_Y : constant := 0;
19
Nico Huberfda2d6e2017-07-09 16:47:52 +020020 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010021
Nico Huber3b654a02017-07-15 22:27:14 +020022 type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
23 type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
24 package GTT is new MMIO_Range
25 (Base_Addr => 0,
26 Element_T => GTT_PTE_Type,
27 Index_T => GTT_Range,
28 Array_T => GTT_Registers_Type);
29
30 GTT_Backup : GTT_Registers_Type;
31
32 procedure Backup_GTT
33 is
34 begin
35 for Idx in GTT_Range loop
36 GTT.Read (GTT_Backup (Idx), Idx);
37 end loop;
38 end Backup_GTT;
39
40 procedure Restore_GTT
41 is
42 begin
43 for Idx in GTT_Range loop
44 GTT.Write (Idx, GTT_Backup (Idx));
45 end loop;
46 end Restore_GTT;
47
Nico Huber1d0abe42017-03-05 14:14:09 +010048 type Pixel_Type is record
49 Red : Byte;
50 Green : Byte;
51 Blue : Byte;
52 Alpha : Byte;
53 end record;
54
55 for Pixel_Type use record
56 Blue at 0 range 0 .. 7;
57 Green at 1 range 0 .. 7;
58 Red at 2 range 0 .. 7;
59 Alpha at 3 range 0 .. 7;
60 end record;
61
Nico Huber244ea7e2017-08-28 11:38:23 +020062 White : constant Pixel_Type := (255, 255, 255, 255);
63 Black : constant Pixel_Type := ( 0, 0, 0, 255);
64 Red : constant Pixel_Type := (255, 0, 0, 255);
65 Green : constant Pixel_Type := ( 0, 255, 0, 255);
66 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
67
Nico Huberfda2d6e2017-07-09 16:47:52 +020068 function Pixel_To_Word (P : Pixel_Type) return Word32
69 with
70 SPARK_Mode => Off
71 is
72 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
73 begin
74 return To_Word (P);
75 end Pixel_To_Word;
76
Nico Huber1d0abe42017-03-05 14:14:09 +010077 Max_W : constant := 4096;
78 Max_H : constant := 2160;
79 FB_Align : constant := 16#0004_0000#;
Nico Huberfda2d6e2017-07-09 16:47:52 +020080 subtype Screen_Index is Natural range
81 0 .. 3 * (Max_W * Max_H + FB_Align / 4) - 1;
82 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +010083
Nico Huber34be6542017-12-13 09:26:24 +010084 function Screen_Offset (FB : Framebuffer_Type) return Natural is
85 (Natural (Phys_Offset (FB) / 4));
86
Nico Huberfda2d6e2017-07-09 16:47:52 +020087 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +010088
Nico Huber3b654a02017-07-15 22:27:14 +020089 Screen_Backup : Screen_Type;
90
91 procedure Backup_Screen (FB : Framebuffer_Type)
92 is
Nico Huber34be6542017-12-13 09:26:24 +010093 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +020094 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
95 begin
96 for Idx in Screen_Index range First .. Last loop
97 Screen.Read (Screen_Backup (Idx), Idx);
98 end loop;
99 end Backup_Screen;
100
101 procedure Restore_Screen (FB : Framebuffer_Type)
102 is
Nico Huber34be6542017-12-13 09:26:24 +0100103 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200104 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
105 begin
106 for Idx in Screen_Index range First .. Last loop
107 Screen.Write (Idx, Screen_Backup (Idx));
108 end loop;
109 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100110
Nico Huber5ef4d602017-12-13 13:56:47 +0100111 function Drawing_Width (FB : Framebuffer_Type) return Natural is
112 (Natural (FB.Width + 2 * Start_X));
113
114 function Drawing_Height (FB : Framebuffer_Type) return Natural is
115 (Natural (FB.Height + 2 * Start_Y));
116
Nico Huber244ea7e2017-08-28 11:38:23 +0200117 function Corner_Fill
118 (X, Y : Natural;
119 FB : Framebuffer_Type;
120 Pipe : Pipe_Index)
121 return Pixel_Type
122 is
123 Xrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100124 (if X < 32 then X else X - (Drawing_Width (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200125 Yrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100126 (if Y < 32 then Y else Y - (Drawing_Height (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200127
128 function Color (Idx : Natural) return Pixel_Type is
129 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
130 when 0 => Blue, when 1 => Black,
131 when 3 => Green, when others => Red);
132 begin
133 return
134 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
135 White
136 elsif Yrel < 16 then
137 (if Xrel < 16 then Color (0) else Color (1))
138 else
139 (if Xrel < 16 then Color (3) else Color (2)));
140 end Corner_Fill;
141
Nico Huber1d0abe42017-03-05 14:14:09 +0100142 function Fill
143 (X, Y : Natural;
144 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200145 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100146 return Pixel_Type
147 is
148 use type HW.Byte;
149
Nico Huber5ef4d602017-12-13 13:56:47 +0100150 Xp : constant Natural := X * 256 / Drawing_Width (Framebuffer);
151 Yp : constant Natural := Y * 256 / Drawing_Height (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100152 Xn : constant Natural := 255 - Xp;
153 Yn : constant Natural := 255 - Yp;
154
155 function Map (X, Y : Natural) return Byte is
156 begin
157 return Byte (X * Y / 255);
158 end Map;
159 begin
160 return
161 (case Pipe is
162 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
163 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
164 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
165 end Fill;
166
167 procedure Test_Screen
168 (Framebuffer : Framebuffer_Type;
169 Pipe : GMA.Pipe_Index)
170 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100171 P : Pixel_Type;
172 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100173 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100174 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200175
176 function Top_Test (X, Y : Natural) return Boolean
177 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100178 C : constant Natural := Drawing_Width (Framebuffer) / 2;
179 S_Y : constant Natural := 3 * (Y - Start_Y) / 2;
Nico Huber9ca69f12017-08-28 14:31:46 +0200180 Left : constant Integer := X - C + S_Y;
181 Right : constant Integer := X - C - S_Y;
182 begin
183 return
Nico Huber5ef4d602017-12-13 13:56:47 +0100184 (Y - Start_Y) < 12 and
Nico Huber9ca69f12017-08-28 14:31:46 +0200185 ((-1 <= Left and Left <= 0) or
186 (0 <= Right and Right <= 1));
187 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100188 begin
Nico Huber5ef4d602017-12-13 13:56:47 +0100189 for Y in 0 .. Drawing_Height (Framebuffer) - 1 loop
Nico Huber1d0abe42017-03-05 14:14:09 +0100190 Offset := Offset_Y;
Nico Huber5ef4d602017-12-13 13:56:47 +0100191 for X in 0 .. Drawing_Width (Framebuffer) - 1 loop
192 if (X < 32 or X >= Drawing_Width (Framebuffer) - 32) and
193 (Y < 32 or Y >= Drawing_Height (Framebuffer) - 32)
Nico Huber244ea7e2017-08-28 11:38:23 +0200194 then
195 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200196 elsif Framebuffer.Rotation /= No_Rotation and then
197 Top_Test (X, Y)
198 then
199 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200200 elsif Y mod 16 = 0 or X mod 16 = 0 then
201 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100202 else
203 P := Fill (X, Y, Framebuffer, Pipe);
204 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200205 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100206 Offset := Offset + 1;
207 end loop;
208 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
209 end loop;
210 end Test_Screen;
211
212 procedure Calc_Framebuffer
213 (FB : out Framebuffer_Type;
214 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200215 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100216 Offset : in out Word32)
217 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100218 Width : constant Width_Type := Width_Type (Mode.H_Visible);
219 Height : constant Height_Type := Height_Type (Mode.V_Visible);
Nico Huber1d0abe42017-03-05 14:14:09 +0100220 begin
221 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200222 if Rotation = Rotated_90 or Rotation = Rotated_270 then
223 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100224 (Width => Width_Type (Height),
225 Height => Height_Type (Width),
226 Start_X => Start_X,
227 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200228 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100229 Stride => Div_Round_Up (Pos32 (Height + 2 * Start_X), 32) * 32,
230 V_Stride => Div_Round_Up (Pos32 (Width + 2 * Start_Y), 32) * 32,
Nico Huber88f3c982017-08-28 13:31:38 +0200231 Tiling => Y_Tiled,
232 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100233 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200234 else
235 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100236 (Width => Width,
237 Height => Height,
238 Start_X => Start_X,
239 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200240 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100241 Stride => Div_Round_Up (Width + 2 * Start_X, 16) * 16,
242 V_Stride => Height + 2 * Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200243 Tiling => Linear,
244 Rotation => Rotation,
245 Offset => Offset);
246 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100247 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100248 end Calc_Framebuffer;
249
Nico Huber3b654a02017-07-15 22:27:14 +0200250 Pipes : GMA.Pipe_Configs;
251
Nico Huber88f3c982017-08-28 13:31:38 +0200252 procedure Prepare_Configs (Rotation : Rotation_Type)
Nico Huber1d0abe42017-03-05 14:14:09 +0100253 is
254 use type HW.GFX.GMA.Port_Type;
255
Nico Huberfda2d6e2017-07-09 16:47:52 +0200256 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200257 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100258 begin
259 GMA.Display_Probing.Scan_Ports (Pipes);
260
261 for Pipe in GMA.Pipe_Index loop
262 if Pipes (Pipe).Port /= GMA.Disabled then
263 Calc_Framebuffer
264 (FB => Pipes (Pipe).Framebuffer,
265 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200266 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100267 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200268 GMA.Setup_Default_FB
269 (FB => Pipes (Pipe).Framebuffer,
270 Clear => False,
271 Success => Success);
272 if not Success then
273 Pipes (Pipe).Port := GMA.Disabled;
274 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100275 end if;
276 end loop;
277
278 GMA.Dump_Configs (Pipes);
279 end Prepare_Configs;
280
Nico Huber3b654a02017-07-15 22:27:14 +0200281 procedure Print_Usage
282 is
283 begin
284 Debug.Put_Line
Nico Huber88f3c982017-08-28 13:31:38 +0200285 ("Usage: " & Ada.Command_Line.Command_Name &
286 " <delay seconds>" &
287 " [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200288 Debug.New_Line;
289 end Print_Usage;
290
Nico Huber1d0abe42017-03-05 14:14:09 +0100291 procedure Main
292 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100293 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200294 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100295 use type Interfaces.C.int;
296
Nico Huberfda2d6e2017-07-09 16:47:52 +0200297 Res_Addr : Word64;
298
Nico Huber3b654a02017-07-15 22:27:14 +0200299 Delay_S : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200300 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200301
Nico Huberfda2d6e2017-07-09 16:47:52 +0200302 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100303 Initialized : Boolean;
304
305 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
306 pragma Import (C, iopl, "iopl");
307 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200308 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200309 Print_Usage;
310 return;
311 end if;
312
313 Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
314
Nico Huber88f3c982017-08-28 13:31:38 +0200315 if Ada.Command_Line.Argument_Count >= 2 then
316 declare
317 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
318 begin
319 if Rotation_Degree = "0" then Rotation := No_Rotation;
320 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
321 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
322 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
323 else Print_Usage; return; end if;
324 end;
325 end if;
326
Nico Huber1d0abe42017-03-05 14:14:09 +0100327 if iopl (3) /= 0 then
328 Debug.Put_Line ("Failed to change i/o privilege level.");
329 return;
330 end if;
331
Nico Huberfda2d6e2017-07-09 16:47:52 +0200332 Dev.Initialize (Dev_Init);
333 if not Dev_Init then
334 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100335 return;
336 end if;
337
Nico Huber3b654a02017-07-15 22:27:14 +0200338 Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
339 if Res_Addr = 0 then
340 Debug.Put_Line ("Failed to map PCI resource0.");
341 return;
342 end if;
343 GTT.Set_Base_Address (Res_Addr);
344
Nico Huberfda2d6e2017-07-09 16:47:52 +0200345 Dev.Map (Res_Addr, PCI.Res2, WC => True);
346 if Res_Addr = 0 then
347 Debug.Put_Line ("Failed to map PCI resource2.");
348 return;
349 end if;
350 Screen.Set_Base_Address (Res_Addr);
351
Nico Huber1d0abe42017-03-05 14:14:09 +0100352 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200353 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100354 Success => Initialized);
355
356 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200357 Backup_GTT;
358
Nico Huber88f3c982017-08-28 13:31:38 +0200359 Prepare_Configs (Rotation);
Nico Huber1d0abe42017-03-05 14:14:09 +0100360
361 GMA.Update_Outputs (Pipes);
362
363 for Pipe in GMA.Pipe_Index loop
364 if Pipes (Pipe).Port /= GMA.Disabled then
Nico Huber3b654a02017-07-15 22:27:14 +0200365 Backup_Screen (Pipes (Pipe).Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100366 Test_Screen
367 (Framebuffer => Pipes (Pipe).Framebuffer,
368 Pipe => Pipe);
369 end if;
370 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200371
372 Time.M_Delay (Delay_S * 1_000);
373
374 for Pipe in GMA.Pipe_Index loop
375 if Pipes (Pipe).Port /= GMA.Disabled then
376 Restore_Screen (Pipes (Pipe).Framebuffer);
377 end if;
378 end loop;
379 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100380 end if;
381 end Main;
382
Nico Huberfda2d6e2017-07-09 16:47:52 +0200383end HW.GFX.GMA.GFX_Test;