blob: 514e60fa87e02f0c6beda82efd32caaecfb20908 [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 Huberfda2d6e2017-07-09 16:47:52 +020017 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010018
Nico Huber3b654a02017-07-15 22:27:14 +020019 type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
20 type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
21 package GTT is new MMIO_Range
22 (Base_Addr => 0,
23 Element_T => GTT_PTE_Type,
24 Index_T => GTT_Range,
25 Array_T => GTT_Registers_Type);
26
27 GTT_Backup : GTT_Registers_Type;
28
29 procedure Backup_GTT
30 is
31 begin
32 for Idx in GTT_Range loop
33 GTT.Read (GTT_Backup (Idx), Idx);
34 end loop;
35 end Backup_GTT;
36
37 procedure Restore_GTT
38 is
39 begin
40 for Idx in GTT_Range loop
41 GTT.Write (Idx, GTT_Backup (Idx));
42 end loop;
43 end Restore_GTT;
44
Nico Huber1d0abe42017-03-05 14:14:09 +010045 type Pixel_Type is record
46 Red : Byte;
47 Green : Byte;
48 Blue : Byte;
49 Alpha : Byte;
50 end record;
51
52 for Pixel_Type use record
53 Blue at 0 range 0 .. 7;
54 Green at 1 range 0 .. 7;
55 Red at 2 range 0 .. 7;
56 Alpha at 3 range 0 .. 7;
57 end record;
58
Nico Huber244ea7e2017-08-28 11:38:23 +020059 White : constant Pixel_Type := (255, 255, 255, 255);
60 Black : constant Pixel_Type := ( 0, 0, 0, 255);
61 Red : constant Pixel_Type := (255, 0, 0, 255);
62 Green : constant Pixel_Type := ( 0, 255, 0, 255);
63 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
64
Nico Huberfda2d6e2017-07-09 16:47:52 +020065 function Pixel_To_Word (P : Pixel_Type) return Word32
66 with
67 SPARK_Mode => Off
68 is
69 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
70 begin
71 return To_Word (P);
72 end Pixel_To_Word;
73
Nico Huber1d0abe42017-03-05 14:14:09 +010074 Max_W : constant := 4096;
75 Max_H : constant := 2160;
76 FB_Align : constant := 16#0004_0000#;
Nico Huberfda2d6e2017-07-09 16:47:52 +020077 subtype Screen_Index is Natural range
78 0 .. 3 * (Max_W * Max_H + FB_Align / 4) - 1;
79 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +010080
Nico Huberfda2d6e2017-07-09 16:47:52 +020081 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +010082
Nico Huber3b654a02017-07-15 22:27:14 +020083 Screen_Backup : Screen_Type;
84
85 procedure Backup_Screen (FB : Framebuffer_Type)
86 is
87 First : constant Screen_Index := Natural (FB.Offset) / 4;
88 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
89 begin
90 for Idx in Screen_Index range First .. Last loop
91 Screen.Read (Screen_Backup (Idx), Idx);
92 end loop;
93 end Backup_Screen;
94
95 procedure Restore_Screen (FB : Framebuffer_Type)
96 is
97 First : constant Screen_Index := Natural (FB.Offset) / 4;
98 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
99 begin
100 for Idx in Screen_Index range First .. Last loop
101 Screen.Write (Idx, Screen_Backup (Idx));
102 end loop;
103 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100104
Nico Huber244ea7e2017-08-28 11:38:23 +0200105 function Corner_Fill
106 (X, Y : Natural;
107 FB : Framebuffer_Type;
108 Pipe : Pipe_Index)
109 return Pixel_Type
110 is
111 Xrel : constant Integer :=
112 (if X < 32 then X else X - (Natural (FB.Width) - 32));
113 Yrel : constant Integer :=
114 (if Y < 32 then Y else Y - (Natural (FB.Height) - 32));
115
116 function Color (Idx : Natural) return Pixel_Type is
117 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
118 when 0 => Blue, when 1 => Black,
119 when 3 => Green, when others => Red);
120 begin
121 return
122 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
123 White
124 elsif Yrel < 16 then
125 (if Xrel < 16 then Color (0) else Color (1))
126 else
127 (if Xrel < 16 then Color (3) else Color (2)));
128 end Corner_Fill;
129
Nico Huber1d0abe42017-03-05 14:14:09 +0100130 function Fill
131 (X, Y : Natural;
132 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200133 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100134 return Pixel_Type
135 is
136 use type HW.Byte;
137
138 Xp : constant Natural := X * 256 / Natural (Framebuffer.Width);
139 Yp : constant Natural := Y * 256 / Natural (Framebuffer.Height);
140 Xn : constant Natural := 255 - Xp;
141 Yn : constant Natural := 255 - Yp;
142
143 function Map (X, Y : Natural) return Byte is
144 begin
145 return Byte (X * Y / 255);
146 end Map;
147 begin
148 return
149 (case Pipe is
150 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
151 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
152 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
153 end Fill;
154
155 procedure Test_Screen
156 (Framebuffer : Framebuffer_Type;
157 Pipe : GMA.Pipe_Index)
158 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100159 P : Pixel_Type;
160 -- We have pixel offset wheras the framebuffer has a byte offset
161 Offset_Y : Natural := Natural (Framebuffer.Offset / 4);
162 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200163
164 function Top_Test (X, Y : Natural) return Boolean
165 is
166 C : constant Natural := Natural (Framebuffer.Width) / 2;
167 S_Y : constant Natural := 3 * Y / 2;
168 Left : constant Integer := X - C + S_Y;
169 Right : constant Integer := X - C - S_Y;
170 begin
171 return
172 Y < 12 and
173 ((-1 <= Left and Left <= 0) or
174 (0 <= Right and Right <= 1));
175 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100176 begin
177 for Y in 0 .. Natural (Framebuffer.Height) - 1 loop
178 Offset := Offset_Y;
179 for X in 0 .. Natural (Framebuffer.Width) - 1 loop
Nico Huber244ea7e2017-08-28 11:38:23 +0200180 if (X < 32 or X >= Natural (Framebuffer.Width) - 32) and
181 (Y < 32 or Y >= Natural (Framebuffer.Height) - 32)
182 then
183 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200184 elsif Framebuffer.Rotation /= No_Rotation and then
185 Top_Test (X, Y)
186 then
187 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200188 elsif Y mod 16 = 0 or X mod 16 = 0 then
189 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100190 else
191 P := Fill (X, Y, Framebuffer, Pipe);
192 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200193 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100194 Offset := Offset + 1;
195 end loop;
196 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
197 end loop;
198 end Test_Screen;
199
200 procedure Calc_Framebuffer
201 (FB : out Framebuffer_Type;
202 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200203 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100204 Offset : in out Word32)
205 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100206 begin
207 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200208 if Rotation = Rotated_90 or Rotation = Rotated_270 then
209 FB :=
210 (Width => Width_Type (Mode.V_Visible),
211 Height => Height_Type (Mode.H_Visible),
212 BPC => 8,
213 Stride => Div_Round_Up (Pos32 (Mode.V_Visible), 32) * 32,
214 V_Stride => Div_Round_Up (Pos32 (Mode.H_Visible), 32) * 32,
215 Tiling => Y_Tiled,
216 Rotation => Rotation,
217 Offset => Offset);
218 else
219 FB :=
220 (Width => Width_Type (Mode.H_Visible),
221 Height => Width_Type (Mode.V_Visible),
222 BPC => 8,
223 Stride => Div_Round_Up (Pos32 (Mode.H_Visible), 16) * 16,
224 V_Stride => Height_Type (Mode.V_Visible),
225 Tiling => Linear,
226 Rotation => Rotation,
227 Offset => Offset);
228 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100229 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100230 end Calc_Framebuffer;
231
Nico Huber3b654a02017-07-15 22:27:14 +0200232 Pipes : GMA.Pipe_Configs;
233
Nico Huber88f3c982017-08-28 13:31:38 +0200234 procedure Prepare_Configs (Rotation : Rotation_Type)
Nico Huber1d0abe42017-03-05 14:14:09 +0100235 is
236 use type HW.GFX.GMA.Port_Type;
237
Nico Huberfda2d6e2017-07-09 16:47:52 +0200238 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200239 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100240 begin
241 GMA.Display_Probing.Scan_Ports (Pipes);
242
243 for Pipe in GMA.Pipe_Index loop
244 if Pipes (Pipe).Port /= GMA.Disabled then
245 Calc_Framebuffer
246 (FB => Pipes (Pipe).Framebuffer,
247 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200248 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100249 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200250 GMA.Setup_Default_FB
251 (FB => Pipes (Pipe).Framebuffer,
252 Clear => False,
253 Success => Success);
254 if not Success then
255 Pipes (Pipe).Port := GMA.Disabled;
256 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100257 end if;
258 end loop;
259
260 GMA.Dump_Configs (Pipes);
261 end Prepare_Configs;
262
Nico Huber3b654a02017-07-15 22:27:14 +0200263 procedure Print_Usage
264 is
265 begin
266 Debug.Put_Line
Nico Huber88f3c982017-08-28 13:31:38 +0200267 ("Usage: " & Ada.Command_Line.Command_Name &
268 " <delay seconds>" &
269 " [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200270 Debug.New_Line;
271 end Print_Usage;
272
Nico Huber1d0abe42017-03-05 14:14:09 +0100273 procedure Main
274 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100275 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200276 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100277 use type Interfaces.C.int;
278
Nico Huberfda2d6e2017-07-09 16:47:52 +0200279 Res_Addr : Word64;
280
Nico Huber3b654a02017-07-15 22:27:14 +0200281 Delay_S : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200282 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200283
Nico Huberfda2d6e2017-07-09 16:47:52 +0200284 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100285 Initialized : Boolean;
286
287 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
288 pragma Import (C, iopl, "iopl");
289 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200290 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200291 Print_Usage;
292 return;
293 end if;
294
295 Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
296
Nico Huber88f3c982017-08-28 13:31:38 +0200297 if Ada.Command_Line.Argument_Count >= 2 then
298 declare
299 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
300 begin
301 if Rotation_Degree = "0" then Rotation := No_Rotation;
302 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
303 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
304 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
305 else Print_Usage; return; end if;
306 end;
307 end if;
308
Nico Huber1d0abe42017-03-05 14:14:09 +0100309 if iopl (3) /= 0 then
310 Debug.Put_Line ("Failed to change i/o privilege level.");
311 return;
312 end if;
313
Nico Huberfda2d6e2017-07-09 16:47:52 +0200314 Dev.Initialize (Dev_Init);
315 if not Dev_Init then
316 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100317 return;
318 end if;
319
Nico Huber3b654a02017-07-15 22:27:14 +0200320 Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
321 if Res_Addr = 0 then
322 Debug.Put_Line ("Failed to map PCI resource0.");
323 return;
324 end if;
325 GTT.Set_Base_Address (Res_Addr);
326
Nico Huberfda2d6e2017-07-09 16:47:52 +0200327 Dev.Map (Res_Addr, PCI.Res2, WC => True);
328 if Res_Addr = 0 then
329 Debug.Put_Line ("Failed to map PCI resource2.");
330 return;
331 end if;
332 Screen.Set_Base_Address (Res_Addr);
333
Nico Huber1d0abe42017-03-05 14:14:09 +0100334 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200335 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100336 Success => Initialized);
337
338 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200339 Backup_GTT;
340
Nico Huber88f3c982017-08-28 13:31:38 +0200341 Prepare_Configs (Rotation);
Nico Huber1d0abe42017-03-05 14:14:09 +0100342
343 GMA.Update_Outputs (Pipes);
344
345 for Pipe in GMA.Pipe_Index loop
346 if Pipes (Pipe).Port /= GMA.Disabled then
Nico Huber3b654a02017-07-15 22:27:14 +0200347 Backup_Screen (Pipes (Pipe).Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100348 Test_Screen
349 (Framebuffer => Pipes (Pipe).Framebuffer,
350 Pipe => Pipe);
351 end if;
352 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200353
354 Time.M_Delay (Delay_S * 1_000);
355
356 for Pipe in GMA.Pipe_Index loop
357 if Pipes (Pipe).Port /= GMA.Disabled then
358 Restore_Screen (Pipes (Pipe).Framebuffer);
359 end if;
360 end loop;
361 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100362 end if;
363 end Main;
364
Nico Huberfda2d6e2017-07-09 16:47:52 +0200365end HW.GFX.GMA.GFX_Test;