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 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); 160 161 ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); 162 KUNIT_EXPECT_EQ(test, ret, 0); 163 } 164 165 crtc_state = drm_atomic_get_crtc_state(state, crtc); 166 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); 167 168 ret = drm_atomic_set_mode_for_crtc(crtc_state, &drm_atomic_test_mode); 169 KUNIT_EXPECT_EQ(test, ret, 0); 170 171 crtc_state->enable = true; 172 crtc_state->active = true; 173 174 if (connector) { 175 ret = drm_atomic_commit(state); 176 KUNIT_ASSERT_EQ(test, ret, 0); 177 } else { 178 // dummy connector mask 179 crtc_state->connector_mask = DRM_TEST_CONN_0; 180 } 181 182 return 0; 183 } 184 185 /* 186 * Test that the DRM encoder mode_set() is called when the atomic state 187 * connectors are changed but the CRTC mode is not. 188 */ 189 static void drm_test_check_connector_changed_modeset(struct kunit *test) 190 { 191 struct drm_atomic_test_priv *priv; 192 struct drm_modeset_acquire_ctx ctx; 193 struct drm_connector *old_conn, *new_conn; 194 struct drm_atomic_state *state; 195 struct drm_device *drm; 196 struct drm_connector_state *new_conn_state, *old_conn_state; 197 int ret, initial_modeset_count; 198 199 priv = drm_atomic_test_init_drm_components(test, true); 200 KUNIT_ASSERT_NOT_NULL(test, priv); 201 202 drm = &priv->drm; 203 old_conn = &priv->connectors[0]; 204 new_conn = &priv->connectors[1]; 205 206 drm_modeset_acquire_init(&ctx, 0); 207 208 // first modeset to enable 209 ret = set_up_atomic_state(test, priv, old_conn, &ctx); 210 KUNIT_ASSERT_EQ(test, ret, 0); 211 212 state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); 213 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 214 215 new_conn_state = drm_atomic_get_connector_state(state, new_conn); 216 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); 217 218 old_conn_state = drm_atomic_get_connector_state(state, old_conn); 219 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); 220 221 ret = drm_atomic_set_crtc_for_connector(old_conn_state, NULL); 222 KUNIT_EXPECT_EQ(test, ret, 0); 223 224 ret = drm_atomic_set_crtc_for_connector(new_conn_state, priv->crtc); 225 KUNIT_EXPECT_EQ(test, ret, 0); 226 227 initial_modeset_count = modeset_counter; 228 229 // modeset_disables is called as part of the atomic commit tail 230 ret = drm_atomic_commit(state); 231 KUNIT_ASSERT_EQ(test, ret, 0); 232 KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1); 233 234 drm_modeset_drop_locks(&ctx); 235 drm_modeset_acquire_fini(&ctx); 236 } 237 238 /* 239 * Test that the drm_crtc_in_clone_mode() helper can detect if a given CRTC 240 * state is in clone mode 241 */ 242 static void drm_test_check_in_clone_mode(struct kunit *test) 243 { 244 bool ret; 245 const struct drm_clone_mode_test *param = test->param_value; 246 struct drm_crtc_state *crtc_state; 247 248 crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL); 249 KUNIT_ASSERT_NOT_NULL(test, crtc_state); 250 251 crtc_state->encoder_mask = param->encoder_mask; 252 253 ret = drm_crtc_in_clone_mode(crtc_state); 254 255 KUNIT_ASSERT_EQ(test, ret, param->expected_result); 256 } 257 258 /* 259 * Test that the atomic commit path will succeed for valid clones (or non-cloned 260 * states) and fail for states where the cloned encoders are not possible_clones 261 * of each other. 262 */ 263 static void drm_test_check_valid_clones(struct kunit *test) 264 { 265 int ret; 266 const struct drm_clone_mode_test *param = test->param_value; 267 struct drm_atomic_test_priv *priv; 268 struct drm_modeset_acquire_ctx ctx; 269 struct drm_device *drm; 270 struct drm_atomic_state *state; 271 struct drm_crtc_state *crtc_state; 272 273 priv = drm_atomic_test_init_drm_components(test, false); 274 KUNIT_ASSERT_NOT_NULL(test, priv); 275 276 drm = &priv->drm; 277 278 drm_modeset_acquire_init(&ctx, 0); 279 280 ret = set_up_atomic_state(test, priv, NULL, &ctx); 281 KUNIT_ASSERT_EQ(test, ret, 0); 282 283 state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); 284 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 285 286 crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); 287 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); 288 289 crtc_state->encoder_mask = param->encoder_mask; 290 291 // force modeset 292 crtc_state->mode_changed = true; 293 294 ret = drm_atomic_helper_check_modeset(drm, state); 295 KUNIT_ASSERT_EQ(test, ret, param->expected_result); 296 297 drm_modeset_drop_locks(&ctx); 298 drm_modeset_acquire_fini(&ctx); 299 } 300 301 static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t, 302 char *desc) 303 { 304 sprintf(desc, "%s", t->name); 305 } 306 307 static void drm_check_valid_clones_desc(const struct drm_clone_mode_test *t, 308 char *desc) 309 { 310 sprintf(desc, "%s", t->name); 311 } 312 313 static const struct drm_clone_mode_test drm_clone_mode_tests[] = { 314 { 315 .name = "in_clone_mode", 316 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1, 317 .expected_result = true, 318 }, 319 { 320 .name = "not_in_clone_mode", 321 .encoder_mask = DRM_TEST_ENC_0, 322 .expected_result = false, 323 }, 324 }; 325 326 static const struct drm_clone_mode_test drm_valid_clone_mode_tests[] = { 327 { 328 .name = "not_in_clone_mode", 329 .encoder_mask = DRM_TEST_ENC_0, 330 .expected_result = 0, 331 }, 332 333 { 334 .name = "valid_clone", 335 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1, 336 .expected_result = 0, 337 }, 338 { 339 .name = "invalid_clone", 340 .encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_2, 341 .expected_result = -EINVAL, 342 }, 343 }; 344 345 KUNIT_ARRAY_PARAM(drm_check_in_clone_mode, drm_clone_mode_tests, 346 drm_check_in_clone_mode_desc); 347 348 KUNIT_ARRAY_PARAM(drm_check_valid_clones, drm_valid_clone_mode_tests, 349 drm_check_valid_clones_desc); 350 351 static struct kunit_case drm_test_check_modeset_test[] = { 352 KUNIT_CASE(drm_test_check_connector_changed_modeset), 353 {} 354 }; 355 356 static struct kunit_case drm_in_clone_mode_check_test[] = { 357 KUNIT_CASE_PARAM(drm_test_check_in_clone_mode, 358 drm_check_in_clone_mode_gen_params), 359 KUNIT_CASE_PARAM(drm_test_check_valid_clones, 360 drm_check_valid_clones_gen_params), 361 {} 362 }; 363 364 static struct kunit_suite drm_test_check_modeset_test_suite = { 365 .name = "drm_validate_modeset", 366 .test_cases = drm_test_check_modeset_test, 367 }; 368 369 static struct kunit_suite drm_in_clone_mode_check_test_suite = { 370 .name = "drm_validate_clone_mode", 371 .test_cases = drm_in_clone_mode_check_test, 372 }; 373 374 kunit_test_suites(&drm_in_clone_mode_check_test_suite, 375 &drm_test_check_modeset_test_suite); 376 377 MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com"); 378 MODULE_DESCRIPTION("Test cases for the drm_atomic_helper functions"); 379 MODULE_LICENSE("GPL"); 380