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_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 19 static 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 test 26 * @test: The test context object 27 * 28 * This allocates a fake struct &device to create a mock for a KUnit 29 * test. The device will also be bound to a fake driver. It will thus be 30 * able to leverage the usual infrastructure and most notably the 31 * device-managed resources just like a "real" device. 32 * 33 * Resources will be cleaned up automatically, but the removal can be 34 * forced using @drm_kunit_helper_free_device. 35 * 36 * Returns: 37 * A pointer to the new device, or an ERR_PTR() otherwise. 38 */ 39 struct device *drm_kunit_helper_alloc_device(struct kunit *test) 40 { 41 return kunit_device_register(test, KUNIT_DEVICE_NAME); 42 } 43 EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); 44 45 /** 46 * drm_kunit_helper_free_device - Frees a mock device 47 * @test: The test context object 48 * @dev: The device to free 49 * 50 * Frees a device allocated with drm_kunit_helper_alloc_device(). 51 */ 52 void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) 53 { 54 kunit_device_unregister(test, dev); 55 } 56 EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); 57 58 struct 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 } 81 EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); 82 83 static 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 context 93 * @test: The test context object 94 * 95 * Allocates and initializes a modeset acquire context. 96 * 97 * The context is tied to the kunit test context, so we must not call 98 * 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 otherwise 102 */ 103 struct drm_modeset_acquire_ctx * 104 drm_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 } 122 EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc); 123 124 static 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 state 133 * @test: The test context object 134 * @drm: The device to alloc the state for 135 * @ctx: Locking context for that atomic update 136 * 137 * Allocates a empty atomic state. 138 * 139 * The state is tied to the kunit test context, so we must not call 140 * 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 otherwise 144 */ 145 struct drm_atomic_state * 146 drm_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 } 167 EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); 168 169 static const uint32_t default_plane_formats[] = { 170 DRM_FORMAT_XRGB8888, 171 }; 172 173 static const uint64_t default_plane_modifiers[] = { 174 DRM_FORMAT_MOD_LINEAR, 175 DRM_FORMAT_MOD_INVALID 176 }; 177 178 static const struct drm_plane_helper_funcs default_plane_helper_funcs = { 179 }; 180 181 static 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 test 189 * @test: The test context object 190 * @drm: The device to alloc the plane for 191 * @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 @formats 195 * @modifiers: array of struct drm_format modifiers terminated by 196 * DRM_FORMAT_MOD_INVALID. Optional. 197 * 198 * This allocates and initializes a mock struct &drm_plane meant to be 199 * 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 will 205 * default to XRGB8888 only. @modifiers will default to a linear 206 * modifier only. 207 * 208 * Returns: 209 * A pointer to the new plane, or an ERR_PTR() otherwise. 210 */ 211 struct drm_plane * 212 drm_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 } 251 EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane); 252 253 static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = { 254 }; 255 256 static 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 test 264 * @test: The test context object 265 * @drm: The device to alloc the plane for 266 * @primary: Primary plane for CRTC 267 * @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 be 272 * 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 */ 282 struct drm_crtc * 283 drm_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 } 313 EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc); 314 315 static 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 VIC 324 for a KUnit test 325 * @test: The test context object 326 * @dev: DRM device 327 * @video_code: CEA VIC of the mode 328 * 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 failure 334 */ 335 struct drm_display_mode * 336 drm_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 } 354 EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic); 355 356 MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); 357 MODULE_DESCRIPTION("KUnit test suite helper functions"); 358 MODULE_LICENSE("GPL"); 359