blob: 085895785225dea14b349f7cf9f440a2b676ff7f [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-display/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/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/driver/fake-mmio-reg/cpp/fake-mmio-reg.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/driver/testing/cpp/scoped_global_logger.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/mmio-ptr/fake.h>
#include <lib/sync/completion.h>
#include <gtest/gtest.h>
#include "src/devices/pci/testing/pci_protocol_fake.h"
#include "src/graphics/display/drivers/intel-display/pci-ids.h"
#include "src/lib/testing/predicates/status.h"
namespace intel_display {
namespace {
void NopPipeVsyncCallback(PipeId, zx::time_monotonic) {}
void NopHotplugCallback(DdiId, bool) {}
void NopIrqCallback(void*, uint32_t, uint64_t) {}
class InterruptTest : public testing::Test {
public:
static constexpr uint32_t kMmioRegCount = 0xd0000 / sizeof(uint32_t);
InterruptTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override {
loop_.StartThread("pci-fidl-server-thread");
pci_ = fake_pci_.SetUpFidlServer(loop_);
}
protected:
// `logger_` must outlive `driver_runtime_` to allow for any
// logging in driver de-initialization code.
fdf_testing::ScopedGlobalLogger logger_;
fdf_testing::DriverRuntime driver_runtime_;
fdf::UnownedSynchronizedDispatcher interrupt_dispatcher_ =
driver_runtime_.StartBackgroundDispatcher();
async::Loop loop_;
ddk::Pci pci_;
pci::FakePciProtocol fake_pci_;
fake_mmio::FakeMmioRegRegion mmio_space_{32, kMmioRegCount};
fdf::MmioBuffer mmio_buffer_{mmio_space_.GetMmioBuffer()};
};
TEST_F(InterruptTest, InitErrorWithoutAvailablePciInterrupt) {
async_patterns::TestDispatcherBound<Interrupts> interrupts(
interrupt_dispatcher_->async_dispatcher(), std::in_place);
zx_status_t init_status = interrupts.SyncCall([&](Interrupts* interrupts) {
return interrupts->Init(NopPipeVsyncCallback, NopHotplugCallback, pci_, &mmio_buffer_,
kTestDeviceDid);
});
EXPECT_STATUS(ZX_ERR_INTERNAL, init_status);
}
TEST_F(InterruptTest, InitWithLegacyInterrupt) {
pci::RunAsync(loop_, [&] { fake_pci_.AddLegacyInterrupt(); });
async_patterns::TestDispatcherBound<Interrupts> interrupts(
interrupt_dispatcher_->async_dispatcher(), std::in_place);
zx_status_t init_status = interrupts.SyncCall([&](Interrupts* interrupts) {
return interrupts->Init(NopPipeVsyncCallback, NopHotplugCallback, pci_, &mmio_buffer_,
kTestDeviceDid);
});
EXPECT_OK(init_status);
}
TEST_F(InterruptTest, InitWithMsiInterrupt) {
pci::RunAsync(loop_, [&] { fake_pci_.AddMsiInterrupt(); });
async_patterns::TestDispatcherBound<Interrupts> interrupts(
interrupt_dispatcher_->async_dispatcher(), std::in_place);
zx_status_t init_status = interrupts.SyncCall([&](Interrupts* interrupts) {
return interrupts->Init(NopPipeVsyncCallback, NopHotplugCallback, pci_, &mmio_buffer_,
kTestDeviceDid);
});
EXPECT_OK(init_status);
pci::RunAsync(loop_, [&] {
EXPECT_EQ(1u, fake_pci_.GetIrqCount());
EXPECT_EQ(fuchsia_hardware_pci::InterruptMode::kMsi, fake_pci_.GetIrqMode());
});
}
TEST_F(InterruptTest, InitWithMsiAndLegacyInterrupts) {
pci::RunAsync(loop_, [&] {
fake_pci_.AddLegacyInterrupt();
fake_pci_.AddMsiInterrupt();
});
async_patterns::TestDispatcherBound<Interrupts> interrupts(
interrupt_dispatcher_->async_dispatcher(), std::in_place);
zx_status_t init_status = interrupts.SyncCall([&](Interrupts* interrupts) {
return interrupts->Init(NopPipeVsyncCallback, NopHotplugCallback, pci_, &mmio_buffer_,
kTestDeviceDid);
});
EXPECT_OK(init_status);
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 = NopIrqCallback, .ctx = nullptr};
const uint32_t gpu_interrupt_mask = 0;
EXPECT_OK(interrupts.SetGpuInterruptCallback(callback, gpu_interrupt_mask));
// Setting a callback when one is already assigned should fail.
EXPECT_STATUS(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_OK(interrupts.SetGpuInterruptCallback(null_callback, gpu_interrupt_mask));
// It should be possible to set a new callback after clearing the old one.
EXPECT_OK(interrupts.SetGpuInterruptCallback(callback, gpu_interrupt_mask));
}
} // namespace
} // namespace intel_display