1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <drm/drm_atomic.h> 4 #include <drm/drm_drv.h> 5 #include <drm/drm_kunit_helpers.h> 6 #include <drm/drm_managed.h> 7 8 #include <kunit/resource.h> 9 10 #include <linux/device.h> 11 #include <linux/platform_device.h> 12 13 #define KUNIT_DEVICE_NAME "drm-kunit-mock-device" 14 15 static const struct drm_mode_config_funcs drm_mode_config_funcs = { 16 }; 17 18 static int fake_probe(struct platform_device *pdev) 19 { 20 return 0; 21 } 22 23 static struct platform_driver fake_platform_driver = { 24 .probe = fake_probe, 25 .driver = { 26 .name = KUNIT_DEVICE_NAME, 27 }, 28 }; 29 30 static void kunit_action_platform_driver_unregister(void *ptr) 31 { 32 struct platform_driver *drv = ptr; 33 34 platform_driver_unregister(drv); 35 36 } 37 38 static void kunit_action_platform_device_put(void *ptr) 39 { 40 struct platform_device *pdev = ptr; 41 42 platform_device_put(pdev); 43 } 44 45 static void kunit_action_platform_device_del(void *ptr) 46 { 47 struct platform_device *pdev = ptr; 48 49 platform_device_del(pdev); 50 } 51 52 /** 53 * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test 54 * @test: The test context object 55 * 56 * This allocates a fake struct &device to create a mock for a KUnit 57 * test. The device will also be bound to a fake driver. It will thus be 58 * able to leverage the usual infrastructure and most notably the 59 * device-managed resources just like a "real" device. 60 * 61 * Resources will be cleaned up automatically, but the removal can be 62 * forced using @drm_kunit_helper_free_device. 63 * 64 * Returns: 65 * A pointer to the new device, or an ERR_PTR() otherwise. 66 */ 67 struct device *drm_kunit_helper_alloc_device(struct kunit *test) 68 { 69 struct platform_device *pdev; 70 int ret; 71 72 ret = platform_driver_register(&fake_platform_driver); 73 KUNIT_ASSERT_EQ(test, ret, 0); 74 75 ret = kunit_add_action_or_reset(test, 76 kunit_action_platform_driver_unregister, 77 &fake_platform_driver); 78 KUNIT_ASSERT_EQ(test, ret, 0); 79 80 pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); 81 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); 82 83 ret = kunit_add_action_or_reset(test, 84 kunit_action_platform_device_put, 85 pdev); 86 KUNIT_ASSERT_EQ(test, ret, 0); 87 88 ret = platform_device_add(pdev); 89 KUNIT_ASSERT_EQ(test, ret, 0); 90 91 ret = kunit_add_action_or_reset(test, 92 kunit_action_platform_device_del, 93 pdev); 94 KUNIT_ASSERT_EQ(test, ret, 0); 95 96 return &pdev->dev; 97 } 98 EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); 99 100 /** 101 * drm_kunit_helper_free_device - Frees a mock device 102 * @test: The test context object 103 * @dev: The device to free 104 * 105 * Frees a device allocated with drm_kunit_helper_alloc_device(). 106 */ 107 void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) 108 { 109 struct platform_device *pdev = to_platform_device(dev); 110 111 kunit_release_action(test, 112 kunit_action_platform_device_del, 113 pdev); 114 115 kunit_release_action(test, 116 kunit_action_platform_device_put, 117 pdev); 118 119 kunit_release_action(test, 120 kunit_action_platform_driver_unregister, 121 &fake_platform_driver); 122 } 123 EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); 124 125 struct drm_device * 126 __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, 127 struct device *dev, 128 size_t size, size_t offset, 129 const struct drm_driver *driver) 130 { 131 struct drm_device *drm; 132 void *container; 133 int ret; 134 135 container = __devm_drm_dev_alloc(dev, driver, size, offset); 136 if (IS_ERR(container)) 137 return ERR_CAST(container); 138 139 drm = container + offset; 140 drm->mode_config.funcs = &drm_mode_config_funcs; 141 142 ret = drmm_mode_config_init(drm); 143 if (ret) 144 return ERR_PTR(ret); 145 146 return drm; 147 } 148 EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); 149 150 static void action_drm_release_context(void *ptr) 151 { 152 struct drm_modeset_acquire_ctx *ctx = ptr; 153 154 drm_modeset_drop_locks(ctx); 155 drm_modeset_acquire_fini(ctx); 156 } 157 158 /** 159 * drm_kunit_helper_acquire_ctx_alloc - Allocates an acquire context 160 * @test: The test context object 161 * 162 * Allocates and initializes a modeset acquire context. 163 * 164 * The context is tied to the kunit test context, so we must not call 165 * drm_modeset_acquire_fini() on it, it will be done so automatically. 166 * 167 * Returns: 168 * An ERR_PTR on error, a pointer to the newly allocated context otherwise 169 */ 170 struct drm_modeset_acquire_ctx * 171 drm_kunit_helper_acquire_ctx_alloc(struct kunit *test) 172 { 173 struct drm_modeset_acquire_ctx *ctx; 174 int ret; 175 176 ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); 177 KUNIT_ASSERT_NOT_NULL(test, ctx); 178 179 drm_modeset_acquire_init(ctx, 0); 180 181 ret = kunit_add_action_or_reset(test, 182 action_drm_release_context, 183 ctx); 184 if (ret) 185 return ERR_PTR(ret); 186 187 return ctx; 188 } 189 EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc); 190 191 static void kunit_action_drm_atomic_state_put(void *ptr) 192 { 193 struct drm_atomic_state *state = ptr; 194 195 drm_atomic_state_put(state); 196 } 197 198 /** 199 * drm_kunit_helper_atomic_state_alloc - Allocates an atomic state 200 * @test: The test context object 201 * @drm: The device to alloc the state for 202 * @ctx: Locking context for that atomic update 203 * 204 * Allocates a empty atomic state. 205 * 206 * The state is tied to the kunit test context, so we must not call 207 * drm_atomic_state_put() on it, it will be done so automatically. 208 * 209 * Returns: 210 * An ERR_PTR on error, a pointer to the newly allocated state otherwise 211 */ 212 struct drm_atomic_state * 213 drm_kunit_helper_atomic_state_alloc(struct kunit *test, 214 struct drm_device *drm, 215 struct drm_modeset_acquire_ctx *ctx) 216 { 217 struct drm_atomic_state *state; 218 int ret; 219 220 state = drm_atomic_state_alloc(drm); 221 if (!state) 222 return ERR_PTR(-ENOMEM); 223 224 ret = kunit_add_action_or_reset(test, 225 kunit_action_drm_atomic_state_put, 226 state); 227 if (ret) 228 return ERR_PTR(ret); 229 230 state->acquire_ctx = ctx; 231 232 return state; 233 } 234 EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); 235 236 MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); 237 MODULE_LICENSE("GPL"); 238