|
|
|
@@ -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.
|
|
|
|
|
*
|
|
|
|
|
* Authors:
|
|
|
|
@@ -38,117 +38,146 @@
|
|
|
|
|
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
get_pll_limits(pll_limits* limits, bool isLVDS)
|
|
|
|
|
// Static pll limits taken from Linux kernel KMS
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
// tested
|
|
|
|
|
float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
|
|
|
|
|
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)) {
|
|
|
|
|
pll_limits kLimits = {
|
|
|
|
|
// 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);
|
|
|
|
|
return ((read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
|
|
|
|
== LVDS_CLKB_POWER_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -157,9 +186,9 @@ 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;
|
|
|
|
|
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
|
|
|
|
|
|| vco < limits->min_vco || vco > limits->max_vco
|
|
|
|
|
|| 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
|
|
|
|
|
compute_pll_m(pll_divisors* divisors)
|
|
|
|
|
{
|
|
|
|
@@ -194,68 +247,160 @@ compute_pll_m(pll_divisors* divisors)
|
|
|
|
|
static uint32
|
|
|
|
|
compute_pll_p(pll_divisors* divisors)
|
|
|
|
|
{
|
|
|
|
|
return divisors->post1 * divisors->post2;
|
|
|
|
|
return divisors->p1 * divisors->p2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
|
|
|
|
bool isLVDS)
|
|
|
|
|
static void
|
|
|
|
|
compute_dpll_g4x(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, isLVDS);
|
|
|
|
|
|
|
|
|
|
TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
|
|
|
|
|
|
|
|
|
|
// Calculate p2
|
|
|
|
|
if (isLVDS) {
|
|
|
|
|
if (requestedPixelClock > 112.999
|
|
|
|
|
|| (read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
|
|
|
|
|
== LVDS_CLKB_POWER_UP) {
|
|
|
|
|
// fast DAC timing via 2 channels
|
|
|
|
|
divisors->post2 = limits.max.post2;
|
|
|
|
|
divisors->post2_high = limits.max.post2_high;
|
|
|
|
|
} else {
|
|
|
|
|
// slow DAC timing
|
|
|
|
|
divisors->post2 = limits.min.post2;
|
|
|
|
|
divisors->post2_high = limits.min.post2_high;
|
|
|
|
|
}
|
|
|
|
|
pll_limits limits;
|
|
|
|
|
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)) {
|
|
|
|
|
// TODO: Pass port type via video_configuration
|
|
|
|
|
if (isLVDS) {
|
|
|
|
|
if (lvds_dual_link(current))
|
|
|
|
|
memcpy(&limits, &kLimitsG4xLvdsDual, sizeof(pll_limits));
|
|
|
|
|
else
|
|
|
|
|
memcpy(&limits, &kLimitsG4xLvdsSingle, sizeof(pll_limits));
|
|
|
|
|
//} else if (type == INTEL_PORT_TYPE_HDMI) {
|
|
|
|
|
// memcpy(&limits, &kLimitsG4xHdmi, sizeof(pll_limits));
|
|
|
|
|
} else
|
|
|
|
|
memcpy(&limits, &kLimitsG4xSdvo, sizeof(pll_limits));
|
|
|
|
|
} 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;
|
|
|
|
|
if (isLVDS) {
|
|
|
|
|
if (lvds_dual_link(current)) {
|
|
|
|
|
if (referenceClock == 100.0)
|
|
|
|
|
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 {
|
|
|
|
|
// fast DAC timing
|
|
|
|
|
divisors->post2 = limits.max.post2;
|
|
|
|
|
divisors->post2_high = limits.max.post2_high;
|
|
|
|
|
memcpy(&limits, &kLimitsIlkDac, 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);
|
|
|
|
|
|
|
|
|
|
float best = requestedPixelClock;
|
|
|
|
|
pll_divisors bestDivisors;
|
|
|
|
|
|
|
|
|
|
bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN);
|
|
|
|
|
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->post1 = limits.min.post1;
|
|
|
|
|
divisors->post1 <= limits.max.post1; divisors->post1++) {
|
|
|
|
|
uint32 maxn = limits.max.n;
|
|
|
|
|
for (divisors->n = limits.min.n; divisors->n <= maxn; divisors->n++) {
|
|
|
|
|
for (divisors->m1 = limits.max.m1; divisors->m1 >= limits.min.m1;
|
|
|
|
|
divisors->m1--) {
|
|
|
|
|
for (divisors->m2 = limits.max.m2; divisors->m2 >= limits.min.m2;
|
|
|
|
|
divisors->m2--) {
|
|
|
|
|
for (divisors->p1 = limits.max.p1;
|
|
|
|
|
divisors->p1 >= limits.max.p1; divisors->p1--) {
|
|
|
|
|
divisors->m = compute_pll_m(divisors);
|
|
|
|
|
divisors->post = compute_pll_p(divisors);
|
|
|
|
|
divisors->p = compute_pll_p(divisors);
|
|
|
|
|
|
|
|
|
|
if (!valid_pll_divisors(divisors, &limits))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
float error = fabs(requestedPixelClock
|
|
|
|
|
- ((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) {
|
|
|
|
|
best = error;
|
|
|
|
|
bestDivisors = *divisors;
|
|
|
|
@@ -270,10 +415,28 @@ compute_pll_divisors(display_mode* current, pll_divisors* divisors,
|
|
|
|
|
|
|
|
|
|
*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 " "
|
|
|
|
|
"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
|
|
|
|
|
((referenceClock * divisors->m) / divisors->n) / divisors->post,
|
|
|
|
|
divisors->post, divisors->post1, divisors->post2, divisors->n,
|
|
|
|
|
divisors->p, divisors->p1, divisors->p2, divisors->n,
|
|
|
|
|
divisors->m, divisors->m1, divisors->m2);
|
|
|
|
|
}
|
|
|
|
|