// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #define KUNIT_DEVICE_NAME "drm-kunit-mock-device" static const struct drm_mode_config_funcs drm_mode_config_funcs = { }; static int fake_probe(struct platform_device *pdev) { return 0; } static struct platform_driver fake_platform_driver = { .probe = fake_probe, .driver = { .name = KUNIT_DEVICE_NAME, }, }; static void kunit_action_platform_driver_unregister(void *ptr) { struct platform_driver *drv = ptr; platform_driver_unregister(drv); } static void kunit_action_platform_device_put(void *ptr) { struct platform_device *pdev = ptr; platform_device_put(pdev); } static void kunit_action_platform_device_del(void *ptr) { struct platform_device *pdev = ptr; platform_device_del(pdev); } /** * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test * @test: The test context object * * This allocates a fake struct &device to create a mock for a KUnit * test. The device will also be bound to a fake driver. It will thus be * able to leverage the usual infrastructure and most notably the * device-managed resources just like a "real" device. * * Resources will be cleaned up automatically, but the removal can be * forced using @drm_kunit_helper_free_device. * * Returns: * A pointer to the new device, or an ERR_PTR() otherwise. */ struct device *drm_kunit_helper_alloc_device(struct kunit *test) { struct platform_device *pdev; int ret; ret = platform_driver_register(&fake_platform_driver); KUNIT_ASSERT_EQ(test, ret, 0); ret = kunit_add_action_or_reset(test, kunit_action_platform_driver_unregister, &fake_platform_driver); KUNIT_ASSERT_EQ(test, ret, 0); pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); ret = kunit_add_action_or_reset(test, kunit_action_platform_device_put, pdev); KUNIT_ASSERT_EQ(test, ret, 0); ret = platform_device_add(pdev); KUNIT_ASSERT_EQ(test, ret, 0); ret = kunit_add_action_or_reset(test, kunit_action_platform_device_del, pdev); KUNIT_ASSERT_EQ(test, ret, 0); return &pdev->dev; } EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); /** * drm_kunit_helper_free_device - Frees a mock device * @test: The test context object * @dev: The device to free * * Frees a device allocated with drm_kunit_helper_alloc_device(). */ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) { struct platform_device *pdev = to_platform_device(dev); kunit_release_action(test, kunit_action_platform_device_del, pdev); kunit_release_action(test, kunit_action_platform_device_put, pdev); kunit_release_action(test, kunit_action_platform_driver_unregister, &fake_platform_driver); } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); struct drm_device * __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, struct device *dev, size_t size, size_t offset, const struct drm_driver *driver) { struct drm_device *drm; void *container; int ret; container = __devm_drm_dev_alloc(dev, driver, size, offset); if (IS_ERR(container)) return ERR_CAST(container); drm = container + offset; drm->mode_config.funcs = &drm_mode_config_funcs; ret = drmm_mode_config_init(drm); if (ret) return ERR_PTR(ret); return drm; } EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); static void action_drm_release_context(void *ptr) { struct drm_modeset_acquire_ctx *ctx = ptr; drm_modeset_drop_locks(ctx); drm_modeset_acquire_fini(ctx); } /** * drm_kunit_helper_acquire_ctx_alloc - Allocates an acquire context * @test: The test context object * * Allocates and initializes a modeset acquire context. * * The context is tied to the kunit test context, so we must not call * drm_modeset_acquire_fini() on it, it will be done so automatically. * * Returns: * An ERR_PTR on error, a pointer to the newly allocated context otherwise */ struct drm_modeset_acquire_ctx * drm_kunit_helper_acquire_ctx_alloc(struct kunit *test) { struct drm_modeset_acquire_ctx *ctx; int ret; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx); drm_modeset_acquire_init(ctx, 0); ret = kunit_add_action_or_reset(test, action_drm_release_context, ctx); if (ret) return ERR_PTR(ret); return ctx; } EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc); static void kunit_action_drm_atomic_state_put(void *ptr) { struct drm_atomic_state *state = ptr; drm_atomic_state_put(state); } /** * drm_kunit_helper_atomic_state_alloc - Allocates an atomic state * @test: The test context object * @drm: The device to alloc the state for * @ctx: Locking context for that atomic update * * Allocates a empty atomic state. * * The state is tied to the kunit test context, so we must not call * drm_atomic_state_put() on it, it will be done so automatically. * * Returns: * An ERR_PTR on error, a pointer to the newly allocated state otherwise */ struct drm_atomic_state * drm_kunit_helper_atomic_state_alloc(struct kunit *test, struct drm_device *drm, struct drm_modeset_acquire_ctx *ctx) { struct drm_atomic_state *state; int ret; state = drm_atomic_state_alloc(drm); if (!state) return ERR_PTR(-ENOMEM); ret = kunit_add_action_or_reset(test, kunit_action_drm_atomic_state_put, state); if (ret) return ERR_PTR(ret); state->acquire_ctx = ctx; return state; } EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); MODULE_AUTHOR("Maxime Ripard "); MODULE_LICENSE("GPL");