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:
Alexander von Gluck IV 2015-10-25 20:56:08 -05:00
parent e747cbe116
commit b3f14fb7c7
9 changed files with 661 additions and 549 deletions

View File

@ -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

View File

@ -16,6 +16,7 @@ Addon intel_extreme.accelerant :
mode.cpp
overlay.cpp
# overlay_3d_i965.cpp
pll.cpp
# classes
# DisplayPipe.cpp
# FlexibleDisplayInterface.cpp

View File

@ -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)

View File

@ -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();
};

View File

@ -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();

View File

@ -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

View File

@ -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 &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);
}
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);
}
}

View 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);
}

View 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 */