Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 19static 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 */
 39struct device *drm_kunit_helper_alloc_device(struct kunit *test)
 40{
 41	return kunit_device_register(test, KUNIT_DEVICE_NAME);
 42}
 43EXPORT_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 */
 52void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
 53{
 54	kunit_device_unregister(test, dev);
 55}
 56EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
 57
 58struct 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}
 81EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);
 82
 83static 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 */
103struct drm_modeset_acquire_ctx *
104drm_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}
122EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc);
123
124static 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 */
145struct drm_atomic_state *
146drm_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}
167EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);
168
169static const uint32_t default_plane_formats[] = {
170	DRM_FORMAT_XRGB8888,
171};
172
173static const uint64_t default_plane_modifiers[] = {
174	DRM_FORMAT_MOD_LINEAR,
175	DRM_FORMAT_MOD_INVALID
176};
177
178static const struct drm_plane_helper_funcs default_plane_helper_funcs = {
179};
180
181static 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 */
211struct drm_plane *
212drm_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}
251EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
252
253static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
254};
255
256static 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 */
282struct drm_crtc *
283drm_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}
313EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);
314
315static 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 for a KUnit test
324 * @test: The test context object
325 * @dev: DRM device
326 * @video_code: CEA VIC of the mode
327 *
328 * Creates a new mode matching the specified CEA VIC for a KUnit test.
329 *
330 * Resources will be cleaned up automatically.
331 *
332 * Returns: A new drm_display_mode on success or NULL on failure
333 */
334struct drm_display_mode *
335drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev,
336				    u8 video_code)
337{
338	struct drm_display_mode *mode;
339	int ret;
340
341	mode = drm_display_mode_from_cea_vic(dev, video_code);
342	if (!mode)
343		return NULL;
344
345	ret = kunit_add_action_or_reset(test,
346					kunit_action_drm_mode_destroy,
347					mode);
348	if (ret)
349		return NULL;
350
351	return mode;
352}
353EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic);
354
355MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
356MODULE_DESCRIPTION("KUnit test suite helper functions");
357MODULE_LICENSE("GPL");