diff options
Diffstat (limited to 'drm-common.c')
-rw-r--r-- | drm-common.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drm-common.c b/drm-common.c new file mode 100644 index 0000000..764ffa2 --- /dev/null +++ b/drm-common.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017 Rob Clark <rclark@redhat.com> + * + * 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; + 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; +} |