drm/amd/display: Add KUnit tests for CACP on Linux

[Why]
The CACP enablement on Linux added panel-type detection and CACP
capability logic that was not covered by KUnit tests.

[How]
Export amdgpu_dm_set_panel_type() and amdgpu_dm_update_cacp_caps() for
KUnit via STATIC_IFN_KUNIT/EXPORT_IF_KUNIT and add unit tests covering:
- amdgpu_dm_update_cacp_caps(): IP version gating (including the 3.1.6
  exclusion), eDP/LVDS signal handling, non-eDP signals, and OLED vs LCD
  panel types.
- amdgpu_dm_set_panel_type(): VSDB OLED/MINILED, DPCD oled/miniled bits,
  the DID path (OLED and LCD), the vendor luminance heuristic, and the
  LCD default.

Update the should_create_sysfs backlight tests to reflect the new
OLED/CACP behavior (OLED with/without CACP and LCD eDP panels).

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Chenyu Chen <chen-yu.chen@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:
Chenyu Chen
2026-07-14 19:12:35 -04:00
committed by Alex Deucher
parent 560cb7437c
commit 9dfd0da5e3
4 changed files with 392 additions and 10 deletions
@@ -392,7 +392,7 @@ amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
return NULL;
}
static void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
STATIC_IFN_KUNIT void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
{
struct drm_connector *connector = &aconnector->base;
struct drm_display_info *display_info = &connector->display_info;
@@ -459,8 +459,9 @@ static void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
}
EXPORT_IF_KUNIT(amdgpu_dm_set_panel_type);
static void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector)
STATIC_IFN_KUNIT void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector)
{
struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
struct dc_link *link = aconnector->dc_link;
@@ -485,6 +486,7 @@ static void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector)
drm_dbg_kms(aconnector->base.dev, "cacp_supported: %d\n",
link->panel_config.cacp.cacp_supported);
}
EXPORT_IF_KUNIT(amdgpu_dm_update_cacp_caps);
DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
@@ -158,5 +158,7 @@ enum dc_aspect_ratio get_aspect_ratio(const struct drm_display_mode *mode_in);
void decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
const struct drm_display_mode *native_mode,
bool scale_enabled);
void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector);
void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector);
#endif
#endif /* __AMDGPU_DM_CONNECTOR_H__ */
@@ -1034,10 +1034,13 @@ static void dm_test_should_create_sysfs_no_backlight_index(struct kunit *test)
}
/**
* dm_test_should_create_sysfs_aux_backlight - Test AUX backlight disables sysfs
* dm_test_should_create_sysfs_oled_no_cacp - Test OLED without CACP disables sysfs
* @test: The KUnit test context
*
* A non-LCD panel that does not support CACP must not expose the sysfs
* backlight interface.
*/
static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
static void dm_test_should_create_sysfs_oled_no_cacp(struct kunit *test)
{
struct dm_backlight_connector_fixture fixture = {};
int saved_abm_level = amdgpu_dm_get_abm_level_param();
@@ -1045,7 +1048,8 @@ static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
amdgpu_dm_set_abm_level_param(-1);
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
fixture.adev->dm.backlight_caps[0].aux_support = true;
fixture.link->panel_type = PANEL_TYPE_OLED;
fixture.link->panel_config.cacp.cacp_supported = false;
KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
@@ -1053,10 +1057,33 @@ static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
}
/**
* dm_test_should_create_sysfs_pwm_backlight - Test PWM backlight enables sysfs
* dm_test_should_create_sysfs_oled_cacp - Test OLED with CACP enables sysfs
* @test: The KUnit test context
*
* An OLED panel that supports CACP must expose the sysfs backlight
* interface so the ABM/CACP level can be controlled.
*/
static void dm_test_should_create_sysfs_oled_cacp(struct kunit *test)
{
struct dm_backlight_connector_fixture fixture = {};
int saved_abm_level = amdgpu_dm_get_abm_level_param();
amdgpu_dm_set_abm_level_param(-1);
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
fixture.link->panel_type = PANEL_TYPE_OLED;
fixture.link->panel_config.cacp.cacp_supported = true;
KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
amdgpu_dm_set_abm_level_param(saved_abm_level);
}
/**
* dm_test_should_create_sysfs_lcd_panel - Test LCD eDP panel enables sysfs
* @test: The KUnit test context
*/
static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test)
static void dm_test_should_create_sysfs_lcd_panel(struct kunit *test)
{
struct dm_backlight_connector_fixture fixture = {};
int saved_abm_level = amdgpu_dm_get_abm_level_param();
@@ -1065,7 +1092,6 @@ static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test)
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
fixture.link->panel_type = PANEL_TYPE_LCD;
fixture.adev->dm.backlight_caps[0].aux_support = false;
KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
@@ -1225,8 +1251,9 @@ static struct kunit_case dm_backlight_test_cases[] = {
KUNIT_CASE(dm_test_should_create_sysfs_abm_forced),
KUNIT_CASE(dm_test_should_create_sysfs_non_edp),
KUNIT_CASE(dm_test_should_create_sysfs_no_backlight_index),
KUNIT_CASE(dm_test_should_create_sysfs_aux_backlight),
KUNIT_CASE(dm_test_should_create_sysfs_pwm_backlight),
KUNIT_CASE(dm_test_should_create_sysfs_oled_no_cacp),
KUNIT_CASE(dm_test_should_create_sysfs_oled_cacp),
KUNIT_CASE(dm_test_should_create_sysfs_lcd_panel),
/* amdgpu_dm_setup_backlight_device */
KUNIT_CASE(dm_test_setup_backlight_device_non_edp),
KUNIT_CASE(dm_test_setup_backlight_device_connection_none),
@@ -2015,6 +2015,341 @@ static void dm_test_is_freesync_video_mode_no_match(struct kunit *test)
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_freesync_video_mode(&candidate, aconnector));
}
/* Tests for amdgpu_dm_update_cacp_caps() */
struct dm_cacp_fixture {
struct amdgpu_device *adev;
struct amdgpu_dm_connector *aconnector;
struct dc_link *link;
};
static void setup_cacp_fixture(struct kunit *test,
struct dm_cacp_fixture *fixture,
enum signal_type signal,
enum dc_panel_type panel_type)
{
fixture->adev = kunit_kzalloc(test, sizeof(*fixture->adev), GFP_KERNEL);
fixture->aconnector = kunit_kzalloc(test, sizeof(*fixture->aconnector),
GFP_KERNEL);
fixture->link = kunit_kzalloc(test, sizeof(*fixture->link), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->adev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->aconnector);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->link);
fixture->aconnector->dc_link = fixture->link;
fixture->aconnector->base.dev = &fixture->adev->ddev;
fixture->link->connector_signal = signal;
fixture->link->panel_type = panel_type;
}
/**
* dm_test_cacp_caps_unsupported_ip - Test CACP disabled on old DCE IP
* @test: The KUnit test context
*
* A DCE IP version below 3.1.4 does not support CACP, so cacp_supported
* must remain false regardless of signal or panel type.
*/
static void dm_test_cacp_caps_unsupported_ip(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_EDP, PANEL_TYPE_OLED);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 2);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_FALSE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/**
* dm_test_cacp_caps_excluded_ip_316 - Test CACP disabled on DCE IP 3.1.6
* @test: The KUnit test context
*
* DCE IP version 3.1.6 is explicitly excluded from CACP support.
*/
static void dm_test_cacp_caps_excluded_ip_316(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_EDP, PANEL_TYPE_OLED);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 6);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_FALSE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/**
* dm_test_cacp_caps_edp_oled_supported - Test CACP enabled on eDP OLED
* @test: The KUnit test context
*
* A supported DCE IP version on an eDP OLED panel must enable CACP.
*/
static void dm_test_cacp_caps_edp_oled_supported(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_EDP, PANEL_TYPE_OLED);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 4);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_TRUE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/**
* dm_test_cacp_caps_lvds_oled_supported - Test CACP enabled on LVDS OLED
* @test: The KUnit test context
*
* LVDS is an accepted connector signal for CACP support.
*/
static void dm_test_cacp_caps_lvds_oled_supported(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_LVDS, PANEL_TYPE_OLED);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_TRUE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/**
* dm_test_cacp_caps_non_edp_signal - Test CACP disabled on non-eDP/LVDS signal
* @test: The KUnit test context
*
* External DisplayPort is neither eDP nor LVDS, so CACP must be disabled.
*/
static void dm_test_cacp_caps_non_edp_signal(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_DISPLAY_PORT,
PANEL_TYPE_OLED);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 4);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_FALSE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/**
* dm_test_cacp_caps_lcd_panel - Test CACP disabled on LCD panel
* @test: The KUnit test context
*
* Plain LCD panels do not benefit from CACP, so it must be disabled even
* on a supported IP version and eDP signal.
*/
static void dm_test_cacp_caps_lcd_panel(struct kunit *test)
{
struct dm_cacp_fixture fixture = {};
setup_cacp_fixture(test, &fixture, SIGNAL_TYPE_EDP, PANEL_TYPE_LCD);
fixture.adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 4);
amdgpu_dm_update_cacp_caps(fixture.aconnector);
KUNIT_EXPECT_FALSE(test, fixture.link->panel_config.cacp.cacp_supported);
}
/* Tests for amdgpu_dm_set_panel_type() */
struct dm_panel_type_fixture {
struct amdgpu_device *adev;
struct drm_device *drm;
struct amdgpu_dm_connector *aconnector;
struct dc_link *link;
struct dc_sink *sink;
};
static void setup_panel_type_fixture(struct kunit *test,
struct dm_panel_type_fixture *fixture)
{
struct device *dev;
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
fixture->drm = __drm_kunit_helper_alloc_drm_device(test, dev,
sizeof(*fixture->adev),
offsetof(struct amdgpu_device, ddev),
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->drm);
fixture->adev = drm_to_adev(fixture->drm);
fixture->aconnector = kunit_kzalloc(test, sizeof(*fixture->aconnector),
GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->aconnector);
fixture->link = kunit_kzalloc(test, sizeof(*fixture->link), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->link);
fixture->sink = kunit_kzalloc(test, sizeof(*fixture->sink), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->sink);
fixture->aconnector->dc_link = fixture->link;
drmm_connector_init(fixture->drm, &fixture->aconnector->base,
&dm_test_connector_funcs, DRM_MODE_CONNECTOR_eDP,
NULL);
}
/**
* dm_test_set_panel_type_vsdb_oled - Test VSDB OLED maps to PANEL_TYPE_OLED
* @test: The KUnit test context
*/
static void dm_test_set_panel_type_vsdb_oled(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.aconnector->base.display_info.amd_vsdb.panel_type =
AMD_VSDB_PANEL_TYPE_OLED;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_OLED);
}
/**
* dm_test_set_panel_type_vsdb_miniled - Test VSDB MINILED maps to PANEL_TYPE_MINILED
* @test: The KUnit test context
*/
static void dm_test_set_panel_type_vsdb_miniled(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.aconnector->base.display_info.amd_vsdb.panel_type =
AMD_VSDB_PANEL_TYPE_MINILED;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_MINILED);
}
/**
* dm_test_set_panel_type_dpcd_oled - Test DPCD oled bit maps to PANEL_TYPE_OLED
* @test: The KUnit test context
*/
static void dm_test_set_panel_type_dpcd_oled(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.link->dpcd_sink_ext_caps.bits.oled = 1;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_OLED);
}
/**
* dm_test_set_panel_type_dpcd_miniled - Test DPCD miniled bit maps to PANEL_TYPE_MINILED
* @test: The KUnit test context
*/
static void dm_test_set_panel_type_dpcd_miniled(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.link->dpcd_sink_ext_caps.bits.miniled = 1;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_MINILED);
}
/**
* dm_test_set_panel_type_did_oled - Test DID OLED maps to PANEL_TYPE_OLED
* @test: The KUnit test context
*
* When VSDB and DPCD do not identify the panel, a DID panel type of
* DRM_MODE_PANEL_TYPE_OLED must map to PANEL_TYPE_OLED.
*/
static void dm_test_set_panel_type_did_oled(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.aconnector->base.display_info.panel_type =
DRM_MODE_PANEL_TYPE_OLED;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_OLED);
}
/**
* dm_test_set_panel_type_did_lcd - Test DID LCD maps to PANEL_TYPE_LCD
* @test: The KUnit test context
*
* When VSDB and DPCD do not identify the panel, a DID panel type of
* DRM_MODE_PANEL_TYPE_LCD must map to PANEL_TYPE_LCD.
*/
static void dm_test_set_panel_type_did_lcd(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
fixture.aconnector->base.display_info.panel_type =
DRM_MODE_PANEL_TYPE_LCD;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_LCD);
}
/**
* dm_test_set_panel_type_vendor_lum_heuristic - Test vendor luminance heuristic maps to MINILED
* @test: The KUnit test context
*
* A panel from the specific vendor whose first luminance range is at least
* 1.5x the second is treated as a mini-LED panel.
*/
static void dm_test_set_panel_type_vendor_lum_heuristic(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
struct drm_amd_vsdb_info *vsdb;
setup_panel_type_fixture(test, &fixture);
fixture.link->local_sink = fixture.sink;
fixture.sink->edid_caps.manufacturer_id = DDC_MANUFACTURERNAME_SAMSUNG;
vsdb = &fixture.aconnector->base.display_info.amd_vsdb;
vsdb->version = 1;
vsdb->luminance_range1.max_luminance = 3000;
vsdb->luminance_range2.max_luminance = 1000;
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_MINILED);
}
/**
* dm_test_set_panel_type_defaults_to_lcd - Test undetermined panel defaults to LCD
* @test: The KUnit test context
*
* When no source identifies the panel, the type now defaults to
* PANEL_TYPE_LCD instead of remaining PANEL_TYPE_NONE.
*/
static void dm_test_set_panel_type_defaults_to_lcd(struct kunit *test)
{
struct dm_panel_type_fixture fixture = {};
setup_panel_type_fixture(test, &fixture);
amdgpu_dm_set_panel_type(fixture.aconnector);
KUNIT_EXPECT_EQ(test, (int)fixture.link->panel_type,
(int)PANEL_TYPE_LCD);
}
static struct kunit_case amdgpu_dm_connector_tests[] = {
/* get_subconnector_type */
KUNIT_CASE(dm_test_subconnector_type_none),
@@ -2143,6 +2478,22 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
KUNIT_CASE(dm_test_is_freesync_video_mode_null_mode),
KUNIT_CASE(dm_test_is_freesync_video_mode_match),
KUNIT_CASE(dm_test_is_freesync_video_mode_no_match),
/* amdgpu_dm_update_cacp_caps */
KUNIT_CASE(dm_test_cacp_caps_unsupported_ip),
KUNIT_CASE(dm_test_cacp_caps_excluded_ip_316),
KUNIT_CASE(dm_test_cacp_caps_edp_oled_supported),
KUNIT_CASE(dm_test_cacp_caps_lvds_oled_supported),
KUNIT_CASE(dm_test_cacp_caps_non_edp_signal),
KUNIT_CASE(dm_test_cacp_caps_lcd_panel),
/* amdgpu_dm_set_panel_type */
KUNIT_CASE(dm_test_set_panel_type_vsdb_oled),
KUNIT_CASE(dm_test_set_panel_type_vsdb_miniled),
KUNIT_CASE(dm_test_set_panel_type_dpcd_oled),
KUNIT_CASE(dm_test_set_panel_type_dpcd_miniled),
KUNIT_CASE(dm_test_set_panel_type_did_oled),
KUNIT_CASE(dm_test_set_panel_type_did_lcd),
KUNIT_CASE(dm_test_set_panel_type_vendor_lum_heuristic),
KUNIT_CASE(dm_test_set_panel_type_defaults_to_lcd),
{}
};