intel_extreme: decoupled PIPE/eDP link programming from FDI train, fixed eDP pgm error.

This commit is contained in:
Rudolf Cornelissen
2022-02-01 23:48:48 +00:00
parent 41f6b7906d
commit ed9bb4dc76
6 changed files with 167 additions and 101 deletions
@@ -1248,31 +1248,31 @@ struct intel_free_graphics_memory {
#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2 << 22)
#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3 << 22)
//(FDI) PIPE M/N DATA AND LINK VALUES (refreshrate)
#define PCH_FDI_PIPE_A_DATA_M1 0x0030
#define PCH_FDI_PIPE_A_DATA_M2 0x0038
#define PCH_FDI_PIPE_B_DATA_M1 0x1030
#define PCH_FDI_PIPE_B_DATA_M2 0x1038
#define PCH_FDI_PIPE_C_DATA_M1 0x2030
#define PCH_FDI_PIPE_C_DATA_M2 0x2038
#define PCH_FDI_PIPE_A_DATA_N1 0x0034
#define PCH_FDI_PIPE_A_DATA_N2 0x003c
#define PCH_FDI_PIPE_B_DATA_N1 0x1034
#define PCH_FDI_PIPE_B_DATA_N2 0x103c
#define PCH_FDI_PIPE_C_DATA_N1 0x2034
#define PCH_FDI_PIPE_C_DATA_N2 0x203c
#define PCH_FDI_PIPE_A_LINK_M1 0x0040
#define PCH_FDI_PIPE_A_LINK_M2 0x0048
#define PCH_FDI_PIPE_B_LINK_M1 0x1040
#define PCH_FDI_PIPE_B_LINK_M2 0x1048
#define PCH_FDI_PIPE_C_LINK_M1 0x2040
#define PCH_FDI_PIPE_C_LINK_M2 0x2048
#define PCH_FDI_PIPE_A_LINK_N1 0x0044
#define PCH_FDI_PIPE_A_LINK_N2 0x004c
#define PCH_FDI_PIPE_B_LINK_N1 0x1044
#define PCH_FDI_PIPE_B_LINK_N2 0x104c
#define PCH_FDI_PIPE_C_LINK_N1 0x2044
#define PCH_FDI_PIPE_C_LINK_N2 0x204c
// FDI/PIPE M/N DATA AND LINK VALUES (refreshrate)
#define PCH_FDI_PIPE_A_DATA_M1 (0x0030 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_DATA_M2 (0x0038 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_DATA_M1 (0x1030 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_DATA_M2 (0x1038 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_DATA_M1 (0x2030 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_DATA_M2 (0x2038 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_DATA_N1 (0x0034 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_DATA_N2 (0x003c | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_DATA_N1 (0x1034 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_DATA_N2 (0x103c | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_DATA_N1 (0x2034 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_DATA_N2 (0x203c | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_LINK_M1 (0x0040 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_LINK_M2 (0x0048 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_LINK_M1 (0x1040 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_LINK_M2 (0x1048 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_LINK_M1 (0x2040 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_LINK_M2 (0x2048 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_LINK_N1 (0x0044 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_A_LINK_N2 (0x004c | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_LINK_N1 (0x1044 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_B_LINK_N2 (0x104c | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_LINK_N1 (0x2044 | REGS_NORTH_PIPE_AND_PORT)
#define PCH_FDI_PIPE_C_LINK_N2 (0x204c | REGS_NORTH_PIPE_AND_PORT)
#define FDI_PIPE_MN_TU_SIZE_MASK (0x3f << 25)
#define FDI_PIPE_MN_VALUE_MASK (0xffffff << 0)
@@ -250,60 +250,56 @@ FDILink::FDILink(pipe_index pipeIndex)
status_t
FDILink::Train(display_timing* target) // fixme: seperate FDI training and PIPE M/N programming..
FDILink::PreTrain(display_timing* target, uint32* linkBandwidth, uint32* lanes, uint32* bitsPerPixel)
{
CALLED();
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) {
*bitsPerPixel = ((read32(rxControl) & FDI_RX_LINK_BPC_MASK) >> FDI_RX_LINK_COLOR_SHIFT);
switch (*bitsPerPixel) {
case INTEL_PIPE_8BPC:
bitsPerPixel = 24;
*bitsPerPixel = 24;
break;
case INTEL_PIPE_10BPC:
bitsPerPixel = 30;
*bitsPerPixel = 30;
break;
case INTEL_PIPE_6BPC:
bitsPerPixel = 18;
*bitsPerPixel = 18;
break;
case INTEL_PIPE_12BPC:
bitsPerPixel = 36;
*bitsPerPixel = 36;
break;
default:
*bitsPerPixel = 0;
ERROR("%s: FDI illegal link colordepth set.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI Link %s:\n", __func__, (fPipeIndex == INTEL_PIPE_A) ? "A" : "B");
TRACE("%s: FDI Link Colordepth: %" B_PRIu32 "\n", __func__, bitsPerPixel);
TRACE("%s: FDI Link Colordepth: %" B_PRIu32 "\n", __func__, *bitsPerPixel);
// Khz / 10. ( each output octet encoded as 10 bits.
// note: if used for eDP (PORT_A) might be we should check reg. DP_CTL (0x64000), bit 16-17 (Ivy).
uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
*linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
//Reserving 5% bandwidth for possible spread spectrum clock use
uint32 bps = target->pixel_clock * bitsPerPixel * 21 / 20;
uint32 bps = target->pixel_clock * *bitsPerPixel * 21 / 20;
//use DIV_ROUND_UP:
uint32 lanes = (bps + (linkBandwidth * 8) - 1) / (linkBandwidth * 8);
*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;
*lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;
TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes);
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) {
if (*lanes > 4) {
ERROR("%s: FDI not enough lanes in hardware.\n", __func__);
return result;
return B_ERROR;
}
TRACE("%s: FDI TX ctrl: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
TRACE("%s: FDI TX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
#if 0
//when link training is to be done re-enable this code
@@ -330,51 +326,19 @@ FDILink::Train(display_timing* target) // fixme: seperate FDI training and PIPE
Receiver().DisablePLL();
#endif
TRACE("%s: FDI M1 data before: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_M1));
TRACE("%s: FDI N1 data before: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_N1));
TRACE("%s: FDI M1 link before: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_M1));
TRACE("%s: FDI N1 link before: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_N1));
return B_OK;
}
//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->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->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);
status_t
FDILink::Train(display_timing* target, uint32 lanes)
{
CALLED();
TRACE("%s: FDI M1 data after: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_M1));
TRACE("%s: FDI N1 data after: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_DATA_N1));
TRACE("%s: FDI M1 link after: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_M1));
TRACE("%s: FDI N1 link after: 0x%" B_PRIx32 "\n", __func__, read32(Transmitter().Base() + PCH_FDI_PIPE_A_LINK_N1));
status_t result = B_OK;
uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
//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);
@@ -399,9 +363,11 @@ FDILink::Train(display_timing* target) // fixme: seperate FDI training and PIPE
result = _NormalTrain(lanes);
#endif
if (result != B_OK) {
TRACE("%s: FDI TX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
if (result != B_OK)
ERROR("%s: FDI training fault.\n", __func__);
}
return result;
}
@@ -65,7 +65,12 @@ public:
FDIReceiver& Receiver()
{ return fReceiver; };
status_t Train(display_timing* target);
status_t PreTrain(
display_timing* target,
uint32* linkBandwidth,
uint32* lanes,
uint32* bitsPerPixel);
status_t Train(display_timing* target, uint32 lanes);
private:
status_t _NormalTrain(uint32 lanes);
@@ -219,6 +219,73 @@ Pipe::_ConfigureTranscoder(display_mode* target)
}
status_t
Pipe::SetFDILink(const display_timing& timing, uint32 linkBandwidth, uint32 lanes, uint32 bitsPerPixel)
{
TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
TRACE("%s: FDI/PIPE link reference clock is %gMhz\n", __func__, linkBandwidth / 1000.0f);
TRACE("%s: FDI/PIPE M1 data before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset));
TRACE("%s: FDI/PIPE N1 data before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset));
TRACE("%s: FDI/PIPE M1 link before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset));
TRACE("%s: FDI/PIPE N1 link before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset));
if ((bitsPerPixel < 18) || (bitsPerPixel > 36)) {
ERROR("%s: FDI/PIPE illegal colordepth set.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI/PIPE link colordepth: %" B_PRIu32 "\n", __func__, bitsPerPixel);
if (lanes > 4) {
ERROR("%s: FDI/PIPE illegal number of lanes set.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI/PIPE link with %" B_PRIx32 " lane(s) in use\n", __func__, lanes);
//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 = 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(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset, ret_m | FDI_PIPE_MN_TU_SIZE_MASK);
write32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset, 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 = timing.pixel_clock * ret_n / linkspeed;
while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
ret_m >>= 1;
ret_n >>= 1;
}
write32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset, ret_m);
//Writing Link N triggers all four registers to be activated also (on next VBlank)
write32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset, ret_n);
TRACE("%s: FDI/PIPE M1 data after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset));
TRACE("%s: FDI/PIPE N1 data after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset));
TRACE("%s: FDI/PIPE M1 link after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset));
TRACE("%s: FDI/PIPE N1 link after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset));
return B_OK;
}
void
Pipe::ConfigureScalePos(display_mode* target)
{
@@ -40,6 +40,11 @@ public:
void Disable();
void Configure(display_mode* mode);
status_t SetFDILink(
const display_timing& timing,
uint32 linkBandwidth,
uint32 lanes,
uint32 bitsPerPixel);
void ConfigureScalePos(display_mode* mode);
void ConfigureTimings(display_mode* mode,
bool hardware = true);
@@ -416,9 +416,14 @@ AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if (fitter != NULL)
fitter->Enable(target->timing);
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
if (link != NULL) {
uint32 lanes = 0;
uint32 linkBandwidth = 0;
uint32 bitsPerPixel = 0;
link->PreTrain(&target->timing, &linkBandwidth, &lanes, &bitsPerPixel);
fPipe->SetFDILink(target->timing, linkBandwidth, lanes, bitsPerPixel);
link->Train(&target->timing, lanes);
}
pll_divisors divisors;
compute_pll_divisors(&target->timing, &divisors, false);
@@ -656,8 +661,14 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if (fitter != NULL)
fitter->Enable(hardwareTarget);
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&hardwareTarget);
if (link != NULL) {
uint32 lanes = 0;
uint32 linkBandwidth = 0;
uint32 bitsPerPixel = 0;
link->PreTrain(&hardwareTarget, &linkBandwidth, &lanes, &bitsPerPixel);
fPipe->SetFDILink(hardwareTarget, linkBandwidth, lanes, bitsPerPixel);
link->Train(&hardwareTarget, lanes);
}
pll_divisors divisors;
compute_pll_divisors(&hardwareTarget, &divisors, true);
@@ -853,8 +864,14 @@ DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if (fitter != NULL)
fitter->Enable(target->timing);
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
if (link != NULL) {
uint32 lanes = 0;
uint32 linkBandwidth = 0;
uint32 bitsPerPixel = 0;
link->PreTrain(&target->timing, &linkBandwidth, &lanes, &bitsPerPixel);
fPipe->SetFDILink(target->timing, linkBandwidth, lanes, bitsPerPixel);
link->Train(&target->timing, lanes);
}
pll_divisors divisors;
compute_pll_divisors(&target->timing, &divisors, false);
@@ -1371,8 +1388,14 @@ DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode)
fitter->Enable(hardwareTarget);
// we should skip FDI if PORT_A, but need pipe M/N programming (is eDP link), so call always for now
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
if (link != NULL) {
uint32 lanes = 0;
uint32 linkBandwidth = 0;
uint32 bitsPerPixel = 0;
link->PreTrain(&hardwareTarget, &linkBandwidth, &lanes, &bitsPerPixel);
fPipe->SetFDILink(hardwareTarget, linkBandwidth, lanes, bitsPerPixel);
link->Train(&hardwareTarget, lanes);
}
// Program general pipe config
fPipe->Configure(target);