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 10 11 #include "FlexibleDisplayInterface.h" 12 13 #include <stdlib.h> 14 #include <string.h> 15 #include <Debug.h> 16 #include <KernelExport.h> 17 18 #include "accelerant.h" 19 #include "intel_extreme.h" 20 21 22 #undef TRACE 23 #define TRACE_FDI 24 #ifdef TRACE_FDI 25 # define TRACE(x...) _sPrintf("intel_extreme: " x) 26 #else 27 # define TRACE(x...) 28 #endif 29 30 #define ERROR(x...) _sPrintf("intel_extreme: " x) 31 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 32 33 34 // #pragma mark - FDITransmitter 35 36 37 FDITransmitter::FDITransmitter(pipe_index pipeIndex) 38 : 39 fRegisterBase(PCH_FDI_TX_BASE_REGISTER) 40 { 41 if (pipeIndex == INTEL_PIPE_B) 42 fRegisterBase += PCH_FDI_TX_PIPE_OFFSET * 1; 43 } 44 45 46 FDITransmitter::~FDITransmitter() 47 { 48 } 49 50 51 void 52 FDITransmitter::Enable() 53 { 54 CALLED(); 55 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 56 uint32 value = read32(targetRegister); 57 58 write32(targetRegister, value | FDI_TX_ENABLE); 59 read32(targetRegister); 60 spin(150); 61 } 62 63 64 void 65 FDITransmitter::Disable() 66 { 67 CALLED(); 68 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 69 uint32 value = read32(targetRegister); 70 71 write32(targetRegister, value & ~FDI_TX_ENABLE); 72 read32(targetRegister); 73 spin(150); 74 } 75 76 77 bool 78 FDITransmitter::IsPLLEnabled() 79 { 80 CALLED(); 81 return (read32(fRegisterBase + PCH_FDI_TX_CONTROL) & FDI_TX_PLL_ENABLED) 82 != 0; 83 } 84 85 86 void 87 FDITransmitter::EnablePLL() 88 { 89 CALLED(); 90 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 91 uint32 value = read32(targetRegister); 92 if ((value & FDI_TX_PLL_ENABLED) != 0) { 93 // already enabled, possibly IronLake where it always is 94 return; 95 } 96 97 write32(targetRegister, value | FDI_TX_PLL_ENABLED); 98 read32(targetRegister); 99 spin(100); // warmup 10us + dmi delay 20us, be generous 100 } 101 102 103 void 104 FDITransmitter::DisablePLL() 105 { 106 CALLED(); 107 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) { 108 // on IronLake the FDI PLL is alaways enabled, so no point in trying... 109 return; 110 } 111 112 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 113 write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED); 114 read32(targetRegister); 115 spin(100); 116 } 117 118 119 // #pragma mark - FDIReceiver 120 121 122 FDIReceiver::FDIReceiver(pipe_index pipeIndex) 123 : 124 fRegisterBase(PCH_FDI_RX_BASE_REGISTER) 125 { 126 if (pipeIndex == INTEL_PIPE_B) 127 fRegisterBase += PCH_FDI_RX_PIPE_OFFSET * 1; 128 } 129 130 131 FDIReceiver::~FDIReceiver() 132 { 133 } 134 135 136 void 137 FDIReceiver::Enable() 138 { 139 CALLED(); 140 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 141 uint32 value = read32(targetRegister); 142 143 write32(targetRegister, value | FDI_RX_ENABLE); 144 read32(targetRegister); 145 spin(150); 146 } 147 148 149 void 150 FDIReceiver::Disable() 151 { 152 CALLED(); 153 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 154 uint32 value = read32(targetRegister); 155 156 write32(targetRegister, value & ~FDI_RX_ENABLE); 157 read32(targetRegister); 158 spin(150); 159 } 160 161 162 bool 163 FDIReceiver::IsPLLEnabled() 164 { 165 CALLED(); 166 return (read32(fRegisterBase + PCH_FDI_RX_CONTROL) & FDI_RX_PLL_ENABLED) 167 != 0; 168 } 169 170 171 void 172 FDIReceiver::EnablePLL() 173 { 174 CALLED(); 175 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 176 uint32 value = read32(targetRegister); 177 if ((value & FDI_RX_PLL_ENABLED) != 0) 178 return; 179 180 write32(targetRegister, value | FDI_RX_PLL_ENABLED); 181 read32(targetRegister); 182 spin(200); // warmup 10us + dmi delay 20us, be generous 183 } 184 185 186 void 187 FDIReceiver::DisablePLL() 188 { 189 CALLED(); 190 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 191 write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED); 192 read32(targetRegister); 193 spin(100); 194 } 195 196 197 void 198 FDIReceiver::SwitchClock(bool toPCDClock) 199 { 200 CALLED(); 201 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 202 write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK) 203 | (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW)); 204 read32(targetRegister); 205 spin(200); 206 } 207 208 209 // #pragma mark - FDILink 210 211 212 FDILink::FDILink(pipe_index pipeIndex) 213 : 214 fTransmitter(pipeIndex), 215 fReceiver(pipeIndex) 216 { 217 } 218 219 220 status_t 221 FDILink::Train(display_mode* target) 222 { 223 CALLED(); 224 225 uint32 bitsPerPixel; 226 switch (target->space) { 227 case B_RGB32_LITTLE: 228 bitsPerPixel = 32; 229 break; 230 case B_RGB16_LITTLE: 231 bitsPerPixel = 16; 232 break; 233 case B_RGB15_LITTLE: 234 bitsPerPixel = 15; 235 break; 236 case B_CMAP8: 237 default: 238 bitsPerPixel = 8; 239 break; 240 } 241 242 // Khz / 10. ( each output octet encoded as 10 bits. 243 uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10; 244 uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20; 245 246 uint32 lanes = bps / (linkBandwidth * 8); 247 248 TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes); 249 250 // Enable FDI clocks 251 Receiver().EnablePLL(); 252 Receiver().SwitchClock(true); 253 Transmitter().EnablePLL(); 254 255 status_t result = B_ERROR; 256 257 // Over IVB supports AutoTraining of FDI 258 if (gInfo->shared_info->device_type.Generation() >= 7) { 259 result = _AutoTrain(lanes); 260 if (result != B_OK) { 261 ERROR("%s: FDI auto-training fault. Attempting manual train.\n", 262 __func__); 263 return _ManualTrain(lanes); 264 } 265 return B_OK; 266 } 267 return _ManualTrain(lanes); 268 } 269 270 271 status_t 272 FDILink::_ManualTrain(uint32 lanes) 273 { 274 CALLED(); 275 276 // This needs completed 277 ERROR("TODO: Manual FDI Link Training\n"); 278 279 // Enable pipes 280 Transmitter().Enable(); 281 Receiver().Enable(); 282 return B_OK; 283 } 284 285 286 status_t 287 FDILink::_AutoTrain(uint32 lanes) 288 { 289 CALLED(); 290 uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 291 uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 292 293 uint32 buffer = read32(txControl); 294 295 // Clear port width selection and set number of lanes 296 buffer &= ~(7 << 19); 297 buffer |= (lanes - 1) << 19; 298 299 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) 300 buffer &= ~FDI_LINK_TRAIN_NONE_IVB; 301 else 302 buffer &= ~FDI_LINK_TRAIN_NONE; 303 write32(txControl, buffer); 304 305 static const int snb_b_fdi_train_param[] = { 306 FDI_LINK_TRAIN_400MV_0DB_SNB_B, 307 FDI_LINK_TRAIN_400MV_6DB_SNB_B, 308 FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, 309 FDI_LINK_TRAIN_800MV_0DB_SNB_B, 310 }; 311 312 bool trained = false; 313 314 for (uint32 i = 0; i < (sizeof(snb_b_fdi_train_param) 315 / sizeof(snb_b_fdi_train_param[0])); i++) { 316 for (int j = 0; j < 2; j++) { 317 buffer = read32(txControl); 318 buffer |= FDI_AUTO_TRAINING; 319 buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 320 buffer |= snb_b_fdi_train_param[i]; 321 write32(txControl, buffer | FDI_TX_ENABLE); 322 323 write32(rxControl, read32(rxControl) | FDI_RX_ENABLE); 324 325 spin(5); 326 327 buffer = read32(txControl); 328 if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) { 329 TRACE("%s: FDI auto train complete!\n", __func__); 330 trained = true; 331 break; 332 } 333 334 write32(txControl, read32(txControl) & ~FDI_TX_ENABLE); 335 write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE); 336 read32(rxControl); 337 338 spin(31); 339 } 340 341 // If Trained, we fall out of autotraining 342 if (trained) 343 break; 344 } 345 346 if (!trained) { 347 ERROR("%s: FDI auto train failed!\n", __func__); 348 return B_ERROR; 349 } 350 351 // Enable ecc on IVB 352 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) { 353 write32(rxControl, read32(rxControl) 354 | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); 355 read32(rxControl); 356 } 357 358 return B_OK; 359 } 360 361 362 FDILink::~FDILink() 363 { 364 } 365