blob: 0fac337401f7c0d5cdb0184ee2aaf411d5cad87e [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/gpu-device-driver.h"
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/driver.h>
#include <lib/virtio/driver_utils.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <cstring>
#include <memory>
#include <utility>
#include <ddktl/device.h>
#include <ddktl/init-txn.h>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/virtio-guest/v1/display-controller-banjo.h"
#include "src/graphics/display/drivers/virtio-guest/v1/display-coordinator-events-banjo.h"
#include "src/graphics/display/drivers/virtio-guest/v1/display-engine.h"
namespace virtio_display {
// static
zx_status_t GpuDeviceDriver::Create(zx_device_t* parent) {
fbl::AllocChecker alloc_checker;
auto coordinator_events = fbl::make_unique_checked<DisplayCoordinatorEventsBanjo>(&alloc_checker);
if (!alloc_checker.check()) {
zxlogf(ERROR, "Failed to allocate memory for DisplayCoordinatorEventsBanjo");
return ZX_ERR_NO_MEMORY;
}
zx::result<std::unique_ptr<DisplayEngine>> display_engine_result =
DisplayEngine::Create(parent, coordinator_events.get());
if (display_engine_result.is_error()) {
// DisplayEngine::Create() logs on error.
return display_engine_result.error_value();
}
auto driver = fbl::make_unique_checked<GpuDeviceDriver>(&alloc_checker, parent,
std::move(coordinator_events),
std::move(display_engine_result).value());
if (!alloc_checker.check()) {
zxlogf(ERROR, "Failed to allocate memory for GpuDeviceDriver");
return ZX_ERR_NO_MEMORY;
}
zx::result<> init_result = driver->Init();
if (init_result.is_error()) {
// DisplayEngine::Init() logs on error.
return init_result.error_value();
}
// devmgr now owns the memory for `device`.
[[maybe_unused]] auto device_ptr = driver.release();
return ZX_OK;
}
GpuDeviceDriver::GpuDeviceDriver(zx_device_t* bus_device,
std::unique_ptr<DisplayCoordinatorEventsBanjo> coordinator_events,
std::unique_ptr<DisplayEngine> display_engine)
: DdkDeviceType(bus_device),
coordinator_events_(std::move(coordinator_events)),
display_engine_(std::move(display_engine)),
display_controller_banjo_(display_engine_.get(), coordinator_events_.get()) {
ZX_DEBUG_ASSERT(coordinator_events_);
ZX_DEBUG_ASSERT(display_engine_);
}
GpuDeviceDriver::~GpuDeviceDriver() {
if (start_thread_.joinable()) {
start_thread_.join();
}
}
zx::result<> GpuDeviceDriver::Init() {
zx_status_t status = DdkAdd(
ddk::DeviceAddArgs("virtio-gpu-display").set_proto_id(ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL));
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to add device node: %s", zx_status_get_string(status));
return zx::error(status);
}
gpu_control_server_ = std::make_unique<GpuControlServer>(
this, display_engine_->pci_device().GetCapabilitySetLimit());
zx::result<> result = gpu_control_server_->Init(parent());
if (result.is_error()) {
zxlogf(ERROR, "Failed to init virtio gpu server: %s", result.status_string());
return zx::error(result.status_value());
}
zxlogf(TRACE, "GpuDeviceDriver::Init success");
return zx::ok();
}
zx_status_t GpuDeviceDriver::DdkGetProtocol(uint32_t proto_id, void* out) {
return display_controller_banjo_.DdkGetProtocol(proto_id, out);
}
void GpuDeviceDriver::DdkInit(ddk::InitTxn txn) {
start_thread_ = std::thread([this, init_txn = std::move(txn)]() mutable {
zx_status_t status = display_engine_->Start();
init_txn.Reply(status);
});
}
void GpuDeviceDriver::DdkRelease() { delete this; }
void GpuDeviceDriver::SendHardwareCommand(cpp20::span<uint8_t> request,
std::function<void(cpp20::span<uint8_t>)> callback) {
display_engine_->pci_device().ExchangeControlqVariableLengthRequestResponse(std::move(request),
std::move(callback));
}
namespace {
constexpr zx_driver_ops_t kDriverOps = {
.version = DRIVER_OPS_VERSION,
.bind = [](void* ctx, zx_device_t* parent) { return GpuDeviceDriver::Create(parent); },
};
} // namespace
} // namespace virtio_display
ZIRCON_DRIVER(virtio_gpu, virtio_display::kDriverOps, "zircon", "0.1");