[COMMON] media: smfc: add skelton implementation of v4l2.
authorCho KyongHo <pullip.cho@samsung.com>
Wed, 25 Mar 2015 00:03:25 +0000 (09:03 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:18 +0000 (20:22 +0300)
The SMFC driver with V4L2 support is prepared. This patch includes
 - registration to video_device
 - buffer management with videobuf2-dma-sg
 - job scheduling by v4l2-mem2mem
 - V4L2 control handling by v4l2_fh
It is not the complete implementation. The following patches complete
the driver.

Change-Id: I19c3c5668c6ff975a7b55a0b9efc6147f94f143c
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/media/platform/exynos/smfc/smfc.c
drivers/media/platform/exynos/smfc/smfc.h

index 7e50a866961754fa16921109fa1834652da259ad..49fd0c184170ddc8bb032be83a96f8656d23824a 100644 (file)
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/mutex.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-sg.h>
 
 #include "smfc.h"
 #include "smfc-regs.h"
@@ -27,6 +34,266 @@ static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
        return IRQ_HANDLED;
 }
 
+static int smfc_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+                               unsigned int *num_planes, unsigned int sizes[],
+                               struct device *alloc_devs[])
+{
+       struct smfc_ctx *ctx = vb2_get_drv_priv(vq);
+       unsigned int i;
+
+       /* FIXME: correct output*/
+       *num_planes = 1;
+       for (i = 0; i < *num_planes; i++) {
+               sizes[i] = 100000;
+               alloc_devs[i] = ctx->smfc->dev;
+       }
+
+       return 0;
+}
+
+static int smfc_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+       /* TODO: fix configuration of payload */
+
+       return 0;
+}
+
+static void smfc_vb2_buf_finish(struct vb2_buffer *vb)
+{
+}
+
+static void smfc_vb2_buf_queue(struct vb2_buffer *vb)
+{
+       struct smfc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       v4l2_m2m_buf_queue(ctx->m2mctx, to_vb2_v4l2_buffer(vb));
+}
+
+static void smfc_vb2_lock(struct vb2_queue *vq)
+{
+       struct smfc_ctx *ctx = vb2_get_drv_priv(vq);
+       mutex_lock(&ctx->smfc->video_device_mutex);
+}
+
+static void smfc_vb2_unlock(struct vb2_queue *vq)
+{
+       struct smfc_ctx *ctx = vb2_get_drv_priv(vq);
+       mutex_unlock(&ctx->smfc->video_device_mutex);
+}
+
+static struct vb2_ops smfc_vb2_ops = {
+       .queue_setup    = smfc_vb2_queue_setup,
+       .buf_prepare    = smfc_vb2_buf_prepare,
+       .buf_finish     = smfc_vb2_buf_finish,
+       .buf_queue      = smfc_vb2_buf_queue,
+       .wait_finish    = smfc_vb2_lock,
+       .wait_prepare   = smfc_vb2_unlock,
+};
+
+static int smfc_queue_init(void *priv, struct vb2_queue *src_vq,
+                     struct vb2_queue *dst_vq)
+{
+       struct smfc_ctx *ctx = priv;
+       int ret;
+
+       memset(src_vq, 0, sizeof(*src_vq));
+       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+       src_vq->ops = &smfc_vb2_ops;
+       src_vq->mem_ops = &vb2_dma_sg_memops;
+       src_vq->drv_priv = ctx;
+       src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+       ret = vb2_queue_init(src_vq);
+       if (ret)
+               return ret;
+
+       memset(dst_vq, 0, sizeof(*dst_vq));
+       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+       dst_vq->ops = &smfc_vb2_ops;
+       dst_vq->mem_ops = &vb2_dma_sg_memops;
+       dst_vq->drv_priv = ctx;
+       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+       return vb2_queue_init(dst_vq);
+}
+
+static int exynos_smfc_open(struct file *filp)
+{
+       struct smfc_dev *smfc = video_drvdata(filp);
+       struct smfc_ctx *ctx;
+       int ret;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               dev_err(smfc->dev, "Failed to allocate smfc_ctx");
+               return -ENOMEM;
+       }
+
+       ctx->m2mctx = v4l2_m2m_ctx_init(smfc->m2mdev, ctx, smfc_queue_init);
+       if (IS_ERR(ctx->m2mctx)) {
+               ret = PTR_ERR(ctx->m2mctx);
+               dev_err(smfc->dev, "Failed(%d) to init m2m_ctx\n", ret);
+               goto err_m2m_ctx_init;
+       }
+
+
+       v4l2_fh_init(&ctx->v4l2_fh, smfc->videodev);
+       v4l2_fh_add(&ctx->v4l2_fh);
+
+       filp->private_data = &ctx->v4l2_fh;
+
+       if (!IS_ERR(smfc->clk_gate)) {
+               ret = clk_prepare(smfc->clk_gate);
+               if (!ret && !IS_ERR(smfc->clk_gate2))
+                       ret = clk_prepare(smfc->clk_gate2);
+               if (ret) {
+                       clk_unprepare(smfc->clk_gate);
+                       dev_err(smfc->dev,
+                               "Failed(%d) to prepare gate clocks\n", ret);
+                       goto err_clk;
+               }
+       }
+
+       ctx->smfc = smfc;
+
+       return 0;
+err_clk:
+       v4l2_fh_del(&ctx->v4l2_fh);
+       v4l2_fh_exit(&ctx->v4l2_fh);
+       v4l2_m2m_ctx_release(ctx->m2mctx);
+err_m2m_ctx_init:
+       kfree(ctx);
+       return ret;
+}
+
+static int exynos_smfc_release(struct file *filp)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(filp->private_data);
+
+       v4l2_fh_del(&ctx->v4l2_fh);
+       v4l2_m2m_ctx_release(ctx->m2mctx);
+
+       if (!IS_ERR(ctx->smfc->clk_gate)) {
+               clk_unprepare(ctx->smfc->clk_gate);
+               if (!IS_ERR(ctx->smfc->clk_gate2))
+                       clk_unprepare(ctx->smfc->clk_gate2);
+       }
+
+       kfree(ctx);
+
+       return 0;
+}
+
+static unsigned int exynos_smfc_poll(struct file *filp,
+                                    struct poll_table_struct *wait)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(filp->private_data);
+       return v4l2_m2m_poll(filp, ctx->m2mctx, wait);
+}
+
+static int exynos_smfc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(filp->private_data);
+       return v4l2_m2m_mmap(filp, ctx->m2mctx, vma);
+}
+
+static const struct v4l2_file_operations smfc_v4l2_fops = {
+       .owner          = THIS_MODULE,
+       .open           = exynos_smfc_open,
+       .release        = exynos_smfc_release,
+       .poll           = exynos_smfc_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = exynos_smfc_mmap,
+};
+
+static const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops = {
+};
+
+static void smfc_m2m_device_run(void *priv)
+{
+       /* TODO: H/W initialization */
+}
+
+static void smfc_m2m_job_abort(void *priv)
+{
+       /* TODO: aborting next job */
+}
+
+static struct v4l2_m2m_ops smfc_m2m_ops = {
+       .device_run     = smfc_m2m_device_run,
+       .job_abort      = smfc_m2m_job_abort,
+};
+
+static int smfc_init_v4l2(struct device *dev, struct smfc_dev *smfc)
+{
+       int ret;
+       size_t str_len;
+
+       strncpy(smfc->v4l2_dev.name, "exynos-hwjpeg",
+               sizeof(smfc->v4l2_dev.name) - 1);
+       smfc->v4l2_dev.name[sizeof(smfc->v4l2_dev.name) - 1] = '\0';
+
+       ret = v4l2_device_register(dev, &smfc->v4l2_dev);
+       if (ret) {
+               dev_err(dev, "Failed to register v4l2 device\n");
+               return ret;
+       }
+
+       smfc->videodev = video_device_alloc();
+       if (!smfc->videodev) {
+               dev_err(dev, "Failed to allocate video_device");
+               ret = -ENOMEM;
+               goto err_videodev_alloc;
+       }
+
+       str_len = sizeof(smfc->videodev->name);
+       if (smfc->device_id < 0) {
+               strncpy(smfc->videodev->name, MODULE_NAME, str_len);
+               smfc->videodev->name[str_len - 1] = '\0';
+       } else {
+               scnprintf(smfc->videodev->name, str_len,
+                         "%s.%d", MODULE_NAME, smfc->device_id);
+       }
+
+       mutex_init(&smfc->video_device_mutex);
+
+       smfc->videodev->fops            = &smfc_v4l2_fops;
+       smfc->videodev->ioctl_ops       = &smfc_v4l2_ioctl_ops;
+       smfc->videodev->release         = video_device_release;
+       smfc->videodev->lock            = &smfc->video_device_mutex;
+       smfc->videodev->vfl_dir         = VFL_DIR_M2M;
+       smfc->videodev->v4l2_dev        = &smfc->v4l2_dev;
+
+       video_set_drvdata(smfc->videodev, smfc);
+
+       smfc->m2mdev = v4l2_m2m_init(&smfc_m2m_ops);
+       if (IS_ERR(smfc->m2mdev)) {
+               ret = PTR_ERR(smfc->m2mdev);
+               dev_err(dev, "Failed(%d) to create v4l2_m2m_device\n", ret);
+               goto err_m2m_init;
+       }
+
+       /* TODO: promote the magic number 12 in public */
+       ret = video_register_device(smfc->videodev, VFL_TYPE_GRABBER, 12);
+       if (ret < 0) {
+               dev_err(dev, "Failed(%d) to register video_device[%d]\n",
+                       ret, 12);
+               goto err_register;
+       }
+
+       return 0;
+
+err_register:
+       v4l2_m2m_release(smfc->m2mdev);
+err_m2m_init:
+       video_device_release(smfc->videodev);
+err_videodev_alloc:
+       v4l2_device_unregister(&smfc->v4l2_dev);
+       return ret;
+}
 
 static int smfc_init_clock(struct device *dev, struct smfc_dev *smfc)
 {
@@ -137,6 +404,10 @@ static int exynos_smfc_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
+       ret = smfc_init_v4l2(&pdev->dev, smfc);
+       if (ret < 0)
+               return ret;
+
        platform_set_drvdata(pdev, smfc);
 
        dev_info(&pdev->dev, "Probed H/W Version: %02x.%02x.%04x\n",
@@ -203,7 +474,7 @@ static struct platform_driver exynos_smfc_driver = {
        .probe          = exynos_smfc_probe,
        .remove         = exynos_smfc_remove,
        .driver = {
-               .name   = "exynos-jpeg",
+               .name   = MODULE_NAME,
                .owner  = THIS_MODULE,
                .pm     = &exynos_smfc_pm_ops,
                .of_match_table = of_match_ptr(exynos_smfc_match),
index f2ff37888d5de85b05b2a96edbef8b7a695b83b4..a7ea8fb7dfbc9bc05b4a221ce48f96c1c8c127a9 100644 (file)
 #ifndef _MEDIA_EXYNOS_SMFC_H_
 #define _MEDIA_EXYNOS_SMFC_H_
 
+#define MODULE_NAME    "exynos-jpeg"
+
 struct device;
+struct video_device;
 
 struct smfc_dev {
+       struct v4l2_device v4l2_dev;
+       struct video_device *videodev;
+       struct v4l2_m2m_dev *m2mdev;
        struct device *dev;
        void __iomem *reg;
+       struct mutex video_device_mutex;
        int device_id;
        u32 hwver;
 
@@ -24,4 +31,15 @@ struct smfc_dev {
        struct clk *clk_gate2; /* available if clk_gate is valid */
 };
 
+struct smfc_ctx {
+       struct v4l2_fh v4l2_fh;
+       struct smfc_dev *smfc;
+       struct v4l2_m2m_ctx *m2mctx;
+};
+
+static inline struct smfc_ctx *v4l2_fh_to_smfc_ctx(struct v4l2_fh *fh)
+{
+       return container_of(fh, struct smfc_ctx, v4l2_fh);
+}
+
 #endif /* _MEDIA_EXYNOS_SMFC_H_ */