blob: 2841208de3ee770b843172b8818c0245c4803ddf [file] [log] [blame]
--
-- Copyright (C) 2014-2016, 2019 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 GNAT.Source_Info;
with HW.Time;
with HW.Debug;
with HW.GFX.GMA.Config;
with HW.GFX.GMA.Registers;
with HW.GFX.GMA.PCode;
with HW.GFX.GMA.Transcoder;
use type HW.Word64;
package body HW.GFX.GMA.Power_And_Clocks is
type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
subtype Power_Well is Power_Domain range PW1 .. PW2;
subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
DFSM_DISPLAY_CDCLK_LIMIT_675MHZ : constant := 0 * 2 ** 23;
DFSM_DISPLAY_CDCLK_LIMIT_540MHZ : constant := 1 * 2 ** 23;
DFSM_DISPLAY_CDCLK_LIMIT_450MHZ : constant := 2 * 2 ** 23;
DFSM_DISPLAY_CDCLK_LIMIT_337_5MHZ : constant := 3 * 2 ** 23;
DFSM_DISPLAY_CDCLK_LIMIT_MASK : constant := 3 * 2 ** 23;
SFUSE_STRAP_RAW_FREQUENCY : constant := 1 * 2 ** 8;
type Power_Domain_Values is array (Power_Domain) of Word32;
PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
(MISC_IO => 1 * 2 ** 1,
DDI_AE => 1 * 2 ** 3,
DDI_B => 1 * 2 ** 5,
DDI_C => 1 * 2 ** 7,
DDI_D => 1 * 2 ** 9,
PW1 => 1 * 2 ** 29,
PW2 => 1 * 2 ** 31);
PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
(MISC_IO => 1 * 2 ** 0,
DDI_AE => 1 * 2 ** 2,
DDI_B => 1 * 2 ** 4,
DDI_C => 1 * 2 ** 6,
DDI_D => 1 * 2 ** 8,
PW1 => 1 * 2 ** 28,
PW2 => 1 * 2 ** 30);
type Power_Well_Values is array (Power_Well) of Word32;
FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
(PW1 => 1 * 2 ** 26,
PW2 => 1 * 2 ** 25);
DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
----------------------------------------------------------------------------
DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
LCPLL1_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
LCPLL1_CTL_PLL_LOCK : constant := 1 * 2 ** 30;
DISP_FBC_MEMORY_WAKE : constant := 1 * 2 ** 31;
----------------------------------------------------------------------------
CDCLK_CTL_CD_FREQ_SELECT_MASK : constant := 3 * 2 ** 26;
CDCLK_CTL_CD_FREQ_SELECT_450MHZ : constant := 0 * 2 ** 26;
CDCLK_CTL_CD_FREQ_SELECT_540MHZ : constant := 1 * 2 ** 26;
CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ : constant := 2 * 2 ** 26;
CDCLK_CTL_CD_FREQ_SELECT_675MHZ : constant := 3 * 2 ** 26;
CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
SKL_PCODE_CDCLK_CONTROL : constant := 7;
SKL_CDCLK_PREPARE_FOR_CHANGE : constant := 3;
SKL_CDCLK_READY_FOR_CHANGE : constant := 1;
function CDCLK_CTL_CD_FREQ_DECIMAL (CDClk : Frequency_Type) return Word32 is
begin
-- Weirdest representation: CDClk - 1MHz in 10.1 (10 + 1 fractional bit)
return Word32 ((CDClk - 1_000_000) / 500_000);
end CDCLK_CTL_CD_FREQ_DECIMAL;
----------------------------------------------------------------------------
procedure PD_Off (PD : Power_Domain)
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_POWER_REQUEST (PD)) /= 0
then
Registers.Wait_Set_Mask
(Register => Registers.PWR_WELL_CTL_DRIVER,
Mask => PWR_WELL_CTL_POWER_STATE (PD));
end if;
if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
Registers.Unset_Mask
(Register => Registers.PWR_WELL_CTL_BIOS,
Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
end if;
if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
Registers.Unset_Mask
(Register => Registers.PWR_WELL_CTL_DRIVER,
Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
end if;
end PD_Off;
procedure PD_On (PD : Power_Domain)
with
Pre => True
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_POWER_REQUEST (PD)) = 0
then
Registers.Wait_Unset_Mask
(Register => Registers.PWR_WELL_CTL_DRIVER,
Mask => PWR_WELL_CTL_POWER_STATE (PD));
end if;
if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
Registers.Set_Mask
(Register => Registers.PWR_WELL_CTL_DRIVER,
Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
Registers.Wait_Set_Mask
(Register => Registers.PWR_WELL_CTL_DRIVER,
Mask => PWR_WELL_CTL_POWER_STATE (PD));
if PD in Power_Well then
Registers.Wait_Set_Mask
(Register => Registers.FUSE_STATUS,
Mask => FUSE_STATUS_PGx_DIST_STATUS (PD));
end if;
end if;
end PD_On;
function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
is
begin
return (case PD is
when DDI_AE => Configs (Primary).Port = eDP or
Configs (Secondary).Port = eDP or
Configs (Tertiary).Port = eDP,
when DDI_B => Configs (Primary).Port = HDMI1 or
Configs (Primary).Port = DP1 or
Configs (Secondary).Port = HDMI1 or
Configs (Secondary).Port = DP1 or
Configs (Tertiary).Port = HDMI1 or
Configs (Tertiary).Port = DP1,
when DDI_C => Configs (Primary).Port = HDMI2 or
Configs (Primary).Port = DP2 or
Configs (Secondary).Port = HDMI2 or
Configs (Secondary).Port = DP2 or
Configs (Tertiary).Port = HDMI2 or
Configs (Tertiary).Port = DP2,
when DDI_D => Configs (Primary).Port = HDMI3 or
Configs (Primary).Port = DP3 or
Configs (Secondary).Port = HDMI3 or
Configs (Secondary).Port = DP3 or
Configs (Tertiary).Port = HDMI3 or
Configs (Tertiary).Port = DP3,
when PW2 => (Configs (Primary).Port /= Disabled and
Configs (Primary).Port /= eDP) or
Configs (Secondary).Port /= Disabled or
Configs (Tertiary).Port /= Disabled);
end Need_PD;
----------------------------------------------------------------------------
procedure Pre_All_Off is
begin
Transcoder.PSR_Off;
end Pre_All_Off;
procedure Post_All_Off is
begin
for PD in reverse Dynamic_Domain loop
PD_Off (PD);
end loop;
Registers.Unset_Mask
(Register => Registers.DBUF_CTL_S0,
Mask => DBUF_CTL_DBUF_POWER_REQUEST);
Registers.Wait_Unset_Mask
(Register => Registers.DBUF_CTL_S0,
Mask => DBUF_CTL_DBUF_POWER_STATE);
Registers.Unset_Mask
(Register => Registers.LCPLL1_CTL,
Mask => LCPLL1_CTL_PLL_ENABLE);
Registers.Wait_Unset_Mask
(Register => Registers.LCPLL1_CTL,
Mask => LCPLL1_CTL_PLL_LOCK);
PD_Off (MISC_IO);
PD_Off (PW1);
end Post_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
CDCLK_CTL : Word32;
begin
Registers.Read (Registers.CDCLK_CTL, CDCLK_CTL);
CDCLK_CTL := CDCLK_CTL and CDCLK_CTL_CD_FREQ_DECIMAL_MASK;
CDClk := Normalize_CDClk (Int64 (CDCLK_CTL) * 500_000 + 1_000_000);
end Get_Cur_CDClk;
procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
is
DFSM : Word32;
begin
Registers.Read (Registers.DFSM, DFSM);
CDClk :=
(case DFSM and DFSM_DISPLAY_CDCLK_LIMIT_MASK is
when DFSM_DISPLAY_CDCLK_LIMIT_675MHZ => 675_000_000,
when DFSM_DISPLAY_CDCLK_LIMIT_540MHZ => 540_000_000,
when DFSM_DISPLAY_CDCLK_LIMIT_450MHZ => 450_000_000,
when others => 337_500_000);
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
PCode.Mailbox_Request
(MBox => SKL_PCODE_CDCLK_CONTROL,
Command => SKL_CDCLK_PREPARE_FOR_CHANGE,
Reply_Mask => SKL_CDCLK_READY_FOR_CHANGE,
Wait_Ready => True,
Success => Success);
if not Success then
pragma Debug (Debug.Put_Line
("ERROR: PCODE not ready for frequency change."));
return;
end if;
Registers.Write
(Register => Registers.CDCLK_CTL,
Value => (case CDClk is
when 675_000_000 => CDCLK_CTL_CD_FREQ_SELECT_675MHZ,
when 540_000_000 => CDCLK_CTL_CD_FREQ_SELECT_540MHZ,
when 450_000_000 => CDCLK_CTL_CD_FREQ_SELECT_450MHZ,
when others => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ)
or CDCLK_CTL_CD_FREQ_DECIMAL (CDClk));
PCode.Mailbox_Write
(MBox => SKL_PCODE_CDCLK_CONTROL,
Command => (case CDClk is
when 675_000_000 => 3,
when 540_000_000 => 2,
when 450_000_000 => 1,
when others => 0));
Registers.Set_Mask
(Register => Registers.DBUF_CTL_S0,
Mask => DBUF_CTL_DBUF_POWER_REQUEST);
Registers.Wait_Set_Mask
(Register => Registers.DBUF_CTL_S0,
Mask => DBUF_CTL_DBUF_POWER_STATE);
Config.CDClk := CDClk;
end Set_CDClk;
procedure Get_Raw_Clock (Raw_Clock : out Frequency_Type)
is
Freq_24MHz : Boolean;
begin
Raw_Clock := Config.Default_RawClk_Freq;
if Config.Has_Fractional_RawClk then
Registers.Is_Set_Mask
(Register => Registers.SFUSE_STRAP,
Mask => SFUSE_STRAP_RAW_FREQUENCY,
Result => Freq_24MHz);
if not Freq_24MHz then
Raw_Clock := 19_200_000;
end if;
end if;
end Get_Raw_Clock;
procedure Initialize is
begin
Registers.Set_Mask
(Register => Registers.NDE_RSTWRN_OPT,
Mask => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
Registers.Wait_Set_Mask
(Register => Registers.FUSE_STATUS,
Mask => FUSE_STATUS_PG0_DIST_STATUS);
PD_On (PW1);
PD_On (MISC_IO);
-- TODO: Set to preferred eDP rate:
-- Registers.Unset_And_Set_Mask
-- (Register => Registers.DPLL_CTRL1,
-- Unset_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
-- Set_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_...);
Registers.Set_Mask
(Register => Registers.LCPLL1_CTL,
Mask => LCPLL1_CTL_PLL_ENABLE);
Registers.Wait_Set_Mask
(Register => Registers.LCPLL1_CTL,
Mask => LCPLL1_CTL_PLL_LOCK);
-- WaEnableChickenDCPR:skl,bxt,kbl,glk,cfl
Registers.Set_Mask
(Register => Registers.GEN8_CHICKEN_DCPR_1,
Mask => 1 * 2 ** 13);
-- Display WA #0859 WaFbcWakeMemOn:skl,bxt,kbl,glk,cfl
Registers.Set_Mask
(Register => Registers.ARB_CTL,
Mask => DISP_FBC_MEMORY_WAKE);
Get_Cur_CDClk (Config.CDClk);
Get_Max_CDClk (Config.Max_CDClk);
Set_CDClk (Config.Default_CDClk_Freq);
declare -- avoid aliasing
Raw_Clock : Frequency_Type;
begin
Get_Raw_Clock (Raw_Clock);
Config.Raw_Clock := Raw_Clock;
end;
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.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
for PD in reverse Dynamic_Domain loop
if not Need_PD (PD, Configs) then
PD_Off (PD);
end if;
end loop;
for PD in Dynamic_Domain loop
if Need_PD (PD, Configs) then
PD_On (PD);
end if;
end loop;
end Power_Set_To;
procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
begin
for PD in Dynamic_Domain loop
if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
PD_On (PD);
end if;
end loop;
end Power_Up;
procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
is
begin
for PD in reverse Dynamic_Domain loop
if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
not Need_PD (PD, New_Configs)
then
PD_Off (PD);
end if;
end loop;
end Power_Down;
end HW.GFX.GMA.Power_And_Clocks;