diff options
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; +}; +} |