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