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>
This commit is contained in:
Alex Hung
2026-06-29 18:37:52 -06:00
committed by Alex Deucher
parent 9a77c86ec8
commit ee486cb800
3 changed files with 272 additions and 2 deletions

View File

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

View File

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

View File

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