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 | action: kunit_action_platform_driver_unregister, |
77 | ctx: &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 | action: kunit_action_platform_device_put, |
85 | ctx: 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 | action: kunit_action_platform_device_del, |
93 | ctx: 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 | action: kunit_action_platform_device_del, |
113 | ctx: pdev); |
114 | |
115 | kunit_release_action(test, |
116 | action: kunit_action_platform_device_put, |
117 | ctx: pdev); |
118 | |
119 | kunit_release_action(test, |
120 | action: kunit_action_platform_driver_unregister, |
121 | ctx: &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(parent: dev, driver, size, offset); |
136 | if (IS_ERR(ptr: container)) |
137 | return ERR_CAST(ptr: container); |
138 | |
139 | drm = container + offset; |
140 | drm->mode_config.funcs = &drm_mode_config_funcs; |
141 | |
142 | ret = drmm_mode_config_init(dev: drm); |
143 | if (ret) |
144 | return ERR_PTR(error: 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, size: sizeof(*ctx), GFP_KERNEL); |
177 | KUNIT_ASSERT_NOT_NULL(test, ctx); |
178 | |
179 | drm_modeset_acquire_init(ctx, flags: 0); |
180 | |
181 | ret = kunit_add_action_or_reset(test, |
182 | action: action_drm_release_context, |
183 | ctx); |
184 | if (ret) |
185 | return ERR_PTR(error: 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(dev: drm); |
221 | if (!state) |
222 | return ERR_PTR(error: -ENOMEM); |
223 | |
224 | ret = kunit_add_action_or_reset(test, |
225 | action: kunit_action_drm_atomic_state_put, |
226 | ctx: state); |
227 | if (ret) |
228 | return ERR_PTR(error: 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 | |