intel_extreme: More FDI training work

* IvyBridge or higher can auto-train.
* Linux doesn't use this feature, however
  manual FDI link training is *really*
  complex... lets try auto-training first.
This commit is contained in:
Alexander von Gluck IV
2015-11-19 17:49:51 -06:00
parent aa06863ccd
commit e6fefa6cbf
5 changed files with 213 additions and 12 deletions
@@ -230,6 +230,8 @@ struct intel_shared_info {
addr_t frame_buffer;
uint32 frame_buffer_offset;
uint32 fdi_link_frequency; // In Mhz
bool got_vbt;
bool single_head_locked;
@@ -721,11 +723,15 @@ struct intel_free_graphics_memory {
#define FDI_RX_ENABLE (1 << 31)
#define FDI_RX_PLL_ENABLED (1 << 13)
#define FDI_FS_ERRC_ENABLE (1 << 27)
#define FDI_FE_ERRC_ENABLE (1 << 26)
#define PCH_FDI_RX_TRANS_UNIT_SIZE_1 0x30
#define PCH_FDI_RX_TRANS_UNIT_SIZE_2 0x38
#define FDI_RX_TRANS_UNIT_SIZE(x) ((x - 1) << 25)
#define FDI_RX_TRANS_UNIT_MASK 0x7e000000
#define FDI_RX_ENHANCE_FRAME_ENABLE (1 << 6)
#define FDI_RX_CLOCK_MASK (1 << 4)
#define FDI_RX_CLOCK_RAW (0 << 4)
#define FDI_RX_CLOCK_PCD (1 << 4)
@@ -734,8 +740,55 @@ struct intel_free_graphics_memory {
#define PCH_FDI_TX_PIPE_OFFSET 0x01000
#define PCH_FDI_TX_CONTROL 0x100
#define FDI_TX_ENABLE (1 << 31)
#define FDI_TX_ENHANCE_FRAME_ENABLE (1 << 18)
#define FDI_TX_PLL_ENABLED (1 << 14)
#define FDI_PLL_BIOS_0 0x46000
#define FDI_PLL_FB_CLOCK_MASK 0xff
#define FDI_PLL_BIOS_1 0x46004
#define FDI_PLL_BIOS_2 0x46008
#define FDI_LINK_TRAIN_PATTERN_1 (0 << 28)
#define FDI_LINK_TRAIN_PATTERN_2 (1 << 28)
#define FDI_LINK_TRAIN_PATTERN_IDLE (2 << 28)
#define FDI_LINK_TRAIN_NONE (3 << 28)
#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0 << 25)
#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1 << 25)
#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2 << 25)
#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3 << 25)
#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0 << 22)
#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1 << 22)
#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2 << 22)
#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3 << 22)
#define FDI_AUTO_TRAINING (1 << 10)
#define FDI_AUTO_TRAIN_DONE (1 << 1)
// SNB A-stepping
#define FDI_LINK_TRAIN_400MV_0DB_SNB_A (0x38 << 22)
#define FDI_LINK_TRAIN_400MV_6DB_SNB_A (0x02 << 22)
#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01 << 22)
#define FDI_LINK_TRAIN_800MV_0DB_SNB_A (0x00 << 22)
// SNB B-stepping
#define FDI_LINK_TRAIN_400MV_0DB_SNB_B (0x00 << 22)
#define FDI_LINK_TRAIN_400MV_6DB_SNB_B (0x3a << 22)
#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39 << 22)
#define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38 << 22)
#define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f << 22)
#define FDI_LINK_TRAIN_PATTERN_1_CPT (0 << 8)
#define FDI_LINK_TRAIN_PATTERN_2_CPT (1 << 8)
#define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2 << 8)
#define FDI_LINK_TRAIN_NORMAL_CPT (3 << 8)
#define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3 << 8)
// IvyBridge changes it up because... they hate developers?
#define FDI_LINK_TRAIN_PATTERN_1_IVB (0 << 8)
#define FDI_LINK_TRAIN_PATTERN_2_IVB (1 << 8)
#define FDI_LINK_TRAIN_PATTERN_IDLE_IVB (2 << 8)
#define FDI_LINK_TRAIN_NONE_IVB (3 << 8)
// CPU Panel Fitters - These are for IronLake and up and are the CPU internal
// panel fitters.
#define PCH_PANEL_FITTER_BASE_REGISTER 0x68000
@@ -1,9 +1,10 @@
/*
* Copyright 2011, Haiku, Inc. All Rights Reserved.
* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz, [email protected]
* Alexander von Gluck IV, [email protected]
*/
@@ -216,6 +217,135 @@ FDILink::FDILink(pipe_index pipeIndex)
}
status_t
FDILink::Train(display_mode* target)
{
CALLED();
uint32 bitsPerPixel;
switch (target->space) {
case B_RGB32_LITTLE:
bitsPerPixel = 32;
break;
case B_RGB16_LITTLE:
bitsPerPixel = 16;
break;
case B_RGB15_LITTLE:
bitsPerPixel = 15;
break;
case B_CMAP8:
default:
bitsPerPixel = 8;
break;
}
// Khz / 10. ( each output octet encoded as 10 bits.
uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20;
uint32 lanes = bps / (linkBandwidth * 8);
TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes);
// Over IVB supports AutoTraining of FDI
if (gInfo->shared_info->device_type.Generation() >= 7)
return _AutoTrain(lanes);
//return _ManualTrain(lanes);
return B_ERROR;
}
status_t
FDILink::_ManualTrain(uint32 lanes)
{
CALLED();
// This all needs review
// Enable FDI clocks
Receiver().EnablePLL();
Receiver().SwitchClock(true);
Transmitter().EnablePLL();
ERROR("TODO: Generation 5 FDI Link Training\n");
// Enable pipes
Transmitter().Enable();
Receiver().Enable();
return B_OK;
}
status_t
FDILink::_AutoTrain(uint32 lanes)
{
CALLED();
uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
uint32 buffer = read32(txControl);
buffer &= ~(7 << 19);
buffer |= (lanes - 1) << 19;
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
else
buffer &= ~FDI_LINK_TRAIN_NONE;
write32(txControl, buffer);
static const int snb_b_fdi_train_param[] = {
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
FDI_LINK_TRAIN_800MV_0DB_SNB_B,
};
bool trained = false;
for (uint32 i = 0; i < (sizeof(snb_b_fdi_train_param)
/ sizeof(snb_b_fdi_train_param[0])); i++) {
for (int j = 0; j < 2; j++) {
buffer = read32(txControl);
buffer |= FDI_AUTO_TRAINING;
buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
buffer |= snb_b_fdi_train_param[i];
write32(txControl, buffer | FDI_TX_ENABLE);
write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
spin(5);
buffer = read32(txControl);
if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
TRACE("%s: FDI auto train complete!\n", __func__);
trained = true;
break;
}
write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
read32(rxControl);
spin(31);
}
}
if (!trained) {
ERROR("%s: FDI auto train failed!\n", __func__);
return B_ERROR;
}
// Enable ecc
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
write32(rxControl, read32(rxControl)
| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
read32(rxControl);
}
return B_OK;
}
FDILink::~FDILink()
{
}
@@ -1,9 +1,10 @@
/*
* Copyright 2011, Haiku, Inc. All Rights Reserved.
* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz, [email protected]
* Alexander von Gluck IV, [email protected]
*/
#ifndef INTEL_FDI_H
#define INTEL_FDI_H
@@ -24,6 +25,9 @@ public:
void EnablePLL();
void DisablePLL();
uint32 Base()
{ return fRegisterBase; };
private:
uint32 fRegisterBase;
};
@@ -43,6 +47,9 @@ public:
void SwitchClock(bool toPCDClock);
uint32 Base()
{ return fRegisterBase; };
protected:
uint32 fRegisterBase;
};
@@ -58,7 +65,12 @@ public:
FDIReceiver& Receiver()
{ return fReceiver; };
status_t Train(display_mode* target);
private:
status_t _ManualTrain(uint32 lanes);
status_t _AutoTrain(uint32 lanes);
FDITransmitter fTransmitter;
FDIReceiver fReceiver;
};
@@ -262,11 +262,8 @@ AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
// Train FDI if it exists
FDILink* link = fDisplayPipe->FDI();
if (link != NULL) {
link->Receiver().EnablePLL();
link->Receiver().SwitchClock(true);
link->Transmitter().EnablePLL();
}
if (link != NULL)
link->Train(target);
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
@@ -677,11 +674,8 @@ DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
// Train FDI if it exists
FDILink* link = fDisplayPipe->FDI();
if (link != NULL) {
link->Receiver().EnablePLL();
link->Receiver().SwitchClock(true);
link->Transmitter().EnablePLL();
}
if (link != NULL)
link->Train(target);
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
@@ -472,6 +472,18 @@ intel_extreme_init(intel_info &info)
init_interrupt_handler(info);
if (info.device_type.HasPlatformControlHub()) {
if (info.device_type.Generation() == 5) {
info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0)
& FDI_PLL_FB_CLOCK_MASK) + 2;
info.shared_info->fdi_link_frequency *= 100;
} else {
info.shared_info->fdi_link_frequency = 2700;
}
} else {
info.shared_info->fdi_link_frequency = 0;
}
TRACE("%s: completed successfully!\n", __func__);
return B_OK;
}