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