tests: add kms_setmode
[platform/upstream/intel-gpu-tools.git] / tests / kms_setmode.c
1 /*
2  * Permission is hereby granted, free of charge, to any person obtaining a
3  * copy of this software and associated documentation files (the "Software"),
4  * to deal in the Software without restriction, including without limitation
5  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6  * and/or sell copies of the Software, and to permit persons to whom the
7  * Software is furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18  * IN THE SOFTWARE.
19  *
20  * Authors:
21  *    Imre Deak <imre.deak@intel.com>
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <assert.h>
28 #include <cairo.h>
29 #include <errno.h>
30 #include <stdint.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <getopt.h>
34 #include <sys/time.h>
35
36 #include "drm_fourcc.h"
37 #include "drmtest.h"
38 #include "intel_bufmgr.h"
39 #include "intel_batchbuffer.h"
40 #include "intel_gpu_tools.h"
41
42 #define MAX_CONNECTORS  10
43 #define MAX_CRTCS       3
44
45 /* max combinations with repetitions */
46 #define MAX_COMBINATION_COUNT   \
47         (MAX_CONNECTORS * MAX_CONNECTORS * MAX_CONNECTORS)
48 #define MAX_COMBINATION_ELEMS   MAX_CRTCS
49
50 static int drm_fd;
51 static drmModeRes *drm_resources;
52 static int filter_test_id;
53 static bool dry_run;
54
55 const drmModeModeInfo mode_640_480 = {
56         .name           = "640x480",
57         .vrefresh       = 60,
58         .clock          = 25200,
59
60         .hdisplay       = 640,
61         .hsync_start    = 656,
62         .hsync_end      = 752,
63         .htotal         = 800,
64
65         .vdisplay       = 480,
66         .vsync_start    = 490,
67         .vsync_end      = 492,
68         .vtotal         = 525,
69
70         .flags          = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
71 };
72
73 enum test_flags {
74         TEST_INVALID                    = 0x01,
75         TEST_CLONE                      = 0x02,
76         TEST_SINGLE_CRTC_CLONE          = 0x04,
77         TEST_EXCLUSIVE_CRTC_CLONE       = 0x08,
78 };
79
80 struct test_config {
81         const char *name;
82         enum test_flags flags;
83         drmModeRes *resources;
84 };
85
86 struct connector_config {
87         drmModeConnector *connector;
88         int crtc_idx;
89         bool connected;
90         drmModeModeInfo default_mode;
91 };
92
93 struct crtc_config {
94         int crtc_idx;
95         int crtc_id;
96         int pipe_id;
97         int connector_count;
98         struct connector_config *cconfs;
99         struct kmstest_fb fb_info;
100         drmModeModeInfo mode;
101 };
102
103 static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2)
104 {
105 #define COMP(x) do { if (m1->x != m2->x) return false; } while (0)
106         COMP(vrefresh);
107         COMP(clock);
108         COMP(hdisplay);
109         COMP(hsync_start);
110         COMP(hsync_end);
111         COMP(htotal);
112         COMP(vdisplay);
113         COMP(vsync_start);
114         COMP(vsync_end);
115         COMP(vtotal);
116         COMP(flags);
117
118         return true;
119 }
120
121 static bool connector_supports_mode(drmModeConnector *connector,
122                                     drmModeModeInfo *mode)
123 {
124         int i;
125
126         for (i = 0; i < connector->count_modes; i++)
127                 if (drm_mode_equal(&connector->modes[i], mode))
128                         return true;
129
130         return false;
131 }
132
133 static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode)
134 {
135         int i;
136
137         for (i = 0; i < crtc->connector_count; i++) {
138                 if (!connector_supports_mode(crtc->cconfs[i].connector, mode))
139                         return false;
140         }
141
142         return true;
143 }
144
145 static int paint_fb(struct kmstest_fb *fb, const char *test_name,
146                     const char **crtc_str, int crtc_count, int current_crtc_idx)
147 {
148         double x, y;
149         cairo_t *cr;
150         int i;
151
152         cr = kmstest_get_cairo_ctx(drm_fd, fb);
153         if (!cr)
154                 return -1;
155
156         kmstest_paint_test_pattern(cr, fb->width, fb->height);
157
158         cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
159                                CAIRO_FONT_WEIGHT_NORMAL);
160         cairo_move_to(cr, fb->width / 2, fb->height / 2);
161         cairo_set_font_size(cr, 24);
162         kmstest_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name);
163
164         cairo_get_current_point(cr, &x, &y);
165         cairo_move_to(cr, 60, y);
166
167         for (i = 0; i < crtc_count; i++) {
168                 if (i == current_crtc_idx) {
169                         cairo_get_current_point(cr, &x, &y);
170                         cairo_move_to(cr, x - 20, y);
171                         kmstest_cairo_printf_line(cr, align_right, 20, "X");
172                         cairo_move_to(cr, x, y);
173                 }
174                 kmstest_cairo_printf_line(cr, align_left, 20, "%s",
175                                           crtc_str[i]);
176         }
177
178         return 0;
179 }
180
181 static void create_fb_for_crtc(struct crtc_config *crtc,
182                                struct kmstest_fb *fb_info)
183 {
184         int bpp;
185         int depth;
186         bool enable_tiling;
187         int fb_id;
188
189         bpp = 32;
190         depth = 24;
191         enable_tiling = false;
192         fb_id = kmstest_create_fb(drm_fd, crtc->mode.hdisplay,
193                                   crtc->mode.vdisplay, bpp, depth,
194                                   enable_tiling, fb_info);
195         assert(fb_id > 0);
196 }
197
198 static void get_mode_for_crtc(struct crtc_config *crtc,
199                               drmModeModeInfo *mode_ret)
200 {
201         drmModeModeInfo mode;
202         int i;
203
204         /*
205          * First try to select a default mode that is supported by all
206          * connectors.
207          */
208         for (i = 0; i < crtc->connector_count; i++) {
209                 mode = crtc->cconfs[i].default_mode;
210                 if (crtc_supports_mode(crtc, &mode))
211                         goto found;
212         }
213
214         /*
215          * Then just fall back to find any that is supported by all
216          * connectors.
217          */
218         for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) {
219                 mode = crtc->cconfs[0].connector->modes[i];
220                 if (crtc_supports_mode(crtc, &mode))
221                         goto found;
222         }
223
224         /*
225          * If none is found then just pick the default mode of the first
226          * connector and hope the other connectors can support it by scaling
227          * etc.
228          */
229         mode = crtc->cconfs[0].default_mode;
230 found:
231         *mode_ret = mode;
232 }
233
234 static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder)
235 {
236         int i;
237
238         for (i = 0; i < resources->count_encoders; i++)
239                 if (resources->encoders[i] == encoder->encoder_id)
240                         return i;
241         assert(0);
242 }
243
244 static void get_crtc_config_str(struct crtc_config *crtc, char *buf,
245                                 size_t buf_size)
246 {
247         int pos;
248         int i;
249
250         pos = snprintf(buf, buf_size,
251                        "CRTC[%d] [Pipe %s] Mode: %s@%dHz Connectors: ",
252                        crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id),
253                        crtc->mode.name, crtc->mode.vrefresh);
254         if (pos > buf_size)
255                 return;
256         for (i = 0; i < crtc->connector_count; i++) {
257                 drmModeConnector *connector = crtc->cconfs[i].connector;
258
259                 pos += snprintf(&buf[pos], buf_size - pos,
260                         "%s%s-%d[%d]%s", i ? ", " : "",
261                         kmstest_connector_type_str(connector->connector_type),
262                         connector->connector_type_id, connector->connector_id,
263                         crtc->cconfs[i].connected ? "" : " (NC)");
264                 if (pos > buf_size)
265                         return;
266         }
267 }
268
269 static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf,
270                         int connector_count, struct crtc_config *crtcs,
271                         int *crtc_count_ret, bool *config_valid_ret)
272 {
273         struct crtc_config *crtc;
274         int crtc_count;
275         bool config_valid;
276         int i;
277
278         i = 0;
279         crtc_count = 0;
280         crtc = crtcs;
281         config_valid = true;
282
283         while (i < connector_count) {
284                 drmModeCrtc *drm_crtc;
285                 unsigned long encoder_mask;
286                 int j;
287
288                 igt_assert(crtc_count < MAX_CRTCS);
289
290                 crtc->crtc_idx = cconf[i].crtc_idx;
291                 drm_crtc = drmModeGetCrtc(drm_fd,
292                                           resources->crtcs[crtc->crtc_idx]);
293                 crtc->crtc_id = drm_crtc->crtc_id;
294                 drmModeFreeCrtc(drm_crtc);
295                 crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd,
296                                                               crtc->crtc_id);
297
298                 crtc->connector_count = 1;
299                 for (j = i + 1; j < connector_count; j++)
300                         if (cconf[j].crtc_idx == crtc->crtc_idx)
301                                 crtc->connector_count++;
302
303                 crtc->cconfs = malloc(sizeof(*crtc->cconfs) *
304                                       crtc->connector_count);
305                 assert(crtc->cconfs);
306
307                 encoder_mask = 0;
308                 for (j = 0; j < crtc->connector_count; j++) {
309                         drmModeConnector *connector;
310                         drmModeEncoder *encoder;
311
312                         crtc->cconfs[j] = cconf[i + j];
313                         connector = cconf[i + j].connector;
314
315                         /* Intel connectors have only a single encoder */
316                         igt_assert(connector->count_encoders == 1);
317                         encoder = drmModeGetEncoder(drm_fd,
318                                                     connector->encoders[0]);
319                         assert(encoder);
320
321                         config_valid &= !!(encoder->possible_crtcs &
322                                           (1 << crtc->crtc_idx));
323
324                         encoder_mask |= 1 << get_encoder_idx(resources,
325                                                              encoder);
326                         config_valid &= !(encoder_mask &
327                                           ~encoder->possible_clones);
328
329                         drmModeFreeEncoder(encoder);
330                 }
331                 get_mode_for_crtc(crtc, &crtc->mode);
332                 create_fb_for_crtc(crtc, &crtc->fb_info);
333
334                 i += crtc->connector_count;
335                 crtc_count++;
336                 crtc++;
337         }
338
339         *crtc_count_ret = crtc_count;
340         *config_valid_ret = config_valid;
341 }
342
343 static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count)
344 {
345         int i;
346
347         for (i = 0; i < crtc_count; i++) {
348                 free(crtcs[i].cconfs);
349         }
350 }
351
352 static uint32_t *get_connector_ids(struct crtc_config *crtc)
353 {
354         uint32_t *ids;
355         int i;
356
357         ids = malloc(sizeof(*ids) * crtc->connector_count);
358         assert(ids);
359         for (i = 0; i < crtc->connector_count; i++)
360                 ids[i] = crtc->cconfs[i].connector->connector_id;
361
362         return ids;
363 }
364
365 static void test_crtc_config(const struct test_config *tconf,
366                              struct crtc_config *crtcs, int crtc_count)
367 {
368         char str_buf[MAX_CRTCS][1024];
369         const char *crtc_strs[MAX_CRTCS];
370         struct crtc_config *crtc;
371         static int test_id;
372         bool config_failed = false;
373         bool connector_connected = false;
374         int ret = 0;
375         int i;
376
377         test_id++;
378
379         if (filter_test_id && filter_test_id != test_id)
380                 return;
381
382         printf("  Test id#%d CRTC count %d\n", test_id, crtc_count);
383
384         for (i = 0; i < crtc_count; i++) {
385                 get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i]));
386                 crtc_strs[i] = &str_buf[i][0];
387         }
388
389         if (dry_run) {
390                 for (i = 0; i < crtc_count; i++)
391                         printf("    %s\n", crtc_strs[i]);
392                 return;
393         }
394
395         for (i = 0; i < crtc_count; i++) {
396                 uint32_t *ids;
397                 int j;
398
399                 crtc = &crtcs[i];
400
401                 printf("    %s\n", crtc_strs[i]);
402
403                 create_fb_for_crtc(crtc, &crtc->fb_info);
404                 paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i);
405
406                 ids = get_connector_ids(crtc);
407                 ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
408                                      crtc->fb_info.fb_id, 0, 0, ids,
409                                      crtc->connector_count, &crtc->mode);
410                 free(ids);
411
412                 if (ret < 0) {
413                         igt_assert(errno == EINVAL);
414                         config_failed = true;
415                 }
416
417                 for (j = 0; j < crtc->connector_count; j++)
418                         connector_connected |= crtc->cconfs[j].connected;
419         }
420
421         igt_assert(config_failed == !!(tconf->flags & TEST_INVALID));
422
423         if (ret == 0 && connector_connected && !(tconf->flags & TEST_INVALID))
424                 sleep(5);
425
426         for (i = 0; i < crtc_count; i++) {
427                 if (crtcs[i].fb_info.fb_id) {
428                         drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL,
429                                         0, NULL);
430                         drmModeRmFB(drm_fd, crtcs[i].fb_info.fb_id);
431                         crtcs[i].fb_info.fb_id = 0;
432                 }
433         }
434
435         return;
436 }
437
438 static void test_one_combination(const struct test_config *tconf,
439                                  struct connector_config *cconfs,
440                                  int connector_count)
441 {
442         struct crtc_config crtcs[MAX_CRTCS];
443         int crtc_count;
444         bool config_valid;
445
446         setup_crtcs(tconf->resources, cconfs, connector_count, crtcs,
447                     &crtc_count, &config_valid);
448
449         if (config_valid == !(tconf->flags & TEST_INVALID))
450                 test_crtc_config(tconf, crtcs, crtc_count);
451
452         cleanup_crtcs(crtcs, crtc_count);
453 }
454
455 static int assign_crtc_to_connectors(const struct test_config *tconf,
456                                      int *crtc_idxs, int connector_count,
457                                      struct connector_config *cconfs)
458 {
459         unsigned long crtc_idx_mask;
460         int i;
461
462         crtc_idx_mask = 0;
463         for (i = 0; i < connector_count; i++) {
464                 int crtc_idx = crtc_idxs[i];
465
466                 if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) &&
467                     crtc_idx_mask & ~(1 << crtc_idx))
468                         return -1;
469
470                 if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) &&
471                     crtc_idx_mask & (1 << crtc_idx))
472                         return -1;
473
474                 crtc_idx_mask |= 1 << crtc_idx;
475
476                 cconfs[i].crtc_idx = crtc_idx;
477         }
478
479         return 0;
480 }
481
482 static int get_one_connector(drmModeRes *resources, int connector_id,
483                              struct connector_config *cconf)
484 {
485         drmModeConnector *connector;
486         drmModeModeInfo mode;
487
488         connector = drmModeGetConnector(drm_fd, connector_id);
489         assert(connector);
490         cconf->connector = connector;
491
492         cconf->connected = connector->connection == DRM_MODE_CONNECTED;
493
494         /*
495          * For DP/eDP we need a connected sink, since mode setting depends
496          * on successful link training and retrieved DPCD parameters.
497          */
498         switch (connector->connector_type) {
499         case DRM_MODE_CONNECTOR_DisplayPort:
500         case DRM_MODE_CONNECTOR_eDP:
501                 if (!cconf->connected) {
502                         drmModeFreeConnector(connector);
503                         return -1;
504                 }
505         }
506
507         if (cconf->connected) {
508                 if (kmstest_get_connector_default_mode(drm_fd, connector,
509                                                         &mode) < 0)
510                         mode = mode_640_480;
511         } else {
512                 mode = mode_640_480;
513         }
514
515         cconf->default_mode = mode;
516
517         return 0;
518 }
519
520 static int get_connectors(drmModeRes *resources, int *connector_idxs,
521                           int connector_count, struct connector_config *cconfs)
522 {
523         int i;
524
525         for (i = 0; i < connector_count; i++) {
526                 int connector_idx;
527                 int connector_id;
528
529                 connector_idx = connector_idxs[i];
530                 assert(connector_idx < resources->count_connectors);
531                 connector_id = resources->connectors[connector_idx];
532
533                 if (get_one_connector(resources, connector_id, &cconfs[i]) < 0)
534                         goto err;
535
536         }
537
538         return 0;
539
540 err:
541         while (i--)
542                 drmModeFreeConnector(cconfs[i].connector);
543
544         return -1;
545 }
546
547 static void free_connectors(struct connector_config *cconfs,
548                             int connector_count)
549 {
550         int i;
551
552         for (i = 0; i < connector_count; i++)
553                 drmModeFreeConnector(cconfs[i].connector);
554 }
555
556 struct combination {
557         int elems[MAX_COMBINATION_ELEMS];
558 };
559
560 struct combination_set {
561         int count;
562         struct combination items[MAX_COMBINATION_COUNT];
563 };
564
565 /*
566  * Get all possible selection of k elements from n elements with or without
567  * repetitions.
568  */
569 static void iterate_combinations(int n, int k, bool allow_repetitions,
570                                  int depth, int base, struct combination *comb,
571                                  struct combination_set *set)
572 {
573         int v;
574
575         if (!k) {
576                 assert(set->count < ARRAY_SIZE(set->items));
577                 set->items[set->count++] = *comb;
578                 return;
579         }
580
581         for (v = base; v < n; v++) {
582                 comb->elems[depth] = v;
583                 iterate_combinations(n, k - 1, allow_repetitions,
584                                      depth + 1, allow_repetitions ? 0 : v + 1,
585                                      comb, set);
586         }
587
588 }
589
590 static void get_combinations(int n, int k, bool allow_repetitions,
591                              struct combination_set *set)
592 {
593         struct combination comb;
594
595         assert(k <= ARRAY_SIZE(set->items[0].elems));
596         set->count = 0;
597         iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set);
598 }
599
600 static void test_combinations(const struct test_config *tconf,
601                               int connector_count)
602 {
603         struct combination_set connector_combs;
604         struct combination_set crtc_combs;
605         struct connector_config *cconfs;
606         int i;
607
608         get_combinations(tconf->resources->count_connectors, connector_count,
609                          false, &connector_combs);
610         get_combinations(tconf->resources->count_crtcs, connector_count,
611                          true, &crtc_combs);
612
613         printf("Testing: %s %d connector combinations\n", tconf->name,
614                 connector_count);
615         for (i = 0; i < connector_combs.count; i++) {
616                 int *connector_idxs;
617                 int ret;
618                 int j;
619
620                 cconfs = malloc(sizeof(*cconfs) * connector_count);
621                 assert(cconfs);
622
623                 connector_idxs = &connector_combs.items[i].elems[0];
624                 ret = get_connectors(tconf->resources, connector_idxs,
625                                      connector_count, cconfs);
626                 if (ret < 0)
627                         goto free_cconfs;
628
629                 for (j = 0; j < crtc_combs.count; j++) {
630                         int *crtc_idxs = &crtc_combs.items[j].elems[0];
631                         ret = assign_crtc_to_connectors(tconf, crtc_idxs,
632                                                         connector_count,
633                                                         cconfs);
634                         if (ret < 0)
635                                 continue;
636
637                         test_one_combination(tconf, cconfs, connector_count);
638                 }
639
640                 free_connectors(cconfs, connector_count);
641 free_cconfs:
642                 free(cconfs);
643         }
644 }
645
646 static void run_test(const struct test_config *tconf)
647 {
648         int connector_num;
649
650         connector_num = tconf->flags & TEST_CLONE ? 2 : 1;
651         for (; connector_num <= tconf->resources->count_crtcs; connector_num++)
652                 test_combinations(tconf, connector_num);
653 }
654
655 static int opt_handler(int opt, int opt_index)
656 {
657         switch (opt) {
658         case 'd':
659                 dry_run = true;
660                 break;
661         case 't':
662                 filter_test_id = atoi(optarg);
663                 break;
664         default:
665                 assert(0);
666         }
667
668         return 0;
669 }
670
671 int main(int argc, char **argv)
672 {
673         const struct {
674                 enum test_flags flags;
675                 const char *name;
676         } tests[] = {
677                 { TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
678                                         "invalid-clone-single-crtc" },
679                 { TEST_INVALID | TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
680                                         "invalid-clone-exclusive-crtc" },
681                 { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
682                                         "clone-exclusive-crtc" },
683         };
684         const char *help_str =
685                "  -d\t\tDon't run any test, only print what would be done. (still needs DRM access)\n"
686                "  -t <test id>\tRun only the test with this id.";
687         int i;
688         int ret;
689
690         ret = igt_subtest_init_parse_opts(argc, argv, "dt:", NULL, help_str,
691                                           opt_handler);
692         if (ret < 0)
693                 return ret == -1 ? 0 : ret;
694
695         igt_skip_on_simulation();
696
697         if (dry_run && filter_test_id) {
698                 fprintf(stderr, "only one of -d and -t is accepted\n");
699                 exit(1);
700         }
701
702         igt_fixture {
703                 drm_fd = drm_open_any();
704                 do_or_die(igt_set_vt_graphics_mode());
705
706                 drm_resources = drmModeGetResources(drm_fd);
707                 assert(drm_resources);
708         }
709
710         for (i = 0; i < ARRAY_SIZE(tests); i++) {
711                 igt_subtest(tests[i].name) {
712                         struct test_config tconf = {
713                                 .flags          = tests[i].flags,
714                                 .name           = tests[i].name,
715                                 .resources      = drm_resources,
716                         };
717                         run_test(&tconf);
718                 }
719         }
720
721         igt_fixture {
722                 drmModeFreeResources(drm_resources);
723
724                 close(drm_fd);
725         }
726
727         igt_exit();
728 }