]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amd/display: Add more KUnit tests for amdgpu_dm_ism
authorAlex Hung <alex.hung@amd.com>
Fri, 15 May 2026 15:55:45 +0000 (09:55 -0600)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 3 Jun 2026 17:41:38 +0000 (13:41 -0400)
[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 <ray.wu@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c

index 06594fbfceee0ca41e4c1ebb8e425711bf6f8cfd..0a010802540d6cbf52e8a02b4519b2961340cba1 100644 (file)
@@ -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,
index 72e2dac49e55a6e5687d0c79dbf62efbfb893fa8..da3e192e8d25f87b543cb4ceebf5bfe9fd678cbc 100644 (file)
@@ -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
index e761105e19952b39b1c15fc6489baae7fec08b4c..c333dab40959012428545a664cba083bd50a0137 100644 (file)
@@ -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),
        {}
 };