mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 21:39:35 -04:00
drm/amd/display: Add i2c and EDID parsing tests for connector
Add KUnit coverage for the i2c and EDID parsing helpers: amdgpu_dm_i2c_func(), amdgpu_dm_i2c_xfer(), get_amd_vsdb(), parse_hdmi_amd_vsdb() and parse_edid_displayid_vrr(). Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Wayne Lin <wayne.lin@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
committed by
Alex Deucher
parent
46011ff577
commit
6be28c11dc
@@ -3087,7 +3087,7 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
|
||||
}
|
||||
}
|
||||
|
||||
static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
|
||||
@@ -3131,11 +3131,13 @@ static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
kfree(cmd.payloads);
|
||||
return result;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_i2c_xfer);
|
||||
|
||||
static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
|
||||
STATIC_IFN_KUNIT u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_i2c_func);
|
||||
|
||||
static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
|
||||
.master_xfer = amdgpu_dm_i2c_xfer,
|
||||
@@ -3496,7 +3498,7 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void parse_edid_displayid_vrr(struct drm_connector *connector,
|
||||
STATIC_IFN_KUNIT void parse_edid_displayid_vrr(struct drm_connector *connector,
|
||||
const struct edid *edid)
|
||||
{
|
||||
u8 *edid_ext = NULL;
|
||||
@@ -3538,8 +3540,9 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector,
|
||||
j++;
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(parse_edid_displayid_vrr);
|
||||
|
||||
static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
STATIC_IFN_KUNIT int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
struct amdgpu_hdmi_vsdb_info *vsdb_info)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -3549,8 +3552,9 @@ static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
|
||||
return connector->display_info.amd_vsdb.version != 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(get_amd_vsdb);
|
||||
|
||||
static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
STATIC_IFN_KUNIT int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
const struct edid *edid,
|
||||
struct amdgpu_hdmi_vsdb_info *vsdb_info)
|
||||
{
|
||||
@@ -3581,6 +3585,7 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
|
||||
return valid_vsdb_found ? i : -ENODEV;
|
||||
}
|
||||
EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_update_freesync_caps - Update Freesync capabilities
|
||||
|
||||
@@ -146,6 +146,16 @@ int amdgpu_dm_encoder_init(struct drm_device *dev,
|
||||
uint32_t link_index);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg *msgs, int num);
|
||||
u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap);
|
||||
void parse_edid_displayid_vrr(struct drm_connector *connector,
|
||||
const struct edid *edid);
|
||||
int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
struct amdgpu_hdmi_vsdb_info *vsdb_info);
|
||||
int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
|
||||
const struct edid *edid,
|
||||
struct amdgpu_hdmi_vsdb_info *vsdb_info);
|
||||
void amdgpu_dm_connector_funcs_force(struct drm_connector *connector);
|
||||
enum dc_status dm_validate_stream_and_context(struct dc *dc,
|
||||
struct dc_stream_state *stream);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_property.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "amdgpu.h"
|
||||
@@ -4666,6 +4667,239 @@ static void dm_test_add_freesync_modes_null_edid_noop(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, aconnector->num_modes, 7);
|
||||
}
|
||||
|
||||
/* EDID extension block tag values (avoids pulling in private drm headers). */
|
||||
#define DM_TEST_CEA_EXT 0x02
|
||||
#define DM_TEST_DISPLAYID_EXT 0x70
|
||||
|
||||
/**
|
||||
* dm_test_i2c_func_returns_flags - Test the i2c functionality flags
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* The algorithm advertises plain I2C plus emulated SMBUS regardless of the
|
||||
* adapter argument, which it never dereferences.
|
||||
*/
|
||||
static void dm_test_i2c_func_returns_flags(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_func(NULL),
|
||||
I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_i2c_xfer_no_ddc_pin - Test transfers without a DDC pin are rejected
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When the backing ddc_service has no ddc_pin the transfer bails out early
|
||||
* with -EIO before touching the message buffers or the dc handle.
|
||||
*/
|
||||
static void dm_test_i2c_xfer_no_ddc_pin(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_i2c_adapter *i2c;
|
||||
struct ddc_service *ddc;
|
||||
|
||||
i2c = kunit_kzalloc(test, sizeof(*i2c), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, i2c);
|
||||
ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ddc);
|
||||
|
||||
i2c->ddc_service = ddc;
|
||||
i2c_set_adapdata(&i2c->base, i2c);
|
||||
|
||||
/* ddc->ddc_pin is NULL -> transfer is rejected with -EIO. */
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_xfer(&i2c->base, NULL, 0), -EIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_get_amd_vsdb_unsupported - Test a zero VSDB version reports no support
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_get_amd_vsdb_unsupported(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
|
||||
aconnector->base.display_info.amd_vsdb.version = 0;
|
||||
aconnector->base.display_info.amd_vsdb.replay_mode = false;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 0);
|
||||
KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_get_amd_vsdb_supported - Test a non-zero VSDB version is reported
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* The display info's VSDB version and replay mode are copied out and a
|
||||
* non-zero version reports support.
|
||||
*/
|
||||
static void dm_test_get_amd_vsdb_supported(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
|
||||
aconnector->base.display_info.amd_vsdb.version = 2;
|
||||
aconnector->base.display_info.amd_vsdb.replay_mode = true;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 1);
|
||||
KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 2);
|
||||
KUNIT_EXPECT_TRUE(test, vsdb_info.replay_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_hdmi_amd_vsdb_null_edid - Test NULL EDID returns -ENODEV
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_hdmi_amd_vsdb_null_edid(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
parse_hdmi_amd_vsdb(aconnector, NULL, &vsdb_info),
|
||||
-ENODEV);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_hdmi_amd_vsdb_no_extensions - Test EDID without extensions
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* An EDID that declares no extension blocks has no CEA block to parse.
|
||||
*/
|
||||
static void dm_test_parse_hdmi_amd_vsdb_no_extensions(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||
struct edid *edid;
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, edid);
|
||||
|
||||
edid->extensions = 0;
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info),
|
||||
-ENODEV);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_hdmi_amd_vsdb_no_cea_ext - Test EDID with no CEA extension
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* An extension block that is not a CEA block leaves no VSDB to parse.
|
||||
*/
|
||||
static void dm_test_parse_hdmi_amd_vsdb_no_cea_ext(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
|
||||
struct edid *edid;
|
||||
u8 *raw;
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
|
||||
/* Base block + one extension block that is NOT a CEA extension. */
|
||||
raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, raw);
|
||||
edid = (struct edid *)raw;
|
||||
edid->extensions = 1;
|
||||
raw[EDID_LENGTH] = DM_TEST_DISPLAYID_EXT;
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info),
|
||||
-ENODEV);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_displayid_vrr_null_edid - Test NULL EDID leaves range untouched
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_parse_displayid_vrr_null_edid(struct kunit *test)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
|
||||
parse_edid_displayid_vrr(connector, NULL);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 0);
|
||||
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_displayid_vrr_no_displayid - Test EDID without a DisplayID ext
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Without a DisplayID extension block there is no dynamic range to extract.
|
||||
*/
|
||||
static void dm_test_parse_displayid_vrr_no_displayid(struct kunit *test)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct edid *edid;
|
||||
u8 *raw;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, raw);
|
||||
edid = (struct edid *)raw;
|
||||
edid->extensions = 1;
|
||||
raw[EDID_LENGTH] = DM_TEST_CEA_EXT;
|
||||
|
||||
parse_edid_displayid_vrr(connector, edid);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_parse_displayid_vrr_sets_range - Test a DisplayID VRR block is parsed
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* A DisplayID dynamic video timing range descriptor populates the connector's
|
||||
* monitor refresh range.
|
||||
*/
|
||||
static void dm_test_parse_displayid_vrr_sets_range(struct kunit *test)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct edid *edid;
|
||||
u8 *raw, *ext;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, connector);
|
||||
raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, raw);
|
||||
edid = (struct edid *)raw;
|
||||
edid->extensions = 1;
|
||||
|
||||
ext = raw + EDID_LENGTH;
|
||||
ext[0] = DM_TEST_DISPLAYID_EXT;
|
||||
/*
|
||||
* DisplayID dynamic video timing range descriptor, parsed from offset
|
||||
* 1: tag 0x25, flags 0 (single-byte max), payload length 9, then the
|
||||
* min/max vfreq bytes.
|
||||
*/
|
||||
ext[1] = 0x25;
|
||||
ext[2] = 0x00;
|
||||
ext[3] = 9;
|
||||
ext[10] = 40;
|
||||
ext[11] = 144;
|
||||
|
||||
parse_edid_displayid_vrr(connector, edid);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 40);
|
||||
KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 144);
|
||||
}
|
||||
|
||||
static struct kunit_case amdgpu_dm_connector_tests[] = {
|
||||
/* get_subconnector_type */
|
||||
KUNIT_CASE(dm_test_subconnector_type_none),
|
||||
@@ -4917,6 +5151,21 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
|
||||
KUNIT_CASE(dm_test_add_fs_modes_no_preferred_mode),
|
||||
/* amdgpu_dm_connector_add_freesync_modes */
|
||||
KUNIT_CASE(dm_test_add_freesync_modes_null_edid_noop),
|
||||
/* amdgpu_dm_i2c_func */
|
||||
KUNIT_CASE(dm_test_i2c_func_returns_flags),
|
||||
/* amdgpu_dm_i2c_xfer */
|
||||
KUNIT_CASE(dm_test_i2c_xfer_no_ddc_pin),
|
||||
/* get_amd_vsdb */
|
||||
KUNIT_CASE(dm_test_get_amd_vsdb_unsupported),
|
||||
KUNIT_CASE(dm_test_get_amd_vsdb_supported),
|
||||
/* parse_hdmi_amd_vsdb */
|
||||
KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_null_edid),
|
||||
KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_extensions),
|
||||
KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_cea_ext),
|
||||
/* parse_edid_displayid_vrr */
|
||||
KUNIT_CASE(dm_test_parse_displayid_vrr_null_edid),
|
||||
KUNIT_CASE(dm_test_parse_displayid_vrr_no_displayid),
|
||||
KUNIT_CASE(dm_test_parse_displayid_vrr_sets_range),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user