File Index
Files and directories
Sign in
FILE

drm_bridge_test.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/tests/drm_bridge_test.c C
  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 */
 21struct drm_bridge_priv {
 22	unsigned int enable_count;
 23	unsigned int disable_count;
 24	struct drm_bridge bridge;
 25	void *data;
 26};
 27
 28struct 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
 40static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge)
 41{
 42	return container_of(bridge, struct drm_bridge_priv, bridge);
 43}
 44
 45static 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
 53static 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
 60static 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
 67static 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
 73static 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
 81static 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
 89static 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
 98KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper,
 99			    drm_bridge_remove,
100			    struct drm_bridge *);
101
102static 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
112static struct drm_bridge_init_priv *
113drm_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 */
187static 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
207retry_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
225retry_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 */
245static 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
267static 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
274static 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 */
283static 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
299retry_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
320retry_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 */
339static 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
359retry_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 */
378static 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
394retry_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
415retry_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
430static 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
437static 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
442static 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 */
469static 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 */
483static 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
499static 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
505static 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
511kunit_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
517MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
518MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
519
520MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
521MODULE_LICENSE("GPL");
~/ Read-only file preview.