From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for dm_force_atomic_commit() covering a successful
restore of the connector, its CRTC and the CRTC primary plane, and the
error path taken when the connector state cannot be duplicated.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Chenyu Chen <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c   |   3 +-
 .../display/amdgpu_dm/amdgpu_dm_connector.h   |   1 +
 .../tests/amdgpu_dm_connector_test.c          | 131 ++++++++++++++++++
 3 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 7056bb6f7e26..4fb992871b95 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -3448,7 +3448,7 @@ int amdgpu_dm_connector_init(struct 
amdgpu_display_manager *dm,
 }
 EXPORT_IF_KUNIT(amdgpu_dm_connector_init);
 
-static int dm_force_atomic_commit(struct drm_connector *connector)
+STATIC_IFN_KUNIT int dm_force_atomic_commit(struct drm_connector *connector)
 {
        int ret = 0;
        struct drm_device *ddev = connector->dev;
@@ -3508,6 +3508,7 @@ static int dm_force_atomic_commit(struct drm_connector 
*connector)
 
        return ret;
 }
+EXPORT_IF_KUNIT(dm_force_atomic_commit);
 
 /*
  * This function handles all cases when set mode does not come upon hotplug.
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
index e42a0d53b549..dec4f35765b8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
@@ -239,5 +239,6 @@ void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector 
*aconnector);
 struct drm_atomic_commit;
 int amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
                                     struct drm_atomic_commit *state);
+int dm_force_atomic_commit(struct drm_connector *connector);
 #endif
 #endif /* __AMDGPU_DM_CONNECTOR_H__ */
diff --git 
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index d63529336d69..cbdcea33f097 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -10046,6 +10046,134 @@ static void dm_test_conn_init_get_modes_hook(struct 
kunit *test)
        KUNIT_EXPECT_GT(test, ctx->aconnector->num_modes, 0);
 }
 
+/* Tests for dm_force_atomic_commit() */
+
+/* Fails the very first state duplication so the error path is taken. */
+static struct drm_connector_state *
+dm_test_force_dup_state_fail(struct drm_connector *connector)
+{
+       return NULL;
+}
+
+static const struct drm_connector_funcs dm_test_force_fail_funcs = {
+       .reset = amdgpu_dm_connector_funcs_reset,
+       .atomic_duplicate_state = dm_test_force_dup_state_fail,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/* The atomic modeset check dereferences the helper funcs unconditionally. */
+static int dm_test_force_get_modes(struct drm_connector *connector)
+{
+       return 0;
+}
+
+static const struct drm_connector_helper_funcs dm_test_force_conn_helper_funcs 
= {
+       .get_modes = dm_test_force_get_modes,
+};
+
+struct dm_test_force_ctx {
+       struct drm_device *drm;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_modeset_acquire_ctx acquire_ctx;
+};
+
+static void dm_test_force_drop_locks(void *data)
+{
+       struct dm_test_force_ctx *ctx = data;
+
+       ctx->drm->mode_config.acquire_ctx = NULL;
+       drm_modeset_drop_locks(&ctx->acquire_ctx);
+       drm_modeset_acquire_fini(&ctx->acquire_ctx);
+}
+
+/*
+ * Build a disabled but fully initialized pipe: a primary plane, a CRTC (whose
+ * drm_crtc is the first member of amdgpu_crtc, so to_amdgpu_crtc() resolves)
+ * and a connector wired to an encoder that still points at the CRTC, which is
+ * the state dm_force_atomic_commit() is asked to restore.
+ */
+static struct dm_test_force_ctx *
+dm_test_force_ctx_alloc(struct kunit *test, const struct drm_connector_funcs 
*funcs)
+{
+       struct dm_test_force_ctx *ctx;
+       struct drm_encoder *encoder;
+       struct drm_plane *primary;
+       struct drm_crtc *crtc;
+       struct device *dev;
+
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dev = drm_kunit_helper_alloc_device(test);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+       ctx->drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+                                                      sizeof(struct 
amdgpu_device),
+                                                      offsetof(struct 
amdgpu_device, ddev),
+                                                      DRIVER_MODESET | 
DRIVER_ATOMIC);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm);
+
+       primary = drm_kunit_helper_create_primary_plane(test, ctx->drm, NULL, 
NULL,
+                                                       NULL, 0, NULL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary);
+       crtc = drm_kunit_helper_create_crtc(test, ctx->drm, primary, NULL, 
NULL, NULL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+       ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), 
GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
+       KUNIT_ASSERT_EQ(test,
+                       drmm_connector_init(ctx->drm, &ctx->aconnector->base, 
funcs,
+                                           DRM_MODE_CONNECTOR_HDMIA, NULL), 0);
+       drm_connector_helper_add(&ctx->aconnector->base, 
&dm_test_force_conn_helper_funcs);
+
+       encoder = drmm_kzalloc(ctx->drm, sizeof(*encoder), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, encoder);
+       KUNIT_ASSERT_EQ(test,
+                       drmm_encoder_init(ctx->drm, encoder, NULL,
+                                         DRM_MODE_ENCODER_TMDS, NULL), 0);
+       encoder->crtc = crtc;
+       ctx->aconnector->base.encoder = encoder;
+
+       drm_mode_config_reset(ctx->drm);
+
+       drm_modeset_acquire_init(&ctx->acquire_ctx, 0);
+       ctx->drm->mode_config.acquire_ctx = &ctx->acquire_ctx;
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_force_drop_locks, ctx), 0);
+
+       return ctx;
+}
+
+/**
+ * dm_test_force_commit_succeeds - Test the forced restore commits the pipe
+ * @test: The KUnit test context
+ *
+ * The connector, its CRTC and the CRTC primary plane are pulled into a fresh
+ * atomic state, the CRTC is marked as needing a modeset and the commit
+ * succeeds.
+ */
+static void dm_test_force_commit_succeeds(struct kunit *test)
+{
+       struct dm_test_force_ctx *ctx =
+               dm_test_force_ctx_alloc(test, &dm_test_connector_funcs);
+
+       KUNIT_EXPECT_EQ(test, dm_force_atomic_commit(&ctx->aconnector->base), 
0);
+}
+
+/**
+ * dm_test_force_commit_conn_state_error - Test a failed connector state 
duplication
+ * @test: The KUnit test context
+ *
+ * When the connector state cannot be duplicated the error is propagated and
+ * the partially built atomic state is released without committing.
+ */
+static void dm_test_force_commit_conn_state_error(struct kunit *test)
+{
+       struct dm_test_force_ctx *ctx =
+               dm_test_force_ctx_alloc(test, &dm_test_force_fail_funcs);
+
+       KUNIT_EXPECT_EQ(test, dm_force_atomic_commit(&ctx->aconnector->base), 
-ENOMEM);
+}
+
 static struct kunit_case amdgpu_dm_connector_tests[] = {
        /* get_subconnector_type */
        KUNIT_CASE(dm_test_subconnector_type_none),
@@ -10471,6 +10599,9 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
        KUNIT_CASE(dm_test_conn_init_hdmi),
        KUNIT_CASE(dm_test_conn_init_dvi),
        KUNIT_CASE(dm_test_conn_init_get_modes_hook),
+       /* dm_force_atomic_commit */
+       KUNIT_CASE(dm_test_force_commit_succeeds),
+       KUNIT_CASE(dm_test_force_commit_conn_state_error),
        {}
 };
 
-- 
2.43.0

Reply via email to