blob: 0ebd1d9841b5c4f8b7b1b428c814883ea7dc7f0d [file] [log] [blame] [edit]
// 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/intel-display.h"
#include <fidl/fuchsia.sysmem2/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/driver/testing/cpp/scoped_global_logger.h>
#include <lib/mmio-ptr/fake.h>
#include <gtest/gtest.h>
#include "src/devices/pci/testing/pci_protocol_fake.h"
#include "src/graphics/display/drivers/intel-display/registers.h"
#include "src/graphics/display/drivers/intel-display/testing/fake-buffer-collection.h"
#include "src/graphics/display/drivers/intel-display/testing/mock-allocator.h"
#include "src/graphics/display/lib/api-protocols/cpp/display-engine-events-fidl.h"
#include "src/graphics/display/lib/api-types/cpp/driver-buffer-collection-id.h"
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/testing/predicates/status.h"
namespace {
constexpr uint32_t kBytesPerRowDivisor = 1024;
} // namespace
namespace intel_display {
namespace {
// Test fixture for tests that only uses fake sysmem but doesn't have any
// other dependency, so that we won't need a fully-fledged device tree.
class FakeSysmemSingleThreadedTest : public testing::Test {
public:
FakeSysmemSingleThreadedTest()
: loop_(&kAsyncLoopConfigAttachToCurrentThread),
sysmem_(loop_.dispatcher()),
display_(&engine_events_, inspect::Inspector{}) {}
void SetUp() override {
auto [sysmem_client, sysmem_server] = fidl::Endpoints<fuchsia_sysmem2::Allocator>::Create();
fidl::BindServer(loop_.dispatcher(), std::move(sysmem_server), &sysmem_);
sysmem_.SetNewBufferCollectionConfig({
.cpu_domain_supported = false,
.ram_domain_supported = true,
.inaccessible_domain_supported = false,
.bytes_per_row_divisor = kBytesPerRowDivisor,
.format_modifier = fuchsia_images2::wire::PixelFormatModifier::kLinear,
});
ASSERT_OK(display_.SetAndInitSysmemForTesting(fidl::WireSyncClient(std::move(sysmem_client))));
EXPECT_OK(loop_.RunUntilIdle());
}
void TearDown() override {
// Shutdown the loop before destroying the FakeSysmem and MockAllocator which
// may still have pending callbacks.
loop_.Shutdown();
}
protected:
fdf_testing::ScopedGlobalLogger logger_;
async::Loop loop_;
MockAllocator sysmem_;
// Must outlive `display_`.
display::DisplayEngineEventsFidl engine_events_;
Controller display_;
};
using ControllerWithFakeSysmemTest = FakeSysmemSingleThreadedTest;
TEST_F(ControllerWithFakeSysmemTest, ImportBufferCollection) {
const MockAllocator& allocator = sysmem_;
zx::result token1_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token1_endpoints.is_ok());
zx::result token2_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token2_endpoints.is_ok());
// Test ImportBufferCollection().
constexpr display::DriverBufferCollectionId kValidBufferCollectionId(1);
EXPECT_OK(display_.ImportBufferCollection(kValidBufferCollectionId,
std::move(token1_endpoints->client)));
// `collection_id` must be unused.
EXPECT_STATUS(display_.ImportBufferCollection(kValidBufferCollectionId,
std::move(token2_endpoints->client)),
zx::error(ZX_ERR_ALREADY_EXISTS));
loop_.RunUntilIdle();
// Verify that the current buffer collection token is used.
{
auto active_buffer_token_clients = allocator.GetActiveBufferCollectionTokenClients();
EXPECT_EQ(active_buffer_token_clients.size(), 1u);
auto inactive_buffer_token_clients = allocator.GetInactiveBufferCollectionTokenClients();
EXPECT_EQ(inactive_buffer_token_clients.size(), 0u);
auto [client_koid, client_related_koid] =
fsl::GetKoids(active_buffer_token_clients[0].channel()->get());
auto [server_koid, server_related_koid] =
fsl::GetKoids(token1_endpoints->server.channel().get());
EXPECT_NE(client_koid, ZX_KOID_INVALID);
EXPECT_NE(client_related_koid, ZX_KOID_INVALID);
EXPECT_NE(server_koid, ZX_KOID_INVALID);
EXPECT_NE(server_related_koid, ZX_KOID_INVALID);
EXPECT_EQ(client_koid, server_related_koid);
EXPECT_EQ(server_koid, client_related_koid);
}
// Test ReleaseBufferCollection().
constexpr display::DriverBufferCollectionId kInvalidBufferCollectionId(2);
EXPECT_STATUS(display_.ReleaseBufferCollection(kInvalidBufferCollectionId),
zx::error(ZX_ERR_NOT_FOUND));
EXPECT_OK(display_.ReleaseBufferCollection(kValidBufferCollectionId));
loop_.RunUntilIdle();
// Verify that the current buffer collection token is released.
{
auto active_buffer_token_clients = allocator.GetActiveBufferCollectionTokenClients();
EXPECT_EQ(active_buffer_token_clients.size(), 0u);
auto inactive_buffer_token_clients = allocator.GetInactiveBufferCollectionTokenClients();
EXPECT_EQ(inactive_buffer_token_clients.size(), 1u);
auto [client_koid, client_related_koid] =
fsl::GetKoids(inactive_buffer_token_clients[0].channel()->get());
auto [server_koid, server_related_koid] =
fsl::GetKoids(token1_endpoints->server.channel().get());
EXPECT_NE(client_koid, ZX_KOID_INVALID);
EXPECT_NE(client_related_koid, ZX_KOID_INVALID);
EXPECT_NE(server_koid, ZX_KOID_INVALID);
EXPECT_NE(server_related_koid, ZX_KOID_INVALID);
EXPECT_EQ(client_koid, server_related_koid);
EXPECT_EQ(server_koid, client_related_koid);
}
}
fdf::MmioBuffer MakeMmioBuffer(uint8_t* buffer, size_t size) {
return fdf::MmioBuffer({
.vaddr = FakeMmioPtr(buffer),
.offset = 0,
.size = size,
.vmo = ZX_HANDLE_INVALID,
});
}
TEST(IntelDisplay, ImportImage) {
fdf_testing::ScopedGlobalLogger logger;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
loop.StartThread("fidl-loop");
// Prepare fake sysmem.
MockAllocator fake_sysmem(loop.dispatcher());
auto [sysmem_client, sysmem_server] = fidl::Endpoints<fuchsia_sysmem2::Allocator>::Create();
fidl::BindServer(loop.dispatcher(), std::move(sysmem_server), &fake_sysmem);
// Prepare fake PCI.
pci::FakePciProtocol fake_pci;
ddk::Pci pci = fake_pci.SetUpFidlServer(loop);
fake_sysmem.SetNewBufferCollectionConfig({
.cpu_domain_supported = false,
.ram_domain_supported = true,
.inaccessible_domain_supported = false,
.width_fallback_px = 32,
.height_fallback_px = 32,
.bytes_per_row_divisor = kBytesPerRowDivisor,
.format_modifier = fuchsia_images2::wire::PixelFormatModifier::kLinear,
});
// Initialize display controller and sysmem allocator.
// Must outlive `display`.
display::DisplayEngineEventsFidl display_events;
Controller display(&display_events, inspect::Inspector{});
ASSERT_OK(display.SetAndInitSysmemForTesting(fidl::WireSyncClient(std::move(sysmem_client))));
// Initialize the GTT to the smallest allowed size (which is 2MB with the |gtt_size| bits of the
// graphics control register set to 0x01.
constexpr size_t kGraphicsTranslationTableSizeBytes = (1 << 21);
ASSERT_OK(pci.WriteConfig16(registers::GmchGfxControl::kAddr,
registers::GmchGfxControl().set_gtt_size(0x01).reg_value()));
auto buffer = std::make_unique<uint8_t[]>(kGraphicsTranslationTableSizeBytes);
memset(buffer.get(), 0, kGraphicsTranslationTableSizeBytes);
fdf::MmioBuffer mmio = MakeMmioBuffer(buffer.get(), kGraphicsTranslationTableSizeBytes);
ASSERT_OK(display.InitGttForTesting(pci, std::move(mmio), /*fb_offset=*/0));
// Import buffer collection.
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
zx::result token_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token_endpoints.is_ok());
EXPECT_OK(
display.ImportBufferCollection(kBufferCollectionId, std::move(token_endpoints->client)));
static constexpr display::ImageBufferUsage kDisplayUsage({
.tiling_type = display::ImageTilingType::kLinear,
});
EXPECT_OK(display.SetBufferCollectionConstraints(kDisplayUsage, kBufferCollectionId));
// Invalid import: bad collection id
static constexpr display::ImageMetadata kDisplayImageMetadata = {{
.width = 32,
.height = 32,
.tiling_type = display::ImageTilingType::kLinear,
}};
static constexpr display::DriverBufferCollectionId kInvalidCollectionId(100);
zx::result<display::DriverImageId> import_result =
display.ImportImage(kDisplayImageMetadata, kInvalidCollectionId, 0);
EXPECT_STATUS(import_result, zx::error(ZX_ERR_NOT_FOUND));
// Invalid import: bad index
static constexpr uint32_t kInvalidIndex = 100;
import_result = display.ImportImage(kDisplayImageMetadata, kBufferCollectionId, kInvalidIndex);
EXPECT_STATUS(import_result, zx::error(ZX_ERR_OUT_OF_RANGE));
// Invalid import: bad type
static constexpr display::ImageMetadata kInvalidTilingTypeMetadata = {{
.width = 32,
.height = 32,
.tiling_type = display::ImageTilingType::kCapture,
}};
import_result = display.ImportImage(kInvalidTilingTypeMetadata, kBufferCollectionId,
/*index=*/0);
EXPECT_STATUS(import_result, zx::error(ZX_ERR_INVALID_ARGS));
// Valid import
import_result = display.ImportImage(kDisplayImageMetadata, kBufferCollectionId, 0);
ASSERT_OK(import_result);
display::DriverImageId image_id = import_result.value();
EXPECT_NE(image_id.value(), 0u);
display.ReleaseImage(image_id);
// Release buffer collection.
EXPECT_OK(display.ReleaseBufferCollection(kBufferCollectionId));
// Shutdown the loop before destroying the FakeSysmem and MockAllocator which
// may still have pending callbacks.
loop.Shutdown();
}
TEST_F(ControllerWithFakeSysmemTest, SysmemRequirements) {
zx::result token_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token_endpoints.is_ok());
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
EXPECT_OK(
display_.ImportBufferCollection(kBufferCollectionId, std::move(token_endpoints->client)));
loop_.RunUntilIdle();
static constexpr display::ImageBufferUsage kDisplayUsage({
.tiling_type = display::ImageTilingType::kLinear,
});
EXPECT_OK(display_.SetBufferCollectionConstraints(kDisplayUsage, kBufferCollectionId));
loop_.RunUntilIdle();
MockAllocator& allocator = sysmem_;
FakeBufferCollection* collection = allocator.GetMostRecentBufferCollection();
ASSERT_TRUE(collection);
EXPECT_TRUE(collection->HasConstraints());
}
TEST_F(ControllerWithFakeSysmemTest, SysmemInvalidType) {
zx::result token_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token_endpoints.is_ok());
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
EXPECT_OK(
display_.ImportBufferCollection(kBufferCollectionId, std::move(token_endpoints->client)));
loop_.RunUntilIdle();
static constexpr display::ImageBufferUsage kInvalidTilingUsage({
.tiling_type = display::ImageTilingType(1000000),
});
EXPECT_STATUS(zx::error(ZX_ERR_INVALID_ARGS),
display_.SetBufferCollectionConstraints(kInvalidTilingUsage, kBufferCollectionId));
loop_.RunUntilIdle();
MockAllocator& allocator = sysmem_;
FakeBufferCollection* collection = allocator.GetMostRecentBufferCollection();
ASSERT_TRUE(collection);
EXPECT_FALSE(collection->HasConstraints());
}
} // namespace
} // namespace intel_display