gma: Add option for VGA plane on the primary pipe

Add special VGA_PLANE_FRAMEBUFFER_OFFSET that, if set on the primary
pipe, toggles the use of the legacy VGA plane instead of the `hires`
plane.

The caller is responsible for the configuration of the VGA plane and
has to specify the framebuffer width and height accordingly.

Change-Id: I9f678fe033d835c9183fbb2d2b05b6585eb545ca
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/17276
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
index b902b77..1f13c86 100644
--- a/common/hw-gfx-gma-pipe_setup.adb
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -47,6 +47,22 @@
 
    SPCNTR_ENABLE : constant :=  1 * 2 ** 31;
 
+   VGA_SR01_SCREEN_OFF                 : constant :=        1 * 2 **  5;
+
+   VGA_CONTROL_VGA_DISPLAY_DISABLE     : constant :=        1 * 2 ** 31;
+   VGA_CONTROL_BLINK_DUTY_CYCLE_MASK   : constant := 16#0003# * 2 **  6;
+   VGA_CONTROL_BLINK_DUTY_CYCLE_50     : constant :=        2 * 2 **  6;
+   VGA_CONTROL_VSYNC_BLINK_RATE_MASK   : constant := 16#003f# * 2 **  0;
+
+   subtype VGA_Cycle_Count is Pos32 range 2 .. 128;
+   function VGA_CONTROL_VSYNC_BLINK_RATE
+     (Cycles : VGA_Cycle_Count)
+      return Word32
+   is
+   begin
+      return Word32 (Cycles) / 2 - 1;
+   end VGA_CONTROL_VSYNC_BLINK_RATE;
+
    TRANS_CLK_SEL_PORT_NONE : constant := 0 * 2 ** 29;
 
    type TRANS_CLK_SEL_PORT_Array is
@@ -311,9 +327,8 @@
 
    ----------------------------------------------------------------------------
 
-   procedure Setup_Display
+   procedure Setup_Hires_Plane
      (Controller  : in     Controller_Type;
-      Head        : in     Head_Type;
       Mode        : in     HW.GFX.Mode_Type;
       Framebuffer : in     HW.GFX.Framebuffer_Type)
    with
@@ -323,7 +338,6 @@
             =>+
               (Registers.Register_State,
                Controller,
-               Head,
                Mode,
                Framebuffer))
    is
@@ -341,13 +355,7 @@
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
 
-      Registers.Write
-        (Register => Controller.PIPESRC,
-         Value    => Encode
-           (Pos16 (Framebuffer.Height), Pos16 (Framebuffer.Width)));
-
       if Config.Has_Plane_Control then
-         Setup_Watermarks (Controller);
          Registers.Write
            (Register    => Controller.PLANE_CTL,
             Value       => PLANE_CTL_PLANE_ENABLE or
@@ -376,6 +384,57 @@
          end if;
          Registers.Write (Controller.DSPTILEOFF, 0);
       end if;
+   end Setup_Hires_Plane;
+
+   procedure Setup_Display
+     (Controller  : in     Controller_Type;
+      Head        : in     Head_Type;
+      Mode        : in     HW.GFX.Mode_Type;
+      Framebuffer : in     HW.GFX.Framebuffer_Type)
+   with
+      Global => (In_Out => (Registers.Register_State, Port_IO.State)),
+      Depends =>
+        (Registers.Register_State
+            =>+
+              (Registers.Register_State,
+               Controller,
+               Head,
+               Mode,
+               Framebuffer),
+         Port_IO.State
+            =>+
+               (Framebuffer))
+   is
+      use type Word8;
+
+      Reg8 : Word8;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_Plane_Control then
+         Setup_Watermarks (Controller);
+      end if;
+
+      if Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.VGACNTRL,
+            Mask_Unset  => VGA_CONTROL_VGA_DISPLAY_DISABLE or
+                           VGA_CONTROL_BLINK_DUTY_CYCLE_MASK or
+                           VGA_CONTROL_VSYNC_BLINK_RATE_MASK,
+            Mask_Set    => VGA_CONTROL_BLINK_DUTY_CYCLE_50 or
+                           VGA_CONTROL_VSYNC_BLINK_RATE (30));
+
+         Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
+         Port_IO.InB  (Reg8, VGA_SR_DATA);
+         Port_IO.OutB (VGA_SR_DATA, Reg8 and not (VGA_SR01_SCREEN_OFF));
+      else
+         Setup_Hires_Plane (Controller, Mode, Framebuffer);
+      end if;
+
+      Registers.Write
+        (Register => Controller.PIPESRC,
+         Value    => Encode
+           (Pos16 (Framebuffer.Height), Pos16 (Framebuffer.Width)));
 
       Registers.Write (Head.HTOTAL,  Encode (Mode.H_Visible,    Mode.H_Total));
       Registers.Write (Head.HBLANK,  Encode (Mode.H_Visible,    Mode.H_Total));
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index f8b0e7b..c50d943 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -192,16 +192,19 @@
    begin
       -- No downscaling
       -- Respect maximum scalable width
-      -- Only 32bpp RGB
-      -- Stride must be a multiple of 64
+      -- VGA plane is only allowed on the primary pipe
+      -- Only 32bpp RGB (ignored for VGA plane)
+      -- Stride must be a multiple of 64 (ignored for VGA plane)
       return
          ((Framebuffer.Width = Pos32 (Port_Cfg.Mode.H_Visible) and
            Framebuffer.Height = Pos32 (Port_Cfg.Mode.V_Visible)) or
           (Framebuffer.Width <= Config.Maximum_Scalable_Width (I) and
            Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
            Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))) and
-         Framebuffer.BPC = 8 and
-         Framebuffer.Stride mod 64 = 0;
+         (Framebuffer.Offset /= VGA_PLANE_FRAMEBUFFER_OFFSET or I = Primary) and
+         (Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET or
+          (Framebuffer.BPC = 8 and
+           Framebuffer.Stride mod 64 = 0));
    end Validate_Config;
 
    procedure Fill_Port_Config
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index 1e64cb4..ed240ca 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -58,6 +58,10 @@
    type Config_Index is (Primary, Secondary, Tertiary);
    type Configs_Type is array (Config_Index) of Config_Type;
 
+   -- Special framebuffer offset to indicate legacy VGA plane.
+   -- Only valid on primary pipe.
+   VGA_PLANE_FRAMEBUFFER_OFFSET : constant := 16#ffff_ffff#;
+
    procedure Initialize
      (MMIO_Base   : in     Word64 := 0;
       Write_Delay : in     Word64 := 0;