blob: 968fbbe9bf3372b8674493d19475766fb2efaa02 [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
220 if Config.Internal_Is_EDP then
221 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
222 else
223 Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
224 end if;
225 else
226 Port_Select := 0;
227 end if;
228
229 -- Force power-up to backlight-on delay to 100us as recommended by PRM.
230 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200231 (Register => Panel_PP_Regs.ON_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200232 Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
233 PCH_PP_ON_DELAYS_PWR_UP_MASK or
234 PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
235 Mask_Set => Port_Select or
236 PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
237 or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
238
239 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200240 (Register => Panel_PP_Regs.OFF_DELAYS,
Nico Huber83693c82016-10-08 22:17:55 +0200241 Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
242 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
243 Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
244 (Delays_US (Power_Down_Delay)) or
245 PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
246 (Delays_US (BL_Off_To_Power_Down)));
247
248 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200249 (Register => Panel_PP_Regs.DIVISOR,
Nico Huber83693c82016-10-08 22:17:55 +0200250 Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
251 Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
252 (Delays_US (Power_Cycle_Delay)));
253 end if;
254
255 if Config.Has_PP_Write_Protection then
256 Registers.Unset_And_Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200257 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200258 Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
259 Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
260 PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
261 else
262 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200263 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200264 Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
265 end if;
266 end Setup_PP_Sequencer;
267
268 ----------------------------------------------------------------------------
269
270 procedure VDD_Override is
271 begin
272 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
273
274 -- Yeah, We could do, what we are supposed to do here. But OTOH, we
275 -- are should wait for the full Power Up Delay, which we would have
276 -- to do later again. And just powering on the display seems to work
277 -- too. Also this function vanished on newer hardware.
278 On;
279 end VDD_Override;
280
281 procedure On (Wait : Boolean := True)
282 is
283 Was_On : Boolean;
284 begin
285 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
286
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200287 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200288 if not Was_On then
289 Time.Delay_Until (Power_Cycle_Timer);
290 end if;
291
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200292 Registers.Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON);
Nico Huber83693c82016-10-08 22:17:55 +0200293 if not Was_On then
294 Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
295 end if;
296 if Wait then
297 Wait_On;
298 end if;
299 end On;
300
301 procedure Wait_On is
302 begin
303 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
304
305 Time.Delay_Until (Power_Up_Timer);
306 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200307 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200308 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
309 TOut_MS => 300);
310
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200311 Registers.Unset_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
Nico Huber83693c82016-10-08 22:17:55 +0200312 end Wait_On;
313
314 procedure Off
315 is
316 Was_On : Boolean;
317 begin
318 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
319
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200320 Registers.Is_Set_Mask (Panel_PP_Regs.CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
Nico Huber83693c82016-10-08 22:17:55 +0200321 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200322 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200323 Mask => PCH_PP_CONTROL_TARGET_ON or
324 PCH_PP_CONTROL_VDD_OVERRIDE);
325 if Was_On then
326 Time.U_Delay (Delays_US (Power_Down_Delay));
327 end if;
328 Registers.Wait_Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200329 (Register => Panel_PP_Regs.STATUS,
Nico Huber83693c82016-10-08 22:17:55 +0200330 Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
331 TOut_MS => 600);
332 if Was_On then
333 Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
334 end if;
335 end Off;
336
337 ----------------------------------------------------------------------------
338
339 procedure Backlight_On is
340 begin
341 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
342
343 Registers.Set_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200344 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200345 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
346 end Backlight_On;
347
348 procedure Backlight_Off is
349 begin
350 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
351
352 Registers.Unset_Mask
Arthur Heymanse87d0d12018-03-28 17:02:49 +0200353 (Register => Panel_PP_Regs.CONTROL,
Nico Huber83693c82016-10-08 22:17:55 +0200354 Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
355 end Backlight_Off;
356
357 procedure Set_Backlight (Level : Word16) is
358 begin
359 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
360
361 Registers.Unset_And_Set_Mask
362 (Register => Registers.BLC_PWM_CPU_CTL,
363 Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
364 Mask_Set => Word32 (Level));
365 end Set_Backlight;
366
367 procedure Get_Max_Backlight (Level : out Word16)
368 is
369 Reg : Word32;
370 begin
371 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
372
373 Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
374 Level := Word16
375 (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
376 end Get_Max_Backlight;
377
378end HW.GFX.GMA.Panel;