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