/* * Copyright (c) 2021, Sahan Fernando * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace Kernel { #define VIRTIO_GPU_F_VIRGL (1 << 0) #define VIRTIO_GPU_F_EDID (1 << 1) #define VIRTIO_GPU_FLAG_FENCE (1 << 0) #define CONTROLQ 0 #define CURSORQ 1 #define MAX_VIRTIOGPU_RESOLUTION_WIDTH 3840 #define MAX_VIRTIOGPU_RESOLUTION_HEIGHT 2160 #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) class VirtIODisplayConnector; class VirtIOGPU3DDevice; class VirtIOGraphicsAdapter final : public GenericGraphicsAdapter , public VirtIO::Device { friend class VirtIODisplayConnector; friend class VirtIOGPU3DDevice; public: static NonnullLockRefPtr initialize(PCI::DeviceIdentifier const&); virtual void initialize() override; ErrorOr mode_set_resolution(Badge, VirtIODisplayConnector&, size_t width, size_t height); void set_dirty_displayed_rect(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); ErrorOr flush_displayed_image(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer); ErrorOr transfer_framebuffer_data_to_host(Badge, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer); private: ErrorOr attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size); ErrorOr initialize_3d_device(); ErrorOr flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID, Graphics::VirtIOGPU::ResourceID, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); struct Scanout { struct PhysicalBuffer { size_t framebuffer_offset { 0 }; Graphics::VirtIOGPU::Protocol::Rect dirty_rect {}; Graphics::VirtIOGPU::ResourceID resource_id { 0 }; }; LockRefPtr display_connector; PhysicalBuffer main_buffer; PhysicalBuffer back_buffer; }; VirtIOGraphicsAdapter(PCI::DeviceIdentifier const&, Bitmap&& active_context_ids, NonnullOwnPtr scratch_space_region); ErrorOr initialize_adapter(); virtual bool handle_device_config_change() override; virtual void handle_queue_update(u16 queue_index) override; u32 get_pending_events(); void clear_pending_events(u32 event_bitmask); // 2D framebuffer stuff static ErrorOr calculate_framebuffer_size(size_t width, size_t height) { // VirtIO resources can only map on page boundaries! return Memory::page_round_up(sizeof(u32) * width * height); } // 3D Command stuff ErrorOr create_context(); ErrorOr attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id); ErrorOr submit_command_buffer(Graphics::VirtIOGPU::ContextID, Function buffer_writer); Graphics::VirtIOGPU::Protocol::TextureFormat get_framebuffer_format() const { return Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; } auto& operation_lock() { return m_operation_lock; } Graphics::VirtIOGPU::ResourceID allocate_resource_id(); PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } AK::BinaryBufferWriter create_scratchspace_writer() { return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) }; } ErrorOr synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size); ErrorOr create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect); ErrorOr create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification); ErrorOr delete_resource(Graphics::VirtIOGPU::ResourceID resource_id); ErrorOr ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length); ErrorOr detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id); ErrorOr set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect); ErrorOr transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& rect); ErrorOr flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); ErrorOr query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector); size_t m_num_scanouts { 0 }; Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS]; VirtIO::Configuration const* m_device_configuration { nullptr }; // Note: Resource ID 0 is invalid, and we must not allocate 0 as the first resource ID. Atomic m_resource_id_counter { 1 }; SpinlockProtected m_active_context_ids { LockRank::None }; LockRefPtr m_3d_device; bool m_has_virgl_support { false }; Spinlock m_operation_lock { LockRank::None }; NonnullOwnPtr m_scratch_space; }; }