uterm: add fbdev video backend
authorDavid Herrmann <dh.herrmann@googlemail.com>
Sat, 23 Jun 2012 16:24:44 +0000 (18:24 +0200)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Sat, 23 Jun 2012 16:24:44 +0000 (18:24 +0200)
This adds a fully working fbdev backend to the uterm library. This allows
us to create our rendering pipeline on any linux machine.

The fbdev backend is not yet hooked up into kmscon. There are still some
remaining issues as we do not have OpenGL on fbdev if EGL is not compiled
with fbdev backend (which is usually not).

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Makefile.am
src/uterm_internal.h
src/uterm_video_fbdev.c

index c4b266d..3d49dce 100644 (file)
@@ -55,6 +55,7 @@ libkmscon_core_la_SOURCES = \
        src/uterm.h src/uterm_internal.h \
        src/uterm_video.c \
        src/uterm_video_drm.c \
+       src/uterm_video_fbdev.c \
        src/uterm_monitor.c \
        src/uterm_input.c \
        src/uterm_vt.c \
index 045b97d..88627a3 100644 (file)
@@ -138,6 +138,7 @@ static const struct video_ops drm_video_ops;
 
 /* fbdev */
 
+#define UTERM_HAVE_FBDEV /* TODO: move to autoconf */
 #ifdef UTERM_HAVE_FBDEV
 
 #include <linux/fb.h>
@@ -147,17 +148,19 @@ struct fbdev_mode {
 };
 
 struct fbdev_display {
-       int id;
        char *node;
        int fd;
 
-       size_t len;
-       void *map;
-       unsigned int stride;
-
        struct fb_fix_screeninfo finfo;
        struct fb_var_screeninfo vinfo;
        unsigned int rate;
+
+       unsigned int bufid;
+       size_t xres;
+       size_t yres;
+       size_t len;
+       void *map;
+       unsigned int stride;
 };
 
 struct fbdev_video {
index 59c48d1..4321d1a 100644 (file)
@@ -42,6 +42,8 @@
 #include "uterm.h"
 #include "uterm_internal.h"
 
+#define LOG_SUBSYSTEM "video_fbdev"
+
 static const char *mode_get_name(const struct uterm_mode *mode)
 {
        return NULL;
@@ -49,104 +51,176 @@ static const char *mode_get_name(const struct uterm_mode *mode)
 
 static unsigned int mode_get_width(const struct uterm_mode *mode)
 {
-       return mode->fbdev.unused;
+       return 0;
 }
 
 static unsigned int mode_get_height(const struct uterm_mode *mode)
 {
-       return mode->fbdev.unused;
+       return 0;
 }
 
-static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
+static int refresh_info(struct uterm_display *disp)
 {
-       struct fb_var_screeninfo *info;
        int ret;
+
+       ret = ioctl(disp->fbdev.fd, FBIOGET_FSCREENINFO, &disp->fbdev.finfo);
+       if (ret) {
+               log_err("cannot get finfo (%d): %m", errno);
+               return -EFAULT;
+       }
+
+       ret = ioctl(disp->fbdev.fd, FBIOGET_VSCREENINFO, &disp->fbdev.vinfo);
+       if (ret) {
+               log_err("cannot get vinfo (%d): %m", errno);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int display_activate_force(struct uterm_display *disp,
+                                 struct uterm_mode *mode,
+                                 bool force)
+{
+       static const char depths[] = { 32, 24, 16, 0 };
+       struct fb_var_screeninfo *vinfo;
+       struct fb_fix_screeninfo *finfo;
+       int ret, i;
        uint64_t quot;
        size_t len;
 
-       if (!disp->video || !(disp->flags & DISPLAY_OPEN))
+       if (!disp->video || !video_is_awake(disp->video))
+               return -EINVAL;
+       if (!force && (disp->flags & DISPLAY_ONLINE))
+               return 0;
+
+       /* TODO: We do not support explicit modesetting in fbdev, so we require
+        * @mode to be NULL. You can still switch modes via "fbset" on the
+        * console and then restart the app. It will automatically adapt to the
+        * new mode. The only values changed here are bpp and color mode. */
+       if (mode)
                return -EINVAL;
 
-       /* TODO: we currently use the current mode of the framebuffer and do not
-        * allow changing this mode. However, we should rather list valid modes
-        * when opening the device and set the framebuffer to the requested mode
-        * here first.
-        * For now you can use the fbset(1) program to modify
-        * frambuffer-resolutions and timers.
-        *
-        * info->bits_per_pixel = 32 is almost a prerequisite here! We also need
-        * to check for TRUECOLOR. Everything else is just old-school.
-        */
-
-       info = &disp->fbdev.vinfo;
-       info->xoffset = 0;
-       info->yoffset = 0;
-       info->activate = FB_ACTIVATE_NOW;
-       info->xres_virtual = info->xres;
-       info->yres_virtual = info->yres * 2;
-
-       log_info("video_fbdev: activating display %p to %ux%u", disp,
-                       info->xres, info->yres);
-
-       ret = ioctl(disp->fbdev.fd, FBIOPUT_VSCREENINFO, info);
+       ret = refresh_info(disp);
+       if (ret)
+               return ret;
+
+       finfo = &disp->fbdev.finfo;
+       vinfo = &disp->fbdev.vinfo;
+
+       /* We require TRUECOLOR mode here. That is, each pixel has a color value
+        * that is split into rgba values that we can set directly. Other visual
+        * modes like pseudocolor or direct-color do not provide this. As I have
+        * never seen a device that does not support TRUECOLOR, I think we can
+        * ignore them here. */
+       if (finfo->visual != FB_VISUAL_TRUECOLOR) {
+               for (i = 0; depths[i]; ++i) {
+                       vinfo->bits_per_pixel = depths[i];
+                       vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+
+                       ret = ioctl(disp->fbdev.fd, FBIOPUT_VSCREENINFO,
+                                   vinfo);
+                       if (ret < 0)
+                               continue;
+
+                       ret = refresh_info(disp);
+                       if (ret)
+                               return ret;
+
+                       if (finfo->visual == FB_VISUAL_TRUECOLOR)
+                               break;
+               }
+       }
+
+       vinfo->xoffset = 0;
+       vinfo->yoffset = 0;
+       vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+       vinfo->xres_virtual = vinfo->xres;
+       vinfo->yres_virtual = vinfo->yres * 2;
+
+       log_info("activating display %s to %ux%u", disp->fbdev.node,
+                vinfo->xres, vinfo->yres);
+
+       ret = ioctl(disp->fbdev.fd, FBIOPUT_VSCREENINFO, vinfo);
        if (ret) {
                log_err("video_fbdev: cannot set vinfo (%d): %m", errno);
                return -EFAULT;
        }
 
-       /* vinfo/finfo may have changed so refetch them */
+       ret = refresh_info(disp);
+       if (ret)
+               return ret;
 
-       ret = ioctl(disp->fbdev.fd, FBIOGET_VSCREENINFO, &info);
-       if (ret) {
-               log_err("video_fbdev: cannot get vinfo (%d): %m", errno);
+       if (finfo->visual != FB_VISUAL_TRUECOLOR ||
+           vinfo->bits_per_pixel < 16) {
+               log_error("device %s does not support true-color bpp >= 16",
+                         disp->fbdev.node);
                return -EFAULT;
        }
 
-       ret = ioctl(disp->fbdev.fd, FBIOGET_FSCREENINFO, &disp->fbdev.finfo);
-       if (ret) {
-               log_err("video_fbdev: cannot get finfo (%d): %m", errno);
+       if (vinfo->yres_virtual < vinfo->yres * 2 ||
+           vinfo->xres_virtual < vinfo->xres) {
+               log_error("device %s does not double-buffering",
+                         disp->fbdev.node);
                return -EFAULT;
        }
 
-       quot = (info->upper_margin + info->lower_margin + info->yres);
-       quot *= (info->left_margin + info->right_margin + info->xres);
-       quot *= info->pixclock;
-       disp->fbdev.rate = quot ? (1000000000000000LLU / quot) : 0;
-       if (!disp->fbdev.rate)
-               disp->fbdev.rate = 60 * 1000; /* 60 Hz by default */
-
-       len = disp->fbdev.finfo.line_length * disp->fbdev.vinfo.yres_virtual;
-       disp->fbdev.map = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED,
-                               disp->fbdev.fd, 0);
+       /* calculate monitor rate, default is 60 Hz */
+       quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
+       quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
+       quot *= vinfo->pixclock;
+       if (quot)
+               disp->fbdev.rate = 1000000000000000LLU / quot;
+       else
+               disp->fbdev.rate = 60 * 1000;
+
+       len = finfo->line_length * (vinfo->yres * 2);
+       disp->fbdev.map = mmap(0, len, PROT_WRITE, MAP_SHARED,
+                              disp->fbdev.fd, 0);
        if (disp->fbdev.map == MAP_FAILED) {
-               log_err("video_fbdev: cannot mmap framebuffer (%d): %m", errno);
+               log_error("cannot mmap device %s (%d): %m", disp->fbdev.node,
+                         errno);
                return -EFAULT;
        }
+
        memset(disp->fbdev.map, 0, len);
+       disp->fbdev.xres = vinfo->xres;
+       disp->fbdev.yres = vinfo->yres;
        disp->fbdev.len = len;
+       disp->fbdev.bufid = 0;
 
-       disp->current_mode = mode;
        disp->flags |= DISPLAY_ONLINE;
 
        return 0;
 }
 
-static void display_deactivate(struct uterm_display *disp)
+static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
+{
+       return display_activate_force(disp, mode, false);
+}
+
+static void display_deactivate_force(struct uterm_display *disp, bool force)
 {
        if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
                return;
 
-       log_info("video_fbdev: deactivating display %p", disp);
-       disp->current_mode = NULL;
-       disp->flags &= ~DISPLAY_ONLINE;
+       log_info("deactivating device %s", disp->fbdev.node);
        munmap(disp->fbdev.map, disp->fbdev.len);
+
+       if (!force)
+               disp->flags &= ~DISPLAY_ONLINE;
+}
+
+static void display_deactivate(struct uterm_display *disp)
+{
+       return display_deactivate_force(disp, false);
 }
 
 static int display_set_dpms(struct uterm_display *disp, int state)
 {
        int set, ret;
 
-       if (!disp->video || !(disp->flags & DISPLAY_OPEN))
+       if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
                return -EINVAL;
 
        switch (state) {
@@ -166,13 +240,13 @@ static int display_set_dpms(struct uterm_display *disp, int state)
                return -EINVAL;
        }
 
-       log_info("video_fbdev: setting DPMS of display %p to %s", disp,
-                       uterm_dpms_to_name(state));
+       log_info("setting DPMS of device %p to %s", disp->fbdev.node,
+                uterm_dpms_to_name(state));
 
        ret = ioctl(disp->fbdev.fd, FBIOBLANK, set);
        if (ret) {
-               log_err("video_fbdev: cannot set DPMS on %p (%d): %m", disp,
-                               errno);
+               log_error("cannot set DPMS on %s (%d): %m", disp->fbdev.node,
+                         errno);
                return -EFAULT;
        }
 
@@ -182,24 +256,23 @@ static int display_set_dpms(struct uterm_display *disp, int state)
 
 void *fbdev_display_map(struct uterm_display *disp)
 {
-       if (!disp->video || !(disp->flags & DISPLAY_OPEN))
+       if (!disp->video || !video_is_awake(disp->video))
                return NULL;
        if (!(disp->flags & DISPLAY_ONLINE))
                return NULL;
 
        /* TODO: temporary function to obtain a pointer to the frambuffer from
         * the calling application. Stuff like bpp, size, etc. must be
-        * published, too, otherwise, no-one will be able to use it.
-        */
+        * published, too, otherwise, no-one will be able to use it. */
 
        return disp->fbdev.map;
 }
 
 static int display_use(struct uterm_display *disp)
 {
-       if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
+       if (!disp->video || !video_is_awake(disp->video))
                return -EINVAL;
-       if (!(disp->flags & DISPLAY_OPEN))
+       if (!(disp->flags & DISPLAY_ONLINE))
                return -EINVAL;
 
        return 0;
@@ -207,353 +280,97 @@ static int display_use(struct uterm_display *disp)
 
 static int display_swap(struct uterm_display *disp)
 {
-       if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
-               return -EINVAL;
-       if (!(disp->flags & DISPLAY_OPEN) || (disp->dpms != UTERM_DPMS_ON))
-               return -EINVAL;
-
-       /* TODO: swap */
-/*
-       vinfo.activate = FB_ACTIVATE_VBL;
-       vinfo.yoffset = 0 or vinfo.vres;
-       ioctl(fd, FBIOPUT_VSCREENINFO, &info)
-*/
-
-       return 0;
-}
-
-static int get_id(struct udev_device *dev)
-{
-       const char *name;
-       char *end;
-       int devnum;
-
-       name = udev_device_get_sysname(dev);
-       if (!name)
-               return -ENODEV;
-       if (strncmp(name, "fb", 2) || !name[2])
-               return -ENODEV;
-
-       devnum = strtol(&name[2], &end, 10);
-       if (devnum < 0 || *end)
-               return -ENODEV;
-
-       return devnum;
-}
-
-static int open_display(struct uterm_display *disp)
-{
+       struct fb_var_screeninfo *vinfo;
        int ret;
 
-       if (!(disp->video->flags & VIDEO_AWAKE))
+       if (!disp->video || !video_is_awake(disp->video))
+               return -EINVAL;
+       if (!(disp->flags & DISPLAY_ONLINE))
                return -EINVAL;
-       if (disp->flags & DISPLAY_OPEN)
-               return 0;
 
-       disp->fbdev.fd = open(disp->fbdev.node, O_RDWR | O_CLOEXEC);
-       if (disp->fbdev.fd < 0) {
-               log_err("video_fbdev: cannot open %s (%d): %m",
-                               disp->fbdev.node, errno);
-               return -EFAULT;
-       }
+       vinfo = &disp->fbdev.vinfo;
+       vinfo->activate = FB_ACTIVATE_VBL;
 
-       ret = ioctl(disp->fbdev.fd, FBIOGET_FSCREENINFO, &disp->fbdev.finfo);
-       if (ret) {
-               log_err("video_fbdev: cannot get finfo (%d): %m", errno);
-               ret = -EFAULT;
-               goto err_fd;
-       }
+       if (!disp->fbdev.bufid)
+               vinfo->yoffset = disp->fbdev.yres;
+       else
+               vinfo->yoffset = 0;
 
-       ret = ioctl(disp->fbdev.fd, FBIOGET_VSCREENINFO, &disp->fbdev.vinfo);
+       ret = ioctl(disp->fbdev.fd, FBIOPUT_VSCREENINFO, vinfo);
        if (ret) {
-               log_err("video_fbdev: cannot get vinfo (%d): %m", errno);
-               ret = -EFAULT;
-               goto err_fd;
-       }
-
-       disp->flags |= DISPLAY_OPEN;
-
-       if (disp->flags & DISPLAY_ONLINE) {
-               ret = display_activate(disp, NULL);
-               if (ret)
-                       goto err_open;
+               log_warning("cannot swap buffers on %s (%d): %m",
+                           disp->fbdev.node, errno);
+               return -EFAULT;
        }
 
+       disp->fbdev.bufid ^= 1;
        return 0;
-
-err_open:
-       disp->flags &= ~DISPLAY_OPEN;
-err_fd:
-       close(disp->fbdev.fd);
-       return ret;
 }
 
-static void close_display(struct uterm_display *disp)
-{
-       if (!(disp->flags & DISPLAY_OPEN))
-               return;
-
-       close(disp->fbdev.fd);
-       disp->flags &= ~DISPLAY_OPEN;
-       if (disp->fbdev.map)
-               munmap(disp->fbdev.map, disp->fbdev.len);
-}
-
-static int init_display(struct uterm_video *video, struct udev_device *dev)
+static int video_init(struct uterm_video *video, const char *node)
 {
+       int ret;
        struct uterm_display *disp;
-       int ret, id;
-       const char *node;
-
-       id = get_id(dev);
-       if (id < 0)
-               return id;
 
        ret = display_new(&disp, &fbdev_display_ops);
        if (ret)
                return ret;
 
-       log_info("video_fbdev: probing %s", udev_device_get_sysname(dev));
-
-       node = udev_device_get_devnode(dev);
-       if (!node) {
-               log_err("video_fbdev: cannot get device node");
-               return -ENODEV;
-       }
-       disp->fbdev.node = mem_strdup(node);
+       disp->fbdev.node = strdup(node);
        if (!disp->fbdev.node) {
+               log_err("cannot dup node name");
                ret = -ENOMEM;
                goto err_free;
        }
 
-       disp->video = video;
-       disp->fbdev.id = id;
-       disp->dpms = UTERM_DPMS_UNKNOWN;
-
-       if (video->flags & VIDEO_AWAKE) {
-               ret = open_display(disp);
-               if (ret)
-                       goto err_str;
+       disp->fbdev.fd = open(node, O_RDWR | O_CLOEXEC);
+       if (disp->fbdev.fd < 0) {
+               log_err("cannot open %s (%d): %m", node, errno);
+               ret = -EFAULT;
+               goto err_node;
        }
 
-       disp->next = video->displays;
+       disp->video = video;
+       disp->dpms = UTERM_DPMS_UNKNOWN;
        video->displays = disp;
+
+       log_info("new device on %s", disp->fbdev.node);
        return 0;
 
-err_str:
-       disp->video = NULL;
-       mem_free(disp->fbdev.node);
+err_node:
+       free(disp->fbdev.node);
 err_free:
        uterm_display_unref(disp);
        return ret;
 }
 
-static void destroy_display(struct uterm_display *disp)
-{
-       display_deactivate(disp);
-       close_display(disp);
-       mem_free(disp->fbdev.node);
-       disp->video = NULL;
-       uterm_display_unref(disp);
-}
-
-static int video_init(struct uterm_video *video)
-{
-       struct udev_enumerate *e;
-       struct udev_list_entry *name;
-       const char *path;
-       struct udev_device *dev;
-       int ret;
-
-       ret = udev_monitor_filter_add_match_subsystem_devtype(video->umon,
-                                                       "graphics", NULL);
-       if (ret) {
-               log_err("video_fbdev: cannot add udev filter (%d): %m", ret);
-               return -EFAULT;
-       }
-
-       ret = udev_monitor_enable_receiving(video->umon);
-       if (ret) {
-               log_err("video_fbdev: cannot start udev_monitor (%d): %m", ret);
-               return -EFAULT;
-       }
-
-       e = udev_enumerate_new(video->udev);
-       if (!e) {
-               log_err("video_fbdev: cannot create udev_enumerate object");
-               return -EFAULT;
-       }
-
-       ret = udev_enumerate_add_match_subsystem(e, "graphics");
-       if (ret) {
-               log_err("video_fbdev: cannot add udev match (%d): %m", ret);
-               ret = -EFAULT;
-               goto err_enum;
-       }
-       ret = udev_enumerate_add_match_sysname(e, "fb[0-9]*");
-       if (ret) {
-               log_err("video_fbdev: cannot add udev match (%d): %m", ret);
-               ret = -EFAULT;
-               goto err_enum;
-       }
-       ret = udev_enumerate_scan_devices(e);
-       if (ret) {
-               log_err("video_fbdev: cannot scan udev devices (%d): %m", ret);
-               ret = -EFAULT;
-               goto err_enum;
-       }
-
-       udev_list_entry_foreach(name, udev_enumerate_get_list_entry(e)) {
-               path = udev_list_entry_get_name(name);
-               if (!path || !*path)
-                       continue;
-               dev = udev_device_new_from_syspath(video->udev, path);
-               if (!dev)
-                       continue;
-
-               init_display(video, dev);
-               udev_device_unref(dev);
-       }
-
-       log_info("video_fbdev: new fbdev device");
-       return 0;
-
-err_enum:
-       udev_enumerate_unref(e);
-       return ret;
-}
-
 static void video_destroy(struct uterm_video *video)
 {
-       struct uterm_display *disp;
-
-       log_info("video_fbdev: free fbdev device");
-
-       while ((disp = video->displays)) {
-               video->displays = disp->next;
-               disp->next = NULL;
-               destroy_display(disp);
-       }
-}
-
-static int video_use(struct uterm_video *video)
-{
-       return 0;
-}
-
-static int hotplug(struct uterm_video *video)
-{
-       struct udev_device *dev;
-       const char *action;
-       unsigned int id;
-       struct uterm_display *disp, *tmp;
-
-       dev = udev_monitor_receive_device(video->umon);
-       if (!dev) {
-               log_warn("video_fbdev: cannot receive device from udev_monitor");
-               return 0;
-       }
-
-       action = udev_device_get_action(dev);
-       if (!action)
-               goto ignore;
-       if (!strcmp(action, "add")) {
-               init_display(video, dev);
-       } else if (!strcmp(action, "remove")) {
-               if (!video->displays)
-                       goto ignore;
-               id = get_id(dev);
-               if (id < 0)
-                       goto ignore;
-
-               disp = NULL;
-               if (video->displays->fbdev.id == id) {
-                       disp = video->displays;
-                       video->displays = disp->next;
-               } else for (tmp = video->displays; tmp->next; tmp = tmp->next) {
-                       if (tmp->next->fbdev.id == id) {
-                               disp = tmp->next;
-                               tmp->next = tmp->next->next;
-                               break;
-                       }
-               }
-               if (disp) {
-                       disp->next = NULL;
-                       destroy_display(disp);
-               }
-       }
-
-ignore:
-       udev_device_unref(dev);
-       return 0;
-}
-
-static int video_poll(struct uterm_video *video, unsigned int num)
-{
-       unsigned int i;
-       struct epoll_event *ev;
-       int ret;
-
-       for (i = 0; i < num; ++i) {
-               ev = &video->efd_evs[i];
-               if (ev->data.fd == video->umon_fd) {
-                       if (ev->events & (EPOLLERR | EPOLLHUP)) {
-                               log_err("video_fbdev: udev_monitor closed unexpectedly");
-                               return -EFAULT;
-                       }
-                       if (ev->events & (EPOLLIN)) {
-                               ret = hotplug(video);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-       }
-
-       return 0;
+       log_info("free device %p", video);
 }
 
 static void video_sleep(struct uterm_video *video)
 {
-       struct uterm_display *disp;
-
        if (!(video->flags & VIDEO_AWAKE))
                return;
 
-       for (disp = video->displays; disp; disp = disp->next)
-               close_display(disp);
-
+       display_deactivate_force(video->displays, true);
        video->flags &= ~VIDEO_AWAKE;
 }
 
 static int video_wake_up(struct uterm_video *video)
 {
-       struct uterm_display *disp, *tmp;
        int ret;
 
        if (video->flags & VIDEO_AWAKE)
                return 0;
 
        video->flags |= VIDEO_AWAKE;
-
-       while (video->displays) {
-               tmp = video->displays;
-               ret = open_display(tmp);
-               if (!ret)
-                       break;
-
-               video->displays = tmp->next;
-               tmp->next = NULL;
-               destroy_display(tmp);
-       }
-       for (disp = video->displays; disp && disp->next; ) {
-               tmp = disp->next;
-               ret = open_display(tmp);
-               if (!ret) {
-                       disp = tmp;
-               } else {
-                       disp->next = tmp->next;
-                       tmp->next = NULL;
-                       destroy_display(tmp);
+       if (video->displays->flags & DISPLAY_ONLINE) {
+               ret = display_activate_force(video->displays, NULL, true);
+               if (ret) {
+                       video->flags &= ~VIDEO_AWAKE;
+                       return ret;
                }
        }
 
@@ -582,8 +399,8 @@ const struct video_ops fbdev_video_ops = {
        .init = video_init,
        .destroy = video_destroy,
        .segfault = NULL, /* TODO */
-       .use = video_use,
-       .poll = video_poll,
+       .use = NULL,
+       .poll = NULL,
        .sleep = video_sleep,
        .wake_up = video_wake_up,
 };