Move `PSR_Off` out of `Power_And_Clocks_Haswell`
Allowing other platforms to use the Haswell-specific power and clocks
package precludes with'ing other Haswell-specific units unless they are
placed under `haswell_shared` as well. This unnecessarily forces several
implementation-specific details to be public, breaking encapsulation.
The only benefit is that the `PSR_Off` procedure gets to be shared.
However, we can allow reusing said procedure without having to destroy
encapsulation, by moving it elsewhere. As the SRD/PSR registers are tied
to transcoders, place `PSR_Off` and the corresponding definitions to the
common `Transcoder` package. Also update the callers of this procedure
to refer to the `Transcoder` package, and then drop the visibility of
the power and clocks package for Haswell.
Change-Id: I7483409b8b7db58874cbba3c0a7edb1968bba456
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/libgfxinit/+/43563
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/haswell/hw-gfx-gma-power_and_clocks_haswell.adb b/common/haswell/hw-gfx-gma-power_and_clocks_haswell.adb
new file mode 100644
index 0000000..e261ace
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-power_and_clocks_haswell.adb
@@ -0,0 +1,341 @@
+--
+-- Copyright (C) 2014-2018 secunet Security Networks AG
+-- Copyright (C) 2019 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 GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCode;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Transcoder;
+
+package body HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+ LCPLL_CTL_CD_FREQ_SEL_MASK : constant := 3 * 2 ** 26;
+ LCPLL_CTL_CD_FREQ_SEL_450_MHZ : constant := 0 * 2 ** 26;
+ LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE : constant := 1 * 2 ** 26;
+ LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ : constant := 1 * 2 ** 26;
+ LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ : constant := 2 * 2 ** 26;
+ LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ : constant := 3 * 2 ** 26;
+ LCPLL_CTL_CD_SOURCE_SELECT_FCLK : constant := 1 * 2 ** 21;
+ LCPLL_CTL_CD_SOURCE_FCLK_DONE : constant := 1 * 2 ** 19;
+
+ function LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk : Config.CDClk_Range) return Word32
+ is
+ (case CDClk is
+ when 675_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ,
+ when 540_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ,
+ when 450_000_000 => LCPLL_CTL_CD_FREQ_SEL_450_MHZ,
+ when others => LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ);
+
+ FUSE_STRAP_DISPLAY_CDCLK_LIMIT : constant := 1 * 2 ** 24;
+
+ HSW_PCODE_DE_WRITE_FREQ : constant := 16#17#;
+ BDW_PCODE_DISPLAY_FREQ_CHANGE : constant := 16#18#;
+
+ ----------------------------------------------------------------------------
+
+ PWR_WELL_CTL_ENABLE_REQUEST : constant := 1 * 2 ** 31;
+ PWR_WELL_CTL_DISABLE_REQUEST : constant := 0 * 2 ** 31;
+ PWR_WELL_CTL_STATE_ENABLED : constant := 1 * 2 ** 30;
+
+ ----------------------------------------------------------------------------
+
+ IPS_CTL_ENABLE : constant := 1 * 2 ** 31;
+ DISPLAY_IPS_CONTROL : constant := 16#19#;
+
+ ----------------------------------------------------------------------------
+
+ procedure IPS_Off
+ is
+ Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_IPS then
+ Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+ if Enabled then
+ if Config.Has_IPS_CTL_Mailbox then
+ PCode.Mailbox_Write (DISPLAY_IPS_CONTROL, 0, Wait_Ready => True);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.IPS_CTL,
+ Mask => IPS_CTL_ENABLE,
+ TOut_MS => 42);
+ else
+ Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+ end if;
+
+ pragma Debug (Debug.Put_Line ("Disabled IPS."));
+ -- We have to wait until the next vblank here.
+ -- 20ms should be enough.
+ Time.M_Delay (20);
+ end if;
+ end if;
+ end IPS_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure PDW_Off
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_ENABLE_REQUEST) /= 0
+ then
+ Registers.Wait_Set_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+
+ if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
+ end if;
+ end PDW_Off;
+
+ procedure PDW_On
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_ENABLE_REQUEST) = 0
+ then
+ Registers.Wait_Unset_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
+ Registers.Wait_Set_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+ end PDW_On;
+
+ function Need_PDW (Checked_Configs : Pipe_Configs) return Boolean
+ is
+ Primary : Pipe_Config renames Checked_Configs (GMA.Primary);
+ begin
+ return
+ (Config.Use_PDW_For_EDP_Scaling and then
+ (Primary.Port = eDP and Requires_Scaling (Primary)))
+ or
+ (Primary.Port /= Disabled and Primary.Port /= eDP)
+ or
+ Checked_Configs (Secondary).Port /= Disabled
+ or
+ Checked_Configs (Tertiary).Port /= Disabled;
+ end Need_PDW;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_All_Off is
+ begin
+ -- HSW: disable panel self refresh (PSR) on eDP if enabled
+ -- wait for PSR idling
+ Transcoder.PSR_Off;
+ IPS_Off;
+ end Pre_All_Off;
+
+ function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
+ ( if CDClk <= 337_500_000 then 337_500_000
+ elsif CDClk <= 450_000_000 then 450_000_000
+ elsif CDClk <= 540_000_000 then 540_000_000
+ else 675_000_000);
+
+ procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
+ is
+ LCPLL_CTL : Word32;
+ begin
+ Registers.Read (Registers.LCPLL_CTL, LCPLL_CTL);
+ CDClk :=
+ (if Config.Has_Broadwell_CDClk then
+ (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
+ when LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ => 540_000_000,
+ when LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ => 337_500_000,
+ when LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ => 675_000_000,
+ when others => 450_000_000)
+ else
+ (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
+ when LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE =>
+ (if Config.Is_ULX then 337_500_000
+ elsif Config.Is_ULT then 450_000_000
+ else 540_000_000),
+ when others => 450_000_000));
+ end Get_Cur_CDClk;
+
+ procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
+ is
+ FUSE_STRAP : Word32;
+ begin
+ if Config.Has_Broadwell_CDClk then
+ Registers.Read (Registers.FUSE_STRAP, FUSE_STRAP);
+ CDClk :=
+ (if (FUSE_STRAP and FUSE_STRAP_DISPLAY_CDCLK_LIMIT) /= 0 then
+ 450_000_000
+ elsif Config.Is_ULX then
+ 450_000_000
+ elsif Config.Is_ULT then
+ 540_000_000
+ else
+ 675_000_000);
+ else
+ -- We may never switch CDClk on Haswell. So from our point
+ -- of view, the CDClk we start with is the maximum.
+ Get_Cur_CDClk (CDClk);
+ end if;
+ end Get_Max_CDClk;
+
+ procedure Set_CDClk (CDClk_In : Frequency_Type)
+ is
+ CDClk : constant Config.CDClk_Range :=
+ Normalize_CDClk (Frequency_Type'Min (CDClk_In, Config.Max_CDClk));
+ Success : Boolean;
+ begin
+ if not Config.Can_Switch_CDClk then
+ return;
+ end if;
+
+ PCode.Mailbox_Write
+ (MBox => BDW_PCODE_DISPLAY_FREQ_CHANGE,
+ Command => 0,
+ Wait_Ready => True,
+ Success => Success);
+
+ if not Success then
+ pragma Debug (Debug.Put_Line
+ ("ERROR: PCODE didn't acknowledge frequency change."));
+ return;
+ end if;
+
+ Registers.Set_Mask
+ (Register => Registers.LCPLL_CTL,
+ Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
+ Registers.Wait_Set_Mask
+ (Register => Registers.LCPLL_CTL,
+ Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.LCPLL_CTL,
+ Mask_Unset => LCPLL_CTL_CD_FREQ_SEL_MASK,
+ Mask_Set => LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk));
+ Registers.Posting_Read (Registers.LCPLL_CTL);
+
+ Registers.Unset_Mask
+ (Register => Registers.LCPLL_CTL,
+ Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.LCPLL_CTL,
+ Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
+
+ PCode.Mailbox_Write
+ (MBox => HSW_PCODE_DE_WRITE_FREQ,
+ Command => (case CDClk is
+ when 675_000_000 => 3,
+ when 540_000_000 => 1,
+ when 450_000_000 => 0,
+ when others => 2));
+
+ Registers.Write
+ (Register => Registers.CDCLK_FREQ,
+ Value => Word32 (Div_Round_Closest (CDClk, 1_000_000) - 1));
+
+ Config.CDClk := CDClk;
+ end Set_CDClk;
+
+ procedure Initialize
+ is
+ CDClk : Config.CDClk_Range;
+ begin
+ -- HSW: disable power down well
+ PDW_Off;
+
+ Get_Cur_CDClk (CDClk);
+ Config.CDClk := CDClk;
+ Get_Max_CDClk (CDClk);
+ Config.Max_CDClk := CDClk;
+ Set_CDClk (Config.Default_CDClk_Freq);
+
+ Config.Raw_Clock := Config.Default_RawClk_Freq;
+ end Initialize;
+
+ procedure Limit_Dotclocks
+ (Configs : in out Pipe_Configs;
+ CDClk_Switch : out Boolean)
+ is
+ begin
+ Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
+ CDClk_Switch :=
+ Config.Can_Switch_CDClk and then
+ Config.CDClk /= Normalize_CDClk
+ (Config_Helpers.Highest_Dotclock (Configs));
+ end Limit_Dotclocks;
+
+ procedure Update_CDClk (Configs : in out Pipe_Configs)
+ is
+ New_CDClk : constant Frequency_Type :=
+ Config_Helpers.Highest_Dotclock (Configs);
+ begin
+ Set_CDClk (New_CDClk);
+ Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
+ end Update_CDClk;
+
+ procedure Power_Set_To (Configs : Pipe_Configs) is
+ begin
+ if Need_PDW (Configs) then
+ PDW_On;
+ else
+ PDW_Off;
+ end if;
+ end Power_Set_To;
+
+ procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
+ begin
+ if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
+ PDW_On;
+ end if;
+ end Power_Up;
+
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
+ is
+ begin
+ if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
+ not Need_PDW (New_Configs)
+ then
+ PDW_Off;
+ end if;
+ end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;