mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
drm/amd/display: Test EDID and ACPI/VBIOS readers
[WHAT] Add KUnit coverage for the EDID parsing helpers edid_extract_panel_id, apply_edid_quirks and dm_helpers_parse_edid_caps, together with the ACPI/VBIOS EDID readers dm_helpers_probe_acpi_edid, dm_helpers_read_acpi_edid and dm_helpers_read_vbios_hardcoded_edid. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: George Zhang <george.zhang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
@@ -98,7 +98,7 @@ STATIC_IFN_KUNIT u32 edid_extract_panel_id(struct edid *edid)
|
||||
}
|
||||
EXPORT_IF_KUNIT(edid_extract_panel_id);
|
||||
|
||||
static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
struct dc_edid_caps *edid_caps)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = link->priv;
|
||||
@@ -141,6 +141,7 @@ static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(apply_edid_quirks);
|
||||
|
||||
/**
|
||||
* dm_helpers_parse_edid_caps() - Parse edid caps
|
||||
@@ -237,6 +238,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_parse_edid_caps);
|
||||
|
||||
static void
|
||||
fill_dc_mst_payload_table_from_drm(struct dc_link *link,
|
||||
@@ -979,6 +981,20 @@ bool dm_helpers_dp_write_dsc_enable(
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
uint dm_helpers_get_dc_debug_mask(void)
|
||||
{
|
||||
return amdgpu_dc_debug_mask;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_get_dc_debug_mask);
|
||||
|
||||
void dm_helpers_set_dc_debug_mask(uint debug_mask)
|
||||
{
|
||||
amdgpu_dc_debug_mask = debug_mask;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_set_dc_debug_mask);
|
||||
#endif
|
||||
|
||||
bool dm_helpers_dp_write_hblank_reduction(struct dc_context *ctx, const struct dc_stream_state *stream)
|
||||
{
|
||||
// TODO
|
||||
@@ -1002,7 +1018,7 @@ bool dm_helpers_is_dp_sink_present(struct dc_link *link)
|
||||
return dp_sink_present;
|
||||
}
|
||||
|
||||
static int
|
||||
STATIC_IFN_KUNIT int
|
||||
dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
@@ -1040,8 +1056,9 @@ dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_probe_acpi_edid);
|
||||
|
||||
static const struct drm_edid *
|
||||
STATIC_IFN_KUNIT const struct drm_edid *
|
||||
dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -1062,8 +1079,9 @@ dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector)
|
||||
|
||||
return drm_edid_read_custom(connector, dm_helpers_probe_acpi_edid, connector);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_read_acpi_edid);
|
||||
|
||||
static const struct drm_edid *
|
||||
STATIC_IFN_KUNIT const struct drm_edid *
|
||||
dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct dc_bios *bios = link->ctx->dc_bios;
|
||||
@@ -1101,6 +1119,7 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn
|
||||
|
||||
return edid;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_read_vbios_hardcoded_edid);
|
||||
|
||||
STATIC_IFN_KUNIT uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
|
||||
{
|
||||
|
||||
@@ -9,12 +9,31 @@
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
struct amdgpu_dm_connector;
|
||||
struct dc_link;
|
||||
struct dc_context;
|
||||
struct dc_stream_state;
|
||||
struct dc_edid_caps;
|
||||
struct dc_dp_mst_stream_allocation_table;
|
||||
struct drm_dp_aux;
|
||||
struct drm_dp_mst_atomic_payload;
|
||||
struct drm_dp_mst_topology_mgr;
|
||||
struct drm_dp_mst_topology_state;
|
||||
|
||||
/* Exported for KUnit testing */
|
||||
u32 edid_extract_panel_id(struct edid *edid);
|
||||
void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
struct dc_edid_caps *edid_caps);
|
||||
uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane);
|
||||
bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id);
|
||||
extern const uint32_t dm_freesync_pcon_whitelist[];
|
||||
uint32_t dm_freesync_pcon_whitelist_count(void);
|
||||
uint dm_helpers_get_dc_debug_mask(void);
|
||||
void dm_helpers_set_dc_debug_mask(uint debug_mask);
|
||||
int dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len);
|
||||
const struct drm_edid *dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector);
|
||||
const struct drm_edid *dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link,
|
||||
struct amdgpu_dm_connector *aconnector);
|
||||
#endif /* CONFIG_DRM_AMD_DC_KUNIT_TEST */
|
||||
|
||||
#endif /* __AMDGPU_DM_HELPERS_H__ */
|
||||
|
||||
@@ -8,13 +8,18 @@
|
||||
#include <kunit/test.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/display/drm_dp_mst_helper.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "core_types.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
#include "amdgpu_dm_mst_types.h"
|
||||
#include "dc_bios_types.h"
|
||||
#include "dm_helpers.h"
|
||||
#include "ddc_service_types.h"
|
||||
#include "dmub_cmd.h"
|
||||
#include "amdgpu_dm_helpers.h"
|
||||
#include "amdgpu_dm_kunit_test_helpers.h"
|
||||
|
||||
@@ -61,6 +66,701 @@ static void dm_test_edid_extract_panel_id_zeros(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, edid_extract_panel_id(edid), 0U);
|
||||
}
|
||||
|
||||
/* Tests for apply_edid_quirks() */
|
||||
|
||||
/*
|
||||
* Build an EDID whose extracted panel id equals @panel_id. Inverse of
|
||||
* edid_extract_panel_id(): mfg_id holds the top 16 bits, prod_code the
|
||||
* low 16 bits (prod_code[0] | prod_code[1] << 8).
|
||||
*/
|
||||
static struct edid *dm_test_edid_with_panel_id(struct kunit *test, u32 panel_id)
|
||||
{
|
||||
struct edid *edid;
|
||||
|
||||
edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid);
|
||||
|
||||
edid->mfg_id[0] = (panel_id >> 24) & 0xff;
|
||||
edid->mfg_id[1] = (panel_id >> 16) & 0xff;
|
||||
edid->prod_code[0] = panel_id & 0xff;
|
||||
edid->prod_code[1] = (panel_id >> 8) & 0xff;
|
||||
|
||||
return edid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wire a connector-backed link so apply_edid_quirks() can resolve
|
||||
* link->priv->base.dev for its drm_dbg_driver() calls.
|
||||
*/
|
||||
static struct dc_link *dm_test_quirk_link(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_link *link;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
link = dm_kunit_alloc_link(test);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
link->priv = aconnector;
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_dpcd_poweroff_delay - Test GBT 0x3215 delay quirk
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_dpcd_poweroff_delay(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('G', 'B', 'T', 0x3215));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, edid_caps.panel_patch.wait_after_dpcd_poweroff_ms, 10000U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_disable_fams - Test SAM panel FAMS-disable quirk
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_disable_fams(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('S', 'A', 'M', 0x0E5E));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps.panel_patch.disable_fams);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_remove_sink_ext_caps - Test AUO 0x317 clear quirk
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_remove_sink_ext_caps(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('A', 'U', 'O', 0xA7AB));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps.panel_patch.remove_sink_ext_caps);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_disable_colorimetry - Test SDC VSC-disable quirk
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_disable_colorimetry(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('S', 'D', 'C', 0x4154));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps.panel_patch.disable_colorimetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_skip_phy_ssc - Test DEL 0x4147 PHY SSC quirk
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_skip_phy_ssc(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('D', 'E', 'L', 0x4147));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, link->wa_flags.skip_phy_ssc_reduction);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_apply_edid_quirks_unknown_noop - Test unknown panel id is a no-op
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_apply_edid_quirks_unknown_noop(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct edid *edid = dm_test_edid_with_panel_id(test,
|
||||
drm_edid_encode_panel_id('X', 'Y', 'Z', 0x0000));
|
||||
|
||||
apply_edid_quirks(link, edid, &edid_caps);
|
||||
|
||||
/* default: branch leaves every quirk field untouched */
|
||||
KUNIT_EXPECT_EQ(test, edid_caps.panel_patch.wait_after_dpcd_poweroff_ms, 0U);
|
||||
KUNIT_EXPECT_FALSE(test, edid_caps.panel_patch.disable_fams);
|
||||
KUNIT_EXPECT_FALSE(test, edid_caps.panel_patch.remove_sink_ext_caps);
|
||||
KUNIT_EXPECT_FALSE(test, edid_caps.panel_patch.disable_colorimetry);
|
||||
KUNIT_EXPECT_FALSE(test, link->wa_flags.skip_phy_ssc_reduction);
|
||||
}
|
||||
|
||||
/* Tests for dm_helpers_parse_edid_caps() */
|
||||
|
||||
/*
|
||||
* Build a minimal valid base EDID block into @dc_edid. When @good_checksum is
|
||||
* false the final checksum byte is corrupted so drm_edid_is_valid() fails.
|
||||
*
|
||||
* manufacturer_id = 0xAC10, product_id = 0x1234, serial = 0x12345678,
|
||||
* week = 10, year (raw) = 30, digital input, no CEA extension.
|
||||
*/
|
||||
static void dm_test_fill_base_edid(struct dc_edid *dc_edid, bool good_checksum)
|
||||
{
|
||||
static const u8 header[8] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
||||
};
|
||||
u8 *raw = dc_edid->raw_edid;
|
||||
int sum = 0;
|
||||
int i;
|
||||
|
||||
memset(raw, 0, EDID_LENGTH);
|
||||
memcpy(raw, header, sizeof(header));
|
||||
|
||||
raw[8] = 0x10; raw[9] = 0xAC; /* mfg_id -> 0xAC10 */
|
||||
raw[10] = 0x34; raw[11] = 0x12; /* prod_code -> 0x1234 */
|
||||
raw[12] = 0x78; raw[13] = 0x56; /* serial -> 0x12345678 */
|
||||
raw[14] = 0x34; raw[15] = 0x12;
|
||||
raw[16] = 10; /* mfg_week */
|
||||
raw[17] = 30; /* mfg_year (raw) */
|
||||
raw[18] = 1; /* version */
|
||||
raw[19] = 4; /* revision */
|
||||
raw[20] = DRM_EDID_INPUT_DIGITAL; /* digital input */
|
||||
raw[126] = 0; /* no extensions */
|
||||
|
||||
for (i = 0; i < EDID_LENGTH - 1; i++)
|
||||
sum += raw[i];
|
||||
raw[127] = (256 - (sum % 256)) % 256;
|
||||
if (!good_checksum)
|
||||
raw[127] ^= 0xFF; /* corrupt checksum */
|
||||
|
||||
dc_edid->length = EDID_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_null_edid - Test NULL edid returns EDID_BAD_INPUT
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_null_edid(struct kunit *test)
|
||||
{
|
||||
struct dc_edid_caps edid_caps = {0};
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, NULL, &edid_caps), EDID_BAD_INPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_null_caps - Test NULL edid_caps returns EDID_BAD_INPUT
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_null_caps(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct dc_edid *dc_edid;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, NULL), EDID_BAD_INPUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_valid - Test field extraction from a valid EDID
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_valid(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->manufacturer_id, 0xAC10);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->product_id, 0x1234);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->serial_number, 0x12345678U);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->manufacture_week, 10);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->manufacture_year, 30);
|
||||
KUNIT_EXPECT_FALSE(test, edid_caps->analog);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_bad_checksum - Test bad checksum still parses fields
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_bad_checksum(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
|
||||
dm_test_fill_base_edid(dc_edid, false);
|
||||
|
||||
/* Invalid checksum -> EDID_BAD_CHECKSUM but fields are still parsed */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
dm_helpers_parse_edid_caps(link, dc_edid, edid_caps),
|
||||
EDID_BAD_CHECKSUM);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->manufacturer_id, 0xAC10);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->product_id, 0x1234);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_hdmi_frl - Test HDMI/FRL branch via connector info
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Drives the edid_caps->edid_hdmi path by marking the connector as HDMI and
|
||||
* providing a fake dc with FRL enabled, so populate_hdmi_info_from_connector()
|
||||
* is exercised inside dm_helpers_parse_edid_caps().
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_hdmi_frl(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct amdgpu_dm_connector *aconnector = link->priv;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
struct dc *dc;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
link->dc = dc;
|
||||
dc->config.enable_frl = true;
|
||||
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
|
||||
/* Drive the HDMI/FRL branch */
|
||||
connector->display_info.is_hdmi = true;
|
||||
connector->display_info.hdmi.scdc.supported = true;
|
||||
connector->display_info.hdmi.max_lanes = 4;
|
||||
connector->display_info.hdmi.max_frl_rate_per_lane = 12;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK);
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps->edid_hdmi);
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps->scdc_present);
|
||||
/* max_lanes 4 + max_frl_rate_per_lane 12 -> rate index 6 */
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->max_frl_rate, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_hdmi_frl_dsc - Test HDMI FRL DSC sub-branch
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Sets the connector's HDMI DSC capability so populate_hdmi_info_from_connector()
|
||||
* reports frl_dsc_support, exercising the frl_dsc_support log path.
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_hdmi_frl_dsc(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct amdgpu_dm_connector *aconnector = link->priv;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
struct dc *dc;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
link->dc = dc;
|
||||
dc->config.enable_frl = true;
|
||||
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
|
||||
connector->display_info.is_hdmi = true;
|
||||
/* Drive the frl_dsc_support sub-branch */
|
||||
connector->display_info.hdmi.dsc_cap.v_1p2 = true;
|
||||
connector->display_info.hdmi.dsc_cap.bpc_supported = 10;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK);
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps->frl_dsc_support);
|
||||
KUNIT_EXPECT_TRUE(test, edid_caps->frl_dsc_10bpc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a 2-block EDID: a valid base block plus a CTA-861 extension block
|
||||
* carrying one LPCM Short Audio Descriptor and, when @with_speaker is set, a
|
||||
* Speaker Allocation Data Block, so the audio/speaker parsing code is
|
||||
* exercised.
|
||||
*/
|
||||
static void dm_test_fill_cea_edid(struct dc_edid *dc_edid, bool with_speaker)
|
||||
{
|
||||
u8 *raw = dc_edid->raw_edid;
|
||||
u8 *ext = raw + EDID_LENGTH;
|
||||
u8 dtd_offset;
|
||||
int sum = 0;
|
||||
int i;
|
||||
|
||||
/* Base block, flagged as having one extension */
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
raw[126] = 1;
|
||||
for (i = 0; i < EDID_LENGTH - 1; i++)
|
||||
sum += raw[i];
|
||||
raw[127] = (256 - (sum % 256)) % 256;
|
||||
|
||||
/* Audio DB spans [4,7]; optional Speaker Alloc DB spans [8,11] */
|
||||
dtd_offset = with_speaker ? 12 : 8;
|
||||
|
||||
/* CTA-861 extension block */
|
||||
memset(ext, 0, EDID_LENGTH);
|
||||
ext[0] = 0x02; /* CTA extension tag */
|
||||
ext[1] = 0x03; /* revision 3 */
|
||||
ext[2] = dtd_offset; /* DTD offset; end of data blocks */
|
||||
ext[3] = 0x00; /* no native DTDs / feature flags */
|
||||
/* Audio Data Block: tag 1, length 3, one LPCM SAD */
|
||||
ext[4] = (1 << 5) | 3;
|
||||
ext[5] = (1 << 3) | 1; /* format 1 (LPCM), 2 channels */
|
||||
ext[6] = 0x07; /* 32 / 44.1 / 48 kHz */
|
||||
ext[7] = 0x07; /* 16 / 20 / 24-bit */
|
||||
if (with_speaker) {
|
||||
/* Speaker Allocation Data Block: tag 4, length 3 */
|
||||
ext[8] = (4 << 5) | 3;
|
||||
ext[9] = 0x01; /* front left / front right */
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < EDID_LENGTH - 1; i++)
|
||||
sum += ext[i];
|
||||
ext[127] = (256 - (sum % 256)) % 256;
|
||||
|
||||
dc_edid->length = 2 * EDID_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_cea_audio - Test CEA audio/speaker block parsing
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_cea_audio(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
|
||||
dm_test_fill_cea_edid(dc_edid, true);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_mode_count, 1);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_modes[0].format_code, 1);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_modes[0].channel_count, 2);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_modes[0].sample_rate, 0x07);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_modes[0].sample_size, 0x07);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->speaker_flags, 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_edid_caps_cea_no_speaker - Test default speaker flags path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Audio data block present but no Speaker Allocation Data Block, so
|
||||
* speaker_flags falls back to DEFAULT_SPEAKER_LOCATION.
|
||||
*/
|
||||
static void dm_test_parse_edid_caps_cea_no_speaker(struct kunit *test)
|
||||
{
|
||||
struct dc_link *link = dm_test_quirk_link(test);
|
||||
struct dc_edid_caps *edid_caps;
|
||||
struct dc_edid *dc_edid;
|
||||
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
edid_caps = kunit_kzalloc(test, sizeof(*edid_caps), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid_caps);
|
||||
|
||||
dm_test_fill_cea_edid(dc_edid, false);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->audio_mode_count, 1);
|
||||
KUNIT_EXPECT_EQ(test, edid_caps->speaker_flags, DEFAULT_SPEAKER_LOCATION);
|
||||
}
|
||||
|
||||
/* Tests for ACPI and VBIOS EDID readers */
|
||||
|
||||
/**
|
||||
* dm_test_probe_acpi_edid_no_companion - Test ACPI probe without companion
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_probe_acpi_edid_no_companion(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
u8 buf[EDID_LENGTH] = {0};
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
dm_helpers_probe_acpi_edid(&aconnector->base, buf, 0, sizeof(buf)),
|
||||
-ENODEV);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_acpi_edid_debug_mask_disabled - Test debug mask early return
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_acpi_edid_debug_mask_disabled(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
uint old_debug_mask;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
old_debug_mask = dm_helpers_get_dc_debug_mask();
|
||||
|
||||
dm_helpers_set_dc_debug_mask(old_debug_mask | DC_DISABLE_ACPI_EDID);
|
||||
KUNIT_EXPECT_NULL(test, dm_helpers_read_acpi_edid(aconnector));
|
||||
dm_helpers_set_dc_debug_mask(old_debug_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_acpi_edid_non_panel_connector - Test non-panel connector skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_acpi_edid_non_panel_connector(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
|
||||
KUNIT_EXPECT_NULL(test, dm_helpers_read_acpi_edid(aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_acpi_edid_force_off - Test forced-off connector skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_acpi_edid_force_off(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
aconnector->base.force = DRM_FORCE_OFF;
|
||||
|
||||
KUNIT_EXPECT_NULL(test, dm_helpers_read_acpi_edid(aconnector));
|
||||
}
|
||||
|
||||
struct dm_test_vbios_edid {
|
||||
struct dc_bios bios;
|
||||
enum bp_result result;
|
||||
const u8 *fake_edid;
|
||||
u16 fake_edid_size;
|
||||
u16 width_mm;
|
||||
u16 height_mm;
|
||||
};
|
||||
|
||||
static enum bp_result dm_test_get_embedded_panel_info(struct dc_bios *bios,
|
||||
struct embedded_panel_info *info)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
|
||||
fixture = container_of(bios, struct dm_test_vbios_edid, bios);
|
||||
if (fixture->result != BP_RESULT_OK)
|
||||
return fixture->result;
|
||||
|
||||
info->fake_edid = fixture->fake_edid;
|
||||
info->fake_edid_size = fixture->fake_edid_size;
|
||||
info->panel_width_mm = fixture->width_mm;
|
||||
info->panel_height_mm = fixture->height_mm;
|
||||
|
||||
return BP_RESULT_OK;
|
||||
}
|
||||
|
||||
static const struct dc_vbios_funcs dm_test_vbios_edid_funcs = {
|
||||
.get_embedded_panel_info = dm_test_get_embedded_panel_info,
|
||||
};
|
||||
|
||||
static void dm_test_setup_vbios_link(struct kunit *test,
|
||||
struct dc_link **link_out,
|
||||
struct amdgpu_dm_connector **aconnector_out,
|
||||
struct dm_test_vbios_edid **fixture_out)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
link = dm_kunit_alloc_link(test);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx);
|
||||
fixture = kunit_kzalloc(test, sizeof(*fixture), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, fixture);
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, link);
|
||||
|
||||
fixture->bios.funcs = &dm_test_vbios_edid_funcs;
|
||||
fixture->result = BP_RESULT_OK;
|
||||
ctx->dc_bios = &fixture->bios;
|
||||
link->ctx = ctx;
|
||||
link->priv = aconnector;
|
||||
link->connector_signal = SIGNAL_TYPE_EDP;
|
||||
|
||||
*link_out = link;
|
||||
*aconnector_out = aconnector;
|
||||
*fixture_out = fixture;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_non_embedded - Test non-embedded signal skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_non_embedded(struct kunit *test)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
dm_helpers_read_vbios_hardcoded_edid(link, aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_missing_callback - Test missing callback skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_missing_callback(struct kunit *test)
|
||||
{
|
||||
static const struct dc_vbios_funcs empty_funcs;
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
fixture->bios.funcs = &empty_funcs;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
dm_helpers_read_vbios_hardcoded_edid(link, aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_callback_error - Test callback failure skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_callback_error(struct kunit *test)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
fixture->result = BP_RESULT_BADINPUT;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
dm_helpers_read_vbios_hardcoded_edid(link, aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_missing_fake_edid - Test missing fake EDID skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_missing_fake_edid(struct kunit *test)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
fixture->fake_edid = NULL;
|
||||
fixture->fake_edid_size = 0;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
dm_helpers_read_vbios_hardcoded_edid(link, aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_invalid_fake_edid - Test invalid fake EDID skip
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_invalid_fake_edid(struct kunit *test)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct dc_edid *dc_edid;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
dm_test_fill_base_edid(dc_edid, false);
|
||||
fixture->fake_edid = dc_edid->raw_edid;
|
||||
fixture->fake_edid_size = EDID_LENGTH;
|
||||
|
||||
KUNIT_EXPECT_NULL(test,
|
||||
dm_helpers_read_vbios_hardcoded_edid(link, aconnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_read_vbios_edid_valid - Test valid fake EDID updates display size
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_read_vbios_edid_valid(struct kunit *test)
|
||||
{
|
||||
struct dm_test_vbios_edid *fixture;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
const struct drm_edid *edid;
|
||||
struct dc_edid *dc_edid;
|
||||
struct dc_link *link;
|
||||
|
||||
dm_test_setup_vbios_link(test, &link, &aconnector, &fixture);
|
||||
dc_edid = kunit_kzalloc(test, sizeof(*dc_edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc_edid);
|
||||
dm_test_fill_base_edid(dc_edid, true);
|
||||
fixture->fake_edid = dc_edid->raw_edid;
|
||||
fixture->fake_edid_size = EDID_LENGTH;
|
||||
fixture->width_mm = 301;
|
||||
fixture->height_mm = 201;
|
||||
|
||||
edid = dm_helpers_read_vbios_hardcoded_edid(link, aconnector);
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->base.display_info.width_mm, 301U);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->base.display_info.height_mm, 201U);
|
||||
drm_edid_free(edid);
|
||||
}
|
||||
|
||||
/* Tests for dm_is_freesync_pcon_whitelist() */
|
||||
|
||||
/**
|
||||
@@ -581,6 +1281,33 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
|
||||
/* edid_extract_panel_id */
|
||||
KUNIT_CASE(dm_test_edid_extract_panel_id_basic),
|
||||
KUNIT_CASE(dm_test_edid_extract_panel_id_zeros),
|
||||
/* apply_edid_quirks */
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_dpcd_poweroff_delay),
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_disable_fams),
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_remove_sink_ext_caps),
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_disable_colorimetry),
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_skip_phy_ssc),
|
||||
KUNIT_CASE(dm_test_apply_edid_quirks_unknown_noop),
|
||||
/* dm_helpers_parse_edid_caps */
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_null_edid),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_null_caps),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_valid),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_bad_checksum),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_hdmi_frl),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_hdmi_frl_dsc),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_cea_audio),
|
||||
KUNIT_CASE(dm_test_parse_edid_caps_cea_no_speaker),
|
||||
/* ACPI / VBIOS / local EDID readers */
|
||||
KUNIT_CASE(dm_test_probe_acpi_edid_no_companion),
|
||||
KUNIT_CASE(dm_test_read_acpi_edid_debug_mask_disabled),
|
||||
KUNIT_CASE(dm_test_read_acpi_edid_non_panel_connector),
|
||||
KUNIT_CASE(dm_test_read_acpi_edid_force_off),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_non_embedded),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_missing_callback),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_callback_error),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_missing_fake_edid),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_invalid_fake_edid),
|
||||
KUNIT_CASE(dm_test_read_vbios_edid_valid),
|
||||
/* dm_is_freesync_pcon_whitelist */
|
||||
KUNIT_CASE(dm_test_freesync_pcon_whitelist_all_known),
|
||||
KUNIT_CASE(dm_test_freesync_pcon_whitelist_not_in_list),
|
||||
|
||||
Reference in New Issue
Block a user