| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Kunit test for drm_bridge functions
- */
- #include <drm/drm_atomic_state_helper.h>
- #include <drm/drm_bridge.h>
- #include <drm/drm_bridge_connector.h>
- #include <drm/drm_bridge_helper.h>
- #include <drm/drm_kunit_helpers.h>
- #include <kunit/device.h>
- #include <kunit/test.h>
- /*
- * Mimick the typical "private" struct defined by a bridge driver, which
- * embeds a bridge plus other fields.
- *
- * Having at least one member before @bridge ensures we test non-zero
- * @bridge offset.
- */
- struct drm_bridge_priv {
- unsigned int enable_count;
- unsigned int disable_count;
- struct drm_bridge bridge;
- void *data;
- };
- struct drm_bridge_init_priv {
- struct drm_device drm;
- /** @dev: device, only for tests not needing a whole drm_device */
- struct device *dev;
- struct drm_plane *plane;
- struct drm_crtc *crtc;
- struct drm_encoder encoder;
- struct drm_bridge_priv *test_bridge;
- struct drm_connector *connector;
- bool destroyed;
- };
- static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge)
- {
- return container_of(bridge, struct drm_bridge_priv, bridge);
- }
- static void drm_test_bridge_priv_destroy(struct drm_bridge *bridge)
- {
- struct drm_bridge_priv *bridge_priv = bridge_to_priv(bridge);
- struct drm_bridge_init_priv *priv = (struct drm_bridge_init_priv *)bridge_priv->data;
- priv->destroyed = true;
- }
- static void drm_test_bridge_enable(struct drm_bridge *bridge)
- {
- struct drm_bridge_priv *priv = bridge_to_priv(bridge);
- priv->enable_count++;
- }
- static void drm_test_bridge_disable(struct drm_bridge *bridge)
- {
- struct drm_bridge_priv *priv = bridge_to_priv(bridge);
- priv->disable_count++;
- }
- static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = {
- .destroy = drm_test_bridge_priv_destroy,
- .enable = drm_test_bridge_enable,
- .disable = drm_test_bridge_disable,
- };
- static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct drm_bridge_priv *priv = bridge_to_priv(bridge);
- priv->enable_count++;
- }
- static void drm_test_bridge_atomic_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *state)
- {
- struct drm_bridge_priv *priv = bridge_to_priv(bridge);
- priv->disable_count++;
- }
- static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = {
- .destroy = drm_test_bridge_priv_destroy,
- .atomic_enable = drm_test_bridge_atomic_enable,
- .atomic_disable = drm_test_bridge_atomic_disable,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- };
- KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper,
- drm_bridge_remove,
- struct drm_bridge *);
- static int drm_kunit_bridge_add(struct kunit *test,
- struct drm_bridge *bridge)
- {
- drm_bridge_add(bridge);
- return kunit_add_action_or_reset(test,
- drm_bridge_remove_wrapper,
- bridge);
- }
- static struct drm_bridge_init_priv *
- drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs)
- {
- struct drm_bridge_init_priv *priv;
- struct drm_encoder *enc;
- struct drm_bridge *bridge;
- struct drm_device *drm;
- struct device *dev;
- int ret;
- dev = drm_kunit_helper_alloc_device(test);
- if (IS_ERR(dev))
- return ERR_CAST(dev);
- priv = drm_kunit_helper_alloc_drm_device(test, dev,
- struct drm_bridge_init_priv, drm,
- DRIVER_MODESET | DRIVER_ATOMIC);
- if (IS_ERR(priv))
- return ERR_CAST(priv);
- priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs);
- if (IS_ERR(priv->test_bridge))
- return ERR_CAST(priv->test_bridge);
- priv->test_bridge->data = priv;
- drm = &priv->drm;
- priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
- NULL,
- NULL,
- NULL, 0,
- NULL);
- if (IS_ERR(priv->plane))
- return ERR_CAST(priv->plane);
- priv->crtc = drm_kunit_helper_create_crtc(test, drm,
- priv->plane, NULL,
- NULL,
- NULL);
- if (IS_ERR(priv->crtc))
- return ERR_CAST(priv->crtc);
- enc = &priv->encoder;
- ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
- if (ret)
- return ERR_PTR(ret);
- enc->possible_crtcs = drm_crtc_mask(priv->crtc);
- bridge = &priv->test_bridge->bridge;
- bridge->type = DRM_MODE_CONNECTOR_VIRTUAL;
- ret = drm_kunit_bridge_add(test, bridge);
- if (ret)
- return ERR_PTR(ret);
- ret = drm_bridge_attach(enc, bridge, NULL, 0);
- if (ret)
- return ERR_PTR(ret);
- priv->connector = drm_bridge_connector_init(drm, enc);
- if (IS_ERR(priv->connector))
- return ERR_CAST(priv->connector);
- drm_connector_attach_encoder(priv->connector, enc);
- drm_mode_config_reset(drm);
- return priv;
- }
- /*
- * Test that drm_bridge_get_current_state() returns the last committed
- * state for an atomic bridge.
- */
- static void drm_test_drm_bridge_get_current_state_atomic(struct kunit *test)
- {
- struct drm_modeset_acquire_ctx ctx;
- struct drm_bridge_init_priv *priv;
- struct drm_bridge_state *curr_bridge_state;
- struct drm_bridge_state *bridge_state;
- struct drm_atomic_state *state;
- struct drm_bridge *bridge;
- struct drm_device *drm;
- int ret;
- priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- drm_modeset_acquire_init(&ctx, 0);
- drm = &priv->drm;
- state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- retry_commit:
- bridge = &priv->test_bridge->bridge;
- bridge_state = drm_atomic_get_bridge_state(state, bridge);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state);
- ret = drm_atomic_commit(state);
- if (ret == -EDEADLK) {
- drm_atomic_state_clear(state);
- drm_modeset_backoff(&ctx);
- goto retry_commit;
- }
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- drm_modeset_acquire_init(&ctx, 0);
- retry_state:
- ret = drm_modeset_lock(&bridge->base.lock, &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_state;
- }
- curr_bridge_state = drm_bridge_get_current_state(bridge);
- KUNIT_EXPECT_PTR_EQ(test, curr_bridge_state, bridge_state);
- drm_modeset_unlock(&bridge->base.lock);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- }
- /*
- * Test that drm_bridge_get_current_state() returns NULL for a
- * non-atomic bridge.
- */
- static void drm_test_drm_bridge_get_current_state_legacy(struct kunit *test)
- {
- struct drm_bridge_init_priv *priv;
- struct drm_bridge *bridge;
- priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- /*
- * NOTE: Strictly speaking, we should take the bridge->base.lock
- * before calling that function. However, bridge->base is only
- * initialized if the bridge is atomic, while we explicitly
- * initialize one that isn't there.
- *
- * In order to avoid unnecessary warnings, let's skip the
- * locking. The function would return NULL in all cases anyway,
- * so we don't really have any concurrency to worry about.
- */
- bridge = &priv->test_bridge->bridge;
- KUNIT_EXPECT_NULL(test, drm_bridge_get_current_state(bridge));
- }
- static struct kunit_case drm_bridge_get_current_state_tests[] = {
- KUNIT_CASE(drm_test_drm_bridge_get_current_state_atomic),
- KUNIT_CASE(drm_test_drm_bridge_get_current_state_legacy),
- { }
- };
- static struct kunit_suite drm_bridge_get_current_state_test_suite = {
- .name = "drm_test_bridge_get_current_state",
- .test_cases = drm_bridge_get_current_state_tests,
- };
- /*
- * Test that an atomic bridge is properly power-cycled when calling
- * drm_bridge_helper_reset_crtc().
- */
- static void drm_test_drm_bridge_helper_reset_crtc_atomic(struct kunit *test)
- {
- struct drm_modeset_acquire_ctx ctx;
- struct drm_bridge_init_priv *priv;
- struct drm_display_mode *mode;
- struct drm_bridge_priv *bridge_priv;
- int ret;
- priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
- drm_modeset_acquire_init(&ctx, 0);
- retry_commit:
- ret = drm_kunit_helper_enable_crtc_connector(test,
- &priv->drm, priv->crtc,
- priv->connector,
- mode,
- &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_commit;
- }
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- bridge_priv = priv->test_bridge;
- KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
- KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
- drm_modeset_acquire_init(&ctx, 0);
- retry_reset:
- ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_reset;
- }
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
- KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
- }
- /*
- * Test that calling drm_bridge_helper_reset_crtc() on a disabled atomic
- * bridge will fail and not call the enable / disable callbacks
- */
- static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit *test)
- {
- struct drm_modeset_acquire_ctx ctx;
- struct drm_bridge_init_priv *priv;
- struct drm_display_mode *mode;
- struct drm_bridge_priv *bridge_priv;
- int ret;
- priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
- bridge_priv = priv->test_bridge;
- KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 0);
- KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
- drm_modeset_acquire_init(&ctx, 0);
- retry_reset:
- ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_reset;
- }
- KUNIT_EXPECT_LT(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 0);
- KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 0);
- }
- /*
- * Test that a non-atomic bridge is properly power-cycled when calling
- * drm_bridge_helper_reset_crtc().
- */
- static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test)
- {
- struct drm_modeset_acquire_ctx ctx;
- struct drm_bridge_init_priv *priv;
- struct drm_display_mode *mode;
- struct drm_bridge_priv *bridge_priv;
- int ret;
- priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
- drm_modeset_acquire_init(&ctx, 0);
- retry_commit:
- ret = drm_kunit_helper_enable_crtc_connector(test,
- &priv->drm, priv->crtc,
- priv->connector,
- mode,
- &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_commit;
- }
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- bridge_priv = priv->test_bridge;
- KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
- KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
- drm_modeset_acquire_init(&ctx, 0);
- retry_reset:
- ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(&ctx);
- goto retry_reset;
- }
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
- KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
- }
- static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
- KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
- KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
- KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
- { }
- };
- static struct kunit_suite drm_bridge_helper_reset_crtc_test_suite = {
- .name = "drm_test_bridge_helper_reset_crtc",
- .test_cases = drm_bridge_helper_reset_crtc_tests,
- };
- static int drm_test_bridge_alloc_init(struct kunit *test)
- {
- struct drm_bridge_init_priv *priv;
- priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
- priv->dev = kunit_device_register(test, "drm-bridge-dev");
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
- test->priv = priv;
- priv->test_bridge = devm_drm_bridge_alloc(priv->dev, struct drm_bridge_priv, bridge,
- &drm_test_bridge_atomic_funcs);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->test_bridge);
- priv->test_bridge->data = priv;
- KUNIT_ASSERT_FALSE(test, priv->destroyed);
- return 0;
- }
- /*
- * Test that a bridge is freed when the device is destroyed in lack of
- * other drm_bridge_get/put() operations.
- */
- static void drm_test_drm_bridge_alloc_basic(struct kunit *test)
- {
- struct drm_bridge_init_priv *priv = test->priv;
- KUNIT_ASSERT_FALSE(test, priv->destroyed);
- kunit_device_unregister(test, priv->dev);
- KUNIT_EXPECT_TRUE(test, priv->destroyed);
- }
- /*
- * Test that a bridge is not freed when the device is destroyed when there
- * is still a reference to it, and freed when that reference is put.
- */
- static void drm_test_drm_bridge_alloc_get_put(struct kunit *test)
- {
- struct drm_bridge_init_priv *priv = test->priv;
- KUNIT_ASSERT_FALSE(test, priv->destroyed);
- drm_bridge_get(&priv->test_bridge->bridge);
- KUNIT_EXPECT_FALSE(test, priv->destroyed);
- kunit_device_unregister(test, priv->dev);
- KUNIT_EXPECT_FALSE(test, priv->destroyed);
- drm_bridge_put(&priv->test_bridge->bridge);
- KUNIT_EXPECT_TRUE(test, priv->destroyed);
- }
- static struct kunit_case drm_bridge_alloc_tests[] = {
- KUNIT_CASE(drm_test_drm_bridge_alloc_basic),
- KUNIT_CASE(drm_test_drm_bridge_alloc_get_put),
- { }
- };
- static struct kunit_suite drm_bridge_alloc_test_suite = {
- .name = "drm_bridge_alloc",
- .init = drm_test_bridge_alloc_init,
- .test_cases = drm_bridge_alloc_tests,
- };
- kunit_test_suites(
- &drm_bridge_get_current_state_test_suite,
- &drm_bridge_helper_reset_crtc_test_suite,
- &drm_bridge_alloc_test_suite,
- );
- MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
- MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
- MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
- MODULE_LICENSE("GPL");
|