intel_extreme: Rework PLL calculation
* More like linux, improved G4x calculations * Reduce un-needed pll limit complexity * Improved pll limits on ports based on type
This commit is contained in:
@@ -239,7 +239,7 @@ Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
|
|||||||
float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
||||||
|
|
||||||
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
|
||||||
float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.post;
|
float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p;
|
||||||
uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f));
|
uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f));
|
||||||
write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
|
write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
|
||||||
}
|
}
|
||||||
@@ -273,34 +273,33 @@ Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
|
|||||||
uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
|
uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
|
||||||
|
|
||||||
if (gInfo->shared_info->device_type.Generation() >= 3) {
|
if (gInfo->shared_info->device_type.Generation() >= 3) {
|
||||||
// post1 divisor << 1 , 1-8
|
// p1 divisor << 1 , 1-8
|
||||||
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
|
||||||
pll |= ((1 << (divisors.post1 - 1))
|
pll |= ((1 << (divisors.p1 - 1))
|
||||||
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
|
<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
|
||||||
& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
|
& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
|
||||||
} else {
|
} else {
|
||||||
pll |= ((1 << (divisors.post1 - 1))
|
pll |= ((1 << (divisors.p1 - 1))
|
||||||
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||||
& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||||
// pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
// pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||||
// & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
// & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// p2 clock divider. 5 or 7 high
|
if (divisors.p2 == 5 || divisors.p2 == 7)
|
||||||
if (divisors.post2_high)
|
|
||||||
pll |= DISPLAY_PLL_DIVIDE_HIGH;
|
pll |= DISPLAY_PLL_DIVIDE_HIGH;
|
||||||
|
|
||||||
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
|
||||||
pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
|
pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
|
||||||
} else {
|
} else {
|
||||||
if (!divisors.post2_high)
|
if (divisors.p2 != 5 && divisors.p2 != 7)
|
||||||
pll |= DISPLAY_PLL_DIVIDE_4X;
|
pll |= DISPLAY_PLL_DIVIDE_4X;
|
||||||
|
|
||||||
pll |= DISPLAY_PLL_2X_CLOCK;
|
pll |= DISPLAY_PLL_2X_CLOCK;
|
||||||
|
|
||||||
// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
|
// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
|
||||||
if (divisors.post1 > 2) {
|
if (divisors.p1 > 2) {
|
||||||
pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
|
||||||
& DISPLAY_PLL_POST1_DIVISOR_MASK;
|
& DISPLAY_PLL_POST1_DIVISOR_MASK;
|
||||||
} else
|
} else
|
||||||
pll |= DISPLAY_PLL_POST1_DIVIDE_2;
|
pll |= DISPLAY_PLL_POST1_DIVIDE_2;
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ 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.p2 == 5 || divisors.p2 == 7) {
|
||||||
TRACE("LVDS: dual channel\n");
|
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 {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
|
* Copyright 2006-2016, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -38,117 +38,146 @@
|
|||||||
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||||
|
|
||||||
|
|
||||||
void
|
// Static pll limits taken from Linux kernel KMS
|
||||||
get_pll_limits(pll_limits* limits, bool isLVDS)
|
|
||||||
|
static pll_limits kLimitsIlkDac = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 5, 2, 14, 1, 79, 12, 5}, // min
|
||||||
|
{ 80, 8, 14, 3, 118, 22, 9}, // max
|
||||||
|
225000, 1760000, 3510000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsIlkLvdsSingle = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 28, 2, 14, 1, 79, 12, 5}, // min
|
||||||
|
{112, 8, 14, 3, 118, 22, 9}, // max
|
||||||
|
225000, 1760000, 3510000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsIlkLvdsDual = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 14, 2, 7, 1, 79, 12, 5}, // min
|
||||||
|
{ 56, 8, 7, 3, 127, 22, 9}, // max
|
||||||
|
225000, 1760000, 3510000
|
||||||
|
};
|
||||||
|
|
||||||
|
// 100Mhz RefClock
|
||||||
|
static pll_limits kLimitsIlkLvdsSingle100 = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 28, 2, 14, 1, 79, 12, 5}, // min
|
||||||
|
{112, 8, 14, 2, 126, 22, 9}, // max
|
||||||
|
225000, 1760000, 3510000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsIlkLvdsDual100 = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 14, 2, 7, 1, 79, 12, 5}, // min
|
||||||
|
{ 42, 6, 7, 3, 126, 22, 9}, // max
|
||||||
|
225000, 1760000, 3510000
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static pll_limits kLimitsChv = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 0, 2, 14, 1, 79, 2, 24 << 22}, // min
|
||||||
|
{ 0, 4, 1, 1, 127, 2, 175 << 22}, // max
|
||||||
|
0, 4800000, 6480000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsVlv = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 0, 2, 20, 1, 79, 2, 11}, // min
|
||||||
|
{ 0, 3, 2, 7, 127, 3, 156}, // max
|
||||||
|
0, 4000000, 6000000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsBxt = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 0, 2, 1, 1, 0, 2, 2 << 22}, // min
|
||||||
|
{ 0, 4, 20, 1, 0, 2, 255 << 22}, // max
|
||||||
|
0, 4800000, 6700000
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static pll_limits kLimits9xxSdvo = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 5, 1, 10, 5, 70, 12, 7}, // min
|
||||||
|
{ 80, 8, 5, 10, 120, 22, 11}, // max
|
||||||
|
200000, 1400000, 2800000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimits9xxLvds = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 7, 1, 14, 1, 70, 8, 3}, // min
|
||||||
|
{ 98, 8, 7, 6, 120, 18, 7}, // max
|
||||||
|
112000, 1400000, 2800000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsG4xSdvo = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 10, 1, 10, 1, 104, 17, 5}, // min
|
||||||
|
{ 30, 3, 10, 4, 138, 23, 11}, // max
|
||||||
|
270000, 1750000, 3500000
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static pll_limits kLimitsG4xHdmi = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 5, 1, 10, 1, 104, 16, 5}, // min
|
||||||
|
{ 80, 8, 5, 4, 138, 23, 11}, // max
|
||||||
|
165000, 1750000, 3500000
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static pll_limits kLimitsG4xLvdsSingle = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 28, 2, 14, 1, 104, 17, 5}, // min
|
||||||
|
{ 112, 8, 14, 3, 138, 23, 11}, // max
|
||||||
|
0, 1750000, 3500000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsG4xLvdsDual = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 14, 2, 7, 1, 104, 17, 5}, // min
|
||||||
|
{ 42, 6, 7, 3, 138, 23, 11}, // max
|
||||||
|
0, 1750000, 3500000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsPinSdvo = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 5, 1, 10, 3, 2, 0, 0}, // min
|
||||||
|
{ 80, 8, 5, 6, 256, 0, 254}, // max
|
||||||
|
200000, 1700000, 3500000
|
||||||
|
};
|
||||||
|
|
||||||
|
static pll_limits kLimitsPinLvds = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 7, 1, 14, 3, 2, 0, 0}, // min
|
||||||
|
{112, 8, 14, 6, 256, 0, 254}, // max
|
||||||
|
112000, 1700000, 3500000
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static pll_limits kLimits = {
|
||||||
|
// p, p1, p2, n, m, m1, m2
|
||||||
|
{ 4, 2, 4, 5, 96, 20, 8},
|
||||||
|
{128, 33, 2, 18, 140, 28, 18},
|
||||||
|
165000, 930000, 1400000
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
lvds_dual_link(display_mode* current)
|
||||||
{
|
{
|
||||||
// Note, the limits are taken from the X driver; they have not yet been
|
float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
|
||||||
// tested
|
if (requestedPixelClock > 112.999)
|
||||||
|
return true;
|
||||||
|
|
||||||
// TODO: Breakout BXT
|
// TODO: Force dual link on MacBookPro6,2 MacBookPro8,2 MacBookPro9,1
|
||||||
|
|
||||||
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) {
|
return ((read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
||||||
pll_limits kLimits = {
|
== LVDS_CLKB_POWER_UP);
|
||||||
// p, p1, p2, high, n, m, m1, m2
|
|
||||||
{ 5, 2, 14, false, 1, 79, 2, 24 << 22}, // min
|
|
||||||
{ 80, 4, 1, true, 1, 127, 2, 175 << 22}, // max
|
|
||||||
225000, 4800000, 6480000
|
|
||||||
};
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) {
|
|
||||||
pll_limits kLimits = {
|
|
||||||
// p, p1, p2, high, n, m, m1, m2
|
|
||||||
{ 5, 2, 20, false, 1, 79, 2, 11}, // min
|
|
||||||
{ 80, 3, 2, true, 7, 127, 3, 156}, // max
|
|
||||||
225000, 4000000, 6000000
|
|
||||||
};
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_SER5)
|
|
||||||
|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_BXT)) {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
// TODO: validate these LVDS dividers!
|
|
||||||
if (isLVDS) {
|
|
||||||
kLimits.min.post = 7;
|
|
||||||
kLimits.max.post = 98;
|
|
||||||
kLimits.min.post2 = 14;
|
|
||||||
kLimits.max.post2 = 7;
|
|
||||||
}
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_9xx)) {
|
|
||||||
pll_limits kLimits = {
|
|
||||||
// p, p1, p2, high, n, m, m1, m2
|
|
||||||
{ 5, 1, 10, false, 1, 70, 8, 3}, // min
|
|
||||||
{ 80, 8, 5, true, 6, 120, 18, 7}, // max
|
|
||||||
200000, 1400000, 2800000
|
|
||||||
};
|
|
||||||
if (isLVDS) {
|
|
||||||
kLimits.min.post = 7;
|
|
||||||
kLimits.max.post = 98;
|
|
||||||
kLimits.min.post2 = 14;
|
|
||||||
kLimits.max.post2 = 7;
|
|
||||||
}
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)) {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
// TODO: validate these LVDS dividers!
|
|
||||||
if (isLVDS) {
|
|
||||||
kLimits.min.post = 7;
|
|
||||||
kLimits.max.post = 98;
|
|
||||||
kLimits.min.post2 = 14;
|
|
||||||
kLimits.max.post2 = 7;
|
|
||||||
}
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
|
|
||||||
// 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, 254}, // max
|
|
||||||
200000, 1700000, 3500000
|
|
||||||
};
|
|
||||||
if (isLVDS) {
|
|
||||||
kLimits.min.post = 7;
|
|
||||||
kLimits.max.post = 112;
|
|
||||||
kLimits.min.post2 = 14;
|
|
||||||
kLimits.max.post2 = 14;
|
|
||||||
}
|
|
||||||
memcpy(limits, &kLimits, sizeof(pll_limits));
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
// TODO: Validate these LVDS dividers!
|
|
||||||
if (isLVDS) {
|
|
||||||
kLimits.min.post = 7;
|
|
||||||
kLimits.max.post = 98;
|
|
||||||
kLimits.min.post2 = 14;
|
|
||||||
kLimits.max.post2 = 7;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -157,9 +186,9 @@ valid_pll_divisors(pll_divisors* divisors, pll_limits* limits)
|
|||||||
{
|
{
|
||||||
pll_info &info = gInfo->shared_info->pll_info;
|
pll_info &info = gInfo->shared_info->pll_info;
|
||||||
uint32 vco = info.reference_frequency * divisors->m / divisors->n;
|
uint32 vco = info.reference_frequency * divisors->m / divisors->n;
|
||||||
uint32 frequency = vco / divisors->post;
|
uint32 frequency = vco / divisors->p;
|
||||||
|
|
||||||
if (divisors->post < limits->min.post || divisors->post > limits->max.post
|
if (divisors->p < limits->min.p || divisors->p > limits->max.p
|
||||||
|| divisors->m < limits->min.m || divisors->m > limits->max.m
|
|| divisors->m < limits->min.m || divisors->m > limits->max.m
|
||||||
|| vco < limits->min_vco || vco > limits->max_vco
|
|| vco < limits->min_vco || vco > limits->max_vco
|
||||||
|| frequency < info.min_frequency || frequency > info.max_frequency)
|
|| frequency < info.min_frequency || frequency > info.max_frequency)
|
||||||
@@ -169,6 +198,30 @@ valid_pll_divisors(pll_divisors* divisors, pll_limits* limits)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
compute_pll_p2(display_mode* current, pll_divisors* divisors,
|
||||||
|
pll_limits* limits, bool isLVDS)
|
||||||
|
{
|
||||||
|
if (isLVDS) {
|
||||||
|
if (lvds_dual_link(current)) {
|
||||||
|
// fast DAC timing via 2 channels (dual link LVDS)
|
||||||
|
divisors->p2 = limits->max.p2;
|
||||||
|
} else {
|
||||||
|
// slow DAC timing
|
||||||
|
divisors->p2 = limits->min.p2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current->timing.pixel_clock < limits->dot_limit) {
|
||||||
|
// slow DAC timing
|
||||||
|
divisors->p2 = limits->min.p2;
|
||||||
|
} else {
|
||||||
|
// fast DAC timing
|
||||||
|
divisors->p2 = limits->max.p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint32
|
static uint32
|
||||||
compute_pll_m(pll_divisors* divisors)
|
compute_pll_m(pll_divisors* divisors)
|
||||||
{
|
{
|
||||||
@@ -194,68 +247,160 @@ compute_pll_m(pll_divisors* divisors)
|
|||||||
static uint32
|
static uint32
|
||||||
compute_pll_p(pll_divisors* divisors)
|
compute_pll_p(pll_divisors* divisors)
|
||||||
{
|
{
|
||||||
return divisors->post1 * divisors->post2;
|
return divisors->p1 * divisors->p2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
compute_dpll_g4x(display_mode* current, pll_divisors* divisors, bool isLVDS)
|
||||||
bool isLVDS)
|
|
||||||
{
|
{
|
||||||
float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
|
float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
|
||||||
float referenceClock
|
float referenceClock
|
||||||
= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
|
||||||
pll_limits limits;
|
|
||||||
get_pll_limits(&limits, isLVDS);
|
|
||||||
|
|
||||||
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
||||||
|
|
||||||
// Calculate p2
|
pll_limits limits;
|
||||||
if (isLVDS) {
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)) {
|
||||||
if (requestedPixelClock > 112.999
|
// TODO: Pass port type via video_configuration
|
||||||
|| (read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
if (isLVDS) {
|
||||||
== LVDS_CLKB_POWER_UP) {
|
if (lvds_dual_link(current))
|
||||||
// fast DAC timing via 2 channels
|
memcpy(&limits, &kLimitsG4xLvdsDual, sizeof(pll_limits));
|
||||||
divisors->post2 = limits.max.post2;
|
else
|
||||||
divisors->post2_high = limits.max.post2_high;
|
memcpy(&limits, &kLimitsG4xLvdsSingle, sizeof(pll_limits));
|
||||||
} else {
|
//} else if (type == INTEL_PORT_TYPE_HDMI) {
|
||||||
// slow DAC timing
|
// memcpy(&limits, &kLimitsG4xHdmi, sizeof(pll_limits));
|
||||||
divisors->post2 = limits.min.post2;
|
} else
|
||||||
divisors->post2_high = limits.min.post2_high;
|
memcpy(&limits, &kLimitsG4xSdvo, sizeof(pll_limits));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (current->timing.pixel_clock < limits.min_post2_frequency) {
|
if (isLVDS) {
|
||||||
// slow DAC timing
|
if (lvds_dual_link(current)) {
|
||||||
divisors->post2 = limits.min.post2;
|
if (referenceClock == 100.0)
|
||||||
divisors->post2_high = limits.min.post2_high;
|
memcpy(&limits, &kLimitsIlkLvdsDual100, sizeof(pll_limits));
|
||||||
|
else
|
||||||
|
memcpy(&limits, &kLimitsIlkLvdsDual, sizeof(pll_limits));
|
||||||
|
} else {
|
||||||
|
if (referenceClock == 100.0) {
|
||||||
|
memcpy(&limits, &kLimitsIlkLvdsSingle100,
|
||||||
|
sizeof(pll_limits));
|
||||||
|
} else {
|
||||||
|
memcpy(&limits, &kLimitsIlkLvdsSingle, sizeof(pll_limits));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// fast DAC timing
|
memcpy(&limits, &kLimitsIlkDac, sizeof(pll_limits));
|
||||||
divisors->post2 = limits.max.post2;
|
|
||||||
divisors->post2_high = limits.max.post2_high;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compute_pll_p2(current, divisors, &limits, isLVDS);
|
||||||
|
|
||||||
|
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.p,
|
||||||
|
limits.min.p1, limits.min.p2, 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.p,
|
||||||
|
limits.max.p1, limits.max.p2, limits.max.n, limits.max.m,
|
||||||
|
limits.max.m1, limits.max.m2);
|
||||||
|
|
||||||
float best = requestedPixelClock;
|
float best = requestedPixelClock;
|
||||||
pll_divisors bestDivisors;
|
pll_divisors bestDivisors;
|
||||||
|
|
||||||
bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN);
|
uint32 maxn = limits.max.n;
|
||||||
for (divisors->m1 = limits.min.m1; divisors->m1 <= limits.max.m1;
|
for (divisors->n = limits.min.n; divisors->n <= maxn; divisors->n++) {
|
||||||
divisors->m1++) {
|
for (divisors->m1 = limits.max.m1; divisors->m1 >= limits.min.m1;
|
||||||
for (divisors->m2 = limits.min.m2; divisors->m2 <= limits.max.m2
|
divisors->m1--) {
|
||||||
&& ((divisors->m2 < divisors->m1) || is_pine); divisors->m2++) {
|
for (divisors->m2 = limits.max.m2; divisors->m2 >= limits.min.m2;
|
||||||
for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
|
divisors->m2--) {
|
||||||
divisors->n++) {
|
for (divisors->p1 = limits.max.p1;
|
||||||
for (divisors->post1 = limits.min.post1;
|
divisors->p1 >= limits.max.p1; divisors->p1--) {
|
||||||
divisors->post1 <= limits.max.post1; divisors->post1++) {
|
|
||||||
divisors->m = compute_pll_m(divisors);
|
divisors->m = compute_pll_m(divisors);
|
||||||
divisors->post = compute_pll_p(divisors);
|
divisors->p = compute_pll_p(divisors);
|
||||||
|
|
||||||
if (!valid_pll_divisors(divisors, &limits))
|
if (!valid_pll_divisors(divisors, &limits))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float error = fabs(requestedPixelClock
|
float error = fabs(requestedPixelClock
|
||||||
- ((referenceClock * divisors->m) / divisors->n)
|
- ((referenceClock * divisors->m) / divisors->n)
|
||||||
/ divisors->post);
|
/ divisors->p);
|
||||||
|
if (error < best) {
|
||||||
|
best = error;
|
||||||
|
bestDivisors = *divisors;
|
||||||
|
maxn = divisors->n;
|
||||||
|
|
||||||
|
if (error == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*divisors = bestDivisors;
|
||||||
|
TRACE("%s: best MHz: %g (error: %g)\n", __func__,
|
||||||
|
((referenceClock * divisors->m) / divisors->n) / divisors->p,
|
||||||
|
best);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
compute_dpll_9xx(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;
|
||||||
|
|
||||||
|
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
||||||
|
|
||||||
|
pll_limits limits;
|
||||||
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
|
||||||
|
if (isLVDS)
|
||||||
|
memcpy(&limits, &kLimitsPinLvds, sizeof(pll_limits));
|
||||||
|
else
|
||||||
|
memcpy(&limits, &kLimitsPinSdvo, sizeof(pll_limits));
|
||||||
|
} else {
|
||||||
|
if (isLVDS)
|
||||||
|
memcpy(&limits, &kLimits9xxLvds, sizeof(pll_limits));
|
||||||
|
else
|
||||||
|
memcpy(&limits, &kLimits9xxSdvo, sizeof(pll_limits));
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_pll_p2(current, divisors, &limits, isLVDS);
|
||||||
|
|
||||||
|
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.p,
|
||||||
|
limits.min.p1, limits.min.p2, 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.p,
|
||||||
|
limits.max.p1, limits.max.p2, limits.max.n, limits.max.m,
|
||||||
|
limits.max.m1, limits.max.m2);
|
||||||
|
|
||||||
|
bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN);
|
||||||
|
|
||||||
|
float best = requestedPixelClock;
|
||||||
|
pll_divisors bestDivisors;
|
||||||
|
|
||||||
|
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_pine); divisors->m2++) {
|
||||||
|
for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
|
||||||
|
divisors->n++) {
|
||||||
|
for (divisors->p1 = limits.min.p1;
|
||||||
|
divisors->p1 <= limits.max.p1; divisors->p1++) {
|
||||||
|
divisors->m = compute_pll_m(divisors);
|
||||||
|
divisors->p = compute_pll_p(divisors);
|
||||||
|
|
||||||
|
if (!valid_pll_divisors(divisors, &limits))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float error = fabs(requestedPixelClock
|
||||||
|
- ((referenceClock * divisors->m) / divisors->n)
|
||||||
|
/ divisors->p);
|
||||||
if (error < best) {
|
if (error < best) {
|
||||||
best = error;
|
best = error;
|
||||||
bestDivisors = *divisors;
|
bestDivisors = *divisors;
|
||||||
@@ -270,10 +415,28 @@ compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
|||||||
|
|
||||||
*divisors = bestDivisors;
|
*divisors = bestDivisors;
|
||||||
|
|
||||||
TRACE("%s: found: %g MHz, p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
|
TRACE("%s: best MHz: %g (error: %g)\n", __func__,
|
||||||
|
((referenceClock * divisors->m) / divisors->n) / divisors->p,
|
||||||
|
best);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
compute_pll_divisors(display_mode* current, pll_divisors* divisors, bool isLVDS)
|
||||||
|
{
|
||||||
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)
|
||||||
|
|| gInfo->shared_info->device_type.HasPlatformControlHub()) {
|
||||||
|
compute_dpll_g4x(current, divisors, isLVDS);
|
||||||
|
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) {
|
||||||
|
// TODO: CherryView
|
||||||
|
} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) {
|
||||||
|
// TODO: VallyView
|
||||||
|
} else
|
||||||
|
compute_dpll_9xx(current, divisors, isLVDS);
|
||||||
|
|
||||||
|
TRACE("%s: found: p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
|
||||||
"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
|
"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
|
||||||
"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
|
"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
|
||||||
((referenceClock * divisors->m) / divisors->n) / divisors->post,
|
divisors->p, divisors->p1, divisors->p2, divisors->n,
|
||||||
divisors->post, divisors->post1, divisors->post2, divisors->n,
|
|
||||||
divisors->m, divisors->m1, divisors->m2);
|
divisors->m, divisors->m1, divisors->m2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,9 @@
|
|||||||
|
|
||||||
|
|
||||||
struct pll_divisors {
|
struct pll_divisors {
|
||||||
uint32 post;
|
uint32 p;
|
||||||
uint32 post1;
|
uint32 p1;
|
||||||
uint32 post2;
|
uint32 p2;
|
||||||
bool post2_high;
|
|
||||||
uint32 n;
|
uint32 n;
|
||||||
uint32 m;
|
uint32 m;
|
||||||
uint32 m1;
|
uint32 m1;
|
||||||
@@ -27,13 +26,12 @@ struct pll_divisors {
|
|||||||
struct pll_limits {
|
struct pll_limits {
|
||||||
pll_divisors min;
|
pll_divisors min;
|
||||||
pll_divisors max;
|
pll_divisors max;
|
||||||
uint32 min_post2_frequency;
|
uint32 dot_limit;
|
||||||
uint32 min_vco;
|
uint32 min_vco;
|
||||||
uint32 max_vco;
|
uint32 max_vco;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void get_pll_limits(pll_limits* limits, bool isLVDS);
|
|
||||||
bool valid_pll_divisors(pll_divisors* divisors, pll_limits* limits);
|
bool valid_pll_divisors(pll_divisors* divisors, pll_limits* limits);
|
||||||
void compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
void compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
||||||
bool isLVDS);
|
bool isLVDS);
|
||||||
|
|||||||
Reference in New Issue
Block a user