/* * 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 "common.h" #include "drm-common.h" 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, 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); #ifdef HAVE_GBM_MODIFIERS 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 %lx\n", modifiers[0]); } ret = drmModeAddFB2WithModifiers(drm_fd, width, height, DRM_FORMAT_XRGB8888, handles, strides, offsets, modifiers, &fb->fb_id, flags); #endif 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, DRM_FORMAT_XRGB8888, 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) { int i; for (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 = 1 << i; const uint32_t crtc_id = resources->crtcs[i]; if (encoder->possible_crtcs & crtc_mask) { return crtc_id; } } /* no match found */ return -1; } static uint32_t find_crtc_for_connector(const struct drm *drm, const drmModeRes *resources, const drmModeConnector *connector) { int i; for (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 -1; } int init_drm(struct drm *drm, const char *device) { drmModeRes *resources; drmModeConnector *connector = NULL; drmModeEncoder *encoder = NULL; int i, area; drm->fd = open(device, O_RDWR); if (drm->fd < 0) { printf("could not open drm device\n"); return -1; } resources = drmModeGetResources(drm->fd); 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 preferred mode or the highest resolution 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; } 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; } for (i = 0; i < resources->count_crtcs; i++) { if (resources->crtcs[i] == drm->crtc_id) { drm->crtc_index = i; break; } } drmModeFreeResources(resources); drm->connector_id = connector->connector_id; return 0; }