This patch adds generic frame buffer emulator for any video output device
that uses videobuf2 framework. This emulator assumes that the driver
is capable of working in single-buffering mode and use memory allocator
that allows coherent memory mapping.

Signed-off-by: Marek Szyprowski <m.szyprow...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/media/video/Kconfig        |    7 +
 drivers/media/video/Makefile       |    1 +
 drivers/media/video/videobuf2-fb.c |  565 ++++++++++++++++++++++++++++++++++++
 include/media/videobuf2-fb.h       |   22 ++
 4 files changed, 595 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-fb.c
 create mode 100644 include/media/videobuf2-fb.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 6fa35b8..14ba464 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -50,6 +50,13 @@ config VIDEOBUF2_CORE
 config VIDEOBUF2_MEMOPS
        tristate
 
+config VIDEOBUF2_FB
+       depends on VIDEOBUF2_CORE
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       tristate
+
 config VIDEOBUF2_DMA_CONTIG
        select VIDEOBUF2_CORE
        select VIDEOBUF2_MEMOPS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f1bba24..f9e7616 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -115,6 +115,7 @@ obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 
 obj-$(CONFIG_VIDEOBUF2_CORE)           += videobuf2-core.o
 obj-$(CONFIG_VIDEOBUF2_MEMOPS)         += videobuf2-memops.o
+obj-$(CONFIG_VIDEOBUF2_FB)             += videobuf2-fb.o
 obj-$(CONFIG_VIDEOBUF2_VMALLOC)                += videobuf2-vmalloc.o
 obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG)     += videobuf2-dma-contig.o
 obj-$(CONFIG_VIDEOBUF2_DMA_SG)         += videobuf2-dma-sg.o
diff --git a/drivers/media/video/videobuf2-fb.c 
b/drivers/media/video/videobuf2-fb.c
new file mode 100644
index 0000000..c48e450
--- /dev/null
+++ b/drivers/media/video/videobuf2-fb.c
@@ -0,0 +1,565 @@
+/*
+ * videobuf2-fb.c - FrameBuffer API emulator on top of Videobuf2 framework
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Author: Marek Szyprowski <m.szyprow...@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/fb.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-fb.h>
+
+static int debug = 1;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...)                                    \
+       do {                                                            \
+               if (debug >= level)                                     \
+                       printk(KERN_DEBUG "vb2: " fmt, ## arg);         \
+       } while (0)
+
+struct vb2_fb_data {
+       struct video_device *vfd;
+       struct vb2_queue *q;
+       struct device *dev;
+       struct v4l2_requestbuffers req;
+       struct v4l2_buffer b;
+       struct v4l2_plane p;
+       void *vaddr;
+       unsigned int size;
+       int refcount;
+       int blank;
+       int streaming;
+
+       struct file fake_file;
+       struct dentry fake_dentry;
+       struct inode fake_inode;
+};
+
+static int vb2_fb_stop(struct fb_info *info);
+
+struct fmt_desc {
+       __u32                   fourcc;
+       __u32                   bits_per_pixel;
+       struct fb_bitfield      red;
+       struct fb_bitfield      green;
+       struct fb_bitfield      blue;
+       struct fb_bitfield      transp;
+};
+
+static struct fmt_desc fmt_conv_table[] = {
+       {
+               .fourcc = V4L2_PIX_FMT_RGB565,
+               .bits_per_pixel = 16,
+               .red = {        .offset = 11,   .length = 5,    },
+               .green = {      .offset = 5,    .length = 6,    },
+               .blue = {       .offset = 0,    .length = 5,    },
+       }, {
+               .fourcc = V4L2_PIX_FMT_RGB555,
+               .bits_per_pixel = 16,
+               .red = {        .offset = 11,   .length = 5,    },
+               .green = {      .offset = 5,    .length = 5,    },
+               .blue = {       .offset = 0,    .length = 5,    },
+       }, {
+               .fourcc = V4L2_PIX_FMT_RGB444,
+               .bits_per_pixel = 16,
+               .red = {        .offset = 8,    .length = 4,    },
+               .green = {      .offset = 4,    .length = 4,    },
+               .blue = {       .offset = 0,    .length = 4,    },
+               .transp = {     .offset = 12,   .length = 4,    },
+       }, {
+               .fourcc = V4L2_PIX_FMT_BGR32,
+               .bits_per_pixel = 32,
+               .red = {        .offset = 16,   .length = 4,    },
+               .green = {      .offset = 8,    .length = 8,    },
+               .blue = {       .offset = 0,    .length = 8,    },
+               .transp = {     .offset = 24,   .length = 8,    },
+       },
+       /* TODO: add more format descriptors */
+};
+
+/**
+ * vb2_drv_lock() - a shortcut to call driver specific lock()
+ * @q:         videobuf2 queue
+ */
+static inline void vb2_drv_lock(struct vb2_queue *q)
+{
+       q->ops->wait_finish(q);
+}
+
+/**
+ * vb2_drv_unlock() - a shortcut to call driver specific unlock()
+ * @q:         videobuf2 queue
+ */
+static inline void vb2_drv_unlock(struct vb2_queue *q)
+{
+       q->ops->wait_prepare(q);
+}
+
+/**
+ * vb2_fb_activate() - activate framebuffer emulator
+ * @info:      framebuffer vb2 emulator data
+ * This function activates framebuffer emulator. The pixel format
+ * is acquired from video node, memory is allocated and framebuffer
+ * structures are filled with valid data.
+ */
+static int vb2_fb_activate(struct fb_info *info)
+{
+       struct vb2_fb_data *data = info->par;
+       struct vb2_queue *q = data->q;
+       struct fb_var_screeninfo *var;
+       struct v4l2_format fmt;
+       struct fmt_desc *conv = NULL;
+       int width, height, fourcc, bpl, size;
+       int i, ret = 0;
+       int (*g_fmt)(struct file *file, void *fh, struct v4l2_format *f);
+
+       /*
+        * Check if streaming api has not been already activated.
+        */
+       if (q->streaming || q->num_buffers > 0)
+               return -EBUSY;
+
+       dprintk(3, "setting up framebuffer\n");
+
+       /*
+        * Open video node.
+        */
+       ret = data->vfd->fops->open(&data->fake_file);
+       if (ret)
+               return ret;
+
+       /*
+        * Get format from the video node.
+        */
+       memset(&fmt, 0, sizeof(fmt));
+       fmt.type = q->type;
+       if (data->vfd->ioctl_ops->vidioc_g_fmt_vid_out) {
+               g_fmt = data->vfd->ioctl_ops->vidioc_g_fmt_vid_out;
+               ret = g_fmt(&data->fake_file, data->fake_file.private_data, 
&fmt);
+               if (ret)
+                       goto err;
+               width = fmt.fmt.pix.width;
+               height = fmt.fmt.pix.height;
+               fourcc = fmt.fmt.pix.pixelformat;
+               bpl = fmt.fmt.pix.bytesperline;
+               size = fmt.fmt.pix.sizeimage;
+       } else if (data->vfd->ioctl_ops->vidioc_g_fmt_vid_out_mplane) {
+               g_fmt = data->vfd->ioctl_ops->vidioc_g_fmt_vid_out_mplane;
+               ret = g_fmt(&data->fake_file, data->fake_file.private_data, 
&fmt);
+               if (ret)
+                       goto err;
+               width = fmt.fmt.pix_mp.width;
+               height = fmt.fmt.pix_mp.height;
+               fourcc = fmt.fmt.pix_mp.pixelformat;
+               bpl = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+               size = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+       } else {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       dprintk(3, "fb emu: width %d height %d fourcc %08x size %d bpl %d\n",
+               width, height, fourcc, size, bpl);
+
+       /*
+        * Find format mapping with fourcc returned by g_fmt().
+        */
+       for (i = 0; i < ARRAY_SIZE(fmt_conv_table); i++) {
+               if (fmt_conv_table[i].fourcc == fourcc) {
+                       conv = &fmt_conv_table[i];
+                       break;
+               }
+       }
+
+       if (conv == NULL) {
+               ret = -EBUSY;
+               goto err;
+       }
+
+       /*
+        * Request buffers and use MMAP type to force driver
+        * to allocate buffers by itself.
+        */
+       data->req.count = 1;
+       data->req.memory = V4L2_MEMORY_MMAP;
+       data->req.type = q->type;
+       ret = vb2_reqbufs(q, &data->req);
+       if (ret)
+               goto err;
+
+       /*
+        * Check if plane_count is correct,
+        * multiplane buffers are not supported.
+        */
+       if (q->bufs[0]->num_planes != 1) {
+               data->req.count = 0;
+               ret = -EBUSY;
+               goto err;
+       }
+
+       /*
+        * Get kernel address of the buffer.
+        */
+       data->vaddr = vb2_plane_vaddr(q->bufs[0], 0);
+       if (data->vaddr == NULL) {
+               ret = -EINVAL;
+               goto err;
+       }
+       data->size = size = vb2_plane_size(q->bufs[0], 0);
+
+       /*
+        * Clear the buffer
+        */
+       memset(data->vaddr, 0, size);
+
+       /*
+        * Setup framebuffer parameters
+        */
+       info->screen_base = data->vaddr;
+       info->screen_size = size;
+       info->fix.line_length = bpl;
+       info->fix.smem_len = info->fix.mmio_len = size;
+
+       var = &info->var;
+       var->xres = var->xres_virtual = var->width = width;
+       var->yres = var->yres_virtual = var->height = height;
+       var->bits_per_pixel = conv->bits_per_pixel;
+       var->red = conv->red;
+       var->green = conv->green;
+       var->blue = conv->blue;
+       var->transp = conv->transp;
+
+       return 0;
+
+err:
+       data->vfd->fops->release(&data->fake_file);
+       return ret;
+}
+
+/**
+ * vb2_fb_deactivate() - deactivate framebuffer emulator
+ * @info:      framebuffer vb2 emulator data
+ * Stop displaying video data and close framebuffer emulator.
+ */
+static int vb2_fb_deactivate(struct fb_info *info)
+{
+       struct vb2_fb_data *data = info->par;
+
+       info->screen_base = NULL;
+       info->screen_size = 0;
+       data->blank = 1;
+       data->streaming = 0;
+
+       vb2_fb_stop(info);
+       return data->vfd->fops->release(&data->fake_file);
+}
+
+/**
+ * vb2_fb_start() - start displaying the video buffer
+ * @info:      framebuffer vb2 emulator data
+ * This function queues video buffer to the driver and starts streaming.
+ */
+static int vb2_fb_start(struct fb_info *info)
+{
+       struct vb2_fb_data *data = info->par;
+       struct v4l2_buffer *b = &data->b;
+       struct v4l2_plane *p = &data->p;
+       struct vb2_queue *q = data->q;
+       int ret;
+
+       if (data->streaming)
+               return 0;
+
+       /*
+        * Prepare the buffer and queue it.
+        */
+       memset(b, 0, sizeof(*b));
+       b->type = q->type;
+       b->memory = q->memory;
+       b->index = 0;
+
+       if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               b->bytesused = data->size;
+               b->length = data->size;
+       } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               memset(p, 0, sizeof(*p));
+               b->m.planes = p;
+               b->length = 1;
+               p->bytesused = data->size;
+               p->length = data->size;
+       }
+       ret = vb2_qbuf(q, b);
+       if (ret)
+               return ret;
+
+       /*
+        * Start streaming.
+        */
+       ret = vb2_streamon(q, q->type);
+       if (ret == 0) {
+               data->streaming = 1;
+               dprintk(3, "fb emu: enabled streaming\n");
+       }
+       return ret;
+}
+
+/**
+ * vb2_fb_start() - stop displaying video buffer
+ * @info:      framebuffer vb2 emulator data
+ * This function stops streaming on the video driver.
+ */
+static int vb2_fb_stop(struct fb_info *info)
+{
+       struct vb2_fb_data *data = info->par;
+       struct vb2_queue *q = data->q;
+       int ret = 0;
+
+       if (data->streaming) {
+               ret = vb2_streamoff(q, q->type);
+               data->streaming = 0;
+               dprintk(3, "fb emu: disabled streaming\n");
+       }
+
+       return ret;
+}
+
+/**
+ * vb2_fb_open() - open method for emulated framebuffer
+ * @info:      framebuffer vb2 emulator data
+ * @user:      client type (0 means kernel, 1 mean userspace)
+ */
+static int vb2_fb_open(struct fb_info *info, int user)
+{
+       struct vb2_fb_data *data = info->par;
+       int ret = 0;
+       dprintk(3, "fb emu: open()\n");
+
+       /*
+        * Reject open() call from fb console.
+        */
+       if (user == 0)
+               return -ENODEV;
+
+       vb2_drv_lock(data->q);
+
+       /*
+        * Activate emulation on the first open.
+        */
+       if (data->refcount == 0)
+               ret = vb2_fb_activate(info);
+
+       if (ret == 0)
+               data->refcount++;
+
+       vb2_drv_unlock(data->q);
+
+       return ret;
+}
+
+/**
+ * vb2_fb_release() - release method for emulated framebuffer
+ * @info:      framebuffer vb2 emulator data
+ * @user:      client type (0 means kernel, 1 mean userspace)
+ */
+static int vb2_fb_release(struct fb_info *info, int user)
+{
+       struct vb2_fb_data *data = info->par;
+       int ret = 0;
+
+       dprintk(3, "fb emu: release()\n");
+
+       vb2_drv_lock(data->q);
+
+       if (--data->refcount == 0)
+               ret = vb2_fb_deactivate(info);
+
+       vb2_drv_unlock(data->q);
+
+       return ret;
+}
+
+/**
+ * vb2_fb_mmap() - mmap method for emulated framebuffer
+ * @info:      framebuffer vb2 emulator data
+ * @vma:       memory area to map
+ */
+static int vb2_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+       struct vb2_fb_data *data = info->par;
+       int ret = 0;
+
+       dprintk(3, "fb emu: mmap offset %ld\n", vma->vm_pgoff);
+
+       /*
+        * Add flags required by v4l2/vb2
+        */
+       vma->vm_flags |= VM_SHARED;
+
+       /*
+        * Only the most common case (mapping the whole framebuffer) is
+        * supported for now.
+        */
+       if (vma->vm_pgoff != 0 || (vma->vm_end - vma->vm_start) < data->size)
+               return -EINVAL;
+
+       vb2_drv_lock(data->q);
+       ret = vb2_mmap(data->q, vma);
+       vb2_drv_unlock(data->q);
+
+       return ret;
+}
+
+/**
+ * vb2_fb_blank() - blank method for emulated framebuffer
+ * @blank_mode:        requested blank method
+ * @info:      framebuffer vb2 emulator data
+ */
+static int vb2_fb_blank(int blank_mode, struct fb_info *info)
+{
+       struct vb2_fb_data *data = info->par;
+       int ret = -EBUSY;
+
+       dprintk(3, "fb emu: blank mode %d, blank %d, streaming %d\n",
+               blank_mode, data->blank, data->streaming);
+
+       /*
+        * If no blank mode change then return immediately
+        */
+       if ((data->blank && blank_mode != FB_BLANK_UNBLANK) ||
+           (!data->blank && blank_mode == FB_BLANK_UNBLANK))
+               return 0;
+
+       /*
+        * Currently blank works only if device has been opened first.
+        */
+       if (!data->refcount)
+               return -EBUSY;
+
+       vb2_drv_lock(data->q);
+
+       /*
+        * Start emulation if user requested mode == FB_BLANK_UNBLANK.
+        */
+       if (blank_mode == FB_BLANK_UNBLANK && data->blank) {
+               ret = vb2_fb_start(info);
+               if (ret == 0)
+                       data->blank = 0;
+       }
+
+       /*
+        * Stop emulation if user requested mode != FB_BLANK_UNBLANK.
+        */
+       if (blank_mode != FB_BLANK_UNBLANK && !data->blank) {
+               ret = vb2_fb_stop(info);
+               if (ret == 0)
+                       data->blank = 1;
+       }
+
+       vb2_drv_unlock(data->q);
+
+       return ret;
+}
+
+static struct fb_ops vb2_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_open        = vb2_fb_open,
+       .fb_release     = vb2_fb_release,
+       .fb_mmap        = vb2_fb_mmap,
+       .fb_blank       = vb2_fb_blank,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+};
+
+/**
+ * vb2_fb_reqister() - register framebuffer emulation
+ * @q:         videobuf2 queue
+ * @vfd:       video node
+ * This function registers framebuffer emulation for specified
+ * videobuf2 queue and video node. It returns a pointer to the registered
+ * framebuffer device.
+ */
+void *vb2_fb_register(struct vb2_queue *q, struct video_device *vfd)
+{
+       struct vb2_fb_data *data;
+       struct fb_info *info;
+       int ret;
+
+       BUG_ON(q->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+            q->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+       BUG_ON(!q->mem_ops->vaddr);
+       BUG_ON(!q->ops->wait_prepare || !q->ops->wait_finish);
+       BUG_ON(!vfd->ioctl_ops || !vfd->fops);
+
+       if (!try_module_get(vfd->fops->owner))
+               return ERR_PTR(-ENODEV);
+
+       info = framebuffer_alloc(sizeof(struct vb2_fb_data), &vfd->dev);
+       if (!info)
+               return ERR_PTR(-ENOMEM);
+
+       data = info->par;
+
+       info->fix.type  = FB_TYPE_PACKED_PIXELS;
+       info->fix.accel = FB_ACCEL_NONE;
+       info->fix.visual = FB_VISUAL_TRUECOLOR,
+       info->var.activate = FB_ACTIVATE_NOW;
+       info->var.vmode = FB_VMODE_NONINTERLACED;
+       info->fbops = &vb2_fb_ops;
+       info->flags = FBINFO_FLAG_DEFAULT;
+       info->screen_base = NULL;
+
+       ret = register_framebuffer(info);
+       if (ret)
+               return ERR_PTR(ret);
+
+       printk(KERN_INFO "fb%d: registered frame buffer emulation for 
/dev/%s\n",
+              info->node, dev_name(&vfd->dev));
+
+       data->blank = 1;
+       data->vfd = vfd;
+       data->q = q;
+       data->fake_file.f_path.dentry = &data->fake_dentry;
+       data->fake_dentry.d_inode = &data->fake_inode;
+       data->fake_inode.i_rdev = vfd->cdev->dev;
+
+       return info;
+}
+EXPORT_SYMBOL_GPL(vb2_fb_register);
+
+/**
+ * vb2_fb_unreqister() - unregister framebuffer emulation
+ * @fb_emu:    emulated framebuffer device
+ */
+int vb2_fb_unregister(void *fb_emu)
+{
+       struct fb_info *info = fb_emu;
+       struct vb2_fb_data *data = info->par;
+       struct module *owner = data->vfd->fops->owner;
+
+       unregister_framebuffer(info);
+       module_put(owner);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_fb_unregister);
+
+MODULE_DESCRIPTION("FrameBuffer emulator for Videobuf2 and Video for Linux 2");
+MODULE_AUTHOR("Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-fb.h b/include/media/videobuf2-fb.h
new file mode 100644
index 0000000..fea16b6
--- /dev/null
+++ b/include/media/videobuf2-fb.h
@@ -0,0 +1,22 @@
+/*
+ * videobuf2-fb.h - FrameBuffer API emulator on top of Videobuf2 framework
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Author: Marek Szyprowski <m.szyprow...@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_FB_H
+#define _MEDIA_VIDEOBUF2_FB_H
+
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+void *vb2_fb_register(struct vb2_queue *q, struct video_device *vfd);
+int vb2_fb_unregister(void *fb_emu);
+
+#endif
-- 
1.7.1.569.g6f426
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to