blob: 1fc043dd0863ec402ee8f300ee65f7024774304a [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
6-- the Free Software Foundation; version 2 of the License.
7--
8-- This program is distributed in the hope that it will be useful,
9-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11-- GNU General Public License for more details.
12--
13
14with HW.GFX.GMA.Config;
15
16with HW.Debug;
17with GNAT.Source_Info;
18
19package body HW.GFX.GMA.Panel
20with
21 Refined_State =>
22 (Panel_State =>
23 (Delays_US, Power_Cycle_Timer, Power_Up_Timer))
24is
25 type Delays_Enum is
26 (Power_Up_Delay,
27 Power_Up_To_BL_On,
28 Power_Down_Delay,
29 BL_Off_To_Power_Down,
30 Power_Cycle_Delay);
31
32 type Panel_Power_Delays is array (Delays_Enum) of Natural;
33 Default_EDP_Delays_US : constant Panel_Power_Delays := Panel_Power_Delays'
34 (Power_Up_Delay => 210_000,
35 Power_Up_To_BL_On => 50_000,
36 Power_Down_Delay => 500_000,
37 BL_Off_To_Power_Down => 50_000,
38 Power_Cycle_Delay => 510_000);
39
40 Delays_US : Panel_Power_Delays;
41
42 ----------------------------------------------------------------------------
43
44 -- And here the mess starts: We have this pretty hardware power sequencer
45 -- that should ensure the panel's timing constraints are satisfied. But
46 -- (at least on some generations) it doesn't do it's job. On Haswell, it
47 -- seems to ignore the Power_Cycle_Delay, so we ensure the delay in soft-
48 -- ware. On at least Ivy Bridge and Broadwell Power_Up_Delay is ignored.
49 --
50 -- If we ever do all delays in software, there are two ways: Either confi-
51 -- gure the hardware to zero delays or wait for both the software timeout
52 -- and the hardware power sequencer. The latter option would be less error
53 -- prone, as the hardware might just don't work as expected.
54
55 Power_Cycle_Timer : Time.T;
56 Power_Up_Timer : Time.T;
57
58 ----------------------------------------------------------------------------
59
60 function Div_Round_Up32 (Numerator, Denominator : Natural) return Word32 is
61 begin
62 return (Word32 (Numerator) + Word32 (Denominator) - 1)
63 / Word32 (Denominator);
64 end Div_Round_Up32;
65
66 PCH_PP_STATUS_ENABLED : constant := 16#00_0001# * 2 ** 31;
67 PCH_PP_STATUS_REQUIRE_ASSET : constant := 16#00_0001# * 2 ** 30;
68 PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK : constant := 16#00_0003# * 2 ** 28;
69 PCH_PP_STATUS_PWR_SEQ_PROGRESS_NONE : constant := 16#00_0000# * 2 ** 28;
70 PCH_PP_STATUS_PWR_SEQ_PROGRESS_UP : constant := 16#00_0001# * 2 ** 28;
71 PCH_PP_STATUS_PWR_SEQ_PROGRESS_DOWN : constant := 16#00_0002# * 2 ** 28;
72 PCH_PP_STATUS_PWR_CYC_DELAY_ACTIVE : constant := 16#00_0001# * 2 ** 27;
73
74 PCH_PP_CONTROL_WRITE_PROTECT_MASK : constant := 16#00_ffff# * 2 ** 16;
75 PCH_PP_CONTROL_WRITE_PROTECT_KEY : constant := 16#00_abcd# * 2 ** 16;
76 PCH_PP_CONTROL_VDD_OVERRIDE : constant := 16#00_0001# * 2 ** 3;
77 PCH_PP_CONTROL_BACKLIGHT_ENABLE : constant := 16#00_0001# * 2 ** 2;
78 PCH_PP_CONTROL_POWER_DOWN_ON_RESET : constant := 16#00_0001# * 2 ** 1;
79 PCH_PP_CONTROL_TARGET_ON : constant := 16#00_0001# * 2 ** 0;
80
81 PCH_PP_ON_DELAYS_PORT_SELECT_MASK : constant := 16#00_0003# * 2 ** 30;
82 PCH_PP_ON_DELAYS_PORT_SELECT_LVDS : constant := 16#00_0000# * 2 ** 30;
83 PCH_PP_ON_DELAYS_PORT_SELECT_DP_A : constant := 16#00_0001# * 2 ** 30;
84 PCH_PP_ON_DELAYS_PORT_SELECT_DP_C : constant := 16#00_0002# * 2 ** 30;
85 PCH_PP_ON_DELAYS_PORT_SELECT_DP_D : constant := 16#00_0003# * 2 ** 30;
86 PCH_PP_ON_DELAYS_PWR_UP_MASK : constant := 16#00_1fff# * 2 ** 16;
87 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK : constant := 16#00_1fff# * 2 ** 0;
88 function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
89 begin
90 return Shift_Left (Div_Round_Up32 (US, 100), 16);
91 end PCH_PP_ON_DELAYS_PWR_UP;
92 function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
93 begin
94 return Div_Round_Up32 (US, 100);
95 end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
96
97 PCH_PP_OFF_DELAYS_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 16;
98 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 0;
99 function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
100 begin
101 return Shift_Left (Div_Round_Up32 (US, 100), 16);
102 end PCH_PP_OFF_DELAYS_PWR_DOWN;
103 function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
104 begin
105 return Div_Round_Up32 (US, 100);
106 end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
107
108 PCH_PP_DIVISOR_REF_DIVIDER_MASK : constant := 16#ff_ffff# * 2 ** 8;
109 PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK : constant := 16#00_001f# * 2 ** 0;
110 function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
111 begin
112 return Div_Round_Up32 (US, 100_000) + 1;
113 end PCH_PP_DIVISOR_PWR_CYC_DELAY;
114
115 CPU_BLC_PWM_CTL_ENABLE : constant := 16#00_0001# * 2 ** 31;
116 CPU_BLC_PWM_CTL_PIPE_SELECT_MASK : constant := 16#00_0003# * 2 ** 29;
117 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A : constant := 16#00_0000# * 2 ** 29;
118 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B : constant := 16#00_0001# * 2 ** 29;
119 CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C : constant := 16#00_0002# * 2 ** 29;
120
121 CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
122
123 PCH_BLC_PWM_CTL1_ENABLE : constant := 16#00_0001# * 2 ** 31;
124 PCH_BLC_PWM_CTL1_BL_POLARITY_MASK : constant := 16#00_0001# * 2 ** 29;
125 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
126 PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE : constant := 16#00_0001# * 2 ** 25;
127 PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN : constant := 16#00_0001# * 2 ** 24;
128 PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
129 PCH_BLC_PWM_CTL1_PHASE_IN_COUNT : constant := 16#00_00ff# * 2 ** 8;
130 PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 ** 0;
131
132 PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK : constant := 16#00_ffff# * 2 ** 16;
133 PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
134
135 ----------------------------------------------------------------------------
136
137 procedure Static_Init
138 with
139 Refined_Global =>
140 (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
141 Input => (Time.State))
142 is
143 begin
144 Power_Cycle_Timer := Time.Now;
145 Power_Up_Timer := Power_Cycle_Timer;
146
147 Delays_US := Default_EDP_Delays_US;
148 end Static_Init;
149
150 ----------------------------------------------------------------------------
151
152 procedure Check_PP_Delays
153 (Delays : in out Panel_Power_Delays;
154 Override : in out Boolean) is
155 begin
156 for D in Delays_Enum loop
157 if Delays (D) = 0 then
158 Delays (D) := Default_EDP_Delays_US (D);
159 Override := True;
160 end if;
161 end loop;
162 end Check_PP_Delays;
163
164 procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
165 is
166 Power_Delay, Port_Select : Word32;
167
168 Override_Delays : Boolean := False;
169 begin
170 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
171
172 Static_Init;
173
174 if Default_Delays then
175 Override_Delays := True;
176 else
177 Registers.Read (Registers.PCH_PP_ON_DELAYS, Power_Delay);
178 Delays_US (Power_Up_Delay) := 100 * Natural
179 (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
180 Delays_US (Power_Up_To_BL_On) := 100 * Natural
181 (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
182
183 Registers.Read (Registers.PCH_PP_OFF_DELAYS, Power_Delay);
184 Delays_US (Power_Down_Delay) := 100 * Natural
185 (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
186 Delays_US (BL_Off_To_Power_Down) := 100 * Natural
187 (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
188
189 Registers.Read (Registers.PCH_PP_DIVISOR, Power_Delay);
190 if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
191 Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
192 (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
193 end if;
194
195 Check_PP_Delays (Delays_US, Override_Delays);
196 end if;
197
198 if Override_Delays then
199 if Config.Has_PP_Port_Select then
200 if Config.Internal_Is_EDP then
201 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
202 else
203 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
204 end if;
205 else
206 Port_Select := 0;
207 end if;
208
209 -- Force power-up to backlight-on delay to 100us as recommended by PRM.
210 Registers.Unset_And_Set_Mask
211 (Register => Registers.PCH_PP_ON_DELAYS,
212 Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
213 PCH_PP_ON_DELAYS_PWR_UP_MASK or
214 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
215 Mask_Set => Port_Select or
216 PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
217 or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
218
219 Registers.Unset_And_Set_Mask
220 (Register => Registers.PCH_PP_OFF_DELAYS,
221 Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
222 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
223 Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
224 (Delays_US (Power_Down_Delay)) or
225 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
226 (Delays_US (BL_Off_To_Power_Down)));
227
228 Registers.Unset_And_Set_Mask
229 (Register => Registers.PCH_PP_DIVISOR,
230 Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
231 Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
232 (Delays_US (Power_Cycle_Delay)));
233 end if;
234
235 if Config.Has_PP_Write_Protection then
236 Registers.Unset_And_Set_Mask
237 (Register => Registers.PCH_PP_CONTROL,
238 Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
239 Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
240 PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
241 else
242 Registers.Set_Mask
243 (Register => Registers.PCH_PP_CONTROL,
244 Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
245 end if;
246 end Setup_PP_Sequencer;
247
248 ----------------------------------------------------------------------------
249
250 procedure VDD_Override is
251 begin
252 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
253
254 -- Yeah, We could do, what we are supposed to do here. But OTOH, we
255 -- are should wait for the full Power Up Delay, which we would have
256 -- to do later again. And just powering on the display seems to work
257 -- too. Also this function vanished on newer hardware.
258 On;
259 end VDD_Override;
260
261 procedure On (Wait : Boolean := True)
262 is
263 Was_On : Boolean;
264 begin
265 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
266
267 Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
268 if not Was_On then
269 Time.Delay_Until (Power_Cycle_Timer);
270 end if;
271
272 Registers.Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON);
273 if not Was_On then
274 Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
275 end if;
276 if Wait then
277 Wait_On;
278 end if;
279 end On;
280
281 procedure Wait_On is
282 begin
283 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
284
285 Time.Delay_Until (Power_Up_Timer);
286 Registers.Wait_Unset_Mask
287 (Register => Registers.PCH_PP_STATUS,
288 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
289 TOut_MS => 300);
290
291 Registers.Unset_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
292 end Wait_On;
293
294 procedure Off
295 is
296 Was_On : Boolean;
297 begin
298 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
299
300 Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
301 Registers.Unset_Mask
302 (Register => Registers.PCH_PP_CONTROL,
303 Mask => PCH_PP_CONTROL_TARGET_ON or
304 PCH_PP_CONTROL_VDD_OVERRIDE);
305 if Was_On then
306 Time.U_Delay (Delays_US (Power_Down_Delay));
307 end if;
308 Registers.Wait_Unset_Mask
309 (Register => Registers.PCH_PP_STATUS,
310 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
311 TOut_MS => 600);
312 if Was_On then
313 Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
314 end if;
315 end Off;
316
317 ----------------------------------------------------------------------------
318
319 procedure Backlight_On is
320 begin
321 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
322
323 Registers.Set_Mask
324 (Register => Registers.PCH_PP_CONTROL,
325 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
326 end Backlight_On;
327
328 procedure Backlight_Off is
329 begin
330 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
331
332 Registers.Unset_Mask
333 (Register => Registers.PCH_PP_CONTROL,
334 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
335 end Backlight_Off;
336
337 procedure Set_Backlight (Level : Word16) is
338 begin
339 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
340
341 Registers.Unset_And_Set_Mask
342 (Register => Registers.BLC_PWM_CPU_CTL,
343 Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
344 Mask_Set => Word32 (Level));
345 end Set_Backlight;
346
347 procedure Get_Max_Backlight (Level : out Word16)
348 is
349 Reg : Word32;
350 begin
351 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
352
353 Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
354 Level := Word16
355 (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
356 end Get_Max_Backlight;
357
358end HW.GFX.GMA.Panel;