aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c')
-rw-r--r--drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c b/drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c
new file mode 100644
index 0000000..98dba01
--- /dev/null
+++ b/drivers/media/video/samsung/jpeg_v2x/jpeg_enc.c
@@ -0,0 +1,572 @@
+/* linux/drivers/media/video/samsung/jpeg_v2x/jpeg_dev.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Core file for Samsung Jpeg v2.x Interface driver
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/semaphore.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include <asm/page.h>
+
+#include <plat/regs_jpeg_v2_x.h>
+#include <mach/irqs.h>
+
+#include <media/v4l2-ioctl.h>
+
+#include "jpeg_core.h"
+#include "jpeg_dev.h"
+
+#include "jpeg_mem.h"
+#include "jpeg_regs.h"
+
+static struct jpeg_fmt formats[] = {
+ {
+ .name = "JPEG compressed format",
+ .fourcc = V4L2_PIX_FMT_JPEG_444,
+ .depth = {8},
+ .color = JPEG_444,
+ .memplanes = 1,
+ .types = M2M_CAPTURE,
+ }, {
+ .name = "JPEG compressed format",
+ .fourcc = V4L2_PIX_FMT_JPEG_422,
+ .depth = {8},
+ .color = JPEG_422,
+ .memplanes = 1,
+ .types = M2M_CAPTURE,
+ }, {
+ .name = "JPEG compressed format",
+ .fourcc = V4L2_PIX_FMT_JPEG_420,
+ .depth = {8},
+ .color = JPEG_420,
+ .memplanes = 1,
+ .types = M2M_CAPTURE,
+ }, {
+ .name = "JPEG compressed format",
+ .fourcc = V4L2_PIX_FMT_JPEG_GRAY,
+ .depth = {8},
+ .color = JPEG_GRAY,
+ .memplanes = 1,
+ .types = M2M_CAPTURE,
+ }, {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = {16},
+ .color = RGB_565,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:4:4 packed, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_YUV444_2P,
+ .depth = {8, 16},
+ .color = YCBCR_444_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:4:4 packed, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_YVU444_2P,
+ .depth = {8, 16},
+ .color = YCRCB_444_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:4:4 packed, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV444_3P,
+ .depth = {8, 8, 8},
+ .color = YCBCR_444_3P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "XRGB-8-8-8-8, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = {32},
+ .color = RGB_888,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = {16},
+ .color = YCRYCB_422_1P,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = {16},
+ .color = YCBYCR_422_1P,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = {8, 8},
+ .color = YCRCB_422_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = {8, 8},
+ .color = YCBCR_422_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = {8, 4},
+ .color = YCBCR_420_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .depth = {8, 4},
+ .color = YCRCB_420_2P,
+ .memplanes = 2,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = {8, 2, 2},
+ .color = YCBCR_420_3P,
+ .memplanes = 3,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:0 contiguous 3-planar, Y/Cr/Cb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = {8, 2, 2},
+ .color = YCRCB_420_3P,
+ .memplanes = 3,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "Gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = {8},
+ .color = GRAY,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ },
+#ifdef CONFIG_JPEG_V2_2
+ {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = {16},
+ .color = CRYCBY_422_1P,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = {16},
+ .color = CRYCBY_422_1P,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ }, {
+ .name = "XBGR-8-8-8-8, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = {32},
+ .color = BGR_888,
+ .memplanes = 1,
+ .types = M2M_OUTPUT,
+ },
+#endif
+};
+
+static struct jpeg_fmt *find_format(struct v4l2_format *f)
+{
+ struct jpeg_fmt *fmt;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ fmt = &formats[i];
+ if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
+ break;
+ }
+
+ return (i == ARRAY_SIZE(formats)) ? NULL : fmt;
+}
+
+static int jpeg_enc_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct jpeg_ctx *ctx = file->private_data;
+ struct jpeg_dev *dev = ctx->dev;
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ return 0;
+}
+
+int jpeg_enc_vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct jpeg_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+int jpeg_enc_vidioc_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct jpeg_ctx *ctx = priv;
+ struct v4l2_pix_format_mplane *pixm;
+ struct jpeg_enc_param *enc_param = &ctx->param.enc_param;
+
+ pixm = &f->fmt.pix_mp;
+
+ pixm->field = V4L2_FIELD_NONE;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ pixm->pixelformat =
+ enc_param->in_fmt;
+ pixm->num_planes =
+ enc_param->in_plane;
+ pixm->width =
+ enc_param->in_width;
+ pixm->height =
+ enc_param->in_height;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pixm->pixelformat =
+ enc_param->out_fmt;
+ pixm->num_planes =
+ enc_param->out_plane;
+ pixm->width =
+ enc_param->out_width;
+ pixm->height =
+ enc_param->out_height;
+ } else {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", f->type);
+ }
+
+ return 0;
+}
+
+static int jpeg_enc_vidioc_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct jpeg_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct jpeg_ctx *ctx = priv;
+ int i;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ fmt = find_format(f);
+
+ if (!fmt) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != pix->field)
+ return -EINVAL;
+
+
+ pix->num_planes = fmt->memplanes;
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ int bpl = pix->plane_fmt[i].bytesperline;
+
+ jpeg_dbg("[%d] bpl: %d, depth: %d, w: %d, h: %d",
+ i, bpl, fmt->depth[i], pix->width, pix->height);
+
+ if (!bpl || (bpl * 8 / fmt->depth[i]) > pix->width)
+ bpl = (pix->width * fmt->depth[i]) >> 3;
+
+ if (!pix->plane_fmt[i].sizeimage)
+ pix->plane_fmt[i].sizeimage = pix->height * bpl;
+
+ pix->plane_fmt[i].bytesperline = bpl;
+
+ jpeg_dbg("[%d]: bpl: %d, sizeimage: %d",
+ i, pix->plane_fmt[i].bytesperline,
+ pix->plane_fmt[i].sizeimage);
+ }
+
+ if (f->fmt.pix.height > MAX_JPEG_HEIGHT)
+ f->fmt.pix.height = MAX_JPEG_HEIGHT;
+
+ if (f->fmt.pix.width > MAX_JPEG_WIDTH)
+ f->fmt.pix.width = MAX_JPEG_WIDTH;
+
+ return 0;
+}
+
+static int jpeg_enc_vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct jpeg_ctx *ctx = priv;
+ struct vb2_queue *vq;
+ struct v4l2_pix_format_mplane *pix;
+ struct jpeg_fmt *fmt;
+ int ret;
+ int i;
+
+ ret = jpeg_enc_vidioc_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "queue (%d) busy\n", f->type);
+ return -EBUSY;
+ }
+
+ pix = &f->fmt.pix_mp;
+ fmt = find_format(f);
+
+ for (i = 0; i < fmt->memplanes; i++)
+ ctx->payload[i] =
+ pix->plane_fmt[i].bytesperline * pix->height;
+
+ ctx->param.enc_param.out_width = pix->height;
+ ctx->param.enc_param.out_height = pix->width;
+ ctx->param.enc_param.out_plane = fmt->memplanes;
+ ctx->param.enc_param.out_depth = fmt->depth[0];
+ ctx->param.enc_param.out_fmt = fmt->color;
+
+ return 0;
+}
+
+static int jpeg_enc_vidioc_s_fmt_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct jpeg_ctx *ctx = priv;
+ struct vb2_queue *vq;
+ struct v4l2_pix_format_mplane *pix;
+ struct jpeg_fmt *fmt;
+ int ret;
+ int i;
+
+ ret = jpeg_enc_vidioc_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "queue (%d) busy\n", f->type);
+ return -EBUSY;
+ }
+
+ /* TODO: width & height has to be multiple of two */
+ pix = &f->fmt.pix_mp;
+ fmt = find_format(f);
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ ctx->payload[i] =
+ pix->plane_fmt[i].bytesperline * pix->height;
+ ctx->param.enc_param.in_depth[i] = fmt->depth[i];
+ }
+ ctx->param.enc_param.in_width = pix->width;
+ ctx->param.enc_param.in_height = pix->height;
+ ctx->param.enc_param.in_plane = fmt->memplanes;
+ ctx->param.enc_param.in_fmt = fmt->color;
+
+ return 0;
+}
+
+static int jpeg_enc_m2m_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct jpeg_ctx *ctx = priv;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, reqbufs->type);
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ ctx->dev->vb2->set_cacheable(ctx->dev->alloc_ctx, ctx->input_cacheable);
+ else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ctx->dev->vb2->set_cacheable(ctx->dev->alloc_ctx, ctx->output_cacheable);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int jpeg_enc_m2m_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct jpeg_ctx *ctx = priv;
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int jpeg_enc_m2m_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct jpeg_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int jpeg_enc_m2m_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct jpeg_ctx *ctx = priv;
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int jpeg_enc_m2m_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct jpeg_ctx *ctx = priv;
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int jpeg_enc_m2m_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct jpeg_ctx *ctx = priv;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int jpeg_enc_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct jpeg_ctx *ctx = priv;
+ struct jpeg_dev *dev = ctx->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CAM_JPEG_ENCODEDSIZE:
+ ctrl->value = jpeg_get_stream_size(dev->reg_base);
+ break;
+ default:
+ break;
+ }
+ return ctrl->value;
+}
+
+static int vidioc_enc_s_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jpegcomp)
+{
+ struct jpeg_ctx *ctx = priv;
+
+ ctx->param.enc_param.quality = jpegcomp->quality;
+ return 0;
+}
+
+static int vidioc_enc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jpegcomp)
+{
+ struct jpeg_ctx *ctx = priv;
+
+ jpegcomp->quality = ctx->param.enc_param.quality;
+ return 0;
+}
+
+static int jpeg_enc_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct jpeg_ctx *ctx = priv;
+/*
+* 0 : input/output noncacheable
+* 1 : input/output cacheable
+* 2 : input cacheable / output noncacheable
+* 3 : input noncacheable / output cacheable
+*/
+ switch (ctrl->id) {
+ case V4L2_CID_CACHEABLE:
+ if (ctrl->value == 0) {
+ ctx->input_cacheable = 0;
+ ctx->output_cacheable = 0;
+ } else if (ctrl->value == 1) {
+ ctx->input_cacheable = 1;
+ ctx->output_cacheable = 1;
+ } else if (ctrl->value == 2) {
+ ctx->input_cacheable = 1;
+ ctx->output_cacheable = 0;
+ } else if (ctrl->value == 3) {
+ ctx->input_cacheable = 0;
+ ctx->output_cacheable = 1;
+ } else {
+ ctx->input_cacheable = 0;
+ ctx->output_cacheable = 0;
+ }
+ break;
+ default:
+ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops jpeg_enc_ioctl_ops = {
+ .vidioc_querycap = jpeg_enc_vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap_mplane = jpeg_enc_vidioc_enum_fmt,
+ .vidioc_enum_fmt_vid_out_mplane = jpeg_enc_vidioc_enum_fmt,
+
+ .vidioc_g_fmt_vid_cap_mplane = jpeg_enc_vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = jpeg_enc_vidioc_g_fmt,
+
+ .vidioc_try_fmt_vid_cap_mplane = jpeg_enc_vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = jpeg_enc_vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = jpeg_enc_vidioc_s_fmt_cap,
+ .vidioc_s_fmt_vid_out_mplane = jpeg_enc_vidioc_s_fmt_out,
+
+ .vidioc_reqbufs = jpeg_enc_m2m_reqbufs,
+ .vidioc_querybuf = jpeg_enc_m2m_querybuf,
+ .vidioc_qbuf = jpeg_enc_m2m_qbuf,
+ .vidioc_dqbuf = jpeg_enc_m2m_dqbuf,
+ .vidioc_streamon = jpeg_enc_m2m_streamon,
+ .vidioc_streamoff = jpeg_enc_m2m_streamoff,
+ .vidioc_g_ctrl = jpeg_enc_vidioc_g_ctrl,
+ .vidioc_g_jpegcomp = vidioc_enc_g_jpegcomp,
+ .vidioc_s_jpegcomp = vidioc_enc_s_jpegcomp,
+ .vidioc_s_ctrl = jpeg_enc_vidioc_s_ctrl,
+};
+const struct v4l2_ioctl_ops *get_jpeg_enc_v4l2_ioctl_ops(void)
+{
+ return &jpeg_enc_ioctl_ops;
+}