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