blob: 9124e68939e2cc29f05e066e494a1874e0cbb5bf [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/ddi-physical-layer-manager.h"
#include <lib/mmio/mmio-buffer.h>
#include <fake-mmio-reg/fake-mmio-reg.h>
#include <gtest/gtest.h>
#include <mock-mmio-range/mock-mmio-range.h>
#include "src/graphics/display/drivers/intel-i915/ddi-physical-layer.h"
#include "src/graphics/display/drivers/intel-i915/hardware-common.h"
#include "src/graphics/display/drivers/intel-i915/igd.h"
namespace i915 {
namespace {
const std::unordered_map<PowerWellId, PowerWellInfo> kPowerWellInfoTestDevice = {};
// A fake power well implementation used only for tests.
class TestPower : public Power {
public:
explicit TestPower(fdf::MmioBuffer* mmio_space) : Power(mmio_space, &kPowerWellInfoTestDevice) {}
void Resume() override {}
PowerWellRef GetCdClockPowerWellRef() override { return PowerWellRef(); }
PowerWellRef GetPipePowerWellRef(PipeId pipe_id) override { return PowerWellRef(); }
PowerWellRef GetDdiPowerWellRef(DdiId ddi_id) override { return PowerWellRef(); }
bool GetDdiIoPowerState(DdiId ddi_id) override { return true; }
void SetDdiIoPowerState(DdiId ddi_id, bool enable) override {}
bool GetAuxIoPowerState(DdiId ddi_id) override { return true; }
void SetAuxIoPowerState(DdiId ddi_id, bool enable) override {}
private:
void SetPowerWell(PowerWellId power_well, bool enable) override {}
std::unordered_map<DdiId, bool> aux_state_;
};
// A fake DdiPhysicalLayer that tracks if a DDI has been enabled / disabled.
class TestDdi : public DdiPhysicalLayer {
public:
explicit TestDdi(DdiId ddi_id) : DdiPhysicalLayer(ddi_id) {}
~TestDdi() override = default;
bool IsEnabled() const override { return enabled_; }
bool IsHealthy() const override { return true; }
bool Enable() override {
enabled_ = enabled_ || can_enable_;
return enabled_;
}
bool Disable() override {
enabled_ = enabled_ && !can_disable_;
return !enabled_;
}
PhysicalLayerInfo GetPhysicalLayerInfo() const override { return PhysicalLayerInfo{}; }
void SetCanEnable(bool result) { can_enable_ = result; }
void SetCanDisable(bool result) { can_disable_ = result; }
private:
bool enabled_ = false;
bool can_enable_ = true;
bool can_disable_ = true;
};
// An instance of DdiManager which only creates `TestDdi` for DDIs.
// Used to test interfaces of `DdiManager`.
class TestDdiManager : public DdiManager {
public:
TestDdiManager() = default;
void AddDdi(DdiId ddi_id) { ddi_map()[ddi_id] = std::make_unique<TestDdi>(ddi_id); }
TestDdi* GetDdi(DdiId ddi_id) {
if (ddi_map().find(ddi_id) != ddi_map().end()) {
return static_cast<TestDdi*>(ddi_map().at(ddi_id).get());
}
return nullptr;
}
};
TEST(DdiManager, GetDdiReference_Success) {
TestDdiManager ddi_manager;
ddi_manager.AddDdi(DdiId::DDI_A);
ddi_manager.AddDdi(DdiId::DDI_B);
{
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_FALSE(ddi_a_reference.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
}
// Create and destroy multiple references to a single DDI
{
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference_1 = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_FALSE(ddi_a_reference_1.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference_2 = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_FALSE(ddi_a_reference_2.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
ddi_a_reference_1 = {};
EXPECT_TRUE(ddi_a_reference_1.IsNull());
EXPECT_FALSE(ddi_a_reference_2.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
ddi_a_reference_2 = {};
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
}
// DdiReference can be move-constructed
{
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference_1 = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_FALSE(ddi_a_reference_1.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference_2(std::move(ddi_a_reference_1));
EXPECT_FALSE(ddi_a_reference_2.IsNull());
EXPECT_TRUE(ddi_a_reference_1.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
}
// DdiReference can be move-assigned
{
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
DdiReference ddi_a_reference = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_FALSE(ddi_a_reference.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_B)->IsEnabled());
DdiReference ddi_b_reference = ddi_manager.GetDdiReference(DdiId::DDI_B);
EXPECT_FALSE(ddi_b_reference.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_B)->IsEnabled());
ddi_b_reference = std::move(ddi_a_reference);
EXPECT_TRUE(ddi_a_reference.IsNull());
EXPECT_FALSE(ddi_b_reference.IsNull());
EXPECT_TRUE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_B)->IsEnabled());
}
}
TEST(DdiManager, GetDdiReference_Failure_UnsupportedDdi) {
TestDdiManager ddi_manager;
ddi_manager.AddDdi(DdiId::DDI_A);
ddi_manager.AddDdi(DdiId::DDI_B);
EXPECT_DEATH(ddi_manager.GetDdiReference(DdiId::DDI_TC_1), "DDI .+ is not available");
}
TEST(DdiManager, GetDdiReference_Failure_DdiCannotEnable) {
TestDdiManager ddi_manager;
ddi_manager.AddDdi(DdiId::DDI_A);
ddi_manager.AddDdi(DdiId::DDI_B);
ddi_manager.GetDdi(DdiId::DDI_A)->SetCanEnable(false);
auto ddi_reference = ddi_manager.GetDdiReference(DdiId::DDI_A);
EXPECT_TRUE(ddi_reference.IsNull());
EXPECT_FALSE(ddi_manager.GetDdi(DdiId::DDI_A)->IsEnabled());
}
// For testing purpose only.
// This exposes `ddi_map` from `DdiManagerTigerLake`, so that tests can access
// each `DdiPhysicalLayer` directly to check if it is created correctly.
class DdiManagerTigerLakeForTesting : public DdiManagerTigerLake {
public:
DdiManagerTigerLakeForTesting(Power* power, fdf::MmioBuffer* mmio_space,
const IgdOpRegion& igd_opregion)
: DdiManagerTigerLake(power, mmio_space, igd_opregion) {}
using DdiManagerTigerLake::ddi_map;
};
constexpr int kPhyMiscAOffset = 0x64c00;
constexpr int kPhyMiscBOffset = 0x64c04;
constexpr int kPortClDw5BOffset = 0x6c014;
constexpr int kPortCompDw0BOffset = 0x6c100;
constexpr int kPortCompDw1BOffset = 0x6c104;
constexpr int kPortCompDw3BOffset = 0x6c10c;
constexpr int kPortCompDw8BOffset = 0x6c120;
constexpr int kPortCompDw9BOffset = 0x6c124;
constexpr int kPortCompDw10BOffset = 0x6c128;
constexpr int kPortPcsDw1AuxBOffset = 0x6c304;
constexpr int kPortTxDw8AuxBOffset = 0x6c3a0;
constexpr int kPortPcsDw1Ln0BOffset = 0x6c804;
constexpr int kPortTxDw8Ln0BOffset = 0x6c8a0;
constexpr int kPortPcsDw1Ln1BOffset = 0x6c904;
constexpr int kPortTxDw8Ln1BOffset = 0x6c9a0;
constexpr int kPortPcsDw1Ln2BOffset = 0x6ca04;
constexpr int kPortTxDw8Ln2BOffset = 0x6caa0;
constexpr int kPortPcsDw1Ln3BOffset = 0x6cb04;
constexpr int kPortTxDw8Ln3BOffset = 0x6cba0;
constexpr int kPortClDw5AOffset = 0x162014;
constexpr int kPortCompDw0AOffset = 0x162100;
constexpr int kPortCompDw1AOffset = 0x162104;
constexpr int kPortCompDw3AOffset = 0x16210c;
constexpr int kPortCompDw8AOffset = 0x162120;
constexpr int kPortCompDw9AOffset = 0x162124;
constexpr int kPortCompDw10AOffset = 0x162128;
constexpr int kPortPcsDw1AuxAOffset = 0x162304;
constexpr int kPortTxDw8AuxAOffset = 0x1623a0;
constexpr int kPortPcsDw1Ln0AOffset = 0x162804;
constexpr int kPortTxDw8Ln0AOffset = 0x1628a0;
constexpr int kPortPcsDw1Ln1AOffset = 0x162904;
constexpr int kPortTxDw8Ln1AOffset = 0x1629a0;
constexpr int kPortPcsDw1Ln2AOffset = 0x162a04;
constexpr int kPortTxDw8Ln2AOffset = 0x162aa0;
constexpr int kPortPcsDw1Ln3AOffset = 0x162b04;
constexpr int kPortTxDw8Ln3AOffset = 0x162ba0;
TEST(DdiManagerTigerLake, ParseVbtTable_Dell5420) {
// On Dell Latitude 5420, there are 4 DDIs:
// - DDI_A: COMBO eDP port
// - DDI_B: COMBO HDMI port
// - DDI_TC_1: Type-C DDI with Type-C port
// - DDI_TC_2:Type-C DDI with Type-C port
constexpr static int kMmioRangeSize = 0x200000;
ddk_mock::MockMmioRange mmio_range{kMmioRangeSize, ddk_mock::MockMmioRange::Size::k32};
fdf::MmioBuffer mmio_buffer{mmio_range.GetMmioBuffer()};
mmio_range.Expect(ddk_mock::MockMmioRange::AccessList({
// Access pattern from ComboDdiTigerLakeTest.InitializeDdiADell5420.
{.address = kPortCompDw3AOffset, .value = 0xc0606b25},
{.address = kPortCompDw1AOffset, .value = 0x81000400},
{.address = kPortCompDw9AOffset, .value = 0x62ab67bb},
{.address = kPortCompDw10AOffset, .value = 0x51914f96},
{.address = kPortClDw5AOffset, .value = 0x1204047b},
{.address = kPortTxDw8AuxAOffset, .value = 0x30037c9c},
{.address = kPortPcsDw1AuxAOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln0AOffset, .value = 0x300335dc},
{.address = kPortPcsDw1Ln0AOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln1AOffset, .value = 0x3003379c},
{.address = kPortPcsDw1Ln1AOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln2AOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln2AOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln3AOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln3AOffset, .value = 0x1c300004},
{.address = kPhyMiscAOffset, .value = 0x23000000},
{.address = kPortCompDw8AOffset, .value = 0x010d0280},
{.address = kPortCompDw0AOffset, .value = 0x80005f25},
// Access pattern from ComboDdiTigerLakeTest.InitializeDdiBDell5420.
{.address = kPortCompDw3BOffset, .value = 0xc0606b25},
{.address = kPortCompDw1BOffset, .value = 0x81000400},
{.address = kPortCompDw9BOffset, .value = 0x62ab67bb},
{.address = kPortCompDw10BOffset, .value = 0x51914f96},
{.address = kPortClDw5BOffset, .value = 0x12040478},
{.address = kPortTxDw8AuxBOffset, .value = 0x3003501c},
{.address = kPortPcsDw1AuxBOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln0BOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln0BOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln1BOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln1BOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln2BOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln2BOffset, .value = 0x1c300004},
{.address = kPortTxDw8Ln3BOffset, .value = 0x3003501c},
{.address = kPortPcsDw1Ln3BOffset, .value = 0x1c300004},
{.address = kPhyMiscBOffset, .value = 0x23000000},
{.address = kPortCompDw8BOffset, .value = 0x000d0280},
{.address = kPortCompDw0BOffset, .value = 0x80005f26},
}));
TestPower power(&mmio_buffer);
IgdOpRegion test_igd_opregion;
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_A, true);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_A, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_A, false);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_B, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_B, false);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_B, false);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_1, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_1, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_1, true);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_2, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_2, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_2, true);
DdiManagerTigerLakeForTesting ddi_manager(&power, &mmio_buffer, test_igd_opregion);
const auto& ddi_map = ddi_manager.ddi_map();
// Verify if all DDIs are correctly created.
EXPECT_EQ(ddi_map.size(), 4u);
EXPECT_NE(ddi_map.find(DdiId::DDI_A), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_B), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_1), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_2), ddi_map.end());
// Verify DDI physical layer information.
auto ddi_a_info = ddi_map.at(DdiId::DDI_A)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_a_info.ddi_type, DdiPhysicalLayer::DdiType::kCombo);
EXPECT_EQ(ddi_a_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_b_info = ddi_map.at(DdiId::DDI_B)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_b_info.ddi_type, DdiPhysicalLayer::DdiType::kCombo);
EXPECT_EQ(ddi_b_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_tc_1_info = ddi_map.at(DdiId::DDI_TC_1)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_1_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_NE(ddi_tc_1_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_tc_2_info = ddi_map.at(DdiId::DDI_TC_2)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_2_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_NE(ddi_tc_2_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
}
TEST(DdiManagerTigerLake, ParseVbtTable_NUC11PAHi5) {
// On Intel NUC11PAHi5, there are 4 DDIs:
// - DDI_TC_1: Type-C DDI with Type-C port
// - DDI_TC_3: Type-C DDI with built-in HDMI Port
// - DDI_TC_4: Type-C DDI with built-in DisplayPort
// - DDI_TC_6: Type-C DDI with Type-C port
constexpr static int kMmioRangeSize = 0x200000;
ddk_mock::MockMmioRange mmio_range{kMmioRangeSize, ddk_mock::MockMmioRange::Size::k32};
fdf::MmioBuffer mmio_buffer{mmio_range.GetMmioBuffer()};
TestPower power(&mmio_buffer);
IgdOpRegion test_igd_opregion;
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_1, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_1, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_1, true);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_3, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_3, false);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_3, false);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_4, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_4, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_4, false);
test_igd_opregion.SetIsEdpForTesting(DdiId::DDI_TC_6, false);
test_igd_opregion.SetSupportsDpForTesting(DdiId::DDI_TC_6, true);
test_igd_opregion.SetIsTypeCForTesting(DdiId::DDI_TC_6, true);
DdiManagerTigerLakeForTesting ddi_manager(&power, &mmio_buffer, test_igd_opregion);
const auto& ddi_map = ddi_manager.ddi_map();
// Verify if all DDIs are correctly created.
EXPECT_EQ(ddi_map.size(), 4u);
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_1), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_3), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_4), ddi_map.end());
EXPECT_NE(ddi_map.find(DdiId::DDI_TC_6), ddi_map.end());
// Verify DDI physical layer information.
auto ddi_tc_1_info = ddi_map.at(DdiId::DDI_TC_1)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_1_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_NE(ddi_tc_1_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_tc_3_info = ddi_map.at(DdiId::DDI_TC_3)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_3_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_EQ(ddi_tc_3_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_tc_4_info = ddi_map.at(DdiId::DDI_TC_4)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_4_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_EQ(ddi_tc_4_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
auto ddi_tc_6_info = ddi_map.at(DdiId::DDI_TC_6)->GetPhysicalLayerInfo();
EXPECT_EQ(ddi_tc_6_info.ddi_type, DdiPhysicalLayer::DdiType::kTypeC);
EXPECT_NE(ddi_tc_6_info.connection_type, DdiPhysicalLayer::ConnectionType::kBuiltIn);
}
} // namespace
} // namespace i915