diff options
author | Liav A <liavalb@gmail.com> | 2022-04-30 08:28:38 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-05-05 20:55:57 +0200 |
commit | 728358c5996657e56a367e889723f39343176d46 (patch) | |
tree | d6dabbb09549deead08713ef80b212396e340f87 /Kernel/Graphics | |
parent | e9a74cbefba1bdef843362cfb90e8e3c1007643f (diff) | |
download | serenity-728358c5996657e56a367e889723f39343176d46.zip |
Kernel/Graphics: Migrate Intel driver to use the DisplayConnector design
Diffstat (limited to 'Kernel/Graphics')
-rw-r--r-- | Kernel/Graphics/Intel/NativeDisplayConnector.cpp | 732 | ||||
-rw-r--r-- | Kernel/Graphics/Intel/NativeDisplayConnector.h | 167 | ||||
-rw-r--r-- | Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp | 641 | ||||
-rw-r--r-- | Kernel/Graphics/Intel/NativeGraphicsAdapter.h | 167 |
4 files changed, 929 insertions, 778 deletions
diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.cpp b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp new file mode 100644 index 0000000000..724f4c4b1a --- /dev/null +++ b/Kernel/Graphics/Intel/NativeDisplayConnector.cpp @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/Arch/x86/IO.h> +#include <Kernel/Bus/PCI/API.h> +#include <Kernel/Debug.h> +#include <Kernel/Devices/DeviceManagement.h> +#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> +#include <Kernel/Graphics/GraphicsManagement.h> +#include <Kernel/Graphics/Intel/NativeDisplayConnector.h> +#include <Kernel/Memory/Region.h> + +namespace Kernel { + +namespace IntelGraphics { + +#define DDC2_I2C_ADDRESS 0x50 + +struct PLLSettings { + bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); } + u64 compute_dot_clock(u64 refclock) const + { + return (refclock * (5 * m1 + m2) / n) / (p1 * p2); + } + + u64 compute_vco(u64 refclock) const + { + return refclock * (5 * m1 + m2) / n; + } + + u64 compute_m() const + { + return 5 * m1 + m2; + } + + u64 compute_p() const + { + return p1 * p2; + } + u64 n { 0 }; + u64 m1 { 0 }; + u64 m2 { 0 }; + u64 p1 { 0 }; + u64 p2 { 0 }; +}; + +static constexpr PLLMaxSettings G35Limits { + { 20'000'000, 400'000'000 }, // values in Hz, dot_clock + { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO + { 3, 8 }, // n + { 70, 120 }, // m + { 10, 20 }, // m1 + { 5, 9 }, // m2 + { 5, 80 }, // p + { 1, 8 }, // p1 + { 5, 10 } // p2 +}; +} + +static Graphics::Modesetting calculate_modesetting_from_edid(EDID::Parser& edid, size_t index) +{ + auto details = edid.detailed_timing(index).release_value(); + + Graphics::Modesetting mode; + VERIFY(details.pixel_clock_khz()); + mode.pixel_clock_in_khz = details.pixel_clock_khz(); + + size_t horizontal_active = details.horizontal_addressable_pixels(); + size_t horizontal_sync_offset = details.horizontal_front_porch_pixels(); + + mode.horizontal.active = horizontal_active; + mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset; + mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels(); + mode.horizontal.total = horizontal_active + details.horizontal_blanking_pixels(); + + size_t vertical_active = details.vertical_addressable_lines(); + size_t vertical_sync_offset = details.vertical_front_porch_lines(); + + mode.vertical.active = vertical_active; + mode.vertical.sync_start = vertical_active + vertical_sync_offset; + mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines(); + mode.vertical.total = vertical_active + details.vertical_blanking_lines(); + return mode; +} + +static bool check_pll_settings(IntelGraphics::PLLSettings const& settings, size_t reference_clock, IntelGraphics::PLLMaxSettings const& limits) +{ + if (settings.n < limits.n.min || settings.n > limits.n.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); + return false; + } + if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); + return false; + } + if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); + return false; + } + if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); + return false; + } + + if (settings.m1 <= settings.m2) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); + return false; + } + + auto m = settings.compute_m(); + auto p = settings.compute_p(); + + if (m < limits.m.min || m > limits.m.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); + return false; + } + if (p < limits.p.min || p > limits.p.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); + return false; + } + + auto dot = settings.compute_dot_clock(reference_clock); + auto vco = settings.compute_vco(reference_clock); + + if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); + return false; + } + if (vco < limits.vco.min || vco > limits.vco.max) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); + return false; + } + return true; +} + +static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) +{ + if (target_frequency >= checked_frequency) + return target_frequency - checked_frequency; + return checked_frequency - target_frequency; +} + +Optional<IntelGraphics::PLLSettings> IntelNativeDisplayConnector::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits) +{ + IntelGraphics::PLLSettings settings; + IntelGraphics::PLLSettings best_settings; + // FIXME: Is this correct for all Intel Native graphics cards? + settings.p2 = 10; + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); + u64 best_difference = 0xffffffff; + for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { + for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { + for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { + for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { + dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); + if (!check_pll_settings(settings, reference_clock, limits)) + continue; + auto current_dot_clock = settings.compute_dot_clock(reference_clock); + if (current_dot_clock == target_frequency) + return settings; + auto difference = find_absolute_difference(target_frequency, current_dot_clock); + if (difference < best_difference && (current_dot_clock > target_frequency)) { + best_settings = settings; + best_difference = difference; + } + } + } + } + } + if (best_settings.is_valid()) + return best_settings; + return {}; +} + +NonnullRefPtr<IntelNativeDisplayConnector> IntelNativeDisplayConnector::must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length) +{ + auto registers_region = MUST(MM.allocate_kernel_region(PhysicalAddress(registers_region_address), registers_region_length, "Intel Native Graphics Registers", Memory::Region::Access::ReadWrite)); + auto device_or_error = DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, move(registers_region)); + VERIFY(!device_or_error.is_error()); + auto connector = device_or_error.release_value(); + MUST(connector->initialize_gmbus_settings_and_read_edid()); + // Note: This is very important to set the resolution to something safe so we + // can create a framebuffer console with valid resolution. + { + SpinlockLocker control_lock(connector->m_control_lock); + MUST(connector->set_safe_mode_setting()); + } + MUST(connector->create_attached_framebuffer_console()); + return connector; +} + +ErrorOr<void> IntelNativeDisplayConnector::initialize_gmbus_settings_and_read_edid() +{ + gmbus_read_edid(); + return {}; +} + +ErrorOr<void> IntelNativeDisplayConnector::set_y_offset(size_t) +{ + return Error::from_errno(ENOTIMPL); +} + +ErrorOr<void> IntelNativeDisplayConnector::unblank() +{ + return Error::from_errno(ENOTIMPL); +} + +ErrorOr<size_t> IntelNativeDisplayConnector::write_to_first_surface(u64 offset, UserOrKernelBuffer const& buffer, size_t length) +{ + VERIFY(m_control_lock.is_locked()); + if (offset + length > m_framebuffer_region->size()) + return Error::from_errno(EOVERFLOW); + TRY(buffer.read(m_framebuffer_data + offset, 0, length)); + return length; +} + +void IntelNativeDisplayConnector::enable_console() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_framebuffer_console); + m_framebuffer_console->enable(); +} + +void IntelNativeDisplayConnector::disable_console() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_framebuffer_console); + m_framebuffer_console->disable(); +} + +ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface() +{ + return Error::from_errno(ENOTSUP); +} + +ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console() +{ + auto rounded_size = TRY(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride)); + m_framebuffer_region = TRY(MM.allocate_kernel_region(m_framebuffer_address.page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite)); + [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true); + m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr(); + + m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address, m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride); + GraphicsManagement::the().set_console(*m_framebuffer_console); + return {}; +} + +IntelNativeDisplayConnector::IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region) + : DisplayConnector() + , m_framebuffer_address(framebuffer_address) + , m_registers_region(move(registers_region)) +{ + { + SpinlockLocker control_lock(m_control_lock); + set_gmbus_default_rate(); + set_gmbus_pin_pair(IntelGraphics::GMBusPinPair::DedicatedAnalog); + } +} + +ErrorOr<void> IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&) +{ + return Error::from_errno(ENOTIMPL); +} + +ErrorOr<void> IntelNativeDisplayConnector::set_safe_mode_setting() +{ + set_safe_crt_resolution(); + return {}; +} + +void IntelNativeDisplayConnector::enable_vga_plane() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); +} + +[[maybe_unused]] static StringView convert_register_index_to_string(IntelGraphics::RegisterIndex index) +{ + switch (index) { + case IntelGraphics::RegisterIndex::PipeAConf: + return "PipeAConf"sv; + case IntelGraphics::RegisterIndex::PipeBConf: + return "PipeBConf"sv; + case IntelGraphics::RegisterIndex::GMBusData: + return "GMBusData"sv; + case IntelGraphics::RegisterIndex::GMBusStatus: + return "GMBusStatus"sv; + case IntelGraphics::RegisterIndex::GMBusCommand: + return "GMBusCommand"sv; + case IntelGraphics::RegisterIndex::GMBusClock: + return "GMBusClock"sv; + case IntelGraphics::RegisterIndex::DisplayPlaneAControl: + return "DisplayPlaneAControl"sv; + case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset: + return "DisplayPlaneALinearOffset"sv; + case IntelGraphics::RegisterIndex::DisplayPlaneAStride: + return "DisplayPlaneAStride"sv; + case IntelGraphics::RegisterIndex::DisplayPlaneASurface: + return "DisplayPlaneASurface"sv; + case IntelGraphics::RegisterIndex::DPLLDivisorA0: + return "DPLLDivisorA0"sv; + case IntelGraphics::RegisterIndex::DPLLDivisorA1: + return "DPLLDivisorA1"sv; + case IntelGraphics::RegisterIndex::DPLLControlA: + return "DPLLControlA"sv; + case IntelGraphics::RegisterIndex::DPLLControlB: + return "DPLLControlB"sv; + case IntelGraphics::RegisterIndex::DPLLMultiplierA: + return "DPLLMultiplierA"sv; + case IntelGraphics::RegisterIndex::HTotalA: + return "HTotalA"sv; + case IntelGraphics::RegisterIndex::HBlankA: + return "HBlankA"sv; + case IntelGraphics::RegisterIndex::HSyncA: + return "HSyncA"sv; + case IntelGraphics::RegisterIndex::VTotalA: + return "VTotalA"sv; + case IntelGraphics::RegisterIndex::VBlankA: + return "VBlankA"sv; + case IntelGraphics::RegisterIndex::VSyncA: + return "VSyncA"sv; + case IntelGraphics::RegisterIndex::PipeASource: + return "PipeASource"sv; + case IntelGraphics::RegisterIndex::AnalogDisplayPort: + return "AnalogDisplayPort"sv; + case IntelGraphics::RegisterIndex::VGADisplayPlaneControl: + return "VGADisplayPlaneControl"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +void IntelNativeDisplayConnector::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const +{ + VERIFY(m_control_lock.is_locked()); + SpinlockLocker lock(m_registers_lock); + dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_register_index_to_string(index), value); + auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr(); + *reg = value; +} +u32 IntelNativeDisplayConnector::read_from_register(IntelGraphics::RegisterIndex index) const +{ + VERIFY(m_control_lock.is_locked()); + SpinlockLocker lock(m_registers_lock); + auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr(); + u32 value = *reg; + dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_register_index_to_string(index), value); + return value; +} + +bool IntelNativeDisplayConnector::pipe_a_enabled() const +{ + VERIFY(m_control_lock.is_locked()); + return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30); +} + +bool IntelNativeDisplayConnector::pipe_b_enabled() const +{ + VERIFY(m_control_lock.is_locked()); + return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30); +} + +bool IntelNativeDisplayConnector::gmbus_wait_for(IntelGraphics::GMBusStatus desired_status, Optional<size_t> milliseconds_timeout) +{ + VERIFY(m_control_lock.is_locked()); + size_t milliseconds_passed = 0; + while (1) { + if (milliseconds_timeout.has_value() && milliseconds_timeout.value() < milliseconds_passed) + return false; + full_memory_barrier(); + u32 status = read_from_register(IntelGraphics::RegisterIndex::GMBusStatus); + full_memory_barrier(); + VERIFY(!(status & (1 << 10))); // error happened + switch (desired_status) { + case IntelGraphics::GMBusStatus::HardwareReady: + if (status & (1 << 11)) + return true; + break; + case IntelGraphics::GMBusStatus::TransactionCompletion: + if (status & (1 << 14)) + return true; + break; + default: + VERIFY_NOT_REACHED(); + } + IO::delay(1000); + milliseconds_passed++; + } +} + +void IntelNativeDisplayConnector::gmbus_write(unsigned address, u32 byte) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(address < 256); + full_memory_barrier(); + write_to_register(IntelGraphics::RegisterIndex::GMBusData, byte); + full_memory_barrier(); + write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, ((address << 1) | (1 << 16) | (IntelGraphics::GMBusCycle::Wait << 25) | (1 << 30))); + full_memory_barrier(); + gmbus_wait_for(IntelGraphics::GMBusStatus::TransactionCompletion, {}); +} +void IntelNativeDisplayConnector::gmbus_read(unsigned address, u8* buf, size_t length) +{ + VERIFY(address < 256); + VERIFY(m_control_lock.is_locked()); + size_t nread = 0; + auto read_set = [&] { + full_memory_barrier(); + u32 data = read_from_register(IntelGraphics::RegisterIndex::GMBusData); + full_memory_barrier(); + for (size_t index = 0; index < 4; index++) { + if (nread == length) + break; + buf[nread] = (data >> (8 * index)) & 0xFF; + nread++; + } + }; + + full_memory_barrier(); + write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, (1 | (address << 1) | (length << 16) | (IntelGraphics::GMBusCycle::Wait << 25) | (1 << 30))); + full_memory_barrier(); + while (nread < length) { + gmbus_wait_for(IntelGraphics::GMBusStatus::HardwareReady, {}); + read_set(); + } + gmbus_wait_for(IntelGraphics::GMBusStatus::TransactionCompletion, {}); +} + +void IntelNativeDisplayConnector::gmbus_read_edid() +{ + Array<u8, 128> crt_edid_bytes {}; + { + SpinlockLocker control_lock(m_control_lock); + gmbus_write(DDC2_I2C_ADDRESS, 0); + gmbus_read(DDC2_I2C_ADDRESS, (u8*)&crt_edid_bytes, sizeof(crt_edid_bytes)); + // FIXME: It seems like the returned EDID is almost correct, + // but the first byte is set to 0xD0 instead of 0x00. + // For now, this "hack" works well enough. + crt_edid_bytes[0] = 0x0; + } + set_edid_bytes(crt_edid_bytes); +} + +bool IntelNativeDisplayConnector::is_resolution_valid(size_t, size_t) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + // FIXME: Check that we are able to modeset to the requested resolution! + return true; +} + +void IntelNativeDisplayConnector::disable_output() +{ + VERIFY(m_control_lock.is_locked()); + disable_dac_output(); + disable_all_planes(); + disable_pipe_a(); + disable_pipe_b(); + disable_dpll(); + disable_vga_emulation(); +} + +void IntelNativeDisplayConnector::enable_output(PhysicalAddress fb_address, size_t width) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(!pipe_a_enabled()); + enable_pipe_a(); + enable_primary_plane(fb_address, width); + enable_dac_output(); +} + +static size_t compute_dac_multiplier(size_t pixel_clock_in_khz) +{ + dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz); + VERIFY(pixel_clock_in_khz >= 25000); + if (pixel_clock_in_khz >= 100000) { + return 1; + } else if (pixel_clock_in_khz >= 50000) { + return 2; + } else { + return 4; + } +} + +bool IntelNativeDisplayConnector::set_safe_crt_resolution() +{ + VERIFY(m_control_lock.is_locked()); + SpinlockLocker modeset_lock(m_modeset_lock); + + // Note: Just in case we still allow access to VGA IO ports, disable it now. + GraphicsManagement::the().disable_vga_emulation_access_permanently(); + + // FIXME: Get the requested resolution from the EDID!! + auto modesetting = calculate_modesetting_from_edid(m_edid_parser.value(), 0); + + disable_output(); + + auto dac_multiplier = compute_dac_multiplier(modesetting.pixel_clock_in_khz); + auto pll_settings = create_pll_settings((1000 * modesetting.pixel_clock_in_khz * dac_multiplier), 96'000'000, IntelGraphics::G35Limits); + if (!pll_settings.has_value()) + VERIFY_NOT_REACHED(); + auto settings = pll_settings.value(); + dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); + enable_dpll_without_vga(pll_settings.value(), dac_multiplier); + set_display_timings(modesetting); + enable_output(m_framebuffer_address, modesetting.horizontal.blanking_start()); + + DisplayConnector::ModeSetting mode_set { + .horizontal_stride = modesetting.horizontal.blanking_start() * sizeof(u32), + .pixel_clock_in_khz = 0, + .horizontal_active = modesetting.horizontal.blanking_start(), + .horizontal_front_porch_pixels = 0, + .horizontal_sync_time_pixels = 0, + .horizontal_blank_pixels = 0, + .vertical_active = modesetting.vertical.blanking_start(), + .vertical_front_porch_lines = 0, + .vertical_sync_time_lines = 0, + .vertical_blank_lines = 0, + .horizontal_offset = 0, + .vertical_offset = 0, + }; + + m_current_mode_setting = mode_set; + + auto rounded_size = MUST(Memory::page_round_up(m_current_mode_setting.vertical_active * m_current_mode_setting.horizontal_stride)); + m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address, rounded_size, "Intel Native Graphics Framebuffer", Memory::Region::Access::ReadWrite)); + m_framebuffer_data = m_framebuffer_region->vaddr().offset(m_framebuffer_address.offset_in_page()).as_ptr(); + + if (m_framebuffer_console) + m_framebuffer_console->set_resolution(m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride); + + return true; +} + +void IntelNativeDisplayConnector::set_display_timings(Graphics::Modesetting const& modesetting) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31))); + VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30))); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (modesetting.horizontal.active - 1), (modesetting.horizontal.total - 1)); + write_to_register(IntelGraphics::RegisterIndex::HTotalA, (modesetting.horizontal.active - 1) | (modesetting.horizontal.total - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (modesetting.horizontal.blanking_start() - 1), (modesetting.horizontal.blanking_end() - 1)); + write_to_register(IntelGraphics::RegisterIndex::HBlankA, (modesetting.horizontal.blanking_start() - 1) | (modesetting.horizontal.blanking_end() - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (modesetting.horizontal.sync_start - 1), (modesetting.horizontal.sync_end - 1)); + write_to_register(IntelGraphics::RegisterIndex::HSyncA, (modesetting.horizontal.sync_start - 1) | (modesetting.horizontal.sync_end - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (modesetting.vertical.active - 1), (modesetting.vertical.total - 1)); + write_to_register(IntelGraphics::RegisterIndex::VTotalA, (modesetting.vertical.active - 1) | (modesetting.vertical.total - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (modesetting.vertical.blanking_start() - 1), (modesetting.vertical.blanking_end() - 1)); + write_to_register(IntelGraphics::RegisterIndex::VBlankA, (modesetting.vertical.blanking_start() - 1) | (modesetting.vertical.blanking_end() - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (modesetting.vertical.sync_start - 1), (modesetting.vertical.sync_end - 1)); + write_to_register(IntelGraphics::RegisterIndex::VSyncA, (modesetting.vertical.sync_start - 1) | (modesetting.vertical.sync_end - 1) << 16); + + dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1)); + write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16); + + IO::delay(200); +} + +bool IntelNativeDisplayConnector::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const +{ + size_t current_time = 0; + while (current_time < milliseconds_timeout) { + if (pipe_a_enabled()) + return true; + IO::delay(1000); + current_time++; + } + return false; +} +bool IntelNativeDisplayConnector::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const +{ + size_t current_time = 0; + while (current_time < milliseconds_timeout) { + if (!pipe_a_enabled()) + return true; + IO::delay(1000); + current_time++; + } + return false; +} + +bool IntelNativeDisplayConnector::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const +{ + size_t current_time = 0; + while (current_time < milliseconds_timeout) { + if (!pipe_b_enabled()) + return true; + IO::delay(1000); + current_time++; + } + return false; +} + +void IntelNativeDisplayConnector::disable_dpll() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0); + write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, 0); +} + +void IntelNativeDisplayConnector::disable_pipe_a() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0); + dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A"); + wait_for_disabled_pipe_a(100); + dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done."); +} + +void IntelNativeDisplayConnector::disable_pipe_b() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0); + dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B"); + wait_for_disabled_pipe_b(100); + dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done."); +} + +void IntelNativeDisplayConnector::set_gmbus_default_rate() +{ + // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle + VERIFY(m_control_lock.is_locked()); + // Set the rate to 100KHz + write_to_register(IntelGraphics::RegisterIndex::GMBusClock, read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & ~(0b111 << 8)); +} + +void IntelNativeDisplayConnector::enable_pipe_a() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31))); + VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30))); + write_to_register(IntelGraphics::RegisterIndex::PipeAConf, (1 << 31) | (1 << 24)); + dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A"); + // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!! + wait_for_enabled_pipe_a(100); + dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done."); +} + +void IntelNativeDisplayConnector::enable_primary_plane(PhysicalAddress fb_address, size_t width) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + VERIFY(((width * 4) % 64 == 0)); + + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAStride, width * 4); + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset, 0); + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneASurface, fb_address.get()); + + // FIXME: Serenity uses BGR 32 bit pixel format, but maybe we should try to determine it somehow! + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (0b0110 << 26) | (1 << 31)); +} + +void IntelNativeDisplayConnector::set_dpll_registers(IntelGraphics::PLLSettings const& settings) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA0, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16)); + write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16)); + + write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0); +} + +void IntelNativeDisplayConnector::enable_dpll_without_vga(IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + + set_dpll_registers(settings); + + IO::delay(200); + + write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31)); + write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8)); + + // The specification says we should wait (at least) about 150 microseconds + // after enabling the DPLL to allow the clock to stabilize + IO::delay(200); + VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31)); +} + +void IntelNativeDisplayConnector::set_gmbus_pin_pair(IntelGraphics::GMBusPinPair pin_pair) +{ + // FIXME: Verify GMBUS is idle + VERIFY(m_control_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::GMBusClock, (read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & (~0b111)) | (pin_pair & 0b111)); +} + +void IntelNativeDisplayConnector::disable_dac_output() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10); +} + +void IntelNativeDisplayConnector::enable_dac_output() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (1 << 31)); +} + +void IntelNativeDisplayConnector::disable_vga_emulation() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (1 << 31)); + read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl); +} + +void IntelNativeDisplayConnector::disable_all_planes() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, 0); + write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneBControl, 0); +} + +} diff --git a/Kernel/Graphics/Intel/NativeDisplayConnector.h b/Kernel/Graphics/Intel/NativeDisplayConnector.h new file mode 100644 index 0000000000..53598c24f1 --- /dev/null +++ b/Kernel/Graphics/Intel/NativeDisplayConnector.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefPtr.h> +#include <AK/Try.h> +#include <Kernel/Graphics/Console/GenericFramebufferConsole.h> +#include <Kernel/Graphics/Definitions.h> +#include <Kernel/Graphics/DisplayConnector.h> +#include <Kernel/Memory/TypedMapping.h> + +namespace Kernel { + +namespace IntelGraphics { + +enum class RegisterIndex { + PipeAConf = 0x70008, + PipeBConf = 0x71008, + GMBusData = 0x510C, + GMBusStatus = 0x5108, + GMBusCommand = 0x5104, + GMBusClock = 0x5100, + DisplayPlaneAControl = 0x70180, + DisplayPlaneBControl = 0x71180, + DisplayPlaneALinearOffset = 0x70184, + DisplayPlaneAStride = 0x70188, + DisplayPlaneASurface = 0x7019C, + DPLLDivisorA0 = 0x6040, + DPLLDivisorA1 = 0x6044, + DPLLControlA = 0x6014, + DPLLControlB = 0x6018, + DPLLMultiplierA = 0x601C, + HTotalA = 0x60000, + HBlankA = 0x60004, + HSyncA = 0x60008, + VTotalA = 0x6000C, + VBlankA = 0x60010, + VSyncA = 0x60014, + PipeASource = 0x6001C, + AnalogDisplayPort = 0x61100, + VGADisplayPlaneControl = 0x71400, +}; + +struct PLLSettings; + +struct PLLParameterLimit { + size_t min, max; +}; + +struct PLLMaxSettings { + PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2; +}; + +enum GMBusPinPair : u8 { + None = 0, + DedicatedControl = 1, + DedicatedAnalog = 0b10, + IntegratedDigital = 0b11, + sDVO = 0b101, + Dconnector = 0b111, +}; + +enum class GMBusStatus { + TransactionCompletion, + HardwareReady, +}; + +enum GMBusCycle { + Wait = 1, + Stop = 4, +}; +} + +class IntelNativeDisplayConnector final + : public DisplayConnector { + friend class IntelNativeGraphicsAdapter; + friend class DeviceManagement; + +public: + static NonnullRefPtr<IntelNativeDisplayConnector> must_create(PhysicalAddress framebuffer_address, PhysicalAddress registers_region_address, size_t registers_region_length); + +private: + // ^DisplayConnector + // FIXME: Implement modesetting capabilities in runtime from userland... + virtual bool mutable_mode_setting_capable() const override { return false; } + // FIXME: Implement double buffering capabilities in runtime from userland... + virtual bool double_framebuffering_capable() const override { return false; } + virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override; + virtual ErrorOr<void> set_safe_mode_setting() override; + virtual ErrorOr<void> set_y_offset(size_t y) override; + virtual ErrorOr<void> unblank() override; + virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override final; + virtual ErrorOr<void> flush_first_surface() override final; + virtual void enable_console() override; + virtual void disable_console() override; + virtual bool partial_flush_support() const override { return false; } + virtual bool flush_support() const override { return false; } + // Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting. + virtual bool refresh_rate_support() const override { return true; } + + ErrorOr<void> initialize_gmbus_settings_and_read_edid(); + + IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, NonnullOwnPtr<Memory::Region> registers_region); + + ErrorOr<void> create_attached_framebuffer_console(); + + void write_to_register(IntelGraphics::RegisterIndex, u32 value) const; + u32 read_from_register(IntelGraphics::RegisterIndex) const; + + bool pipe_a_enabled() const; + bool pipe_b_enabled() const; + + bool is_resolution_valid(size_t width, size_t height); + + bool set_safe_crt_resolution(); + + void disable_output(); + void enable_output(PhysicalAddress fb_address, size_t width); + + void disable_vga_emulation(); + void enable_vga_plane(); + + void disable_dac_output(); + void enable_dac_output(); + + void disable_all_planes(); + void disable_pipe_a(); + void disable_pipe_b(); + void disable_dpll(); + + void set_dpll_registers(IntelGraphics::PLLSettings const&); + + void enable_dpll_without_vga(IntelGraphics::PLLSettings const&, size_t dac_multiplier); + void set_display_timings(Graphics::Modesetting const&); + void enable_pipe_a(); + void set_framebuffer_parameters(size_t, size_t); + void enable_primary_plane(PhysicalAddress fb_address, size_t stride); + + bool wait_for_enabled_pipe_a(size_t milliseconds_timeout) const; + bool wait_for_disabled_pipe_a(size_t milliseconds_timeout) const; + bool wait_for_disabled_pipe_b(size_t milliseconds_timeout) const; + + void set_gmbus_default_rate(); + void set_gmbus_pin_pair(IntelGraphics::GMBusPinPair pin_pair); + + // FIXME: It would be better if we generalize the I2C access later on + void gmbus_read_edid(); + void gmbus_write(unsigned address, u32 byte); + void gmbus_read(unsigned address, u8* buf, size_t length); + bool gmbus_wait_for(IntelGraphics::GMBusStatus desired_status, Optional<size_t> milliseconds_timeout); + + Optional<IntelGraphics::PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const&); + + mutable Spinlock m_registers_lock; + const PhysicalAddress m_framebuffer_address; + RefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console; + OwnPtr<Memory::Region> m_framebuffer_region; + u8* m_framebuffer_data {}; + + const PhysicalAddress m_registers; + NonnullOwnPtr<Memory::Region> m_registers_region; +}; +} diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp index a158d2f79b..d9bbd29bf4 100644 --- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp +++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp @@ -14,18 +14,6 @@ namespace Kernel { -static constexpr IntelNativeGraphicsAdapter::PLLMaxSettings G35Limits { - { 20'000'000, 400'000'000 }, // values in Hz, dot_clock - { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO - { 3, 8 }, // n - { 70, 120 }, // m - { 10, 20 }, // m1 - { 5, 9 }, // m2 - { 5, 80 }, // p - { 1, 8 }, // p1 - { 5, 10 } // p2 -}; - static constexpr u16 supported_models[] { 0x29c2, // Intel G35 Adapter }; @@ -39,643 +27,34 @@ static bool is_supported_model(u16 device_id) return false; } -#define DDC2_I2C_ADDRESS 0x50 - RefPtr<IntelNativeGraphicsAdapter> IntelNativeGraphicsAdapter::initialize(PCI::DeviceIdentifier const& pci_device_identifier) { VERIFY(pci_device_identifier.hardware_id().vendor_id == 0x8086); if (!is_supported_model(pci_device_identifier.hardware_id().device_id)) return {}; - return adopt_ref(*new IntelNativeGraphicsAdapter(pci_device_identifier.address())); -} - -static size_t compute_dac_multiplier(size_t pixel_clock_in_khz) -{ - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz); - VERIFY(pixel_clock_in_khz >= 25000); - if (pixel_clock_in_khz >= 100000) { - return 1; - } else if (pixel_clock_in_khz >= 50000) { - return 2; - } else { - return 4; - } + auto adapter = adopt_ref(*new IntelNativeGraphicsAdapter(pci_device_identifier.address())); + MUST(adapter->initialize_adapter()); + return adapter; } -static Graphics::Modesetting calculate_modesetting_from_edid(EDID::Parser& edid, size_t index) -{ - auto details = edid.detailed_timing(index).release_value(); - - Graphics::Modesetting mode; - VERIFY(details.pixel_clock_khz()); - mode.pixel_clock_in_khz = details.pixel_clock_khz(); - - size_t horizontal_active = details.horizontal_addressable_pixels(); - size_t horizontal_sync_offset = details.horizontal_front_porch_pixels(); - - mode.horizontal.active = horizontal_active; - mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset; - mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels(); - mode.horizontal.total = horizontal_active + details.horizontal_blanking_pixels(); - - size_t vertical_active = details.vertical_addressable_lines(); - size_t vertical_sync_offset = details.vertical_front_porch_lines(); - - mode.vertical.active = vertical_active; - mode.vertical.sync_start = vertical_active + vertical_sync_offset; - mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines(); - mode.vertical.total = vertical_active + details.vertical_blanking_lines(); - return mode; -} - -static bool check_pll_settings(IntelNativeGraphicsAdapter::PLLSettings const& settings, size_t reference_clock, IntelNativeGraphicsAdapter::PLLMaxSettings const& limits) -{ - if (settings.n < limits.n.min || settings.n > limits.n.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n); - return false; - } - if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1); - return false; - } - if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2); - return false; - } - if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1); - return false; - } - - if (settings.m1 <= settings.m2) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1); - return false; - } - - auto m = settings.compute_m(); - auto p = settings.compute_p(); - - if (m < limits.m.min || m > limits.m.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m); - return false; - } - if (p < limits.p.min || p > limits.p.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p); - return false; - } - - auto dot = settings.compute_dot_clock(reference_clock); - auto vco = settings.compute_vco(reference_clock); - - if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot); - return false; - } - if (vco < limits.vco.min || vco > limits.vco.max) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco); - return false; - } - return true; -} - -static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency) -{ - if (target_frequency >= checked_frequency) - return target_frequency - checked_frequency; - return checked_frequency - target_frequency; -} - -Optional<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::create_pll_settings(u64 target_frequency, u64 reference_clock, PLLMaxSettings const& limits) -{ - IntelNativeGraphicsAdapter::PLLSettings settings; - IntelNativeGraphicsAdapter::PLLSettings best_settings; - // FIXME: Is this correct for all Intel Native graphics cards? - settings.p2 = 10; - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency); - u64 best_difference = 0xffffffff; - for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) { - for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) { - for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) { - for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) { - dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - if (!check_pll_settings(settings, reference_clock, limits)) - continue; - auto current_dot_clock = settings.compute_dot_clock(reference_clock); - if (current_dot_clock == target_frequency) - return settings; - auto difference = find_absolute_difference(target_frequency, current_dot_clock); - if (difference < best_difference && (current_dot_clock > target_frequency)) { - best_settings = settings; - best_difference = difference; - } - } - } - } - } - if (best_settings.is_valid()) - return best_settings; - return {}; -} - -IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address) - : PCIVGACompatibleAdapter(address) - , m_registers(PCI::get_BAR0(address) & 0xfffffffc) - , m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc) +ErrorOr<void> IntelNativeGraphicsAdapter::initialize_adapter() { + auto address = pci_address(); dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", address); auto bar0_space_size = PCI::get_BAR_space_size(address, 0); VERIFY(bar0_space_size == 0x80000); dmesgln("Intel Native Graphics Adapter @ {}, MMIO @ {}, space size is {:x} bytes", address, PhysicalAddress(PCI::get_BAR0(address)), bar0_space_size); dmesgln("Intel Native Graphics Adapter @ {}, framebuffer @ {}", address, PhysicalAddress(PCI::get_BAR2(address))); - auto region_or_error = MM.allocate_kernel_region(PhysicalAddress(PCI::get_BAR0(address)).page_base(), bar0_space_size, "Intel Native Graphics Registers", Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) { - TODO(); - } - m_registers_region = region_or_error.release_value(); PCI::enable_bus_mastering(address); - { - SpinlockLocker control_lock(m_control_lock); - set_gmbus_default_rate(); - set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog); - } - gmbus_read_edid(); - - auto modesetting = calculate_modesetting_from_edid(m_crt_edid.value(), 0); - dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", pci_address(), modesetting.horizontal.active, modesetting.vertical.active); - set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active); - auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); - VERIFY(!framebuffer_address.is_null()); - VERIFY(m_framebuffer_pitch != 0); - VERIFY(m_framebuffer_height != 0); - VERIFY(m_framebuffer_width != 0); - m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch); - GraphicsManagement::the().set_console(*m_framebuffer_console); -} - -void IntelNativeGraphicsAdapter::enable_vga_plane() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); -} - -[[maybe_unused]] static StringView convert_register_index_to_string(IntelGraphics::RegisterIndex index) -{ - switch (index) { - case IntelGraphics::RegisterIndex::PipeAConf: - return "PipeAConf"sv; - case IntelGraphics::RegisterIndex::PipeBConf: - return "PipeBConf"sv; - case IntelGraphics::RegisterIndex::GMBusData: - return "GMBusData"sv; - case IntelGraphics::RegisterIndex::GMBusStatus: - return "GMBusStatus"sv; - case IntelGraphics::RegisterIndex::GMBusCommand: - return "GMBusCommand"sv; - case IntelGraphics::RegisterIndex::GMBusClock: - return "GMBusClock"sv; - case IntelGraphics::RegisterIndex::DisplayPlaneAControl: - return "DisplayPlaneAControl"sv; - case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset: - return "DisplayPlaneALinearOffset"sv; - case IntelGraphics::RegisterIndex::DisplayPlaneAStride: - return "DisplayPlaneAStride"sv; - case IntelGraphics::RegisterIndex::DisplayPlaneASurface: - return "DisplayPlaneASurface"sv; - case IntelGraphics::RegisterIndex::DPLLDivisorA0: - return "DPLLDivisorA0"sv; - case IntelGraphics::RegisterIndex::DPLLDivisorA1: - return "DPLLDivisorA1"sv; - case IntelGraphics::RegisterIndex::DPLLControlA: - return "DPLLControlA"sv; - case IntelGraphics::RegisterIndex::DPLLControlB: - return "DPLLControlB"sv; - case IntelGraphics::RegisterIndex::DPLLMultiplierA: - return "DPLLMultiplierA"sv; - case IntelGraphics::RegisterIndex::HTotalA: - return "HTotalA"sv; - case IntelGraphics::RegisterIndex::HBlankA: - return "HBlankA"sv; - case IntelGraphics::RegisterIndex::HSyncA: - return "HSyncA"sv; - case IntelGraphics::RegisterIndex::VTotalA: - return "VTotalA"sv; - case IntelGraphics::RegisterIndex::VBlankA: - return "VBlankA"sv; - case IntelGraphics::RegisterIndex::VSyncA: - return "VSyncA"sv; - case IntelGraphics::RegisterIndex::PipeASource: - return "PipeASource"sv; - case IntelGraphics::RegisterIndex::AnalogDisplayPort: - return "AnalogDisplayPort"sv; - case IntelGraphics::RegisterIndex::VGADisplayPlaneControl: - return "VGADisplayPlaneControl"sv; - default: - VERIFY_NOT_REACHED(); - } -} - -void IntelNativeGraphicsAdapter::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_registers_region); - SpinlockLocker lock(m_registers_lock); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Write to {} value of {:x}", pci_address(), convert_register_index_to_string(index), value); - auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(index).as_ptr(); - *reg = value; -} -u32 IntelNativeGraphicsAdapter::read_from_register(IntelGraphics::RegisterIndex index) const -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_registers_region); - SpinlockLocker lock(m_registers_lock); - auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(index).as_ptr(); - u32 value = *reg; - dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Read from {} value of {:x}", pci_address(), convert_register_index_to_string(index), value); - return value; -} - -bool IntelNativeGraphicsAdapter::pipe_a_enabled() const -{ - VERIFY(m_control_lock.is_locked()); - return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30); -} - -bool IntelNativeGraphicsAdapter::pipe_b_enabled() const -{ - VERIFY(m_control_lock.is_locked()); - return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30); -} - -bool IntelNativeGraphicsAdapter::gmbus_wait_for(GMBusStatus desired_status, Optional<size_t> milliseconds_timeout) -{ - VERIFY(m_control_lock.is_locked()); - size_t milliseconds_passed = 0; - while (1) { - if (milliseconds_timeout.has_value() && milliseconds_timeout.value() < milliseconds_passed) - return false; - full_memory_barrier(); - u32 status = read_from_register(IntelGraphics::RegisterIndex::GMBusStatus); - full_memory_barrier(); - VERIFY(!(status & (1 << 10))); // error happened - switch (desired_status) { - case GMBusStatus::HardwareReady: - if (status & (1 << 11)) - return true; - break; - case GMBusStatus::TransactionCompletion: - if (status & (1 << 14)) - return true; - break; - default: - VERIFY_NOT_REACHED(); - } - IO::delay(1000); - milliseconds_passed++; - } -} - -void IntelNativeGraphicsAdapter::gmbus_write(unsigned address, u32 byte) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(address < 256); - full_memory_barrier(); - write_to_register(IntelGraphics::RegisterIndex::GMBusData, byte); - full_memory_barrier(); - write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30))); - full_memory_barrier(); - gmbus_wait_for(GMBusStatus::TransactionCompletion, {}); -} -void IntelNativeGraphicsAdapter::gmbus_read(unsigned address, u8* buf, size_t length) -{ - VERIFY(address < 256); - VERIFY(m_control_lock.is_locked()); - size_t nread = 0; - auto read_set = [&] { - full_memory_barrier(); - u32 data = read_from_register(IntelGraphics::RegisterIndex::GMBusData); - full_memory_barrier(); - for (size_t index = 0; index < 4; index++) { - if (nread == length) - break; - buf[nread] = (data >> (8 * index)) & 0xFF; - nread++; - } - }; - - full_memory_barrier(); - write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30))); - full_memory_barrier(); - while (nread < length) { - gmbus_wait_for(GMBusStatus::HardwareReady, {}); - read_set(); - } - gmbus_wait_for(GMBusStatus::TransactionCompletion, {}); -} - -void IntelNativeGraphicsAdapter::gmbus_read_edid() -{ - { - SpinlockLocker control_lock(m_control_lock); - gmbus_write(DDC2_I2C_ADDRESS, 0); - gmbus_read(DDC2_I2C_ADDRESS, (u8*)&m_crt_edid_bytes, sizeof(m_crt_edid_bytes)); - // FIXME: It seems like the returned EDID is almost correct, - // but the first byte is set to 0xD0 instead of 0x00. - // For now, this "hack" works well enough. - m_crt_edid_bytes[0] = 0x0; - } - if (auto parsed_edid = EDID::Parser::from_bytes({ m_crt_edid_bytes, sizeof(m_crt_edid_bytes) }); !parsed_edid.is_error()) { - m_crt_edid = parsed_edid.release_value(); - } else { - for (size_t x = 0; x < 128; x = x + 16) { - dmesgln("IntelNativeGraphicsAdapter: Print offending EDID"); - dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", - m_crt_edid_bytes[x], m_crt_edid_bytes[x + 1], m_crt_edid_bytes[x + 2], m_crt_edid_bytes[x + 3], - m_crt_edid_bytes[x + 4], m_crt_edid_bytes[x + 5], m_crt_edid_bytes[x + 6], m_crt_edid_bytes[x + 7], - m_crt_edid_bytes[x + 8], m_crt_edid_bytes[x + 9], m_crt_edid_bytes[x + 10], m_crt_edid_bytes[x + 11], - m_crt_edid_bytes[x + 12], m_crt_edid_bytes[x + 13], m_crt_edid_bytes[x + 14], m_crt_edid_bytes[x + 15]); - } - dmesgln("IntelNativeGraphicsAdapter: Parsing EDID failed: {}", parsed_edid.error()); - m_crt_edid = {}; - } -} - -bool IntelNativeGraphicsAdapter::is_resolution_valid(size_t, size_t) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - // FIXME: Check that we are able to modeset to the requested resolution! - return true; -} - -void IntelNativeGraphicsAdapter::disable_output() -{ - VERIFY(m_control_lock.is_locked()); - disable_dac_output(); - disable_all_planes(); - disable_pipe_a(); - disable_pipe_b(); - disable_vga_emulation(); - disable_dpll(); -} - -void IntelNativeGraphicsAdapter::enable_output(PhysicalAddress fb_address, size_t width) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(!pipe_a_enabled()); - - enable_pipe_a(); - enable_primary_plane(fb_address, width); - enable_dac_output(); -} -bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height) -{ - SpinlockLocker control_lock(m_control_lock); - SpinlockLocker modeset_lock(m_modeset_lock); - if (!is_resolution_valid(width, height)) { - return false; - } - - // FIXME: Get the requested resolution from the EDID!! - auto modesetting = calculate_modesetting_from_edid(m_crt_edid.value(), 0); - - disable_output(); - - auto dac_multiplier = compute_dac_multiplier(modesetting.pixel_clock_in_khz); - auto pll_settings = create_pll_settings((1000 * modesetting.pixel_clock_in_khz * dac_multiplier), 96'000'000, G35Limits); - if (!pll_settings.has_value()) - VERIFY_NOT_REACHED(); - auto settings = pll_settings.value(); - dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - enable_dpll_without_vga(pll_settings.value(), dac_multiplier); - set_display_timings(modesetting); - auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); - VERIFY(!address.is_null()); - enable_output(address, width); - - m_framebuffer_width = width; - m_framebuffer_height = height; - m_framebuffer_pitch = width * 4; - - return true; -} - -void IntelNativeGraphicsAdapter::set_display_timings(Graphics::Modesetting const& modesetting) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31))); - VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30))); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (modesetting.horizontal.active - 1), (modesetting.horizontal.total - 1)); - write_to_register(IntelGraphics::RegisterIndex::HTotalA, (modesetting.horizontal.active - 1) | (modesetting.horizontal.total - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (modesetting.horizontal.blanking_start() - 1), (modesetting.horizontal.blanking_end() - 1)); - write_to_register(IntelGraphics::RegisterIndex::HBlankA, (modesetting.horizontal.blanking_start() - 1) | (modesetting.horizontal.blanking_end() - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (modesetting.horizontal.sync_start - 1), (modesetting.horizontal.sync_end - 1)); - write_to_register(IntelGraphics::RegisterIndex::HSyncA, (modesetting.horizontal.sync_start - 1) | (modesetting.horizontal.sync_end - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (modesetting.vertical.active - 1), (modesetting.vertical.total - 1)); - write_to_register(IntelGraphics::RegisterIndex::VTotalA, (modesetting.vertical.active - 1) | (modesetting.vertical.total - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (modesetting.vertical.blanking_start() - 1), (modesetting.vertical.blanking_end() - 1)); - write_to_register(IntelGraphics::RegisterIndex::VBlankA, (modesetting.vertical.blanking_start() - 1) | (modesetting.vertical.blanking_end() - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (modesetting.vertical.sync_start - 1), (modesetting.vertical.sync_end - 1)); - write_to_register(IntelGraphics::RegisterIndex::VSyncA, (modesetting.vertical.sync_start - 1) | (modesetting.vertical.sync_end - 1) << 16); - - dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1)); - write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16); - - IO::delay(200); -} - -bool IntelNativeGraphicsAdapter::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const -{ - size_t current_time = 0; - while (current_time < milliseconds_timeout) { - if (pipe_a_enabled()) - return true; - IO::delay(1000); - current_time++; - } - return false; -} -bool IntelNativeGraphicsAdapter::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const -{ - size_t current_time = 0; - while (current_time < milliseconds_timeout) { - if (!pipe_a_enabled()) - return true; - IO::delay(1000); - current_time++; - } - return false; -} - -bool IntelNativeGraphicsAdapter::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const -{ - size_t current_time = 0; - while (current_time < milliseconds_timeout) { - if (!pipe_b_enabled()) - return true; - IO::delay(1000); - current_time++; - } - return false; -} - -void IntelNativeGraphicsAdapter::disable_dpll() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000); - write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, read_from_register(IntelGraphics::RegisterIndex::DPLLControlB) & ~0x80000000); -} - -void IntelNativeGraphicsAdapter::disable_pipe_a() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & ~0x80000000); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A"); - wait_for_disabled_pipe_a(100); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done."); -} - -void IntelNativeGraphicsAdapter::disable_pipe_b() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & ~0x80000000); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B"); - wait_for_disabled_pipe_b(100); - dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done."); -} - -void IntelNativeGraphicsAdapter::set_gmbus_default_rate() -{ - // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle - VERIFY(m_control_lock.is_locked()); - // Set the rate to 100KHz - write_to_register(IntelGraphics::RegisterIndex::GMBusClock, read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & ~(0b111 << 8)); -} - -void IntelNativeGraphicsAdapter::enable_pipe_a() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31))); - VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30))); - write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) | 0x80000000); - dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A"); - // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!! - wait_for_enabled_pipe_a(100); - dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done."); -} - -void IntelNativeGraphicsAdapter::enable_primary_plane(PhysicalAddress fb_address, size_t width) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - VERIFY(((width * 4) % 64 == 0)); - - write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAStride, width * 4); - write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset, 0); - write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneASurface, fb_address.get()); - - // FIXME: Serenity uses BGR 32 bit pixel format, but maybe we should try to determine it somehow! - write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31)); -} - -void IntelNativeGraphicsAdapter::set_dpll_registers(PLLSettings const& settings) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA0, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16)); - write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16)); - - write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000); -} - -void IntelNativeGraphicsAdapter::enable_dpll_without_vga(PLLSettings const& settings, size_t dac_multiplier) -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - - set_dpll_registers(settings); - - IO::delay(200); - - write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31)); - write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8)); - - // The specification says we should wait (at least) about 150 microseconds - // after enabling the DPLL to allow the clock to stabilize - IO::delay(200); - VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31)); -} - -void IntelNativeGraphicsAdapter::set_gmbus_pin_pair(GMBusPinPair pin_pair) -{ - // FIXME: Verify GMBUS is idle - VERIFY(m_control_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::GMBusClock, (read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & (~0b111)) | (pin_pair & 0b111)); -} - -void IntelNativeGraphicsAdapter::disable_dac_output() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10); -} - -void IntelNativeGraphicsAdapter::enable_dac_output() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (read_from_register(IntelGraphics::RegisterIndex::AnalogDisplayPort) & (~(0b11 << 10))) | 0x80000000); -} - -void IntelNativeGraphicsAdapter::disable_vga_emulation() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl) & (~(1 << 30))) | 0x80000000); -} - -void IntelNativeGraphicsAdapter::disable_all_planes() -{ - VERIFY(m_control_lock.is_locked()); - VERIFY(m_modeset_lock.is_locked()); - write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & ~(1 << 31)); -} - -void IntelNativeGraphicsAdapter::initialize_framebuffer_devices() -{ - auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); - VERIFY(!address.is_null()); - VERIFY(m_framebuffer_pitch != 0); - VERIFY(m_framebuffer_height != 0); - VERIFY(m_framebuffer_width != 0); - m_framebuffer_device = FramebufferDevice::create(*this, address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch); - // FIXME: Would be nice to be able to return a ErrorOr<void> here. - auto framebuffer_result = m_framebuffer_device->try_to_initialize(); - VERIFY(!framebuffer_result.is_error()); + m_display_connector = IntelNativeDisplayConnector::must_create(PhysicalAddress(PCI::get_BAR2(address) & 0xfffffff0), PhysicalAddress(PCI::get_BAR0(address) & 0xfffffff0), bar0_space_size); + return {}; } -ErrorOr<ByteBuffer> IntelNativeGraphicsAdapter::get_edid(size_t output_port_index) const +IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address) + : GenericGraphicsAdapter() + , PCI::Device(address) { - if (output_port_index != 0) { - dbgln("IntelNativeGraphicsAdapter: get_edid: Only one output supported"); - return Error::from_errno(ENODEV); - } - - if (m_crt_edid.has_value()) - return ByteBuffer::copy(m_crt_edid_bytes, sizeof(m_crt_edid_bytes)); - - return ByteBuffer {}; } } diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h index 825f443b97..34a2d91137 100644 --- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h +++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h @@ -9,166 +9,39 @@ #include <AK/Types.h> #include <Kernel/Bus/PCI/Device.h> #include <Kernel/Graphics/Definitions.h> -#include <Kernel/Graphics/FramebufferDevice.h> -#include <Kernel/Graphics/VGA/PCIAdapter.h> +#include <Kernel/Graphics/Intel/NativeDisplayConnector.h> #include <Kernel/PhysicalAddress.h> #include <LibEDID/EDID.h> namespace Kernel { -namespace IntelGraphics { - -enum RegisterIndex { - PipeAConf = 0x70008, - PipeBConf = 0x71008, - GMBusData = 0x510C, - GMBusStatus = 0x5108, - GMBusCommand = 0x5104, - GMBusClock = 0x5100, - DisplayPlaneAControl = 0x70180, - DisplayPlaneALinearOffset = 0x70184, - DisplayPlaneAStride = 0x70188, - DisplayPlaneASurface = 0x7019C, - DPLLDivisorA0 = 0x6040, - DPLLDivisorA1 = 0x6044, - DPLLControlA = 0x6014, - DPLLControlB = 0x6018, - DPLLMultiplierA = 0x601C, - HTotalA = 0x60000, - HBlankA = 0x60004, - HSyncA = 0x60008, - VTotalA = 0x6000C, - VBlankA = 0x60010, - VSyncA = 0x60014, - PipeASource = 0x6001C, - AnalogDisplayPort = 0x61100, - VGADisplayPlaneControl = 0x71400, -}; -} - class IntelNativeGraphicsAdapter final - : public PCIVGACompatibleAdapter { -public: - struct PLLSettings { - bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); } - u64 compute_dot_clock(u64 refclock) const - { - return (refclock * (5 * (m1) + (m2)) / (n)) / (p1 * p2); - } - - u64 compute_vco(u64 refclock) const - { - return refclock * (5 * (m1) + (m2)) / n; - } - - u64 compute_m() const - { - return 5 * (m1) + (m2); - } - - u64 compute_p() const - { - return p1 * p2; - } - u64 n { 0 }; - u64 m1 { 0 }; - u64 m2 { 0 }; - u64 p1 { 0 }; - u64 p2 { 0 }; - }; - struct PLLParameterLimit { - size_t min, max; - }; - struct PLLMaxSettings { - PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2; - }; - -private: - enum GMBusPinPair : u8 { - None = 0, - DedicatedControl = 1, - DedicatedAnalog = 0b10, - IntegratedDigital = 0b11, - sDVO = 0b101, - Dconnector = 0b111, - }; - - enum class GMBusStatus { - TransactionCompletion, - HardwareReady - }; - - enum GMBusCycle { - Wait = 1, - Stop = 4, - }; + : public GenericGraphicsAdapter + , public PCI::Device { public: static RefPtr<IntelNativeGraphicsAdapter> initialize(PCI::DeviceIdentifier const&); + virtual ~IntelNativeGraphicsAdapter() = default; + private: - explicit IntelNativeGraphicsAdapter(PCI::Address); + ErrorOr<void> initialize_adapter(); - void write_to_register(IntelGraphics::RegisterIndex, u32 value) const; - u32 read_from_register(IntelGraphics::RegisterIndex) const; + explicit IntelNativeGraphicsAdapter(PCI::Address); // ^GenericGraphicsAdapter - virtual void initialize_framebuffer_devices() override; - virtual ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; - - bool pipe_a_enabled() const; - bool pipe_b_enabled() const; - - bool is_resolution_valid(size_t width, size_t height); - - bool set_crt_resolution(size_t width, size_t height); - - void disable_output(); - void enable_output(PhysicalAddress fb_address, size_t width); - - void disable_vga_emulation(); - void enable_vga_plane(); - - void disable_dac_output(); - void enable_dac_output(); - - void disable_all_planes(); - void disable_pipe_a(); - void disable_pipe_b(); - void disable_dpll(); - - void set_dpll_registers(PLLSettings const&); - - void enable_dpll_without_vga(PLLSettings const&, size_t dac_multiplier); - void set_display_timings(Graphics::Modesetting const&); - void enable_pipe_a(); - void set_framebuffer_parameters(size_t, size_t); - void enable_primary_plane(PhysicalAddress fb_address, size_t stride); - - bool wait_for_enabled_pipe_a(size_t milliseconds_timeout) const; - bool wait_for_disabled_pipe_a(size_t milliseconds_timeout) const; - bool wait_for_disabled_pipe_b(size_t milliseconds_timeout) const; - - void set_gmbus_default_rate(); - void set_gmbus_pin_pair(GMBusPinPair pin_pair); - - // FIXME: It would be better if we generalize the I2C access later on - void gmbus_read_edid(); - void gmbus_write(unsigned address, u32 byte); - void gmbus_read(unsigned address, u8* buf, size_t length); - bool gmbus_wait_for(GMBusStatus desired_status, Optional<size_t> milliseconds_timeout); - - Optional<PLLSettings> create_pll_settings(u64 target_frequency, u64 reference_clock, PLLMaxSettings const&); - - Spinlock m_control_lock; - Spinlock m_modeset_lock; - mutable Spinlock m_registers_lock; - - EDID::Parser::RawBytes m_crt_edid_bytes {}; - Optional<EDID::Parser> m_crt_edid; - const PhysicalAddress m_registers; - const PhysicalAddress m_framebuffer_addr; - OwnPtr<Memory::Region> m_registers_region; + // FIXME: Remove all of these methods when we get rid of the FramebufferDevice class. + virtual bool framebuffer_devices_initialized() const override { return false; } + virtual bool modesetting_capable() const override { return true; } + virtual bool vga_compatible() const override { return true; } + virtual bool double_framebuffering_capable() const override { return true; } + virtual bool try_to_set_resolution(size_t, size_t, size_t) override { VERIFY_NOT_REACHED(); } + virtual bool set_y_offset(size_t, size_t) override { VERIFY_NOT_REACHED(); } + virtual void initialize_framebuffer_devices() override { } + virtual void enable_consoles() override { } + virtual void disable_consoles() override { } + virtual ErrorOr<ByteBuffer> get_edid(size_t) const override { VERIFY_NOT_REACHED(); } + + RefPtr<IntelNativeDisplayConnector> m_display_connector; }; - } |