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
@@ -448,18 +448,18 @@ struct intel_free_graphics_memory {
#define LVDS_POST2_RATE_SLOW 14 // PLL Divisors #define LVDS_POST2_RATE_SLOW 14 // PLL Divisors
#define LVDS_POST2_RATE_FAST 7 #define LVDS_POST2_RATE_FAST 7
#define LVDS_B0B3_POWER_MASK (3 << 2) #define LVDS_B0B3_POWER_MASK (3UL << 2)
#define LVDS_B0B3_POWER_UP (3 << 2) #define LVDS_B0B3_POWER_UP (3UL << 2)
#define LVDS_CLKB_POWER_MASK (3 << 4) #define LVDS_CLKB_POWER_MASK (3UL << 4)
#define LVDS_CLKB_POWER_UP (3 << 4) #define LVDS_CLKB_POWER_UP (3UL << 4)
#define LVDS_A3_POWER_MASK (3 << 6) #define LVDS_A3_POWER_MASK (3UL << 6)
#define LVDS_A3_POWER_UP (3 << 6) #define LVDS_A3_POWER_UP (3UL << 6)
#define LVDS_A0A2_CLKA_POWER_UP (3 << 8) #define LVDS_A0A2_CLKA_POWER_UP (3UL << 8)
#define LVDS_BORDER_ENABLE (1 << 15) #define LVDS_BORDER_ENABLE (1UL << 15)
#define LVDS_HSYNC_POLARITY (1 << 20) #define LVDS_HSYNC_POLARITY (1UL << 20)
#define LVDS_VSYNC_POLARITY (1 << 21) #define LVDS_VSYNC_POLARITY (1UL << 21)
#define LVDS_18BIT_DITHER (1 << 25) #define LVDS_18BIT_DITHER (1UL << 25)
#define LVDS_PORT_EN (1 << 31) #define LVDS_PORT_EN (1UL << 31)
// PLL flags // 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_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)
// 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_ANALOG_PORT (0x1100 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DIGITAL_PORT_A (0x1120 | REGS_SOUTH_TRANSCODER_PORT) #define INTEL_DIGITAL_PORT_A (0x1120 | REGS_SOUTH_TRANSCODER_PORT)
#define INTEL_DIGITAL_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT) #define INTEL_DIGITAL_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT)
+60 -32
View File
@@ -52,26 +52,29 @@ program_pipe_color_modes(uint32 colorMode)
Pipe::Pipe(pipe_index pipeIndex) Pipe::Pipe(pipe_index pipeIndex)
: :
fHasTranscoder(false),
fFDILink(NULL), fFDILink(NULL),
// fPanelFitter(NULL), // fPanelFitter(NULL),
fPipeIndex(pipeIndex), fPipeIndex(pipeIndex),
fPipeBase(REGS_NORTH_PIPE_AND_PORT), fPipeOffset(0),
fPlaneBase(REGS_NORTH_PLANE_CONTROL) fPlaneOffset(0)
{ {
if (pipeIndex == INTEL_PIPE_B) { if (pipeIndex == INTEL_PIPE_B) {
fPipeBase += INTEL_DISPLAY_OFFSET; fPipeOffset = INTEL_DISPLAY_OFFSET;
fPlaneBase += INTEL_PLANE_OFFSET; fPlaneOffset = INTEL_PLANE_OFFSET;
} }
// Program FDILink if PCH
if (gInfo->shared_info->device_type.HasPlatformControlHub()) { if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
fHasTranscoder = true;
// Program FDILink if PCH
if (fFDILink == NULL) if (fFDILink == NULL)
fFDILink = new(std::nothrow) FDILink(pipeIndex); fFDILink = new(std::nothrow) FDILink(pipeIndex);
} }
TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
" Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A) " 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() Pipe::IsEnabled()
{ {
CALLED(); CALLED();
return (read32(fPlaneBase + INTEL_PIPE_CONTROL) & INTEL_PIPE_ENABLED) != 0; return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPlaneOffset)
& INTEL_PIPE_ENABLED) != 0;
} }
void 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(); CALLED();
@@ -97,58 +135,48 @@ Pipe::Enable(display_mode* target, addr_t portAddress)
ERROR("%s: Invalid display mode!\n", __func__); ERROR("%s: Invalid display mode!\n", __func__);
return; return;
} }
if (portAddress == 0) {
ERROR("%s: Invalid port address!\n", __func__);
return;
}
// update timing (fPipeBase bumps the DISPLAY_A to B when needed) // update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_HTOTAL), write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
((uint32)(target->timing.h_total - 1) << 16) ((uint32)(target->timing.h_total - 1) << 16)
| ((uint32)target->timing.h_display - 1)); | ((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_total - 1) << 16)
| ((uint32)target->timing.h_display - 1)); | ((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_end - 1) << 16)
| ((uint32)target->timing.h_sync_start - 1)); | ((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_total - 1) << 16)
| ((uint32)target->timing.v_display - 1)); | ((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_total - 1) << 16)
| ((uint32)target->timing.v_display - 1)); | ((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_end - 1) << 16)
| ((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(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_POS), 0); write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
write32(fPipeBase + REGISTER_REGISTER(INTEL_DISPLAY_A_IMAGE_SIZE), write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
((uint32)(target->virtual_width - 1) << 16) ((uint32)(target->virtual_width - 1) << 16)
| ((uint32)target->virtual_height - 1)); | ((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.v_display - 1) << 16)
| ((uint32)target->timing.h_display - 1)); | ((uint32)target->timing.h_display - 1));
// This is useful for debugging: it sets the border to red, so you // 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 // can see what is border and what is porch (black area around the
// sync) // 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? if (fHasTranscoder)
write32(portAddress, (read32(portAddress) & ~(DISPLAY_MONITOR_POLARITY_MASK _EnableTranscoder(target);
| 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));
// Enable display pipe // Enable display pipe
_Enable(true); _Enable(true);
} }
@@ -263,7 +291,7 @@ Pipe::_Enable(bool enable)
{ {
CALLED(); CALLED();
addr_t targetRegister = fPlaneBase + INTEL_PIPE_CONTROL; addr_t targetRegister = INTEL_DISPLAY_A_PIPE_CONTROL + fPlaneOffset;
write32(targetRegister, (read32(targetRegister) & ~INTEL_PIPE_ENABLED) write32(targetRegister, (read32(targetRegister) & ~INTEL_PIPE_ENABLED)
| (enable ? INTEL_PIPE_ENABLED : 0)); | (enable ? INTEL_PIPE_ENABLED : 0));
@@ -35,8 +35,7 @@ public:
{ return fPipeIndex; } { return fPipeIndex; }
bool IsEnabled(); bool IsEnabled();
void Enable(display_mode* mode, void Enable(display_mode* mode);
addr_t portRegister);
void Disable(); void Disable();
void ConfigureTimings( void ConfigureTimings(
@@ -52,14 +51,17 @@ public:
private: private:
void _Enable(bool enable); void _Enable(bool enable);
void _EnableTranscoder(display_mode* mode);
bool fHasTranscoder;
FDILink* fFDILink; FDILink* fFDILink;
// PanelFitter* fPanelFitter; // PanelFitter* fPanelFitter;
pipe_index fPipeIndex; pipe_index fPipeIndex;
addr_t fPipeBase; addr_t fPipeOffset;
addr_t fPlaneBase; addr_t fPlaneOffset;
}; };
@@ -305,12 +305,19 @@ AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
if (gInfo->shared_info->device_type.Generation() >= 4) if (gInfo->shared_info->device_type.Generation() >= 4)
extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL; 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 // Program pipe PLL's
fPipe->ConfigureTimings(divisors, target->timing.pixel_clock, fPipe->ConfigureTimings(divisors, target->timing.pixel_clock,
extraPLLFlags); extraPLLFlags);
// Program target display mode // Program target display mode
fPipe->Enable(target, _PortRegister()); fPipe->Enable(target);
// Set fCurrentMode to our set display mode // Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(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()) { if (gInfo->shared_info->device_type.HasPlatformControlHub()) {
// 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) {
@@ -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 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.post2_high) if (divisors.post2_high) {
TRACE("LVDS: dual channel\n");
lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; 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 &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
}
// LVDS port control moves polarity bits because Intel hates you.
// Set LVDS sync polarity // Set LVDS sync polarity
lvds &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_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) if ((target->timing.flags & B_POSITIVE_VSYNC) == 0)
lvds |= LVDS_VSYNC_POLARITY; 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); write32(_PortRegister(), lvds);
read32(_PortRegister()); 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()); ERROR("%s: %s didn't power on within 1000ms!\n", __func__, PortName());
// Program target display mode // Program target display mode
fPipe->Enable(target, _PortRegister()); fPipe->Enable(target);
#if 0 #if 0
@@ -732,7 +743,7 @@ DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode)
extraPLLFlags); extraPLLFlags);
// Program target display mode // Program target display mode
fPipe->Enable(target, _PortRegister()); fPipe->Enable(target);
// Set fCurrentMode to our set display mode // Set fCurrentMode to our set display mode
memcpy(&fCurrentMode, target, sizeof(display_mode)); memcpy(&fCurrentMode, target, sizeof(display_mode));