blob: 1b6a31df76d56afa11fa0406be7eb4ec47bd836d [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;
89 function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
90 begin
91 return Shift_Left (Div_Round_Up32 (US, 100), 16);
92 end PCH_PP_ON_DELAYS_PWR_UP;
93 function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
94 begin
95 return Div_Round_Up32 (US, 100);
96 end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
97
98 PCH_PP_OFF_DELAYS_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 16;
99 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 0;
100 function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
101 begin
102 return Shift_Left (Div_Round_Up32 (US, 100), 16);
103 end PCH_PP_OFF_DELAYS_PWR_DOWN;
104 function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
105 begin
106 return Div_Round_Up32 (US, 100);
107 end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
108
109 PCH_PP_DIVISOR_REF_DIVIDER_MASK : constant := 16#ff_ffff# * 2 ** 8;
110 PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK : constant := 16#00_001f# * 2 ** 0;
111 function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
112 begin
113 return Div_Round_Up32 (US, 100_000) + 1;
114 end PCH_PP_DIVISOR_PWR_CYC_DELAY;
115
116 CPU_BLC_PWM_CTL_ENABLE : constant := 16#00_0001# * 2 ** 31;
117 CPU_BLC_PWM_CTL_PIPE_SELECT_MASK : constant := 16#00_0003# * 2 ** 29;
118 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A : constant := 16#00_0000# * 2 ** 29;
119 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B : constant := 16#00_0001# * 2 ** 29;
120 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C : constant := 16#00_0002# * 2 ** 29;
121
122 CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
123
124 PCH_BLC_PWM_CTL1_ENABLE : constant := 16#00_0001# * 2 ** 31;
125 PCH_BLC_PWM_CTL1_BL_POLARITY_MASK : constant := 16#00_0001# * 2 ** 29;
126 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
127 PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE : constant := 16#00_0001# * 2 ** 25;
128 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN : constant := 16#00_0001# * 2 ** 24;
129 PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
130 PCH_BLC_PWM_CTL1_PHASE_IN_COUNT : constant := 16#00_00ff# * 2 ** 8;
131 PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 ** 0;
132
133 PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK : constant := 16#00_ffff# * 2 ** 16;
134 PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
135
136 ----------------------------------------------------------------------------
137
138 procedure Static_Init
139 with
140 Refined_Global =>
141 (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
142 Input => (Time.State))
143 is
144 begin
145 Power_Cycle_Timer := Time.Now;
146 Power_Up_Timer := Power_Cycle_Timer;
147
148 Delays_US := Default_EDP_Delays_US;
149 end Static_Init;
150
151 ----------------------------------------------------------------------------
152
153 procedure Check_PP_Delays
154 (Delays : in out Panel_Power_Delays;
155 Override : in out Boolean) is
156 begin
157 for D in Delays_Enum loop
158 if Delays (D) = 0 then
159 Delays (D) := Default_EDP_Delays_US (D);
160 Override := True;
161 end if;
162 end loop;
163 end Check_PP_Delays;
164
165 procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
166 is
167 Power_Delay, Port_Select : Word32;
168
169 Override_Delays : Boolean := False;
170 begin
171 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
172
173 Static_Init;
174
175 if Default_Delays then
176 Override_Delays := True;
177 else
178 Registers.Read (Registers.PCH_PP_ON_DELAYS, Power_Delay);
179 Delays_US (Power_Up_Delay) := 100 * Natural
180 (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
181 Delays_US (Power_Up_To_BL_On) := 100 * Natural
182 (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
183
184 Registers.Read (Registers.PCH_PP_OFF_DELAYS, Power_Delay);
185 Delays_US (Power_Down_Delay) := 100 * Natural
186 (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
187 Delays_US (BL_Off_To_Power_Down) := 100 * Natural
188 (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
189
190 Registers.Read (Registers.PCH_PP_DIVISOR, Power_Delay);
191 if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
192 Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
193 (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
194 end if;
195
196 Check_PP_Delays (Delays_US, Override_Delays);
197 end if;
198
199 if Override_Delays then
200 if Config.Has_PP_Port_Select then
201 if Config.Internal_Is_EDP then
202 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
203 else
204 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
205 end if;
206 else
207 Port_Select := 0;
208 end if;
209
210 -- Force power-up to backlight-on delay to 100us as recommended by PRM.
211 Registers.Unset_And_Set_Mask
212 (Register => Registers.PCH_PP_ON_DELAYS,
213 Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
214 PCH_PP_ON_DELAYS_PWR_UP_MASK or
215 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
216 Mask_Set => Port_Select or
217 PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
218 or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
219
220 Registers.Unset_And_Set_Mask
221 (Register => Registers.PCH_PP_OFF_DELAYS,
222 Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
223 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
224 Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
225 (Delays_US (Power_Down_Delay)) or
226 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
227 (Delays_US (BL_Off_To_Power_Down)));
228
229 Registers.Unset_And_Set_Mask
230 (Register => Registers.PCH_PP_DIVISOR,
231 Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
232 Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
233 (Delays_US (Power_Cycle_Delay)));
234 end if;
235
236 if Config.Has_PP_Write_Protection then
237 Registers.Unset_And_Set_Mask
238 (Register => Registers.PCH_PP_CONTROL,
239 Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
240 Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
241 PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
242 else
243 Registers.Set_Mask
244 (Register => Registers.PCH_PP_CONTROL,
245 Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
246 end if;
247 end Setup_PP_Sequencer;
248
249 ----------------------------------------------------------------------------
250
251 procedure VDD_Override is
252 begin
253 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
254
255 -- Yeah, We could do, what we are supposed to do here. But OTOH, we
256 -- are should wait for the full Power Up Delay, which we would have
257 -- to do later again. And just powering on the display seems to work
258 -- too. Also this function vanished on newer hardware.
259 On;
260 end VDD_Override;
261
262 procedure On (Wait : Boolean := True)
263 is
264 Was_On : Boolean;
265 begin
266 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
267
268 Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
269 if not Was_On then
270 Time.Delay_Until (Power_Cycle_Timer);
271 end if;
272
273 Registers.Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON);
274 if not Was_On then
275 Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
276 end if;
277 if Wait then
278 Wait_On;
279 end if;
280 end On;
281
282 procedure Wait_On is
283 begin
284 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
285
286 Time.Delay_Until (Power_Up_Timer);
287 Registers.Wait_Unset_Mask
288 (Register => Registers.PCH_PP_STATUS,
289 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
290 TOut_MS => 300);
291
292 Registers.Unset_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
293 end Wait_On;
294
295 procedure Off
296 is
297 Was_On : Boolean;
298 begin
299 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
300
301 Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
302 Registers.Unset_Mask
303 (Register => Registers.PCH_PP_CONTROL,
304 Mask => PCH_PP_CONTROL_TARGET_ON or
305 PCH_PP_CONTROL_VDD_OVERRIDE);
306 if Was_On then
307 Time.U_Delay (Delays_US (Power_Down_Delay));
308 end if;
309 Registers.Wait_Unset_Mask
310 (Register => Registers.PCH_PP_STATUS,
311 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
312 TOut_MS => 600);
313 if Was_On then
314 Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
315 end if;
316 end Off;
317
318 ----------------------------------------------------------------------------
319
320 procedure Backlight_On is
321 begin
322 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
323
324 Registers.Set_Mask
325 (Register => Registers.PCH_PP_CONTROL,
326 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
327 end Backlight_On;
328
329 procedure Backlight_Off is
330 begin
331 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
332
333 Registers.Unset_Mask
334 (Register => Registers.PCH_PP_CONTROL,
335 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
336 end Backlight_Off;
337
338 procedure Set_Backlight (Level : Word16) is
339 begin
340 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
341
342 Registers.Unset_And_Set_Mask
343 (Register => Registers.BLC_PWM_CPU_CTL,
344 Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
345 Mask_Set => Word32 (Level));
346 end Set_Backlight;
347
348 procedure Get_Max_Backlight (Level : out Word16)
349 is
350 Reg : Word32;
351 begin
352 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
353
354 Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
355 Level := Word16
356 (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
357 end Get_Max_Backlight;
358
359end HW.GFX.GMA.Panel;