gma: Add G45 support

The following ports are implemented: HDMI/DVI, VGA, LVDS and DP.

Tested with gfx_test and coreboot on a Thinkpad X200 (GM45).

Change-Id: Ifc05a1516329a61772af84558e5bfceb4d4ca277
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/21295
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/Makefile.inc b/common/Makefile.inc
index fc985b3..8d6634e 100644
--- a/common/Makefile.inc
+++ b/common/Makefile.inc
@@ -62,7 +62,9 @@
 	    $< >$@
 gfxinit-gen-y += $(hw-gfx-gma-config-ads)
 
-ifneq ($(filter Ironlake Sandybridge Ivybridge,$(CONFIG_GFX_GMA_CPU)),)
+ifneq ($(filter G45,$(CONFIG_GFX_GMA_CPU)),)
+subdirs-y += g45
+else ifneq ($(filter Ironlake Sandybridge Ivybridge,$(CONFIG_GFX_GMA_CPU)),)
 subdirs-y += ironlake
 else ifneq ($(filter Haswell Broadwell,$(CONFIG_GFX_GMA_CPU)),)
 subdirs-y += haswell_shared haswell
diff --git a/common/g45/Makefile.inc b/common/g45/Makefile.inc
new file mode 100644
index 0000000..6b598cb
--- /dev/null
+++ b/common/g45/Makefile.inc
@@ -0,0 +1,15 @@
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-gmch-dp.adb
+gfxinit-y += hw-gfx-gma-gmch-dp.ads
+gfxinit-y += hw-gfx-gma-gmch-hdmi.adb
+gfxinit-y += hw-gfx-gma-gmch-hdmi.ads
+gfxinit-y += hw-gfx-gma-gmch-lvds.adb
+gfxinit-y += hw-gfx-gma-gmch-lvds.ads
+gfxinit-y += hw-gfx-gma-gmch-vga.adb
+gfxinit-y += hw-gfx-gma-gmch-vga.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-gmch.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.adb
\ No newline at end of file
diff --git a/common/g45/hw-gfx-gma-connectors.adb b/common/g45/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..5ca7c9b
--- /dev/null
+++ b/common/g45/hw-gfx-gma-connectors.adb
@@ -0,0 +1,127 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.GMCH.VGA;
+with HW.GFX.GMA.GMCH.LVDS;
+with HW.GFX.GMA.GMCH.HDMI;
+with HW.GFX.GMA.GMCH.DP;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors
+is
+
+   procedure Post_Reset_Off is null;
+   procedure Initialize is null;
+
+   function Is_Internal (Port_Cfg : Port_Config) return Boolean
+   is
+   begin
+      return Port_Cfg.Port = LVDS;
+   end Is_Internal;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Pipe        : in     Pipe_Index;
+      Port_Cfg    : in     Port_Config;
+      PLL_Hint    : in     Word32;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      Success := True;
+   end Pre_On;
+
+   procedure Post_On
+     (Pipe     : in     Pipe_Index;
+      Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      Success := True;
+      if Port_Cfg.Port = LVDS then
+         GMCH.LVDS.On (Port_Cfg, Pipe);
+      elsif Port_Cfg.Port = VGA then
+         GMCH.VGA.On (Pipe, Port_Cfg.Mode);
+      elsif Port_Cfg.Port in Digital_Port then
+         if Port_Cfg.Display = DP and Port_Cfg.Port in GMCH_DP_Port then
+            GMCH.DP.On (Port_Cfg, Pipe, Success);
+         elsif Port_Cfg.Display = HDMI and Port_Cfg.Port in GMCH_HDMI_Port then
+            GMCH.HDMI.On (Port_Cfg, Pipe);
+         end if;
+      end if;
+
+      if Is_Internal (Port_Cfg) then
+         Panel.On (Wait => False);
+         Panel.Backlight_On;
+      end if;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Is_Internal (Port_Cfg) then
+         Panel.Backlight_Off;
+         Panel.Off;
+      end if;
+   end Pre_Off;
+
+   procedure Post_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      if Port_Cfg.Port = LVDS then
+         GMCH.LVDS.Off;
+      elsif Port_Cfg.Port = VGA then
+         GMCH.VGA.Off;
+      elsif Port_Cfg.Display = DP and Port_Cfg.Port in GMCH_DP_Port then
+         GMCH.DP.Off (Port_Cfg.Port);
+      elsif Port_Cfg.Display = HDMI and Port_Cfg.Port in GMCH_HDMI_Port then
+         GMCH.HDMI.Off (Port_Cfg.Port);
+      end if;
+   end Post_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Panel.Backlight_Off;
+      Panel.Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      GMCH.LVDS.Off;
+      GMCH.VGA.Off;
+      GMCH.DP.All_Off;
+      GMCH.HDMI.All_Off;
+   end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/g45/hw-gfx-gma-gmch-dp.adb b/common/g45/hw-gfx-gma-gmch-dp.adb
new file mode 100644
index 0000000..8cc7f10
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-dp.adb
@@ -0,0 +1,216 @@
+--
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.GMCH.DP is
+
+   type DP_Array is array (GMCH_DP_Port) of Registers.Registers_Index;
+   DP_CTL : constant DP_Array :=
+     (DIGI_B => Registers.GMCH_DP_B,
+      DIGI_C => Registers.GMCH_DP_C,
+      DIGI_D => Registers.GMCH_DP_D);
+
+   DP_CTL_DISPLAY_PORT_ENABLE          : constant := 1 * 2 ** 31;
+   DP_CTL_VSWING_LEVEL_SET_SHIFT       : constant :=          25;
+   DP_CTL_VSWING_LEVEL_SET_MASK        : constant := 7 * 2 ** 25;
+   DP_CTL_PREEMPH_LEVEL_SET_SHIFT      : constant :=          22;
+   DP_CTL_PREEMPH_LEVEL_SET_MASK       : constant := 7 * 2 ** 22;
+   DP_CTL_PORT_WIDTH_SHIFT             : constant :=          19;
+   DP_CTL_ENHANCED_FRAMING_ENABLE      : constant := 1 * 2 ** 18;
+   DP_CTL_PORT_REVERSAL                : constant := 1 * 2 ** 15;
+   DP_CTL_LINK_TRAIN_MASK              : constant := 3 * 2 ** 28;
+   DP_CTL_LINK_TRAIN_PAT1              : constant := 0 * 2 ** 28;
+   DP_CTL_LINK_TRAIN_PAT2              : constant := 1 * 2 ** 28;
+   DP_CTL_LINK_TRAIN_IDLE              : constant := 2 * 2 ** 28;
+   DP_CTL_LINK_TRAIN_NORMAL            : constant := 3 * 2 ** 28;
+   DP_CTL_AUDIO_OUTPUT_ENABLE          : constant := 1 * 2 **  6;
+   DP_CTL_PORT_DETECT                  : constant := 1 * 2 **  2;
+   DP_CTL_VSYNC_ACTIVE_HIGH            : constant := 1 * 2 **  4;
+   DP_CTL_HSYNC_ACTIVE_HIGH            : constant := 1 * 2 **  3;
+   DP_CTL_COLOR_RANGE_16_235           : constant := 1 * 2 **  8;
+   DP_CTL_SCRAMBLE_DISABLE             : constant := 1 * 2 **  7;
+
+   function DP_CTL_VSWING_LEVEL_SET
+     (VS : DP_Info.DP_Voltage_Swing)
+      return Word32
+   is
+   begin
+      return Shift_Left(Word32 (DP_Info.DP_Voltage_Swing'Pos (VS)), 25);
+   end DP_CTL_VSWING_LEVEL_SET;
+
+   function DP_CTL_PREEMPH_LEVEL_SET (PE : DP_Info.DP_Pre_Emph) return Word32
+   is
+   begin
+      return Shift_Left(Word32 (DP_Info.DP_Pre_Emph'Pos (PE)), 22);
+   end DP_CTL_PREEMPH_LEVEL_SET;
+
+   function DP_CTL_PORT_WIDTH (Lane_Count : DP_Lane_Count) return Word32
+   is
+   begin
+      return Shift_Left(Word32 (Lane_Count_As_Integer (Lane_Count)) - 1, 19);
+   end DP_CTL_PORT_WIDTH;
+
+   type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
+   DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
+     (DP_Info.TP_1      => 0 * 2 ** 28,
+      DP_Info.TP_2      => 1 * 2 ** 28,
+      DP_Info.TP_3      => 1 * 2 ** 28,
+      DP_Info.TP_Idle   => 2 * 2 ** 28,
+      DP_Info.TP_None   => 3 * 2 ** 28);
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   function Max_V_Swing
+     (Port : GMCH_DP_Port)
+      return DP_Info.DP_Voltage_Swing
+   is
+   begin
+      return DP_Info.VS_Level_3;
+   end Max_V_Swing;
+
+   function Max_Pre_Emph
+     (Port        : GMCH_DP_Port;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+   begin
+      return
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+            when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+            when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+            when DP_Info.VS_Level_3 => DP_Info.Emph_Level_0);
+   end Max_Pre_Emph;
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+                    Reason => "Needed for a common interface");
+   procedure Set_Training_Pattern
+     (Port     : GMCH_DP_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN (Pattern));
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Port        : GMCH_DP_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_VSWING_LEVEL_SET_MASK or
+                        DP_CTL_PREEMPH_LEVEL_SET_MASK,
+         Mask_Set    => DP_CTL_VSWING_LEVEL_SET (Train_Set.Voltage_Swing) or
+                        DP_CTL_PREEMPH_LEVEL_SET (Train_Set.Pre_Emph));
+   end Set_Signal_Levels;
+
+   procedure Off (Port : GMCH_DP_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN_IDLE);
+      Registers.Posting_Read (DP_CTL (Port));
+
+      Registers.Write (DP_CTL (Port), 0);
+      Registers.Posting_Read (DP_CTL (Port));
+   end Off;
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+   pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Port_Cfg : in     Port_Config;
+      Pipe     : in     Pipe_Index;
+      Success  :    out Boolean)
+   is
+      function To_DP (Port : GMCH_DP_Port) return DP_Port
+      is
+      begin
+         return
+           (case Port is
+               when DIGI_B => DP_B,
+               when DIGI_C => DP_C,
+               when DIGI_D => DP_D);
+      end To_DP;
+      package Training is new DP_Training
+        (TPS3_Supported    => False,
+         T                 => GMCH_DP_Port,
+         Aux_T             => DP_Port,
+         Aux_Ch            => DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Off);
+
+      Sync_Polarity : constant Word32 :=
+        (if Port_Cfg.Mode.H_Sync_Active_High then DP_CTL_HSYNC_ACTIVE_HIGH else 0) or
+        (if Port_Cfg.Mode.V_Sync_Active_High then DP_CTL_VSYNC_ACTIVE_HIGH else 0);
+
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write
+        (Register => DP_CTL (Port_Cfg.Port),
+         Value    => DP_CTL_DISPLAY_PORT_ENABLE or
+                     DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count) or
+                     DP_CTL_LINK_TRAIN_PAT1 or
+                     (if Port_Cfg.DP.Enhanced_Framing
+                      then DP_CTL_ENHANCED_FRAMING_ENABLE else 0) or
+                     GMCH_PORT_PIPE_SELECT(Pipe) or
+                     Sync_Polarity or
+                     DP_CTL_COLOR_RANGE_16_235);
+      Training.Train_DP
+        (Port     => Port_Cfg.Port,
+         Link     => Port_Cfg.DP,
+         Success  => Success);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for Port in GMCH_DP_Port loop
+         Off (Port);
+      end loop;
+   end All_Off;
+
+end HW.GFX.GMA.GMCH.DP;
diff --git a/common/g45/hw-gfx-gma-gmch-dp.ads b/common/g45/hw-gfx-gma-gmch-dp.ads
new file mode 100644
index 0000000..a99b603
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-dp.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+package HW.GFX.GMA.GMCH.DP
+is
+
+   procedure On
+     (Port_Cfg : in     Port_Config;
+      Pipe     : in     Pipe_Index;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GMCH_DP_Port;
+
+   procedure Off (Port : GMCH_DP_Port);
+   procedure All_Off;
+
+end HW.GFX.GMA.GMCH.DP;
diff --git a/common/g45/hw-gfx-gma-gmch-hdmi.adb b/common/g45/hw-gfx-gma-gmch-hdmi.adb
new file mode 100644
index 0000000..4c84f36
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-hdmi.adb
@@ -0,0 +1,97 @@
+--
+-- Copyright (C) 2015-2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.GMCH.HDMI
+is
+
+   GMCH_HDMI_ENABLE               : constant := 1 * 2 ** 31;
+   GMCH_HDMI_COLOR_FORMAT_8BPC    : constant := 0 * 2 ** 26;
+   GMCH_HDMI_COLOR_FORMAT_12BPC   : constant := 3 * 2 ** 26;
+   GMCH_HDMI_COLOR_FORMAT_MASK    : constant := 7 * 2 ** 26;
+   GMCH_HDMI_SDVO_ENCODING_SDVO   : constant := 0 * 2 ** 10;
+   GMCH_HDMI_SDVO_ENCODING_HDMI   : constant := 2 * 2 ** 10;
+   GMCH_HDMI_SDVO_ENCODING_MASK   : constant := 3 * 2 ** 10;
+   GMCH_HDMI_MODE_SELECT_HDMI     : constant := 1 * 2 **  9;
+   GMCH_HDMI_MODE_SELECT_DVI      : constant := 0 * 2 **  9;
+   GMCH_HDMI_VSYNC_ACTIVE_HIGH    : constant := 1 * 2 **  4;
+   GMCH_HDMI_HSYNC_ACTIVE_HIGH    : constant := 1 * 2 **  3;
+   GMCH_HDMI_PORT_DETECT          : constant := 1 * 2 **  2;
+
+   GMCH_HDMI_MASK : constant Word32 :=
+     GMCH_PORT_PIPE_SELECT_MASK or
+     GMCH_HDMI_ENABLE or
+     GMCH_HDMI_COLOR_FORMAT_MASK or
+     GMCH_HDMI_SDVO_ENCODING_MASK or
+     GMCH_HDMI_MODE_SELECT_HDMI or
+     GMCH_HDMI_HSYNC_ACTIVE_HIGH or
+     GMCH_HDMI_VSYNC_ACTIVE_HIGH;
+
+   type GMCH_HDMI_Array is array (GMCH_HDMI_Port) of Registers.Registers_Index;
+   GMCH_HDMI : constant GMCH_HDMI_Array := GMCH_HDMI_Array'
+     (DIGI_B => Registers.GMCH_HDMIB,
+      DIGI_C => Registers.GMCH_HDMIC);
+
+   ----------------------------------------------------------------------------
+
+   procedure On (Port_Cfg : in Port_Config;
+                 Pipe     : in Pipe_Index)
+   is
+      Polarity : constant Word32 :=
+        (if Port_Cfg.Mode.H_Sync_Active_High then
+            GMCH_HDMI_HSYNC_ACTIVE_HIGH else 0) or
+        (if Port_Cfg.Mode.V_Sync_Active_High then
+            GMCH_HDMI_VSYNC_ACTIVE_HIGH else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+         (Register   => GMCH_HDMI (Port_Cfg.Port),
+          Mask_Unset => GMCH_HDMI_MASK,
+          Mask_Set   => GMCH_HDMI_ENABLE or
+                        GMCH_PORT_PIPE_SELECT (Pipe) or
+                        GMCH_HDMI_SDVO_ENCODING_HDMI or
+                        GMCH_HDMI_MODE_SELECT_HDMI or
+                        Polarity);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : GMCH_HDMI_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+         (Register   => GMCH_HDMI (Port),
+          Mask_Unset => GMCH_HDMI_MASK,
+          Mask_Set   => GMCH_HDMI_HSYNC_ACTIVE_HIGH or
+                        GMCH_HDMI_VSYNC_ACTIVE_HIGH);
+   end Off;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for Port in GMCH_HDMI_Port loop
+         Off (Port);
+      end loop;
+   end All_Off;
+
+end HW.GFX.GMA.GMCH.HDMI;
diff --git a/common/g45/hw-gfx-gma-gmch-hdmi.ads b/common/g45/hw-gfx-gma-gmch-hdmi.ads
new file mode 100644
index 0000000..3905f0c
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-hdmi.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+package HW.GFX.GMA.GMCH.HDMI
+is
+
+   procedure On (Port_Cfg : in Port_Config;
+                 Pipe     : in Pipe_Index)
+   with
+      Pre => Port_Cfg.Port in GMCH_HDMI_Port;
+
+   procedure Off (Port : GMCH_HDMI_Port);
+   procedure All_Off;
+
+end HW.GFX.GMA.GMCH.HDMI;
diff --git a/common/g45/hw-gfx-gma-gmch-lvds.adb b/common/g45/hw-gfx-gma-gmch-lvds.adb
new file mode 100644
index 0000000..746f8c1
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-lvds.adb
@@ -0,0 +1,81 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.GMCH.LVDS is
+
+   LVDS_ENABLE                     : constant :=  1 * 2 ** 31;
+   LVDS_DITHER_EN                  : constant :=  1 * 2 ** 25;
+   LVDS_VSYNC_POLARITY_INVERT      : constant :=  1 * 2 ** 21;
+   LVDS_HSYNC_POLARITY_INVERT      : constant :=  1 * 2 ** 20;
+   LVDS_CLK_A_DATA_A0A2_POWER_MASK : constant :=  3 * 2 **  8;
+   LVDS_CLK_A_DATA_A0A2_POWER_DOWN : constant :=  0 * 2 **  8;
+   LVDS_CLK_A_DATA_A0A2_POWER_UP   : constant :=  3 * 2 **  8;
+   LVDS_CLK_B_POWER_MASK           : constant :=  3 * 2 **  4;
+   LVDS_CLK_B_POWER_DOWN           : constant :=  0 * 2 **  4;
+   LVDS_CLK_B_POWER_UP             : constant :=  3 * 2 **  4;
+   LVDS_DATA_B0B2_POWER_MASK       : constant :=  3 * 2 **  2;
+   LVDS_DATA_B0B2_POWER_DOWN       : constant :=  0 * 2 **  2;
+   LVDS_DATA_B0B2_POWER_UP         : constant :=  3 * 2 **  2;
+
+   ----------------------------------------------------------------------------
+
+   procedure On (Port_Cfg : in Port_Config;
+                 Pipe     : in Pipe_Index)
+   is
+      Sync_Polarity : constant Word32 :=
+        (if Port_Cfg.Mode.H_Sync_Active_High then 0
+         else LVDS_HSYNC_POLARITY_INVERT) or
+        (if Port_Cfg.Mode.V_Sync_Active_High then 0
+         else LVDS_VSYNC_POLARITY_INVERT);
+
+      Two_Channel : constant Word32 :=
+        (if Port_Cfg.Mode.Dotclock >= Config.LVDS_Dual_Threshold then
+            LVDS_CLK_B_POWER_UP or LVDS_DATA_B0B2_POWER_UP else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      pragma Debug (Port_Cfg.Mode.BPC /= 6, Debug.Put_Line
+        ("WARNING: Only 18bpp LVDS mode implemented."));
+
+      Registers.Write
+        (Register => Registers.GMCH_LVDS,
+         Value    => LVDS_ENABLE or
+                     GMCH_PORT_PIPE_SELECT(Pipe) or
+                     Sync_Polarity or
+                     LVDS_CLK_A_DATA_A0A2_POWER_UP or
+                     Two_Channel or
+                     LVDS_DITHER_EN);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write
+        (Register => Registers.GMCH_LVDS,
+         Value    => LVDS_CLK_A_DATA_A0A2_POWER_DOWN or
+                     LVDS_CLK_B_POWER_DOWN or
+                     LVDS_DATA_B0B2_POWER_DOWN);
+   end Off;
+
+end HW.GFX.GMA.GMCH.LVDS;
diff --git a/common/g45/hw-gfx-gma-gmch-lvds.ads b/common/g45/hw-gfx-gma-gmch-lvds.ads
new file mode 100644
index 0000000..c477a40
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-lvds.ads
@@ -0,0 +1,23 @@
+--
+-- Copyright (C) 2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+package HW.GFX.GMA.GMCH.LVDS
+is
+
+   procedure On (Port_Cfg : in Port_Config;
+                 Pipe     : in Pipe_Index);
+
+   procedure Off;
+
+end HW.GFX.GMA.GMCH.LVDS;
diff --git a/common/g45/hw-gfx-gma-gmch-vga.adb b/common/g45/hw-gfx-gma-gmch-vga.adb
new file mode 100644
index 0000000..92f489b
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-vga.adb
@@ -0,0 +1,80 @@
+--
+-- Copyright (C) 2015-2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.GMCH.VGA is
+
+   ADPA_DAC_ENABLE         : constant := 1 * 2 ** 31;
+   ADPA_USE_VGA_HVPOLARITY : constant := 1 * 2 ** 15;
+   ADPA_VSYNC_DISABLE      : constant := 1 * 2 ** 11;
+   ADPA_HSYNC_DISABLE      : constant := 1 * 2 ** 10;
+   ADPA_VSYNC_ACTIVE_HIGH  : constant := 1 * 2 **  4;
+   ADPA_HSYNC_ACTIVE_HIGH  : constant := 1 * 2 **  3;
+
+   ADPA_MASK : constant Word32 :=
+      GMCH_PORT_PIPE_SELECT_MASK or
+      ADPA_DAC_ENABLE        or
+      ADPA_VSYNC_DISABLE     or
+      ADPA_HSYNC_DISABLE     or
+      ADPA_VSYNC_ACTIVE_HIGH or
+      ADPA_HSYNC_ACTIVE_HIGH or
+      ADPA_USE_VGA_HVPOLARITY;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Pipe     : Pipe_Index;
+      Mode     : in Mode_Type)
+   is
+      Polarity : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Mode.H_Sync_Active_High then
+         Polarity := Polarity or ADPA_HSYNC_ACTIVE_HIGH;
+      end if;
+      if Mode.V_Sync_Active_High then
+         Polarity := Polarity or ADPA_VSYNC_ACTIVE_HIGH;
+      end if;
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.GMCH_ADPA,
+         Mask_Unset  => ADPA_MASK,
+         Mask_Set    => ADPA_DAC_ENABLE or
+                        GMCH_PORT_PIPE_SELECT (Pipe) or
+                        Polarity);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.GMCH_ADPA,
+         Mask_Unset  => ADPA_DAC_ENABLE,
+         Mask_Set    => ADPA_HSYNC_DISABLE or
+                        ADPA_VSYNC_DISABLE);
+   end Off;
+
+end HW.GFX.GMA.GMCH.VGA;
diff --git a/common/g45/hw-gfx-gma-gmch-vga.ads b/common/g45/hw-gfx-gma-gmch-vga.ads
new file mode 100644
index 0000000..8a4e934
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch-vga.ads
@@ -0,0 +1,22 @@
+--
+-- Copyright (C) 2015-2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+package HW.GFX.GMA.GMCH.VGA is
+
+   procedure On
+     (Pipe : in Pipe_Index;
+      Mode : in Mode_Type);
+   procedure Off;
+
+end HW.GFX.GMA.GMCH.VGA;
diff --git a/common/g45/hw-gfx-gma-gmch.ads b/common/g45/hw-gfx-gma-gmch.ads
new file mode 100644
index 0000000..0e7832c
--- /dev/null
+++ b/common/g45/hw-gfx-gma-gmch.ads
@@ -0,0 +1,13 @@
+with HW.GFX.GMA.Config;
+
+private package HW.GFX.GMA.GMCH is
+
+   GMCH_PORT_PIPE_SELECT_SHIFT : constant := 30;
+   GMCH_PORT_PIPE_SELECT_MASK  : constant := 1 * 2 ** 30;
+   type GMCH_PORT_PIPE_SELECT_Array is array (Pipe_Index) of Word32;
+   GMCH_PORT_PIPE_SELECT       : constant GMCH_PORT_PIPE_SELECT_Array :=
+     (Primary   => 0 * 2 ** GMCH_PORT_PIPE_SELECT_SHIFT,
+      Secondary => 1 * 2 ** GMCH_PORT_PIPE_SELECT_SHIFT,
+      Tertiary  => 0);
+
+end HW.GFX.GMA.GMCH;
diff --git a/common/g45/hw-gfx-gma-plls.adb b/common/g45/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..67242f2
--- /dev/null
+++ b/common/g45/hw-gfx-gma-plls.adb
@@ -0,0 +1,603 @@
+--
+-- Copyright (C) 2015-2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+   Refined_State => (State => PLLs)
+is
+
+   Debug_Clocks : constant Boolean := False;
+
+   type Count_Range is new Natural range 0 .. 2;
+
+   type PLL_State is record
+      Use_Count   : Count_Range;
+      Used_For_DP : Boolean;
+      Link_Rate   : DP_Bandwidth;
+      Mode        : Mode_Type;
+   end record;
+
+   type PLL_State_Array is array (DPLLs) of PLL_State;
+
+   PLLs : PLL_State_Array;
+
+   ----------------------------------------------------------------------------
+
+   subtype N_Range     is Int64 range          3 ..          8;
+   subtype M_Range     is Int64 range         70 ..        138;
+   subtype M1_Range    is Int64 range         10 ..         25;
+   subtype M2_Range    is Int64 range          4 ..         13;
+   subtype P_Range     is Int64 range          5 ..        112;
+   subtype P1_Range    is Int64 range          1 ..          8;
+   subtype P2_Range    is Int64 range          5 ..         14;
+   subtype VCO_Range   is Int64 range 1400000000 .. 3500000000;
+   subtype Clock_Range is HW.GFX.Frequency_Type;
+
+   type Clock_Type is
+      record
+         N               : N_Range;
+         M1              : M1_Range;
+         M2              : M2_Range;
+         P1              : P1_Range;
+         P2              : P2_Range;
+         M               : M_Range;
+         P               : P_Range;
+         VCO             : VCO_Range;
+         Reference_Clock : Clock_Range;
+         Dotclock        : Clock_Range;
+      end record;
+
+   Invalid_Clock : constant Clock_Type := Clock_Type'
+      (N               => N_Range'Last,
+       M1              => M1_Range'Last,
+       M2              => M2_Range'Last,
+       P1              => P1_Range'Last,
+       P2              => P2_Range'Last,
+       Reference_Clock => Clock_Range'Last,
+       M               => M_Range'Last,
+       P               => P_Range'Last,
+       VCO             => VCO_Range'Last,
+       Dotclock        => Clock_Range'Last);
+
+   type Limits_Type is
+      record
+         N_Lower      : N_Range;
+         N_Upper      : N_Range;
+         M_Lower      : M_Range;
+         M_Upper      : M_Range;
+         M1_Lower     : M1_Range;
+         M1_Upper     : M1_Range;
+         M2_Lower     : M2_Range;
+         M2_Upper     : M2_Range;
+         P_Lower      : P_Range;
+         P_Upper      : P_Range;
+         P1_Lower     : P1_Range;
+         P1_Upper     : P1_Range;
+         P2_Fast      : P2_Range;
+         P2_Slow      : P2_Range;
+         P2_Threshold : Clock_Range;
+         VCO_Lower    : VCO_Range;
+         VCO_Upper    : VCO_Range;
+      end record;
+
+   LVDS_Single_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   5,
+      M_Lower      =>  104,          M_Upper   => 138,
+      M1_Lower     =>  19,           M1_Upper  =>  25, -- this is capped by M_Upper >= 5 * M1 + M2_Lower
+      M2_Lower     =>   7,           M2_Upper  =>  13,
+      P_Lower      =>  28,           P_Upper   => 112,
+      P1_Lower     =>   2,           P1_Upper  =>   8,
+      P2_Fast      =>  14,           P2_Slow   =>  14,
+      P2_Threshold => Clock_Range'First,
+      VCO_Lower    => 1_750_000_000, VCO_Upper => 3_500_000_000);
+   LVDS_Dual_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   5,
+      M_Lower      =>  104,          M_Upper   => 138,
+      M1_Lower     =>  19,           M1_Upper  =>  25,
+      M2_Lower     =>   7,           M2_Upper  =>  13,
+      P_Lower      =>  14,           P_Upper   =>  56,
+      P1_Lower     =>   2,           P1_Upper  =>   6,
+      P2_Fast      =>   7,           P2_Slow   =>   7,
+      P2_Threshold => Clock_Range'First,
+      VCO_Lower    => 1_750_000_000, VCO_Upper => 3_500_000_000);
+    SDVO_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   6,
+      M_Lower      =>  104,          M_Upper   => 138,
+      M1_Lower     =>  16,           M1_Upper  =>  25,
+      M2_Lower     =>   7,           M2_Upper  =>  13,
+      P_Lower      =>   10,          P_Upper   =>  30,
+      P1_Lower     =>   1,           P1_Upper  =>   3,
+      -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
+      P2_Fast      =>   5,          P2_Slow   =>  10,
+      P2_Threshold =>   270_000_000,
+      VCO_Lower    => 1_750_000_000, VCO_Upper => 3_500_000_000);
+    HDMI_Analog_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   6,
+      M_Lower      =>  104,          M_Upper   => 138,
+      M1_Lower     =>  18,           M1_Upper  =>  25,
+      M2_Lower     =>   7,           M2_Upper  =>  13,
+      P_Lower      =>   5,           P_Upper   =>  80,
+      P1_Lower     =>   1,           P1_Upper  =>   8,
+      -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
+      P2_Fast      =>   5,           P2_Slow   =>  10,
+      P2_Threshold =>   165_000_000,
+      VCO_Lower    => 1_750_000_000, VCO_Upper => 3_500_000_000);
+   All_Other_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   8,
+      M_Lower      =>  70,           M_Upper   => 120,
+      M1_Lower     =>  10,           M1_Upper  =>  20,
+      M2_Lower     =>   5,           M2_Upper  =>   9,
+      P_Lower      =>   5,           P_Upper   =>  80,
+      P1_Lower     =>   1,           P1_Upper  =>   8,
+      -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
+      P2_Fast      =>   5,           P2_Slow   =>  10,
+      P2_Threshold =>   200_000_000,
+      VCO_Lower    => 1_400_000_000, VCO_Upper => 2_800_000_000);
+
+   ----------------------------------------------------------------------------
+
+   type Regs is array (DPLLs) of Registers.Registers_Index;
+
+   DPLL : constant Regs := Regs'(Registers.GMCH_DPLL_A, Registers.GMCH_DPLL_B);
+   DPLL_VCO_ENABLE         : constant := 1 * 2 ** 31;
+   DPLL_VGA_MODE_DIS       : constant := 1 * 2 ** 28;
+   DPLL_P2_10_OR_14        : constant := 0 * 2 ** 24;
+   DPLL_P2_5_OR_7          : constant := 1 * 2 ** 24;
+   DPLL_P1_DIVIDER_SHIFT   : constant := 16;
+   DPLL_SDVOCLK            : constant := 2 * 2 ** 13;
+   DPLL_PULSE_PHASE_MASK   : constant := 15 * 2 ** 9;
+   DPLL_PULSE_PHASE_6      : constant :=  6 * 2 ** 9;
+
+   DPLL_HIGH_SPEED : constant := 1 * 2 ** 30;
+   DPLL_MODE_LVDS  : constant := 2 * 2 ** 26;
+   DPLL_MODE_DAC   : constant := 1 * 2 ** 26;
+   DPLL_DREFCLK    : constant := 0 * 2 ** 13;
+   DPLL_SSC        : constant := 3 * 2 ** 13;
+
+   MODE_DPLL_DAC : constant Word32 := Word32'
+     (DPLL_MODE_DAC or DPLL_DREFCLK);
+
+   MODE_DPLL_HDMI : constant Word32 := Word32'
+      (DPLL_MODE_DAC or DPLL_DREFCLK or DPLL_HIGH_SPEED);
+
+   MODE_DPLL_LVDS : constant Word32 := Word32'
+      (DPLL_MODE_LVDS or DPLL_SSC);
+
+   MODE_DPLL_DP : constant Word32 := Word32'
+      (DPLL_MODE_DAC or DPLL_SSC or DPLL_HIGH_SPEED);
+
+   type DPLL_Mode_Array is array (Display_Type) of Word32;
+
+   DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
+     (LVDS     => MODE_DPLL_LVDS,
+      DP       => MODE_DPLL_DP,
+      HDMI     => MODE_DPLL_HDMI,
+      VGA      => MODE_DPLL_DAC,
+      Others   => MODE_DPLL_HDMI); --TODO Add SDVO
+
+   FP0 : constant Regs := Regs'(Registers.GMCH_FPA0, Registers.GMCH_FPB0);
+   FP1 : constant Regs := Regs'(Registers.GMCH_FPA1, Registers.GMCH_FPB1);
+   FP_DOUBLE_CLOCK       : constant := 1 * 2 ** 27;
+   FP_N_SHIFT            : constant := 16;
+   FP_M1_SHIFT           : constant := 8;
+   FP_M2_SHIFT           : constant := 0;
+
+   ----------------------------------------------------------------------------
+
+   procedure Verify_Parameters
+      (N               : in     N_Range;
+       M1              : in     M1_Range;
+       M2              : in     M2_Range;
+       P1              : in     P1_Range;
+       P2              : in     P2_Range;
+       Reference_Clock : in     Clock_Range;
+       Current_Limits  : in     Limits_Type;
+       Result          :    out Clock_Type;
+       Valid           :    out Boolean)
+   with
+      Global => null,
+      Pre => True,
+      Post => True
+   is
+      M        : Int64;
+      P        : Int64;
+      VCO      : Int64;
+      Dotclock : Int64;
+   begin
+      pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      M        := 5 * M1 + M2;
+      P        := P1 * P2;
+      VCO      := (Int64 (Reference_Clock) * M) / N;
+      Dotclock := VCO / P;
+
+      pragma Debug (Debug_Clocks and not (Current_Limits.P1_Lower  <= P1  and P1  <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
+      pragma Debug (Debug_Clocks and     (Current_Limits.P2_Fast   /= P2  and P2  /= Current_Limits.P2_Slow  ), Debug.Put_Line ("P2 out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.P_Lower   <= P   and P   <= Current_Limits.P_Upper  ), Debug.Put_Line ("P out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M1_Lower  <= M1  and M1  <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M2_Lower  <= M2  and M2  <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
+      -- pragma Debug (Debug_Clocks and not (M2 <= M1                                           ), Debug.Put_Line ("M1 greater thant M2."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.N_Lower   <= N   and N   <= Current_Limits.N_Upper  ), Debug.Put_Line ("N out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M_Lower   <= M   and M   <= Current_Limits.M_Upper  ), Debug.Put_Line ("M out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
+
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Line ("Dotclock too low."));
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Int64 (Dotclock));
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.New_Line);
+
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Line ("Dotclock too high."));
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Int64 (Dotclock));
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.New_Line);
+
+      Valid :=
+         Current_Limits.P1_Lower  <= P1  and P1  <= Current_Limits.P1_Upper  and
+         (Current_Limits.P2_Fast   = P2   or P2   = Current_Limits.P2_Slow)  and
+         Current_Limits.P_Lower   <= P   and P   <= Current_Limits.P_Upper   and
+         Current_Limits.M1_Lower  <= M1  and M1  <= Current_Limits.M1_Upper  and
+         Current_Limits.M2_Lower  <= M2  and M2  <= Current_Limits.M2_Upper  and
+         Current_Limits.N_Lower   <= N   and N   <= Current_Limits.N_Upper   and
+         Current_Limits.M_Lower   <= M   and M   <= Current_Limits.M_Upper   and
+         Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper and
+         Int64 (Clock_Range'First) <= Dotclock                               and
+         Dotclock <= Int64 (Clock_Range'Last);
+
+      if Valid
+      then
+         Result := Clock_Type'
+            (N               => N,
+             M1              => M1,
+             M2              => M2,
+             P1              => P1,
+             P2              => P2,
+             Reference_Clock => Reference_Clock,
+             M               => M,
+             P               => P,
+             VCO             => VCO,
+             Dotclock        => Clock_Range (Dotclock));
+      else
+         Result := Invalid_Clock;
+      end if;
+
+   end Verify_Parameters;
+
+   procedure Calculate_Clock_Parameters
+     (Display         : in     Display_Type;
+      Target_Dotclock : in     Clock_Range;
+      Reference_Clock : in     Clock_Range;
+      Best_Clock      :    out Clock_Type;
+      Valid           :    out Boolean)
+   with
+     Global => null,
+     Pre => True,
+     Post => True
+   is
+      Limits : constant Limits_Type :=
+      (case Display is
+          when LVDS   =>
+             (if Target_Dotclock >= Config.LVDS_Dual_Threshold then
+                LVDS_Dual_Limits
+              else
+                LVDS_Single_Limits),
+          when HDMI   => HDMI_Analog_Limits,
+          when VGA    => HDMI_Analog_Limits,
+          when others => All_Other_Limits); --TODO add SDVO type, needs other limits
+
+      P2               : P2_Range;
+      Best_Delta       : Int64 := Int64'Last;
+      Current_Delta    : Int64;
+      Current_Clock    : Clock_Type;
+      Registers_Valid  : Boolean;
+   begin
+      pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Valid      := False;
+      Best_Clock := Invalid_Clock;
+
+      if Target_Dotclock <= Limits.P2_Threshold then
+         P2 := Limits.P2_Slow;
+      else
+         P2 := Limits.P2_Fast;
+      end if;
+
+      for N in N_Range range Limits.N_Lower .. Limits.N_Upper
+      loop
+         -- reverse loops as hardware prefers higher values
+         for M1 in reverse M1_Range range Limits.M1_Lower .. Limits.M1_Upper
+         loop
+            for M2 in reverse M2_Range range Limits.M2_Lower .. Int64'Min (Limits.M2_Upper, M1)
+            loop
+               for P1 in reverse P1_Range range Limits.P1_Lower .. Limits.P1_Upper
+               loop
+                  Verify_Parameters
+                    (N               => N,
+                     M1              => M1,
+                     M2              => M2,
+                     P1              => P1,
+                     P2              => P2,
+                     Reference_Clock => Reference_Clock,
+                     Current_Limits  => Limits,
+                     Result          => Current_Clock,
+                     Valid           => Registers_Valid);
+
+                  if Registers_Valid
+                  then
+                     if Current_Clock.Dotclock > Target_Dotclock
+                     then
+                        Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
+                     else
+                        Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
+                     end if;
+
+                     if Current_Delta < Best_Delta
+                     then
+                        Best_Delta := Current_Delta;
+                        Best_Clock := Current_Clock;
+                        Valid      := True;
+                     end if;
+
+                     pragma Debug (Debug_Clocks, Debug.Put ("Current/Target/Best_Delta: "));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Current_Clock.Dotclock));
+                     pragma Debug (Debug_Clocks, Debug.Put ("/"));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Target_Dotclock));
+                     pragma Debug (Debug_Clocks, Debug.Put ("/"));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Best_Delta));
+                     pragma Debug (Debug_Clocks, Debug.Put_Line ("."));
+
+                  end if;
+               end loop;
+            end loop;
+         end loop;
+      end loop;
+
+      pragma Debug (Valid,     Debug.Put_Line ("Valid clock found."));
+      pragma Debug (Valid,     Debug.Put ("Best/Target/Delta: "));
+      pragma Debug (Valid,     Debug.Put_Int64 (Best_Clock.Dotclock));
+      pragma Debug (Valid,     Debug.Put ("/"));
+      pragma Debug (Valid,     Debug.Put_Int64 (Target_Dotclock));
+      pragma Debug (Valid,     Debug.Put ("/"));
+      pragma Debug (Valid,     Debug.Put_Int64 (Best_Delta));
+      pragma Debug (Valid,     Debug.Put_Line ("."));
+      pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
+
+   end Calculate_Clock_Parameters;
+
+   procedure Program_DPLL
+     (PLL      : DPLLs;
+      Display  : Display_Type;
+      Clk      : Clock_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Pre => True,
+      Post => True
+   is
+      FP, Encoded_P1, Encoded_P2 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      FP :=
+         Shift_Left (Word32 (Clk.N - 2), FP_N_SHIFT)     or
+         Shift_Left (Word32 (Clk.M1 - 2), FP_M1_SHIFT)   or
+         Shift_Left (Word32 (Clk.M2 - 2), FP_M2_SHIFT);
+
+      Registers.Write (FP0 (PLL), FP);
+      Registers.Write (FP1 (PLL), FP);
+
+      Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
+
+      if Clk.P2 = 5 or Clk.P2 = 7
+      then
+         Encoded_P2 := DPLL_P2_5_OR_7;
+      else
+         Encoded_P2 := DPLL_P2_10_OR_14;
+      end if;
+
+      Registers.Write
+        (Register => DPLL (PLL),
+         Value    => DPLL_Mode (Display)                            or
+                     DPLL_VGA_MODE_DIS                              or
+                     DPLL_PULSE_PHASE_6                             or
+                     Encoded_P2                                     or
+                     Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT));
+   end Program_DPLL;
+
+   procedure On
+     (PLL      : in     T;
+      Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+      Target_Clock : constant Frequency_Type :=
+        (if Port_Cfg.Display = DP then
+            DP_Symbol_Rate (Port_Cfg.DP.Bandwidth)
+         else
+            Port_Cfg.Mode.Dotclock);
+      Clk : Clock_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Success := PLL in DPLLs;
+      Clk := Invalid_Clock;
+
+      if Success then
+         if Port_Cfg.Display = DP then
+            Success := True;
+            -- we use static values for DP
+            case Port_Cfg.DP.Bandwidth is
+               when DP_Bandwidth_1_62 =>
+                  Clk.N    :=  4;
+                  Clk.M1   := 25;
+                  Clk.M2   := 10;
+                  Clk.P1   :=  2;
+                  Clk.P2   := 10;
+               when DP_Bandwidth_2_7 =>
+                  Clk.N    :=  3;
+                  Clk.M1   := 16;
+                  Clk.M2   :=  4;
+                  Clk.P1   :=  1;
+                  Clk.P2   := 10;
+               when others =>
+                  Success := False;
+            end case;
+         elsif Target_Clock <= 340_000_000 then
+            Calculate_Clock_Parameters
+              (Display           => Port_Cfg.Display,
+               Target_Dotclock   => Target_Clock,
+               -- should be, but doesn't has to be always the same:
+               Reference_Clock   => 96_000_000,
+               Best_Clock        => Clk,
+               Valid             => Success);
+         else
+            Success := False;
+            pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
+            pragma Debug (Debug.Put_Int64 (Target_Clock));
+            pragma Debug (Debug.Put (" > "));
+            pragma Debug (Debug.Put_Int32 (340_000_000));
+            pragma Debug (Debug.New_Line);
+            pragma Debug (Debug.New_Line);
+         end if;
+      end if;
+
+      if Success then
+         Program_DPLL (PLL, Port_Cfg.Display, Clk);
+
+         Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+         Registers.Posting_Read (DPLL (PLL));
+         Time.U_Delay (150);
+      end if;
+   end On;
+
+   procedure Off (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in DPLLs then
+         Registers.Unset_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+      end if;
+   end Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Initialize
+   is
+   begin
+      PLLs :=
+        (DPLLs =>
+           (Use_Count   => 0,
+            Used_For_DP => False,
+            Link_Rate   => DP_Bandwidth'First,
+            Mode        => Invalid_Mode));
+   end Initialize;
+
+   procedure Alloc_Configurable
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   with
+      Pre => True
+   is
+      function Config_Matches (PE : PLL_State) return Boolean
+      is
+      begin
+         return
+            PE.Used_For_DP = (Port_Cfg.Display = DP) and
+            ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
+             (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
+      end Config_Matches;
+   begin
+      -- try to find shareable PLL
+      for P in DPLLs loop
+         Success := PLLs (P).Use_Count /= 0 and
+                     PLLs (P).Use_Count /= Count_Range'Last and
+                     Config_Matches (PLLs (P));
+         if Success then
+            PLL := P;
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+            return;
+         end if;
+      end loop;
+
+      -- try to find free PLL
+      for P in DPLLs loop
+         if PLLs (P).Use_Count = 0 then
+            PLL := P;
+            On (PLL, Port_Cfg, Success);
+            if Success then
+               PLLs (PLL) :=
+                 (Use_Count   => 1,
+                  Used_For_DP => Port_Cfg.Display = DP,
+                  Link_Rate   => Port_Cfg.DP.Bandwidth,
+                  Mode        => Port_Cfg.Mode);
+            end if;
+            return;
+         end if;
+      end loop;
+
+      PLL := Invalid;
+   end Alloc_Configurable;
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         PLL := Invalid;
+         Success := True;
+      else
+         Alloc_Configurable (Port_Cfg, PLL, Success);
+      end if;
+   end Alloc;
+
+   procedure Free (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in DPLLs then
+         if PLLs (PLL).Use_Count /= 0 then
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+            if PLLs (PLL).Use_Count = 0 then
+               Off (PLL);
+            end if;
+         end if;
+      end if;
+   end Free;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for PLL in DPLLs loop
+         Off (PLL);
+      end loop;
+   end All_Off;
+
+   function Register_Value (PLL : T) return Word32
+   is
+   begin
+      return (if PLL = DPLL_B then 1 else 0);
+   end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/g45/hw-gfx-gma-plls.ads b/common/g45/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..09d6946
--- /dev/null
+++ b/common/g45/hw-gfx-gma-plls.ads
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2015-2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+   Abstract_State => (State with Part_Of => GMA.State)
+is
+
+   -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+   type T is (Invalid_PLL, DPLL_A, DPLL_B);
+   subtype DPLLs is T range DPLL_A .. DPLL_B;
+   Invalid : constant T := Invalid_PLL;
+
+   procedure Initialize
+   with
+      Global => (Output => State);
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean);
+
+   procedure Free (PLL : T);
+
+   procedure All_Off;
+
+   function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/g45/hw-gfx-gma-port_detect.adb b/common/g45/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..c865079
--- /dev/null
+++ b/common/g45/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,124 @@
+--
+-- 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Config_Helpers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+   PORT_DETECTED                    : constant := 1 * 2 **  2;
+
+   PORTB_HOTPLUG_INT_EN             : constant := 1 * 2 ** 29;
+   PORTC_HOTPLUG_INT_EN             : constant := 1 * 2 ** 28;
+   PORTD_HOTPLUG_INT_EN             : constant := 1 * 2 ** 27;
+   SDVOB_HOTPLUG_INT_EN             : constant := 1 * 2 ** 26;
+   SDVOC_HOTPLUG_INT_EN             : constant := 1 * 2 ** 25;
+   CRT_HOTPLUG_INT_EN               : constant := 1 * 2 ** 9;
+   CRT_HOTPLUG_ACTIVATION_PERIOD_64 : constant := 1 * 2 ** 8;
+
+   type HDMI_Port_Value is array (GMCH_HDMI_Port) of Word32;
+   type DP_Port_Value is array (GMCH_DP_Port) of Word32;
+   HDMI_PORT_HOTPLUG_EN : constant HDMI_Port_Value :=
+     (DIGI_B => SDVOB_HOTPLUG_INT_EN,
+      DIGI_C => SDVOC_HOTPLUG_INT_EN);
+   DP_PORT_HOTPLUG_EN : constant DP_Port_Value :=
+     (DIGI_B => PORTB_HOTPLUG_INT_EN,
+      DIGI_C => PORTC_HOTPLUG_INT_EN,
+      DIGI_D => PORTD_HOTPLUG_INT_EN);
+
+   type HDMI_Regs is array (GMCH_HDMI_Port) of Registers.Registers_Index;
+   type DP_Regs is array (GMCH_DP_Port) of Registers.Registers_Index;
+   GMCH_HDMI : constant HDMI_Regs :=
+     (DIGI_B => Registers.GMCH_HDMIB,
+      DIGI_C => Registers.GMCH_HDMIC);
+   GMCH_DP : constant DP_Regs :=
+     (DIGI_B => Registers.GMCH_DP_B,
+      DIGI_C => Registers.GMCH_DP_C,
+      DIGI_D => Registers.GMCH_DP_D);
+
+   HOTPLUG_INT_STATUS : constant array (Active_Port_Type) of word32 :=
+     (DP1    => 3 * 2 ** 17,
+      DP2    => 3 * 2 ** 19,
+      DP3    => 3 * 2 ** 21,
+      HDMI1  => 1 * 2 **  2,
+      HDMI2  => 1 * 2 **  3,
+      Analog => 1 * 2 ** 11,
+      others => 0);
+
+   procedure Initialize
+   is
+      Detected : Boolean;
+      hotplug_mask_set : Word32 :=
+        CRT_HOTPLUG_INT_EN or CRT_HOTPLUG_ACTIVATION_PERIOD_64;
+
+      To_HDMI_Port : constant array (GMCH_HDMI_Port) of Port_Type :=
+        (DIGI_B => HDMI1,
+         DIGI_C => HDMI2);
+      To_DP_Port : constant array (GMCH_DP_Port) of Port_Type :=
+        (DIGI_B => DP1,
+         DIGI_C => DP2,
+         DIGI_D => DP3);
+
+   begin
+      for HDMI_Port in GMCH_HDMI_Port loop
+         Registers.Is_Set_Mask
+           (Register => GMCH_HDMI (HDMI_Port),
+            Mask     => PORT_DETECTED,
+            Result   => Detected);
+         Config.Valid_Port (To_HDMI_Port (HDMI_Port)) := Detected;
+         hotplug_mask_set := hotplug_mask_set or
+           (if Detected then HDMI_PORT_HOTPLUG_EN (HDMI_Port) else 0);
+      end loop;
+      for DP_Port in GMCH_DP_Port loop
+         Registers.Is_Set_Mask
+           (Register => GMCH_DP (DP_Port),
+            Mask     => PORT_DETECTED,
+            Result   => Detected);
+         Config.Valid_Port (To_DP_Port (DP_Port)) := Detected;
+         hotplug_mask_set := hotplug_mask_set or
+           (if Detected then DP_PORT_HOTPLUG_EN (DP_Port) else 0);
+      end loop;
+      Registers.Write
+        (Register => Registers.PORT_HOTPLUG_EN,
+         Value => hotplug_mask_set);
+   end Initialize;
+
+   procedure Hotplug_Detect (Port : in Active_Port_Type; Detected : out Boolean)
+   is
+      Ctl32 : Word32;
+   begin
+      Registers.Read (Register => Registers.PORT_HOTPLUG_STAT,
+                      Value    => Ctl32);
+      Detected := (Ctl32 and HOTPLUG_INT_STATUS (Port)) /= 0;
+
+      if Detected then
+         registers.Set_Mask
+           (Register => Registers.PORT_HOTPLUG_STAT,
+            Mask     => HOTPLUG_INT_STATUS (Port));
+      end if;
+   end Hotplug_Detect;
+
+   procedure Clear_Hotplug_Detect (Port : Active_Port_Type)
+   is
+      Ignored_HPD : Boolean;
+   begin
+      pragma Warnings (GNATprove, Off, "unused assignment to ""Ignored_HPD""",
+                       Reason => "We want to clear pending events only");
+      Port_Detect.Hotplug_Detect (Port, Ignored_HPD);
+      pragma Warnings (GNATprove, On, "unused assignment to ""Ignored_HPD""");
+   end Clear_Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/g45/hw-gfx-gma-power_and_clocks.adb b/common/g45/hw-gfx-gma-power_and_clocks.adb
new file mode 100644
index 0000000..d6ee9f9
--- /dev/null
+++ b/common/g45/hw-gfx-gma-power_and_clocks.adb
@@ -0,0 +1,50 @@
+--
+-- Copyright (C) 2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks is
+
+   FSB_FREQ_SEL_MASK : constant := 7 * 2 ** 0;
+   CLKCFG_FSB_400    : constant Frequency_Type := 100_000_000;
+   CLKCFG_FSB_533    : constant Frequency_Type := 133_333_333;
+   CLKCFG_FSB_667    : constant Frequency_Type := 166_666_666;
+   CLKCFG_FSB_800    : constant Frequency_Type := 200_000_000;
+   CLKCFG_FSB_1067   : constant Frequency_Type := 266_666_666;
+   CLKCFG_FSB_1333   : constant Frequency_Type := 333_333_333;
+
+   -- The Raw Freq is 1/4 of the FSB freq
+   procedure Initialize
+   is
+      CLK_CFG : Word32;
+      type Freq_Sel is new Natural range 0 .. 7;
+   begin
+      Registers.Read
+        (Register => Registers.GMCH_CLKCFG,
+         Value => CLK_CFG);
+      case Freq_Sel (CLK_CFG and FSB_FREQ_SEL_MASK) is
+         when 0      => Config.Raw_Clock := CLKCFG_FSB_1067;
+         when 1      => Config.Raw_Clock := CLKCFG_FSB_533;
+         when 2      => Config.Raw_Clock := CLKCFG_FSB_800;
+         when 3      => Config.Raw_Clock := CLKCFG_FSB_667;
+         when 4      => Config.Raw_Clock := CLKCFG_FSB_1333;
+         when 5      => Config.Raw_Clock := CLKCFG_FSB_400;
+         when 6      => Config.Raw_Clock := CLKCFG_FSB_1067;
+         when 7      => Config.Raw_Clock := CLKCFG_FSB_1333;
+      end case;
+   end Initialize;
+
+end HW.GFX.GMA.Power_And_Clocks;
diff --git a/common/g45/hw-gfx-gma-power_and_clocks.ads b/common/g45/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..8f6c13c
--- /dev/null
+++ b/common/g45/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,30 @@
+--
+-- Copyright (C) 2016 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
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks is
+
+   procedure Initialize;
+
+   procedure Pre_All_Off is null;
+
+   procedure Post_All_Off is null;
+
+   procedure Power_Set_To (Configs : Pipe_Configs) is null;
+
+   procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is null;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
+   is null;
+
+end HW.GFX.GMA.Power_And_Clocks;
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
index 9473745..1a4d124 100644
--- a/common/hw-gfx-gma-config.ads.template
+++ b/common/hw-gfx-gma-config.ads.template
@@ -56,9 +56,9 @@
    Has_DSP_Linoff          : constant Boolean := CPU <= Ivybridge;
    Has_PF_Pipe_Select      : constant Boolean := CPU in Ivybridge .. Haswell;
    VGA_Plane_Workaround    : constant Boolean := CPU = Ivybridge;
-   Has_GMCH_VGACNTRL       : constant Boolean := false;
-   Has_GMCH_DP_Transcoder  : constant Boolean := false;
-   Has_GMCH_PFIT_CONTROL   : constant Boolean := false;
+   Has_GMCH_DP_Transcoder  : constant Boolean := CPU = G45;
+   Has_GMCH_VGACNTRL       : constant Boolean := CPU = G45;
+   Has_GMCH_PFIT_CONTROL   : constant Boolean := CPU = G45;
 
    ----- Panel power: -----
    Has_PP_Write_Protection : constant Boolean := CPU <= Ivybridge;
@@ -67,7 +67,7 @@
    Has_PCH_Panel_Power     : constant Boolean := CPU >= Ironlake;
 
    ----- PCH/FDI: ---------
-   Has_PCH                 : constant Boolean := CPU /= Broxton;
+   Has_PCH                 : constant Boolean := CPU /= Broxton and CPU /= G45;
    Has_PCH_DAC             : constant Boolean := CPU in Ironlake .. Ivybridge or
                                                  (CPU in Broadwell .. Haswell
                                                   and CPU_Var = Normal);
@@ -87,6 +87,8 @@
 
    Has_FDI_RX_Power_Down   : constant Boolean := CPU in Haswell .. Broadwell;
 
+   Has_GMCH_RawClk         : constant Boolean := CPU = G45;
+
    ----- DDI: -------------
    End_EDP_Training_Late   : constant Boolean := CPU in Haswell .. Broadwell;
    Has_Per_DDI_Clock_Sel   : constant Boolean := CPU in Haswell .. Broadwell;
@@ -142,7 +144,12 @@
    type Valid_Per_Port is array (Port_Type) of Boolean;
    type Valid_Per_GPU is array (CPU_Type) of Valid_Per_Port;
    Valid_Port_GPU : Valid_Per_GPU :=
-     (Ironlake    =>
+     (G45         =>
+        (Disabled => False,
+         Internal => Config.Internal_Display = LVDS,
+         HDMI3    => False,
+         others   => True),
+      Ironlake    =>
         (Disabled => False,
          Internal => Config.Internal_Display = LVDS,
          others   => True),
@@ -230,6 +237,7 @@
 
    Default_CDClk_Freq : constant Frequency_Type :=
      (case CPU is
+         when G45          => 320_000_000, -- unused
          when Ironlake     |
               Haswell      |
               Broadwell    => 450_000_000,
@@ -240,6 +248,7 @@
 
    Default_RawClk_Freq : constant Frequency_Type :=
      (case CPU is
+         when G45          => 100_000_000, -- unused, depends on FSB
          when Ironlake     |
               Sandybridge  |
               Ivybridge    => 125_000_000,
@@ -263,6 +272,10 @@
 
    Maximum_Scalable_Width : constant Width_Per_Pipe :=
      (case CPU is
+         when G45 => -- TODO: Is this true?
+           (Primary     => 4096,
+            Secondary   => 2048,
+            Tertiary    => Pos16'First),
          when Ironlake..Haswell =>
            (Primary     => 4096,
             Secondary   => 2048,
@@ -281,25 +294,25 @@
    ----------------------------------------------------------------------------
 
    GTT_Offset  : constant := (case CPU is
-                                 when Ironlake .. Haswell   => 16#0020_0000#,
+                                 when G45 .. Haswell   => 16#0020_0000#,
                                  when Broadwell .. Skylake  => 16#0080_0000#);
 
    GTT_Size    : constant := (case CPU is
-                                 when Ironlake .. Haswell   => 16#0020_0000#,
+                                 when G45 .. Haswell   => 16#0020_0000#,
                                  -- Limit Broadwell to 4MiB to have a stable
                                  -- interface (i.e. same number of entries):
                                  when Broadwell .. Skylake  => 16#0040_0000#);
 
    GTT_PTE_Size   : constant := (case CPU is
-                                    when Ironlake .. Haswell   => 4,
-                                    when Broadwell .. Skylake  => 8);
+                                    when G45 .. Haswell       => 4,
+                                    when Broadwell .. Skylake => 8);
 
    Fence_Base : constant := (case CPU is
-                              when Ironlake                 => 16#0000_3000#,
+                              when G45 .. Ironlake          => 16#0000_3000#,
                               when Sandybridge .. Skylake   => 16#0010_0000#);
 
    Fence_Count : constant := (case CPU is
-                                 when Ironlake .. Sandybridge  => 16,
+                                 when G45 .. Sandybridge       => 16,
                                  when Ivybridge .. Skylake     => 32);
 
    ----------------------------------------------------------------------------
@@ -318,6 +331,8 @@
    function Is_GPU (Device_Id : Word16; CPU : CPU_Type; CPU_Var : CPU_Variant)
       return Boolean is
      (case CPU is
+         when G45          => (Device_Id and 16#ff02#) = 16#2e02# or
+                              (Device_Id and 16#fffe#) = 16#2a42#,
          when Ironlake     => (Device_Id and 16#fff3#) = 16#0042#,
          when Sandybridge  => (Device_Id and 16#ffc2#) = 16#0102#,
          when Ivybridge    => (Device_Id and 16#ffc3#) = 16#0142#,
diff --git a/common/hw-gfx-gma-config_helpers.adb b/common/hw-gfx-gma-config_helpers.adb
index 5e5be5d..adc749c 100644
--- a/common/hw-gfx-gma-config_helpers.adb
+++ b/common/hw-gfx-gma-config_helpers.adb
@@ -30,6 +30,13 @@
    begin
       return
         (case Config.CPU is
+            when G45 =>
+               (case Port is
+                   when Internal     => LVDS,
+                   when HDMI1 | DP1  => DIGI_B,
+                   when HDMI2 | DP2  => DIGI_C,
+                   when HDMI3 | DP3  => DIGI_D,
+                   when Analog       => VGA),
             when Ironlake .. Ivybridge => -- everything but eDP through FDI/PCH
               (if Config.Internal_Is_EDP and then Port = Internal then
                   DIGI_A
diff --git a/common/hw-gfx-gma-dp_aux_request.adb b/common/hw-gfx-gma-dp_aux_request.adb
index 97083b2..75a2cca 100644
--- a/common/hw-gfx-gma-dp_aux_request.adb
+++ b/common/hw-gfx-gma-dp_aux_request.adb
@@ -235,6 +235,8 @@
                Word32 ((Config.Default_CDClk_Freq + 1_000_000) / 2_000_000)
             else
                Word32 ((Config.Raw_Clock + 1_000_000) / 2_000_000))
+         elsif Config.Has_GMCH_RawClk then
+            Word32 (Div_Round_Closest (Config.Raw_Clock, 2_000_000))
          else 0);
 
       Busy : Boolean;
diff --git a/common/hw-gfx-gma-pch.ads b/common/hw-gfx-gma-pch.ads
index 619ccb6..106d2e8 100644
--- a/common/hw-gfx-gma-pch.ads
+++ b/common/hw-gfx-gma-pch.ads
@@ -26,13 +26,13 @@
      (case Config.CPU is
          when Ironlake                       => 30,
          when Sandybridge | Ivybridge        => 29,
-         when Haswell .. Skylake             =>  0);
+         when others                         =>  0);
 
    PCH_TRANSCODER_SELECT_MASK : constant :=
      (case Config.CPU is
          when Ironlake                       => 1 * 2 ** 30,
          when Sandybridge | Ivybridge        => 3 * 2 ** 29,
-         when Haswell .. Skylake             =>           0);
+         when others                         =>  0);
 
    type PCH_TRANSCODER_SELECT_Array is array (FDI_Port_Type) of Word32;
    PCH_TRANSCODER_SELECT : constant PCH_TRANSCODER_SELECT_Array :=
diff --git a/common/hw-gfx-gma-registers.ads b/common/hw-gfx-gma-registers.ads
index 480ee32..da100ce 100644
--- a/common/hw-gfx-gma-registers.ads
+++ b/common/hw-gfx-gma-registers.ads
@@ -50,9 +50,16 @@
       GMCH_GMBUS3,
       GMCH_GMBUS4,
       GMCH_GMBUS5,
+      GMCH_DPLL_A,
+      GMCH_DPLL_B,
+      GMCH_FPA0,
+      GMCH_FPA1,
+      GMCH_FPB0,
+      GMCH_FPB1,
       MBCTL,
       UCGCTL1,
       UCGCTL2,
+      GMCH_CLKCFG,
       VCS_RING_BUFFER_TAIL,
       VCS_RING_BUFFER_HEAD,
       VCS_RING_BUFFER_STRT,
@@ -155,6 +162,11 @@
       PIPEB_LINK_M1,
       PIPEB_LINK_N1,
       FDI_TX_CTL_B,
+      PORT_HOTPLUG_EN,
+      PORT_HOTPLUG_STAT,
+      GMCH_SDVOB,
+      GMCH_SDVOC,
+      GMCH_LVDS,
       GMCH_PP_STATUS,
       GMCH_PP_CONTROL,
       GMCH_PP_ON_DELAYS,
@@ -172,6 +184,7 @@
       VBLANK_C,
       VSYNC_C,
       PIPECSRC,
+      G4X_AUD_VID_DID,
       PIPE_VSYNCSHIFT_C,
       PIPEC_DATA_M1,
       PIPEC_DATA_N1,
@@ -733,7 +746,12 @@
       SBI_DATA              => 16#0c_6004# / Register_Width,
       SBI_CTL_STAT          => 16#0c_6008# / Register_Width,
 
-      -- clock registers
+      -- GMCH clock registers
+      GMCH_DPLL_A           => 16#00_6014# / Register_Width,
+      GMCH_FPA0             => 16#00_6040# / Register_Width,
+      GMCH_FPA1             => 16#00_6044# / Register_Width,
+
+      -- PCH clock registers
       PCH_DPLL_A            => 16#0c_6014# / Register_Width,
       PCH_PIXCLK_GATE       => 16#0c_6020# / Register_Width,
       PCH_FPA0              => 16#0c_6040# / Register_Width,
@@ -823,7 +841,12 @@
       PIPEB_DDI_FUNC_CTL    => 16#06_1400# / Register_Width,
       PIPEB_MSA_MISC        => 16#06_1410# / Register_Width,
 
-      -- clock registers
+      -- GMCH clock registers
+      GMCH_DPLL_B           => 16#00_6018# / Register_Width,
+      GMCH_FPB0             => 16#00_6048# / Register_Width,
+      GMCH_FPB1             => 16#00_604c# / Register_Width,
+
+      -- PCH clock registers
       PCH_DPLL_B            => 16#0c_6018# / Register_Width,
       PCH_FPB0              => 16#0c_6048# / Register_Width,
       PCH_FPB1              => 16#0c_604c# / Register_Width,
@@ -852,7 +875,7 @@
       SPBCNTR               => 16#07_1280# / Register_Width,
 
       -- FDI and PCH transcoder control
-      FDI_TX_CTL_B          => 16#06_1100# / Register_Width,
+      FDI_TX_CTL_B          => 16#06_1100# / Register_Width, -- aliased by GMCH_ADPA
       FDI_RXB_CTL           => 16#0f_100c# / Register_Width,
       FDI_RX_MISC_B         => 16#0f_1010# / Register_Width,
       FDI_RXB_IIR           => 16#0f_1014# / Register_Width,
@@ -1015,7 +1038,7 @@
       DDI_AUX_DATA_A_5      => 16#06_4024# / Register_Width, -- aliased by DP_AUX_DATA_A_5
       DDI_AUX_MUTEX_A       => 16#06_402c# / Register_Width,
 
-      DDI_BUF_CTL_B         => 16#06_4100# / Register_Width,
+      DDI_BUF_CTL_B         => 16#06_4100# / Register_Width, -- aliased by GMCH_DP_B
       DDI_BUF_TRANS_B_S0T1  => 16#06_4e60# / Register_Width,
       DDI_BUF_TRANS_B_S0T2  => 16#06_4e64# / Register_Width,
       DDI_BUF_TRANS_B_S1T1  => 16#06_4e68# / Register_Width,
@@ -1044,7 +1067,7 @@
       DDI_AUX_DATA_B_5      => 16#06_4124# / Register_Width,
       DDI_AUX_MUTEX_B       => 16#06_412c# / Register_Width,
 
-      DDI_BUF_CTL_C         => 16#06_4200# / Register_Width,
+      DDI_BUF_CTL_C         => 16#06_4200# / Register_Width, -- aliased by GMCH_DP_C
       DDI_BUF_TRANS_C_S0T1  => 16#06_4ec0# / Register_Width,
       DDI_BUF_TRANS_C_S0T2  => 16#06_4ec4# / Register_Width,
       DDI_BUF_TRANS_C_S1T1  => 16#06_4ec8# / Register_Width,
@@ -1073,7 +1096,7 @@
       DDI_AUX_DATA_C_5      => 16#06_4224# / Register_Width,
       DDI_AUX_MUTEX_C       => 16#06_422c# / Register_Width,
 
-      DDI_BUF_CTL_D         => 16#06_4300# / Register_Width,
+      DDI_BUF_CTL_D         => 16#06_4300# / Register_Width, -- aliased by GMCH_DP_D
       DDI_BUF_TRANS_D_S0T1  => 16#06_4f20# / Register_Width,
       DDI_BUF_TRANS_D_S0T2  => 16#06_4f24# / Register_Width,
       DDI_BUF_TRANS_D_S1T1  => 16#06_4f28# / Register_Width,
@@ -1303,15 +1326,24 @@
       BLC_PWM_CPU_CTL       => 16#04_8254# / Register_Width,
       BLC_PWM_PCH_CTL2      => 16#0c_8254# / Register_Width,
 
+      -- GMCH LVDS Connector Registers
+      GMCH_LVDS             => 16#06_1180# / Register_Width,
+
       -- PCH LVDS Connector Registers
       PCH_LVDS              => 16#0e_1180# / Register_Width,
 
       -- PCH ADPA Connector Registers
       PCH_ADPA              => 16#0e_1100# / Register_Width,
 
+      -- GMCH DVOB Connector Registers
+      GMCH_SDVOB            => 16#06_1140# / Register_Width,
+
       -- PCH HDMIB Connector Registers
       PCH_HDMIB             => 16#0e_1140# / Register_Width,
 
+      -- GMCH DVOC Connector Registers
+      GMCH_SDVOC            => 16#06_1160# / Register_Width,
+
       -- PCH HDMIC Connector Registers
       PCH_HDMIC             => 16#0e_1150# / Register_Width,
 
@@ -1365,6 +1397,7 @@
       -- audio VID/DID
       AUD_VID_DID           => 16#06_5020# / Register_Width,
       PCH_AUD_VID_DID       => 16#0e_5020# / Register_Width,
+      G4X_AUD_VID_DID       => 16#06_2020# / Register_Width,
 
       -- interrupt registers
       DEISR                 => 16#04_4000# / Register_Width,
@@ -1401,6 +1434,8 @@
 
       -- hotplug and initial detection
       HOTPLUG_CTL           => 16#04_4030# / Register_Width,
+      PORT_HOTPLUG_EN       => 16#06_1110# / Register_Width,
+      PORT_HOTPLUG_STAT     => 16#06_1114# / Register_Width,
       SHOTPLUG_CTL          => 16#0c_4030# / Register_Width,
       SFUSE_STRAP           => 16#0c_2014# / Register_Width,
 
@@ -1478,7 +1513,11 @@
       TRANS_VSYNCSHIFT_B    => 16#0e_1028# / Register_Width,
       TRANS_VSYNCSHIFT_C    => 16#0e_2028# / Register_Width,
       PCH_RAWCLK_FREQ       => 16#0c_6204# / Register_Width,
-      QUIRK_C2004           => 16#0c_2004# / Register_Width);
+      QUIRK_C2004           => 16#0c_2004# / Register_Width,
+
+      -- MCHBAR Mirror
+
+      GMCH_CLKCFG           => 16#01_0c00# / Register_Width);
 
    subtype Registers_Index is Registers_Invalid_Index range
       Registers_Invalid_Index'Succ (Invalid_Register) ..
@@ -1486,6 +1525,9 @@
 
    -- aliased registers
    DP_CTL_A             : constant Registers_Index := DDI_BUF_CTL_A;
+   GMCH_DP_B            : constant Registers_Index := DDI_BUF_CTL_B;
+   GMCH_DP_C            : constant Registers_Index := DDI_BUF_CTL_C;
+   GMCH_DP_D            : constant Registers_Index := DDI_BUF_CTL_D;
    DP_AUX_CTL_A         : constant Registers_Index := DDI_AUX_CTL_A;
    DP_AUX_DATA_A_1      : constant Registers_Index := DDI_AUX_DATA_A_1;
    DP_AUX_DATA_A_2      : constant Registers_Index := DDI_AUX_DATA_A_2;
@@ -1493,6 +1535,9 @@
    DP_AUX_DATA_A_4      : constant Registers_Index := DDI_AUX_DATA_A_4;
    DP_AUX_DATA_A_5      : constant Registers_Index := DDI_AUX_DATA_A_5;
    ILK_DISPLAY_CHICKEN1 : constant Registers_Index := FUSE_STATUS;
+   GMCH_ADPA            : constant Registers_Index := FDI_TX_CTL_B;
+   GMCH_HDMIB           : constant Registers_Index := GMCH_SDVOB;
+   GMCH_HDMIC           : constant Registers_Index := GMCH_SDVOC;
 
    ---------------------------------------------------------------------------
 
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index 809881a..c3b2238 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -334,6 +334,8 @@
          Audio_VID_DID : Word32;
       begin
          case Config.CPU is
+            when G45 =>
+               Registers.Read (Registers.G4X_AUD_VID_DID, Audio_VID_DID);
             when Haswell .. Skylake =>
                Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
             when Ironlake .. Ivybridge =>
@@ -348,7 +350,10 @@
                when Ivybridge |
                     Sandybridge  => Audio_VID_DID = 16#8086_2806# or
                                     Audio_VID_DID = 16#8086_2805#,
-               when Ironlake     => Audio_VID_DID = 16#0000_0000#);
+               when Ironlake     => Audio_VID_DID = 16#0000_0000#,
+               when G45          => Audio_VID_DID = 16#8086_2801# or
+                                    Audio_VID_DID = 16#8086_2802# or
+                                    Audio_VID_DID = 16#8086_2803#);
       end Check_Platform;
 
       procedure Check_Platform_PCI (Success : out Boolean)
@@ -600,13 +605,13 @@
    is
       GGC_Reg : constant :=
         (case Config.CPU is
-            when Ironlake                 => 16#52#,
+            when G45 | Ironlake           => 16#52#,
             when Sandybridge .. Skylake   => 16#50#);
       GGC : Word16;
    begin
       Dev.Read16 (GGC, GGC_Reg);
       case Config.CPU is
-         when Ironlake =>
+         when G45 | Ironlake =>
             GTT_Size    := GTT_Size_Gen4 (GGC);
             Stolen_Size := Stolen_Size_Gen4 (GGC);
          when Sandybridge .. Haswell =>
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index c35c687..fb5c7e5 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -30,7 +30,8 @@
 is
 
    type CPU_Type is
-     (Ironlake,
+     (G45,
+      Ironlake,
       Sandybridge,
       Ivybridge,
       Haswell,