summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/CMakeLists.txt1
-rw-r--r--Kernel/Graphics/DisplayConnector.cpp333
-rw-r--r--Kernel/Graphics/DisplayConnector.h144
-rw-r--r--Kernel/Graphics/GraphicsManagement.cpp19
-rw-r--r--Kernel/Graphics/GraphicsManagement.h6
-rw-r--r--Userland/Libraries/LibC/sys/ioctl_numbers.h19
6 files changed, 522 insertions, 0 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt
index 6ef7641326..c95f5464e3 100644
--- a/Kernel/CMakeLists.txt
+++ b/Kernel/CMakeLists.txt
@@ -80,6 +80,7 @@ set(KERNEL_SOURCES
Graphics/Console/ContiguousFramebufferConsole.cpp
Graphics/Console/TextModeConsole.cpp
Graphics/Console/VGAConsole.cpp
+ Graphics/DisplayConnector.cpp
Graphics/FramebufferDevice.cpp
Graphics/GraphicsManagement.cpp
Graphics/Intel/NativeGraphicsAdapter.cpp
diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp
new file mode 100644
index 0000000000..facf9b7ea9
--- /dev/null
+++ b/Kernel/Graphics/DisplayConnector.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/DisplayConnector.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
+#include <LibC/sys/ioctl_numbers.h>
+
+namespace Kernel {
+
+DisplayConnector::DisplayConnector()
+ : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number())
+{
+}
+
+ErrorOr<Memory::Region*> DisplayConnector::mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool)
+{
+ return Error::from_errno(ENOTSUP);
+}
+
+ErrorOr<size_t> DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
+{
+ return Error::from_errno(ENOTIMPL);
+}
+ErrorOr<size_t> DisplayConnector::write(OpenFileDescription&, u64 offset, UserOrKernelBuffer const& framebuffer_data, size_t length)
+{
+ SpinlockLocker locker(m_control_lock);
+ // FIXME: We silently ignore the request if we are in console mode.
+ // WindowServer is not ready yet to handle errors such as EBUSY currently.
+ if (console_mode()) {
+ return length;
+ }
+ return write_to_first_surface(offset, framebuffer_data, length);
+}
+
+void DisplayConnector::will_be_destroyed()
+{
+ GraphicsManagement::the().detach_display_connector({}, *this);
+ Device::will_be_destroyed();
+}
+
+void DisplayConnector::after_inserting()
+{
+ Device::after_inserting();
+ GraphicsManagement::the().attach_new_display_connector({}, *this);
+}
+
+bool DisplayConnector::console_mode() const
+{
+ VERIFY(m_control_lock.is_locked());
+ return m_console_mode;
+}
+
+void DisplayConnector::set_display_mode(Badge<GraphicsManagement>, DisplayMode mode)
+{
+ SpinlockLocker locker(m_control_lock);
+ m_console_mode = mode == DisplayMode::Console ? true : false;
+ if (m_console_mode)
+ enable_console();
+ else
+ disable_console();
+}
+
+ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor()
+{
+ Array<u8, 128> virtual_monitor_edid = {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
+ 0x0, 0x0, /* manufacturer */
+ 0x00, 0x00, /* product code */
+ 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
+ 0x01, /* week of manufacture */
+ 0x00, /* year of manufacture */
+ 0x01, 0x03, /* EDID version */
+ 0x80, /* capabilities - digital */
+ 0x00, /* horiz. res in cm, zero for projectors */
+ 0x00, /* vert. res in cm */
+ 0x78, /* display gamma (120 == 2.2). */
+ 0xEE, /* features (standby, suspend, off, RGB, std */
+ /* colour space, preferred timing mode) */
+ 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
+ /* chromaticity for standard colour space. */
+ 0x00, 0x00, 0x00, /* no default timings */
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, /* no standard timings */
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
+ 0x02, 0x02,
+ /* descriptor block 1 goes below */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* descriptor block 2, monitor ranges */
+ 0x00, 0x00, 0x00, 0xFD, 0x00,
+ 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
+ 0x20, 0x20,
+ /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
+ 0x20,
+ /* descriptor block 3, monitor name */
+ 0x00, 0x00, 0x00, 0xFC, 0x00,
+ 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'c', 'r', 'e', 'e', 'n',
+ /* descriptor block 4: dummy data */
+ 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20,
+ 0x00, /* number of extensions */
+ 0x00 /* checksum goes here */
+ };
+ set_edid_bytes(virtual_monitor_edid);
+ return {};
+}
+
+void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes)
+{
+ 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]);
+ }
+ dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error());
+ }
+}
+
+ErrorOr<void> DisplayConnector::flush_rectangle(size_t, FBRect const&)
+{
+ return Error::from_errno(ENOTSUP);
+}
+
+DisplayConnector::ModeSetting DisplayConnector::current_mode_setting() const
+{
+ SpinlockLocker locker(m_modeset_lock);
+ return m_current_mode_setting;
+}
+
+ErrorOr<ByteBuffer> DisplayConnector::get_edid() const
+{
+ if (!m_edid_valid)
+ return Error::from_errno(ENOTIMPL);
+ return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes));
+}
+
+ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
+{
+ if (request != FB_IOCTL_GET_HEAD_EDID) {
+ // Allow anyone to query the EDID. Eventually we'll publish the current EDID on /sys
+ // so it doesn't really make sense to require the video pledge to query it.
+ TRY(Process::current().require_promise(Pledge::video));
+ }
+
+ // TODO: We really should have ioctls for destroying resources as well
+ switch (request) {
+ case FB_IOCTL_GET_PROPERTIES: {
+ auto user_properties = static_ptr_cast<FBProperties*>(arg);
+ FBProperties properties {};
+ properties.flushing_support = flush_support();
+ properties.doublebuffer_support = double_framebuffering_capable();
+ properties.partial_flushing_support = partial_flush_support();
+ properties.refresh_rate_support = refresh_rate_support();
+
+ return copy_to_user(user_properties, &properties);
+ }
+ case FB_IOCTL_GET_HEAD_MODE_SETTING: {
+ auto user_head_mode_setting = static_ptr_cast<FBHeadModeSetting*>(arg);
+ FBHeadModeSetting head_mode_setting {};
+ TRY(copy_from_user(&head_mode_setting, user_head_mode_setting));
+ {
+ SpinlockLocker control_locker(m_control_lock);
+ head_mode_setting.horizontal_stride = m_current_mode_setting.horizontal_stride;
+ head_mode_setting.pixel_clock_in_khz = m_current_mode_setting.pixel_clock_in_khz;
+ head_mode_setting.horizontal_active = m_current_mode_setting.horizontal_active;
+ head_mode_setting.horizontal_front_porch_pixels = m_current_mode_setting.horizontal_front_porch_pixels;
+ head_mode_setting.horizontal_sync_time_pixels = m_current_mode_setting.horizontal_sync_time_pixels;
+ head_mode_setting.horizontal_blank_pixels = m_current_mode_setting.horizontal_blank_pixels;
+ head_mode_setting.vertical_active = m_current_mode_setting.vertical_active;
+ head_mode_setting.vertical_front_porch_lines = m_current_mode_setting.vertical_front_porch_lines;
+ head_mode_setting.vertical_sync_time_lines = m_current_mode_setting.vertical_sync_time_lines;
+ head_mode_setting.vertical_blank_lines = m_current_mode_setting.vertical_blank_lines;
+ head_mode_setting.horizontal_offset = m_current_mode_setting.horizontal_offset;
+ head_mode_setting.vertical_offset = m_current_mode_setting.vertical_offset;
+ }
+ return copy_to_user(user_head_mode_setting, &head_mode_setting);
+ }
+ case FB_IOCTL_GET_HEAD_EDID: {
+ auto user_head_edid = static_ptr_cast<FBHeadEDID*>(arg);
+ FBHeadEDID head_edid {};
+ TRY(copy_from_user(&head_edid, user_head_edid));
+
+ auto edid_bytes = TRY(get_edid());
+ if (head_edid.bytes != nullptr) {
+ // Only return the EDID if a buffer was provided. Either way,
+ // we'll write back the bytes_size with the actual size
+ if (head_edid.bytes_size < edid_bytes.size()) {
+ head_edid.bytes_size = edid_bytes.size();
+ TRY(copy_to_user(user_head_edid, &head_edid));
+ return Error::from_errno(EOVERFLOW);
+ }
+ TRY(copy_to_user(head_edid.bytes, (void const*)edid_bytes.data(), edid_bytes.size()));
+ }
+ head_edid.bytes_size = edid_bytes.size();
+ return copy_to_user(user_head_edid, &head_edid);
+ }
+ case FB_IOCTL_SET_HEAD_MODE_SETTING: {
+ auto user_mode_setting = static_ptr_cast<FBHeadModeSetting const*>(arg);
+ auto head_mode_setting = TRY(copy_typed_from_user(user_mode_setting));
+
+ if (head_mode_setting.horizontal_stride < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.pixel_clock_in_khz < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.horizontal_active < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.horizontal_front_porch_pixels < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.horizontal_sync_time_pixels < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.horizontal_blank_pixels < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.vertical_active < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.vertical_front_porch_lines < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.vertical_sync_time_lines < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.vertical_blank_lines < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.horizontal_offset < 0)
+ return Error::from_errno(EINVAL);
+ if (head_mode_setting.vertical_offset < 0)
+ return Error::from_errno(EINVAL);
+ {
+ SpinlockLocker control_locker(m_control_lock);
+ ModeSetting requested_mode_setting;
+ requested_mode_setting.horizontal_stride = 0;
+ requested_mode_setting.pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz;
+ requested_mode_setting.horizontal_active = head_mode_setting.horizontal_active;
+ requested_mode_setting.horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels;
+ requested_mode_setting.horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels;
+ requested_mode_setting.horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels;
+ requested_mode_setting.vertical_active = head_mode_setting.vertical_active;
+ requested_mode_setting.vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines;
+ requested_mode_setting.vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines;
+ requested_mode_setting.vertical_blank_lines = head_mode_setting.vertical_blank_lines;
+ requested_mode_setting.horizontal_offset = head_mode_setting.horizontal_offset;
+ requested_mode_setting.vertical_offset = head_mode_setting.vertical_offset;
+
+ TRY(set_mode_setting(requested_mode_setting));
+ }
+ return {};
+ }
+
+ case FB_IOCTL_SET_SAFE_HEAD_MODE_SETTING: {
+ SpinlockLocker control_locker(m_control_lock);
+ TRY(set_safe_mode_setting());
+ return {};
+ }
+
+ case FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: {
+ // FIXME: We silently ignore the request if we are in console mode.
+ // WindowServer is not ready yet to handle errors such as EBUSY currently.
+ SpinlockLocker control_locker(m_control_lock);
+ if (console_mode()) {
+ return {};
+ }
+
+ auto user_head_vertical_buffer_offset = static_ptr_cast<FBHeadVerticalOffset const*>(arg);
+ auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset));
+
+ SpinlockLocker locker(m_modeset_lock);
+
+ if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1)
+ return Error::from_errno(EINVAL);
+ TRY(set_y_offset(head_vertical_buffer_offset.offsetted == 0 ? 0 : m_current_mode_setting.vertical_active));
+ if (head_vertical_buffer_offset.offsetted == 0)
+ m_vertical_offsetted = false;
+ else
+ m_vertical_offsetted = true;
+ return {};
+ }
+ case FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: {
+ auto user_head_vertical_buffer_offset = static_ptr_cast<FBHeadVerticalOffset*>(arg);
+ FBHeadVerticalOffset head_vertical_buffer_offset {};
+ TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset));
+
+ head_vertical_buffer_offset.offsetted = m_vertical_offsetted;
+ return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset);
+ }
+ case FB_IOCTL_FLUSH_HEAD_BUFFERS: {
+ if (!partial_flush_support())
+ return Error::from_errno(ENOTSUP);
+ MutexLocker locker(m_flushing_lock);
+ auto user_flush_rects = static_ptr_cast<FBFlushRects const*>(arg);
+ auto flush_rects = TRY(copy_typed_from_user(user_flush_rects));
+ if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
+ return Error::from_errno(EFAULT);
+ if (flush_rects.count > 0) {
+ for (unsigned i = 0; i < flush_rects.count; i++) {
+ FBRect user_dirty_rect;
+ TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i]));
+ {
+ SpinlockLocker control_locker(m_control_lock);
+ if (console_mode())
+ return {};
+ TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect));
+ }
+ }
+ }
+ return {};
+ };
+ case FB_IOCTL_FLUSH_HEAD: {
+ // FIXME: We silently ignore the request if we are in console mode.
+ // WindowServer is not ready yet to handle errors such as EBUSY currently.
+ MutexLocker locker(m_flushing_lock);
+ SpinlockLocker control_locker(m_control_lock);
+ if (console_mode())
+ return {};
+ if (!flush_support())
+ return Error::from_errno(ENOTSUP);
+ TRY(flush_first_surface());
+ return {};
+ }
+ }
+ return EINVAL;
+}
+
+}
diff --git a/Kernel/Graphics/DisplayConnector.h b/Kernel/Graphics/DisplayConnector.h
new file mode 100644
index 0000000000..ee01d7cdf4
--- /dev/null
+++ b/Kernel/Graphics/DisplayConnector.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+#include <Kernel/Devices/CharacterDevice.h>
+#include <LibC/sys/ioctl_numbers.h>
+#include <LibEDID/EDID.h>
+
+namespace Kernel {
+
+class GraphicsManagement;
+class DisplayConnector : public CharacterDevice {
+ friend class GraphicsManagement;
+ friend class DeviceManagement;
+
+public:
+ struct ModeSetting {
+ size_t horizontal_blanking_start() const
+ {
+ return horizontal_active;
+ }
+ size_t horizontal_sync_start() const
+ {
+ return horizontal_active + horizontal_front_porch_pixels;
+ }
+ size_t horizontal_sync_end() const
+ {
+ return horizontal_active + horizontal_front_porch_pixels + horizontal_sync_time_pixels;
+ }
+ size_t horizontal_total() const
+ {
+ return horizontal_active + horizontal_blank_pixels;
+ }
+
+ size_t vertical_blanking_start() const
+ {
+ return vertical_active;
+ }
+ size_t vertical_sync_start() const
+ {
+ return vertical_active + vertical_front_porch_lines;
+ }
+ size_t vertical_sync_end() const
+ {
+ return vertical_active + vertical_front_porch_lines + vertical_sync_time_lines;
+ }
+ size_t vertical_total() const
+ {
+ return vertical_active + vertical_blank_lines;
+ }
+
+ size_t horizontal_stride; // Note: This is commonly known as "pitch"
+ size_t pixel_clock_in_khz;
+
+ size_t horizontal_active;
+ size_t horizontal_front_porch_pixels;
+ size_t horizontal_sync_time_pixels;
+ size_t horizontal_blank_pixels;
+
+ size_t vertical_active;
+ size_t vertical_front_porch_lines;
+ size_t vertical_sync_time_lines;
+ size_t vertical_blank_lines;
+
+ size_t horizontal_offset; // Note: This is commonly known as "x offset"
+ size_t vertical_offset; // Note: This is commonly known as "y offset"
+ };
+
+public:
+ enum class DisplayMode {
+ Graphical,
+ Console,
+ };
+
+public:
+ virtual ~DisplayConnector() = default;
+
+ virtual bool mutable_mode_setting_capable() const = 0;
+ virtual bool double_framebuffering_capable() const = 0;
+ virtual bool flush_support() const = 0;
+ virtual bool partial_flush_support() const = 0;
+ // Note: This can indicate to userland if the underlying hardware requires
+ // a defined refresh rate being supplied when modesetting the screen resolution.
+ // Paravirtualized hardware don't need such setting and can safely ignore this.
+ virtual bool refresh_rate_support() const = 0;
+
+ bool console_mode() const;
+ ErrorOr<ByteBuffer> get_edid() const;
+ virtual ErrorOr<void> set_mode_setting(ModeSetting const&) = 0;
+ virtual ErrorOr<void> set_safe_mode_setting() = 0;
+ ModeSetting current_mode_setting() const;
+ virtual ErrorOr<void> set_y_offset(size_t y) = 0;
+ virtual ErrorOr<void> unblank() = 0;
+
+ void set_display_mode(Badge<GraphicsManagement>, DisplayMode);
+
+protected:
+ void set_edid_bytes(Array<u8, 128> const& edid_bytes);
+
+ // ^File
+ virtual bool is_seekable() const override { return true; }
+ virtual bool can_read(OpenFileDescription const&, u64) const final override { return true; }
+ virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; }
+ virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final;
+ virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final;
+ virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final;
+ virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final;
+ virtual StringView class_name() const override final { return "DisplayConnector"sv; }
+
+ DisplayConnector();
+ virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) = 0;
+ virtual void enable_console() = 0;
+ virtual void disable_console() = 0;
+ virtual ErrorOr<void> flush_first_surface() = 0;
+ virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const& rect);
+
+ ErrorOr<void> initialize_edid_for_generic_monitor();
+
+ mutable Spinlock m_control_lock;
+ mutable Mutex m_flushing_lock;
+
+ bool m_console_mode { false };
+
+ bool m_vertical_offsetted { false };
+
+ mutable Spinlock m_modeset_lock;
+ ModeSetting m_current_mode_setting {};
+
+ Optional<EDID::Parser> m_edid_parser;
+ EDID::Parser::RawBytes m_edid_bytes {};
+ bool m_edid_valid { false };
+
+private:
+ virtual void will_be_destroyed() override;
+ virtual void after_inserting() override;
+
+ IntrusiveListNode<DisplayConnector, RefPtr<DisplayConnector>> m_list_node;
+};
+}
diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp
index 48536992dc..d5fb78d185 100644
--- a/Kernel/Graphics/GraphicsManagement.cpp
+++ b/Kernel/Graphics/GraphicsManagement.cpp
@@ -95,15 +95,34 @@ void GraphicsManagement::set_vga_text_mode_cursor(size_t console_width, size_t x
void GraphicsManagement::deactivate_graphical_mode()
{
+ // FIXME: Remove this once we don't support Framebuffer devices anymore.
for (auto& graphics_device : m_graphics_devices) {
graphics_device.enable_consoles();
}
+
+ for (auto& connector : m_display_connector_nodes) {
+ connector.set_display_mode({}, DisplayConnector::DisplayMode::Console);
+ }
}
void GraphicsManagement::activate_graphical_mode()
{
+ // FIXME: Remove this once we don't support Framebuffer devices anymore.
for (auto& graphics_device : m_graphics_devices) {
graphics_device.disable_consoles();
}
+
+ for (auto& connector : m_display_connector_nodes) {
+ connector.set_display_mode({}, DisplayConnector::DisplayMode::Graphical);
+ }
+}
+
+void GraphicsManagement::attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
+{
+ m_display_connector_nodes.append(connector);
+}
+void GraphicsManagement::detach_display_connector(Badge<DisplayConnector>, DisplayConnector& connector)
+{
+ m_display_connector_nodes.remove(connector);
}
static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier)
diff --git a/Kernel/Graphics/GraphicsManagement.h b/Kernel/Graphics/GraphicsManagement.h
index 428ac52b0d..b730d4012a 100644
--- a/Kernel/Graphics/GraphicsManagement.h
+++ b/Kernel/Graphics/GraphicsManagement.h
@@ -12,6 +12,7 @@
#include <AK/Types.h>
#include <Kernel/Bus/PCI/Definitions.h>
#include <Kernel/Graphics/Console/Console.h>
+#include <Kernel/Graphics/DisplayConnector.h>
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
#include <Kernel/Graphics/VGACompatibleAdapter.h>
#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
@@ -29,6 +30,9 @@ public:
unsigned allocate_minor_device_number() { return m_current_minor_number++; };
GraphicsManagement();
+ void attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector&);
+ void detach_display_connector(Badge<DisplayConnector>, DisplayConnector&);
+
bool framebuffer_devices_console_only() const;
bool framebuffer_devices_use_bootloader_framebuffer() const;
bool framebuffer_devices_exist() const;
@@ -55,6 +59,8 @@ private:
RefPtr<VGACompatibleAdapter> m_vga_adapter;
unsigned m_current_minor_number { 0 };
+ IntrusiveList<&DisplayConnector::m_list_node> m_display_connector_nodes;
+
RecursiveSpinlock m_main_vga_lock;
bool m_vga_access_is_disabled { false };
};
diff --git a/Userland/Libraries/LibC/sys/ioctl_numbers.h b/Userland/Libraries/LibC/sys/ioctl_numbers.h
index a1a314e378..95e8b74742 100644
--- a/Userland/Libraries/LibC/sys/ioctl_numbers.h
+++ b/Userland/Libraries/LibC/sys/ioctl_numbers.h
@@ -23,6 +23,7 @@ struct FBProperties {
unsigned char doublebuffer_support;
unsigned char flushing_support;
unsigned char partial_flushing_support;
+ unsigned char refresh_rate_support;
};
struct FBHeadProperties {
@@ -36,6 +37,21 @@ struct FBHeadProperties {
unsigned buffer_length;
};
+struct FBHeadModeSetting {
+ int horizontal_stride;
+ int pixel_clock_in_khz;
+ int horizontal_active;
+ int horizontal_front_porch_pixels;
+ int horizontal_sync_time_pixels;
+ int horizontal_blank_pixels;
+ int vertical_active;
+ int vertical_front_porch_lines;
+ int vertical_sync_time_lines;
+ int vertical_blank_lines;
+ int horizontal_offset;
+ int vertical_offset;
+};
+
struct FBHeadResolution {
int head_index;
int pitch;
@@ -103,6 +119,9 @@ enum IOCtlNumber {
FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER,
FB_IOCTL_FLUSH_HEAD_BUFFERS,
FB_IOCTL_FLUSH_HEAD,
+ FB_IOCTL_SET_HEAD_MODE_SETTING,
+ FB_IOCTL_GET_HEAD_MODE_SETTING,
+ FB_IOCTL_SET_SAFE_HEAD_MODE_SETTING,
KEYBOARD_IOCTL_GET_NUM_LOCK,
KEYBOARD_IOCTL_SET_NUM_LOCK,
KEYBOARD_IOCTL_GET_CAPS_LOCK,