Procházet zdrojové kódy

Kernel/Graphics: Migrate Intel driver to use the DisplayConnector design

Liav A před 3 roky
rodič
revize
728358c599

+ 1 - 0
Kernel/CMakeLists.txt

@@ -85,6 +85,7 @@ set(KERNEL_SOURCES
     Graphics/DisplayConnector.cpp
     Graphics/FramebufferDevice.cpp
     Graphics/GraphicsManagement.cpp
+    Graphics/Intel/NativeDisplayConnector.cpp
     Graphics/Intel/NativeGraphicsAdapter.cpp
     Graphics/VGA/ISAAdapter.cpp
     Graphics/VGA/PCIAdapter.cpp

+ 732 - 0
Kernel/Graphics/Intel/NativeDisplayConnector.cpp

@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Arch/x86/IO.h>
+#include <Kernel/Bus/PCI/API.h>
+#include <Kernel/Debug.h>
+#include <Kernel/Devices/DeviceManagement.h>
+#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
+#include <Kernel/Graphics/Intel/NativeDisplayConnector.h>
+#include <Kernel/Memory/Region.h>
+
+namespace Kernel {
+
+namespace IntelGraphics {
+
+#define DDC2_I2C_ADDRESS 0x50
+
+struct PLLSettings {
+    bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); }
+    u64 compute_dot_clock(u64 refclock) const
+    {
+        return (refclock * (5 * m1 + m2) / n) / (p1 * p2);
+    }
+
+    u64 compute_vco(u64 refclock) const
+    {
+        return refclock * (5 * m1 + m2) / n;
+    }
+
+    u64 compute_m() const
+    {
+        return 5 * m1 + m2;
+    }
+
+    u64 compute_p() const
+    {
+        return p1 * p2;
+    }
+    u64 n { 0 };
+    u64 m1 { 0 };
+    u64 m2 { 0 };
+    u64 p1 { 0 };
+    u64 p2 { 0 };
+};
+
+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 Graphics::Modesetting calculate_modesetting_from_edid(EDID::Parser& edid, size_t index)
+{
+    auto details = edid.detailed_timing(index).release_value();
+
+    Graphics::Modesetting mode;
+    VERIFY(details.pixel_clock_khz());
+    mode.pixel_clock_in_khz = details.pixel_clock_khz();
+
+    size_t horizontal_active = details.horizontal_addressable_pixels();
+    size_t horizontal_sync_offset = details.horizontal_front_porch_pixels();
+
+    mode.horizontal.active = horizontal_active;
+    mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
+    mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels();
+    mode.horizontal.total = horizontal_active + details.horizontal_blanking_pixels();
+
+    size_t vertical_active = details.vertical_addressable_lines();
+    size_t vertical_sync_offset = details.vertical_front_porch_lines();
+
+    mode.vertical.active = vertical_active;
+    mode.vertical.sync_start = vertical_active + vertical_sync_offset;
+    mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines();
+    mode.vertical.total = vertical_active + details.vertical_blanking_lines();
+    return mode;
+}
+
+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<IntelGraphics::PLLSettings> IntelNativeDisplayConnector::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 {};
+}
+
+NonnullRefPtr<IntelNativeDisplayConnector> IntelNativeDisplayConnector::must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length)
+{
+    auto registers_region = MUST(MM.allocate_kernel_region(PhysicalAddress(registers_region_address), registers_region_length, "Intel Native Graphics Registers", Memory::Region::Access::ReadWrite));
+    auto device_or_error = DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, move(registers_region));
+    VERIFY(!device_or_error.is_error());
+    auto connector = device_or_error.release_value();
+    MUST(connector->initialize_gmbus_settings_and_read_edid());
+    // Note: This is very important to set the resolution to something safe so we
+    // can create a framebuffer console with valid resolution.
+    {
+        SpinlockLocker control_lock(connector->m_control_lock);
+        MUST(connector->set_safe_mode_setting());
+    }
+    MUST(connector->create_attached_framebuffer_console());
+    return connector;
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::initialize_gmbus_settings_and_read_edid()
+{
+    gmbus_read_edid();
+    return {};
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::set_y_offset(size_t)
+{
+    return Error::from_errno(ENOTIMPL);
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::unblank()
+{
+    return Error::from_errno(ENOTIMPL);
+}
+
+ErrorOr<size_t> IntelNativeDisplayConnector::write_to_first_surface(u64 offset, UserOrKernelBuffer const& buffer, size_t length)
+{
+    VERIFY(m_control_lock.is_locked());
+    if (offset + length > m_framebuffer_region->size())
+        return Error::from_errno(EOVERFLOW);
+    TRY(buffer.read(m_framebuffer_data + offset, 0, length));
+    return length;
+}
+
+void IntelNativeDisplayConnector::enable_console()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_framebuffer_console);
+    m_framebuffer_console->enable();
+}
+
+void IntelNativeDisplayConnector::disable_console()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_framebuffer_console);
+    m_framebuffer_console->disable();
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface()
+{
+    return Error::from_errno(ENOTSUP);
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console()
+{
+    auto rounded_size = TRY(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride));
+    m_framebuffer_region = TRY(MM.allocate_kernel_region(m_framebuffer_address.page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite));
+    [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true);
+    m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr();
+
+    m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address, m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride);
+    GraphicsManagement::the().set_console(*m_framebuffer_console);
+    return {};
+}
+
+IntelNativeDisplayConnector::IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region)
+    : DisplayConnector()
+    , m_framebuffer_address(framebuffer_address)
+    , m_registers_region(move(registers_region))
+{
+    {
+        SpinlockLocker control_lock(m_control_lock);
+        set_gmbus_default_rate();
+        set_gmbus_pin_pair(IntelGraphics::GMBusPinPair::DedicatedAnalog);
+    }
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&)
+{
+    return Error::from_errno(ENOTIMPL);
+}
+
+ErrorOr<void> IntelNativeDisplayConnector::set_safe_mode_setting()
+{
+    set_safe_crt_resolution();
+    return {};
+}
+
+void IntelNativeDisplayConnector::enable_vga_plane()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+}
+
+[[maybe_unused]] static StringView convert_register_index_to_string(IntelGraphics::RegisterIndex index)
+{
+    switch (index) {
+    case IntelGraphics::RegisterIndex::PipeAConf:
+        return "PipeAConf"sv;
+    case IntelGraphics::RegisterIndex::PipeBConf:
+        return "PipeBConf"sv;
+    case IntelGraphics::RegisterIndex::GMBusData:
+        return "GMBusData"sv;
+    case IntelGraphics::RegisterIndex::GMBusStatus:
+        return "GMBusStatus"sv;
+    case IntelGraphics::RegisterIndex::GMBusCommand:
+        return "GMBusCommand"sv;
+    case IntelGraphics::RegisterIndex::GMBusClock:
+        return "GMBusClock"sv;
+    case IntelGraphics::RegisterIndex::DisplayPlaneAControl:
+        return "DisplayPlaneAControl"sv;
+    case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset:
+        return "DisplayPlaneALinearOffset"sv;
+    case IntelGraphics::RegisterIndex::DisplayPlaneAStride:
+        return "DisplayPlaneAStride"sv;
+    case IntelGraphics::RegisterIndex::DisplayPlaneASurface:
+        return "DisplayPlaneASurface"sv;
+    case IntelGraphics::RegisterIndex::DPLLDivisorA0:
+        return "DPLLDivisorA0"sv;
+    case IntelGraphics::RegisterIndex::DPLLDivisorA1:
+        return "DPLLDivisorA1"sv;
+    case IntelGraphics::RegisterIndex::DPLLControlA:
+        return "DPLLControlA"sv;
+    case IntelGraphics::RegisterIndex::DPLLControlB:
+        return "DPLLControlB"sv;
+    case IntelGraphics::RegisterIndex::DPLLMultiplierA:
+        return "DPLLMultiplierA"sv;
+    case IntelGraphics::RegisterIndex::HTotalA:
+        return "HTotalA"sv;
+    case IntelGraphics::RegisterIndex::HBlankA:
+        return "HBlankA"sv;
+    case IntelGraphics::RegisterIndex::HSyncA:
+        return "HSyncA"sv;
+    case IntelGraphics::RegisterIndex::VTotalA:
+        return "VTotalA"sv;
+    case IntelGraphics::RegisterIndex::VBlankA:
+        return "VBlankA"sv;
+    case IntelGraphics::RegisterIndex::VSyncA:
+        return "VSyncA"sv;
+    case IntelGraphics::RegisterIndex::PipeASource:
+        return "PipeASource"sv;
+    case IntelGraphics::RegisterIndex::AnalogDisplayPort:
+        return "AnalogDisplayPort"sv;
+    case IntelGraphics::RegisterIndex::VGADisplayPlaneControl:
+        return "VGADisplayPlaneControl"sv;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+void IntelNativeDisplayConnector::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const
+{
+    VERIFY(m_control_lock.is_locked());
+    SpinlockLocker lock(m_registers_lock);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_register_index_to_string(index), value);
+    auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr();
+    *reg = value;
+}
+u32 IntelNativeDisplayConnector::read_from_register(IntelGraphics::RegisterIndex index) const
+{
+    VERIFY(m_control_lock.is_locked());
+    SpinlockLocker lock(m_registers_lock);
+    auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr();
+    u32 value = *reg;
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_register_index_to_string(index), value);
+    return value;
+}
+
+bool IntelNativeDisplayConnector::pipe_a_enabled() const
+{
+    VERIFY(m_control_lock.is_locked());
+    return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
+}
+
+bool IntelNativeDisplayConnector::pipe_b_enabled() const
+{
+    VERIFY(m_control_lock.is_locked());
+    return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30);
+}
+
+bool IntelNativeDisplayConnector::gmbus_wait_for(IntelGraphics::GMBusStatus desired_status, Optional<size_t> milliseconds_timeout)
+{
+    VERIFY(m_control_lock.is_locked());
+    size_t milliseconds_passed = 0;
+    while (1) {
+        if (milliseconds_timeout.has_value() && milliseconds_timeout.value() < milliseconds_passed)
+            return false;
+        full_memory_barrier();
+        u32 status = read_from_register(IntelGraphics::RegisterIndex::GMBusStatus);
+        full_memory_barrier();
+        VERIFY(!(status & (1 << 10))); // error happened
+        switch (desired_status) {
+        case IntelGraphics::GMBusStatus::HardwareReady:
+            if (status & (1 << 11))
+                return true;
+            break;
+        case IntelGraphics::GMBusStatus::TransactionCompletion:
+            if (status & (1 << 14))
+                return true;
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+        }
+        IO::delay(1000);
+        milliseconds_passed++;
+    }
+}
+
+void IntelNativeDisplayConnector::gmbus_write(unsigned address, u32 byte)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(address < 256);
+    full_memory_barrier();
+    write_to_register(IntelGraphics::RegisterIndex::GMBusData, byte);
+    full_memory_barrier();
+    write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, ((address << 1) | (1 << 16) | (IntelGraphics::GMBusCycle::Wait << 25) | (1 << 30)));
+    full_memory_barrier();
+    gmbus_wait_for(IntelGraphics::GMBusStatus::TransactionCompletion, {});
+}
+void IntelNativeDisplayConnector::gmbus_read(unsigned address, u8* buf, size_t length)
+{
+    VERIFY(address < 256);
+    VERIFY(m_control_lock.is_locked());
+    size_t nread = 0;
+    auto read_set = [&] {
+        full_memory_barrier();
+        u32 data = read_from_register(IntelGraphics::RegisterIndex::GMBusData);
+        full_memory_barrier();
+        for (size_t index = 0; index < 4; index++) {
+            if (nread == length)
+                break;
+            buf[nread] = (data >> (8 * index)) & 0xFF;
+            nread++;
+        }
+    };
+
+    full_memory_barrier();
+    write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, (1 | (address << 1) | (length << 16) | (IntelGraphics::GMBusCycle::Wait << 25) | (1 << 30)));
+    full_memory_barrier();
+    while (nread < length) {
+        gmbus_wait_for(IntelGraphics::GMBusStatus::HardwareReady, {});
+        read_set();
+    }
+    gmbus_wait_for(IntelGraphics::GMBusStatus::TransactionCompletion, {});
+}
+
+void IntelNativeDisplayConnector::gmbus_read_edid()
+{
+    Array<u8, 128> crt_edid_bytes {};
+    {
+        SpinlockLocker control_lock(m_control_lock);
+        gmbus_write(DDC2_I2C_ADDRESS, 0);
+        gmbus_read(DDC2_I2C_ADDRESS, (u8*)&crt_edid_bytes, sizeof(crt_edid_bytes));
+        // FIXME: It seems like the returned EDID is almost correct,
+        // but the first byte is set to 0xD0 instead of 0x00.
+        // For now, this "hack" works well enough.
+        crt_edid_bytes[0] = 0x0;
+    }
+    set_edid_bytes(crt_edid_bytes);
+}
+
+bool IntelNativeDisplayConnector::is_resolution_valid(size_t, size_t)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    // FIXME: Check that we are able to modeset to the requested resolution!
+    return true;
+}
+
+void IntelNativeDisplayConnector::disable_output()
+{
+    VERIFY(m_control_lock.is_locked());
+    disable_dac_output();
+    disable_all_planes();
+    disable_pipe_a();
+    disable_pipe_b();
+    disable_dpll();
+    disable_vga_emulation();
+}
+
+void IntelNativeDisplayConnector::enable_output(PhysicalAddress fb_address, size_t width)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(!pipe_a_enabled());
+    enable_pipe_a();
+    enable_primary_plane(fb_address, width);
+    enable_dac_output();
+}
+
+static size_t compute_dac_multiplier(size_t pixel_clock_in_khz)
+{
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz);
+    VERIFY(pixel_clock_in_khz >= 25000);
+    if (pixel_clock_in_khz >= 100000) {
+        return 1;
+    } else if (pixel_clock_in_khz >= 50000) {
+        return 2;
+    } else {
+        return 4;
+    }
+}
+
+bool IntelNativeDisplayConnector::set_safe_crt_resolution()
+{
+    VERIFY(m_control_lock.is_locked());
+    SpinlockLocker modeset_lock(m_modeset_lock);
+
+    // Note: Just in case we still allow access to VGA IO ports, disable it now.
+    GraphicsManagement::the().disable_vga_emulation_access_permanently();
+
+    // FIXME: Get the requested resolution from the EDID!!
+    auto modesetting = calculate_modesetting_from_edid(m_edid_parser.value(), 0);
+
+    disable_output();
+
+    auto dac_multiplier = compute_dac_multiplier(modesetting.pixel_clock_in_khz);
+    auto pll_settings = create_pll_settings((1000 * modesetting.pixel_clock_in_khz * dac_multiplier), 96'000'000, IntelGraphics::G35Limits);
+    if (!pll_settings.has_value())
+        VERIFY_NOT_REACHED();
+    auto settings = pll_settings.value();
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
+    enable_dpll_without_vga(pll_settings.value(), dac_multiplier);
+    set_display_timings(modesetting);
+    enable_output(m_framebuffer_address, modesetting.horizontal.blanking_start());
+
+    DisplayConnector::ModeSetting mode_set {
+        .horizontal_stride = modesetting.horizontal.blanking_start() * sizeof(u32),
+        .pixel_clock_in_khz = 0,
+        .horizontal_active = modesetting.horizontal.blanking_start(),
+        .horizontal_front_porch_pixels = 0,
+        .horizontal_sync_time_pixels = 0,
+        .horizontal_blank_pixels = 0,
+        .vertical_active = modesetting.vertical.blanking_start(),
+        .vertical_front_porch_lines = 0,
+        .vertical_sync_time_lines = 0,
+        .vertical_blank_lines = 0,
+        .horizontal_offset = 0,
+        .vertical_offset = 0,
+    };
+
+    m_current_mode_setting = mode_set;
+
+    auto rounded_size = MUST(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride));
+    m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address, rounded_size, "Intel Native Graphics Framebuffer", Memory::Region::Access::ReadWrite));
+    m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr();
+
+    if (m_framebuffer_console)
+        m_framebuffer_console->set_resolution(m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride);
+
+    return true;
+}
+
+void IntelNativeDisplayConnector::set_display_timings(Graphics::Modesetting const& modesetting)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
+    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (modesetting.horizontal.active - 1), (modesetting.horizontal.total - 1));
+    write_to_register(IntelGraphics::RegisterIndex::HTotalA, (modesetting.horizontal.active - 1) | (modesetting.horizontal.total - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (modesetting.horizontal.blanking_start() - 1), (modesetting.horizontal.blanking_end() - 1));
+    write_to_register(IntelGraphics::RegisterIndex::HBlankA, (modesetting.horizontal.blanking_start() - 1) | (modesetting.horizontal.blanking_end() - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (modesetting.horizontal.sync_start - 1), (modesetting.horizontal.sync_end - 1));
+    write_to_register(IntelGraphics::RegisterIndex::HSyncA, (modesetting.horizontal.sync_start - 1) | (modesetting.horizontal.sync_end - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (modesetting.vertical.active - 1), (modesetting.vertical.total - 1));
+    write_to_register(IntelGraphics::RegisterIndex::VTotalA, (modesetting.vertical.active - 1) | (modesetting.vertical.total - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (modesetting.vertical.blanking_start() - 1), (modesetting.vertical.blanking_end() - 1));
+    write_to_register(IntelGraphics::RegisterIndex::VBlankA, (modesetting.vertical.blanking_start() - 1) | (modesetting.vertical.blanking_end() - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (modesetting.vertical.sync_start - 1), (modesetting.vertical.sync_end - 1));
+    write_to_register(IntelGraphics::RegisterIndex::VSyncA, (modesetting.vertical.sync_start - 1) | (modesetting.vertical.sync_end - 1) << 16);
+
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1));
+    write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16);
+
+    IO::delay(200);
+}
+
+bool IntelNativeDisplayConnector::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const
+{
+    size_t current_time = 0;
+    while (current_time < milliseconds_timeout) {
+        if (pipe_a_enabled())
+            return true;
+        IO::delay(1000);
+        current_time++;
+    }
+    return false;
+}
+bool IntelNativeDisplayConnector::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const
+{
+    size_t current_time = 0;
+    while (current_time < milliseconds_timeout) {
+        if (!pipe_a_enabled())
+            return true;
+        IO::delay(1000);
+        current_time++;
+    }
+    return false;
+}
+
+bool IntelNativeDisplayConnector::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const
+{
+    size_t current_time = 0;
+    while (current_time < milliseconds_timeout) {
+        if (!pipe_b_enabled())
+            return true;
+        IO::delay(1000);
+        current_time++;
+    }
+    return false;
+}
+
+void IntelNativeDisplayConnector::disable_dpll()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0);
+    write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, 0);
+}
+
+void IntelNativeDisplayConnector::disable_pipe_a()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A");
+    wait_for_disabled_pipe_a(100);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done.");
+}
+
+void IntelNativeDisplayConnector::disable_pipe_b()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B");
+    wait_for_disabled_pipe_b(100);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done.");
+}
+
+void IntelNativeDisplayConnector::set_gmbus_default_rate()
+{
+    // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle
+    VERIFY(m_control_lock.is_locked());
+    // Set the rate to 100KHz
+    write_to_register(IntelGraphics::RegisterIndex::GMBusClock, read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & ~(0b111 << 8));
+}
+
+void IntelNativeDisplayConnector::enable_pipe_a()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
+    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
+    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, (1 << 31) | (1 << 24));
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A");
+    // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
+    wait_for_enabled_pipe_a(100);
+    dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done.");
+}
+
+void IntelNativeDisplayConnector::enable_primary_plane(PhysicalAddress fb_address, size_t width)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    VERIFY(((width * 4) % 64 == 0));
+
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAStride, width * 4);
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset, 0);
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneASurface, fb_address.get());
+
+    // FIXME: Serenity uses BGR 32 bit pixel format, but maybe we should try to determine it somehow!
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (0b0110 << 26) | (1 << 31));
+}
+
+void IntelNativeDisplayConnector::set_dpll_registers(IntelGraphics::PLLSettings const& settings)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA0, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
+    write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
+
+    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0);
+}
+
+void IntelNativeDisplayConnector::enable_dpll_without_vga(IntelGraphics::PLLSettings const& settings, size_t dac_multiplier)
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+
+    set_dpll_registers(settings);
+
+    IO::delay(200);
+
+    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31));
+    write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8));
+
+    // The specification says we should wait (at least) about 150 microseconds
+    // after enabling the DPLL to allow the clock to stabilize
+    IO::delay(200);
+    VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
+}
+
+void IntelNativeDisplayConnector::set_gmbus_pin_pair(IntelGraphics::GMBusPinPair pin_pair)
+{
+    // FIXME: Verify GMBUS is idle
+    VERIFY(m_control_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::GMBusClock, (read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & (~0b111)) | (pin_pair & 0b111));
+}
+
+void IntelNativeDisplayConnector::disable_dac_output()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10);
+}
+
+void IntelNativeDisplayConnector::enable_dac_output()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (1 << 31));
+}
+
+void IntelNativeDisplayConnector::disable_vga_emulation()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (1 << 31));
+    read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl);
+}
+
+void IntelNativeDisplayConnector::disable_all_planes()
+{
+    VERIFY(m_control_lock.is_locked());
+    VERIFY(m_modeset_lock.is_locked());
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, 0);
+    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneBControl, 0);
+}
+
+}

+ 167 - 0
Kernel/Graphics/Intel/NativeDisplayConnector.h

@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefPtr.h>
+#include <AK/Try.h>
+#include <Kernel/Graphics/Console/GenericFramebufferConsole.h>
+#include <Kernel/Graphics/Definitions.h>
+#include <Kernel/Graphics/DisplayConnector.h>
+#include <Kernel/Memory/TypedMapping.h>
+
+namespace Kernel {
+
+namespace IntelGraphics {
+
+enum class RegisterIndex {
+    PipeAConf = 0x70008,
+    PipeBConf = 0x71008,
+    GMBusData = 0x510C,
+    GMBusStatus = 0x5108,
+    GMBusCommand = 0x5104,
+    GMBusClock = 0x5100,
+    DisplayPlaneAControl = 0x70180,
+    DisplayPlaneBControl = 0x71180,
+    DisplayPlaneALinearOffset = 0x70184,
+    DisplayPlaneAStride = 0x70188,
+    DisplayPlaneASurface = 0x7019C,
+    DPLLDivisorA0 = 0x6040,
+    DPLLDivisorA1 = 0x6044,
+    DPLLControlA = 0x6014,
+    DPLLControlB = 0x6018,
+    DPLLMultiplierA = 0x601C,
+    HTotalA = 0x60000,
+    HBlankA = 0x60004,
+    HSyncA = 0x60008,
+    VTotalA = 0x6000C,
+    VBlankA = 0x60010,
+    VSyncA = 0x60014,
+    PipeASource = 0x6001C,
+    AnalogDisplayPort = 0x61100,
+    VGADisplayPlaneControl = 0x71400,
+};
+
+struct PLLSettings;
+
+struct PLLParameterLimit {
+    size_t min, max;
+};
+
+struct PLLMaxSettings {
+    PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2;
+};
+
+enum GMBusPinPair : u8 {
+    None = 0,
+    DedicatedControl = 1,
+    DedicatedAnalog = 0b10,
+    IntegratedDigital = 0b11,
+    sDVO = 0b101,
+    Dconnector = 0b111,
+};
+
+enum class GMBusStatus {
+    TransactionCompletion,
+    HardwareReady,
+};
+
+enum GMBusCycle {
+    Wait = 1,
+    Stop = 4,
+};
+}
+
+class IntelNativeDisplayConnector final
+    : public DisplayConnector {
+    friend class IntelNativeGraphicsAdapter;
+    friend class DeviceManagement;
+
+public:
+    static NonnullRefPtr<IntelNativeDisplayConnector> must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length);
+
+private:
+    // ^DisplayConnector
+    // FIXME: Implement modesetting capabilities in runtime from userland...
+    virtual bool mutable_mode_setting_capable() const override { return false; }
+    // FIXME: Implement double buffering capabilities in runtime from userland...
+    virtual bool double_framebuffering_capable() const override { return false; }
+    virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override;
+    virtual ErrorOr<void> set_safe_mode_setting() override;
+    virtual ErrorOr<void> set_y_offset(size_t y) override;
+    virtual ErrorOr<void> unblank() override;
+    virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override final;
+    virtual ErrorOr<void> flush_first_surface() override final;
+    virtual void enable_console() override;
+    virtual void disable_console() override;
+    virtual bool partial_flush_support() const override { return false; }
+    virtual bool flush_support() const override { return false; }
+    // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting.
+    virtual bool refresh_rate_support() const override { return true; }
+
+    ErrorOr<void> initialize_gmbus_settings_and_read_edid();
+
+    IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region);
+
+    ErrorOr<void> create_attached_framebuffer_console();
+
+    void write_to_register(IntelGraphics::RegisterIndex, u32 value) const;
+    u32 read_from_register(IntelGraphics::RegisterIndex) const;
+
+    bool pipe_a_enabled() const;
+    bool pipe_b_enabled() const;
+
+    bool is_resolution_valid(size_t width, size_t height);
+
+    bool set_safe_crt_resolution();
+
+    void disable_output();
+    void enable_output(PhysicalAddress fb_address, size_t width);
+
+    void disable_vga_emulation();
+    void enable_vga_plane();
+
+    void disable_dac_output();
+    void enable_dac_output();
+
+    void disable_all_planes();
+    void disable_pipe_a();
+    void disable_pipe_b();
+    void disable_dpll();
+
+    void set_dpll_registers(IntelGraphics::PLLSettings const&);
+
+    void enable_dpll_without_vga(IntelGraphics::PLLSettings const&, size_t dac_multiplier);
+    void set_display_timings(Graphics::Modesetting const&);
+    void enable_pipe_a();
+    void set_framebuffer_parameters(size_t, size_t);
+    void enable_primary_plane(PhysicalAddress fb_address, size_t stride);
+
+    bool wait_for_enabled_pipe_a(size_t milliseconds_timeout) const;
+    bool wait_for_disabled_pipe_a(size_t milliseconds_timeout) const;
+    bool wait_for_disabled_pipe_b(size_t milliseconds_timeout) const;
+
+    void set_gmbus_default_rate();
+    void set_gmbus_pin_pair(IntelGraphics::GMBusPinPair pin_pair);
+
+    // FIXME: It would be better if we generalize the I2C access later on
+    void gmbus_read_edid();
+    void gmbus_write(unsigned address, u32 byte);
+    void gmbus_read(unsigned address, u8* buf, size_t length);
+    bool gmbus_wait_for(IntelGraphics::GMBusStatus desired_status, Optional<size_t> milliseconds_timeout);
+
+    Optional<IntelGraphics::PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&);
+
+    mutable Spinlock m_registers_lock;
+    const PhysicalAddress m_framebuffer_address;
+    RefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console;
+    OwnPtr<Memory::Region> m_framebuffer_region;
+    u8* m_framebuffer_data {};
+
+    const PhysicalAddress m_registers;
+    NonnullOwnPtr<Memory::Region> m_registers_region;
+};
+}

+ 10 - 631
Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp

@@ -14,18 +14,6 @@
 
 namespace Kernel {
 
-static constexpr IntelNativeGraphicsAdapter::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 constexpr u16 supported_models[] {
     0x29c2, // Intel G35 Adapter
 };
@@ -39,643 +27,34 @@ static bool is_supported_model(u16 device_id)
     return false;
 }
 
-#define DDC2_I2C_ADDRESS 0x50
-
 RefPtr<IntelNativeGraphicsAdapter> IntelNativeGraphicsAdapter::initialize(PCI::DeviceIdentifier const& pci_device_identifier)
 {
     VERIFY(pci_device_identifier.hardware_id().vendor_id == 0x8086);
     if (!is_supported_model(pci_device_identifier.hardware_id().device_id))
         return {};
-    return adopt_ref(*new IntelNativeGraphicsAdapter(pci_device_identifier.address()));
-}
-
-static size_t compute_dac_multiplier(size_t pixel_clock_in_khz)
-{
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz);
-    VERIFY(pixel_clock_in_khz >= 25000);
-    if (pixel_clock_in_khz >= 100000) {
-        return 1;
-    } else if (pixel_clock_in_khz >= 50000) {
-        return 2;
-    } else {
-        return 4;
-    }
+    auto adapter = adopt_ref(*new IntelNativeGraphicsAdapter(pci_device_identifier.address()));
+    MUST(adapter->initialize_adapter());
+    return adapter;
 }
 
-static Graphics::Modesetting calculate_modesetting_from_edid(EDID::Parser& edid, size_t index)
-{
-    auto details = edid.detailed_timing(index).release_value();
-
-    Graphics::Modesetting mode;
-    VERIFY(details.pixel_clock_khz());
-    mode.pixel_clock_in_khz = details.pixel_clock_khz();
-
-    size_t horizontal_active = details.horizontal_addressable_pixels();
-    size_t horizontal_sync_offset = details.horizontal_front_porch_pixels();
-
-    mode.horizontal.active = horizontal_active;
-    mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
-    mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels();
-    mode.horizontal.total = horizontal_active + details.horizontal_blanking_pixels();
-
-    size_t vertical_active = details.vertical_addressable_lines();
-    size_t vertical_sync_offset = details.vertical_front_porch_lines();
-
-    mode.vertical.active = vertical_active;
-    mode.vertical.sync_start = vertical_active + vertical_sync_offset;
-    mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines();
-    mode.vertical.total = vertical_active + details.vertical_blanking_lines();
-    return mode;
-}
-
-static bool check_pll_settings(IntelNativeGraphicsAdapter::PLLSettings const& settings, size_t reference_clock, IntelNativeGraphicsAdapter::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<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::create_pll_settings(u64 target_frequency, u64 reference_clock, PLLMaxSettings const& limits)
-{
-    IntelNativeGraphicsAdapter::PLLSettings settings;
-    IntelNativeGraphicsAdapter::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 {};
-}
-
-IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
-    : PCIVGACompatibleAdapter(address)
-    , m_registers(PCI::get_BAR0(address) & 0xfffffffc)
-    , m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc)
+ErrorOr<void> IntelNativeGraphicsAdapter::initialize_adapter()
 {
+    auto address = pci_address();
     dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", address);
     auto bar0_space_size = PCI::get_BAR_space_size(address, 0);
     VERIFY(bar0_space_size == 0x80000);
     dmesgln("Intel Native Graphics Adapter @ {}, MMIO @ {}, space size is {:x} bytes", address, PhysicalAddress(PCI::get_BAR0(address)), bar0_space_size);
     dmesgln("Intel Native Graphics Adapter @ {}, framebuffer @ {}", address, PhysicalAddress(PCI::get_BAR2(address)));
-    auto region_or_error = MM.allocate_kernel_region(PhysicalAddress(PCI::get_BAR0(address)).page_base(), bar0_space_size, "Intel Native Graphics Registers", Memory::Region::Access::ReadWrite);
-    if (region_or_error.is_error()) {
-        TODO();
-    }
-    m_registers_region = region_or_error.release_value();
     PCI::enable_bus_mastering(address);
-    {
-        SpinlockLocker control_lock(m_control_lock);
-        set_gmbus_default_rate();
-        set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog);
-    }
-    gmbus_read_edid();
-
-    auto modesetting = calculate_modesetting_from_edid(m_crt_edid.value(), 0);
-    dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", pci_address(), modesetting.horizontal.active, modesetting.vertical.active);
-    set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active);
-    auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
-    VERIFY(!framebuffer_address.is_null());
-    VERIFY(m_framebuffer_pitch != 0);
-    VERIFY(m_framebuffer_height != 0);
-    VERIFY(m_framebuffer_width != 0);
-    m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
-    GraphicsManagement::the().set_console(*m_framebuffer_console);
-}
-
-void IntelNativeGraphicsAdapter::enable_vga_plane()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-}
-
-[[maybe_unused]] static StringView convert_register_index_to_string(IntelGraphics::RegisterIndex index)
-{
-    switch (index) {
-    case IntelGraphics::RegisterIndex::PipeAConf:
-        return "PipeAConf"sv;
-    case IntelGraphics::RegisterIndex::PipeBConf:
-        return "PipeBConf"sv;
-    case IntelGraphics::RegisterIndex::GMBusData:
-        return "GMBusData"sv;
-    case IntelGraphics::RegisterIndex::GMBusStatus:
-        return "GMBusStatus"sv;
-    case IntelGraphics::RegisterIndex::GMBusCommand:
-        return "GMBusCommand"sv;
-    case IntelGraphics::RegisterIndex::GMBusClock:
-        return "GMBusClock"sv;
-    case IntelGraphics::RegisterIndex::DisplayPlaneAControl:
-        return "DisplayPlaneAControl"sv;
-    case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset:
-        return "DisplayPlaneALinearOffset"sv;
-    case IntelGraphics::RegisterIndex::DisplayPlaneAStride:
-        return "DisplayPlaneAStride"sv;
-    case IntelGraphics::RegisterIndex::DisplayPlaneASurface:
-        return "DisplayPlaneASurface"sv;
-    case IntelGraphics::RegisterIndex::DPLLDivisorA0:
-        return "DPLLDivisorA0"sv;
-    case IntelGraphics::RegisterIndex::DPLLDivisorA1:
-        return "DPLLDivisorA1"sv;
-    case IntelGraphics::RegisterIndex::DPLLControlA:
-        return "DPLLControlA"sv;
-    case IntelGraphics::RegisterIndex::DPLLControlB:
-        return "DPLLControlB"sv;
-    case IntelGraphics::RegisterIndex::DPLLMultiplierA:
-        return "DPLLMultiplierA"sv;
-    case IntelGraphics::RegisterIndex::HTotalA:
-        return "HTotalA"sv;
-    case IntelGraphics::RegisterIndex::HBlankA:
-        return "HBlankA"sv;
-    case IntelGraphics::RegisterIndex::HSyncA:
-        return "HSyncA"sv;
-    case IntelGraphics::RegisterIndex::VTotalA:
-        return "VTotalA"sv;
-    case IntelGraphics::RegisterIndex::VBlankA:
-        return "VBlankA"sv;
-    case IntelGraphics::RegisterIndex::VSyncA:
-        return "VSyncA"sv;
-    case IntelGraphics::RegisterIndex::PipeASource:
-        return "PipeASource"sv;
-    case IntelGraphics::RegisterIndex::AnalogDisplayPort:
-        return "AnalogDisplayPort"sv;
-    case IntelGraphics::RegisterIndex::VGADisplayPlaneControl:
-        return "VGADisplayPlaneControl"sv;
-    default:
-        VERIFY_NOT_REACHED();
-    }
-}
-
-void IntelNativeGraphicsAdapter::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_registers_region);
-    SpinlockLocker lock(m_registers_lock);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Write to {} value of {:x}", pci_address(), convert_register_index_to_string(index), value);
-    auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(index).as_ptr();
-    *reg = value;
-}
-u32 IntelNativeGraphicsAdapter::read_from_register(IntelGraphics::RegisterIndex index) const
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_registers_region);
-    SpinlockLocker lock(m_registers_lock);
-    auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(index).as_ptr();
-    u32 value = *reg;
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Read from {} value of {:x}", pci_address(), convert_register_index_to_string(index), value);
-    return value;
-}
-
-bool IntelNativeGraphicsAdapter::pipe_a_enabled() const
-{
-    VERIFY(m_control_lock.is_locked());
-    return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
-}
-
-bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
-{
-    VERIFY(m_control_lock.is_locked());
-    return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30);
-}
-
-bool IntelNativeGraphicsAdapter::gmbus_wait_for(GMBusStatus desired_status, Optional<size_t> milliseconds_timeout)
-{
-    VERIFY(m_control_lock.is_locked());
-    size_t milliseconds_passed = 0;
-    while (1) {
-        if (milliseconds_timeout.has_value() && milliseconds_timeout.value() < milliseconds_passed)
-            return false;
-        full_memory_barrier();
-        u32 status = read_from_register(IntelGraphics::RegisterIndex::GMBusStatus);
-        full_memory_barrier();
-        VERIFY(!(status & (1 << 10))); // error happened
-        switch (desired_status) {
-        case GMBusStatus::HardwareReady:
-            if (status & (1 << 11))
-                return true;
-            break;
-        case GMBusStatus::TransactionCompletion:
-            if (status & (1 << 14))
-                return true;
-            break;
-        default:
-            VERIFY_NOT_REACHED();
-        }
-        IO::delay(1000);
-        milliseconds_passed++;
-    }
-}
-
-void IntelNativeGraphicsAdapter::gmbus_write(unsigned address, u32 byte)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(address < 256);
-    full_memory_barrier();
-    write_to_register(IntelGraphics::RegisterIndex::GMBusData, byte);
-    full_memory_barrier();
-    write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30)));
-    full_memory_barrier();
-    gmbus_wait_for(GMBusStatus::TransactionCompletion, {});
-}
-void IntelNativeGraphicsAdapter::gmbus_read(unsigned address, u8* buf, size_t length)
-{
-    VERIFY(address < 256);
-    VERIFY(m_control_lock.is_locked());
-    size_t nread = 0;
-    auto read_set = [&] {
-        full_memory_barrier();
-        u32 data = read_from_register(IntelGraphics::RegisterIndex::GMBusData);
-        full_memory_barrier();
-        for (size_t index = 0; index < 4; index++) {
-            if (nread == length)
-                break;
-            buf[nread] = (data >> (8 * index)) & 0xFF;
-            nread++;
-        }
-    };
-
-    full_memory_barrier();
-    write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30)));
-    full_memory_barrier();
-    while (nread < length) {
-        gmbus_wait_for(GMBusStatus::HardwareReady, {});
-        read_set();
-    }
-    gmbus_wait_for(GMBusStatus::TransactionCompletion, {});
-}
-
-void IntelNativeGraphicsAdapter::gmbus_read_edid()
-{
-    {
-        SpinlockLocker control_lock(m_control_lock);
-        gmbus_write(DDC2_I2C_ADDRESS, 0);
-        gmbus_read(DDC2_I2C_ADDRESS, (u8*)&m_crt_edid_bytes, sizeof(m_crt_edid_bytes));
-        // FIXME: It seems like the returned EDID is almost correct,
-        // but the first byte is set to 0xD0 instead of 0x00.
-        // For now, this "hack" works well enough.
-        m_crt_edid_bytes[0] = 0x0;
-    }
-    if (auto parsed_edid = EDID::Parser::from_bytes({ m_crt_edid_bytes, sizeof(m_crt_edid_bytes) }); !parsed_edid.is_error()) {
-        m_crt_edid = parsed_edid.release_value();
-    } else {
-        for (size_t x = 0; x < 128; x = x + 16) {
-            dmesgln("IntelNativeGraphicsAdapter: Print offending EDID");
-            dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
-                m_crt_edid_bytes[x], m_crt_edid_bytes[x + 1], m_crt_edid_bytes[x + 2], m_crt_edid_bytes[x + 3],
-                m_crt_edid_bytes[x + 4], m_crt_edid_bytes[x + 5], m_crt_edid_bytes[x + 6], m_crt_edid_bytes[x + 7],
-                m_crt_edid_bytes[x + 8], m_crt_edid_bytes[x + 9], m_crt_edid_bytes[x + 10], m_crt_edid_bytes[x + 11],
-                m_crt_edid_bytes[x + 12], m_crt_edid_bytes[x + 13], m_crt_edid_bytes[x + 14], m_crt_edid_bytes[x + 15]);
-        }
-        dmesgln("IntelNativeGraphicsAdapter: Parsing EDID failed: {}", parsed_edid.error());
-        m_crt_edid = {};
-    }
-}
-
-bool IntelNativeGraphicsAdapter::is_resolution_valid(size_t, size_t)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    // FIXME: Check that we are able to modeset to the requested resolution!
-    return true;
-}
-
-void IntelNativeGraphicsAdapter::disable_output()
-{
-    VERIFY(m_control_lock.is_locked());
-    disable_dac_output();
-    disable_all_planes();
-    disable_pipe_a();
-    disable_pipe_b();
-    disable_vga_emulation();
-    disable_dpll();
-}
-
-void IntelNativeGraphicsAdapter::enable_output(PhysicalAddress fb_address, size_t width)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(!pipe_a_enabled());
-
-    enable_pipe_a();
-    enable_primary_plane(fb_address, width);
-    enable_dac_output();
-}
 
-bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
-{
-    SpinlockLocker control_lock(m_control_lock);
-    SpinlockLocker modeset_lock(m_modeset_lock);
-    if (!is_resolution_valid(width, height)) {
-        return false;
-    }
-
-    // FIXME: Get the requested resolution from the EDID!!
-    auto modesetting = calculate_modesetting_from_edid(m_crt_edid.value(), 0);
-
-    disable_output();
-
-    auto dac_multiplier = compute_dac_multiplier(modesetting.pixel_clock_in_khz);
-    auto pll_settings = create_pll_settings((1000 * modesetting.pixel_clock_in_khz * dac_multiplier), 96'000'000, G35Limits);
-    if (!pll_settings.has_value())
-        VERIFY_NOT_REACHED();
-    auto settings = pll_settings.value();
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
-    enable_dpll_without_vga(pll_settings.value(), dac_multiplier);
-    set_display_timings(modesetting);
-    auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
-    VERIFY(!address.is_null());
-    enable_output(address, width);
-
-    m_framebuffer_width = width;
-    m_framebuffer_height = height;
-    m_framebuffer_pitch = width * 4;
-
-    return true;
-}
-
-void IntelNativeGraphicsAdapter::set_display_timings(Graphics::Modesetting const& modesetting)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
-    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (modesetting.horizontal.active - 1), (modesetting.horizontal.total - 1));
-    write_to_register(IntelGraphics::RegisterIndex::HTotalA, (modesetting.horizontal.active - 1) | (modesetting.horizontal.total - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (modesetting.horizontal.blanking_start() - 1), (modesetting.horizontal.blanking_end() - 1));
-    write_to_register(IntelGraphics::RegisterIndex::HBlankA, (modesetting.horizontal.blanking_start() - 1) | (modesetting.horizontal.blanking_end() - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (modesetting.horizontal.sync_start - 1), (modesetting.horizontal.sync_end - 1));
-    write_to_register(IntelGraphics::RegisterIndex::HSyncA, (modesetting.horizontal.sync_start - 1) | (modesetting.horizontal.sync_end - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (modesetting.vertical.active - 1), (modesetting.vertical.total - 1));
-    write_to_register(IntelGraphics::RegisterIndex::VTotalA, (modesetting.vertical.active - 1) | (modesetting.vertical.total - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (modesetting.vertical.blanking_start() - 1), (modesetting.vertical.blanking_end() - 1));
-    write_to_register(IntelGraphics::RegisterIndex::VBlankA, (modesetting.vertical.blanking_start() - 1) | (modesetting.vertical.blanking_end() - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (modesetting.vertical.sync_start - 1), (modesetting.vertical.sync_end - 1));
-    write_to_register(IntelGraphics::RegisterIndex::VSyncA, (modesetting.vertical.sync_start - 1) | (modesetting.vertical.sync_end - 1) << 16);
-
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1));
-    write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16);
-
-    IO::delay(200);
-}
-
-bool IntelNativeGraphicsAdapter::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const
-{
-    size_t current_time = 0;
-    while (current_time < milliseconds_timeout) {
-        if (pipe_a_enabled())
-            return true;
-        IO::delay(1000);
-        current_time++;
-    }
-    return false;
-}
-bool IntelNativeGraphicsAdapter::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const
-{
-    size_t current_time = 0;
-    while (current_time < milliseconds_timeout) {
-        if (!pipe_a_enabled())
-            return true;
-        IO::delay(1000);
-        current_time++;
-    }
-    return false;
-}
-
-bool IntelNativeGraphicsAdapter::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const
-{
-    size_t current_time = 0;
-    while (current_time < milliseconds_timeout) {
-        if (!pipe_b_enabled())
-            return true;
-        IO::delay(1000);
-        current_time++;
-    }
-    return false;
-}
-
-void IntelNativeGraphicsAdapter::disable_dpll()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
-    write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, read_from_register(IntelGraphics::RegisterIndex::DPLLControlB) & ~0x80000000);
-}
-
-void IntelNativeGraphicsAdapter::disable_pipe_a()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & ~0x80000000);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A");
-    wait_for_disabled_pipe_a(100);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done.");
-}
-
-void IntelNativeGraphicsAdapter::disable_pipe_b()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & ~0x80000000);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B");
-    wait_for_disabled_pipe_b(100);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done.");
-}
-
-void IntelNativeGraphicsAdapter::set_gmbus_default_rate()
-{
-    // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle
-    VERIFY(m_control_lock.is_locked());
-    // Set the rate to 100KHz
-    write_to_register(IntelGraphics::RegisterIndex::GMBusClock, read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & ~(0b111 << 8));
-}
-
-void IntelNativeGraphicsAdapter::enable_pipe_a()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
-    VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
-    write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) | 0x80000000);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A");
-    // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
-    wait_for_enabled_pipe_a(100);
-    dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done.");
-}
-
-void IntelNativeGraphicsAdapter::enable_primary_plane(PhysicalAddress fb_address, size_t width)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    VERIFY(((width * 4) % 64 == 0));
-
-    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAStride, width * 4);
-    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset, 0);
-    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneASurface, fb_address.get());
-
-    // FIXME: Serenity uses BGR 32 bit pixel format, but maybe we should try to determine it somehow!
-    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31));
-}
-
-void IntelNativeGraphicsAdapter::set_dpll_registers(PLLSettings const& settings)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA0, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
-    write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
-
-    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
-}
-
-void IntelNativeGraphicsAdapter::enable_dpll_without_vga(PLLSettings const& settings, size_t dac_multiplier)
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-
-    set_dpll_registers(settings);
-
-    IO::delay(200);
-
-    write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31));
-    write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8));
-
-    // The specification says we should wait (at least) about 150 microseconds
-    // after enabling the DPLL to allow the clock to stabilize
-    IO::delay(200);
-    VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
-}
-
-void IntelNativeGraphicsAdapter::set_gmbus_pin_pair(GMBusPinPair pin_pair)
-{
-    // FIXME: Verify GMBUS is idle
-    VERIFY(m_control_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::GMBusClock, (read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & (~0b111)) | (pin_pair & 0b111));
-}
-
-void IntelNativeGraphicsAdapter::disable_dac_output()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10);
-}
-
-void IntelNativeGraphicsAdapter::enable_dac_output()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (read_from_register(IntelGraphics::RegisterIndex::AnalogDisplayPort) & (~(0b11 << 10))) | 0x80000000);
-}
-
-void IntelNativeGraphicsAdapter::disable_vga_emulation()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl) & (~(1 << 30))) | 0x80000000);
-}
-
-void IntelNativeGraphicsAdapter::disable_all_planes()
-{
-    VERIFY(m_control_lock.is_locked());
-    VERIFY(m_modeset_lock.is_locked());
-    write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & ~(1 << 31));
-}
-
-void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
-{
-    auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
-    VERIFY(!address.is_null());
-    VERIFY(m_framebuffer_pitch != 0);
-    VERIFY(m_framebuffer_height != 0);
-    VERIFY(m_framebuffer_width != 0);
-    m_framebuffer_device = FramebufferDevice::create(*this, address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
-    // FIXME: Would be nice to be able to return a ErrorOr<void> here.
-    auto framebuffer_result = m_framebuffer_device->try_to_initialize();
-    VERIFY(!framebuffer_result.is_error());
+    m_display_connector = IntelNativeDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR2(address) & 0xfffffff0), PhysicalAddress(PCI::get_BAR0(address) & 0xfffffff0), bar0_space_size);
+    return {};
 }
 
-ErrorOr<ByteBuffer> IntelNativeGraphicsAdapter::get_edid(size_t output_port_index) const
+IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
+    : GenericGraphicsAdapter()
+    , PCI::Device(address)
 {
-    if (output_port_index != 0) {
-        dbgln("IntelNativeGraphicsAdapter: get_edid: Only one output supported");
-        return Error::from_errno(ENODEV);
-    }
-
-    if (m_crt_edid.has_value())
-        return ByteBuffer::copy(m_crt_edid_bytes, sizeof(m_crt_edid_bytes));
-
-    return ByteBuffer {};
 }
 
 }

+ 20 - 147
Kernel/Graphics/Intel/NativeGraphicsAdapter.h

@@ -9,166 +9,39 @@
 #include <AK/Types.h>
 #include <Kernel/Bus/PCI/Device.h>
 #include <Kernel/Graphics/Definitions.h>
-#include <Kernel/Graphics/FramebufferDevice.h>
-#include <Kernel/Graphics/VGA/PCIAdapter.h>
+#include <Kernel/Graphics/Intel/NativeDisplayConnector.h>
 #include <Kernel/PhysicalAddress.h>
 #include <LibEDID/EDID.h>
 
 namespace Kernel {
 
-namespace IntelGraphics {
-
-enum RegisterIndex {
-    PipeAConf = 0x70008,
-    PipeBConf = 0x71008,
-    GMBusData = 0x510C,
-    GMBusStatus = 0x5108,
-    GMBusCommand = 0x5104,
-    GMBusClock = 0x5100,
-    DisplayPlaneAControl = 0x70180,
-    DisplayPlaneALinearOffset = 0x70184,
-    DisplayPlaneAStride = 0x70188,
-    DisplayPlaneASurface = 0x7019C,
-    DPLLDivisorA0 = 0x6040,
-    DPLLDivisorA1 = 0x6044,
-    DPLLControlA = 0x6014,
-    DPLLControlB = 0x6018,
-    DPLLMultiplierA = 0x601C,
-    HTotalA = 0x60000,
-    HBlankA = 0x60004,
-    HSyncA = 0x60008,
-    VTotalA = 0x6000C,
-    VBlankA = 0x60010,
-    VSyncA = 0x60014,
-    PipeASource = 0x6001C,
-    AnalogDisplayPort = 0x61100,
-    VGADisplayPlaneControl = 0x71400,
-};
-}
-
 class IntelNativeGraphicsAdapter final
-    : public PCIVGACompatibleAdapter {
-public:
-    struct PLLSettings {
-        bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); }
-        u64 compute_dot_clock(u64 refclock) const
-        {
-            return (refclock * (5 * (m1) + (m2)) / (n)) / (p1 * p2);
-        }
-
-        u64 compute_vco(u64 refclock) const
-        {
-            return refclock * (5 * (m1) + (m2)) / n;
-        }
-
-        u64 compute_m() const
-        {
-            return 5 * (m1) + (m2);
-        }
-
-        u64 compute_p() const
-        {
-            return p1 * p2;
-        }
-        u64 n { 0 };
-        u64 m1 { 0 };
-        u64 m2 { 0 };
-        u64 p1 { 0 };
-        u64 p2 { 0 };
-    };
-    struct PLLParameterLimit {
-        size_t min, max;
-    };
-    struct PLLMaxSettings {
-        PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2;
-    };
-
-private:
-    enum GMBusPinPair : u8 {
-        None = 0,
-        DedicatedControl = 1,
-        DedicatedAnalog = 0b10,
-        IntegratedDigital = 0b11,
-        sDVO = 0b101,
-        Dconnector = 0b111,
-    };
-
-    enum class GMBusStatus {
-        TransactionCompletion,
-        HardwareReady
-    };
-
-    enum GMBusCycle {
-        Wait = 1,
-        Stop = 4,
-    };
+    : public GenericGraphicsAdapter
+    , public PCI::Device {
 
 public:
     static RefPtr<IntelNativeGraphicsAdapter> initialize(PCI::DeviceIdentifier const&);
 
+    virtual ~IntelNativeGraphicsAdapter() = default;
+
 private:
-    explicit IntelNativeGraphicsAdapter(PCI::Address);
+    ErrorOr<void> initialize_adapter();
 
-    void write_to_register(IntelGraphics::RegisterIndex, u32 value) const;
-    u32 read_from_register(IntelGraphics::RegisterIndex) const;
+    explicit IntelNativeGraphicsAdapter(PCI::Address);
 
     // ^GenericGraphicsAdapter
-    virtual void initialize_framebuffer_devices() override;
-    virtual ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override;
-
-    bool pipe_a_enabled() const;
-    bool pipe_b_enabled() const;
-
-    bool is_resolution_valid(size_t width, size_t height);
-
-    bool set_crt_resolution(size_t width, size_t height);
-
-    void disable_output();
-    void enable_output(PhysicalAddress fb_address, size_t width);
-
-    void disable_vga_emulation();
-    void enable_vga_plane();
-
-    void disable_dac_output();
-    void enable_dac_output();
-
-    void disable_all_planes();
-    void disable_pipe_a();
-    void disable_pipe_b();
-    void disable_dpll();
-
-    void set_dpll_registers(PLLSettings const&);
-
-    void enable_dpll_without_vga(PLLSettings const&, size_t dac_multiplier);
-    void set_display_timings(Graphics::Modesetting const&);
-    void enable_pipe_a();
-    void set_framebuffer_parameters(size_t, size_t);
-    void enable_primary_plane(PhysicalAddress fb_address, size_t stride);
-
-    bool wait_for_enabled_pipe_a(size_t milliseconds_timeout) const;
-    bool wait_for_disabled_pipe_a(size_t milliseconds_timeout) const;
-    bool wait_for_disabled_pipe_b(size_t milliseconds_timeout) const;
-
-    void set_gmbus_default_rate();
-    void set_gmbus_pin_pair(GMBusPinPair pin_pair);
-
-    // FIXME: It would be better if we generalize the I2C access later on
-    void gmbus_read_edid();
-    void gmbus_write(unsigned address, u32 byte);
-    void gmbus_read(unsigned address, u8* buf, size_t length);
-    bool gmbus_wait_for(GMBusStatus desired_status, Optional<size_t> milliseconds_timeout);
-
-    Optional<PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, PLLMaxSettings const&);
-
-    Spinlock m_control_lock;
-    Spinlock m_modeset_lock;
-    mutable Spinlock m_registers_lock;
-
-    EDID::Parser::RawBytes m_crt_edid_bytes {};
-    Optional<EDID::Parser> m_crt_edid;
-    const PhysicalAddress m_registers;
-    const PhysicalAddress m_framebuffer_addr;
-    OwnPtr<Memory::Region> m_registers_region;
+    // FIXME: Remove all of these methods when we get rid of the FramebufferDevice class.
+    virtual bool framebuffer_devices_initialized() const override { return false; }
+    virtual bool modesetting_capable() const override { return true; }
+    virtual bool vga_compatible() const override { return true; }
+    virtual bool double_framebuffering_capable() const override { return true; }
+    virtual bool try_to_set_resolution(size_t, size_t, size_t) override { VERIFY_NOT_REACHED(); }
+    virtual bool set_y_offset(size_t, size_t) override { VERIFY_NOT_REACHED(); }
+    virtual void initialize_framebuffer_devices() override { }
+    virtual void enable_consoles() override { }
+    virtual void disable_consoles() override { }
+    virtual ErrorOr<ByteBuffer> get_edid(size_t) const override { VERIFY_NOT_REACHED(); }
+
+    RefPtr<IntelNativeDisplayConnector> m_display_connector;
 };
-
 }