From: Alex Hung Date: Fri, 15 May 2026 15:55:45 +0000 (-0600) Subject: drm/amd/display: Add more KUnit tests for amdgpu_dm_ism X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3142b13fe9fdbc3738026cc1cfeb7b2ab38a382;p=thirdparty%2Flinux.git drm/amd/display: Add more KUnit tests for amdgpu_dm_ism [Why & How] Add 8 more KUnit tests: - dm_ism_get_idle_allow_delay: add a case where filter_entry_count exceeds filter_history_size, exercising the max() branch in the history_size calculation. - amdgpu_dm_ism_init: verify all fields are initialised to expected values after construction. - amdgpu_dm_ism_fini: smoke-test cancellation of never-scheduled delayed work items paired with a preceding init. - dm_ism_set_last_idle_ts: verify last_idle_timestamp_ns is updated to at least the value captured before the call. - dm_ism_insert_record: verify index increment and duration calculation; verify out-of-bounds index wraps to slot 0. - dm_ism_trigger_event: verify current_state and previous_state are updated on a valid transition and left unchanged on an invalid one. Assisted-by: Copilot:Claude-Sonnet-4.6 Reviewed-by: Ray Wu Signed-off-by: Alex Hung Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c index 06594fbfceee0..0a010802540d6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -230,7 +230,8 @@ EXPORT_IF_KUNIT(dm_ism_get_idle_allow_delay); * dm_ism_insert_record - Insert a record into the circular history buffer * @ism: ISM instance */ -static void dm_ism_insert_record(struct amdgpu_dm_ism *ism) +STATIC_IFN_KUNIT +void dm_ism_insert_record(struct amdgpu_dm_ism *ism) { struct amdgpu_dm_ism_record *record; @@ -245,15 +246,19 @@ static void dm_ism_insert_record(struct amdgpu_dm_ism *ism) record->duration_ns = record->timestamp_ns - ism->last_idle_timestamp_ns; } +EXPORT_IF_KUNIT(dm_ism_insert_record); -static void dm_ism_set_last_idle_ts(struct amdgpu_dm_ism *ism) +STATIC_IFN_KUNIT +void dm_ism_set_last_idle_ts(struct amdgpu_dm_ism *ism) { ism->last_idle_timestamp_ns = ktime_get_ns(); } +EXPORT_IF_KUNIT(dm_ism_set_last_idle_ts); -static bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism, +STATIC_IFN_KUNIT +bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism, enum amdgpu_dm_ism_event event) { enum amdgpu_dm_ism_state next_state; @@ -268,6 +273,7 @@ static bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism, return gotNextState; } +EXPORT_IF_KUNIT(dm_ism_trigger_event); static void dm_ism_commit_idle_optimization_state(struct amdgpu_dm_ism *ism, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h index 72e2dac49e55a..da3e192e8d25f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h @@ -157,6 +157,10 @@ uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, const struct dc_stream_state *stream); uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, const struct dc_stream_state *stream); +void dm_ism_insert_record(struct amdgpu_dm_ism *ism); +void dm_ism_set_last_idle_ts(struct amdgpu_dm_ism *ism); +bool dm_ism_trigger_event(struct amdgpu_dm_ism *ism, + enum amdgpu_dm_ism_event event); #endif #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c index e761105e19952..c333dab409590 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c @@ -579,6 +579,208 @@ static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test) (uint64_t)0); } +/** + * dm_test_ism_idle_delay_entry_count_exceeds_history_size - entry_count > history_size sets delay + * @test: KUnit test context + */ +static void dm_test_ism_idle_delay_entry_count_exceeds_history_size(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns, expected; + + /* + * filter_entry_count (5) > filter_history_size (3), so history_size + * is determined by filter_entry_count via max(). All 5 records are + * short idles, triggering the delay. + */ + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + + one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL, + 1485000); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 5; + ism->config.filter_history_size = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_old_history_threshold = 0; + + for (int i = 0; i < 5; i++) { + ism->records[i].duration_ns = one_frame_ns; + ism->records[i].timestamp_ns = 0; + } + ism->next_record_idx = 5; + + expected = 10 * one_frame_ns; + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), expected); +} + +/* ===== Tests for amdgpu_dm_ism_init ===== */ + +/** + * dm_test_ism_init_sets_initial_state - all ISM fields initialized to expected values + * @test: KUnit test context + */ +static void dm_test_ism_init_sets_initial_state(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct amdgpu_dm_ism_config config = { + .filter_num_frames = 5, + .filter_entry_count = 3, + .activation_num_delay_frames = 10, + .filter_history_size = 8, + .filter_old_history_threshold = 20, + .sso_num_frames = 2, + }; + + amdgpu_dm_ism_init(ism, &config); + + KUNIT_EXPECT_EQ(test, (int)ism->current_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); + KUNIT_EXPECT_EQ(test, (int)ism->previous_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); + KUNIT_EXPECT_EQ(test, ism->next_record_idx, 0); + KUNIT_EXPECT_EQ(test, ism->last_idle_timestamp_ns, (uint64_t)0); + KUNIT_EXPECT_EQ(test, ism->config.filter_num_frames, + config.filter_num_frames); + KUNIT_EXPECT_EQ(test, ism->config.filter_entry_count, + config.filter_entry_count); + KUNIT_EXPECT_EQ(test, ism->config.activation_num_delay_frames, + config.activation_num_delay_frames); + KUNIT_EXPECT_EQ(test, ism->config.sso_num_frames, config.sso_num_frames); +} + +/* ===== Tests for amdgpu_dm_ism_fini ===== */ + +/** + * dm_test_ism_fini_after_init - fini cancels never-scheduled work without error + * @test: KUnit test context + */ +static void dm_test_ism_fini_after_init(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct amdgpu_dm_ism_config config = { + .filter_num_frames = 5, + .filter_entry_count = 3, + .activation_num_delay_frames = 10, + .sso_num_frames = 2, + }; + + amdgpu_dm_ism_init(ism, &config); + /* Work was never scheduled; cancel_delayed_work_sync is a no-op. */ + amdgpu_dm_ism_fini(ism); + + /* FSM state is untouched by fini */ + KUNIT_EXPECT_EQ(test, (int)ism->current_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +/* ===== Tests for dm_ism_set_last_idle_ts ===== */ + +/** + * dm_test_ism_set_last_idle_ts_updates_timestamp - last_idle_timestamp_ns updated to current time + * @test: KUnit test context + */ +static void dm_test_ism_set_last_idle_ts_updates_timestamp(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + uint64_t before; + + ism->last_idle_timestamp_ns = 0; + before = ktime_get_ns(); + dm_ism_set_last_idle_ts(ism); + + KUNIT_EXPECT_GE(test, ism->last_idle_timestamp_ns, before); +} + +/* ===== Tests for dm_ism_insert_record ===== */ + +/** + * dm_test_ism_insert_record_basic - record inserted with correct index and duration + * @test: KUnit test context + */ +static void dm_test_ism_insert_record_basic(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + + ism->last_idle_timestamp_ns = 0; + ism->next_record_idx = 0; + + dm_ism_insert_record(ism); + + KUNIT_EXPECT_EQ(test, ism->next_record_idx, 1); + KUNIT_EXPECT_GT(test, ism->records[0].timestamp_ns, (uint64_t)0); + /* duration = timestamp - last_idle_timestamp_ns (0) */ + KUNIT_EXPECT_EQ(test, ism->records[0].duration_ns, + ism->records[0].timestamp_ns); +} + +/** + * dm_test_ism_insert_record_wraps_around - out-of-bounds index wraps to slot 0 + * @test: KUnit test context + */ +static void dm_test_ism_insert_record_wraps_around(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + + ism->last_idle_timestamp_ns = 0; + /* Out-of-bounds index triggers reset to 0 */ + ism->next_record_idx = AMDGPU_DM_IDLE_HIST_LEN; + + dm_ism_insert_record(ism); + + KUNIT_EXPECT_EQ(test, ism->next_record_idx, 1); + KUNIT_EXPECT_GT(test, ism->records[0].timestamp_ns, (uint64_t)0); +} + +/* ===== Tests for dm_ism_trigger_event ===== */ + +/** + * dm_test_ism_trigger_event_valid_transition - valid event advances current and previous state + * @test: KUnit test context + */ +static void dm_test_ism_trigger_event_valid_transition(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + bool ok; + + ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING; + ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING; + + ok = dm_ism_trigger_event(ism, DM_ISM_EVENT_ENTER_IDLE_REQUESTED); + + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)ism->current_state, + (int)DM_ISM_STATE_HYSTERESIS_WAITING); + KUNIT_EXPECT_EQ(test, (int)ism->previous_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +/** + * dm_test_ism_trigger_event_invalid_transition - invalid event leaves state unchanged + * @test: KUnit test context + */ +static void dm_test_ism_trigger_event_invalid_transition(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + bool ok; + + ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING; + ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING; + + /* EXIT_IDLE_REQUESTED is not valid from FULL_POWER_RUNNING */ + ok = dm_ism_trigger_event(ism, DM_ISM_EVENT_EXIT_IDLE_REQUESTED); + + KUNIT_EXPECT_FALSE(test, ok); + /* State must remain unchanged on invalid transition */ + KUNIT_EXPECT_EQ(test, (int)ism->current_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); + KUNIT_EXPECT_EQ(test, (int)ism->previous_state, + (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + static struct kunit_case dm_ism_test_cases[] = { /* dm_ism_next_state — FULL_POWER_RUNNING */ KUNIT_CASE(dm_test_ism_next_state_running_enter_idle), @@ -621,6 +823,19 @@ static struct kunit_case dm_ism_test_cases[] = { KUNIT_CASE(dm_test_ism_idle_delay_wraps_around_buffer), KUNIT_CASE(dm_test_ism_idle_delay_old_history_cutoff), KUNIT_CASE(dm_test_ism_idle_delay_mixed_durations), + KUNIT_CASE(dm_test_ism_idle_delay_entry_count_exceeds_history_size), + /* amdgpu_dm_ism_init */ + KUNIT_CASE(dm_test_ism_init_sets_initial_state), + /* amdgpu_dm_ism_fini */ + KUNIT_CASE(dm_test_ism_fini_after_init), + /* dm_ism_set_last_idle_ts */ + KUNIT_CASE(dm_test_ism_set_last_idle_ts_updates_timestamp), + /* dm_ism_insert_record */ + KUNIT_CASE(dm_test_ism_insert_record_basic), + KUNIT_CASE(dm_test_ism_insert_record_wraps_around), + /* dm_ism_trigger_event */ + KUNIT_CASE(dm_test_ism_trigger_event_valid_transition), + KUNIT_CASE(dm_test_ism_trigger_event_invalid_transition), {} };