blob: b46116ac190ecc70c855d46f10892890efc83443 [file]
// 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