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