[COMMON] media: smfc: add g/try/s_fmt V4L2 functions
authorCho KyongHo <pullip.cho@samsung.com>
Fri, 27 Mar 2015 01:08:45 +0000 (10:08 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:30:49 +0000 (14:30 +0900)
Change-Id: If8768fa8ef4f5ffa4318260545c2bfcea35fc296
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/media/platform/exynos/smfc/smfc-regs.h
drivers/media/platform/exynos/smfc/smfc.c
drivers/media/platform/exynos/smfc/smfc.h

index ac3af77b307f2f0ea6b85cd46bf89b62d7f05289..03e8982b5b8014387be426189373361cf50d2b2e 100644 (file)
 #ifndef _MEDIA_EXYNOS_SMFC_REGS_H_
 #define _MEDIA_EXYNOS_SMFC_REGS_H_
 
+/********** H/W RESTRICTIONS **************************************************/
+#define SMFC_MAX_WIDTH 16368U
+#define SMFC_MAX_HEIGHT        16368U
+#define SMFC_MIN_WIDTH 8U
+#define SMFC_MIN_HEIGHT        8U
+
 #define REG_MAIN_IMAGE_FORMAT          0x040
 #define REG_SECD_IMAGE_FORMAT          0x0B8
 
index 16486923927cc0370c7eaa41e47f4ce3304b9ca7..f00fd147e8cd5f18adb4da14bddc99687aedd626 100644 (file)
@@ -45,6 +45,9 @@
 /* set if H/W does not have 128-bit alignment constraint for image base */
 #define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN                0x4000
 
+#define SMFC_DEFAULT_OUTPUT_FORMAT     (&smfc_image_formats[1])
+#define SMFC_DEFAULT_CAPTURE_FORMAT    (&smfc_image_formats[0])
+
 const struct smfc_image_format smfc_image_formats[] = {
        {
                /* JPEG should be the first format */
@@ -249,6 +252,26 @@ const struct smfc_image_format smfc_image_formats[] = {
        }
 };
 
+static const struct smfc_image_format *smfc_find_format(
+                               struct smfc_dev *smfc, __u32 v4l2_pixfmt)
+{
+       size_t i;
+       for (i = 0; i < ARRAY_SIZE(smfc_image_formats); i++)
+               if (smfc_image_formats[i].v4l2_pixfmt == v4l2_pixfmt)
+                       return &smfc_image_formats[i];
+
+       dev_warn(smfc->dev, "Pixel format '%08X' not found, YUYV is forced.\n",
+               v4l2_pixfmt);
+
+       return V4L2_TYPE_IS_OUTPUT(v4l2_pixfmt) ? SMFC_DEFAULT_OUTPUT_FORMAT
+                                               : SMFC_DEFAULT_CAPTURE_FORMAT;
+}
+
+static const char *buf_type_name(__u32 type)
+{
+       return V4L2_TYPE_IS_OUTPUT(type) ? "capture" : "output";
+}
+
 static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
 {
        return IRQ_HANDLED;
@@ -261,11 +284,23 @@ static int smfc_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
        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;
+       if (smfc_is_compressed_type(ctx, vq->type)) {
+               /*
+                * SMFC is able to stop compression if the target buffer is not
+                * enough. Therefore, it is not required to configure larger
+                * buffer for compression.
+                */
+               sizes[0] = PAGE_SIZE;
+               *num_planes = 1;
                alloc_devs[i] = ctx->smfc->dev;
+       } else {
+               unsigned int i;
+               *num_planes = ctx->img_fmt->num_buffers;
+               for (i = 0; i < *num_planes; i++) {
+                       sizes[i] = ctx->width * ctx->height;
+                       sizes[i] = (sizes[i] * ctx->img_fmt->bpp_buf[i]) / 8;
+                       alloc_devs[i] = ctx->smfc->dev;
+               }
        }
 
        return 0;
@@ -377,6 +412,19 @@ static int exynos_smfc_open(struct file *filp)
                }
        }
 
+       /*
+        * default mode: compression
+        * default image format: YUYV
+        * default size: 16x8
+        * default chroma subsampling for JPEG: YUV422
+        */
+       ctx->img_fmt = SMFC_DEFAULT_OUTPUT_FORMAT;
+       ctx->width = SMFC_MIN_WIDTH << ctx->img_fmt->chroma_hfactor;
+       ctx->height = SMFC_MIN_HEIGHT << ctx->img_fmt->chroma_vfactor;
+       ctx->chroma_hfactor = ctx->img_fmt->chroma_hfactor;
+       ctx->chroma_vfactor = ctx->img_fmt->chroma_vfactor;
+       ctx->flags |= SMFC_CTX_COMPRESS;
+
        ctx->smfc = smfc;
 
        return 0;
@@ -485,12 +533,234 @@ static int smfc_v4l2_enum_fmt(struct file *filp, void *fh,
        return smfc_image_formats[f->index].num_buffers > 1 ? -EINVAL : 0;
 }
 
+static int smfc_v4l2_g_fmt_mplane(struct file *filp, void *fh,
+                                 struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+       f->fmt.pix_mp.width = ctx->width;
+       f->fmt.pix_mp.height = ctx->height;
+
+       if (!smfc_is_compressed_type(ctx, f->type)) {
+               int i;
+               /* uncompressed image */
+               f->fmt.pix_mp.pixelformat = ctx->img_fmt->v4l2_pixfmt;
+               f->fmt.pix_mp.num_planes = ctx->img_fmt->num_buffers;
+               for (i = 0; i < ctx->img_fmt->num_buffers; i++) {
+                       f->fmt.pix_mp.plane_fmt[i].bytesperline =
+                               (f->fmt.pix_mp.width *
+                                        ctx->img_fmt->bpp_buf[i]) / 8;
+                       f->fmt.pix_mp.plane_fmt[i].sizeimage =
+                                       f->fmt.pix_mp.plane_fmt[i].bytesperline;
+                       f->fmt.pix_mp.plane_fmt[i].sizeimage *=
+                                                       f->fmt.pix_mp.height;
+               }
+       } else {
+               f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG;
+               f->fmt.pix_mp.num_planes = 1;
+               f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+               f->fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+       }
+
+       f->fmt.pix_mp.field = 0;
+       f->fmt.pix_mp.colorspace = 0;
+
+       return 0;
+}
+
+static int smfc_v4l2_g_fmt(struct file *filp, void *fh, struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+
+       f->fmt.pix.width = ctx->width;
+       f->fmt.pix.height = ctx->height;
+
+       if (!smfc_is_compressed_type(ctx, f->type)) {
+               if (ctx->img_fmt->num_buffers > 1) {
+                       dev_err(ctx->smfc->dev,
+                               "Current format is a multi-planar format\n");
+                       return -EINVAL;
+               }
+
+               /* uncompressed image */
+               f->fmt.pix.pixelformat = ctx->img_fmt->v4l2_pixfmt;
+               f->fmt.pix.bytesperline =
+                       (f->fmt.pix.width * ctx->img_fmt->bpp_buf[0]) / 8;
+               f->fmt.pix.sizeimage = f->fmt.pix.bytesperline;
+               f->fmt.pix.sizeimage *= f->fmt.pix.height;
+       } else {
+               f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
+               f->fmt.pix.bytesperline = 0;
+               f->fmt.pix.sizeimage = 0;
+       }
+
+       f->fmt.pix.field = 0;
+       f->fmt.pix.colorspace = 0;
+
+       return 0;
+}
+
+static bool smfc_check_image_size(struct device *dev, __u32 type,
+                                 const struct smfc_image_format *smfc_fmt,
+                                 __u32 width, __u32 height)
+{
+       __u32 min_width = SMFC_MIN_WIDTH << smfc_fmt->chroma_hfactor;
+       __u32 min_height = SMFC_MIN_WIDTH << smfc_fmt->chroma_vfactor;
+
+       if ((width < min_width) || (height < min_height)) {
+               dev_warn(dev, "Too small image size(%ux%u) for '%s'\n",
+                               width, height, buf_type_name(type));
+               return false;
+       }
+
+       if ((width > SMFC_MAX_WIDTH) || (height > SMFC_MAX_HEIGHT)) {
+               dev_warn(dev, "Too large image size(%ux%u) for '%s'\n",
+                               width, height, buf_type_name(type));
+               return false;
+       }
+
+       if (((width % smfc_fmt->chroma_hfactor) != 0) ||
+               ((height % smfc_fmt->chroma_vfactor) != 0)) {
+               dev_err(dev, "Invalid size %ux%u for format '%s'\n",
+                       width, height, smfc_fmt->description);
+               return false;
+       }
+
+       return true;
+}
+
+static bool smfc_v4l2_init_fmt_mplane(const struct smfc_ctx *ctx,
+                       const struct smfc_image_format *smfc_fmt,
+                       __u32 type, struct v4l2_pix_format_mplane *pix_mp)
+{
+       unsigned int i;
+
+       if (!smfc_check_image_size(ctx->smfc->dev, type,
+                               smfc_fmt, pix_mp->width, pix_mp->height))
+               return false;
+       /* informs the user that the format might be changed to the default */
+       pix_mp->pixelformat = smfc_fmt->v4l2_pixfmt;
+       /* JPEG format has zero in smfc_fmt->bpp_buf[0] */
+       for (i = 0; i < smfc_fmt->num_buffers; i++) {
+               pix_mp->plane_fmt[i].bytesperline =
+                               (pix_mp->width * smfc_fmt->bpp_buf[i]) / 8;
+               pix_mp->plane_fmt[i].sizeimage =
+                               pix_mp->plane_fmt[i].bytesperline;
+               pix_mp->plane_fmt[i].sizeimage *= pix_mp->height;
+       }
+
+       pix_mp->field = 0;
+       pix_mp->num_planes = smfc_fmt->num_buffers;
+
+       return true;
+}
+
+static int smfc_v4l2_try_fmt_mplane(struct file *filp, void *fh,
+                                 struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+       const struct smfc_image_format *smfc_fmt =
+                       smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat);
+
+       return smfc_v4l2_init_fmt_mplane(
+                       ctx, smfc_fmt, f->type, &f->fmt.pix_mp) ? 0 : -EINVAL;
+}
+
+static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh,
+                                   struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+       const struct smfc_image_format *smfc_fmt =
+                       smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat);
+
+       if (!smfc_v4l2_init_fmt_mplane(ctx, smfc_fmt, f->type, &f->fmt.pix_mp))
+               return -EINVAL;
+
+       ctx->width = f->fmt.pix_mp.width;
+       ctx->height = f->fmt.pix_mp.height;
+
+       if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_JPEG)
+               ctx->img_fmt = smfc_fmt;
+
+       smfc_config_ctxflag(ctx, SMFC_CTX_COMPRESS,
+                       is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(f->type));
+
+       /* reset the buf type */
+       v4l2_m2m_get_vq(ctx->m2mctx, f->type)->type = f->type;
+
+       return 0;
+}
+
+static bool smfc_v4l2_init_fmt(const struct smfc_ctx *ctx,
+                               const struct smfc_image_format *smfc_fmt,
+                               __u32 type, struct v4l2_pix_format *pix)
+{
+       BUG_ON(V4L2_TYPE_IS_MULTIPLANAR(pix->pixelformat));
+
+       if (!smfc_check_image_size(ctx->smfc->dev, type,
+                               smfc_fmt, pix->width, pix->height))
+               return false;
+       /* informs the user that the format might be changed to the default */
+       pix->pixelformat = smfc_fmt->v4l2_pixfmt;
+       /* JPEG format has zero in smfc_fmt->bpp_buf[0] */
+       pix->bytesperline = (pix->width *  smfc_fmt->bpp_buf[0]) / 8;
+       pix->sizeimage = pix->bytesperline * pix->height;
+       pix->field = 0;
+
+       return true;
+}
+
+static int smfc_v4l2_try_fmt(struct file *filp, void *fh, struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+       const struct smfc_image_format *smfc_fmt =
+                       smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat);
+
+       return smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix)
+                                                               ? 0 : -EINVAL;
+}
+
+static int smfc_v4l2_s_fmt(struct file *filp, void *fh, struct v4l2_format *f)
+{
+       struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
+       const struct smfc_image_format *smfc_fmt =
+                       smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat);
+
+       if (!smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix))
+               return -EINVAL;
+
+       ctx->width = f->fmt.pix.width;
+       ctx->height = f->fmt.pix.height;
+
+       if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+               ctx->img_fmt = smfc_fmt;
+
+       smfc_config_ctxflag(ctx, SMFC_CTX_COMPRESS,
+                       is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(f->type));
+
+       /* reset the buf type */
+       v4l2_m2m_get_vq(ctx->m2mctx, f->type)->type = f->type;
+
+       return 0;
+}
+
 static const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops = {
        .vidioc_querycap                = smfc_v4l2_querycap,
        .vidioc_enum_fmt_vid_cap        = smfc_v4l2_enum_fmt,
        .vidioc_enum_fmt_vid_out        = smfc_v4l2_enum_fmt,
        .vidioc_enum_fmt_vid_cap_mplane = smfc_v4l2_enum_fmt_mplane,
        .vidioc_enum_fmt_vid_out_mplane = smfc_v4l2_enum_fmt_mplane,
+       .vidioc_g_fmt_vid_cap           = smfc_v4l2_g_fmt,
+       .vidioc_g_fmt_vid_out           = smfc_v4l2_g_fmt,
+       .vidioc_g_fmt_vid_cap_mplane    = smfc_v4l2_g_fmt_mplane,
+       .vidioc_g_fmt_vid_out_mplane    = smfc_v4l2_g_fmt_mplane,
+       .vidioc_try_fmt_vid_cap         = smfc_v4l2_try_fmt,
+       .vidioc_try_fmt_vid_out         = smfc_v4l2_try_fmt,
+       .vidioc_try_fmt_vid_cap_mplane  = smfc_v4l2_try_fmt_mplane,
+       .vidioc_try_fmt_vid_out_mplane  = smfc_v4l2_try_fmt_mplane,
+       .vidioc_s_fmt_vid_cap           = smfc_v4l2_s_fmt,
+       .vidioc_s_fmt_vid_out           = smfc_v4l2_s_fmt,
+       .vidioc_s_fmt_vid_cap_mplane    = smfc_v4l2_s_fmt_mplane,
+       .vidioc_s_fmt_vid_out_mplane    = smfc_v4l2_s_fmt_mplane,
 };
 
 static void smfc_m2m_device_run(void *priv)
index cdc0df0a00e4bfdf8419e433f0697d36914dbd84..56a1366e856626dfe053fd1e8dce93f5151e361d 100644 (file)
@@ -29,6 +29,11 @@ struct smfc_image_format {
        unsigned char   chroma_vfactor;
 };
 
+static inline bool is_jpeg(const struct smfc_image_format *fmt)
+{
+       return fmt->bpp_buf[0] == 0;
+}
+
 struct smfc_dev {
        struct v4l2_device v4l2_dev;
        struct video_device *videodev;
@@ -43,10 +48,20 @@ struct smfc_dev {
        struct clk *clk_gate2; /* available if clk_gate is valid */
 };
 
+#define SMFC_CTX_COMPRESS      (1 << 0)
+
 struct smfc_ctx {
        struct v4l2_fh v4l2_fh;
        struct smfc_dev *smfc;
        struct v4l2_m2m_ctx *m2mctx;
+       u32 flags;
+       /* uncomressed image description */
+       const struct smfc_image_format *img_fmt;
+       __u32 width;
+       __u32 height;
+       /* JPEG chroma subsampling factors */
+       unsigned char chroma_hfactor;
+       unsigned char chroma_vfactor;
 };
 
 static inline struct smfc_ctx *v4l2_fh_to_smfc_ctx(struct v4l2_fh *fh)
@@ -54,4 +69,15 @@ static inline struct smfc_ctx *v4l2_fh_to_smfc_ctx(struct v4l2_fh *fh)
        return container_of(fh, struct smfc_ctx, v4l2_fh);
 }
 
+static inline void smfc_config_ctxflag(struct smfc_ctx *ctx,
+                                      u32 flag, bool set)
+{
+       ctx->flags = set ? ctx->flags | flag : ctx->flags & ~flag;
+}
+
+static inline bool smfc_is_compressed_type(struct smfc_ctx *ctx, __u32 type)
+{
+       return !(ctx->flags & SMFC_CTX_COMPRESS) == V4L2_TYPE_IS_OUTPUT(type);
+}
+
 #endif /* _MEDIA_EXYNOS_SMFC_H_ */