intel_extreme: Fix i965 LVDS panel programming

* polarity regs move on LVDS vs analog
* add knowledge or transcoder registers, they
  exist seperately on PCH-split
* Native resolutions now work on LVDS under i965
This commit is contained in:
Alexander von Gluck IV 2016-01-03 10:46:13 -06:00
parent e28962a280
commit d35a52e8e2
4 changed files with 113 additions and 55 deletions

View File

@ -448,18 +448,18 @@ struct intel_free_graphics_memory {
#define LVDS_POST2_RATE_SLOW 14 // PLL Divisors
#define LVDS_POST2_RATE_FAST 7
#define LVDS_B0B3_POWER_MASK (3 << 2)
#define LVDS_B0B3_POWER_UP (3 << 2)
#define LVDS_CLKB_POWER_MASK (3 << 4)
#define LVDS_CLKB_POWER_UP (3 << 4)
#define LVDS_A3_POWER_MASK (3 << 6)
#define LVDS_A3_POWER_UP (3 << 6)
#define LVDS_A0A2_CLKA_POWER_UP (3 << 8)
#define LVDS_BORDER_ENABLE (1 << 15)
#define LVDS_HSYNC_POLARITY (1 << 20)
#define LVDS_VSYNC_POLARITY (1 << 21)
#define LVDS_18BIT_DITHER (1 << 25)
#define LVDS_PORT_EN (1 << 31)
#define LVDS_B0B3_POWER_MASK (3UL << 2)
#define LVDS_B0B3_POWER_UP (3UL << 2)
#define LVDS_CLKB_POWER_MASK (3UL << 4)
#define LVDS_CLKB_POWER_UP (3UL << 4)
#define LVDS_A3_POWER_MASK (3UL << 6)
#define LVDS_A3_POWER_UP (3UL << 6)
#define LVDS_A0A2_CLKA_POWER_UP (3UL << 8)
#define LVDS_BORDER_ENABLE (1UL << 15)
#define LVDS_HSYNC_POLARITY (1UL << 20)
#define LVDS_VSYNC_POLARITY (1UL << 21)
#define LVDS_18BIT_DITHER (1UL << 25)
#define LVDS_PORT_EN (1UL << 31)
// PLL flags
@ -508,6 +508,23 @@ 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)
// 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)
#define INTEL_TRANSCODER_A_HSYNC (0x0008 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_VTOTAL (0x000c | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_VBLANK (0x0010 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_VSYNC (0x0014 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_HTOTAL (0x1000 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_HBLANK (0x1004 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_HSYNC (0x1008 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_VTOTAL (0x100c | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_VBLANK (0x1010 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_VSYNC (0x1014 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_A_IMAGE_SIZE (0x001c | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_TRANSCODER_B_IMAGE_SIZE (0x101c | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_ANALOG_PORT (0x1100 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DIGITAL_PORT_A (0x1120 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DIGITAL_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT)

View File

@ -52,26 +52,29 @@ program_pipe_color_modes(uint32 colorMode)
Pipe::Pipe(pipe_index pipeIndex)
:
fHasTranscoder(false),
fFDILink(NULL),
// fPanelFitter(NULL),
fPipeIndex(pipeIndex),
fPipeBase(REGS_NORTH_PIPE_AND_PORT),
fPlaneBase(REGS_NORTH_PLANE_CONTROL)
fPipeOffset(0),
fPlaneOffset(0)
{
if (pipeIndex == INTEL_PIPE_B) {
fPipeBase += INTEL_DISPLAY_OFFSET;
fPlaneBase += INTEL_PLANE_OFFSET;
fPipeOffset = INTEL_DISPLAY_OFFSET;
fPlaneOffset = INTEL_PLANE_OFFSET;
}
// Program FDILink if PCH
if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
fHasTranscoder = true;
// Program FDILink if PCH
if (fFDILink == NULL)
fFDILink = new(std::nothrow) FDILink(pipeIndex);
}
TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
" Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A)
? "A" : "B", fPipeBase, fPlaneBase);
? "A" : "B", fPipeOffset, fPlaneOffset);
}
@ -84,12 +87,47 @@ bool
Pipe::IsEnabled()
{
CALLED();
return (read32(fPlaneBase + INTEL_PIPE_CONTROL) & INTEL_PIPE_ENABLED) != 0;
return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPlaneOffset)
& INTEL_PIPE_ENABLED) != 0;
}
void
Pipe::Enable(display_mode* target, addr_t portAddress)
Pipe::_EnableTranscoder(display_mode* target)
{
// 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->virtual_width - 1) << 16)
| ((uint32)target->virtual_height - 1));
#endif
}
void
Pipe::Enable(display_mode* target)
{
CALLED();
@ -97,58 +135,48 @@ Pipe::Enable(display_mode* target, addr_t portAddress)
ERROR("%s: Invalid display mode!\n", __func__);
return;
}
if (portAddress == 0) {
ERROR("%s: Invalid port address!\n", __func__);
return;
}
// update timing (fPipeBase bumps the DISPLAY_A to B when needed)
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_HTOTAL),
// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_HBLANK),
write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_HSYNC),
write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset,
((uint32)(target->timing.h_sync_end - 1) << 16)
| ((uint32)target->timing.h_sync_start - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_VTOTAL),
write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_VBLANK),
write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset,
((uint32)(target->timing.v_total - 1) << 16)
| ((uint32)target->timing.v_display - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_VSYNC),
write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset,
((uint32)(target->timing.v_sync_end - 1) << 16)
| ((uint32)target->timing.v_sync_start - 1));
// XXX: Is it ok to do these on non-digital?
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_POS), 0);
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_IMAGE_SIZE),
write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->virtual_width - 1) << 16)
| ((uint32)target->virtual_height - 1));
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_PIPE_SIZE),
write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset,
((uint32)(target->timing.v_display - 1) << 16)
| ((uint32)target->timing.h_display - 1));
// This is useful for debugging: it sets the border to red, so you
// can see what is border and what is porch (black area around the
// sync)
//write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_RED), 0x00FF0000);
//write32(INTEL_DISPLAY_A_RED + fPipeOffset, 0x00FF0000);
// XXX: Is it ok to do this on non-analog?
write32(portAddress, (read32(portAddress) & ~(DISPLAY_MONITOR_POLARITY_MASK
| DISPLAY_MONITOR_VGA_POLARITY))
| ((target->timing.flags & B_POSITIVE_HSYNC) != 0
? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
| ((target->timing.flags & B_POSITIVE_VSYNC) != 0
? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
if (fHasTranscoder)
_EnableTranscoder(target);
// Enable display pipe
_Enable(true);
}
@ -263,7 +291,7 @@ Pipe::_Enable(bool enable)
{
CALLED();
addr_t targetRegister = fPlaneBase + INTEL_PIPE_CONTROL;
addr_t targetRegister = INTEL_DISPLAY_A_PIPE_CONTROL + fPlaneOffset;
write32(targetRegister, (read32(targetRegister) & ~INTEL_PIPE_ENABLED)
| (enable ? INTEL_PIPE_ENABLED : 0));

View File

@ -35,8 +35,7 @@ public:
{ return fPipeIndex; }
bool IsEnabled();
void Enable(display_mode* mode,
addr_t portRegister);
void Enable(display_mode* mode);
void Disable();
void ConfigureTimings(
@ -52,14 +51,17 @@ public:
private:
void _Enable(bool enable);
void _EnableTranscoder(display_mode* mode);
bool fHasTranscoder;
FDILink* fFDILink;
// PanelFitter* fPanelFitter;
pipe_index fPipeIndex;
addr_t fPipeBase;
addr_t fPlaneBase;
addr_t fPipeOffset;
addr_t fPlaneOffset;
};

View File

@ -305,12 +305,19 @@ AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if (gInfo->shared_info->device_type.Generation() >= 4)
extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL;
write32(_PortRegister(), (read32(_PortRegister())
& ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY))
| ((target->timing.flags & B_POSITIVE_HSYNC) != 0
? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
| ((target->timing.flags & B_POSITIVE_VSYNC) != 0
? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
// Program pipe PLL's
fPipe->ConfigureTimings(divisors, target->timing.pixel_clock,
extraPLLFlags);
// Program target display mode
fPipe->Enable(target, _PortRegister());
fPipe->Enable(target);
// Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(display_mode));
@ -345,7 +352,7 @@ LVDSPort::IsConnected()
}
}
uint32 registerValue = read32(INTEL_DIGITAL_LVDS_PORT);
uint32 registerValue = read32(_PortRegister());
if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
// there's a detection bit we can use
if ((registerValue & PCH_LVDS_DETECTED) == 0) {
@ -524,11 +531,15 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
// Set the B0-B3 data pairs corresponding to whether we're going to
// set the DPLLs for dual-channel mode or not.
if (divisors.post2_high)
if (divisors.post2_high) {
TRACE("LVDS: dual channel\n");
lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
else
} else {
TRACE("LVDS: single channel\n");
lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
}
// LVDS port control moves polarity bits because Intel hates you.
// Set LVDS sync polarity
lvds &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
@ -538,7 +549,7 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if ((target->timing.flags & B_POSITIVE_VSYNC) == 0)
lvds |= LVDS_VSYNC_POLARITY;
TRACE("%s: LVDS Control: 0x%" B_PRIx32 "\n", __func__, lvds);
TRACE("%s: LVDS Write: 0x%" B_PRIx32 "\n", __func__, lvds);
write32(_PortRegister(), lvds);
read32(_PortRegister());
@ -560,7 +571,7 @@ LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
ERROR("%s: %s didn't power on within 1000ms!\n", __func__, PortName());
// Program target display mode
fPipe->Enable(target, _PortRegister());
fPipe->Enable(target);
#if 0
@ -732,7 +743,7 @@ DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
extraPLLFlags);
// Program target display mode
fPipe->Enable(target, _PortRegister());
fPipe->Enable(target);
// Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(display_mode));