blob: 44d9acd6ff290dfb1f30c56274f095ba32fd634a [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;
23
24package body HW.GFX.GMA.Power_And_Clocks_Haswell is
25
Nico Huberd0f84b92019-09-22 21:31:52 +020026 LCPLL_CTL_CD_FREQ_SEL_MASK : constant := 3 * 2 ** 26;
27 LCPLL_CTL_CD_FREQ_SEL_450_MHZ : constant := 0 * 2 ** 26;
28 LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE : constant := 1 * 2 ** 26;
29 LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ : constant := 1 * 2 ** 26;
30 LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ : constant := 2 * 2 ** 26;
31 LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ : constant := 3 * 2 ** 26;
32 LCPLL_CTL_CD_SOURCE_SELECT_FCLK : constant := 1 * 2 ** 21;
33 LCPLL_CTL_CD_SOURCE_FCLK_DONE : constant := 1 * 2 ** 19;
34
35 function LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk : Config.CDClk_Range) return Word32
36 is
37 (case CDClk is
38 when 675_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ,
39 when 540_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ,
40 when 450_000_000 => LCPLL_CTL_CD_FREQ_SEL_450_MHZ,
41 when others => LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ);
42
43 FUSE_STRAP_DISPLAY_CDCLK_LIMIT : constant := 1 * 2 ** 24;
44
45 HSW_PCODE_DE_WRITE_FREQ : constant := 16#17#;
46 BDW_PCODE_DISPLAY_FREQ_CHANGE : constant := 16#18#;
47
48 ----------------------------------------------------------------------------
49
Nico Huber83693c82016-10-08 22:17:55 +020050 PWR_WELL_CTL_ENABLE_REQUEST : constant := 1 * 2 ** 31;
51 PWR_WELL_CTL_DISABLE_REQUEST : constant := 0 * 2 ** 31;
52 PWR_WELL_CTL_STATE_ENABLED : constant := 1 * 2 ** 30;
53
54 ----------------------------------------------------------------------------
55
56 SRD_CTL_ENABLE : constant := 1 * 2 ** 31;
57 SRD_STATUS_STATE_MASK : constant := 7 * 2 ** 29;
58
59 type Pipe is (EDP, A, B, C);
60 type SRD_Regs is record
61 CTL : Registers.Registers_Index;
62 STATUS : Registers.Registers_Index;
63 end record;
64 type SRD_Per_Pipe_Regs is array (Pipe) of SRD_Regs;
65 SRD : constant SRD_Per_Pipe_Regs := SRD_Per_Pipe_Regs'
66 (A => SRD_Regs'
67 (CTL => Registers.SRD_CTL_A,
68 STATUS => Registers.SRD_STATUS_A),
69 B => SRD_Regs'
70 (CTL => Registers.SRD_CTL_B,
71 STATUS => Registers.SRD_STATUS_B),
72 C => SRD_Regs'
73 (CTL => Registers.SRD_CTL_C,
74 STATUS => Registers.SRD_STATUS_C),
75 EDP => SRD_Regs'
76 (CTL => Registers.SRD_CTL_EDP,
77 STATUS => Registers.SRD_STATUS_EDP));
78
79 ----------------------------------------------------------------------------
80
81 IPS_CTL_ENABLE : constant := 1 * 2 ** 31;
82 DISPLAY_IPS_CONTROL : constant := 16#19#;
83
Nico Huber83693c82016-10-08 22:17:55 +020084 ----------------------------------------------------------------------------
85
86 procedure PSR_Off
87 is
88 Enabled : Boolean;
89 begin
90 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
91
92 if Config.Has_Per_Pipe_SRD then
93 for P in Pipe loop
94 Registers.Is_Set_Mask (SRD (P).CTL, SRD_CTL_ENABLE, Enabled);
95 if Enabled then
96 Registers.Unset_Mask (SRD (P).CTL, SRD_CTL_ENABLE);
97 Registers.Wait_Unset_Mask (SRD (P).STATUS, SRD_STATUS_STATE_MASK);
98
99 pragma Debug (Debug.Put_Line ("Disabled PSR."));
100 end if;
101 end loop;
102 else
103 Registers.Is_Set_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE, Enabled);
104 if Enabled then
105 Registers.Unset_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE);
106 Registers.Wait_Unset_Mask (Registers.SRD_STATUS, SRD_STATUS_STATE_MASK);
107
108 pragma Debug (Debug.Put_Line ("Disabled PSR."));
109 end if;
110 end if;
111 end PSR_Off;
112
113 ----------------------------------------------------------------------------
114
Nico Huber83693c82016-10-08 22:17:55 +0200115 procedure IPS_Off
116 is
117 Enabled : Boolean;
118 begin
119 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
120
121 if Config.Has_IPS then
122 Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
123 if Enabled then
124 if Config.Has_IPS_CTL_Mailbox then
Nico Huber312433c2019-09-28 03:15:48 +0200125 PCode.Mailbox_Write (DISPLAY_IPS_CONTROL, 0, Wait_Ready => True);
Nico Huber43cf8d52016-09-08 17:24:52 +0200126 Registers.Wait_Unset_Mask
127 (Register => Registers.IPS_CTL,
128 Mask => IPS_CTL_ENABLE,
129 TOut_MS => 42);
Nico Huber83693c82016-10-08 22:17:55 +0200130 else
131 Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
132 end if;
133
134 pragma Debug (Debug.Put_Line ("Disabled IPS."));
135 -- We have to wait until the next vblank here.
136 -- 20ms should be enough.
137 Time.M_Delay (20);
138 end if;
139 end if;
140 end IPS_Off;
141
142 ----------------------------------------------------------------------------
143
144 procedure PDW_Off
145 is
146 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
147 begin
148 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
149
150 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
151 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
152 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
153 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
154 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
155 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
156
157 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
158 PWR_WELL_CTL_ENABLE_REQUEST) /= 0
159 then
160 Registers.Wait_Set_Mask
161 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
162 end if;
163
164 if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
165 Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
166 end if;
167
168 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
169 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
170 end if;
171 end PDW_Off;
172
173 procedure PDW_On
174 is
175 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
176 begin
177 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
178
179 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
180 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
181 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
182 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
183 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
184 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
185
186 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
187 PWR_WELL_CTL_ENABLE_REQUEST) = 0
188 then
189 Registers.Wait_Unset_Mask
190 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
191 end if;
192
193 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
194 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
195 Registers.Wait_Set_Mask
196 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
197 end if;
198 end PDW_On;
199
Nico Huber3d06de82018-05-29 01:35:04 +0200200 function Need_PDW (Checked_Configs : Pipe_Configs) return Boolean
201 is
202 Primary : Pipe_Config renames Checked_Configs (GMA.Primary);
Nico Huber83693c82016-10-08 22:17:55 +0200203 begin
Nico Huber3d06de82018-05-29 01:35:04 +0200204 return
205 (Config.Use_PDW_For_EDP_Scaling and then
206 (Primary.Port = Internal and Requires_Scaling (Primary)))
207 or
208 (Primary.Port /= Disabled and Primary.Port /= Internal)
209 or
210 Checked_Configs (Secondary).Port /= Disabled
211 or
212 Checked_Configs (Tertiary).Port /= Disabled;
Nico Huber83693c82016-10-08 22:17:55 +0200213 end Need_PDW;
214
215 ----------------------------------------------------------------------------
216
217 procedure Pre_All_Off is
218 begin
219 -- HSW: disable panel self refresh (PSR) on eDP if enabled
220 -- wait for PSR idling
221 PSR_Off;
222 IPS_Off;
223 end Pre_All_Off;
224
Nico Huberd0f84b92019-09-22 21:31:52 +0200225 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
226 ( if CDClk <= 337_500_000 then 337_500_000
227 elsif CDClk <= 450_000_000 then 450_000_000
228 elsif CDClk <= 540_000_000 then 540_000_000
229 else 675_000_000);
230
231 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
232 is
233 LCPLL_CTL : Word32;
234 begin
235 Registers.Read (Registers.LCPLL_CTL, LCPLL_CTL);
236 CDClk :=
237 (if Config.Has_Broadwell_CDClk then
238 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
239 when LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ => 540_000_000,
240 when LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ => 337_500_000,
241 when LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ => 675_000_000,
242 when others => 450_000_000)
243 else
244 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
245 when LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE =>
246 (if Config.Is_ULX then 337_500_000
247 elsif Config.Is_ULT then 450_000_000
248 else 540_000_000),
249 when others => 450_000_000));
250 end Get_Cur_CDClk;
251
252 procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
253 is
254 FUSE_STRAP : Word32;
255 begin
256 if Config.Has_Broadwell_CDClk then
257 Registers.Read (Registers.FUSE_STRAP, FUSE_STRAP);
258 CDClk :=
259 (if (FUSE_STRAP and FUSE_STRAP_DISPLAY_CDCLK_LIMIT) /= 0 then
260 450_000_000
261 elsif Config.Is_ULX then
262 450_000_000
263 elsif Config.Is_ULT then
264 540_000_000
265 else
266 675_000_000);
267 else
268 -- We may never switch CDClk on Haswell. So from our point
269 -- of view, the CDClk we start with is the maximum.
270 Get_Cur_CDClk (CDClk);
271 end if;
272 end Get_Max_CDClk;
273
274 procedure Set_CDClk (CDClk_In : Frequency_Type)
275 is
276 CDClk : constant Config.CDClk_Range :=
277 Normalize_CDClk (Frequency_Type'Min (CDClk_In, Config.Max_CDClk));
278 Success : Boolean;
279 begin
280 if not Config.Can_Switch_CDClk then
281 return;
282 end if;
283
284 PCode.Mailbox_Write
285 (MBox => BDW_PCODE_DISPLAY_FREQ_CHANGE,
286 Command => 0,
287 Wait_Ready => True,
288 Success => Success);
289
290 if not Success then
291 pragma Debug (Debug.Put_Line
292 ("ERROR: PCODE didn't acknowledge frequency change."));
293 return;
294 end if;
295
296 Registers.Set_Mask
297 (Register => Registers.LCPLL_CTL,
298 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
299 Registers.Wait_Set_Mask
300 (Register => Registers.LCPLL_CTL,
301 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
302
303 Registers.Unset_And_Set_Mask
304 (Register => Registers.LCPLL_CTL,
305 Mask_Unset => LCPLL_CTL_CD_FREQ_SEL_MASK,
306 Mask_Set => LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk));
307 Registers.Posting_Read (Registers.LCPLL_CTL);
308
309 Registers.Unset_Mask
310 (Register => Registers.LCPLL_CTL,
311 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
312 Registers.Wait_Unset_Mask
313 (Register => Registers.LCPLL_CTL,
314 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
315
316 PCode.Mailbox_Write
317 (MBox => HSW_PCODE_DE_WRITE_FREQ,
318 Command => (case CDClk is
319 when 675_000_000 => 3,
320 when 540_000_000 => 1,
321 when 450_000_000 => 0,
322 when others => 2));
323
324 Registers.Write
325 (Register => Registers.CDCLK_FREQ,
326 Value => Word32 (Div_Round_Closest (CDClk, 1_000_000) - 1));
327
328 Config.CDClk := CDClk;
329 end Set_CDClk;
330
331 procedure Initialize
332 is
333 CDClk : Config.CDClk_Range;
Nico Huber83693c82016-10-08 22:17:55 +0200334 begin
335 -- HSW: disable power down well
336 PDW_Off;
Nico Huberd0f84b92019-09-22 21:31:52 +0200337
338 Get_Cur_CDClk (CDClk);
339 Config.CDClk := CDClk;
340 Get_Max_CDClk (CDClk);
341 Config.Max_CDClk := CDClk;
342 Set_CDClk (Config.Default_CDClk_Freq);
343
Arthur Heymansd1988d12018-03-28 16:27:57 +0200344 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber83693c82016-10-08 22:17:55 +0200345 end Initialize;
346
Nico Huberd0f84b92019-09-22 21:31:52 +0200347 procedure Limit_Dotclocks
348 (Configs : in out Pipe_Configs;
349 CDClk_Switch : out Boolean)
350 is
351 begin
352 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
353 CDClk_Switch :=
354 Config.Can_Switch_CDClk and then
355 Config.CDClk /= Normalize_CDClk
356 (Config_Helpers.Highest_Dotclock (Configs));
357 end Limit_Dotclocks;
358
359 procedure Update_CDClk (Configs : in out Pipe_Configs)
360 is
361 New_CDClk : constant Frequency_Type :=
362 Config_Helpers.Highest_Dotclock (Configs);
363 begin
364 Set_CDClk (New_CDClk);
365 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
366 end Update_CDClk;
367
Nico Huber99f10f32016-11-20 00:34:05 +0100368 procedure Power_Set_To (Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200369 begin
370 if Need_PDW (Configs) then
371 PDW_On;
372 else
373 PDW_Off;
374 end if;
375 end Power_Set_To;
376
Nico Huber99f10f32016-11-20 00:34:05 +0100377 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200378 begin
379 if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
380 PDW_On;
381 end if;
382 end Power_Up;
383
Nico Huber99f10f32016-11-20 00:34:05 +0100384 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200385 is
386 begin
387 if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
388 not Need_PDW (New_Configs)
389 then
390 PDW_Off;
391 end if;
392 end Power_Down;
393
394end HW.GFX.GMA.Power_And_Clocks_Haswell;