blob: 1b436cd64d496900403fbba0cf1dfd0cbe9f151a [file] [log] [blame]
// Copyright 2023 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/amlogic-display/capture.h"
#include <fidl/fuchsia.hardware.platform.device/cpp/wire.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/result.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/threads.h>
#include <memory>
#include <utility>
#include <fbl/alloc_checker.h>
#include "src/graphics/display/drivers/amlogic-display/board-resources.h"
#include "src/graphics/display/lib/driver-framework-migration-utils/dispatcher/dispatcher-factory.h"
#include "src/graphics/display/lib/driver-framework-migration-utils/logging/zxlogf.h"
namespace amlogic_display {
// static
zx::result<std::unique_ptr<Capture>> Capture::Create(
display::DispatcherFactory& dispatcher_factory,
fidl::UnownedClientEnd<fuchsia_hardware_platform_device::Device> platform_device,
OnCaptureCompleteHandler on_capture_complete) {
ZX_DEBUG_ASSERT(platform_device.is_valid());
zx::result<zx::interrupt> capture_interrupt_result =
GetInterrupt(InterruptResourceIndex::kVid1Write, platform_device);
if (capture_interrupt_result.is_error()) {
return capture_interrupt_result.take_error();
}
zx::result<std::unique_ptr<display::Dispatcher>> create_dispatcher_result =
dispatcher_factory.Create("capture-interrupt-thread", /*scheduler_role=*/{});
if (create_dispatcher_result.is_error()) {
zxlogf(ERROR, "Failed to create capture interrupt handler dispatcher: %s",
create_dispatcher_result.status_string());
return create_dispatcher_result.take_error();
}
std::unique_ptr<display::Dispatcher> dispatcher = std::move(create_dispatcher_result).value();
fbl::AllocChecker alloc_checker;
auto capture =
fbl::make_unique_checked<Capture>(&alloc_checker, std::move(capture_interrupt_result).value(),
std::move(on_capture_complete), std::move(dispatcher));
if (!alloc_checker.check()) {
zxlogf(ERROR, "Out of memory while allocating Capture");
return zx::error(ZX_ERR_NO_MEMORY);
}
zx::result<> init_result = capture->Init();
if (init_result.is_error()) {
zxlogf(ERROR, "Failed to initalize Capture: %s", init_result.status_string());
return init_result.take_error();
}
return zx::ok(std::move(capture));
}
Capture::Capture(zx::interrupt capture_finished_interrupt,
OnCaptureCompleteHandler on_capture_complete,
std::unique_ptr<display::Dispatcher> irq_handler_dispatcher)
: capture_finished_irq_(std::move(capture_finished_interrupt)),
on_capture_complete_(std::move(on_capture_complete)),
irq_handler_dispatcher_(std::move(irq_handler_dispatcher)) {
irq_handler_.set_object(capture_finished_irq_.get());
}
Capture::~Capture() {
// In order to shut down the interrupt handler and join the thread, the
// interrupt must be destroyed first.
if (capture_finished_irq_.is_valid()) {
zx_status_t status = capture_finished_irq_.destroy();
if (status != ZX_OK) {
zxlogf(ERROR, "Capture done interrupt destroy failed: %s", zx_status_get_string(status));
}
}
irq_handler_dispatcher_.reset();
}
zx::result<> Capture::Init() {
zx_status_t status = irq_handler_.Begin(irq_handler_dispatcher_->async_dispatcher());
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to bind the capture interrupt handler to the async loop: %s",
zx_status_get_string(status));
return zx::error(status);
}
return zx::ok();
}
void Capture::InterruptHandler(async_dispatcher_t* dispatcher, async::IrqBase* irq,
zx_status_t status, const zx_packet_interrupt_t* interrupt) {
if (status == ZX_ERR_CANCELED) {
zxlogf(INFO, "Capture finished interrupt wait is cancelled.");
return;
}
if (status != ZX_OK) {
zxlogf(ERROR, "Capture finished interrupt wait failed: %s", zx_status_get_string(status));
// A failed async interrupt wait doesn't remove the interrupt from the
// async loop, so we have to manually cancel it.
irq->Cancel();
return;
}
OnCaptureComplete();
// For interrupts bound to ports (including those bound to async loops), the
// interrupt must be re-armed using zx_interrupt_ack() for each incoming
// interrupt request. This is best done after the interrupt has been fully
// processed.
zx::unowned_interrupt(irq->object())->ack();
}
void Capture::OnCaptureComplete() { on_capture_complete_(); }
} // namespace amlogic_display