intel_extreme: skylake/DDI improvements. no resolution changing possible yet (missing DPLL code yet)

This commit is contained in:
Rudolf Cornelissen
2021-11-14 23:16:44 +00:00
parent a82616528d
commit 0eb2bf0e66
6 changed files with 287 additions and 41 deletions
@@ -307,7 +307,8 @@ enum pipe_index {
INTEL_PIPE_ANY,
INTEL_PIPE_A,
INTEL_PIPE_B,
INTEL_PIPE_C
INTEL_PIPE_C,
INTEL_PIPE_D
};
//----------------- ioctl() interface ----------------
@@ -543,6 +544,7 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_OFFSET 0x1000
// Note: on Skylake below registers are part of the transcoder
#define INTEL_DISPLAY_A_HTOTAL (0x0000 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DISPLAY_A_HBLANK (0x0004 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DISPLAY_A_HSYNC (0x0008 | REGS_NORTH_PIPE_AND_PORT)
@@ -569,6 +571,16 @@ struct intel_free_graphics_memory {
#define INTEL_PIPE_A_LINK_N (0x0064 | REGS_NORTH_PLANE_CONTROL)
#define INTEL_PIPE_B_LINK_N (0x1064 | REGS_NORTH_PLANE_CONTROL)
//DDI port link
#define INTEL_DDI_PIPE_A_DATA_M (0x0030 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_B_DATA_M (0x1030 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_A_DATA_N (0x0034 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_B_DATA_N (0x1034 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_A_LINK_M (0x0040 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_B_LINK_M (0x1040 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_A_LINK_N (0x0044 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DDI_PIPE_B_LINK_N (0x1044 | REGS_NORTH_PIPE_AND_PORT)
// on PCH we also have to set the transcoder
#define INTEL_TRANSCODER_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT)
@@ -661,6 +673,19 @@ struct intel_free_graphics_memory {
#define PIPE_DDI_PORT_C 2
#define PIPE_DDI_PORT_D 3
#define PIPE_DDI_PORT_E 4
#define PIPE_DDI_MODESEL_SHIFT 24
#define PIPE_DDI_MODESEL_MODE(x) ((x) << PIPE_DDI_MODESEL_SHIFT)
#define PIPE_DDI_MODESEL_MASK (7 << PIPE_DDI_MODESEL_SHIFT)
#define PIPE_DDI_MODE_HDMI 0
#define PIPE_DDI_MODE_DVI 1
#define PIPE_DDI_MODE_DP_SST 2
#define PIPE_DDI_MODE_DP_MST 3
#define PIPE_DDI_DP_WIDTH_SHIFT 1
#define PIPE_DDI_DP_WIDTH_SEL(x) ((x) << PIPE_DDI_DP_WIDTH_SHIFT)
#define PIPE_DDI_DP_WIDTH_MASK (7 << PIPE_DDI_DP_WIDTH_SHIFT)
#define PIPE_DDI_DP_WIDTH_1 0
#define PIPE_DDI_DP_WIDTH_2 1
#define PIPE_DDI_DP_WIDTH_4 2
// DP_A always @ 6xxxx, DP_B-DP_D move with PCH
#define INTEL_DISPLAY_PORT_A (0x4000 | REGS_NORTH_PIPE_AND_PORT)
@@ -992,6 +1017,12 @@ struct intel_free_graphics_memory {
#define PCH_TRANS_CONF_B 0x1008
#define PCH_TRANS_CONF_C 0x2008
// Transcoder - skylake DDI
#define DDI_SKL_TRANS_CONF_A (0x0008 | REGS_NORTH_PLANE_CONTROL)
#define DDI_SKL_TRANS_CONF_B (0x1008 | REGS_NORTH_PLANE_CONTROL)
#define DDI_SKL_TRANS_CONF_C (0x2008 | REGS_NORTH_PLANE_CONTROL)
#define DDI_SKL_TRANS_CONF_EDP (0xf008 | REGS_NORTH_PLANE_CONTROL)
#define TRANS_ENABLE (1 << 31)
#define TRANS_ENABLED (1 << 30)
@@ -42,6 +42,15 @@ PanelFitter::PanelFitter(pipe_index pipeIndex)
if (pipeIndex == INTEL_PIPE_C) {
fRegisterBase += 2 * PCH_PANEL_FITTER_PIPE_OFFSET;
}
TRACE("%s: requested fitter #%d\n", __func__, (int)pipeIndex);
uint32 fitCtl = read32(fRegisterBase + PCH_PANEL_FITTER_CONTROL);
if (fitCtl & PANEL_FITTER_ENABLED) {
TRACE("%s: this fitter is connected to pipe #%" B_PRIx32 "\n", __func__,
((fitCtl & PANEL_FITTER_PIPE_MASK) >> 29) + 1);
} else {
TRACE("%s: this fitter is not setup by the BIOS\n", __func__);
}
}
+77 -34
View File
@@ -69,9 +69,25 @@ Pipe::Pipe(pipe_index pipeIndex)
fPlaneOffset(0)
{
if (pipeIndex == INTEL_PIPE_B) {
fPipeOffset = INTEL_DISPLAY_OFFSET;
fPlaneOffset = INTEL_PLANE_OFFSET;
}
switch (pipeIndex) {
case INTEL_PIPE_B:
TRACE("Pipe B.\n");
fPipeOffset = 0x1000;
break;
case INTEL_PIPE_C:
TRACE("Pipe C.\n");
fPipeOffset = 0x2000;
break;
case INTEL_PIPE_D:
TRACE("Pipe D.\n");
fPipeOffset = 0xf000;
break;
default:
TRACE("Pipe A.\n");
break;
}
// IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
// Haswell: Only VGA behind FDI (on northbridge)
@@ -81,17 +97,18 @@ Pipe::Pipe(pipe_index pipeIndex)
TRACE("%s: Pipe %s routed through FDI\n", __func__,
(pipeIndex == INTEL_PIPE_A) ? "A" : "B");
fHasTranscoder = true;
// Program FDILink if PCH
fFDILink = new(std::nothrow) FDILink(pipeIndex);
// Program gen5(+) style panelfitter as well
}
if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
// DDI also has transcoders
fHasTranscoder = true;
// Program gen5(+) style panelfitter as well (DDI has this as well..)
fPanelFitter = new(std::nothrow) PanelFitter(pipeIndex);
}
TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
" Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A)
? "A" : "B", fPipeOffset, fPlaneOffset);
TRACE("Pipe Base: 0x%" B_PRIxADDR " Plane Base: 0x% " B_PRIxADDR "\n",
fPipeOffset, fPlaneOffset);
}
@@ -147,34 +164,58 @@ Pipe::_ConfigureTranscoder(display_mode* target)
TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
((uint32)(target->timing.h_sync_end - 1) << 16)
| ((uint32)target->timing.h_sync_start - 1));
if (gInfo->shared_info->device_type.Generation() < 9) {
// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
((uint32)(target->timing.h_sync_end - 1) << 16)
| ((uint32)target->timing.h_sync_start - 1));
write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
((uint32)(target->timing.v_sync_end - 1) << 16)
| ((uint32)target->timing.v_sync_start - 1));
#if 0
// XXX: Is it ok to do these on non-digital?
write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->timing.h_display - 1) << 16)
write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
#endif
write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
((uint32)(target->timing.v_sync_end - 1) << 16)
| ((uint32)target->timing.v_sync_start - 1));
#if 0
// XXX: Is it ok to do these on non-digital?
write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->timing.h_display - 1) << 16)
| ((uint32)target->timing.v_display - 1));
#endif
} else {
//on Skylake timing is already done in ConfigureTimings()
TRACE("%s: trans conf reg: 0x%" B_PRIx32"\n", __func__,
read32(DDI_SKL_TRANS_CONF_A + fPipeOffset));
TRACE("%s: trans DDI func ctl reg: 0x%" B_PRIx32"\n", __func__,
read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset));
switch ((read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset) & PIPE_DDI_MODESEL_MASK)
>> PIPE_DDI_MODESEL_SHIFT) {
case PIPE_DDI_MODE_DVI:
TRACE("%s: Transcoder uses DVI mode\n", __func__);
break;
case PIPE_DDI_MODE_DP_SST:
TRACE("%s: Transcoder uses DP SST mode\n", __func__);
break;
case PIPE_DDI_MODE_DP_MST:
TRACE("%s: Transcoder uses DP MST mode\n", __func__);
break;
default:
TRACE("%s: Transcoder uses HDMI mode\n", __func__);
break;
}
}
}
@@ -207,7 +248,8 @@ Pipe::ConfigureScalePos(display_mode* target)
// Set the plane size as well while we're at it (this is independant, we
// could have a larger plane and scroll through it).
if (gInfo->shared_info->device_type.Generation() <= 4) {
if ((gInfo->shared_info->device_type.Generation() <= 4)
|| gInfo->shared_info->device_type.HasDDI()) {
// This is "reserved" on G35 and GMA965, but needed on 945 (for which
// there is no public documentation), and I assume earlier devices as
// well.
@@ -240,6 +282,7 @@ Pipe::ConfigureTimings(display_mode* target, bool hardware)
if (!fHasTranscoder || hardware)
{
// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
// Note: on Skylake below registers are part of the transcoder
write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
+165 -4
View File
@@ -126,8 +126,24 @@ Port::SetPipe(Pipe* pipe)
return B_ERROR;
}
TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe %s\n", __func__,
PortName(), portRegister, (pipe->Index() == INTEL_PIPE_A) ? "A" : "B");
switch (pipe->Index()) {
case INTEL_PIPE_B:
TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe B\n", __func__,
PortName(), portRegister);
break;
case INTEL_PIPE_C:
TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe C\n", __func__,
PortName(), portRegister);
break;
case INTEL_PIPE_D:
TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe D\n", __func__,
PortName(), portRegister);
break;
default:
TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe A\n", __func__,
PortName(), portRegister);
break;
}
uint32 portState = read32(portRegister);
@@ -257,7 +273,38 @@ Port::PipePreference()
}
if (gInfo->shared_info->device_type.HasDDI()) {
//fixme implement detection via PIPE_DDI_FUNC_CTL_x scan..
// scan all our pipes to find the one connected to the current port
uint32 pipeState = 0;
for (uint32 pipeCnt = 0; pipeCnt < 4; pipeCnt++) {
switch (pipeCnt) {
case 0:
pipeState = read32(PIPE_DDI_FUNC_CTL_A);
break;
case 1:
pipeState = read32(PIPE_DDI_FUNC_CTL_B);
break;
case 2:
pipeState = read32(PIPE_DDI_FUNC_CTL_C);
break;
default:
pipeState = read32(PIPE_DDI_FUNC_CTL_EDP);
break;
}
if ((((pipeState & PIPE_DDI_SELECT_MASK) >> PIPE_DDI_SELECT_SHIFT) + 1)
== (uint32)PortIndex()) {
switch (pipeCnt) {
case 0:
return INTEL_PIPE_A;
case 1:
return INTEL_PIPE_B;
case 2:
return INTEL_PIPE_C;
default:
return INTEL_PIPE_D;
}
}
}
}
return INTEL_PIPE_ANY;
@@ -1283,11 +1330,14 @@ DigitalDisplayInterface::Power(bool enabled)
fPipe->Enable(enabled);
//nogo currently.. (kills output forever)
#if 0
addr_t portRegister = _PortRegister();
uint32 state = read32(portRegister);
write32(portRegister,
enabled ? (state | DDI_BUF_CTL_ENABLE) : (state & ~DDI_BUF_CTL_ENABLE));
read32(portRegister);
#endif
return B_OK;
}
@@ -1334,9 +1384,119 @@ DigitalDisplayInterface::IsConnected()
TRACE("%s: %s Maximum Lanes: %" B_PRId8 "\n", __func__,
PortName(), fMaxLanes);
return HasEDID();
// fetch EDID but determine 'in use' later (below) so we also catch screens that fail EDID
HasEDID();
// scan all our pipes to find the one connected to the current port and check it's enabled
uint32 pipeState = 0;
for (uint32 pipeCnt = 0; pipeCnt < 4; pipeCnt++) {
switch (pipeCnt) {
case 0:
pipeState = read32(PIPE_DDI_FUNC_CTL_A);
break;
case 1:
pipeState = read32(PIPE_DDI_FUNC_CTL_B);
break;
case 2:
pipeState = read32(PIPE_DDI_FUNC_CTL_C);
break;
default:
pipeState = read32(PIPE_DDI_FUNC_CTL_EDP);
break;
}
if (((((pipeState & PIPE_DDI_SELECT_MASK) >> PIPE_DDI_SELECT_SHIFT) + 1) == (uint32)PortIndex())
&& (pipeState & PIPE_DDI_FUNC_CTL_ENABLE)) {
TRACE("%s: Connected\n", __func__);
return true;
}
}
TRACE("%s: Not connected\n", __func__);
return false;
}
status_t
DigitalDisplayInterface::_SetPortLinkGen8(display_mode* target)
{
// Khz / 10. ( each output octet encoded as 10 bits.
//uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10; //=270000 khz
//fixme: always so?
uint32 linkBandwidth = 270000; //khz
uint32 fPipeOffset = 0;
switch (fPipe->Index()) {
case INTEL_PIPE_B:
fPipeOffset = 0x1000;
break;
case INTEL_PIPE_C:
fPipeOffset = 0x2000;
break;
case INTEL_PIPE_D:
fPipeOffset = 0xf000;
break;
default:
break;
}
TRACE("%s: DDI M1 data before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_DATA_M + fPipeOffset));
TRACE("%s: DDI N1 data before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_DATA_N + fPipeOffset));
TRACE("%s: DDI M1 link before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_LINK_M + fPipeOffset));
TRACE("%s: DDI N1 link before: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_LINK_N + fPipeOffset));
uint32 bitsPerPixel = 24; //fixme: always so?
uint32 lanes = 4;
// Only DP modes supports less than 4 lanes: read current config
uint32 pipeFunc = read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset);
if (((pipeFunc & PIPE_DDI_MODESEL_MASK) >> PIPE_DDI_MODESEL_SHIFT) >= PIPE_DDI_MODE_DP_SST) {
lanes = 1 << ((pipeFunc & PIPE_DDI_DP_WIDTH_MASK) >> PIPE_DDI_DP_WIDTH_SHIFT);
TRACE("%s: DDI in DP mode with %" B_PRIx32 " lanes in use\n", __func__, lanes);
} else {
TRACE("%s: DDI in non-DP mode with %" B_PRIx32 " lanes 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 = 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(INTEL_DDI_PIPE_A_DATA_M + fPipeOffset, ret_m | FDI_PIPE_MN_TU_SIZE_MASK);
write32(INTEL_DDI_PIPE_A_DATA_N + 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 = target->timing.pixel_clock * ret_n / linkspeed;
while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
ret_m >>= 1;
ret_n >>= 1;
}
write32(INTEL_DDI_PIPE_A_LINK_M + fPipeOffset, ret_m);
//Writing Link N triggers all four registers to be activated also (on next VBlank)
write32(INTEL_DDI_PIPE_A_LINK_N + fPipeOffset, ret_n);
TRACE("%s: DDI M1 data after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_DATA_M + fPipeOffset));
TRACE("%s: DDI N1 data after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_DATA_N + fPipeOffset));
TRACE("%s: DDI M1 link after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_LINK_M + fPipeOffset));
TRACE("%s: DDI N1 link after: 0x%" B_PRIx32 "\n", __func__, read32(INTEL_DDI_PIPE_A_LINK_N + fPipeOffset));
return B_OK;
}
status_t
DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode)
@@ -1379,6 +1539,7 @@ DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode)
// Program target display mode
fPipe->ConfigureTimings(target);
_SetPortLinkGen8(target);
// Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(display_mode));
@@ -226,6 +226,8 @@ virtual addr_t _DDCRegister();
virtual addr_t _PortRegister();
private:
uint8 fMaxLanes;
status_t _SetPortLinkGen8(display_mode* target);
};
@@ -289,7 +289,7 @@ probe_ports()
// Digital Display Interface (for DP, HDMI and DVI)
if (gInfo->shared_info->device_type.HasDDI()) {
for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
for (int i = INTEL_PORT_B; i <= INTEL_PORT_E; i++) {
TRACE("Probing DDI %d\n", i);
Port* ddiPort
@@ -431,7 +431,7 @@ assign_pipes()
pipe_index preference = gInfo->ports[i]->PipePreference();
if (preference != INTEL_PIPE_ANY) {
int index = (preference == INTEL_PIPE_B) ? 1 : 0;
int index = (int)preference - 1;
if (assigned[index]) {
TRACE("Pipe %d is already assigned, it will drive multiple "
"displays\n", index);