intel_extreme: sandy/ivybridge DP links to screens are now programmed to the actual mode if possible

This commit is contained in:
Rudolf Cornelissen 2022-01-26 23:42:14 +00:00
parent 49ddb1ffd4
commit 022986d510
3 changed files with 144 additions and 32 deletions

View File

@ -857,6 +857,12 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_PORT_C (0x4200 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DISPLAY_PORT_D (0x4300 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DISP_PORT_WIDTH_SHIFT 19
#define INTEL_DISP_PORT_WIDTH_MASK (7 << INTEL_DISP_PORT_WIDTH_SHIFT)
#define INTEL_DISP_PORT_WIDTH_1 0
#define INTEL_DISP_PORT_WIDTH_2 1
#define INTEL_DISP_PORT_WIDTH_4 2
#define INTEL_TRANSCODER_A_DP_CTL (0x0300 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_DP_CTL (0x1300 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_C_DP_CTL (0x2300 | REGS_SOUTH_TRANSCODER_PORT)

View File

@ -1173,6 +1173,117 @@ DisplayPort::_SetPortLinkGen4(const display_timing& timing)
}
status_t
DisplayPort::_SetPortLinkGen6(const display_timing& timing)
{
// Khz / 10. ( each output octet encoded as 10 bits.
//uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10; //=270000 khz
//fixme: eDP is fixed option 162 or 270Mc, other DPs go via DPLL programming to one of the same vals.
uint32 linkBandwidth = 270000; //khz
TRACE("%s: DP link reference clock is %gMhz\n", __func__, linkBandwidth / 1000.0f);
uint32 fPipeOffset = 0;
switch (fPipe->Index()) {
case INTEL_PIPE_B:
fPipeOffset = 0x1000;
break;
case INTEL_PIPE_C:
fPipeOffset = 0x2000;
break;
default:
break;
}
TRACE("%s: DP M1 data before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_DATA_M1 + fPipeOffset));
TRACE("%s: DP N1 data before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_DATA_N1 + fPipeOffset));
TRACE("%s: DP M1 link before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_LINK_M1 + fPipeOffset));
TRACE("%s: DP N1 link before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_LINK_N1 + fPipeOffset));
uint32 bitsPerPixel =
(read32(INTEL_TRANSCODER_A_DP_CTL + fPipeOffset) & INTEL_TRANS_DP_BPC_MASK) >> INTEL_TRANS_DP_COLOR_SHIFT;
switch (bitsPerPixel) {
case PIPE_DDI_8BPC:
bitsPerPixel = 24;
break;
case PIPE_DDI_10BPC:
bitsPerPixel = 30;
break;
case PIPE_DDI_6BPC:
bitsPerPixel = 18;
break;
case PIPE_DDI_12BPC:
bitsPerPixel = 36;
break;
default:
ERROR("%s: DP illegal link colordepth set.\n", __func__);
return B_ERROR;
}
TRACE("%s: DP link colordepth: %" B_PRIu32 "\n", __func__, bitsPerPixel);
uint32 lanes =
1 << ((read32(_PortRegister()) & INTEL_DISP_PORT_WIDTH_MASK) >> INTEL_DISP_PORT_WIDTH_SHIFT);
if (lanes > 4) {
ERROR("%s: DP illegal number of lanes set.\n", __func__);
return B_ERROR;
}
TRACE("%s: DP mode with %" B_PRIx32 " lane(s) in use\n", __func__, lanes);
//Reserving 5% bandwidth for possible spread spectrum clock use
uint32 bps = timing.pixel_clock * bitsPerPixel * 21 / 20;
//use DIV_ROUND_UP:
uint32 required_lanes = (bps + (linkBandwidth * 8) - 1) / (linkBandwidth * 8);
TRACE("%s: DP mode needs %" B_PRIx32 " lane(s) in use\n", __func__, required_lanes);
if (required_lanes > lanes) {
//Note that we *must* abort as otherwise the PIPE/DP-link hangs forever (without retraining!).
ERROR("%s: DP not enough lanes active for requested mode.\n", __func__);
return B_ERROR;
}
//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(INTEL_TRANSCODER_A_DATA_M1 + fPipeOffset, ret_m | INTEL_TRANSCODER_MN_TU_SIZE_MASK);
write32(INTEL_TRANSCODER_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(INTEL_TRANSCODER_A_LINK_M1 + fPipeOffset, ret_m);
//Writing Link N triggers all four registers to be activated also (on next VBlank)
write32(INTEL_TRANSCODER_A_LINK_N1 + fPipeOffset, ret_n);
TRACE("%s: DP M1 data after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_DATA_M1 + fPipeOffset));
TRACE("%s: DP N1 data after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_DATA_N1 + fPipeOffset));
TRACE("%s: DP M1 link after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_LINK_M1 + fPipeOffset));
TRACE("%s: DP N1 link after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_TRANSCODER_A_LINK_N1 + fPipeOffset));
return B_OK;
}
status_t
DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode)
{
@ -1190,37 +1301,31 @@ DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode)
fPipe->ConfigureTimings(target);
result = _SetPortLinkGen4(target->timing);
} else {
//fixme: doesn't work yet. For now just scale to native mode.
#if 0
// Setup PanelFitter and Train FDI if it exists
PanelFitter* fitter = fPipe->PFT();
if (fitter != NULL)
fitter->Enable(*target);
// skip FDI if it doesn't exist
if (gInfo->shared_info->device_type.Generation() <= 8) {
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(target);
result = _SetPortLinkGen6(target->timing);
if (result == B_OK) {
// Setup PanelFitter and Train FDI if it exists
PanelFitter* fitter = fPipe->PFT();
if (fitter != NULL)
fitter->Enable(target->timing);
// skip FDI if it doesn't exist or we don't use it (eDP)
if ((gInfo->shared_info->device_type.Generation() <= 8) && (PortIndex() != INTEL_PORT_A)) {
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
}
// Program general pipe config
fPipe->Configure(target);
// Pll programming is not needed for (e)DP..
// Program target display mode
fPipe->ConfigureTimings(target);
} else {
TRACE("%s: Setting display mode via fallback: using scaling!\n", __func__);
// Keep monitor at native mode and scale image to that
fPipe->ConfigureScalePos(target);
}
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
uint32 extraPLLFlags = 0;
if (gInfo->shared_info->device_type.Generation() >= 3)
extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL | DISPLAY_PLL_2X_CLOCK;
// Program general pipe config
fPipe->Configure(target);
// Program pipe PLL's
fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags);
// Program target display mode
fPipe->ConfigureTimings(target);
#endif
// Keep monitor at native mode and scale image to that
fPipe->ConfigureScalePos(target);
}
// Set fCurrentMode to our set display mode
@ -1533,9 +1638,9 @@ DigitalDisplayInterface::_SetPortLinkGen8(const display_timing& timing, uint32 p
if (((pipeFunc & PIPE_DDI_MODESEL_MASK) >> PIPE_DDI_MODESEL_SHIFT) >= PIPE_DDI_MODE_DP_SST) {
// On gen 9.5 IceLake 3x mode exists (DSI only), earlier models: reserved value.
lanes = ((pipeFunc & PIPE_DDI_DP_WIDTH_MASK) >> PIPE_DDI_DP_WIDTH_SHIFT) + 1;
TRACE("%s: DDI in DP mode with %" B_PRIx32 " lanes in use\n", __func__, lanes);
TRACE("%s: DDI in DP mode with %" B_PRIx32 " lane(s) in use\n", __func__, lanes);
} else {
TRACE("%s: DDI in non-DP mode with %" B_PRIx32 " lanes in use\n", __func__, lanes);
TRACE("%s: DDI in non-DP mode with %" B_PRIx32 " lane(s) in use\n", __func__, lanes);
}
//Setup Data M/N

View File

@ -182,6 +182,7 @@ virtual addr_t _PortRegister();
private:
status_t _SetPortLinkGen4(const display_timing& timing);
status_t _SetPortLinkGen6(const display_timing& timing);
};