blob: 3302b9b41b0b3e5aacf021f964bb022d620c5592 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/display/drivers/virtio-guest/v1/virtio-gpu-device.h"
#include <fidl/fuchsia.images2/cpp/wire.h>
#include <lib/ddk/debug.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <cinttypes>
#include <cstdint>
#include <memory>
#include <optional>
#include <fbl/alloc_checker.h>
#include <fbl/vector.h>
#include "src/graphics/display/drivers/virtio-guest/v1/virtio-pci-device.h"
#include "src/graphics/lib/virtio/virtio-abi.h"
namespace virtio_display {
VirtioGpuDevice::VirtioGpuDevice(std::unique_ptr<VirtioPciDevice> virtio_device)
: virtio_device_(std::move(virtio_device)) {
ZX_DEBUG_ASSERT(virtio_device_);
}
VirtioGpuDevice::~VirtioGpuDevice() = default;
zx::result<uint32_t> VirtioGpuDevice::UpdateCursor() {
const virtio_abi::UpdateCursorCommand command = {
.header = {.type = virtio_abi::ControlType::kUpdateCursorCommand},
.resource_id = next_resource_id_++,
};
const auto& response =
virtio_device_->ExchangeCursorqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(WARNING, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok(command.resource_id);
}
zx::result<uint32_t> VirtioGpuDevice::SetCursorPosition(uint32_t scanout_id, uint32_t x, uint32_t y,
uint32_t padding) {
const virtio_abi::UpdateCursorCommand command = {
.header = {.type = virtio_abi::ControlType::kMoveCursorCommand},
.pos = {.scanout_id = scanout_id, .x = x, .y = y, .padding = padding},
// The fields below are ignored by the Move Cursor command.
.resource_id = 0,
.hot_x = 0,
.hot_y = 0,
.padding = 0,
};
const auto& response =
virtio_device_->ExchangeCursorqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(WARNING, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok(command.resource_id);
} // namespace virtio_display
zx::result<fbl::Vector<DisplayInfo>> VirtioGpuDevice::GetDisplayInfo() {
const virtio_abi::GetDisplayInfoCommand command = {
.header = {.type = virtio_abi::ControlType::kGetDisplayInfoCommand},
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::DisplayInfoResponse>(command);
if (response.header.type != virtio_abi::ControlType::kDisplayInfoResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
size_t enabled_scanout_count = 0;
for (const virtio_abi::ScanoutInfo& scanout : response.scanouts) {
if (scanout.enabled) {
++enabled_scanout_count;
}
}
fbl::AllocChecker alloc_checker;
fbl::Vector<DisplayInfo> display_infos;
display_infos.reserve(enabled_scanout_count, &alloc_checker);
if (!alloc_checker.check()) {
zxlogf(ERROR, "Failed to allocate memory for DisplayInfo vector");
return zx::error(ZX_ERR_NO_MEMORY);
}
for (int i = 0; i < virtio_abi::kMaxScanouts; ++i) {
const virtio_abi::ScanoutInfo& scanout = response.scanouts[i];
if (!scanout.enabled) {
continue;
}
zxlogf(TRACE,
"Scanout %d: placement (%" PRIu32 ", %" PRIu32 "), resolution %" PRIu32 "x%" PRIu32
" flags 0x%08" PRIx32,
i, scanout.geometry.placement_x, scanout.geometry.placement_y, scanout.geometry.width,
scanout.geometry.height, scanout.flags);
ZX_DEBUG_ASSERT(display_infos.size() < enabled_scanout_count);
display_infos.push_back({.scanout_info = scanout, .scanout_id = i}, &alloc_checker);
ZX_DEBUG_ASSERT(alloc_checker.check());
}
return zx::ok(std::move(display_infos));
}
namespace {
// Returns nullopt for an unsupported format.
std::optional<virtio_abi::ResourceFormat> To2DResourceFormat(
fuchsia_images2::wire::PixelFormat pixel_format) {
// TODO(https://fxbug.dev/42073721): Support more formats.
switch (pixel_format) {
case fuchsia_images2::PixelFormat::kB8G8R8A8:
return virtio_abi::ResourceFormat::kBgra32;
default:
return std::nullopt;
}
}
} // namespace
zx::result<uint32_t> VirtioGpuDevice::Create2DResource(
uint32_t width, uint32_t height, fuchsia_images2::wire::PixelFormat pixel_format) {
zxlogf(TRACE, "Allocate2DResource");
std::optional<virtio_abi::ResourceFormat> resource_format = To2DResourceFormat(pixel_format);
if (!resource_format.has_value()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
const virtio_abi::Create2DResourceCommand command = {
.header = {.type = virtio_abi::ControlType::kCreate2DResourceCommand},
.resource_id = next_resource_id_++,
.format = virtio_abi::ResourceFormat::kBgra32,
.width = width,
.height = height,
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok(command.resource_id);
}
zx::result<> VirtioGpuDevice::AttachResourceBacking(uint32_t resource_id, zx_paddr_t ptr,
size_t buf_len) {
ZX_ASSERT(ptr);
zxlogf(TRACE,
"AttachResourceBacking - resource ID %" PRIu32 ", address 0x%" PRIx64 ", length %zu",
resource_id, ptr, buf_len);
const virtio_abi::AttachResourceBackingCommand<1> command = {
.header = {.type = virtio_abi::ControlType::kAttachResourceBackingCommand},
.resource_id = resource_id,
.entries =
{
{.address = ptr, .length = static_cast<uint32_t>(buf_len)},
},
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
zx::result<> VirtioGpuDevice::SetScanoutProperties(uint32_t scanout_id, uint32_t resource_id,
uint32_t width, uint32_t height) {
zxlogf(TRACE,
"SetScanoutProperties - scanout ID %" PRIu32 ", resource ID %" PRIu32 ", size %" PRIu32
"x%" PRIu32,
scanout_id, resource_id, width, height);
const virtio_abi::SetScanoutCommand command = {
.header = {.type = virtio_abi::ControlType::kSetScanoutCommand},
.geometry =
{
.placement_x = 0,
.placement_y = 0,
.width = width,
.height = height,
},
.scanout_id = scanout_id,
.resource_id = resource_id,
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
zx::result<> VirtioGpuDevice::FlushResource(uint32_t resource_id, uint32_t width, uint32_t height) {
zxlogf(TRACE, "FlushResource - resource ID %" PRIu32 ", size %" PRIu32 "x%" PRIu32, resource_id,
width, height);
virtio_abi::FlushResourceCommand command = {
.header = {.type = virtio_abi::ControlType::kFlushResourceCommand},
.geometry = {.placement_x = 0, .placement_y = 0, .width = width, .height = height},
.resource_id = resource_id,
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
zx::result<> VirtioGpuDevice::TransferToHost2D(uint32_t resource_id, uint32_t width,
uint32_t height) {
zxlogf(TRACE, "Transfer2DResourceToHost - resource ID %" PRIu32 ", size %" PRIu32 "x%" PRIu32,
resource_id, width, height);
virtio_abi::Transfer2DResourceToHostCommand command = {
.header = {.type = virtio_abi::ControlType::kTransfer2DResourceToHostCommand},
.geometry =
{
.placement_x = 0,
.placement_y = 0,
.width = width,
.height = height,
},
.destination_offset = 0,
.resource_id = resource_id,
};
const auto& response =
virtio_device_->ExchangeControlqRequestResponse<virtio_abi::EmptyResponse>(command);
if (response.header.type != virtio_abi::ControlType::kEmptyResponse) {
zxlogf(ERROR, "Unexpected response type: %s (0x%04" PRIx32 ")",
ControlTypeToString(response.header.type), static_cast<uint32_t>(response.header.type));
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
} // namespace virtio_display