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