1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <inttypes.h>
9 #include <getopt.h>
10
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13
14 #include "gfx.h"
15 #include "drmtools.h"
16
17 /* ------------------------------------------------------------------ */
18
19 /* device */
20 int drm_fd;
21 drmModeEncoder *drm_enc = NULL;
22 drmModeModeInfo *drm_mode = NULL;
23 drmModeConnector *drm_conn = NULL;
24 static drmModeCrtc *scrtc = NULL;
25
26 struct drmfb {
27 uint32_t id;
28 struct drm_mode_create_dumb creq;
29 uint8_t *mem;
30 } fb1, fb2, *fbc;
31
32 /* ------------------------------------------------------------------ */
33
34 static const char *conn_type[] = {
35 [ DRM_MODE_CONNECTOR_Unknown ] = "unknown",
36 [ DRM_MODE_CONNECTOR_VGA ] = "vga",
37 [ DRM_MODE_CONNECTOR_DVII ] = "dvi-i",
38 [ DRM_MODE_CONNECTOR_DVID ] = "dvi-d",
39 [ DRM_MODE_CONNECTOR_DVIA ] = "dvi-a",
40 [ DRM_MODE_CONNECTOR_Composite ] = "composite",
41 [ DRM_MODE_CONNECTOR_SVIDEO ] = "svideo",
42 [ DRM_MODE_CONNECTOR_LVDS ] = "lvds",
43 [ DRM_MODE_CONNECTOR_Component ] = "component",
44 [ DRM_MODE_CONNECTOR_9PinDIN ] = "9pin-din",
45 [ DRM_MODE_CONNECTOR_DisplayPort ] = "dp",
46 [ DRM_MODE_CONNECTOR_HDMIA ] = "hdmi-a",
47 [ DRM_MODE_CONNECTOR_HDMIB ] = "hdmi-b",
48 [ DRM_MODE_CONNECTOR_TV ] = "tv",
49 [ DRM_MODE_CONNECTOR_eDP ] = "edp",
50 [ DRM_MODE_CONNECTOR_VIRTUAL ] = "virtual",
51 [ DRM_MODE_CONNECTOR_DSI ] = "dsi",
52 };
53
drm_conn_name(drmModeConnector * conn,char * dest,int dlen)54 static void drm_conn_name(drmModeConnector *conn, char *dest, int dlen)
55 {
56 const char *type;
57
58 if (conn->connector_type_id < sizeof(conn_type)/sizeof(conn_type[0]) &&
59 conn_type[conn->connector_type]) {
60 type = conn_type[conn->connector_type];
61 } else {
62 type = "unknown";
63 }
64 snprintf(dest, dlen, "%s-%d", type, conn->connector_type_id);
65 }
66
67 /* ------------------------------------------------------------------ */
68
drm_cleanup_display(void)69 void drm_cleanup_display(void)
70 {
71 /* restore crtc */
72 if (scrtc) {
73 drmModeSetCrtc(drm_fd, scrtc->crtc_id, scrtc->buffer_id, scrtc->x, scrtc->y,
74 &drm_conn->connector_id, 1, &scrtc->mode);
75 }
76 }
77
drm_init_dev(const char * dev,const char * output)78 int drm_init_dev(const char *dev, const char *output)
79 {
80 drmModeRes *res;
81 char name[64];
82 uint64_t has_dumb;
83 int i, rc;
84
85 /* open device */
86 drm_fd = open(dev, O_RDWR);
87 if (drm_fd < 0) {
88 fprintf(stderr, "drm: open %s: %s\n", dev, strerror(errno));
89 return -1;
90 }
91
92 rc = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &has_dumb);
93 if (rc < 0 || !has_dumb) {
94 fprintf(stderr, "drm: no dumb buffer support\n");
95 return -1;
96 }
97
98 /* find connector (using first for now) */
99 res = drmModeGetResources(drm_fd);
100 if (res == NULL) {
101 fprintf(stderr, "drm: drmModeGetResources() failed\n");
102 return -1;
103 }
104 for (i = 0; i < res->count_connectors; i++) {
105 drm_conn = drmModeGetConnector(drm_fd, res->connectors[i]);
106 if (drm_conn &&
107 (drm_conn->connection == DRM_MODE_CONNECTED) &&
108 drm_conn->count_modes) {
109 if (output) {
110 drm_conn_name(drm_conn, name, sizeof(name));
111 if (strcmp(name, output) == 0) {
112 break;
113 }
114 } else {
115 break;
116 }
117 }
118 drmModeFreeConnector(drm_conn);
119 drm_conn = NULL;
120 }
121 if (!drm_conn) {
122 if (output) {
123 fprintf(stderr, "drm: output %s not found or disconnected\n",
124 output);
125 } else {
126 fprintf(stderr, "drm: no usable output found\n");
127 }
128 return -1;
129 }
130 drm_mode = &drm_conn->modes[0];
131 drm_enc = drmModeGetEncoder(drm_fd, drm_conn->encoder_id);
132 if (drm_enc == NULL) {
133 fprintf(stderr, "drm: drmModeGetEncoder() failed\n");
134 return -1;
135 }
136
137 /* save crtc */
138 scrtc = drmModeGetCrtc(drm_fd, drm_enc->crtc_id);
139 return 0;
140 }
141
drm_init_fb(struct drmfb * fb)142 static int drm_init_fb(struct drmfb *fb)
143 {
144 struct drm_mode_map_dumb mreq;
145 int rc;
146
147 /* create framebuffer */
148 memset(&fb->creq, 0, sizeof(fb->creq));
149 fb->creq.width = drm_mode->hdisplay;
150 fb->creq.height = drm_mode->vdisplay;
151 fb->creq.bpp = 32;
152 rc = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &fb->creq);
153 if (rc < 0) {
154 fprintf(stderr, "drm: DRM_IOCTL_MODE_CREATE_DUMB: %s\n", strerror(errno));
155 return -1;
156 }
157 rc = drmModeAddFB(drm_fd, fb->creq.width, fb->creq.height,
158 24, 32, fb->creq.pitch,
159 fb->creq.handle, &fb->id);
160 if (rc < 0) {
161 fprintf(stderr, "drm: drmModeAddFB() failed\n");
162 return -1;
163 }
164
165 /* map framebuffer */
166 memset(&mreq, 0, sizeof(mreq));
167 mreq.handle = fb->creq.handle;
168 rc = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
169 if (rc < 0) {
170 fprintf(stderr, "drm: DRM_IOCTL_MODE_MAP_DUMB: %s\n", strerror(errno));
171 return -1;
172 }
173 fb->mem = mmap(0, fb->creq.size, PROT_READ | PROT_WRITE, MAP_SHARED,
174 drm_fd, mreq.offset);
175 if (fb->mem == MAP_FAILED) {
176 fprintf(stderr, "drm: framebuffer mmap: %s\n", strerror(errno));
177 return -1;
178 }
179 return 0;
180 }
181
drm_show_fb(struct drmfb * fb)182 static int drm_show_fb(struct drmfb *fb)
183 {
184 int rc;
185
186 rc = drmModeSetCrtc(drm_fd, drm_enc->crtc_id, fb->id, 0, 0,
187 &drm_conn->connector_id, 1,
188 &drm_conn->modes[0]);
189 if (rc < 0) {
190 fprintf(stderr, "drm: drmModeSetCrtc() failed\n");
191 return -1;
192 }
193 return 0;
194 }
195
196 /* ------------------------------------------------------------------ */
197
drm_restore_display(void)198 static void drm_restore_display(void)
199 {
200 drm_show_fb(fbc);
201 }
202
drm_flush_display(bool second)203 static void drm_flush_display(bool second)
204 {
205 fbc = second ? &fb2 : &fb1;
206 drm_show_fb(fbc);
207 drmModeDirtyFB(drm_fd, fbc->id, 0, 0);
208 }
209
drm_init(const char * device,const char * output,bool pageflip)210 gfxstate *drm_init(const char *device, const char *output, bool pageflip)
211 {
212 gfxstate *gfx;
213 char dev[64];
214
215 if (device) {
216 snprintf(dev, sizeof(dev), "%s", device);
217 } else {
218 snprintf(dev, sizeof(dev), DRM_DEV_NAME, DRM_DIR_NAME, 0);
219 }
220 fprintf(stderr, "trying drm: %s ...\n", dev);
221
222 if (drm_init_dev(dev, output) < 0)
223 return NULL;
224 if (drm_init_fb(&fb1) < 0)
225 return NULL;
226 if (drm_show_fb(&fb1) < 0)
227 return NULL;
228
229 /* prepare gfx */
230 gfx = malloc(sizeof(*gfx));
231 memset(gfx, 0, sizeof(*gfx));
232
233 gfx->hdisplay = drm_mode->hdisplay;
234 gfx->vdisplay = drm_mode->vdisplay;
235 gfx->stride = fb1.creq.pitch;
236 gfx->mem = fb1.mem;
237
238 gfx->rlen = 8;
239 gfx->glen = 8;
240 gfx->blen = 8;
241 gfx->tlen = 8;
242 gfx->roff = 16;
243 gfx->goff = 8;
244 gfx->boff = 0;
245 gfx->toff = 24;
246 gfx->bits_per_pixel = 32;
247
248 gfx->restore_display = drm_restore_display;
249 gfx->cleanup_display = drm_cleanup_display;
250 gfx->flush_display = drm_flush_display;
251
252 if (pageflip) {
253 if (drm_init_fb(&fb2) == 0) {
254 gfx->mem2 = fb2.mem;
255 } else {
256 fprintf(stderr, "drm: can't alloc two fbs, pageflip disabled.\n");
257 }
258 }
259 return gfx;
260 }
261
262 /* ------------------------------------------------------------------ */
263
drm_info(const char * device)264 void drm_info(const char *device)
265 {
266 drmModeConnector *conn;
267 drmModeEncoder *enc;
268 drmModeCrtc *crtc;
269 drmModeRes *res;
270 char name[64];
271 char dev[64];
272 int i;
273
274 if (device) {
275 snprintf(dev, sizeof(dev), "%s", device);
276 } else {
277 snprintf(dev, sizeof(dev), DRM_DEV_NAME, DRM_DIR_NAME, 0);
278 }
279 drm_fd = open(dev, O_RDWR);
280 if (drm_fd < 0) {
281 fprintf(stderr, "drm: open %s: %s\n", dev, strerror(errno));
282 return;
283 }
284 fprintf(stdout, "connectors for %s:\n", dev);
285
286 res = drmModeGetResources(drm_fd);
287 if (res == NULL) {
288 return;
289 }
290
291 for (i = 0; i < res->count_connectors; i++) {
292 conn = drmModeGetConnector(drm_fd, res->connectors[i]);
293 if (!conn)
294 continue;
295 if (!conn->count_encoders)
296 return;
297 drm_conn_name(conn, name, sizeof(name));
298
299 enc = NULL;
300 crtc = NULL;
301 if (conn->encoder_id) {
302 enc = drmModeGetEncoder(drm_fd, conn->encoder_id);
303 if (enc && enc->crtc_id) {
304 crtc = drmModeGetCrtc(drm_fd, enc->crtc_id);
305 }
306 }
307
308 if (conn->connection == DRM_MODE_CONNECTED && crtc) {
309 fprintf(stdout, " %-10s: %dx%d\n", name,
310 crtc->width, crtc->height);
311 } else {
312 fprintf(stdout, " %-10s: disconnected\n", name);
313 }
314
315 drmModeFreeCrtc(crtc);
316 drmModeFreeEncoder(enc);
317 drmModeFreeConnector(conn);
318 }
319 }
320