mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 14:57:35 +00:00
Kernel: Add ioctl to get the EDID from a framebuffer
This commit is contained in:
parent
8184870f93
commit
03c45b1865
20 changed files with 265 additions and 84 deletions
|
@ -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)
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue