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