blob: e261ace3fadd85fdb1372533d8c33b039db86dbf [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber3d06de82018-05-29 01:35:04 +02002-- Copyright (C) 2014-2018 secunet Security Networks AG
Nico Huberd0f84b92019-09-22 21:31:52 +02003-- Copyright (C) 2019 Nico Huber <nico.h@gmx.de>
Nico Huber83693c82016-10-08 22:17:55 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02007-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02009--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
16with GNAT.Source_Info;
17
18with HW.Time;
19with HW.Debug;
20with HW.GFX.GMA.Config;
Nico Huber312433c2019-09-28 03:15:48 +020021with HW.GFX.GMA.PCode;
Nico Huber83693c82016-10-08 22:17:55 +020022with HW.GFX.GMA.Registers;
Angel Pons3f86b0b2020-07-18 00:22:32 +020023with HW.GFX.GMA.Transcoder;
Nico Huber83693c82016-10-08 22:17:55 +020024
25package body HW.GFX.GMA.Power_And_Clocks_Haswell is
26
Nico Huberd0f84b92019-09-22 21:31:52 +020027 LCPLL_CTL_CD_FREQ_SEL_MASK : constant := 3 * 2 ** 26;
28 LCPLL_CTL_CD_FREQ_SEL_450_MHZ : constant := 0 * 2 ** 26;
29 LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE : constant := 1 * 2 ** 26;
30 LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ : constant := 1 * 2 ** 26;
31 LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ : constant := 2 * 2 ** 26;
32 LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ : constant := 3 * 2 ** 26;
33 LCPLL_CTL_CD_SOURCE_SELECT_FCLK : constant := 1 * 2 ** 21;
34 LCPLL_CTL_CD_SOURCE_FCLK_DONE : constant := 1 * 2 ** 19;
35
36 function LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk : Config.CDClk_Range) return Word32
37 is
38 (case CDClk is
39 when 675_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ,
40 when 540_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ,
41 when 450_000_000 => LCPLL_CTL_CD_FREQ_SEL_450_MHZ,
42 when others => LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ);
43
44 FUSE_STRAP_DISPLAY_CDCLK_LIMIT : constant := 1 * 2 ** 24;
45
46 HSW_PCODE_DE_WRITE_FREQ : constant := 16#17#;
47 BDW_PCODE_DISPLAY_FREQ_CHANGE : constant := 16#18#;
48
49 ----------------------------------------------------------------------------
50
Nico Huber83693c82016-10-08 22:17:55 +020051 PWR_WELL_CTL_ENABLE_REQUEST : constant := 1 * 2 ** 31;
52 PWR_WELL_CTL_DISABLE_REQUEST : constant := 0 * 2 ** 31;
53 PWR_WELL_CTL_STATE_ENABLED : constant := 1 * 2 ** 30;
54
55 ----------------------------------------------------------------------------
56
Nico Huber83693c82016-10-08 22:17:55 +020057 IPS_CTL_ENABLE : constant := 1 * 2 ** 31;
58 DISPLAY_IPS_CONTROL : constant := 16#19#;
59
Nico Huber83693c82016-10-08 22:17:55 +020060 ----------------------------------------------------------------------------
61
Nico Huber83693c82016-10-08 22:17:55 +020062 procedure IPS_Off
63 is
64 Enabled : Boolean;
65 begin
66 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
67
68 if Config.Has_IPS then
69 Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
70 if Enabled then
71 if Config.Has_IPS_CTL_Mailbox then
Nico Huber312433c2019-09-28 03:15:48 +020072 PCode.Mailbox_Write (DISPLAY_IPS_CONTROL, 0, Wait_Ready => True);
Nico Huber43cf8d52016-09-08 17:24:52 +020073 Registers.Wait_Unset_Mask
74 (Register => Registers.IPS_CTL,
75 Mask => IPS_CTL_ENABLE,
76 TOut_MS => 42);
Nico Huber83693c82016-10-08 22:17:55 +020077 else
78 Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
79 end if;
80
81 pragma Debug (Debug.Put_Line ("Disabled IPS."));
82 -- We have to wait until the next vblank here.
83 -- 20ms should be enough.
84 Time.M_Delay (20);
85 end if;
86 end if;
87 end IPS_Off;
88
89 ----------------------------------------------------------------------------
90
91 procedure PDW_Off
92 is
93 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
94 begin
95 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
96
97 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
98 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
99 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
100 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
101 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
102 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
103
104 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
105 PWR_WELL_CTL_ENABLE_REQUEST) /= 0
106 then
107 Registers.Wait_Set_Mask
108 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
109 end if;
110
111 if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
112 Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
113 end if;
114
115 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
116 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
117 end if;
118 end PDW_Off;
119
120 procedure PDW_On
121 is
122 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
123 begin
124 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
125
126 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
127 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
128 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
129 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
130 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
131 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
132
133 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
134 PWR_WELL_CTL_ENABLE_REQUEST) = 0
135 then
136 Registers.Wait_Unset_Mask
137 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
138 end if;
139
140 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
141 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
142 Registers.Wait_Set_Mask
143 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
144 end if;
145 end PDW_On;
146
Nico Huber3d06de82018-05-29 01:35:04 +0200147 function Need_PDW (Checked_Configs : Pipe_Configs) return Boolean
148 is
149 Primary : Pipe_Config renames Checked_Configs (GMA.Primary);
Nico Huber83693c82016-10-08 22:17:55 +0200150 begin
Nico Huber3d06de82018-05-29 01:35:04 +0200151 return
152 (Config.Use_PDW_For_EDP_Scaling and then
Nico Huber8beafd72020-01-07 14:59:44 +0100153 (Primary.Port = eDP and Requires_Scaling (Primary)))
Nico Huber3d06de82018-05-29 01:35:04 +0200154 or
Nico Huber8beafd72020-01-07 14:59:44 +0100155 (Primary.Port /= Disabled and Primary.Port /= eDP)
Nico Huber3d06de82018-05-29 01:35:04 +0200156 or
157 Checked_Configs (Secondary).Port /= Disabled
158 or
159 Checked_Configs (Tertiary).Port /= Disabled;
Nico Huber83693c82016-10-08 22:17:55 +0200160 end Need_PDW;
161
162 ----------------------------------------------------------------------------
163
164 procedure Pre_All_Off is
165 begin
166 -- HSW: disable panel self refresh (PSR) on eDP if enabled
167 -- wait for PSR idling
Angel Pons3f86b0b2020-07-18 00:22:32 +0200168 Transcoder.PSR_Off;
Nico Huber83693c82016-10-08 22:17:55 +0200169 IPS_Off;
170 end Pre_All_Off;
171
Nico Huberd0f84b92019-09-22 21:31:52 +0200172 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
173 ( if CDClk <= 337_500_000 then 337_500_000
174 elsif CDClk <= 450_000_000 then 450_000_000
175 elsif CDClk <= 540_000_000 then 540_000_000
176 else 675_000_000);
177
178 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
179 is
180 LCPLL_CTL : Word32;
181 begin
182 Registers.Read (Registers.LCPLL_CTL, LCPLL_CTL);
183 CDClk :=
184 (if Config.Has_Broadwell_CDClk then
185 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
186 when LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ => 540_000_000,
187 when LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ => 337_500_000,
188 when LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ => 675_000_000,
189 when others => 450_000_000)
190 else
191 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
192 when LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE =>
193 (if Config.Is_ULX then 337_500_000
194 elsif Config.Is_ULT then 450_000_000
195 else 540_000_000),
196 when others => 450_000_000));
197 end Get_Cur_CDClk;
198
199 procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
200 is
201 FUSE_STRAP : Word32;
202 begin
203 if Config.Has_Broadwell_CDClk then
204 Registers.Read (Registers.FUSE_STRAP, FUSE_STRAP);
205 CDClk :=
206 (if (FUSE_STRAP and FUSE_STRAP_DISPLAY_CDCLK_LIMIT) /= 0 then
207 450_000_000
208 elsif Config.Is_ULX then
209 450_000_000
210 elsif Config.Is_ULT then
211 540_000_000
212 else
213 675_000_000);
214 else
215 -- We may never switch CDClk on Haswell. So from our point
216 -- of view, the CDClk we start with is the maximum.
217 Get_Cur_CDClk (CDClk);
218 end if;
219 end Get_Max_CDClk;
220
221 procedure Set_CDClk (CDClk_In : Frequency_Type)
222 is
223 CDClk : constant Config.CDClk_Range :=
224 Normalize_CDClk (Frequency_Type'Min (CDClk_In, Config.Max_CDClk));
225 Success : Boolean;
226 begin
227 if not Config.Can_Switch_CDClk then
228 return;
229 end if;
230
231 PCode.Mailbox_Write
232 (MBox => BDW_PCODE_DISPLAY_FREQ_CHANGE,
233 Command => 0,
234 Wait_Ready => True,
235 Success => Success);
236
237 if not Success then
238 pragma Debug (Debug.Put_Line
239 ("ERROR: PCODE didn't acknowledge frequency change."));
240 return;
241 end if;
242
243 Registers.Set_Mask
244 (Register => Registers.LCPLL_CTL,
245 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
246 Registers.Wait_Set_Mask
247 (Register => Registers.LCPLL_CTL,
248 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
249
250 Registers.Unset_And_Set_Mask
251 (Register => Registers.LCPLL_CTL,
252 Mask_Unset => LCPLL_CTL_CD_FREQ_SEL_MASK,
253 Mask_Set => LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk));
254 Registers.Posting_Read (Registers.LCPLL_CTL);
255
256 Registers.Unset_Mask
257 (Register => Registers.LCPLL_CTL,
258 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
259 Registers.Wait_Unset_Mask
260 (Register => Registers.LCPLL_CTL,
261 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
262
263 PCode.Mailbox_Write
264 (MBox => HSW_PCODE_DE_WRITE_FREQ,
265 Command => (case CDClk is
266 when 675_000_000 => 3,
267 when 540_000_000 => 1,
268 when 450_000_000 => 0,
269 when others => 2));
270
271 Registers.Write
272 (Register => Registers.CDCLK_FREQ,
273 Value => Word32 (Div_Round_Closest (CDClk, 1_000_000) - 1));
274
275 Config.CDClk := CDClk;
276 end Set_CDClk;
277
278 procedure Initialize
279 is
280 CDClk : Config.CDClk_Range;
Nico Huber83693c82016-10-08 22:17:55 +0200281 begin
282 -- HSW: disable power down well
283 PDW_Off;
Nico Huberd0f84b92019-09-22 21:31:52 +0200284
285 Get_Cur_CDClk (CDClk);
286 Config.CDClk := CDClk;
287 Get_Max_CDClk (CDClk);
288 Config.Max_CDClk := CDClk;
289 Set_CDClk (Config.Default_CDClk_Freq);
290
Arthur Heymansd1988d12018-03-28 16:27:57 +0200291 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber83693c82016-10-08 22:17:55 +0200292 end Initialize;
293
Nico Huberd0f84b92019-09-22 21:31:52 +0200294 procedure Limit_Dotclocks
295 (Configs : in out Pipe_Configs;
296 CDClk_Switch : out Boolean)
297 is
298 begin
299 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
300 CDClk_Switch :=
301 Config.Can_Switch_CDClk and then
302 Config.CDClk /= Normalize_CDClk
303 (Config_Helpers.Highest_Dotclock (Configs));
304 end Limit_Dotclocks;
305
306 procedure Update_CDClk (Configs : in out Pipe_Configs)
307 is
308 New_CDClk : constant Frequency_Type :=
309 Config_Helpers.Highest_Dotclock (Configs);
310 begin
311 Set_CDClk (New_CDClk);
312 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
313 end Update_CDClk;
314
Nico Huber99f10f32016-11-20 00:34:05 +0100315 procedure Power_Set_To (Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200316 begin
317 if Need_PDW (Configs) then
318 PDW_On;
319 else
320 PDW_Off;
321 end if;
322 end Power_Set_To;
323
Nico Huber99f10f32016-11-20 00:34:05 +0100324 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200325 begin
326 if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
327 PDW_On;
328 end if;
329 end Power_Up;
330
Nico Huber99f10f32016-11-20 00:34:05 +0100331 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200332 is
333 begin
334 if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
335 not Need_PDW (New_Configs)
336 then
337 PDW_Off;
338 end if;
339 end Power_Down;
340
341end HW.GFX.GMA.Power_And_Clocks_Haswell;