diff options
author | Tom <tomut@yahoo.com> | 2021-12-31 22:02:55 -0700 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-01-23 22:45:21 +0000 |
commit | 03c45b18653b960f1e7c1705ef0cdbb5b2d2f94d (patch) | |
tree | 4f20b2f7977b4b96f1087af98ac7a5836fe8ecf1 /Kernel | |
parent | 8184870f93522e53e719a34861cad6337ae35cf0 (diff) | |
download | serenity-03c45b18653b960f1e7c1705ef0cdbb5b2d2f94d.zip |
Kernel: Add ioctl to get the EDID from a framebuffer
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/API/FB.h | 5 | ||||
-rw-r--r-- | Kernel/CMakeLists.txt | 7 | ||||
-rw-r--r-- | Kernel/Graphics/Bochs/GraphicsAdapter.cpp | 11 | ||||
-rw-r--r-- | Kernel/Graphics/Bochs/GraphicsAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/Definitions.h | 50 | ||||
-rw-r--r-- | Kernel/Graphics/FramebufferDevice.cpp | 8 | ||||
-rw-r--r-- | Kernel/Graphics/FramebufferDevice.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/GenericFramebufferDevice.cpp | 26 | ||||
-rw-r--r-- | Kernel/Graphics/GenericFramebufferDevice.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/GenericGraphicsAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp | 64 | ||||
-rw-r--r-- | Kernel/Graphics/Intel/NativeGraphicsAdapter.h | 5 | ||||
-rw-r--r-- | Kernel/Graphics/VGACompatibleAdapter.cpp | 5 | ||||
-rw-r--r-- | Kernel/Graphics/VGACompatibleAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp | 9 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.h | 2 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp | 115 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h | 6 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/Protocol.h | 15 |
19 files changed, 255 insertions, 83 deletions
diff --git a/Kernel/API/FB.h b/Kernel/API/FB.h index eaab017ff0..ad929082d3 100644 --- a/Kernel/API/FB.h +++ b/Kernel/API/FB.h @@ -41,6 +41,11 @@ ALWAYS_INLINE int fb_set_resolution(int fd, FBHeadResolution* info) return ioctl(fd, FB_IOCTL_SET_HEAD_RESOLUTION, info); } +ALWAYS_INLINE int fb_get_head_edid(int fd, FBHeadEDID* info) +{ + return ioctl(fd, FB_IOCTL_GET_HEAD_EDID, info); +} + ALWAYS_INLINE int fb_get_head_vertical_offset_buffer(int fd, FBHeadVerticalOffset* vertical_offset) { return ioctl(fd, FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, vertical_offset); diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index c433b27fda..3a41fda517 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -343,6 +343,12 @@ set(AK_SOURCES ../AK/Utf16View.cpp ) +set(EDID_SOURCES + ../Userland/Libraries/LibEDID/DMT.cpp + ../Userland/Libraries/LibEDID/EDID.cpp + ../Userland/Libraries/LibEDID/VIC.cpp +) + set(ELF_SOURCES ../Userland/Libraries/LibELF/Image.cpp ../Userland/Libraries/LibELF/Validation.cpp @@ -368,6 +374,7 @@ if (NOT "${SERENITY_ARCH}" STREQUAL "aarch64") set(SOURCES ${KERNEL_SOURCES} ${SOURCES} + ${EDID_SOURCES} ${ELF_SOURCES} ${VT_SOURCES} ${CRYPTO_SOURCES} diff --git a/Kernel/Graphics/Bochs/GraphicsAdapter.cpp b/Kernel/Graphics/Bochs/GraphicsAdapter.cpp index 732445e22b..aee5db05ff 100644 --- a/Kernel/Graphics/Bochs/GraphicsAdapter.cpp +++ b/Kernel/Graphics/Bochs/GraphicsAdapter.cpp @@ -276,4 +276,15 @@ void BochsGraphicsAdapter::disable_consoles() m_framebuffer_device->activate_writes(); } +ErrorOr<ByteBuffer> BochsGraphicsAdapter::get_edid(size_t output_port_index) const +{ + if (output_port_index != 0) + return Error::from_errno(ENODEV); + + auto bytes = ByteBuffer::copy(const_cast<u8 const*>(m_registers->edid_data), sizeof(m_registers->edid_data)); + if (!bytes.has_value()) + return Error::from_errno(ENOMEM); + return bytes.release_value(); +} + } diff --git a/Kernel/Graphics/Bochs/GraphicsAdapter.h b/Kernel/Graphics/Bochs/GraphicsAdapter.h index 550d490980..83bddb87b3 100644 --- a/Kernel/Graphics/Bochs/GraphicsAdapter.h +++ b/Kernel/Graphics/Bochs/GraphicsAdapter.h @@ -36,6 +36,8 @@ public: virtual bool vga_compatible() const override; + ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; + private: // ^GenericGraphicsAdapter virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) override; diff --git a/Kernel/Graphics/Definitions.h b/Kernel/Graphics/Definitions.h index 49885a574f..f31e9cbf23 100644 --- a/Kernel/Graphics/Definitions.h +++ b/Kernel/Graphics/Definitions.h @@ -32,54 +32,4 @@ struct Modesetting { Timings vertical; }; -struct [[gnu::packed]] StandardTimings { - u8 resolution; - u8 frequency; -}; - -struct [[gnu::packed]] DetailTimings { - u16 pixel_clock; - u8 horizontal_active; - u8 horizontal_blank; - u8 horizontal_active_blank_msb; - u8 vertical_active; - u8 vertical_blank; - u8 vertical_active_blank_msb; - u8 horizontal_sync_offset; - u8 horizontal_sync_pulse; - u8 vertical_sync; - u8 sync_msb; - u8 dimension_width; - u8 dimension_height; - u8 dimension_msb; - u8 horizontal_border; - u8 vertical_border; - u8 features; -}; - -struct [[gnu::packed]] VideoInfoBlock { - u64 padding; - u16 manufacture_id; - u16 product_id; - u32 serial_number; - u8 manufacture_week; - u8 manufacture_year; - u8 edid_version; - u8 edid_revision; - u8 video_input_type; - u8 max_horizontal_size; - u8 max_vertical_size; - u8 gama_factor; - u8 dpms_flags; - u8 chroma_info[10]; - u8 established_timing[2]; - u8 manufacture_reserved_timings; - StandardTimings timings[8]; - DetailTimings details[4]; - u8 unused; - u8 checksum; -}; - -static_assert(AssertSize<VideoInfoBlock, 128>()); - } diff --git a/Kernel/Graphics/FramebufferDevice.cpp b/Kernel/Graphics/FramebufferDevice.cpp index 73b50f1226..2882437f6e 100644 --- a/Kernel/Graphics/FramebufferDevice.cpp +++ b/Kernel/Graphics/FramebufferDevice.cpp @@ -243,4 +243,12 @@ ErrorOr<void> FramebufferDevice::flush_rectangle(size_t, FBRect const&) VERIFY_NOT_REACHED(); } +ErrorOr<ByteBuffer> FramebufferDevice::get_edid(size_t head) const +{ + auto adapter = m_graphics_adapter.strong_ref(); + if (!adapter) + return Error::from_errno(EIO); + return adapter->get_edid(head); +} + } diff --git a/Kernel/Graphics/FramebufferDevice.h b/Kernel/Graphics/FramebufferDevice.h index a81f2d1011..8846112434 100644 --- a/Kernel/Graphics/FramebufferDevice.h +++ b/Kernel/Graphics/FramebufferDevice.h @@ -40,6 +40,8 @@ public: virtual ErrorOr<size_t> vertical_offset(size_t head) const override; virtual ErrorOr<bool> vertical_offsetted(size_t head) const override; + virtual ErrorOr<ByteBuffer> get_edid(size_t head) const override; + private: virtual ErrorOr<void> set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) override; virtual ErrorOr<void> set_head_buffer(size_t head, bool second_buffer) override; diff --git a/Kernel/Graphics/GenericFramebufferDevice.cpp b/Kernel/Graphics/GenericFramebufferDevice.cpp index 80f4ba9032..d2b37f1e8e 100644 --- a/Kernel/Graphics/GenericFramebufferDevice.cpp +++ b/Kernel/Graphics/GenericFramebufferDevice.cpp @@ -33,7 +33,11 @@ ErrorOr<void> GenericFramebufferDevice::verify_head_index(int head_index) const ErrorOr<void> GenericFramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) { - TRY(Process::current().require_promise(Pledge::video)); + 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)); + } switch (request) { case FB_IOCTL_GET_PROPERTIES: { auto user_properties = static_ptr_cast<FBProperties*>(arg); @@ -60,6 +64,26 @@ ErrorOr<void> GenericFramebufferDevice::ioctl(OpenFileDescription&, unsigned req head_properties.offset = TRY(vertical_offset(head_properties.head_index)); return copy_to_user(user_head_properties, &head_properties); } + 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)); + TRY(verify_head_index(head_edid.head_index)); + + auto edid_bytes = TRY(get_edid(head_edid.head_index)); + 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_RESOLUTION: { auto user_head_resolution = static_ptr_cast<FBHeadResolution const*>(arg); auto head_resolution = TRY(copy_typed_from_user(user_head_resolution)); diff --git a/Kernel/Graphics/GenericFramebufferDevice.h b/Kernel/Graphics/GenericFramebufferDevice.h index 93435221bb..ccf549a282 100644 --- a/Kernel/Graphics/GenericFramebufferDevice.h +++ b/Kernel/Graphics/GenericFramebufferDevice.h @@ -57,6 +57,8 @@ protected: // FIXME: This method is too much specific to the VirtIO implementation (especially the buffer_index parameter) virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const&) = 0; + virtual ErrorOr<ByteBuffer> get_edid(size_t head) const = 0; + ErrorOr<void> verify_head_index(int head_index) const; GenericFramebufferDevice(const GenericGraphicsAdapter&); diff --git a/Kernel/Graphics/GenericGraphicsAdapter.h b/Kernel/Graphics/GenericGraphicsAdapter.h index c383d19bd8..4109af8c0d 100644 --- a/Kernel/Graphics/GenericGraphicsAdapter.h +++ b/Kernel/Graphics/GenericGraphicsAdapter.h @@ -29,6 +29,8 @@ public: virtual bool vga_compatible() const = 0; + virtual ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const = 0; + virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) = 0; virtual bool set_y_offset(size_t output_port_index, size_t y) = 0; diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp index 2ef73ab4c9..51489e46eb 100644 --- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp +++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.cpp @@ -62,31 +62,29 @@ static size_t compute_dac_multiplier(size_t pixel_clock_in_khz) } } -static Graphics::Modesetting calculate_modesetting_from_edid(const Graphics::VideoInfoBlock& edid, size_t index) +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(edid.details[0].pixel_clock); - mode.pixel_clock_in_khz = edid.details[0].pixel_clock * 10; + VERIFY(details.pixel_clock_khz()); + mode.pixel_clock_in_khz = details.pixel_clock_khz(); - size_t horizontal_active = edid.details[index].horizontal_active | ((edid.details[index].horizontal_active_blank_msb >> 4) << 8); - size_t horizontal_blank = edid.details[index].horizontal_blank | ((edid.details[index].horizontal_active_blank_msb & 0xF) << 8); - size_t horizontal_sync_offset = edid.details[index].horizontal_sync_offset | ((edid.details[index].sync_msb >> 6) << 8); - size_t horizontal_sync_pulse = edid.details[index].horizontal_sync_pulse | (((edid.details[index].sync_msb >> 4) & 0x3) << 8); + 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 + horizontal_sync_pulse; - mode.horizontal.total = horizontal_active + horizontal_blank; + 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 = edid.details[index].vertical_active | ((edid.details[index].vertical_active_blank_msb >> 4) << 8); - size_t vertical_blank = edid.details[index].vertical_blank | ((edid.details[index].vertical_active_blank_msb & 0xF) << 8); - size_t vertical_sync_offset = (edid.details[index].vertical_sync >> 4) | (((edid.details[index].sync_msb >> 2) & 0x3) << 4); - size_t vertical_sync_pulse = (edid.details[index].vertical_sync & 0xF) | ((edid.details[index].sync_msb & 0x3) << 4); + 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 + vertical_sync_pulse; - mode.vertical.total = vertical_active + vertical_blank; + 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; } @@ -202,7 +200,7 @@ IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address) } gmbus_read_edid(); - auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0); + 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); @@ -377,9 +375,17 @@ void IntelNativeGraphicsAdapter::gmbus_read(unsigned address, u8* buf, size_t le 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, sizeof(Graphics::VideoInfoBlock)); + { + 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)); + } + 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 { + dbgln("IntelNativeGraphicsAdapter: Parsing EDID failed: {}", parsed_edid.error()); + m_crt_edid = {}; + } } bool IntelNativeGraphicsAdapter::is_resolution_valid(size_t, size_t) @@ -420,7 +426,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height) } // FIXME: Get the requested resolution from the EDID!! - auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0); + auto modesetting = calculate_modesetting_from_edid(m_crt_edid.value(), 0); disable_output(); @@ -647,4 +653,22 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices() auto framebuffer_result = m_framebuffer_device->try_to_initialize(); VERIFY(!framebuffer_result.is_error()); } + +ErrorOr<ByteBuffer> IntelNativeGraphicsAdapter::get_edid(size_t output_port_index) const +{ + if (output_port_index != 0) { + dbgln("IntelNativeGraphicsAdapter: get_edid: Only one output supported"); + return Error::from_errno(ENODEV); + } + + if (m_crt_edid.has_value()) { + auto bytes = ByteBuffer::copy(m_crt_edid_bytes, sizeof(m_crt_edid_bytes)); + if (!bytes.has_value()) + return Error::from_errno(ENOMEM); + return bytes.release_value(); + } + + return ByteBuffer {}; +} + } diff --git a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h index 3aa0200b85..ace18e24ce 100644 --- a/Kernel/Graphics/Intel/NativeGraphicsAdapter.h +++ b/Kernel/Graphics/Intel/NativeGraphicsAdapter.h @@ -12,6 +12,7 @@ #include <Kernel/Graphics/FramebufferDevice.h> #include <Kernel/Graphics/VGACompatibleAdapter.h> #include <Kernel/PhysicalAddress.h> +#include <LibEDID/EDID.h> namespace Kernel { @@ -113,6 +114,7 @@ private: // ^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; @@ -162,7 +164,8 @@ private: Spinlock m_modeset_lock; mutable Spinlock m_registers_lock; - Graphics::VideoInfoBlock m_crt_edid; + 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; diff --git a/Kernel/Graphics/VGACompatibleAdapter.cpp b/Kernel/Graphics/VGACompatibleAdapter.cpp index d64da3897b..5a62e6e485 100644 --- a/Kernel/Graphics/VGACompatibleAdapter.cpp +++ b/Kernel/Graphics/VGACompatibleAdapter.cpp @@ -80,4 +80,9 @@ bool VGACompatibleAdapter::set_y_offset(size_t, size_t) return false; } +ErrorOr<ByteBuffer> VGACompatibleAdapter::get_edid(size_t) const +{ + return Error::from_errno(ENOTSUP); +} + } diff --git a/Kernel/Graphics/VGACompatibleAdapter.h b/Kernel/Graphics/VGACompatibleAdapter.h index efbe53e47a..bfd8e33497 100644 --- a/Kernel/Graphics/VGACompatibleAdapter.h +++ b/Kernel/Graphics/VGACompatibleAdapter.h @@ -31,6 +31,8 @@ public: virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) override; virtual bool set_y_offset(size_t output_port_index, size_t y) override; + ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; + protected: explicit VGACompatibleAdapter(PCI::Address); diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp index 888facb22f..ad53d75bdd 100644 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp +++ b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp @@ -140,6 +140,15 @@ ErrorOr<void> FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect con return {}; } +ErrorOr<ByteBuffer> FramebufferDevice::get_edid(size_t head) const +{ + // Note: This FramebufferDevice class doesn't support multihead setup. + // We take care to verify this at the GenericFramebufferDevice::ioctl method + // so if we happen to accidentally have a value different than 0, assert. + VERIFY(head == 0); + return adapter()->get_edid(m_scanout.value()); +} + FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout) : GenericFramebufferDevice(adapter) , m_scanout(scanout) diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h index ed72e47af7..1aed6582b9 100644 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h +++ b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h @@ -62,6 +62,8 @@ private: virtual ErrorOr<void> flush_head_buffer(size_t head) override; virtual ErrorOr<void> flush_rectangle(size_t head, FBRect const&) override; + virtual ErrorOr<ByteBuffer> get_edid(size_t head) const override; + void flush_dirty_window(Protocol::Rect const&, Buffer&); void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&); void flush_displayed_image(Protocol::Rect const&, Buffer&); diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp index 1eb4d1824b..6264396d33 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp @@ -78,7 +78,7 @@ void GraphicsAdapter::initialize() if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VIRGL is not yet supported!"); if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: EDID is not yet supported!"); + negotiated |= VIRTIO_GPU_F_EDID; return negotiated; }); if (success) { @@ -93,6 +93,7 @@ void GraphicsAdapter::initialize() MutexLocker locker(m_operation_lock); // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO query_display_information(); + query_display_edid({}); } else { VERIFY_NOT_REACHED(); } @@ -160,10 +161,96 @@ void GraphicsAdapter::query_display_information() dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Scanout {}: enabled: {} x: {}, y: {}, width: {}, height: {}", i, !!scanout.enabled, scanout.rect.x, scanout.rect.y, scanout.rect.width, scanout.rect.height); if (scanout.enabled && !m_default_scanout.has_value()) m_default_scanout = i; + + m_scanouts[i].edid = {}; } VERIFY(m_default_scanout.has_value()); } +void GraphicsAdapter::query_display_edid(Optional<ScanoutID> scanout_id) +{ + VERIFY(m_operation_lock.is_locked()); + + if (!is_feature_accepted(VIRTIO_GPU_F_EDID)) + return; + + for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) { + if (scanout_id.has_value() && scanout_id.value() != i) + continue; + + // scanout.display_info.enabled doesn't seem to reflect the actual state, + // even if we were to call query_display_information prior to calling + // this function. So, just ignore, we seem to get EDID information regardless. + + auto query_edid_result = query_edid(i); + if (query_edid_result.is_error()) { + dbgln("VirtIO::GraphicsAdapater: Scanout {}: Failed to parse EDID: {}", i, query_edid_result.error()); + m_scanouts[i].edid = {}; + } else { + m_scanouts[i].edid = query_edid_result.release_value(); + if (m_scanouts[i].edid.has_value()) { + auto& parsed_edid = m_scanouts[i].edid.value(); + dbgln("VirtIO::GraphicsAdapater: Scanout {}: EDID {}: Manufacturer: {} Product: {} Serial #{}", i, + parsed_edid.version(), parsed_edid.legacy_manufacturer_id(), parsed_edid.product_code(), parsed_edid.serial_number()); + if (auto screen_size = parsed_edid.screen_size(); screen_size.has_value()) { + auto& size = screen_size.value(); + dbgln("VirtIO::GraphicsAdapater: Scanout {}: Screen size: {}cm x {}cm", i, + size.horizontal_cm(), size.vertical_cm()); + } else if (auto aspect_ratio = parsed_edid.aspect_ratio(); aspect_ratio.has_value()) { + auto& ratio = aspect_ratio.value(); + dbgln("VirtIO::GraphicsAdapater: Scanout {}: Aspect ratio: {} : 1", i, ratio.ratio()); + } else { + dbgln("VirtIO::GraphicsAdapater: Scanout {}: Unknown screen size or aspect ratio", i); + } + } else { + dbgln("VirtIO::GraphicsAdapater: Scanout {}: No EDID", i); + } + } + } +} + +ErrorOr<ByteBuffer> GraphicsAdapter::get_edid(size_t output_port_index) const +{ + if (output_port_index >= VIRTIO_GPU_MAX_SCANOUTS) + return Error::from_errno(ENODEV); + auto& edid = m_scanouts[output_port_index].edid; + if (edid.has_value()) { + auto bytes = ByteBuffer::copy(edid.value().bytes()); + if (!bytes.has_value()) + return Error::from_errno(ENOMEM); + return bytes.release_value(); + } + return ByteBuffer {}; +} + +auto GraphicsAdapter::query_edid(u32 scanout_id) -> ErrorOr<Optional<EDID::Parser>> +{ + VERIFY(m_operation_lock.is_locked()); + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::GetEDID>(); + auto& response = writer.append_structure<Protocol::GetEDIDResponse>(); + + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_FLAG_FENCE); + + request.scanout_id = scanout_id; + request.padding = 0; + + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + + if (response.header.type != static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_EDID)) + return Error::from_string_literal("VirtIO::GraphicsAdapter: Failed to get EDID"); + + if (response.size == 0) + return Error::from_string_literal("VirtIO::GraphicsAdapter: Failed to get EDID, empty buffer"); + + auto edid_buffer = ByteBuffer::copy(response.edid, response.size); + if (!edid_buffer.has_value()) + return Error::from_errno(ENOMEM); + + auto edid = TRY(EDID::Parser::from_bytes(edid_buffer.release_value())); + return edid; +} + ResourceID GraphicsAdapter::create_2d_resource(Protocol::Rect rect) { VERIFY(m_operation_lock.is_locked()); @@ -235,19 +322,25 @@ void GraphicsAdapter::detach_backing_storage(ResourceID resource_id) void GraphicsAdapter::set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect) { VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::SetScanOut>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + { + // We need to scope the request/response here so that we can query display information later on + auto writer = create_scratchspace_writer(); + auto& request = writer.append_structure<Protocol::SetScanOut>(); + auto& response = writer.append_structure<Protocol::ControlHeader>(); - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = resource_id.value(); - request.scanout_id = scanout.value(); - request.rect = rect; + populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_FLAG_FENCE); + request.resource_id = resource_id.value(); + request.scanout_id = scanout.value(); + request.rect = rect; - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); + synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout"); + VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout"); + } + + // Now that the Scanout should be enabled, update the EDID + query_display_edid(scanout); } void GraphicsAdapter::transfer_framebuffer_data_to_host(ScanoutID scanout, ResourceID resource_id, Protocol::Rect const& dirty_rect) diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h index 2dd0a1ad57..d3dc3feb1f 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h @@ -15,6 +15,7 @@ #include <Kernel/Graphics/VirtIOGPU/Console.h> #include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/Protocol.h> +#include <LibEDID/EDID.h> namespace Kernel::Graphics::VirtIOGPU { @@ -47,6 +48,8 @@ public: virtual void initialize() override; + ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; + private: void flush_dirty_rectangle(ScanoutID, ResourceID, Protocol::Rect const& dirty_rect); @@ -98,6 +101,7 @@ private: RefPtr<Graphics::VirtIOGPU::FramebufferDevice> framebuffer; RefPtr<Console> console; Protocol::DisplayInfoResponse::Display display_info {}; + Optional<EDID::Parser> edid; }; virtual bool handle_device_config_change() override; @@ -117,6 +121,7 @@ private: void populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags = 0); void query_display_information(); + void query_display_edid(Optional<ScanoutID>); ResourceID create_2d_resource(Protocol::Rect rect); void delete_resource(ResourceID resource_id); void ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length); @@ -124,6 +129,7 @@ private: void set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect); void transfer_framebuffer_data_to_host(ScanoutID scanout, ResourceID resource_id, Protocol::Rect const& rect); void flush_displayed_image(ResourceID resource_id, Protocol::Rect const& dirty_rect); + ErrorOr<Optional<EDID::Parser>> query_edid(u32 scanout_id); bool m_created_framebuffer_devices { false }; Optional<ScanoutID> m_default_scanout; diff --git a/Kernel/Graphics/VirtIOGPU/Protocol.h b/Kernel/Graphics/VirtIOGPU/Protocol.h index c715084dd5..704f565352 100644 --- a/Kernel/Graphics/VirtIOGPU/Protocol.h +++ b/Kernel/Graphics/VirtIOGPU/Protocol.h @@ -156,4 +156,19 @@ struct ResourceFlush { u32 padding; }; +// Specification equivalent: struct virtio_gpu_get_edid +struct GetEDID { + ControlHeader header; + u32 scanout_id; + u32 padding; +}; + +// Specification equivalent: struct virtio_gpu_resp_edid +struct GetEDIDResponse { + ControlHeader header; + u32 size; + u32 padding; + u8 edid[1024]; +}; + } |