xref: /linux/drivers/gpu/drm/tests/drm_kunit_helpers.c (revision d53b8e36925256097a08d7cb749198d85cbf9b2b)
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