intel_extreme: Start doing mode-setting at port level
* I really hope we can kill head_mode some day * Break pll code out from mode code * The LVDS and Digital are smooshed together and likely need broken apart.
This commit is contained in:
parent
e747cbe116
commit
b3f14fb7c7
@ -570,7 +570,7 @@ struct intel_free_graphics_memory {
|
||||
#define I2C_RESERVED ((1 << 13) | (1 << 5))
|
||||
|
||||
// TODO: on IronLake this is in the north shared block at 0x41000
|
||||
#define INTEL_VGA_DISPLAY_CONTROL 0x71400
|
||||
#define INTEL_VGA_DISPLAY_CONTROL (0x1400 | REGS_NORTH_PLANE_CONTROL)
|
||||
#define VGA_DISPLAY_DISABLED (1UL << 31)
|
||||
|
||||
// LVDS panel
|
||||
@ -587,7 +587,6 @@ struct intel_free_graphics_memory {
|
||||
#define PANEL_REGISTER_UNLOCK (0xabcd << 16)
|
||||
#define PCH_LVDS_DETECTED (1 << 1)
|
||||
|
||||
|
||||
// ring buffer commands
|
||||
|
||||
#define COMMAND_NOOP 0x00
|
||||
|
@ -16,6 +16,7 @@ Addon intel_extreme.accelerant :
|
||||
mode.cpp
|
||||
overlay.cpp
|
||||
# overlay_3d_i965.cpp
|
||||
pll.cpp
|
||||
# classes
|
||||
# DisplayPipe.cpp
|
||||
# FlexibleDisplayInterface.cpp
|
||||
|
@ -11,13 +11,14 @@
|
||||
|
||||
#include "Ports.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <ddc.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <Debug.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include "accelerant.h"
|
||||
#include "accelerant_protos.h"
|
||||
#include "intel_extreme.h"
|
||||
|
||||
|
||||
@ -193,6 +194,131 @@ AnalogPort::_DDCRegister()
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode)
|
||||
{
|
||||
TRACE("%s: %s-%d %dx%d\n", __func__, PortName(), PortIndex(),
|
||||
target->virtual_width, target->virtual_height);
|
||||
|
||||
pll_divisors divisors;
|
||||
compute_pll_divisors(target, &divisors, false);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
|
||||
uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
|
||||
if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
pll |= ((1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
|
||||
} else {
|
||||
pll |= ((1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||
// pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
// & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||
}
|
||||
if (divisors.post2_high)
|
||||
pll |= DISPLAY_PLL_DIVIDE_HIGH;
|
||||
|
||||
pll |= DISPLAY_PLL_MODE_ANALOG;
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x))
|
||||
pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
|
||||
} else {
|
||||
if (!divisors.post2_high)
|
||||
pll |= DISPLAY_PLL_DIVIDE_4X;
|
||||
|
||||
pll |= DISPLAY_PLL_2X_CLOCK;
|
||||
|
||||
if (divisors.post1 > 2) {
|
||||
pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_POST1_DIVISOR_MASK;
|
||||
} else
|
||||
pll |= DISPLAY_PLL_POST1_DIVIDE_2;
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_A_PLL, pll);
|
||||
read32(INTEL_DISPLAY_A_PLL);
|
||||
spin(150);
|
||||
write32(INTEL_DISPLAY_A_PLL, pll);
|
||||
read32(INTEL_DISPLAY_A_PLL);
|
||||
spin(150);
|
||||
|
||||
// update timing parameters
|
||||
write32(INTEL_DISPLAY_A_HTOTAL,
|
||||
((uint32)(target->timing.h_total - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_A_HBLANK,
|
||||
((uint32)(target->timing.h_total - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_A_HSYNC,
|
||||
((uint32)(target->timing.h_sync_end - 1) << 16)
|
||||
| ((uint32)target->timing.h_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_A_VTOTAL,
|
||||
((uint32)(target->timing.v_total - 1) << 16)
|
||||
| ((uint32)target->timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_A_VBLANK,
|
||||
((uint32)(target->timing.v_total - 1) << 16)
|
||||
| ((uint32)target->timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_A_VSYNC,
|
||||
((uint32)(target->timing.v_sync_end - 1) << 16)
|
||||
| ((uint32)target->timing.v_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_A_IMAGE_SIZE,
|
||||
((uint32)(target->virtual_width - 1) << 16)
|
||||
| ((uint32)target->virtual_height - 1));
|
||||
|
||||
write32(INTEL_ANALOG_PORT, (read32(INTEL_ANALOG_PORT)
|
||||
& ~(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));
|
||||
|
||||
// TODO: verify the two comments below: the X driver doesn't seem to
|
||||
// care about both of them!
|
||||
|
||||
// These two have to be set for display B, too - this obviously means
|
||||
// that the second head always must adopt the color space of the first
|
||||
// head.
|
||||
write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
|
||||
write32(INTEL_DISPLAY_B_IMAGE_SIZE,
|
||||
((uint32)(target->virtual_width - 1) << 16)
|
||||
| ((uint32)target->virtual_height - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
}
|
||||
|
||||
// Set fCurrentMode to our set display mode
|
||||
memcpy(fCurrentMode, target, sizeof(display_mode));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - LVDS Panel
|
||||
|
||||
|
||||
@ -227,6 +353,257 @@ LVDSPort::_DDCRegister()
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode)
|
||||
{
|
||||
// For LVDS panels, we actually always set the native mode in hardware
|
||||
// Then we use the panel fitter to scale the picture to that.
|
||||
display_mode hardwareTarget;
|
||||
bool needsScaling = false;
|
||||
// Try to get the panel preferred screen mode from EDID info
|
||||
if (gInfo->has_edid) {
|
||||
hardwareTarget.space = target->space;
|
||||
hardwareTarget.virtual_width
|
||||
= gInfo->edid_info.std_timing[0].h_size;
|
||||
hardwareTarget.virtual_height
|
||||
= gInfo->edid_info.std_timing[0].v_size;
|
||||
for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
|
||||
if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type
|
||||
== EDID1_IS_DETAILED_TIMING) {
|
||||
hardwareTarget.virtual_width = gInfo->edid_info
|
||||
.detailed_monitor[i].data.detailed_timing.h_active;
|
||||
hardwareTarget.virtual_height = gInfo->edid_info
|
||||
.detailed_monitor[i].data.detailed_timing.v_active;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TRACE("%s: hardware mode will actually be %dx%d\n", __func__,
|
||||
hardwareTarget.virtual_width, hardwareTarget.virtual_height);
|
||||
|
||||
if ((hardwareTarget.virtual_width <= target->virtual_width
|
||||
&& hardwareTarget.virtual_height <= target->virtual_height
|
||||
&& hardwareTarget.space <= target->space)
|
||||
|| intel_propose_display_mode(&hardwareTarget, target, target)) {
|
||||
hardwareTarget = *target;
|
||||
} else
|
||||
needsScaling = true;
|
||||
} else {
|
||||
// We don't have EDID data, try to set the requested mode directly
|
||||
hardwareTarget = *target;
|
||||
}
|
||||
pll_divisors divisors;
|
||||
if (needsScaling)
|
||||
compute_pll_divisors(&hardwareTarget, &divisors, true);
|
||||
else
|
||||
compute_pll_divisors(target, &divisors, true);
|
||||
|
||||
uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED;
|
||||
if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
dpll |= LVDS_PLL_MODE_LVDS;
|
||||
// DPLL mode LVDS for i915+
|
||||
}
|
||||
|
||||
// Compute bitmask from p1 value
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
dpll |= (1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
|
||||
} else {
|
||||
dpll |= (1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT;
|
||||
}
|
||||
switch (divisors.post2) {
|
||||
case 5:
|
||||
case 7:
|
||||
dpll |= DISPLAY_PLL_DIVIDE_HIGH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable panel fitting, but enable 8 to 6-bit dithering
|
||||
write32(INTEL_PANEL_FIT_CONTROL, 0x4);
|
||||
// TODO: do not do this if the connected panel is 24-bit
|
||||
// (I don't know how to detect that)
|
||||
|
||||
if ((dpll & DISPLAY_PLL_ENABLED) != 0) {
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED);
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
spin(150);
|
||||
}
|
||||
|
||||
uint32 lvds = read32(INTEL_DIGITAL_LVDS_PORT) | LVDS_PORT_EN
|
||||
| LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
|
||||
|
||||
lvds |= LVDS_18BIT_DITHER;
|
||||
// TODO: do not do this if the connected panel is 24-bit
|
||||
// (I don't know how to detect that)
|
||||
|
||||
float referenceClock = gInfo->shared_info->pll_info.reference_frequency
|
||||
/ 1000.0f;
|
||||
|
||||
// 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 == LVDS_POST2_RATE_FAST)
|
||||
lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP;
|
||||
else
|
||||
lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP);
|
||||
|
||||
write32(INTEL_DIGITAL_LVDS_PORT, lvds);
|
||||
read32(INTEL_DIGITAL_LVDS_PORT);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll);
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
|
||||
// Wait for the clocks to stabilize
|
||||
spin(150);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
|
||||
float adjusted = ((referenceClock * divisors.m) / divisors.n)
|
||||
/ divisors.post;
|
||||
uint32 pixelMultiply;
|
||||
if (needsScaling) {
|
||||
pixelMultiply = uint32(adjusted
|
||||
/ (hardwareTarget.timing.pixel_clock / 1000.0f));
|
||||
} else {
|
||||
pixelMultiply = uint32(adjusted
|
||||
/ (target->timing.pixel_clock / 1000.0f));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24)
|
||||
| ((pixelMultiply - 1) << 8));
|
||||
} else
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll);
|
||||
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
spin(150);
|
||||
|
||||
// update timing parameters
|
||||
if (needsScaling) {
|
||||
// TODO: Alternatively, it should be possible to use the panel
|
||||
// fitter and scale the picture.
|
||||
|
||||
// TODO: Perform some sanity check, for example if the target is
|
||||
// wider than the hardware mode we end up with negative borders and
|
||||
// broken timings
|
||||
uint32 borderWidth = hardwareTarget.timing.h_display
|
||||
- target->timing.h_display;
|
||||
|
||||
uint32 syncWidth = hardwareTarget.timing.h_sync_end
|
||||
- hardwareTarget.timing.h_sync_start;
|
||||
|
||||
uint32 syncCenter = target->timing.h_display
|
||||
+ (hardwareTarget.timing.h_total
|
||||
- target->timing.h_display) / 2;
|
||||
|
||||
write32(INTEL_DISPLAY_B_HTOTAL,
|
||||
((uint32)(hardwareTarget.timing.h_total - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HBLANK,
|
||||
((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1)
|
||||
<< 16)
|
||||
| ((uint32)target->timing.h_display + borderWidth / 2 - 1));
|
||||
write32(INTEL_DISPLAY_B_HSYNC,
|
||||
((uint32)(syncCenter + syncWidth / 2 - 1) << 16)
|
||||
| ((uint32)syncCenter - syncWidth / 2 - 1));
|
||||
|
||||
uint32 borderHeight = hardwareTarget.timing.v_display
|
||||
- target->timing.v_display;
|
||||
|
||||
uint32 syncHeight = hardwareTarget.timing.v_sync_end
|
||||
- hardwareTarget.timing.v_sync_start;
|
||||
|
||||
syncCenter = target->timing.v_display
|
||||
+ (hardwareTarget.timing.v_total
|
||||
- target->timing.v_display) / 2;
|
||||
|
||||
write32(INTEL_DISPLAY_B_VTOTAL,
|
||||
((uint32)(hardwareTarget.timing.v_total - 1) << 16)
|
||||
| ((uint32)target->timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VBLANK,
|
||||
((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1)
|
||||
<< 16)
|
||||
| ((uint32)target->timing.v_display
|
||||
+ borderHeight / 2 - 1));
|
||||
write32(INTEL_DISPLAY_B_VSYNC,
|
||||
((uint32)(syncCenter + syncHeight / 2 - 1) << 16)
|
||||
| ((uint32)syncCenter - syncHeight / 2 - 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(0x61020, 0x00FF0000);
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_HTOTAL,
|
||||
((uint32)(target->timing.h_total - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HBLANK,
|
||||
((uint32)(target->timing.h_total - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HSYNC,
|
||||
((uint32)(target->timing.h_sync_end - 1) << 16)
|
||||
| ((uint32)target->timing.h_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_VTOTAL,
|
||||
((uint32)(target->timing.v_total - 1) << 16)
|
||||
| ((uint32)target->timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VBLANK,
|
||||
((uint32)(target->timing.v_total - 1) << 16)
|
||||
| ((uint32)target->timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VSYNC, (
|
||||
(uint32)(target->timing.v_sync_end - 1) << 16)
|
||||
| ((uint32)target->timing.v_sync_start - 1));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_IMAGE_SIZE,
|
||||
((uint32)(target->virtual_width - 1) << 16)
|
||||
| ((uint32)target->virtual_height - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_POS, 0);
|
||||
write32(INTEL_DISPLAY_B_PIPE_SIZE,
|
||||
((uint32)(target->timing.v_display - 1) << 16)
|
||||
| ((uint32)target->timing.h_display - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
|
||||
write32(INTEL_DISPLAY_B_PIPE_CONTROL,
|
||||
read32(INTEL_DISPLAY_B_PIPE_CONTROL) | INTEL_PIPE_ENABLED);
|
||||
read32(INTEL_DISPLAY_B_PIPE_CONTROL);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - DVI/SDVO/generic
|
||||
|
||||
|
||||
@ -282,7 +659,7 @@ HDMIPort::IsConnected()
|
||||
return false;
|
||||
|
||||
uint32 portRegister = _PortRegister();
|
||||
TRACE("%s %d: PortRegister: %" B_PRIx32 "\n", PortName(), PortIndex(),
|
||||
TRACE("%s - %d: PortRegister: %" B_PRIx32 "\n", PortName(), PortIndex(),
|
||||
portRegister);
|
||||
|
||||
if (portRegister == 0)
|
||||
|
@ -1,15 +1,20 @@
|
||||
/*
|
||||
* Copyright 2011, Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Michael Lotz, mmlr@mlotz.ch
|
||||
* Alexander von Gluck IV, kallisti5@unixzen.com
|
||||
*/
|
||||
#ifndef INTEL_PORTS_H
|
||||
#define INTEL_PORTS_H
|
||||
|
||||
|
||||
#include <edid.h>
|
||||
|
||||
#include "intel_extreme.h"
|
||||
#include "pll.h"
|
||||
|
||||
|
||||
#define MAX_PORTS 20 // a generous upper bound
|
||||
|
||||
@ -55,6 +60,9 @@ virtual status_t GetEDID(edid1_info* edid,
|
||||
|
||||
virtual status_t GetPLLLimits(pll_limits& limits);
|
||||
|
||||
virtual status_t SetDisplayMode(display_mode* mode,
|
||||
uint32 colorMode) { return B_ERROR; };
|
||||
|
||||
protected:
|
||||
void _SetName(const char* name);
|
||||
|
||||
@ -63,6 +71,8 @@ static status_t _GetI2CSignals(void* cookie, int* _clock,
|
||||
static status_t _SetI2CSignals(void* cookie, int clock,
|
||||
int data);
|
||||
|
||||
display_mode* fCurrentMode;
|
||||
|
||||
private:
|
||||
virtual addr_t _DDCRegister() = 0;
|
||||
|
||||
@ -83,6 +93,9 @@ virtual uint32 Type() const
|
||||
|
||||
virtual bool IsConnected();
|
||||
|
||||
virtual status_t SetDisplayMode(display_mode* mode,
|
||||
uint32 colorMode);
|
||||
|
||||
protected:
|
||||
virtual addr_t _DDCRegister();
|
||||
};
|
||||
@ -97,6 +110,9 @@ virtual uint32 Type() const
|
||||
|
||||
virtual bool IsConnected();
|
||||
|
||||
virtual status_t SetDisplayMode(display_mode* mode,
|
||||
uint32 colorMode);
|
||||
|
||||
protected:
|
||||
virtual addr_t _DDCRegister();
|
||||
};
|
||||
@ -113,6 +129,7 @@ virtual uint32 Type() const
|
||||
|
||||
virtual bool IsConnected();
|
||||
|
||||
|
||||
protected:
|
||||
virtual addr_t _DDCRegister();
|
||||
};
|
||||
|
@ -325,6 +325,8 @@ intel_init_accelerant(int device)
|
||||
} else
|
||||
delete analogPort;
|
||||
|
||||
gInfo->head_mode |= HEAD_MODE_TESTING;
|
||||
|
||||
// On TRACE, dump ports and states
|
||||
dump_ports();
|
||||
|
||||
|
@ -69,6 +69,7 @@ struct accelerant_info {
|
||||
display_mode lvds_panel_mode;
|
||||
};
|
||||
|
||||
|
||||
#define HEAD_MODE_A_ANALOG 0x0001
|
||||
#define HEAD_MODE_B_DIGITAL 0x0002
|
||||
#define HEAD_MODE_CLONE 0x0003
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "DisplayPipe.h"
|
||||
#include "FlexibleDisplayInterface.h"
|
||||
#endif
|
||||
#include "pll.h"
|
||||
#include "Ports.h"
|
||||
#include "utility.h"
|
||||
|
||||
@ -43,189 +44,6 @@
|
||||
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||
|
||||
|
||||
struct pll_divisors {
|
||||
uint32 post;
|
||||
uint32 post1;
|
||||
uint32 post2;
|
||||
bool post2_high;
|
||||
uint32 n;
|
||||
uint32 m;
|
||||
uint32 m1;
|
||||
uint32 m2;
|
||||
};
|
||||
|
||||
struct pll_limits {
|
||||
pll_divisors min;
|
||||
pll_divisors max;
|
||||
uint32 min_post2_frequency;
|
||||
uint32 min_vco;
|
||||
uint32 max_vco;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
get_pll_limits(pll_limits &limits)
|
||||
{
|
||||
// Note, the limits are taken from the X driver; they have not yet been
|
||||
// tested
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_VLV)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
static const pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 1, 79, 12, 5}, // min
|
||||
{ 80, 8, 5, true, 5, 127, 22, 9}, // max
|
||||
225000, 1760000, 3510000
|
||||
};
|
||||
limits = kLimits;
|
||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
static const pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 10, 1, 10, false, 1, 104, 17, 5}, // min
|
||||
{ 30, 3, 10, true, 4, 138, 23, 11}, // max
|
||||
270000, 1750000, 3500000
|
||||
};
|
||||
limits = kLimits;
|
||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
// m1 is reserved and must be 0
|
||||
static const pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 3, 2, 0, 2}, // min
|
||||
{ 80, 8, 5, true, 6, 256, 0, 256}, // max
|
||||
200000, 1700000, 3500000
|
||||
};
|
||||
limits = kLimits;
|
||||
} else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
// (Update: Output limits are adjusted in the computation (post2=7/14))
|
||||
// Should move them here!
|
||||
static const pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 5, 70, 12, 7}, // min
|
||||
{ 80, 8, 5, true, 10, 120, 22, 11}, // max
|
||||
200000, 1400000, 2800000
|
||||
};
|
||||
limits = kLimits;
|
||||
} else {
|
||||
// TODO: support LVDS output limits as well
|
||||
static const pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 4, 2, 4, false, 5, 96, 20, 8},
|
||||
{128, 33, 2, true, 18, 140, 28, 18},
|
||||
165000, 930000, 1400000
|
||||
};
|
||||
limits = kLimits;
|
||||
}
|
||||
|
||||
TRACE("PLL limits, min: p %" B_PRId32 " (p1 %" B_PRId32 ", "
|
||||
"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
|
||||
"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.min.post,
|
||||
limits.min.post1, limits.min.post2, limits.min.n, limits.min.m,
|
||||
limits.min.m1, limits.min.m2);
|
||||
TRACE("PLL limits, max: p %" B_PRId32 " (p1 %" B_PRId32 ", "
|
||||
"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
|
||||
"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.max.post,
|
||||
limits.max.post1, limits.max.post2, limits.max.n, limits.max.m,
|
||||
limits.max.m1, limits.max.m2);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
|
||||
{
|
||||
pll_info &info = gInfo->shared_info->pll_info;
|
||||
uint32 vco = info.reference_frequency * divisors.m / divisors.n;
|
||||
uint32 frequency = vco / divisors.post;
|
||||
|
||||
if (divisors.post < limits.min.post || divisors.post > limits.max.post
|
||||
|| divisors.m < limits.min.m || divisors.m > limits.max.m
|
||||
|| vco < limits.min_vco || vco > limits.max_vco
|
||||
|| frequency < info.min_frequency || frequency > info.max_frequency)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
compute_pll_divisors(const display_mode ¤t, pll_divisors& divisors,
|
||||
bool isLVDS)
|
||||
{
|
||||
float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
|
||||
float referenceClock
|
||||
= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
||||
pll_limits limits;
|
||||
get_pll_limits(limits);
|
||||
|
||||
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
||||
|
||||
if (isLVDS) {
|
||||
if ((read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
||||
== LVDS_CLKB_POWER_UP)
|
||||
divisors.post2 = LVDS_POST2_RATE_FAST;
|
||||
else
|
||||
divisors.post2 = LVDS_POST2_RATE_SLOW;
|
||||
} else {
|
||||
if (current.timing.pixel_clock < limits.min_post2_frequency) {
|
||||
// slow DAC timing
|
||||
divisors.post2 = limits.min.post2;
|
||||
divisors.post2_high = limits.min.post2_high;
|
||||
} else {
|
||||
// fast DAC timing
|
||||
divisors.post2 = limits.max.post2;
|
||||
divisors.post2_high = limits.max.post2_high;
|
||||
}
|
||||
}
|
||||
|
||||
float best = requestedPixelClock;
|
||||
pll_divisors bestDivisors;
|
||||
|
||||
bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD);
|
||||
for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1;
|
||||
divisors.m1++) {
|
||||
for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2
|
||||
&& ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) {
|
||||
for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
|
||||
divisors.n++) {
|
||||
for (divisors.post1 = limits.min.post1;
|
||||
divisors.post1 <= limits.max.post1; divisors.post1++) {
|
||||
divisors.m = 5 * divisors.m1 + divisors.m2;
|
||||
divisors.post = divisors.post1 * divisors.post2;
|
||||
|
||||
if (!valid_pll_divisors(divisors, limits))
|
||||
continue;
|
||||
|
||||
float error = fabs(requestedPixelClock
|
||||
- ((referenceClock * divisors.m) / divisors.n)
|
||||
/ divisors.post);
|
||||
if (error < best) {
|
||||
best = error;
|
||||
bestDivisors = divisors;
|
||||
|
||||
if (error == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
divisors = bestDivisors;
|
||||
|
||||
TRACE("%s: found: %g MHz, p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
|
||||
"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
|
||||
"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
|
||||
((referenceClock * divisors.m) / divisors.n) / divisors.post,
|
||||
divisors.post, divisors.post1, divisors.post2, divisors.n,
|
||||
divisors.m, divisors.m1, divisors.m2);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
retrieve_current_mode(display_mode& mode, uint32 pllRegister)
|
||||
{
|
||||
@ -280,7 +98,7 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister)
|
||||
}
|
||||
|
||||
pll_limits limits;
|
||||
get_pll_limits(limits);
|
||||
get_pll_limits(&limits);
|
||||
|
||||
if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
@ -789,256 +607,23 @@ if (first) {
|
||||
write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
|
||||
read32(INTEL_VGA_DISPLAY_CONTROL);
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
|
||||
// For LVDS panels, we actually always set the native mode in hardware
|
||||
// Then we use the panel fitter to scale the picture to that.
|
||||
display_mode hardwareTarget;
|
||||
bool needsScaling = false;
|
||||
// Go over each port and set the display mode
|
||||
for (uint32 i = 0; i < gInfo->port_count; i++) {
|
||||
if (gInfo->ports[i] == NULL)
|
||||
continue;
|
||||
if (!gInfo->ports[i]->IsConnected())
|
||||
continue;
|
||||
|
||||
// Try to get the panel preferred screen mode from EDID info
|
||||
if (gInfo->has_edid) {
|
||||
hardwareTarget.space = target.space;
|
||||
hardwareTarget.virtual_width
|
||||
= gInfo->edid_info.std_timing[0].h_size;
|
||||
hardwareTarget.virtual_height
|
||||
= gInfo->edid_info.std_timing[0].v_size;
|
||||
for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
|
||||
if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type
|
||||
== EDID1_IS_DETAILED_TIMING) {
|
||||
hardwareTarget.virtual_width = gInfo->edid_info
|
||||
.detailed_monitor[i].data.detailed_timing.h_active;
|
||||
hardwareTarget.virtual_height = gInfo->edid_info
|
||||
.detailed_monitor[i].data.detailed_timing.v_active;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TRACE("%s: hardware mode will actually be %dx%d\n", __func__,
|
||||
hardwareTarget.virtual_width, hardwareTarget.virtual_height);
|
||||
if ((hardwareTarget.virtual_width <= target.virtual_width
|
||||
&& hardwareTarget.virtual_height <= target.virtual_height
|
||||
&& hardwareTarget.space <= target.space)
|
||||
|| intel_propose_display_mode(&hardwareTarget, mode, mode)) {
|
||||
hardwareTarget = target;
|
||||
} else
|
||||
needsScaling = true;
|
||||
} else {
|
||||
// We don't have EDID data, try to set the requested mode directly
|
||||
hardwareTarget = target;
|
||||
status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
|
||||
if (status != B_OK)
|
||||
ERROR("%s: Unable to set display mode!\n", __func__);
|
||||
}
|
||||
|
||||
pll_divisors divisors;
|
||||
if (needsScaling)
|
||||
compute_pll_divisors(hardwareTarget, divisors, true);
|
||||
else
|
||||
compute_pll_divisors(target, divisors, true);
|
||||
|
||||
uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED;
|
||||
if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
dpll |= LVDS_PLL_MODE_LVDS;
|
||||
// DPLL mode LVDS for i915+
|
||||
}
|
||||
|
||||
// Compute bitmask from p1 value
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
dpll |= (1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
|
||||
} else {
|
||||
dpll |= (1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT;
|
||||
}
|
||||
switch (divisors.post2) {
|
||||
case 5:
|
||||
case 7:
|
||||
dpll |= DISPLAY_PLL_DIVIDE_HIGH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable panel fitting, but enable 8 to 6-bit dithering
|
||||
write32(INTEL_PANEL_FIT_CONTROL, 0x4);
|
||||
// TODO: do not do this if the connected panel is 24-bit
|
||||
// (I don't know how to detect that)
|
||||
|
||||
if ((dpll & DISPLAY_PLL_ENABLED) != 0) {
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED);
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
spin(150);
|
||||
}
|
||||
|
||||
uint32 lvds = read32(INTEL_DIGITAL_LVDS_PORT) | LVDS_PORT_EN
|
||||
| LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
|
||||
|
||||
lvds |= LVDS_18BIT_DITHER;
|
||||
// TODO: do not do this if the connected panel is 24-bit
|
||||
// (I don't know how to detect that)
|
||||
|
||||
float referenceClock = gInfo->shared_info->pll_info.reference_frequency
|
||||
/ 1000.0f;
|
||||
|
||||
// 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 == LVDS_POST2_RATE_FAST)
|
||||
lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP;
|
||||
else
|
||||
lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP);
|
||||
|
||||
write32(INTEL_DIGITAL_LVDS_PORT, lvds);
|
||||
read32(INTEL_DIGITAL_LVDS_PORT);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll);
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
|
||||
// Wait for the clocks to stabilize
|
||||
spin(150);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
|
||||
float adjusted = ((referenceClock * divisors.m) / divisors.n)
|
||||
/ divisors.post;
|
||||
uint32 pixelMultiply;
|
||||
if (needsScaling) {
|
||||
pixelMultiply = uint32(adjusted
|
||||
/ (hardwareTarget.timing.pixel_clock / 1000.0f));
|
||||
} else {
|
||||
pixelMultiply = uint32(adjusted
|
||||
/ (target.timing.pixel_clock / 1000.0f));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24)
|
||||
| ((pixelMultiply - 1) << 8));
|
||||
} else
|
||||
write32(INTEL_DISPLAY_B_PLL, dpll);
|
||||
|
||||
read32(INTEL_DISPLAY_B_PLL);
|
||||
spin(150);
|
||||
|
||||
// update timing parameters
|
||||
if (needsScaling) {
|
||||
// TODO: Alternatively, it should be possible to use the panel
|
||||
// fitter and scale the picture.
|
||||
|
||||
// TODO: Perform some sanity check, for example if the target is
|
||||
// wider than the hardware mode we end up with negative borders and
|
||||
// broken timings
|
||||
uint32 borderWidth = hardwareTarget.timing.h_display
|
||||
- target.timing.h_display;
|
||||
|
||||
uint32 syncWidth = hardwareTarget.timing.h_sync_end
|
||||
- hardwareTarget.timing.h_sync_start;
|
||||
|
||||
uint32 syncCenter = target.timing.h_display
|
||||
+ (hardwareTarget.timing.h_total
|
||||
- target.timing.h_display) / 2;
|
||||
|
||||
write32(INTEL_DISPLAY_B_HTOTAL,
|
||||
((uint32)(hardwareTarget.timing.h_total - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HBLANK,
|
||||
((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1)
|
||||
<< 16)
|
||||
| ((uint32)target.timing.h_display + borderWidth / 2 - 1));
|
||||
write32(INTEL_DISPLAY_B_HSYNC,
|
||||
((uint32)(syncCenter + syncWidth / 2 - 1) << 16)
|
||||
| ((uint32)syncCenter - syncWidth / 2 - 1));
|
||||
|
||||
uint32 borderHeight = hardwareTarget.timing.v_display
|
||||
- target.timing.v_display;
|
||||
|
||||
uint32 syncHeight = hardwareTarget.timing.v_sync_end
|
||||
- hardwareTarget.timing.v_sync_start;
|
||||
|
||||
syncCenter = target.timing.v_display
|
||||
+ (hardwareTarget.timing.v_total
|
||||
- target.timing.v_display) / 2;
|
||||
|
||||
write32(INTEL_DISPLAY_B_VTOTAL,
|
||||
((uint32)(hardwareTarget.timing.v_total - 1) << 16)
|
||||
| ((uint32)target.timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VBLANK,
|
||||
((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1)
|
||||
<< 16)
|
||||
| ((uint32)target.timing.v_display
|
||||
+ borderHeight / 2 - 1));
|
||||
write32(INTEL_DISPLAY_B_VSYNC,
|
||||
((uint32)(syncCenter + syncHeight / 2 - 1) << 16)
|
||||
| ((uint32)syncCenter - syncHeight / 2 - 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(0x61020, 0x00FF0000);
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_B_HTOTAL,
|
||||
((uint32)(target.timing.h_total - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HBLANK,
|
||||
((uint32)(target.timing.h_total - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_B_HSYNC,
|
||||
((uint32)(target.timing.h_sync_end - 1) << 16)
|
||||
| ((uint32)target.timing.h_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_VTOTAL,
|
||||
((uint32)(target.timing.v_total - 1) << 16)
|
||||
| ((uint32)target.timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VBLANK,
|
||||
((uint32)(target.timing.v_total - 1) << 16)
|
||||
| ((uint32)target.timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_B_VSYNC, (
|
||||
(uint32)(target.timing.v_sync_end - 1) << 16)
|
||||
| ((uint32)target.timing.v_sync_start - 1));
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_B_IMAGE_SIZE,
|
||||
((uint32)(target.virtual_width - 1) << 16)
|
||||
| ((uint32)target.virtual_height - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_POS, 0);
|
||||
write32(INTEL_DISPLAY_B_PIPE_SIZE,
|
||||
((uint32)(target.timing.v_display - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
|
||||
write32(INTEL_DISPLAY_B_PIPE_CONTROL,
|
||||
read32(INTEL_DISPLAY_B_PIPE_CONTROL) | INTEL_PIPE_ENABLED);
|
||||
read32(INTEL_DISPLAY_B_PIPE_CONTROL);
|
||||
}
|
||||
// RIP DIGITAL / LVDS (strange..)
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_STIPPI) != 0) {
|
||||
pll_divisors divisors;
|
||||
compute_pll_divisors(target, divisors, false);
|
||||
compute_pll_divisors(&target, &divisors, false);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
|
||||
@ -1124,119 +709,7 @@ if (first) {
|
||||
| colorMode);
|
||||
}
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_A_ANALOG) != 0) {
|
||||
pll_divisors divisors;
|
||||
compute_pll_divisors(target, divisors, false);
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
|
||||
(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
|
||||
} else {
|
||||
write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
|
||||
(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_N_DIVISOR_MASK)
|
||||
| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M1_DIVISOR_MASK)
|
||||
| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_M2_DIVISOR_MASK));
|
||||
}
|
||||
|
||||
uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
|
||||
if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
pll |= ((1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
|
||||
} else {
|
||||
pll |= ((1 << (divisors.post1 - 1))
|
||||
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||
// pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
// & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||
}
|
||||
if (divisors.post2_high)
|
||||
pll |= DISPLAY_PLL_DIVIDE_HIGH;
|
||||
|
||||
pll |= DISPLAY_PLL_MODE_ANALOG;
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x))
|
||||
pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
|
||||
} else {
|
||||
if (!divisors.post2_high)
|
||||
pll |= DISPLAY_PLL_DIVIDE_4X;
|
||||
|
||||
pll |= DISPLAY_PLL_2X_CLOCK;
|
||||
|
||||
if (divisors.post1 > 2) {
|
||||
pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||
& DISPLAY_PLL_POST1_DIVISOR_MASK;
|
||||
} else
|
||||
pll |= DISPLAY_PLL_POST1_DIVIDE_2;
|
||||
}
|
||||
|
||||
write32(INTEL_DISPLAY_A_PLL, pll);
|
||||
read32(INTEL_DISPLAY_A_PLL);
|
||||
spin(150);
|
||||
write32(INTEL_DISPLAY_A_PLL, pll);
|
||||
read32(INTEL_DISPLAY_A_PLL);
|
||||
spin(150);
|
||||
|
||||
// update timing parameters
|
||||
write32(INTEL_DISPLAY_A_HTOTAL,
|
||||
((uint32)(target.timing.h_total - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_A_HBLANK,
|
||||
((uint32)(target.timing.h_total - 1) << 16)
|
||||
| ((uint32)target.timing.h_display - 1));
|
||||
write32(INTEL_DISPLAY_A_HSYNC,
|
||||
((uint32)(target.timing.h_sync_end - 1) << 16)
|
||||
| ((uint32)target.timing.h_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_A_VTOTAL,
|
||||
((uint32)(target.timing.v_total - 1) << 16)
|
||||
| ((uint32)target.timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_A_VBLANK,
|
||||
((uint32)(target.timing.v_total - 1) << 16)
|
||||
| ((uint32)target.timing.v_display - 1));
|
||||
write32(INTEL_DISPLAY_A_VSYNC,
|
||||
((uint32)(target.timing.v_sync_end - 1) << 16)
|
||||
| ((uint32)target.timing.v_sync_start - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_A_IMAGE_SIZE,
|
||||
((uint32)(target.virtual_width - 1) << 16)
|
||||
| ((uint32)target.virtual_height - 1));
|
||||
|
||||
write32(INTEL_ANALOG_PORT, (read32(INTEL_ANALOG_PORT)
|
||||
& ~(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));
|
||||
|
||||
// TODO: verify the two comments below: the X driver doesn't seem to
|
||||
// care about both of them!
|
||||
|
||||
// These two have to be set for display B, too - this obviously means
|
||||
// that the second head always must adopt the color space of the first
|
||||
// head.
|
||||
write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
|
||||
write32(INTEL_DISPLAY_B_IMAGE_SIZE,
|
||||
((uint32)(target.virtual_width - 1) << 16)
|
||||
| ((uint32)target.virtual_height - 1));
|
||||
|
||||
write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
|
||||
& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
|
||||
| colorMode);
|
||||
}
|
||||
}
|
||||
// RIP ANALOG
|
||||
|
||||
if ((gInfo->head_mode & HEAD_MODE_TESTING) == 0) {
|
||||
set_display_power_mode(sharedInfo.dpms_mode);
|
||||
@ -1378,4 +851,3 @@ intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
|
||||
write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
|
||||
}
|
||||
}
|
||||
|
||||
|
201
src/add-ons/accelerants/intel_extreme/pll.cpp
Normal file
201
src/add-ons/accelerants/intel_extreme/pll.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
* Alexander von Gluck IV, kallisti5@unixzen.com
|
||||
*/
|
||||
|
||||
|
||||
#include "pll.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
#include <create_display_modes.h>
|
||||
#include <ddc.h>
|
||||
#include <edid.h>
|
||||
#include <validate_display_mode.h>
|
||||
|
||||
#include "accelerant_protos.h"
|
||||
#include "accelerant.h"
|
||||
#include "utility.h"
|
||||
|
||||
|
||||
#undef TRACE
|
||||
#define TRACE_MODE
|
||||
#ifdef TRACE_MODE
|
||||
# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x)
|
||||
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||
|
||||
|
||||
void
|
||||
get_pll_limits(pll_limits* limits)
|
||||
{
|
||||
// Note, the limits are taken from the X driver; they have not yet been
|
||||
// tested
|
||||
|
||||
if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB)
|
||||
|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_VLV)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 1, 79, 12, 5}, // min
|
||||
{ 80, 8, 5, true, 5, 127, 22, 9}, // max
|
||||
225000, 1760000, 3510000
|
||||
};
|
||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 10, 1, 10, false, 1, 104, 17, 5}, // min
|
||||
{ 30, 3, 10, true, 4, 138, 23, 11}, // max
|
||||
270000, 1750000, 3500000
|
||||
};
|
||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
// m1 is reserved and must be 0
|
||||
pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 3, 2, 0, 2}, // min
|
||||
{ 80, 8, 5, true, 6, 256, 0, 256}, // max
|
||||
200000, 1700000, 3500000
|
||||
};
|
||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
||||
} else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
|
||||
// TODO: support LVDS output limits as well
|
||||
// (Update: Output limits are adjusted in the computation (post2=7/14))
|
||||
// Should move them here!
|
||||
pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 5, 1, 10, false, 5, 70, 12, 7}, // min
|
||||
{ 80, 8, 5, true, 10, 120, 22, 11}, // max
|
||||
200000, 1400000, 2800000
|
||||
};
|
||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
||||
} else {
|
||||
// TODO: support LVDS output limits as well
|
||||
static pll_limits kLimits = {
|
||||
// p, p1, p2, high, n, m, m1, m2
|
||||
{ 4, 2, 4, false, 5, 96, 20, 8},
|
||||
{128, 33, 2, true, 18, 140, 28, 18},
|
||||
165000, 930000, 1400000
|
||||
};
|
||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
||||
}
|
||||
|
||||
TRACE("PLL limits, min: p %" B_PRId32 " (p1 %" B_PRId32 ", "
|
||||
"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
|
||||
"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits->min.post,
|
||||
limits->min.post1, limits->min.post2, limits->min.n, limits->min.m,
|
||||
limits->min.m1, limits->min.m2);
|
||||
TRACE("PLL limits, max: p %" B_PRId32 " (p1 %" B_PRId32 ", "
|
||||
"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
|
||||
"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits->max.post,
|
||||
limits->max.post1, limits->max.post2, limits->max.n, limits->max.m,
|
||||
limits->max.m1, limits->max.m2);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
valid_pll_divisors(pll_divisors* divisors, pll_limits* limits)
|
||||
{
|
||||
pll_info &info = gInfo->shared_info->pll_info;
|
||||
uint32 vco = info.reference_frequency * divisors->m / divisors->n;
|
||||
uint32 frequency = vco / divisors->post;
|
||||
|
||||
if (divisors->post < limits->min.post || divisors->post > limits->max.post
|
||||
|| divisors->m < limits->min.m || divisors->m > limits->max.m
|
||||
|| vco < limits->min_vco || vco > limits->max_vco
|
||||
|| frequency < info.min_frequency || frequency > info.max_frequency)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
||||
bool isLVDS)
|
||||
{
|
||||
float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
|
||||
float referenceClock
|
||||
= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
||||
pll_limits limits;
|
||||
get_pll_limits(&limits);
|
||||
|
||||
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
||||
|
||||
if (isLVDS) {
|
||||
if ((read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
||||
== LVDS_CLKB_POWER_UP)
|
||||
divisors->post2 = LVDS_POST2_RATE_FAST;
|
||||
else
|
||||
divisors->post2 = LVDS_POST2_RATE_SLOW;
|
||||
} else {
|
||||
if (current->timing.pixel_clock < limits.min_post2_frequency) {
|
||||
// slow DAC timing
|
||||
divisors->post2 = limits.min.post2;
|
||||
divisors->post2_high = limits.min.post2_high;
|
||||
} else {
|
||||
// fast DAC timing
|
||||
divisors->post2 = limits.max.post2;
|
||||
divisors->post2_high = limits.max.post2_high;
|
||||
}
|
||||
}
|
||||
|
||||
float best = requestedPixelClock;
|
||||
pll_divisors bestDivisors;
|
||||
|
||||
bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD);
|
||||
for (divisors->m1 = limits.min.m1; divisors->m1 <= limits.max.m1;
|
||||
divisors->m1++) {
|
||||
for (divisors->m2 = limits.min.m2; divisors->m2 <= limits.max.m2
|
||||
&& ((divisors->m2 < divisors->m1) || is_igd); divisors->m2++) {
|
||||
for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
|
||||
divisors->n++) {
|
||||
for (divisors->post1 = limits.min.post1;
|
||||
divisors->post1 <= limits.max.post1; divisors->post1++) {
|
||||
divisors->m = 5 * divisors->m1 + divisors->m2;
|
||||
divisors->post = divisors->post1 * divisors->post2;
|
||||
|
||||
if (!valid_pll_divisors(divisors, &limits))
|
||||
continue;
|
||||
|
||||
float error = fabs(requestedPixelClock
|
||||
- ((referenceClock * divisors->m) / divisors->n)
|
||||
/ divisors->post);
|
||||
if (error < best) {
|
||||
best = error;
|
||||
bestDivisors = *divisors;
|
||||
|
||||
if (error == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*divisors = bestDivisors;
|
||||
|
||||
TRACE("%s: found: %g MHz, p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
|
||||
"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
|
||||
"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
|
||||
((referenceClock * divisors->m) / divisors->n) / divisors->post,
|
||||
divisors->post, divisors->post1, divisors->post2, divisors->n,
|
||||
divisors->m, divisors->m1, divisors->m2);
|
||||
}
|
42
src/add-ons/accelerants/intel_extreme/pll.h
Normal file
42
src/add-ons/accelerants/intel_extreme/pll.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
* Alexander von Gluck IV, kallisti5@unixzen.com
|
||||
*/
|
||||
#ifndef INTEL_EXTREME_PLL_H
|
||||
#define INTEL_EXTREME_PLL_H
|
||||
|
||||
|
||||
#include "intel_extreme.h"
|
||||
|
||||
|
||||
struct pll_divisors {
|
||||
uint32 post;
|
||||
uint32 post1;
|
||||
uint32 post2;
|
||||
bool post2_high;
|
||||
uint32 n;
|
||||
uint32 m;
|
||||
uint32 m1;
|
||||
uint32 m2;
|
||||
};
|
||||
|
||||
struct pll_limits {
|
||||
pll_divisors min;
|
||||
pll_divisors max;
|
||||
uint32 min_post2_frequency;
|
||||
uint32 min_vco;
|
||||
uint32 max_vco;
|
||||
};
|
||||
|
||||
|
||||
void get_pll_limits(pll_limits* limits);
|
||||
bool valid_pll_divisors(pll_divisors* divisors, pll_limits* limits);
|
||||
void compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
||||
bool isLVDS);
|
||||
|
||||
|
||||
#endif /* INTEL_EXTREME_PLL_H */
|
Loading…
Reference in New Issue
Block a user