summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-06-29 18:37:52 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-07-15 09:15:39 -0400
commitee486cb800915b2fc7ecfd108982198045860060 (patch)
tree7d5bd371d7bc324e0041889b18b9e88ee6b55d1a
parent9a77c86ec8f5e3186b77437958d918698f1d53dd (diff)
drm/amd/display: Test backlight level programming path
[WHAT] Add KUnit coverage for the backlight level write path in amdgpu_dm_backlight.c. Tests cover stream lookup hit/miss, the connector-off and no-stream early returns, and the AUX/PWM/idle-reallow programming branches. The DC programming calls stay safe by injecting a NULL power module and keeping idle optimizations a no-op via disable_idle_power_optimizations. 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>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h5
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c260
3 files changed, 272 insertions, 2 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index ca60c72855fd..d35b90912bd2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -219,7 +219,8 @@ u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
}
EXPORT_IF_KUNIT(convert_brightness_to_user);
-static struct dc_stream_state *dm_find_stream_with_link(
+STATIC_IFN_KUNIT
+struct dc_stream_state *dm_find_stream_with_link(
struct amdgpu_display_manager *dm,
struct dc_link *link)
{
@@ -235,6 +236,7 @@ static struct dc_stream_state *dm_find_stream_with_link(
return NULL;
}
+EXPORT_IF_KUNIT(dm_find_stream_with_link);
STATIC_IFN_KUNIT
int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm,
@@ -346,8 +348,10 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
if (rc)
dm->actual_brightness[bl_idx] = user_brightness;
}
+EXPORT_IF_KUNIT(amdgpu_dm_backlight_set_level);
-static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
+STATIC_IFN_KUNIT
+int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
{
struct amdgpu_display_manager *dm = bl_get_data(bd);
int i = amdgpu_dm_backlight_get_device_index(dm, bd);
@@ -356,6 +360,7 @@ static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_backlight_update_status);
static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
int bl_idx)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
index 98d612c60ae9..5fbaf67369f1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
@@ -28,6 +28,8 @@ struct amdgpu_display_manager;
struct amdgpu_dm_connector;
struct backlight_device;
struct backlight_properties;
+struct dc_link;
+struct dc_stream_state;
struct drm_connector;
struct attribute_group;
@@ -49,6 +51,9 @@ bool amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *aconnector);
extern const struct attribute_group amdgpu_group;
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+struct dc_stream_state *dm_find_stream_with_link(struct amdgpu_display_manager *dm,
+ struct dc_link *link);
+int amdgpu_dm_backlight_update_status(struct backlight_device *bd);
int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
unsigned int *min, unsigned int *max);
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
index 8ebc0f263e3e..732ac8db742d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
@@ -9,6 +9,7 @@
#include <linux/backlight.h>
#include "dc.h"
+#include "dc_dmub_srv.h"
#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
@@ -40,6 +41,255 @@ static void setup_test_connector(struct kunit *test,
fixture->link->connector_signal = signal;
}
+static void setup_test_dm_ddev(struct kunit *test, struct amdgpu_display_manager *dm)
+{
+ struct drm_device *ddev;
+
+ ddev = kunit_kzalloc(test, sizeof(*ddev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddev);
+
+ INIT_LIST_HEAD(&ddev->mode_config.connector_list);
+ spin_lock_init(&ddev->mode_config.connector_list_lock);
+ dm->ddev = ddev;
+}
+
+/* Tests for dm_find_stream_with_link() */
+
+/**
+ * dm_test_find_stream_with_link_returns_match - Test matching stream lookup
+ * @test: The KUnit test context
+ */
+static void dm_test_find_stream_with_link_returns_match(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *other_link = dm_kunit_alloc_link(test);
+ struct dc_link *target_link = dm_kunit_alloc_link(test);
+ struct dc_stream_state *stream;
+
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, other_link);
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 1, target_link);
+ stream = dm_find_stream_with_link(dm, target_link);
+
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+ KUNIT_EXPECT_PTR_EQ(test, stream->link, target_link);
+}
+
+/**
+ * dm_test_find_stream_with_link_missing - Test missing stream lookup
+ * @test: The KUnit test context
+ */
+static void dm_test_find_stream_with_link_missing(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *stream_link = dm_kunit_alloc_link(test);
+ struct dc_link *missing_link = dm_kunit_alloc_link(test);
+
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, stream_link);
+
+ KUNIT_EXPECT_NULL(test, dm_find_stream_with_link(dm, missing_link));
+}
+
+/* Tests for amdgpu_dm_backlight_set_level() */
+
+/**
+ * dm_test_backlight_set_level_connector_off - Test connector-off cache path
+ * @test: The KUnit test context
+ *
+ * If the matching connector has no encoder, set_level() must cache the
+ * requested brightness and return before touching DC or backlight hardware.
+ */
+static void dm_test_backlight_set_level_connector_off(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct amdgpu_dm_connector *aconnector;
+
+ setup_test_dm_ddev(test, dm);
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+ INIT_LIST_HEAD(&aconnector->base.head);
+ aconnector->bl_idx = 1;
+ aconnector->base.encoder = NULL;
+ list_add_tail(&aconnector->base.head, &dm->ddev->mode_config.connector_list);
+
+ amdgpu_dm_backlight_set_level(dm, 1, 1234);
+
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 1234U);
+ KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
+}
+
+/**
+ * dm_test_backlight_set_level_no_stream - Test no-stream early return
+ * @test: The KUnit test context
+ *
+ * With no stream for the backlight link, set_level() records the requested
+ * brightness and exits before calling the power-module programming path.
+ */
+static void dm_test_backlight_set_level_no_stream(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
+
+ setup_test_dm_ddev(test, dm);
+ dm->backlight_caps[1].caps_valid = true;
+ dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ dm->backlight_link[1] = link;
+
+ amdgpu_dm_backlight_set_level(dm, 1, 2000);
+
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
+ KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
+}
+
+/**
+ * dm_test_backlight_set_level_aux_programs_power_module - Test AUX programming path
+ * @test: The KUnit test context
+ *
+ * With a matching stream present, set_level() walks into the DC programming
+ * path. A NULL power_module makes mod_power_set_backlight_nits() a safe
+ * early-false, and ips_support disabled leaves idle optimizations untouched.
+ * A non-matching connector exercises the connector-list skip, and a non-zero
+ * brightness_mask exercises the quirk-OR path.
+ */
+static void dm_test_backlight_set_level_aux_programs_power_module(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
+ struct amdgpu_dm_connector *other;
+
+ setup_test_dm_ddev(test, dm);
+ mutex_init(&dm->dc_lock);
+ dm->power_module = NULL;
+
+ /* Non-matching connector exercises the bl_idx skip (continue). */
+ other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other);
+ INIT_LIST_HEAD(&other->base.head);
+ other->bl_idx = 0;
+ list_add_tail(&other->base.head, &dm->ddev->mode_config.connector_list);
+
+ dm->backlight_caps[1].caps_valid = true;
+ dm->backlight_caps[1].aux_support = true;
+ dm->backlight_caps[1].brightness_mask = 0x3;
+ dm->backlight_caps[1].aux_min_input_signal = 1;
+ dm->backlight_caps[1].aux_max_input_signal = 512;
+ dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ dm->backlight_link[1] = link;
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
+
+ amdgpu_dm_backlight_set_level(dm, 1, 2000);
+
+ /* power_module is NULL so programming fails; actual stays unchanged. */
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
+ KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
+}
+
+/**
+ * dm_test_backlight_set_level_pwm_programs_power_module - Test PWM programming path
+ * @test: The KUnit test context
+ *
+ * With aux_support cleared, set_level() takes the millipercent branch:
+ * get_brightness_range() + mod_power_set_backlight_percent(). A NULL
+ * power_module keeps the call a safe early-false.
+ */
+static void dm_test_backlight_set_level_pwm_programs_power_module(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
+
+ setup_test_dm_ddev(test, dm);
+ mutex_init(&dm->dc_lock);
+ dm->power_module = NULL;
+
+ dm->backlight_caps[1].caps_valid = true;
+ dm->backlight_caps[1].aux_support = false;
+ dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ dm->backlight_link[1] = link;
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
+
+ amdgpu_dm_backlight_set_level(dm, 1, 2000);
+
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
+ KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
+}
+
+/**
+ * dm_test_backlight_set_level_reallows_idle - Test idle-optimization toggle path
+ * @test: The KUnit test context
+ *
+ * When ips_support is set and dmub idle is allowed, set_level() disables idle
+ * optimizations around the programming call and re-enables them afterwards.
+ * disable_idle_power_optimizations keeps dc_allow_idle_optimizations() a safe
+ * early return, and ctx->logger is wired because DC_LOG_* dereferences it.
+ */
+static void dm_test_backlight_set_level_reallows_idle(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
+ struct dc_dmub_srv *dmub_srv;
+ struct dal_logger *logger;
+ struct dc_context *ctx;
+
+ setup_test_dm_ddev(test, dm);
+ mutex_init(&dm->dc_lock);
+ dm->power_module = NULL;
+
+ /* dm_kunit_alloc_dm() leaves dc->ctx NULL; the idle path dereferences it. */
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ dm->dc->ctx = ctx;
+
+ logger = kunit_kzalloc(test, sizeof(*logger), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, logger);
+ logger->dev = &adev->ddev;
+ dm->dc->ctx->logger = logger;
+
+ dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
+ dmub_srv->idle_allowed = true;
+ dm->dc->ctx->dmub_srv = dmub_srv;
+ dm->dc->caps.ips_support = true;
+ /* Keep dc_allow_idle_optimizations() a safe early return. */
+ dm->dc->debug.disable_idle_power_optimizations = true;
+
+ dm->backlight_caps[1].caps_valid = true;
+ dm->backlight_caps[1].aux_support = true;
+ dm->backlight_caps[1].aux_min_input_signal = 1;
+ dm->backlight_caps[1].aux_max_input_signal = 512;
+ dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ dm->backlight_link[1] = link;
+ dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
+
+ amdgpu_dm_backlight_set_level(dm, 1, 2000);
+
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
+}
+
+/**
+ * dm_test_backlight_update_status_no_stream - Test update_status wrapper
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_update_status_no_stream(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct backlight_device *bd;
+
+ setup_test_dm_ddev(test, dm);
+ bd = kunit_kzalloc(test, sizeof(*bd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bd);
+ dev_set_drvdata(&bd->dev, dm);
+ bd->props.brightness = 3456;
+ dm->num_of_edps = 2;
+ dm->backlight_dev[1] = bd;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_update_status(bd), 0);
+ KUNIT_EXPECT_EQ(test, dm->brightness[1], 3456U);
+}
+
/* Tests for amdgpu_dm_backlight_get_device_index() */
/**
@@ -1200,6 +1450,16 @@ static void dm_test_setup_backlight_device_oled_success(struct kunit *test)
}
static struct kunit_case dm_backlight_test_cases[] = {
+ /* dm_find_stream_with_link */
+ KUNIT_CASE(dm_test_find_stream_with_link_returns_match),
+ KUNIT_CASE(dm_test_find_stream_with_link_missing),
+ /* amdgpu_dm_backlight_set_level / update_status */
+ KUNIT_CASE(dm_test_backlight_set_level_connector_off),
+ KUNIT_CASE(dm_test_backlight_set_level_no_stream),
+ KUNIT_CASE(dm_test_backlight_set_level_aux_programs_power_module),
+ KUNIT_CASE(dm_test_backlight_set_level_pwm_programs_power_module),
+ KUNIT_CASE(dm_test_backlight_set_level_reallows_idle),
+ KUNIT_CASE(dm_test_backlight_update_status_no_stream),
/* amdgpu_dm_backlight_get_device_index */
KUNIT_CASE(dm_test_backlight_device_index_matches_second),
KUNIT_CASE(dm_test_backlight_device_index_missing_fallback),