1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test cases for the drm_plane_helper functions 4 * 5 * Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net> 6 */ 7 8 #include <kunit/test.h> 9 10 #include <drm/drm_atomic_helper.h> 11 #include <drm/drm_framebuffer.h> 12 #include <drm/drm_modes.h> 13 #include <drm/drm_rect.h> 14 15 static const struct drm_crtc_state crtc_state = { 16 .crtc = ZERO_SIZE_PTR, 17 .enable = true, 18 .active = true, 19 .mode = { 20 DRM_MODE("1024x768", 0, 65000, 1024, 1048, 21 1184, 1344, 0, 768, 771, 777, 806, 0, 22 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) 23 }, 24 }; 25 26 struct drm_check_plane_state_test { 27 const char *name; 28 const char *msg; 29 struct { 30 unsigned int x; 31 unsigned int y; 32 unsigned int w; 33 unsigned int h; 34 } src, src_expected; 35 struct { 36 int x; 37 int y; 38 unsigned int w; 39 unsigned int h; 40 } crtc, crtc_expected; 41 unsigned int rotation; 42 int min_scale; 43 int max_scale; 44 bool can_position; 45 }; 46 47 static int drm_plane_helper_init(struct kunit *test) 48 { 49 const struct drm_check_plane_state_test *params = test->param_value; 50 struct drm_plane *plane; 51 struct drm_framebuffer *fb; 52 struct drm_plane_state *mock; 53 54 plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); 55 KUNIT_ASSERT_NOT_NULL(test, plane); 56 57 fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); 58 KUNIT_ASSERT_NOT_NULL(test, fb); 59 fb->width = 2048; 60 fb->height = 2048; 61 62 mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL); 63 KUNIT_ASSERT_NOT_NULL(test, mock); 64 mock->plane = plane; 65 mock->crtc = ZERO_SIZE_PTR; 66 mock->fb = fb; 67 mock->rotation = params->rotation; 68 mock->src_x = params->src.x; 69 mock->src_y = params->src.y; 70 mock->src_w = params->src.w; 71 mock->src_h = params->src.h; 72 mock->crtc_x = params->crtc.x; 73 mock->crtc_y = params->crtc.y; 74 mock->crtc_w = params->crtc.w; 75 mock->crtc_h = params->crtc.h; 76 77 test->priv = mock; 78 79 return 0; 80 } 81 82 static void check_src_eq(struct kunit *test, struct drm_plane_state *plane_state, 83 unsigned int src_x, unsigned int src_y, 84 unsigned int src_w, unsigned int src_h) 85 { 86 struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h); 87 88 KUNIT_ASSERT_GE_MSG(test, plane_state->src.x1, 0, 89 "src x coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT, 90 plane_state->src.x1, DRM_RECT_FP_ARG(&plane_state->src)); 91 92 KUNIT_ASSERT_GE_MSG(test, plane_state->src.y1, 0, 93 "src y coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT, 94 plane_state->src.y1, DRM_RECT_FP_ARG(&plane_state->src)); 95 96 KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->src, &expected), 97 "dst: " DRM_RECT_FP_FMT ", expected: " DRM_RECT_FP_FMT, 98 DRM_RECT_FP_ARG(&plane_state->src), DRM_RECT_FP_ARG(&expected)); 99 } 100 101 static void check_crtc_eq(struct kunit *test, struct drm_plane_state *plane_state, 102 int crtc_x, int crtc_y, 103 unsigned int crtc_w, unsigned int crtc_h) 104 { 105 struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, crtc_h); 106 107 KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->dst, &expected), 108 "dst: " DRM_RECT_FMT ", expected: " DRM_RECT_FMT, 109 DRM_RECT_ARG(&plane_state->dst), DRM_RECT_ARG(&expected)); 110 } 111 112 static void drm_test_check_plane_state(struct kunit *test) 113 { 114 const struct drm_check_plane_state_test *params = test->param_value; 115 struct drm_plane_state *plane_state = test->priv; 116 117 KUNIT_ASSERT_EQ_MSG(test, 118 drm_atomic_helper_check_plane_state(plane_state, &crtc_state, 119 params->min_scale, 120 params->max_scale, 121 params->can_position, false), 122 0, params->msg); 123 KUNIT_EXPECT_TRUE(test, plane_state->visible); 124 check_src_eq(test, plane_state, params->src_expected.x, params->src_expected.y, 125 params->src_expected.w, params->src_expected.h); 126 check_crtc_eq(test, plane_state, params->crtc_expected.x, params->crtc_expected.y, 127 params->crtc_expected.w, params->crtc_expected.h); 128 } 129 130 static void drm_check_plane_state_desc(const struct drm_check_plane_state_test *t, 131 char *desc) 132 { 133 sprintf(desc, "%s", t->name); 134 } 135 136 static const struct drm_check_plane_state_test drm_check_plane_state_tests[] = { 137 { 138 .name = "clipping_simple", 139 .msg = "Simple clipping check should pass", 140 .src = { 0, 0, 141 2048 << 16, 142 2048 << 16 }, 143 .crtc = { 0, 0, 2048, 2048 }, 144 .rotation = DRM_MODE_ROTATE_0, 145 .min_scale = DRM_PLANE_NO_SCALING, 146 .max_scale = DRM_PLANE_NO_SCALING, 147 .can_position = false, 148 .src_expected = { 0, 0, 1024 << 16, 768 << 16 }, 149 .crtc_expected = { 0, 0, 1024, 768 }, 150 }, 151 { 152 .name = "clipping_rotate_reflect", 153 .msg = "Rotated clipping check should pass", 154 .src = { 0, 0, 155 2048 << 16, 156 2048 << 16 }, 157 .crtc = { 0, 0, 2048, 2048 }, 158 .rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X, 159 .min_scale = DRM_PLANE_NO_SCALING, 160 .max_scale = DRM_PLANE_NO_SCALING, 161 .can_position = false, 162 .src_expected = { 0, 0, 768 << 16, 1024 << 16 }, 163 .crtc_expected = { 0, 0, 1024, 768 }, 164 }, 165 { 166 .name = "positioning_simple", 167 .msg = "Simple positioning should work", 168 .src = { 0, 0, 1023 << 16, 767 << 16 }, 169 .crtc = { 0, 0, 1023, 767 }, 170 .rotation = DRM_MODE_ROTATE_0, 171 .min_scale = DRM_PLANE_NO_SCALING, 172 .max_scale = DRM_PLANE_NO_SCALING, 173 .can_position = true, 174 .src_expected = { 0, 0, 1023 << 16, 767 << 16 }, 175 .crtc_expected = { 0, 0, 1023, 767 }, 176 }, 177 { 178 .name = "upscaling", 179 .msg = "Upscaling exactly 2x should work", 180 .src = { 0, 0, 512 << 16, 384 << 16 }, 181 .crtc = { 0, 0, 1024, 768 }, 182 .rotation = DRM_MODE_ROTATE_0, 183 .min_scale = 0x8000, 184 .max_scale = DRM_PLANE_NO_SCALING, 185 .can_position = false, 186 .src_expected = { 0, 0, 512 << 16, 384 << 16 }, 187 .crtc_expected = { 0, 0, 1024, 768 }, 188 }, 189 { 190 .name = "downscaling", 191 .msg = "Should succeed with exact scaling limit", 192 .src = { 0, 0, 2048 << 16, 1536 << 16 }, 193 .crtc = { 0, 0, 1024, 768 }, 194 .rotation = DRM_MODE_ROTATE_0, 195 .min_scale = DRM_PLANE_NO_SCALING, 196 .max_scale = 0x20000, 197 .can_position = false, 198 .src_expected = { 0, 0, 2048 << 16, 1536 << 16 }, 199 .crtc_expected = { 0, 0, 1024, 768 }, 200 }, 201 { 202 .name = "rounding1", 203 .msg = "Should succeed by clipping to exact multiple", 204 .src = { 0, 0, 0x40001, 0x40001 }, 205 .crtc = { 1022, 766, 4, 4 }, 206 .rotation = DRM_MODE_ROTATE_0, 207 .min_scale = DRM_PLANE_NO_SCALING, 208 .max_scale = 0x10001, 209 .can_position = true, 210 .src_expected = { 0, 0, 2 << 16, 2 << 16 }, 211 .crtc_expected = { 1022, 766, 2, 2 }, 212 }, 213 { 214 .name = "rounding2", 215 .msg = "Should succeed by clipping to exact multiple", 216 .src = { 0x20001, 0x20001, 0x4040001, 0x3040001 }, 217 .crtc = { -2, -2, 1028, 772 }, 218 .rotation = DRM_MODE_ROTATE_0, 219 .min_scale = DRM_PLANE_NO_SCALING, 220 .max_scale = 0x10001, 221 .can_position = false, 222 .src_expected = { 0x40002, 0x40002, 1024 << 16, 768 << 16 }, 223 .crtc_expected = { 0, 0, 1024, 768 }, 224 }, 225 { 226 .name = "rounding3", 227 .msg = "Should succeed by clipping to exact multiple", 228 .src = { 0, 0, 0x3ffff, 0x3ffff }, 229 .crtc = { 1022, 766, 4, 4 }, 230 .rotation = DRM_MODE_ROTATE_0, 231 .min_scale = 0xffff, 232 .max_scale = DRM_PLANE_NO_SCALING, 233 .can_position = true, 234 /* Should not be rounded to 0x20001, which would be upscaling. */ 235 .src_expected = { 0, 0, 2 << 16, 2 << 16 }, 236 .crtc_expected = { 1022, 766, 2, 2 }, 237 }, 238 { 239 .name = "rounding4", 240 .msg = "Should succeed by clipping to exact multiple", 241 .src = { 0x1ffff, 0x1ffff, 0x403ffff, 0x303ffff }, 242 .crtc = { -2, -2, 1028, 772 }, 243 .rotation = DRM_MODE_ROTATE_0, 244 .min_scale = 0xffff, 245 .max_scale = DRM_PLANE_NO_SCALING, 246 .can_position = false, 247 .src_expected = { 0x3fffe, 0x3fffe, 1024 << 16, 768 << 16 }, 248 .crtc_expected = { 0, 0, 1024, 768 }, 249 }, 250 }; 251 252 KUNIT_ARRAY_PARAM(drm_check_plane_state, drm_check_plane_state_tests, drm_check_plane_state_desc); 253 254 static void drm_test_check_invalid_plane_state(struct kunit *test) 255 { 256 const struct drm_check_plane_state_test *params = test->param_value; 257 struct drm_plane_state *plane_state = test->priv; 258 259 KUNIT_ASSERT_LT_MSG(test, 260 drm_atomic_helper_check_plane_state(plane_state, &crtc_state, 261 params->min_scale, 262 params->max_scale, 263 params->can_position, false), 264 0, params->msg); 265 } 266 267 static const struct drm_check_plane_state_test drm_check_invalid_plane_state_tests[] = { 268 { 269 .name = "positioning_invalid", 270 .msg = "Should not be able to position on the crtc with can_position=false", 271 .src = { 0, 0, 1023 << 16, 767 << 16 }, 272 .crtc = { 0, 0, 1023, 767 }, 273 .rotation = DRM_MODE_ROTATE_0, 274 .min_scale = DRM_PLANE_NO_SCALING, 275 .max_scale = DRM_PLANE_NO_SCALING, 276 .can_position = false, 277 }, 278 { 279 .name = "upscaling_invalid", 280 .msg = "Upscaling out of range should fail", 281 .src = { 0, 0, 512 << 16, 384 << 16 }, 282 .crtc = { 0, 0, 1024, 768 }, 283 .rotation = DRM_MODE_ROTATE_0, 284 .min_scale = 0x8001, 285 .max_scale = DRM_PLANE_NO_SCALING, 286 .can_position = false, 287 }, 288 { 289 .name = "downscaling_invalid", 290 .msg = "Downscaling out of range should fail", 291 .src = { 0, 0, 2048 << 16, 1536 << 16 }, 292 .crtc = { 0, 0, 1024, 768 }, 293 .rotation = DRM_MODE_ROTATE_0, 294 .min_scale = DRM_PLANE_NO_SCALING, 295 .max_scale = 0x1ffff, 296 .can_position = false, 297 }, 298 }; 299 300 KUNIT_ARRAY_PARAM(drm_check_invalid_plane_state, drm_check_invalid_plane_state_tests, 301 drm_check_plane_state_desc); 302 303 static struct kunit_case drm_plane_helper_test[] = { 304 KUNIT_CASE_PARAM(drm_test_check_plane_state, drm_check_plane_state_gen_params), 305 KUNIT_CASE_PARAM(drm_test_check_invalid_plane_state, 306 drm_check_invalid_plane_state_gen_params), 307 {} 308 }; 309 310 static struct kunit_suite drm_plane_helper_test_suite = { 311 .name = "drm_plane_helper", 312 .init = drm_plane_helper_init, 313 .test_cases = drm_plane_helper_test, 314 }; 315 316 kunit_test_suite(drm_plane_helper_test_suite); 317 318 MODULE_LICENSE("GPL"); 319