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
get_possible_crtcs() const198 vector<Crtc*> Connector::get_possible_crtcs() const
199 {
200 vector<Crtc*> crtcs;
201
202 for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) {
203 auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]);
204
205 auto l = enc->get_possible_crtcs();
206
207 crtcs.insert(crtcs.end(), l.begin(), l.end());
208 }
209
210 return crtcs;
211 }
212
get_current_crtc() const213 Crtc* Connector::get_current_crtc() const
214 {
215 if (m_current_encoder)
216 return m_current_encoder->get_crtc();
217 else
218 return 0;
219 }
220
connector_type() const221 uint32_t Connector::connector_type() const
222 {
223 return m_priv->drm_connector->connector_type;
224 }
225
connector_type_id() const226 uint32_t Connector::connector_type_id() const
227 {
228 return m_priv->drm_connector->connector_type_id;
229 }
230
mmWidth() const231 uint32_t Connector::mmWidth() const
232 {
233 return m_priv->drm_connector->mmWidth;
234 }
235
mmHeight() const236 uint32_t Connector::mmHeight() const
237 {
238 return m_priv->drm_connector->mmHeight;
239 }
240
subpixel() const241 uint32_t Connector::subpixel() const
242 {
243 return m_priv->drm_connector->subpixel;
244 }
245
subpixel_str() const246 const string& Connector::subpixel_str() const
247 {
248 return subpix_str.at(subpixel());
249 }
250
get_modes() const251 std::vector<Videomode> Connector::get_modes() const
252 {
253 vector<Videomode> modes;
254
255 for (int i = 0; i < m_priv->drm_connector->count_modes; i++)
256 modes.push_back(drm_mode_to_video_mode(
257 m_priv->drm_connector->modes[i]));
258
259 return modes;
260 }
261
get_encoders() const262 std::vector<Encoder*> Connector::get_encoders() const
263 {
264 vector<Encoder*> encoders;
265
266 for (int i = 0; i < m_priv->drm_connector->count_encoders; i++) {
267 auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]);
268 encoders.push_back(enc);
269 }
270 return encoders;
271 }
272
273 }
274