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 20:59:02 -05:00
parent 8199f204da
commit 92e254d047
8 changed files with 121 additions and 26 deletions

View File

@ -83,6 +83,17 @@
#define INTEL_MODEL_SKYM (INTEL_GROUP_SKY | INTEL_TYPE_MOBILE)
#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
#define VLV_DISPLAY_BASE 0x180000
@ -163,11 +174,6 @@ struct DeviceType {
|| InFamily(INTEL_FAMILY_SOC0);
}
bool HasPlatformControlHub() const
{
return InFamily(INTEL_FAMILY_SER5);
}
bool HasDDI() const
{
// 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
struct pll_info {
uint32 reference_frequency;
@ -273,6 +288,8 @@ struct intel_shared_info {
char device_identifier[32];
struct pll_info pll_info;
enum pch_info pch_info;
edid1_info 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_PORT_EN (1UL << 31)
// PLL flags
#define DISPLAY_PLL_ENABLED (1UL << 31)
#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_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
#define INTEL_TRANSCODER_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT)

View File

@ -67,7 +67,7 @@ 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->device_type.HasPlatformControlHub()) {
if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
TRACE("%s: Pipe %s routed through FDI\n", __func__,
(pipeIndex == INTEL_PIPE_A) ? "A" : "B");
@ -136,6 +136,10 @@ Pipe::Configure(display_mode* mode)
void
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)
write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
@ -172,6 +176,8 @@ Pipe::ConfigureTimings(display_mode* target)
{
CALLED();
TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
if (target == NULL) {
ERROR("%s: Invalid display mode!\n", __func__);
return;
@ -199,6 +205,7 @@ Pipe::ConfigureTimings(display_mode* target)
| ((uint32)target->timing.v_sync_start - 1));
// XXX: Is it ok to do these on non-digital?
write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->virtual_width - 1) << 16)

View File

@ -130,10 +130,18 @@ Port::SetPipe(Pipe* pipe)
uint32 portState = read32(portRegister);
if (pipe->Index() == INTEL_PIPE_A)
write32(portRegister, portState & ~DISPLAY_MONITOR_PIPE_B);
else
write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B);
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)
write32(portRegister, portState & ~DISPLAY_MONITOR_PIPE_B);
else
write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B);
}
fPipe = pipe;
@ -348,7 +356,7 @@ LVDSPort::LVDSPort()
{
// Always unlock LVDS port as soon as we start messing with it.
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;
write32(panelControl, read32(panelControl) | PANEL_REGISTER_UNLOCK);
}
@ -360,7 +368,7 @@ LVDSPort::IsConnected()
TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(),
_PortRegister());
if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
uint32 registerValue = read32(_PortRegister());
// there's a detection bit we can use
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 panelStatus = INTEL_PANEL_STATUS;
if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
panelControl = PCH_PANEL_CONTROL;
panelStatus = PCH_PANEL_STATUS;
}
@ -504,6 +512,15 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
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 DPLLs for dual-channel mode or not.
if (divisors.p2 == 5 || divisors.p2 == 7) {
@ -763,8 +780,8 @@ HDMIPort::IsConnected()
if (portRegister == 0)
return false;
if (!gInfo->shared_info->device_type.HasPlatformControlHub()
&& PortIndex() == INTEL_PORT_C) {
bool hasPCH = (gInfo->shared_info->pch_info != INTEL_PCH_NONE);
if (!hasPCH && PortIndex() == INTEL_PORT_C) {
// there's no detection bit on this port
} else if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0)
return false;
@ -777,7 +794,7 @@ addr_t
HDMIPort::_PortRegister()
{
// 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);
switch (PortIndex()) {

View File

@ -46,7 +46,7 @@ enable_all_pipes(bool enable)
static void
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 statusRegister = hasPCH ? PCH_PANEL_STATUS : INTEL_PANEL_STATUS;

View File

@ -446,7 +446,7 @@ void
compute_pll_divisors(display_mode* current, pll_divisors* divisors, bool isLVDS)
{
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);
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) {
ERROR("%s: TODO: CherryView\n", __func__);

View File

@ -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**
publish_devices(void)
{
@ -226,6 +267,8 @@ init_driver(void)
gPCIx86Module = NULL;
}
// find the PCH device (if any)
enum pch_info pchInfo = detect_intel_pch();
// find devices
@ -269,6 +312,7 @@ init_driver(void)
gDeviceInfo[found]->registers = info->u.h0.base_registers[0];
gDeviceInfo[found]->device_identifier = kSupportedDevices[type].name;
gDeviceInfo[found]->device_type = kSupportedDevices[type].type;
gDeviceInfo[found]->pch_info = pchInfo;
dprintf(DEVICE_NAME ": (%ld) %s, revision = 0x%x\n", found,
kSupportedDevices[type].name, info->revision);

View File

@ -93,7 +93,7 @@ intel_interrupt_handler(void* data)
while (identity != 0) {
// 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;
// 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);
// enable interrupts - we only want VBLANK interrupts
bool hasPCH = info.device_type.HasPlatformControlHub();
bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
uint16 enable = hasPCH
? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB)
: (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB);
@ -316,15 +316,16 @@ intel_extreme_init(intel_info &info)
return info.registers_area;
}
bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n",
info.device_type.Generation(),
info.device_type.HasPlatformControlHub() ? "with" : "without");
info.device_type.Generation(), hasPCH ? "with" : "without");
uint32* blocks = info.shared_info->register_blocks;
blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
// setup the register blocks for the different architectures
if (info.device_type.HasPlatformControlHub()) {
if (hasPCH) {
// PCH based platforms (IronLake through ultra-low-power Broadwells)
blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
= 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->pch_info = info.pch_info;
info.shared_info->device_type = info.device_type;
#ifdef __HAIKU__
strlcpy(info.shared_info->device_identifier, info.device_identifier,
@ -474,7 +477,7 @@ intel_extreme_init(intel_info &info)
init_interrupt_handler(info);
if (info.device_type.HasPlatformControlHub()) {
if (hasPCH) {
if (info.device_type.Generation() == 5) {
info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0)
& FDI_PLL_FB_CLOCK_MASK) + 2;

View File

@ -40,6 +40,8 @@ struct intel_info {
const char* device_identifier;
DeviceType device_type;
enum pch_info pch_info;
};
@ -51,7 +53,7 @@ find_reg(const intel_info& info, uint32 target)
return target;
}
if (!info.device_type.HasPlatformControlHub())
if (info.pch_info == INTEL_PCH_NONE)
return target;
#define RETURN_REG(x) case INTEL_##x: return PCH_##x;