2 * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Michael Lotz, mmlr@mlotz.ch
7 * Alexander von Gluck IV, kallisti5@unixzen.com
11 #include "accelerant.h"
12 #include "intel_extreme.h"
13 #include <KernelExport.h>
23 extern "C" void _sPrintf(const char* format
, ...);
24 # define TRACE(x...) _sPrintf("intel_extreme: " x)
26 # define TRACE(x...) ;
29 #define ERROR(x...) _sPrintf("intel_extreme: " x)
30 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
38 program_pipe_color_modes(uint32 colorMode
)
40 // All pipes get the same color mode
41 write32(INTEL_DISPLAY_A_CONTROL
, (read32(INTEL_DISPLAY_A_CONTROL
)
42 & ~(DISPLAY_CONTROL_COLOR_MASK
| DISPLAY_CONTROL_GAMMA
))
44 write32(INTEL_DISPLAY_B_CONTROL
, (read32(INTEL_DISPLAY_B_CONTROL
)
45 & ~(DISPLAY_CONTROL_COLOR_MASK
| DISPLAY_CONTROL_GAMMA
))
50 // #pragma mark - Pipe
53 Pipe::Pipe(pipe_index pipeIndex
)
55 fHasTranscoder(false),
57 // fPanelFitter(NULL),
58 fPipeIndex(pipeIndex
),
62 if (pipeIndex
== INTEL_PIPE_B
) {
63 fPipeOffset
= INTEL_DISPLAY_OFFSET
;
64 fPlaneOffset
= INTEL_PLANE_OFFSET
;
67 // IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
68 // Haswell: Only VGA behind FDI (on northbridge)
69 // SkyLake: FDI gone. No more northbridge video.
70 if (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
) {
71 TRACE("%s: Pipe %s routed through FDI\n", __func__
,
72 (pipeIndex
== INTEL_PIPE_A
) ? "A" : "B");
74 fHasTranscoder
= true;
76 // Program FDILink if PCH
77 fFDILink
= new(std::nothrow
) FDILink(pipeIndex
);
80 TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
81 " Plane Base: 0x% " B_PRIxADDR
"\n", (pipeIndex
== INTEL_PIPE_A
)
82 ? "A" : "B", fPipeOffset
, fPlaneOffset
);
96 return (read32(INTEL_DISPLAY_A_PIPE_CONTROL
+ fPipeOffset
)
97 & INTEL_PIPE_ENABLED
) != 0;
102 Pipe::Configure(display_mode
* mode
)
104 uint32 pipeControl
= read32(INTEL_DISPLAY_A_PIPE_CONTROL
+ fPipeOffset
);
106 // TODO: Haswell+ dithering changes.
107 if (gInfo
->shared_info
->device_type
.Generation() >= 4) {
108 pipeControl
|= (INTEL_PIPE_DITHER_EN
| INTEL_PIPE_DITHER_TYPE_SP
);
109 switch (mode
->space
) {
113 pipeControl
|= INTEL_PIPE_6BPC
;
116 pipeControl
|= INTEL_PIPE_8BPC
;
120 pipeControl
|= INTEL_PIPE_10BPC
;
125 // TODO: CxSR downclocking?
127 // TODO: Interlaced modes
128 pipeControl
|= INTEL_PIPE_PROGRESSIVE
;
130 write32(INTEL_DISPLAY_A_PIPE_CONTROL
+ fPipeOffset
, pipeControl
);
131 read32(INTEL_DISPLAY_A_PIPE_CONTROL
+ fPipeOffset
);
136 Pipe::_ConfigureTranscoder(display_mode
* target
)
140 TRACE("%s: fPipeOffset: 0x%" B_PRIx32
"\n", __func__
, fPipeOffset
);
142 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
143 write32(INTEL_TRANSCODER_A_HTOTAL
+ fPipeOffset
,
144 ((uint32
)(target
->timing
.h_total
- 1) << 16)
145 | ((uint32
)target
->timing
.h_display
- 1));
146 write32(INTEL_TRANSCODER_A_HBLANK
+ fPipeOffset
,
147 ((uint32
)(target
->timing
.h_total
- 1) << 16)
148 | ((uint32
)target
->timing
.h_display
- 1));
149 write32(INTEL_TRANSCODER_A_HSYNC
+ fPipeOffset
,
150 ((uint32
)(target
->timing
.h_sync_end
- 1) << 16)
151 | ((uint32
)target
->timing
.h_sync_start
- 1));
153 write32(INTEL_TRANSCODER_A_VTOTAL
+ fPipeOffset
,
154 ((uint32
)(target
->timing
.v_total
- 1) << 16)
155 | ((uint32
)target
->timing
.v_display
- 1));
156 write32(INTEL_TRANSCODER_A_VBLANK
+ fPipeOffset
,
157 ((uint32
)(target
->timing
.v_total
- 1) << 16)
158 | ((uint32
)target
->timing
.v_display
- 1));
159 write32(INTEL_TRANSCODER_A_VSYNC
+ fPipeOffset
,
160 ((uint32
)(target
->timing
.v_sync_end
- 1) << 16)
161 | ((uint32
)target
->timing
.v_sync_start
- 1));
164 // XXX: Is it ok to do these on non-digital?
165 write32(INTEL_TRANSCODER_A_POS
+ fPipeOffset
, 0);
166 write32(INTEL_TRANSCODER_A_IMAGE_SIZE
+ fPipeOffset
,
167 ((uint32
)(target
->virtual_width
- 1) << 16)
168 | ((uint32
)target
->virtual_height
- 1));
174 Pipe::ConfigureTimings(display_mode
* target
)
178 TRACE("%s: fPipeOffset: 0x%" B_PRIx32
"\n", __func__
, fPipeOffset
);
180 if (target
== NULL
) {
181 ERROR("%s: Invalid display mode!\n", __func__
);
185 /* If there is a transcoder, leave the display at its native resolution,
186 * and configure only the transcoder (panel fitting will match them
190 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
191 write32(INTEL_DISPLAY_A_HTOTAL
+ fPipeOffset
,
192 ((uint32
)(target
->timing
.h_total
- 1) << 16)
193 | ((uint32
)target
->timing
.h_display
- 1));
194 write32(INTEL_DISPLAY_A_HBLANK
+ fPipeOffset
,
195 ((uint32
)(target
->timing
.h_total
- 1) << 16)
196 | ((uint32
)target
->timing
.h_display
- 1));
197 write32(INTEL_DISPLAY_A_HSYNC
+ fPipeOffset
,
198 ((uint32
)(target
->timing
.h_sync_end
- 1) << 16)
199 | ((uint32
)target
->timing
.h_sync_start
- 1));
201 write32(INTEL_DISPLAY_A_VTOTAL
+ fPipeOffset
,
202 ((uint32
)(target
->timing
.v_total
- 1) << 16)
203 | ((uint32
)target
->timing
.v_display
- 1));
204 write32(INTEL_DISPLAY_A_VBLANK
+ fPipeOffset
,
205 ((uint32
)(target
->timing
.v_total
- 1) << 16)
206 | ((uint32
)target
->timing
.v_display
- 1));
207 write32(INTEL_DISPLAY_A_VSYNC
+ fPipeOffset
,
208 ((uint32
)(target
->timing
.v_sync_end
- 1) << 16)
209 | ((uint32
)target
->timing
.v_sync_start
- 1));
212 // XXX: Is it ok to do these on non-digital?
214 write32(INTEL_DISPLAY_A_POS
+ fPipeOffset
, 0);
216 // Set the image size for both pipes, just in case.
217 write32(INTEL_DISPLAY_A_IMAGE_SIZE
,
218 ((uint32
)(target
->virtual_width
- 1) << 16)
219 | ((uint32
)target
->virtual_height
- 1));
220 write32(INTEL_DISPLAY_B_IMAGE_SIZE
,
221 ((uint32
)(target
->virtual_width
- 1) << 16)
222 | ((uint32
)target
->virtual_height
- 1));
224 write32(INTEL_DISPLAY_A_PIPE_SIZE
+ fPipeOffset
,
225 ((uint32
)(target
->timing
.v_display
- 1) << 16)
226 | ((uint32
)target
->timing
.h_display
- 1));
228 // This is useful for debugging: it sets the border to red, so you
229 // can see what is border and what is porch (black area around the
231 //write32(INTEL_DISPLAY_A_RED + fPipeOffset, 0x00FF0000);
234 _ConfigureTranscoder(target
);
239 Pipe::ConfigureClocks(const pll_divisors
& divisors
, uint32 pixelClock
,
244 addr_t pllDivisorA
= INTEL_DISPLAY_A_PLL_DIVISOR_0
;
245 addr_t pllDivisorB
= INTEL_DISPLAY_A_PLL_DIVISOR_1
;
246 addr_t pllControl
= INTEL_DISPLAY_A_PLL
;
247 addr_t pllMD
= INTEL_DISPLAY_A_PLL_MD
;
249 if (fPipeIndex
== INTEL_PIPE_B
) {
250 pllDivisorA
= INTEL_DISPLAY_B_PLL_DIVISOR_0
;
251 pllDivisorB
= INTEL_DISPLAY_B_PLL_DIVISOR_1
;
252 pllControl
= INTEL_DISPLAY_B_PLL
;
253 pllMD
= INTEL_DISPLAY_B_PLL_MD
;
256 float refFreq
= gInfo
->shared_info
->pll_info
.reference_frequency
/ 1000.0f
;
258 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_96x
)) {
259 float adjusted
= ((refFreq
* divisors
.m
) / divisors
.n
) / divisors
.p
;
260 uint32 pixelMultiply
= uint32(adjusted
/ (pixelClock
/ 1000.0f
));
261 write32(pllMD
, (0 << 24) | ((pixelMultiply
- 1) << 8));
264 // XXX: For now we assume no LVDS downclocking and program the same divisor
265 // value to both divisor 0 (standard) and 1 (reduced divisor)
266 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_PIN
)) {
267 write32(pllDivisorA
, (((1 << divisors
.n
) << DISPLAY_PLL_N_DIVISOR_SHIFT
)
268 & DISPLAY_PLL_IGD_N_DIVISOR_MASK
)
269 | (((divisors
.m2
- 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT
)
270 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK
));
271 write32(pllDivisorB
, (((1 << divisors
.n
) << DISPLAY_PLL_N_DIVISOR_SHIFT
)
272 & DISPLAY_PLL_IGD_N_DIVISOR_MASK
)
273 | (((divisors
.m2
- 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT
)
274 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK
));
276 write32(pllDivisorA
, (((divisors
.n
- 2) << DISPLAY_PLL_N_DIVISOR_SHIFT
)
277 & DISPLAY_PLL_N_DIVISOR_MASK
)
278 | (((divisors
.m1
- 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT
)
279 & DISPLAY_PLL_M1_DIVISOR_MASK
)
280 | (((divisors
.m2
- 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT
)
281 & DISPLAY_PLL_M2_DIVISOR_MASK
));
282 write32(pllDivisorB
, (((divisors
.n
- 2) << DISPLAY_PLL_N_DIVISOR_SHIFT
)
283 & DISPLAY_PLL_N_DIVISOR_MASK
)
284 | (((divisors
.m1
- 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT
)
285 & DISPLAY_PLL_M1_DIVISOR_MASK
)
286 | (((divisors
.m2
- 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT
)
287 & DISPLAY_PLL_M2_DIVISOR_MASK
));
290 uint32 pll
= DISPLAY_PLL_ENABLED
| DISPLAY_PLL_NO_VGA_CONTROL
| extraFlags
;
292 if (gInfo
->shared_info
->device_type
.Generation() >= 3) {
293 // p1 divisor << 1 , 1-8
294 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_PIN
)) {
295 pll
|= ((1 << (divisors
.p1
- 1))
296 << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT
)
297 & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK
;
299 pll
|= ((1 << (divisors
.p1
- 1))
300 << DISPLAY_PLL_POST1_DIVISOR_SHIFT
)
301 & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK
;
302 // pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
303 // & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
306 if (divisors
.p2
== 5 || divisors
.p2
== 7)
307 pll
|= DISPLAY_PLL_DIVIDE_HIGH
;
309 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_96x
))
310 pll
|= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT
;
312 if (divisors
.p2
!= 5 && divisors
.p2
!= 7)
313 pll
|= DISPLAY_PLL_DIVIDE_4X
;
315 pll
|= DISPLAY_PLL_2X_CLOCK
;
317 // TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
318 if (divisors
.p1
> 2) {
319 pll
|= ((divisors
.p1
- 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT
)
320 & DISPLAY_PLL_POST1_DIVISOR_MASK
;
322 pll
|= DISPLAY_PLL_POST1_DIVIDE_2
;
325 // Allow the PLL to warm up by masking its bit.
326 write32(pllControl
, pll
& ~DISPLAY_PLL_NO_VGA_CONTROL
);
329 write32(pllControl
, pll
);
336 Pipe::Enable(bool enable
)
340 addr_t pipeReg
= INTEL_DISPLAY_A_PIPE_CONTROL
+ fPipeOffset
;
341 addr_t planeReg
= INTEL_DISPLAY_A_CONTROL
+ fPlaneOffset
;
343 // Planes always have to operate on an enabled pipe
346 write32(pipeReg
, read32(pipeReg
) | INTEL_PIPE_ENABLED
);
348 write32(planeReg
, read32(planeReg
) | DISPLAY_CONTROL_ENABLED
);
350 write32(planeReg
, read32(planeReg
) & ~DISPLAY_CONTROL_ENABLED
);
352 write32(pipeReg
, read32(pipeReg
) & ~INTEL_PIPE_ENABLED
);
355 read32(INTEL_DISPLAY_A_BASE
);
356 // flush the eventually cached PCI bus writes