blob: aaa9e4e1924a9013ffd2746bec50ba8273d153d6 [file] [log] [blame]
Ethan Yonkerfbb43532015-12-28 21:54:50 +01001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <drm_fourcc.h>
18#include <fcntl.h>
19#include <stdbool.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/cdefs.h>
24#include <sys/ioctl.h>
25#include <sys/mman.h>
26#include <sys/types.h>
27#include <unistd.h>
28#include <xf86drm.h>
29#include <xf86drmMode.h>
30
31#include "minui.h"
32#include "graphics.h"
33#include <pixelflinger/pixelflinger.h>
34
35#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
36
37struct drm_surface {
38 GRSurface base;
39 uint32_t fb_id;
40 uint32_t handle;
41};
42
43static drm_surface *drm_surfaces[2];
44static int current_buffer;
Simon Shields34ad7282018-01-25 13:36:44 +110045static bool flip_pending = false;
Simon Shields16d831b2018-01-25 13:35:23 +110046static GRSurface *draw_buf = NULL;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010047
48static drmModeCrtc *main_monitor_crtc;
49static drmModeConnector *main_monitor_connector;
50
51static int drm_fd = -1;
52
53static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
54 if (crtc) {
55 drmModeSetCrtc(drm_fd, crtc->crtc_id,
56 0, // fb_id
57 0, 0, // x,y
58 NULL, // connectors
59 0, // connector_count
60 NULL); // mode
61 }
62}
63
64static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
65 struct drm_surface *surface) {
66 int32_t ret;
67
68 ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
69 surface->fb_id,
70 0, 0, // x,y
71 &main_monitor_connector->connector_id,
72 1, // connector_count
73 &main_monitor_crtc->mode);
74
75 if (ret)
76 printf("drmModeSetCrtc failed ret=%d\n", ret);
77}
78
79static void drm_blank(minui_backend* backend __unused, bool blank) {
80 if (blank)
81 drm_disable_crtc(drm_fd, main_monitor_crtc);
82 else
83 drm_enable_crtc(drm_fd, main_monitor_crtc,
84 drm_surfaces[current_buffer]);
85}
86
87static void drm_destroy_surface(struct drm_surface *surface) {
88 struct drm_gem_close gem_close;
89 int ret;
90
91 if(!surface)
92 return;
93
94 if (surface->base.data)
95 munmap(surface->base.data,
96 surface->base.row_bytes * surface->base.height);
97
98 if (surface->fb_id) {
99 ret = drmModeRmFB(drm_fd, surface->fb_id);
100 if (ret)
101 printf("drmModeRmFB failed ret=%d\n", ret);
102 }
103
104 if (surface->handle) {
105 memset(&gem_close, 0, sizeof(gem_close));
106 gem_close.handle = surface->handle;
107
108 ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
109 if (ret)
110 printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
111 }
112
113 free(surface);
114}
115
116static int drm_format_to_bpp(uint32_t format) {
117 switch(format) {
118 case DRM_FORMAT_ABGR8888:
119 case DRM_FORMAT_BGRA8888:
120 case DRM_FORMAT_RGBX8888:
121 case DRM_FORMAT_BGRX8888:
122 case DRM_FORMAT_XBGR8888:
123 case DRM_FORMAT_ARGB8888:
124 case DRM_FORMAT_XRGB8888:
125 return 32;
126 case DRM_FORMAT_RGB565:
127 return 16;
128 default:
129 printf("Unknown format %d\n", format);
130 return 32;
131 }
132}
133
134static drm_surface *drm_create_surface(int width, int height) {
135 struct drm_surface *surface;
136 struct drm_mode_create_dumb create_dumb;
137 uint32_t format;
138 __u32 base_format;
139 int ret;
140
141 surface = (struct drm_surface*)calloc(1, sizeof(*surface));
142 if (!surface) {
143 printf("Can't allocate memory\n");
144 return NULL;
145 }
146
147#if defined(RECOVERY_ABGR)
148 format = DRM_FORMAT_RGBA8888;
149 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
150 printf("setting DRM_FORMAT_RGBA8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
151#elif defined(RECOVERY_BGRA)
152 format = DRM_FORMAT_ARGB8888;
Simon Shields1021c5a2018-01-25 12:46:41 +1100153 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
154 printf("setting DRM_FORMAT_ARGB8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100155#elif defined(RECOVERY_RGBA)
156 format = DRM_FORMAT_ABGR8888;
157 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
158 printf("setting DRM_FORMAT_ABGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n");
159#elif defined(RECOVERY_RGBX)
160 format = DRM_FORMAT_XBGR8888;
161 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
162 printf("setting DRM_FORMAT_XBGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n");
163#else
164 format = DRM_FORMAT_RGB565;
165 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
166 printf("setting DRM_FORMAT_RGB565 and GGL_PIXEL_FORMAT_RGB_565\n");
167#endif
168
169 memset(&create_dumb, 0, sizeof(create_dumb));
170 create_dumb.height = height;
171 create_dumb.width = width;
172 create_dumb.bpp = drm_format_to_bpp(format);
173 create_dumb.flags = 0;
174
175 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
176 if (ret) {
177 printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
178 drm_destroy_surface(surface);
179 return NULL;
180 }
181 surface->handle = create_dumb.handle;
182
183 uint32_t handles[4], pitches[4], offsets[4];
184
185 handles[0] = surface->handle;
186 pitches[0] = create_dumb.pitch;
187 offsets[0] = 0;
188
189 ret = drmModeAddFB2(drm_fd, width, height,
190 format, handles, pitches, offsets,
191 &(surface->fb_id), 0);
192 if (ret) {
193 printf("drmModeAddFB2 failed ret=%d\n", ret);
194 drm_destroy_surface(surface);
195 return NULL;
196 }
197
198 struct drm_mode_map_dumb map_dumb;
199 memset(&map_dumb, 0, sizeof(map_dumb));
200 map_dumb.handle = create_dumb.handle;
201 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
202 if (ret) {
203 printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
204 drm_destroy_surface(surface);
205 return NULL;;
206 }
207
208 surface->base.height = height;
209 surface->base.width = width;
210 surface->base.row_bytes = create_dumb.pitch;
211 surface->base.pixel_bytes = create_dumb.bpp / 8;
212 surface->base.format = base_format;
213 surface->base.data = (unsigned char*)
214 mmap(NULL,
215 surface->base.height * surface->base.row_bytes,
216 PROT_READ | PROT_WRITE, MAP_SHARED,
217 drm_fd, map_dumb.offset);
218 if (surface->base.data == MAP_FAILED) {
219 perror("mmap() failed");
220 drm_destroy_surface(surface);
221 return NULL;
222 }
223
224 return surface;
225}
226
227static drmModeCrtc *find_crtc_for_connector(int fd,
228 drmModeRes *resources,
229 drmModeConnector *connector) {
230 int i, j;
231 drmModeEncoder *encoder;
232 int32_t crtc;
233
234 /*
235 * Find the encoder. If we already have one, just use it.
236 */
237 if (connector->encoder_id)
238 encoder = drmModeGetEncoder(fd, connector->encoder_id);
239 else
240 encoder = NULL;
241
242 if (encoder && encoder->crtc_id) {
243 crtc = encoder->crtc_id;
244 drmModeFreeEncoder(encoder);
245 return drmModeGetCrtc(fd, crtc);
246 }
247
248 /*
249 * Didn't find anything, try to find a crtc and encoder combo.
250 */
251 crtc = -1;
252 for (i = 0; i < connector->count_encoders; i++) {
253 encoder = drmModeGetEncoder(fd, connector->encoders[i]);
254
255 if (encoder) {
256 for (j = 0; j < resources->count_crtcs; j++) {
257 if (!(encoder->possible_crtcs & (1 << j)))
258 continue;
259 crtc = resources->crtcs[j];
260 break;
261 }
262 if (crtc >= 0) {
263 drmModeFreeEncoder(encoder);
264 return drmModeGetCrtc(fd, crtc);
265 }
266 }
267 }
268
269 return NULL;
270}
271
272static drmModeConnector *find_used_connector_by_type(int fd,
273 drmModeRes *resources,
274 unsigned type) {
275 int i;
276 for (i = 0; i < resources->count_connectors; i++) {
277 drmModeConnector *connector;
278
279 connector = drmModeGetConnector(fd, resources->connectors[i]);
280 if (connector) {
281 if ((connector->connector_type == type) &&
282 (connector->connection == DRM_MODE_CONNECTED) &&
283 (connector->count_modes > 0))
284 return connector;
285
286 drmModeFreeConnector(connector);
287 }
288 }
289 return NULL;
290}
291
292static drmModeConnector *find_first_connected_connector(int fd,
293 drmModeRes *resources) {
294 int i;
295 for (i = 0; i < resources->count_connectors; i++) {
296 drmModeConnector *connector;
297
298 connector = drmModeGetConnector(fd, resources->connectors[i]);
299 if (connector) {
300 if ((connector->count_modes > 0) &&
301 (connector->connection == DRM_MODE_CONNECTED))
302 return connector;
303
304 drmModeFreeConnector(connector);
305 }
306 }
307 return NULL;
308}
309
310static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
311 uint32_t *mode_index) {
312 unsigned i = 0;
313 int modes;
314 /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
315 unsigned kConnectorPriority[] = {
316 DRM_MODE_CONNECTOR_LVDS,
317 DRM_MODE_CONNECTOR_eDP,
318 DRM_MODE_CONNECTOR_DSI,
319 };
320
321 drmModeConnector *main_monitor_connector = NULL;
322 do {
323 main_monitor_connector = find_used_connector_by_type(fd,
324 resources,
325 kConnectorPriority[i]);
326 i++;
327 } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
328
329 /* If we didn't find a connector, grab the first one that is connected. */
330 if (!main_monitor_connector)
331 main_monitor_connector =
332 find_first_connected_connector(fd, resources);
333
334 /* If we still didn't find a connector, give up and return. */
335 if (!main_monitor_connector)
336 return NULL;
337
338 *mode_index = 0;
339 for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
340 if (main_monitor_connector->modes[modes].type &
341 DRM_MODE_TYPE_PREFERRED) {
342 *mode_index = modes;
343 break;
344 }
345 }
346
347 return main_monitor_connector;
348}
349
350static void disable_non_main_crtcs(int fd,
351 drmModeRes *resources,
352 drmModeCrtc* main_crtc) {
353 int i;
354 drmModeCrtc* crtc;
355
356 for (i = 0; i < resources->count_connectors; i++) {
357 drmModeConnector *connector;
358
359 connector = drmModeGetConnector(fd, resources->connectors[i]);
360 crtc = find_crtc_for_connector(fd, resources, connector);
361 if (crtc->crtc_id != main_crtc->crtc_id)
362 drm_disable_crtc(fd, crtc);
363 drmModeFreeCrtc(crtc);
364 }
365}
366
367static GRSurface* drm_init(minui_backend* backend __unused) {
368 drmModeRes *res = NULL;
369 uint32_t selected_mode;
370 char *dev_name;
371 int width, height;
372 int ret, i;
373
374 /* Consider DRM devices in order. */
375 for (i = 0; i < DRM_MAX_MINOR; i++) {
376 uint64_t cap = 0;
377
378 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
379 if (ret < 0)
380 continue;
381
382 drm_fd = open(dev_name, O_RDWR, 0);
383 free(dev_name);
384 if (drm_fd < 0)
385 continue;
386
387 /* We need dumb buffers. */
388 ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
389 if (ret || cap == 0) {
390 close(drm_fd);
391 continue;
392 }
393
394 res = drmModeGetResources(drm_fd);
395 if (!res) {
396 close(drm_fd);
397 continue;
398 }
399
400 /* Use this device if it has at least one connected monitor. */
401 if (res->count_crtcs > 0 && res->count_connectors > 0)
402 if (find_first_connected_connector(drm_fd, res))
403 break;
404
405 drmModeFreeResources(res);
406 close(drm_fd);
407 res = NULL;
408 }
409
410 if (drm_fd < 0 || res == NULL) {
411 perror("cannot find/open a drm device");
412 return NULL;
413 }
414
415 main_monitor_connector = find_main_monitor(drm_fd,
416 res, &selected_mode);
417
418 if (!main_monitor_connector) {
419 printf("main_monitor_connector not found\n");
420 drmModeFreeResources(res);
421 close(drm_fd);
422 return NULL;
423 }
424
425 main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
426 main_monitor_connector);
427
428 if (!main_monitor_crtc) {
429 printf("main_monitor_crtc not found\n");
430 drmModeFreeResources(res);
431 close(drm_fd);
432 return NULL;
433 }
434
435 disable_non_main_crtcs(drm_fd,
436 res, main_monitor_crtc);
437
438 main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
439
440 width = main_monitor_crtc->mode.hdisplay;
441 height = main_monitor_crtc->mode.vdisplay;
442
443 drmModeFreeResources(res);
444
445 drm_surfaces[0] = drm_create_surface(width, height);
446 drm_surfaces[1] = drm_create_surface(width, height);
447 if (!drm_surfaces[0] || !drm_surfaces[1]) {
448 drm_destroy_surface(drm_surfaces[0]);
449 drm_destroy_surface(drm_surfaces[1]);
450 drmModeFreeResources(res);
451 close(drm_fd);
452 return NULL;
453 }
454
Simon Shields16d831b2018-01-25 13:35:23 +1100455 draw_buf = (GRSurface *)malloc(sizeof(GRSurface));
456 if (!draw_buf) {
457 printf("failed to alloc draw_buf\n");
458 drm_destroy_surface(drm_surfaces[0]);
459 drm_destroy_surface(drm_surfaces[1]);
460 drmModeFreeResources(res);
461 close(drm_fd);
462 return NULL;
463 }
464
465 memcpy(draw_buf, &drm_surfaces[0]->base, sizeof(GRSurface));
466 draw_buf->data = (unsigned char *)calloc(draw_buf->height * draw_buf->row_bytes, 1);
467 if (!draw_buf->data) {
468 printf("failed to alloc draw_buf surface\n");
469 free(draw_buf);
470 drm_destroy_surface(drm_surfaces[0]);
471 drm_destroy_surface(drm_surfaces[1]);
472 drmModeFreeResources(res);
473 close(drm_fd);
474 return NULL;
475 }
476
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100477 current_buffer = 0;
478
479 drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
480
Simon Shields16d831b2018-01-25 13:35:23 +1100481 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100482}
483
Simon Shields34ad7282018-01-25 13:36:44 +1100484static void drm_page_flip_event(int fd, unsigned int frame, unsigned int sec,
485 unsigned int usec, void *data) {
486 flip_pending = false;
487}
488
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100489static GRSurface* drm_flip(minui_backend* backend __unused) {
490 int ret;
Simon Shields34ad7282018-01-25 13:36:44 +1100491 drmEventContext ev;
492
493 memset(&ev, 0, sizeof(ev));
494
Simon Shields16d831b2018-01-25 13:35:23 +1100495 memcpy(drm_surfaces[current_buffer]->base.data,
496 draw_buf->data, draw_buf->height * draw_buf->row_bytes);
497
Simon Shields34ad7282018-01-25 13:36:44 +1100498 ev.version = 2;
499 ev.page_flip_handler = drm_page_flip_event;
500
501 /* wait for previous page flip, if any, to complete */
502 while (flip_pending) {
503 drmHandleEvent(drm_fd, &ev);
504 }
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100505
506 ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
Simon Shields34ad7282018-01-25 13:36:44 +1100507 drm_surfaces[current_buffer]->fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100508 if (ret < 0) {
509 printf("drmModePageFlip failed ret=%d\n", ret);
510 return NULL;
511 }
Simon Shields34ad7282018-01-25 13:36:44 +1100512
513 flip_pending = true;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100514 current_buffer = 1 - current_buffer;
Simon Shields16d831b2018-01-25 13:35:23 +1100515 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100516}
517
518static void drm_exit(minui_backend* backend __unused) {
519 drm_disable_crtc(drm_fd, main_monitor_crtc);
520 drm_destroy_surface(drm_surfaces[0]);
521 drm_destroy_surface(drm_surfaces[1]);
522 drmModeFreeCrtc(main_monitor_crtc);
523 drmModeFreeConnector(main_monitor_connector);
524 close(drm_fd);
525 drm_fd = -1;
526}
527
528static minui_backend drm_backend = {
529 .init = drm_init,
530 .flip = drm_flip,
531 .blank = drm_blank,
532 .exit = drm_exit,
533};
534
535minui_backend* open_drm() {
536 return &drm_backend;
537}