| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- // SPDX-License-Identifier: GPL-2.0
- #include <drm/drm_atomic.h>
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_drv.h>
- #include <drm/drm_edid.h>
- #include <drm/drm_fourcc.h>
- #include <drm/drm_kunit_helpers.h>
- #include <drm/drm_managed.h>
- #include <kunit/device.h>
- #include <kunit/resource.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
- static const struct drm_mode_config_funcs drm_mode_config_funcs = {
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
- };
- /**
- * 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)
- {
- return kunit_device_register(test, KUNIT_DEVICE_NAME);
- }
- 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)
- {
- kunit_device_unregister(test, dev);
- }
- 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);
- static const uint32_t default_plane_formats[] = {
- DRM_FORMAT_XRGB8888,
- };
- static const uint64_t default_plane_modifiers[] = {
- DRM_FORMAT_MOD_LINEAR,
- DRM_FORMAT_MOD_INVALID
- };
- static const struct drm_plane_helper_funcs default_plane_helper_funcs = {
- };
- static const struct drm_plane_funcs default_plane_funcs = {
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .reset = drm_atomic_helper_plane_reset,
- };
- /**
- * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test
- * @test: The test context object
- * @drm: The device to alloc the plane for
- * @funcs: Callbacks for the new plane. Optional.
- * @helper_funcs: Helpers callbacks for the new plane. Optional.
- * @formats: array of supported formats (DRM_FORMAT\_\*). Optional.
- * @num_formats: number of elements in @formats
- * @modifiers: array of struct drm_format modifiers terminated by
- * DRM_FORMAT_MOD_INVALID. Optional.
- *
- * This allocates and initializes a mock struct &drm_plane meant to be
- * part of a mock device for a KUnit test.
- *
- * Resources will be cleaned up automatically.
- *
- * @funcs will default to the default helpers implementations.
- * @helper_funcs will default to an empty implementation. @formats will
- * default to XRGB8888 only. @modifiers will default to a linear
- * modifier only.
- *
- * Returns:
- * A pointer to the new plane, or an ERR_PTR() otherwise.
- */
- struct drm_plane *
- drm_kunit_helper_create_primary_plane(struct kunit *test,
- struct drm_device *drm,
- const struct drm_plane_funcs *funcs,
- const struct drm_plane_helper_funcs *helper_funcs,
- const uint32_t *formats,
- unsigned int num_formats,
- const uint64_t *modifiers)
- {
- struct drm_plane *plane;
- if (!funcs)
- funcs = &default_plane_funcs;
- if (!helper_funcs)
- helper_funcs = &default_plane_helper_funcs;
- if (!formats || !num_formats) {
- formats = default_plane_formats;
- num_formats = ARRAY_SIZE(default_plane_formats);
- }
- if (!modifiers)
- modifiers = default_plane_modifiers;
- plane = __drmm_universal_plane_alloc(drm,
- sizeof(struct drm_plane), 0,
- 0,
- funcs,
- formats,
- num_formats,
- default_plane_modifiers,
- DRM_PLANE_TYPE_PRIMARY,
- NULL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
- drm_plane_helper_add(plane, helper_funcs);
- return plane;
- }
- EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
- static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
- };
- static const struct drm_crtc_funcs default_crtc_funcs = {
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .reset = drm_atomic_helper_crtc_reset,
- };
- /**
- * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test
- * @test: The test context object
- * @drm: The device to alloc the plane for
- * @primary: Primary plane for CRTC
- * @cursor: Cursor plane for CRTC. Optional.
- * @funcs: Callbacks for the new plane. Optional.
- * @helper_funcs: Helpers callbacks for the new plane. Optional.
- *
- * This allocates and initializes a mock struct &drm_crtc meant to be
- * part of a mock device for a KUnit test.
- *
- * Resources will be cleaned up automatically.
- *
- * @funcs will default to the default helpers implementations.
- * @helper_funcs will default to an empty implementation.
- *
- * Returns:
- * A pointer to the new CRTC, or an ERR_PTR() otherwise.
- */
- struct drm_crtc *
- drm_kunit_helper_create_crtc(struct kunit *test,
- struct drm_device *drm,
- struct drm_plane *primary,
- struct drm_plane *cursor,
- const struct drm_crtc_funcs *funcs,
- const struct drm_crtc_helper_funcs *helper_funcs)
- {
- struct drm_crtc *crtc;
- int ret;
- if (!funcs)
- funcs = &default_crtc_funcs;
- if (!helper_funcs)
- helper_funcs = &default_crtc_helper_funcs;
- crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, crtc);
- ret = drmm_crtc_init_with_planes(drm, crtc,
- primary,
- cursor,
- funcs,
- NULL);
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_crtc_helper_add(crtc, helper_funcs);
- return crtc;
- }
- EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);
- static void kunit_action_drm_mode_destroy(void *ptr)
- {
- struct drm_display_mode *mode = ptr;
- drm_mode_destroy(NULL, mode);
- }
- /**
- * drm_kunit_add_mode_destroy_action() - Add a drm_destroy_mode kunit action
- * @test: The test context object
- * @mode: The drm_display_mode to destroy eventually
- *
- * Registers a kunit action that will destroy the drm_display_mode at
- * the end of the test.
- *
- * If an error occurs, the drm_display_mode will be destroyed.
- *
- * Returns:
- * 0 on success, an error code otherwise.
- */
- int drm_kunit_add_mode_destroy_action(struct kunit *test,
- struct drm_display_mode *mode)
- {
- return kunit_add_action_or_reset(test,
- kunit_action_drm_mode_destroy,
- mode);
- }
- EXPORT_SYMBOL_GPL(drm_kunit_add_mode_destroy_action);
- /**
- * drm_kunit_display_mode_from_cea_vic() - return a mode for CEA VIC for a KUnit test
- * @test: The test context object
- * @dev: DRM device
- * @video_code: CEA VIC of the mode
- *
- * Creates a new mode matching the specified CEA VIC for a KUnit test.
- *
- * Resources will be cleaned up automatically.
- *
- * Returns: A new drm_display_mode on success or NULL on failure
- */
- struct drm_display_mode *
- drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev,
- u8 video_code)
- {
- struct drm_display_mode *mode;
- int ret;
- mode = drm_display_mode_from_cea_vic(dev, video_code);
- if (!mode)
- return NULL;
- ret = kunit_add_action_or_reset(test,
- kunit_action_drm_mode_destroy,
- mode);
- if (ret)
- return NULL;
- return mode;
- }
- EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic);
- MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
- MODULE_DESCRIPTION("KUnit test suite helper functions");
- MODULE_LICENSE("GPL");
|