xref: /linux/drivers/gpu/drm/vkms/vkms_config.c (revision 3f1c07fc21c68bd3bd2df9d2c9441f6485e934d9)
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