vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / intel_extreme / Pipes.cpp
blob285415d1d5de390a34962b77cd60f7df9e284fd3
1 /*
2 * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Lotz, mmlr@mlotz.ch
7 * Alexander von Gluck IV, kallisti5@unixzen.com
8 */
9 #include "Pipes.h"
11 #include "accelerant.h"
12 #include "intel_extreme.h"
13 #include <KernelExport.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include <new>
21 #define TRACE_PIPE
22 #ifdef TRACE_PIPE
23 extern "C" void _sPrintf(const char* format, ...);
24 # define TRACE(x...) _sPrintf("intel_extreme: " x)
25 #else
26 # define TRACE(x...) ;
27 #endif
29 #define ERROR(x...) _sPrintf("intel_extreme: " x)
30 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
33 // PIPE: 6
34 // PLANE: 7
37 void
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))
43 | colorMode);
44 write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
45 & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
46 | colorMode);
50 // #pragma mark - Pipe
53 Pipe::Pipe(pipe_index pipeIndex)
55 fHasTranscoder(false),
56 fFDILink(NULL),
57 // fPanelFitter(NULL),
58 fPipeIndex(pipeIndex),
59 fPipeOffset(0),
60 fPlaneOffset(0)
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);
86 Pipe::~Pipe()
91 bool
92 Pipe::IsEnabled()
94 CALLED();
96 return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset)
97 & INTEL_PIPE_ENABLED) != 0;
101 void
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) {
110 case B_CMAP8:
111 case B_RGB15_LITTLE:
112 case B_RGB16_LITTLE:
113 pipeControl |= INTEL_PIPE_6BPC;
114 break;
115 case B_RGB24_LITTLE:
116 pipeControl |= INTEL_PIPE_8BPC;
117 break;
118 case B_RGB32_LITTLE:
119 default:
120 pipeControl |= INTEL_PIPE_10BPC;
121 break;
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);
135 void
136 Pipe::_ConfigureTranscoder(display_mode* target)
138 CALLED();
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));
163 #if 0
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));
169 #endif
173 void
174 Pipe::ConfigureTimings(display_mode* target)
176 CALLED();
178 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
180 if (target == NULL) {
181 ERROR("%s: Invalid display mode!\n", __func__);
182 return;
185 /* If there is a transcoder, leave the display at its native resolution,
186 * and configure only the transcoder (panel fitting will match them
187 * together). */
188 if (!fHasTranscoder)
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
230 // sync)
231 //write32(INTEL_DISPLAY_A_RED + fPipeOffset, 0x00FF0000);
233 if (fHasTranscoder)
234 _ConfigureTranscoder(target);
238 void
239 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
240 uint32 extraFlags)
242 CALLED();
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));
275 } else {
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;
298 } else {
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;
311 } else {
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;
321 } else
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);
327 read32(pllControl);
328 spin(150);
329 write32(pllControl, pll);
330 read32(pllControl);
331 spin(150);
335 void
336 Pipe::Enable(bool enable)
338 CALLED();
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
345 if (enable) {
346 write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
347 wait_for_vblank();
348 write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED);
349 } else {
350 write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED);
351 wait_for_vblank();
352 write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED);
355 read32(INTEL_DISPLAY_A_BASE);
356 // flush the eventually cached PCI bus writes