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