blob: c321bb7d860de5fc9a8639aa6479e2a72f140a55 [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 Shields16d831b2018-01-25 13:35:23 +110045static GRSurface *draw_buf = NULL;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010046
47static drmModeCrtc *main_monitor_crtc;
48static drmModeConnector *main_monitor_connector;
49
50static int drm_fd = -1;
51
52static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
53 if (crtc) {
54 drmModeSetCrtc(drm_fd, crtc->crtc_id,
55 0, // fb_id
56 0, 0, // x,y
57 NULL, // connectors
58 0, // connector_count
59 NULL); // mode
60 }
61}
62
63static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
64 struct drm_surface *surface) {
65 int32_t ret;
66
67 ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
68 surface->fb_id,
69 0, 0, // x,y
70 &main_monitor_connector->connector_id,
71 1, // connector_count
72 &main_monitor_crtc->mode);
73
74 if (ret)
75 printf("drmModeSetCrtc failed ret=%d\n", ret);
76}
77
78static void drm_blank(minui_backend* backend __unused, bool blank) {
79 if (blank)
80 drm_disable_crtc(drm_fd, main_monitor_crtc);
81 else
82 drm_enable_crtc(drm_fd, main_monitor_crtc,
83 drm_surfaces[current_buffer]);
84}
85
86static void drm_destroy_surface(struct drm_surface *surface) {
87 struct drm_gem_close gem_close;
88 int ret;
89
90 if(!surface)
91 return;
92
93 if (surface->base.data)
94 munmap(surface->base.data,
95 surface->base.row_bytes * surface->base.height);
96
97 if (surface->fb_id) {
98 ret = drmModeRmFB(drm_fd, surface->fb_id);
99 if (ret)
100 printf("drmModeRmFB failed ret=%d\n", ret);
101 }
102
103 if (surface->handle) {
104 memset(&gem_close, 0, sizeof(gem_close));
105 gem_close.handle = surface->handle;
106
107 ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
108 if (ret)
109 printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
110 }
111
112 free(surface);
113}
114
115static int drm_format_to_bpp(uint32_t format) {
116 switch(format) {
117 case DRM_FORMAT_ABGR8888:
118 case DRM_FORMAT_BGRA8888:
119 case DRM_FORMAT_RGBX8888:
120 case DRM_FORMAT_BGRX8888:
121 case DRM_FORMAT_XBGR8888:
122 case DRM_FORMAT_ARGB8888:
123 case DRM_FORMAT_XRGB8888:
124 return 32;
125 case DRM_FORMAT_RGB565:
126 return 16;
127 default:
128 printf("Unknown format %d\n", format);
129 return 32;
130 }
131}
132
133static drm_surface *drm_create_surface(int width, int height) {
134 struct drm_surface *surface;
135 struct drm_mode_create_dumb create_dumb;
136 uint32_t format;
137 __u32 base_format;
138 int ret;
139
140 surface = (struct drm_surface*)calloc(1, sizeof(*surface));
141 if (!surface) {
142 printf("Can't allocate memory\n");
143 return NULL;
144 }
145
146#if defined(RECOVERY_ABGR)
147 format = DRM_FORMAT_RGBA8888;
148 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
149 printf("setting DRM_FORMAT_RGBA8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
150#elif defined(RECOVERY_BGRA)
151 format = DRM_FORMAT_ARGB8888;
Simon Shields1021c5a2018-01-25 12:46:41 +1100152 base_format = GGL_PIXEL_FORMAT_RGBA_8888;
153 printf("setting DRM_FORMAT_ARGB8888 and GGL_PIXEL_FORMAT_RGBA_8888\n");
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100154#elif defined(RECOVERY_RGBA)
155 format = DRM_FORMAT_ABGR8888;
156 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
157 printf("setting DRM_FORMAT_ABGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n");
158#elif defined(RECOVERY_RGBX)
159 format = DRM_FORMAT_XBGR8888;
160 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
161 printf("setting DRM_FORMAT_XBGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n");
162#else
163 format = DRM_FORMAT_RGB565;
164 base_format = GGL_PIXEL_FORMAT_BGRA_8888;
165 printf("setting DRM_FORMAT_RGB565 and GGL_PIXEL_FORMAT_RGB_565\n");
166#endif
167
168 memset(&create_dumb, 0, sizeof(create_dumb));
169 create_dumb.height = height;
170 create_dumb.width = width;
171 create_dumb.bpp = drm_format_to_bpp(format);
172 create_dumb.flags = 0;
173
174 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
175 if (ret) {
176 printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
177 drm_destroy_surface(surface);
178 return NULL;
179 }
180 surface->handle = create_dumb.handle;
181
182 uint32_t handles[4], pitches[4], offsets[4];
183
184 handles[0] = surface->handle;
185 pitches[0] = create_dumb.pitch;
186 offsets[0] = 0;
187
188 ret = drmModeAddFB2(drm_fd, width, height,
189 format, handles, pitches, offsets,
190 &(surface->fb_id), 0);
191 if (ret) {
192 printf("drmModeAddFB2 failed ret=%d\n", ret);
193 drm_destroy_surface(surface);
194 return NULL;
195 }
196
197 struct drm_mode_map_dumb map_dumb;
198 memset(&map_dumb, 0, sizeof(map_dumb));
199 map_dumb.handle = create_dumb.handle;
200 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
201 if (ret) {
202 printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
203 drm_destroy_surface(surface);
204 return NULL;;
205 }
206
207 surface->base.height = height;
208 surface->base.width = width;
209 surface->base.row_bytes = create_dumb.pitch;
210 surface->base.pixel_bytes = create_dumb.bpp / 8;
211 surface->base.format = base_format;
212 surface->base.data = (unsigned char*)
213 mmap(NULL,
214 surface->base.height * surface->base.row_bytes,
215 PROT_READ | PROT_WRITE, MAP_SHARED,
216 drm_fd, map_dumb.offset);
217 if (surface->base.data == MAP_FAILED) {
218 perror("mmap() failed");
219 drm_destroy_surface(surface);
220 return NULL;
221 }
222
223 return surface;
224}
225
226static drmModeCrtc *find_crtc_for_connector(int fd,
227 drmModeRes *resources,
228 drmModeConnector *connector) {
229 int i, j;
230 drmModeEncoder *encoder;
231 int32_t crtc;
232
233 /*
234 * Find the encoder. If we already have one, just use it.
235 */
236 if (connector->encoder_id)
237 encoder = drmModeGetEncoder(fd, connector->encoder_id);
238 else
239 encoder = NULL;
240
241 if (encoder && encoder->crtc_id) {
242 crtc = encoder->crtc_id;
243 drmModeFreeEncoder(encoder);
244 return drmModeGetCrtc(fd, crtc);
245 }
246
247 /*
248 * Didn't find anything, try to find a crtc and encoder combo.
249 */
250 crtc = -1;
251 for (i = 0; i < connector->count_encoders; i++) {
252 encoder = drmModeGetEncoder(fd, connector->encoders[i]);
253
254 if (encoder) {
255 for (j = 0; j < resources->count_crtcs; j++) {
256 if (!(encoder->possible_crtcs & (1 << j)))
257 continue;
258 crtc = resources->crtcs[j];
259 break;
260 }
261 if (crtc >= 0) {
262 drmModeFreeEncoder(encoder);
263 return drmModeGetCrtc(fd, crtc);
264 }
265 }
266 }
267
268 return NULL;
269}
270
271static drmModeConnector *find_used_connector_by_type(int fd,
272 drmModeRes *resources,
273 unsigned type) {
274 int i;
275 for (i = 0; i < resources->count_connectors; i++) {
276 drmModeConnector *connector;
277
278 connector = drmModeGetConnector(fd, resources->connectors[i]);
279 if (connector) {
280 if ((connector->connector_type == type) &&
281 (connector->connection == DRM_MODE_CONNECTED) &&
282 (connector->count_modes > 0))
283 return connector;
284
285 drmModeFreeConnector(connector);
286 }
287 }
288 return NULL;
289}
290
291static drmModeConnector *find_first_connected_connector(int fd,
292 drmModeRes *resources) {
293 int i;
294 for (i = 0; i < resources->count_connectors; i++) {
295 drmModeConnector *connector;
296
297 connector = drmModeGetConnector(fd, resources->connectors[i]);
298 if (connector) {
299 if ((connector->count_modes > 0) &&
300 (connector->connection == DRM_MODE_CONNECTED))
301 return connector;
302
303 drmModeFreeConnector(connector);
304 }
305 }
306 return NULL;
307}
308
309static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
310 uint32_t *mode_index) {
311 unsigned i = 0;
312 int modes;
313 /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
314 unsigned kConnectorPriority[] = {
315 DRM_MODE_CONNECTOR_LVDS,
316 DRM_MODE_CONNECTOR_eDP,
317 DRM_MODE_CONNECTOR_DSI,
318 };
319
320 drmModeConnector *main_monitor_connector = NULL;
321 do {
322 main_monitor_connector = find_used_connector_by_type(fd,
323 resources,
324 kConnectorPriority[i]);
325 i++;
326 } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
327
328 /* If we didn't find a connector, grab the first one that is connected. */
329 if (!main_monitor_connector)
330 main_monitor_connector =
331 find_first_connected_connector(fd, resources);
332
333 /* If we still didn't find a connector, give up and return. */
334 if (!main_monitor_connector)
335 return NULL;
336
337 *mode_index = 0;
338 for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
339 if (main_monitor_connector->modes[modes].type &
340 DRM_MODE_TYPE_PREFERRED) {
341 *mode_index = modes;
342 break;
343 }
344 }
345
346 return main_monitor_connector;
347}
348
349static void disable_non_main_crtcs(int fd,
350 drmModeRes *resources,
351 drmModeCrtc* main_crtc) {
352 int i;
353 drmModeCrtc* crtc;
354
355 for (i = 0; i < resources->count_connectors; i++) {
356 drmModeConnector *connector;
357
358 connector = drmModeGetConnector(fd, resources->connectors[i]);
359 crtc = find_crtc_for_connector(fd, resources, connector);
360 if (crtc->crtc_id != main_crtc->crtc_id)
361 drm_disable_crtc(fd, crtc);
362 drmModeFreeCrtc(crtc);
363 }
364}
365
366static GRSurface* drm_init(minui_backend* backend __unused) {
367 drmModeRes *res = NULL;
368 uint32_t selected_mode;
369 char *dev_name;
370 int width, height;
371 int ret, i;
372
373 /* Consider DRM devices in order. */
374 for (i = 0; i < DRM_MAX_MINOR; i++) {
375 uint64_t cap = 0;
376
377 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
378 if (ret < 0)
379 continue;
380
381 drm_fd = open(dev_name, O_RDWR, 0);
382 free(dev_name);
383 if (drm_fd < 0)
384 continue;
385
386 /* We need dumb buffers. */
387 ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
388 if (ret || cap == 0) {
389 close(drm_fd);
390 continue;
391 }
392
393 res = drmModeGetResources(drm_fd);
394 if (!res) {
395 close(drm_fd);
396 continue;
397 }
398
399 /* Use this device if it has at least one connected monitor. */
400 if (res->count_crtcs > 0 && res->count_connectors > 0)
401 if (find_first_connected_connector(drm_fd, res))
402 break;
403
404 drmModeFreeResources(res);
405 close(drm_fd);
406 res = NULL;
407 }
408
409 if (drm_fd < 0 || res == NULL) {
410 perror("cannot find/open a drm device");
411 return NULL;
412 }
413
414 main_monitor_connector = find_main_monitor(drm_fd,
415 res, &selected_mode);
416
417 if (!main_monitor_connector) {
418 printf("main_monitor_connector not found\n");
419 drmModeFreeResources(res);
420 close(drm_fd);
421 return NULL;
422 }
423
424 main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
425 main_monitor_connector);
426
427 if (!main_monitor_crtc) {
428 printf("main_monitor_crtc not found\n");
429 drmModeFreeResources(res);
430 close(drm_fd);
431 return NULL;
432 }
433
434 disable_non_main_crtcs(drm_fd,
435 res, main_monitor_crtc);
436
437 main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
438
439 width = main_monitor_crtc->mode.hdisplay;
440 height = main_monitor_crtc->mode.vdisplay;
441
442 drmModeFreeResources(res);
443
444 drm_surfaces[0] = drm_create_surface(width, height);
445 drm_surfaces[1] = drm_create_surface(width, height);
446 if (!drm_surfaces[0] || !drm_surfaces[1]) {
447 drm_destroy_surface(drm_surfaces[0]);
448 drm_destroy_surface(drm_surfaces[1]);
449 drmModeFreeResources(res);
450 close(drm_fd);
451 return NULL;
452 }
453
Simon Shields16d831b2018-01-25 13:35:23 +1100454 draw_buf = (GRSurface *)malloc(sizeof(GRSurface));
455 if (!draw_buf) {
456 printf("failed to alloc draw_buf\n");
457 drm_destroy_surface(drm_surfaces[0]);
458 drm_destroy_surface(drm_surfaces[1]);
459 drmModeFreeResources(res);
460 close(drm_fd);
461 return NULL;
462 }
463
464 memcpy(draw_buf, &drm_surfaces[0]->base, sizeof(GRSurface));
465 draw_buf->data = (unsigned char *)calloc(draw_buf->height * draw_buf->row_bytes, 1);
466 if (!draw_buf->data) {
467 printf("failed to alloc draw_buf surface\n");
468 free(draw_buf);
469 drm_destroy_surface(drm_surfaces[0]);
470 drm_destroy_surface(drm_surfaces[1]);
471 drmModeFreeResources(res);
472 close(drm_fd);
473 return NULL;
474 }
475
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100476 current_buffer = 0;
477
478 drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
479
Simon Shields16d831b2018-01-25 13:35:23 +1100480 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100481}
482
483static GRSurface* drm_flip(minui_backend* backend __unused) {
484 int ret;
Simon Shields16d831b2018-01-25 13:35:23 +1100485 memcpy(drm_surfaces[current_buffer]->base.data,
486 draw_buf->data, draw_buf->height * draw_buf->row_bytes);
487
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100488
489 ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
Simon Shi25b850d2018-06-02 16:18:45 +0200490 drm_surfaces[current_buffer]->fb_id, 0, NULL);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100491 if (ret < 0) {
492 printf("drmModePageFlip failed ret=%d\n", ret);
493 return NULL;
494 }
495 current_buffer = 1 - current_buffer;
Simon Shields16d831b2018-01-25 13:35:23 +1100496 return draw_buf;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100497}
498
499static void drm_exit(minui_backend* backend __unused) {
500 drm_disable_crtc(drm_fd, main_monitor_crtc);
501 drm_destroy_surface(drm_surfaces[0]);
502 drm_destroy_surface(drm_surfaces[1]);
503 drmModeFreeCrtc(main_monitor_crtc);
504 drmModeFreeConnector(main_monitor_connector);
505 close(drm_fd);
506 drm_fd = -1;
507}
508
509static minui_backend drm_backend = {
510 .init = drm_init,
511 .flip = drm_flip,
512 .blank = drm_blank,
513 .exit = drm_exit,
514};
515
516minui_backend* open_drm() {
517 return &drm_backend;
518}