blob: 500bb57e37b7b40e072408ee4585f711e04575ab [file]
// Copyright 2019 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.
#ifndef SRC_MEDIA_DRIVERS_AMLOGIC_ENCODER_DEVICE_CTX_H_
#define SRC_MEDIA_DRIVERS_AMLOGIC_ENCODER_DEVICE_CTX_H_
#include <fuchsia/hardware/mediacodec/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/device-protocol/pdev.h>
#include <lib/device-protocol/platform-device.h>
#include <lib/fidl-utils/bind.h>
#include <lib/sync/completion.h>
#include <zircon/fidl.h>
#include <zircon/types.h>
#include <memory>
#include <thread>
#include <unordered_map>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/amlogiccanvas.h>
#include <ddk/protocol/platform/device.h>
#include <ddk/protocol/sysmem.h>
#include <ddktl/device.h>
#include <ddktl/protocol/empty-protocol.h>
#include "ddktl/protocol/amlogiccanvas.h"
#include "ddktl/protocol/platform/device.h"
#include "ddktl/protocol/sysmem.h"
#include "fuchsia/media/cpp/fidl.h"
#include "lib/mmio/mmio.h"
#include "lib/zx/interrupt.h"
#include "src/media/drivers/amlogic_encoder/internal_buffer.h"
#include "src/media/drivers/amlogic_encoder/local_codec_factory.h"
#include "src/media/drivers/amlogic_encoder/registers.h"
enum class SocType {
kUnknown,
// These should be ordered from oldest to newest.
kG12A = 2, // S905D2
kG12B = 3, // T931
};
// Returns true if |a| is newer than or the same as |b|.
inline bool IsDeviceAtLeast(SocType a, SocType b) {
return static_cast<int>(a) >= static_cast<int>(b);
}
class DeviceCtx;
using DeviceType = ddk::Device<DeviceCtx, ddk::UnbindableNew, ddk::Messageable>;
// Manages context for the device's lifetime.
class DeviceCtx : public DeviceType, public ddk::EmptyProtocol<ZX_PROTOCOL_MEDIA_CODEC> {
public:
// Creates and binds an instance of `DeviceCtx`. If successful, returns ZX_OK
// and a bound instance of `DeviceCtx`, otherwise an error status and
// `nullptr`.
static std::unique_ptr<DeviceCtx> Create(zx_device_t* parent);
explicit DeviceCtx(zx_device_t* parent, ddk::PDevProtocolClient pdev,
ddk::AmlogicCanvasProtocolClient canvas, ddk::SysmemProtocolClient sysmem,
CbusRegisterIo cbus, DosRegisterIo dosbus, AoRegisterIo aobus,
HiuRegisterIo hiubus, zx::interrupt interrupt, zx::bti bti)
: DeviceType(parent),
pdev_(pdev),
canvas_(canvas),
sysmem_(sysmem),
cbus_(std::move(cbus)),
dosbus_(std::move(dosbus)),
aobus_(std::move(aobus)),
hiubus_(std::move(hiubus)),
bti_(std::move(bti)),
interrupt_handle_(std::move(interrupt)),
loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
codec_admission_control_(std::make_unique<CodecAdmissionControl>(loop_.dispatcher())) {}
DeviceCtx(const DeviceCtx&) = delete;
DeviceCtx& operator=(const DeviceCtx&) = delete;
~DeviceCtx() { ShutDown(); }
// init and add device
zx_status_t Bind();
// media codec FIDL implementation
void GetCodecFactory(zx::channel request);
// Methods required by the ddk.
void DdkRelease();
void DdkUnbindNew(ddk::UnbindTxn txn);
zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
zx::unowned_bti bti() { return zx::unowned_bti(bti_); }
// This gets started connecting to sysmem, but returns an InterfaceHandle
// instead of InterfacePtr so that the caller can bind to the dispatcher.
fidl::InterfaceHandle<fuchsia::sysmem::Allocator> ConnectToSysmem();
// encoder control, runs on input processing thread
zx_status_t EncoderInit(const fuchsia::media::FormatDetails& format_details);
zx_status_t StartEncoder();
zx_status_t StopEncoder();
zx_status_t WaitForIdle();
zx_status_t EncodeFrame(const CodecBuffer* buffer, uint8_t* data, uint32_t len);
void ReturnBuffer(const CodecBuffer* buffer);
void SetOutputBuffers(std::vector<const CodecBuffer*> buffers);
// TODO(afoxley), this should likely take some encoder specific info struct. The intention is to
// support quality adjustments on the fly.
void SetEncodeParams(fuchsia::media::FormatDetails format_details);
private:
void ShutDown();
zx_status_t EnsureHwInited();
zx_status_t PowerOn();
void InterruptInit();
zx_status_t BufferAlloc();
zx_status_t CanvasInit();
zx_status_t LoadFirmware();
void Reset();
// configures hcodec block with frame and pic count info.
void Config();
ddk::PDevProtocolClient pdev_;
ddk::AmlogicCanvasProtocolClient canvas_;
ddk::SysmemProtocolClient sysmem_;
SocType soc_type_ = SocType::kUnknown;
CbusRegisterIo cbus_;
DosRegisterIo dosbus_;
AoRegisterIo aobus_;
HiuRegisterIo hiubus_;
zx::bti bti_;
zx::vmo firmware_vmo_;
uintptr_t firmware_ptr_ = 0;
uint64_t firmware_size_ = 0;
EncoderStatus hw_status_;
zx::interrupt interrupt_handle_;
std::thread interrupt_thread_;
sync_completion_t interrupt_sync_;
fuchsia::sysmem::AllocatorSyncPtr sysmem_sync_ptr_;
// working buffers for encoder
std::optional<InternalBuffer> dec0_;
std::optional<InternalBuffer> dec1_;
std::optional<InternalBuffer> assit_;
std::optional<InternalBuffer> scale_buff_;
std::optional<InternalBuffer> dump_info_;
std::optional<InternalBuffer> cbr_info_;
async::Loop loop_;
thrd_t loop_thread_;
// FIDL interfaces
std::unordered_map<LocalCodecFactory*, std::unique_ptr<LocalCodecFactory>> codec_factories_;
std::unique_ptr<CodecAdmissionControl> codec_admission_control_;
std::unique_ptr<CodecImpl> codec_instance_;
// Picture info
uint32_t encoder_width_;
uint32_t encoder_height_;
uint32_t rows_per_slice_;
// reset to 0 for IDR
uint32_t idr_pic_id_ = 0;
// increment each frame
uint32_t frame_number_ = 0;
// reset to 0 for IDR and imcrement by 2 for NON-IDR
uint32_t pic_order_cnt_lsb_ = 0;
};
#endif // SRC_MEDIA_DRIVERS_AMLOGIC_ENCODER_DEVICE_CTX_H_