| // 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 <lib/driver/mmio/testing/cpp/test-helper.h> |
| #include <lib/media/codec_impl/codec_diagnostics.h> |
| #include <lib/mmio-ptr/fake.h> |
| #include <lib/zx/vmo.h> |
| |
| #include <memory> |
| #include <string_view> |
| |
| #include <fbl/algorithm.h> |
| #include <gtest/gtest.h> |
| |
| #include "amlogic-video.h" |
| #include "tests/test_basic_client.h" |
| #include "tests/test_support.h" |
| #include "vp9_decoder.h" |
| |
| namespace amlogic_decoder { |
| namespace test { |
| namespace { |
| |
| template <typename U> |
| bool MmioMemcmp(const fdf::MmioBuffer& actual, const std::unique_ptr<U[]>& expected) { |
| auto* raw_ptr = reinterpret_cast<uint8_t*>(expected.get()); |
| for (size_t i = 0; i < actual.get_size(); i++) { |
| if (actual.Read8(i) != raw_ptr[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| class FakeVideoOwner : public AmlogicVideo::Owner { |
| public: |
| // AmlogicVideo::Owner implementation. |
| void SetThreadProfile(zx::unowned_thread thread, ThreadRole role) const override {} |
| }; |
| |
| class FakeDecoderCore : public DecoderCore { |
| public: |
| std::optional<InternalBuffer> LoadFirmwareToBuffer(const uint8_t* data, uint32_t len) override { |
| return {}; |
| } |
| zx_status_t LoadFirmware(const uint8_t* data, uint32_t len) override { return ZX_OK; } |
| zx_status_t LoadFirmware(InternalBuffer& buffer) override { return ZX_OK; } |
| zx_status_t PowerOn() override { |
| powered_on_ = true; |
| return ZX_OK; |
| } |
| zx_status_t PowerOff() override { |
| powered_on_ = false; |
| return ZX_OK; |
| } |
| void StartDecoding() override {} |
| void StopDecoding() override {} |
| void WaitForIdle() override {} |
| void InitializeStreamInput(bool use_parser, uint32_t buffer_address, |
| uint32_t buffer_size) override {} |
| void InitializeParserInput() override {} |
| void InitializeDirectInput() override {} |
| void UpdateWriteOffset(uint32_t write_offset) override {} |
| void UpdateWritePointer(uint32_t write_pointer) override {} |
| uint32_t GetStreamInputOffset() override { return 0; } |
| uint32_t GetReadOffset() override { return 0; } |
| |
| bool powered_on_ = false; |
| }; |
| |
| class FakeOwner : public VideoDecoder::Owner { |
| public: |
| FakeOwner(DosRegisterIo* dosbus, AmlogicVideo* video) |
| : device_type_(DeviceType::kGXM), dosbus_(dosbus), video_(video), blob_(device_type_) { |
| blob_.LoadFakeFirmwareForTesting(FirmwareBlob::FirmwareType::kDec_Vp9_Mmu, nullptr, 0); |
| } |
| |
| CodecMetrics& metrics() override { return default_nop_metrics_; } |
| DosRegisterIo* dosbus() override { return dosbus_; } |
| zx::unowned_bti bti() override { return video_->bti(); } |
| DeviceType device_type() override { return device_type_; } |
| FirmwareBlob* firmware_blob() override { return &blob_; } |
| bool is_tee_available() override { return false; } |
| zx_status_t TeeSmcLoadVideoFirmware(FirmwareBlob::FirmwareType index, |
| FirmwareBlob::FirmwareVdecLoadMode vdec) override { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| [[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 { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| std::unique_ptr<CanvasEntry> ConfigureCanvas( |
| io_buffer_t* io_buffer, uint32_t offset, uint32_t width, uint32_t height, |
| fuchsia_hardware_amlogiccanvas::CanvasFlags flags, |
| fuchsia_hardware_amlogiccanvas::CanvasBlockMode blockmode) override { |
| return nullptr; |
| } |
| DecoderCore* core() override { return &core_; } |
| DecoderCore* vdec1_core() const override { return nullptr; } |
| DecoderCore* hevc_core() const override { return &core_; } |
| zx_status_t AllocateContiguousIoBuffer(io_buffer_t* buffer, size_t size, uint32_t alignment_log2, |
| uint32_t flags_param, const char* name) override { |
| // We add IO_BUFFER_CONTIG per semantics of AllocateContiguousIoBuffer, but see below re. not |
| // actually allocating a contiguous VMO for these tests (this would still be true if we used |
| // fake-bti lib). |
| uint32_t flags = flags_param | IO_BUFFER_CONTIG; |
| |
| // The below impl doesn't work for alignments > zx_system_get_page_size(), because size isn't |
| // increased to give room to align up. We also don't use AllocateContiguousSysmemVmo here |
| // because these unit tests don't have a real zx::bti. |
| ZX_ASSERT(alignment_log2 <= static_cast<uint32_t>(__builtin_ctzl(zx_system_get_page_size()))); |
| |
| zx_status_t status = io_buffer_init(buffer, ZX_HANDLE_INVALID, size, flags & ~IO_BUFFER_CONTIG); |
| if (status != ZX_OK) |
| return status; |
| if (flags & IO_BUFFER_CONTIG) { |
| if (alignment_log2 == 0) |
| alignment_log2 = 12; |
| phys_map_start_ = fbl::round_up(phys_map_start_, 1ul << alignment_log2); |
| buffer->phys = phys_map_start_; |
| phys_map_start_ += size; |
| } |
| return ZX_OK; |
| } |
| fit::result<fit::failed, zx::vmo> AllocateContiguousSysmemVmo( |
| size_t size, uint32_t alignment_log2, std::string_view debug_name) override { |
| ZX_ASSERT(alignment_log2 <= static_cast<uint32_t>(__builtin_ctzl(zx_system_get_page_size()))); |
| zx::vmo result; |
| zx::vmo::create(size, 0, &result); |
| return fit::ok(std::move(result)); |
| } |
| fidl::SyncClient<fuchsia_sysmem2::Allocator>& SysmemAllocatorSync() override { |
| return video_->SysmemAllocatorSync(); |
| } |
| |
| bool IsDecoderCurrent(VideoDecoder* decoder) override { return true; } |
| zx_status_t SetProtected(ProtectableHardwareUnit unit, bool protect) override { |
| have_set_protected_ = true; |
| return ZX_OK; |
| } |
| void TryToReschedule() override { EXPECT_TRUE(false); } |
| Watchdog* watchdog() override { |
| std::lock_guard<std::mutex> lock(*video_->video_decoder_lock()); |
| return video_->watchdog(); |
| } |
| |
| bool have_set_protected() const { return have_set_protected_; } |
| |
| private: |
| DeviceType device_type_; |
| DosRegisterIo* dosbus_; |
| AmlogicVideo* video_; |
| mutable FakeDecoderCore core_; |
| uint64_t phys_map_start_ = 0x1000; |
| FirmwareBlob blob_; |
| bool have_set_protected_ = false; |
| CodecMetrics default_nop_metrics_; |
| }; |
| |
| constexpr uint32_t kDosbusMemorySize = 0x10000; |
| } // namespace |
| |
| class Vp9UnitTest { |
| public: |
| static void LoopFilter() { |
| FakeVideoOwner video_owner; |
| auto video = std::make_unique<AmlogicVideo>(&video_owner); |
| ASSERT_TRUE(video); |
| video->SetDeviceType(DeviceType::kSM1); |
| EXPECT_EQ(ZX_OK, video->InitRegisters(TestSupport::parent_device())); |
| |
| DosRegisterIo dosbus(fdf_testing::CreateMmioBuffer(kDosbusMemorySize)); |
| FakeOwner fake_owner(&dosbus, video.get()); |
| TestBasicClient client; |
| auto decoder = std::make_unique<Vp9Decoder>( |
| &fake_owner, &client, Vp9Decoder::InputType::kSingleStream, std::nullopt, false, false); |
| decoder->InitLoopFilter(); |
| // This should be the 32nd value written to this register. |
| EXPECT_EQ(0x3fc13ebeu, HevcDblkCfg9::Get().ReadFrom(fake_owner.dosbus()).reg_value()); |
| } |
| |
| static void InitializeMemory(bool use_compressed_output) { |
| FakeVideoOwner video_owner; |
| auto video = std::make_unique<AmlogicVideo>(&video_owner); |
| ASSERT_TRUE(video); |
| video->SetDeviceType(DeviceType::kSM1); |
| EXPECT_EQ(ZX_OK, video->InitRegisters(TestSupport::parent_device())); |
| |
| auto zeroed_memory = std::unique_ptr<uint32_t[]>(new uint32_t[kDosbusMemorySize]); |
| memset(zeroed_memory.get(), 0, kDosbusMemorySize); |
| DosRegisterIo dosbus(fdf_testing::CreateMmioBuffer(kDosbusMemorySize)); |
| FakeOwner fake_owner(&dosbus, video.get()); |
| TestBasicClient client; |
| auto decoder = |
| std::make_unique<Vp9Decoder>(&fake_owner, &client, Vp9Decoder::InputType::kSingleStream, |
| std::nullopt, use_compressed_output, false); |
| EXPECT_EQ(ZX_OK, decoder->InitializeBuffers()); |
| EXPECT_EQ(0, MmioMemcmp(dosbus, zeroed_memory)); |
| EXPECT_FALSE(fake_owner.have_set_protected()); |
| EXPECT_TRUE(static_cast<FakeDecoderCore*>(fake_owner.hevc_core())->powered_on_); |
| |
| EXPECT_EQ(ZX_OK, decoder->InitializeHardware()); |
| EXPECT_NE(0, MmioMemcmp(dosbus, zeroed_memory)); |
| EXPECT_TRUE(fake_owner.have_set_protected()); |
| auto dosbus_memory_copy = std::unique_ptr<uint32_t[]>(new uint32_t[kDosbusMemorySize]); |
| dosbus.ReadBuffer(0, dosbus_memory_copy.get(), kDosbusMemorySize); |
| dosbus.WriteBuffer(0, zeroed_memory.get(), kDosbusMemorySize); |
| |
| decoder->state_ = Vp9Decoder::DecoderState::kSwappedOut; |
| |
| { |
| std::lock_guard<std::mutex> lock(*video->video_decoder_lock()); |
| video->watchdog()->Cancel(); |
| } |
| EXPECT_EQ(ZX_OK, decoder->InitializeHardware()); |
| EXPECT_EQ(0, MmioMemcmp(dosbus, dosbus_memory_copy)); |
| } |
| }; |
| |
| TEST(Vp9UnitTest, LoopFilter) { Vp9UnitTest::LoopFilter(); } |
| TEST(Vp9UnitTest, InitializeMemory) { Vp9UnitTest::InitializeMemory(false); } |
| TEST(Vp9UnitTest, InitializeMemoryCompressed) { Vp9UnitTest::InitializeMemory(true); } |
| |
| } // namespace test |
| } // namespace amlogic_decoder |