1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test cases for the drm_atomic_state helpers 4 * 5 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. 6 */ 7 8 #include <drm/drm_atomic.h> 9 #include <drm/drm_atomic_helper.h> 10 #include <drm/drm_atomic_uapi.h> 11 #include <drm/drm_kunit_helpers.h> 12 #include <drm/drm_probe_helper.h> 13 14 #define DRM_TEST_ENC_0 BIT(0) 15 #define DRM_TEST_ENC_1 BIT(1) 16 #define DRM_TEST_ENC_2 BIT(2) 17 18 #define DRM_TEST_CONN_0 BIT(0) 19 20 struct drm_clone_mode_test { 21 const char *name; 22 u32 encoder_mask; 23 int expected_result; 24 }; 25 26 static const struct drm_display_mode drm_atomic_test_mode = { 27 DRM_MODE("1024x768", 0, 65000, 1024, 1048, 28 1184, 1344, 0, 768, 771, 777, 806, 0, 29 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) 30 }; 31 32 struct drm_atomic_test_priv { 33 struct drm_device drm; 34 struct drm_plane *plane; 35 struct drm_crtc *crtc; 36 struct drm_encoder encoders[3]; 37 struct drm_connector connectors[2]; 38 }; 39 40 static int modeset_counter; 41 42 static void drm_test_encoder_mode_set(struct drm_encoder *encoder, 43 struct drm_crtc_state *crtc_state, 44 struct drm_connector_state *conn_state) 45 { 46 modeset_counter++; 47 } 48 49 static const struct drm_encoder_helper_funcs drm_atomic_test_encoder_funcs = { 50 .atomic_mode_set = drm_test_encoder_mode_set, 51 }; 52 53 static const struct drm_connector_funcs dummy_connector_funcs = { 54 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 55 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 56 .reset = drm_atomic_helper_connector_reset, 57 }; 58 59 static int drm_atomic_test_dummy_get_modes(struct drm_connector *connector) 60 { 61 return drm_connector_helper_get_modes_fixed(connector, 62 &drm_atomic_test_mode); 63 } 64 65 static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = { 66 .get_modes = drm_atomic_test_dummy_get_modes, 67 }; 68 69 static struct drm_atomic_test_priv * 70 drm_atomic_test_init_drm_components(struct kunit *test, bool has_connectors) 71 { 72 struct drm_atomic_test_priv *priv; 73 struct drm_encoder *enc; 74 struct drm_connector *conn; 75 struct drm_device *drm; 76 struct device *dev; 77 int ret; 78 79 dev = drm_kunit_helper_alloc_device(test); 80 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); 81 82 priv = drm_kunit_helper_alloc_drm_device(test, dev, 83 struct drm_atomic_test_priv, 84 drm, 85 DRIVER_MODESET | DRIVER_ATOMIC); 86 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); 87 test->priv = priv; 88 89 drm = &priv->drm; 90 priv->plane = drm_kunit_helper_create_primary_plane(test, drm, 91 NULL, 92 NULL, 93 NULL, 0, 94 NULL); 95 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->plane); 96 97 priv->crtc = drm_kunit_helper_create_crtc(test, drm, 98 priv->plane, NULL, 99 NULL, 100 NULL); 101 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->crtc); 102 103 for (int i = 0; i < ARRAY_SIZE(priv->encoders); i++) { 104 enc = &priv->encoders[i]; 105 106 ret = drmm_encoder_init(drm, enc, NULL, 107 DRM_MODE_ENCODER_DSI, NULL); 108 KUNIT_ASSERT_EQ(test, ret, 0); 109 110 enc->possible_crtcs = drm_crtc_mask(priv->crtc); 111 } 112 113 priv->encoders[0].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1; 114 priv->encoders[1].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1; 115 priv->encoders[2].possible_clones = DRM_TEST_ENC_2; 116 117 if (!has_connectors) 118 goto done; 119 120 BUILD_BUG_ON(ARRAY_SIZE(priv->connectors) > ARRAY_SIZE(priv->encoders)); 121 122 for (int i = 0; i < ARRAY_SIZE(priv->connectors); i++) { 123 conn = &priv->connectors[i]; 124 125 ret = drmm_connector_init(drm, conn, &dummy_connector_funcs, 126 DRM_MODE_CONNECTOR_DSI, NULL); 127 KUNIT_ASSERT_EQ(test, ret, 0); 128 129 drm_connector_helper_add(conn, &dummy_connector_helper_funcs); 130 drm_encoder_helper_add(&priv->encoders[i], 131 &drm_atomic_test_encoder_funcs); 132 133 drm_connector_attach_encoder(conn, &priv->encoders[i]); 134 } 135 136 done: 137 drm_mode_config_reset(drm); 138 139 return priv; 140 } 141 142 static int set_up_atomic_state(struct kunit *test, 143 struct drm_atomic_test_priv *priv, 144 struct drm_connector *connector, 145 struct drm_modeset_acquire_ctx *ctx) 146 { 147 struct drm_device *drm = &priv->drm; 148 struct drm_crtc *crtc = priv->crtc; 149 struct drm_atomic_state *state; 150 struct drm_connector_state *conn_state; 151 struct drm_crtc_state *crtc_state; 152 int ret; 153 154 state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); 155 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 156 157 if (connector) { 158 conn_state = drm_atomic_get_connector_state(state, connector); 159 if (IS_ERR(conn_state)) 160 return PTR_ERR(conn_state); 161 162 ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); 163 if (ret) 164 return ret; 165 } 166 167 crtc_state = drm_atomic_get_crtc_state(state, crtc); 168 if (IS_ERR(crtc_state)) 169 return PTR_ERR(crtc_state); 170 171 ret = drm_atomic_set_mode_for_crtc(crtc_state, &drm_atomic_test_mode); 172 if (ret) 173 return ret; 174 175 crtc_state->enable = true; 176 crtc_state->active = true; 177 178 if (connector) { 179 ret = drm_atomic_commit(state); 180 if (ret) 181 return ret; 182 } else { 183 // dummy connector mask 184 crtc_state->connector_mask = DRM_TEST_CONN_0; 185 } 186 187 return 0; 188 } 189 190 /* 191 * Test that the DRM encoder mode_set() is called when the atomic state 192 * connectors are changed but the CRTC mode is not. 193 */ 194 static void drm_test_check_connector_changed_modeset(struct kunit *test) 195 { 196 struct drm_atomic_test_priv *priv; 197 struct drm_modeset_acquire_ctx ctx; 198 struct drm_connector *old_conn, *new_conn; 199 struct drm_atomic_state *state; 200 struct drm_device *drm; 201 struct drm_connector_state *new_conn_state, *old_conn_state; 202 int ret, initial_modeset_count; 203 204 priv = drm_atomic_test_init_drm_components(test, true); 205 KUNIT_ASSERT_NOT_NULL(test, priv); 206 207 drm = &priv->drm; 208 old_conn = &priv->connectors[0]; 209 new_conn = &priv->connectors[1]; 210 211 drm_modeset_acquire_init(&ctx, 0); 212 213 // first modeset to enable 214 retry_set_up: 215 ret = set_up_atomic_state(test, priv, old_conn, &ctx); 216 if (ret == -EDEADLK) { 217 ret = drm_modeset_backoff(&ctx); 218 if (!ret) 219 goto retry_set_up; 220 } 221 KUNIT_ASSERT_EQ(test, ret, 0); 222 223 state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); 224 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 225 226 new_conn_state = drm_atomic_get_connector_state(state, new_conn); 227 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); 228 229 old_conn_state = drm_atomic_get_connector_state(state, old_conn); 230 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); 231 232 ret = drm_atomic_set_crtc_for_connector(old_conn_state, NULL); 233 KUNIT_EXPECT_EQ(test, ret, 0); 234 235 ret = drm_atomic_set_crtc_for_connector(new_conn_state, priv->crtc); 236 KUNIT_EXPECT_EQ(test, ret, 0); 237 238 initial_modeset_count = modeset_counter; 239 240 // modeset_disables is called as part of the atomic commit tail 241 ret = drm_atomic_commit(state); 242 KUNIT_ASSERT_EQ(test, ret, 0); 243 KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1); 244 245 drm_modeset_drop_locks(&ctx); 246 drm_modeset_acquire_fini(&ctx); 247 } 248 249 /* 250 * Test that the drm_crtc_in_clone_mode() helper can detect if a given CRTC 251 * state is in clone mode 252 */ 253 static void drm_test_check_in_clone_mode(struct kunit *test) 254 { 255 bool ret; 256 const struct drm_clone_mode_test *param = test->param_value; 257 struct drm_crtc_state *crtc_state; 258 259 crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL); 260 KUNIT_ASSERT_NOT_NULL(test, crtc_state); 261 262 crtc_state->encoder_mask = param->encoder_mask; 263 264 ret = drm_crtc_in_clone_mode(crtc_state); 265 266 KUNIT_ASSERT_EQ(test, ret, param->expected_result); 267 } 268 269 /* 270 * Test that the atomic commit path will succeed for valid clones (or non-cloned 271 * states) and fail for states where the cloned encoders are not possible_clones 272 * of each other. 273 */ 274 static void drm_test_check_valid_clones(struct kunit *test) 275 { 276 int ret; 277 const struct drm_clone_mode_test *param = test->param_value; 278 struct drm_atomic_test_priv *priv; 279 struct drm_modeset_acquire_ctx ctx; 280 struct drm_device *drm; 281 struct drm_atomic_state *state; 282 struct drm_crtc_state *crtc_state; 283 284 priv = drm_atomic_test_init_drm_components(test, false); 285 KUNIT_ASSERT_NOT_NULL(test, priv); 286 287 drm = &priv->drm; 288 289 drm_modeset_acquire_init(&ctx, 0); 290 291 retry_set_up: 292 ret = set_up_atomic_state(test, priv, NULL, &ctx); 293 if (ret == -EDEADLK) { 294 ret = drm_modeset_backoff(&ctx); 295 if (!ret) 296 goto retry_set_up; 297 } 298 KUNIT_ASSERT_EQ(test, ret, 0); 299 300 state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); 301 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 302 303 retry: 304 crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); 305 if (PTR_ERR(crtc_state) == -EDEADLK) { 306 drm_atomic_state_clear(state); 307 ret = drm_modeset_backoff(&ctx); 308 if (!ret) 309 goto retry; 310 } 311 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); 312 313 crtc_state->encoder_mask = param->encoder_mask; 314 315 // force modeset 316 crtc_state->mode_changed = true; 317 318 ret = drm_atomic_helper_check_modeset(drm, state); 319 if (ret == -EDEADLK) { 320 drm_atomic_state_clear(state); 321 ret = drm_modeset_backoff(&ctx); 322 if (!ret) 323 goto retry; 324 } 325 KUNIT_ASSERT_EQ(test, ret, param->expected_result); 326 327 drm_modeset_drop_locks(&ctx); 328 drm_modeset_acquire_fini(&ctx); 329 } 330 331 static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t, 332 char *desc) 333 { 334 sprintf(desc, "%s", t->name); 335 } 336 337 static void drm_check_valid_clones_desc(const struct drm_clone_mode_test *t, 338 char *desc) 339 { 340 sprintf(desc, "%s", t->name); 341 } 342 343 static const struct drm_clone_mode_test drm_clone_mode_tests[] = { 344 { 345 .name = "in_clone_mode", 346 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1, 347 .expected_result = true, 348 }, 349 { 350 .name = "not_in_clone_mode", 351 .encoder_mask = DRM_TEST_ENC_0, 352 .expected_result = false, 353 }, 354 }; 355 356 static const struct drm_clone_mode_test drm_valid_clone_mode_tests[] = { 357 { 358 .name = "not_in_clone_mode", 359 .encoder_mask = DRM_TEST_ENC_0, 360 .expected_result = 0, 361 }, 362 363 { 364 .name = "valid_clone", 365 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1, 366 .expected_result = 0, 367 }, 368 { 369 .name = "invalid_clone", 370 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_2, 371 .expected_result = -EINVAL, 372 }, 373 }; 374 375 KUNIT_ARRAY_PARAM(drm_check_in_clone_mode, drm_clone_mode_tests, 376 drm_check_in_clone_mode_desc); 377 378 KUNIT_ARRAY_PARAM(drm_check_valid_clones, drm_valid_clone_mode_tests, 379 drm_check_valid_clones_desc); 380 381 static struct kunit_case drm_test_check_modeset_test[] = { 382 KUNIT_CASE(drm_test_check_connector_changed_modeset), 383 {} 384 }; 385 386 static struct kunit_case drm_in_clone_mode_check_test[] = { 387 KUNIT_CASE_PARAM(drm_test_check_in_clone_mode, 388 drm_check_in_clone_mode_gen_params), 389 KUNIT_CASE_PARAM(drm_test_check_valid_clones, 390 drm_check_valid_clones_gen_params), 391 {} 392 }; 393 394 static struct kunit_suite drm_test_check_modeset_test_suite = { 395 .name = "drm_validate_modeset", 396 .test_cases = drm_test_check_modeset_test, 397 }; 398 399 static struct kunit_suite drm_in_clone_mode_check_test_suite = { 400 .name = "drm_validate_clone_mode", 401 .test_cases = drm_in_clone_mode_check_test, 402 }; 403 404 kunit_test_suites(&drm_in_clone_mode_check_test_suite, 405 &drm_test_check_modeset_test_suite); 406 407 MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com"); 408 MODULE_DESCRIPTION("Test cases for the drm_atomic_helper functions"); 409 MODULE_LICENSE("GPL"); 410