/* * 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.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; struct gbm_device *gbm = gbm_bo_get_device(bo); 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, stride, handle; int ret; if (fb) return fb; fb = calloc(1, sizeof *fb); fb->bo = bo; width = gbm_bo_get_width(bo); height = gbm_bo_get_height(bo); stride = gbm_bo_get_stride(bo); handle = gbm_bo_get_handle(bo).u32; ret = drmModeAddFB(drm_fd, width, height, 24, 32, stride, handle, &fb->fb_id); 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 prefered 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; }