diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 524fc59937..724380a4ba 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -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) diff --git a/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp b/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp index 4d15f31905..7150b38755 100644 --- a/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp +++ b/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp @@ -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__); + } } diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.cpp b/src/add-ons/accelerants/intel_extreme/Pipes.cpp index 6d70e832f5..5b2af12245 100644 --- a/src/add-ons/accelerants/intel_extreme/Pipes.cpp +++ b/src/add-ons/accelerants/intel_extreme/Pipes.cpp @@ -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)); diff --git a/src/add-ons/accelerants/intel_extreme/Ports.cpp b/src/add-ons/accelerants/intel_extreme/Ports.cpp index 416b819fdb..e33c1691a6 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.cpp +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -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)); diff --git a/src/add-ons/accelerants/intel_extreme/Ports.h b/src/add-ons/accelerants/intel_extreme/Ports.h index d3ae0532eb..e69beaf6eb 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.h +++ b/src/add-ons/accelerants/intel_extreme/Ports.h @@ -226,6 +226,8 @@ virtual addr_t _DDCRegister(); virtual addr_t _PortRegister(); private: uint8 fMaxLanes; + + status_t _SetPortLinkGen8(display_mode* target); }; diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.cpp b/src/add-ons/accelerants/intel_extreme/accelerant.cpp index b1e889ffa5..d034a2de15 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.cpp +++ b/src/add-ons/accelerants/intel_extreme/accelerant.cpp @@ -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);