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 uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 105 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; 122 } 123 } 124 125 // TODO: CxSR downclocking? 126 127 // TODO: Interlaced modes 128 pipeControl |= INTEL_PIPE_PROGRESSIVE; 129 130 write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl); 131 read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); 132 } 133 134 135 void 136 Pipe::_ConfigureTranscoder(display_mode* target) 137 { 138 CALLED(); 139 140 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset); 141 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)); 152 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)); 162 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 170 } 171 172 173 void 174 Pipe::ConfigureTimings(display_mode* target) 175 { 176 CALLED(); 177 178 TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset); 179 180 if (target == NULL) { 181 ERROR("%s: Invalid display mode!\n", __func__); 182 return; 183 } 184 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) 189 { 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)); 200 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)); 210 } 211 212 // XXX: Is it ok to do these on non-digital? 213 214 write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0); 215 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)); 223 224 write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset, 225 ((uint32)(target->timing.v_display - 1) << 16) 226 | ((uint32)target->timing.h_display - 1)); 227 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); 232 233 if (fHasTranscoder) 234 _ConfigureTranscoder(target); 235 } 236 237 238 void 239 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock, 240 uint32 extraFlags) 241 { 242 CALLED(); 243 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; 248 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; 254 } 255 256 float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 257 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)); 262 } 263 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)); 288 } 289 290 uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags; 291 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; 304 } 305 306 if (divisors.p2 == 5 || divisors.p2 == 7) 307 pll |= DISPLAY_PLL_DIVIDE_HIGH; 308 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; 314 315 pll |= DISPLAY_PLL_2X_CLOCK; 316 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; 323 } 324 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); 332 } 333 334 335 void 336 Pipe::Enable(bool enable) 337 { 338 CALLED(); 339 340 addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; 341 addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset; 342 343 // Planes always have to operate on an enabled pipe 344 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); 353 } 354 355 read32(INTEL_DISPLAY_A_BASE); 356 // flush the eventually cached PCI bus writes 357 } 358