1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Kunit test for drm_bridge functions
4 */
5 #include <drm/drm_atomic_state_helper.h>
6 #include <drm/drm_bridge.h>
7 #include <drm/drm_bridge_connector.h>
8 #include <drm/drm_bridge_helper.h>
9 #include <drm/drm_kunit_helpers.h>
10
11 #include <kunit/device.h>
12 #include <kunit/test.h>
13
14 /*
15 * Mimick the typical "private" struct defined by a bridge driver, which
16 * embeds a bridge plus other fields.
17 *
18 * Having at least one member before @bridge ensures we test non-zero
19 * @bridge offset.
20 */
21 struct drm_bridge_priv {
22 unsigned int enable_count;
23 unsigned int disable_count;
24 struct drm_bridge bridge;
25 void *data;
26 };
27
28 struct drm_bridge_init_priv {
29 struct drm_device drm;
30 /** @dev: device, only for tests not needing a whole drm_device */
31 struct device *dev;
32 struct drm_plane *plane;
33 struct drm_crtc *crtc;
34 struct drm_encoder encoder;
35 struct drm_bridge_priv *test_bridge;
36 struct drm_connector *connector;
37 bool destroyed;
38 };
39
bridge_to_priv(struct drm_bridge * bridge)40 static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge)
41 {
42 return container_of(bridge, struct drm_bridge_priv, bridge);
43 }
44
drm_test_bridge_priv_destroy(struct drm_bridge * bridge)45 static void drm_test_bridge_priv_destroy(struct drm_bridge *bridge)
46 {
47 struct drm_bridge_priv *bridge_priv = bridge_to_priv(bridge);
48 struct drm_bridge_init_priv *priv = (struct drm_bridge_init_priv *)bridge_priv->data;
49
50 priv->destroyed = true;
51 }
52
drm_test_bridge_enable(struct drm_bridge * bridge)53 static void drm_test_bridge_enable(struct drm_bridge *bridge)
54 {
55 struct drm_bridge_priv *priv = bridge_to_priv(bridge);
56
57 priv->enable_count++;
58 }
59
drm_test_bridge_disable(struct drm_bridge * bridge)60 static void drm_test_bridge_disable(struct drm_bridge *bridge)
61 {
62 struct drm_bridge_priv *priv = bridge_to_priv(bridge);
63
64 priv->disable_count++;
65 }
66
67 static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = {
68 .destroy = drm_test_bridge_priv_destroy,
69 .enable = drm_test_bridge_enable,
70 .disable = drm_test_bridge_disable,
71 };
72
drm_test_bridge_atomic_enable(struct drm_bridge * bridge,struct drm_atomic_state * state)73 static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge,
74 struct drm_atomic_state *state)
75 {
76 struct drm_bridge_priv *priv = bridge_to_priv(bridge);
77
78 priv->enable_count++;
79 }
80
drm_test_bridge_atomic_disable(struct drm_bridge * bridge,struct drm_atomic_state * state)81 static void drm_test_bridge_atomic_disable(struct drm_bridge *bridge,
82 struct drm_atomic_state *state)
83 {
84 struct drm_bridge_priv *priv = bridge_to_priv(bridge);
85
86 priv->disable_count++;
87 }
88
89 static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = {
90 .destroy = drm_test_bridge_priv_destroy,
91 .atomic_enable = drm_test_bridge_atomic_enable,
92 .atomic_disable = drm_test_bridge_atomic_disable,
93 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
94 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
95 .atomic_reset = drm_atomic_helper_bridge_reset,
96 };
97
98 KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper,
99 drm_bridge_remove,
100 struct drm_bridge *);
101
drm_kunit_bridge_add(struct kunit * test,struct drm_bridge * bridge)102 static int drm_kunit_bridge_add(struct kunit *test,
103 struct drm_bridge *bridge)
104 {
105 drm_bridge_add(bridge);
106
107 return kunit_add_action_or_reset(test,
108 drm_bridge_remove_wrapper,
109 bridge);
110 }
111
112 static struct drm_bridge_init_priv *
drm_test_bridge_init(struct kunit * test,const struct drm_bridge_funcs * funcs)113 drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs)
114 {
115 struct drm_bridge_init_priv *priv;
116 struct drm_encoder *enc;
117 struct drm_bridge *bridge;
118 struct drm_device *drm;
119 struct device *dev;
120 int ret;
121
122 dev = drm_kunit_helper_alloc_device(test);
123 if (IS_ERR(dev))
124 return ERR_CAST(dev);
125
126 priv = drm_kunit_helper_alloc_drm_device(test, dev,
127 struct drm_bridge_init_priv, drm,
128 DRIVER_MODESET | DRIVER_ATOMIC);
129 if (IS_ERR(priv))
130 return ERR_CAST(priv);
131
132 priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs);
133 if (IS_ERR(priv->test_bridge))
134 return ERR_CAST(priv->test_bridge);
135
136 priv->test_bridge->data = priv;
137
138 drm = &priv->drm;
139 priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
140 NULL,
141 NULL,
142 NULL, 0,
143 NULL);
144 if (IS_ERR(priv->plane))
145 return ERR_CAST(priv->plane);
146
147 priv->crtc = drm_kunit_helper_create_crtc(test, drm,
148 priv->plane, NULL,
149 NULL,
150 NULL);
151 if (IS_ERR(priv->crtc))
152 return ERR_CAST(priv->crtc);
153
154 enc = &priv->encoder;
155 ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
156 if (ret)
157 return ERR_PTR(ret);
158
159 enc->possible_crtcs = drm_crtc_mask(priv->crtc);
160
161 bridge = &priv->test_bridge->bridge;
162 bridge->type = DRM_MODE_CONNECTOR_VIRTUAL;
163
164 ret = drm_kunit_bridge_add(test, bridge);
165 if (ret)
166 return ERR_PTR(ret);
167
168 ret = drm_bridge_attach(enc, bridge, NULL, 0);
169 if (ret)
170 return ERR_PTR(ret);
171
172 priv->connector = drm_bridge_connector_init(drm, enc);
173 if (IS_ERR(priv->connector))
174 return ERR_CAST(priv->connector);
175
176 drm_connector_attach_encoder(priv->connector, enc);
177
178 drm_mode_config_reset(drm);
179
180 return priv;
181 }
182
183 /*
184 * Test that drm_bridge_get_current_state() returns the last committed
185 * state for an atomic bridge.
186 */
drm_test_drm_bridge_get_current_state_atomic(struct kunit * test)187 static void drm_test_drm_bridge_get_current_state_atomic(struct kunit *test)
188 {
189 struct drm_modeset_acquire_ctx ctx;
190 struct drm_bridge_init_priv *priv;
191 struct drm_bridge_state *curr_bridge_state;
192 struct drm_bridge_state *bridge_state;
193 struct drm_atomic_state *state;
194 struct drm_bridge *bridge;
195 struct drm_device *drm;
196 int ret;
197
198 priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
199 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
200
201 drm_modeset_acquire_init(&ctx, 0);
202
203 drm = &priv->drm;
204 state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
205 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
206
207 retry_commit:
208 bridge = &priv->test_bridge->bridge;
209 bridge_state = drm_atomic_get_bridge_state(state, bridge);
210 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state);
211
212 ret = drm_atomic_commit(state);
213 if (ret == -EDEADLK) {
214 drm_atomic_state_clear(state);
215 drm_modeset_backoff(&ctx);
216 goto retry_commit;
217 }
218 KUNIT_ASSERT_EQ(test, ret, 0);
219
220 drm_modeset_drop_locks(&ctx);
221 drm_modeset_acquire_fini(&ctx);
222
223 drm_modeset_acquire_init(&ctx, 0);
224
225 retry_state:
226 ret = drm_modeset_lock(&bridge->base.lock, &ctx);
227 if (ret == -EDEADLK) {
228 drm_modeset_backoff(&ctx);
229 goto retry_state;
230 }
231
232 curr_bridge_state = drm_bridge_get_current_state(bridge);
233 KUNIT_EXPECT_PTR_EQ(test, curr_bridge_state, bridge_state);
234
235 drm_modeset_unlock(&bridge->base.lock);
236
237 drm_modeset_drop_locks(&ctx);
238 drm_modeset_acquire_fini(&ctx);
239 }
240
241 /*
242 * Test that drm_bridge_get_current_state() returns NULL for a
243 * non-atomic bridge.
244 */
drm_test_drm_bridge_get_current_state_legacy(struct kunit * test)245 static void drm_test_drm_bridge_get_current_state_legacy(struct kunit *test)
246 {
247 struct drm_bridge_init_priv *priv;
248 struct drm_bridge *bridge;
249
250 priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
251 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
252
253 /*
254 * NOTE: Strictly speaking, we should take the bridge->base.lock
255 * before calling that function. However, bridge->base is only
256 * initialized if the bridge is atomic, while we explicitly
257 * initialize one that isn't there.
258 *
259 * In order to avoid unnecessary warnings, let's skip the
260 * locking. The function would return NULL in all cases anyway,
261 * so we don't really have any concurrency to worry about.
262 */
263 bridge = &priv->test_bridge->bridge;
264 KUNIT_EXPECT_NULL(test, drm_bridge_get_current_state(bridge));
265 }
266
267 static struct kunit_case drm_bridge_get_current_state_tests[] = {
268 KUNIT_CASE(drm_test_drm_bridge_get_current_state_atomic),
269 KUNIT_CASE(drm_test_drm_bridge_get_current_state_legacy),
270 { }
271 };
272
273
274 static struct kunit_suite drm_bridge_get_current_state_test_suite = {
275 .name = "drm_test_bridge_get_current_state",
276 .test_cases = drm_bridge_get_current_state_tests,
277 };
278
279 /*
280 * Test that an atomic bridge is properly power-cycled when calling
281 * drm_bridge_helper_reset_crtc().
282 */
drm_test_drm_bridge_helper_reset_crtc_atomic(struct kunit * test)283 static void drm_test_drm_bridge_helper_reset_crtc_atomic(struct kunit *test)
284 {
285 struct drm_modeset_acquire_ctx ctx;
286 struct drm_bridge_init_priv *priv;
287 struct drm_display_mode *mode;
288 struct drm_bridge_priv *bridge_priv;
289 int ret;
290
291 priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
292 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
293
294 mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
295 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
296
297 drm_modeset_acquire_init(&ctx, 0);
298
299 retry_commit:
300 ret = drm_kunit_helper_enable_crtc_connector(test,
301 &priv->drm, priv->crtc,
302 priv->connector,
303 mode,
304 &ctx);
305 if (ret == -EDEADLK) {
306 drm_modeset_backoff(&ctx);
307 goto retry_commit;
308 }
309 KUNIT_ASSERT_EQ(test, ret, 0);
310
311 drm_modeset_drop_locks(&ctx);
312 drm_modeset_acquire_fini(&ctx);
313
314 bridge_priv = priv->test_bridge;
315 KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
316 KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
317
318 drm_modeset_acquire_init(&ctx, 0);
319
320 retry_reset:
321 ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
322 if (ret == -EDEADLK) {
323 drm_modeset_backoff(&ctx);
324 goto retry_reset;
325 }
326 KUNIT_ASSERT_EQ(test, ret, 0);
327
328 drm_modeset_drop_locks(&ctx);
329 drm_modeset_acquire_fini(&ctx);
330
331 KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
332 KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
333 }
334
335 /*
336 * Test that calling drm_bridge_helper_reset_crtc() on a disabled atomic
337 * bridge will fail and not call the enable / disable callbacks
338 */
drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit * test)339 static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit *test)
340 {
341 struct drm_modeset_acquire_ctx ctx;
342 struct drm_bridge_init_priv *priv;
343 struct drm_display_mode *mode;
344 struct drm_bridge_priv *bridge_priv;
345 int ret;
346
347 priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
348 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
349
350 mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
351 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
352
353 bridge_priv = priv->test_bridge;
354 KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 0);
355 KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
356
357 drm_modeset_acquire_init(&ctx, 0);
358
359 retry_reset:
360 ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
361 if (ret == -EDEADLK) {
362 drm_modeset_backoff(&ctx);
363 goto retry_reset;
364 }
365 KUNIT_EXPECT_LT(test, ret, 0);
366
367 drm_modeset_drop_locks(&ctx);
368 drm_modeset_acquire_fini(&ctx);
369
370 KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 0);
371 KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 0);
372 }
373
374 /*
375 * Test that a non-atomic bridge is properly power-cycled when calling
376 * drm_bridge_helper_reset_crtc().
377 */
drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit * test)378 static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test)
379 {
380 struct drm_modeset_acquire_ctx ctx;
381 struct drm_bridge_init_priv *priv;
382 struct drm_display_mode *mode;
383 struct drm_bridge_priv *bridge_priv;
384 int ret;
385
386 priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
387 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
388
389 mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
390 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
391
392 drm_modeset_acquire_init(&ctx, 0);
393
394 retry_commit:
395 ret = drm_kunit_helper_enable_crtc_connector(test,
396 &priv->drm, priv->crtc,
397 priv->connector,
398 mode,
399 &ctx);
400 if (ret == -EDEADLK) {
401 drm_modeset_backoff(&ctx);
402 goto retry_commit;
403 }
404 KUNIT_ASSERT_EQ(test, ret, 0);
405
406 drm_modeset_drop_locks(&ctx);
407 drm_modeset_acquire_fini(&ctx);
408
409 bridge_priv = priv->test_bridge;
410 KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
411 KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
412
413 drm_modeset_acquire_init(&ctx, 0);
414
415 retry_reset:
416 ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
417 if (ret == -EDEADLK) {
418 drm_modeset_backoff(&ctx);
419 goto retry_reset;
420 }
421 KUNIT_ASSERT_EQ(test, ret, 0);
422
423 drm_modeset_drop_locks(&ctx);
424 drm_modeset_acquire_fini(&ctx);
425
426 KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
427 KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
428 }
429
430 static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
431 KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
432 KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
433 KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
434 { }
435 };
436
437 static struct kunit_suite drm_bridge_helper_reset_crtc_test_suite = {
438 .name = "drm_test_bridge_helper_reset_crtc",
439 .test_cases = drm_bridge_helper_reset_crtc_tests,
440 };
441
drm_test_bridge_alloc_init(struct kunit * test)442 static int drm_test_bridge_alloc_init(struct kunit *test)
443 {
444 struct drm_bridge_init_priv *priv;
445
446 priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
447 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
448
449 priv->dev = kunit_device_register(test, "drm-bridge-dev");
450 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
451
452 test->priv = priv;
453
454 priv->test_bridge = devm_drm_bridge_alloc(priv->dev, struct drm_bridge_priv, bridge,
455 &drm_test_bridge_atomic_funcs);
456 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->test_bridge);
457
458 priv->test_bridge->data = priv;
459
460 KUNIT_ASSERT_FALSE(test, priv->destroyed);
461
462 return 0;
463 }
464
465 /*
466 * Test that a bridge is freed when the device is destroyed in lack of
467 * other drm_bridge_get/put() operations.
468 */
drm_test_drm_bridge_alloc_basic(struct kunit * test)469 static void drm_test_drm_bridge_alloc_basic(struct kunit *test)
470 {
471 struct drm_bridge_init_priv *priv = test->priv;
472
473 KUNIT_ASSERT_FALSE(test, priv->destroyed);
474
475 kunit_device_unregister(test, priv->dev);
476 KUNIT_EXPECT_TRUE(test, priv->destroyed);
477 }
478
479 /*
480 * Test that a bridge is not freed when the device is destroyed when there
481 * is still a reference to it, and freed when that reference is put.
482 */
drm_test_drm_bridge_alloc_get_put(struct kunit * test)483 static void drm_test_drm_bridge_alloc_get_put(struct kunit *test)
484 {
485 struct drm_bridge_init_priv *priv = test->priv;
486
487 KUNIT_ASSERT_FALSE(test, priv->destroyed);
488
489 drm_bridge_get(&priv->test_bridge->bridge);
490 KUNIT_EXPECT_FALSE(test, priv->destroyed);
491
492 kunit_device_unregister(test, priv->dev);
493 KUNIT_EXPECT_FALSE(test, priv->destroyed);
494
495 drm_bridge_put(&priv->test_bridge->bridge);
496 KUNIT_EXPECT_TRUE(test, priv->destroyed);
497 }
498
499 static struct kunit_case drm_bridge_alloc_tests[] = {
500 KUNIT_CASE(drm_test_drm_bridge_alloc_basic),
501 KUNIT_CASE(drm_test_drm_bridge_alloc_get_put),
502 { }
503 };
504
505 static struct kunit_suite drm_bridge_alloc_test_suite = {
506 .name = "drm_bridge_alloc",
507 .init = drm_test_bridge_alloc_init,
508 .test_cases = drm_bridge_alloc_tests,
509 };
510
511 kunit_test_suites(
512 &drm_bridge_get_current_state_test_suite,
513 &drm_bridge_helper_reset_crtc_test_suite,
514 &drm_bridge_alloc_test_suite,
515 );
516
517 MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
518 MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
519
520 MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
521 MODULE_LICENSE("GPL");
522