Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.19-rc5 400 lines 11 kB view raw
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_atomic_uapi.h> 6#include <drm/drm_drv.h> 7#include <drm/drm_edid.h> 8#include <drm/drm_fourcc.h> 9#include <drm/drm_kunit_helpers.h> 10#include <drm/drm_managed.h> 11 12#include <kunit/device.h> 13#include <kunit/resource.h> 14 15#include <linux/device.h> 16#include <linux/export.h> 17#include <linux/platform_device.h> 18 19#define KUNIT_DEVICE_NAME "drm-kunit-mock-device" 20 21static const struct drm_mode_config_funcs drm_mode_config_funcs = { 22 .atomic_check = drm_atomic_helper_check, 23 .atomic_commit = drm_atomic_helper_commit, 24}; 25 26/** 27 * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test 28 * @test: The test context object 29 * 30 * This allocates a fake struct &device to create a mock for a KUnit 31 * test. The device will also be bound to a fake driver. It will thus be 32 * able to leverage the usual infrastructure and most notably the 33 * device-managed resources just like a "real" device. 34 * 35 * Resources will be cleaned up automatically, but the removal can be 36 * forced using @drm_kunit_helper_free_device. 37 * 38 * Returns: 39 * A pointer to the new device, or an ERR_PTR() otherwise. 40 */ 41struct device *drm_kunit_helper_alloc_device(struct kunit *test) 42{ 43 return kunit_device_register(test, KUNIT_DEVICE_NAME); 44} 45EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); 46 47/** 48 * drm_kunit_helper_free_device - Frees a mock device 49 * @test: The test context object 50 * @dev: The device to free 51 * 52 * Frees a device allocated with drm_kunit_helper_alloc_device(). 53 */ 54void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) 55{ 56 kunit_device_unregister(test, dev); 57} 58EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); 59 60struct drm_device * 61__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, 62 struct device *dev, 63 size_t size, size_t offset, 64 const struct drm_driver *driver) 65{ 66 struct drm_device *drm; 67 void *container; 68 int ret; 69 70 container = __devm_drm_dev_alloc(dev, driver, size, offset); 71 if (IS_ERR(container)) 72 return ERR_CAST(container); 73 74 drm = container + offset; 75 drm->mode_config.funcs = &drm_mode_config_funcs; 76 77 ret = drmm_mode_config_init(drm); 78 if (ret) 79 return ERR_PTR(ret); 80 81 return drm; 82} 83EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); 84 85static void kunit_action_drm_atomic_state_put(void *ptr) 86{ 87 struct drm_atomic_state *state = ptr; 88 89 drm_atomic_state_put(state); 90} 91 92/** 93 * drm_kunit_helper_atomic_state_alloc - Allocates an atomic state 94 * @test: The test context object 95 * @drm: The device to alloc the state for 96 * @ctx: Locking context for that atomic update 97 * 98 * Allocates a empty atomic state. 99 * 100 * The state is tied to the kunit test context, so we must not call 101 * drm_atomic_state_put() on it, it will be done so automatically. 102 * 103 * Returns: 104 * An ERR_PTR on error, a pointer to the newly allocated state otherwise 105 */ 106struct drm_atomic_state * 107drm_kunit_helper_atomic_state_alloc(struct kunit *test, 108 struct drm_device *drm, 109 struct drm_modeset_acquire_ctx *ctx) 110{ 111 struct drm_atomic_state *state; 112 int ret; 113 114 state = drm_atomic_state_alloc(drm); 115 if (!state) 116 return ERR_PTR(-ENOMEM); 117 118 ret = kunit_add_action_or_reset(test, 119 kunit_action_drm_atomic_state_put, 120 state); 121 if (ret) 122 return ERR_PTR(ret); 123 124 state->acquire_ctx = ctx; 125 126 return state; 127} 128EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); 129 130static const uint32_t default_plane_formats[] = { 131 DRM_FORMAT_XRGB8888, 132}; 133 134static const uint64_t default_plane_modifiers[] = { 135 DRM_FORMAT_MOD_LINEAR, 136 DRM_FORMAT_MOD_INVALID 137}; 138 139static const struct drm_plane_helper_funcs default_plane_helper_funcs = { 140}; 141 142static const struct drm_plane_funcs default_plane_funcs = { 143 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 144 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 145 .reset = drm_atomic_helper_plane_reset, 146}; 147 148/** 149 * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test 150 * @test: The test context object 151 * @drm: The device to alloc the plane for 152 * @funcs: Callbacks for the new plane. Optional. 153 * @helper_funcs: Helpers callbacks for the new plane. Optional. 154 * @formats: array of supported formats (DRM_FORMAT\_\*). Optional. 155 * @num_formats: number of elements in @formats 156 * @modifiers: array of struct drm_format modifiers terminated by 157 * DRM_FORMAT_MOD_INVALID. Optional. 158 * 159 * This allocates and initializes a mock struct &drm_plane meant to be 160 * part of a mock device for a KUnit test. 161 * 162 * Resources will be cleaned up automatically. 163 * 164 * @funcs will default to the default helpers implementations. 165 * @helper_funcs will default to an empty implementation. @formats will 166 * default to XRGB8888 only. @modifiers will default to a linear 167 * modifier only. 168 * 169 * Returns: 170 * A pointer to the new plane, or an ERR_PTR() otherwise. 171 */ 172struct drm_plane * 173drm_kunit_helper_create_primary_plane(struct kunit *test, 174 struct drm_device *drm, 175 const struct drm_plane_funcs *funcs, 176 const struct drm_plane_helper_funcs *helper_funcs, 177 const uint32_t *formats, 178 unsigned int num_formats, 179 const uint64_t *modifiers) 180{ 181 struct drm_plane *plane; 182 183 if (!funcs) 184 funcs = &default_plane_funcs; 185 186 if (!helper_funcs) 187 helper_funcs = &default_plane_helper_funcs; 188 189 if (!formats || !num_formats) { 190 formats = default_plane_formats; 191 num_formats = ARRAY_SIZE(default_plane_formats); 192 } 193 194 if (!modifiers) 195 modifiers = default_plane_modifiers; 196 197 plane = __drmm_universal_plane_alloc(drm, 198 sizeof(struct drm_plane), 0, 199 0, 200 funcs, 201 formats, 202 num_formats, 203 default_plane_modifiers, 204 DRM_PLANE_TYPE_PRIMARY, 205 NULL); 206 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); 207 208 drm_plane_helper_add(plane, helper_funcs); 209 210 return plane; 211} 212EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane); 213 214static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = { 215}; 216 217static const struct drm_crtc_funcs default_crtc_funcs = { 218 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 219 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 220 .reset = drm_atomic_helper_crtc_reset, 221}; 222 223/** 224 * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test 225 * @test: The test context object 226 * @drm: The device to alloc the plane for 227 * @primary: Primary plane for CRTC 228 * @cursor: Cursor plane for CRTC. Optional. 229 * @funcs: Callbacks for the new plane. Optional. 230 * @helper_funcs: Helpers callbacks for the new plane. Optional. 231 * 232 * This allocates and initializes a mock struct &drm_crtc meant to be 233 * part of a mock device for a KUnit test. 234 * 235 * Resources will be cleaned up automatically. 236 * 237 * @funcs will default to the default helpers implementations. 238 * @helper_funcs will default to an empty implementation. 239 * 240 * Returns: 241 * A pointer to the new CRTC, or an ERR_PTR() otherwise. 242 */ 243struct drm_crtc * 244drm_kunit_helper_create_crtc(struct kunit *test, 245 struct drm_device *drm, 246 struct drm_plane *primary, 247 struct drm_plane *cursor, 248 const struct drm_crtc_funcs *funcs, 249 const struct drm_crtc_helper_funcs *helper_funcs) 250{ 251 struct drm_crtc *crtc; 252 int ret; 253 254 if (!funcs) 255 funcs = &default_crtc_funcs; 256 257 if (!helper_funcs) 258 helper_funcs = &default_crtc_helper_funcs; 259 260 crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL); 261 KUNIT_ASSERT_NOT_NULL(test, crtc); 262 263 ret = drmm_crtc_init_with_planes(drm, crtc, 264 primary, 265 cursor, 266 funcs, 267 NULL); 268 KUNIT_ASSERT_EQ(test, ret, 0); 269 270 drm_crtc_helper_add(crtc, helper_funcs); 271 272 return crtc; 273} 274EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc); 275 276/** 277 * drm_kunit_helper_enable_crtc_connector - Enables a CRTC -> Connector output 278 * @test: The test context object 279 * @drm: The device to alloc the plane for 280 * @crtc: The CRTC to enable 281 * @connector: The Connector to enable 282 * @mode: The display mode to configure the CRTC with 283 * @ctx: Locking context 284 * 285 * This function creates an atomic update to enable the route from @crtc 286 * to @connector, with the given @mode. 287 * 288 * Returns: 289 * 290 * A pointer to the new CRTC, or an ERR_PTR() otherwise. If the error 291 * returned is EDEADLK, the entire atomic sequence must be restarted. 292 */ 293int drm_kunit_helper_enable_crtc_connector(struct kunit *test, 294 struct drm_device *drm, 295 struct drm_crtc *crtc, 296 struct drm_connector *connector, 297 const struct drm_display_mode *mode, 298 struct drm_modeset_acquire_ctx *ctx) 299{ 300 struct drm_atomic_state *state; 301 struct drm_connector_state *conn_state; 302 struct drm_crtc_state *crtc_state; 303 int ret; 304 305 state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); 306 if (IS_ERR(state)) 307 return PTR_ERR(state); 308 309 conn_state = drm_atomic_get_connector_state(state, connector); 310 if (IS_ERR(conn_state)) 311 return PTR_ERR(conn_state); 312 313 ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); 314 if (ret) 315 return ret; 316 317 crtc_state = drm_atomic_get_crtc_state(state, crtc); 318 if (IS_ERR(crtc_state)) 319 return PTR_ERR(crtc_state); 320 321 ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); 322 if (ret) 323 return ret; 324 325 crtc_state->enable = true; 326 crtc_state->active = true; 327 328 ret = drm_atomic_commit(state); 329 if (ret) 330 return ret; 331 332 return 0; 333} 334EXPORT_SYMBOL_GPL(drm_kunit_helper_enable_crtc_connector); 335 336static void kunit_action_drm_mode_destroy(void *ptr) 337{ 338 struct drm_display_mode *mode = ptr; 339 340 drm_mode_destroy(NULL, mode); 341} 342 343/** 344 * drm_kunit_add_mode_destroy_action() - Add a drm_destroy_mode kunit action 345 * @test: The test context object 346 * @mode: The drm_display_mode to destroy eventually 347 * 348 * Registers a kunit action that will destroy the drm_display_mode at 349 * the end of the test. 350 * 351 * If an error occurs, the drm_display_mode will be destroyed. 352 * 353 * Returns: 354 * 0 on success, an error code otherwise. 355 */ 356int drm_kunit_add_mode_destroy_action(struct kunit *test, 357 struct drm_display_mode *mode) 358{ 359 return kunit_add_action_or_reset(test, 360 kunit_action_drm_mode_destroy, 361 mode); 362} 363EXPORT_SYMBOL_GPL(drm_kunit_add_mode_destroy_action); 364 365/** 366 * drm_kunit_display_mode_from_cea_vic() - return a mode for CEA VIC for a KUnit test 367 * @test: The test context object 368 * @dev: DRM device 369 * @video_code: CEA VIC of the mode 370 * 371 * Creates a new mode matching the specified CEA VIC for a KUnit test. 372 * 373 * Resources will be cleaned up automatically. 374 * 375 * Returns: A new drm_display_mode on success or NULL on failure 376 */ 377struct drm_display_mode * 378drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev, 379 u8 video_code) 380{ 381 struct drm_display_mode *mode; 382 int ret; 383 384 mode = drm_display_mode_from_cea_vic(dev, video_code); 385 if (!mode) 386 return NULL; 387 388 ret = kunit_add_action_or_reset(test, 389 kunit_action_drm_mode_destroy, 390 mode); 391 if (ret) 392 return NULL; 393 394 return mode; 395} 396EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic); 397 398MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); 399MODULE_DESCRIPTION("KUnit test suite helper functions"); 400MODULE_LICENSE("GPL");