The DMA 3D LUT pipeline is not covered by the colorop tests, which
always build the pipeline with a host-loaded 3D LUT.

Export amdgpu_dm_colorop_dma_3dlut_active() and
amdgpu_dm_initialize_dma_3dlut_pipeline() for KUnit and test that:

- the 3D LUT slot becomes a bypassable DMA 3D LUT colorop advertising
  the given mode, and only it has prepare_fb and cleanup_fb hooks;
- no DMA 3D LUT colorop is created without 3D LUT hardware;
- the advertised mode is a 17-point XBGR16161616 linear LUT, and its
  pitch follows the DC pitch alignment;
- the LUT is only active when not bypassed and a framebuffer is set;
- prepare_fb and cleanup_fb do not touch the framebuffer of an
  inactive LUT.

Signed-off-by: Harry Wentland <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |   2 +
 .../amdgpu_dm/tests/amdgpu_dm_colorop_test.c  | 282 ++++++++++++++++++
 2 files changed, 284 insertions(+)

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 b737b45fb2f4..f42339a98549 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
@@ -123,6 +123,7 @@ bool amdgpu_dm_colorop_dma_3dlut_active(const struct 
drm_colorop *colorop,
        return colorop->type == DRM_COLOROP_3D_LUT_DMA && colorop_state &&
               !colorop_state->bypass && colorop_state->fb;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_colorop_dma_3dlut_active);
 
 /* Pinned while its colorop state is current, since DC re-fetches the LUT */
 static int dm_colorop_prepare_fb(struct drm_colorop *colorop,
@@ -414,3 +415,4 @@ int amdgpu_dm_initialize_dma_3dlut_pipeline(struct 
drm_plane *plane, struct drm_
 
        return amdgpu_dm_build_default_pipeline(dev, plane, hw_3d_lut, &mode, 
list);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_initialize_dma_3dlut_pipeline);
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 fda30c29a81f..fadeb690b8f0 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
@@ -7,7 +7,11 @@
 
 #include <kunit/test.h>
 #include <drm/drm_colorop.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_kunit_helpers.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_property.h>
 
 #include "dc.h"
 #include "amdgpu.h"
@@ -310,6 +314,277 @@ static void 
dm_test_initialize_default_pipeline_no_3d_lut(struct kunit *test)
                                                 expected, 
ARRAY_SIZE(expected));
 }
 
+/* Tests for the DMA (fast-loading) 3D LUT pipeline */
+
+static const enum drm_colorop_type dm_dma_3dlut_pipeline[] = {
+       DRM_COLOROP_1D_CURVE,   /* degam TF */
+       DRM_COLOROP_MULTIPLIER,
+       DRM_COLOROP_CTM_3X4,
+       DRM_COLOROP_1D_CURVE,   /* shaper TF */
+       DRM_COLOROP_1D_LUT,     /* shaper LUT */
+       DRM_COLOROP_3D_LUT_DMA,
+       DRM_COLOROP_1D_CURVE,   /* blnd TF */
+       DRM_COLOROP_1D_LUT,     /* blnd LUT */
+};
+
+static const struct drm_mode_3dlut_mode dm_dma_3dlut_test_mode = {
+       .lut_size = 17,
+       .format = DRM_FORMAT_XBGR16161616,
+       .modifier = DRM_FORMAT_MOD_LINEAR,
+       .pitch = 256,
+       .traversal_order = DRM_COLOROP_LUT3D_TRAVERSAL_RGB,
+       .interpolation = DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+};
+
+static struct drm_colorop *dm_find_dma_3dlut_colorop(struct kunit *test,
+                                                    struct drm_device *drm)
+{
+       struct drm_colorop *op;
+
+       drm_for_each_colorop(op, drm)
+               if (op->type == DRM_COLOROP_3D_LUT_DMA)
+                       return op;
+
+       KUNIT_FAIL_AND_ABORT(test, "no DMA 3D LUT colorop\n");
+       return NULL;
+}
+
+static const struct drm_mode_3dlut_mode *
+dm_expect_dma_3dlut_formats(struct kunit *test, struct drm_colorop *op)
+{
+       struct drm_property_blob *blob = op->lut_3d_formats_blob;
+
+       KUNIT_ASSERT_NOT_NULL(test, blob);
+       KUNIT_ASSERT_EQ(test, blob->length, sizeof(struct drm_mode_3dlut_mode));
+
+       return blob->data;
+}
+
+/**
+ * dm_test_build_dma_3dlut_pipeline() - Verify the 3D LUT slot becomes a DMA
+ *   3D LUT colorop advertising the given mode, and only it pins framebuffers.
+ * @test: KUnit test context.
+ */
+static void dm_test_build_dma_3dlut_pipeline(struct kunit *test)
+{
+       const struct drm_colorop_helper_funcs *funcs;
+       const struct drm_mode_3dlut_mode *mode;
+       struct drm_prop_enum_list list = {};
+       struct drm_colorop *op;
+       struct drm_device *drm;
+       struct drm_plane *plane;
+       int ret;
+
+       plane = dm_colorop_alloc_plane(test, &drm);
+
+       ret = amdgpu_dm_build_default_pipeline(drm, plane, true,
+                                              &dm_dma_3dlut_test_mode, &list);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+       kfree(list.name);
+
+       dm_expect_colorop_pipeline(test, drm, &list, dm_dma_3dlut_pipeline,
+                                  ARRAY_SIZE(dm_dma_3dlut_pipeline));
+
+       drm_for_each_colorop(op, drm) {
+               if (op->type == DRM_COLOROP_3D_LUT_DMA)
+                       continue;
+               KUNIT_EXPECT_NULL(test, op->helper_private);
+       }
+
+       op = dm_find_dma_3dlut_colorop(test, drm);
+       KUNIT_EXPECT_NOT_NULL(test, op->fb_property);
+       KUNIT_EXPECT_NOT_NULL(test, op->lut_3d_format_property);
+       KUNIT_EXPECT_NOT_NULL(test, op->lut_3d_formats_property);
+
+       funcs = op->helper_private;
+       KUNIT_ASSERT_NOT_NULL(test, funcs);
+       KUNIT_EXPECT_NOT_NULL(test, funcs->prepare_fb);
+       KUNIT_EXPECT_NOT_NULL(test, funcs->cleanup_fb);
+
+       mode = dm_expect_dma_3dlut_formats(test, op);
+       KUNIT_EXPECT_MEMEQ(test, mode, &dm_dma_3dlut_test_mode, sizeof(*mode));
+}
+
+/**
+ * dm_test_build_dma_3dlut_pipeline_no_3d_lut() - Verify no DMA 3D LUT colorop
+ *   is created without 3D LUT hardware.
+ * @test: KUnit test context.
+ */
+static void dm_test_build_dma_3dlut_pipeline_no_3d_lut(struct kunit *test)
+{
+       static const enum drm_colorop_type expected[] = {
+               DRM_COLOROP_1D_CURVE,
+               DRM_COLOROP_MULTIPLIER,
+               DRM_COLOROP_CTM_3X4,
+               DRM_COLOROP_1D_CURVE,
+               DRM_COLOROP_1D_LUT,
+       };
+       struct drm_prop_enum_list list = {};
+       struct drm_device *drm;
+       struct drm_plane *plane;
+       int ret;
+
+       plane = dm_colorop_alloc_plane(test, &drm);
+
+       ret = amdgpu_dm_build_default_pipeline(drm, plane, false,
+                                              &dm_dma_3dlut_test_mode, &list);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+       kfree(list.name);
+
+       dm_expect_colorop_pipeline(test, drm, &list, expected, 
ARRAY_SIZE(expected));
+}
+
+struct dm_dma_3dlut_pitch_param {
+       const char *name;
+       u32 pitch_alignment;
+       u32 pitch;
+};
+
+/* A 17-entry row of XBGR16161616 is 136 bytes. */
+static const struct dm_dma_3dlut_pitch_param dm_dma_3dlut_pitch_params[] = {
+       { "align_1",    1,      136 },
+       { "align_64",   64,     192 },
+       { "align_256",  256,    256 },
+};
+
+KUNIT_ARRAY_PARAM_DESC(dm_dma_3dlut_pitch, dm_dma_3dlut_pitch_params, name);
+
+/**
+ * dm_test_initialize_dma_3dlut_pipeline() - Verify the advertised DMA 3D LUT
+ *   mode, and that its pitch follows the DC pitch alignment.
+ * @test: KUnit test context.
+ */
+static void dm_test_initialize_dma_3dlut_pipeline(struct kunit *test)
+{
+       const struct dm_dma_3dlut_pitch_param *param = test->param_value;
+       const struct drm_mode_3dlut_mode *mode;
+       struct drm_prop_enum_list list = {};
+       struct amdgpu_device *adev;
+       struct drm_device *drm;
+       struct drm_plane *plane;
+       struct dc *dc;
+       int ret;
+
+       adev = dm_kunit_alloc_adev(test);
+       drm = &adev->ddev;
+
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
+       adev->dm.dc = dc;
+       dc->caps.color.mpc.preblend = true;
+       dc->caps.color.mpc.mcm_3d_lut_caps.pitch_alignment = 
param->pitch_alignment;
+
+       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);
+
+       ret = amdgpu_dm_initialize_dma_3dlut_pipeline(plane, &list);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+       kfree(list.name);
+
+       dm_expect_colorop_pipeline(test, drm, &list, dm_dma_3dlut_pipeline,
+                                  ARRAY_SIZE(dm_dma_3dlut_pipeline));
+
+       mode = dm_expect_dma_3dlut_formats(test, 
dm_find_dma_3dlut_colorop(test, drm));
+       KUNIT_EXPECT_EQ(test, mode->lut_size, 17);
+       KUNIT_EXPECT_EQ(test, mode->format, DRM_FORMAT_XBGR16161616);
+       KUNIT_EXPECT_EQ(test, mode->modifier, DRM_FORMAT_MOD_LINEAR);
+       KUNIT_EXPECT_EQ(test, mode->pitch, param->pitch);
+       KUNIT_EXPECT_EQ(test, mode->traversal_order, 
DRM_COLOROP_LUT3D_TRAVERSAL_RGB);
+       KUNIT_EXPECT_EQ(test, mode->interpolation,
+                       DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL);
+       KUNIT_EXPECT_EQ(test, mode->flags, 0);
+}
+
+/**
+ * dm_test_colorop_dma_3dlut_active() - Verify the DMA 3D LUT is only active
+ *   when not bypassed and a framebuffer is set.
+ * @test: KUnit test context.
+ */
+static void dm_test_colorop_dma_3dlut_active(struct kunit *test)
+{
+       struct drm_colorop_state *state;
+       struct drm_framebuffer *fb;
+       struct drm_colorop *op;
+
+       op = kunit_kzalloc(test, sizeof(*op), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, op);
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+       fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, fb);
+
+       op->type = DRM_COLOROP_3D_LUT_DMA;
+       state->colorop = op;
+       state->bypass = false;
+       state->fb = fb;
+       KUNIT_EXPECT_TRUE(test, amdgpu_dm_colorop_dma_3dlut_active(op, state));
+
+       KUNIT_EXPECT_FALSE(test, amdgpu_dm_colorop_dma_3dlut_active(op, NULL));
+
+       state->bypass = true;
+       KUNIT_EXPECT_FALSE(test, amdgpu_dm_colorop_dma_3dlut_active(op, state));
+
+       state->bypass = false;
+       state->fb = NULL;
+       KUNIT_EXPECT_FALSE(test, amdgpu_dm_colorop_dma_3dlut_active(op, state));
+
+       state->fb = fb;
+       op->type = DRM_COLOROP_3D_LUT;
+       KUNIT_EXPECT_FALSE(test, amdgpu_dm_colorop_dma_3dlut_active(op, state));
+}
+
+/**
+ * dm_test_dma_3dlut_inactive_prepare_cleanup() - Verify prepare_fb and
+ *   cleanup_fb leave a bypassed or framebuffer-less DMA 3D LUT alone.
+ * @test: KUnit test context.
+ *
+ * The framebuffer has no GEM object, so any attempt to pin it would fail or
+ * crash.
+ */
+static void dm_test_dma_3dlut_inactive_prepare_cleanup(struct kunit *test)
+{
+       const struct drm_colorop_helper_funcs *funcs;
+       struct drm_prop_enum_list list = {};
+       struct drm_colorop_state *state;
+       struct drm_framebuffer *fb;
+       struct drm_colorop *op;
+       struct drm_device *drm;
+       struct drm_plane *plane;
+       int ret;
+
+       plane = dm_colorop_alloc_plane(test, &drm);
+
+       ret = amdgpu_dm_build_default_pipeline(drm, plane, true,
+                                              &dm_dma_3dlut_test_mode, &list);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+       kfree(list.name);
+
+       op = dm_find_dma_3dlut_colorop(test, drm);
+       funcs = op->helper_private;
+       KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+       fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, fb);
+       state->colorop = op;
+
+       /* Bypassed, but still pointing at a framebuffer */
+       state->bypass = true;
+       state->fb = fb;
+       KUNIT_EXPECT_EQ(test, funcs->prepare_fb(op, state), 0);
+       funcs->cleanup_fb(op, state);
+
+       /* Enabled without a framebuffer */
+       state->bypass = false;
+       state->fb = NULL;
+       KUNIT_EXPECT_EQ(test, funcs->prepare_fb(op, state), 0);
+       funcs->cleanup_fb(op, state);
+}
+
 /* Failure paths of amdgpu_dm_build_default_pipeline() */
 
 /*
@@ -517,6 +792,13 @@ 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),
+       /* DMA 3D LUT */
+       KUNIT_CASE(dm_test_build_dma_3dlut_pipeline),
+       KUNIT_CASE(dm_test_build_dma_3dlut_pipeline_no_3d_lut),
+       KUNIT_CASE_PARAM(dm_test_initialize_dma_3dlut_pipeline,
+                        dm_dma_3dlut_pitch_gen_params),
+       KUNIT_CASE(dm_test_colorop_dma_3dlut_active),
+       KUNIT_CASE(dm_test_dma_3dlut_inactive_prepare_cleanup),
        /* 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.56.0

Reply via email to