| // 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. |
| |
| #ifndef SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_AMLOGIC_VIDEO_H_ |
| #define SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_AMLOGIC_VIDEO_H_ |
| |
| #include <fuchsia/tee/cpp/fidl.h> |
| #include <lib/device-protocol/pdev.h> |
| #include <lib/zx/handle.h> |
| #include <zircon/errors.h> |
| #include <zircon/syscalls.h> |
| |
| #include <future> |
| #include <list> |
| #include <memory> |
| #include <mutex> |
| #include <thread> |
| |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddktl/protocol/amlogiccanvas.h> |
| #include <ddktl/protocol/clock.h> |
| #include <ddktl/protocol/sysmem.h> |
| #include <ddktl/protocol/tee.h> |
| #include <tee-client-api/tee-client-types.h> |
| |
| #include "decoder_core.h" |
| #include "decoder_instance.h" |
| #include "device_ctx.h" |
| #include "firmware_blob.h" |
| #include "parser.h" |
| #include "registers.h" |
| #include "secmem_session.h" |
| #include "stream_buffer.h" |
| #include "video_decoder.h" |
| #include "watchdog.h" |
| |
| class AmlogicVideo final : public VideoDecoder::Owner, |
| public DecoderCore::Owner, |
| public CanvasEntry::Owner, |
| public Parser::Owner, |
| public Watchdog::Owner { |
| public: |
| AmlogicVideo(); |
| |
| ~AmlogicVideo(); |
| |
| void SetMetrics(CodecMetrics* metrics); |
| [[nodiscard]] zx_status_t InitRegisters(zx_device_t* parent); |
| [[nodiscard]] zx_status_t InitDecoder(); |
| |
| // VideoDecoder::Owner implementation. |
| [[nodiscard]] CodecMetrics& metrics() override { |
| ZX_DEBUG_ASSERT(metrics_); |
| return *metrics_; |
| } |
| [[nodiscard]] DosRegisterIo* dosbus() override { return &*dosbus_; } |
| [[nodiscard]] zx::unowned_bti bti() override { return zx::unowned_bti(bti_); } |
| [[nodiscard]] DeviceType device_type() override { return device_type_; } |
| [[nodiscard]] FirmwareBlob* firmware_blob() override { return firmware_.get(); } |
| [[nodiscard]] bool is_tee_available() override { return is_tee_available_; } |
| [[nodiscard]] zx_status_t TeeSmcLoadVideoFirmware( |
| FirmwareBlob::FirmwareType index, FirmwareBlob::FirmwareVdecLoadMode vdec) override; |
| [[nodiscard]] zx_status_t TeeVp9AddHeaders(zx_paddr_t page_phys_base, uint32_t before_size, |
| uint32_t max_after_size, |
| uint32_t* after_size) override; |
| [[nodiscard]] std::unique_ptr<CanvasEntry> ConfigureCanvas(io_buffer_t* io_buffer, |
| uint32_t offset, uint32_t width, |
| uint32_t height, uint32_t wrap, |
| uint32_t blockmode) override; |
| |
| [[nodiscard]] DecoderCore* core() override { return core_; } |
| [[nodiscard]] zx_status_t AllocateIoBuffer(io_buffer_t* buffer, size_t size, |
| uint32_t alignment_log2, uint32_t flags, |
| const char* name) override; |
| [[nodiscard]] fuchsia::sysmem::AllocatorSyncPtr& SysmemAllocatorSyncPtr() override; |
| |
| [[nodiscard]] bool IsDecoderCurrent(VideoDecoder* decoder) override { |
| AssertVideoDecoderLockHeld(); |
| assert(decoder); |
| return decoder == video_decoder_; |
| } |
| [[nodiscard]] zx_status_t SetProtected(ProtectableHardwareUnit unit, bool protect) override; |
| // This tries to schedule the next runnable decoder. It may leave the current |
| // decoder scheduled if no other decoder is runnable. |
| void TryToReschedule() override __TA_REQUIRES(video_decoder_lock_); |
| [[nodiscard]] Watchdog* watchdog() override __TA_REQUIRES(video_decoder_lock_) { |
| return &watchdog_; |
| } |
| |
| // DecoderCore::Owner implementation. |
| [[nodiscard]] MmioRegisters* mmio() override { return registers_.get(); } |
| void UngateClocks() override; |
| void GateClocks() override; |
| void ToggleClock(ClockType type, bool enable) override; |
| |
| // CanvasEntry::Owner implementation. |
| void FreeCanvas(CanvasEntry* canvas) override; |
| |
| // Parser::Owner implementation. |
| [[nodiscard]] bool is_parser_gated() const override { return is_parser_gated_; } |
| |
| // Watchdog::Owner implementation. |
| void OnSignaledWatchdog() override; |
| |
| // The pts manager has its own locking, so don't worry about the video decoder |
| // lock. |
| [[nodiscard]] PtsManager* pts_manager() __TA_NO_THREAD_SAFETY_ANALYSIS { |
| ZX_DEBUG_ASSERT(video_decoder_); |
| return video_decoder_->pts_manager(); |
| } |
| |
| // Reset the current instance - only for use with single-stream decoders. |
| void ClearDecoderInstance(); |
| |
| // Erase a specific decoder. May switch to a different decoder in multi-stream |
| // mode. This will stop and power off the core if the decoder is currently |
| // running. |
| void RemoveDecoder(VideoDecoder* decoder); |
| void RemoveDecoderLocked(VideoDecoder* decoder) __TA_REQUIRES(video_decoder_lock_); |
| |
| [[nodiscard]] zx_status_t InitializeStreamBuffer(bool use_parser, uint32_t size, bool is_secure); |
| [[nodiscard]] zx_status_t InitializeEsParser(); |
| |
| [[nodiscard]] Parser* parser() override { return parser_.get(); } |
| |
| void UngateParserClock(); |
| void GateParserClock(); |
| |
| [[nodiscard]] zx_status_t ProcessVideoNoParser(const void* data, uint32_t len, |
| uint32_t* written_out = nullptr) override; |
| |
| [[nodiscard]] uint32_t GetStreamBufferEmptySpaceAfterWriteOffsetBeforeReadOffset( |
| uint32_t write_offset, uint32_t read_offset) override; |
| |
| [[nodiscard]] uint32_t GetStreamBufferEmptySpaceAfterOffset(uint32_t write_offset); |
| |
| // Similar to GetStreamBufferEmptySpaceAfterOffset, but uses the current core write offset. |
| [[nodiscard]] uint32_t GetStreamBufferEmptySpace() override; |
| |
| [[nodiscard]] DecoderCore* hevc_core() const override { return hevc_core_.get(); } |
| [[nodiscard]] DecoderCore* vdec1_core() const override { return vdec1_core_.get(); } |
| |
| // Add the instance as a swapped-out decoder. |
| void AddNewDecoderInstance(std::unique_ptr<DecoderInstance> instance) |
| __TA_REQUIRES(video_decoder_lock_); |
| |
| // For single-instance decoders, set the default instance. |
| void SetDefaultInstance(std::unique_ptr<VideoDecoder> decoder, bool hevc) |
| __TA_REQUIRES(video_decoder_lock_); |
| [[nodiscard]] std::mutex* video_decoder_lock() __TA_RETURN_CAPABILITY(video_decoder_lock_) { |
| return &video_decoder_lock_; |
| } |
| [[nodiscard]] VideoDecoder* video_decoder() __TA_REQUIRES(video_decoder_lock_) { |
| return video_decoder_; |
| } |
| [[nodiscard]] DecoderInstance* current_instance() override __TA_REQUIRES(video_decoder_lock_) { |
| return current_instance_.get(); |
| } |
| |
| // This should be called only to mollify the lock detection in cases where |
| // it's guaranteed that the video decoder lock is already held. This can't |
| // actually be implemented on top of std::mutex. |
| void AssertVideoDecoderLockHeld() __TA_ASSERT(video_decoder_lock_) {} |
| |
| [[nodiscard]] zx_status_t AllocateStreamBuffer(StreamBuffer* buffer, uint32_t size, |
| bool use_parser, bool is_secure); |
| |
| // 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(); |
| |
| private: |
| friend class TestH264; |
| friend class TestMpeg2; |
| friend class TestVP9; |
| friend class TestFrameProvider; |
| |
| zx_status_t ConnectToTee(fuchsia::tee::DeviceSyncPtr* tee); |
| |
| zx_status_t EnsureSecmemSessionIsConnected(); |
| |
| void InitializeStreamInput(bool use_parser); |
| |
| [[nodiscard]] zx_status_t ProcessVideoNoParserAtOffset(const void* data, uint32_t len, |
| uint32_t current_offset, |
| uint32_t* written_out = nullptr); |
| |
| zx_status_t PreloadFirmwareViaTee(); |
| void InitializeInterrupts(); |
| void SwapOutCurrentInstance() __TA_REQUIRES(video_decoder_lock_); |
| void SwapInCurrentInstance() __TA_REQUIRES(video_decoder_lock_); |
| // Signals the current decoder that there's an error and tells it to power off. |
| void PowerOffForError() __TA_REQUIRES(video_decoder_lock_); |
| |
| zx_device_t* parent_ = nullptr; |
| ddk::PDev pdev_; |
| ddk::SysmemProtocolClient sysmem_; |
| ddk::AmlogicCanvasProtocolClient canvas_; |
| |
| ddk::ClockProtocolClient clocks_[static_cast<int>(ClockType::kMax)]; |
| |
| // Unlike sysmem and canvas, tee is optional (no tee on vim2). |
| ddk::TeeProtocolClient tee_; |
| bool is_tee_available_ = false; |
| std::optional<SecmemSession> secmem_session_; |
| |
| CodecMetrics default_nop_metrics_; |
| CodecMetrics* metrics_ = &default_nop_metrics_; |
| |
| DeviceType device_type_ = DeviceType::kUnknown; |
| zx::resource secure_monitor_; |
| std::optional<CbusRegisterIo> cbus_; |
| std::optional<DosRegisterIo> dosbus_; |
| std::optional<HiuRegisterIo> hiubus_; |
| std::optional<AoRegisterIo> aobus_; |
| std::optional<DmcRegisterIo> dmc_; |
| std::optional<ResetRegisterIo> reset_; |
| std::optional<DemuxRegisterIo> demux_; |
| std::optional<ParserRegisterIo> parser_regs_; |
| |
| std::unique_ptr<MmioRegisters> registers_; |
| |
| std::unique_ptr<FirmwareBlob> firmware_; |
| |
| // Private for use by AmlogicVideo, when creating InternalBuffer(s). Decoders |
| // can create their own separate InterfaceHandle<Allocator>(s) by calling |
| // ConnectToSysmem(). |
| fuchsia::sysmem::AllocatorSyncPtr sysmem_sync_ptr_; |
| |
| zx::bti bti_; |
| |
| zx::interrupt parser_interrupt_handle_; |
| zx::interrupt vdec0_interrupt_handle_; |
| zx::interrupt vdec1_interrupt_handle_; |
| |
| std::thread parser_interrupt_thread_; |
| std::thread vdec0_interrupt_thread_; |
| std::thread vdec1_interrupt_thread_; |
| |
| std::unique_ptr<DecoderCore> hevc_core_; |
| std::unique_ptr<DecoderCore> vdec1_core_; |
| |
| std::mutex video_decoder_lock_; |
| // This is the video decoder that's currently attached to the hardware. |
| __TA_GUARDED(video_decoder_lock_) |
| VideoDecoder* video_decoder_ = nullptr; |
| |
| // This is the stream buffer that's currently attached to the hardware. |
| StreamBuffer* stream_buffer_ = nullptr; |
| |
| // The decoder core for the currently-running decoder. It must be powered on. |
| DecoderCore* core_ = nullptr; |
| |
| std::unique_ptr<Parser> parser_; |
| bool is_parser_gated_ = true; |
| |
| __TA_GUARDED(video_decoder_lock_) |
| std::unique_ptr<DecoderInstance> current_instance_; |
| __TA_GUARDED(video_decoder_lock_) |
| std::list<std::unique_ptr<DecoderInstance>> swapped_out_instances_; |
| |
| // This is the watchdog for the video decoder core. It's owned by AmlogicVideo and not the video |
| // decoder because destroying it can (implicitly) block on the video_decoder_lock_, while decoder |
| // destruction happens with the video decoder lock held. |
| __TA_GUARDED(video_decoder_lock_) |
| Watchdog watchdog_{this}; |
| }; |
| |
| #endif // SRC_MEDIA_DRIVERS_AMLOGIC_DECODER_AMLOGIC_VIDEO_H_ |