gfx_test: Set our own framebuffers up, update README

Use GMA.Setup_Default_FB() to allocate framebuffers in the stolen
memory. To help with setups where we can't unload the i915 driver,
back up and restore the current GTT setup and framebuffer contents.

Also add a wrapper script and update the README.

Change-Id: I10790d35d38b7b211f41b2452f6d2baf17372e31
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/20604
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/gfxtest/gfx_test.sh b/gfxtest/gfx_test.sh
new file mode 100755
index 0000000..9df0005
--- /dev/null
+++ b/gfxtest/gfx_test.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+set -e
+
+usage="Usage: $0 [seconds]\n"
+
+err_msgs=
+command -v chvt >/dev/null || err_msgs="${err_msgs}"'Need `chvt`\n'
+command -v openvt >/dev/null || err_msgs="${err_msgs}"'Need `openvt`\n'
+command -v fgconsole >/dev/null || err_msgs="${err_msgs}"'Need `fgconsole`\n'
+[ -n "$err_msgs" ] && err_msgs="${err_msgs}"'e.g. install the `kbd` package\n'
+
+[ -x build/gfx_test ] || \
+	err_msgs="${err_msgs}"'Please run from *libgfxinit* source dir and build `gfx_test` first.\n'
+
+[ "$#" -gt 1 ] && err_msgs="${err_msgs}${usage}"
+
+if [ -n "$err_msgs" ]; then
+	printf "$err_msgs"
+	exit 1
+fi
+
+if [ "$#" -lt 1 ]; then
+	# default duration of 5s
+	set 5
+fi
+
+reload_i915=0
+prepare_vt() {
+	# switch VT, we might be in X
+	orig_vt=`fgconsole`
+	openvt -s -- true
+
+	# poll until the VT switch is done
+	while [ `fgconsole` -eq $orig_vt ]; do :; done
+
+	# take i915 out of charge
+	echo 0 >/sys/devices/virtual/vtconsole/vtcon1/bind
+
+	# try unloading it
+	if lsmod | grep -q i915 && modprobe -r i915 >/dev/null 2>&1; then
+		reload_i915=1
+	fi
+}
+
+restore_vt() {
+	# reload i915
+	if [ $reload_i915 -eq 1 ]; then
+		modprobe i915
+	fi
+
+	# put i915 back in charge
+	echo 1 >/sys/devices/virtual/vtconsole/vtcon1/bind
+
+	# return to original VT
+	chvt $orig_vt
+}
+
+prepare_vt
+
+build/gfx_test "$@" || true
+
+restore_vt
diff --git a/gfxtest/hw-gfx-gma-gfx_test.adb b/gfxtest/hw-gfx-gma-gfx_test.adb
index ad396fd..d870401 100644
--- a/gfxtest/hw-gfx-gma-gfx_test.adb
+++ b/gfxtest/hw-gfx-gma-gfx_test.adb
@@ -2,10 +2,12 @@
 with Ada.Command_Line;
 with Interfaces.C;
 
+with HW.Time;
 with HW.Debug;
 with HW.PCI.Dev;
 with HW.MMIO_Range;
 with HW.GFX.GMA;
+with HW.GFX.GMA.Config;
 with HW.GFX.GMA.Display_Probing;
 
 package body HW.GFX.GMA.GFX_Test
@@ -14,6 +16,32 @@
 
    package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
 
+   type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
+   type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
+   package GTT is new MMIO_Range
+     (Base_Addr   => 0,
+      Element_T   => GTT_PTE_Type,
+      Index_T     => GTT_Range,
+      Array_T     => GTT_Registers_Type);
+
+   GTT_Backup : GTT_Registers_Type;
+
+   procedure Backup_GTT
+   is
+   begin
+      for Idx in GTT_Range loop
+         GTT.Read (GTT_Backup (Idx), Idx);
+      end loop;
+   end Backup_GTT;
+
+   procedure Restore_GTT
+   is
+   begin
+      for Idx in GTT_Range loop
+         GTT.Write (Idx, GTT_Backup (Idx));
+      end loop;
+   end Restore_GTT;
+
    type Pixel_Type is record
       Red   : Byte;
       Green : Byte;
@@ -46,7 +74,27 @@
 
    package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
 
-   Pipes : GMA.Pipe_Configs;
+   Screen_Backup : Screen_Type;
+
+   procedure Backup_Screen (FB : Framebuffer_Type)
+   is
+      First : constant Screen_Index := Natural (FB.Offset) / 4;
+      Last  : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+   begin
+      for Idx in Screen_Index range First .. Last loop
+         Screen.Read (Screen_Backup (Idx), Idx);
+      end loop;
+   end Backup_Screen;
+
+   procedure Restore_Screen (FB : Framebuffer_Type)
+   is
+      First : constant Screen_Index := Natural (FB.Offset) / 4;
+      Last  : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+   begin
+      for Idx in Screen_Index range First .. Last loop
+         Screen.Write (Idx, Screen_Backup (Idx));
+      end loop;
+   end Restore_Screen;
 
    function Fill
      (X, Y        : Natural;
@@ -113,11 +161,14 @@
       Offset := Offset + Word32 (FB.Stride * FB.Height * 4);
    end Calc_Framebuffer;
 
+   Pipes : GMA.Pipe_Configs;
+
    procedure Prepare_Configs
    is
       use type HW.GFX.GMA.Port_Type;
 
       Offset : Word32 := 0;
+      Success : Boolean;
    begin
       GMA.Display_Probing.Scan_Ports (Pipes);
 
@@ -127,12 +178,27 @@
               (FB       => Pipes (Pipe).Framebuffer,
                Mode     => Pipes (Pipe).Mode,
                Offset   => Offset);
+            GMA.Setup_Default_FB
+              (FB       => Pipes (Pipe).Framebuffer,
+               Clear    => False,
+               Success  => Success);
+            if not Success then
+               Pipes (Pipe).Port := GMA.Disabled;
+            end if;
          end if;
       end loop;
 
       GMA.Dump_Configs (Pipes);
    end Prepare_Configs;
 
+   procedure Print_Usage
+   is
+   begin
+      Debug.Put_Line
+        ("Usage: " & Ada.Command_Line.Command_Name & " <delay seconds>");
+      Debug.New_Line;
+   end Print_Usage;
+
    procedure Main
    is
       use type HW.GFX.GMA.Port_Type;
@@ -141,12 +207,21 @@
 
       Res_Addr : Word64;
 
+      Delay_S : Natural;
+
       Dev_Init,
       Initialized : Boolean;
 
       function iopl (level : Interfaces.C.int) return Interfaces.C.int;
       pragma Import (C, iopl, "iopl");
    begin
+      if Ada.Command_Line.Argument_Count /= 1 then
+         Print_Usage;
+         return;
+      end if;
+
+      Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
+
       if iopl (3) /= 0 then
          Debug.Put_Line ("Failed to change i/o privilege level.");
          return;
@@ -158,6 +233,13 @@
          return;
       end if;
 
+      Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
+      if Res_Addr = 0 then
+         Debug.Put_Line ("Failed to map PCI resource0.");
+         return;
+      end if;
+      GTT.Set_Base_Address (Res_Addr);
+
       Dev.Map (Res_Addr, PCI.Res2, WC => True);
       if Res_Addr = 0 then
          Debug.Put_Line ("Failed to map PCI resource2.");
@@ -170,17 +252,29 @@
          Success     => Initialized);
 
       if Initialized then
+         Backup_GTT;
+
          Prepare_Configs;
 
          GMA.Update_Outputs (Pipes);
 
          for Pipe in GMA.Pipe_Index loop
             if Pipes (Pipe).Port /= GMA.Disabled then
+               Backup_Screen (Pipes (Pipe).Framebuffer);
                Test_Screen
                  (Framebuffer => Pipes (Pipe).Framebuffer,
                   Pipe        => Pipe);
             end if;
          end loop;
+
+         Time.M_Delay (Delay_S * 1_000);
+
+         for Pipe in GMA.Pipe_Index loop
+            if Pipes (Pipe).Port /= GMA.Disabled then
+               Restore_Screen (Pipes (Pipe).Framebuffer);
+            end if;
+         end loop;
+         Restore_GTT;
       end if;
    end Main;