blob: 288fec03d44c340f35a9d17ff1aa549ad3ca2fe4 [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/aml-canvas/aml-canvas-driver.h"
#include <fidl/fuchsia.driver.framework/cpp/wire.h>
#include <fidl/fuchsia.hardware.platform.device/cpp/wire.h>
#include <lib/driver/compat/cpp/logging.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/mmio/mmio-buffer.h>
#include <lib/zx/bti.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <cinttypes>
#include <cstdint>
#include <memory>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/aml-canvas/aml-canvas.h"
#include "src/graphics/display/drivers/aml-canvas/board-resources.h"
namespace aml_canvas {
AmlCanvasDriver::AmlCanvasDriver(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase("aml-canvas", std::move(start_args), std::move(driver_dispatcher)) {}
void AmlCanvasDriver::Stop() {
fidl::OneWayStatus result = controller_->Remove();
if (!result.ok()) {
FDF_LOG(ERROR, "Failed to remove the Node: %s", result.status_string());
}
}
zx::result<std::unique_ptr<inspect::ComponentInspector>>
AmlCanvasDriver::CreateComponentInspector() {
zx::result<fidl::ClientEnd<fuchsia_inspect::InspectSink>> inspect_sink_connect_result =
incoming()->Connect<fuchsia_inspect::InspectSink>();
if (inspect_sink_connect_result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to InspectSink protocol: %s",
inspect_sink_connect_result.status_string());
return inspect_sink_connect_result.take_error();
}
fbl::AllocChecker alloc_checker;
auto component_inspector = fbl::make_unique_checked<inspect::ComponentInspector>(
&alloc_checker, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
inspect::PublishOptions{.inspector = inspect::Inspector{},
.client_end = std::move(inspect_sink_connect_result).value()});
if (!alloc_checker.check()) {
FDF_LOG(ERROR, "Failed to allocate memory for ComponentInspector");
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok(std::move(component_inspector));
}
zx::result<std::unique_ptr<AmlCanvas>> AmlCanvasDriver::CreateAndServeCanvas(
inspect::Inspector inspector) {
zx::result<fidl::ClientEnd<fuchsia_hardware_platform_device::Device>> pdev_result =
incoming()->Connect<fuchsia_hardware_platform_device::Service::Device>();
if (pdev_result.is_error()) {
FDF_LOG(ERROR, "Failed to connect to platform device: %s", pdev_result.status_string());
}
fidl::ClientEnd pdev(std::move(pdev_result).value());
ZX_DEBUG_ASSERT(pdev.is_valid());
zx::result<zx::bti> bti_result = GetBti(BtiResourceIndex::kCanvas, pdev);
if (bti_result.is_error()) {
FDF_LOG(ERROR, "Failed to get BTI from the platform device: %s", bti_result.status_string());
return bti_result.take_error();
}
zx::bti bti = std::move(bti_result).value();
zx::result<fdf::MmioBuffer> mmio_result = MapMmio(MmioResourceIndex::kDmc, pdev);
if (mmio_result.is_error()) {
FDF_LOG(ERROR, "Failed to map MMIO from the platform device: %s", mmio_result.status_string());
return mmio_result.take_error();
}
fdf::MmioBuffer mmio = std::move(mmio_result).value();
fbl::AllocChecker alloc_checker;
auto canvas = fbl::make_unique_checked<AmlCanvas>(&alloc_checker, std::move(mmio), std::move(bti),
std::move(inspector));
if (!alloc_checker.check()) {
FDF_LOG(ERROR, "Failed to allocate AmlCanvas");
return zx::error(ZX_ERR_NO_MEMORY);
}
zx_status_t status = canvas->ServeOutgoing(outgoing());
if (status != ZX_OK) {
FDF_LOG(ERROR, "Failed to serve to outgoing directory: %s", zx_status_get_string(status));
return zx::error(status);
}
return zx::ok(std::move(canvas));
}
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>> AmlCanvasDriver::AddChildNode(
compat::SyncInitializedDeviceServer* compat_server) {
fidl::Arena arena;
std::vector<fuchsia_driver_framework::wire::Offer> offers;
if (compat_server != nullptr) {
offers = compat_server->CreateOffers2(arena);
}
offers.push_back(
fdf::MakeOffer2<fuchsia_hardware_amlogiccanvas::Service>(arena, component::kDefaultInstance));
auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
.name(arena, name())
.offers2(arena, std::move(offers))
.Build();
zx::result<fidl::Endpoints<fuchsia_driver_framework::NodeController>>
controller_endpoints_result =
fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
if (controller_endpoints_result.is_error()) {
FDF_LOG(ERROR, "Failed to create endpoints: %s", controller_endpoints_result.status_string());
return controller_endpoints_result.take_error();
}
auto [controller_client, controller_server] = std::move(controller_endpoints_result).value();
fidl::WireResult<fuchsia_driver_framework::Node::AddChild> add_child_result =
fidl::WireCall(node())->AddChild(std::move(args), std::move(controller_server), {});
if (!add_child_result.ok()) {
FDF_LOG(ERROR, "Failed to call FIDL AddChild: %s", add_child_result.status_string());
return zx::error(add_child_result.status());
}
if (add_child_result->is_error()) {
fuchsia_driver_framework::NodeError error = add_child_result->error_value();
FDF_LOG(ERROR, "Failed to AddChild: %" PRIu32, static_cast<uint32_t>(error));
return zx::error(ZX_ERR_INTERNAL);
}
return zx::ok(std::move(controller_client));
}
zx::result<> AmlCanvasDriver::Start() {
zx::result<std::unique_ptr<inspect::ComponentInspector>> inspector_result =
CreateComponentInspector();
if (inspector_result.is_error()) {
FDF_LOG(ERROR, "Failed to create component inspector: %s", inspector_result.status_string());
return inspector_result.take_error();
}
component_inspector_ = std::move(inspector_result).value();
zx::result<> compat_server_init_result =
compat_server_.Initialize(incoming(), outgoing(), node_name(), name());
if (compat_server_init_result.is_error()) {
return compat_server_init_result.take_error();
}
auto canvas_result = CreateAndServeCanvas(component_inspector_->inspector());
if (canvas_result.is_error()) {
FDF_LOG(ERROR, "Failed to create AmlCanvas and set up service: %s",
canvas_result.status_string());
return canvas_result.take_error();
}
canvas_ = std::move(canvas_result).value();
zx::result<fidl::ClientEnd<fuchsia_driver_framework::NodeController>> controller_client_result =
AddChildNode(&compat_server_);
if (controller_client_result.is_error()) {
FDF_LOG(ERROR, "Failed to add child node: %s", controller_client_result.status_string());
return controller_client_result.take_error();
}
controller_ = fidl::WireSyncClient(std::move(controller_client_result).value());
return zx::ok();
}
} // namespace aml_canvas
FUCHSIA_DRIVER_EXPORT(aml_canvas::AmlCanvasDriver);