File Index
Files and directories
Sign in
FILE

drm_kunit_helpers.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/tests/drm_kunit_helpers.c C
  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");
~/ Read-only file preview.