blob: ab82f8a108e930e0772895683839ee9005484e86 [file] [log] [blame]
// Copyright 2019 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/intel-i915.h"
#include <fidl/fuchsia.sysmem/cpp/wire_test_base.h>
#include <fuchsia/hardware/sysmem/cpp/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/ddk/driver.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/mmio-ptr/fake.h>
#include <lib/zircon-internal/align.h>
#include <lib/zx/vmar.h>
#include <zircon/pixelformat.h>
#include <type_traits>
#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/pci-ids.h"
#define ASSERT_OK(x) ASSERT_EQ(ZX_OK, (x))
#define EXPECT_OK(x) EXPECT_EQ(ZX_OK, (x))
namespace sysmem = fuchsia_sysmem;
namespace {
// Module-scope global data structure that acts as the data source for the zx_framebuffer_get_info
// implementation below.
struct Framebuffer {
zx_status_t status = ZX_OK;
uint32_t format = 0u;
uint32_t width = 0u;
uint32_t height = 0u;
uint32_t stride = 0u;
};
thread_local Framebuffer g_framebuffer;
void SetFramebuffer(const Framebuffer& buffer) { g_framebuffer = buffer; }
} // namespace
zx_status_t zx_framebuffer_get_info(zx_handle_t resource, uint32_t* format, uint32_t* width,
uint32_t* height, uint32_t* stride) {
*format = g_framebuffer.format;
*width = g_framebuffer.width;
*height = g_framebuffer.height;
*stride = g_framebuffer.stride;
return g_framebuffer.status;
}
namespace {
class MockNoCpuBufferCollection
: public fidl::testing::WireTestBase<fuchsia_sysmem::BufferCollection> {
public:
bool set_constraints_called() const { return set_constraints_called_; }
void SetConstraints(SetConstraintsRequestView request,
SetConstraintsCompleter::Sync& _completer) override {
set_constraints_called_ = true;
EXPECT_FALSE(request->constraints.buffer_memory_constraints.inaccessible_domain_supported);
EXPECT_FALSE(request->constraints.buffer_memory_constraints.cpu_domain_supported);
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
EXPECT_TRUE(false);
}
private:
bool set_constraints_called_ = false;
};
class FakeSysmem : public ddk::SysmemProtocol<FakeSysmem> {
public:
FakeSysmem() = default;
const sysmem_protocol_ops_t* proto_ops() const { return &sysmem_protocol_ops_; }
zx_status_t SysmemConnect(zx::channel allocator2_request) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t SysmemRegisterHeap(uint64_t heap, zx::channel heap_connection) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t SysmemRegisterSecureMem(zx::channel tee_connection) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t SysmemUnregisterSecureMem() { return ZX_ERR_NOT_SUPPORTED; }
};
class IntegrationTest : public ::testing::Test {
protected:
void SetUp() final {
SetFramebuffer({});
pci_.CreateBar(0u, std::numeric_limits<uint32_t>::max(), /*is_mmio=*/true);
pci_.AddLegacyInterrupt();
// This configures the "GMCH Graphics Control" register to report 2MB for the available GTT
// Graphics Memory. All other bits of this register are set to zero and should get populated as
// required for the tests below.
pci_.PciWriteConfig16(registers::GmchGfxControl::kAddr, 0x40);
constexpr uint16_t kIntelVendorId = 0x8086;
pci_.SetDeviceInfo(pci_device_info_t{
.vendor_id = kIntelVendorId,
.device_id = i915::kTestDeviceDid,
});
parent_ = MockDevice::FakeRootParent();
parent_->AddProtocol(ZX_PROTOCOL_SYSMEM, sysmem_.proto_ops(), &sysmem_, "sysmem");
parent_->AddProtocol(ZX_PROTOCOL_PCI, pci_.get_protocol().ops, pci_.get_protocol().ctx, "pci");
}
MockDevice* parent() const { return parent_.get(); }
private:
// Emulated parent protocols.
pci::FakePciProtocol pci_;
FakeSysmem sysmem_;
// mock-ddk parent device of the i915::Controller under test.
std::shared_ptr<MockDevice> parent_;
};
TEST(IntelI915Display, SysmemRequirements) {
i915::Controller display(nullptr);
zx::channel server_channel, client_channel;
ASSERT_OK(zx::channel::create(0u, &server_channel, &client_channel));
MockNoCpuBufferCollection collection;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
image_t image = {};
image.pixel_format = ZX_PIXEL_FORMAT_ARGB_8888;
ASSERT_OK(
fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(server_channel), &collection));
EXPECT_OK(
display.DisplayControllerImplSetBufferCollectionConstraints(&image, client_channel.get()));
loop.RunUntilIdle();
EXPECT_TRUE(collection.set_constraints_called());
}
TEST(IntelI915Display, SysmemNoneFormat) {
i915::Controller display(nullptr);
zx::channel server_channel, client_channel;
ASSERT_OK(zx::channel::create(0u, &server_channel, &client_channel));
MockNoCpuBufferCollection collection;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
image_t image = {};
image.pixel_format = ZX_PIXEL_FORMAT_NONE;
ASSERT_OK(
fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(server_channel), &collection));
EXPECT_OK(
display.DisplayControllerImplSetBufferCollectionConstraints(&image, client_channel.get()));
loop.RunUntilIdle();
EXPECT_TRUE(collection.set_constraints_called());
}
TEST(IntelI915Display, SysmemInvalidFormat) {
i915::Controller display(nullptr);
zx::channel server_channel, client_channel;
ASSERT_OK(zx::channel::create(0u, &server_channel, &client_channel));
MockNoCpuBufferCollection collection;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
image_t image = {};
image.pixel_format = UINT32_MAX;
ASSERT_OK(
fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(server_channel), &collection));
EXPECT_EQ(ZX_ERR_INVALID_ARGS, display.DisplayControllerImplSetBufferCollectionConstraints(
&image, client_channel.get()));
loop.RunUntilIdle();
EXPECT_FALSE(collection.set_constraints_called());
}
TEST(IntelI915Display, SysmemInvalidType) {
i915::Controller display(nullptr);
zx::channel server_channel, client_channel;
ASSERT_OK(zx::channel::create(0u, &server_channel, &client_channel));
MockNoCpuBufferCollection collection;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
image_t image = {};
image.pixel_format = ZX_PIXEL_FORMAT_ARGB_8888;
image.type = 1000000;
ASSERT_OK(
fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(server_channel), &collection));
EXPECT_EQ(ZX_ERR_INVALID_ARGS, display.DisplayControllerImplSetBufferCollectionConstraints(
&image, client_channel.get()));
loop.RunUntilIdle();
EXPECT_FALSE(collection.set_constraints_called());
}
// Tests that DDK basic DDK lifecycle hooks function as expected.
TEST_F(IntegrationTest, BindAndInit) {
ASSERT_OK(i915::Controller::Create(parent()));
// There should be two published devices: one "intel_i915" device rooted at |parent()|, and a
// grandchild "intel-gpu-core" device.
ASSERT_EQ(1u, parent()->child_count());
auto dev = parent()->GetLatestChild();
ASSERT_EQ(2u, dev->child_count());
// Perform the async initialization and wait for a response.
dev->InitOp();
EXPECT_EQ(ZX_OK, dev->WaitUntilInitReplyCalled());
// Unbind the device and ensure it completes synchronously.
dev->UnbindOp();
EXPECT_TRUE(dev->UnbindReplyCalled());
mock_ddk::ReleaseFlaggedDevices(parent());
EXPECT_EQ(0u, dev->child_count());
}
// Tests that the device can initialize even if bootloader framebuffer information is not available
// and global GTT allocations start at offset 0.
TEST_F(IntegrationTest, InitFailsIfBootloaderGetInfoFails) {
SetFramebuffer({.status = ZX_ERR_INVALID_ARGS});
ASSERT_EQ(ZX_OK, i915::Controller::Create(parent()));
auto dev = parent()->GetLatestChild();
i915::Controller* ctx = dev->GetDeviceContext<i915::Controller>();
uint64_t addr;
EXPECT_EQ(ZX_OK, ctx->IntelGpuCoreGttAlloc(1, &addr));
EXPECT_EQ(0u, addr);
}
// TODO(fxbug.dev/85836): Add tests for DisplayPort display enumeration by InitOp, covering the
// following cases:
// - Display found during start up but not already powered.
// - Display found during start up but already powered up.
// - Display added and removed in a hotplug event.
// TODO(fxbug.dev/86314): Add test for HDMI display enumeration by InitOp.
// TODO(fxbug.dev/86315): Add test for DVI display enumeration by InitOp.
TEST_F(IntegrationTest, GttAllocationDoesNotOverlapBootloaderFramebuffer) {
constexpr uint32_t kStride = 1920;
constexpr uint32_t kHeight = 1080;
SetFramebuffer({
.format = ZX_PIXEL_FORMAT_RGB_888,
.width = kStride,
.height = kHeight,
.stride = kStride,
});
ASSERT_OK(i915::Controller::Create(parent()));
// There should be two published devices: one "intel_i915" device rooted at |parent()|, and a
// grandchild "intel-gpu-core" device.
ASSERT_EQ(1u, parent()->child_count());
auto dev = parent()->GetLatestChild();
i915::Controller* ctx = dev->GetDeviceContext<i915::Controller>();
uint64_t addr;
EXPECT_EQ(ZX_OK, ctx->IntelGpuCoreGttAlloc(1, &addr));
EXPECT_EQ(ZX_ROUNDUP(kHeight * kStride * 3, PAGE_SIZE), addr);
}
} // namespace