• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <cstdio>
2 #include <iostream>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <cassert>
6 #include <cmath>
7 
8 #include <kms++/kms++.h>
9 #include "helpers.h"
10 
11 using namespace std;
12 
13 namespace kms
14 {
15 #ifndef DRM_MODE_CONNECTOR_DPI
16 #define DRM_MODE_CONNECTOR_DPI 17
17 #endif
18 
19 static const map<int, string> connector_names = {
20 	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
21 	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
22 	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
23 	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
24 	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
25 	{ DRM_MODE_CONNECTOR_Composite, "Composite" },
26 	{ DRM_MODE_CONNECTOR_SVIDEO, "S-Video" },
27 	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
28 	{ DRM_MODE_CONNECTOR_Component, "Component" },
29 	{ DRM_MODE_CONNECTOR_9PinDIN, "9-Pin-DIN" },
30 	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
31 	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
32 	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
33 	{ DRM_MODE_CONNECTOR_TV, "TV" },
34 	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
35 	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
36 	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
37 	{ DRM_MODE_CONNECTOR_DPI, "DPI" },
38 };
39 
40 static const map<int, string> connection_str = {
41 	{ 0, "<unknown>" },
42 	{ DRM_MODE_CONNECTED, "Connected" },
43 	{ DRM_MODE_DISCONNECTED, "Disconnected" },
44 	{ DRM_MODE_UNKNOWNCONNECTION, "Unknown" },
45 };
46 
47 static const map<int, string> subpix_str = {
48 #define DEF_SUBPIX(c)                     \
49 	{                                 \
50 		DRM_MODE_SUBPIXEL_##c, #c \
51 	}
52 	DEF_SUBPIX(UNKNOWN),
53 	DEF_SUBPIX(HORIZONTAL_RGB),
54 	DEF_SUBPIX(HORIZONTAL_BGR),
55 	DEF_SUBPIX(VERTICAL_RGB),
56 	DEF_SUBPIX(VERTICAL_BGR),
57 	DEF_SUBPIX(NONE),
58 #undef DEF_SUBPIX
59 };
60 
61 struct ConnectorPriv {
62 	drmModeConnectorPtr drm_connector;
63 };
64 
Connector(Card & card,uint32_t id,uint32_t idx)65 Connector::Connector(Card& card, uint32_t id, uint32_t idx)
66 	: DrmPropObject(card, id, DRM_MODE_OBJECT_CONNECTOR, idx)
67 {
68 	m_priv = new ConnectorPriv();
69 
70 	m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id());
71 	assert(m_priv->drm_connector);
72 
73 	// XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id.
74 	// XXX So refresh the props again here.
75 	refresh_props();
76 
77 	const auto& name = connector_names.at(m_priv->drm_connector->connector_type);
78 	m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id);
79 }
80 
~Connector()81 Connector::~Connector()
82 {
83 	drmModeFreeConnector(m_priv->drm_connector);
84 	delete m_priv;
85 }
86 
refresh()87 void Connector::refresh()
88 {
89 	drmModeFreeConnector(m_priv->drm_connector);
90 
91 	m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id());
92 	assert(m_priv->drm_connector);
93 
94 	// XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id.
95 	// XXX So refresh the props again here.
96 	refresh_props();
97 
98 	const auto& name = connector_names.at(m_priv->drm_connector->connector_type);
99 	m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id);
100 }
101 
setup()102 void Connector::setup()
103 {
104 	if (m_priv->drm_connector->encoder_id != 0)
105 		m_current_encoder = card().get_encoder(m_priv->drm_connector->encoder_id);
106 	else
107 		m_current_encoder = 0;
108 
109 	if (m_current_encoder)
110 		m_saved_crtc = m_current_encoder->get_crtc();
111 	else
112 		m_saved_crtc = 0;
113 }
114 
restore_mode()115 void Connector::restore_mode()
116 {
117 	if (m_saved_crtc)
118 		m_saved_crtc->restore_mode(this);
119 }
120 
get_default_mode() const121 Videomode Connector::get_default_mode() const
122 {
123 	if (m_priv->drm_connector->count_modes == 0)
124 		return Videomode();
125 
126 	drmModeModeInfo drmmode = m_priv->drm_connector->modes[0];
127 
128 	return drm_mode_to_video_mode(drmmode);
129 }
130 
get_mode(const string & mode) const131 Videomode Connector::get_mode(const string& mode) const
132 {
133 	auto c = m_priv->drm_connector;
134 
135 	size_t idx = mode.find('@');
136 
137 	string name = idx == string::npos ? mode : mode.substr(0, idx);
138 	float vrefresh = idx == string::npos ? 0.0 : stod(mode.substr(idx + 1));
139 
140 	for (int i = 0; i < c->count_modes; i++) {
141 		Videomode m = drm_mode_to_video_mode(c->modes[i]);
142 
143 		if (m.name != name)
144 			continue;
145 
146 		if (vrefresh && vrefresh != m.calculated_vrefresh())
147 			continue;
148 
149 		return m;
150 	}
151 
152 	throw invalid_argument(mode + ": mode not found");
153 }
154 
get_mode(unsigned xres,unsigned yres,float vrefresh,bool ilace) const155 Videomode Connector::get_mode(unsigned xres, unsigned yres, float vrefresh, bool ilace) const
156 {
157 	auto c = m_priv->drm_connector;
158 
159 	for (int i = 0; i < c->count_modes; i++) {
160 		Videomode m = drm_mode_to_video_mode(c->modes[i]);
161 
162 		if (m.hdisplay != xres || m.vdisplay != yres)
163 			continue;
164 
165 		if (ilace != m.interlace())
166 			continue;
167 
168 		if (vrefresh && vrefresh != m.calculated_vrefresh())
169 			continue;
170 
171 		return m;
172 	}
173 
174 	// If not found, do another round using rounded vrefresh
175 
176 	for (int i = 0; i < c->count_modes; i++) {
177 		Videomode m = drm_mode_to_video_mode(c->modes[i]);
178 
179 		if (m.hdisplay != xres || m.vdisplay != yres)
180 			continue;
181 
182 		if (ilace != m.interlace())
183 			continue;
184 
185 		if (vrefresh && vrefresh != roundf(m.calculated_vrefresh()))
186 			continue;
187 
188 		return m;
189 	}
190 
191 	throw invalid_argument("mode not found");
192 }
193 
connected() const194 bool Connector::connected() const
195 {
196 	return m_priv->drm_connector->connection == DRM_MODE_CONNECTED ||
197 	       m_priv->drm_connector->connection == DRM_MODE_UNKNOWNCONNECTION;
198 }
199 
connector_status() const200 ConnectorStatus Connector::connector_status() const
201 {
202 	switch (m_priv->drm_connector->connection) {
203 	case DRM_MODE_CONNECTED:
204 		return ConnectorStatus::Connected;
205 	case DRM_MODE_DISCONNECTED:
206 		return ConnectorStatus::Disconnected;
207 	default:
208 		return ConnectorStatus::Unknown;
209 	}
210 }
211 
get_possible_crtcs() const212 vector<Crtc*> Connector::get_possible_crtcs() const
213 {
214 	vector<Crtc*> crtcs;
215 
216 	for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) {
217 		auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]);
218 
219 		auto l = enc->get_possible_crtcs();
220 
221 		crtcs.insert(crtcs.end(), l.begin(), l.end());
222 	}
223 
224 	return crtcs;
225 }
226 
get_current_crtc() const227 Crtc* Connector::get_current_crtc() const
228 {
229 	if (m_current_encoder)
230 		return m_current_encoder->get_crtc();
231 	else
232 		return 0;
233 }
234 
connector_type() const235 uint32_t Connector::connector_type() const
236 {
237 	return m_priv->drm_connector->connector_type;
238 }
239 
connector_type_id() const240 uint32_t Connector::connector_type_id() const
241 {
242 	return m_priv->drm_connector->connector_type_id;
243 }
244 
mmWidth() const245 uint32_t Connector::mmWidth() const
246 {
247 	return m_priv->drm_connector->mmWidth;
248 }
249 
mmHeight() const250 uint32_t Connector::mmHeight() const
251 {
252 	return m_priv->drm_connector->mmHeight;
253 }
254 
subpixel() const255 uint32_t Connector::subpixel() const
256 {
257 	return m_priv->drm_connector->subpixel;
258 }
259 
subpixel_str() const260 const string& Connector::subpixel_str() const
261 {
262 	return subpix_str.at(subpixel());
263 }
264 
get_modes() const265 std::vector<Videomode> Connector::get_modes() const
266 {
267 	vector<Videomode> modes;
268 
269 	for (int i = 0; i < m_priv->drm_connector->count_modes; i++)
270 		modes.push_back(drm_mode_to_video_mode(
271 			m_priv->drm_connector->modes[i]));
272 
273 	return modes;
274 }
275 
get_encoders() const276 std::vector<Encoder*> Connector::get_encoders() const
277 {
278 	vector<Encoder*> encoders;
279 
280 	for (int i = 0; i < m_priv->drm_connector->count_encoders; i++) {
281 		auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]);
282 		encoders.push_back(enc);
283 	}
284 	return encoders;
285 }
286 
287 } // namespace kms
288