summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/CMakeLists.txt5
-rw-r--r--Kernel/Graphics/Definitions.h21
-rw-r--r--Kernel/Graphics/DisplayConnector.cpp20
-rw-r--r--Kernel/Graphics/DisplayConnector.h2
-rw-r--r--Kernel/Graphics/Intel/Definitions.h55
-rw-r--r--Kernel/Graphics/Intel/DisplayConnectorGroup.cpp464
-rw-r--r--Kernel/Graphics/Intel/DisplayConnectorGroup.h115
-rw-r--r--Kernel/Graphics/Intel/NativeDisplayConnector.cpp590
-rw-r--r--Kernel/Graphics/Intel/NativeDisplayConnector.h127
-rw-r--r--Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp18
-rw-r--r--Kernel/Graphics/Intel/NativeGraphicsAdapter.h3
-rw-r--r--Kernel/Graphics/Intel/Plane/DisplayPlane.cpp49
-rw-r--r--Kernel/Graphics/Intel/Plane/DisplayPlane.h64
-rw-r--r--Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp59
-rw-r--r--Kernel/Graphics/Intel/Plane/G33DisplayPlane.h26
-rw-r--r--Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp88
-rw-r--r--Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h42
-rw-r--r--Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.cpp57
-rw-r--r--Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h91
19 files changed, 1209 insertions, 687 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt
index 3fc97ac722..a9555d1e35 100644
--- a/Kernel/CMakeLists.txt
+++ b/Kernel/CMakeLists.txt
@@ -76,6 +76,11 @@ set(KERNEL_SOURCES
Graphics/Generic/DisplayConnector.cpp
Graphics/GraphicsManagement.cpp
Graphics/Intel/Auxiliary/GMBusConnector.cpp
+ Graphics/Intel/Plane/DisplayPlane.cpp
+ Graphics/Intel/Plane/G33DisplayPlane.cpp
+ Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp
+ Graphics/Intel/Transcoder/DisplayTranscoder.cpp
+ Graphics/Intel/DisplayConnectorGroup.cpp
Graphics/Intel/NativeDisplayConnector.cpp
Graphics/Intel/NativeGraphicsAdapter.cpp
Graphics/VMWare/Console.cpp
diff --git a/Kernel/Graphics/Definitions.h b/Kernel/Graphics/Definitions.h
index b9d33c67eb..343a5125ed 100644
--- a/Kernel/Graphics/Definitions.h
+++ b/Kernel/Graphics/Definitions.h
@@ -10,27 +10,6 @@
namespace Kernel::Graphics {
-struct Timings {
- size_t blanking_start() const
- {
- return active;
- }
- size_t blanking_end() const
- {
- return total;
- }
-
- size_t active;
- size_t sync_start;
- size_t sync_end;
- size_t total;
-};
-
-struct Modesetting {
- size_t pixel_clock_in_khz;
- Timings horizontal;
- Timings vertical;
-};
// Note: Address 0x50 is expected to be the DDC2 (EDID) i2c address.
static constexpr u8 ddc2_i2c_address = 0x50;
diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp
index 9d95ddfa65..b3516d7fe4 100644
--- a/Kernel/Graphics/DisplayConnector.cpp
+++ b/Kernel/Graphics/DisplayConnector.cpp
@@ -233,22 +233,24 @@ ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor(Optional<Arr
return {};
}
-void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes)
+void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes, bool might_be_invalid)
{
memcpy((u8*)m_edid_bytes, edid_bytes.data(), sizeof(m_edid_bytes));
if (auto parsed_edid = EDID::Parser::from_bytes({ m_edid_bytes, sizeof(m_edid_bytes) }); !parsed_edid.is_error()) {
m_edid_parser = parsed_edid.release_value();
m_edid_valid = true;
} else {
- dmesgln("DisplayConnector: Print offending EDID");
- for (size_t x = 0; x < 128; x = x + 16) {
- dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
- m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3],
- m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7],
- m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11],
- m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]);
+ if (!might_be_invalid) {
+ dmesgln("DisplayConnector: Print offending EDID");
+ for (size_t x = 0; x < 128; x = x + 16) {
+ dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
+ m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3],
+ m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7],
+ m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11],
+ m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]);
+ }
+ dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error());
}
- dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error());
}
}
diff --git a/Kernel/Graphics/DisplayConnector.h b/Kernel/Graphics/DisplayConnector.h
index e49b219320..4e9f9634dd 100644
--- a/Kernel/Graphics/DisplayConnector.h
+++ b/Kernel/Graphics/DisplayConnector.h
@@ -103,7 +103,7 @@ public:
Memory::Region const& framebuffer_region() const { return *m_framebuffer_region; }
protected:
- void set_edid_bytes(Array<u8, 128> const& edid_bytes);
+ void set_edid_bytes(Array<u8, 128> const& edid_bytes, bool might_be_invalid = false);
DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization);
DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization);
diff --git a/Kernel/Graphics/Intel/Definitions.h b/Kernel/Graphics/Intel/Definitions.h
new file mode 100644
index 0000000000..46306ba287
--- /dev/null
+++ b/Kernel/Graphics/Intel/Definitions.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+
+namespace Kernel::IntelGraphics {
+
+enum class GlobalGenerationRegister {
+ PipeAConf = 0x70008,
+ PipeBConf = 0x71008,
+};
+
+struct PLLSettings;
+
+struct PLLParameterLimit {
+ size_t min, max;
+};
+
+struct PLLMaxSettings {
+ PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2;
+};
+
+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 };
+};
+}
diff --git a/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp b/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp
new file mode 100644
index 0000000000..86af3c91b0
--- /dev/null
+++ b/Kernel/Graphics/Intel/DisplayConnectorGroup.cpp
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Arch/Delay.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/DisplayConnectorGroup.h>
+#include <Kernel/Graphics/Intel/Plane/G33DisplayPlane.h>
+#include <Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h>
+#include <Kernel/Memory/Region.h>
+#include <Kernel/Memory/TypedMapping.h>
+
+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<IntelGraphics::PLLSettings> 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<NonnullLockRefPtr<IntelDisplayConnectorGroup>> IntelDisplayConnectorGroup::try_create(Badge<IntelNativeGraphicsAdapter>, 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));
+ // NOTE: 0x5100 is the offset of the start of the GMBus registers
+ auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(first_region.pci_bar_paddr.offset(0x5100)));
+ auto connector_group = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelDisplayConnectorGroup(generation, move(gmbus_connector), move(registers_region), first_region, second_region)));
+ TRY(connector_group->initialize_connectors());
+ return connector_group;
+}
+
+IntelDisplayConnectorGroup::IntelDisplayConnectorGroup(Generation generation, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const& first_region, MMIORegion const& second_region)
+ : m_mmio_first_region(first_region)
+ , m_mmio_second_region(second_region)
+ , m_assigned_mmio_registers_region(m_mmio_first_region)
+ , m_generation(generation)
+ , m_registers_region(move(registers_region))
+ , m_gmbus_connector(move(gmbus_connector))
+{
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::initialize_gen4_connectors()
+{
+ // NOTE: Just assume we will need one Gen4 "transcoder"
+ // NOTE: Main block of registers starting at HorizontalTotalA register (0x60000)
+ auto transcoder_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x60000);
+ // NOTE: DPLL registers starting at DPLLDivisorA0 register (0x6040)
+ auto dpll_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6040);
+ // NOTE: DPLL A control registers starting at 0x6014 (DPLL A Control register),
+ // DPLL A Multiplier is at 0x601C, between them (at 0x6018) there is the DPLL B Control register.
+ auto dpll_control_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6014);
+ m_transcoders[0] = TRY(IntelAnalogDisplayTranscoder::create_with_physical_addresses(transcoder_registers_paddr, dpll_registers_paddr, dpll_control_registers_paddr));
+ m_planes[0] = TRY(IntelG33DisplayPlane::create_with_physical_address(m_mmio_first_region.pci_bar_paddr.offset(0x70180)));
+ Array<u8, 128> crt_edid_bytes {};
+ {
+ SpinlockLocker control_lock(m_control_lock);
+ TRY(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0));
+ TRY(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size()));
+ }
+ m_connectors[0] = TRY(IntelNativeDisplayConnector::try_create_with_display_connector_group(*this, IntelNativeDisplayConnector::ConnectorIndex::PortA, IntelNativeDisplayConnector::Type::Analog, m_mmio_second_region.pci_bar_paddr, m_mmio_second_region.pci_bar_space_length));
+ m_connectors[0]->set_edid_bytes({}, crt_edid_bytes);
+ return {};
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::initialize_connectors()
+{
+
+ // NOTE: Intel Graphics Generation 4 is pretty ancient beast, and we should not
+ // assume we can find a VBT for it. Just initialize the (assumed) CRT connector and be done with it.
+ if (m_generation == Generation::Gen4) {
+ TRY(initialize_gen4_connectors());
+ } else {
+ VERIFY_NOT_REACHED();
+ }
+
+ for (size_t connector_index = 0; connector_index < m_connectors.size(); connector_index++) {
+ if (!m_connectors[connector_index])
+ continue;
+ if (!m_connectors[connector_index]->m_edid_valid)
+ continue;
+ TRY(m_connectors[connector_index]->set_safe_mode_setting());
+ TRY(m_connectors[connector_index]->create_attached_framebuffer_console({}));
+ }
+ return {};
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector)
+{
+ VERIFY(connector.m_modeset_lock.is_locked());
+ if (!connector.m_edid_parser.has_value())
+ return Error::from_errno(ENOTSUP);
+ if (!connector.m_edid_parser.value().detailed_timing(0).has_value())
+ return Error::from_errno(ENOTSUP);
+ auto details = connector.m_edid_parser.value().detailed_timing(0).release_value();
+
+ DisplayConnector::ModeSetting modesetting {
+ // Note: We assume that we always use 32 bit framebuffers.
+ .horizontal_stride = details.horizontal_addressable_pixels() * sizeof(u32),
+ .pixel_clock_in_khz = details.pixel_clock_khz(),
+ .horizontal_active = details.horizontal_addressable_pixels(),
+ .horizontal_front_porch_pixels = details.horizontal_front_porch_pixels(),
+ .horizontal_sync_time_pixels = details.horizontal_sync_pulse_width_pixels(),
+ .horizontal_blank_pixels = details.horizontal_blanking_pixels(),
+ .vertical_active = details.vertical_addressable_lines(),
+ .vertical_front_porch_lines = details.vertical_front_porch_lines(),
+ .vertical_sync_time_lines = details.vertical_sync_pulse_width_lines(),
+ .vertical_blank_lines = details.vertical_blanking_lines(),
+ .horizontal_offset = 0,
+ .vertical_offset = 0,
+ };
+
+ return set_mode_setting(connector, modesetting);
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
+{
+ return set_mode_setting(connector, mode_setting);
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
+{
+ VERIFY(connector.m_modeset_lock.is_locked());
+
+ VERIFY(to_underlying(connector.connector_index()) < m_connectors.size());
+ VERIFY(&connector == m_connectors[to_underlying(connector.connector_index())].ptr());
+
+ DisplayConnector::ModeSetting actual_mode_setting = mode_setting;
+ actual_mode_setting.horizontal_stride = actual_mode_setting.horizontal_active * sizeof(u32);
+ VERIFY(actual_mode_setting.horizontal_stride != 0);
+ if (m_generation == Generation::Gen4) {
+ TRY(set_gen4_mode_setting(connector, actual_mode_setting));
+ } else {
+ VERIFY_NOT_REACHED();
+ }
+
+ connector.m_current_mode_setting = actual_mode_setting;
+ if (!connector.m_framebuffer_console.is_null())
+ static_cast<Graphics::GenericFramebufferConsoleImpl*>(connector.m_framebuffer_console.ptr())->set_resolution(actual_mode_setting.horizontal_active, actual_mode_setting.vertical_active, actual_mode_setting.horizontal_stride);
+ return {};
+}
+
+ErrorOr<void> IntelDisplayConnectorGroup::set_gen4_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
+{
+ VERIFY(connector.m_modeset_lock.is_locked());
+ SpinlockLocker control_lock(m_control_lock);
+ SpinlockLocker modeset_lock(m_modeset_lock);
+ if (!set_crt_resolution(mode_setting))
+ return Error::from_errno(ENOTSUP);
+ return {};
+}
+
+void IntelDisplayConnectorGroup::enable_vga_plane()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+}
+
+StringView IntelDisplayConnectorGroup::convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const
+{
+ switch (index) {
+ case AnalogOutputRegisterOffset::AnalogDisplayPort:
+ return "AnalogDisplayPort"sv;
+ case AnalogOutputRegisterOffset::VGADisplayPlaneControl:
+ return "VGADisplayPlaneControl"sv;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+}
+
+void IntelDisplayConnectorGroup::write_to_general_register(RegisterOffset offset, u32 value)
+{
+ VERIFY(m_control_lock.is_locked());
+ SpinlockLocker lock(m_registers_lock);
+ auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
+ *reg = value;
+}
+u32 IntelDisplayConnectorGroup::read_from_general_register(RegisterOffset offset) const
+{
+ VERIFY(m_control_lock.is_locked());
+ SpinlockLocker lock(m_registers_lock);
+ auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
+ u32 value = *reg;
+ return value;
+}
+
+void IntelDisplayConnectorGroup::write_to_analog_output_register(AnalogOutputRegisterOffset index, u32 value)
+{
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_analog_output_register_to_string(index), value);
+ write_to_general_register(to_underlying(index), value);
+}
+
+u32 IntelDisplayConnectorGroup::read_from_analog_output_register(AnalogOutputRegisterOffset index) const
+{
+ u32 value = read_from_general_register(to_underlying(index));
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_analog_output_register_to_string(index), value);
+ return value;
+}
+
+void IntelDisplayConnectorGroup::write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister index, u32 value)
+{
+ write_to_general_register(to_underlying(index), value);
+}
+
+u32 IntelDisplayConnectorGroup::read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister index) const
+{
+ u32 value = read_from_general_register(to_underlying(index));
+ return value;
+}
+
+bool IntelDisplayConnectorGroup::pipe_a_enabled() const
+{
+ VERIFY(m_control_lock.is_locked());
+ return read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 30);
+}
+
+bool IntelDisplayConnectorGroup::pipe_b_enabled() const
+{
+ VERIFY(m_control_lock.is_locked());
+ return read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeBConf) & (1 << 30);
+}
+
+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 IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSetting const& mode_setting)
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+
+ // Note: Just in case we still allow access to VGA IO ports, disable it now.
+ 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);
+ if (!pll_settings.has_value())
+ return false;
+ auto settings = pll_settings.value();
+
+ disable_dac_output();
+ MUST(m_planes[0]->disable({}));
+ disable_pipe_a();
+ disable_pipe_b();
+ MUST(m_transcoders[0]->disable_dpll({}));
+ disable_vga_emulation();
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
+ MUST(m_transcoders[0]->set_dpll_settings({}, settings, dac_multiplier));
+ MUST(m_transcoders[0]->disable_dpll({}));
+ MUST(m_transcoders[0]->enable_dpll_without_vga({}));
+ MUST(m_transcoders[0]->set_mode_setting_timings({}, mode_setting));
+
+ VERIFY(!pipe_a_enabled());
+ enable_pipe_a();
+ MUST(m_planes[0]->set_plane_settings({}, m_mmio_second_region.pci_bar_paddr, IntelDisplayPlane::PipeSelect::PipeA, mode_setting.horizontal_active));
+ MUST(m_planes[0]->enable({}));
+ enable_dac_output();
+
+ return true;
+}
+
+bool IntelDisplayConnectorGroup::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;
+ microseconds_delay(1000);
+ current_time++;
+ }
+ return false;
+}
+bool IntelDisplayConnectorGroup::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;
+ microseconds_delay(1000);
+ current_time++;
+ }
+ return false;
+}
+
+bool IntelDisplayConnectorGroup::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;
+ microseconds_delay(1000);
+ current_time++;
+ }
+ return false;
+}
+
+void IntelDisplayConnectorGroup::disable_pipe_a()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::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 IntelDisplayConnectorGroup::disable_pipe_b()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::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 IntelDisplayConnectorGroup::enable_pipe_a()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ VERIFY(!(read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 31)));
+ VERIFY(!(read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 30)));
+ write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::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 IntelDisplayConnectorGroup::disable_dac_output()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, 0b11 << 10);
+}
+
+void IntelDisplayConnectorGroup::enable_dac_output()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, (1 << 31));
+}
+
+void IntelDisplayConnectorGroup::disable_vga_emulation()
+{
+ VERIFY(m_control_lock.is_locked());
+ VERIFY(m_modeset_lock.is_locked());
+ write_to_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl, (1 << 31));
+ read_from_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl);
+}
+
+}
diff --git a/Kernel/Graphics/Intel/DisplayConnectorGroup.h b/Kernel/Graphics/Intel/DisplayConnectorGroup.h
new file mode 100644
index 0000000000..f5b93a6a9d
--- /dev/null
+++ b/Kernel/Graphics/Intel/DisplayConnectorGroup.h
@@ -0,0 +1,115 @@
+/*
+ * 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/Intel/Auxiliary/GMBusConnector.h>
+#include <Kernel/Graphics/Intel/Definitions.h>
+#include <Kernel/Graphics/Intel/NativeDisplayConnector.h>
+#include <Kernel/Graphics/Intel/Plane/DisplayPlane.h>
+#include <Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h>
+#include <Kernel/Library/LockRefPtr.h>
+#include <Kernel/Memory/TypedMapping.h>
+#include <LibEDID/EDID.h>
+
+namespace Kernel {
+
+class IntelNativeGraphicsAdapter;
+class IntelDisplayConnectorGroup : public RefCounted<IntelDisplayConnectorGroup> {
+ friend class IntelNativeGraphicsAdapter;
+
+public:
+ enum class Generation {
+ Gen4,
+ };
+ struct MMIORegion {
+ enum class BARAssigned {
+ BAR0,
+ BAR2,
+ };
+ BARAssigned pci_bar_assigned;
+ PhysicalAddress pci_bar_paddr;
+ size_t pci_bar_space_length;
+ };
+
+private:
+ AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, RegisterOffset);
+
+ enum class AnalogOutputRegisterOffset {
+ AnalogDisplayPort = 0x61100,
+ VGADisplayPlaneControl = 0x71400,
+ };
+
+public:
+ static ErrorOr<NonnullLockRefPtr<IntelDisplayConnectorGroup>> try_create(Badge<IntelNativeGraphicsAdapter>, Generation, MMIORegion const&, MMIORegion const&);
+
+ ErrorOr<void> set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector&);
+ ErrorOr<void> set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
+
+private:
+ IntelDisplayConnectorGroup(Generation generation, NonnullOwnPtr<GMBusConnector>, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const&, MMIORegion const&);
+
+ ErrorOr<void> set_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
+
+ StringView convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const;
+ void write_to_analog_output_register(AnalogOutputRegisterOffset, u32 value);
+ u32 read_from_analog_output_register(AnalogOutputRegisterOffset) const;
+ u32 read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister index) const;
+ void write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister index, u32 value);
+ void write_to_general_register(RegisterOffset offset, u32 value);
+ u32 read_from_general_register(RegisterOffset offset) const;
+
+ // DisplayConnector initialization related methods
+ ErrorOr<void> initialize_connectors();
+ ErrorOr<void> initialize_gen4_connectors();
+
+ // General Modesetting methods
+ ErrorOr<void> set_gen4_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
+ bool pipe_a_enabled() const;
+ bool pipe_b_enabled() const;
+
+ bool set_crt_resolution(DisplayConnector::ModeSetting const&);
+
+ void disable_vga_emulation();
+ void enable_vga_plane();
+
+ void disable_dac_output();
+ void enable_dac_output();
+
+ void disable_pipe_a();
+ void disable_pipe_b();
+
+ void enable_pipe_a();
+
+ 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;
+
+ Optional<IntelGraphics::PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&);
+
+ Spinlock<LockRank::None> m_control_lock;
+ Spinlock<LockRank::None> m_modeset_lock;
+ mutable Spinlock<LockRank::None> m_registers_lock;
+
+ // Note: The linux driver specifies an enum of possible ports and there is only
+ // 9 ports (PORT_{A-I}). PORT_TC{1-6} are mapped to PORT_{D-I}.
+ Array<LockRefPtr<IntelNativeDisplayConnector>, 9> m_connectors;
+
+ Array<OwnPtr<IntelDisplayTranscoder>, 5> m_transcoders;
+ Array<OwnPtr<IntelDisplayPlane>, 3> m_planes;
+
+ const MMIORegion m_mmio_first_region;
+ const MMIORegion m_mmio_second_region;
+ MMIORegion const& m_assigned_mmio_registers_region;
+
+ const Generation m_generation;
+ NonnullOwnPtr<Memory::Region> m_registers_region;
+ NonnullOwnPtr<GMBusConnector> m_gmbus_connector;
+};
+}
diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.cpp b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp
index 3bfd099e07..2a7c8d6693 100644
--- a/Kernel/Graphics/Intel/NativeDisplayConnector.cpp
+++ b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp
@@ -10,194 +10,47 @@
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
#include <Kernel/Graphics/GraphicsManagement.h>
+#include <Kernel/Graphics/Intel/DisplayConnectorGroup.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)
+ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> IntelNativeDisplayConnector::try_create_with_display_connector_group(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size)
{
- if (target_frequency >= checked_frequency)
- return target_frequency - checked_frequency;
- return checked_frequency - target_frequency;
+ return TRY(DeviceManagement::try_create_device<IntelNativeDisplayConnector>(parent_connector_group, connector_index, type, framebuffer_address, framebuffer_resource_size));
}
-Optional<IntelGraphics::PLLSettings> IntelNativeDisplayConnector::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits)
+ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console(Badge<IntelDisplayConnectorGroup>)
{
- 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;
- }
- }
- }
- }
+ size_t width = 0;
+ size_t height = 0;
+ size_t pitch = 0;
+ {
+ SpinlockLocker control_locker(m_control_lock);
+ SpinlockLocker mode_set_locker(m_modeset_lock);
+ width = m_current_mode_setting.horizontal_active;
+ height = m_current_mode_setting.vertical_active;
+ pitch = m_current_mode_setting.horizontal_stride;
}
- if (best_settings.is_valid())
- return best_settings;
+ m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch);
+ GraphicsManagement::the().set_console(*m_framebuffer_console);
return {};
}
-ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> IntelNativeDisplayConnector::try_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, PhysicalAddress registers_region_address, size_t registers_region_length)
+IntelNativeDisplayConnector::IntelNativeDisplayConnector(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size)
+ : DisplayConnector(framebuffer_address, framebuffer_resource_size, true)
+ , m_type(type)
+ , m_connector_index(connector_index)
+ , m_parent_connector_group(parent_connector_group)
{
- auto registers_region = TRY(MM.allocate_kernel_region(PhysicalAddress(registers_region_address), registers_region_length, "Intel Native Graphics Registers"sv, Memory::Region::Access::ReadWrite));
-
- // FIXME: Try to put the address as parameter to this function to allow creating this DisplayConnector for many generations...
- auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(registers_region_address.offset(to_underlying(IntelGraphics::RegisterIndex::GMBusClock))));
-
- auto connector = TRY(DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, framebuffer_resource_size, move(gmbus_connector), move(registers_region)));
- TRY(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);
- TRY(connector->set_safe_mode_setting());
- }
- TRY(connector->create_attached_framebuffer_console());
- return connector;
}
-ErrorOr<void> IntelNativeDisplayConnector::initialize_gmbus_settings_and_read_edid()
+void IntelNativeDisplayConnector::set_edid_bytes(Badge<IntelDisplayConnectorGroup>, Array<u8, 128> const& raw_bytes)
{
- gmbus_read_edid();
- return {};
+ // Note: The provided EDID might be invalid (because there's no attached monitor)
+ // Therefore, set might_be_invalid to true to indicate that.
+ DisplayConnector::set_edid_bytes(raw_bytes, true);
}
ErrorOr<void> IntelNativeDisplayConnector::set_y_offset(size_t)
@@ -210,6 +63,12 @@ ErrorOr<void> IntelNativeDisplayConnector::unblank()
return Error::from_errno(ENOTIMPL);
}
+ErrorOr<void> IntelNativeDisplayConnector::set_safe_mode_setting()
+{
+ SpinlockLocker locker(m_modeset_lock);
+ return m_parent_connector_group->set_safe_mode_setting({}, *this);
+}
+
void IntelNativeDisplayConnector::enable_console()
{
VERIFY(m_control_lock.is_locked());
@@ -229,398 +88,9 @@ ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface()
return Error::from_errno(ENOTSUP);
}
-ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console()
-{
- m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), 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, size_t framebuffer_resource_size, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region)
- : DisplayConnector(framebuffer_address, framebuffer_resource_size, true)
- , m_registers_region(move(registers_region))
- , m_gmbus_connector(move(gmbus_connector))
-{
-}
-
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);
-}
-
-void IntelNativeDisplayConnector::gmbus_read_edid()
-{
- Array<u8, 128> crt_edid_bytes {};
- {
- SpinlockLocker control_lock(m_control_lock);
- MUST(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0));
- MUST(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size()));
- }
- 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.value(), 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;
-
- 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);
-
- microseconds_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;
- microseconds_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;
- microseconds_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;
- microseconds_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::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);
-
- microseconds_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
- microseconds_delay(200);
- VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
-}
-
-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);
-}
-
}
diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.h b/Kernel/Graphics/Intel/NativeDisplayConnector.h
index 2900fab636..d832a9d6c6 100644
--- a/Kernel/Graphics/Intel/NativeDisplayConnector.h
+++ b/Kernel/Graphics/Intel/NativeDisplayConnector.h
@@ -16,54 +16,42 @@
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;
-};
-}
-
+class IntelDisplayConnectorGroup;
class IntelNativeDisplayConnector final
: public DisplayConnector {
- friend class IntelNativeGraphicsAdapter;
+ friend class IntelDisplayConnectorGroup;
friend class DeviceManagement;
public:
- static ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> try_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, PhysicalAddress registers_region_address, size_t registers_region_length);
+ enum class Type {
+ Invalid,
+ Analog,
+ DVO,
+ LVDS,
+ TVOut,
+ HDMI,
+ DisplayPort,
+ EmbeddedDisplayPort,
+ };
+
+ enum class ConnectorIndex : size_t {
+ PortA = 0,
+ PortB = 1,
+ PortC = 2,
+ PortD = 3,
+ PortE = 4,
+ PortF = 5,
+ PortH = 6,
+ PortG = 7,
+ PortI = 8,
+ };
+
+ static ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> try_create_with_display_connector_group(IntelDisplayConnectorGroup const&, ConnectorIndex, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size);
+
+ void set_edid_bytes(Badge<IntelDisplayConnectorGroup>, Array<u8, 128> const& edid_bytes);
+ ErrorOr<void> create_attached_framebuffer_console(Badge<IntelDisplayConnectorGroup>);
+
+ ConnectorIndex connector_index() const { return m_connector_index; }
private:
// ^DisplayConnector
@@ -83,55 +71,10 @@ private:
// Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting.
virtual bool refresh_rate_support() const override { return true; }
- IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, NonnullOwnPtr<GMBusConnector>, NonnullOwnPtr<Memory::Region> registers_region);
-
- ErrorOr<void> create_attached_framebuffer_console();
- ErrorOr<void> initialize_gmbus_settings_and_read_edid();
-
- 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 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 gmbus_read_edid();
-
- Optional<IntelGraphics::PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&);
-
- mutable Spinlock<LockRank::None> m_registers_lock {};
+ IntelNativeDisplayConnector(IntelDisplayConnectorGroup const&, ConnectorIndex connector_index, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size);
+ Type const m_type { Type::Analog };
+ ConnectorIndex const m_connector_index { 0 };
+ NonnullLockRefPtr<IntelDisplayConnectorGroup> m_parent_connector_group;
LockRefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console;
-
- const PhysicalAddress m_registers;
- NonnullOwnPtr<Memory::Region> m_registers_region;
- NonnullOwnPtr<GMBusConnector> m_gmbus_connector;
};
}
diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp
index 40e07bd3be..7252417033 100644
--- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp
+++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp
@@ -42,14 +42,26 @@ ErrorOr<void> IntelNativeGraphicsAdapter::initialize_adapter()
{
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", device_identifier().address());
auto bar0_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR0);
- VERIFY(bar0_space_size == 0x80000);
auto bar2_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR2);
dmesgln_pci(*this, "MMIO @ {}, space size is {:x} bytes", PhysicalAddress(PCI::get_BAR0(device_identifier())), bar0_space_size);
dmesgln_pci(*this, "framebuffer @ {}", PhysicalAddress(PCI::get_BAR2(device_identifier())));
+
+ using MMIORegion = IntelDisplayConnectorGroup::MMIORegion;
+ MMIORegion first_region { MMIORegion::BARAssigned::BAR0, PhysicalAddress(PCI::get_BAR0(device_identifier()) & 0xfffffff0), bar0_space_size };
+ MMIORegion second_region { MMIORegion::BARAssigned::BAR2, PhysicalAddress(PCI::get_BAR2(device_identifier()) & 0xfffffff0), bar2_space_size };
+
PCI::enable_bus_mastering(device_identifier());
+ PCI::enable_io_space(device_identifier());
+ PCI::enable_memory_space(device_identifier());
- m_display_connector = TRY(IntelNativeDisplayConnector::try_create(PhysicalAddress(PCI::get_BAR2(device_identifier()) & 0xfffffff0), bar2_space_size, PhysicalAddress(PCI::get_BAR0(device_identifier()) & 0xfffffff0), bar0_space_size));
- return {};
+ using Generation = IntelDisplayConnectorGroup::Generation;
+ switch (device_identifier().hardware_id().device_id) {
+ case 0x29c2:
+ m_connector_group = TRY(IntelDisplayConnectorGroup::try_create({}, Generation::Gen4, first_region, second_region));
+ return {};
+ default:
+ return Error::from_errno(ENODEV);
+ }
}
IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier)
diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h
index 0219c63738..321e9763fe 100644
--- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h
+++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h
@@ -9,6 +9,7 @@
#include <AK/Types.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Graphics/Definitions.h>
+#include <Kernel/Graphics/Intel/DisplayConnectorGroup.h>
#include <Kernel/Graphics/Intel/NativeDisplayConnector.h>
#include <Kernel/PhysicalAddress.h>
#include <LibEDID/EDID.h>
@@ -32,6 +33,6 @@ private:
explicit IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const&);
- LockRefPtr<IntelNativeDisplayConnector> m_display_connector;
+ LockRefPtr<IntelDisplayConnectorGroup> m_connector_group;
};
}
diff --git a/Kernel/Graphics/Intel/Plane/DisplayPlane.cpp b/Kernel/Graphics/Intel/Plane/DisplayPlane.cpp
new file mode 100644
index 0000000000..21010ea012
--- /dev/null
+++ b/Kernel/Graphics/Intel/Plane/DisplayPlane.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Intel/Plane/DisplayPlane.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel {
+
+IntelDisplayPlane::IntelDisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> plane_registers_mapping)
+ : m_plane_registers(move(plane_registers_mapping))
+{
+}
+
+IntelDisplayPlane::ShadowRegisters IntelDisplayPlane::shadow_registers() const
+{
+ SpinlockLocker locker(m_access_lock);
+ return m_shadow_registers;
+}
+
+ErrorOr<void> IntelDisplayPlane::enable(Badge<IntelDisplayConnectorGroup>)
+{
+ SpinlockLocker locker(m_access_lock);
+ // Note: We use the shadow register so we don't have the already set
+ // settings being lost.
+ m_plane_registers->control = m_shadow_registers.control | (1 << 31);
+ m_shadow_registers.control |= (1 << 31);
+ return {};
+}
+
+bool IntelDisplayPlane::is_enabled(Badge<IntelDisplayConnectorGroup>)
+{
+ SpinlockLocker locker(m_access_lock);
+ return m_shadow_registers.control & (1 << 31);
+}
+
+ErrorOr<void> IntelDisplayPlane::disable(Badge<IntelDisplayConnectorGroup>)
+{
+ SpinlockLocker locker(m_access_lock);
+ // Note: We use the shadow register so we don't have the already set
+ // settings being lost.
+ m_shadow_registers.control &= ~(1 << 31);
+ m_plane_registers->control = m_shadow_registers.control;
+ return {};
+}
+
+}
diff --git a/Kernel/Graphics/Intel/Plane/DisplayPlane.h b/Kernel/Graphics/Intel/Plane/DisplayPlane.h
new file mode 100644
index 0000000000..47f08ab111
--- /dev/null
+++ b/Kernel/Graphics/Intel/Plane/DisplayPlane.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <AK/Types.h>
+#include <Kernel/Graphics/DisplayConnector.h>
+#include <Kernel/Graphics/Intel/Definitions.h>
+#include <Kernel/Locking/Spinlock.h>
+#include <Kernel/Memory/TypedMapping.h>
+
+namespace Kernel {
+
+class IntelDisplayConnectorGroup;
+class IntelDisplayPlane {
+public:
+ enum class PipeSelect {
+ PipeA,
+ PipeB,
+ PipeC,
+ PipeD,
+ };
+
+ // Note: This is used to "cache" all the registers we wrote to, because
+ // we might not be able to read them directly from hardware later.
+ struct ShadowRegisters {
+ u32 control;
+ u32 linear_offset;
+ u32 stride;
+ u32 surface_base;
+ };
+
+public:
+ static ErrorOr<NonnullOwnPtr<IntelDisplayPlane>> create_with_physical_address(PhysicalAddress plane_registers_start_address);
+
+ virtual ErrorOr<void> set_plane_settings(Badge<IntelDisplayConnectorGroup>, PhysicalAddress aperture_start, PipeSelect, size_t horizontal_active_pixels_count) = 0;
+ ErrorOr<void> enable(Badge<IntelDisplayConnectorGroup>);
+ bool is_enabled(Badge<IntelDisplayConnectorGroup>);
+ ErrorOr<void> disable(Badge<IntelDisplayConnectorGroup>);
+
+ ShadowRegisters shadow_registers() const;
+
+ virtual ~IntelDisplayPlane() = default;
+
+protected:
+ struct [[gnu::packed]] PlaneRegisters {
+ u32 control;
+ u32 linear_offset;
+ u32 stride;
+ u8 padding[24]; // Note: This might contain other registers, don't touch them.
+ u32 surface_base;
+ };
+
+ explicit IntelDisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> registers_mapping);
+ mutable Spinlock<LockRank::None> m_access_lock;
+ ShadowRegisters m_shadow_registers {};
+ Memory::TypedMapping<PlaneRegisters volatile> m_plane_registers;
+};
+}
diff --git a/Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp b/Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp
new file mode 100644
index 0000000000..c71635f205
--- /dev/null
+++ b/Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Intel/Plane/G33DisplayPlane.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel {
+
+ErrorOr<NonnullOwnPtr<IntelG33DisplayPlane>> IntelG33DisplayPlane::create_with_physical_address(PhysicalAddress plane_registers_start_address)
+{
+ auto registers_mapping = TRY(Memory::map_typed<PlaneRegisters volatile>(plane_registers_start_address, sizeof(PlaneRegisters), Memory::Region::Access::ReadWrite));
+ return adopt_nonnull_own_or_enomem(new (nothrow) IntelG33DisplayPlane(move(registers_mapping)));
+}
+
+IntelG33DisplayPlane::IntelG33DisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> registers_mapping)
+ : IntelDisplayPlane(move(registers_mapping))
+{
+}
+
+ErrorOr<void> IntelG33DisplayPlane::set_plane_settings(Badge<IntelDisplayConnectorGroup>, PhysicalAddress aperture_start, PipeSelect pipe_select, size_t horizontal_active_pixels_count)
+{
+ SpinlockLocker locker(m_access_lock);
+ VERIFY(((horizontal_active_pixels_count * 4) % 64 == 0));
+ VERIFY(aperture_start < PhysicalAddress(0x1'0000'0000));
+
+ u32 control_value = 0;
+
+ switch (pipe_select) {
+ case PipeSelect::PipeA:
+ control_value |= (0b00 << 24);
+ break;
+ case PipeSelect::PipeB:
+ control_value |= (0b01 << 24);
+ break;
+ case PipeSelect::PipeC:
+ control_value |= (0b10 << 24);
+ break;
+ case PipeSelect::PipeD:
+ control_value |= (0b11 << 24);
+ break;
+ }
+
+ // Note: Set the plane to work with 32 bit BGRX (Ignore Alpha channel).
+ control_value |= (0b0110 << 26);
+
+ m_plane_registers->stride = horizontal_active_pixels_count * 4;
+ m_shadow_registers.stride = horizontal_active_pixels_count * 4;
+ m_plane_registers->linear_offset = 0;
+ m_shadow_registers.linear_offset = 0;
+ m_plane_registers->surface_base = aperture_start.get();
+ m_shadow_registers.surface_base = aperture_start.get();
+ m_plane_registers->control = control_value;
+ m_shadow_registers.control = control_value;
+ return {};
+}
+}
diff --git a/Kernel/Graphics/Intel/Plane/G33DisplayPlane.h b/Kernel/Graphics/Intel/Plane/G33DisplayPlane.h
new file mode 100644
index 0000000000..de0d02d4fc
--- /dev/null
+++ b/Kernel/Graphics/Intel/Plane/G33DisplayPlane.h
@@ -0,0 +1,26 @@
+/*
+ * 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 <AK/Types.h>
+#include <Kernel/Graphics/Intel/Plane/DisplayPlane.h>
+
+namespace Kernel {
+
+class IntelDisplayConnectorGroup;
+class IntelG33DisplayPlane final : public IntelDisplayPlane {
+public:
+ static ErrorOr<NonnullOwnPtr<IntelG33DisplayPlane>> create_with_physical_address(PhysicalAddress plane_registers_start_address);
+
+ virtual ErrorOr<void> set_plane_settings(Badge<IntelDisplayConnectorGroup>, PhysicalAddress aperture_start, PipeSelect, size_t horizontal_active_pixels_count) override;
+
+private:
+ explicit IntelG33DisplayPlane(Memory::TypedMapping<volatile IntelDisplayPlane::PlaneRegisters> plane_registers_mapping);
+};
+}
diff --git a/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp b/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp
new file mode 100644
index 0000000000..af86115a05
--- /dev/null
+++ b/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Arch/Delay.h>
+#include <Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel {
+
+ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> IntelAnalogDisplayTranscoder::create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address,
+ PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_multiplier_register_start_address)
+{
+ auto transcoder_registers_mapping = TRY(Memory::map_typed<TranscoderRegisters volatile>(transcoder_registers_start_address, sizeof(IntelDisplayTranscoder::TranscoderRegisters), Memory::Region::Access::ReadWrite));
+ auto dpll_registers_mapping = TRY(Memory::map_typed<DPLLRegisters volatile>(dpll_registers_start_address, sizeof(DPLLRegisters), Memory::Region::Access::ReadWrite));
+ auto dpll_control_mapping = TRY(Memory::map_typed<DPLLControlRegisters volatile>(dpll_multiplier_register_start_address, sizeof(DPLLControlRegisters), Memory::Region::Access::ReadWrite));
+ return adopt_nonnull_own_or_enomem(new (nothrow) IntelAnalogDisplayTranscoder(move(transcoder_registers_mapping), move(dpll_registers_mapping), move(dpll_control_mapping)));
+}
+
+IntelAnalogDisplayTranscoder::IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> transcoder_registers_mapping,
+ Memory::TypedMapping<DPLLRegisters volatile> dpll_registers_mapping, Memory::TypedMapping<DPLLControlRegisters volatile> dpll_control_registers)
+ : IntelDisplayTranscoder(move(transcoder_registers_mapping))
+ , m_dpll_registers(move(dpll_registers_mapping))
+ , m_dpll_control_registers(move(dpll_control_registers))
+{
+}
+
+ErrorOr<void> IntelAnalogDisplayTranscoder::set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier)
+{
+ SpinlockLocker locker(m_access_lock);
+ u32 value = (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16);
+ m_dpll_registers->divisor_a0 = value;
+ m_dpll_registers->divisor_a1 = value;
+ m_shadow_registers.dpll_divisor_a0 = value;
+ m_shadow_registers.dpll_divisor_a1 = value;
+
+ // Note: We don't set the DAC multiplier now but reserve it for later usage (e.g. when enabling the DPLL)
+ m_shadow_registers.dpll_reserved_dac_multiplier = dac_multiplier;
+ // Note: We don't set the DPLL P1 now but reserve it for later usage (e.g. when enabling the DPLL)
+ m_shadow_registers.dpll_p1 = settings.p1;
+ return {};
+}
+
+ErrorOr<void> IntelAnalogDisplayTranscoder::enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>)
+{
+ SpinlockLocker locker(m_access_lock);
+ // Explanation for Gen4 DPLL control bits:
+ // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
+ // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
+ // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
+ // 4. bit 28 - set to 0b1 to disable VGA mode
+ // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
+ u32 control_value = (6 << 9) | (m_shadow_registers.dpll_p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31);
+ m_dpll_control_registers->control = control_value;
+ m_shadow_registers.dpll_control = control_value;
+
+ // Explanation for Gen4 DPLL multiplier bits:
+ // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
+ // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
+ // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
+ // 4. bit 28 - set to 0b1 to disable VGA mode
+ // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
+ u32 dac_multiplier_value = (m_shadow_registers.dpll_reserved_dac_multiplier - 1) | ((m_shadow_registers.dpll_reserved_dac_multiplier - 1) << 8);
+ m_dpll_control_registers->multiplier = dac_multiplier_value;
+ m_shadow_registers.dpll_raw_dac_multiplier = dac_multiplier_value;
+
+ // The specification says we should wait (at least) about 150 microseconds
+ // after enabling the DPLL to allow the clock to stabilize
+ microseconds_delay(200);
+ for (size_t milliseconds_elapsed = 0; milliseconds_elapsed < 5; milliseconds_elapsed++) {
+ u32 control_value = m_dpll_control_registers->control;
+ if (control_value & (1 << 31))
+ return {};
+ }
+ return Error::from_errno(EBUSY);
+}
+
+ErrorOr<void> IntelAnalogDisplayTranscoder::disable_dpll(Badge<IntelDisplayConnectorGroup>)
+{
+ SpinlockLocker locker(m_access_lock);
+ m_dpll_control_registers->control = 0;
+ m_shadow_registers.dpll_control = 0;
+ return {};
+}
+
+}
diff --git a/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h b/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h
new file mode 100644
index 0000000000..ac0ff9e725
--- /dev/null
+++ b/Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <AK/Types.h>
+#include <Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h>
+
+namespace Kernel {
+
+class IntelDisplayConnectorGroup;
+class IntelAnalogDisplayTranscoder final : public IntelDisplayTranscoder {
+public:
+ static ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address,
+ PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_control_registers_start_address);
+
+ virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) override;
+ virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) override;
+ virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) override;
+
+private:
+ struct [[gnu::packed]] DPLLRegisters {
+ u32 divisor_a0;
+ u32 divisor_a1;
+ };
+
+ struct [[gnu::packed]] DPLLControlRegisters {
+ u32 control;
+ u32 padding; // On Gen4, this is the control register of DPLL B, don't touch this
+ u32 multiplier;
+ };
+
+ IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>, Memory::TypedMapping<DPLLRegisters volatile>, Memory::TypedMapping<DPLLControlRegisters volatile>);
+ Memory::TypedMapping<DPLLRegisters volatile> m_dpll_registers;
+ Memory::TypedMapping<DPLLControlRegisters volatile> m_dpll_control_registers;
+};
+}
diff --git a/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.cpp b/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.cpp
new file mode 100644
index 0000000000..72fbc5ca9c
--- /dev/null
+++ b/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel {
+
+IntelDisplayTranscoder::IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> registers_mapping)
+ : m_transcoder_registers(move(registers_mapping))
+{
+}
+
+IntelDisplayTranscoder::ShadowRegisters IntelDisplayTranscoder::current_registers_state() const
+{
+ SpinlockLocker locker(m_access_lock);
+ return m_shadow_registers;
+}
+
+ErrorOr<void> IntelDisplayTranscoder::set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const& mode_setting)
+{
+ SpinlockLocker locker(m_access_lock);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (mode_setting.horizontal_active - 1), (mode_setting.horizontal_total() - 1));
+ m_shadow_registers.horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16);
+ m_transcoder_registers->horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (mode_setting.horizontal_blanking_start() - 1), (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1));
+ m_shadow_registers.horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16);
+ m_transcoder_registers->horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (mode_setting.horizontal_sync_start() - 1), (mode_setting.horizontal_sync_end() - 1));
+ m_shadow_registers.horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16);
+ m_transcoder_registers->horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1));
+ m_shadow_registers.vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
+ m_transcoder_registers->vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (mode_setting.vertical_blanking_start() - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1));
+ m_shadow_registers.vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
+ m_transcoder_registers->vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (mode_setting.vertical_sync_start() - 1), (mode_setting.vertical_sync_end() - 1));
+ m_shadow_registers.vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16);
+ m_transcoder_registers->vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16);
+
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.horizontal_active - 1));
+ m_shadow_registers.pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16);
+ m_transcoder_registers->pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16);
+ return {};
+}
+
+}
diff --git a/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h b/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h
new file mode 100644
index 0000000000..1a6551cfb7
--- /dev/null
+++ b/Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <AK/Types.h>
+#include <Kernel/Graphics/DisplayConnector.h>
+#include <Kernel/Graphics/Intel/Definitions.h>
+#include <Kernel/Locking/Spinlock.h>
+#include <Kernel/Memory/TypedMapping.h>
+
+namespace Kernel {
+
+class IntelDisplayConnectorGroup;
+class IntelDisplayTranscoder {
+public:
+ // Note: This is used to "cache" all the registers we wrote to, because
+ // we might not be able to read them directly from hardware later.
+ struct ShadowRegisters {
+ u32 horizontal_total;
+ u32 horizontal_blank;
+ u32 horizontal_sync;
+ u32 vertical_total;
+ u32 vertical_blank;
+ u32 vertical_sync;
+ u32 exit_line;
+ u32 pipe_source;
+ u32 pipe_border_color_pattern;
+ u32 reserved;
+ u32 vsync_shift;
+ u32 pipe_mult;
+ u32 dpll_reserved_dac_multiplier;
+ u32 dpll_raw_dac_multiplier;
+ u32 dpll_divisor_a0;
+ u32 dpll_divisor_a1;
+ u32 dpll_p1;
+ u32 dpll_control;
+ u32 m1_value;
+ u32 n1_value;
+ u32 m2_value;
+ u32 n2_value;
+ u32 m1_link;
+ u32 n1_link;
+ u32 m2_link;
+ u32 n2_link;
+ };
+
+ ErrorOr<void> set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const&);
+ virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) = 0;
+ virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) = 0;
+ virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) = 0;
+
+ ShadowRegisters current_registers_state() const;
+
+ virtual ~IntelDisplayTranscoder() = default;
+
+protected:
+ struct [[gnu::packed]] TranscoderRegisters {
+ u32 horizontal_total;
+ u32 horizontal_blank;
+ u32 horizontal_sync;
+ u32 vertical_total;
+ u32 vertical_blank;
+ u32 vertical_sync;
+ u32 exit_line;
+ u32 pipe_source;
+ u32 pipe_border_color_pattern;
+ u32 reserved;
+ u32 vsync_shift;
+ u32 pipe_mult;
+ u32 m1_value;
+ u32 n1_value;
+ u32 m2_value;
+ u32 n2_value;
+ u32 m1_link;
+ u32 n1_link;
+ u32 m2_link;
+ u32 n2_link;
+ };
+
+ explicit IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>);
+ mutable Spinlock<LockRank::None> m_access_lock;
+ ShadowRegisters m_shadow_registers {};
+ Memory::TypedMapping<TranscoderRegisters volatile> m_transcoder_registers;
+};
+}