blob: 7524be4f3a0cc7b2c47342ae656e98379ef12c59 [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/pipe.h"
#include <lib/mmio-ptr/fake.h>
#include <lib/mmio/mmio.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <memory>
#include <vector>
#include <fake-mmio-reg/fake-mmio-reg.h>
#include <gtest/gtest.h>
#include "src/graphics/display/drivers/intel-i915/hardware-common.h"
#include "src/graphics/display/drivers/intel-i915/registers-pipe.h"
#include "src/graphics/display/lib/api-types-cpp/config-stamp.h"
namespace i915 {
class PipeTest : public ::testing::Test {
public:
PipeTest() = default;
void SetUp() override { mmio_buffer_.emplace(reg_region_.GetMmioBuffer()); }
void TearDown() override {}
protected:
constexpr static uint32_t kMinimumRegCount = 0xd0000 / sizeof(uint32_t);
ddk_fake::FakeMmioRegRegion reg_region_{sizeof(uint32_t), kMinimumRegCount};
std::optional<fdf::MmioBuffer> mmio_buffer_;
};
namespace {
class TestGttRegionImpl : public GttRegion {
public:
explicit TestGttRegionImpl(uint64_t handle) : handle_(handle) {}
uint64_t bytes_per_row() const override { return 64; }
uint64_t base() const override { return handle_ + 0xf0000000; }
private:
uint64_t handle_ = 0;
};
std::map<uint64_t, TestGttRegionImpl> region_map;
PixelFormatAndModifier GetPixelFormat(uint64_t image_handle) {
return PixelFormatAndModifier(
fuchsia_images2::PixelFormat::kB8G8R8A8,
/*pixel_format_modifier_param=*/fuchsia_images2::PixelFormatModifier::kLinear);
}
const GttRegion& GetGttImageHandle(const image_metadata_t& image_metadata, uint64_t image_handle,
uint32_t rotation) {
auto it = region_map.find(image_handle);
if (it != region_map.end()) {
return it->second;
}
return region_map.try_emplace(image_handle, image_handle).first->second;
}
layer_t CreatePrimaryLayerConfig(uint64_t handle, uint32_t z_index = 1u) {
uint32_t kWidth = 1024u;
uint32_t kHeight = 768u;
layer_t layer;
layer.type = LAYER_TYPE_PRIMARY;
layer.z_index = z_index;
layer.cfg.primary = {
.image_handle = handle,
.image_metadata =
{
.width = kWidth,
.height = kHeight,
.tiling_type = IMAGE_TILING_TYPE_LINEAR,
},
.alpha_mode = ALPHA_DISABLE,
.transform_mode = FRAME_TRANSFORM_IDENTITY,
.src_frame = {0, 0, kWidth, kHeight},
.dest_frame = {0, 0, kWidth, kHeight},
};
return layer;
}
} // namespace
TEST_F(PipeTest, TiedTranscoderId) {
PipeSkylake pipe_a(&mmio_buffer_.value(), PipeId::PIPE_A, {});
EXPECT_EQ(TranscoderId::TRANSCODER_A, pipe_a.tied_transcoder_id());
PipeSkylake pipe_b(&mmio_buffer_.value(), PipeId::PIPE_B, {});
EXPECT_EQ(TranscoderId::TRANSCODER_B, pipe_b.tied_transcoder_id());
PipeSkylake pipe_c(&mmio_buffer_.value(), PipeId::PIPE_C, {});
EXPECT_EQ(TranscoderId::TRANSCODER_C, pipe_c.tied_transcoder_id());
// TODO(https://fxbug.dev/42060657): Add a test for transcoder D, when we support it.
}
// Verifies that GetVsyncConfigStamp() could return the correct config stamp
// given different image handles from device registers.
TEST_F(PipeTest, GetVsyncConfigStamp) {
PipeSkylake pipe(&*mmio_buffer_, PipeId::PIPE_A, {});
uint64_t kImageHandle1 = 0x1111u;
uint64_t kImageHandle2 = 0x2222u;
uint64_t kImageHandle3 = 0x3333u;
layer_t layer_1 = CreatePrimaryLayerConfig(kImageHandle1, 1u);
layer_t layer_2 = CreatePrimaryLayerConfig(kImageHandle2, 1u);
layer_t layer_3 = CreatePrimaryLayerConfig(kImageHandle3, 2u);
// Applies configuration with only one layer (layer_1).
const layer_t* test_layers_1[] = {&layer_1};
display_config_t config = {
.display_id = 1u,
.mode = {},
.cc_flags = 0u,
.layer_list = test_layers_1,
.layer_count = 1,
};
display::ConfigStamp stamp_1{1};
pipe.ApplyConfiguration(&config, stamp_1, GetGttImageHandle, GetPixelFormat);
// For images that are not registered with Pipe yet, GetVsyncConfigStamp()
// should return nullopt.
display::ConfigStamp vsync_config_stamp_not_found = pipe.GetVsyncConfigStamp({kImageHandle2});
EXPECT_EQ(vsync_config_stamp_not_found, display::kInvalidConfigStamp);
// Otherwise, for a valid image handle that has occurred in a past config,
// GetVsyncConfigStamp() should return the latest config where it occurred.
display::ConfigStamp vsync_config_stamp_1 = pipe.GetVsyncConfigStamp({kImageHandle1});
EXPECT_NE(vsync_config_stamp_1, display::kInvalidConfigStamp);
EXPECT_EQ(vsync_config_stamp_1, stamp_1);
// Applies another configuration with two layers (layer_2 replacing layer_1,
// and a new layer layer_3).
const layer_t* test_layers_2[] = {&layer_2, &layer_3};
display_config_t config_2 = {
.display_id = 1u,
.mode = {},
.cc_flags = 0u,
.layer_list = test_layers_2,
.layer_count = 1,
};
display::ConfigStamp stamp_2{2};
pipe.ApplyConfiguration(&config_2, stamp_2, GetGttImageHandle, GetPixelFormat);
// It is possible that a layer update is slower than other layers, so on
// Vsync time the device may have layers from different configurations. In
// that case, the device should return the oldest configuration stamp, i.e.
// stamp_1.
display::ConfigStamp vsync_config_stamp_2 =
pipe.GetVsyncConfigStamp({kImageHandle1, kImageHandle3});
EXPECT_NE(vsync_config_stamp_2, display::kInvalidConfigStamp);
EXPECT_EQ(vsync_config_stamp_2, stamp_1);
// Now both layers are updated in another new Vsync. GetVsyncConfigStamp()
// should return the updated stamp value.
display::ConfigStamp vsync_config_stamp_3 =
pipe.GetVsyncConfigStamp({kImageHandle2, kImageHandle3});
EXPECT_NE(vsync_config_stamp_3, display::kInvalidConfigStamp);
EXPECT_EQ(vsync_config_stamp_3, stamp_2);
// Old image handle should be evicted from Pipe completely.
display::ConfigStamp vsync_config_stamp_4 =
pipe.GetVsyncConfigStamp({kImageHandle1, kImageHandle3});
EXPECT_EQ(vsync_config_stamp_4, display::kInvalidConfigStamp);
}
} // namespace i915