intel_extreme: Improve PCH detection

* Detect PCH model based on ISA bridge and save
  into shared info for later use.
* On CougarPoint PCH systems, assign pipes via
  special CPT registers
* Drop HasPlatformControlHub as PCH should be
  based on more than just generation.
This commit is contained in:
Alexander von Gluck IV
2016-07-10 21:02:01 -05:00
parent 8199f204da
commit 92e254d047
8 changed files with 121 additions and 26 deletions
@@ -83,6 +83,17 @@
#define INTEL_MODEL_SKYM (INTEL_GROUP_SKY | INTEL_TYPE_MOBILE) #define INTEL_MODEL_SKYM (INTEL_GROUP_SKY | INTEL_TYPE_MOBILE)
#define INTEL_MODEL_SKYS (INTEL_GROUP_SKY | INTEL_TYPE_SERVER) #define INTEL_MODEL_SKYS (INTEL_GROUP_SKY | INTEL_TYPE_SERVER)
#define INTEL_PCH_DEVICE_ID_MASK 0xff00
#define INTEL_PCH_IBX_DEVICE_ID 0x3b00
#define INTEL_PCH_CPT_DEVICE_ID 0x1c00
#define INTEL_PCH_PPT_DEVICE_ID 0x1e00
#define INTEL_PCH_LPT_DEVICE_ID 0x8c00
#define INTEL_PCH_LPT_LP_DEVICE_ID 0x9c00
#define INTEL_PCH_SPT_DEVICE_ID 0xA100
#define INTEL_PCH_SPT_LP_DEVICE_ID 0x9D00
#define INTEL_PCH_P2X_DEVICE_ID 0x7100
#define INTEL_PCH_P3X_DEVICE_ID 0x7000
// ValleyView MMIO offset // ValleyView MMIO offset
#define VLV_DISPLAY_BASE 0x180000 #define VLV_DISPLAY_BASE 0x180000
@@ -163,11 +174,6 @@ struct DeviceType {
|| InFamily(INTEL_FAMILY_SOC0); || InFamily(INTEL_FAMILY_SOC0);
} }
bool HasPlatformControlHub() const
{
return InFamily(INTEL_FAMILY_SER5);
}
bool HasDDI() const bool HasDDI() const
{ {
// Intel Digital Display Interface // Intel Digital Display Interface
@@ -201,6 +207,15 @@ struct DeviceType {
} }
}; };
enum pch_info {
INTEL_PCH_NONE = 0, // No PCH present
INTEL_PCH_IBX, // Ibexpeak
INTEL_PCH_CPT, // Cougarpoint
INTEL_PCH_LPT, // Lynxpoint
INTEL_PCH_SPT, // Sunrisepoint
INTEL_PCH_NOP
};
// info about PLL on graphics card // info about PLL on graphics card
struct pll_info { struct pll_info {
uint32 reference_frequency; uint32 reference_frequency;
@@ -273,6 +288,8 @@ struct intel_shared_info {
char device_identifier[32]; char device_identifier[32];
struct pll_info pll_info; struct pll_info pll_info;
enum pch_info pch_info;
edid1_info vesa_edid_info; edid1_info vesa_edid_info;
bool has_vesa_edid_info; bool has_vesa_edid_info;
}; };
@@ -473,7 +490,6 @@ struct intel_free_graphics_memory {
#define LVDS_18BIT_DITHER (1UL << 25) #define LVDS_18BIT_DITHER (1UL << 25)
#define LVDS_PORT_EN (1UL << 31) #define LVDS_PORT_EN (1UL << 31)
// PLL flags // PLL flags
#define DISPLAY_PLL_ENABLED (1UL << 31) #define DISPLAY_PLL_ENABLED (1UL << 31)
#define DISPLAY_PLL_2X_CLOCK (1UL << 30) #define DISPLAY_PLL_2X_CLOCK (1UL << 30)
@@ -520,6 +536,12 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_A_IMAGE_SIZE (0x001c | REGS_NORTH_PIPE_AND_PORT) #define INTEL_DISPLAY_A_IMAGE_SIZE (0x001c | REGS_NORTH_PIPE_AND_PORT)
#define INTEL_DISPLAY_B_IMAGE_SIZE (0x101c | REGS_NORTH_PIPE_AND_PORT) #define INTEL_DISPLAY_B_IMAGE_SIZE (0x101c | REGS_NORTH_PIPE_AND_PORT)
// Cougar Point transcoder pipe selection
#define PORT_TRANS_A_SEL_CPT 0
#define PORT_TRANS_B_SEL_CPT (1<<29)
#define PORT_TRANS_C_SEL_CPT (2<<29)
#define PORT_TRANS_SEL_MASK (3<<29)
// on PCH we also have to set the transcoder // on PCH we also have to set the transcoder
#define INTEL_TRANSCODER_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT) #define INTEL_TRANSCODER_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT) #define INTEL_TRANSCODER_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT)
@@ -67,7 +67,7 @@ Pipe::Pipe(pipe_index pipeIndex)
// IvyBridge: Analog + Digital Ports behind FDI (on northbridge) // IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
// Haswell: Only VGA behind FDI (on northbridge) // Haswell: Only VGA behind FDI (on northbridge)
// SkyLake: FDI gone. No more northbridge video. // SkyLake: FDI gone. No more northbridge video.
if (gInfo->shared_info->device_type.HasPlatformControlHub()) { if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
TRACE("%s: Pipe %s routed through FDI\n", __func__, TRACE("%s: Pipe %s routed through FDI\n", __func__,
(pipeIndex == INTEL_PIPE_A) ? "A" : "B"); (pipeIndex == INTEL_PIPE_A) ? "A" : "B");
@@ -136,6 +136,10 @@ Pipe::Configure(display_mode* mode)
void void
Pipe::_ConfigureTranscoder(display_mode* target) Pipe::_ConfigureTranscoder(display_mode* target)
{ {
CALLED();
TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
// update timing (fPipeOffset bumps the DISPLAY_A to B when needed) // update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset, write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16) ((uint32)(target->timing.h_total - 1) << 16)
@@ -172,6 +176,8 @@ Pipe::ConfigureTimings(display_mode* target)
{ {
CALLED(); CALLED();
TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
if (target == NULL) { if (target == NULL) {
ERROR("%s: Invalid display mode!\n", __func__); ERROR("%s: Invalid display mode!\n", __func__);
return; return;
@@ -199,6 +205,7 @@ Pipe::ConfigureTimings(display_mode* target)
| ((uint32)target->timing.v_sync_start - 1)); | ((uint32)target->timing.v_sync_start - 1));
// XXX: Is it ok to do these on non-digital? // XXX: Is it ok to do these on non-digital?
write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0); write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset, write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->virtual_width - 1) << 16) ((uint32)(target->virtual_width - 1) << 16)
@@ -130,10 +130,18 @@ Port::SetPipe(Pipe* pipe)
uint32 portState = read32(portRegister); uint32 portState = read32(portRegister);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
portState &= PORT_TRANS_SEL_MASK;
if (pipe->Index() == INTEL_PIPE_A)
write32(portRegister, portState | PORT_TRANS_A_SEL_CPT);
else
write32(portRegister, portState | PORT_TRANS_B_SEL_CPT);
} else {
if (pipe->Index() == INTEL_PIPE_A) if (pipe->Index() == INTEL_PIPE_A)
write32(portRegister, portState & ~DISPLAY_MONITOR_PIPE_B); write32(portRegister, portState & ~DISPLAY_MONITOR_PIPE_B);
else else
write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B); write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B);
}
fPipe = pipe; fPipe = pipe;
@@ -348,7 +356,7 @@ LVDSPort::LVDSPort()
{ {
// Always unlock LVDS port as soon as we start messing with it. // Always unlock LVDS port as soon as we start messing with it.
uint32 panelControl = INTEL_PANEL_CONTROL; uint32 panelControl = INTEL_PANEL_CONTROL;
if (gInfo->shared_info->device_type.HasPlatformControlHub()) if (gInfo->shared_info->pch_info != INTEL_PCH_NONE)
panelControl = PCH_PANEL_CONTROL; panelControl = PCH_PANEL_CONTROL;
write32(panelControl, read32(panelControl) | PANEL_REGISTER_UNLOCK); write32(panelControl, read32(panelControl) | PANEL_REGISTER_UNLOCK);
} }
@@ -360,7 +368,7 @@ LVDSPort::IsConnected()
TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(),
_PortRegister()); _PortRegister());
if (gInfo->shared_info->device_type.HasPlatformControlHub()) { if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
uint32 registerValue = read32(_PortRegister()); uint32 registerValue = read32(_PortRegister());
// there's a detection bit we can use // there's a detection bit we can use
if ((registerValue & PCH_LVDS_DETECTED) == 0) { if ((registerValue & PCH_LVDS_DETECTED) == 0) {
@@ -438,7 +446,7 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
addr_t panelControl = INTEL_PANEL_CONTROL; addr_t panelControl = INTEL_PANEL_CONTROL;
addr_t panelStatus = INTEL_PANEL_STATUS; addr_t panelStatus = INTEL_PANEL_STATUS;
if (gInfo->shared_info->device_type.HasPlatformControlHub()) { if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
panelControl = PCH_PANEL_CONTROL; panelControl = PCH_PANEL_CONTROL;
panelStatus = PCH_PANEL_STATUS; panelStatus = PCH_PANEL_STATUS;
} }
@@ -504,6 +512,15 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
lvds |= LVDS_18BIT_DITHER; lvds |= LVDS_18BIT_DITHER;
} }
// LVDS on PCH needs set before display enable
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
lvds &= PORT_TRANS_SEL_MASK;
if (fPipe->Index() == INTEL_PIPE_A)
lvds |= PORT_TRANS_A_SEL_CPT;
else
lvds |= PORT_TRANS_B_SEL_CPT;
}
// Set the B0-B3 data pairs corresponding to whether we're going to // Set the B0-B3 data pairs corresponding to whether we're going to
// set the DPLLs for dual-channel mode or not. // set the DPLLs for dual-channel mode or not.
if (divisors.p2 == 5 || divisors.p2 == 7) { if (divisors.p2 == 5 || divisors.p2 == 7) {
@@ -763,8 +780,8 @@ HDMIPort::IsConnected()
if (portRegister == 0) if (portRegister == 0)
return false; return false;
if (!gInfo->shared_info->device_type.HasPlatformControlHub() bool hasPCH = (gInfo->shared_info->pch_info != INTEL_PCH_NONE);
&& PortIndex() == INTEL_PORT_C) { if (!hasPCH && PortIndex() == INTEL_PORT_C) {
// there's no detection bit on this port // there's no detection bit on this port
} else if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0) } else if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0)
return false; return false;
@@ -777,7 +794,7 @@ addr_t
HDMIPort::_PortRegister() HDMIPort::_PortRegister()
{ {
// on PCH there's an additional port sandwiched in // on PCH there's an additional port sandwiched in
bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub(); bool hasPCH = (gInfo->shared_info->pch_info != INTEL_PCH_NONE);
bool fourthGen = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV); bool fourthGen = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV);
switch (PortIndex()) { switch (PortIndex()) {
@@ -46,7 +46,7 @@ enable_all_pipes(bool enable)
static void static void
enable_lvds_panel(bool enable) enable_lvds_panel(bool enable)
{ {
bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub(); bool hasPCH = (gInfo->shared_info->pch_info != INTEL_PCH_NONE);
int controlRegister = hasPCH ? PCH_PANEL_CONTROL : INTEL_PANEL_CONTROL; int controlRegister = hasPCH ? PCH_PANEL_CONTROL : INTEL_PANEL_CONTROL;
int statusRegister = hasPCH ? PCH_PANEL_STATUS : INTEL_PANEL_STATUS; int statusRegister = hasPCH ? PCH_PANEL_STATUS : INTEL_PANEL_STATUS;
@@ -446,7 +446,7 @@ void
compute_pll_divisors(display_mode* current, pll_divisors* divisors, bool isLVDS) compute_pll_divisors(display_mode* current, pll_divisors* divisors, bool isLVDS)
{ {
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x) if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)
|| gInfo->shared_info->device_type.HasPlatformControlHub()) { || (gInfo->shared_info->pch_info != INTEL_PCH_NONE)) {
compute_dpll_g4x(current, divisors, isLVDS); compute_dpll_g4x(current, divisors, isLVDS);
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) { } else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) {
ERROR("%s: TODO: CherryView\n", __func__); ERROR("%s: TODO: CherryView\n", __func__);
@@ -170,6 +170,47 @@ get_next_intel_extreme(int32* _cookie, pci_info &info, uint32 &type)
} }
static enum pch_info
detect_intel_pch()
{
pci_info info;
// find devices
for (int32 i = 0; gPCI->get_nth_pci_info(i, &info) == B_OK; i++) {
// check vendor
if (info.vendor_id != VENDOR_ID_INTEL
|| info.class_base != PCI_bridge
|| info.class_sub != PCI_isa) {
continue;
}
// check device
unsigned short id = info.device_id & INTEL_PCH_DEVICE_ID_MASK;
switch(id) {
case INTEL_PCH_IBX_DEVICE_ID:
ERROR("%s: Found Ibex Peak PCH\n", __func__);
return INTEL_PCH_IBX;
case INTEL_PCH_CPT_DEVICE_ID:
ERROR("%s: Found CougarPoint PCH\n", __func__);
return INTEL_PCH_CPT;
case INTEL_PCH_PPT_DEVICE_ID:
ERROR("%s: Found PantherPoint PCH\n", __func__);
return INTEL_PCH_CPT;
case INTEL_PCH_LPT_DEVICE_ID:
ERROR("%s: Found LynxPoint PCH\n", __func__);
return INTEL_PCH_LPT;
case INTEL_PCH_SPT_DEVICE_ID:
case INTEL_PCH_SPT_LP_DEVICE_ID:
ERROR("%s: Found SunrisePoint PCH\n", __func__);
return INTEL_PCH_SPT;
}
}
ERROR("%s: No PCH detected.\n", __func__);
return INTEL_PCH_NONE;
}
extern "C" const char** extern "C" const char**
publish_devices(void) publish_devices(void)
{ {
@@ -226,6 +267,8 @@ init_driver(void)
gPCIx86Module = NULL; gPCIx86Module = NULL;
} }
// find the PCH device (if any)
enum pch_info pchInfo = detect_intel_pch();
// find devices // find devices
@@ -269,6 +312,7 @@ init_driver(void)
gDeviceInfo[found]->registers = info->u.h0.base_registers[0]; gDeviceInfo[found]->registers = info->u.h0.base_registers[0];
gDeviceInfo[found]->device_identifier = kSupportedDevices[type].name; gDeviceInfo[found]->device_identifier = kSupportedDevices[type].name;
gDeviceInfo[found]->device_type = kSupportedDevices[type].type; gDeviceInfo[found]->device_type = kSupportedDevices[type].type;
gDeviceInfo[found]->pch_info = pchInfo;
dprintf(DEVICE_NAME ": (%ld) %s, revision = 0x%x\n", found, dprintf(DEVICE_NAME ": (%ld) %s, revision = 0x%x\n", found,
kSupportedDevices[type].name, info->revision); kSupportedDevices[type].name, info->revision);
@@ -93,7 +93,7 @@ intel_interrupt_handler(void* data)
while (identity != 0) { while (identity != 0) {
// TODO: verify that these aren't actually the same // TODO: verify that these aren't actually the same
bool hasPCH = info.device_type.HasPlatformControlHub(); bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
uint16 mask; uint16 mask;
// Intel changed the PCH register mapping between Sandy Bridge and the // Intel changed the PCH register mapping between Sandy Bridge and the
@@ -215,7 +215,7 @@ init_interrupt_handler(intel_info &info)
write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0); write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0);
// enable interrupts - we only want VBLANK interrupts // enable interrupts - we only want VBLANK interrupts
bool hasPCH = info.device_type.HasPlatformControlHub(); bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
uint16 enable = hasPCH uint16 enable = hasPCH
? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB) ? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB)
: (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB); : (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB);
@@ -316,15 +316,16 @@ intel_extreme_init(intel_info &info)
return info.registers_area; return info.registers_area;
} }
bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n", ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n",
info.device_type.Generation(), info.device_type.Generation(), hasPCH ? "with" : "without");
info.device_type.HasPlatformControlHub() ? "with" : "without");
uint32* blocks = info.shared_info->register_blocks; uint32* blocks = info.shared_info->register_blocks;
blocks[REGISTER_BLOCK(REGS_FLAT)] = 0; blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
// setup the register blocks for the different architectures // setup the register blocks for the different architectures
if (info.device_type.HasPlatformControlHub()) { if (hasPCH) {
// PCH based platforms (IronLake through ultra-low-power Broadwells) // PCH based platforms (IronLake through ultra-low-power Broadwells)
blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
= PCH_NORTH_SHARED_REGISTER_BASE; = PCH_NORTH_SHARED_REGISTER_BASE;
@@ -428,6 +429,8 @@ intel_extreme_init(intel_info &info)
info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
info.shared_info->pch_info = info.pch_info;
info.shared_info->device_type = info.device_type; info.shared_info->device_type = info.device_type;
#ifdef __HAIKU__ #ifdef __HAIKU__
strlcpy(info.shared_info->device_identifier, info.device_identifier, strlcpy(info.shared_info->device_identifier, info.device_identifier,
@@ -474,7 +477,7 @@ intel_extreme_init(intel_info &info)
init_interrupt_handler(info); init_interrupt_handler(info);
if (info.device_type.HasPlatformControlHub()) { if (hasPCH) {
if (info.device_type.Generation() == 5) { if (info.device_type.Generation() == 5) {
info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0) info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0)
& FDI_PLL_FB_CLOCK_MASK) + 2; & FDI_PLL_FB_CLOCK_MASK) + 2;
@@ -40,6 +40,8 @@ struct intel_info {
const char* device_identifier; const char* device_identifier;
DeviceType device_type; DeviceType device_type;
enum pch_info pch_info;
}; };
@@ -51,7 +53,7 @@ find_reg(const intel_info& info, uint32 target)
return target; return target;
} }
if (!info.device_type.HasPlatformControlHub()) if (info.pch_info == INTEL_PCH_NONE)
return target; return target;
#define RETURN_REG(x) case INTEL_##x: return PCH_##x; #define RETURN_REG(x) case INTEL_##x: return PCH_##x;