| // 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 |