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:
Alex Hung
2026-06-24 23:34:18 -06:00
committed by Alex Deucher
parent acf97c57d0
commit 50dd2baefb
3 changed files with 769 additions and 4 deletions

View File

@@ -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)
{

View File

@@ -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__ */

View File

@@ -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),