mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 09:38:11 +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
|
@ -41,6 +41,11 @@ ALWAYS_INLINE int fb_set_resolution(int fd, FBHeadResolution* info)
|
||||||
return ioctl(fd, FB_IOCTL_SET_HEAD_RESOLUTION, 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)
|
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);
|
return ioctl(fd, FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, vertical_offset);
|
||||||
|
|
|
@ -343,6 +343,12 @@ set(AK_SOURCES
|
||||||
../AK/Utf16View.cpp
|
../AK/Utf16View.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(EDID_SOURCES
|
||||||
|
../Userland/Libraries/LibEDID/DMT.cpp
|
||||||
|
../Userland/Libraries/LibEDID/EDID.cpp
|
||||||
|
../Userland/Libraries/LibEDID/VIC.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(ELF_SOURCES
|
set(ELF_SOURCES
|
||||||
../Userland/Libraries/LibELF/Image.cpp
|
../Userland/Libraries/LibELF/Image.cpp
|
||||||
../Userland/Libraries/LibELF/Validation.cpp
|
../Userland/Libraries/LibELF/Validation.cpp
|
||||||
|
@ -368,6 +374,7 @@ if (NOT "${SERENITY_ARCH}" STREQUAL "aarch64")
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
${KERNEL_SOURCES}
|
${KERNEL_SOURCES}
|
||||||
${SOURCES}
|
${SOURCES}
|
||||||
|
${EDID_SOURCES}
|
||||||
${ELF_SOURCES}
|
${ELF_SOURCES}
|
||||||
${VT_SOURCES}
|
${VT_SOURCES}
|
||||||
${CRYPTO_SOURCES}
|
${CRYPTO_SOURCES}
|
||||||
|
|
|
@ -276,4 +276,15 @@ void BochsGraphicsAdapter::disable_consoles()
|
||||||
m_framebuffer_device->activate_writes();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
|
|
||||||
virtual bool vga_compatible() const override;
|
virtual bool vga_compatible() const override;
|
||||||
|
|
||||||
|
ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ^GenericGraphicsAdapter
|
// ^GenericGraphicsAdapter
|
||||||
virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) override;
|
virtual bool try_to_set_resolution(size_t output_port_index, size_t width, size_t height) override;
|
||||||
|
|
|
@ -32,54 +32,4 @@ struct Modesetting {
|
||||||
Timings vertical;
|
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>());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,4 +243,12 @@ ErrorOr<void> FramebufferDevice::flush_rectangle(size_t, FBRect const&)
|
||||||
VERIFY_NOT_REACHED();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,8 @@ public:
|
||||||
virtual ErrorOr<size_t> vertical_offset(size_t head) const override;
|
virtual ErrorOr<size_t> vertical_offset(size_t head) const override;
|
||||||
virtual ErrorOr<bool> vertical_offsetted(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:
|
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_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;
|
virtual ErrorOr<void> set_head_buffer(size_t head, bool second_buffer) override;
|
||||||
|
|
|
@ -33,7 +33,11 @@ ErrorOr<void> GenericFramebufferDevice::verify_head_index(int head_index) const
|
||||||
|
|
||||||
ErrorOr<void> GenericFramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
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) {
|
switch (request) {
|
||||||
case FB_IOCTL_GET_PROPERTIES: {
|
case FB_IOCTL_GET_PROPERTIES: {
|
||||||
auto user_properties = static_ptr_cast<FBProperties*>(arg);
|
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));
|
head_properties.offset = TRY(vertical_offset(head_properties.head_index));
|
||||||
return copy_to_user(user_head_properties, &head_properties);
|
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: {
|
case FB_IOCTL_SET_HEAD_RESOLUTION: {
|
||||||
auto user_head_resolution = static_ptr_cast<FBHeadResolution const*>(arg);
|
auto user_head_resolution = static_ptr_cast<FBHeadResolution const*>(arg);
|
||||||
auto head_resolution = TRY(copy_typed_from_user(user_head_resolution));
|
auto head_resolution = TRY(copy_typed_from_user(user_head_resolution));
|
||||||
|
|
|
@ -57,6 +57,8 @@ protected:
|
||||||
// FIXME: This method is too much specific to the VirtIO implementation (especially the buffer_index parameter)
|
// 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<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;
|
ErrorOr<void> verify_head_index(int head_index) const;
|
||||||
|
|
||||||
GenericFramebufferDevice(const GenericGraphicsAdapter&);
|
GenericFramebufferDevice(const GenericGraphicsAdapter&);
|
||||||
|
|
|
@ -29,6 +29,8 @@ public:
|
||||||
|
|
||||||
virtual bool vga_compatible() const = 0;
|
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 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;
|
virtual bool set_y_offset(size_t output_port_index, size_t y) = 0;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
Graphics::Modesetting mode;
|
auto details = edid.detailed_timing(index).release_value();
|
||||||
VERIFY(edid.details[0].pixel_clock);
|
|
||||||
mode.pixel_clock_in_khz = edid.details[0].pixel_clock * 10;
|
|
||||||
|
|
||||||
size_t horizontal_active = edid.details[index].horizontal_active | ((edid.details[index].horizontal_active_blank_msb >> 4) << 8);
|
Graphics::Modesetting mode;
|
||||||
size_t horizontal_blank = edid.details[index].horizontal_blank | ((edid.details[index].horizontal_active_blank_msb & 0xF) << 8);
|
VERIFY(details.pixel_clock_khz());
|
||||||
size_t horizontal_sync_offset = edid.details[index].horizontal_sync_offset | ((edid.details[index].sync_msb >> 6) << 8);
|
mode.pixel_clock_in_khz = details.pixel_clock_khz();
|
||||||
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.active = horizontal_active;
|
||||||
mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
|
mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
|
||||||
mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + horizontal_sync_pulse;
|
mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels();
|
||||||
mode.horizontal.total = horizontal_active + horizontal_blank;
|
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_active = details.vertical_addressable_lines();
|
||||||
size_t vertical_blank = edid.details[index].vertical_blank | ((edid.details[index].vertical_active_blank_msb & 0xF) << 8);
|
size_t vertical_sync_offset = details.vertical_front_porch_lines();
|
||||||
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);
|
|
||||||
|
|
||||||
mode.vertical.active = vertical_active;
|
mode.vertical.active = vertical_active;
|
||||||
mode.vertical.sync_start = vertical_active + vertical_sync_offset;
|
mode.vertical.sync_start = vertical_active + vertical_sync_offset;
|
||||||
mode.vertical.sync_end = vertical_active + vertical_sync_offset + vertical_sync_pulse;
|
mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines();
|
||||||
mode.vertical.total = vertical_active + vertical_blank;
|
mode.vertical.total = vertical_active + details.vertical_blanking_lines();
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +200,7 @@ IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
|
||||||
}
|
}
|
||||||
gmbus_read_edid();
|
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);
|
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);
|
set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active);
|
||||||
auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
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()
|
void IntelNativeGraphicsAdapter::gmbus_read_edid()
|
||||||
{
|
{
|
||||||
SpinlockLocker control_lock(m_control_lock);
|
{
|
||||||
gmbus_write(DDC2_I2C_ADDRESS, 0);
|
SpinlockLocker control_lock(m_control_lock);
|
||||||
gmbus_read(DDC2_I2C_ADDRESS, (u8*)&m_crt_edid, sizeof(Graphics::VideoInfoBlock));
|
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)
|
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!!
|
// 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();
|
disable_output();
|
||||||
|
|
||||||
|
@ -647,4 +653,22 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
|
||||||
auto framebuffer_result = m_framebuffer_device->try_to_initialize();
|
auto framebuffer_result = m_framebuffer_device->try_to_initialize();
|
||||||
VERIFY(!framebuffer_result.is_error());
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||||
#include <Kernel/PhysicalAddress.h>
|
#include <Kernel/PhysicalAddress.h>
|
||||||
|
#include <LibEDID/EDID.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -113,6 +114,7 @@ private:
|
||||||
|
|
||||||
// ^GenericGraphicsAdapter
|
// ^GenericGraphicsAdapter
|
||||||
virtual void initialize_framebuffer_devices() override;
|
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_a_enabled() const;
|
||||||
bool pipe_b_enabled() const;
|
bool pipe_b_enabled() const;
|
||||||
|
@ -162,7 +164,8 @@ private:
|
||||||
Spinlock m_modeset_lock;
|
Spinlock m_modeset_lock;
|
||||||
mutable Spinlock m_registers_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_registers;
|
||||||
const PhysicalAddress m_framebuffer_addr;
|
const PhysicalAddress m_framebuffer_addr;
|
||||||
OwnPtr<Memory::Region> m_registers_region;
|
OwnPtr<Memory::Region> m_registers_region;
|
||||||
|
|
|
@ -80,4 +80,9 @@ bool VGACompatibleAdapter::set_y_offset(size_t, size_t)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<ByteBuffer> VGACompatibleAdapter::get_edid(size_t) const
|
||||||
|
{
|
||||||
|
return Error::from_errno(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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;
|
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:
|
protected:
|
||||||
explicit VGACompatibleAdapter(PCI::Address);
|
explicit VGACompatibleAdapter(PCI::Address);
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,15 @@ ErrorOr<void> FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect con
|
||||||
return {};
|
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)
|
FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout)
|
||||||
: GenericFramebufferDevice(adapter)
|
: GenericFramebufferDevice(adapter)
|
||||||
, m_scanout(scanout)
|
, m_scanout(scanout)
|
||||||
|
|
|
@ -62,6 +62,8 @@ private:
|
||||||
virtual ErrorOr<void> flush_head_buffer(size_t head) override;
|
virtual ErrorOr<void> flush_head_buffer(size_t head) override;
|
||||||
virtual ErrorOr<void> flush_rectangle(size_t head, FBRect const&) 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 flush_dirty_window(Protocol::Rect const&, Buffer&);
|
||||||
void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&);
|
void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&);
|
||||||
void flush_displayed_image(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))
|
if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL))
|
||||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VIRGL is not yet supported!");
|
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VIRGL is not yet supported!");
|
||||||
if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID))
|
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;
|
return negotiated;
|
||||||
});
|
});
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -93,6 +93,7 @@ void GraphicsAdapter::initialize()
|
||||||
MutexLocker locker(m_operation_lock);
|
MutexLocker locker(m_operation_lock);
|
||||||
// Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO
|
// Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO
|
||||||
query_display_information();
|
query_display_information();
|
||||||
|
query_display_edid({});
|
||||||
} else {
|
} else {
|
||||||
VERIFY_NOT_REACHED();
|
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);
|
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())
|
if (scanout.enabled && !m_default_scanout.has_value())
|
||||||
m_default_scanout = i;
|
m_default_scanout = i;
|
||||||
|
|
||||||
|
m_scanouts[i].edid = {};
|
||||||
}
|
}
|
||||||
VERIFY(m_default_scanout.has_value());
|
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)
|
ResourceID GraphicsAdapter::create_2d_resource(Protocol::Rect rect)
|
||||||
{
|
{
|
||||||
VERIFY(m_operation_lock.is_locked());
|
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)
|
void GraphicsAdapter::set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect)
|
||||||
{
|
{
|
||||||
VERIFY(m_operation_lock.is_locked());
|
VERIFY(m_operation_lock.is_locked());
|
||||||
auto writer = create_scratchspace_writer();
|
{
|
||||||
auto& request = writer.append_structure<Protocol::SetScanOut>();
|
// We need to scope the request/response here so that we can query display information later on
|
||||||
auto& response = writer.append_structure<Protocol::ControlHeader>();
|
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);
|
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.resource_id = resource_id.value();
|
||||||
request.scanout_id = scanout.value();
|
request.scanout_id = scanout.value();
|
||||||
request.rect = rect;
|
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));
|
VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
|
||||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout");
|
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)
|
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/Console.h>
|
||||||
#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h>
|
#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h>
|
||||||
#include <Kernel/Graphics/VirtIOGPU/Protocol.h>
|
#include <Kernel/Graphics/VirtIOGPU/Protocol.h>
|
||||||
|
#include <LibEDID/EDID.h>
|
||||||
|
|
||||||
namespace Kernel::Graphics::VirtIOGPU {
|
namespace Kernel::Graphics::VirtIOGPU {
|
||||||
|
|
||||||
|
@ -47,6 +48,8 @@ public:
|
||||||
|
|
||||||
virtual void initialize() override;
|
virtual void initialize() override;
|
||||||
|
|
||||||
|
ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void flush_dirty_rectangle(ScanoutID, ResourceID, Protocol::Rect const& dirty_rect);
|
void flush_dirty_rectangle(ScanoutID, ResourceID, Protocol::Rect const& dirty_rect);
|
||||||
|
|
||||||
|
@ -98,6 +101,7 @@ private:
|
||||||
RefPtr<Graphics::VirtIOGPU::FramebufferDevice> framebuffer;
|
RefPtr<Graphics::VirtIOGPU::FramebufferDevice> framebuffer;
|
||||||
RefPtr<Console> console;
|
RefPtr<Console> console;
|
||||||
Protocol::DisplayInfoResponse::Display display_info {};
|
Protocol::DisplayInfoResponse::Display display_info {};
|
||||||
|
Optional<EDID::Parser> edid;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual bool handle_device_config_change() override;
|
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 populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags = 0);
|
||||||
|
|
||||||
void query_display_information();
|
void query_display_information();
|
||||||
|
void query_display_edid(Optional<ScanoutID>);
|
||||||
ResourceID create_2d_resource(Protocol::Rect rect);
|
ResourceID create_2d_resource(Protocol::Rect rect);
|
||||||
void delete_resource(ResourceID resource_id);
|
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);
|
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 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 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);
|
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 };
|
bool m_created_framebuffer_devices { false };
|
||||||
Optional<ScanoutID> m_default_scanout;
|
Optional<ScanoutID> m_default_scanout;
|
||||||
|
|
|
@ -156,4 +156,19 @@ struct ResourceFlush {
|
||||||
u32 padding;
|
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];
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,13 @@ struct FBHeadResolution {
|
||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FBHeadEDID {
|
||||||
|
int head_index;
|
||||||
|
|
||||||
|
unsigned char* bytes;
|
||||||
|
unsigned bytes_size;
|
||||||
|
};
|
||||||
|
|
||||||
struct FBHeadVerticalOffset {
|
struct FBHeadVerticalOffset {
|
||||||
int head_index;
|
int head_index;
|
||||||
int offsetted;
|
int offsetted;
|
||||||
|
@ -85,6 +92,7 @@ enum IOCtlNumber {
|
||||||
FB_IOCTL_GET_PROPERTIES,
|
FB_IOCTL_GET_PROPERTIES,
|
||||||
FB_IOCTL_GET_HEAD_PROPERTIES,
|
FB_IOCTL_GET_HEAD_PROPERTIES,
|
||||||
FB_IOCTL_SET_HEAD_RESOLUTION,
|
FB_IOCTL_SET_HEAD_RESOLUTION,
|
||||||
|
FB_IOCTL_GET_HEAD_EDID,
|
||||||
FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER,
|
FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER,
|
||||||
FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER,
|
FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER,
|
||||||
FB_IOCTL_FLUSH_HEAD_BUFFERS,
|
FB_IOCTL_FLUSH_HEAD_BUFFERS,
|
||||||
|
@ -133,6 +141,7 @@ enum IOCtlNumber {
|
||||||
#define TIOCSWINSZ TIOCSWINSZ
|
#define TIOCSWINSZ TIOCSWINSZ
|
||||||
#define FB_IOCTL_GET_PROPERTIES FB_IOCTL_GET_PROPERTIES
|
#define FB_IOCTL_GET_PROPERTIES FB_IOCTL_GET_PROPERTIES
|
||||||
#define FB_IOCTL_GET_HEAD_PROPERTIES FB_IOCTL_GET_HEAD_PROPERTIES
|
#define FB_IOCTL_GET_HEAD_PROPERTIES FB_IOCTL_GET_HEAD_PROPERTIES
|
||||||
|
#define FB_IOCTL_GET_HEAD_EDID FB_IOCTL_GET_HEAD_EDID
|
||||||
#define FB_IOCTL_SET_HEAD_RESOLUTION FB_IOCTL_SET_HEAD_RESOLUTION
|
#define FB_IOCTL_SET_HEAD_RESOLUTION FB_IOCTL_SET_HEAD_RESOLUTION
|
||||||
#define FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER
|
#define FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER
|
||||||
#define FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER
|
#define FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue