summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-06-26 17:35:52 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-07-15 09:15:39 -0400
commit4133f1caf3c8d07d4a6b9814eeea5eeef475300e (patch)
treeafc72eaff539a1c89a3e2746bbf6a123f26323f4 /drivers
parente0e426f10ad653c5e9dd33fb4a76d55f06101992 (diff)
downloadlinux-4133f1caf3c8d07d4a6b9814eeea5eeef475300e.tar.gz
linux-4133f1caf3c8d07d4a6b9814eeea5eeef475300e.zip
drm/amd/display: Test plane atomic check and hooks
[WHAT] Add KUnit tests for amdgpu_dm_plane_atomic_async_check(), amdgpu_dm_plane_atomic_check(), amdgpu_dm_plane_panic_flush() and the DRM plane reset, duplicate-state and destroy-state hooks. Add the dm_test_init_atomic_check_state() helper and plane reset vtable used by the new tests. Assisted-by: Copilot:Claude-Opus-4.8 GPT-5.5 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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c25
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c457
3 files changed, 482 insertions, 9 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index 1b564cfe2120..fa05734047e0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -1254,8 +1254,8 @@ int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev,
}
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_dc_scaling_info);
-static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
- struct drm_atomic_commit *state)
+STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
@@ -1300,9 +1300,10 @@ static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
return -EINVAL;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_check);
-static int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
- struct drm_atomic_commit *state, bool flip)
+STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state, bool flip)
{
struct drm_crtc_state *new_crtc_state;
struct drm_plane_state *new_plane_state;
@@ -1324,6 +1325,7 @@ static int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_async_check);
int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc,
struct dc_cursor_position *position)
@@ -1483,7 +1485,7 @@ static void amdgpu_dm_plane_atomic_async_update(struct drm_plane *plane,
amdgpu_dm_plane_handle_cursor_update(plane, old_state);
}
-static void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
{
struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane->state);
struct drm_framebuffer *fb = plane->state->fb;
@@ -1496,6 +1498,7 @@ static void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
dc_plane_force_dcc_and_tiling_disable(dc_plane_state, fb->modifier ? true : false);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_panic_flush);
static const struct drm_plane_helper_funcs dm_plane_helper_funcs = {
.prepare_fb = amdgpu_dm_plane_helper_prepare_fb,
@@ -1515,7 +1518,7 @@ static const struct drm_plane_helper_funcs dm_primary_plane_helper_funcs = {
.panic_flush = amdgpu_dm_plane_panic_flush,
};
-static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
{
struct dm_plane_state *amdgpu_state;
@@ -1532,8 +1535,10 @@ static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
amdgpu_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
amdgpu_state->blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_reset);
-static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane)
+STATIC_IFN_KUNIT struct drm_plane_state *
+amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane)
{
struct dm_plane_state *dm_plane_state, *old_dm_plane_state;
@@ -1572,6 +1577,7 @@ static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct
return &dm_plane_state->base;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_duplicate_state);
STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
uint32_t format,
@@ -1636,8 +1642,8 @@ STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *pla
}
EXPORT_IF_KUNIT(amdgpu_dm_plane_format_mod_supported);
-static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
- struct drm_plane_state *state)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
{
struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
@@ -1657,6 +1663,7 @@ static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
drm_atomic_helper_plane_destroy_state(plane, state);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_destroy_state);
#ifdef AMD_PRIVATE_COLOR
static void
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
index 911fb2d73e22..ad22324a5d2e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
@@ -115,5 +115,14 @@ void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
struct drm_framebuffer *fb,
int *min_downscale,
int *max_upscale);
+int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state, bool flip);
+int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state);
+void amdgpu_dm_plane_panic_flush(struct drm_plane *plane);
+void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane);
+struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane);
+void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state);
#endif
#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c
index f36c5c139554..df69e2235e24 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c
@@ -2726,6 +2726,446 @@ static void dm_test_format_mod_supported_d_swizzle_reject(struct kunit *test)
listed_mod));
}
+/**
+ * dm_test_atomic_async_check_rejects() - Verify async check rejections.
+ * @test: KUnit test context.
+ *
+ * Verify if async flip on non-overlay planes and async cursor update on
+ * non-cursor planes are rejected.
+ */
+static void dm_test_atomic_async_check_rejects(struct kunit *test)
+{
+ struct drm_plane *plane;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+
+ plane->type = DRM_PLANE_TYPE_PRIMARY;
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_atomic_async_check(plane, NULL, true),
+ -EINVAL);
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_atomic_async_check(plane, NULL, false),
+ -EINVAL);
+}
+
+/**
+ * dm_test_atomic_async_check_overlay_cursor() - Verify overlay cursor rejection.
+ * @test: KUnit test context.
+ *
+ * Verify if async cursor updates are rejected while the CRTC is using an
+ * overlay cursor mode.
+ */
+static void dm_test_atomic_async_check_overlay_cursor(struct kunit *test)
+{
+ struct drm_atomic_commit *state;
+ struct __drm_planes_state *planes;
+ struct __drm_crtcs_state *crtcs;
+ struct drm_plane *plane;
+ struct drm_plane_state *plane_state;
+ struct drm_crtc *crtc;
+ struct dm_crtc_state *dm_crtc_state;
+
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ planes = kunit_kzalloc(test, sizeof(*planes), GFP_KERNEL);
+ crtcs = kunit_kzalloc(test, sizeof(*crtcs), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ plane_state = kunit_kzalloc(test, sizeof(*plane_state), GFP_KERNEL);
+ crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+ dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, planes);
+ KUNIT_ASSERT_NOT_NULL(test, crtcs);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, plane_state);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+ KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+
+ plane->type = DRM_PLANE_TYPE_CURSOR;
+ plane->index = 0;
+ crtc->index = 0;
+ plane_state->crtc = crtc;
+ dm_crtc_state->cursor_mode = DM_CURSOR_OVERLAY_MODE;
+ state->planes = planes;
+ state->crtcs = crtcs;
+ state->planes[0].new_state = plane_state;
+ state->crtcs[0].new_state = &dm_crtc_state->base;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_atomic_async_check(plane, state, false),
+ -EINVAL);
+
+ dm_crtc_state->cursor_mode = DM_CURSOR_NATIVE_MODE;
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_atomic_async_check(plane, state, false),
+ 0);
+}
+
+static struct amdgpu_device *dm_test_init_atomic_check_state(struct kunit *test,
+ struct drm_atomic_commit **state,
+ struct drm_plane **plane,
+ struct dm_plane_state **dm_plane_state,
+ struct drm_crtc_state **new_crtc_state,
+ struct drm_framebuffer **fb)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct __drm_planes_state *planes;
+ struct __drm_crtcs_state *crtcs;
+ struct dc_plane_state *dc_plane_state;
+ struct drm_crtc *crtc;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ *state = kunit_kzalloc(test, sizeof(**state), GFP_KERNEL);
+ planes = kunit_kzalloc(test, sizeof(*planes), GFP_KERNEL);
+ crtcs = kunit_kzalloc(test, sizeof(*crtcs), GFP_KERNEL);
+ *plane = kunit_kzalloc(test, sizeof(**plane), GFP_KERNEL);
+ *dm_plane_state = kunit_kzalloc(test, sizeof(**dm_plane_state), GFP_KERNEL);
+ dc_plane_state = kunit_kzalloc(test, sizeof(*dc_plane_state), GFP_KERNEL);
+ crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+ *new_crtc_state = kunit_kzalloc(test, sizeof(**new_crtc_state), GFP_KERNEL);
+ *fb = kunit_kzalloc(test, sizeof(**fb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, *state);
+ KUNIT_ASSERT_NOT_NULL(test, planes);
+ KUNIT_ASSERT_NOT_NULL(test, crtcs);
+ KUNIT_ASSERT_NOT_NULL(test, *plane);
+ KUNIT_ASSERT_NOT_NULL(test, *dm_plane_state);
+ KUNIT_ASSERT_NOT_NULL(test, dc_plane_state);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+ KUNIT_ASSERT_NOT_NULL(test, *new_crtc_state);
+ KUNIT_ASSERT_NOT_NULL(test, *fb);
+
+ adev->dm.dc = dc;
+ dc->caps.planes[0].max_upscale_factor.argb8888 = 1000;
+ dc->caps.planes[0].max_downscale_factor.argb8888 = 1000;
+ dc->caps.planes[0].max_upscale_factor.nv12 = 1000;
+ dc->caps.planes[0].max_downscale_factor.nv12 = 1000;
+
+ (*plane)->dev = &adev->ddev;
+ (*plane)->index = 0;
+ (*plane)->type = DRM_PLANE_TYPE_OVERLAY;
+ (*plane)->name = "kunit-plane";
+ crtc->dev = &adev->ddev;
+ crtc->index = 0;
+ (*fb)->width = 100;
+ (*fb)->height = 100;
+ (*fb)->format = drm_format_info(DRM_FORMAT_XRGB8888);
+ KUNIT_ASSERT_NOT_NULL(test, (*fb)->format);
+
+ (*dm_plane_state)->base.plane = *plane;
+ (*dm_plane_state)->base.state = *state;
+ (*dm_plane_state)->base.crtc = crtc;
+ (*dm_plane_state)->base.fb = *fb;
+ (*dm_plane_state)->base.src_w = 100 << 16;
+ (*dm_plane_state)->base.src_h = 100 << 16;
+ (*dm_plane_state)->base.crtc_w = 100;
+ (*dm_plane_state)->base.crtc_h = 100;
+ (*dm_plane_state)->dc_state = dc_plane_state;
+
+ (*new_crtc_state)->crtc = crtc;
+ (*new_crtc_state)->enable = true;
+ (*new_crtc_state)->mode.crtc_hdisplay = 1920;
+ (*new_crtc_state)->mode.crtc_vdisplay = 1080;
+
+ (*state)->planes = planes;
+ (*state)->crtcs = crtcs;
+ (*state)->planes[0].new_state = &(*dm_plane_state)->base;
+ (*state)->crtcs[0].new_state = *new_crtc_state;
+
+ return adev;
+}
+
+/**
+ * dm_test_atomic_check_no_dc_state() - Verify missing DC plane state succeeds.
+ * @test: KUnit test context.
+ *
+ * Verify if atomic_check exits before deeper validation when the DM plane state
+ * has no DC plane state attached.
+ */
+static void dm_test_atomic_check_no_dc_state(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_atomic_commit *state;
+ struct __drm_planes_state *planes;
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ planes = kunit_kzalloc(test, sizeof(*planes), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, planes);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, dm_plane_state);
+
+ plane->dev = &adev->ddev;
+ plane->index = 0;
+ dm_plane_state->base.plane = plane;
+ state->planes = planes;
+ state->planes[0].new_state = &dm_plane_state->base;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), 0);
+}
+
+/**
+ * dm_test_atomic_check_missing_crtc_state() - Verify missing CRTC state fails.
+ * @test: KUnit test context.
+ *
+ * Verify if atomic_check rejects a plane with DC state when the atomic CRTC
+ * state is absent.
+ */
+static void dm_test_atomic_check_missing_crtc_state(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_atomic_commit *state;
+ struct __drm_planes_state *planes;
+ struct __drm_crtcs_state *crtcs;
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+ struct dc_plane_state *dc_plane_state;
+ struct drm_crtc *crtc;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ planes = kunit_kzalloc(test, sizeof(*planes), GFP_KERNEL);
+ crtcs = kunit_kzalloc(test, sizeof(*crtcs), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL);
+ dc_plane_state = kunit_kzalloc(test, sizeof(*dc_plane_state), GFP_KERNEL);
+ crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, planes);
+ KUNIT_ASSERT_NOT_NULL(test, crtcs);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, dm_plane_state);
+ KUNIT_ASSERT_NOT_NULL(test, dc_plane_state);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+ plane->dev = &adev->ddev;
+ plane->index = 0;
+ crtc->index = 0;
+ dm_plane_state->base.plane = plane;
+ dm_plane_state->base.crtc = crtc;
+ dm_plane_state->dc_state = dc_plane_state;
+ state->planes = planes;
+ state->crtcs = crtcs;
+ state->planes[0].new_state = &dm_plane_state->base;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), -EINVAL);
+}
+
+/**
+ * dm_test_atomic_check_helper_failure() - Verify helper-check failures return.
+ * @test: KUnit test context.
+ *
+ * Verify if atomic_check returns before DC validation when the DRM helper state
+ * validation rejects the plane.
+ */
+static void dm_test_atomic_check_helper_failure(struct kunit *test)
+{
+ struct drm_atomic_commit *state;
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_framebuffer *fb;
+
+ dm_test_init_atomic_check_state(test, &state, &plane, &dm_plane_state,
+ &new_crtc_state, &fb);
+ dm_plane_state->base.crtc_w = 10;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), -EINVAL);
+}
+
+/**
+ * dm_test_atomic_check_color_pipeline_conflict() - Verify color conflict rejection.
+ * @test: KUnit test context.
+ *
+ * Verify if atomic_check rejects use of both plane COLOR_PIPELINE and CRTC
+ * DEGAMMA_LUT before DC validation.
+ */
+static void dm_test_atomic_check_color_pipeline_conflict(struct kunit *test)
+{
+ struct drm_atomic_commit *state;
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_framebuffer *fb;
+ void *color_pipeline;
+ void *degamma_lut;
+
+ dm_test_init_atomic_check_state(test, &state, &plane, &dm_plane_state,
+ &new_crtc_state, &fb);
+ color_pipeline = kunit_kzalloc(test, 1, GFP_KERNEL);
+ degamma_lut = kunit_kzalloc(test, 1, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, color_pipeline);
+ KUNIT_ASSERT_NOT_NULL(test, degamma_lut);
+
+ dm_plane_state->base.color_pipeline = color_pipeline;
+ new_crtc_state->degamma_lut = degamma_lut;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), -EINVAL);
+}
+
+/**
+ * dm_test_atomic_check_scaling_failure() - Verify scaling-info failures return.
+ * @test: KUnit test context.
+ *
+ * Verify if atomic_check returns the scaling-info error before DC validation.
+ */
+static void dm_test_atomic_check_scaling_failure(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_atomic_commit *state;
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_framebuffer *fb;
+
+ adev = dm_test_init_atomic_check_state(test, &state, &plane, &dm_plane_state,
+ &new_crtc_state, &fb);
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(1, 0, 0);
+ fb->width = 200;
+ fb->format = drm_format_info(DRM_FORMAT_NV12);
+ KUNIT_ASSERT_NOT_NULL(test, fb->format);
+ dm_plane_state->base.src_x = 1 << 16;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_atomic_check(plane, state), -EINVAL);
+}
+
+/**
+ * dm_test_panic_flush_no_dc_state() - Verify panic flush exits without DC state.
+ * @test: KUnit test context.
+ *
+ * Verify if panic_flush returns without dereferencing DC state when the current
+ * plane state has no DC plane state attached.
+ */
+static void dm_test_panic_flush_no_dc_state(struct kunit *test)
+{
+ struct drm_plane *plane;
+ struct dm_plane_state *dm_plane_state;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, dm_plane_state);
+
+ plane->state = &dm_plane_state->base;
+
+ amdgpu_dm_plane_panic_flush(plane);
+}
+
+static const struct drm_plane_funcs dm_test_plane_reset_funcs = {
+ .atomic_destroy_state = amdgpu_dm_plane_drm_plane_destroy_state,
+};
+
+/**
+ * dm_test_plane_reset_initializes_state() - Verify reset installs default state.
+ * @test: KUnit test context.
+ *
+ * Verify amdgpu_dm_plane_drm_plane_reset() destroys the existing plane state,
+ * allocates a fresh dm_plane_state, and initializes the AMD-specific transfer
+ * function and HDR multiplier defaults.
+ */
+static void dm_test_plane_reset_initializes_state(struct kunit *test)
+{
+ struct dm_plane_state *old_state;
+ struct dm_plane_state *new_state;
+ struct drm_plane *plane;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+
+ /*
+ * Provide an existing state plus a funcs table so reset exercises the
+ * destroy-existing-state path. The destroy hook frees this state, so it
+ * must be a plain (non-KUnit-managed) allocation.
+ */
+ old_state = kzalloc(sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, old_state);
+ plane->funcs = &dm_test_plane_reset_funcs;
+ plane->state = &old_state->base;
+
+ amdgpu_dm_plane_drm_plane_reset(plane);
+
+ KUNIT_ASSERT_NOT_NULL(test, plane->state);
+ new_state = to_dm_plane_state(plane->state);
+ KUNIT_EXPECT_EQ(test, new_state->degamma_tf, AMDGPU_TRANSFER_FUNCTION_DEFAULT);
+ KUNIT_EXPECT_EQ(test, new_state->hdr_mult, AMDGPU_HDR_MULT_DEFAULT);
+ KUNIT_EXPECT_EQ(test, new_state->shaper_tf, AMDGPU_TRANSFER_FUNCTION_DEFAULT);
+ KUNIT_EXPECT_EQ(test, new_state->blend_tf, AMDGPU_TRANSFER_FUNCTION_DEFAULT);
+
+ kfree(new_state);
+}
+
+/**
+ * dm_test_plane_duplicate_state_copies_fields() - Verify state duplication.
+ * @test: KUnit test context.
+ *
+ * Verify amdgpu_dm_plane_drm_plane_duplicate_state() allocates a new state and
+ * copies the transfer-function and HDR-multiplier fields from the current
+ * plane state when no DC state or color blob is attached.
+ */
+static void dm_test_plane_duplicate_state_copies_fields(struct kunit *test)
+{
+ struct dm_plane_state *old_state;
+ struct drm_plane_state *dup_base;
+ struct dm_plane_state *dup_state;
+ struct drm_plane *plane;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, old_state);
+
+ old_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_PQ_EOTF;
+ old_state->hdr_mult = 0x123456789ULL;
+ old_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_IDENTITY;
+ old_state->blend_tf = AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF;
+ plane->state = &old_state->base;
+
+ dup_base = amdgpu_dm_plane_drm_plane_duplicate_state(plane);
+ KUNIT_ASSERT_NOT_NULL(test, dup_base);
+
+ dup_state = to_dm_plane_state(dup_base);
+ KUNIT_EXPECT_EQ(test, dup_state->degamma_tf, AMDGPU_TRANSFER_FUNCTION_PQ_EOTF);
+ KUNIT_EXPECT_EQ(test, dup_state->hdr_mult, 0x123456789ULL);
+ KUNIT_EXPECT_EQ(test, dup_state->shaper_tf, AMDGPU_TRANSFER_FUNCTION_IDENTITY);
+ KUNIT_EXPECT_EQ(test, dup_state->blend_tf, AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF);
+ KUNIT_EXPECT_NULL(test, dup_state->dc_state);
+
+ kfree(dup_state);
+}
+
+/**
+ * dm_test_plane_destroy_state_minimal() - Verify destroy of a minimal state.
+ * @test: KUnit test context.
+ *
+ * Verify amdgpu_dm_plane_drm_plane_destroy_state() tears down a plane state
+ * that has no color blobs or DC plane state attached without dereferencing
+ * NULL resources.
+ */
+static void dm_test_plane_destroy_state_minimal(struct kunit *test)
+{
+ struct dm_plane_state *dm_plane_state;
+ struct drm_plane *plane;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+
+ /* destroy_state frees the state itself, so use a plain allocation. */
+ dm_plane_state = kzalloc(sizeof(*dm_plane_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_plane_state);
+
+ amdgpu_dm_plane_drm_plane_destroy_state(plane, &dm_plane_state->base);
+}
+
static struct kunit_case amdgpu_dm_plane_test_cases[] = {
/* amdgpu_dm_plane_is_video_format() */
KUNIT_CASE(dm_test_plane_is_video_format_known_video),
@@ -2790,6 +3230,23 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = {
KUNIT_CASE(dm_test_helper_check_state_small_viewport_height),
KUNIT_CASE(dm_test_helper_check_state_bottom_clipped_height),
KUNIT_CASE(dm_test_helper_check_state_scaling_caps),
+ /* amdgpu_dm_plane_atomic_async_check() */
+ KUNIT_CASE(dm_test_atomic_async_check_rejects),
+ KUNIT_CASE(dm_test_atomic_async_check_overlay_cursor),
+ /* amdgpu_dm_plane_atomic_check() */
+ KUNIT_CASE(dm_test_atomic_check_no_dc_state),
+ KUNIT_CASE(dm_test_atomic_check_missing_crtc_state),
+ KUNIT_CASE(dm_test_atomic_check_helper_failure),
+ KUNIT_CASE(dm_test_atomic_check_color_pipeline_conflict),
+ KUNIT_CASE(dm_test_atomic_check_scaling_failure),
+ /* amdgpu_dm_plane_panic_flush() */
+ KUNIT_CASE(dm_test_panic_flush_no_dc_state),
+ /* amdgpu_dm_plane_drm_plane_reset() */
+ KUNIT_CASE(dm_test_plane_reset_initializes_state),
+ /* amdgpu_dm_plane_drm_plane_duplicate_state() */
+ KUNIT_CASE(dm_test_plane_duplicate_state_copies_fields),
+ /* amdgpu_dm_plane_drm_plane_destroy_state() */
+ KUNIT_CASE(dm_test_plane_destroy_state_minimal),
/* amdgpu_dm_plane_add_modifier() */
KUNIT_CASE(dm_test_add_modifier_appends_value),
KUNIT_CASE(dm_test_add_modifier_grows_capacity),