359 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <drm/drm_atomic.h>4#include <drm/drm_atomic_helper.h>5#include <drm/drm_drv.h>6#include <drm/drm_edid.h>7#include <drm/drm_fourcc.h>8#include <drm/drm_kunit_helpers.h>9#include <drm/drm_managed.h>10 11#include <kunit/device.h>12#include <kunit/resource.h>13 14#include <linux/device.h>15#include <linux/platform_device.h>16 17#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"18 19static const struct drm_mode_config_funcs drm_mode_config_funcs = {20 .atomic_check = drm_atomic_helper_check,21 .atomic_commit = drm_atomic_helper_commit,22};23 24/**25 * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test26 * @test: The test context object27 *28 * This allocates a fake struct &device to create a mock for a KUnit29 * test. The device will also be bound to a fake driver. It will thus be30 * able to leverage the usual infrastructure and most notably the31 * device-managed resources just like a "real" device.32 *33 * Resources will be cleaned up automatically, but the removal can be34 * forced using @drm_kunit_helper_free_device.35 *36 * Returns:37 * A pointer to the new device, or an ERR_PTR() otherwise.38 */39struct device *drm_kunit_helper_alloc_device(struct kunit *test)40{41 return kunit_device_register(test, KUNIT_DEVICE_NAME);42}43EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);44 45/**46 * drm_kunit_helper_free_device - Frees a mock device47 * @test: The test context object48 * @dev: The device to free49 *50 * Frees a device allocated with drm_kunit_helper_alloc_device().51 */52void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)53{54 kunit_device_unregister(test, dev);55}56EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);57 58struct drm_device *59__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test,60 struct device *dev,61 size_t size, size_t offset,62 const struct drm_driver *driver)63{64 struct drm_device *drm;65 void *container;66 int ret;67 68 container = __devm_drm_dev_alloc(dev, driver, size, offset);69 if (IS_ERR(container))70 return ERR_CAST(container);71 72 drm = container + offset;73 drm->mode_config.funcs = &drm_mode_config_funcs;74 75 ret = drmm_mode_config_init(drm);76 if (ret)77 return ERR_PTR(ret);78 79 return drm;80}81EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);82 83static void action_drm_release_context(void *ptr)84{85 struct drm_modeset_acquire_ctx *ctx = ptr;86 87 drm_modeset_drop_locks(ctx);88 drm_modeset_acquire_fini(ctx);89}90 91/**92 * drm_kunit_helper_acquire_ctx_alloc - Allocates an acquire context93 * @test: The test context object94 *95 * Allocates and initializes a modeset acquire context.96 *97 * The context is tied to the kunit test context, so we must not call98 * drm_modeset_acquire_fini() on it, it will be done so automatically.99 *100 * Returns:101 * An ERR_PTR on error, a pointer to the newly allocated context otherwise102 */103struct drm_modeset_acquire_ctx *104drm_kunit_helper_acquire_ctx_alloc(struct kunit *test)105{106 struct drm_modeset_acquire_ctx *ctx;107 int ret;108 109 ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);110 KUNIT_ASSERT_NOT_NULL(test, ctx);111 112 drm_modeset_acquire_init(ctx, 0);113 114 ret = kunit_add_action_or_reset(test,115 action_drm_release_context,116 ctx);117 if (ret)118 return ERR_PTR(ret);119 120 return ctx;121}122EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc);123 124static void kunit_action_drm_atomic_state_put(void *ptr)125{126 struct drm_atomic_state *state = ptr;127 128 drm_atomic_state_put(state);129}130 131/**132 * drm_kunit_helper_atomic_state_alloc - Allocates an atomic state133 * @test: The test context object134 * @drm: The device to alloc the state for135 * @ctx: Locking context for that atomic update136 *137 * Allocates a empty atomic state.138 *139 * The state is tied to the kunit test context, so we must not call140 * drm_atomic_state_put() on it, it will be done so automatically.141 *142 * Returns:143 * An ERR_PTR on error, a pointer to the newly allocated state otherwise144 */145struct drm_atomic_state *146drm_kunit_helper_atomic_state_alloc(struct kunit *test,147 struct drm_device *drm,148 struct drm_modeset_acquire_ctx *ctx)149{150 struct drm_atomic_state *state;151 int ret;152 153 state = drm_atomic_state_alloc(drm);154 if (!state)155 return ERR_PTR(-ENOMEM);156 157 ret = kunit_add_action_or_reset(test,158 kunit_action_drm_atomic_state_put,159 state);160 if (ret)161 return ERR_PTR(ret);162 163 state->acquire_ctx = ctx;164 165 return state;166}167EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);168 169static const uint32_t default_plane_formats[] = {170 DRM_FORMAT_XRGB8888,171};172 173static const uint64_t default_plane_modifiers[] = {174 DRM_FORMAT_MOD_LINEAR,175 DRM_FORMAT_MOD_INVALID176};177 178static const struct drm_plane_helper_funcs default_plane_helper_funcs = {179};180 181static const struct drm_plane_funcs default_plane_funcs = {182 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,183 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,184 .reset = drm_atomic_helper_plane_reset,185};186 187/**188 * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test189 * @test: The test context object190 * @drm: The device to alloc the plane for191 * @funcs: Callbacks for the new plane. Optional.192 * @helper_funcs: Helpers callbacks for the new plane. Optional.193 * @formats: array of supported formats (DRM_FORMAT\_\*). Optional.194 * @num_formats: number of elements in @formats195 * @modifiers: array of struct drm_format modifiers terminated by196 * DRM_FORMAT_MOD_INVALID. Optional.197 *198 * This allocates and initializes a mock struct &drm_plane meant to be199 * part of a mock device for a KUnit test.200 *201 * Resources will be cleaned up automatically.202 *203 * @funcs will default to the default helpers implementations.204 * @helper_funcs will default to an empty implementation. @formats will205 * default to XRGB8888 only. @modifiers will default to a linear206 * modifier only.207 *208 * Returns:209 * A pointer to the new plane, or an ERR_PTR() otherwise.210 */211struct drm_plane *212drm_kunit_helper_create_primary_plane(struct kunit *test,213 struct drm_device *drm,214 const struct drm_plane_funcs *funcs,215 const struct drm_plane_helper_funcs *helper_funcs,216 const uint32_t *formats,217 unsigned int num_formats,218 const uint64_t *modifiers)219{220 struct drm_plane *plane;221 222 if (!funcs)223 funcs = &default_plane_funcs;224 225 if (!helper_funcs)226 helper_funcs = &default_plane_helper_funcs;227 228 if (!formats || !num_formats) {229 formats = default_plane_formats;230 num_formats = ARRAY_SIZE(default_plane_formats);231 }232 233 if (!modifiers)234 modifiers = default_plane_modifiers;235 236 plane = __drmm_universal_plane_alloc(drm,237 sizeof(struct drm_plane), 0,238 0,239 funcs,240 formats,241 num_formats,242 default_plane_modifiers,243 DRM_PLANE_TYPE_PRIMARY,244 NULL);245 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);246 247 drm_plane_helper_add(plane, helper_funcs);248 249 return plane;250}251EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);252 253static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {254};255 256static const struct drm_crtc_funcs default_crtc_funcs = {257 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,258 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,259 .reset = drm_atomic_helper_crtc_reset,260};261 262/**263 * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test264 * @test: The test context object265 * @drm: The device to alloc the plane for266 * @primary: Primary plane for CRTC267 * @cursor: Cursor plane for CRTC. Optional.268 * @funcs: Callbacks for the new plane. Optional.269 * @helper_funcs: Helpers callbacks for the new plane. Optional.270 *271 * This allocates and initializes a mock struct &drm_crtc meant to be272 * part of a mock device for a KUnit test.273 *274 * Resources will be cleaned up automatically.275 *276 * @funcs will default to the default helpers implementations.277 * @helper_funcs will default to an empty implementation.278 *279 * Returns:280 * A pointer to the new CRTC, or an ERR_PTR() otherwise.281 */282struct drm_crtc *283drm_kunit_helper_create_crtc(struct kunit *test,284 struct drm_device *drm,285 struct drm_plane *primary,286 struct drm_plane *cursor,287 const struct drm_crtc_funcs *funcs,288 const struct drm_crtc_helper_funcs *helper_funcs)289{290 struct drm_crtc *crtc;291 int ret;292 293 if (!funcs)294 funcs = &default_crtc_funcs;295 296 if (!helper_funcs)297 helper_funcs = &default_crtc_helper_funcs;298 299 crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL);300 KUNIT_ASSERT_NOT_NULL(test, crtc);301 302 ret = drmm_crtc_init_with_planes(drm, crtc,303 primary,304 cursor,305 funcs,306 NULL);307 KUNIT_ASSERT_EQ(test, ret, 0);308 309 drm_crtc_helper_add(crtc, helper_funcs);310 311 return crtc;312}313EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);314 315static void kunit_action_drm_mode_destroy(void *ptr)316{317 struct drm_display_mode *mode = ptr;318 319 drm_mode_destroy(NULL, mode);320}321 322/**323 * drm_kunit_display_mode_from_cea_vic() - return a mode for CEA VIC324 for a KUnit test325 * @test: The test context object326 * @dev: DRM device327 * @video_code: CEA VIC of the mode328 *329 * Creates a new mode matching the specified CEA VIC for a KUnit test.330 *331 * Resources will be cleaned up automatically.332 *333 * Returns: A new drm_display_mode on success or NULL on failure334 */335struct drm_display_mode *336drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev,337 u8 video_code)338{339 struct drm_display_mode *mode;340 int ret;341 342 mode = drm_display_mode_from_cea_vic(dev, video_code);343 if (!mode)344 return NULL;345 346 ret = kunit_add_action_or_reset(test,347 kunit_action_drm_mode_destroy,348 mode);349 if (ret)350 return NULL;351 352 return mode;353}354EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic);355 356MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");357MODULE_DESCRIPTION("KUnit test suite helper functions");358MODULE_LICENSE("GPL");359