1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <linux/slab.h>
4
5 #include <drm/drm_print.h>
6 #include <drm/drm_debugfs.h>
7 #include <kunit/visibility.h>
8
9 #include "vkms_config.h"
10
vkms_config_create(const char * dev_name)11 struct vkms_config *vkms_config_create(const char *dev_name)
12 {
13 struct vkms_config *config;
14
15 config = kzalloc(sizeof(*config), GFP_KERNEL);
16 if (!config)
17 return ERR_PTR(-ENOMEM);
18
19 config->dev_name = kstrdup_const(dev_name, GFP_KERNEL);
20 if (!config->dev_name) {
21 kfree(config);
22 return ERR_PTR(-ENOMEM);
23 }
24
25 INIT_LIST_HEAD(&config->planes);
26 INIT_LIST_HEAD(&config->crtcs);
27 INIT_LIST_HEAD(&config->encoders);
28 INIT_LIST_HEAD(&config->connectors);
29
30 return config;
31 }
32 EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);
33
vkms_config_default_create(bool enable_cursor,bool enable_writeback,bool enable_overlay,bool enable_plane_pipeline)34 struct vkms_config *vkms_config_default_create(bool enable_cursor,
35 bool enable_writeback,
36 bool enable_overlay,
37 bool enable_plane_pipeline)
38 {
39 struct vkms_config *config;
40 struct vkms_config_plane *plane_cfg;
41 struct vkms_config_crtc *crtc_cfg;
42 struct vkms_config_encoder *encoder_cfg;
43 struct vkms_config_connector *connector_cfg;
44 int n;
45
46 config = vkms_config_create(DEFAULT_DEVICE_NAME);
47 if (IS_ERR(config))
48 return config;
49
50 plane_cfg = vkms_config_create_plane(config);
51 if (IS_ERR(plane_cfg))
52 goto err_alloc;
53 vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
54
55 crtc_cfg = vkms_config_create_crtc(config);
56 if (IS_ERR(crtc_cfg))
57 goto err_alloc;
58 vkms_config_crtc_set_writeback(crtc_cfg, enable_writeback);
59
60 if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
61 goto err_alloc;
62 vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
63
64 if (enable_overlay) {
65 for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
66 plane_cfg = vkms_config_create_plane(config);
67 if (IS_ERR(plane_cfg))
68 goto err_alloc;
69
70 vkms_config_plane_set_type(plane_cfg,
71 DRM_PLANE_TYPE_OVERLAY);
72 vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
73
74 if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
75 goto err_alloc;
76 }
77 }
78
79 if (enable_cursor) {
80 plane_cfg = vkms_config_create_plane(config);
81 if (IS_ERR(plane_cfg))
82 goto err_alloc;
83
84 vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
85 vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
86
87 if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
88 goto err_alloc;
89 }
90
91 encoder_cfg = vkms_config_create_encoder(config);
92 if (IS_ERR(encoder_cfg))
93 goto err_alloc;
94
95 if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
96 goto err_alloc;
97
98 connector_cfg = vkms_config_create_connector(config);
99 if (IS_ERR(connector_cfg))
100 goto err_alloc;
101
102 if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
103 goto err_alloc;
104
105 return config;
106
107 err_alloc:
108 vkms_config_destroy(config);
109 return ERR_PTR(-ENOMEM);
110 }
111 EXPORT_SYMBOL_IF_KUNIT(vkms_config_default_create);
112
vkms_config_destroy(struct vkms_config * config)113 void vkms_config_destroy(struct vkms_config *config)
114 {
115 struct vkms_config_plane *plane_cfg, *plane_tmp;
116 struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
117 struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
118 struct vkms_config_connector *connector_cfg, *connector_tmp;
119
120 list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
121 vkms_config_destroy_plane(plane_cfg);
122
123 list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
124 vkms_config_destroy_crtc(config, crtc_cfg);
125
126 list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
127 vkms_config_destroy_encoder(config, encoder_cfg);
128
129 list_for_each_entry_safe(connector_cfg, connector_tmp, &config->connectors, link)
130 vkms_config_destroy_connector(connector_cfg);
131
132 kfree_const(config->dev_name);
133 kfree(config);
134 }
135 EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy);
136
valid_plane_number(const struct vkms_config * config)137 static bool valid_plane_number(const struct vkms_config *config)
138 {
139 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
140 size_t n_planes;
141
142 n_planes = list_count_nodes((struct list_head *)&config->planes);
143 if (n_planes <= 0 || n_planes >= 32) {
144 drm_info(dev, "The number of planes must be between 1 and 31\n");
145 return false;
146 }
147
148 return true;
149 }
150
valid_planes_for_crtc(const struct vkms_config * config,struct vkms_config_crtc * crtc_cfg)151 static bool valid_planes_for_crtc(const struct vkms_config *config,
152 struct vkms_config_crtc *crtc_cfg)
153 {
154 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
155 struct vkms_config_plane *plane_cfg;
156 bool has_primary_plane = false;
157 bool has_cursor_plane = false;
158
159 vkms_config_for_each_plane(config, plane_cfg) {
160 struct vkms_config_crtc *possible_crtc;
161 unsigned long idx = 0;
162 enum drm_plane_type type;
163
164 type = vkms_config_plane_get_type(plane_cfg);
165
166 vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
167 if (possible_crtc != crtc_cfg)
168 continue;
169
170 if (type == DRM_PLANE_TYPE_PRIMARY) {
171 if (has_primary_plane) {
172 drm_info(dev, "Multiple primary planes\n");
173 return false;
174 }
175
176 has_primary_plane = true;
177 } else if (type == DRM_PLANE_TYPE_CURSOR) {
178 if (has_cursor_plane) {
179 drm_info(dev, "Multiple cursor planes\n");
180 return false;
181 }
182
183 has_cursor_plane = true;
184 }
185 }
186 }
187
188 if (!has_primary_plane) {
189 drm_info(dev, "Primary plane not found\n");
190 return false;
191 }
192
193 return true;
194 }
195
valid_plane_possible_crtcs(const struct vkms_config * config)196 static bool valid_plane_possible_crtcs(const struct vkms_config *config)
197 {
198 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
199 struct vkms_config_plane *plane_cfg;
200
201 vkms_config_for_each_plane(config, plane_cfg) {
202 if (xa_empty(&plane_cfg->possible_crtcs)) {
203 drm_info(dev, "All planes must have at least one possible CRTC\n");
204 return false;
205 }
206 }
207
208 return true;
209 }
210
valid_crtc_number(const struct vkms_config * config)211 static bool valid_crtc_number(const struct vkms_config *config)
212 {
213 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
214 size_t n_crtcs;
215
216 n_crtcs = list_count_nodes((struct list_head *)&config->crtcs);
217 if (n_crtcs <= 0 || n_crtcs >= 32) {
218 drm_info(dev, "The number of CRTCs must be between 1 and 31\n");
219 return false;
220 }
221
222 return true;
223 }
224
valid_encoder_number(const struct vkms_config * config)225 static bool valid_encoder_number(const struct vkms_config *config)
226 {
227 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
228 size_t n_encoders;
229
230 n_encoders = list_count_nodes((struct list_head *)&config->encoders);
231 if (n_encoders <= 0 || n_encoders >= 32) {
232 drm_info(dev, "The number of encoders must be between 1 and 31\n");
233 return false;
234 }
235
236 return true;
237 }
238
valid_encoder_possible_crtcs(const struct vkms_config * config)239 static bool valid_encoder_possible_crtcs(const struct vkms_config *config)
240 {
241 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
242 struct vkms_config_crtc *crtc_cfg;
243 struct vkms_config_encoder *encoder_cfg;
244
245 vkms_config_for_each_encoder(config, encoder_cfg) {
246 if (xa_empty(&encoder_cfg->possible_crtcs)) {
247 drm_info(dev, "All encoders must have at least one possible CRTC\n");
248 return false;
249 }
250 }
251
252 vkms_config_for_each_crtc(config, crtc_cfg) {
253 bool crtc_has_encoder = false;
254
255 vkms_config_for_each_encoder(config, encoder_cfg) {
256 struct vkms_config_crtc *possible_crtc;
257 unsigned long idx = 0;
258
259 vkms_config_encoder_for_each_possible_crtc(encoder_cfg,
260 idx, possible_crtc) {
261 if (possible_crtc == crtc_cfg)
262 crtc_has_encoder = true;
263 }
264 }
265
266 if (!crtc_has_encoder) {
267 drm_info(dev, "All CRTCs must have at least one possible encoder\n");
268 return false;
269 }
270 }
271
272 return true;
273 }
274
valid_connector_number(const struct vkms_config * config)275 static bool valid_connector_number(const struct vkms_config *config)
276 {
277 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
278 size_t n_connectors;
279
280 n_connectors = list_count_nodes((struct list_head *)&config->connectors);
281 if (n_connectors <= 0 || n_connectors >= 32) {
282 drm_info(dev, "The number of connectors must be between 1 and 31\n");
283 return false;
284 }
285
286 return true;
287 }
288
valid_connector_possible_encoders(const struct vkms_config * config)289 static bool valid_connector_possible_encoders(const struct vkms_config *config)
290 {
291 struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
292 struct vkms_config_connector *connector_cfg;
293
294 vkms_config_for_each_connector(config, connector_cfg) {
295 if (xa_empty(&connector_cfg->possible_encoders)) {
296 drm_info(dev,
297 "All connectors must have at least one possible encoder\n");
298 return false;
299 }
300 }
301
302 return true;
303 }
304
vkms_config_is_valid(const struct vkms_config * config)305 bool vkms_config_is_valid(const struct vkms_config *config)
306 {
307 struct vkms_config_crtc *crtc_cfg;
308
309 if (!valid_plane_number(config))
310 return false;
311
312 if (!valid_crtc_number(config))
313 return false;
314
315 if (!valid_encoder_number(config))
316 return false;
317
318 if (!valid_connector_number(config))
319 return false;
320
321 if (!valid_plane_possible_crtcs(config))
322 return false;
323
324 vkms_config_for_each_crtc(config, crtc_cfg) {
325 if (!valid_planes_for_crtc(config, crtc_cfg))
326 return false;
327 }
328
329 if (!valid_encoder_possible_crtcs(config))
330 return false;
331
332 if (!valid_connector_possible_encoders(config))
333 return false;
334
335 return true;
336 }
337 EXPORT_SYMBOL_IF_KUNIT(vkms_config_is_valid);
338
vkms_config_show(struct seq_file * m,void * data)339 static int vkms_config_show(struct seq_file *m, void *data)
340 {
341 struct drm_debugfs_entry *entry = m->private;
342 struct drm_device *dev = entry->dev;
343 struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
344 const char *dev_name;
345 struct vkms_config_plane *plane_cfg;
346 struct vkms_config_crtc *crtc_cfg;
347 struct vkms_config_encoder *encoder_cfg;
348 struct vkms_config_connector *connector_cfg;
349
350 dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
351 seq_printf(m, "dev_name=%s\n", dev_name);
352
353 vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
354 seq_puts(m, "plane:\n");
355 seq_printf(m, "\ttype=%d\n",
356 vkms_config_plane_get_type(plane_cfg));
357 }
358
359 vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg) {
360 seq_puts(m, "crtc:\n");
361 seq_printf(m, "\twriteback=%d\n",
362 vkms_config_crtc_get_writeback(crtc_cfg));
363 }
364
365 vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
366 seq_puts(m, "encoder\n");
367
368 vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
369 seq_puts(m, "connector:\n");
370 seq_printf(m, "\tstatus=%d\n",
371 vkms_config_connector_get_status(connector_cfg));
372 }
373
374 return 0;
375 }
376
377 static const struct drm_debugfs_info vkms_config_debugfs_list[] = {
378 { "vkms_config", vkms_config_show, 0 },
379 };
380
vkms_config_register_debugfs(struct vkms_device * vkms_device)381 void vkms_config_register_debugfs(struct vkms_device *vkms_device)
382 {
383 drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
384 ARRAY_SIZE(vkms_config_debugfs_list));
385 }
386
vkms_config_create_plane(struct vkms_config * config)387 struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
388 {
389 struct vkms_config_plane *plane_cfg;
390
391 plane_cfg = kzalloc(sizeof(*plane_cfg), GFP_KERNEL);
392 if (!plane_cfg)
393 return ERR_PTR(-ENOMEM);
394
395 plane_cfg->config = config;
396 plane_cfg->default_pipeline = false;
397 vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
398 xa_init_flags(&plane_cfg->possible_crtcs, XA_FLAGS_ALLOC);
399
400 list_add_tail(&plane_cfg->link, &config->planes);
401
402 return plane_cfg;
403 }
404 EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_plane);
405
vkms_config_destroy_plane(struct vkms_config_plane * plane_cfg)406 void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
407 {
408 xa_destroy(&plane_cfg->possible_crtcs);
409 list_del(&plane_cfg->link);
410 kfree(plane_cfg);
411 }
412 EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_plane);
413
vkms_config_plane_attach_crtc(struct vkms_config_plane * plane_cfg,struct vkms_config_crtc * crtc_cfg)414 int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *plane_cfg,
415 struct vkms_config_crtc *crtc_cfg)
416 {
417 struct vkms_config_crtc *possible_crtc;
418 unsigned long idx = 0;
419 u32 crtc_idx = 0;
420
421 if (plane_cfg->config != crtc_cfg->config)
422 return -EINVAL;
423
424 vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
425 if (possible_crtc == crtc_cfg)
426 return -EEXIST;
427 }
428
429 return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
430 xa_limit_32b, GFP_KERNEL);
431 }
432 EXPORT_SYMBOL_IF_KUNIT(vkms_config_plane_attach_crtc);
433
vkms_config_plane_detach_crtc(struct vkms_config_plane * plane_cfg,struct vkms_config_crtc * crtc_cfg)434 void vkms_config_plane_detach_crtc(struct vkms_config_plane *plane_cfg,
435 struct vkms_config_crtc *crtc_cfg)
436 {
437 struct vkms_config_crtc *possible_crtc;
438 unsigned long idx = 0;
439
440 vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
441 if (possible_crtc == crtc_cfg)
442 xa_erase(&plane_cfg->possible_crtcs, idx);
443 }
444 }
445 EXPORT_SYMBOL_IF_KUNIT(vkms_config_plane_detach_crtc);
446
vkms_config_create_crtc(struct vkms_config * config)447 struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
448 {
449 struct vkms_config_crtc *crtc_cfg;
450
451 crtc_cfg = kzalloc(sizeof(*crtc_cfg), GFP_KERNEL);
452 if (!crtc_cfg)
453 return ERR_PTR(-ENOMEM);
454
455 crtc_cfg->config = config;
456 vkms_config_crtc_set_writeback(crtc_cfg, false);
457
458 list_add_tail(&crtc_cfg->link, &config->crtcs);
459
460 return crtc_cfg;
461 }
462 EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_crtc);
463
vkms_config_destroy_crtc(struct vkms_config * config,struct vkms_config_crtc * crtc_cfg)464 void vkms_config_destroy_crtc(struct vkms_config *config,
465 struct vkms_config_crtc *crtc_cfg)
466 {
467 struct vkms_config_plane *plane_cfg;
468 struct vkms_config_encoder *encoder_cfg;
469
470 vkms_config_for_each_plane(config, plane_cfg)
471 vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
472
473 vkms_config_for_each_encoder(config, encoder_cfg)
474 vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
475
476 list_del(&crtc_cfg->link);
477 kfree(crtc_cfg);
478 }
479 EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_crtc);
480
481 /**
482 * vkms_config_crtc_get_plane() - Return the first attached plane to a CRTC with
483 * the specific type
484 * @config: Configuration containing the CRTC and the plane
485 * @crtc_cfg: Only find planes attached to this CRTC
486 * @type: Plane type to search
487 *
488 * Returns:
489 * The first plane found attached to @crtc_cfg with the type @type.
490 */
vkms_config_crtc_get_plane(const struct vkms_config * config,struct vkms_config_crtc * crtc_cfg,enum drm_plane_type type)491 static struct vkms_config_plane *vkms_config_crtc_get_plane(const struct vkms_config *config,
492 struct vkms_config_crtc *crtc_cfg,
493 enum drm_plane_type type)
494 {
495 struct vkms_config_plane *plane_cfg;
496 struct vkms_config_crtc *possible_crtc;
497 enum drm_plane_type current_type;
498 unsigned long idx = 0;
499
500 vkms_config_for_each_plane(config, plane_cfg) {
501 current_type = vkms_config_plane_get_type(plane_cfg);
502
503 vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
504 if (possible_crtc == crtc_cfg && current_type == type)
505 return plane_cfg;
506 }
507 }
508
509 return NULL;
510 }
511
vkms_config_crtc_primary_plane(const struct vkms_config * config,struct vkms_config_crtc * crtc_cfg)512 struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_config *config,
513 struct vkms_config_crtc *crtc_cfg)
514 {
515 return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_PRIMARY);
516 }
517 EXPORT_SYMBOL_IF_KUNIT(vkms_config_crtc_primary_plane);
518
vkms_config_crtc_cursor_plane(const struct vkms_config * config,struct vkms_config_crtc * crtc_cfg)519 struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
520 struct vkms_config_crtc *crtc_cfg)
521 {
522 return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_CURSOR);
523 }
524 EXPORT_SYMBOL_IF_KUNIT(vkms_config_crtc_cursor_plane);
525
vkms_config_create_encoder(struct vkms_config * config)526 struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config)
527 {
528 struct vkms_config_encoder *encoder_cfg;
529
530 encoder_cfg = kzalloc(sizeof(*encoder_cfg), GFP_KERNEL);
531 if (!encoder_cfg)
532 return ERR_PTR(-ENOMEM);
533
534 encoder_cfg->config = config;
535 xa_init_flags(&encoder_cfg->possible_crtcs, XA_FLAGS_ALLOC);
536
537 list_add_tail(&encoder_cfg->link, &config->encoders);
538
539 return encoder_cfg;
540 }
541 EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_encoder);
542
vkms_config_destroy_encoder(struct vkms_config * config,struct vkms_config_encoder * encoder_cfg)543 void vkms_config_destroy_encoder(struct vkms_config *config,
544 struct vkms_config_encoder *encoder_cfg)
545 {
546 struct vkms_config_connector *connector_cfg;
547
548 vkms_config_for_each_connector(config, connector_cfg)
549 vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
550
551 xa_destroy(&encoder_cfg->possible_crtcs);
552 list_del(&encoder_cfg->link);
553 kfree(encoder_cfg);
554 }
555 EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_encoder);
556
vkms_config_encoder_attach_crtc(struct vkms_config_encoder * encoder_cfg,struct vkms_config_crtc * crtc_cfg)557 int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
558 struct vkms_config_crtc *crtc_cfg)
559 {
560 struct vkms_config_crtc *possible_crtc;
561 unsigned long idx = 0;
562 u32 crtc_idx = 0;
563
564 if (encoder_cfg->config != crtc_cfg->config)
565 return -EINVAL;
566
567 vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
568 if (possible_crtc == crtc_cfg)
569 return -EEXIST;
570 }
571
572 return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
573 xa_limit_32b, GFP_KERNEL);
574 }
575 EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_attach_crtc);
576
vkms_config_encoder_detach_crtc(struct vkms_config_encoder * encoder_cfg,struct vkms_config_crtc * crtc_cfg)577 void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
578 struct vkms_config_crtc *crtc_cfg)
579 {
580 struct vkms_config_crtc *possible_crtc;
581 unsigned long idx = 0;
582
583 vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
584 if (possible_crtc == crtc_cfg)
585 xa_erase(&encoder_cfg->possible_crtcs, idx);
586 }
587 }
588 EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_detach_crtc);
589
vkms_config_create_connector(struct vkms_config * config)590 struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config)
591 {
592 struct vkms_config_connector *connector_cfg;
593
594 connector_cfg = kzalloc(sizeof(*connector_cfg), GFP_KERNEL);
595 if (!connector_cfg)
596 return ERR_PTR(-ENOMEM);
597
598 connector_cfg->config = config;
599 connector_cfg->status = connector_status_connected;
600 xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
601
602 list_add_tail(&connector_cfg->link, &config->connectors);
603
604 return connector_cfg;
605 }
606 EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_connector);
607
vkms_config_destroy_connector(struct vkms_config_connector * connector_cfg)608 void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
609 {
610 xa_destroy(&connector_cfg->possible_encoders);
611 list_del(&connector_cfg->link);
612 kfree(connector_cfg);
613 }
614 EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_connector);
615
vkms_config_connector_attach_encoder(struct vkms_config_connector * connector_cfg,struct vkms_config_encoder * encoder_cfg)616 int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
617 struct vkms_config_encoder *encoder_cfg)
618 {
619 struct vkms_config_encoder *possible_encoder;
620 unsigned long idx = 0;
621 u32 encoder_idx = 0;
622
623 if (connector_cfg->config != encoder_cfg->config)
624 return -EINVAL;
625
626 vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
627 possible_encoder) {
628 if (possible_encoder == encoder_cfg)
629 return -EEXIST;
630 }
631
632 return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
633 encoder_cfg, xa_limit_32b, GFP_KERNEL);
634 }
635 EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_attach_encoder);
636
vkms_config_connector_detach_encoder(struct vkms_config_connector * connector_cfg,struct vkms_config_encoder * encoder_cfg)637 void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
638 struct vkms_config_encoder *encoder_cfg)
639 {
640 struct vkms_config_encoder *possible_encoder;
641 unsigned long idx = 0;
642
643 vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
644 possible_encoder) {
645 if (possible_encoder == encoder_cfg)
646 xa_erase(&connector_cfg->possible_encoders, idx);
647 }
648 }
649 EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_detach_encoder);
650