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
