overlay: Add a raw kms output
authorChris Wilson <chris@chris-wilson.co.uk>
Sun, 25 Aug 2013 12:15:55 +0000 (13:15 +0100)
committerChris Wilson <chris@chris-wilson.co.uk>
Sun, 25 Aug 2013 12:34:31 +0000 (13:34 +0100)
For when you don't have any display server, use brute force.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
overlay/Makefile.am
overlay/kms/kms-overlay.c [new file with mode: 0644]
overlay/overlay.c
overlay/overlay.h

index 52a6127..b01bf38 100644 (file)
@@ -55,6 +55,10 @@ intel_gpu_overlay_SOURCES += \
        $(NULL)
 endif
 
+intel_gpu_overlay_SOURCES += \
+       kms/kms-overlay.c \
+       $(NULL)
+
 intel_gpu_overlay_SOURCES += $(both_x11_sources)
 
 EXTRA_DIST=README
diff --git a/overlay/kms/kms-overlay.c b/overlay/kms/kms-overlay.c
new file mode 100644 (file)
index 0000000..e49a495
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/mman.h>
+#include <cairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <drm.h>
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <i915_drm.h>
+#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_position(struct overlay *overlay,
+                                enum position p)
+{
+}
+
+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.position = kms_overlay_position;
+       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;
+}
index 1cf0eab..a1d21df 100644 (file)
@@ -760,10 +760,14 @@ int main(int argc, char **argv)
 
        ctx.width = 640;
        ctx.height = 236;
-       ctx.surface = x11_overlay_create(&config, &ctx.width, &ctx.height);
+       ctx.surface = NULL;
+       if (ctx.surface == NULL)
+               ctx.surface = x11_overlay_create(&config, &ctx.width, &ctx.height);
        if (ctx.surface == NULL)
                ctx.surface = x11_window_create(&config, &ctx.width, &ctx.height);
        if (ctx.surface == NULL)
+               ctx.surface = kms_overlay_create(&config, &ctx.width, &ctx.height);
+       if (ctx.surface == NULL)
                return ENOMEM;
 
        signal(SIGUSR1, signal_snapshot);
index f7b94d1..3733fce 100644 (file)
@@ -100,4 +100,6 @@ cairo_surface_t *x11_window_create(struct config *config, int *width, int *heigh
 static inline cairo_surface_t *x11_window_create(struct config *config, int *width, int *height) { return NULL; }
 #endif
 
+cairo_surface_t *kms_overlay_create(struct config *config, int *width, int *height);
+
 #endif /* OVERLAY_H */