summaryrefslogtreecommitdiff
path: root/drm-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drm-common.c')
-rw-r--r--drm-common.c209
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;
+}