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