[RFC PATCH 5/8] s5p-fimc: Add ISP video capture driver stubs
Sylwester Nawrocki
s.nawrocki at samsung.com
Mon Mar 11 15:44:49 EDT 2013
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 at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at 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 at 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 at 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__ */
--
1.7.9.5
More information about the linux-arm-kernel
mailing list