/* * Copyright (c) 2017 Rob Clark * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #define WEAK __attribute__((weak)) WEAK uint64_t gbm_bo_get_modifier(struct gbm_bo *bo); WEAK int gbm_bo_get_plane_count(struct gbm_bo *bo); WEAK uint32_t gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane); WEAK uint32_t gbm_bo_get_offset(struct gbm_bo *bo, int plane); static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); struct drm_fb *fb = data; if (fb->fb_id) drmModeRmFB(drm_fd, fb->fb_id); free(fb); } struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo) { int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); struct drm_fb *fb = gbm_bo_get_user_data(bo); uint32_t width, height, format, strides[4] = {0}, handles[4] = {0}, offsets[4] = {0}, flags = 0; int ret = -1; if (fb) return fb; fb = calloc(1, sizeof *fb); fb->bo = bo; width = gbm_bo_get_width(bo); height = gbm_bo_get_height(bo); format = gbm_bo_get_format(bo); if (gbm_bo_get_modifier && gbm_bo_get_plane_count && gbm_bo_get_stride_for_plane && gbm_bo_get_offset) { uint64_t modifiers[4] = {0}; modifiers[0] = gbm_bo_get_modifier(bo); const int num_planes = gbm_bo_get_plane_count(bo); for (int i = 0; i < num_planes; i++) { strides[i] = gbm_bo_get_stride_for_plane(bo, i); handles[i] = gbm_bo_get_handle(bo).u32; offsets[i] = gbm_bo_get_offset(bo, i); modifiers[i] = modifiers[0]; } if (modifiers[0]) { flags = DRM_MODE_FB_MODIFIERS; // printf("Using modifier %" PRIx64 "\n", modifiers[0]); } ret = drmModeAddFB2WithModifiers(drm_fd, width, height, format, handles, strides, offsets, modifiers, &fb->fb_id, flags); } if (ret) { // if (flags) // fprintf(stderr, "Modifiers failed!\n"); memcpy(handles, (uint32_t [4]){gbm_bo_get_handle(bo).u32,0,0,0}, 16); memcpy(strides, (uint32_t [4]){gbm_bo_get_stride(bo),0,0,0}, 16); memset(offsets, 0, 16); ret = drmModeAddFB2(drm_fd, width, height, format, handles, strides, offsets, &fb->fb_id, 0); } if (ret) { printf("failed to create fb: %s\n", strerror(errno)); free(fb); return NULL; } gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); return fb; } static uint32_t find_crtc_for_encoder(const drmModeRes *resources, const drmModeEncoder *encoder) { for (int i = 0; i < resources->count_crtcs; i++) { /* possible_crtcs is a bitmask as described here: * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api */ const uint32_t crtc_mask = 1U << i; const uint32_t crtc_id = resources->crtcs[i]; if (encoder->possible_crtcs & crtc_mask) { return crtc_id; } } /* no match found */ return 0; } static uint32_t find_crtc_for_connector(const struct drm *drm, const drmModeRes *resources, const drmModeConnector *connector) { for (int i = 0; i < connector->count_encoders; i++) { const uint32_t encoder_id = connector->encoders[i]; drmModeEncoder *encoder = drmModeGetEncoder(drm->fd, encoder_id); if (encoder) { const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder); drmModeFreeEncoder(encoder); if (crtc_id != 0) { return crtc_id; } } } /* no match found */ return 0; } static int get_resources(int fd, drmModeRes **resources) { *resources = drmModeGetResources(fd); if (*resources == NULL) return -1; return 0; } static int has_monitor_connected(int fd, drmModeRes* resources) { int i; drmModeConnector *connector; for (i = 0; i < resources->count_connectors; i++) { connector = drmModeGetConnector(fd, resources->connectors[i]); if (connector->connection == DRM_MODE_CONNECTED) { /* There is a monitor connected */ drmModeFreeConnector(connector); connector = NULL; return 1; } drmModeFreeConnector(connector); connector = NULL; } return 0; } #define MAX_DRM_DEVICES 64 static int find_drm_device(drmModeRes **resources) { drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL }; int num_devices, fd = -1; num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); if (num_devices < 0) { printf("drmGetDevices2 failed: %s\n", strerror(-num_devices)); return -1; } for (int i = 0; i < num_devices; i++) { drmDevicePtr device = devices[i]; int ret; if (!(device->available_nodes & (1 << DRM_NODE_PRIMARY))) continue; /* OK, it's a primary device. If we can get the * drmModeResources, it means it's also a * KMS-capable device. */ fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR); if (fd < 0) continue; ret = get_resources(fd, resources); if(getenv("SFML_DRM_DEBUG")) { printf("DRM device used: %d\n", i); } if(!ret && has_monitor_connected(fd, *resources) != 0) break; close(fd); fd = -1; } drmFreeDevices(devices, num_devices); if (fd < 0) printf("no drm device found!\n"); return fd; } int init_drm(struct drm *drm, const char *device, const char *mode_str, unsigned int vrefresh) { drmModeRes *resources; drmModeConnector *connector = NULL; drmModeEncoder *encoder = NULL; int i, ret, area; if (device) { drm->fd = open(device, O_RDWR); ret = get_resources(drm->fd, &resources); if (ret < 0 && errno == EOPNOTSUPP) printf("%s does not look like a modeset device\n", device); } else { drm->fd = find_drm_device(&resources); } if (drm->fd < 0) { printf("could not open drm device\n"); return -1; } if (!resources) { printf("drmModeGetResources failed: %s\n", strerror(errno)); return -1; } /* find a connected connector: */ for (i = 0; i < resources->count_connectors; i++) { connector = drmModeGetConnector(drm->fd, resources->connectors[i]); if (connector->connection == DRM_MODE_CONNECTED) { /* it's connected, let's use this! */ break; } drmModeFreeConnector(connector); connector = NULL; } if (!connector) { /* we could be fancy and listen for hotplug events and wait for * a connector.. */ printf("no connected connector!\n"); return -1; } /* find user requested mode: */ if (mode_str && *mode_str) { for (i = 0; i < connector->count_modes; i++) { drmModeModeInfo *current_mode = &connector->modes[i]; if (strcmp(current_mode->name, mode_str) == 0) { if (vrefresh == 0 || current_mode->vrefresh == vrefresh) { drm->mode = current_mode; break; } } } if (!drm->mode) printf("requested mode not found, using default mode!\n"); } /* find preferred mode or the highest resolution mode: */ if (!drm->mode) { for (i = 0, area = 0; i < connector->count_modes; i++) { drmModeModeInfo *current_mode = &connector->modes[i]; if (current_mode->type & DRM_MODE_TYPE_PREFERRED) { drm->mode = current_mode; break; } int current_area = current_mode->hdisplay * current_mode->vdisplay; if (current_area > area) { drm->mode = current_mode; area = current_area; } } } if (!drm->mode) { printf("could not find mode!\n"); return -1; } /* find encoder: */ for (i = 0; i < resources->count_encoders; i++) { encoder = drmModeGetEncoder(drm->fd, resources->encoders[i]); if (encoder->encoder_id == connector->encoder_id) break; drmModeFreeEncoder(encoder); encoder = NULL; } if (encoder) { drm->crtc_id = encoder->crtc_id; } else { uint32_t crtc_id = find_crtc_for_connector(drm, resources, connector); if (crtc_id == 0) { printf("no crtc found!\n"); return -1; } drm->crtc_id = crtc_id; } drmModeFreeResources(resources); drm->connector_id = connector->connector_id; drm->saved_connector = connector; drm->saved_encoder = encoder; // get original display mode so we can restore display mode after program exits drm->original_crtc = drmModeGetCrtc(drm->fd, drm->crtc_id); if(getenv("SFML_DRM_DEBUG")) { printf("DRM Mode used: %s@%d\n", drm->mode->name, drm->mode->vrefresh); } return 0; }