gma: Refactor Hotplug_Detect() interface and usage

Checking for hotplug events should be done before any other work to
reduce load and (spurious) debug output. Therefor, use the simpler
`Port_Type` in the interface of `Hotplug_Detect()` so we don't have to
fill the whole `Port_Cfg` before checking for events.

Also, now that it's possible, factor the disabling of a single output
out of `Update_Outputs()`.

Change-Id: I2a0ba5530c8d511fa95f9cac12297ad428a40d77
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/18119
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/common/haswell_shared/hw-gfx-gma-port_detect.adb b/common/haswell_shared/hw-gfx-gma-port_detect.adb
index 9987aca..2f5854d 100644
--- a/common/haswell_shared/hw-gfx-gma-port_detect.adb
+++ b/common/haswell_shared/hw-gfx-gma-port_detect.adb
@@ -1,5 +1,5 @@
 --
--- Copyright (C) 2016 secunet Security Networks AG
+-- Copyright (C) 2016-2017 secunet Security Networks AG
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -14,6 +14,7 @@
 
 with HW.GFX.GMA.Config;
 with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Config_Helpers;
 
 package body HW.GFX.GMA.Port_Detect
 is
@@ -149,11 +150,13 @@
       end loop;
    end Initialize;
 
-   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+   procedure Hotplug_Detect (Port : in Active_Port_Type; Detected : out Boolean)
    is
       Ctl32 : Word32;
+      GPU_Port : constant GMA.GPU_Port :=
+         Config_Helpers.To_GPU_Port (Primary, Port);
    begin
-      if Port_Cfg.Display = VGA then
+      if Port = Analog then
          Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
          Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
          Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
@@ -162,7 +165,7 @@
               (Register => Registers.PCH_ADPA,
                Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK);
          end if;
-      elsif Config.Has_HOTPLUG_CTL and then Port_Cfg.Port = DIGI_A then
+      elsif Config.Has_HOTPLUG_CTL and then GPU_Port = DIGI_A then
          Registers.Read (Registers.HOTPLUG_CTL, Ctl32, Verbose => False);
          Detected := (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT) /= 0;
 
@@ -171,16 +174,15 @@
               (Register => Registers.HOTPLUG_CTL,
                Mask     => HOTPLUG_CTL_DDI_A_HPD_STATUS);
          end if;
-      elsif Port_Cfg.Port in DIGI_A .. DIGI_D then
+      elsif GPU_Port in DIGI_A .. DIGI_D then
          Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
-         Detected :=
-           (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (Port_Cfg.Port)) /= 0;
+         Detected := (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (GPU_Port)) /= 0;
 
-         if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port)) /= 0 then
+         if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (GPU_Port)) /= 0 then
             Registers.Unset_And_Set_Mask
               (Register    => Registers.SHOTPLUG_CTL,
                Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
-               Mask_Set    => SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port));
+               Mask_Set    => SHOTPLUG_CTL_HPD_STATUS (GPU_Port));
          end if;
       else
          Detected := False;
diff --git a/common/hw-gfx-gma-config_helpers.ads b/common/hw-gfx-gma-config_helpers.ads
index b56e1b1..1029177 100644
--- a/common/hw-gfx-gma-config_helpers.ads
+++ b/common/hw-gfx-gma-config_helpers.ads
@@ -1,5 +1,5 @@
 --
--- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2015-2017 secunet Security Networks AG
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -17,6 +17,11 @@
 private package HW.GFX.GMA.Config_Helpers
 is
 
+   function To_GPU_Port
+     (Pipe  : Pipe_Index;
+      Port  : Active_Port_Type)
+      return GPU_Port;
+
    function To_PCH_Port (Port : Active_Port_Type) return PCH_Port;
 
    function To_Display_Type (Port : Active_Port_Type) return Display_Type;
diff --git a/common/hw-gfx-gma-port_detect.ads b/common/hw-gfx-gma-port_detect.ads
index c9d339b..1cd8d6b 100644
--- a/common/hw-gfx-gma-port_detect.ads
+++ b/common/hw-gfx-gma-port_detect.ads
@@ -1,5 +1,5 @@
 --
--- Copyright (C) 2016 secunet Security Networks AG
+-- Copyright (C) 2016-2017 secunet Security Networks AG
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
 
    procedure Initialize;
 
-   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean);
+   procedure Hotplug_Detect
+     (Port     : in Active_Port_Type;
+      Detected : out Boolean);
 
 end HW.GFX.GMA.Port_Detect;
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index cbb18a6..005fff7 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -1,5 +1,5 @@
 --
--- Copyright (C) 2014-2016 secunet Security Networks AG
+-- Copyright (C) 2014-2017 secunet Security Networks AG
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -61,7 +61,7 @@
    type PLLs_Type is array (Pipe_Index) of PLLs.T;
 
    type HPD_Type is array (Port_Type) of Boolean;
-   type HPD_Delay_Type is array (Port_Type) of Time.T;
+   type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
 
    Allocated_PLLs : PLLs_Type;
    HPD_Delay : HPD_Delay_Type;
@@ -80,21 +80,6 @@
 
    ----------------------------------------------------------------------------
 
-   procedure Check_HPD
-     (Port_Cfg : in     Port_Config;
-      Port     : in     Port_Type;
-      Detected :    out Boolean)
-   is
-      HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
-   begin
-      if HPD_Delay_Over then
-         Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
-         HPD_Delay (Port) := Time.MS_From_Now (333);
-      else
-         Detected := False;
-      end if;
-   end Check_HPD;
-
    procedure Enable_Output
      (Pipe     : in     Pipe_Index;
       Pipe_Cfg : in     Pipe_Config;
@@ -102,6 +87,10 @@
    is
       Port_Cfg : Port_Config;
    begin
+      pragma Debug (Debug.New_Line);
+      pragma Debug (Debug.Put_Line
+        ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
+
       Config_Helpers.Fill_Port_Config
         (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
 
@@ -110,16 +99,7 @@
            (Pipe_Cfg.Framebuffer, Port_Cfg, Pipe);
       end if;
 
-      if Success and then Wait_For_HPD (Pipe_Cfg.Port) then
-         Check_HPD (Port_Cfg, Pipe_Cfg.Port, Success);
-         Wait_For_HPD (Pipe_Cfg.Port) := not Success;
-      end if;
-
       if Success then
-         pragma Debug (Debug.New_Line);
-         pragma Debug (Debug.Put_Line
-           ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
-
          Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
       end if;
 
@@ -186,6 +166,27 @@
       end if;
    end Enable_Output;
 
+   procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
+   is
+      Port_Cfg : Port_Config;
+      Success  : Boolean;
+   begin
+      Config_Helpers.Fill_Port_Config
+        (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
+      if Success then
+         pragma Debug (Debug.New_Line);
+         pragma Debug (Debug.Put_Line
+           ("Disabling port " & Port_Names (Pipe_Cfg.Port)));
+         pragma Debug (Debug.New_Line);
+
+         Connectors.Pre_Off (Port_Cfg);
+         Display_Controller.Off (Pipe);
+         Connectors.Post_Off (Port_Cfg);
+
+         PLLs.Free (Allocated_PLLs (Pipe));
+      end if;
+   end Disable_Output;
+
    procedure Update_Outputs (Configs : Pipe_Configs)
    is
       Did_Power_Up : Boolean := False;
@@ -193,7 +194,18 @@
       HPD, Success : Boolean;
       Old_Config, New_Config : Pipe_Config;
       Old_Configs : Pipe_Configs;
-      Port_Cfg : Port_Config;
+
+      procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
+      is
+         HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
+      begin
+         if HPD_Delay_Over then
+            Port_Detect.Hotplug_Detect (Port, Detected);
+            HPD_Delay (Port) := Time.MS_From_Now (333);
+         else
+            Detected := False;
+         end if;
+      end Check_HPD;
    begin
       Old_Configs := Cur_Configs;
 
@@ -203,43 +215,37 @@
          Old_Config := Cur_Configs (I);
          New_Config := Configs (I);
 
-         Config_Helpers.Fill_Port_Config
-           (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
-         if Success then
-            Check_HPD (Port_Cfg, Old_Config.Port, HPD);
+         if Old_Config.Port /= Disabled then
+            Check_HPD (Old_Config.Port, HPD);
          end if;
 
-         -- Connector changed?
-         if (Success and then HPD) or
+         -- hotplug event or configuration changed?
+         if HPD or
             Old_Config.Port /= New_Config.Port or
             Old_Config.Mode /= New_Config.Mode
          then
+            -- disable old configuration if any
             if Old_Config.Port /= Disabled then
-               if Success then
-                  pragma Debug (Debug.New_Line);
-                  pragma Debug (Debug.Put_Line
-                    ("Disabling port " & Port_Names (Old_Config.Port)));
-
-                  Connectors.Pre_Off (Port_Cfg);
-
-                  Display_Controller.Off (I);
-
-                  Connectors.Post_Off (Port_Cfg);
-               end if;
-
-               -- Free PLL
-               PLLs.Free (Allocated_PLLs (I));
-
+               Disable_Output (I, Old_Config);
                Cur_Configs (I).Port := Disabled;
             end if;
 
+            -- enable new configuration if any
             if New_Config.Port /= Disabled then
-               if not Did_Power_Up then
-                  Power_And_Clocks.Power_Up (Old_Configs, Configs);
-                  Did_Power_Up := True;
+               if Wait_For_HPD (New_Config.Port) then
+                  Check_HPD (New_Config.Port, Success);
+                  Wait_For_HPD (New_Config.Port) := not Success;
+               else
+                  Success := True;
                end if;
 
-               Enable_Output (I, New_Config, Success);
+               if Success then
+                  if not Did_Power_Up then
+                     Power_And_Clocks.Power_Up (Old_Configs, Configs);
+                     Did_Power_Up := True;
+                  end if;
+                  Enable_Output (I, New_Config, Success);
+               end if;
 
                if Success then
                   Cur_Configs (I) := New_Config;
@@ -247,6 +253,8 @@
             else
                Cur_Configs (I) := New_Config;
             end if;
+
+         -- update framebuffer offset only
          elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
                Old_Config.Port /= Disabled
          then
diff --git a/common/ironlake/hw-gfx-gma-port_detect.adb b/common/ironlake/hw-gfx-gma-port_detect.adb
index e8cb3fa..ff4ff88 100644
--- a/common/ironlake/hw-gfx-gma-port_detect.adb
+++ b/common/ironlake/hw-gfx-gma-port_detect.adb
@@ -1,5 +1,5 @@
 --
--- Copyright (C) 2016 secunet Security Networks AG
+-- Copyright (C) 2016-2017 secunet Security Networks AG
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -14,6 +14,7 @@
 
 with HW.GFX.GMA.Config;
 with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Config_Helpers;
 
 package body HW.GFX.GMA.Port_Detect
 is
@@ -125,15 +126,15 @@
       end loop;
    end Initialize;
 
-   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+   procedure Hotplug_Detect (Port : in Active_Port_Type; Detected : out Boolean)
    is
       Ctl32 : Word32;
       PCH_Port : constant GMA.PCH_Port :=
-        (case Port_Cfg.PCH_Port is
-            when PCH_DP_B  => PCH_HDMI_B,
-            when PCH_DP_C  => PCH_HDMI_C,
-            when PCH_DP_D  => PCH_HDMI_D,
-            when others    => Port_Cfg.PCH_Port);
+        (case Port is
+            when DP1    => PCH_HDMI_B,
+            when DP2    => PCH_HDMI_C,
+            when DP3    => PCH_HDMI_D,
+            when others => Config_Helpers.To_PCH_Port (Port));
    begin
       case PCH_Port is
          when PCH_DAC =>