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 42static 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 66static 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 81static 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 102static 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 128out: 129 strncpy(buf, "unknown path", len); 130} 131 132static 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 144static 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 190static 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 253static 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 269static 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 282static 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 318int 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