1 /*
2 * Copyright (c) 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * To compile standalone: gcc -o dri3info dri3info.c `pkg-config --cflags --libs xcb-dri3 x11-xcb xrandr xxf86vm libdrm`
24 */
25
26 #include <X11/Xlib.h>
27 #include <X11/Xlib-xcb.h>
28 #include <xcb/xcb.h>
29 #include <xcb/dri3.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <drm.h>
37 #include <xf86drm.h>
38
39 #include <X11/extensions/Xrandr.h>
40 #include <X11/extensions/xf86vmode.h>
41
dri3_query_version(Display * dpy,int * major,int * minor)42 static int dri3_query_version(Display *dpy, int *major, int *minor)
43 {
44 xcb_connection_t *c = XGetXCBConnection(dpy);
45 xcb_dri3_query_version_reply_t *reply;
46 xcb_generic_error_t *error;
47
48 *major = *minor = -1;
49
50 reply = xcb_dri3_query_version_reply(c,
51 xcb_dri3_query_version(c,
52 XCB_DRI3_MAJOR_VERSION,
53 XCB_DRI3_MINOR_VERSION),
54 &error);
55 free(error);
56 if (reply == NULL)
57 return -1;
58
59 *major = reply->major_version;
60 *minor = reply->minor_version;
61 free(reply);
62
63 return 0;
64 }
65
dri3_exists(Display * dpy)66 static int dri3_exists(Display *dpy)
67 {
68 const xcb_query_extension_reply_t *ext;
69 int major, minor;
70
71 ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id);
72 if (ext == NULL || !ext->present)
73 return 0;
74
75 if (dri3_query_version(dpy, &major, &minor) < 0)
76 return 0;
77
78 return major >= 0;
79 }
80
dri3_open(Display * dpy)81 static int dri3_open(Display *dpy)
82 {
83 xcb_connection_t *c = XGetXCBConnection(dpy);
84 xcb_dri3_open_cookie_t cookie;
85 xcb_dri3_open_reply_t *reply;
86
87 if (!dri3_exists(dpy))
88 return -1;
89
90 cookie = xcb_dri3_open(c, RootWindow(dpy, DefaultScreen(dpy)), None);
91 reply = xcb_dri3_open_reply(c, cookie, NULL);
92
93 if (!reply)
94 return -1;
95
96 if (reply->nfd != 1)
97 return -1;
98
99 return xcb_dri3_open_reply_fds(c, reply)[0];
100 }
101
get_device_path(int fd,char * buf,int len)102 static void get_device_path(int fd, char *buf, int len)
103 {
104 struct stat remote, local;
105 int i;
106
107 if (fstat(fd, &remote))
108 goto out;
109
110 for (i = 0; i < 16; i++) {
111 snprintf(buf, len, "/dev/dri/card%d", i);
112 if (stat(buf, &local))
113 continue;
114
115 if (local.st_mode == remote.st_mode &&
116 local.st_rdev == remote.st_rdev)
117 return;
118
119 snprintf(buf, len, "/dev/dri/renderD%d", i + 128);
120 if (stat(buf, &local))
121 continue;
122
123 if (local.st_mode == remote.st_mode &&
124 local.st_rdev == remote.st_rdev)
125 return;
126 }
127
128 out:
129 strncpy(buf, "unknown path", len);
130 }
131
get_driver_name(int fd,char * name,int len)132 static void get_driver_name(int fd, char *name, int len)
133 {
134 drm_version_t version;
135
136 memset(name, 0, len);
137 memset(&version, 0, sizeof(version));
138 version.name_len = len;
139 version.name = name;
140
141 (void)drmIoctl(fd, DRM_IOCTL_VERSION, &version);
142 }
143
compute_refresh_rate_from_mode(long n,long d,unsigned flags,int32_t * numerator,int32_t * denominator)144 static int compute_refresh_rate_from_mode(long n, long d, unsigned flags,
145 int32_t *numerator,
146 int32_t *denominator)
147 {
148 int i;
149
150 /* The mode flags are only defined privately to the Xserver (in xf86str.h)
151 * but they at least bit compatible between VidMode, RandR and DRM.
152 */
153 # define V_INTERLACE 0x010
154 # define V_DBLSCAN 0x020
155
156 if (flags & V_INTERLACE)
157 n *= 2;
158 else if (flags & V_DBLSCAN)
159 d *= 2;
160
161 /* The OML_sync_control spec requires that if the refresh rate is a
162 * whole number, that the returned numerator be equal to the refresh
163 * rate and the denominator be 1.
164 */
165
166 if (n % d == 0) {
167 n /= d;
168 d = 1;
169 }
170 else {
171 static const unsigned f[] = { 13, 11, 7, 5, 3, 2, 0 };
172
173 /* This is a poor man's way to reduce a fraction. It's far from
174 * perfect, but it will work well enough for this situation.
175 */
176
177 for (i = 0; f[i] != 0; i++) {
178 while (n % f[i] == 0 && d % f[i] == 0) {
179 d /= f[i];
180 n /= f[i];
181 }
182 }
183 }
184
185 *numerator = n;
186 *denominator = d;
187 return 1;
188 }
189
RRGetMscRate(Display * dpy,int32_t * numerator,int32_t * denominator)190 static int RRGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator)
191 {
192 int ret = 0;
193 Window root = RootWindow(dpy, DefaultScreen(dpy));
194 XRRScreenResources *res;
195 int rr_event, rr_error;
196 RROutput primary;
197 RRMode mode = 0;
198 int n;
199
200 if (!XRRQueryExtension(dpy, &rr_event, &rr_error))
201 return ret;
202
203 res = XRRGetScreenResourcesCurrent(dpy, root);
204 if (res == NULL)
205 return ret;
206
207 /* Use the primary output if specified, otherwise
208 * use the mode on the first enabled crtc.
209 */
210 primary = XRRGetOutputPrimary(dpy, root);
211 if (primary) {
212 XRROutputInfo *output;
213
214 output = XRRGetOutputInfo(dpy, res, primary);
215 if (output != NULL) {
216 if (output->crtc) {
217 XRRCrtcInfo *crtc;
218
219 crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
220 if (crtc) {
221 mode = crtc->mode;
222 XRRFreeCrtcInfo(crtc);
223 }
224 }
225 XRRFreeOutputInfo(output);
226 }
227 }
228
229 for (n = 0; mode == 0 && n < res->ncrtc; n++) {
230 XRRCrtcInfo *crtc;
231
232 crtc = XRRGetCrtcInfo(dpy, res, res->crtcs[n]);
233 if (crtc) {
234 mode = crtc->mode;
235 XRRFreeCrtcInfo(crtc);
236 }
237 }
238
239 for (n = 0; n < res->nmode; n++) {
240 if (res->modes[n].id == mode) {
241 ret = compute_refresh_rate_from_mode(res->modes[n].dotClock,
242 res->modes[n].hTotal*res->modes[n].vTotal,
243 res->modes[n].modeFlags,
244 numerator, denominator);
245 break;
246 }
247 }
248
249 XRRFreeScreenResources(res);
250 return ret;
251 }
252
VMGetMscRate(Display * dpy,int32_t * numerator,int32_t * denominator)253 static int VMGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator)
254 {
255 XF86VidModeModeLine mode_line;
256 int dot_clock;
257 int i;
258
259 if (XF86VidModeQueryVersion(dpy, &i, &i) &&
260 XF86VidModeGetModeLine(dpy, DefaultScreen(dpy), &dot_clock, &mode_line))
261 return compute_refresh_rate_from_mode(dot_clock * 1000,
262 mode_line.vtotal * mode_line.htotal,
263 mode_line.flags,
264 numerator, denominator);
265
266 return 0;
267 }
268
get_refresh_rate(Display * dpy,int32_t * numerator,int32_t * denominator)269 static int get_refresh_rate(Display *dpy,
270 int32_t *numerator,
271 int32_t *denominator)
272 {
273 if (RRGetMscRate(dpy, numerator, denominator))
274 return 1;
275
276 if (VMGetMscRate(dpy, numerator, denominator))
277 return 1;
278
279 return 0;
280 }
281
info(const char * dpyname)282 static void info(const char *dpyname)
283 {
284 Display *dpy;
285 int device;
286 int32_t numerator, denominator;
287
288 dpy = XOpenDisplay(dpyname);
289 if (dpy == NULL) {
290 printf("Unable to connect to display '%s'\n",
291 dpyname ?: getenv("DISPLAY") ?: "unset");
292 return;
293 }
294
295 printf("Display '%s'\n", DisplayString(dpy));
296 device = dri3_open(dpy);
297 if (device < 0) {
298 printf("\tUnable to connect to DRI3\n");
299 } else {
300 char device_path[1024];
301 char driver_name[1024];
302
303 get_device_path(device, device_path, sizeof(device_path));
304 get_driver_name(device, driver_name, sizeof(driver_name));
305
306 printf("Connected to DRI3, using fd %d which matches %s, driver %s\n",
307 device, device_path, driver_name);
308 close(device);
309 }
310
311 if (get_refresh_rate(dpy, &numerator, &denominator))
312 printf("\tPrimary refresh rate: %d/%d (%.1fHz)\n",
313 numerator, denominator, numerator/(float)denominator);
314
315 XCloseDisplay(dpy);
316 }
317
main(int argc,char ** argv)318 int main(int argc, char **argv)
319 {
320 int i;
321
322 if (argc > 1) {
323 for (i = 1; i < argc; i++)
324 info(argv[i]);
325 } else
326 info(NULL);
327
328 return 0;
329 }
330