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 } 79 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); 83 } 84 85 86 Pipe::~Pipe() 87 { 88 } 89 90 91 bool 92 Pipe::IsEnabled() 93 { 94 CALLED(); 95 96 return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset) 97 & INTEL_PIPE_ENABLED) != 0; 98 } 99 100 101 void 102 Pipe::Configure(display_mode* mode) 103 { 104 #if 0 105 // FIXME the previous values are never masked out from the 106 // register, so we just OR things together and hope to fall on a working 107 // mode. Better do nothing at all for now. 108 uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 109 110 // TODO: Haswell+ dithering changes. 111 if (gInfo->shared_info->device_type.Generation() >= 4) { 112 pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP); 113 // FIXME this makes no sense, if only because B_CMAP8, B_RGB24 and 114 // B_RGB32 have the same color precision (8bit per component). 115 // Also because the color mode is a property of the hardware 116 // (depends on which LVDS panel is used, typically), not the video mode. 117 switch (mode->space) { 118 case B_CMAP8: 119 case B_RGB15_LITTLE: 120 case B_RGB16_LITTLE: 121 pipeControl |= INTEL_PIPE_6BPC; 122 break; 123 case B_RGB24_LITTLE: 124 pipeControl |= INTEL_PIPE_8BPC; 125 break; 126 case B_RGB32_LITTLE: 127 default: 128 pipeControl |= INTEL_PIPE_10BPC; 129 break; 130 } 131 } 132 133 // TODO: CxSR downclocking? 134 135 // TODO: Interlaced modes 136 pipeControl |= INTEL_PIPE_PROGRESSIVE; 137 138 write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl); 139 read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 140 #endif 141 142 if (gInfo->shared_info->device_type.Generation() >= 6) { 143 // According to SandyBridge modesetting sequence, pipe must be enabled 144 // before PLL are configured. 145 addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; 146 write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED); 147 } 148 } 149 150 151 void 152 Pipe::_ConfigureTranscoder(display_mode* target) 153 { 154 CALLED(); 155 156 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset); 157 158 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) 159 write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset, 160 ((uint32)(target->timing.h_total - 1) << 16) 161 | ((uint32)target->timing.h_display - 1)); 162 write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset, 163 ((uint32)(target->timing.h_total - 1) << 16) 164 | ((uint32)target->timing.h_display - 1)); 165 write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset, 166 ((uint32)(target->timing.h_sync_end - 1) << 16) 167 | ((uint32)target->timing.h_sync_start - 1)); 168 169 write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset, 170 ((uint32)(target->timing.v_total - 1) << 16) 171 | ((uint32)target->timing.v_display - 1)); 172 write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset, 173 ((uint32)(target->timing.v_total - 1) << 16) 174 | ((uint32)target->timing.v_display - 1)); 175 write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset, 176 ((uint32)(target->timing.v_sync_end - 1) << 16) 177 | ((uint32)target->timing.v_sync_start - 1)); 178 179 #if 0 180 // XXX: Is it ok to do these on non-digital? 181 write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0); 182 write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset, 183 ((uint32)(target->virtual_width - 1) << 16) 184 | ((uint32)target->virtual_height - 1)); 185 #endif 186 } 187 188 189 void 190 Pipe::ConfigureTimings(display_mode* target, bool hardware) 191 { 192 CALLED(); 193 194 TRACE("%s(%d): fPipeOffset: 0x%" B_PRIx32"\n", __func__, hardware, 195 fPipeOffset); 196 197 if (target == NULL) { 198 ERROR("%s: Invalid display mode!\n", __func__); 199 return; 200 } 201 202 /* If using the transcoder, leave the display at its native resolution, 203 * and configure only the transcoder (panel fitting will match them 204 * together). */ 205 if (!fHasTranscoder || hardware) 206 { 207 // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) 208 write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset, 209 ((uint32)(target->timing.h_total - 1) << 16) 210 | ((uint32)target->timing.h_display - 1)); 211 write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset, 212 ((uint32)(target->timing.h_total - 1) << 16) 213 | ((uint32)target->timing.h_display - 1)); 214 write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset, 215 ((uint32)(target->timing.h_sync_end - 1) << 16) 216 | ((uint32)target->timing.h_sync_start - 1)); 217 218 write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset, 219 ((uint32)(target->timing.v_total - 1) << 16) 220 | ((uint32)target->timing.v_display - 1)); 221 write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset, 222 ((uint32)(target->timing.v_total - 1) << 16) 223 | ((uint32)target->timing.v_display - 1)); 224 write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset, 225 ((uint32)(target->timing.v_sync_end - 1) << 16) 226 | ((uint32)target->timing.v_sync_start - 1)); 227 } 228 229 if (gInfo->shared_info->device_type.Generation() < 6) { 230 // FIXME check on which generations this register exists 231 // (it appears it would be available only for cursor planes, not 232 // display planes) 233 // Since we set the plane to be the same size as the display, we can 234 // just show it starting at top-left. 235 write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0); 236 } 237 238 // The only thing that really matters: set the image size and let the 239 // panel fitter or the transcoder worry about the rest 240 write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset, 241 ((uint32)(target->virtual_width - 1) << 16) 242 | ((uint32)target->virtual_height - 1)); 243 244 // Set the plane size as well while we're at it (this is independant, we 245 // could have a larger plane and scroll through it). 246 if (gInfo->shared_info->device_type.Generation() <= 4) { 247 // This is "reserved" on G35 and GMA965, but needed on 945 (for which 248 // there is no public documentation), and I assume earlier devices as 249 // well. Note that the height and width are swapped when compared to 250 // the other registers. 251 write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset, 252 ((uint32)(target->virtual_height - 1) << 16) 253 | ((uint32)target->virtual_width - 1)); 254 } 255 256 if (fHasTranscoder && hardware) { 257 _ConfigureTranscoder(target); 258 } 259 } 260 261 262 void 263 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock, 264 uint32 extraFlags) 265 { 266 CALLED(); 267 268 addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0; 269 addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1; 270 addr_t pllControl = INTEL_DISPLAY_A_PLL; 271 addr_t pllMD = INTEL_DISPLAY_A_PLL_MD; 272 273 if (fPipeIndex == INTEL_PIPE_B) { 274 pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0; 275 pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1; 276 pllControl = INTEL_DISPLAY_B_PLL; 277 pllMD = INTEL_DISPLAY_B_PLL_MD; 278 } 279 280 float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 281 282 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { 283 float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p; 284 uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f)); 285 write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8)); 286 } 287 288 // XXX: For now we assume no LVDS downclocking and program the same divisor 289 // value to both divisor 0 (standard) and 1 (reduced divisor) 290 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 291 write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 292 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 293 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 294 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 295 write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 296 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 297 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 298 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 299 } else { 300 write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 301 & DISPLAY_PLL_N_DIVISOR_MASK) 302 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 303 & DISPLAY_PLL_M1_DIVISOR_MASK) 304 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 305 & DISPLAY_PLL_M2_DIVISOR_MASK)); 306 write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 307 & DISPLAY_PLL_N_DIVISOR_MASK) 308 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 309 & DISPLAY_PLL_M1_DIVISOR_MASK) 310 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 311 & DISPLAY_PLL_M2_DIVISOR_MASK)); 312 } 313 314 uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags; 315 316 if (gInfo->shared_info->device_type.Generation() >= 3) { 317 // p1 divisor << 1 , 1-8 318 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 319 pll |= ((1 << (divisors.p1 - 1)) 320 << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT) 321 & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK; 322 } else { 323 pll |= ((1 << (divisors.p1 - 1)) 324 << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 325 & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 326 // pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 327 // & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 328 } 329 330 // Also configure the FP0 divisor on SandyBridge 331 if (gInfo->shared_info->device_type.Generation() == 6) { 332 pll |= ((1 << (divisors.p1 - 1)) 333 << DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_SHIFT) 334 & DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_MASK; 335 } 336 337 if (divisors.p2 == 5 || divisors.p2 == 7) 338 pll |= DISPLAY_PLL_DIVIDE_HIGH; 339 340 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) 341 pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT; 342 } else { 343 if (divisors.p2 != 5 && divisors.p2 != 7) 344 pll |= DISPLAY_PLL_DIVIDE_4X; 345 346 pll |= DISPLAY_PLL_2X_CLOCK; 347 348 // TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK?? 349 if (divisors.p1 > 2) { 350 pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 351 & DISPLAY_PLL_POST1_DIVISOR_MASK; 352 } else 353 pll |= DISPLAY_PLL_POST1_DIVIDE_2; 354 } 355 356 write32(pllControl, pll & ~DISPLAY_PLL_NO_VGA_CONTROL); 357 // FIXME what is this doing? Why put the PLL back under VGA_CONTROL 358 // here? 359 read32(pllControl); 360 spin(150); 361 362 // Configure and enable the PLL 363 write32(pllControl, pll); 364 read32(pllControl); 365 366 // Allow the PLL to warm up. 367 spin(150); 368 369 if (gInfo->shared_info->device_type.Generation() >= 6) { 370 // SandyBridge has 3 transcoders, but only 2 PLLs. So there is a new 371 // register which routes the PLL output to the transcoder that we need 372 // to configure 373 uint32 pllSel = read32(SNB_DPLL_SEL); 374 TRACE("Old PLL selection: %x\n", pllSel); 375 uint32 shift = 0; 376 uint32 pllIndex = 0; 377 378 // FIXME we assume that pipe A is used with transcoder A, and pipe B 379 // with transcoder B, that may not always be the case 380 if (fPipeIndex == INTEL_PIPE_A) { 381 shift = 0; 382 pllIndex = 0; 383 TRACE("Route PLL A to transcoder A\n"); 384 } else if (fPipeIndex == INTEL_PIPE_B) { 385 shift = 4; 386 pllIndex = 1; 387 TRACE("Route PLL B to transcoder B\n"); 388 } else { 389 ERROR("Attempting to configure PLL for unhandled pipe"); 390 return; 391 } 392 393 // Mask out the previous PLL configuration for this transcoder 394 pllSel &= ~(0xF << shift); 395 396 // Set up the new configuration for this transcoder and enable it 397 pllSel |= (8 | pllIndex) << shift; 398 399 TRACE("New PLL selection: %x\n", pllSel); 400 write32(SNB_DPLL_SEL, pllSel); 401 } 402 } 403 404 405 void 406 Pipe::Enable(bool enable) 407 { 408 CALLED(); 409 410 addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; 411 addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset; 412 413 // Planes always have to operate on an enabled pipe 414 415 if (enable) { 416 write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED); 417 wait_for_vblank(); 418 write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED); 419 } else { 420 write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED); 421 wait_for_vblank(); 422 write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED); 423 } 424 425 read32(INTEL_DISPLAY_A_BASE); 426 // flush the eventually cached PCI bus writes 427 } 428