File/dir renames
[android/external-libkmsxx.git] / utils / testpat.cpp
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 #include <regex>
5 #include <set>
7 #include "kms++.h"
8 #include "modedb.h"
10 #include "test.h"
11 #include "opts.h"
13 using namespace std;
14 using namespace kms;
16 struct PlaneInfo
17 {
18         Plane* plane;
20         unsigned x;
21         unsigned y;
22         unsigned w;
23         unsigned h;
25         DumbFramebuffer* fb;
26 };
28 struct OutputInfo
29 {
30         Connector* connector;
32         Crtc* crtc;
33         Videomode mode;
34         bool user_set_crtc;
35         DumbFramebuffer* fb;
37         vector<PlaneInfo> planes;
38 };
40 static bool s_use_dmt;
41 static bool s_use_cea;
43 static set<Crtc*> s_used_crtcs;
44 static set<Plane*> s_used_planes;
46 __attribute__ ((unused))
47 static void print_regex_match(smatch sm)
48 {
49         for (unsigned i = 0; i < sm.size(); ++i) {
50                 string str = sm[i].str();
51                 printf("%u: %s\n", i, str.c_str());
52         }
53 }
55 static void get_default_connector(Card& card, OutputInfo& output)
56 {
57         output.connector = card.get_first_connected_connector();
58         output.mode = output.connector->get_default_mode();
59 }
61 static void parse_connector(Card& card, const string& str, OutputInfo& output)
62 {
63         Connector* conn = nullptr;
65         auto connectors = card.get_connectors();
67         if (str[0] == '@') {
68                 char* endptr;
69                 unsigned idx = strtoul(str.c_str() + 1, &endptr, 10);
70                 if (*endptr == 0) {
71                         if (idx >= connectors.size())
72                                 EXIT("Bad connector number '%u'", idx);
74                         conn = connectors[idx];
75                 }
76         } else {
77                 char* endptr;
78                 unsigned id = strtoul(str.c_str(), &endptr, 10);
79                 if (*endptr == 0) {
80                         Connector* c = card.get_connector(id);
81                         if (!c)
82                                 EXIT("Bad connector id '%u'", id);
84                         conn = c;
85                 }
86         }
88         if (!conn) {
89                 auto iter = find_if(connectors.begin(), connectors.end(), [&str](Connector *c) { return c->fullname() == str; });
90                 if (iter != connectors.end())
91                         conn = *iter;
92         }
94         if (!conn)
95                 EXIT("No connector '%s'", str.c_str());
97         if (!conn->connected())
98                 EXIT("Connector '%s' not connected", conn->fullname().c_str());
100         output.connector = conn;
101         output.mode = output.connector->get_default_mode();
104 static void get_default_crtc(Card& card, OutputInfo& output)
106         Crtc* crtc = output.connector->get_current_crtc();
108         if (crtc && s_used_crtcs.find(crtc) == s_used_crtcs.end()) {
109                 s_used_crtcs.insert(crtc);
110                 output.crtc = crtc;
111                 return;
112         }
114         for (const auto& possible : output.connector->get_possible_crtcs()) {
115                 if (s_used_crtcs.find(possible) == s_used_crtcs.end()) {
116                         s_used_crtcs.insert(possible);
117                         output.crtc = possible;
118                         return;
119                 }
120         }
122         EXIT("Could not find available crtc");
125 static void parse_crtc(Card& card, const string& crtc_str, OutputInfo& output)
127         // @12:1920x1200@60
128         const regex mode_re("(?:(@?)(\\d+):)?(?:(\\d+)x(\\d+)(i)?)(?:@(\\d+))?");
130         smatch sm;
131         if (!regex_match(crtc_str, sm, mode_re))
132                 EXIT("Failed to parse crtc option '%s'", crtc_str.c_str());
134         if (sm[2].matched) {
135                 bool use_idx = sm[1].length() == 1;
136                 unsigned num = stoul(sm[2].str());
138                 if (use_idx) {
139                         auto crtcs = card.get_crtcs();
141                         if (num >= crtcs.size())
142                                 EXIT("Bad crtc number '%u'", num);
144                         output.crtc = crtcs[num];
145                 } else {
146                         Crtc* c = card.get_crtc(num);
147                         if (!c)
148                                 EXIT("Bad crtc id '%u'", num);
150                         output.crtc = c;
151                 }
152         } else {
153                 output.crtc = output.connector->get_current_crtc();
154         }
156         unsigned w = stoul(sm[3]);
157         unsigned h = stoul(sm[4]);
158         bool ilace = sm[5].matched ? true : false;
159         unsigned refresh = sm[6].matched ? stoul(sm[6]) : 0;
161         bool found_mode = false;
163         try {
164                 output.mode = output.connector->get_mode(w, h, refresh, ilace);
165                 found_mode = true;
166         } catch (exception& e) { }
168         if (!found_mode && s_use_dmt) {
169                 try {
170                         output.mode = find_dmt(w, h, refresh, ilace);
171                         found_mode = true;
172                         printf("Found mode from DMT\n");
173                 } catch (exception& e) { }
174         }
176         if (!found_mode && s_use_cea) {
177                 try {
178                         output.mode = find_cea(w, h, refresh, ilace);
179                         found_mode = true;
180                         printf("Found mode from CEA\n");
181                 } catch (exception& e) { }
182         }
184         if (!found_mode)
185                 throw invalid_argument("Mode not found");
188 static void parse_plane(Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo)
190         // 3:400,400-400x400
191         const regex plane_re("(?:(@?)(\\d+):)?(?:(\\d+),(\\d+)-)?(\\d+)x(\\d+)");
193         smatch sm;
194         if (!regex_match(plane_str, sm, plane_re))
195                 EXIT("Failed to parse plane option '%s'", plane_str.c_str());
197         if (sm[2].matched) {
198                 bool use_idx = sm[1].length() == 1;
199                 unsigned num = stoul(sm[2].str());
201                 if (use_idx) {
202                         auto planes = card.get_planes();
204                         if (num >= planes.size())
205                                 EXIT("Bad plane number '%u'", num);
207                         pinfo.plane = planes[num];
208                 } else {
209                         Plane* p = card.get_plane(num);
210                         if (!p)
211                                 EXIT("Bad plane id '%u'", num);
213                         pinfo.plane = p;
214                 }
215         } else {
216                 for (Plane* p : output.crtc->get_possible_planes()) {
217                         if (s_used_planes.find(p) != s_used_planes.end())
218                                 continue;
220                         if (p->plane_type() != PlaneType::Overlay)
221                                 continue;
223                         pinfo.plane = p;
224                 }
226                 if (!pinfo.plane)
227                         EXIT("Failed to find available plane");
228         }
230         s_used_planes.insert(pinfo.plane);
232         pinfo.w = stoul(sm[5]);
233         pinfo.h = stoul(sm[6]);
235         if (sm[3].matched)
236                 pinfo.x = stoul(sm[3]);
237         else
238                 pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2;
240         if (sm[4].matched)
241                 pinfo.y = stoul(sm[4]);
242         else
243                 pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2;
246 static DumbFramebuffer* get_default_fb(Card& card, unsigned width, unsigned height)
248         auto fb = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888);
249         draw_test_pattern(*fb);
250         return fb;
253 static DumbFramebuffer* parse_fb(Card& card, const string& fb_str, unsigned def_w, unsigned def_h)
255         unsigned w = def_w;
256         unsigned h = def_h;
257         PixelFormat format = PixelFormat::XRGB8888;
259         if (!fb_str.empty()) {
260                 // XXX the regexp is not quite correct
261                 // 400x400-NV12
262                 const regex fb_re("(?:(\\d+)x(\\d+))?(?:-)?(\\w\\w\\w\\w)?");
264                 smatch sm;
265                 if (!regex_match(fb_str, sm, fb_re))
266                         EXIT("Failed to parse fb option '%s'", fb_str.c_str());
268                 if (sm[1].matched)
269                         w = stoul(sm[1]);
270                 if (sm[2].matched)
271                         h = stoul(sm[2]);
272                 if (sm[3].matched)
273                         format = FourCCToPixelFormat(sm[3]);
274         }
276         auto fb = new DumbFramebuffer(card, w, h, format);
277         draw_test_pattern(*fb);
278         return fb;
281 static const char* usage_str =
282                 "Usage: testpat [OPTION]...\n\n"
283                 "Show a test pattern on a display or plane\n\n"
284                 "Options:\n"
285                 "      --device=DEVICE       DEVICE is the path to DRM card to open\n"
286                 "  -c, --connector=CONN      CONN is <connector>\n"
287                 "  -r, --crtc=CRTC           CRTC is [<crtc>:]<w>x<h>[@<Hz>]\n"
288                 "  -p, --plane=PLANE         PLANE is [<plane>:][<x>,<y>-]<w>x<h>\n"
289                 "  -f, --fb=FB               FB is [<w>x<h>][-][<4cc>]\n"
290                 "      --dmt                 Search for the given mode from DMT tables\n"
291                 "      --cea                 Search for the given mode from CEA tables\n"
292                 "\n"
293                 "<connector>, <crtc> and <plane> can be given by id (<id>) or index (@<idx>).\n"
294                 "<connector> can also be given by name.\n"
295                 "\n"
296                 "Options can be given multiple times to set up multiple displays or planes.\n"
297                 "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n"
298                 "an earlier option.\n"
299                 "If you omit parameters, testpat tries to guess what you mean\n"
300                 "\n"
301                 "Examples:\n"
302                 "\n"
303                 "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n"
304                 "    testpat -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n"
305                 "XR24 framebuffer on first connected connector in the default mode:\n"
306                 "    testpat -f XR24\n\n"
307                 "XR24 framebuffer on a 400x400 plane on the first connected connector in the default mode:\n"
308                 "    testpat -p 400x400 -f XR24\n\n"
309                 "Test pattern on the second connector with default mode:\n"
310                 "    testpat -c @1\n"
311                 ;
313 static void usage()
315         puts(usage_str);
318 enum class ObjectType
320         Connector,
321         Crtc,
322         Plane,
323         Framebuffer,
324 };
326 struct Arg
328         ObjectType type;
329         string arg;
330 };
332 static string s_device_path = "/dev/dri/card0";
334 static vector<Arg> parse_cmdline(int argc, char **argv)
336         vector<Arg> args;
338         OptionSet optionset = {
339                 Option("|device=",
340                 [&](string s)
341                 {
342                         s_device_path = s;
343                 }),
344                 Option("c|connector=",
345                 [&](string s)
346                 {
347                         args.push_back(Arg { ObjectType::Connector, s });
348                 }),
349                 Option("r|crtc=", [&](string s)
350                 {
351                         args.push_back(Arg { ObjectType::Crtc, s });
352                 }),
353                 Option("p|plane=", [&](string s)
354                 {
355                         args.push_back(Arg { ObjectType::Plane, s });
356                 }),
357                 Option("f|fb=", [&](string s)
358                 {
359                         args.push_back(Arg { ObjectType::Framebuffer, s });
360                 }),
361                 Option("|dmt", []()
362                 {
363                         s_use_dmt = true;
364                 }),
365                 Option("|cea", []()
366                 {
367                         s_use_cea = true;
368                 }),
369                 Option("h|help", [&]()
370                 {
371                         usage();
372                         exit(-1);
373                 }),
374         };
376         optionset.parse(argc, argv);
378         if (optionset.params().size() > 0) {
379                 usage();
380                 exit(-1);
381         }
383         return args;
386 static vector<OutputInfo> setups_to_outputs(Card& card, const vector<Arg>& output_args)
388         vector<OutputInfo> outputs;
390         if (output_args.size() == 0) {
391                 // no output args, show a pattern on all screens
392                 for (auto& pipe : card.get_connected_pipelines()) {
393                         OutputInfo output = { };
394                         output.connector = pipe.connector;
395                         output.crtc = pipe.crtc;
396                         output.mode = output.connector->get_default_mode();
398                         output.fb = get_default_fb(card, output.mode.hdisplay, output.mode.vdisplay);
400                         outputs.push_back(output);
401                 }
403                 return outputs;
404         }
406         OutputInfo* current_output = 0;
407         PlaneInfo* current_plane = 0;
409         for (auto& arg : output_args) {
410                 switch (arg.type) {
411                 case ObjectType::Connector:
412                 {
413                         outputs.push_back(OutputInfo { });
414                         current_output = &outputs.back();
416                         parse_connector(card, arg.arg, *current_output);
417                         current_plane = 0;
419                         break;
420                 }
422                 case ObjectType::Crtc:
423                 {
424                         if (!current_output) {
425                                 outputs.push_back(OutputInfo { });
426                                 current_output = &outputs.back();
427                         }
429                         if (!current_output->connector)
430                                 get_default_connector(card, *current_output);
432                         parse_crtc(card, arg.arg, *current_output);
434                         current_output->user_set_crtc = true;
436                         current_plane = 0;
438                         break;
439                 }
441                 case ObjectType::Plane:
442                 {
443                         if (!current_output) {
444                                 outputs.push_back(OutputInfo { });
445                                 current_output = &outputs.back();
446                         }
448                         if (!current_output->connector)
449                                 get_default_connector(card, *current_output);
451                         if (!current_output->crtc)
452                                 get_default_crtc(card, *current_output);
454                         current_output->planes.push_back(PlaneInfo { });
455                         current_plane = &current_output->planes.back();
457                         parse_plane(card, arg.arg, *current_output, *current_plane);
459                         break;
460                 }
462                 case ObjectType::Framebuffer:
463                 {
464                         if (!current_output) {
465                                 outputs.push_back(OutputInfo { });
466                                 current_output = &outputs.back();
467                         }
469                         if (!current_output->connector)
470                                 get_default_connector(card, *current_output);
472                         if (!current_output->crtc)
473                                 get_default_crtc(card, *current_output);
475                         int def_w, def_h;
477                         if (current_plane) {
478                                 def_w = current_plane->w;
479                                 def_h = current_plane->h;
480                         } else {
481                                 def_w = current_output->mode.hdisplay;
482                                 def_h = current_output->mode.vdisplay;
483                         }
485                         auto fb = parse_fb(card, arg.arg, def_w, def_h);
487                         if (current_plane)
488                                 current_plane->fb = fb;
489                         else
490                                 current_output->fb = fb;
492                         break;
493                 }
494                 }
495         }
497         // create default framebuffers if needed
498         for (OutputInfo& o : outputs) {
499                 if (!o.crtc) {
500                         get_default_crtc(card, *current_output);
501                         o.user_set_crtc = true;
502                 }
504                 if (!o.fb && o.user_set_crtc)
505                         o.fb = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay);
507                 for (PlaneInfo &p : o.planes) {
508                         if (!p.fb)
509                                 p.fb = get_default_fb(card, p.w, p.h);
510                 }
511         }
513         return outputs;
516 static std::string videomode_to_string(const Videomode& mode)
518         unsigned hfp = mode.hsync_start - mode.hdisplay;
519         unsigned hsw = mode.hsync_end - mode.hsync_start;
520         unsigned hbp = mode.htotal - mode.hsync_end;
522         unsigned vfp = mode.vsync_start - mode.vdisplay;
523         unsigned vsw = mode.vsync_end - mode.vsync_start;
524         unsigned vbp = mode.vtotal - mode.vsync_end;
526         float hz = (mode.clock * 1000.0) / (mode.htotal * mode.vtotal);
527         if (mode.flags & (1<<4)) // XXX interlace
528                 hz *= 2;
530         char buf[256];
532         sprintf(buf, "%.2f MHz %u/%u/%u/%u %u/%u/%u/%u %uHz (%.2fHz)",
533                 mode.clock / 1000.0,
534                 mode.hdisplay, hfp, hsw, hbp,
535                 mode.vdisplay, vfp, vsw, vbp,
536                 mode.vrefresh, hz);
538         return std::string(buf);
541 static void print_outputs(const vector<OutputInfo>& outputs)
543         for (unsigned i = 0; i < outputs.size(); ++i) {
544                 const OutputInfo& o = outputs[i];
546                 printf("Connector %u/@%u: %s\n", o.connector->id(), o.connector->idx(),
547                        o.connector->fullname().c_str());
548                 printf("  Crtc %u/@%u: %ux%u-%u (%s)\n", o.crtc->id(), o.crtc->idx(),
549                        o.mode.hdisplay, o.mode.vdisplay, o.mode.vrefresh,
550                        videomode_to_string(o.mode).c_str());
551                 if (o.fb)
552                         printf("    Fb %ux%u-%s\n", o.fb->width(), o.fb->height(),
553                                PixelFormatToFourCC(o.fb->format()).c_str());
555                 for (unsigned j = 0; j < o.planes.size(); ++j) {
556                         const PlaneInfo& p = o.planes[j];
557                         printf("  Plane %u/@%u: %u,%u-%ux%u\n", p.plane->id(), p.plane->idx(),
558                                p.x, p.y, p.w, p.h);
559                         printf("    Fb %ux%u-%s\n", p.fb->width(), p.fb->height(),
560                                PixelFormatToFourCC(p.fb->format()).c_str());
561                 }
562         }
565 static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
567         for (const OutputInfo& o : outputs) {
568                 auto conn = o.connector;
569                 auto crtc = o.crtc;
571                 if (o.fb) {
572                         int r = crtc->set_mode(conn, *o.fb, o.mode);
573                         if (r)
574                                 printf("crtc->set_mode() failed for crtc %u: %s\n",
575                                        crtc->id(), strerror(-r));
576                 }
578                 for (const PlaneInfo& p : o.planes) {
579                         int r = crtc->set_plane(p.plane, *p.fb,
580                                                 p.x, p.y, p.w, p.h,
581                                                 0, 0, p.fb->width(), p.fb->height());
582                         if (r)
583                                 printf("crtc->set_plane() failed for plane %u: %s\n",
584                                        p.plane->id(), strerror(-r));
585                 }
586         }
589 int main(int argc, char **argv)
591         vector<Arg> output_args = parse_cmdline(argc, argv);
593         Card card(s_device_path);
595         vector<OutputInfo> outputs = setups_to_outputs(card, output_args);
597         print_outputs(outputs);
599         set_crtcs_n_planes(card, outputs);
601         printf("press enter to exit\n");
603         getchar();