• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <xf86drm.h>
2 #include <xf86drmMode.h>
3 #include <cmath>
4 #include <sstream>
5 #include <fmt/format.h>
6 
7 #include <kms++/kms++.h>
8 #include "helpers.h"
9 
10 using namespace std;
11 
12 namespace kms
13 {
valid() const14 bool Videomode::valid() const
15 {
16 	return !!clock;
17 }
18 
to_blob(Card & card) const19 unique_ptr<Blob> Videomode::to_blob(Card& card) const
20 {
21 	drmModeModeInfo drm_mode = video_mode_to_drm_mode(*this);
22 
23 	return unique_ptr<Blob>(new Blob(card, &drm_mode, sizeof(drm_mode)));
24 }
25 
calculated_vrefresh() const26 float Videomode::calculated_vrefresh() const
27 {
28 	// XXX interlace should only halve visible vertical lines, not blanking
29 	float refresh = (clock * 1000.0) / (htotal * vtotal) * (interlace() ? 2 : 1);
30 	return roundf(refresh * 100.0) / 100.0;
31 }
32 
interlace() const33 bool Videomode::interlace() const
34 {
35 	return flags & DRM_MODE_FLAG_INTERLACE;
36 }
37 
hsync() const38 SyncPolarity Videomode::hsync() const
39 {
40 	if (flags & DRM_MODE_FLAG_PHSYNC)
41 		return SyncPolarity::Positive;
42 	if (flags & DRM_MODE_FLAG_NHSYNC)
43 		return SyncPolarity::Negative;
44 	return SyncPolarity::Undefined;
45 }
46 
vsync() const47 SyncPolarity Videomode::vsync() const
48 {
49 	if (flags & DRM_MODE_FLAG_PVSYNC)
50 		return SyncPolarity::Positive;
51 	if (flags & DRM_MODE_FLAG_NVSYNC)
52 		return SyncPolarity::Negative;
53 	return SyncPolarity::Undefined;
54 }
55 
set_interlace(bool ilace)56 void Videomode::set_interlace(bool ilace)
57 {
58 	if (ilace)
59 		flags |= DRM_MODE_FLAG_INTERLACE;
60 	else
61 		flags &= ~DRM_MODE_FLAG_INTERLACE;
62 }
63 
set_hsync(SyncPolarity pol)64 void Videomode::set_hsync(SyncPolarity pol)
65 {
66 	flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC);
67 
68 	switch (pol) {
69 	case SyncPolarity::Positive:
70 		flags |= DRM_MODE_FLAG_PHSYNC;
71 		break;
72 	case SyncPolarity::Negative:
73 		flags |= DRM_MODE_FLAG_NHSYNC;
74 		break;
75 	default:
76 		break;
77 	}
78 }
79 
set_vsync(SyncPolarity pol)80 void Videomode::set_vsync(SyncPolarity pol)
81 {
82 	flags &= ~(DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC);
83 
84 	switch (pol) {
85 	case SyncPolarity::Positive:
86 		flags |= DRM_MODE_FLAG_PVSYNC;
87 		break;
88 	case SyncPolarity::Negative:
89 		flags |= DRM_MODE_FLAG_NVSYNC;
90 		break;
91 	default:
92 		break;
93 	}
94 }
95 
to_string_short() const96 string Videomode::to_string_short() const
97 {
98 	return fmt::format("{}x{}{}@{:.2f}", hdisplay, vdisplay, interlace() ? "i" : "", calculated_vrefresh());
99 }
100 
sync_to_char(SyncPolarity pol)101 static char sync_to_char(SyncPolarity pol)
102 {
103 	switch (pol) {
104 	case SyncPolarity::Positive:
105 		return '+';
106 	case SyncPolarity::Negative:
107 		return '-';
108 	default:
109 		return '?';
110 	}
111 }
112 
113 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
114 
115 template<typename T>
join(const T & values,const std::string & delim)116 std::string join(const T& values, const std::string& delim)
117 {
118 	std::ostringstream ss;
119 	for (const auto& v : values) {
120 		if (&v != &values[0])
121 			ss << delim;
122 		ss << v;
123 	}
124 	return ss.str();
125 }
126 
127 static const map<int, string> mode_type_map = {
128 	// the deprecated ones don't care about a short name
129 	{ DRM_MODE_TYPE_BUILTIN, "builtin" }, // deprecated
130 	{ DRM_MODE_TYPE_CLOCK_C, "clock_c" }, // deprecated
131 	{ DRM_MODE_TYPE_CRTC_C, "crtc_c" }, // deprecated
132 	{ DRM_MODE_TYPE_PREFERRED, "P" },
133 	{ DRM_MODE_TYPE_DEFAULT, "default" }, // deprecated
134 	{ DRM_MODE_TYPE_USERDEF, "U" },
135 	{ DRM_MODE_TYPE_DRIVER, "D" },
136 };
137 
138 static const map<int, string> mode_flag_map = {
139 	// the first 5 flags are displayed elsewhere
140 	{ DRM_MODE_FLAG_PHSYNC, "" },
141 	{ DRM_MODE_FLAG_NHSYNC, "" },
142 	{ DRM_MODE_FLAG_PVSYNC, "" },
143 	{ DRM_MODE_FLAG_NVSYNC, "" },
144 	{ DRM_MODE_FLAG_INTERLACE, "" },
145 	{ DRM_MODE_FLAG_DBLSCAN, "dblscan" },
146 	{ DRM_MODE_FLAG_CSYNC, "csync" },
147 	{ DRM_MODE_FLAG_PCSYNC, "pcsync" },
148 	{ DRM_MODE_FLAG_NCSYNC, "ncsync" },
149 	{ DRM_MODE_FLAG_HSKEW, "hskew" },
150 	{ DRM_MODE_FLAG_BCAST, "bcast" }, // deprecated
151 	{ DRM_MODE_FLAG_PIXMUX, "pixmux" }, // deprecated
152 	{ DRM_MODE_FLAG_DBLCLK, "2x" },
153 	{ DRM_MODE_FLAG_CLKDIV2, "clkdiv2" },
154 };
155 
156 static const map<int, string> mode_3d_map = {
157 	{ DRM_MODE_FLAG_3D_NONE, "" },
158 	{ DRM_MODE_FLAG_3D_FRAME_PACKING, "3dfp" },
159 	{ DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE, "3dfa" },
160 	{ DRM_MODE_FLAG_3D_LINE_ALTERNATIVE, "3dla" },
161 	{ DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL, "3dsbs" },
162 	{ DRM_MODE_FLAG_3D_L_DEPTH, "3dldepth" },
163 	{ DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH, "3dgfx" },
164 	{ DRM_MODE_FLAG_3D_TOP_AND_BOTTOM, "3dtab" },
165 	{ DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF, "3dsbs" },
166 };
167 
168 static const map<int, string> mode_aspect_map = {
169 	{ DRM_MODE_FLAG_PIC_AR_NONE, "" },
170 	{ DRM_MODE_FLAG_PIC_AR_4_3, "4:3" },
171 	{ DRM_MODE_FLAG_PIC_AR_16_9, "16:9" },
172 	{ DRM_MODE_FLAG_PIC_AR_64_27, "64:27" },
173 	{ DRM_MODE_FLAG_PIC_AR_256_135, "256:135" },
174 };
175 
mode_type_str(uint32_t val)176 static string mode_type_str(uint32_t val)
177 {
178 	vector<string> s;
179 	for (const auto& [k, v] : mode_type_map) {
180 		if (val & k) {
181 			if (!v.empty())
182 				s.push_back(v);
183 			val &= ~k;
184 		}
185 	}
186 	// any unknown bits
187 	if (val != 0)
188 		s.push_back(fmt::format("{:#x}", val));
189 	return join(s, "|");
190 }
191 
mode_flag_str(uint32_t val)192 static string mode_flag_str(uint32_t val)
193 {
194 	vector<string> s;
195 	for (const auto& [k, v] : mode_flag_map) {
196 		if (val & k) {
197 			if (!v.empty())
198 				s.push_back(v);
199 			val &= ~k;
200 		}
201 	}
202 	auto it = mode_3d_map.find(val & DRM_MODE_FLAG_3D_MASK);
203 	if (it != mode_3d_map.end()) {
204 		if (!it->second.empty())
205 			s.push_back(it->second);
206 		val &= ~DRM_MODE_FLAG_3D_MASK;
207 	}
208 	it = mode_aspect_map.find(val & DRM_MODE_FLAG_PIC_AR_MASK);
209 	if (it != mode_aspect_map.end()) {
210 		if (!it->second.empty())
211 			s.push_back(it->second);
212 		val &= ~DRM_MODE_FLAG_PIC_AR_MASK;
213 	}
214 	// any unknown bits
215 	if (val != 0)
216 		s.push_back(fmt::format("{:#x}", val));
217 	return join(s, "|");
218 }
219 
to_string_long() const220 string Videomode::to_string_long() const
221 {
222 	string h = fmt::format("{}/{}/{}/{}/{}", hdisplay, hfp(), hsw(), hbp(), sync_to_char(hsync()));
223 	string v = fmt::format("{}/{}/{}/{}/{}", vdisplay, vfp(), vsw(), vbp(), sync_to_char(vsync()));
224 
225 	string str = fmt::format("{} {:.3f} {} {} {} ({:.2f}) {} {}",
226 				 to_string_short(),
227 				 clock / 1000.0,
228 				 h, v,
229 				 vrefresh, calculated_vrefresh(),
230 				 mode_type_str(type),
231 				 mode_flag_str(flags));
232 
233 	return str;
234 }
235 
to_string_long_padded() const236 string Videomode::to_string_long_padded() const
237 {
238 	string h = fmt::format("{}/{}/{}/{}/{}", hdisplay, hfp(), hsw(), hbp(), sync_to_char(hsync()));
239 	string v = fmt::format("{}/{}/{}/{}/{}", vdisplay, vfp(), vsw(), vbp(), sync_to_char(vsync()));
240 
241 	string str = fmt::format("{:<16} {:7.3f} {:<18} {:<18} {:2} ({:.2f}) {:<5} {}",
242 				 to_string_short(),
243 				 clock / 1000.0,
244 				 h, v,
245 				 vrefresh, calculated_vrefresh(),
246 				 mode_type_str(type),
247 				 mode_flag_str(flags));
248 
249 	return str;
250 }
251 
videomode_from_timings(uint32_t clock_khz,uint16_t hact,uint16_t hfp,uint16_t hsw,uint16_t hbp,uint16_t vact,uint16_t vfp,uint16_t vsw,uint16_t vbp)252 Videomode videomode_from_timings(uint32_t clock_khz,
253 				 uint16_t hact, uint16_t hfp, uint16_t hsw, uint16_t hbp,
254 				 uint16_t vact, uint16_t vfp, uint16_t vsw, uint16_t vbp)
255 {
256 	Videomode m{};
257 	m.clock = clock_khz;
258 
259 	m.hdisplay = hact;
260 	m.hsync_start = hact + hfp;
261 	m.hsync_end = hact + hfp + hsw;
262 	m.htotal = hact + hfp + hsw + hbp;
263 
264 	m.vdisplay = vact;
265 	m.vsync_start = vact + vfp;
266 	m.vsync_end = vact + vfp + vsw;
267 	m.vtotal = vact + vfp + vsw + vbp;
268 
269 	return m;
270 }
271 
272 } // namespace kms
273