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