From 2def16a3d21ec3984c3f7d493c6d80b91d4adb8a Mon Sep 17 00:00:00 2001 From: Liav A Date: Thu, 5 May 2022 13:25:51 +0300 Subject: Kernel/Graphics: Introduce the IntelDisplayConnectorGroup class In the real world, graphics hardware tend to have multiple display connectors. However, usually the connectors share one register space but still keeping different PLL timings and display lanes. This new class should represent a group of multiple display connectors working together in the same Intel graphics adapter. This opens an opportunity to abstract the interface so we could support future Intel iGPU generations. This is also a preparation before the driver can support newer devices and utilize their capabilities. The mentioned preparation is applied in a these aspects: 1. The code is splitted into more classes to adjust to future expansion. 2 classes are introduced: IntelDisplayPlane and IntelDisplayTranscoder, so the IntelDisplayPlane controls the plane registers and second class controls the pipeline (transcoder, encoder) registers. On gen4 it's not really useful because there are probably one plane and one encoder to care about, but in future generations, there are likely to be multiple transcoders and planes to accommodate multi head support. 2. The set_edid_bytes method in the DisplayConnector class can now be told to not assume the provided EDID bytes are always invalid. Therefore it can refrain from printing error messages if this flag parameter is true. This is useful for supporting real hardware situation when on boot not all ports are connected to a monitor, which can result in floating bus condition (essentially all the bytes we read are 0xFF). 3. An IntelNativeDisplayConnector could now be set to flag other types of connections such as eDP (embedded DisplayPort), Analog output, etc. This is important because on the Intel gen4 graphics we could assume to have one analog output connector, but on future generations this is very likely to not be the case, as there might be no VGA outputs, but rather only an eDP connector which is converted to VGA by a design choice of the motherboard manufacturer. 4. Add ConnectorIndex to IntelNativeDisplayConnector class - Currently this is used to verify we always handle the correct connector when doing modesetting. Later, it will be used to locate special settings needed when handling connector requests. 5. Prepare to support more types of display planes. For example, the Intel Skylake register set for display planes is a bit different, so let's ensure we can properly support it in the near future. --- Kernel/CMakeLists.txt | 5 + Kernel/Graphics/Definitions.h | 21 - Kernel/Graphics/DisplayConnector.cpp | 20 +- Kernel/Graphics/DisplayConnector.h | 2 +- Kernel/Graphics/Intel/Definitions.h | 55 ++ Kernel/Graphics/Intel/DisplayConnectorGroup.cpp | 464 ++++++++++++++++ Kernel/Graphics/Intel/DisplayConnectorGroup.h | 115 ++++ Kernel/Graphics/Intel/NativeDisplayConnector.cpp | 590 ++------------------- Kernel/Graphics/Intel/NativeDisplayConnector.h | 127 ++--- Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp | 18 +- Kernel/Graphics/Intel/NativeGraphicsAdapter.h | 3 +- Kernel/Graphics/Intel/Plane/DisplayPlane.cpp | 49 ++ Kernel/Graphics/Intel/Plane/DisplayPlane.h | 64 +++ Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp | 59 +++ Kernel/Graphics/Intel/Plane/G33DisplayPlane.h | 26 + .../Intel/Transcoder/AnalogDisplayTranscoder.cpp | 88 +++ .../Intel/Transcoder/AnalogDisplayTranscoder.h | 42 ++ .../Intel/Transcoder/DisplayTranscoder.cpp | 57 ++ .../Graphics/Intel/Transcoder/DisplayTranscoder.h | 91 ++++ 19 files changed, 1209 insertions(+), 687 deletions(-) create mode 100644 Kernel/Graphics/Intel/Definitions.h create mode 100644 Kernel/Graphics/Intel/DisplayConnectorGroup.cpp create mode 100644 Kernel/Graphics/Intel/DisplayConnectorGroup.h create mode 100644 Kernel/Graphics/Intel/Plane/DisplayPlane.cpp create mode 100644 Kernel/Graphics/Intel/Plane/DisplayPlane.h create mode 100644 Kernel/Graphics/Intel/Plane/G33DisplayPlane.cpp create mode 100644 Kernel/Graphics/Intel/Plane/G33DisplayPlane.h create mode 100644 Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.cpp create mode 100644 Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h create mode 100644 Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.cpp create mode 100644 Kernel/Graphics/Intel/Transcoder/DisplayTranscoder.h (limited to 'Kernel') 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 DisplayConnector::initialize_edid_for_generic_monitor(Optional const& edid_bytes) +void DisplayConnector::set_edid_bytes(Array 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 const& edid_bytes); + void set_edid_bytes(Array 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +namespace IntelGraphics { + +static constexpr PLLMaxSettings G35Limits { + { 20'000'000, 400'000'000 }, // values in Hz, dot_clock + { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO + { 3, 8 }, // n + { 70, 120 }, // m + { 10, 20 }, // m1 + { 5, 9 }, // m2 + { 5, 80 }, // p + { 1, 8 }, // p1 + { 5, 10 } // p2 +}; +} + +static bool check_pll_settings(IntelGraphics::PLLSettings const& settings, size_t reference_clock, IntelGraphics::PLLMaxSettings const& limits) +{ + if (settings.n < limits.n.min || settings.n > limits.n.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); + return false; + } + if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); + return false; + } + if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); + return false; + } + if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); + return false; + } + + if (settings.m1 <= settings.m2) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); + return false; + } + + auto m = settings.compute_m(); + auto p = settings.compute_p(); + + if (m < limits.m.min || m > limits.m.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); + return false; + } + if (p < limits.p.min || p > limits.p.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); + return false; + } + + auto dot = settings.compute_dot_clock(reference_clock); + auto vco = settings.compute_vco(reference_clock); + + if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); + return false; + } + if (vco < limits.vco.min || vco > limits.vco.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); + return false; + } + return true; +} + +static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) +{ + if (target_frequency >= checked_frequency) + return target_frequency - checked_frequency; + return checked_frequency - target_frequency; +} + +Optional IntelDisplayConnectorGroup::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits) +{ + IntelGraphics::PLLSettings settings; + IntelGraphics::PLLSettings best_settings; + // FIXME: Is this correct for all Intel Native graphics cards? + settings.p2 = 10; + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); + u64 best_difference = 0xffffffff; + for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { + for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { + for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { + for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); + if (!check_pll_settings(settings, reference_clock, limits)) + continue; + auto current_dot_clock = settings.compute_dot_clock(reference_clock); + if (current_dot_clock == target_frequency) + return settings; + auto difference = find_absolute_difference(target_frequency, current_dot_clock); + if (difference < best_difference && (current_dot_clock > target_frequency)) { + best_settings = settings; + best_difference = difference; + } + } + } + } + } + if (best_settings.is_valid()) + return best_settings; + return {}; +} + +ErrorOr> IntelDisplayConnectorGroup::try_create(Badge, 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 gmbus_connector, NonnullOwnPtr 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 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 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 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 IntelDisplayConnectorGroup::set_safe_mode_setting(Badge, 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 IntelDisplayConnectorGroup::set_mode_setting(Badge, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) +{ + return set_mode_setting(connector, mode_setting); +} + +ErrorOr 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(connector.m_framebuffer_console.ptr())->set_resolution(actual_mode_setting.horizontal_active, actual_mode_setting.vertical_active, actual_mode_setting.horizontal_stride); + return {}; +} + +ErrorOr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class IntelNativeGraphicsAdapter; +class IntelDisplayConnectorGroup : public RefCounted { + 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> try_create(Badge, Generation, MMIORegion const&, MMIORegion const&); + + ErrorOr set_safe_mode_setting(Badge, IntelNativeDisplayConnector&); + ErrorOr set_mode_setting(Badge, IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&); + +private: + IntelDisplayConnectorGroup(Generation generation, NonnullOwnPtr, NonnullOwnPtr registers_region, MMIORegion const&, MMIORegion const&); + + ErrorOr 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 initialize_connectors(); + ErrorOr initialize_gen4_connectors(); + + // General Modesetting methods + ErrorOr 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 create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&); + + Spinlock m_control_lock; + Spinlock m_modeset_lock; + mutable Spinlock m_registers_lock; + + // 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, 9> m_connectors; + + Array, 5> m_transcoders; + Array, 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 m_registers_region; + NonnullOwnPtr 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 #include #include +#include #include #include 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> 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(parent_connector_group, connector_index, type, framebuffer_address, framebuffer_resource_size)); } -Optional IntelNativeDisplayConnector::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits) +ErrorOr IntelNativeDisplayConnector::create_attached_framebuffer_console(Badge) { - 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> 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(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 IntelNativeDisplayConnector::initialize_gmbus_settings_and_read_edid() +void IntelNativeDisplayConnector::set_edid_bytes(Badge, Array 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 IntelNativeDisplayConnector::set_y_offset(size_t) @@ -210,6 +63,12 @@ ErrorOr IntelNativeDisplayConnector::unblank() return Error::from_errno(ENOTIMPL); } +ErrorOr 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 IntelNativeDisplayConnector::flush_first_surface() return Error::from_errno(ENOTSUP); } -ErrorOr 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 gmbus_connector, NonnullOwnPtr registers_region) - : DisplayConnector(framebuffer_address, framebuffer_resource_size, true) - , m_registers_region(move(registers_region)) - , m_gmbus_connector(move(gmbus_connector)) -{ -} - ErrorOr IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&) { return Error::from_errno(ENOTIMPL); } -ErrorOr 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 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> 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> try_create_with_display_connector_group(IntelDisplayConnectorGroup const&, ConnectorIndex, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size); + + void set_edid_bytes(Badge, Array const& edid_bytes); + ErrorOr create_attached_framebuffer_console(Badge); + + 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, NonnullOwnPtr registers_region); - - ErrorOr create_attached_framebuffer_console(); - ErrorOr 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 create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&); - - mutable Spinlock 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 m_parent_connector_group; LockRefPtr m_framebuffer_console; - - const PhysicalAddress m_registers; - NonnullOwnPtr m_registers_region; - NonnullOwnPtr 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 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 #include #include +#include #include #include #include @@ -32,6 +33,6 @@ private: explicit IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const&); - LockRefPtr m_display_connector; + LockRefPtr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +IntelDisplayPlane::IntelDisplayPlane(Memory::TypedMapping 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 IntelDisplayPlane::enable(Badge) +{ + 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) +{ + SpinlockLocker locker(m_access_lock); + return m_shadow_registers.control & (1 << 31); +} + +ErrorOr IntelDisplayPlane::disable(Badge) +{ + 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +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> create_with_physical_address(PhysicalAddress plane_registers_start_address); + + virtual ErrorOr set_plane_settings(Badge, PhysicalAddress aperture_start, PipeSelect, size_t horizontal_active_pixels_count) = 0; + ErrorOr enable(Badge); + bool is_enabled(Badge); + ErrorOr disable(Badge); + + 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 registers_mapping); + mutable Spinlock m_access_lock; + ShadowRegisters m_shadow_registers {}; + Memory::TypedMapping 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +ErrorOr> IntelG33DisplayPlane::create_with_physical_address(PhysicalAddress plane_registers_start_address) +{ + auto registers_mapping = TRY(Memory::map_typed(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 registers_mapping) + : IntelDisplayPlane(move(registers_mapping)) +{ +} + +ErrorOr IntelG33DisplayPlane::set_plane_settings(Badge, 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class IntelDisplayConnectorGroup; +class IntelG33DisplayPlane final : public IntelDisplayPlane { +public: + static ErrorOr> create_with_physical_address(PhysicalAddress plane_registers_start_address); + + virtual ErrorOr set_plane_settings(Badge, PhysicalAddress aperture_start, PipeSelect, size_t horizontal_active_pixels_count) override; + +private: + explicit IntelG33DisplayPlane(Memory::TypedMapping 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel { + +ErrorOr> 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(transcoder_registers_start_address, sizeof(IntelDisplayTranscoder::TranscoderRegisters), Memory::Region::Access::ReadWrite)); + auto dpll_registers_mapping = TRY(Memory::map_typed(dpll_registers_start_address, sizeof(DPLLRegisters), Memory::Region::Access::ReadWrite)); + auto dpll_control_mapping = TRY(Memory::map_typed(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 transcoder_registers_mapping, + Memory::TypedMapping dpll_registers_mapping, Memory::TypedMapping dpll_control_registers) + : IntelDisplayTranscoder(move(transcoder_registers_mapping)) + , m_dpll_registers(move(dpll_registers_mapping)) + , m_dpll_control_registers(move(dpll_control_registers)) +{ +} + +ErrorOr IntelAnalogDisplayTranscoder::set_dpll_settings(Badge, 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 IntelAnalogDisplayTranscoder::enable_dpll_without_vga(Badge) +{ + 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 IntelAnalogDisplayTranscoder::disable_dpll(Badge) +{ + 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class IntelDisplayConnectorGroup; +class IntelAnalogDisplayTranscoder final : public IntelDisplayTranscoder { +public: + static ErrorOr> create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address, + PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_control_registers_start_address); + + virtual ErrorOr set_dpll_settings(Badge, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) override; + virtual ErrorOr enable_dpll_without_vga(Badge) override; + virtual ErrorOr disable_dpll(Badge) 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, Memory::TypedMapping, Memory::TypedMapping); + Memory::TypedMapping m_dpll_registers; + Memory::TypedMapping 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +IntelDisplayTranscoder::IntelDisplayTranscoder(Memory::TypedMapping 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 IntelDisplayTranscoder::set_mode_setting_timings(Badge, 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +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 set_mode_setting_timings(Badge, DisplayConnector::ModeSetting const&); + virtual ErrorOr set_dpll_settings(Badge, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) = 0; + virtual ErrorOr enable_dpll_without_vga(Badge) = 0; + virtual ErrorOr disable_dpll(Badge) = 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); + mutable Spinlock m_access_lock; + ShadowRegisters m_shadow_registers {}; + Memory::TypedMapping m_transcoder_registers; +}; +} -- cgit v1.2.3