1 /* 2 * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz, mmlr@mlotz.ch 7 * Alexander von Gluck IV, kallisti5@unixzen.com 8 */ 9 #include "Pipes.h" 10 11 #include "accelerant.h" 12 #include "intel_extreme.h" 13 #include <KernelExport.h> 14 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <new> 19 20 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 28 29 #define ERROR(x...) _sPrintf("intel_extreme: " x) 30 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 31 32 33 // PIPE: 6 34 // PLANE: 7 35 36 37 void 38 program_pipe_color_modes(uint32 colorMode) 39 { 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); 47 } 48 49 50 // #pragma mark - Pipe 51 52 53 Pipe::Pipe(pipe_index pipeIndex) 54 : 55 fHasTranscoder(false), 56 fFDILink(NULL), 57 fPanelFitter(NULL), 58 fPipeIndex(pipeIndex), 59 fPipeOffset(0), 60 fPlaneOffset(0) 61 { 62 if (pipeIndex == INTEL_PIPE_B) { 63 fPipeOffset = INTEL_DISPLAY_OFFSET; 64 fPlaneOffset = INTEL_PLANE_OFFSET; 65 } 66 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"); 73 74 fHasTranscoder = true; 75 76 // Program FDILink if PCH 77 fFDILink = new(std::nothrow) FDILink(pipeIndex); 78 // Program gen5(+) style panelfitter as well 79 fPanelFitter = new(std::nothrow) PanelFitter(pipeIndex); 80 } 81 82 TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR 83 " Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A) 84 ? "A" : "B", fPipeOffset, fPlaneOffset); 85 } 86 87 88 Pipe::~Pipe() 89 { 90 } 91 92 93 bool 94 Pipe::IsEnabled() 95 { 96 CALLED(); 97 98 return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset) 99 & INTEL_PIPE_ENABLED) != 0; 100 } 101 102 103 void 104 Pipe::Configure(display_mode* mode) 105 { 106 uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 107 108 // TODO: Haswell+ dithering changes. 109 //if (gInfo->shared_info->device_type.Generation() >= 4) { 110 // pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP); 111 112 //Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8) 113 //currently using BIOS preconfigured setup 114 //pipeControl = (pipeControl & ~INTEL_PIPE_BPC_MASK) | INTEL_PIPE_BPC(INTEL_PIPE_8BPC); 115 116 // TODO: CxSR downclocking? 117 118 // TODO: Interlaced modes 119 pipeControl = (pipeControl & ~(0x7 << 21)) | INTEL_PIPE_PROGRESSIVE; 120 121 write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl); 122 read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 123 124 if (gInfo->shared_info->device_type.Generation() >= 6) { 125 // According to SandyBridge modesetting sequence, pipe must be enabled 126 // before PLL are configured. 127 addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; 128 write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED); 129 } 130 } 131 132 133 void 134 Pipe::_ConfigureTranscoder(display_mode* target) 135 { 136 CALLED(); 137 138 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset); 139 140 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) 141 write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset, 142 ((uint32)(target->timing.h_total - 1) << 16) 143 | ((uint32)target->timing.h_display - 1)); 144 write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset, 145 ((uint32)(target->timing.h_total - 1) << 16) 146 | ((uint32)target->timing.h_display - 1)); 147 write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset, 148 ((uint32)(target->timing.h_sync_end - 1) << 16) 149 | ((uint32)target->timing.h_sync_start - 1)); 150 151 write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset, 152 ((uint32)(target->timing.v_total - 1) << 16) 153 | ((uint32)target->timing.v_display - 1)); 154 write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset, 155 ((uint32)(target->timing.v_total - 1) << 16) 156 | ((uint32)target->timing.v_display - 1)); 157 write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset, 158 ((uint32)(target->timing.v_sync_end - 1) << 16) 159 | ((uint32)target->timing.v_sync_start - 1)); 160 161 #if 0 162 // XXX: Is it ok to do these on non-digital? 163 write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0); 164 write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset, 165 ((uint32)(target->timing.h_display - 1) << 16) 166 | ((uint32)target->timing.v_display - 1)); 167 #endif 168 } 169 170 171 void 172 Pipe::ConfigureScalePos(display_mode* target) 173 { 174 CALLED(); 175 176 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset); 177 178 if (target == NULL) { 179 ERROR("%s: Invalid display mode!\n", __func__); 180 return; 181 } 182 183 if (gInfo->shared_info->device_type.Generation() < 6) { 184 // FIXME check on which generations this register exists 185 // (it appears it would be available only for cursor planes, not 186 // display planes) 187 // Since we set the plane to be the same size as the display, we can 188 // just show it starting at top-left. 189 write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0); 190 } 191 192 // The only thing that really matters: set the image size and let the 193 // panel fitter or the transcoder worry about the rest 194 write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset, 195 ((uint32)(target->timing.h_display - 1) << 16) 196 | ((uint32)target->timing.v_display - 1)); 197 198 // Set the plane size as well while we're at it (this is independant, we 199 // could have a larger plane and scroll through it). 200 if (gInfo->shared_info->device_type.Generation() <= 4) { 201 // This is "reserved" on G35 and GMA965, but needed on 945 (for which 202 // there is no public documentation), and I assume earlier devices as 203 // well. Note that the height and width are swapped when compared to 204 // the other registers. 205 write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset, 206 ((uint32)(target->timing.h_display - 1) << 16) 207 | ((uint32)target->timing.v_display - 1)); 208 } 209 } 210 211 212 void 213 Pipe::ConfigureTimings(display_mode* target, bool hardware) 214 { 215 CALLED(); 216 217 TRACE("%s(%d): fPipeOffset: 0x%" B_PRIx32"\n", __func__, hardware, 218 fPipeOffset); 219 220 if (target == NULL) { 221 ERROR("%s: Invalid display mode!\n", __func__); 222 return; 223 } 224 225 /* If using the transcoder, leave the display at its native resolution, 226 * and configure only the transcoder (panel fitting will match them 227 * together). */ 228 if (!fHasTranscoder || hardware) 229 { 230 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) 231 write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset, 232 ((uint32)(target->timing.h_total - 1) << 16) 233 | ((uint32)target->timing.h_display - 1)); 234 write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset, 235 ((uint32)(target->timing.h_total - 1) << 16) 236 | ((uint32)target->timing.h_display - 1)); 237 write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset, 238 ((uint32)(target->timing.h_sync_end - 1) << 16) 239 | ((uint32)target->timing.h_sync_start - 1)); 240 241 write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset, 242 ((uint32)(target->timing.v_total - 1) << 16) 243 | ((uint32)target->timing.v_display - 1)); 244 write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset, 245 ((uint32)(target->timing.v_total - 1) << 16) 246 | ((uint32)target->timing.v_display - 1)); 247 write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset, 248 ((uint32)(target->timing.v_sync_end - 1) << 16) 249 | ((uint32)target->timing.v_sync_start - 1)); 250 } 251 252 ConfigureScalePos(target); 253 254 if (fHasTranscoder && hardware) { 255 _ConfigureTranscoder(target); 256 } 257 } 258 259 260 void 261 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock, 262 uint32 extraFlags) 263 { 264 CALLED(); 265 266 addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0; 267 addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1; 268 addr_t pllControl = INTEL_DISPLAY_A_PLL; 269 addr_t pllMD = INTEL_DISPLAY_A_PLL_MD; 270 271 if (fPipeIndex == INTEL_PIPE_B) { 272 pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0; 273 pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1; 274 pllControl = INTEL_DISPLAY_B_PLL; 275 pllMD = INTEL_DISPLAY_B_PLL_MD; 276 } 277 278 // Disable DPLL first 279 write32(pllControl, read32(pllControl) & ~DISPLAY_PLL_ENABLED); 280 spin(150); 281 282 float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 283 284 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { 285 float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p; 286 uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f)); 287 write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8)); 288 } 289 290 // XXX: For now we assume no LVDS downclocking and program the same divisor 291 // value to both divisor 0 (standard) and 1 (reduced divisor) 292 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 293 write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 294 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 295 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 296 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 297 write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 298 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 299 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 300 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 301 } else { 302 write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 303 & DISPLAY_PLL_N_DIVISOR_MASK) 304 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 305 & DISPLAY_PLL_M1_DIVISOR_MASK) 306 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 307 & DISPLAY_PLL_M2_DIVISOR_MASK)); 308 write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 309 & DISPLAY_PLL_N_DIVISOR_MASK) 310 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 311 & DISPLAY_PLL_M1_DIVISOR_MASK) 312 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 313 & DISPLAY_PLL_M2_DIVISOR_MASK)); 314 } 315 316 //note: bit DISPLAY_PLL_NO_VGA_CONTROL does not exist on IvyBridge and should be left 317 // zero there. It does not influence it though. 318 uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags; 319 320 if (gInfo->shared_info->device_type.Generation() >= 3) { 321 // p1 divisor << 1 , 1-8 322 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 323 pll |= ((1 << (divisors.p1 - 1)) 324 << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT) 325 & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK; 326 } else { 327 pll |= ((1 << (divisors.p1 - 1)) 328 << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 329 & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 330 // pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 331 // & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 332 } 333 334 // Also configure the FP0 divisor on SandyBridge 335 if (gInfo->shared_info->device_type.Generation() == 6) { 336 pll |= ((1 << (divisors.p1 - 1)) 337 << DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_SHIFT) 338 & DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_MASK; 339 } 340 341 if (divisors.p2 == 5 || divisors.p2 == 7) 342 pll |= DISPLAY_PLL_DIVIDE_HIGH; 343 344 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) 345 pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT; 346 } else { 347 if (divisors.p2 != 5 && divisors.p2 != 7) 348 pll |= DISPLAY_PLL_DIVIDE_4X; 349 350 pll |= DISPLAY_PLL_2X_CLOCK; 351 352 // TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK?? 353 if (divisors.p1 > 2) { 354 pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 355 & DISPLAY_PLL_POST1_DIVISOR_MASK; 356 } else 357 pll |= DISPLAY_PLL_POST1_DIVIDE_2; 358 } 359 360 // Configure PLL while -keeping- it disabled 361 //note: on older chipsets DISPLAY_PLL_NO_VGA_CONTROL probably enables the PLL and locks regs; 362 // on newer chipsets DISPLAY_PLL_ENABLED does this. 363 write32(pllControl, pll & ~DISPLAY_PLL_ENABLED & ~DISPLAY_PLL_NO_VGA_CONTROL); 364 read32(pllControl); 365 spin(150); 366 367 // enable pre-configured PLL (locks PLL settings directly blocking changes in this write even) 368 write32(pllControl, pll); 369 read32(pllControl); 370 371 // Allow the PLL to warm up. 372 spin(150); 373 374 if (gInfo->shared_info->device_type.Generation() >= 6) { 375 // SandyBridge has 3 transcoders, but only 2 PLLs. So there is a new 376 // register which routes the PLL output to the transcoder that we need 377 // to configure 378 uint32 pllSel = read32(SNB_DPLL_SEL); 379 TRACE("Old PLL selection: 0x%" B_PRIx32 "\n", pllSel); 380 uint32 shift = 0; 381 uint32 pllIndex = 0; 382 383 // FIXME we assume that pipe A is used with transcoder A, and pipe B 384 // with transcoder B, that may not always be the case 385 if (fPipeIndex == INTEL_PIPE_A) { 386 shift = 0; 387 pllIndex = 0; 388 TRACE("Route PLL A to transcoder A\n"); 389 } else if (fPipeIndex == INTEL_PIPE_B) { 390 shift = 4; 391 pllIndex = 1; 392 TRACE("Route PLL B to transcoder B\n"); 393 } else { 394 ERROR("Attempting to configure PLL for unhandled pipe"); 395 return; 396 } 397 398 // Mask out the previous PLL configuration for this transcoder 399 pllSel &= ~(0xF << shift); 400 401 // Set up the new configuration for this transcoder and enable it 402 pllSel |= (8 | pllIndex) << shift; 403 404 TRACE("New PLL selection: 0x%" B_PRIx32 "\n", pllSel); 405 write32(SNB_DPLL_SEL, pllSel); 406 } 407 } 408 409 void 410 Pipe::Enable(bool enable) 411 { 412 CALLED(); 413 414 addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; 415 addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset; 416 417 // Planes always have to operate on an enabled pipe 418 419 if (enable) { 420 write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED); 421 wait_for_vblank(); 422 write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED); 423 424 //Enable default display main watermarks 425 if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) { 426 if (fPipeOffset == 0) 427 write32(INTEL_DISPLAY_A_PIPE_WATERMARK, 0x0783818); 428 else 429 write32(INTEL_DISPLAY_B_PIPE_WATERMARK, 0x0783818); 430 } 431 } else { 432 write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED); 433 wait_for_vblank(); 434 //Sandy+: when link training is to be done re-enable this line but otherwise don't touch! 435 //GMA(Q45): must disable PIPE or DPLL programming fails. 436 if (gInfo->shared_info->device_type.Generation() <= 5) { 437 write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED); 438 } 439 } 440 441 // flush the eventually cached PCI bus writes 442 read32(INTEL_DISPLAY_A_BASE); 443 } 444