intel_extreme: haswell and skylake DDI EDID support added, modesetting not finished yet.

This commit is contained in:
Rudolf Cornelissen 2021-11-02 20:04:37 +00:00
parent 3457006a9a
commit 661732341f
4 changed files with 131 additions and 76 deletions

View File

@ -847,7 +847,7 @@ struct intel_free_graphics_memory {
#define SNB_DPLL_SEL (0x7000 | REGS_SOUTH_SHARED)
// i2c
// i2c bit banging interface
#define INTEL_I2C_IO_A (0x5010 | REGS_SOUTH_SHARED)
#define INTEL_I2C_IO_B (0x5014 | REGS_SOUTH_SHARED)
#define INTEL_I2C_IO_C (0x5018 | REGS_SOUTH_SHARED)
@ -856,6 +856,9 @@ struct intel_free_graphics_memory {
#define INTEL_I2C_IO_F (0x5024 | REGS_SOUTH_SHARED)
#define INTEL_I2C_IO_G (0x5028 | REGS_SOUTH_SHARED)
#define INTEL_I2C_IO_H (0x502c | REGS_SOUTH_SHARED)
// i2c hardware controller
#define INTEL_GMBUS0 (0x5100 | REGS_SOUTH_SHARED)
#define INTEL_GMBUS4 (0x5110 | REGS_SOUTH_SHARED)
#define I2C_CLOCK_DIRECTION_MASK (1 << 0)
#define I2C_CLOCK_DIRECTION_OUT (1 << 1)
@ -869,6 +872,15 @@ struct intel_free_graphics_memory {
#define I2C_DATA_VALUE_IN (1 << 12)
#define I2C_RESERVED ((1 << 13) | (1 << 5))
// gpu block clock gating disable bits
#define INTEL_DSPCLK_GATE_D (0x2020 | REGS_SOUTH_SHARED)
#define PCH_GMBUSUNIT_CLK_GATE_DIS (1UL << 31)
#define INTEL_GEN9_CLKGATE_DIS_4 (0x653c | REGS_NORTH_SHARED)
#define BXT_GMBUSUNIT_CLK_GATE_DIS (1 << 14)
// gpu power wells
#define INTEL_PWR_WELL_CTL_2 (0x5404 | REGS_NORTH_SHARED)
// TODO: on IronLake this is in the north shared block at 0x41000
#define INTEL_VGA_DISPLAY_CONTROL (0x1400 | REGS_NORTH_PLANE_CONTROL)
#define VGA_DISPLAY_DISABLED (1UL << 31)

View File

@ -76,7 +76,8 @@ Pipe::Pipe(pipe_index pipeIndex)
// IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
// Haswell: Only VGA behind FDI (on northbridge)
// SkyLake: FDI gone. No more northbridge video.
if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
if ((gInfo->shared_info->pch_info != INTEL_PCH_NONE) &&
(gInfo->shared_info->device_type.Generation() <= 8)) {
TRACE("%s: Pipe %s routed through FDI\n", __func__,
(pipeIndex == INTEL_PIPE_A) ? "A" : "B");

View File

@ -291,18 +291,22 @@ Port::_SetI2CSignals(void* cookie, int clock, int data)
value = read32(ioRegister) & I2C_RESERVED;
}
// if we send clk or data, we always send low logic level;
// if we want to send high level, we actually receive and let the
// external pullup resistors create the high level on the bus.
value |= I2C_DATA_VALUE_MASK; //sets data = 0, always latch
value |= I2C_CLOCK_VALUE_MASK; //sets clock = 0, always latch
if (data != 0)
value |= I2C_DATA_DIRECTION_MASK;
else {
value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT
| I2C_DATA_VALUE_MASK;
value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT;
}
if (clock != 0)
value |= I2C_CLOCK_DIRECTION_MASK;
else {
value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT
| I2C_CLOCK_VALUE_MASK;
value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT;
}
write32(ioRegister, value);
@ -1142,10 +1146,12 @@ DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode)
PanelFitter* fitter = fPipe->PFT();
if (fitter != NULL)
fitter->Enable(*target);
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(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);
}
pll_divisors divisors;
compute_pll_divisors(target, &divisors, false);
@ -1192,6 +1198,7 @@ EmbeddedDisplayPort::IsConnected()
TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(),
portRegister);
// fixme: Skylake and up use eDP for a seperate active VGA converter chip sometimes.
if (!gInfo->shared_info->device_type.IsMobile()) {
TRACE("%s: skipping eDP on non-mobile GPU\n", __func__);
return false;
@ -1249,7 +1256,17 @@ DigitalDisplayInterface::_PortRegister()
addr_t
DigitalDisplayInterface::_DDCRegister()
{
// TODO: No idea, does DDI have DDC?
switch (PortIndex()) {
case INTEL_PORT_B:
return INTEL_I2C_IO_E;
case INTEL_PORT_C:
return INTEL_I2C_IO_D;
case INTEL_PORT_D:
return INTEL_I2C_IO_F;
default:
return 0;
}
return 0;
}
@ -1257,6 +1274,10 @@ DigitalDisplayInterface::_DDCRegister()
status_t
DigitalDisplayInterface::Power(bool enabled)
{
if (fPipe == NULL) {
ERROR("%s: Setting power without assigned pipe!\n", __func__);
return B_ERROR;
}
TRACE("%s: %s DDI enabled: %s\n", __func__, PortName(),
enabled ? "true" : "false");
@ -1283,11 +1304,6 @@ DigitalDisplayInterface::IsConnected()
if (portRegister == 0)
return false;
if ((read32(portRegister) & DDI_INIT_DISPLAY_DETECTED) == 0) {
TRACE("%s: %s link not detected\n", __func__, PortName());
return false;
}
// Probe a little port info.
if ((read32(DDI_BUF_CTL_A) & DDI_A_4_LANES) != 0) {
switch (PortIndex()) {
@ -1318,7 +1334,6 @@ DigitalDisplayInterface::IsConnected()
TRACE("%s: %s Maximum Lanes: %" B_PRId8 "\n", __func__,
PortName(), fMaxLanes);
//DDI and also its EDID do not work yet, we fail on purpose as a workaround for now
return HasEDID();
}
@ -1339,11 +1354,14 @@ DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode)
PanelFitter* fitter = fPipe->PFT();
if (fitter != NULL)
fitter->Enable(target->timing);
// Skip FDI if we have a CPU connected display
if (PortIndex() != INTEL_PORT_A) {
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
// skip FDI if it doesn't exist
if (gInfo->shared_info->device_type.Generation() <= 8) {
// Skip FDI if we have a CPU connected display
if (PortIndex() != INTEL_PORT_A) {
FDILink* link = fPipe->FDI();
if (link != NULL)
link->Train(&target->timing);
}
}
pll_divisors divisors;

View File

@ -255,6 +255,21 @@ probe_ports()
bool foundDDI = false;
gInfo->port_count = 0;
#if 0
// make sure I2C hardware controller is off (we use bit-banging)
if (gInfo->shared_info->device_type.Generation() >= 5) {
write32(INTEL_DSPCLK_GATE_D,
read32(INTEL_DSPCLK_GATE_D) | PCH_GMBUSUNIT_CLK_GATE_DIS);
read32(INTEL_DSPCLK_GATE_D);
write32(INTEL_GEN9_CLKGATE_DIS_4,
read32(INTEL_GEN9_CLKGATE_DIS_4) | BXT_GMBUSUNIT_CLK_GATE_DIS);
read32(INTEL_GEN9_CLKGATE_DIS_4);
write32(INTEL_GMBUS0, 0); //reset, idle
write32(INTEL_GMBUS4, 0); //block interrupts
}
#endif
// Display Port
if (!gInfo->shared_info->device_type.HasDDI()) {
@ -272,9 +287,9 @@ probe_ports()
}
}
// Digital Display Interface
// Digital Display Interface (for DP, HDMI and DVI)
if (gInfo->shared_info->device_type.HasDDI()) {
for (int i = INTEL_PORT_A; i <= INTEL_PORT_E; i++) {
for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
TRACE("Probing DDI %d\n", i);
Port* ddiPort
@ -292,6 +307,7 @@ probe_ports()
}
// Ensure DP_A isn't already taken (or DDI)
// Please note that Skylake and up use eDP for a seperate active VGA converter chip.
TRACE("Probing eDP\n");
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
@ -304,64 +320,72 @@ probe_ports()
delete eDPPort;
}
for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
TRACE("Probing HDMI %d\n", i);
if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
// Ensure port not already claimed by something like DDI
TRACE("Port already claimed\n");
continue;
}
if (!gInfo->shared_info->device_type.HasDDI()) {
for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
TRACE("Probing HDMI %d\n", i);
if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
// Ensure port not already claimed by something like DDI
TRACE("Port already claimed\n");
continue;
}
Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
if (hdmiPort == NULL)
return B_NO_MEMORY;
if (hdmiPort->IsConnected())
gInfo->ports[gInfo->port_count++] = hdmiPort;
else
delete hdmiPort;
}
// always try the LVDS port, it'll simply fail if not applicable
TRACE("Probing LVDS\n");
Port* lvdsPort = new(std::nothrow) LVDSPort();
if (lvdsPort == NULL)
return B_NO_MEMORY;
if (lvdsPort->IsConnected()) {
foundLVDS = true;
gInfo->ports[gInfo->port_count++] = lvdsPort;
gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
} else
delete lvdsPort;
if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
TRACE("Probing DVI\n");
// there's neither DisplayPort nor HDMI so far, assume DVI B
for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C;
index = (port_index)(index + 1)) {
Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI");
if (dviPort == NULL)
Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
if (hdmiPort == NULL)
return B_NO_MEMORY;
if (dviPort->IsConnected()) {
gInfo->ports[gInfo->port_count++] = dviPort;
gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
} else
delete dviPort;
if (hdmiPort->IsConnected())
gInfo->ports[gInfo->port_count++] = hdmiPort;
else
delete hdmiPort;
}
}
// then finally always try the analog port
TRACE("Probing Analog\n");
Port* analogPort = new(std::nothrow) AnalogPort();
if (analogPort == NULL)
return B_NO_MEMORY;
if (analogPort->IsConnected()) {
gInfo->ports[gInfo->port_count++] = analogPort;
gInfo->head_mode |= HEAD_MODE_A_ANALOG;
} else
delete analogPort;
// always try the LVDS port when chipset supports it, it'll simply fail if not applicable
if (!gInfo->shared_info->device_type.HasDDI()) {
TRACE("Probing LVDS\n");
Port* lvdsPort = new(std::nothrow) LVDSPort();
if (lvdsPort == NULL)
return B_NO_MEMORY;
if (lvdsPort->IsConnected()) {
foundLVDS = true;
gInfo->ports[gInfo->port_count++] = lvdsPort;
gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
} else
delete lvdsPort;
}
if (!gInfo->shared_info->device_type.HasDDI()) {
if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
TRACE("Probing DVI\n");
// there's neither DisplayPort nor HDMI so far, assume DVI B
for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C;
index = (port_index)(index + 1)) {
Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI");
if (dviPort == NULL)
return B_NO_MEMORY;
if (dviPort->IsConnected()) {
gInfo->ports[gInfo->port_count++] = dviPort;
gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
} else
delete dviPort;
}
}
}
// then finally always try the analog port when chipsets supports it
if (gInfo->shared_info->device_type.Generation() <= 8) {
TRACE("Probing Analog\n");
Port* analogPort = new(std::nothrow) AnalogPort();
if (analogPort == NULL)
return B_NO_MEMORY;
if (analogPort->IsConnected()) {
gInfo->ports[gInfo->port_count++] = analogPort;
gInfo->head_mode |= HEAD_MODE_A_ANALOG;
} else
delete analogPort;
}
if (gInfo->port_count == 0)
return B_ERROR;