intel_extreme: Add initial work for DDI ports

This commit is contained in:
Alexander von Gluck IV 2016-03-15 18:12:28 -05:00
parent 3d1bd895ad
commit ca95e9dad9
4 changed files with 165 additions and 10 deletions

View File

@ -162,6 +162,12 @@ struct DeviceType {
return InFamily(INTEL_FAMILY_SER5); return InFamily(INTEL_FAMILY_SER5);
} }
bool HasDDI() const
{
// Intel Digital Display Interface
return InGroup(INTEL_GROUP_HAS) || (Generation() >= 8);
}
int Generation() const int Generation() const
{ {
if (InFamily(INTEL_FAMILY_7xx)) if (InFamily(INTEL_FAMILY_7xx))
@ -542,6 +548,18 @@ struct intel_free_graphics_memory {
#define GEN4_HDMI_PORT_C (0x1160 | REGS_SOUTH_TRANSCODER_PORT) #define GEN4_HDMI_PORT_C (0x1160 | REGS_SOUTH_TRANSCODER_PORT)
#define CHV_HDMI_PORT_D (0x116C | REGS_SOUTH_TRANSCODER_PORT) #define CHV_HDMI_PORT_D (0x116C | REGS_SOUTH_TRANSCODER_PORT)
// DDI Buffer Control (This replaces DP on Haswell+)
#define DDI_BUF_CTL_A (0x4000 | REGS_NORTH_PIPE_AND_PORT)
#define DDI_BUF_CTL_B (0x4100 | REGS_NORTH_PIPE_AND_PORT)
#define DDI_BUF_CTL_ENABLE (1 << 31)
#define DDI_BUF_TRANS_SELECT(n) ((n) << 24)
#define DDI_BUF_EMP_MASK (0xf << 24)
#define DDI_BUF_PORT_REVERSAL (1 << 16)
#define DDI_BUF_IS_IDLE (1 << 7)
#define DDI_A_4_LANES (1 << 4)
#define DDI_PORT_WIDTH(width) (((width) - 1) << 1)
#define DDI_INIT_DISPLAY_DETECTED (1 << 0)
// DP_A always @ 6xxxx, DP_B-DP_D move with PCH // DP_A always @ 6xxxx, DP_B-DP_D move with PCH
#define INTEL_DISPLAY_PORT_A (0x4000 | REGS_NORTH_PIPE_AND_PORT) #define INTEL_DISPLAY_PORT_A (0x4000 | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DISPLAY_PORT_B (0x4100 | REGS_SOUTH_TRANSCODER_PORT) #define INTEL_DISPLAY_PORT_B (0x4100 | REGS_SOUTH_TRANSCODER_PORT)

View File

@ -984,3 +984,100 @@ EmbeddedDisplayPort::IsConnected()
// No EDID? The modesetting code falls back to VBIOS panel_mode // No EDID? The modesetting code falls back to VBIOS panel_mode
return true; return true;
} }
// #pragma mark - Digital Display Port
DigitalDisplayInterface::DigitalDisplayInterface(port_index index,
const char* baseName)
:
Port(index, baseName)
{
// As of Haswell, Intel decided to change eDP ports to a "DDI" bus...
// on a dare because the hardware engineers were drunk one night.
}
addr_t
DigitalDisplayInterface::_PortRegister()
{
switch (PortIndex()) {
case INTEL_PORT_A:
return DDI_BUF_CTL_A;
case INTEL_PORT_B:
return DDI_BUF_CTL_B;
default:
return 0;
}
return 0;
}
addr_t
DigitalDisplayInterface::_DDCRegister()
{
// TODO: No idea, does DDI have DDC?
return 0;
}
bool
DigitalDisplayInterface::IsConnected()
{
addr_t portRegister = _PortRegister();
TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(),
portRegister);
if (portRegister == 0)
return false;
if ((read32(portRegister) & DDI_INIT_DISPLAY_DETECTED) == 0) {
TRACE("%s: %s link not detected\n", __func__, PortName());
return false;
}
HasEDID();
return true;
}
status_t
DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode)
{
TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width,
target->virtual_height);
if (fPipe == NULL) {
ERROR("%s: Setting display mode without assigned pipe!\n", __func__);
return B_ERROR;
}
// Train FDI if it exists
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(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;
// 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);
// Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(display_mode));
return B_OK;
}

View File

@ -199,4 +199,25 @@ virtual uint32 Type() const
virtual bool IsConnected(); virtual bool IsConnected();
}; };
class DigitalDisplayInterface : public Port {
public:
DigitalDisplayInterface(
port_index index = INTEL_PORT_A,
const char* baseName = "Digital Display Interface");
virtual uint32 Type() const
{ return INTEL_PORT_TYPE_DVI; }
virtual bool IsConnected();
virtual status_t SetDisplayMode(display_mode* mode,
uint32 colorMode);
protected:
virtual addr_t _DDCRegister();
virtual addr_t _PortRegister();
};
#endif // INTEL_PORTS_H #endif // INTEL_PORTS_H

View File

@ -306,9 +306,37 @@ probe_ports()
delete displayPort; delete displayPort;
} }
// Digital Display Interface
if (gInfo->shared_info->device_type.HasDDI()) {
for (int i = INTEL_PORT_A; i <= INTEL_PORT_B; i++) {
Port* ddiPort
= new(std::nothrow) DigitalDisplayInterface((port_index)i);
if (ddiPort == NULL)
return B_NO_MEMORY;
if (ddiPort->IsConnected())
gInfo->ports[gInfo->port_count++] = ddiPort;
else
delete ddiPort;
}
}
// Ensure DP_A isn't already taken (or DDI)
if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) {
// also always try eDP, it'll also just fail if not applicable
Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort();
if (eDPPort == NULL)
return B_NO_MEMORY;
if (eDPPort->IsConnected())
gInfo->ports[gInfo->port_count++] = eDPPort;
else
delete eDPPort;
}
for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) { for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) { if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
// we overlap with a DisplayPort, this is not HDMI // Ensure port not already claimed by something like DDI
continue; continue;
} }
@ -346,15 +374,6 @@ probe_ports()
} else } else
delete lvdsPort; delete lvdsPort;
// also always try eDP, it'll also just fail if not applicable
Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort();
if (eDPPort == NULL)
return B_NO_MEMORY;
if (eDPPort->IsConnected())
gInfo->ports[gInfo->port_count++] = eDPPort;
else
delete eDPPort;
// then finally always try the analog port // then finally always try the analog port
Port* analogPort = new(std::nothrow) AnalogPort(); Port* analogPort = new(std::nothrow) AnalogPort();
if (analogPort == NULL) if (analogPort == NULL)