From: Alex Hung <[email protected]> [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 <[email protected]> Signed-off-by: Alex Hung <[email protected]> Signed-off-by: George Zhang <[email protected]> --- .../display/amdgpu_dm/amdgpu_dm_backlight.c | 9 +- .../display/amdgpu_dm/amdgpu_dm_backlight.h | 5 + .../tests/amdgpu_dm_backlight_test.c | 260 ++++++++++++++++++ 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), -- 2.55.0
