From 0600b2783e71dd67d85400db04a415e2e8e650b5 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Wed, 25 Mar 2015 09:03:25 +0900 Subject: [PATCH] [COMMON] media: smfc: add skelton implementation of v4l2. 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 --- drivers/media/platform/exynos/smfc/smfc.c | 273 +++++++++++++++++++++- drivers/media/platform/exynos/smfc/smfc.h | 18 ++ 2 files changed, 290 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos/smfc/smfc.c b/drivers/media/platform/exynos/smfc/smfc.c index 7e50a8669617..49fd0c184170 100644 --- a/drivers/media/platform/exynos/smfc/smfc.c +++ b/drivers/media/platform/exynos/smfc/smfc.c @@ -18,6 +18,13 @@ #include #include #include +#include + +#include +#include +#include +#include +#include #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), diff --git a/drivers/media/platform/exynos/smfc/smfc.h b/drivers/media/platform/exynos/smfc/smfc.h index f2ff37888d5d..a7ea8fb7dfbc 100644 --- a/drivers/media/platform/exynos/smfc/smfc.h +++ b/drivers/media/platform/exynos/smfc/smfc.h @@ -12,11 +12,18 @@ #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_ */ -- 2.20.1