diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index ab6aaa74e7..2f9fef534c 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -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 diff --git a/src/add-ons/accelerants/intel_extreme/Jamfile b/src/add-ons/accelerants/intel_extreme/Jamfile index 701d2881fc..5d2ea96c66 100644 --- a/src/add-ons/accelerants/intel_extreme/Jamfile +++ b/src/add-ons/accelerants/intel_extreme/Jamfile @@ -16,6 +16,7 @@ Addon intel_extreme.accelerant : mode.cpp overlay.cpp # overlay_3d_i965.cpp + pll.cpp # classes # DisplayPipe.cpp # FlexibleDisplayInterface.cpp diff --git a/src/add-ons/accelerants/intel_extreme/Ports.cpp b/src/add-ons/accelerants/intel_extreme/Ports.cpp index 25999f24cb..d6140106e5 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.cpp +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -11,13 +11,14 @@ #include "Ports.h" -#include #include - #include #include +#include +#include #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) diff --git a/src/add-ons/accelerants/intel_extreme/Ports.h b/src/add-ons/accelerants/intel_extreme/Ports.h index 69994dbe65..13e4fd281a 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.h +++ b/src/add-ons/accelerants/intel_extreme/Ports.h @@ -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 +#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(); }; diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.cpp b/src/add-ons/accelerants/intel_extreme/accelerant.cpp index c5a4d64d0c..6df884b4d8 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.cpp +++ b/src/add-ons/accelerants/intel_extreme/accelerant.cpp @@ -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(); diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.h b/src/add-ons/accelerants/intel_extreme/accelerant.h index e8f958e27c..0f3f6d8904 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.h +++ b/src/add-ons/accelerants/intel_extreme/accelerant.h @@ -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 diff --git a/src/add-ons/accelerants/intel_extreme/mode.cpp b/src/add-ons/accelerants/intel_extreme/mode.cpp index 06999fff70..b459cee9cb 100644 --- a/src/add-ons/accelerants/intel_extreme/mode.cpp +++ b/src/add-ons/accelerants/intel_extreme/mode.cpp @@ -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; - } - - 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); + status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); + if (status != B_OK) + ERROR("%s: Unable to set display mode!\n", __func__); } + // 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); } } - diff --git a/src/add-ons/accelerants/intel_extreme/pll.cpp b/src/add-ons/accelerants/intel_extreme/pll.cpp new file mode 100644 index 0000000000..e70129a8c7 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/pll.cpp @@ -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 +#include +#include + +#include + +#include +#include +#include +#include + +#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); +} diff --git a/src/add-ons/accelerants/intel_extreme/pll.h b/src/add-ons/accelerants/intel_extreme/pll.h new file mode 100644 index 0000000000..9e8e306fb8 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/pll.h @@ -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 */