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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/drivers/gpu/drm/vkms/vkms_config.c