diff mbox

[RFC,5/8] s5p-fimc: Add ISP video capture driver stubs

Message ID 1363031092-29950-6-git-send-email-s.nawrocki@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

This patch adds a video capture node for the FIMC-IS ISP IP block
and Makefile/Kconfig to actually enable the driver's compilation.

The ISP video capture driver is still a work in progress.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/platform/s5p-fimc/Kconfig          |   13 +
 drivers/media/platform/s5p-fimc/Makefile         |    4 +
 drivers/media/platform/s5p-fimc/fimc-isp-video.c |  414 ++++++++++++++++++++++
 drivers/media/platform/s5p-fimc/fimc-isp-video.h |   50 +++
 4 files changed, 481 insertions(+)
 create mode 100644 drivers/media/platform/s5p-fimc/fimc-isp-video.c
 create mode 100644 drivers/media/platform/s5p-fimc/fimc-isp-video.h

Comments

Hans Verkuil March 12, 2013, 2:44 p.m. UTC | #1
On Mon 11 March 2013 20:44:49 Sylwester Nawrocki wrote:
> This patch adds a video capture node for the FIMC-IS ISP IP block
> and Makefile/Kconfig to actually enable the driver's compilation.
> 
> The ISP video capture driver is still a work in progress.
> 
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/platform/s5p-fimc/Kconfig          |   13 +
>  drivers/media/platform/s5p-fimc/Makefile         |    4 +
>  drivers/media/platform/s5p-fimc/fimc-isp-video.c |  414 ++++++++++++++++++++++
>  drivers/media/platform/s5p-fimc/fimc-isp-video.h |   50 +++
>  4 files changed, 481 insertions(+)
>  create mode 100644 drivers/media/platform/s5p-fimc/fimc-isp-video.c
>  create mode 100644 drivers/media/platform/s5p-fimc/fimc-isp-video.h
> 
> diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/s5p-fimc/Kconfig
> index c16b20d..1253e25 100644
> --- a/drivers/media/platform/s5p-fimc/Kconfig
> +++ b/drivers/media/platform/s5p-fimc/Kconfig
> @@ -46,4 +46,17 @@ config VIDEO_EXYNOS_FIMC_LITE
>  	  module will be called exynos-fimc-lite.
>  endif
>  
> +if (SOC_EXYNOS4212 || SOC_EXYNOS4412) && OF && !ARCH_MULTIPLATFORM
> +
> +config VIDEO_EXYNOS4_FIMC_IS
> +	tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
> +	select VIDEOBUF2_DMA_CONTIG
> +	help
> +	  This is a V4L2 driver for Samsung EXYNOS4x12 SoC FIMC-IS
> +	  (Imaging Subsystem).
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called exynos-fimc-is.
> +endif
> +
>  endif # VIDEO_SAMSUNG_S5P_FIMC
> diff --git a/drivers/media/platform/s5p-fimc/Makefile b/drivers/media/platform/s5p-fimc/Makefile
> index 4648514..55b171a 100644
> --- a/drivers/media/platform/s5p-fimc/Makefile
> +++ b/drivers/media/platform/s5p-fimc/Makefile
> @@ -1,7 +1,11 @@
>  s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o
>  exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
> +exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
> +exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
> +exynos-fimc-is-objs += fimc-isp-video.o
>  s5p-csis-objs := mipi-csis.o
>  
>  obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS)	+= s5p-csis.o
>  obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)	+= exynos-fimc-lite.o
> +obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)	+= exynos-fimc-is.o
>  obj-$(CONFIG_VIDEO_S5P_FIMC)		+= s5p-fimc.o
> diff --git a/drivers/media/platform/s5p-fimc/fimc-isp-video.c b/drivers/media/platform/s5p-fimc/fimc-isp-video.c
> new file mode 100644
> index 0000000..bdeacaa
> --- /dev/null
> +++ b/drivers/media/platform/s5p-fimc/fimc-isp-video.c
> @@ -0,0 +1,414 @@
> +/*
> + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/printk.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "fimc-mdevice.h"
> +#include "fimc-core.h"
> +#include "fimc-is.h"
> +
> +static int isp_video_capture_start_streaming(struct vb2_queue *q,
> +					unsigned int count)
> +{
> +	/* TODO: start ISP output DMA */
> +	return 0;
> +}
> +
> +static int isp_video_capture_stop_streaming(struct vb2_queue *q)
> +{
> +	/* TODO: stop ISP output DMA */
> +	return 0;
> +}
> +
> +static int isp_video_capture_queue_setup(struct vb2_queue *vq,
> +			const struct v4l2_format *pfmt,
> +			unsigned int *num_buffers, unsigned int *num_planes,
> +			unsigned int sizes[], void *allocators[])
> +{
> +	const struct v4l2_pix_format_mplane *pixm = NULL;
> +	struct fimc_isp *isp = vq->drv_priv;
> +	struct fimc_isp_frame *frame = &isp->out_frame;
> +	const struct fimc_fmt *fmt = isp->video_capture_format;
> +	unsigned long wh;
> +	int i;
> +
> +	if (pfmt) {
> +		pixm = &pfmt->fmt.pix_mp;
> +		fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
> +		wh = pixm->width * pixm->height;
> +	} else {
> +		wh = frame->f_width * frame->f_height;
> +	}
> +
> +	if (fmt == NULL)
> +		return -EINVAL;
> +
> +	*num_planes = fmt->memplanes;
> +
> +	for (i = 0; i < fmt->memplanes; i++) {
> +		unsigned int size = (wh * fmt->depth[i]) / 8;
> +		if (pixm)
> +			sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
> +		else
> +			sizes[i] = size;
> +		allocators[i] = isp->alloc_ctx;
> +	}
> +
> +	return 0;
> +}
> +
> +static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct fimc_isp *isp = vq->drv_priv;
> +	int i;
> +
> +	if (isp->video_capture_format == NULL)
> +		return -EINVAL;
> +
> +	for (i = 0; i < isp->video_capture_format->memplanes; i++) {
> +		unsigned long size = isp->payload[i];
> +
> +		if (vb2_plane_size(vb, i) < size) {
> +			v4l2_err(&isp->vfd,
> +				 "User buffer too small (%ld < %ld)\n",
> +				 vb2_plane_size(vb, i), size);
> +			return -EINVAL;
> +		}
> +		vb2_set_plane_payload(vb, i, size);
> +	}
> +
> +	return 0;
> +}
> +
> +static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
> +{
> +	/* TODO: */
> +}
> +
> +static void isp_video_lock(struct vb2_queue *vq)
> +{
> +	struct fimc_isp *isp = vb2_get_drv_priv(vq);
> +	mutex_lock(&isp->video_lock);
> +}
> +
> +static void isp_video_unlock(struct vb2_queue *vq)
> +{
> +	struct fimc_isp *isp = vb2_get_drv_priv(vq);
> +	mutex_unlock(&isp->video_lock);
> +}
> +
> +static const struct vb2_ops isp_video_capture_qops = {
> +	.queue_setup	 = isp_video_capture_queue_setup,
> +	.buf_prepare	 = isp_video_capture_buffer_prepare,
> +	.buf_queue	 = isp_video_capture_buffer_queue,
> +	.wait_prepare	 = isp_video_unlock,
> +	.wait_finish	 = isp_video_lock,
> +	.start_streaming = isp_video_capture_start_streaming,
> +	.stop_streaming	 = isp_video_capture_stop_streaming,
> +};
> +
> +static int isp_video_capture_open(struct file *file)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	int ret = 0;
> +
> +	if (mutex_lock_interruptible(&isp->video_lock))
> +		return -ERESTARTSYS;
> +
> +	/* ret = pm_runtime_get_sync(&isp->pdev->dev); */
> +	if (ret < 0)
> +		goto done;
> +
> +	ret = v4l2_fh_open(file);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* TODO: prepare video pipeline */
> +done:
> +	mutex_unlock(&isp->video_lock);
> +	return ret;
> +}
> +
> +static int isp_video_capture_close(struct file *file)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	int ret = 0;
> +
> +	mutex_lock(&isp->video_lock);
> +
> +	if (isp->out_path == FIMC_IO_DMA) {
> +		/* TODO: stop capture, cleanup */
> +	}
> +
> +	/* pm_runtime_put(&isp->pdev->dev); */
> +
> +	if (isp->ref_count == 0)
> +		vb2_queue_release(&isp->capture_vb_queue);
> +
> +	ret = v4l2_fh_release(file);
> +
> +	mutex_unlock(&isp->video_lock);
> +	return ret;
> +}
> +
> +static unsigned int isp_video_capture_poll(struct file *file,
> +				   struct poll_table_struct *wait)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	int ret;
> +
> +	mutex_lock(&isp->video_lock);
> +	ret = vb2_poll(&isp->capture_vb_queue, file, wait);
> +	mutex_unlock(&isp->video_lock);
> +	return ret;
> +}
> +
> +static int isp_video_capture_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	int ret;
> +
> +	if (mutex_lock_interruptible(&isp->video_lock))
> +		return -ERESTARTSYS;
> +
> +	ret = vb2_mmap(&isp->capture_vb_queue, vma);
> +	mutex_unlock(&isp->video_lock);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_file_operations isp_video_capture_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= isp_video_capture_open,
> +	.release	= isp_video_capture_close,
> +	.poll		= isp_video_capture_poll,
> +	.unlocked_ioctl	= video_ioctl2,
> +	.mmap		= isp_video_capture_mmap,

Can't you use the helper functions vb2_fop_open/release/poll/mmap here?

> +};
> +
> +/*
> + * Video node ioctl operations
> + */
> +static int fimc_isp_capture_querycap_capture(struct file *file, void *priv,
> +					     struct v4l2_capability *cap)
> +{
> +
> +	strlcpy(cap->driver, FIMC_IS_DRV_NAME, sizeof(cap->driver));
> +	strlcpy(cap->card, FIMC_IS_DRV_NAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +				"platform:exynos4x12-isp");
> +
> +	cap->device_caps = V4L2_CAP_STREAMING;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> +	return 0;
> +}
> +
> +static int fimc_isp_capture_enum_fmt_mplane(struct file *file, void *priv,
> +					    struct v4l2_fmtdesc *f)
> +{
> +	const struct fimc_fmt *fmt;
> +
> +	if (f->index >= FIMC_ISP_NUM_FORMATS)
> +		return -EINVAL;
> +
> +	fmt = fimc_isp_find_format(NULL, NULL, f->index);
> +	if (WARN_ON(fmt == NULL))
> +		return -EINVAL;
> +
> +	strlcpy(f->description, fmt->name, sizeof(f->description));
> +	f->pixelformat = fmt->fourcc;
> +
> +	return 0;
> +}
> +
> +static int fimc_isp_capture_g_fmt_mplane(struct file *file, void *fh,
> +					 struct v4l2_format *f)
> +{
> +	/* TODO:  */
> +	return 0;
> +}
> +
> +static int fimc_isp_capture_try_fmt(struct fimc_isp *isp,
> +				    struct v4l2_pix_format_mplane *pixm,
> +				    const struct fimc_fmt **ffmt)
> +{
> +	/* TODO: */
> +	return 0;
> +}
> +
> +static int fimc_isp_capture_try_fmt_mplane(struct file *file, void *fh,
> +					   struct v4l2_format *f)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	return fimc_isp_capture_try_fmt(isp, &f->fmt.pix_mp, NULL);
> +}
> +
> +static int fimc_isp_capture_s_fmt_mplane(struct file *file, void *priv,
> +					 struct v4l2_format *f)
> +{
> +	/* TODO: */
> +	return 0;
> +}
> +
> +static int fimc_isp_pipeline_validate(struct fimc_isp *isp)
> +{
> +	/* TODO: */
> +	return 0;
> +}
> +
> +static int fimc_isp_capture_streamon(struct file *file, void *priv,
> +				     enum v4l2_buf_type type)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	struct v4l2_subdev *sensor = isp->pipeline.subdevs[IDX_SENSOR];
> +	struct fimc_pipeline *p = &isp->pipeline;
> +	int ret;
> +
> +	/* TODO: check if the OTF interface is not running */
> +
> +	ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = fimc_isp_pipeline_validate(isp);
> +	if (ret) {
> +		media_entity_pipeline_stop(&sensor->entity);
> +		return ret;
> +	}
> +
> +	return vb2_streamon(&isp->capture_vb_queue, type);
> +}
> +
> +static int fimc_isp_capture_streamoff(struct file *file, void *priv,
> +				      enum v4l2_buf_type type)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	struct v4l2_subdev *sd = isp->pipeline.subdevs[IDX_SENSOR];
> +	int ret;
> +
> +	ret = vb2_streamoff(&isp->capture_vb_queue, type);
> +	if (ret == 0)
> +		media_entity_pipeline_stop(&sd->entity);
> +	return ret;
> +}
> +
> +static int fimc_isp_capture_reqbufs(struct file *file, void *priv,
> +				    struct v4l2_requestbuffers *reqbufs)
> +{
> +	struct fimc_isp *isp = video_drvdata(file);
> +	int ret;
> +
> +	reqbufs->count = max_t(u32, FIMC_IS_REQ_BUFS_MIN, reqbufs->count);
> +	ret = vb2_reqbufs(&isp->capture_vb_queue, reqbufs);

You probably want to call vb2_ioctl_reqbufs here since that does additional
ownership checks that vb2_reqbufs doesn't.

The same is true for vb2_ioctl_streamon/off, BTW.

> +	if (!ret < 0)
> +		isp->reqbufs_count = reqbufs->count;
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ioctl_ops isp_video_capture_ioctl_ops = {
> +	.vidioc_querycap		= fimc_isp_capture_querycap_capture,
> +	.vidioc_enum_fmt_vid_cap_mplane	= fimc_isp_capture_enum_fmt_mplane,
> +	.vidioc_try_fmt_vid_cap_mplane	= fimc_isp_capture_try_fmt_mplane,
> +	.vidioc_s_fmt_vid_cap_mplane	= fimc_isp_capture_s_fmt_mplane,
> +	.vidioc_g_fmt_vid_cap_mplane	= fimc_isp_capture_g_fmt_mplane,
> +	.vidioc_reqbufs			= fimc_isp_capture_reqbufs,
> +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
> +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> +	.vidioc_streamon		= fimc_isp_capture_streamon,
> +	.vidioc_streamoff		= fimc_isp_capture_streamoff,
> +};
> +
> +int fimc_isp_video_device_register(struct fimc_isp *isp,
> +				   struct v4l2_device *v4l2_dev)
> +{
> +	struct vb2_queue *q = &isp->capture_vb_queue;
> +	struct video_device *vfd = &isp->vfd;
> +	int ret;
> +
> +	mutex_init(&isp->video_lock);
> +	INIT_LIST_HEAD(&isp->pending_buf_q);
> +	INIT_LIST_HEAD(&isp->active_buf_q);
> +
> +	memset(vfd, 0, sizeof(*vfd));
> +	snprintf(vfd->name, sizeof(vfd->name), "fimc-is-isp.capture");
> +
> +	isp->video_capture_format = fimc_isp_find_format(NULL, NULL, 0);
> +	isp->out_path = FIMC_IO_DMA;
> +	isp->ref_count = 0;
> +	isp->reqbufs_count = 0;
> +
> +	memset(q, 0, sizeof(*q));
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	q->io_modes = VB2_MMAP;
> +	q->ops = &isp_video_capture_qops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +	q->buf_struct_size = sizeof(struct flite_buffer);
> +	q->drv_priv = isp;
> +	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret < 0)
> +		return ret;
> +
> +	vfd->queue = q;
> +	vfd->fops = &isp_video_capture_fops;
> +	vfd->ioctl_ops = &isp_video_capture_ioctl_ops;
> +	vfd->v4l2_dev = v4l2_dev;
> +	vfd->minor = -1;
> +	vfd->release = video_device_release_empty;
> +	vfd->lock = &isp->video_lock;
> +
> +	isp->vd_pad.flags = MEDIA_PAD_FL_SINK;
> +	ret = media_entity_init(&vfd->entity, 1, &isp->vd_pad, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	video_set_drvdata(vfd, isp);
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
> +	if (ret < 0) {
> +		media_entity_cleanup(&vfd->entity);
> +		return ret;
> +	}
> +
> +	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
> +		  vfd->name, video_device_node_name(vfd));
> +
> +	return 0;
> +}
> +
> +void fimc_isp_video_device_unregister(struct fimc_isp *isp)
> +{
> +	if (isp && video_is_registered(&isp->vfd)) {
> +		video_unregister_device(&isp->vfd);
> +		media_entity_cleanup(&isp->vfd.entity);
> +	}
> +}

Regards,

	Hans
Sylwester Nawrocki March 17, 2013, 9:03 p.m. UTC | #2
On 03/12/2013 03:44 PM, Hans Verkuil wrote:
> On Mon 11 March 2013 20:44:49 Sylwester Nawrocki wrote:
[...]
>> +static int isp_video_capture_open(struct file *file)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	int ret = 0;
>> +
>> +	if (mutex_lock_interruptible(&isp->video_lock))
>> +		return -ERESTARTSYS;
>> +
>> +	/* ret = pm_runtime_get_sync(&isp->pdev->dev); */
>> +	if (ret<  0)
>> +		goto done;
>> +
>> +	ret = v4l2_fh_open(file);
>> +	if (ret<  0)
>> +		goto done;
>> +
>> +	/* TODO: prepare video pipeline */
>> +done:
>> +	mutex_unlock(&isp->video_lock);
>> +	return ret;
>> +}
>> +
>> +static int isp_video_capture_close(struct file *file)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	int ret = 0;
>> +
>> +	mutex_lock(&isp->video_lock);
>> +
>> +	if (isp->out_path == FIMC_IO_DMA) {
>> +		/* TODO: stop capture, cleanup */
>> +	}
>> +
>> +	/* pm_runtime_put(&isp->pdev->dev); */
>> +
>> +	if (isp->ref_count == 0)
>> +		vb2_queue_release(&isp->capture_vb_queue);
>> +
>> +	ret = v4l2_fh_release(file);
>> +
>> +	mutex_unlock(&isp->video_lock);
>> +	return ret;
>> +}
>> +
>> +static unsigned int isp_video_capture_poll(struct file *file,
>> +				   struct poll_table_struct *wait)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	int ret;
>> +
>> +	mutex_lock(&isp->video_lock);
>> +	ret = vb2_poll(&isp->capture_vb_queue, file, wait);
>> +	mutex_unlock(&isp->video_lock);
>> +	return ret;
>> +}
>> +
>> +static int isp_video_capture_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	int ret;
>> +
>> +	if (mutex_lock_interruptible(&isp->video_lock))
>> +		return -ERESTARTSYS;
>> +
>> +	ret = vb2_mmap(&isp->capture_vb_queue, vma);
>> +	mutex_unlock(&isp->video_lock);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct v4l2_file_operations isp_video_capture_fops = {
>> +	.owner		= THIS_MODULE,
>> +	.open		= isp_video_capture_open,
>> +	.release	= isp_video_capture_close,
>> +	.poll		= isp_video_capture_poll,
>> +	.unlocked_ioctl	= video_ioctl2,
>> +	.mmap		= isp_video_capture_mmap,
>
> Can't you use the helper functions vb2_fop_open/release/poll/mmap here?

It seems vb2_fop_mmap/poll can be used directly, open(), release() are
a bit more complicated as some media pipeline operations need to
additionally be done within these callbacks. There is no vb2_fop_open(),
and AFAICS v4l2_fh_open() is sufficient and intended as open() helper.
For the next iteration I have used vb2_fop_release(), called indirectly,
as it nicely simplifies things a bit.

BTW, shouldn't vb2_fop_release() also be taking the lock ? Actually it is
more useful for me in current form, but the drivers that directly assign
it to struct v4l2_file_operations::open might be in trouble, unless I'm
missing something.

>> +};
>> +
>> +/*
>> + * Video node ioctl operations
>> + */
[...]
>> +static int fimc_isp_capture_streamon(struct file *file, void *priv,
>> +				     enum v4l2_buf_type type)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	struct v4l2_subdev *sensor = isp->pipeline.subdevs[IDX_SENSOR];
>> +	struct fimc_pipeline *p =&isp->pipeline;
>> +	int ret;
>> +
>> +	/* TODO: check if the OTF interface is not running */
>> +
>> +	ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
>> +	if (ret<  0)
>> +		return ret;
>> +
>> +	ret = fimc_isp_pipeline_validate(isp);
>> +	if (ret) {
>> +		media_entity_pipeline_stop(&sensor->entity);
>> +		return ret;
>> +	}
>> +
>> +	return vb2_streamon(&isp->capture_vb_queue, type);
>> +}
>> +
>> +static int fimc_isp_capture_streamoff(struct file *file, void *priv,
>> +				      enum v4l2_buf_type type)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	struct v4l2_subdev *sd = isp->pipeline.subdevs[IDX_SENSOR];
>> +	int ret;
>> +
>> +	ret = vb2_streamoff(&isp->capture_vb_queue, type);
>> +	if (ret == 0)
>> +		media_entity_pipeline_stop(&sd->entity);
>> +	return ret;
>> +}
>> +
>> +static int fimc_isp_capture_reqbufs(struct file *file, void *priv,
>> +				    struct v4l2_requestbuffers *reqbufs)
>> +{
>> +	struct fimc_isp *isp = video_drvdata(file);
>> +	int ret;
>> +
>> +	reqbufs->count = max_t(u32, FIMC_IS_REQ_BUFS_MIN, reqbufs->count);
>> +	ret = vb2_reqbufs(&isp->capture_vb_queue, reqbufs);
>
> You probably want to call vb2_ioctl_reqbufs here since that does additional
> ownership checks that vb2_reqbufs doesn't.

Yes, thanks for the suggestion. That was actually helpful, previously
it wasn't immediately clear to me one can still take advantage of those
vb2_ioctl_* helpers, mainly have the ownership handling in the core,
and have some handlers assigned directly to v4l2_ioctl_ops an some called
indirectly from the driver's own callbacks, should they need something
else that is done in the helpers.

I found those helpers really useful, especially in drivers that need to
support several video nodes. Lots of boilerplate can be eliminated. And
also vb2_ops_wait_prepare/finish are a simple and nice improvement.

> The same is true for vb2_ioctl_streamon/off, BTW.

Indeed. I have already applied all possible helpers for the next iteration.

I need to yet resolve an issue with locking order, as I previously missed
that media_entity_pipeline_start/stop() also takes the graph mutex.

And currently the driver is supposed to take the graph mutex first and
then the video mutex. Since the link_notify callback of the media device
is called with the graph mutex already held.

The only solution I came up so far is to provide unlocked versions of
media_entity_pipeline_start/stop().

Ideally using video mutex in link_notify() callback should not be needed,
but there are things done there needed for backward video device
compatibility.

--

Regards,
Sylwester
Hans Verkuil March 18, 2013, 12:51 p.m. UTC | #3
On Sun March 17 2013 22:03:38 Sylwester Nawrocki wrote:
> On 03/12/2013 03:44 PM, Hans Verkuil wrote:
> > On Mon 11 March 2013 20:44:49 Sylwester Nawrocki wrote:
> [...]
> >> +static int isp_video_capture_open(struct file *file)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	int ret = 0;
> >> +
> >> +	if (mutex_lock_interruptible(&isp->video_lock))
> >> +		return -ERESTARTSYS;
> >> +
> >> +	/* ret = pm_runtime_get_sync(&isp->pdev->dev); */
> >> +	if (ret<  0)
> >> +		goto done;
> >> +
> >> +	ret = v4l2_fh_open(file);
> >> +	if (ret<  0)
> >> +		goto done;
> >> +
> >> +	/* TODO: prepare video pipeline */
> >> +done:
> >> +	mutex_unlock(&isp->video_lock);
> >> +	return ret;
> >> +}
> >> +
> >> +static int isp_video_capture_close(struct file *file)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	int ret = 0;
> >> +
> >> +	mutex_lock(&isp->video_lock);
> >> +
> >> +	if (isp->out_path == FIMC_IO_DMA) {
> >> +		/* TODO: stop capture, cleanup */
> >> +	}
> >> +
> >> +	/* pm_runtime_put(&isp->pdev->dev); */
> >> +
> >> +	if (isp->ref_count == 0)
> >> +		vb2_queue_release(&isp->capture_vb_queue);
> >> +
> >> +	ret = v4l2_fh_release(file);
> >> +
> >> +	mutex_unlock(&isp->video_lock);
> >> +	return ret;
> >> +}
> >> +
> >> +static unsigned int isp_video_capture_poll(struct file *file,
> >> +				   struct poll_table_struct *wait)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	int ret;
> >> +
> >> +	mutex_lock(&isp->video_lock);
> >> +	ret = vb2_poll(&isp->capture_vb_queue, file, wait);
> >> +	mutex_unlock(&isp->video_lock);
> >> +	return ret;
> >> +}
> >> +
> >> +static int isp_video_capture_mmap(struct file *file, struct vm_area_struct *vma)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	int ret;
> >> +
> >> +	if (mutex_lock_interruptible(&isp->video_lock))
> >> +		return -ERESTARTSYS;
> >> +
> >> +	ret = vb2_mmap(&isp->capture_vb_queue, vma);
> >> +	mutex_unlock(&isp->video_lock);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static const struct v4l2_file_operations isp_video_capture_fops = {
> >> +	.owner		= THIS_MODULE,
> >> +	.open		= isp_video_capture_open,
> >> +	.release	= isp_video_capture_close,
> >> +	.poll		= isp_video_capture_poll,
> >> +	.unlocked_ioctl	= video_ioctl2,
> >> +	.mmap		= isp_video_capture_mmap,
> >
> > Can't you use the helper functions vb2_fop_open/release/poll/mmap here?
> 
> It seems vb2_fop_mmap/poll can be used directly, open(), release() are
> a bit more complicated as some media pipeline operations need to
> additionally be done within these callbacks. There is no vb2_fop_open(),
> and AFAICS v4l2_fh_open() is sufficient and intended as open() helper.

That's correct. Sorry for the misinformation about the non-existant
vb2_fop_open.

> For the next iteration I have used vb2_fop_release(), called indirectly,
> as it nicely simplifies things a bit.
> 
> BTW, shouldn't vb2_fop_release() also be taking the lock ? Actually it is
> more useful for me in current form, but the drivers that directly assign
> it to struct v4l2_file_operations::open might be in trouble, unless I'm
> missing something.

I don't see where a lock would be needed. I don't see any concurrency here.
Nobody else can mess with the queue as long as they are not the owner.

> 
> >> +};
> >> +
> >> +/*
> >> + * Video node ioctl operations
> >> + */
> [...]
> >> +static int fimc_isp_capture_streamon(struct file *file, void *priv,
> >> +				     enum v4l2_buf_type type)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	struct v4l2_subdev *sensor = isp->pipeline.subdevs[IDX_SENSOR];
> >> +	struct fimc_pipeline *p =&isp->pipeline;
> >> +	int ret;
> >> +
> >> +	/* TODO: check if the OTF interface is not running */
> >> +
> >> +	ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
> >> +	if (ret<  0)
> >> +		return ret;
> >> +
> >> +	ret = fimc_isp_pipeline_validate(isp);
> >> +	if (ret) {
> >> +		media_entity_pipeline_stop(&sensor->entity);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return vb2_streamon(&isp->capture_vb_queue, type);
> >> +}
> >> +
> >> +static int fimc_isp_capture_streamoff(struct file *file, void *priv,
> >> +				      enum v4l2_buf_type type)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	struct v4l2_subdev *sd = isp->pipeline.subdevs[IDX_SENSOR];
> >> +	int ret;
> >> +
> >> +	ret = vb2_streamoff(&isp->capture_vb_queue, type);
> >> +	if (ret == 0)
> >> +		media_entity_pipeline_stop(&sd->entity);
> >> +	return ret;
> >> +}
> >> +
> >> +static int fimc_isp_capture_reqbufs(struct file *file, void *priv,
> >> +				    struct v4l2_requestbuffers *reqbufs)
> >> +{
> >> +	struct fimc_isp *isp = video_drvdata(file);
> >> +	int ret;
> >> +
> >> +	reqbufs->count = max_t(u32, FIMC_IS_REQ_BUFS_MIN, reqbufs->count);
> >> +	ret = vb2_reqbufs(&isp->capture_vb_queue, reqbufs);
> >
> > You probably want to call vb2_ioctl_reqbufs here since that does additional
> > ownership checks that vb2_reqbufs doesn't.
> 
> Yes, thanks for the suggestion. That was actually helpful, previously
> it wasn't immediately clear to me one can still take advantage of those
> vb2_ioctl_* helpers, mainly have the ownership handling in the core,
> and have some handlers assigned directly to v4l2_ioctl_ops an some called
> indirectly from the driver's own callbacks, should they need something
> else that is done in the helpers.
> 
> I found those helpers really useful, especially in drivers that need to
> support several video nodes. Lots of boilerplate can be eliminated. And
> also vb2_ops_wait_prepare/finish are a simple and nice improvement.

Glad to hear!

I still think there are some more improvements that could be made with some
helper functions and perhaps some more fields: most (all?) drivers need a
list for their active buffers, so I really think adding a struct list to
vb2_buffer and a list for active buffers + a spinlock to vb2_queue would
help.

Helper functions could then be added to handle that queue and also to set
the timestamp and sequence fields. I would need to think more about the
details, but after using vb2 for a while I see a lot of shared code between
the various drivers.

Regards,

	Hans

> > The same is true for vb2_ioctl_streamon/off, BTW.
> 
> Indeed. I have already applied all possible helpers for the next iteration.
> 
> I need to yet resolve an issue with locking order, as I previously missed
> that media_entity_pipeline_start/stop() also takes the graph mutex.
> 
> And currently the driver is supposed to take the graph mutex first and
> then the video mutex. Since the link_notify callback of the media device
> is called with the graph mutex already held.
> 
> The only solution I came up so far is to provide unlocked versions of
> media_entity_pipeline_start/stop().
> 
> Ideally using video mutex in link_notify() callback should not be needed,
> but there are things done there needed for backward video device
> compatibility.
> 
> --
> 
> Regards,
> Sylwester
>
diff mbox

Patch

diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/s5p-fimc/Kconfig
index c16b20d..1253e25 100644
--- a/drivers/media/platform/s5p-fimc/Kconfig
+++ b/drivers/media/platform/s5p-fimc/Kconfig
@@ -46,4 +46,17 @@  config VIDEO_EXYNOS_FIMC_LITE
 	  module will be called exynos-fimc-lite.
 endif
 
+if (SOC_EXYNOS4212 || SOC_EXYNOS4412) && OF && !ARCH_MULTIPLATFORM
+
+config VIDEO_EXYNOS4_FIMC_IS
+	tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	  This is a V4L2 driver for Samsung EXYNOS4x12 SoC FIMC-IS
+	  (Imaging Subsystem).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called exynos-fimc-is.
+endif
+
 endif # VIDEO_SAMSUNG_S5P_FIMC
diff --git a/drivers/media/platform/s5p-fimc/Makefile b/drivers/media/platform/s5p-fimc/Makefile
index 4648514..55b171a 100644
--- a/drivers/media/platform/s5p-fimc/Makefile
+++ b/drivers/media/platform/s5p-fimc/Makefile
@@ -1,7 +1,11 @@ 
 s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o
 exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
+exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
+exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
+exynos-fimc-is-objs += fimc-isp-video.o
 s5p-csis-objs := mipi-csis.o
 
 obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS)	+= s5p-csis.o
 obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)	+= exynos-fimc-lite.o
+obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)	+= exynos-fimc-is.o
 obj-$(CONFIG_VIDEO_S5P_FIMC)		+= s5p-fimc.o
diff --git a/drivers/media/platform/s5p-fimc/fimc-isp-video.c b/drivers/media/platform/s5p-fimc/fimc-isp-video.c
new file mode 100644
index 0000000..bdeacaa
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-isp-video.c
@@ -0,0 +1,414 @@ 
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-mdevice.h"
+#include "fimc-core.h"
+#include "fimc-is.h"
+
+static int isp_video_capture_start_streaming(struct vb2_queue *q,
+					unsigned int count)
+{
+	/* TODO: start ISP output DMA */
+	return 0;
+}
+
+static int isp_video_capture_stop_streaming(struct vb2_queue *q)
+{
+	/* TODO: stop ISP output DMA */
+	return 0;
+}
+
+static int isp_video_capture_queue_setup(struct vb2_queue *vq,
+			const struct v4l2_format *pfmt,
+			unsigned int *num_buffers, unsigned int *num_planes,
+			unsigned int sizes[], void *allocators[])
+{
+	const struct v4l2_pix_format_mplane *pixm = NULL;
+	struct fimc_isp *isp = vq->drv_priv;
+	struct fimc_isp_frame *frame = &isp->out_frame;
+	const struct fimc_fmt *fmt = isp->video_capture_format;
+	unsigned long wh;
+	int i;
+
+	if (pfmt) {
+		pixm = &pfmt->fmt.pix_mp;
+		fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
+		wh = pixm->width * pixm->height;
+	} else {
+		wh = frame->f_width * frame->f_height;
+	}
+
+	if (fmt == NULL)
+		return -EINVAL;
+
+	*num_planes = fmt->memplanes;
+
+	for (i = 0; i < fmt->memplanes; i++) {
+		unsigned int size = (wh * fmt->depth[i]) / 8;
+		if (pixm)
+			sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+		else
+			sizes[i] = size;
+		allocators[i] = isp->alloc_ctx;
+	}
+
+	return 0;
+}
+
+static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct fimc_isp *isp = vq->drv_priv;
+	int i;
+
+	if (isp->video_capture_format == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < isp->video_capture_format->memplanes; i++) {
+		unsigned long size = isp->payload[i];
+
+		if (vb2_plane_size(vb, i) < size) {
+			v4l2_err(&isp->vfd,
+				 "User buffer too small (%ld < %ld)\n",
+				 vb2_plane_size(vb, i), size);
+			return -EINVAL;
+		}
+		vb2_set_plane_payload(vb, i, size);
+	}
+
+	return 0;
+}
+
+static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+	/* TODO: */
+}
+
+static void isp_video_lock(struct vb2_queue *vq)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(vq);
+	mutex_lock(&isp->video_lock);
+}
+
+static void isp_video_unlock(struct vb2_queue *vq)
+{
+	struct fimc_isp *isp = vb2_get_drv_priv(vq);
+	mutex_unlock(&isp->video_lock);
+}
+
+static const struct vb2_ops isp_video_capture_qops = {
+	.queue_setup	 = isp_video_capture_queue_setup,
+	.buf_prepare	 = isp_video_capture_buffer_prepare,
+	.buf_queue	 = isp_video_capture_buffer_queue,
+	.wait_prepare	 = isp_video_unlock,
+	.wait_finish	 = isp_video_lock,
+	.start_streaming = isp_video_capture_start_streaming,
+	.stop_streaming	 = isp_video_capture_stop_streaming,
+};
+
+static int isp_video_capture_open(struct file *file)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret = 0;
+
+	if (mutex_lock_interruptible(&isp->video_lock))
+		return -ERESTARTSYS;
+
+	/* ret = pm_runtime_get_sync(&isp->pdev->dev); */
+	if (ret < 0)
+		goto done;
+
+	ret = v4l2_fh_open(file);
+	if (ret < 0)
+		goto done;
+
+	/* TODO: prepare video pipeline */
+done:
+	mutex_unlock(&isp->video_lock);
+	return ret;
+}
+
+static int isp_video_capture_close(struct file *file)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret = 0;
+
+	mutex_lock(&isp->video_lock);
+
+	if (isp->out_path == FIMC_IO_DMA) {
+		/* TODO: stop capture, cleanup */
+	}
+
+	/* pm_runtime_put(&isp->pdev->dev); */
+
+	if (isp->ref_count == 0)
+		vb2_queue_release(&isp->capture_vb_queue);
+
+	ret = v4l2_fh_release(file);
+
+	mutex_unlock(&isp->video_lock);
+	return ret;
+}
+
+static unsigned int isp_video_capture_poll(struct file *file,
+				   struct poll_table_struct *wait)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret;
+
+	mutex_lock(&isp->video_lock);
+	ret = vb2_poll(&isp->capture_vb_queue, file, wait);
+	mutex_unlock(&isp->video_lock);
+	return ret;
+}
+
+static int isp_video_capture_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret;
+
+	if (mutex_lock_interruptible(&isp->video_lock))
+		return -ERESTARTSYS;
+
+	ret = vb2_mmap(&isp->capture_vb_queue, vma);
+	mutex_unlock(&isp->video_lock);
+
+	return ret;
+}
+
+static const struct v4l2_file_operations isp_video_capture_fops = {
+	.owner		= THIS_MODULE,
+	.open		= isp_video_capture_open,
+	.release	= isp_video_capture_close,
+	.poll		= isp_video_capture_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= isp_video_capture_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int fimc_isp_capture_querycap_capture(struct file *file, void *priv,
+					     struct v4l2_capability *cap)
+{
+
+	strlcpy(cap->driver, FIMC_IS_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, FIMC_IS_DRV_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+				"platform:exynos4x12-isp");
+
+	cap->device_caps = V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int fimc_isp_capture_enum_fmt_mplane(struct file *file, void *priv,
+					    struct v4l2_fmtdesc *f)
+{
+	const struct fimc_fmt *fmt;
+
+	if (f->index >= FIMC_ISP_NUM_FORMATS)
+		return -EINVAL;
+
+	fmt = fimc_isp_find_format(NULL, NULL, f->index);
+	if (WARN_ON(fmt == NULL))
+		return -EINVAL;
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+
+static int fimc_isp_capture_g_fmt_mplane(struct file *file, void *fh,
+					 struct v4l2_format *f)
+{
+	/* TODO:  */
+	return 0;
+}
+
+static int fimc_isp_capture_try_fmt(struct fimc_isp *isp,
+				    struct v4l2_pix_format_mplane *pixm,
+				    const struct fimc_fmt **ffmt)
+{
+	/* TODO: */
+	return 0;
+}
+
+static int fimc_isp_capture_try_fmt_mplane(struct file *file, void *fh,
+					   struct v4l2_format *f)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	return fimc_isp_capture_try_fmt(isp, &f->fmt.pix_mp, NULL);
+}
+
+static int fimc_isp_capture_s_fmt_mplane(struct file *file, void *priv,
+					 struct v4l2_format *f)
+{
+	/* TODO: */
+	return 0;
+}
+
+static int fimc_isp_pipeline_validate(struct fimc_isp *isp)
+{
+	/* TODO: */
+	return 0;
+}
+
+static int fimc_isp_capture_streamon(struct file *file, void *priv,
+				     enum v4l2_buf_type type)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct v4l2_subdev *sensor = isp->pipeline.subdevs[IDX_SENSOR];
+	struct fimc_pipeline *p = &isp->pipeline;
+	int ret;
+
+	/* TODO: check if the OTF interface is not running */
+
+	ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
+	if (ret < 0)
+		return ret;
+
+	ret = fimc_isp_pipeline_validate(isp);
+	if (ret) {
+		media_entity_pipeline_stop(&sensor->entity);
+		return ret;
+	}
+
+	return vb2_streamon(&isp->capture_vb_queue, type);
+}
+
+static int fimc_isp_capture_streamoff(struct file *file, void *priv,
+				      enum v4l2_buf_type type)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	struct v4l2_subdev *sd = isp->pipeline.subdevs[IDX_SENSOR];
+	int ret;
+
+	ret = vb2_streamoff(&isp->capture_vb_queue, type);
+	if (ret == 0)
+		media_entity_pipeline_stop(&sd->entity);
+	return ret;
+}
+
+static int fimc_isp_capture_reqbufs(struct file *file, void *priv,
+				    struct v4l2_requestbuffers *reqbufs)
+{
+	struct fimc_isp *isp = video_drvdata(file);
+	int ret;
+
+	reqbufs->count = max_t(u32, FIMC_IS_REQ_BUFS_MIN, reqbufs->count);
+	ret = vb2_reqbufs(&isp->capture_vb_queue, reqbufs);
+	if (!ret < 0)
+		isp->reqbufs_count = reqbufs->count;
+
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops isp_video_capture_ioctl_ops = {
+	.vidioc_querycap		= fimc_isp_capture_querycap_capture,
+	.vidioc_enum_fmt_vid_cap_mplane	= fimc_isp_capture_enum_fmt_mplane,
+	.vidioc_try_fmt_vid_cap_mplane	= fimc_isp_capture_try_fmt_mplane,
+	.vidioc_s_fmt_vid_cap_mplane	= fimc_isp_capture_s_fmt_mplane,
+	.vidioc_g_fmt_vid_cap_mplane	= fimc_isp_capture_g_fmt_mplane,
+	.vidioc_reqbufs			= fimc_isp_capture_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_streamon		= fimc_isp_capture_streamon,
+	.vidioc_streamoff		= fimc_isp_capture_streamoff,
+};
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+				   struct v4l2_device *v4l2_dev)
+{
+	struct vb2_queue *q = &isp->capture_vb_queue;
+	struct video_device *vfd = &isp->vfd;
+	int ret;
+
+	mutex_init(&isp->video_lock);
+	INIT_LIST_HEAD(&isp->pending_buf_q);
+	INIT_LIST_HEAD(&isp->active_buf_q);
+
+	memset(vfd, 0, sizeof(*vfd));
+	snprintf(vfd->name, sizeof(vfd->name), "fimc-is-isp.capture");
+
+	isp->video_capture_format = fimc_isp_find_format(NULL, NULL, 0);
+	isp->out_path = FIMC_IO_DMA;
+	isp->ref_count = 0;
+	isp->reqbufs_count = 0;
+
+	memset(q, 0, sizeof(*q));
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->io_modes = VB2_MMAP;
+	q->ops = &isp_video_capture_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct flite_buffer);
+	q->drv_priv = isp;
+	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+	ret = vb2_queue_init(q);
+	if (ret < 0)
+		return ret;
+
+	vfd->queue = q;
+	vfd->fops = &isp_video_capture_fops;
+	vfd->ioctl_ops = &isp_video_capture_ioctl_ops;
+	vfd->v4l2_dev = v4l2_dev;
+	vfd->minor = -1;
+	vfd->release = video_device_release_empty;
+	vfd->lock = &isp->video_lock;
+
+	isp->vd_pad.flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_init(&vfd->entity, 1, &isp->vd_pad, 0);
+	if (ret < 0)
+		return ret;
+
+	video_set_drvdata(vfd, isp);
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+	if (ret < 0) {
+		media_entity_cleanup(&vfd->entity);
+		return ret;
+	}
+
+	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+		  vfd->name, video_device_node_name(vfd));
+
+	return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp)
+{
+	if (isp && video_is_registered(&isp->vfd)) {
+		video_unregister_device(&isp->vfd);
+		media_entity_cleanup(&isp->vfd.entity);
+	}
+}
diff --git a/drivers/media/platform/s5p-fimc/fimc-isp-video.h b/drivers/media/platform/s5p-fimc/fimc-isp-video.h
new file mode 100644
index 0000000..5378dd9
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-isp-video.h
@@ -0,0 +1,50 @@ 
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_ISP_VIDEO__
+#define FIMC_ISP_VIDEO__
+
+#include <media/videobuf2-core.h>
+#include "fimc-isp.h"
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+				struct v4l2_device *v4l2_dev);
+void fimc_isp_video_device_unregister(struct fimc_isp *isp);
+
+static inline void fimc_is_active_queue_add(struct fimc_isp *isp,
+					 struct fimc_isp_buffer *buf)
+{
+	list_add_tail(&buf->list, &isp->active_buf_q);
+}
+
+static inline struct fimc_isp_buffer *fimc_is_active_queue_pop(
+					struct fimc_isp *isp)
+{
+	struct fimc_isp_buffer *buf = list_entry(isp->active_buf_q.next,
+					      struct fimc_isp_buffer, list);
+	list_del(&buf->list);
+	return buf;
+}
+
+static inline void fimc_is_pending_queue_add(struct fimc_isp *isp,
+					struct fimc_isp_buffer *buf)
+{
+	list_add_tail(&buf->list, &isp->pending_buf_q);
+}
+
+static inline struct fimc_isp_buffer *fimc_is_pending_queue_pop(
+					struct fimc_isp *isp)
+{
+	struct fimc_isp_buffer *buf = list_entry(isp->pending_buf_q.next,
+					      struct fimc_isp_buffer, list);
+	list_del(&buf->list);
+	return buf;
+}
+#endif /* FIMC_ISP_VIDEO__ */