// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_plane_helper functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_modes.h>
#include <drm/drm_rect.h>
static const struct drm_crtc_state crtc_state = {
.crtc = ZERO_SIZE_PTR,
.enable = true ,
.active = true ,
.mode = {
DRM_MODE("1024x768" , 0 , 65000 , 1024 , 1048 ,
1184 , 1344 , 0 , 768 , 771 , 777 , 806 , 0 ,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
},
};
struct drm_check_plane_state_test {
const char *name;
const char *msg;
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} src, src_expected;
struct {
int x;
int y;
unsigned int w;
unsigned int h;
} crtc, crtc_expected;
unsigned int rotation;
int min_scale;
int max_scale;
bool can_position;
};
static int drm_plane_helper_init(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane *plane;
struct drm_framebuffer *fb;
struct drm_plane_state *mock;
plane = kunit_kzalloc(test, sizeof (*plane), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, plane);
fb = kunit_kzalloc(test, sizeof (*fb), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, fb);
fb->width = 2048 ;
fb->height = 2048 ;
mock = kunit_kzalloc(test, sizeof (*mock), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, mock);
mock->plane = plane;
mock->crtc = ZERO_SIZE_PTR;
mock->fb = fb;
mock->rotation = params->rotation;
mock->src_x = params->src.x;
mock->src_y = params->src.y;
mock->src_w = params->src.w;
mock->src_h = params->src.h;
mock->crtc_x = params->crtc.x;
mock->crtc_y = params->crtc.y;
mock->crtc_w = params->crtc.w;
mock->crtc_h = params->crtc.h;
test->priv = mock;
return 0 ;
}
static void check_src_eq(struct kunit *test, struct drm_plane_state *plane_state,
unsigned int src_x, unsigned int src_y,
unsigned int src_w, unsigned int src_h)
{
struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
KUNIT_ASSERT_GE_MSG(test, plane_state->src.x1, 0 ,
"src x coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
plane_state->src.x1, DRM_RECT_FP_ARG(&plane_state->src));
KUNIT_ASSERT_GE_MSG(test, plane_state->src.y1, 0 ,
"src y coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
plane_state->src.y1, DRM_RECT_FP_ARG(&plane_state->src));
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->src, &expected),
"dst: " DRM_RECT_FP_FMT ", expected: " DRM_RECT_FP_FMT,
DRM_RECT_FP_ARG(&plane_state->src), DRM_RECT_FP_ARG(&expected));
}
static void check_crtc_eq(struct kunit *test, struct drm_plane_state *plane_state,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h)
{
struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, crtc_h);
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->dst, &expected),
"dst: " DRM_RECT_FMT ", expected: " DRM_RECT_FMT,
DRM_RECT_ARG(&plane_state->dst), DRM_RECT_ARG(&expected));
}
static void drm_test_check_plane_state(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane_state *plane_state = test->priv;
KUNIT_ASSERT_EQ_MSG(test,
drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
params->min_scale,
params->max_scale,
params->can_position, false ),
0 , params->msg);
KUNIT_EXPECT_TRUE(test, plane_state->visible);
check_src_eq(test, plane_state, params->src_expected.x, params->src_expected.y,
params->src_expected.w, params->src_expected.h);
check_crtc_eq(test, plane_state, params->crtc_expected.x, params->crtc_expected.y,
params->crtc_expected.w, params->crtc_expected.h);
}
static void drm_check_plane_state_desc(const struct drm_check_plane_state_test *t,
char *desc)
{
sprintf(desc, "%s" , t->name);
}
static const struct drm_check_plane_state_test drm_check_plane_state_tests[] = {
{
.name = "clipping_simple" ,
.msg = "Simple clipping check should pass" ,
.src = { 0 , 0 ,
2048 << 16 ,
2048 << 16 },
.crtc = { 0 , 0 , 2048 , 2048 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
.src_expected = { 0 , 0 , 1024 << 16 , 768 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
{
.name = "clipping_rotate_reflect" ,
.msg = "Rotated clipping check should pass" ,
.src = { 0 , 0 ,
2048 << 16 ,
2048 << 16 },
.crtc = { 0 , 0 , 2048 , 2048 },
.rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
.src_expected = { 0 , 0 , 768 << 16 , 1024 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
{
.name = "positioning_simple" ,
.msg = "Simple positioning should work" ,
.src = { 0 , 0 , 1023 << 16 , 767 << 16 },
.crtc = { 0 , 0 , 1023 , 767 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = true ,
.src_expected = { 0 , 0 , 1023 << 16 , 767 << 16 },
.crtc_expected = { 0 , 0 , 1023 , 767 },
},
{
.name = "upscaling" ,
.msg = "Upscaling exactly 2x should work" ,
.src = { 0 , 0 , 512 << 16 , 384 << 16 },
.crtc = { 0 , 0 , 1024 , 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0 x8000,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
.src_expected = { 0 , 0 , 512 << 16 , 384 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
{
.name = "downscaling" ,
.msg = "Should succeed with exact scaling limit" ,
.src = { 0 , 0 , 2048 << 16 , 1536 << 16 },
.crtc = { 0 , 0 , 1024 , 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0 x20000,
.can_position = false ,
.src_expected = { 0 , 0 , 2048 << 16 , 1536 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
{
.name = "rounding1" ,
.msg = "Should succeed by clipping to exact multiple" ,
.src = { 0 , 0 , 0 x40001, 0 x40001 },
.crtc = { 1022 , 766 , 4 , 4 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0 x10001,
.can_position = true ,
.src_expected = { 0 , 0 , 2 << 16 , 2 << 16 },
.crtc_expected = { 1022 , 766 , 2 , 2 },
},
{
.name = "rounding2" ,
.msg = "Should succeed by clipping to exact multiple" ,
.src = { 0 x20001, 0 x20001, 0 x4040001, 0 x3040001 },
.crtc = { -2 , -2 , 1028 , 772 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0 x10001,
.can_position = false ,
.src_expected = { 0 x40002, 0 x40002, 1024 << 16 , 768 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
{
.name = "rounding3" ,
.msg = "Should succeed by clipping to exact multiple" ,
.src = { 0 , 0 , 0 x3ffff, 0 x3ffff },
.crtc = { 1022 , 766 , 4 , 4 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0 xffff,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = true ,
/* Should not be rounded to 0x20001, which would be upscaling. */
.src_expected = { 0 , 0 , 2 << 16 , 2 << 16 },
.crtc_expected = { 1022 , 766 , 2 , 2 },
},
{
.name = "rounding4" ,
.msg = "Should succeed by clipping to exact multiple" ,
.src = { 0 x1ffff, 0 x1ffff, 0 x403ffff, 0 x303ffff },
.crtc = { -2 , -2 , 1028 , 772 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0 xffff,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
.src_expected = { 0 x3fffe, 0 x3fffe, 1024 << 16 , 768 << 16 },
.crtc_expected = { 0 , 0 , 1024 , 768 },
},
};
KUNIT_ARRAY_PARAM(drm_check_plane_state, drm_check_plane_state_tests, drm_check_plane_state_desc);
static void drm_test_check_invalid_plane_state(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane_state *plane_state = test->priv;
KUNIT_ASSERT_LT_MSG(test,
drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
params->min_scale,
params->max_scale,
params->can_position, false ),
0 , params->msg);
}
static const struct drm_check_plane_state_test drm_check_invalid_plane_state_tests[] = {
{
.name = "positioning_invalid" ,
.msg = "Should not be able to position on the crtc with can_position=false" ,
.src = { 0 , 0 , 1023 << 16 , 767 << 16 },
.crtc = { 0 , 0 , 1023 , 767 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
},
{
.name = "upscaling_invalid" ,
.msg = "Upscaling out of range should fail" ,
.src = { 0 , 0 , 512 << 16 , 384 << 16 },
.crtc = { 0 , 0 , 1024 , 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0 x8001,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false ,
},
{
.name = "downscaling_invalid" ,
.msg = "Downscaling out of range should fail" ,
.src = { 0 , 0 , 2048 << 16 , 1536 << 16 },
.crtc = { 0 , 0 , 1024 , 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0 x1ffff,
.can_position = false ,
},
};
KUNIT_ARRAY_PARAM(drm_check_invalid_plane_state, drm_check_invalid_plane_state_tests,
drm_check_plane_state_desc);
static struct kunit_case drm_plane_helper_test[] = {
KUNIT_CASE_PARAM(drm_test_check_plane_state, drm_check_plane_state_gen_params),
KUNIT_CASE_PARAM(drm_test_check_invalid_plane_state,
drm_check_invalid_plane_state_gen_params),
{}
};
static struct kunit_suite drm_plane_helper_test_suite = {
.name = "drm_plane_helper" ,
.init = drm_plane_helper_init,
.test_cases = drm_plane_helper_test,
};
kunit_test_suite(drm_plane_helper_test_suite);
MODULE_DESCRIPTION("Test cases for the drm_plane_helper functions" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=96 H=94 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland