blob: c5382a71ce0a38f6e588d6d1c689ee14d0046fdd [file] [log] [blame]
// Copyright 2018 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 "device_ctx.h"
#include <fidl/fuchsia.hardware.mediacodec/cpp/wire.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/ddk/driver.h>
#include <lib/sync/completion.h>
#include <lib/zx/profile.h>
#include <lib/zx/time.h>
#include <zircon/threads.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include "amlogic-video.h"
#include "macros.h"
#include "sdk/lib/sys/cpp/service_directory.h"
namespace amlogic_decoder {
namespace {
const char* GetRoleName(ThreadRole role) {
switch (role) {
case ThreadRole::kSharedFidl:
return "fuchsia.media.drivers.amlogic-decoder.fidl";
case ThreadRole::kParserIrq:
return "fuchsia.media.drivers.amlogic-decoder.parser-irq";
case ThreadRole::kVdec0Irq:
case ThreadRole::kVdec1Irq:
return "fuchsia.media.drivers.amlogic-decoder.vdec-irq";
case ThreadRole::kH264MultiCore:
return "fuchsia.media.drivers.amlogic-decoder.h264-core";
case ThreadRole::kH264MultiStreamControl:
return "fuchsia.media.drivers.amlogic-decoder.h264-stream-control";
case ThreadRole::kH264MultiResource:
return "fuchsia.media.drivers.amlogic-decoder.h264-resource";
case ThreadRole::kVp9InputProcessing:
return "fuchsia.media.drivers.amlogic-decoder.vp9-input-processing";
case ThreadRole::kVp9StreamControl:
return "fuchsia.media.drivers.amlogic-decoder.vp9-stream-control";
}
}
} // namespace
DeviceCtx::DeviceCtx(DriverCtx* driver, zx_device_t* parent)
: DdkDeviceType(parent),
driver_(driver),
codec_admission_control_(driver->shared_fidl_loop()->dispatcher()) {
video_ = std::make_unique<AmlogicVideo>(this);
video_->SetMetrics(&metrics());
device_fidl_ = std::make_unique<DeviceFidl>(this);
}
DeviceCtx::~DeviceCtx() = default;
void DeviceCtx::TeardownDeviceFidl() {
if (!device_fidl_) {
return;
}
// There are two ways to destroy a fidl::Binding safely:
// * Switch to FIDL thread before Unbind() or ~Binding.
// * async::Loop Quit() + JoinThreads() before Unbind() or ~Binding
//
// For now this code (if implementation needed) will choose the first option
// by destructing DeviceFidl on the FIDL thread. The current way forces this
// thread to wait in this destructor until the shared_fidl_thread() is done
// processing ~DeviceFidl, which means we require that ~DeviceCtx is not
// itself running on the shared_fidl_thread() (or we could check which thread
// here, if we really need to).
//
// This code is only run when we switch between test and production drivers.
sync_completion_t completion;
driver_->PostToSharedFidl([this, &completion]() {
device_fidl_ = nullptr;
sync_completion_signal(&completion);
});
sync_completion_wait(&completion, ZX_TIME_INFINITE);
}
zx_status_t DeviceCtx::Bind() {
SetThreadProfile(zx::unowned_thread(thrd_get_zx_handle(driver_->shared_fidl_thread())),
ThreadRole::kSharedFidl);
zx_status_t status = DdkAdd(
ddk::DeviceAddArgs("amlogic_video").set_inspect_vmo(driver_->diagnostics().DuplicateVmo()));
zxlogf(INFO, "amlogic-video finished initialization with status %d", status);
diagnostics().SetBindTime();
return status;
}
void DeviceCtx::DdkSuspend(ddk::SuspendTxn txn) {
if (txn.enable_wake()) {
zxlogf(ERROR, "Suspending with wake enabled not supported.");
txn.Reply(ZX_ERR_NOT_SUPPORTED, DEV_POWER_STATE_D0);
return;
}
// Tear down everything to ensure that no driver threads are active by the time blobfs is
// unmounted, since attempting to page in code from blobfs after that point could fail. The driver
// framework shouldn't call into the driver after suspend has completed.
TeardownDeviceFidl();
video_.reset();
driver_->Suspend();
txn.Reply(ZX_OK, txn.requested_state());
}
void DeviceCtx::DdkUnbind(ddk::UnbindTxn txn) {
TeardownDeviceFidl();
txn.Reply();
}
void DeviceCtx::SetThreadProfile(zx::unowned_thread thread, ThreadRole role) const {
const char* role_name = GetRoleName(role);
const size_t role_size = strlen(role_name);
const zx_status_t status =
device_set_profile_by_role(parent(), thread->get(), role_name, role_size);
if (status != ZX_OK) {
LOG(WARNING, "Unable to set thread to role %s: %s", role_name, zx_status_get_string(status));
}
}
void DeviceCtx::GetCodecFactory(GetCodecFactoryRequestView request,
GetCodecFactoryCompleter::Sync& completer) {
device_fidl()->ConnectChannelBoundCodecFactory(std::move(request->request));
}
void DeviceCtx::SetAuxServiceDirectory(SetAuxServiceDirectoryRequestView request,
SetAuxServiceDirectoryCompleter::Sync& completer) {
driver_->SetAuxServiceDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory>(request->service_directory.TakeChannel()));
}
CodecMetrics& DeviceCtx::metrics() { return driver_->metrics(); }
CodecDiagnostics& DeviceCtx::diagnostics() { return driver_->diagnostics(); }
} // namespace amlogic_decoder