blob: c495a492b3bc463c6326d51af7f1a64a79d1ef4d [file] [log] [blame]
// Copyright 2022 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 "src/graphics/display/drivers/intel-i915/interrupts.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/ddk/driver.h>
#include <lib/mmio-ptr/fake.h>
#include <lib/sync/completion.h>
#include <vector>
#include <gtest/gtest.h>
#include "src/devices/pci/testing/pci_protocol_fake.h"
#include "src/devices/testing/mock-ddk/mock-device.h"
#include "src/graphics/display/drivers/intel-i915/ddi.h"
#include "src/graphics/display/drivers/intel-i915/pci-ids.h"
namespace i915 {
namespace {
void NopPipeVsyncCb(PipeId, zx_time_t) {}
void NopHotplugCb(DdiId, bool) {}
void NopIrqCb(void*, uint32_t, uint64_t) {}
zx_status_t InitInterrupts(Interrupts* i, zx_device_t* dev, ddk::Pci& pci, fdf::MmioBuffer* mmio) {
return i->Init(NopPipeVsyncCb, NopHotplugCb, dev, pci, mmio, kTestDeviceDid);
}
class InterruptTest : public testing::Test {
public:
InterruptTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override {
loop_.StartThread("pci-fidl-server-thread");
pci_ = fake_pci_.SetUpFidlServer(loop_);
}
async::Loop loop_;
ddk::Pci pci_;
pci::FakePciProtocol fake_pci_;
};
// Test interrupt initialization with both MSI and Legacy interrupt modes.
TEST_F(InterruptTest, Init) {
constexpr uint32_t kMinimumRegCount = 0xd0000 / sizeof(uint32_t);
std::vector<uint32_t> regs(kMinimumRegCount);
fdf::MmioBuffer mmio_space({
.vaddr = FakeMmioPtr(regs.data()),
.offset = 0,
.size = regs.size() * sizeof(uint32_t),
.vmo = ZX_HANDLE_INVALID,
});
std::shared_ptr<MockDevice> parent = MockDevice::FakeRootParent();
Interrupts interrupts;
EXPECT_EQ(ZX_ERR_INTERNAL, InitInterrupts(&interrupts, parent.get(), pci_, &mmio_space));
pci::RunAsync(loop_, [&] { fake_pci_.AddLegacyInterrupt(); });
EXPECT_EQ(ZX_OK, InitInterrupts(&interrupts, parent.get(), pci_, &mmio_space));
pci::RunAsync(loop_, [&] {
EXPECT_EQ(1u, fake_pci_.GetIrqCount());
EXPECT_EQ(fuchsia_hardware_pci::InterruptMode::kLegacy, fake_pci_.GetIrqMode());
fake_pci_.AddMsiInterrupt();
});
EXPECT_EQ(ZX_OK, InitInterrupts(&interrupts, parent.get(), pci_, &mmio_space));
pci::RunAsync(loop_, [&] {
EXPECT_EQ(1u, fake_pci_.GetIrqCount());
EXPECT_EQ(fuchsia_hardware_pci::InterruptMode::kMsi, fake_pci_.GetIrqMode());
});
}
TEST_F(InterruptTest, SetInterruptCallback) {
Interrupts interrupts;
constexpr intel_gpu_core_interrupt_t callback = {.callback = NopIrqCb, .ctx = nullptr};
const uint32_t gpu_interrupt_mask = 0;
EXPECT_EQ(ZX_OK, interrupts.SetGpuInterruptCallback(callback, gpu_interrupt_mask));
// Setting a callback when one is already assigned should fail.
EXPECT_EQ(ZX_ERR_ALREADY_BOUND, interrupts.SetGpuInterruptCallback(callback, gpu_interrupt_mask));
// Clearing the existing callback with a null callback should fail.
constexpr intel_gpu_core_interrupt_t null_callback = {.callback = nullptr, .ctx = nullptr};
EXPECT_EQ(ZX_OK, interrupts.SetGpuInterruptCallback(null_callback, gpu_interrupt_mask));
// It should be possible to set a new callback after clearing the old one.
EXPECT_EQ(ZX_OK, interrupts.SetGpuInterruptCallback(callback, gpu_interrupt_mask));
}
} // namespace
} // namespace i915