drm backend implementation
authorjh13.son <jh13.son@samsung.com>
Fri, 22 May 2015 11:07:33 +0000 (20:07 +0900)
committerTaekyun Kim <tkq.kim@samsung.com>
Fri, 19 Jun 2015 09:06:41 +0000 (18:06 +0900)
    - output implementation
        - initial version
        - create framebuffers
        - draw using gl-renderer or pixman-renderer
        - add test code

Change-Id: I136872213c54ef887c3a58cbccad95b7f01d1801

src/Makefile.am
src/compositor.c
src/modules/drm/drm-common.c
src/modules/drm/drm-internal.h
src/modules/drm/drm-output.c [new file with mode: 0644]
src/modules/libinput/libinput.c
src/modules/libinput/pepper-libinput.h
src/output.c
src/pepper-internal.h
test/Makefile.am
test/drm-backend.c [new file with mode: 0644]

index df69f96..cc9e513 100644 (file)
@@ -62,7 +62,8 @@ libpepper_la_CFLAGS += $(DRM_BACKEND_CFLAGS) -I$(top_srcdir)/src/modules/libinpu
 libpepper_la_LIBADD += $(DRM_BACKEND_LIBS)
 
 libpepper_la_SOURCES += modules/drm/drm-internal.h  \
-                        modules/drm/drm-common.c
+                        modules/drm/drm-common.c    \
+                        modules/drm/drm-output.c
 endif
 
 # libinput module for drm & fbdev backends
index 594c6bd..74ae8ef 100644 (file)
@@ -75,6 +75,7 @@ pepper_compositor_create(const char *socket_name)
                      compositor_bind);
     wl_list_init(&compositor->surfaces);
     wl_list_init(&compositor->seat_list);
+    wl_list_init(&compositor->output_list);
 
     if (wl_display_init_shm(compositor->display) != 0)
     {
index 47f90f1..5e43e15 100644 (file)
@@ -1,3 +1,5 @@
+#include <libudev.h>
+#include <unistd.h>
 #include "drm-internal.h"
 
 PEPPER_API pepper_drm_t *
@@ -12,8 +14,15 @@ pepper_drm_create(pepper_compositor_t *compositor, const char *device)
         goto error;
     }
 
+    drm->udev = udev_new();
+    if (!drm->udev)
+    {
+        PEPPER_ERROR("Failed to create udev context in %s\n", __FUNCTION__);
+        goto error;
+    }
+
     drm->compositor = compositor;
-    drm->input = pepper_libinput_create(compositor);
+    drm->input = pepper_libinput_create(compositor, drm->udev);
 
     if (!drm->input)
     {
@@ -21,13 +30,19 @@ pepper_drm_create(pepper_compositor_t *compositor, const char *device)
         goto error;
     }
 
-    /* TODO */
+    wl_list_init(&drm->output_list);
+
+    if (!pepper_drm_output_create(drm))
+    {
+        PEPPER_ERROR("Failed to connect drm output in %s\n", __FUNCTION__);
+        goto error;
+    }
 
     return drm;
 
 error:
     if (drm)
-        pepper_free(drm);
+        pepper_drm_destroy(drm);
 
     return NULL;
 }
@@ -35,9 +50,29 @@ error:
 PEPPER_API void
 pepper_drm_destroy(pepper_drm_t *drm)
 {
-    /* TODO */
+    drm_output_t *output, *next;
+
+    if (drm->drm_event_source)
+        wl_event_source_remove(drm->drm_event_source);
+
+    if (!wl_list_empty(&drm->output_list))
+    {
+        wl_list_for_each_safe(output, next, &drm->output_list, link)
+            pepper_drm_output_destroy(output);
+    }
+
+    if (drm->crtcs)
+        pepper_free(drm->crtcs);
+
+    if (drm->drm_fd)
+        close(drm->drm_fd);
+
+    if (drm->input)
+        pepper_libinput_destroy(drm->input);
+
+    if (drm->udev)
+        udev_unref(drm->udev);
 
-    pepper_libinput_destroy(drm->input);
     pepper_free(drm);
 
     return;
index 1ec9ab2..ef64fb4 100644 (file)
@@ -1,15 +1,95 @@
 #ifndef DRM_INTERNAL_H
 #define DRM_INTERNAL_H
 
+#include <pixman.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
 #include <common.h>
 #include <pepper-libinput.h>
 
 #include "pepper-drm.h"
 
+#define DUMB_FB_COUNT   2
+
+typedef struct drm_output       drm_output_t;
+typedef struct drm_fb           drm_fb_t;
+
 struct pepper_drm
 {
     pepper_compositor_t        *compositor;
     pepper_libinput_t          *input;
+    struct udev                *udev;
+
+    int                         drm_fd;
+    struct wl_list              output_list;
+    uint32_t                   *crtcs;
+    uint32_t                    count_crtcs;
+    uint32_t                    min_width, min_height;
+    uint32_t                    max_width, max_height;
+
+    struct wl_event_source     *drm_event_source;
 };
 
+struct drm_output
+{
+    pepper_drm_t               *drm;
+    pepper_output_t            *base;
+
+    struct wl_list              link;
+
+    int32_t                     subpixel;
+    uint32_t                    w, h;
+
+    uint32_t                    crtc_id;
+    uint32_t                    conn_id;
+
+    struct wl_signal            destroy_signal;
+    struct wl_signal            mode_change_signal;
+    struct wl_signal            frame_signal;
+
+    int                         mode_count;
+    drmModeModeInfo            *modes;
+    drmModeModeInfo            *current_mode;
+
+    drmModeCrtc                *saved_crtc;
+
+    drm_fb_t                   *dumb_fb[DUMB_FB_COUNT];
+    pixman_image_t             *dumb_image[DUMB_FB_COUNT];
+    int                         back_fb_index;
+
+    struct gbm_device          *gbm_device;
+    struct gbm_surface         *gbm_surface;
+
+    drm_fb_t                   *front_fb;
+    drm_fb_t                   *back_fb;
+
+    pepper_renderer_t          *renderer;
+
+    pepper_bool_t               vblank_pending;
+    pepper_bool_t               page_flip_pending;
+
+    /* TODO */
+};
+
+struct drm_fb
+{
+    drm_output_t               *output;
+
+    int                         fd;
+    uint32_t                    id;
+    uint32_t                    handle;
+    uint32_t                    stride;
+    uint32_t                    size;
+
+    struct gbm_bo              *bo;
+    void                       *map;
+};
+
+pepper_bool_t
+pepper_drm_output_create(pepper_drm_t *drm);
+
+void
+pepper_drm_output_destroy(drm_output_t *output);
+
 #endif /* DRM_INTERNAL_H */
diff --git a/src/modules/drm/drm-output.c b/src/modules/drm/drm-output.c
new file mode 100644 (file)
index 0000000..31043b4
--- /dev/null
@@ -0,0 +1,947 @@
+#include <fcntl.h>
+#include <pixman.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gbm.h>
+#include <libudev.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "drm-internal.h"
+
+#include <pepper-pixman-renderer.h>
+#include <pepper-gl-renderer.h>
+
+#define USE_PIXMAN  0   /* FIXME */
+
+static pepper_bool_t
+init_renderer(drm_output_t *output);
+
+static void
+fini_renderer(drm_output_t *output);
+
+void
+drm_output_destroy(void *o)
+{
+    drm_output_t   *output = (drm_output_t *)o;
+
+    wl_list_remove(&output->link);
+
+    if (output->saved_crtc)
+    {
+        drmModeCrtc *c = output->saved_crtc;
+        drmModeSetCrtc(output->drm->drm_fd, c->crtc_id, c->buffer_id, c->x, c->y,
+                       &output->conn_id, 1, &c->mode);
+    }
+
+    fini_renderer(output);
+
+    if (output->modes)
+        pepper_free(output->modes);
+
+    pepper_free(output);
+}
+
+static void
+drm_output_add_destroy_listener(void *o, struct wl_listener *listener)
+{
+    drm_output_t *output = (drm_output_t *)o;
+    wl_signal_add(&output->destroy_signal, listener);
+}
+
+static void
+drm_output_add_mode_change_listener(void *o, struct wl_listener *listener)
+{
+    drm_output_t *output = (drm_output_t *)o;
+    wl_signal_add(&output->mode_change_signal, listener);
+}
+
+static int32_t
+drm_output_get_subpixel_order(void *data)
+{
+    drm_output_t *output = (drm_output_t *)data;
+
+    switch (output->subpixel)
+    {
+        case DRM_MODE_SUBPIXEL_UNKNOWN:
+            return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+        case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+            return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+        case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+            return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+        case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+            return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+        case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+            return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+        case DRM_MODE_SUBPIXEL_NONE:
+            return WL_OUTPUT_SUBPIXEL_NONE;
+        default:
+            return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+    }
+
+    return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+}
+
+static const char *
+drm_output_get_maker_name(void *output)
+{
+    PEPPER_IGNORE(output);
+    return "PePPer DRM";
+}
+
+static const char *
+drm_output_get_model_name(void *output)
+{
+    PEPPER_IGNORE(output);
+    return "PePPer DRM";
+}
+
+static int
+drm_output_get_mode_count(void *o)
+{
+    drm_output_t       *output = (drm_output_t *)o;
+    drmModeConnector   *c = drmModeGetConnector(output->drm->drm_fd, output->conn_id);
+    int                 count = c->count_modes;
+
+    drmModeFreeConnector(c);
+
+    return count;
+}
+
+static void
+drm_output_get_mode(void *o, int index, pepper_output_mode_t *mode)
+{
+    drm_output_t       *output = (drm_output_t *)o;
+    drmModeModeInfo    *m = &(output->modes[index]);
+
+    mode->flags = 0;
+    mode->w = m->hdisplay;
+    mode->h = m->vdisplay;
+    mode->refresh = 60000/* FIXME */;
+
+    if (m->type & DRM_MODE_TYPE_PREFERRED)
+        mode->flags |= WL_OUTPUT_MODE_PREFERRED;
+
+    if (m == output->current_mode)
+        mode->flags |= WL_OUTPUT_MODE_CURRENT;
+}
+
+static pepper_bool_t
+drm_output_set_mode(void *o, const pepper_output_mode_t *mode)
+{
+    int                 i;
+    drm_output_t       *output = (drm_output_t *)o;
+
+    /*
+     *  typedef struct _drmModeModeInfo {
+     *      uint32_t clock;
+     *      uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+     *      uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+     *
+     *      uint32_t vrefresh;
+     *
+     *      uint32_t flags;
+     *      uint32_t type;
+     *      char name[DRM_DISPLAY_MODE_LEN];
+     *  } drmModeModeInfo, *drmModeModeInfoPtr;
+     */
+    for (i = 0; i < output->mode_count; i++)
+    {
+        drmModeModeInfo *m = &(output->modes[i]);
+        if ((m->hdisplay == mode->w) && (m->vdisplay == mode->h))   /* FIXME */
+        {
+            output->current_mode = m;
+            output->w = m->hdisplay;
+            output->h = m->vdisplay;
+
+            fini_renderer(output);
+            init_renderer(output);
+
+            wl_signal_emit(&output->mode_change_signal, output);
+
+            return PEPPER_TRUE;
+        }
+    }
+
+    return PEPPER_FALSE;
+}
+
+static void
+destroy_fb(struct gbm_bo *bo, void *data)
+{
+    drm_fb_t *fb = (drm_fb_t *)data;
+
+    if (fb->id)
+        drmModeRmFB(fb->fd, fb->id);
+
+    pepper_free(fb);
+}
+
+static drm_fb_t *
+create_fb(drm_output_t *output, struct gbm_bo *bo)
+{
+    int         ret;
+    uint32_t    w, h;
+    drm_fb_t   *fb;
+
+    fb = (drm_fb_t *)pepper_calloc(1, sizeof(drm_fb_t));
+    if (!fb)
+    {
+        PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    w = gbm_bo_get_width(bo);
+    h = gbm_bo_get_height(bo);
+    fb->fd = output->drm->drm_fd;
+    fb->handle = gbm_bo_get_handle(bo).u32;
+    fb->stride = gbm_bo_get_stride(bo);
+    fb->size = fb->stride * h;
+    fb->bo = bo;
+
+    ret = drmModeAddFB(fb->fd, w, h, 24, 32, fb->stride, fb->handle, &fb->id);
+    if (ret)
+    {
+        PEPPER_ERROR("Failed to add fb in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    gbm_bo_set_user_data(bo, fb, destroy_fb);
+
+    return fb;
+
+error:
+
+    if (fb)
+        destroy_fb(bo, fb);
+
+    return NULL;
+}
+
+static drm_fb_t *
+get_fb(drm_output_t *output, struct gbm_bo *bo)
+{
+    drm_fb_t *fb = (drm_fb_t *)gbm_bo_get_user_data(bo);
+
+    if (fb)
+        return fb;
+
+    return create_fb(output, bo);
+}
+
+static void
+draw_gl(drm_output_t *output)
+{
+    struct gbm_bo *bo;
+
+    output->renderer->draw(output->renderer, NULL/*FIXME*/, NULL);
+
+    bo = gbm_surface_lock_front_buffer(output->gbm_surface);
+    if (!bo)
+    {
+        PEPPER_ERROR("Failed to lock front buffer in %s\n", __FUNCTION__);
+        return;
+    }
+
+    output->back_fb = get_fb(output, bo);
+    if (!output->back_fb)
+    {
+        PEPPER_ERROR("Failed to get back fb in %s\n", __FUNCTION__);
+        gbm_surface_release_buffer(output->gbm_surface, bo);
+        return;
+    }
+}
+
+static void
+draw_pixman(drm_output_t *output)
+{
+    output->back_fb_index ^= 1;
+    output->back_fb = output->dumb_fb[output->back_fb_index];
+    output->renderer->draw(output->renderer, output->dumb_image[output->back_fb_index], NULL);
+}
+
+static void
+draw(drm_output_t *output)
+{
+    if (USE_PIXMAN/* FIXME */)
+        draw_pixman(output);
+    else
+        draw_gl(output);
+}
+
+static void
+drm_output_repaint(void *o)
+{
+    int             ret;
+    drm_output_t   *output = (drm_output_t *)o;
+
+    draw(output);
+
+    if (!output->back_fb)
+        return;
+
+    ret = drmModeSetCrtc(output->drm->drm_fd, output->crtc_id, output->back_fb->id,
+                         0, 0, &output->conn_id, 1, output->current_mode);
+    if (ret)
+    {
+        PEPPER_ERROR("Failed to set CRTC[%d] for Connector[%d] in %s\n",
+                     output->crtc_id, output->conn_id, __FUNCTION__);
+        return;
+    }
+
+    ret = drmModePageFlip(output->drm->drm_fd, output->crtc_id, output->back_fb->id,
+                          DRM_MODE_PAGE_FLIP_EVENT, output);
+    if (ret < 0)
+    {
+        PEPPER_ERROR("Failed to queue pageflip in %s\n", __FUNCTION__);
+        return;
+    }
+
+    output->page_flip_pending = PEPPER_TRUE;
+
+    /* TODO: set planes */
+
+}
+
+static void
+drm_output_add_frame_listener(void *o, struct wl_listener *listener)
+{
+    drm_output_t *output = (drm_output_t *)o;
+    wl_signal_add(&output->frame_signal, listener);
+}
+
+struct pepper_output_interface drm_output_interface =
+{
+    drm_output_destroy,
+    drm_output_add_destroy_listener,
+    drm_output_add_mode_change_listener,
+
+    drm_output_get_subpixel_order,
+    drm_output_get_maker_name,
+    drm_output_get_model_name,
+
+    drm_output_get_mode_count,
+    drm_output_get_mode,
+    drm_output_set_mode,
+
+    drm_output_repaint,
+    drm_output_add_frame_listener,
+};
+
+static struct udev_device *
+find_primary_gpu(struct udev *udev) /* FIXME: copied from weston */
+{
+    struct udev_enumerate   *e;
+    struct udev_list_entry  *entry;
+    struct udev_device      *pci, *device, *drm_device = NULL;
+    const char              *path, *device_seat, *id;
+
+    e = udev_enumerate_new(udev);
+    udev_enumerate_add_match_subsystem(e, "drm");
+    udev_enumerate_add_match_sysname(e, "card[0-9]*");
+    udev_enumerate_scan_devices(e);
+
+    udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e))
+    {
+        path = udev_list_entry_get_name(entry);
+        device = udev_device_new_from_syspath(udev, path);
+        if (!device)
+            continue;
+        device_seat = udev_device_get_property_value(device, "ID_SEAT");
+        if (!device_seat)
+            device_seat = "seat0";
+        if (strcmp(device_seat, "seat0" /* FIXME: default seat? */))
+        {
+            udev_device_unref(device);
+            continue;
+        }
+
+        pci = udev_device_get_parent_with_subsystem_devtype(device, "pci", NULL);
+        if (pci)
+        {
+            id = udev_device_get_sysattr_value(pci, "boot_vga");
+            if (id && !strcmp(id, "1"))
+            {
+                if (drm_device)
+                    udev_device_unref(drm_device);
+                drm_device = device;
+                break;
+            }
+        }
+
+        if (!drm_device)
+            drm_device = device;
+        else
+            udev_device_unref(device);
+    }
+
+    udev_enumerate_unref(e);
+    return drm_device;
+}
+
+static int
+drm_open(const char *path, int flags)
+{
+    int             fd;
+    struct stat     s;
+    drm_magic_t     m;
+
+    fd = open(path, flags | O_CLOEXEC);
+    if (fd == -1)
+    {
+        PEPPER_ERROR("Failed to open file[%s] in %s\n", path, __FUNCTION__);
+        goto error;
+    }
+
+    if (fstat(fd, &s) == -1)
+    {
+        PEPPER_ERROR("Failed to get file[%s] state in %s\n", path, __FUNCTION__);
+        goto error;
+    }
+
+    if (major(s.st_rdev) != 226/*drm major*/)
+    {
+        PEPPER_ERROR("File %s is not a drm device file\n", path);
+        goto error;
+    }
+
+    if ((drmGetMagic(fd, &m) != 0) || (drmAuthMagic(fd, m) != 0))
+    {
+        PEPPER_ERROR("drm fd is not master\n");
+        goto error;
+    }
+
+    return fd;
+
+error:
+    if (fd > 0)
+        close(fd);
+    return -1;
+}
+
+static int
+find_crtc(pepper_drm_t *drm, drmModeRes *res, drmModeConnector *conn)
+{
+    unsigned int    i, j;
+    drmModeEncoder *enc;
+    drm_output_t   *output;
+
+    for (i = 0; i < conn->count_encoders; i++)
+    {
+        enc = drmModeGetEncoder(drm->drm_fd, conn->encoders[i]);
+        if (!enc)
+            continue;
+
+        for (j = 0; j < res->count_crtcs; j++)
+        {
+            if (!(enc->possible_crtcs & (1 << j)))
+                continue;
+
+            wl_list_for_each(output, &drm->output_list, link)
+            {
+                if (res->crtcs[j] == output->crtc_id)
+                    continue;
+            }
+
+            return res->crtcs[j];
+        }
+    }
+
+    return -1;
+}
+
+/* FIXME: copied from weston */
+static drm_fb_t *
+create_dumb_fb(drm_output_t *output)
+{
+    drm_fb_t   *fb;
+    int         ret;
+
+    int         drm_fd = output->drm->drm_fd;
+    uint32_t    width = output->w;
+    uint32_t    height = output->h;
+
+    struct drm_mode_create_dumb     create_arg;
+    struct drm_mode_destroy_dumb    destroy_arg;
+    struct drm_mode_map_dumb        map_arg;
+
+    fb = pepper_calloc(1, sizeof(drm_fb_t));
+    if (!fb)
+    {
+        PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
+        return NULL;
+    }
+
+    memset(&create_arg, 0, sizeof create_arg);
+    create_arg.bpp = 32;
+    create_arg.width = width;
+    create_arg.height = height;
+
+    ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
+    if (ret)
+    {
+        PEPPER_ERROR("Failed to create dumb_fb fb with ioctl in %s\n", __FUNCTION__);
+        goto err_fb;
+    }
+
+    fb->handle = create_arg.handle;
+    fb->stride = create_arg.pitch;
+    fb->size = create_arg.size;
+    fb->fd = drm_fd;
+
+    ret = drmModeAddFB(drm_fd, width, height, 24, 32, fb->stride, fb->handle, &fb->id);
+    if (ret)
+    {
+        PEPPER_ERROR("Failed to add fb in %s\n", __FUNCTION__);
+        goto err_bo;
+    }
+
+    memset(&map_arg, 0, sizeof map_arg);
+    map_arg.handle = fb->handle;
+    ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
+    if (ret)
+    {
+        PEPPER_ERROR("Failed to map dumb_fb fb in %s\n", __FUNCTION__);
+        goto err_add_fb;
+    }
+
+    fb->map = mmap(0, fb->size, PROT_WRITE, MAP_SHARED, drm_fd, map_arg.offset);
+    if (fb->map == MAP_FAILED)
+    {
+        PEPPER_ERROR("Failed to map fb in %s\n", __FUNCTION__);
+        goto err_add_fb;
+    }
+
+    return fb;
+
+err_add_fb:
+    drmModeRmFB(drm_fd, fb->id);
+
+err_bo:
+    memset(&destroy_arg, 0, sizeof(destroy_arg));
+    destroy_arg.handle = create_arg.handle;
+    drmIoctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+err_fb:
+    pepper_free(fb);
+    return NULL;
+}
+
+/* FIXME: copied from weston */
+static void
+destroy_dumb_fb(drm_fb_t *fb)
+{
+    struct drm_mode_destroy_dumb destroy_arg;
+
+    if (!fb->map)
+        return;
+
+    if (fb->id)
+        drmModeRmFB(fb->fd, fb->id);
+
+    munmap(fb->map, fb->size);
+
+    memset(&destroy_arg, 0, sizeof(destroy_arg));
+    destroy_arg.handle = fb->handle;
+    drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+    pepper_free(fb);
+}
+
+static void
+fini_pixman_renderer(drm_output_t *output)
+{
+    int i;
+
+    for (i = 0; i < DUMB_FB_COUNT; i++)
+    {
+        if (output->dumb_fb[i])
+            destroy_dumb_fb(output->dumb_fb[i]);
+        if (output->dumb_image[i])
+            pixman_image_unref(output->dumb_image[i]);
+    }
+
+    if (output->renderer && output->renderer->destroy)
+        output->renderer->destroy(output->renderer);
+}
+
+/* FIXME: copied from weston */
+static pepper_bool_t
+init_pixman_renderer(drm_output_t *output)
+{
+    int i;
+
+    for (i = 0; i < DUMB_FB_COUNT; i++)
+    {
+        output->dumb_fb[i] = create_dumb_fb(output);
+        if (!output->dumb_fb[i])
+        {
+            PEPPER_ERROR("Failed to create dumb_fb[%d] in %s\n", i, __FUNCTION__);
+            goto error;
+        }
+
+        output->dumb_image[i] = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+                                                    output->w, output->h,
+                                                    output->dumb_fb[i]->map,
+                                                    output->dumb_fb[i]->stride);
+        if (!output->dumb_image[i])
+        {
+            PEPPER_ERROR("Failed to create (pixman)dumb_image[%d] in %s\n", i, __FUNCTION__);
+            goto error;
+        }
+    }
+
+    output->renderer = pepper_pixman_renderer_create();
+    if (!output->renderer)
+    {
+        PEPPER_ERROR("Failed to create pixman renderer in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    return PEPPER_TRUE;
+
+error:
+
+    fini_pixman_renderer(output);
+
+    return PEPPER_FALSE;
+}
+
+static void
+fini_gl_renderer(drm_output_t *output)
+{
+    if (output->renderer)
+        output->renderer->destroy(output->renderer);
+
+    if (output->gbm_surface)
+        gbm_surface_destroy(output->gbm_surface);
+
+    if (output->gbm_device)
+        gbm_device_destroy(output->gbm_device);
+}
+
+static pepper_bool_t
+init_gl_renderer(drm_output_t *output)
+{
+    uint32_t native_visual_id;
+
+    output->gbm_device = gbm_create_device(output->drm->drm_fd);
+    if (!output->gbm_device)
+    {
+        PEPPER_ERROR("Failed to create gbm device in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    output->gbm_surface = gbm_surface_create(output->gbm_device, output->w, output->h,
+                                             GBM_FORMAT_XRGB8888/*FIXME*/,
+                                             GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING/*FIXME*/);
+    if (!output->gbm_surface)
+    {
+        PEPPER_ERROR("Failed to create gbm surface in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    /*
+     * PEPPER_API pepper_renderer_t *
+     * pepper_gl_renderer_create(pepper_compositor_t *compositor,
+     *                           void *display, void *window, const char *platform,
+     *                           pepper_format_t format, const uint32_t *native_visual_id);
+     */
+    native_visual_id = GBM_FORMAT_XRGB8888;
+    output->renderer = pepper_gl_renderer_create(output->drm->compositor,
+                                                 output->gbm_device, output->gbm_surface, "gbm",
+                                                 PEPPER_FORMAT_ARGB8888/*FIXME*/,
+                                                 &native_visual_id/*FIXME*/);
+    if (!output->renderer)
+    {
+        PEPPER_ERROR("Failed to create gl renderer in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    return PEPPER_TRUE;
+
+error:
+    fini_gl_renderer(output);
+
+    return PEPPER_FALSE;
+}
+
+static pepper_bool_t
+init_renderer(drm_output_t *output)
+{
+    if (USE_PIXMAN/* FIXME */)
+        return init_pixman_renderer(output);
+    else
+        return init_gl_renderer(output);
+}
+
+static void
+fini_renderer(drm_output_t *output)
+{
+    if (USE_PIXMAN/* FIXME */)
+        fini_pixman_renderer(output);
+    else
+        fini_gl_renderer(output);
+}
+
+static drm_output_t *
+drm_output_create(pepper_drm_t *drm, struct udev_device *device,
+                  drmModeRes *res, drmModeConnector *conn)
+{
+    int             i;
+    drm_output_t   *output;
+
+    output = (drm_output_t *)pepper_calloc(1, sizeof(drm_output_t));
+    if (!output)
+    {
+        PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    output->drm = drm;
+    output->subpixel = conn->subpixel;
+
+    wl_signal_init(&output->destroy_signal);
+    wl_signal_init(&output->mode_change_signal);
+    wl_signal_init(&output->frame_signal);
+
+    wl_list_insert(&drm->output_list, &output->link);
+
+    /* find crtc + connector */
+    output->crtc_id = find_crtc(drm, res, conn);
+    if (output->crtc_id < 0)
+    {
+        PEPPER_ERROR("Failed to find crtc in %s\n", __FUNCTION__);
+        goto error;
+    }
+    output->conn_id = conn->connector_id;
+    output->saved_crtc = drmModeGetCrtc(drm->drm_fd, output->crtc_id);
+
+    /* set modes */
+    output->mode_count = conn->count_modes;
+    output->modes = (drmModeModeInfo *)pepper_calloc(conn->count_modes,
+                                                     sizeof(drmModeModeInfo));
+    if (!output->modes)
+    {
+        PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    memcpy(output->modes, conn->modes, conn->count_modes * sizeof(drmModeModeInfo));
+    for (i = 0; i < output->mode_count; i++)
+    {
+        drmModeModeInfo *m = &(output->modes[i]);
+        if (m->type & DRM_MODE_TYPE_PREFERRED)
+        {
+            output->current_mode = m;
+            output->w = m->hdisplay;
+            output->h = m->vdisplay;
+        }
+    }
+
+    if (!init_renderer(output))
+    {
+        PEPPER_ERROR("Failed to initialize renderer in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    return output;
+
+error:
+
+    if (output)
+        drm_output_destroy(output);
+
+    return NULL;
+}
+
+static pepper_bool_t
+drm_add_outputs(pepper_drm_t *drm, struct udev_device *device)
+{
+    int                 i;
+    drmModeRes         *res;
+    drmModeConnector   *conn;
+    drm_output_t       *output;
+
+    res = drmModeGetResources(drm->drm_fd);
+    if (!res)
+    {
+        PEPPER_ERROR("Failed to get drm resources in %s\n", __FUNCTION__);
+        return PEPPER_FALSE;
+    }
+
+    drm->crtcs = pepper_calloc(res->count_crtcs, sizeof(uint32_t));
+    if (!drm->crtcs)
+    {
+        PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
+        return PEPPER_FALSE;
+    }
+    drm->count_crtcs = res->count_crtcs;
+    memcpy(drm->crtcs, res->crtcs, sizeof(uint32_t) * res->count_crtcs);
+
+    drm->min_width = res->min_width;
+    drm->min_height = res->min_height;
+    drm->max_width = res->max_width;
+    drm->max_height = res->max_height;
+
+    for (i = 0; i < res->count_connectors; i++)
+    {
+        conn = drmModeGetConnector(drm->drm_fd, res->connectors[i]);
+        if (!conn)
+            continue;
+
+        if (conn->connection != DRM_MODE_CONNECTED/* CHECKME */)
+        {
+            drmModeFreeConnector(conn);
+            continue;
+        }
+
+        output = drm_output_create(drm, device, res, conn);
+        if (!output)
+        {
+            PEPPER_ERROR("Failed to create drm_output in %s\n", __FUNCTION__);
+            drmModeFreeConnector(conn);
+            continue;
+        }
+
+        /*
+         * PEPPER_API pepper_output_t *
+         * pepper_compositor_add_output(pepper_compositor_t *compositor,
+         *                              const pepper_output_interface_t *interface, void *data)
+         */
+        output->base = pepper_compositor_add_output(output->drm->compositor,
+                                                    &drm_output_interface, output);
+        if (!output->base)
+        {
+            PEPPER_ERROR("Failed to add output to compositor in %s\n", __FUNCTION__);
+            drm_output_destroy(output);
+            drmModeFreeConnector(conn);
+            continue;
+        }
+
+        drmModeFreeConnector(conn);
+    }
+
+    if (wl_list_empty(&drm->output_list))
+    {
+        PEPPER_ERROR("Failed to find active output in %s\n", __FUNCTION__);
+        drmModeFreeResources(res);
+        return PEPPER_FALSE;
+    }
+
+    drmModeFreeResources(res);
+
+    return PEPPER_TRUE;
+}
+
+static void
+handle_vblank(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec,
+              void *user_data)
+{
+    /* TODO */
+}
+
+void pepper_output_schedule_repaint(pepper_output_t *output);   /* TODO: remove */
+
+static void
+handle_page_flip(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec,
+                 void *user_data)
+{
+    drm_output_t *output = (drm_output_t *)user_data;
+
+    if (output->page_flip_pending)
+    {
+        if (output->front_fb && output->front_fb->bo) /* FIXME */
+            gbm_surface_release_buffer(output->gbm_surface, output->front_fb->bo);
+        output->front_fb = output->back_fb;
+        output->back_fb = NULL;
+    }
+
+    output->page_flip_pending = PEPPER_FALSE;
+    if (!output->vblank_pending)
+    {
+        wl_signal_emit(&output->frame_signal, output->base);
+    }
+
+    pepper_output_schedule_repaint(output->base);   /* TODO: remove */
+}
+
+static int
+handle_drm_events(int fd, uint32_t mask, void *data)
+{
+    drmEventContext ctx;
+
+    memset(&ctx, 0, sizeof(drmEventContext));
+    ctx.version = DRM_EVENT_CONTEXT_VERSION;
+    ctx.vblank_handler = handle_vblank;
+    ctx.page_flip_handler = handle_page_flip;
+    drmHandleEvent(fd, &ctx);
+
+    return 0;
+}
+
+pepper_bool_t
+pepper_drm_output_create(pepper_drm_t *drm)
+{
+    struct udev_device      *drm_device;
+    const char              *filepath;
+
+    struct wl_display       *display;
+    struct wl_event_loop    *loop;
+
+    /* drm open */
+    drm_device = find_primary_gpu(drm->udev);
+    if (!drm_device)
+    {
+        PEPPER_ERROR("Failed to find primary gpu in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    filepath = udev_device_get_devnode(drm_device);
+    drm->drm_fd = drm_open(filepath, O_RDWR);
+    if (drm->drm_fd < 0)
+    {
+        PEPPER_ERROR("Failed to open drm[%s] in %s\n", filepath, __FUNCTION__);
+        goto error;
+    }
+
+    /* add outputs */
+    if (drm_add_outputs(drm, drm_device) == PEPPER_FALSE)
+    {
+        PEPPER_ERROR("Failed to add outputs in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    /* add drm fd to main loop */
+    display = pepper_compositor_get_display(drm->compositor);
+    loop = wl_display_get_event_loop(display);
+    drm->drm_event_source = wl_event_loop_add_fd(loop, drm->drm_fd, WL_EVENT_READABLE,
+                                                 handle_drm_events, drm);
+    if (!drm->drm_event_source)
+    {
+        PEPPER_ERROR("Failed to add drm fd to main loop in %s\n", __FUNCTION__);
+        goto error;
+    }
+
+    udev_device_unref(drm_device);
+
+    return PEPPER_TRUE;
+
+error:
+
+    if (drm_device)
+        udev_device_unref(drm_device);
+
+    return PEPPER_FALSE;
+}
+
+void
+pepper_drm_output_destroy(drm_output_t *output)
+{
+    drm_output_destroy(output);
+}
index ffc7821..611c318 100644 (file)
@@ -477,7 +477,7 @@ handle_libinput_events(int fd, uint32_t mask, void *data)
 }
 
 PEPPER_API pepper_libinput_t *
-pepper_libinput_create(pepper_compositor_t *compositor)
+pepper_libinput_create(pepper_compositor_t *compositor, struct udev *udev)
 {
     struct wl_display      *display;
     struct wl_event_loop   *loop;
@@ -487,22 +487,18 @@ pepper_libinput_create(pepper_compositor_t *compositor)
     if (!input)
     {
         PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__);
-        return PEPPER_FALSE;
-    }
-
-    input->udev = udev_new();
-    if (!input->udev)
-    {
-        PEPPER_ERROR("Failed to initialize udev in %s\n", __FUNCTION__);
         goto error;
     }
 
+    input->udev = udev;
+
     input->libinput = libinput_udev_create_context(&libinput_interface, input, input->udev);
     if (!input->libinput)
     {
         PEPPER_ERROR("Failed to initialize libinput in %s\n", __FUNCTION__);
         goto error;
     }
+    libinput_ref(input->libinput);
 
     if (libinput_udev_assign_seat(input->libinput, "seat0"/* FIXME */) != 0)
     {
@@ -546,9 +542,11 @@ pepper_libinput_destroy(pepper_libinput_t *input)
     wl_list_for_each_safe(seat, tmp, &input->seat_list, link)
         libinput_seat_destroy(seat);
 
-    udev_unref(input->udev);
-    libinput_unref(input->libinput);
-    wl_event_source_remove(input->libinput_event_source);
+    if (input->libinput)
+        libinput_unref(input->libinput);
+
+    if (input->libinput_event_source)
+        wl_event_source_remove(input->libinput_event_source);
 
     pepper_free(input);
 }
index c62af96..0f5151e 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef PEPPER_LIBINPUT_H
 #define PEPPER_LIBINPUT_H
 
+#include <libudev.h>
 #include <pepper.h>
 
 #ifdef __cplusplus
@@ -10,7 +11,7 @@ extern "C" {
 typedef struct pepper_libinput   pepper_libinput_t;
 
 PEPPER_API pepper_libinput_t *
-pepper_libinput_create(pepper_compositor_t *compositor);
+pepper_libinput_create(pepper_compositor_t *compositor, struct udev *udev);
 
 PEPPER_API void
 pepper_libinput_destroy(pepper_libinput_t *input);
index 00c9c2b..e9da091 100644 (file)
@@ -3,8 +3,9 @@
 static void
 output_update_mode(pepper_output_t *output)
 {
-    int                 i;
-    struct wl_resource  *resource;
+    int                     i;
+    struct wl_resource     *resource;
+    pepper_output_mode_t   *preferred_mode = NULL;
 
     output->current_mode = NULL;
 
@@ -28,8 +29,13 @@ output_update_mode(pepper_output_t *output)
         if (output->modes[i].flags & WL_OUTPUT_MODE_CURRENT)
             output->current_mode = &output->modes[i];
 
+        if (output->modes[i].flags & WL_OUTPUT_MODE_PREFERRED)
+            preferred_mode = &output->modes[i];
     }
 
+    if (!output->current_mode)
+        output->current_mode = preferred_mode;
+
     wl_resource_for_each(resource, &output->resources)
     {
         for (i = 0; i < output->mode_count; i++)
@@ -211,6 +217,8 @@ pepper_compositor_add_output(pepper_compositor_t *compositor,
     output->geometry.w = output->current_mode->w;
     output->geometry.h = output->current_mode->h;
 
+    wl_list_insert(&compositor->output_list, &output->link);
+
     /* Install listeners. */
     output->data_destroy_listener.notify = handle_output_data_destroy;
     interface->add_destroy_listener(data, &output->data_destroy_listener);
@@ -235,6 +243,8 @@ pepper_output_get_compositor(pepper_output_t *output)
 PEPPER_API void
 pepper_output_destroy(pepper_output_t *output)
 {
+    wl_list_remove(&output->link);
+
     if (output->interface && output->data)
         output->interface->destroy(output->data);
 
@@ -292,5 +302,8 @@ pepper_output_get_mode(pepper_output_t *output, int index)
 PEPPER_API pepper_bool_t
 pepper_output_set_mode(pepper_output_t *output, const pepper_output_mode_t *mode)
 {
+    if (output->current_mode == mode)
+        return PEPPER_TRUE;
+
     return output->interface->set_mode(output->data, mode);
 }
index 8504fef..e88cbd6 100644 (file)
@@ -22,6 +22,7 @@ struct pepper_compositor
     struct wl_list      regions;
     struct wl_list      seat_list;
     struct wl_list      layers;
+    struct wl_list      output_list;
 };
 
 struct pepper_output
@@ -30,6 +31,7 @@ struct pepper_output
 
     struct wl_global           *global;
     struct wl_list              resources;
+    struct wl_list              link;
 
     pepper_output_geometry_t    geometry;
     int32_t                     scale;
index a2e4780..8fdd861 100644 (file)
@@ -1,4 +1,5 @@
-TESTPROGRAMS = wayland-backend      \
+TESTPROGRAMS = drm-backend          \
+               wayland-backend      \
                x11-backend          \
                simple-touch         \
                simple-shm
@@ -7,7 +8,9 @@ AM_CFLAGS = $(TEST_PROGRAM_CFLAGS)                      \
             -I$(top_srcdir)/src/                        \
             -I$(top_srcdir)/src/modules/wayland/        \
             -I$(top_srcdir)/src/modules/x11/            \
-            -I$(top_srcdir)/src/modules/drm/
+            -I$(top_srcdir)/src/modules/drm/            \
+            -I$(top_srcdir)/src/modules/libinput/       \
+            -I$(top_srcdir)/src/modules/desktop-shell
 
 LDADD = $(top_builddir)/src/libpepper.la $(TEST_PROGRAM_LIBS)
 
diff --git a/test/drm-backend.c b/test/drm-backend.c
new file mode 100644 (file)
index 0000000..e0b0f62
--- /dev/null
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <pepper.h>
+#include <pepper-drm.h>
+#include <pepper-desktop-shell.h>
+#include <common.h>
+
+static int
+handle_sigint(int signal_number, void *data)
+{
+    struct wl_display *display = (struct wl_display *)data;
+    wl_display_terminate(display);
+
+    return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+    pepper_compositor_t    *compositor;
+    pepper_drm_t           *drm;
+    struct wl_display      *display;
+    struct wl_event_loop   *loop;
+    struct wl_event_source *sigint;
+
+    {   /* for gdb attach */
+        char cc;
+        scanf("%c", &cc);
+    }
+
+    compositor = pepper_compositor_create("wayland-0");
+    PEPPER_ASSERT(compositor);
+
+    drm = pepper_drm_create(compositor, "");
+    PEPPER_ASSERT(drm);
+
+    if (!pepper_desktop_shell_init(compositor))
+        PEPPER_ASSERT(0);
+
+    display = pepper_compositor_get_display(compositor);
+    PEPPER_ASSERT(display);
+
+    loop = wl_display_get_event_loop(display);
+    sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display);
+    PEPPER_ASSERT(sigint);
+
+    wl_display_run(display);
+
+    wl_event_source_remove(sigint);
+    pepper_drm_destroy(drm);
+    pepper_compositor_destroy(compositor);
+
+    return 0;
+}