@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,565 @@
+/*
+ * videobuf2-fb.c - FrameBuffer API emulator on top of Videobuf2 framework
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Author: Marek Szyprowski <m.szyprowski@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");
new file mode 100644
@@ -0,0 +1,22 @@
+/*
+ * videobuf2-fb.h - FrameBuffer API emulator on top of Videobuf2 framework
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Author: Marek Szyprowski <m.szyprowski@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