1*f0687c8aSRaman Tenneti #include <cstdio>
2*f0687c8aSRaman Tenneti #include <cstring>
3*f0687c8aSRaman Tenneti #include <algorithm>
4*f0687c8aSRaman Tenneti #include <regex>
5*f0687c8aSRaman Tenneti #include <set>
6*f0687c8aSRaman Tenneti #include <chrono>
7*f0687c8aSRaman Tenneti #include <cstdint>
8*f0687c8aSRaman Tenneti #include <cinttypes>
9*f0687c8aSRaman Tenneti
10*f0687c8aSRaman Tenneti #include <sys/select.h>
11*f0687c8aSRaman Tenneti
12*f0687c8aSRaman Tenneti #include <fmt/format.h>
13*f0687c8aSRaman Tenneti
14*f0687c8aSRaman Tenneti #include <kms++/kms++.h>
15*f0687c8aSRaman Tenneti #include <kms++/modedb.h>
16*f0687c8aSRaman Tenneti #include <kms++/mode_cvt.h>
17*f0687c8aSRaman Tenneti
18*f0687c8aSRaman Tenneti #include <kms++util/kms++util.h>
19*f0687c8aSRaman Tenneti
20*f0687c8aSRaman Tenneti using namespace std;
21*f0687c8aSRaman Tenneti using namespace kms;
22*f0687c8aSRaman Tenneti
23*f0687c8aSRaman Tenneti struct PropInfo {
PropInfoPropInfo24*f0687c8aSRaman Tenneti PropInfo(string n, uint64_t v)
25*f0687c8aSRaman Tenneti : prop(NULL), name(n), val(v) {}
26*f0687c8aSRaman Tenneti
27*f0687c8aSRaman Tenneti Property* prop;
28*f0687c8aSRaman Tenneti string name;
29*f0687c8aSRaman Tenneti uint64_t val;
30*f0687c8aSRaman Tenneti };
31*f0687c8aSRaman Tenneti
32*f0687c8aSRaman Tenneti struct PlaneInfo {
33*f0687c8aSRaman Tenneti Plane* plane;
34*f0687c8aSRaman Tenneti
35*f0687c8aSRaman Tenneti unsigned x;
36*f0687c8aSRaman Tenneti unsigned y;
37*f0687c8aSRaman Tenneti unsigned w;
38*f0687c8aSRaman Tenneti unsigned h;
39*f0687c8aSRaman Tenneti
40*f0687c8aSRaman Tenneti unsigned view_x;
41*f0687c8aSRaman Tenneti unsigned view_y;
42*f0687c8aSRaman Tenneti unsigned view_w;
43*f0687c8aSRaman Tenneti unsigned view_h;
44*f0687c8aSRaman Tenneti
45*f0687c8aSRaman Tenneti vector<Framebuffer*> fbs;
46*f0687c8aSRaman Tenneti
47*f0687c8aSRaman Tenneti vector<PropInfo> props;
48*f0687c8aSRaman Tenneti };
49*f0687c8aSRaman Tenneti
50*f0687c8aSRaman Tenneti struct OutputInfo {
51*f0687c8aSRaman Tenneti Connector* connector;
52*f0687c8aSRaman Tenneti
53*f0687c8aSRaman Tenneti Crtc* crtc;
54*f0687c8aSRaman Tenneti Videomode mode;
55*f0687c8aSRaman Tenneti vector<Framebuffer*> legacy_fbs;
56*f0687c8aSRaman Tenneti
57*f0687c8aSRaman Tenneti vector<PlaneInfo> planes;
58*f0687c8aSRaman Tenneti
59*f0687c8aSRaman Tenneti vector<PropInfo> conn_props;
60*f0687c8aSRaman Tenneti vector<PropInfo> crtc_props;
61*f0687c8aSRaman Tenneti };
62*f0687c8aSRaman Tenneti
63*f0687c8aSRaman Tenneti static bool s_use_dmt;
64*f0687c8aSRaman Tenneti static bool s_use_cea;
65*f0687c8aSRaman Tenneti static unsigned s_num_buffers = 1;
66*f0687c8aSRaman Tenneti static bool s_flip_mode;
67*f0687c8aSRaman Tenneti static bool s_flip_sync;
68*f0687c8aSRaman Tenneti static bool s_cvt;
69*f0687c8aSRaman Tenneti static bool s_cvt_v2;
70*f0687c8aSRaman Tenneti static bool s_cvt_vid_opt;
71*f0687c8aSRaman Tenneti static unsigned s_max_flips;
72*f0687c8aSRaman Tenneti static bool s_print_crc;
73*f0687c8aSRaman Tenneti
print_regex_match(smatch sm)74*f0687c8aSRaman Tenneti __attribute__((unused)) static void print_regex_match(smatch sm)
75*f0687c8aSRaman Tenneti {
76*f0687c8aSRaman Tenneti for (unsigned i = 0; i < sm.size(); ++i) {
77*f0687c8aSRaman Tenneti string str = sm[i].str();
78*f0687c8aSRaman Tenneti fmt::print("{}: {}\n", i, str);
79*f0687c8aSRaman Tenneti }
80*f0687c8aSRaman Tenneti }
81*f0687c8aSRaman Tenneti
get_connector(ResourceManager & resman,OutputInfo & output,const string & str="")82*f0687c8aSRaman Tenneti static void get_connector(ResourceManager& resman, OutputInfo& output, const string& str = "")
83*f0687c8aSRaman Tenneti {
84*f0687c8aSRaman Tenneti Connector* conn = resman.reserve_connector(str);
85*f0687c8aSRaman Tenneti
86*f0687c8aSRaman Tenneti if (!conn)
87*f0687c8aSRaman Tenneti EXIT("No connector '%s'", str.c_str());
88*f0687c8aSRaman Tenneti
89*f0687c8aSRaman Tenneti output.connector = conn;
90*f0687c8aSRaman Tenneti output.mode = output.connector->get_default_mode();
91*f0687c8aSRaman Tenneti }
92*f0687c8aSRaman Tenneti
get_default_crtc(ResourceManager & resman,OutputInfo & output)93*f0687c8aSRaman Tenneti static void get_default_crtc(ResourceManager& resman, OutputInfo& output)
94*f0687c8aSRaman Tenneti {
95*f0687c8aSRaman Tenneti output.crtc = resman.reserve_crtc(output.connector);
96*f0687c8aSRaman Tenneti
97*f0687c8aSRaman Tenneti if (!output.crtc)
98*f0687c8aSRaman Tenneti EXIT("Could not find available crtc");
99*f0687c8aSRaman Tenneti }
100*f0687c8aSRaman Tenneti
add_default_planeinfo(OutputInfo * output)101*f0687c8aSRaman Tenneti static PlaneInfo* add_default_planeinfo(OutputInfo* output)
102*f0687c8aSRaman Tenneti {
103*f0687c8aSRaman Tenneti output->planes.push_back(PlaneInfo{});
104*f0687c8aSRaman Tenneti PlaneInfo* ret = &output->planes.back();
105*f0687c8aSRaman Tenneti ret->w = output->mode.hdisplay;
106*f0687c8aSRaman Tenneti ret->h = output->mode.vdisplay;
107*f0687c8aSRaman Tenneti return ret;
108*f0687c8aSRaman Tenneti }
109*f0687c8aSRaman Tenneti
parse_crtc(ResourceManager & resman,Card & card,const string & crtc_str,OutputInfo & output)110*f0687c8aSRaman Tenneti static void parse_crtc(ResourceManager& resman, Card& card, const string& crtc_str, OutputInfo& output)
111*f0687c8aSRaman Tenneti {
112*f0687c8aSRaman Tenneti // @12:1920x1200i@60
113*f0687c8aSRaman Tenneti // @12:33000000,800/210/30/16/-,480/22/13/10/-,i
114*f0687c8aSRaman Tenneti
115*f0687c8aSRaman Tenneti const regex modename_re("(?:(@?)(\\d+):)?" // @12:
116*f0687c8aSRaman Tenneti "(?:(\\d+)x(\\d+)(i)?)" // 1920x1200i
117*f0687c8aSRaman Tenneti "(?:@([\\d\\.]+))?"); // @60
118*f0687c8aSRaman Tenneti
119*f0687c8aSRaman Tenneti const regex modeline_re("(?:(@?)(\\d+):)?" // @12:
120*f0687c8aSRaman Tenneti "(\\d+)," // 33000000,
121*f0687c8aSRaman Tenneti "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-])," // 800/210/30/16/-,
122*f0687c8aSRaman Tenneti "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-])" // 480/22/13/10/-
123*f0687c8aSRaman Tenneti "(?:,([i]+))?" // ,i
124*f0687c8aSRaman Tenneti );
125*f0687c8aSRaman Tenneti
126*f0687c8aSRaman Tenneti smatch sm;
127*f0687c8aSRaman Tenneti if (regex_match(crtc_str, sm, modename_re)) {
128*f0687c8aSRaman Tenneti if (sm[2].matched) {
129*f0687c8aSRaman Tenneti bool use_id = sm[1].length() == 1;
130*f0687c8aSRaman Tenneti unsigned num = stoul(sm[2].str());
131*f0687c8aSRaman Tenneti
132*f0687c8aSRaman Tenneti if (use_id) {
133*f0687c8aSRaman Tenneti Crtc* c = card.get_crtc(num);
134*f0687c8aSRaman Tenneti if (!c)
135*f0687c8aSRaman Tenneti EXIT("Bad crtc id '%u'", num);
136*f0687c8aSRaman Tenneti
137*f0687c8aSRaman Tenneti output.crtc = c;
138*f0687c8aSRaman Tenneti } else {
139*f0687c8aSRaman Tenneti auto crtcs = card.get_crtcs();
140*f0687c8aSRaman Tenneti
141*f0687c8aSRaman Tenneti if (num >= crtcs.size())
142*f0687c8aSRaman Tenneti EXIT("Bad crtc number '%u'", num);
143*f0687c8aSRaman Tenneti
144*f0687c8aSRaman Tenneti output.crtc = crtcs[num];
145*f0687c8aSRaman Tenneti }
146*f0687c8aSRaman Tenneti } else {
147*f0687c8aSRaman Tenneti output.crtc = output.connector->get_current_crtc();
148*f0687c8aSRaman Tenneti }
149*f0687c8aSRaman Tenneti
150*f0687c8aSRaman Tenneti unsigned w = stoul(sm[3]);
151*f0687c8aSRaman Tenneti unsigned h = stoul(sm[4]);
152*f0687c8aSRaman Tenneti bool ilace = sm[5].matched ? true : false;
153*f0687c8aSRaman Tenneti float refresh = sm[6].matched ? stof(sm[6]) : 0;
154*f0687c8aSRaman Tenneti
155*f0687c8aSRaman Tenneti if (s_cvt) {
156*f0687c8aSRaman Tenneti output.mode = videomode_from_cvt(w, h, refresh, ilace, s_cvt_v2, s_cvt_vid_opt);
157*f0687c8aSRaman Tenneti } else if (s_use_dmt) {
158*f0687c8aSRaman Tenneti try {
159*f0687c8aSRaman Tenneti output.mode = find_dmt(w, h, refresh, ilace);
160*f0687c8aSRaman Tenneti } catch (exception& e) {
161*f0687c8aSRaman Tenneti EXIT("Mode not found from DMT tables\n");
162*f0687c8aSRaman Tenneti }
163*f0687c8aSRaman Tenneti } else if (s_use_cea) {
164*f0687c8aSRaman Tenneti try {
165*f0687c8aSRaman Tenneti output.mode = find_cea(w, h, refresh, ilace);
166*f0687c8aSRaman Tenneti } catch (exception& e) {
167*f0687c8aSRaman Tenneti EXIT("Mode not found from CEA tables\n");
168*f0687c8aSRaman Tenneti }
169*f0687c8aSRaman Tenneti } else {
170*f0687c8aSRaman Tenneti try {
171*f0687c8aSRaman Tenneti output.mode = output.connector->get_mode(w, h, refresh, ilace);
172*f0687c8aSRaman Tenneti } catch (exception& e) {
173*f0687c8aSRaman Tenneti EXIT("Mode not found from the connector\n");
174*f0687c8aSRaman Tenneti }
175*f0687c8aSRaman Tenneti }
176*f0687c8aSRaman Tenneti } else if (regex_match(crtc_str, sm, modeline_re)) {
177*f0687c8aSRaman Tenneti if (sm[2].matched) {
178*f0687c8aSRaman Tenneti bool use_id = sm[1].length() == 1;
179*f0687c8aSRaman Tenneti unsigned num = stoul(sm[2].str());
180*f0687c8aSRaman Tenneti
181*f0687c8aSRaman Tenneti if (use_id) {
182*f0687c8aSRaman Tenneti Crtc* c = card.get_crtc(num);
183*f0687c8aSRaman Tenneti if (!c)
184*f0687c8aSRaman Tenneti EXIT("Bad crtc id '%u'", num);
185*f0687c8aSRaman Tenneti
186*f0687c8aSRaman Tenneti output.crtc = c;
187*f0687c8aSRaman Tenneti } else {
188*f0687c8aSRaman Tenneti auto crtcs = card.get_crtcs();
189*f0687c8aSRaman Tenneti
190*f0687c8aSRaman Tenneti if (num >= crtcs.size())
191*f0687c8aSRaman Tenneti EXIT("Bad crtc number '%u'", num);
192*f0687c8aSRaman Tenneti
193*f0687c8aSRaman Tenneti output.crtc = crtcs[num];
194*f0687c8aSRaman Tenneti }
195*f0687c8aSRaman Tenneti } else {
196*f0687c8aSRaman Tenneti output.crtc = output.connector->get_current_crtc();
197*f0687c8aSRaman Tenneti }
198*f0687c8aSRaman Tenneti
199*f0687c8aSRaman Tenneti unsigned clock = stoul(sm[3]);
200*f0687c8aSRaman Tenneti
201*f0687c8aSRaman Tenneti unsigned hact = stoul(sm[4]);
202*f0687c8aSRaman Tenneti unsigned hfp = stoul(sm[5]);
203*f0687c8aSRaman Tenneti unsigned hsw = stoul(sm[6]);
204*f0687c8aSRaman Tenneti unsigned hbp = stoul(sm[7]);
205*f0687c8aSRaman Tenneti bool h_pos_sync = sm[8] == "+" ? true : false;
206*f0687c8aSRaman Tenneti
207*f0687c8aSRaman Tenneti unsigned vact = stoul(sm[9]);
208*f0687c8aSRaman Tenneti unsigned vfp = stoul(sm[10]);
209*f0687c8aSRaman Tenneti unsigned vsw = stoul(sm[11]);
210*f0687c8aSRaman Tenneti unsigned vbp = stoul(sm[12]);
211*f0687c8aSRaman Tenneti bool v_pos_sync = sm[13] == "+" ? true : false;
212*f0687c8aSRaman Tenneti
213*f0687c8aSRaman Tenneti output.mode = videomode_from_timings(clock / 1000, hact, hfp, hsw, hbp, vact, vfp, vsw, vbp);
214*f0687c8aSRaman Tenneti output.mode.set_hsync(h_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative);
215*f0687c8aSRaman Tenneti output.mode.set_vsync(v_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative);
216*f0687c8aSRaman Tenneti
217*f0687c8aSRaman Tenneti if (sm[14].matched) {
218*f0687c8aSRaman Tenneti for (int i = 0; i < sm[14].length(); ++i) {
219*f0687c8aSRaman Tenneti char f = string(sm[14])[i];
220*f0687c8aSRaman Tenneti
221*f0687c8aSRaman Tenneti switch (f) {
222*f0687c8aSRaman Tenneti case 'i':
223*f0687c8aSRaman Tenneti output.mode.set_interlace(true);
224*f0687c8aSRaman Tenneti break;
225*f0687c8aSRaman Tenneti default:
226*f0687c8aSRaman Tenneti EXIT("Bad mode flag %c", f);
227*f0687c8aSRaman Tenneti }
228*f0687c8aSRaman Tenneti }
229*f0687c8aSRaman Tenneti }
230*f0687c8aSRaman Tenneti } else {
231*f0687c8aSRaman Tenneti EXIT("Failed to parse crtc option '%s'", crtc_str.c_str());
232*f0687c8aSRaman Tenneti }
233*f0687c8aSRaman Tenneti
234*f0687c8aSRaman Tenneti if (output.crtc)
235*f0687c8aSRaman Tenneti output.crtc = resman.reserve_crtc(output.crtc);
236*f0687c8aSRaman Tenneti else
237*f0687c8aSRaman Tenneti output.crtc = resman.reserve_crtc(output.connector);
238*f0687c8aSRaman Tenneti
239*f0687c8aSRaman Tenneti if (!output.crtc)
240*f0687c8aSRaman Tenneti EXIT("Could not find available crtc");
241*f0687c8aSRaman Tenneti }
242*f0687c8aSRaman Tenneti
parse_plane(ResourceManager & resman,Card & card,const string & plane_str,const OutputInfo & output,PlaneInfo & pinfo)243*f0687c8aSRaman Tenneti static void parse_plane(ResourceManager& resman, Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo)
244*f0687c8aSRaman Tenneti {
245*f0687c8aSRaman Tenneti // 3:400,400-400x400
246*f0687c8aSRaman Tenneti const regex plane_re("(?:(@?)(\\d+):)?" // 3:
247*f0687c8aSRaman Tenneti "(?:(\\d+),(\\d+)-)?" // 400,400-
248*f0687c8aSRaman Tenneti "(\\d+)x(\\d+)"); // 400x400
249*f0687c8aSRaman Tenneti
250*f0687c8aSRaman Tenneti smatch sm;
251*f0687c8aSRaman Tenneti if (!regex_match(plane_str, sm, plane_re))
252*f0687c8aSRaman Tenneti EXIT("Failed to parse plane option '%s'", plane_str.c_str());
253*f0687c8aSRaman Tenneti
254*f0687c8aSRaman Tenneti if (sm[2].matched) {
255*f0687c8aSRaman Tenneti bool use_id = sm[1].length() == 1;
256*f0687c8aSRaman Tenneti unsigned num = stoul(sm[2].str());
257*f0687c8aSRaman Tenneti
258*f0687c8aSRaman Tenneti if (use_id) {
259*f0687c8aSRaman Tenneti Plane* p = card.get_plane(num);
260*f0687c8aSRaman Tenneti if (!p)
261*f0687c8aSRaman Tenneti EXIT("Bad plane id '%u'", num);
262*f0687c8aSRaman Tenneti
263*f0687c8aSRaman Tenneti pinfo.plane = p;
264*f0687c8aSRaman Tenneti } else {
265*f0687c8aSRaman Tenneti auto planes = card.get_planes();
266*f0687c8aSRaman Tenneti
267*f0687c8aSRaman Tenneti if (num >= planes.size())
268*f0687c8aSRaman Tenneti EXIT("Bad plane number '%u'", num);
269*f0687c8aSRaman Tenneti
270*f0687c8aSRaman Tenneti pinfo.plane = planes[num];
271*f0687c8aSRaman Tenneti }
272*f0687c8aSRaman Tenneti
273*f0687c8aSRaman Tenneti auto plane = resman.reserve_plane(pinfo.plane);
274*f0687c8aSRaman Tenneti if (!plane)
275*f0687c8aSRaman Tenneti EXIT("Plane id %u is not available", pinfo.plane->id());
276*f0687c8aSRaman Tenneti }
277*f0687c8aSRaman Tenneti
278*f0687c8aSRaman Tenneti pinfo.w = stoul(sm[5]);
279*f0687c8aSRaman Tenneti pinfo.h = stoul(sm[6]);
280*f0687c8aSRaman Tenneti
281*f0687c8aSRaman Tenneti if (sm[3].matched)
282*f0687c8aSRaman Tenneti pinfo.x = stoul(sm[3]);
283*f0687c8aSRaman Tenneti else
284*f0687c8aSRaman Tenneti pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2;
285*f0687c8aSRaman Tenneti
286*f0687c8aSRaman Tenneti if (sm[4].matched)
287*f0687c8aSRaman Tenneti pinfo.y = stoul(sm[4]);
288*f0687c8aSRaman Tenneti else
289*f0687c8aSRaman Tenneti pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2;
290*f0687c8aSRaman Tenneti }
291*f0687c8aSRaman Tenneti
parse_prop(const string & prop_str,vector<PropInfo> & props)292*f0687c8aSRaman Tenneti static void parse_prop(const string& prop_str, vector<PropInfo>& props)
293*f0687c8aSRaman Tenneti {
294*f0687c8aSRaman Tenneti string name, val;
295*f0687c8aSRaman Tenneti
296*f0687c8aSRaman Tenneti size_t split = prop_str.find("=");
297*f0687c8aSRaman Tenneti
298*f0687c8aSRaman Tenneti if (split == string::npos)
299*f0687c8aSRaman Tenneti EXIT("Equal sign ('=') not found in %s", prop_str.c_str());
300*f0687c8aSRaman Tenneti
301*f0687c8aSRaman Tenneti name = prop_str.substr(0, split);
302*f0687c8aSRaman Tenneti val = prop_str.substr(split + 1);
303*f0687c8aSRaman Tenneti
304*f0687c8aSRaman Tenneti props.push_back(PropInfo(name, stoull(val, 0, 0)));
305*f0687c8aSRaman Tenneti }
306*f0687c8aSRaman Tenneti
get_props(Card & card,vector<PropInfo> & props,const DrmPropObject * propobj)307*f0687c8aSRaman Tenneti static void get_props(Card& card, vector<PropInfo>& props, const DrmPropObject* propobj)
308*f0687c8aSRaman Tenneti {
309*f0687c8aSRaman Tenneti for (auto& pi : props)
310*f0687c8aSRaman Tenneti pi.prop = propobj->get_prop(pi.name);
311*f0687c8aSRaman Tenneti }
312*f0687c8aSRaman Tenneti
get_default_fb(Card & card,unsigned width,unsigned height)313*f0687c8aSRaman Tenneti static vector<Framebuffer*> get_default_fb(Card& card, unsigned width, unsigned height)
314*f0687c8aSRaman Tenneti {
315*f0687c8aSRaman Tenneti vector<Framebuffer*> v;
316*f0687c8aSRaman Tenneti
317*f0687c8aSRaman Tenneti for (unsigned i = 0; i < s_num_buffers; ++i)
318*f0687c8aSRaman Tenneti v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888));
319*f0687c8aSRaman Tenneti
320*f0687c8aSRaman Tenneti return v;
321*f0687c8aSRaman Tenneti }
322*f0687c8aSRaman Tenneti
parse_fb(Card & card,const string & fb_str,OutputInfo * output,PlaneInfo * pinfo)323*f0687c8aSRaman Tenneti static void parse_fb(Card& card, const string& fb_str, OutputInfo* output, PlaneInfo* pinfo)
324*f0687c8aSRaman Tenneti {
325*f0687c8aSRaman Tenneti unsigned w, h;
326*f0687c8aSRaman Tenneti PixelFormat format = PixelFormat::XRGB8888;
327*f0687c8aSRaman Tenneti
328*f0687c8aSRaman Tenneti if (pinfo) {
329*f0687c8aSRaman Tenneti w = pinfo->w;
330*f0687c8aSRaman Tenneti h = pinfo->h;
331*f0687c8aSRaman Tenneti } else {
332*f0687c8aSRaman Tenneti w = output->mode.hdisplay;
333*f0687c8aSRaman Tenneti h = output->mode.vdisplay;
334*f0687c8aSRaman Tenneti }
335*f0687c8aSRaman Tenneti
336*f0687c8aSRaman Tenneti if (!fb_str.empty()) {
337*f0687c8aSRaman Tenneti // XXX the regexp is not quite correct
338*f0687c8aSRaman Tenneti // 400x400-NV12
339*f0687c8aSRaman Tenneti const regex fb_re("(?:(\\d+)x(\\d+))?" // 400x400
340*f0687c8aSRaman Tenneti "(?:-)?" // -
341*f0687c8aSRaman Tenneti "(\\w\\w\\w\\w)?"); // NV12
342*f0687c8aSRaman Tenneti
343*f0687c8aSRaman Tenneti smatch sm;
344*f0687c8aSRaman Tenneti if (!regex_match(fb_str, sm, fb_re))
345*f0687c8aSRaman Tenneti EXIT("Failed to parse fb option '%s'", fb_str.c_str());
346*f0687c8aSRaman Tenneti
347*f0687c8aSRaman Tenneti if (sm[1].matched)
348*f0687c8aSRaman Tenneti w = stoul(sm[1]);
349*f0687c8aSRaman Tenneti if (sm[2].matched)
350*f0687c8aSRaman Tenneti h = stoul(sm[2]);
351*f0687c8aSRaman Tenneti if (sm[3].matched)
352*f0687c8aSRaman Tenneti format = FourCCToPixelFormat(sm[3]);
353*f0687c8aSRaman Tenneti }
354*f0687c8aSRaman Tenneti
355*f0687c8aSRaman Tenneti vector<Framebuffer*> v;
356*f0687c8aSRaman Tenneti
357*f0687c8aSRaman Tenneti for (unsigned i = 0; i < s_num_buffers; ++i)
358*f0687c8aSRaman Tenneti v.push_back(new DumbFramebuffer(card, w, h, format));
359*f0687c8aSRaman Tenneti
360*f0687c8aSRaman Tenneti if (pinfo)
361*f0687c8aSRaman Tenneti pinfo->fbs = v;
362*f0687c8aSRaman Tenneti else
363*f0687c8aSRaman Tenneti output->legacy_fbs = v;
364*f0687c8aSRaman Tenneti }
365*f0687c8aSRaman Tenneti
parse_view(const string & view_str,PlaneInfo & pinfo)366*f0687c8aSRaman Tenneti static void parse_view(const string& view_str, PlaneInfo& pinfo)
367*f0687c8aSRaman Tenneti {
368*f0687c8aSRaman Tenneti const regex view_re("(\\d+),(\\d+)-(\\d+)x(\\d+)"); // 400,400-400x400
369*f0687c8aSRaman Tenneti
370*f0687c8aSRaman Tenneti smatch sm;
371*f0687c8aSRaman Tenneti if (!regex_match(view_str, sm, view_re))
372*f0687c8aSRaman Tenneti EXIT("Failed to parse view option '%s'", view_str.c_str());
373*f0687c8aSRaman Tenneti
374*f0687c8aSRaman Tenneti pinfo.view_x = stoul(sm[1]);
375*f0687c8aSRaman Tenneti pinfo.view_y = stoul(sm[2]);
376*f0687c8aSRaman Tenneti pinfo.view_w = stoul(sm[3]);
377*f0687c8aSRaman Tenneti pinfo.view_h = stoul(sm[4]);
378*f0687c8aSRaman Tenneti }
379*f0687c8aSRaman Tenneti
380*f0687c8aSRaman Tenneti static const char* usage_str =
381*f0687c8aSRaman Tenneti "Usage: kmstest [OPTION]...\n\n"
382*f0687c8aSRaman Tenneti "Show a test pattern on a display or plane\n\n"
383*f0687c8aSRaman Tenneti "Options:\n"
384*f0687c8aSRaman Tenneti " --device=DEVICE DEVICE is the path to DRM card to open\n"
385*f0687c8aSRaman Tenneti " -c, --connector=CONN CONN is <connector>\n"
386*f0687c8aSRaman Tenneti " -r, --crtc=CRTC CRTC is [<crtc>:]<w>x<h>[@<Hz>]\n"
387*f0687c8aSRaman Tenneti " or\n"
388*f0687c8aSRaman Tenneti " [<crtc>:]<pclk>,<hact>/<hfp>/<hsw>/<hbp>/<hsp>,<vact>/<vfp>/<vsw>/<vbp>/<vsp>[,i]\n"
389*f0687c8aSRaman Tenneti " -p, --plane=PLANE PLANE is [<plane>:][<x>,<y>-]<w>x<h>\n"
390*f0687c8aSRaman Tenneti " -f, --fb=FB FB is [<w>x<h>][-][<4cc>]\n"
391*f0687c8aSRaman Tenneti " -v, --view=VIEW VIEW is <x>,<y>-<w>x<h>\n"
392*f0687c8aSRaman Tenneti " -P, --property=PROP=VAL Set PROP to VAL in the previous DRM object\n"
393*f0687c8aSRaman Tenneti " --dmt Search for the given mode from DMT tables\n"
394*f0687c8aSRaman Tenneti " --cea Search for the given mode from CEA tables\n"
395*f0687c8aSRaman Tenneti " --cvt=CVT Create videomode with CVT. CVT is 'v1', 'v2' or 'v2o'\n"
396*f0687c8aSRaman Tenneti " --flip[=max] Do page flipping for each output with an optional maximum flips count\n"
397*f0687c8aSRaman Tenneti " --sync Synchronize page flipping\n"
398*f0687c8aSRaman Tenneti " --crc Print CRC16 for framebuffer contents\n"
399*f0687c8aSRaman Tenneti "\n"
400*f0687c8aSRaman Tenneti "<connector>, <crtc> and <plane> can be given by index (<idx>) or id (@<id>).\n"
401*f0687c8aSRaman Tenneti "<connector> can also be given by name.\n"
402*f0687c8aSRaman Tenneti "\n"
403*f0687c8aSRaman Tenneti "Options can be given multiple times to set up multiple displays or planes.\n"
404*f0687c8aSRaman Tenneti "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n"
405*f0687c8aSRaman Tenneti "an earlier option.\n"
406*f0687c8aSRaman Tenneti "If you omit parameters, kmstest tries to guess what you mean\n"
407*f0687c8aSRaman Tenneti "\n"
408*f0687c8aSRaman Tenneti "Examples:\n"
409*f0687c8aSRaman Tenneti "\n"
410*f0687c8aSRaman Tenneti "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n"
411*f0687c8aSRaman Tenneti " kmstest -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n"
412*f0687c8aSRaman Tenneti "XR24 framebuffer on first connected connector in the default mode:\n"
413*f0687c8aSRaman Tenneti " kmstest -f XR24\n\n"
414*f0687c8aSRaman Tenneti "XR24 framebuffer on a 400x400 plane on the first connected connector in the default mode:\n"
415*f0687c8aSRaman Tenneti " kmstest -p 400x400 -f XR24\n\n"
416*f0687c8aSRaman Tenneti "Test pattern on the second connector with default mode:\n"
417*f0687c8aSRaman Tenneti " kmstest -c 1\n"
418*f0687c8aSRaman Tenneti "\n"
419*f0687c8aSRaman Tenneti "Environmental variables:\n"
420*f0687c8aSRaman Tenneti " KMSXX_DISABLE_UNIVERSAL_PLANES Don't enable universal planes even if available\n"
421*f0687c8aSRaman Tenneti " KMSXX_DISABLE_ATOMIC Don't enable atomic modesetting even if available\n";
422*f0687c8aSRaman Tenneti
usage()423*f0687c8aSRaman Tenneti static void usage()
424*f0687c8aSRaman Tenneti {
425*f0687c8aSRaman Tenneti puts(usage_str);
426*f0687c8aSRaman Tenneti }
427*f0687c8aSRaman Tenneti
428*f0687c8aSRaman Tenneti enum class ArgType {
429*f0687c8aSRaman Tenneti Connector,
430*f0687c8aSRaman Tenneti Crtc,
431*f0687c8aSRaman Tenneti Plane,
432*f0687c8aSRaman Tenneti Framebuffer,
433*f0687c8aSRaman Tenneti View,
434*f0687c8aSRaman Tenneti Property,
435*f0687c8aSRaman Tenneti };
436*f0687c8aSRaman Tenneti
437*f0687c8aSRaman Tenneti struct Arg {
438*f0687c8aSRaman Tenneti ArgType type;
439*f0687c8aSRaman Tenneti string arg;
440*f0687c8aSRaman Tenneti };
441*f0687c8aSRaman Tenneti
442*f0687c8aSRaman Tenneti static string s_device_path;
443*f0687c8aSRaman Tenneti
parse_cmdline(int argc,char ** argv)444*f0687c8aSRaman Tenneti static vector<Arg> parse_cmdline(int argc, char** argv)
445*f0687c8aSRaman Tenneti {
446*f0687c8aSRaman Tenneti vector<Arg> args;
447*f0687c8aSRaman Tenneti
448*f0687c8aSRaman Tenneti OptionSet optionset = {
449*f0687c8aSRaman Tenneti Option("|device=",
450*f0687c8aSRaman Tenneti [&](string s) {
451*f0687c8aSRaman Tenneti s_device_path = s;
452*f0687c8aSRaman Tenneti }),
453*f0687c8aSRaman Tenneti Option("c|connector=",
454*f0687c8aSRaman Tenneti [&](string s) {
455*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::Connector, s });
456*f0687c8aSRaman Tenneti }),
457*f0687c8aSRaman Tenneti Option("r|crtc=", [&](string s) {
458*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::Crtc, s });
459*f0687c8aSRaman Tenneti }),
460*f0687c8aSRaman Tenneti Option("p|plane=", [&](string s) {
461*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::Plane, s });
462*f0687c8aSRaman Tenneti }),
463*f0687c8aSRaman Tenneti Option("f|fb=", [&](string s) {
464*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::Framebuffer, s });
465*f0687c8aSRaman Tenneti }),
466*f0687c8aSRaman Tenneti Option("v|view=", [&](string s) {
467*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::View, s });
468*f0687c8aSRaman Tenneti }),
469*f0687c8aSRaman Tenneti Option("P|property=", [&](string s) {
470*f0687c8aSRaman Tenneti args.push_back(Arg{ ArgType::Property, s });
471*f0687c8aSRaman Tenneti }),
472*f0687c8aSRaman Tenneti Option("|dmt", []() {
473*f0687c8aSRaman Tenneti s_use_dmt = true;
474*f0687c8aSRaman Tenneti }),
475*f0687c8aSRaman Tenneti Option("|cea", []() {
476*f0687c8aSRaman Tenneti s_use_cea = true;
477*f0687c8aSRaman Tenneti }),
478*f0687c8aSRaman Tenneti Option("|flip?", [&](string s) {
479*f0687c8aSRaman Tenneti s_flip_mode = true;
480*f0687c8aSRaman Tenneti s_num_buffers = 2;
481*f0687c8aSRaman Tenneti if (!s.empty())
482*f0687c8aSRaman Tenneti s_max_flips = stoi(s);
483*f0687c8aSRaman Tenneti }),
484*f0687c8aSRaman Tenneti Option("|sync", []() {
485*f0687c8aSRaman Tenneti s_flip_sync = true;
486*f0687c8aSRaman Tenneti }),
487*f0687c8aSRaman Tenneti Option("|cvt=", [&](string s) {
488*f0687c8aSRaman Tenneti if (s == "v1")
489*f0687c8aSRaman Tenneti s_cvt = true;
490*f0687c8aSRaman Tenneti else if (s == "v2")
491*f0687c8aSRaman Tenneti s_cvt = s_cvt_v2 = true;
492*f0687c8aSRaman Tenneti else if (s == "v2o")
493*f0687c8aSRaman Tenneti s_cvt = s_cvt_v2 = s_cvt_vid_opt = true;
494*f0687c8aSRaman Tenneti else {
495*f0687c8aSRaman Tenneti usage();
496*f0687c8aSRaman Tenneti exit(-1);
497*f0687c8aSRaman Tenneti }
498*f0687c8aSRaman Tenneti }),
499*f0687c8aSRaman Tenneti Option("|crc", []() {
500*f0687c8aSRaman Tenneti s_print_crc = true;
501*f0687c8aSRaman Tenneti }),
502*f0687c8aSRaman Tenneti Option("h|help", [&]() {
503*f0687c8aSRaman Tenneti usage();
504*f0687c8aSRaman Tenneti exit(-1);
505*f0687c8aSRaman Tenneti }),
506*f0687c8aSRaman Tenneti };
507*f0687c8aSRaman Tenneti
508*f0687c8aSRaman Tenneti optionset.parse(argc, argv);
509*f0687c8aSRaman Tenneti
510*f0687c8aSRaman Tenneti if (optionset.params().size() > 0) {
511*f0687c8aSRaman Tenneti usage();
512*f0687c8aSRaman Tenneti exit(-1);
513*f0687c8aSRaman Tenneti }
514*f0687c8aSRaman Tenneti
515*f0687c8aSRaman Tenneti return args;
516*f0687c8aSRaman Tenneti }
517*f0687c8aSRaman Tenneti
setups_to_outputs(Card & card,ResourceManager & resman,const vector<Arg> & output_args)518*f0687c8aSRaman Tenneti static vector<OutputInfo> setups_to_outputs(Card& card, ResourceManager& resman, const vector<Arg>& output_args)
519*f0687c8aSRaman Tenneti {
520*f0687c8aSRaman Tenneti vector<OutputInfo> outputs;
521*f0687c8aSRaman Tenneti
522*f0687c8aSRaman Tenneti OutputInfo* current_output = 0;
523*f0687c8aSRaman Tenneti PlaneInfo* current_plane = 0;
524*f0687c8aSRaman Tenneti
525*f0687c8aSRaman Tenneti for (auto& arg : output_args) {
526*f0687c8aSRaman Tenneti switch (arg.type) {
527*f0687c8aSRaman Tenneti case ArgType::Connector: {
528*f0687c8aSRaman Tenneti outputs.push_back(OutputInfo{});
529*f0687c8aSRaman Tenneti current_output = &outputs.back();
530*f0687c8aSRaman Tenneti
531*f0687c8aSRaman Tenneti get_connector(resman, *current_output, arg.arg);
532*f0687c8aSRaman Tenneti current_plane = 0;
533*f0687c8aSRaman Tenneti
534*f0687c8aSRaman Tenneti break;
535*f0687c8aSRaman Tenneti }
536*f0687c8aSRaman Tenneti
537*f0687c8aSRaman Tenneti case ArgType::Crtc: {
538*f0687c8aSRaman Tenneti if (!current_output) {
539*f0687c8aSRaman Tenneti outputs.push_back(OutputInfo{});
540*f0687c8aSRaman Tenneti current_output = &outputs.back();
541*f0687c8aSRaman Tenneti }
542*f0687c8aSRaman Tenneti
543*f0687c8aSRaman Tenneti if (!current_output->connector)
544*f0687c8aSRaman Tenneti get_connector(resman, *current_output);
545*f0687c8aSRaman Tenneti
546*f0687c8aSRaman Tenneti parse_crtc(resman, card, arg.arg, *current_output);
547*f0687c8aSRaman Tenneti
548*f0687c8aSRaman Tenneti current_plane = 0;
549*f0687c8aSRaman Tenneti
550*f0687c8aSRaman Tenneti break;
551*f0687c8aSRaman Tenneti }
552*f0687c8aSRaman Tenneti
553*f0687c8aSRaman Tenneti case ArgType::Plane: {
554*f0687c8aSRaman Tenneti if (!current_output) {
555*f0687c8aSRaman Tenneti outputs.push_back(OutputInfo{});
556*f0687c8aSRaman Tenneti current_output = &outputs.back();
557*f0687c8aSRaman Tenneti }
558*f0687c8aSRaman Tenneti
559*f0687c8aSRaman Tenneti if (!current_output->connector)
560*f0687c8aSRaman Tenneti get_connector(resman, *current_output);
561*f0687c8aSRaman Tenneti
562*f0687c8aSRaman Tenneti if (!current_output->crtc)
563*f0687c8aSRaman Tenneti get_default_crtc(resman, *current_output);
564*f0687c8aSRaman Tenneti
565*f0687c8aSRaman Tenneti current_plane = add_default_planeinfo(current_output);
566*f0687c8aSRaman Tenneti
567*f0687c8aSRaman Tenneti parse_plane(resman, card, arg.arg, *current_output, *current_plane);
568*f0687c8aSRaman Tenneti
569*f0687c8aSRaman Tenneti break;
570*f0687c8aSRaman Tenneti }
571*f0687c8aSRaman Tenneti
572*f0687c8aSRaman Tenneti case ArgType::Framebuffer: {
573*f0687c8aSRaman Tenneti if (!current_output) {
574*f0687c8aSRaman Tenneti outputs.push_back(OutputInfo{});
575*f0687c8aSRaman Tenneti current_output = &outputs.back();
576*f0687c8aSRaman Tenneti }
577*f0687c8aSRaman Tenneti
578*f0687c8aSRaman Tenneti if (!current_output->connector)
579*f0687c8aSRaman Tenneti get_connector(resman, *current_output);
580*f0687c8aSRaman Tenneti
581*f0687c8aSRaman Tenneti if (!current_output->crtc)
582*f0687c8aSRaman Tenneti get_default_crtc(resman, *current_output);
583*f0687c8aSRaman Tenneti
584*f0687c8aSRaman Tenneti if (!current_plane && card.has_atomic())
585*f0687c8aSRaman Tenneti current_plane = add_default_planeinfo(current_output);
586*f0687c8aSRaman Tenneti
587*f0687c8aSRaman Tenneti parse_fb(card, arg.arg, current_output, current_plane);
588*f0687c8aSRaman Tenneti
589*f0687c8aSRaman Tenneti break;
590*f0687c8aSRaman Tenneti }
591*f0687c8aSRaman Tenneti
592*f0687c8aSRaman Tenneti case ArgType::View: {
593*f0687c8aSRaman Tenneti if (!current_plane || current_plane->fbs.empty())
594*f0687c8aSRaman Tenneti EXIT("'view' parameter requires a plane and a fb");
595*f0687c8aSRaman Tenneti
596*f0687c8aSRaman Tenneti parse_view(arg.arg, *current_plane);
597*f0687c8aSRaman Tenneti break;
598*f0687c8aSRaman Tenneti }
599*f0687c8aSRaman Tenneti
600*f0687c8aSRaman Tenneti case ArgType::Property: {
601*f0687c8aSRaman Tenneti if (!current_output)
602*f0687c8aSRaman Tenneti EXIT("No object to which set the property");
603*f0687c8aSRaman Tenneti
604*f0687c8aSRaman Tenneti if (current_plane)
605*f0687c8aSRaman Tenneti parse_prop(arg.arg, current_plane->props);
606*f0687c8aSRaman Tenneti else if (current_output->crtc)
607*f0687c8aSRaman Tenneti parse_prop(arg.arg, current_output->crtc_props);
608*f0687c8aSRaman Tenneti else if (current_output->connector)
609*f0687c8aSRaman Tenneti parse_prop(arg.arg, current_output->conn_props);
610*f0687c8aSRaman Tenneti else
611*f0687c8aSRaman Tenneti EXIT("no object");
612*f0687c8aSRaman Tenneti
613*f0687c8aSRaman Tenneti break;
614*f0687c8aSRaman Tenneti }
615*f0687c8aSRaman Tenneti }
616*f0687c8aSRaman Tenneti }
617*f0687c8aSRaman Tenneti
618*f0687c8aSRaman Tenneti if (outputs.empty()) {
619*f0687c8aSRaman Tenneti // no outputs defined, show a pattern on all connected screens
620*f0687c8aSRaman Tenneti for (Connector* conn : card.get_connectors()) {
621*f0687c8aSRaman Tenneti if (!conn->connected())
622*f0687c8aSRaman Tenneti continue;
623*f0687c8aSRaman Tenneti
624*f0687c8aSRaman Tenneti OutputInfo output = {};
625*f0687c8aSRaman Tenneti output.connector = resman.reserve_connector(conn);
626*f0687c8aSRaman Tenneti EXIT_IF(!output.connector, "Failed to reserve connector %s", conn->fullname().c_str());
627*f0687c8aSRaman Tenneti output.crtc = resman.reserve_crtc(conn);
628*f0687c8aSRaman Tenneti EXIT_IF(!output.crtc, "Failed to reserve crtc for %s", conn->fullname().c_str());
629*f0687c8aSRaman Tenneti output.mode = output.connector->get_default_mode();
630*f0687c8aSRaman Tenneti
631*f0687c8aSRaman Tenneti outputs.push_back(output);
632*f0687c8aSRaman Tenneti }
633*f0687c8aSRaman Tenneti }
634*f0687c8aSRaman Tenneti
635*f0687c8aSRaman Tenneti for (OutputInfo& o : outputs) {
636*f0687c8aSRaman Tenneti get_props(card, o.conn_props, o.connector);
637*f0687c8aSRaman Tenneti
638*f0687c8aSRaman Tenneti if (!o.crtc)
639*f0687c8aSRaman Tenneti get_default_crtc(resman, o);
640*f0687c8aSRaman Tenneti
641*f0687c8aSRaman Tenneti get_props(card, o.crtc_props, o.crtc);
642*f0687c8aSRaman Tenneti
643*f0687c8aSRaman Tenneti if (!o.mode.valid())
644*f0687c8aSRaman Tenneti EXIT("Mode not valid for %s", o.connector->fullname().c_str());
645*f0687c8aSRaman Tenneti
646*f0687c8aSRaman Tenneti if (card.has_atomic()) {
647*f0687c8aSRaman Tenneti if (o.planes.empty())
648*f0687c8aSRaman Tenneti add_default_planeinfo(&o);
649*f0687c8aSRaman Tenneti } else {
650*f0687c8aSRaman Tenneti if (o.legacy_fbs.empty())
651*f0687c8aSRaman Tenneti o.legacy_fbs = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay);
652*f0687c8aSRaman Tenneti }
653*f0687c8aSRaman Tenneti
654*f0687c8aSRaman Tenneti for (PlaneInfo& p : o.planes) {
655*f0687c8aSRaman Tenneti if (p.fbs.empty())
656*f0687c8aSRaman Tenneti p.fbs = get_default_fb(card, p.w, p.h);
657*f0687c8aSRaman Tenneti }
658*f0687c8aSRaman Tenneti
659*f0687c8aSRaman Tenneti for (PlaneInfo& p : o.planes) {
660*f0687c8aSRaman Tenneti if (!p.plane) {
661*f0687c8aSRaman Tenneti if (card.has_atomic())
662*f0687c8aSRaman Tenneti p.plane = resman.reserve_generic_plane(o.crtc, p.fbs[0]->format());
663*f0687c8aSRaman Tenneti else
664*f0687c8aSRaman Tenneti p.plane = resman.reserve_overlay_plane(o.crtc, p.fbs[0]->format());
665*f0687c8aSRaman Tenneti
666*f0687c8aSRaman Tenneti if (!p.plane)
667*f0687c8aSRaman Tenneti EXIT("Failed to find available plane");
668*f0687c8aSRaman Tenneti }
669*f0687c8aSRaman Tenneti get_props(card, p.props, p.plane);
670*f0687c8aSRaman Tenneti }
671*f0687c8aSRaman Tenneti }
672*f0687c8aSRaman Tenneti
673*f0687c8aSRaman Tenneti return outputs;
674*f0687c8aSRaman Tenneti }
675*f0687c8aSRaman Tenneti
crc16(uint16_t crc,uint8_t data)676*f0687c8aSRaman Tenneti static uint16_t crc16(uint16_t crc, uint8_t data)
677*f0687c8aSRaman Tenneti {
678*f0687c8aSRaman Tenneti const uint16_t CRC16_IBM = 0x8005;
679*f0687c8aSRaman Tenneti
680*f0687c8aSRaman Tenneti for (uint8_t i = 0; i < 8; i++) {
681*f0687c8aSRaman Tenneti if (((crc & 0x8000) >> 8) ^ (data & 0x80))
682*f0687c8aSRaman Tenneti crc = (crc << 1) ^ CRC16_IBM;
683*f0687c8aSRaman Tenneti else
684*f0687c8aSRaman Tenneti crc = (crc << 1);
685*f0687c8aSRaman Tenneti
686*f0687c8aSRaman Tenneti data <<= 1;
687*f0687c8aSRaman Tenneti }
688*f0687c8aSRaman Tenneti
689*f0687c8aSRaman Tenneti return crc;
690*f0687c8aSRaman Tenneti }
691*f0687c8aSRaman Tenneti
fb_crc(IFramebuffer * fb)692*f0687c8aSRaman Tenneti static string fb_crc(IFramebuffer* fb)
693*f0687c8aSRaman Tenneti {
694*f0687c8aSRaman Tenneti uint8_t* p = fb->map(0);
695*f0687c8aSRaman Tenneti uint16_t r, g, b;
696*f0687c8aSRaman Tenneti
697*f0687c8aSRaman Tenneti r = g = b = 0;
698*f0687c8aSRaman Tenneti
699*f0687c8aSRaman Tenneti for (unsigned y = 0; y < fb->height(); ++y) {
700*f0687c8aSRaman Tenneti for (unsigned x = 0; x < fb->width(); ++x) {
701*f0687c8aSRaman Tenneti uint32_t* p32 = (uint32_t*)(p + fb->stride(0) * y + x * 4);
702*f0687c8aSRaman Tenneti RGB rgb(*p32);
703*f0687c8aSRaman Tenneti
704*f0687c8aSRaman Tenneti r = crc16(r, rgb.r);
705*f0687c8aSRaman Tenneti r = crc16(r, 0);
706*f0687c8aSRaman Tenneti
707*f0687c8aSRaman Tenneti g = crc16(g, rgb.g);
708*f0687c8aSRaman Tenneti g = crc16(g, 0);
709*f0687c8aSRaman Tenneti
710*f0687c8aSRaman Tenneti b = crc16(b, rgb.b);
711*f0687c8aSRaman Tenneti b = crc16(b, 0);
712*f0687c8aSRaman Tenneti }
713*f0687c8aSRaman Tenneti }
714*f0687c8aSRaman Tenneti
715*f0687c8aSRaman Tenneti return fmt::format("{:#06x} {:#06x} {:#06x}", r, g, b);
716*f0687c8aSRaman Tenneti }
717*f0687c8aSRaman Tenneti
print_outputs(const vector<OutputInfo> & outputs)718*f0687c8aSRaman Tenneti static void print_outputs(const vector<OutputInfo>& outputs)
719*f0687c8aSRaman Tenneti {
720*f0687c8aSRaman Tenneti for (unsigned i = 0; i < outputs.size(); ++i) {
721*f0687c8aSRaman Tenneti const OutputInfo& o = outputs[i];
722*f0687c8aSRaman Tenneti
723*f0687c8aSRaman Tenneti fmt::print("Connector {}/@{}: {}", o.connector->idx(), o.connector->id(),
724*f0687c8aSRaman Tenneti o.connector->fullname());
725*f0687c8aSRaman Tenneti
726*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.conn_props)
727*f0687c8aSRaman Tenneti fmt::print(" {}={}", prop.prop->name(), prop.val);
728*f0687c8aSRaman Tenneti
729*f0687c8aSRaman Tenneti fmt::print("\n Crtc {}/@{}", o.crtc->idx(), o.crtc->id());
730*f0687c8aSRaman Tenneti
731*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.crtc_props)
732*f0687c8aSRaman Tenneti fmt::print(" {}={}", prop.prop->name(), prop.val);
733*f0687c8aSRaman Tenneti
734*f0687c8aSRaman Tenneti fmt::print(": {}\n", o.mode.to_string_long());
735*f0687c8aSRaman Tenneti
736*f0687c8aSRaman Tenneti if (!o.legacy_fbs.empty()) {
737*f0687c8aSRaman Tenneti auto fb = o.legacy_fbs[0];
738*f0687c8aSRaman Tenneti fmt::print(" Fb {} {}x{}-{}\n", fb->id(), fb->width(), fb->height(), PixelFormatToFourCC(fb->format()));
739*f0687c8aSRaman Tenneti }
740*f0687c8aSRaman Tenneti
741*f0687c8aSRaman Tenneti for (unsigned j = 0; j < o.planes.size(); ++j) {
742*f0687c8aSRaman Tenneti const PlaneInfo& p = o.planes[j];
743*f0687c8aSRaman Tenneti auto fb = p.fbs[0];
744*f0687c8aSRaman Tenneti fmt::print(" Plane {}/@{}: {},{}-{}x{}", p.plane->idx(), p.plane->id(),
745*f0687c8aSRaman Tenneti p.x, p.y, p.w, p.h);
746*f0687c8aSRaman Tenneti for (const PropInfo& prop : p.props)
747*f0687c8aSRaman Tenneti fmt::print(" {}={}", prop.prop->name(), prop.val);
748*f0687c8aSRaman Tenneti fmt::print("\n");
749*f0687c8aSRaman Tenneti
750*f0687c8aSRaman Tenneti fmt::print(" Fb {} {}x{}-{}\n", fb->id(), fb->width(), fb->height(),
751*f0687c8aSRaman Tenneti PixelFormatToFourCC(fb->format()));
752*f0687c8aSRaman Tenneti if (s_print_crc)
753*f0687c8aSRaman Tenneti fmt::print(" CRC16 {}\n", fb_crc(fb).c_str());
754*f0687c8aSRaman Tenneti }
755*f0687c8aSRaman Tenneti }
756*f0687c8aSRaman Tenneti }
757*f0687c8aSRaman Tenneti
draw_test_patterns(const vector<OutputInfo> & outputs)758*f0687c8aSRaman Tenneti static void draw_test_patterns(const vector<OutputInfo>& outputs)
759*f0687c8aSRaman Tenneti {
760*f0687c8aSRaman Tenneti for (const OutputInfo& o : outputs) {
761*f0687c8aSRaman Tenneti for (auto fb : o.legacy_fbs)
762*f0687c8aSRaman Tenneti draw_test_pattern(*fb);
763*f0687c8aSRaman Tenneti
764*f0687c8aSRaman Tenneti for (const PlaneInfo& p : o.planes)
765*f0687c8aSRaman Tenneti for (auto fb : p.fbs)
766*f0687c8aSRaman Tenneti draw_test_pattern(*fb);
767*f0687c8aSRaman Tenneti }
768*f0687c8aSRaman Tenneti }
769*f0687c8aSRaman Tenneti
set_crtcs_n_planes_legacy(Card & card,const vector<OutputInfo> & outputs)770*f0687c8aSRaman Tenneti static void set_crtcs_n_planes_legacy(Card& card, const vector<OutputInfo>& outputs)
771*f0687c8aSRaman Tenneti {
772*f0687c8aSRaman Tenneti // Disable unused crtcs
773*f0687c8aSRaman Tenneti for (Crtc* crtc : card.get_crtcs()) {
774*f0687c8aSRaman Tenneti if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end())
775*f0687c8aSRaman Tenneti continue;
776*f0687c8aSRaman Tenneti
777*f0687c8aSRaman Tenneti crtc->disable_mode();
778*f0687c8aSRaman Tenneti }
779*f0687c8aSRaman Tenneti
780*f0687c8aSRaman Tenneti for (const OutputInfo& o : outputs) {
781*f0687c8aSRaman Tenneti int r;
782*f0687c8aSRaman Tenneti auto conn = o.connector;
783*f0687c8aSRaman Tenneti auto crtc = o.crtc;
784*f0687c8aSRaman Tenneti
785*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.conn_props) {
786*f0687c8aSRaman Tenneti r = conn->set_prop_value(prop.prop, prop.val);
787*f0687c8aSRaman Tenneti EXIT_IF(r, "failed to set connector property %s\n", prop.name.c_str());
788*f0687c8aSRaman Tenneti }
789*f0687c8aSRaman Tenneti
790*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.crtc_props) {
791*f0687c8aSRaman Tenneti r = crtc->set_prop_value(prop.prop, prop.val);
792*f0687c8aSRaman Tenneti EXIT_IF(r, "failed to set crtc property %s\n", prop.name.c_str());
793*f0687c8aSRaman Tenneti }
794*f0687c8aSRaman Tenneti
795*f0687c8aSRaman Tenneti if (!o.legacy_fbs.empty()) {
796*f0687c8aSRaman Tenneti auto fb = o.legacy_fbs[0];
797*f0687c8aSRaman Tenneti r = crtc->set_mode(conn, *fb, o.mode);
798*f0687c8aSRaman Tenneti if (r)
799*f0687c8aSRaman Tenneti fmt::print(stderr, "crtc->set_mode() failed for crtc {}: {}\n",
800*f0687c8aSRaman Tenneti crtc->id(), strerror(-r));
801*f0687c8aSRaman Tenneti }
802*f0687c8aSRaman Tenneti
803*f0687c8aSRaman Tenneti for (const PlaneInfo& p : o.planes) {
804*f0687c8aSRaman Tenneti for (const PropInfo& prop : p.props) {
805*f0687c8aSRaman Tenneti r = p.plane->set_prop_value(prop.prop, prop.val);
806*f0687c8aSRaman Tenneti EXIT_IF(r, "failed to set plane property %s\n", prop.name.c_str());
807*f0687c8aSRaman Tenneti }
808*f0687c8aSRaman Tenneti
809*f0687c8aSRaman Tenneti auto fb = p.fbs[0];
810*f0687c8aSRaman Tenneti r = crtc->set_plane(p.plane, *fb,
811*f0687c8aSRaman Tenneti p.x, p.y, p.w, p.h,
812*f0687c8aSRaman Tenneti 0, 0, fb->width(), fb->height());
813*f0687c8aSRaman Tenneti if (r)
814*f0687c8aSRaman Tenneti fmt::print(stderr, "crtc->set_plane() failed for plane {}: {}\n",
815*f0687c8aSRaman Tenneti p.plane->id(), strerror(-r));
816*f0687c8aSRaman Tenneti }
817*f0687c8aSRaman Tenneti }
818*f0687c8aSRaman Tenneti }
819*f0687c8aSRaman Tenneti
set_crtcs_n_planes_atomic(Card & card,const vector<OutputInfo> & outputs)820*f0687c8aSRaman Tenneti static void set_crtcs_n_planes_atomic(Card& card, const vector<OutputInfo>& outputs)
821*f0687c8aSRaman Tenneti {
822*f0687c8aSRaman Tenneti int r;
823*f0687c8aSRaman Tenneti
824*f0687c8aSRaman Tenneti // XXX DRM framework doesn't allow moving an active plane from one crtc to another.
825*f0687c8aSRaman Tenneti // See drm_atomic.c::plane_switching_crtc().
826*f0687c8aSRaman Tenneti // For the time being, disable all crtcs and planes here.
827*f0687c8aSRaman Tenneti
828*f0687c8aSRaman Tenneti AtomicReq disable_req(card);
829*f0687c8aSRaman Tenneti
830*f0687c8aSRaman Tenneti // Disable unused crtcs
831*f0687c8aSRaman Tenneti for (Crtc* crtc : card.get_crtcs()) {
832*f0687c8aSRaman Tenneti //if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end())
833*f0687c8aSRaman Tenneti // continue;
834*f0687c8aSRaman Tenneti
835*f0687c8aSRaman Tenneti disable_req.add(crtc, {
836*f0687c8aSRaman Tenneti { "ACTIVE", 0 },
837*f0687c8aSRaman Tenneti });
838*f0687c8aSRaman Tenneti }
839*f0687c8aSRaman Tenneti
840*f0687c8aSRaman Tenneti // Disable unused planes
841*f0687c8aSRaman Tenneti for (Plane* plane : card.get_planes())
842*f0687c8aSRaman Tenneti disable_req.add(plane, {
843*f0687c8aSRaman Tenneti { "FB_ID", 0 },
844*f0687c8aSRaman Tenneti { "CRTC_ID", 0 },
845*f0687c8aSRaman Tenneti });
846*f0687c8aSRaman Tenneti
847*f0687c8aSRaman Tenneti r = disable_req.commit_sync(true);
848*f0687c8aSRaman Tenneti if (r)
849*f0687c8aSRaman Tenneti EXIT("Atomic commit failed when disabling: %d\n", r);
850*f0687c8aSRaman Tenneti
851*f0687c8aSRaman Tenneti // Keep blobs here so that we keep ref to them until we have committed the req
852*f0687c8aSRaman Tenneti vector<unique_ptr<Blob>> blobs;
853*f0687c8aSRaman Tenneti
854*f0687c8aSRaman Tenneti AtomicReq req(card);
855*f0687c8aSRaman Tenneti
856*f0687c8aSRaman Tenneti for (const OutputInfo& o : outputs) {
857*f0687c8aSRaman Tenneti auto conn = o.connector;
858*f0687c8aSRaman Tenneti auto crtc = o.crtc;
859*f0687c8aSRaman Tenneti
860*f0687c8aSRaman Tenneti blobs.emplace_back(o.mode.to_blob(card));
861*f0687c8aSRaman Tenneti Blob* mode_blob = blobs.back().get();
862*f0687c8aSRaman Tenneti
863*f0687c8aSRaman Tenneti req.add(conn, {
864*f0687c8aSRaman Tenneti { "CRTC_ID", crtc->id() },
865*f0687c8aSRaman Tenneti });
866*f0687c8aSRaman Tenneti
867*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.conn_props)
868*f0687c8aSRaman Tenneti req.add(conn, prop.prop, prop.val);
869*f0687c8aSRaman Tenneti
870*f0687c8aSRaman Tenneti req.add(crtc, {
871*f0687c8aSRaman Tenneti { "ACTIVE", 1 },
872*f0687c8aSRaman Tenneti { "MODE_ID", mode_blob->id() },
873*f0687c8aSRaman Tenneti });
874*f0687c8aSRaman Tenneti
875*f0687c8aSRaman Tenneti for (const PropInfo& prop : o.crtc_props)
876*f0687c8aSRaman Tenneti req.add(crtc, prop.prop, prop.val);
877*f0687c8aSRaman Tenneti
878*f0687c8aSRaman Tenneti for (const PlaneInfo& p : o.planes) {
879*f0687c8aSRaman Tenneti auto fb = p.fbs[0];
880*f0687c8aSRaman Tenneti
881*f0687c8aSRaman Tenneti req.add(p.plane, {
882*f0687c8aSRaman Tenneti { "FB_ID", fb->id() },
883*f0687c8aSRaman Tenneti { "CRTC_ID", crtc->id() },
884*f0687c8aSRaman Tenneti { "SRC_X", (p.view_x ?: 0) << 16 },
885*f0687c8aSRaman Tenneti { "SRC_Y", (p.view_y ?: 0) << 16 },
886*f0687c8aSRaman Tenneti { "SRC_W", (p.view_w ?: fb->width()) << 16 },
887*f0687c8aSRaman Tenneti { "SRC_H", (p.view_h ?: fb->height()) << 16 },
888*f0687c8aSRaman Tenneti { "CRTC_X", p.x },
889*f0687c8aSRaman Tenneti { "CRTC_Y", p.y },
890*f0687c8aSRaman Tenneti { "CRTC_W", p.w },
891*f0687c8aSRaman Tenneti { "CRTC_H", p.h },
892*f0687c8aSRaman Tenneti });
893*f0687c8aSRaman Tenneti
894*f0687c8aSRaman Tenneti for (const PropInfo& prop : p.props)
895*f0687c8aSRaman Tenneti req.add(p.plane, prop.prop, prop.val);
896*f0687c8aSRaman Tenneti }
897*f0687c8aSRaman Tenneti }
898*f0687c8aSRaman Tenneti
899*f0687c8aSRaman Tenneti r = req.test(true);
900*f0687c8aSRaman Tenneti if (r)
901*f0687c8aSRaman Tenneti EXIT("Atomic test failed: %d\n", r);
902*f0687c8aSRaman Tenneti
903*f0687c8aSRaman Tenneti r = req.commit_sync(true);
904*f0687c8aSRaman Tenneti if (r)
905*f0687c8aSRaman Tenneti EXIT("Atomic commit failed: %d\n", r);
906*f0687c8aSRaman Tenneti }
907*f0687c8aSRaman Tenneti
set_crtcs_n_planes(Card & card,const vector<OutputInfo> & outputs)908*f0687c8aSRaman Tenneti static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs)
909*f0687c8aSRaman Tenneti {
910*f0687c8aSRaman Tenneti if (card.has_atomic())
911*f0687c8aSRaman Tenneti set_crtcs_n_planes_atomic(card, outputs);
912*f0687c8aSRaman Tenneti else
913*f0687c8aSRaman Tenneti set_crtcs_n_planes_legacy(card, outputs);
914*f0687c8aSRaman Tenneti }
915*f0687c8aSRaman Tenneti
916*f0687c8aSRaman Tenneti static bool max_flips_reached;
917*f0687c8aSRaman Tenneti
918*f0687c8aSRaman Tenneti class FlipState : private PageFlipHandlerBase
919*f0687c8aSRaman Tenneti {
920*f0687c8aSRaman Tenneti public:
FlipState(Card & card,const string & name,vector<const OutputInfo * > outputs)921*f0687c8aSRaman Tenneti FlipState(Card& card, const string& name, vector<const OutputInfo*> outputs)
922*f0687c8aSRaman Tenneti : m_card(card), m_name(name), m_outputs(outputs)
923*f0687c8aSRaman Tenneti {
924*f0687c8aSRaman Tenneti }
925*f0687c8aSRaman Tenneti
start_flipping()926*f0687c8aSRaman Tenneti void start_flipping()
927*f0687c8aSRaman Tenneti {
928*f0687c8aSRaman Tenneti m_prev_frame = m_prev_print = std::chrono::steady_clock::now();
929*f0687c8aSRaman Tenneti m_slowest_frame = std::chrono::duration<float>::min();
930*f0687c8aSRaman Tenneti m_frame_num = 0;
931*f0687c8aSRaman Tenneti queue_next();
932*f0687c8aSRaman Tenneti }
933*f0687c8aSRaman Tenneti
934*f0687c8aSRaman Tenneti private:
handle_page_flip(uint32_t frame,double time)935*f0687c8aSRaman Tenneti void handle_page_flip(uint32_t frame, double time)
936*f0687c8aSRaman Tenneti {
937*f0687c8aSRaman Tenneti /*
938*f0687c8aSRaman Tenneti * We get flip event for each crtc in this flipstate. We can commit the next frames
939*f0687c8aSRaman Tenneti * only after we've gotten the flip event for all crtcs
940*f0687c8aSRaman Tenneti */
941*f0687c8aSRaman Tenneti if (++m_flip_count < m_outputs.size())
942*f0687c8aSRaman Tenneti return;
943*f0687c8aSRaman Tenneti
944*f0687c8aSRaman Tenneti m_frame_num++;
945*f0687c8aSRaman Tenneti if (s_max_flips && m_frame_num >= s_max_flips)
946*f0687c8aSRaman Tenneti max_flips_reached = true;
947*f0687c8aSRaman Tenneti
948*f0687c8aSRaman Tenneti auto now = std::chrono::steady_clock::now();
949*f0687c8aSRaman Tenneti
950*f0687c8aSRaman Tenneti std::chrono::duration<float> diff = now - m_prev_frame;
951*f0687c8aSRaman Tenneti if (diff > m_slowest_frame)
952*f0687c8aSRaman Tenneti m_slowest_frame = diff;
953*f0687c8aSRaman Tenneti
954*f0687c8aSRaman Tenneti if (m_frame_num % 100 == 0) {
955*f0687c8aSRaman Tenneti std::chrono::duration<float> fsec = now - m_prev_print;
956*f0687c8aSRaman Tenneti fmt::print("Connector {}: fps {:.2f}, slowest {:.2f} ms\n",
957*f0687c8aSRaman Tenneti m_name.c_str(),
958*f0687c8aSRaman Tenneti 100.0 / fsec.count(),
959*f0687c8aSRaman Tenneti m_slowest_frame.count() * 1000);
960*f0687c8aSRaman Tenneti m_prev_print = now;
961*f0687c8aSRaman Tenneti m_slowest_frame = std::chrono::duration<float>::min();
962*f0687c8aSRaman Tenneti }
963*f0687c8aSRaman Tenneti
964*f0687c8aSRaman Tenneti m_prev_frame = now;
965*f0687c8aSRaman Tenneti
966*f0687c8aSRaman Tenneti queue_next();
967*f0687c8aSRaman Tenneti }
968*f0687c8aSRaman Tenneti
get_bar_pos(Framebuffer * fb,unsigned frame_num)969*f0687c8aSRaman Tenneti static unsigned get_bar_pos(Framebuffer* fb, unsigned frame_num)
970*f0687c8aSRaman Tenneti {
971*f0687c8aSRaman Tenneti return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
972*f0687c8aSRaman Tenneti }
973*f0687c8aSRaman Tenneti
draw_bar(Framebuffer * fb,unsigned frame_num)974*f0687c8aSRaman Tenneti static void draw_bar(Framebuffer* fb, unsigned frame_num)
975*f0687c8aSRaman Tenneti {
976*f0687c8aSRaman Tenneti int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
977*f0687c8aSRaman Tenneti int new_xpos = get_bar_pos(fb, frame_num);
978*f0687c8aSRaman Tenneti
979*f0687c8aSRaman Tenneti draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
980*f0687c8aSRaman Tenneti draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
981*f0687c8aSRaman Tenneti }
982*f0687c8aSRaman Tenneti
do_flip_output(AtomicReq & req,unsigned frame_num,const OutputInfo & o)983*f0687c8aSRaman Tenneti static void do_flip_output(AtomicReq& req, unsigned frame_num, const OutputInfo& o)
984*f0687c8aSRaman Tenneti {
985*f0687c8aSRaman Tenneti unsigned cur = frame_num % s_num_buffers;
986*f0687c8aSRaman Tenneti
987*f0687c8aSRaman Tenneti for (const PlaneInfo& p : o.planes) {
988*f0687c8aSRaman Tenneti auto fb = p.fbs[cur];
989*f0687c8aSRaman Tenneti
990*f0687c8aSRaman Tenneti draw_bar(fb, frame_num);
991*f0687c8aSRaman Tenneti
992*f0687c8aSRaman Tenneti req.add(p.plane, {
993*f0687c8aSRaman Tenneti { "FB_ID", fb->id() },
994*f0687c8aSRaman Tenneti });
995*f0687c8aSRaman Tenneti }
996*f0687c8aSRaman Tenneti }
997*f0687c8aSRaman Tenneti
do_flip_output_legacy(unsigned frame_num,const OutputInfo & o)998*f0687c8aSRaman Tenneti void do_flip_output_legacy(unsigned frame_num, const OutputInfo& o)
999*f0687c8aSRaman Tenneti {
1000*f0687c8aSRaman Tenneti unsigned cur = frame_num % s_num_buffers;
1001*f0687c8aSRaman Tenneti
1002*f0687c8aSRaman Tenneti if (!o.legacy_fbs.empty()) {
1003*f0687c8aSRaman Tenneti auto fb = o.legacy_fbs[cur];
1004*f0687c8aSRaman Tenneti
1005*f0687c8aSRaman Tenneti draw_bar(fb, frame_num);
1006*f0687c8aSRaman Tenneti
1007*f0687c8aSRaman Tenneti int r = o.crtc->page_flip(*fb, this);
1008*f0687c8aSRaman Tenneti ASSERT(r == 0);
1009*f0687c8aSRaman Tenneti }
1010*f0687c8aSRaman Tenneti
1011*f0687c8aSRaman Tenneti for (const PlaneInfo& p : o.planes) {
1012*f0687c8aSRaman Tenneti auto fb = p.fbs[cur];
1013*f0687c8aSRaman Tenneti
1014*f0687c8aSRaman Tenneti draw_bar(fb, frame_num);
1015*f0687c8aSRaman Tenneti
1016*f0687c8aSRaman Tenneti int r = o.crtc->set_plane(p.plane, *fb,
1017*f0687c8aSRaman Tenneti p.x, p.y, p.w, p.h,
1018*f0687c8aSRaman Tenneti 0, 0, fb->width(), fb->height());
1019*f0687c8aSRaman Tenneti ASSERT(r == 0);
1020*f0687c8aSRaman Tenneti }
1021*f0687c8aSRaman Tenneti }
1022*f0687c8aSRaman Tenneti
queue_next()1023*f0687c8aSRaman Tenneti void queue_next()
1024*f0687c8aSRaman Tenneti {
1025*f0687c8aSRaman Tenneti m_flip_count = 0;
1026*f0687c8aSRaman Tenneti
1027*f0687c8aSRaman Tenneti if (m_card.has_atomic()) {
1028*f0687c8aSRaman Tenneti AtomicReq req(m_card);
1029*f0687c8aSRaman Tenneti
1030*f0687c8aSRaman Tenneti for (auto o : m_outputs)
1031*f0687c8aSRaman Tenneti do_flip_output(req, m_frame_num, *o);
1032*f0687c8aSRaman Tenneti
1033*f0687c8aSRaman Tenneti int r = req.commit(this);
1034*f0687c8aSRaman Tenneti if (r)
1035*f0687c8aSRaman Tenneti EXIT("Flip commit failed: %d\n", r);
1036*f0687c8aSRaman Tenneti } else {
1037*f0687c8aSRaman Tenneti ASSERT(m_outputs.size() == 1);
1038*f0687c8aSRaman Tenneti do_flip_output_legacy(m_frame_num, *m_outputs[0]);
1039*f0687c8aSRaman Tenneti }
1040*f0687c8aSRaman Tenneti }
1041*f0687c8aSRaman Tenneti
1042*f0687c8aSRaman Tenneti Card& m_card;
1043*f0687c8aSRaman Tenneti string m_name;
1044*f0687c8aSRaman Tenneti vector<const OutputInfo*> m_outputs;
1045*f0687c8aSRaman Tenneti unsigned m_frame_num;
1046*f0687c8aSRaman Tenneti unsigned m_flip_count;
1047*f0687c8aSRaman Tenneti
1048*f0687c8aSRaman Tenneti chrono::steady_clock::time_point m_prev_print;
1049*f0687c8aSRaman Tenneti chrono::steady_clock::time_point m_prev_frame;
1050*f0687c8aSRaman Tenneti chrono::duration<float> m_slowest_frame;
1051*f0687c8aSRaman Tenneti
1052*f0687c8aSRaman Tenneti static const unsigned bar_width = 20;
1053*f0687c8aSRaman Tenneti static const unsigned bar_speed = 8;
1054*f0687c8aSRaman Tenneti };
1055*f0687c8aSRaman Tenneti
main_flip(Card & card,const vector<OutputInfo> & outputs)1056*f0687c8aSRaman Tenneti static void main_flip(Card& card, const vector<OutputInfo>& outputs)
1057*f0687c8aSRaman Tenneti {
1058*f0687c8aSRaman Tenneti // clang-tidy does not seem to handle FD_xxx macros
1059*f0687c8aSRaman Tenneti #ifndef __clang_analyzer__
1060*f0687c8aSRaman Tenneti fd_set fds;
1061*f0687c8aSRaman Tenneti
1062*f0687c8aSRaman Tenneti FD_ZERO(&fds);
1063*f0687c8aSRaman Tenneti
1064*f0687c8aSRaman Tenneti int fd = card.fd();
1065*f0687c8aSRaman Tenneti
1066*f0687c8aSRaman Tenneti vector<unique_ptr<FlipState>> flipstates;
1067*f0687c8aSRaman Tenneti
1068*f0687c8aSRaman Tenneti if (!s_flip_sync) {
1069*f0687c8aSRaman Tenneti for (const OutputInfo& o : outputs) {
1070*f0687c8aSRaman Tenneti auto fs = unique_ptr<FlipState>(new FlipState(card, to_string(o.connector->idx()), { &o }));
1071*f0687c8aSRaman Tenneti flipstates.push_back(move(fs));
1072*f0687c8aSRaman Tenneti }
1073*f0687c8aSRaman Tenneti } else {
1074*f0687c8aSRaman Tenneti vector<const OutputInfo*> ois;
1075*f0687c8aSRaman Tenneti
1076*f0687c8aSRaman Tenneti string name;
1077*f0687c8aSRaman Tenneti for (const OutputInfo& o : outputs) {
1078*f0687c8aSRaman Tenneti name += to_string(o.connector->idx()) + ",";
1079*f0687c8aSRaman Tenneti ois.push_back(&o);
1080*f0687c8aSRaman Tenneti }
1081*f0687c8aSRaman Tenneti
1082*f0687c8aSRaman Tenneti auto fs = unique_ptr<FlipState>(new FlipState(card, name, ois));
1083*f0687c8aSRaman Tenneti flipstates.push_back(move(fs));
1084*f0687c8aSRaman Tenneti }
1085*f0687c8aSRaman Tenneti
1086*f0687c8aSRaman Tenneti for (unique_ptr<FlipState>& fs : flipstates)
1087*f0687c8aSRaman Tenneti fs->start_flipping();
1088*f0687c8aSRaman Tenneti
1089*f0687c8aSRaman Tenneti while (!max_flips_reached) {
1090*f0687c8aSRaman Tenneti int r;
1091*f0687c8aSRaman Tenneti
1092*f0687c8aSRaman Tenneti FD_SET(0, &fds);
1093*f0687c8aSRaman Tenneti FD_SET(fd, &fds);
1094*f0687c8aSRaman Tenneti
1095*f0687c8aSRaman Tenneti r = select(fd + 1, &fds, NULL, NULL, NULL);
1096*f0687c8aSRaman Tenneti if (r < 0) {
1097*f0687c8aSRaman Tenneti fmt::print(stderr, "select() failed with {}: {}\n", errno, strerror(errno));
1098*f0687c8aSRaman Tenneti break;
1099*f0687c8aSRaman Tenneti } else if (FD_ISSET(0, &fds)) {
1100*f0687c8aSRaman Tenneti fmt::print(stderr, "Exit due to user-input\n");
1101*f0687c8aSRaman Tenneti break;
1102*f0687c8aSRaman Tenneti } else if (FD_ISSET(fd, &fds)) {
1103*f0687c8aSRaman Tenneti card.call_page_flip_handlers();
1104*f0687c8aSRaman Tenneti }
1105*f0687c8aSRaman Tenneti }
1106*f0687c8aSRaman Tenneti #endif
1107*f0687c8aSRaman Tenneti }
1108*f0687c8aSRaman Tenneti
main(int argc,char ** argv)1109*f0687c8aSRaman Tenneti int main(int argc, char** argv)
1110*f0687c8aSRaman Tenneti {
1111*f0687c8aSRaman Tenneti vector<Arg> output_args = parse_cmdline(argc, argv);
1112*f0687c8aSRaman Tenneti
1113*f0687c8aSRaman Tenneti Card card(s_device_path);
1114*f0687c8aSRaman Tenneti
1115*f0687c8aSRaman Tenneti if (!card.is_master())
1116*f0687c8aSRaman Tenneti EXIT("Could not get DRM master permission. Card already in use?");
1117*f0687c8aSRaman Tenneti
1118*f0687c8aSRaman Tenneti if (!card.has_atomic() && s_flip_sync)
1119*f0687c8aSRaman Tenneti EXIT("Synchronized flipping requires atomic modesetting");
1120*f0687c8aSRaman Tenneti
1121*f0687c8aSRaman Tenneti ResourceManager resman(card);
1122*f0687c8aSRaman Tenneti
1123*f0687c8aSRaman Tenneti vector<OutputInfo> outputs = setups_to_outputs(card, resman, output_args);
1124*f0687c8aSRaman Tenneti
1125*f0687c8aSRaman Tenneti if (!s_flip_mode)
1126*f0687c8aSRaman Tenneti draw_test_patterns(outputs);
1127*f0687c8aSRaman Tenneti
1128*f0687c8aSRaman Tenneti print_outputs(outputs);
1129*f0687c8aSRaman Tenneti
1130*f0687c8aSRaman Tenneti set_crtcs_n_planes(card, outputs);
1131*f0687c8aSRaman Tenneti
1132*f0687c8aSRaman Tenneti fmt::print("press enter to exit\n");
1133*f0687c8aSRaman Tenneti
1134*f0687c8aSRaman Tenneti if (s_flip_mode)
1135*f0687c8aSRaman Tenneti main_flip(card, outputs);
1136*f0687c8aSRaman Tenneti else
1137*f0687c8aSRaman Tenneti getchar();
1138*f0687c8aSRaman Tenneti }
1139