blob: 6a00a5f34584289cc8c84f469f6b536b5e905453 [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;
Angel Pons450c24c2020-05-13 00:49:52 +020024with HW.GFX.GMA.PCH.Lynxpoint;
Nico Huber83693c82016-10-08 22:17:55 +020025
Angel Pons3318bf22020-07-19 18:38:32 +020026package body HW.GFX.GMA.Power_And_Clocks is
Nico Huber83693c82016-10-08 22:17:55 +020027
Nico Huberd0f84b92019-09-22 21:31:52 +020028 LCPLL_CTL_CD_FREQ_SEL_MASK : constant := 3 * 2 ** 26;
29 LCPLL_CTL_CD_FREQ_SEL_450_MHZ : constant := 0 * 2 ** 26;
30 LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE : constant := 1 * 2 ** 26;
31 LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ : constant := 1 * 2 ** 26;
32 LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ : constant := 2 * 2 ** 26;
33 LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ : constant := 3 * 2 ** 26;
34 LCPLL_CTL_CD_SOURCE_SELECT_FCLK : constant := 1 * 2 ** 21;
35 LCPLL_CTL_CD_SOURCE_FCLK_DONE : constant := 1 * 2 ** 19;
36
37 function LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk : Config.CDClk_Range) return Word32
38 is
39 (case CDClk is
40 when 675_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ,
41 when 540_000_000 => LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ,
42 when 450_000_000 => LCPLL_CTL_CD_FREQ_SEL_450_MHZ,
43 when others => LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ);
44
45 FUSE_STRAP_DISPLAY_CDCLK_LIMIT : constant := 1 * 2 ** 24;
46
47 HSW_PCODE_DE_WRITE_FREQ : constant := 16#17#;
48 BDW_PCODE_DISPLAY_FREQ_CHANGE : constant := 16#18#;
49
50 ----------------------------------------------------------------------------
51
Nico Huber83693c82016-10-08 22:17:55 +020052 PWR_WELL_CTL_ENABLE_REQUEST : constant := 1 * 2 ** 31;
53 PWR_WELL_CTL_DISABLE_REQUEST : constant := 0 * 2 ** 31;
54 PWR_WELL_CTL_STATE_ENABLED : constant := 1 * 2 ** 30;
55
56 ----------------------------------------------------------------------------
57
Nico Huber83693c82016-10-08 22:17:55 +020058 IPS_CTL_ENABLE : constant := 1 * 2 ** 31;
59 DISPLAY_IPS_CONTROL : constant := 16#19#;
60
Nico Huber83693c82016-10-08 22:17:55 +020061 ----------------------------------------------------------------------------
62
Nico Huber83693c82016-10-08 22:17:55 +020063 procedure IPS_Off
64 is
65 Enabled : Boolean;
66 begin
67 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
68
69 if Config.Has_IPS then
70 Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
71 if Enabled then
72 if Config.Has_IPS_CTL_Mailbox then
Nico Huber312433c2019-09-28 03:15:48 +020073 PCode.Mailbox_Write (DISPLAY_IPS_CONTROL, 0, Wait_Ready => True);
Nico Huber43cf8d52016-09-08 17:24:52 +020074 Registers.Wait_Unset_Mask
75 (Register => Registers.IPS_CTL,
76 Mask => IPS_CTL_ENABLE,
77 TOut_MS => 42);
Nico Huber83693c82016-10-08 22:17:55 +020078 else
79 Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
80 end if;
81
82 pragma Debug (Debug.Put_Line ("Disabled IPS."));
83 -- We have to wait until the next vblank here.
84 -- 20ms should be enough.
85 Time.M_Delay (20);
86 end if;
87 end if;
88 end IPS_Off;
89
90 ----------------------------------------------------------------------------
91
92 procedure PDW_Off
93 is
94 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
95 begin
96 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
97
98 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
99 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
100 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
101 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
102 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
103 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
104
105 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
106 PWR_WELL_CTL_ENABLE_REQUEST) /= 0
107 then
108 Registers.Wait_Set_Mask
109 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
110 end if;
111
112 if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
113 Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
114 end if;
115
116 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
117 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
118 end if;
119 end PDW_Off;
120
121 procedure PDW_On
122 is
123 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
124 begin
125 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
126
127 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
128 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
129 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
130 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
131 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
132 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
133
134 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
135 PWR_WELL_CTL_ENABLE_REQUEST) = 0
136 then
137 Registers.Wait_Unset_Mask
138 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
139 end if;
140
141 if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
142 Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
143 Registers.Wait_Set_Mask
144 (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
145 end if;
146 end PDW_On;
147
Nico Huber3d06de82018-05-29 01:35:04 +0200148 function Need_PDW (Checked_Configs : Pipe_Configs) return Boolean
149 is
150 Primary : Pipe_Config renames Checked_Configs (GMA.Primary);
Nico Huber83693c82016-10-08 22:17:55 +0200151 begin
Nico Huber3d06de82018-05-29 01:35:04 +0200152 return
153 (Config.Use_PDW_For_EDP_Scaling and then
Nico Huber8beafd72020-01-07 14:59:44 +0100154 (Primary.Port = eDP and Requires_Scaling (Primary)))
Nico Huber3d06de82018-05-29 01:35:04 +0200155 or
Nico Huber8beafd72020-01-07 14:59:44 +0100156 (Primary.Port /= Disabled and Primary.Port /= eDP)
Nico Huber3d06de82018-05-29 01:35:04 +0200157 or
158 Checked_Configs (Secondary).Port /= Disabled
159 or
160 Checked_Configs (Tertiary).Port /= Disabled;
Nico Huber83693c82016-10-08 22:17:55 +0200161 end Need_PDW;
162
163 ----------------------------------------------------------------------------
164
165 procedure Pre_All_Off is
166 begin
167 -- HSW: disable panel self refresh (PSR) on eDP if enabled
168 -- wait for PSR idling
Angel Pons3f86b0b2020-07-18 00:22:32 +0200169 Transcoder.PSR_Off;
Nico Huber83693c82016-10-08 22:17:55 +0200170 IPS_Off;
171 end Pre_All_Off;
172
Nico Huberd0f84b92019-09-22 21:31:52 +0200173 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
174 ( if CDClk <= 337_500_000 then 337_500_000
175 elsif CDClk <= 450_000_000 then 450_000_000
176 elsif CDClk <= 540_000_000 then 540_000_000
177 else 675_000_000);
178
179 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
180 is
181 LCPLL_CTL : Word32;
182 begin
183 Registers.Read (Registers.LCPLL_CTL, LCPLL_CTL);
184 CDClk :=
185 (if Config.Has_Broadwell_CDClk then
186 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
187 when LCPLL_CTL_CD_FREQ_SEL_BDW_540_MHZ => 540_000_000,
188 when LCPLL_CTL_CD_FREQ_SEL_BDW_337_5_MHZ => 337_500_000,
189 when LCPLL_CTL_CD_FREQ_SEL_BDW_675_MHZ => 675_000_000,
190 when others => 450_000_000)
191 else
192 (case LCPLL_CTL and LCPLL_CTL_CD_FREQ_SEL_MASK is
193 when LCPLL_CTL_CD_FREQ_SEL_HSW_ALTERNATE =>
194 (if Config.Is_ULX then 337_500_000
195 elsif Config.Is_ULT then 450_000_000
196 else 540_000_000),
197 when others => 450_000_000));
198 end Get_Cur_CDClk;
199
200 procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
201 is
202 FUSE_STRAP : Word32;
203 begin
204 if Config.Has_Broadwell_CDClk then
205 Registers.Read (Registers.FUSE_STRAP, FUSE_STRAP);
206 CDClk :=
207 (if (FUSE_STRAP and FUSE_STRAP_DISPLAY_CDCLK_LIMIT) /= 0 then
208 450_000_000
209 elsif Config.Is_ULX then
210 450_000_000
211 elsif Config.Is_ULT then
212 540_000_000
213 else
214 675_000_000);
215 else
216 -- We may never switch CDClk on Haswell. So from our point
217 -- of view, the CDClk we start with is the maximum.
218 Get_Cur_CDClk (CDClk);
219 end if;
220 end Get_Max_CDClk;
221
222 procedure Set_CDClk (CDClk_In : Frequency_Type)
223 is
224 CDClk : constant Config.CDClk_Range :=
225 Normalize_CDClk (Frequency_Type'Min (CDClk_In, Config.Max_CDClk));
226 Success : Boolean;
227 begin
228 if not Config.Can_Switch_CDClk then
229 return;
230 end if;
231
232 PCode.Mailbox_Write
233 (MBox => BDW_PCODE_DISPLAY_FREQ_CHANGE,
234 Command => 0,
235 Wait_Ready => True,
236 Success => Success);
237
238 if not Success then
239 pragma Debug (Debug.Put_Line
240 ("ERROR: PCODE didn't acknowledge frequency change."));
241 return;
242 end if;
243
244 Registers.Set_Mask
245 (Register => Registers.LCPLL_CTL,
246 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
247 Registers.Wait_Set_Mask
248 (Register => Registers.LCPLL_CTL,
249 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
250
251 Registers.Unset_And_Set_Mask
252 (Register => Registers.LCPLL_CTL,
253 Mask_Unset => LCPLL_CTL_CD_FREQ_SEL_MASK,
254 Mask_Set => LCPLL_CTL_CD_FREQ_SEL_BDW (CDClk));
255 Registers.Posting_Read (Registers.LCPLL_CTL);
256
257 Registers.Unset_Mask
258 (Register => Registers.LCPLL_CTL,
259 Mask => LCPLL_CTL_CD_SOURCE_SELECT_FCLK);
260 Registers.Wait_Unset_Mask
261 (Register => Registers.LCPLL_CTL,
262 Mask => LCPLL_CTL_CD_SOURCE_FCLK_DONE);
263
264 PCode.Mailbox_Write
265 (MBox => HSW_PCODE_DE_WRITE_FREQ,
266 Command => (case CDClk is
267 when 675_000_000 => 3,
268 when 540_000_000 => 1,
269 when 450_000_000 => 0,
270 when others => 2));
271
272 Registers.Write
273 (Register => Registers.CDCLK_FREQ,
274 Value => Word32 (Div_Round_Closest (CDClk, 1_000_000) - 1));
275
276 Config.CDClk := CDClk;
277 end Set_CDClk;
278
Angel Pons450c24c2020-05-13 00:49:52 +0200279 procedure Post_All_Off is
280 begin
281 -- Reset CLKOUT_DP to disabled state
282 PCH.Lynxpoint.Disable_Clkout_DP;
283 PCH.Lynxpoint.Unbend_Clkout_DP;
284 end Post_All_Off;
285
Nico Huberd0f84b92019-09-22 21:31:52 +0200286 procedure Initialize
287 is
288 CDClk : Config.CDClk_Range;
Nico Huber83693c82016-10-08 22:17:55 +0200289 begin
290 -- HSW: disable power down well
291 PDW_Off;
Nico Huberd0f84b92019-09-22 21:31:52 +0200292
293 Get_Cur_CDClk (CDClk);
294 Config.CDClk := CDClk;
295 Get_Max_CDClk (CDClk);
296 Config.Max_CDClk := CDClk;
297 Set_CDClk (Config.Default_CDClk_Freq);
298
Arthur Heymansd1988d12018-03-28 16:27:57 +0200299 Config.Raw_Clock := Config.Default_RawClk_Freq;
Angel Pons450c24c2020-05-13 00:49:52 +0200300
301 -- Configure CLKOUT_DP for FDI
302 if Config.Has_DDI_E then
303 PCH.Lynxpoint.Enable_Clkout_DP_And_FDI_mPHY;
304 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200305 end Initialize;
306
Nico Huberd0f84b92019-09-22 21:31:52 +0200307 procedure Limit_Dotclocks
308 (Configs : in out Pipe_Configs;
309 CDClk_Switch : out Boolean)
310 is
311 begin
312 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
313 CDClk_Switch :=
314 Config.Can_Switch_CDClk and then
315 Config.CDClk /= Normalize_CDClk
316 (Config_Helpers.Highest_Dotclock (Configs));
317 end Limit_Dotclocks;
318
319 procedure Update_CDClk (Configs : in out Pipe_Configs)
320 is
321 New_CDClk : constant Frequency_Type :=
322 Config_Helpers.Highest_Dotclock (Configs);
323 begin
324 Set_CDClk (New_CDClk);
325 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
326 end Update_CDClk;
327
Nico Huber99f10f32016-11-20 00:34:05 +0100328 procedure Power_Set_To (Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200329 begin
330 if Need_PDW (Configs) then
331 PDW_On;
332 else
333 PDW_Off;
334 end if;
335 end Power_Set_To;
336
Nico Huber99f10f32016-11-20 00:34:05 +0100337 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200338 begin
339 if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
340 PDW_On;
341 end if;
342 end Power_Up;
343
Nico Huber99f10f32016-11-20 00:34:05 +0100344 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200345 is
346 begin
347 if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
348 not Need_PDW (New_Configs)
349 then
350 PDW_Off;
351 end if;
352 end Power_Down;
353
Angel Pons3318bf22020-07-19 18:38:32 +0200354end HW.GFX.GMA.Power_And_Clocks;