Intel_extreme: fixed hrev55115 regression and added FDI data/link M/N programming.

This commit is contained in:
Rudolf Cornelissen 2021-06-08 09:20:34 +00:00
parent 1a5fe94b7a
commit aca9888e37
4 changed files with 176 additions and 47 deletions

View File

@ -727,10 +727,13 @@ struct intel_free_graphics_memory {
#define INTEL_PIPE_DITHER_TYPE_ST2 (2 << 2)
#define INTEL_PIPE_DITHER_TYPE_TEMP (3 << 2)
#define INTEL_PIPE_DITHER_EN (1 << 4)
#define INTEL_PIPE_8BPC (0 << 5)
#define INTEL_PIPE_10BPC (1 << 5)
#define INTEL_PIPE_6BPC (2 << 5)
#define INTEL_PIPE_12BPC (3 << 5)
#define INTEL_PIPE_COLOR_SHIFT 5
#define INTEL_PIPE_BPC(x) ((x) << INTEL_PIPE_COLOR_SHIFT)
#define INTEL_PIPE_BPC_MASK (7 << INTEL_PIPE_COLOR_SHIFT)
#define INTEL_PIPE_8BPC 0
#define INTEL_PIPE_10BPC 1
#define INTEL_PIPE_6BPC 2
#define INTEL_PIPE_12BPC 3
#define INTEL_PIPE_PROGRESSIVE (0 << 21)
// cursors
@ -917,6 +920,10 @@ struct intel_free_graphics_memory {
#define FDI_RX_ENABLE (1 << 31)
#define FDI_RX_PLL_ENABLED (1 << 13)
#define FDI_RX_LINK_COLOR_SHIFT 16
#define FDI_RX_LINK_BPC(x) ((x) << FDI_RX_LINK_COLOR_SHIFT)
#define FDI_RX_LINK_BPC_MASK (7 << FDI_RX_LINK_COLOR_SHIFT)
// Transcoder - same base as FDI_RX
#define PCH_TRANS_CONF_A 0x0008
#define PCH_TRANS_CONF_B 0x1008

View File

@ -103,6 +103,13 @@ FDITransmitter::EnablePLL(uint32 lanes)
return;
}
value &= ~FDI_DP_PORT_WIDTH_MASK;
value |= FDI_DP_PORT_WIDTH(lanes);
//first update config, -then- enable PLL to be sure config is indeed updated
write32(targetRegister, value);
read32(targetRegister);
write32(targetRegister, value | FDI_TX_PLL_ENABLED);
read32(targetRegister);
spin(100); // warmup 10us + dmi delay 20us, be generous
@ -189,9 +196,17 @@ FDIReceiver::EnablePLL(uint32 lanes)
return;
}
value &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16));
//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
//currently using BIOS preconfigured setup
//value &= ~FDI_DP_PORT_WIDTH_MASK;
//value |= FDI_RX_LINK_BPC(INTEL_PIPE_8BPC);
value &= ~FDI_DP_PORT_WIDTH_MASK;
value |= FDI_DP_PORT_WIDTH(lanes);
//value |= (read32(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
//first update config, -then- enable PLL to be sure config is indeed updated
write32(targetRegister, value);
read32(targetRegister);
write32(targetRegister, value | FDI_RX_PLL_ENABLED);
read32(targetRegister);
@ -239,38 +254,132 @@ FDILink::Train(display_mode* target)
{
CALLED();
uint32 bitsPerPixel;
switch (target->space) {
case B_RGB32_LITTLE:
bitsPerPixel = 32;
status_t result = B_OK;
uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
uint32 bitsPerPixel = ((read32(rxControl) & FDI_RX_LINK_BPC_MASK) >> FDI_RX_LINK_COLOR_SHIFT);
switch (bitsPerPixel) {
case INTEL_PIPE_8BPC:
bitsPerPixel = 24;
break;
case B_RGB16_LITTLE:
bitsPerPixel = 16;
case INTEL_PIPE_10BPC:
bitsPerPixel = 30;
break;
case B_RGB15_LITTLE:
bitsPerPixel = 15;
case INTEL_PIPE_6BPC:
bitsPerPixel = 18;
break;
case INTEL_PIPE_12BPC:
bitsPerPixel = 36;
break;
case B_CMAP8:
default:
bitsPerPixel = 8;
break;
ERROR("%s: FDI illegal link colordepth set.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI Link Colordepth: %" B_PRIu32 "\n", __func__, bitsPerPixel);
// Khz / 10. ( each output octet encoded as 10 bits.
uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
//Reserving 5% bandwidth for possible spread spectrum clock use
uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20;
uint32 lanes = bps / (linkBandwidth * 8);
//use DIV_ROUND_UP:
uint32 lanes = (bps + (linkBandwidth * 8) - 1) / (linkBandwidth * 8);
//remove below line when link training is to be done
lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;
TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes);
//assuming we'll only use link A and B (not C)
if (lanes > 4) {
result = B_ERROR;
}
if (result != B_OK) {
ERROR("%s: FDI not enough lanes in hardware.\n", __func__);
return result;
}
TRACE("%s: FDI TX ctrl: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
#if 0
//when link training is to be done re-enable this code
//The order of handling things is important here..
write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
read32(rxControl);
write32(txControl, (read32(txControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
read32(txControl);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_PATTERN_MASK_CPT) | FDI_LINK_TRAIN_PATTERN_1_CPT);
} else {
write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
}
read32(rxControl);
spin(100);
// Disable FDI clocks
Receiver().SwitchClock(false);
Transmitter().DisablePLL();
Receiver().DisablePLL();
#endif
//Setup Data M/N
uint64 linkspeed = lanes * linkBandwidth * 8;
uint64 ret_n = 1;
while(ret_n < linkspeed) {
ret_n *= 2;
}
if (ret_n > 0x800000) {
ret_n = 0x800000;
}
uint64 ret_m = target->timing.pixel_clock * ret_n * bitsPerPixel / linkspeed;
while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
ret_m >>= 1;
ret_n >>= 1;
}
//Set TU size bits (to default, max) before link training so that error detection works
write32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_M1, ret_m | FDI_PIPE_MN_TU_SIZE_MASK);
write32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_N1, ret_n);
//Setup Link M/N
linkspeed = linkBandwidth;
ret_n = 1;
while(ret_n < linkspeed) {
ret_n *= 2;
}
if (ret_n > 0x800000) {
ret_n = 0x800000;
}
ret_m = target->timing.pixel_clock * ret_n / linkspeed;
while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
ret_m >>= 1;
ret_n >>= 1;
}
write32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_M1, ret_m);
//Writing Link N triggers all four registers to be activated also (on next VBlank)
write32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_N1, ret_n);
TRACE("%s: FDI data M1: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_M1));
TRACE("%s: FDI data N1: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_N1));
TRACE("%s: FDI link M1: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_M1));
TRACE("%s: FDI link N1: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_N1));
//Set receiving end TU size bits to match sending end's setting
write32(Receiver().Base() + PCH_FDI_RX_TRANS_UNIT_SIZE_1, FDI_RX_TRANS_UNIT_MASK);
write32(Receiver().Base() + PCH_FDI_RX_TRANS_UNIT_SIZE_2, FDI_RX_TRANS_UNIT_MASK);
#if 0
//when link training is to be done re-enable this code
// Enable FDI clocks
Receiver().EnablePLL(lanes);
Receiver().SwitchClock(true);
Transmitter().EnablePLL(lanes);
status_t result = B_ERROR;
// TODO: Only _AutoTrain on IVYB Stepping B or later
// otherwise, _ManualTrain
if (gInfo->shared_info->device_type.Generation() >= 7)
@ -281,6 +390,7 @@ FDILink::Train(display_mode* target)
result = _IlkTrain(lanes);
else
result = _NormalTrain(lanes);
#endif
if (result != B_OK) {
ERROR("%s: FDI training fault.\n", __func__);
@ -580,6 +690,7 @@ FDILink::_AutoTrain(uint32 lanes)
uint32 buffer = read32(txControl);
// Clear port width selection and set number of lanes
// fixme: does not belong in the train routines (?), (now) sits in FDI EnablePLL() routines
buffer &= ~(7 << 19);
buffer |= (lanes - 1) << 19;
@ -589,6 +700,9 @@ FDILink::_AutoTrain(uint32 lanes)
buffer &= ~FDI_LINK_TRAIN_NONE;
write32(txControl, buffer);
write32(Receiver().Base() + PCH_FDI_RX_MISC,
FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
bool trained = false;
for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam)
@ -599,10 +713,11 @@ FDILink::_AutoTrain(uint32 lanes)
buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
buffer |= gSnbBFDITrainParam[i];
write32(txControl, buffer | FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
read32(rxControl);
spin(5);
spin(50);//looks like datasheet specified 5uS is not enough..?
buffer = read32(txControl);
if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
@ -612,6 +727,7 @@ FDILink::_AutoTrain(uint32 lanes)
}
write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
read32(rxControl);
@ -628,11 +744,14 @@ FDILink::_AutoTrain(uint32 lanes)
return B_ERROR;
}
// Enable ecc on IVB
// Enable ecc on IVB (and disable test pattern at sending and receiving end)
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
write32(rxControl, read32(rxControl)
| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
read32(rxControl);
//enable normal pixels (kill testpattern)
write32(txControl, read32(txControl) | (0x3 << 8));
read32(txControl);
}
return B_OK;

View File

@ -107,8 +107,10 @@ Pipe::Configure(display_mode* mode)
//if (gInfo->shared_info->device_type.Generation() >= 4) {
// pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP);
//Link bit depth, which is currently hardcoded to 8-bits per color
pipeControl = (pipeControl & ~(0x7 << 5)) | INTEL_PIPE_8BPC;
//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
//currently using BIOS preconfigured setup
//pipeControl = (pipeControl & ~INTEL_PIPE_BPC_MASK) | INTEL_PIPE_BPC(INTEL_PIPE_8BPC);
// TODO: CxSR downclocking?
// TODO: Interlaced modes
@ -356,7 +358,7 @@ Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
// register which routes the PLL output to the transcoder that we need
// to configure
uint32 pllSel = read32(SNB_DPLL_SEL);
TRACE("Old PLL selection: %x\n", pllSel);
TRACE("Old PLL selection: 0x%" B_PRIx32 "\n", pllSel);
uint32 shift = 0;
uint32 pllIndex = 0;
@ -381,7 +383,7 @@ Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
// Set up the new configuration for this transcoder and enable it
pllSel |= (8 | pllIndex) << shift;
TRACE("New PLL selection: %x\n", pllSel);
TRACE("New PLL selection: 0x%" B_PRIx32 "\n", pllSel);
write32(SNB_DPLL_SEL, pllSel);
}
}

View File

@ -21,6 +21,7 @@
#include "accelerant_protos.h"
#include "FlexibleDisplayInterface.h"
#include "intel_extreme.h"
#include "PanelFitter.h"
#include <new>
@ -133,7 +134,7 @@ Port::SetPipe(Pipe* pipe)
// FIXME is the use of PORT_TRANS_* constants correct for Sandy Bridge /
// Cougar Point? Or is it only for Ivy Bridge / Panther point onwards?
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
portState &= ~PORT_TRANS_SEL_MASK;
portState &= ~PORT_TRANS_SEL_MASK; //fixme should be done sooner, not here!
if (pipe->Index() == INTEL_PIPE_A)
write32(portRegister, portState | PORT_TRANS_A_SEL_CPT);
else
@ -144,7 +145,6 @@ Port::SetPipe(Pipe* pipe)
else
write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B);
}
fPipe = pipe;
if (fPipe == NULL)
@ -306,6 +306,7 @@ AnalogPort::_PortRegister()
status_t
AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
{
CALLED();
TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width,
target->virtual_height);
@ -314,14 +315,13 @@ AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
return B_ERROR;
}
#if 0
// Disabled for now as our code doesn't work. Let's hope VESA/EFI has
// already set things up for us during boot.
// Train FDI if it exists
// Setup PanelFitter and Train FDI if it exists
FDILink* link = fPipe->FDI();
if (link != NULL)
if (link != NULL) {
// fixme insert fitter setup here
link->Train(target);
#endif
}
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
@ -500,14 +500,13 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
}
}
#if 0
// Disabled for now as our code doesn't work. Let's hope VESA/EFI has
// already set things up for us during boot.
// Train FDI if it exists
// Setup PanelFitter and Train FDI if it exists
FDILink* link = fPipe->FDI();
if (link != NULL)
if (link != NULL) {
// fixme insert fitter setup here
link->Train(target);
#endif
}
// For LVDS panels, we may need to set the timings according to the panel
// native video mode, and let the panel fitter do the scaling. But the
@ -727,6 +726,7 @@ DigitalPort::_PortRegister()
status_t
DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
{
CALLED();
TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width,
target->virtual_height);
@ -735,14 +735,13 @@ DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
return B_ERROR;
}
#if 0
// Disabled for now as our code doesn't work. Let's hope VESA/EFI has
// already set things up for us during boot.
// Train FDI if it exists
// Setup PanelFitter and Train FDI if it exists
FDILink* link = fPipe->FDI();
if (link != NULL)
if (link != NULL) {
// fixme insert fitter setup here
link->Train(target);
#endif
}
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
@ -934,6 +933,7 @@ DisplayPort::_PortRegister()
status_t
DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode)
{
CALLED();
TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width,
target->virtual_height);
@ -1095,6 +1095,7 @@ DigitalDisplayInterface::IsConnected()
status_t
DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode)
{
CALLED();
TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width,
target->virtual_height);