diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index a9555d1e350..8d1734e923e 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -80,6 +80,7 @@ set(KERNEL_SOURCES Graphics/Intel/Plane/G33DisplayPlane.cpp Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp Graphics/Intel/Transcoder/DisplayTranscoder.cpp + Graphics/Intel/Transcoder/PLL.cpp Graphics/Intel/DisplayConnectorGroup.cpp Graphics/Intel/NativeDisplayConnector.cpp Graphics/Intel/NativeGraphicsAdapter.cpp diff --git a/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp b/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp index 4d3ba51cb50..b803ac5edac 100644 --- a/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp +++ b/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp @@ -13,115 +13,12 @@ #include #include #include +#include #include #include namespace Kernel { -namespace IntelGraphics { - -static constexpr PLLMaxSettings G35Limits { - { 20'000'000, 400'000'000 }, // values in Hz, dot_clock - { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO - { 3, 8 }, // n - { 70, 120 }, // m - { 10, 20 }, // m1 - { 5, 9 }, // m2 - { 5, 80 }, // p - { 1, 8 }, // p1 - { 5, 10 } // p2 -}; -} - -static bool check_pll_settings(IntelGraphics::PLLSettings const& settings, size_t reference_clock, IntelGraphics::PLLMaxSettings const& limits) -{ - if (settings.n < limits.n.min || settings.n > limits.n.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); - return false; - } - if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); - return false; - } - if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); - return false; - } - if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); - return false; - } - - if (settings.m1 <= settings.m2) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); - return false; - } - - auto m = settings.compute_m(); - auto p = settings.compute_p(); - - if (m < limits.m.min || m > limits.m.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); - return false; - } - if (p < limits.p.min || p > limits.p.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); - return false; - } - - auto dot = settings.compute_dot_clock(reference_clock); - auto vco = settings.compute_vco(reference_clock); - - if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); - return false; - } - if (vco < limits.vco.min || vco > limits.vco.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); - return false; - } - return true; -} - -static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) -{ - if (target_frequency >= checked_frequency) - return target_frequency - checked_frequency; - return checked_frequency - target_frequency; -} - -Optional IntelDisplayConnectorGroup::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits) -{ - IntelGraphics::PLLSettings settings; - IntelGraphics::PLLSettings best_settings; - // FIXME: Is this correct for all Intel Native graphics cards? - settings.p2 = 10; - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); - u64 best_difference = 0xffffffff; - for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { - for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { - for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { - for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - if (!check_pll_settings(settings, reference_clock, limits)) - continue; - auto current_dot_clock = settings.compute_dot_clock(reference_clock); - if (current_dot_clock == target_frequency) - return settings; - auto difference = find_absolute_difference(target_frequency, current_dot_clock); - if (difference < best_difference && (current_dot_clock > target_frequency)) { - best_settings = settings; - best_difference = difference; - } - } - } - } - } - if (best_settings.is_valid()) - return best_settings; - return {}; -} - ErrorOr> IntelDisplayConnectorGroup::try_create(Badge, IntelGraphics::Generation generation, MMIORegion const& first_region, MMIORegion const& second_region) { auto registers_region = TRY(MM.allocate_kernel_region(first_region.pci_bar_paddr, first_region.pci_bar_space_length, "Intel Native Graphics Registers"sv, Memory::Region::Access::ReadWrite)); @@ -323,7 +220,7 @@ bool IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSettin GraphicsManagement::the().disable_vga_emulation_access_permanently(); auto dac_multiplier = compute_dac_multiplier(mode_setting.pixel_clock_in_khz); - auto pll_settings = create_pll_settings((1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000, IntelGraphics::G35Limits); + auto pll_settings = create_pll_settings(m_generation, (1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000); if (!pll_settings.has_value()) return false; auto settings = pll_settings.value(); diff --git a/Kernel/Graphics/Intel/DisplayConnectorGroup.h b/Kernel/Graphics/Intel/DisplayConnectorGroup.h index d9024454bcc..4895b0d3170 100644 --- a/Kernel/Graphics/Intel/DisplayConnectorGroup.h +++ b/Kernel/Graphics/Intel/DisplayConnectorGroup.h @@ -75,8 +75,6 @@ private: void disable_dac_output(); void enable_dac_output(); - Optional create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&); - Spinlock m_control_lock; Spinlock m_modeset_lock; mutable Spinlock m_registers_lock; diff --git a/Kernel/Graphics/Intel/Transcoder/PLL.cpp b/Kernel/Graphics/Intel/Transcoder/PLL.cpp new file mode 100644 index 00000000000..e6a967a449a --- /dev/null +++ b/Kernel/Graphics/Intel/Transcoder/PLL.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel::IntelGraphics { + +static constexpr PLLMaxSettings g35limits { + { 20'000'000, 400'000'000 }, // values in Hz, dot_clock + { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO + { 3, 8 }, // n + { 70, 120 }, // m + { 10, 20 }, // m1 + { 5, 9 }, // m2 + { 5, 80 }, // p + { 1, 8 }, // p1 + { 5, 10 } // p2 +}; + +PLLMaxSettings const& pll_max_settings_for_generation(Generation generation) +{ + switch (generation) { + case Generation::Gen4: + return g35limits; + default: + VERIFY_NOT_REACHED(); + } +} + +static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) +{ + if (target_frequency >= checked_frequency) + return target_frequency - checked_frequency; + return checked_frequency - target_frequency; +} + +Optional create_pll_settings(Generation generation, u64 target_frequency, u64 reference_clock) +{ + PLLSettings settings {}; + PLLSettings best_settings {}; + auto& limits = pll_max_settings_for_generation(generation); + // FIXME: Is this correct for all Intel Native graphics cards? + settings.p2 = 10; + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); + u64 best_difference = 0xffffffff; + for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { + for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { + for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { + for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); + if (!check_pll_settings(settings, reference_clock, limits)) + continue; + auto current_dot_clock = settings.compute_dot_clock(reference_clock); + if (current_dot_clock == target_frequency) + return settings; + auto difference = find_absolute_difference(target_frequency, current_dot_clock); + if (difference < best_difference && (current_dot_clock > target_frequency)) { + best_settings = settings; + best_difference = difference; + } + } + } + } + } + if (best_settings.is_valid()) + return best_settings; + return {}; +} + +bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits) +{ + if (settings.n < limits.n.min || settings.n > limits.n.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); + return false; + } + if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); + return false; + } + if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); + return false; + } + if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); + return false; + } + + if (settings.m1 <= settings.m2) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); + return false; + } + + auto m = settings.compute_m(); + auto p = settings.compute_p(); + + if (m < limits.m.min || m > limits.m.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); + return false; + } + if (p < limits.p.min || p > limits.p.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); + return false; + } + + auto dot = settings.compute_dot_clock(reference_clock); + auto vco = settings.compute_vco(reference_clock); + + if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); + return false; + } + if (vco < limits.vco.min || vco > limits.vco.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); + return false; + } + return true; +} + +} diff --git a/Kernel/Graphics/Intel/Transcoder/PLL.h b/Kernel/Graphics/Intel/Transcoder/PLL.h new file mode 100644 index 00000000000..1d62f82da79 --- /dev/null +++ b/Kernel/Graphics/Intel/Transcoder/PLL.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::IntelGraphics { + +PLLMaxSettings const& pll_max_settings_for_generation(Generation); +Optional create_pll_settings(Generation, u64 target_frequency, u64 reference_clock); +bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits); + +}