/* * Copyright © 2013 Intel Corporation * * 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, sublicense, * 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 NONINFRINGEMENT. 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 #include #include #include #include #include #include "../overlay.h" //#include "rgb2yuv.h" #ifndef ALIGN #define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1)) #endif struct kms_image { uint32_t handle, name; uint32_t format; uint32_t width, height, stride; uint32_t size; void *map; }; struct kms_overlay { struct overlay base; struct kms_image image; int fd; int crtc; int x, y; int visible; void *mem; int size; }; static inline struct kms_overlay *to_kms_overlay(struct overlay *o) { return (struct kms_overlay *)o; } static int kms_create_fb(int fd, struct kms_image *image) { uint32_t offsets[4], pitches[4], handles[4]; handles[0] = image->handle; pitches[0] = image->stride; offsets[0] = 0; return drmModeAddFB2(fd, image->width, image->height, image->format, handles, pitches, offsets, &image->name, 0) == 0; } static int attach_to_crtc(int fd, int crtc, int x, int y, struct kms_image *image) { struct drm_mode_set_plane s; s.crtc_id = crtc; s.fb_id = image->name; s.flags = 0; s.crtc_x = x; s.crtc_y = y; s.crtc_w = image->width; s.crtc_h = image->height; s.src_x = 0; s.src_y = 0; s.src_w = image->width << 16; s.src_h = image->height << 16; return drmIoctl(fd, DRM_IOCTL_MODE_SETPLANE, &s) == 0; } static int detach_from_crtc(int fd, int crtc) { struct drm_mode_set_plane s; memset(&s, 0, sizeof(s)); s.crtc_id = crtc; return drmIoctl(fd, DRM_IOCTL_MODE_SETPLANE, &s) == 0; } static void kms_overlay_show(struct overlay *overlay) { struct kms_overlay *priv = to_kms_overlay(overlay); memcpy(priv->image.map, priv->mem, priv->size); if (!priv->visible) { attach_to_crtc(priv->fd, priv->crtc, priv->x, priv->y, &priv->image); priv->visible = true; } } static void kms_overlay_hide(struct overlay *overlay) { struct kms_overlay *priv = to_kms_overlay(overlay); if (priv->visible) { detach_from_crtc(priv->fd, priv->crtc); priv->visible = false; } } static void kms_overlay_destroy(void *data) { struct kms_overlay *priv = data; drmIoctl(priv->fd, DRM_IOCTL_MODE_RMFB, &priv->image.name); munmap(priv->image.map, priv->image.size); free(priv->mem); close(priv->fd); free(priv); } static int is_i915_device(int fd) { drm_version_t version; char name[5] = ""; memset(&version, 0, sizeof(version)); version.name_len = 4; version.name = name; if (drmIoctl(fd, DRM_IOCTL_VERSION, &version)) return 0; return strcmp("i915", name) == 0; } static int check_device(int fd) { int ret; /* Confirm that this is a i915.ko device with GEM/KMS enabled */ ret = is_i915_device(fd); if (ret) { struct drm_i915_getparam gp; gp.param = I915_PARAM_HAS_GEM; gp.value = &ret; if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp)) ret = 0; } if (ret) { struct drm_mode_card_res res; memset(&res, 0, sizeof(res)); if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) ret = 0; } return ret; } static int i915_open(void) { char buf[80]; int fd, n; for (n = 0; n < 16; n++) { sprintf(buf, "/dev/dri/card%d", n); fd = open(buf, O_RDWR); if (fd == -1) continue; if (!check_device(fd)) { close(fd); continue; } return fd; } return -1; } static int config_get_pipe(struct config *config) { const char *str; str = config_get_value(config, "kms", "pipe"); if (str == NULL) return 0; return atoi(str); } cairo_surface_t * kms_overlay_create(struct config *config, int *width, int *height) { struct drm_i915_gem_create create; struct drm_i915_gem_mmap_gtt map; struct kms_overlay *priv; drmModeResPtr kmode; int i, pipe; priv = malloc(sizeof(*priv)); if (priv == NULL) return NULL; priv->fd = i915_open(); if (priv->fd == -1) goto err_priv; kmode = drmModeGetResources(priv->fd); if (kmode == 0) goto err_fd; pipe = config_get_pipe(config); priv->crtc = 0; for (i = 0; i < kmode->count_crtcs; i++) { struct drm_i915_get_pipe_from_crtc_id get_pipe; get_pipe.pipe = 0; get_pipe.crtc_id = kmode->crtcs[i]; if (drmIoctl(priv->fd, DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID, &get_pipe)) { continue; } if (get_pipe.pipe != pipe) continue; priv->crtc = get_pipe.crtc_id; } if (priv->crtc == 0) goto err_fd; priv->image.format = DRM_FORMAT_XRGB8888; priv->image.width = ALIGN(*width, 4); priv->image.height = ALIGN(*height, 2); priv->image.stride = ALIGN(4*priv->image.width, 64); priv->image.size = ALIGN(priv->image.stride * priv->image.height, 4096); create.handle = 0; create.size = ALIGN(priv->image.size, 4096); drmIoctl(priv->fd, DRM_IOCTL_I915_GEM_CREATE, &create); if (create.handle == 0) goto err_fd; priv->image.handle = create.handle; if (!kms_create_fb(priv->fd, &priv->image)) goto err_create; /* XXX set color keys */ if (!attach_to_crtc(priv->fd, priv->crtc, 0, 0, &priv->image)) goto err_fb; detach_from_crtc(priv->fd, priv->crtc); map.handle = create.handle; if (drmIoctl(priv->fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &map)) goto err_fb; priv->image.map = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, priv->fd, map.offset); if (priv->image.map == (void *)-1) goto err_fb; priv->mem = malloc(create.size); if (priv->mem == NULL) goto err_map; priv->base.surface = cairo_image_surface_create_for_data(priv->mem, CAIRO_FORMAT_RGB24, priv->image.width, priv->image.height, priv->image.stride); if (cairo_surface_status(priv->base.surface)) goto err_mem; priv->base.show = kms_overlay_show; priv->base.hide = kms_overlay_hide; priv->visible = false; priv->x = 0; priv->y = 0; cairo_surface_set_user_data(priv->base.surface, &overlay_key, priv, kms_overlay_destroy); *width = priv->image.width; *height = priv->image.height; drmIoctl(priv->fd, DRM_IOCTL_GEM_CLOSE, &create.handle); return priv->base.surface; err_mem: free(priv->mem); err_map: munmap(priv->image.map, create.size); err_fb: drmIoctl(priv->fd, DRM_IOCTL_MODE_RMFB, &priv->image.name); err_create: drmIoctl(priv->fd, DRM_IOCTL_GEM_CLOSE, &create.handle); err_fd: close(priv->fd); err_priv: free(priv); return NULL; }