blob: 3450a562903aac76f18db41900e56738ab82c7e3 [file] [log] [blame]
// Copyright 2021 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 "aml-hdmi.h"
#include <lib/fake_ddk/fidl-helper.h>
#include <queue>
#include <fbl/alloc_checker.h>
#include <fbl/array.h>
#include <mock-mmio-reg/mock-mmio-reg.h>
namespace aml_hdmi {
namespace {
constexpr size_t kRegSize = 0x00100000 / sizeof(uint32_t); // in 32 bits chunks.
} // namespace
using HdmiClient = fidl::WireSyncClient<fuchsia_hardware_hdmi::Hdmi>;
using fuchsia_hardware_hdmi::wire::ColorDepth;
using fuchsia_hardware_hdmi::wire::ColorFormat;
enum class HdmiDwFn {
kConfigHdmitx,
kSetupInterrupts,
kReset,
kSetupScdc,
kResetFc,
kSetFcScramblerCtrl,
};
class AmlHdmiTest;
class FakeHdmiDw : public hdmi_dw::HdmiDw {
public:
explicit FakeHdmiDw(HdmiIpBase* base, AmlHdmiTest* test) : HdmiDw(base), test_(test) {}
void ConfigHdmitx(const fuchsia_hardware_hdmi::wire::DisplayMode& mode,
const hdmi_dw::hdmi_param_tx& p) override;
void SetupInterrupts() override;
void Reset() override;
void SetupScdc(bool is4k) override;
void ResetFc() override;
void SetFcScramblerCtrl(bool is4k) override;
private:
AmlHdmiTest* test_;
};
class FakeAmlHdmiDevice : public AmlHdmiDevice {
public:
static std::unique_ptr<FakeAmlHdmiDevice> Create(AmlHdmiTest* test, fdf::MmioBuffer mmio) {
fbl::AllocChecker ac;
auto device = fbl::make_unique_checked<FakeAmlHdmiDevice>(&ac, test, std::move(mmio));
if (!ac.check()) {
zxlogf(ERROR, "device object alloc failed");
return nullptr;
}
return device;
}
explicit FakeAmlHdmiDevice(AmlHdmiTest* test, fdf::MmioBuffer mmio)
: AmlHdmiDevice(nullptr, std::move(mmio), std::make_unique<FakeHdmiDw>(this, test)) {}
static zx_status_t MessageOp(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
return static_cast<AmlHdmiDevice*>(ctx)->ddk_device_proto_.message(ctx, msg, txn);
}
zx_status_t InitTest() { return messenger_.SetMessageOp(this, FakeAmlHdmiDevice::MessageOp); }
zx::channel& GetMessengerChannel() { return messenger_.local(); }
private:
fake_ddk::FidlMessenger messenger_;
};
class AmlHdmiTest : public zxtest::Test {
public:
void SetUp() override {
fbl::AllocChecker ac;
regs_ = fbl::Array(new (&ac) ddk_mock::MockMmioReg[kRegSize], kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "regs_ alloc failed");
return;
}
mock_mmio_ = fbl::make_unique_checked<ddk_mock::MockMmioRegRegion>(&ac, regs_.get(),
sizeof(uint32_t), kRegSize);
if (!ac.check()) {
zxlogf(ERROR, "%s: mock_mmio_ alloc failed", __func__);
return;
}
fdf::MmioBuffer mmio(mock_mmio_->GetMmioBuffer());
dut_ = FakeAmlHdmiDevice::Create(this, std::move(mmio));
ASSERT_NOT_NULL(dut_);
ASSERT_OK(dut_->InitTest());
hdmi_client_ = fidl::WireSyncClient(
fidl::ClientEnd<fuchsia_hardware_hdmi::Hdmi>(std::move(dut_->GetMessengerChannel())));
}
void TearDown() override {
ASSERT_TRUE(expected_dw_calls_.size() == 0);
mock_mmio_->VerifyAll();
}
void HdmiDwCall(HdmiDwFn func) {
ASSERT_FALSE(expected_dw_calls_.empty());
ASSERT_EQ(expected_dw_calls_.front(), func);
expected_dw_calls_.pop();
}
void ExpectHdmiDwConfigHdmitx() { expected_dw_calls_.push(HdmiDwFn::kConfigHdmitx); }
void ExpectHdmiDwSetupInterrupts() { expected_dw_calls_.push(HdmiDwFn::kSetupInterrupts); }
void ExpectHdmiDwReset() { expected_dw_calls_.push(HdmiDwFn::kReset); }
void ExpectHdmiDwSetupScdc() { expected_dw_calls_.push(HdmiDwFn::kSetupScdc); }
void ExpectHdmiDwResetFc() { expected_dw_calls_.push(HdmiDwFn::kResetFc); }
void ExpectHdmiDwSetFcScramblerCtrl() { expected_dw_calls_.push(HdmiDwFn::kSetFcScramblerCtrl); }
protected:
std::unique_ptr<FakeAmlHdmiDevice> dut_;
HdmiClient hdmi_client_;
fbl::Array<ddk_mock::MockMmioReg> regs_;
std::unique_ptr<ddk_mock::MockMmioRegRegion> mock_mmio_;
std::queue<HdmiDwFn> expected_dw_calls_;
};
void FakeHdmiDw::ConfigHdmitx(const fuchsia_hardware_hdmi::wire::DisplayMode& mode,
const hdmi_dw::hdmi_param_tx& p) {
test_->HdmiDwCall(HdmiDwFn::kConfigHdmitx);
}
void FakeHdmiDw::SetupInterrupts() { test_->HdmiDwCall(HdmiDwFn::kSetupInterrupts); }
void FakeHdmiDw::Reset() { test_->HdmiDwCall(HdmiDwFn::kReset); }
void FakeHdmiDw::SetupScdc(bool is4k) { test_->HdmiDwCall(HdmiDwFn::kSetupScdc); }
void FakeHdmiDw::ResetFc() { test_->HdmiDwCall(HdmiDwFn::kResetFc); }
void FakeHdmiDw::SetFcScramblerCtrl(bool is4k) { test_->HdmiDwCall(HdmiDwFn::kSetFcScramblerCtrl); }
TEST_F(AmlHdmiTest, ReadTest) {
// Amlogic Register
(*mock_mmio_)[0x12 * 4 + 0x8000].ExpectRead(0x1234);
auto pval_aml = hdmi_client_->ReadReg(0x12);
ASSERT_OK(pval_aml.status());
EXPECT_EQ(pval_aml.value().val, 0x1234);
// Designware Register
(*mock_mmio_)[0x3].ExpectRead(0x21);
auto pval_dwc = hdmi_client_->ReadReg((0x10UL << 24) + 0x3);
ASSERT_OK(pval_dwc.status());
EXPECT_EQ(pval_dwc.value().val, 0x21);
}
TEST_F(AmlHdmiTest, WriteTest) {
// Amlogic Register
(*mock_mmio_)[0x5 * 4 + 0x8000].ExpectWrite(0x4321);
auto pres_aml = hdmi_client_->WriteReg(0x5, 0x4321);
ASSERT_OK(pres_aml.status());
// Designware Register
(*mock_mmio_)[0x420].ExpectWrite(0x15);
auto pres_dwc = hdmi_client_->WriteReg((0x10UL << 24) + 0x420, 0x2415);
ASSERT_OK(pres_dwc.status());
}
TEST_F(AmlHdmiTest, ResetTest) {
(*mock_mmio_)[0x0 * 4 + 0x8000].ExpectWrite(0); // HDMITX_TOP_SW_RESET
(*mock_mmio_)[0x1 * 4 + 0x8000].ExpectWrite(0xff); // HDMITX_TOP_CLK_CNTL
auto pres = hdmi_client_->Reset(1);
ASSERT_OK(pres.status());
}
TEST_F(AmlHdmiTest, ModeSetTest) {
fidl::Arena allocator;
fuchsia_hardware_hdmi::wire::StandardDisplayMode standard_display_mode{
.pixel_clock_10khz = 0,
.h_addressable = 0,
.h_front_porch = 0,
.h_sync_pulse = 0,
.h_blanking = 0,
.v_addressable = 0,
.v_front_porch = 0,
.v_sync_pulse = 0,
.v_blanking = 0,
.flags = 0,
};
fuchsia_hardware_hdmi::wire::ColorParam color{
.input_color_format = ColorFormat::kCfRgb,
.output_color_format = ColorFormat::kCfRgb,
.color_depth = ColorDepth::kCd24B,
};
fuchsia_hardware_hdmi::wire::DisplayMode mode(allocator);
mode.set_mode(allocator, standard_display_mode);
mode.set_color(color);
(*mock_mmio_)[0x6 * 4 + 0x8000].ExpectWrite(1 << 12); // HDMITX_TOP_BIST_CNTL
ExpectHdmiDwConfigHdmitx();
(*mock_mmio_)[0x5 * 4 + 0x8000].ExpectWrite(0x1f); // HDMITX_TOP_INTR_STAT_CLR
ExpectHdmiDwSetupInterrupts();
(*mock_mmio_)[0x3 * 4 + 0x8000].ExpectWrite(0x9f); // HDMITX_TOP_INTR_MASKN
ExpectHdmiDwReset();
(*mock_mmio_)[0xA * 4 + 0x8000].ExpectWrite(0x001f001f); // HDMITX_TOP_TMDS_CLK_PTTN_01
(*mock_mmio_)[0xB * 4 + 0x8000].ExpectWrite(0x001f001f); // HDMITX_TOP_TMDS_CLK_PTTN_23
ExpectHdmiDwSetFcScramblerCtrl();
(*mock_mmio_)[0xC * 4 + 0x8000].ExpectWrite(0x1); // HDMITX_TOP_TMDS_CLK_PTTN_CNTL
(*mock_mmio_)[0xC * 4 + 0x8000].ExpectWrite(0x2); // HDMITX_TOP_TMDS_CLK_PTTN_CNTL
ExpectHdmiDwSetupScdc();
ExpectHdmiDwResetFc();
auto pres = hdmi_client_->ModeSet(1, mode);
ASSERT_OK(pres.status());
}
} // namespace aml_hdmi