blob: b40689b017805dd3ae2eddd978caded07118386a [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2015-2016 secunet Security Networks AG
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02006-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02008--
9-- This program is distributed in the hope that it will be useful,
10-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-- GNU General Public License for more details.
13--
14
15with HW.GFX.GMA.Config;
16
17with HW.Debug;
18with GNAT.Source_Info;
19
20package body HW.GFX.GMA.Panel
21with
22 Refined_State =>
23 (Panel_State =>
24 (Delays_US, Power_Cycle_Timer, Power_Up_Timer))
25is
26 type Delays_Enum is
27 (Power_Up_Delay,
28 Power_Up_To_BL_On,
29 Power_Down_Delay,
30 BL_Off_To_Power_Down,
31 Power_Cycle_Delay);
32
33 type Panel_Power_Delays is array (Delays_Enum) of Natural;
34 Default_EDP_Delays_US : constant Panel_Power_Delays := Panel_Power_Delays'
35 (Power_Up_Delay => 210_000,
36 Power_Up_To_BL_On => 50_000,
37 Power_Down_Delay => 500_000,
38 BL_Off_To_Power_Down => 50_000,
39 Power_Cycle_Delay => 510_000);
40
41 Delays_US : Panel_Power_Delays;
42
43 ----------------------------------------------------------------------------
44
45 -- And here the mess starts: We have this pretty hardware power sequencer
46 -- that should ensure the panel's timing constraints are satisfied. But
47 -- (at least on some generations) it doesn't do it's job. On Haswell, it
48 -- seems to ignore the Power_Cycle_Delay, so we ensure the delay in soft-
49 -- ware. On at least Ivy Bridge and Broadwell Power_Up_Delay is ignored.
50 --
51 -- If we ever do all delays in software, there are two ways: Either confi-
52 -- gure the hardware to zero delays or wait for both the software timeout
53 -- and the hardware power sequencer. The latter option would be less error
54 -- prone, as the hardware might just don't work as expected.
55
56 Power_Cycle_Timer : Time.T;
57 Power_Up_Timer : Time.T;
58
59 ----------------------------------------------------------------------------
60
Nico Huber57bebc72018-06-04 14:42:13 +020061 function Div_Round_Up32 (Num : Natural; Denom : Positive) return Word32 is
62 ((Word32 (Num) + Word32 (Denom) - 1) / Word32 (Denom));
Nico Huber83693c82016-10-08 22:17:55 +020063
64 PCH_PP_STATUS_ENABLED : constant := 16#00_0001# * 2 ** 31;
65 PCH_PP_STATUS_REQUIRE_ASSET : constant := 16#00_0001# * 2 ** 30;
66 PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK : constant := 16#00_0003# * 2 ** 28;
67 PCH_PP_STATUS_PWR_SEQ_PROGRESS_NONE : constant := 16#00_0000# * 2 ** 28;
68 PCH_PP_STATUS_PWR_SEQ_PROGRESS_UP : constant := 16#00_0001# * 2 ** 28;
69 PCH_PP_STATUS_PWR_SEQ_PROGRESS_DOWN : constant := 16#00_0002# * 2 ** 28;
70 PCH_PP_STATUS_PWR_CYC_DELAY_ACTIVE : constant := 16#00_0001# * 2 ** 27;
71
72 PCH_PP_CONTROL_WRITE_PROTECT_MASK : constant := 16#00_ffff# * 2 ** 16;
73 PCH_PP_CONTROL_WRITE_PROTECT_KEY : constant := 16#00_abcd# * 2 ** 16;
74 PCH_PP_CONTROL_VDD_OVERRIDE : constant := 16#00_0001# * 2 ** 3;
75 PCH_PP_CONTROL_BACKLIGHT_ENABLE : constant := 16#00_0001# * 2 ** 2;
76 PCH_PP_CONTROL_POWER_DOWN_ON_RESET : constant := 16#00_0001# * 2 ** 1;
77 PCH_PP_CONTROL_TARGET_ON : constant := 16#00_0001# * 2 ** 0;
78
79 PCH_PP_ON_DELAYS_PORT_SELECT_MASK : constant := 16#00_0003# * 2 ** 30;
80 PCH_PP_ON_DELAYS_PORT_SELECT_LVDS : constant := 16#00_0000# * 2 ** 30;
81 PCH_PP_ON_DELAYS_PORT_SELECT_DP_A : constant := 16#00_0001# * 2 ** 30;
82 PCH_PP_ON_DELAYS_PORT_SELECT_DP_C : constant := 16#00_0002# * 2 ** 30;
83 PCH_PP_ON_DELAYS_PORT_SELECT_DP_D : constant := 16#00_0003# * 2 ** 30;
84 PCH_PP_ON_DELAYS_PWR_UP_MASK : constant := 16#00_1fff# * 2 ** 16;
85 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK : constant := 16#00_1fff# * 2 ** 0;
Arthur Heymanse87d0d12018-03-28 17:02:49 +020086
87 type PP_Regs is record
88 STATUS : Registers.Registers_Index;
89 CONTROL : Registers.Registers_Index;
90 ON_DELAYS : Registers.Registers_Index;
91 OFF_DELAYS : Registers.Registers_Index;
92 DIVISOR : Registers.Registers_Index;
93 end record;
94
95 Panel_PP_Regs : constant PP_Regs := (if Config.Has_PCH_Panel_Power then
96 (STATUS => Registers.PCH_PP_STATUS,
97 CONTROL => Registers.PCH_PP_CONTROL,
98 ON_DELAYS => Registers.PCH_PP_ON_DELAYS,
99 OFF_DELAYS => Registers.PCH_PP_OFF_DELAYS,
100 DIVISOR => Registers.PCH_PP_DIVISOR)
101 else
102 (STATUS => Registers.GMCH_PP_STATUS,
103 CONTROL => Registers.GMCH_PP_CONTROL,
104 ON_DELAYS => Registers.GMCH_PP_ON_DELAYS,
105 OFF_DELAYS => Registers.GMCH_PP_OFF_DELAYS,
106 DIVISOR => Registers.GMCH_PP_DIVISOR));
107
Nico Huber83693c82016-10-08 22:17:55 +0200108 function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
109 begin
110 return Shift_Left (Div_Round_Up32 (US, 100), 16);
111 end PCH_PP_ON_DELAYS_PWR_UP;
112 function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
113 begin
114 return Div_Round_Up32 (US, 100);
115 end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
116
117 PCH_PP_OFF_DELAYS_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 16;
118 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 0;
119 function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
120 begin
121 return Shift_Left (Div_Round_Up32 (US, 100), 16);
122 end PCH_PP_OFF_DELAYS_PWR_DOWN;
123 function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
124 begin
125 return Div_Round_Up32 (US, 100);
126 end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
127
128 PCH_PP_DIVISOR_REF_DIVIDER_MASK : constant := 16#ff_ffff# * 2 ** 8;
129 PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK : constant := 16#00_001f# * 2 ** 0;
130 function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
131 begin
132 return Div_Round_Up32 (US, 100_000) + 1;
133 end PCH_PP_DIVISOR_PWR_CYC_DELAY;
134
135 CPU_BLC_PWM_CTL_ENABLE : constant := 16#00_0001# * 2 ** 31;
136 CPU_BLC_PWM_CTL_PIPE_SELECT_MASK : constant := 16#00_0003# * 2 ** 29;
137 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A : constant := 16#00_0000# * 2 ** 29;
138 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B : constant := 16#00_0001# * 2 ** 29;
139 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C : constant := 16#00_0002# * 2 ** 29;
140
141 CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
142
143 PCH_BLC_PWM_CTL1_ENABLE : constant := 16#00_0001# * 2 ** 31;
144 PCH_BLC_PWM_CTL1_BL_POLARITY_MASK : constant := 16#00_0001# * 2 ** 29;
145 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
146 PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE : constant := 16#00_0001# * 2 ** 25;
147 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN : constant := 16#00_0001# * 2 ** 24;
148 PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
149 PCH_BLC_PWM_CTL1_PHASE_IN_COUNT : constant := 16#00_00ff# * 2 ** 8;
150 PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 ** 0;
151
152 PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK : constant := 16#00_ffff# * 2 ** 16;
153 PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
154
155 ----------------------------------------------------------------------------
156
157 procedure Static_Init
158 with
159 Refined_Global =>
160 (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
161 Input => (Time.State))
162 is
163 begin
164 Power_Cycle_Timer := Time.Now;
165 Power_Up_Timer := Power_Cycle_Timer;
166
167 Delays_US := Default_EDP_Delays_US;
168 end Static_Init;
169
170 ----------------------------------------------------------------------------
171
172 procedure Check_PP_Delays
173 (Delays : in out Panel_Power_Delays;
174 Override : in out Boolean) is
175 begin
176 for D in Delays_Enum loop
177 if Delays (D) = 0 then
178 Delays (D) := Default_EDP_Delays_US (D);
179 Override := True;
180 end if;
181 end loop;
182 end Check_PP_Delays;
183
184 procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
185 is
186 Power_Delay, Port_Select : Word32;
187
188 Override_Delays : Boolean := False;
189 begin
190 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
191
192 Static_Init;
193
194 if Default_Delays then
195 Override_Delays := True;
196 else
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200197 Registers.Read (Panel_PP_Regs.ON_DELAYS, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200198 Delays_US (Power_Up_Delay) := 100 * Natural
199 (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
200 Delays_US (Power_Up_To_BL_On) := 100 * Natural
201 (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
202
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200203 Registers.Read (Panel_PP_Regs.OFF_DELAYS, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200204 Delays_US (Power_Down_Delay) := 100 * Natural
205 (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
206 Delays_US (BL_Off_To_Power_Down) := 100 * Natural
207 (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
208
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200209 Registers.Read (Panel_PP_Regs.DIVISOR, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200210 if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
211 Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
212 (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
213 end if;
214
215 Check_PP_Delays (Delays_US, Override_Delays);
216 end if;
217
218 if Override_Delays then
219 if Config.Has_PP_Port_Select then
Nico Huber8a6e7bd2020-01-07 16:36:38 +0100220 Port_Select :=
221 (case Config.Panel_1_Port is
222 when LVDS => PCH_PP_ON_DELAYS_PORT_SELECT_LVDS,
223 when eDP => PCH_PP_ON_DELAYS_PORT_SELECT_DP_A,
224 when DP2 | HDMI2 => PCH_PP_ON_DELAYS_PORT_SELECT_DP_C,
225 when DP3 | HDMI3 => PCH_PP_ON_DELAYS_PORT_SELECT_DP_D,
226 when others => 0);
Nico Huber83693c82016-10-08 22:17:55 +0200227 else
228 Port_Select := 0;
229 end if;
230
231 -- Force power-up to backlight-on delay to 100us as recommended by PRM.
232 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200233 (Register => Panel_PP_Regs.ON_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200234 Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
235 PCH_PP_ON_DELAYS_PWR_UP_MASK or
236 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
237 Mask_Set => Port_Select or
238 PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
239 or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
240
241 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200242 (Register => Panel_PP_Regs.OFF_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200243 Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
244 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
245 Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
246 (Delays_US (Power_Down_Delay)) or
247 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
248 (Delays_US (BL_Off_To_Power_Down)));
249
250 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200251 (Register => Panel_PP_Regs.DIVISOR,
Nico Huber83693c82016-10-08 22:17:55 +0200252 Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
253 Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
254 (Delays_US (Power_Cycle_Delay)));
255 end if;
256
257 if Config.Has_PP_Write_Protection then
258 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200259 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200260 Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
261 Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
262 PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
263 else
264 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200265 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200266 Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
267 end if;
268 end Setup_PP_Sequencer;
269
270 ----------------------------------------------------------------------------
271
272 procedure VDD_Override is
273 begin
274 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
275
276 -- Yeah, We could do, what we are supposed to do here. But OTOH, we
277 -- are should wait for the full Power Up Delay, which we would have
278 -- to do later again. And just powering on the display seems to work
279 -- too. Also this function vanished on newer hardware.
280 On;
281 end VDD_Override;
282
283 procedure On (Wait : Boolean := True)
284 is
285 Was_On : Boolean;
286 begin
287 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
288
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200289 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200290 if not Was_On then
291 Time.Delay_Until (Power_Cycle_Timer);
292 end if;
293
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200294 Registers.Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON);
Nico Huber83693c82016-10-08 22:17:55 +0200295 if not Was_On then
296 Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
297 end if;
298 if Wait then
299 Wait_On;
300 end if;
301 end On;
302
303 procedure Wait_On is
304 begin
305 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
306
307 Time.Delay_Until (Power_Up_Timer);
308 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200309 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200310 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
311 TOut_MS => 300);
312
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200313 Registers.Unset_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
Nico Huber83693c82016-10-08 22:17:55 +0200314 end Wait_On;
315
316 procedure Off
317 is
318 Was_On : Boolean;
319 begin
320 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
321
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200322 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200323 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200324 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200325 Mask => PCH_PP_CONTROL_TARGET_ON or
326 PCH_PP_CONTROL_VDD_OVERRIDE);
327 if Was_On then
328 Time.U_Delay (Delays_US (Power_Down_Delay));
329 end if;
330 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200331 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200332 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
333 TOut_MS => 600);
334 if Was_On then
335 Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
336 end if;
337 end Off;
338
339 ----------------------------------------------------------------------------
340
341 procedure Backlight_On is
342 begin
343 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
344
345 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200346 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200347 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
348 end Backlight_On;
349
350 procedure Backlight_Off is
351 begin
352 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
353
354 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200355 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200356 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
357 end Backlight_Off;
358
359 procedure Set_Backlight (Level : Word16) is
360 begin
361 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
362
363 Registers.Unset_And_Set_Mask
364 (Register => Registers.BLC_PWM_CPU_CTL,
365 Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
366 Mask_Set => Word32 (Level));
367 end Set_Backlight;
368
369 procedure Get_Max_Backlight (Level : out Word16)
370 is
371 Reg : Word32;
372 begin
373 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
374
375 Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
376 Level := Word16
377 (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
378 end Get_Max_Backlight;
379
380end HW.GFX.GMA.Panel;