[PATCH 05/59] drm/amd/display: Test colorop error paths

Alex Hung <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
[WHAT]
Add KUnit tests for the failure paths of
amdgpu_dm_build_default_pipeline(): a failed allocation and a failed
initialization at each of the eight pipeline steps.

[HOW]
Add struct amdgpu_dm_colorop_kunit_ops, a KUnit-only indirection table
for the colorop allocation and the five DRM colorop constructors. The
constructors cannot be made to fail through their arguments, so tests
install a fake allocator and spies that delegate to the real
constructors and override the return value of one selected call.
Non-KUnit builds call the same functions directly.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 110 ++++++---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h |  28 +++
 .../amdgpu_dm/tests/amdgpu_dm_colorop_test.c  | 232 ++++++++++++++++--
 3 files changed, 312 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index fae9007b9998..d7260518a23e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -63,6 +63,48 @@ static const struct drm_colorop_funcs dm_colorop_funcs = {
 	.destroy = drm_colorop_destroy,
 };
 
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+static struct drm_colorop *amdgpu_dm_colorop_kzalloc(void)
+{
+	return kzalloc_obj(struct drm_colorop);
+}
+
+static const struct amdgpu_dm_colorop_kunit_ops amdgpu_dm_colorop_default_ops = {
+	.colorop_kzalloc_obj = amdgpu_dm_colorop_kzalloc,
+	.curve_1d_init = drm_plane_colorop_curve_1d_init,
+	.curve_1d_lut_init = drm_plane_colorop_curve_1d_lut_init,
+	.ctm_3x4_init = drm_plane_colorop_ctm_3x4_init,
+	.mult_init = drm_plane_colorop_mult_init,
+	.lut3d_init = drm_plane_colorop_3dlut_init,
+};
+
+static const struct amdgpu_dm_colorop_kunit_ops *amdgpu_dm_colorop_ops =
+	&amdgpu_dm_colorop_default_ops;
+
+void amdgpu_dm_colorop_kunit_set_ops(const struct amdgpu_dm_colorop_kunit_ops *ops)
+{
+	amdgpu_dm_colorop_ops = ops ? ops : &amdgpu_dm_colorop_default_ops;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_colorop_kunit_set_ops);
+
+#define colorop_kzalloc_obj		amdgpu_dm_colorop_ops->colorop_kzalloc_obj
+#define colorop_curve_1d_init		amdgpu_dm_colorop_ops->curve_1d_init
+#define colorop_curve_1d_lut_init	amdgpu_dm_colorop_ops->curve_1d_lut_init
+#define colorop_ctm_3x4_init		amdgpu_dm_colorop_ops->ctm_3x4_init
+#define colorop_mult_init		amdgpu_dm_colorop_ops->mult_init
+#define colorop_3dlut_init		amdgpu_dm_colorop_ops->lut3d_init
+
+#else
+
+#define colorop_kzalloc_obj()		kzalloc_obj(struct drm_colorop)
+#define colorop_curve_1d_init		drm_plane_colorop_curve_1d_init
+#define colorop_curve_1d_lut_init	drm_plane_colorop_curve_1d_lut_init
+#define colorop_ctm_3x4_init		drm_plane_colorop_ctm_3x4_init
+#define colorop_mult_init		drm_plane_colorop_mult_init
+#define colorop_3dlut_init		drm_plane_colorop_3dlut_init
+
+#endif
+
 STATIC_IFN_KUNIT int
 amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane,
 				  bool hw_3d_lut, struct drm_prop_enum_list *list)
@@ -74,15 +116,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 	memset(ops, 0, sizeof(ops));
 
 	/* 1D curve - DEGAM TF */
-	ops[i] = kzalloc_obj(*ops[0]);
+	ops[i] = colorop_kzalloc_obj();
 	if (!ops[i]) {
 		ret = -ENOMEM;
 		goto cleanup;
 	}
 
-	ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
-					      amdgpu_dm_supported_degam_tfs,
-					      DRM_COLOROP_FLAG_ALLOW_BYPASS);
+	ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
+				    amdgpu_dm_supported_degam_tfs,
+				    DRM_COLOROP_FLAG_ALLOW_BYPASS);
 	if (ret)
 		goto cleanup;
 
@@ -91,14 +133,14 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 	i++;
 
 	/* Multiplier */
-	ops[i] = kzalloc_obj(struct drm_colorop);
+	ops[i] = colorop_kzalloc_obj();
 	if (!ops[i]) {
 		ret = -ENOMEM;
 		goto cleanup;
 	}
 
-	ret = drm_plane_colorop_mult_init(dev, ops[i], plane, &dm_colorop_funcs,
-					  DRM_COLOROP_FLAG_ALLOW_BYPASS);
+	ret = colorop_mult_init(dev, ops[i], plane, &dm_colorop_funcs,
+				DRM_COLOROP_FLAG_ALLOW_BYPASS);
 	if (ret)
 		goto cleanup;
 
@@ -107,15 +149,14 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 	i++;
 
 	/* 3x4 matrix */
-	ops[i] = kzalloc_obj(struct drm_colorop);
+	ops[i] = colorop_kzalloc_obj();
 	if (!ops[i]) {
 		ret = -ENOMEM;
 		goto cleanup;
 	}
 
-	ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane,
-					     &dm_colorop_funcs,
-					     DRM_COLOROP_FLAG_ALLOW_BYPASS);
+	ret = colorop_ctm_3x4_init(dev, ops[i], plane, &dm_colorop_funcs,
+				   DRM_COLOROP_FLAG_ALLOW_BYPASS);
 	if (ret)
 		goto cleanup;
 
@@ -125,15 +166,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 
 	if (hw_3d_lut) {
 		/* 1D curve - SHAPER TF */
-		ops[i] = kzalloc_obj(*ops[0]);
+		ops[i] = colorop_kzalloc_obj();
 		if (!ops[i]) {
 			ret = -ENOMEM;
 			goto cleanup;
 		}
 
-		ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
-						amdgpu_dm_supported_shaper_tfs,
-						DRM_COLOROP_FLAG_ALLOW_BYPASS);
+		ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
+					    amdgpu_dm_supported_shaper_tfs,
+					    DRM_COLOROP_FLAG_ALLOW_BYPASS);
 		if (ret)
 			goto cleanup;
 
@@ -142,17 +183,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 		i++;
 
 		/* 1D LUT - SHAPER LUT */
-		ops[i] = kzalloc_obj(*ops[0]);
+		ops[i] = colorop_kzalloc_obj();
 		if (!ops[i]) {
 			ret = -ENOMEM;
 			goto cleanup;
 		}
 
-		ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane,
-							&dm_colorop_funcs,
-							MAX_COLOR_LUT_ENTRIES,
-							DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
-							DRM_COLOROP_FLAG_ALLOW_BYPASS);
+		ret = colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs,
+						MAX_COLOR_LUT_ENTRIES,
+						DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+						DRM_COLOROP_FLAG_ALLOW_BYPASS);
 		if (ret)
 			goto cleanup;
 
@@ -161,16 +201,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 		i++;
 
 		/* 3D LUT */
-		ops[i] = kzalloc_obj(*ops[0]);
+		ops[i] = colorop_kzalloc_obj();
 		if (!ops[i]) {
 			ret = -ENOMEM;
 			goto cleanup;
 		}
 
-		ret = drm_plane_colorop_3dlut_init(dev, ops[i], plane,
-					&dm_colorop_funcs, LUT3D_SIZE,
-					DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
-					DRM_COLOROP_FLAG_ALLOW_BYPASS);
+		ret = colorop_3dlut_init(dev, ops[i], plane, &dm_colorop_funcs,
+					 LUT3D_SIZE,
+					 DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+					 DRM_COLOROP_FLAG_ALLOW_BYPASS);
 		if (ret)
 			goto cleanup;
 
@@ -180,15 +220,15 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 	}
 
 	/* 1D curve - BLND TF */
-	ops[i] = kzalloc_obj(*ops[0]);
+	ops[i] = colorop_kzalloc_obj();
 	if (!ops[i]) {
 		ret = -ENOMEM;
 		goto cleanup;
 	}
 
-	ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
-					      amdgpu_dm_supported_blnd_tfs,
-					      DRM_COLOROP_FLAG_ALLOW_BYPASS);
+	ret = colorop_curve_1d_init(dev, ops[i], plane, &dm_colorop_funcs,
+				    amdgpu_dm_supported_blnd_tfs,
+				    DRM_COLOROP_FLAG_ALLOW_BYPASS);
 	if (ret)
 		goto cleanup;
 
@@ -197,16 +237,16 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
 	i++;
 
 	/* 1D LUT - BLND LUT */
-	ops[i] = kzalloc_obj(struct drm_colorop);
+	ops[i] = colorop_kzalloc_obj();
 	if (!ops[i]) {
 		ret = -ENOMEM;
 		goto cleanup;
 	}
 
-	ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs,
-						  MAX_COLOR_LUT_ENTRIES,
-						  DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
-						  DRM_COLOROP_FLAG_ALLOW_BYPASS);
+	ret = colorop_curve_1d_lut_init(dev, ops[i], plane, &dm_colorop_funcs,
+					MAX_COLOR_LUT_ENTRIES,
+					DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+					DRM_COLOROP_FLAG_ALLOW_BYPASS);
 	if (ret)
 		goto cleanup;
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
index 77364d954d3b..ff46c4f7b126 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
@@ -34,8 +34,36 @@ extern const u64 amdgpu_dm_supported_blnd_tfs;
 int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list);
 
 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+#include <drm/drm_colorop.h>
+
+struct amdgpu_dm_colorop_kunit_ops {
+	struct drm_colorop *(*colorop_kzalloc_obj)(void);
+	int (*curve_1d_init)(struct drm_device *dev, struct drm_colorop *colorop,
+			     struct drm_plane *plane, const struct drm_colorop_funcs *funcs,
+			     u64 supported_tfs, uint32_t flags);
+	int (*curve_1d_lut_init)(struct drm_device *dev, struct drm_colorop *colorop,
+				 struct drm_plane *plane,
+				 const struct drm_colorop_funcs *funcs,
+				 uint32_t lut_size,
+				 enum drm_colorop_lut1d_interpolation_type interpolation,
+				 uint32_t flags);
+	int (*ctm_3x4_init)(struct drm_device *dev, struct drm_colorop *colorop,
+			    struct drm_plane *plane, const struct drm_colorop_funcs *funcs,
+			    uint32_t flags);
+	int (*mult_init)(struct drm_device *dev, struct drm_colorop *colorop,
+			 struct drm_plane *plane, const struct drm_colorop_funcs *funcs,
+			 uint32_t flags);
+	int (*lut3d_init)(struct drm_device *dev, struct drm_colorop *colorop,
+			  struct drm_plane *plane,
+			  const struct drm_colorop_funcs *funcs,
+			  uint32_t lut_size,
+			  enum drm_colorop_lut3d_interpolation_type interpolation,
+			  uint32_t flags);
+};
+
 int amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane,
 				      bool hw_3d_lut, struct drm_prop_enum_list *list);
+void amdgpu_dm_colorop_kunit_set_ops(const struct amdgpu_dm_colorop_kunit_ops *ops);
 #endif
 
 #endif /* __AMDGPU_DM_COLOROP_H__*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
index 2e557ff66818..90cc5592ef5d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
@@ -136,6 +136,30 @@ static void kunit_colorop_pipeline_destroy(void *drm)
 	drm_colorop_pipeline_destroy((struct drm_device *)drm);
 }
 
+/*
+ * A plain drm_device (not an amdgpu_device) is enough: building the pipeline
+ * only needs the DRM mode-config infrastructure. The pipeline is destroyed
+ * before the DRM device so the colorops are freed while the device is valid.
+ */
+static struct drm_plane *dm_colorop_alloc_plane(struct kunit *test, struct drm_device **drm)
+{
+	struct drm_plane *plane;
+	struct device *dev;
+
+	dev = drm_kunit_helper_alloc_device(test);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+	*drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(**drm), 0, 0);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, *drm);
+
+	plane = drm_kunit_helper_create_primary_plane(test, *drm, NULL, NULL, NULL, 0, NULL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+	kunit_add_action(test, kunit_colorop_pipeline_destroy, *drm);
+
+	return plane;
+}
+
 static void dm_expect_colorop_pipeline(struct kunit *test, struct drm_device *drm,
 				       const struct drm_prop_enum_list *list,
 				       const enum drm_colorop_type *expected,
@@ -177,34 +201,12 @@ static void dm_test_initialize_default_pipeline(struct kunit *test)
 		DRM_COLOROP_1D_CURVE,	/* blnd TF */
 		DRM_COLOROP_1D_LUT,	/* blnd LUT */
 	};
-	struct device *dev;
 	struct drm_device *drm;
 	struct drm_plane *plane;
 	struct drm_prop_enum_list list = {};
 	int ret;
 
-	dev = drm_kunit_helper_alloc_device(test);
-	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
-
-	/*
-	 * Allocate a plain drm_device (not an amdgpu_device) — sufficient
-	 * because amdgpu_dm_build_default_pipeline() only needs the DRM
-	 * mode-config infrastructure, not the amdgpu device wrapper.
-	 */
-	drm = __drm_kunit_helper_alloc_drm_device(test, dev,
-						   sizeof(*drm), 0, 0);
-	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
-
-	plane = drm_kunit_helper_create_primary_plane(test, drm,
-						       NULL, NULL, NULL, 0, NULL);
-	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
-
-	/*
-	 * Destroy the pipeline before the DRM device is cleaned up so
-	 * that the colorop objects (kzalloc'd inside the function) are
-	 * freed while the device is still valid.
-	 */
-	kunit_add_action(test, kunit_colorop_pipeline_destroy, drm);
+	plane = dm_colorop_alloc_plane(test, &drm);
 
 	ret = amdgpu_dm_build_default_pipeline(drm, plane, true, &list);
 	KUNIT_ASSERT_EQ(test, ret, 0);
@@ -308,6 +310,187 @@ static void dm_test_initialize_default_pipeline_no_3d_lut(struct kunit *test)
 						 expected, ARRAY_SIZE(expected));
 }
 
+/* Failure paths of amdgpu_dm_build_default_pipeline() */
+
+/*
+ * The DRM colorop constructors cannot be made to fail through their arguments,
+ * so failures are injected with a fake allocator and with spies that delegate
+ * to the real constructors and override the return value of one call.
+ */
+struct dm_colorop_fault {
+	int alloc_fail_at;	/* allocation index returning NULL, -1 to disable */
+	int init_fail_at;	/* init call index returning an error, -1 to disable */
+	int allocs;
+	int inits;
+};
+
+static struct dm_colorop_fault dm_colorop_fault;
+
+static struct drm_colorop *dm_colorop_test_alloc(void)
+{
+	if (dm_colorop_fault.allocs++ == dm_colorop_fault.alloc_fail_at)
+		return NULL;
+
+	return kzalloc_obj(struct drm_colorop);
+}
+
+static int dm_colorop_test_init_ret(int ret)
+{
+	if (ret)
+		return ret;
+
+	if (dm_colorop_fault.inits++ == dm_colorop_fault.init_fail_at)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int dm_colorop_test_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop,
+					 struct drm_plane *plane,
+					 const struct drm_colorop_funcs *funcs,
+					 u64 supported_tfs, uint32_t flags)
+{
+	return dm_colorop_test_init_ret(drm_plane_colorop_curve_1d_init(dev, colorop, plane,
+									funcs, supported_tfs,
+									flags));
+}
+
+static int dm_colorop_test_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop,
+				       struct drm_plane *plane,
+				       const struct drm_colorop_funcs *funcs,
+				       uint32_t lut_size,
+				       enum drm_colorop_lut1d_interpolation_type interpolation,
+				       uint32_t flags)
+{
+	return dm_colorop_test_init_ret(drm_plane_colorop_curve_1d_lut_init(dev, colorop, plane,
+									    funcs, lut_size,
+									    interpolation,
+									    flags));
+}
+
+static int dm_colorop_test_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop,
+					struct drm_plane *plane,
+					const struct drm_colorop_funcs *funcs, uint32_t flags)
+{
+	return dm_colorop_test_init_ret(drm_plane_colorop_ctm_3x4_init(dev, colorop, plane,
+								       funcs, flags));
+}
+
+static int dm_colorop_test_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
+				     struct drm_plane *plane,
+				     const struct drm_colorop_funcs *funcs, uint32_t flags)
+{
+	return dm_colorop_test_init_ret(drm_plane_colorop_mult_init(dev, colorop, plane,
+								    funcs, flags));
+}
+
+static int dm_colorop_test_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop,
+				      struct drm_plane *plane,
+				      const struct drm_colorop_funcs *funcs, uint32_t lut_size,
+				      enum drm_colorop_lut3d_interpolation_type interpolation,
+				      uint32_t flags)
+{
+	return dm_colorop_test_init_ret(drm_plane_colorop_3dlut_init(dev, colorop, plane, funcs,
+								     lut_size, interpolation,
+								     flags));
+}
+
+static const struct amdgpu_dm_colorop_kunit_ops dm_colorop_test_ops = {
+	.colorop_kzalloc_obj = dm_colorop_test_alloc,
+	.curve_1d_init = dm_colorop_test_curve_1d_init,
+	.curve_1d_lut_init = dm_colorop_test_1d_lut_init,
+	.ctm_3x4_init = dm_colorop_test_ctm_3x4_init,
+	.mult_init = dm_colorop_test_mult_init,
+	.lut3d_init = dm_colorop_test_3dlut_init,
+};
+
+static void dm_colorop_test_reset_ops(void *unused)
+{
+	amdgpu_dm_colorop_kunit_set_ops(NULL);
+}
+
+/*
+ * Build the full (3D LUT) pipeline with one injected failure and return the
+ * error reported by amdgpu_dm_build_default_pipeline().
+ */
+static int dm_colorop_build_with_fault(struct kunit *test, struct drm_device **drm,
+				       int alloc_fail_at, int init_fail_at)
+{
+	struct drm_prop_enum_list list = {};
+	struct drm_plane *plane;
+	int ret;
+
+	plane = dm_colorop_alloc_plane(test, drm);
+
+	dm_colorop_fault = (struct dm_colorop_fault){
+		.alloc_fail_at = alloc_fail_at,
+		.init_fail_at = init_fail_at,
+	};
+
+	amdgpu_dm_colorop_kunit_set_ops(&dm_colorop_test_ops);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_colorop_test_reset_ops, NULL), 0);
+
+	ret = amdgpu_dm_build_default_pipeline(*drm, plane, true, &list);
+	kfree(list.name);
+
+	return ret;
+}
+
+struct dm_colorop_step_param {
+	const char *name;
+	int index;
+};
+
+/* Pipeline steps in creation order when the 3D LUT block is built. */
+static const struct dm_colorop_step_param dm_colorop_step_params[] = {
+	{ "degam_tf",	0 },
+	{ "multiplier",	1 },
+	{ "ctm_3x4",	2 },
+	{ "shaper_tf",	3 },
+	{ "shaper_lut",	4 },
+	{ "lut3d",	5 },
+	{ "blnd_tf",	6 },
+	{ "blnd_lut",	7 },
+};
+
+KUNIT_ARRAY_PARAM_DESC(dm_colorop_step, dm_colorop_step_params, name);
+
+/**
+ * dm_test_build_pipeline_alloc_fail() - Verify a failed alloc returns -ENOMEM and cleans up.
+ * @test: KUnit test context.
+ */
+static void dm_test_build_pipeline_alloc_fail(struct kunit *test)
+{
+	const struct dm_colorop_step_param *param = test->param_value;
+	struct drm_device *drm;
+	int ret;
+
+	ret = dm_colorop_build_with_fault(test, &drm, param->index, -1);
+
+	KUNIT_EXPECT_EQ(test, ret, -ENOMEM);
+	KUNIT_EXPECT_EQ(test, dm_colorop_fault.allocs, param->index + 1);
+	KUNIT_EXPECT_EQ(test, dm_colorop_fault.inits, param->index);
+	KUNIT_EXPECT_EQ(test, drm->mode_config.num_colorop, 0);
+}
+
+/**
+ * dm_test_build_pipeline_init_fail() - Verify a failed init propagates the error and cleans up.
+ * @test: KUnit test context.
+ */
+static void dm_test_build_pipeline_init_fail(struct kunit *test)
+{
+	const struct dm_colorop_step_param *param = test->param_value;
+	struct drm_device *drm;
+	int ret;
+
+	ret = dm_colorop_build_with_fault(test, &drm, -1, param->index);
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+	KUNIT_EXPECT_EQ(test, dm_colorop_fault.allocs, param->index + 1);
+	KUNIT_EXPECT_EQ(test, dm_colorop_fault.inits, param->index + 1);
+	KUNIT_EXPECT_EQ(test, drm->mode_config.num_colorop, 0);
+}
+
 static struct kunit_case dm_colorop_test_cases[] = {
 	/* degam TFs */
 	KUNIT_CASE(dm_test_supported_degam_tfs_has_srgb_eotf),
@@ -334,6 +517,9 @@ static struct kunit_case dm_colorop_test_cases[] = {
 	KUNIT_CASE(dm_test_initialize_default_pipeline_dpp_3d_lut),
 	KUNIT_CASE(dm_test_initialize_default_pipeline_mpc_preblend),
 	KUNIT_CASE(dm_test_initialize_default_pipeline_no_3d_lut),
+	/* amdgpu_dm_build_default_pipeline failure paths */
+	KUNIT_CASE_PARAM(dm_test_build_pipeline_alloc_fail, dm_colorop_step_gen_params),
+	KUNIT_CASE_PARAM(dm_test_build_pipeline_init_fail, dm_colorop_step_gen_params),
 	{}
 };
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.