blob: ef18158c370c04013b6e66f7ca8f4fabbf58d4be [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
61 function Div_Round_Up32 (Numerator, Denominator : Natural) return Word32 is
62 begin
63 return (Word32 (Numerator) + Word32 (Denominator) - 1)
64 / Word32 (Denominator);
65 end Div_Round_Up32;
66
67 PCH_PP_STATUS_ENABLED : constant := 16#00_0001# * 2 ** 31;
68 PCH_PP_STATUS_REQUIRE_ASSET : constant := 16#00_0001# * 2 ** 30;
69 PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK : constant := 16#00_0003# * 2 ** 28;
70 PCH_PP_STATUS_PWR_SEQ_PROGRESS_NONE : constant := 16#00_0000# * 2 ** 28;
71 PCH_PP_STATUS_PWR_SEQ_PROGRESS_UP : constant := 16#00_0001# * 2 ** 28;
72 PCH_PP_STATUS_PWR_SEQ_PROGRESS_DOWN : constant := 16#00_0002# * 2 ** 28;
73 PCH_PP_STATUS_PWR_CYC_DELAY_ACTIVE : constant := 16#00_0001# * 2 ** 27;
74
75 PCH_PP_CONTROL_WRITE_PROTECT_MASK : constant := 16#00_ffff# * 2 ** 16;
76 PCH_PP_CONTROL_WRITE_PROTECT_KEY : constant := 16#00_abcd# * 2 ** 16;
77 PCH_PP_CONTROL_VDD_OVERRIDE : constant := 16#00_0001# * 2 ** 3;
78 PCH_PP_CONTROL_BACKLIGHT_ENABLE : constant := 16#00_0001# * 2 ** 2;
79 PCH_PP_CONTROL_POWER_DOWN_ON_RESET : constant := 16#00_0001# * 2 ** 1;
80 PCH_PP_CONTROL_TARGET_ON : constant := 16#00_0001# * 2 ** 0;
81
82 PCH_PP_ON_DELAYS_PORT_SELECT_MASK : constant := 16#00_0003# * 2 ** 30;
83 PCH_PP_ON_DELAYS_PORT_SELECT_LVDS : constant := 16#00_0000# * 2 ** 30;
84 PCH_PP_ON_DELAYS_PORT_SELECT_DP_A : constant := 16#00_0001# * 2 ** 30;
85 PCH_PP_ON_DELAYS_PORT_SELECT_DP_C : constant := 16#00_0002# * 2 ** 30;
86 PCH_PP_ON_DELAYS_PORT_SELECT_DP_D : constant := 16#00_0003# * 2 ** 30;
87 PCH_PP_ON_DELAYS_PWR_UP_MASK : constant := 16#00_1fff# * 2 ** 16;
88 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK : constant := 16#00_1fff# * 2 ** 0;
Arthur Heymanse87d0d12018-03-28 17:02:49 +020089
90 type PP_Regs is record
91 STATUS : Registers.Registers_Index;
92 CONTROL : Registers.Registers_Index;
93 ON_DELAYS : Registers.Registers_Index;
94 OFF_DELAYS : Registers.Registers_Index;
95 DIVISOR : Registers.Registers_Index;
96 end record;
97
98 Panel_PP_Regs : constant PP_Regs := (if Config.Has_PCH_Panel_Power then
99 (STATUS => Registers.PCH_PP_STATUS,
100 CONTROL => Registers.PCH_PP_CONTROL,
101 ON_DELAYS => Registers.PCH_PP_ON_DELAYS,
102 OFF_DELAYS => Registers.PCH_PP_OFF_DELAYS,
103 DIVISOR => Registers.PCH_PP_DIVISOR)
104 else
105 (STATUS => Registers.GMCH_PP_STATUS,
106 CONTROL => Registers.GMCH_PP_CONTROL,
107 ON_DELAYS => Registers.GMCH_PP_ON_DELAYS,
108 OFF_DELAYS => Registers.GMCH_PP_OFF_DELAYS,
109 DIVISOR => Registers.GMCH_PP_DIVISOR));
110
Nico Huber83693c82016-10-08 22:17:55 +0200111 function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
112 begin
113 return Shift_Left (Div_Round_Up32 (US, 100), 16);
114 end PCH_PP_ON_DELAYS_PWR_UP;
115 function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
116 begin
117 return Div_Round_Up32 (US, 100);
118 end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
119
120 PCH_PP_OFF_DELAYS_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 16;
121 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 0;
122 function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
123 begin
124 return Shift_Left (Div_Round_Up32 (US, 100), 16);
125 end PCH_PP_OFF_DELAYS_PWR_DOWN;
126 function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
127 begin
128 return Div_Round_Up32 (US, 100);
129 end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
130
131 PCH_PP_DIVISOR_REF_DIVIDER_MASK : constant := 16#ff_ffff# * 2 ** 8;
132 PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK : constant := 16#00_001f# * 2 ** 0;
133 function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
134 begin
135 return Div_Round_Up32 (US, 100_000) + 1;
136 end PCH_PP_DIVISOR_PWR_CYC_DELAY;
137
138 CPU_BLC_PWM_CTL_ENABLE : constant := 16#00_0001# * 2 ** 31;
139 CPU_BLC_PWM_CTL_PIPE_SELECT_MASK : constant := 16#00_0003# * 2 ** 29;
140 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A : constant := 16#00_0000# * 2 ** 29;
141 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B : constant := 16#00_0001# * 2 ** 29;
142 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C : constant := 16#00_0002# * 2 ** 29;
143
144 CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
145
146 PCH_BLC_PWM_CTL1_ENABLE : constant := 16#00_0001# * 2 ** 31;
147 PCH_BLC_PWM_CTL1_BL_POLARITY_MASK : constant := 16#00_0001# * 2 ** 29;
148 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
149 PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE : constant := 16#00_0001# * 2 ** 25;
150 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN : constant := 16#00_0001# * 2 ** 24;
151 PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
152 PCH_BLC_PWM_CTL1_PHASE_IN_COUNT : constant := 16#00_00ff# * 2 ** 8;
153 PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 ** 0;
154
155 PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK : constant := 16#00_ffff# * 2 ** 16;
156 PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
157
158 ----------------------------------------------------------------------------
159
160 procedure Static_Init
161 with
162 Refined_Global =>
163 (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
164 Input => (Time.State))
165 is
166 begin
167 Power_Cycle_Timer := Time.Now;
168 Power_Up_Timer := Power_Cycle_Timer;
169
170 Delays_US := Default_EDP_Delays_US;
171 end Static_Init;
172
173 ----------------------------------------------------------------------------
174
175 procedure Check_PP_Delays
176 (Delays : in out Panel_Power_Delays;
177 Override : in out Boolean) is
178 begin
179 for D in Delays_Enum loop
180 if Delays (D) = 0 then
181 Delays (D) := Default_EDP_Delays_US (D);
182 Override := True;
183 end if;
184 end loop;
185 end Check_PP_Delays;
186
187 procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
188 is
189 Power_Delay, Port_Select : Word32;
190
191 Override_Delays : Boolean := False;
192 begin
193 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
194
195 Static_Init;
196
197 if Default_Delays then
198 Override_Delays := True;
199 else
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200200 Registers.Read (Panel_PP_Regs.ON_DELAYS, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200201 Delays_US (Power_Up_Delay) := 100 * Natural
202 (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
203 Delays_US (Power_Up_To_BL_On) := 100 * Natural
204 (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
205
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200206 Registers.Read (Panel_PP_Regs.OFF_DELAYS, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200207 Delays_US (Power_Down_Delay) := 100 * Natural
208 (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
209 Delays_US (BL_Off_To_Power_Down) := 100 * Natural
210 (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
211
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200212 Registers.Read (Panel_PP_Regs.DIVISOR, Power_Delay);
Nico Huber83693c82016-10-08 22:17:55 +0200213 if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
214 Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
215 (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
216 end if;
217
218 Check_PP_Delays (Delays_US, Override_Delays);
219 end if;
220
221 if Override_Delays then
222 if Config.Has_PP_Port_Select then
223 if Config.Internal_Is_EDP then
224 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
225 else
226 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
227 end if;
228 else
229 Port_Select := 0;
230 end if;
231
232 -- Force power-up to backlight-on delay to 100us as recommended by PRM.
233 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200234 (Register => Panel_PP_Regs.ON_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200235 Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
236 PCH_PP_ON_DELAYS_PWR_UP_MASK or
237 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
238 Mask_Set => Port_Select or
239 PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
240 or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
241
242 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200243 (Register => Panel_PP_Regs.OFF_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200244 Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
245 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
246 Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
247 (Delays_US (Power_Down_Delay)) or
248 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
249 (Delays_US (BL_Off_To_Power_Down)));
250
251 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200252 (Register => Panel_PP_Regs.DIVISOR,
Nico Huber83693c82016-10-08 22:17:55 +0200253 Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
254 Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
255 (Delays_US (Power_Cycle_Delay)));
256 end if;
257
258 if Config.Has_PP_Write_Protection then
259 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200260 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200261 Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
262 Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
263 PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
264 else
265 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200266 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200267 Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
268 end if;
269 end Setup_PP_Sequencer;
270
271 ----------------------------------------------------------------------------
272
273 procedure VDD_Override is
274 begin
275 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
276
277 -- Yeah, We could do, what we are supposed to do here. But OTOH, we
278 -- are should wait for the full Power Up Delay, which we would have
279 -- to do later again. And just powering on the display seems to work
280 -- too. Also this function vanished on newer hardware.
281 On;
282 end VDD_Override;
283
284 procedure On (Wait : Boolean := True)
285 is
286 Was_On : Boolean;
287 begin
288 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
289
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200290 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200291 if not Was_On then
292 Time.Delay_Until (Power_Cycle_Timer);
293 end if;
294
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200295 Registers.Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON);
Nico Huber83693c82016-10-08 22:17:55 +0200296 if not Was_On then
297 Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
298 end if;
299 if Wait then
300 Wait_On;
301 end if;
302 end On;
303
304 procedure Wait_On is
305 begin
306 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
307
308 Time.Delay_Until (Power_Up_Timer);
309 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200310 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200311 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
312 TOut_MS => 300);
313
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200314 Registers.Unset_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
Nico Huber83693c82016-10-08 22:17:55 +0200315 end Wait_On;
316
317 procedure Off
318 is
319 Was_On : Boolean;
320 begin
321 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
322
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200323 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200324 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200325 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200326 Mask => PCH_PP_CONTROL_TARGET_ON or
327 PCH_PP_CONTROL_VDD_OVERRIDE);
328 if Was_On then
329 Time.U_Delay (Delays_US (Power_Down_Delay));
330 end if;
331 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200332 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200333 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
334 TOut_MS => 600);
335 if Was_On then
336 Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
337 end if;
338 end Off;
339
340 ----------------------------------------------------------------------------
341
342 procedure Backlight_On is
343 begin
344 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
345
346 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200347 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200348 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
349 end Backlight_On;
350
351 procedure Backlight_Off is
352 begin
353 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
354
355 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200356 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200357 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
358 end Backlight_Off;
359
360 procedure Set_Backlight (Level : Word16) is
361 begin
362 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
363
364 Registers.Unset_And_Set_Mask
365 (Register => Registers.BLC_PWM_CPU_CTL,
366 Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
367 Mask_Set => Word32 (Level));
368 end Set_Backlight;
369
370 procedure Get_Max_Backlight (Level : out Word16)
371 is
372 Reg : Word32;
373 begin
374 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
375
376 Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
377 Level := Word16
378 (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
379 end Get_Max_Backlight;
380
381end HW.GFX.GMA.Panel;