blob: a02613ff4e6dbbbc011d4a3586aa0a984ab9daac [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>
bigbiffe4a84df2022-04-10 14:47:39 -040019#include <poll.h>
Ethan Yonkerfbb43532015-12-28 21:54:50 +010020#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/cdefs.h>
25#include <sys/ioctl.h>
26#include <sys/mman.h>
27#include <sys/types.h>
28#include <unistd.h>
29#include <xf86drm.h>
30#include <xf86drmMode.h>
31
bigbiffd81833a2021-01-17 11:06:57 -050032#include "minuitwrp/minui.h"
Ethan Yonkerfbb43532015-12-28 21:54:50 +010033#include "graphics.h"
34#include <pixelflinger/pixelflinger.h>
35
36#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
37
38struct drm_surface {
39 GRSurface base;
40 uint32_t fb_id;
41 uint32_t handle;
42};
43
44static drm_surface *drm_surfaces[2];
45static int current_buffer;
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:
bigbiffe4a84df2022-04-10 14:47:39 -0400121 case DRM_FORMAT_RGBA8888:
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100122 case DRM_FORMAT_BGRX8888:
123 case DRM_FORMAT_XBGR8888:
124 case DRM_FORMAT_ARGB8888:
125 case DRM_FORMAT_XRGB8888:
126 return 32;
127 case DRM_FORMAT_RGB565:
128 return 16;
129 default:
130 printf("Unknown format %d\n", format);
131 return 32;
132 }
133}
134
135static drm_surface *drm_create_surface(int width, int height) {
136 struct drm_surface *surface;
137 struct drm_mode_create_dumb create_dumb;
138 uint32_t format;
139 __u32 base_format;
140 int ret;
141
142 surface = (struct drm_surface*)calloc(1, sizeof(*surface));
143 if (!surface) {
144 printf("Can't allocate memory\n");
145 return NULL;
146 }
147
148#if defined(RECOVERY_ABGR)
149 format = DRM_FORMAT_RGBA8888;
150 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
151 printf("setting DRM_FORMAT_RGBA8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
152#elif defined(RECOVERY_BGRA)
153 format = DRM_FORMAT_ARGB8888;
Simon Shields1021c5a2018-01-25 12:46:41 +1100154 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
155 printf("setting DRM_FORMAT_ARGB8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100156#elif defined(RECOVERY_RGBA)
157 format = DRM_FORMAT_ABGR8888;
158 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
159 printf("setting DRM_FORMAT_ABGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n");
160#elif defined(RECOVERY_RGBX)
161 format = DRM_FORMAT_XBGR8888;
Ethan Yonker2f685272018-10-19 08:06:03 -0500162 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
163 printf("setting DRM_FORMAT_XBGR8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100164#else
165 format = DRM_FORMAT_RGB565;
166 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
167 printf("setting DRM_FORMAT_RGB565 and GGL_PIXEL_FORMAT_RGB_565\n");
168#endif
169
170 memset(&create_dumb, 0, sizeof(create_dumb));
171 create_dumb.height = height;
172 create_dumb.width = width;
173 create_dumb.bpp = drm_format_to_bpp(format);
174 create_dumb.flags = 0;
175
176 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
177 if (ret) {
178 printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
179 drm_destroy_surface(surface);
180 return NULL;
181 }
182 surface->handle = create_dumb.handle;
183
184 uint32_t handles[4], pitches[4], offsets[4];
185
186 handles[0] = surface->handle;
187 pitches[0] = create_dumb.pitch;
188 offsets[0] = 0;
189
190 ret = drmModeAddFB2(drm_fd, width, height,
191 format, handles, pitches, offsets,
192 &(surface->fb_id), 0);
193 if (ret) {
194 printf("drmModeAddFB2 failed ret=%d\n", ret);
195 drm_destroy_surface(surface);
196 return NULL;
197 }
198
199 struct drm_mode_map_dumb map_dumb;
200 memset(&map_dumb, 0, sizeof(map_dumb));
201 map_dumb.handle = create_dumb.handle;
202 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
203 if (ret) {
204 printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
205 drm_destroy_surface(surface);
206 return NULL;;
207 }
208
209 surface->base.height = height;
210 surface->base.width = width;
211 surface->base.row_bytes = create_dumb.pitch;
212 surface->base.pixel_bytes = create_dumb.bpp / 8;
213 surface->base.format = base_format;
214 surface->base.data = (unsigned char*)
215 mmap(NULL,
216 surface->base.height * surface->base.row_bytes,
217 PROT_READ | PROT_WRITE, MAP_SHARED,
218 drm_fd, map_dumb.offset);
219 if (surface->base.data == MAP_FAILED) {
220 perror("mmap() failed");
221 drm_destroy_surface(surface);
222 return NULL;
223 }
224
225 return surface;
226}
227
228static drmModeCrtc *find_crtc_for_connector(int fd,
229 drmModeRes *resources,
230 drmModeConnector *connector) {
231 int i, j;
232 drmModeEncoder *encoder;
233 int32_t crtc;
234
235 /*
236 * Find the encoder. If we already have one, just use it.
237 */
238 if (connector->encoder_id)
239 encoder = drmModeGetEncoder(fd, connector->encoder_id);
240 else
241 encoder = NULL;
242
243 if (encoder && encoder->crtc_id) {
244 crtc = encoder->crtc_id;
245 drmModeFreeEncoder(encoder);
246 return drmModeGetCrtc(fd, crtc);
247 }
248
249 /*
250 * Didn't find anything, try to find a crtc and encoder combo.
251 */
252 crtc = -1;
253 for (i = 0; i < connector->count_encoders; i++) {
254 encoder = drmModeGetEncoder(fd, connector->encoders[i]);
255
256 if (encoder) {
257 for (j = 0; j < resources->count_crtcs; j++) {
258 if (!(encoder->possible_crtcs & (1 << j)))
259 continue;
260 crtc = resources->crtcs[j];
261 break;
262 }
263 if (crtc >= 0) {
264 drmModeFreeEncoder(encoder);
265 return drmModeGetCrtc(fd, crtc);
266 }
267 }
268 }
269
270 return NULL;
271}
272
273static drmModeConnector *find_used_connector_by_type(int fd,
274 drmModeRes *resources,
275 unsigned type) {
276 int i;
277 for (i = 0; i < resources->count_connectors; i++) {
278 drmModeConnector *connector;
279
280 connector = drmModeGetConnector(fd, resources->connectors[i]);
281 if (connector) {
282 if ((connector->connector_type == type) &&
283 (connector->connection == DRM_MODE_CONNECTED) &&
284 (connector->count_modes > 0))
285 return connector;
286
287 drmModeFreeConnector(connector);
288 }
289 }
290 return NULL;
291}
292
293static drmModeConnector *find_first_connected_connector(int fd,
294 drmModeRes *resources) {
295 int i;
296 for (i = 0; i < resources->count_connectors; i++) {
297 drmModeConnector *connector;
298
299 connector = drmModeGetConnector(fd, resources->connectors[i]);
300 if (connector) {
301 if ((connector->count_modes > 0) &&
302 (connector->connection == DRM_MODE_CONNECTED))
303 return connector;
304
305 drmModeFreeConnector(connector);
306 }
307 }
308 return NULL;
309}
310
311static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
312 uint32_t *mode_index) {
313 unsigned i = 0;
314 int modes;
315 /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
316 unsigned kConnectorPriority[] = {
317 DRM_MODE_CONNECTOR_LVDS,
318 DRM_MODE_CONNECTOR_eDP,
319 DRM_MODE_CONNECTOR_DSI,
320 };
321
322 drmModeConnector *main_monitor_connector = NULL;
323 do {
324 main_monitor_connector = find_used_connector_by_type(fd,
325 resources,
326 kConnectorPriority[i]);
327 i++;
328 } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
329
330 /* If we didn't find a connector, grab the first one that is connected. */
331 if (!main_monitor_connector)
332 main_monitor_connector =
333 find_first_connected_connector(fd, resources);
334
335 /* If we still didn't find a connector, give up and return. */
336 if (!main_monitor_connector)
337 return NULL;
338
339 *mode_index = 0;
340 for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
341 if (main_monitor_connector->modes[modes].type &
342 DRM_MODE_TYPE_PREFERRED) {
343 *mode_index = modes;
344 break;
345 }
346 }
347
348 return main_monitor_connector;
349}
350
351static void disable_non_main_crtcs(int fd,
352 drmModeRes *resources,
353 drmModeCrtc* main_crtc) {
354 int i;
355 drmModeCrtc* crtc;
356
357 for (i = 0; i < resources->count_connectors; i++) {
358 drmModeConnector *connector;
359
360 connector = drmModeGetConnector(fd, resources->connectors[i]);
361 crtc = find_crtc_for_connector(fd, resources, connector);
362 if (crtc->crtc_id != main_crtc->crtc_id)
363 drm_disable_crtc(fd, crtc);
364 drmModeFreeCrtc(crtc);
365 }
366}
367
368static GRSurface* drm_init(minui_backend* backend __unused) {
369 drmModeRes *res = NULL;
370 uint32_t selected_mode;
371 char *dev_name;
372 int width, height;
373 int ret, i;
374
375 /* Consider DRM devices in order. */
376 for (i = 0; i < DRM_MAX_MINOR; i++) {
377 uint64_t cap = 0;
378
379 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
380 if (ret < 0)
381 continue;
382
383 drm_fd = open(dev_name, O_RDWR, 0);
384 free(dev_name);
385 if (drm_fd < 0)
386 continue;
387
388 /* We need dumb buffers. */
389 ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
390 if (ret || cap == 0) {
391 close(drm_fd);
392 continue;
393 }
394
395 res = drmModeGetResources(drm_fd);
396 if (!res) {
397 close(drm_fd);
398 continue;
399 }
400
401 /* Use this device if it has at least one connected monitor. */
402 if (res->count_crtcs > 0 && res->count_connectors > 0)
403 if (find_first_connected_connector(drm_fd, res))
404 break;
405
406 drmModeFreeResources(res);
407 close(drm_fd);
408 res = NULL;
409 }
410
411 if (drm_fd < 0 || res == NULL) {
412 perror("cannot find/open a drm device");
413 return NULL;
414 }
415
416 main_monitor_connector = find_main_monitor(drm_fd,
417 res, &selected_mode);
418
419 if (!main_monitor_connector) {
420 printf("main_monitor_connector not found\n");
421 drmModeFreeResources(res);
422 close(drm_fd);
423 return NULL;
424 }
425
426 main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
427 main_monitor_connector);
428
429 if (!main_monitor_crtc) {
430 printf("main_monitor_crtc not found\n");
431 drmModeFreeResources(res);
432 close(drm_fd);
433 return NULL;
434 }
435
436 disable_non_main_crtcs(drm_fd,
437 res, main_monitor_crtc);
438
439 main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
440
441 width = main_monitor_crtc->mode.hdisplay;
442 height = main_monitor_crtc->mode.vdisplay;
443
444 drmModeFreeResources(res);
445
446 drm_surfaces[0] = drm_create_surface(width, height);
447 drm_surfaces[1] = drm_create_surface(width, height);
448 if (!drm_surfaces[0] || !drm_surfaces[1]) {
449 drm_destroy_surface(drm_surfaces[0]);
450 drm_destroy_surface(drm_surfaces[1]);
451 drmModeFreeResources(res);
452 close(drm_fd);
453 return NULL;
454 }
455
Simon Shields16d831b2018-01-25 13:35:23 +1100456 draw_buf = (GRSurface *)malloc(sizeof(GRSurface));
457 if (!draw_buf) {
458 printf("failed to alloc draw_buf\n");
459 drm_destroy_surface(drm_surfaces[0]);
460 drm_destroy_surface(drm_surfaces[1]);
461 drmModeFreeResources(res);
462 close(drm_fd);
463 return NULL;
464 }
465
466 memcpy(draw_buf, &drm_surfaces[0]->base, sizeof(GRSurface));
467 draw_buf->data = (unsigned char *)calloc(draw_buf->height * draw_buf->row_bytes, 1);
468 if (!draw_buf->data) {
469 printf("failed to alloc draw_buf surface\n");
470 free(draw_buf);
471 drm_destroy_surface(drm_surfaces[0]);
472 drm_destroy_surface(drm_surfaces[1]);
473 drmModeFreeResources(res);
474 close(drm_fd);
475 return NULL;
476 }
477
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100478 current_buffer = 0;
479
480 drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
481
Simon Shields16d831b2018-01-25 13:35:23 +1100482 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100483}
484
bigbiffe4a84df2022-04-10 14:47:39 -0400485static void page_flip_complete(__unused int fd,
486 __unused unsigned int sequence,
487 __unused unsigned int tv_sec,
488 __unused unsigned int tv_usec,
489 void *user_data) {
490 *static_cast<bool*>(user_data) = false;
491}
492
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100493static GRSurface* drm_flip(minui_backend* backend __unused) {
bigbiffe4a84df2022-04-10 14:47:39 -0400494 bool ongoing_flip = true;
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
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100498
bigbiffe4a84df2022-04-10 14:47:39 -0400499 if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
500 drm_surfaces[current_buffer]->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip)) {
501 printf("Failed to drmModePageFlip");
502 return nullptr;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100503 }
bigbiffe4a84df2022-04-10 14:47:39 -0400504
505 while (ongoing_flip) {
506 struct pollfd fds = {
507 .fd = drm_fd,
508 .events = POLLIN
509 };
510
511 if (poll(&fds, 1, -1) == -1 || !(fds.revents & POLLIN)) {
512 perror("Failed to poll() on drm fd");
513 break;
514 }
515
516 drmEventContext evctx = {
517 .version = DRM_EVENT_CONTEXT_VERSION,
518 .page_flip_handler = page_flip_complete
519 };
520
521 if (drmHandleEvent(drm_fd, &evctx) != 0) {
522 perror("Failed to drmHandleEvent");
523 break;
524 }
525 }
526
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100527 current_buffer = 1 - current_buffer;
Simon Shields16d831b2018-01-25 13:35:23 +1100528 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100529}
530
531static void drm_exit(minui_backend* backend __unused) {
532 drm_disable_crtc(drm_fd, main_monitor_crtc);
533 drm_destroy_surface(drm_surfaces[0]);
534 drm_destroy_surface(drm_surfaces[1]);
535 drmModeFreeCrtc(main_monitor_crtc);
536 drmModeFreeConnector(main_monitor_connector);
537 close(drm_fd);
538 drm_fd = -1;
539}
540
541static minui_backend drm_backend = {
542 .init = drm_init,
543 .flip = drm_flip,
544 .blank = drm_blank,
545 .exit = drm_exit,
546};
547
548minui_backend* open_drm() {
549 return &drm_backend;
550}