[COMMON] media: smfc: support for secondary image compression
authorCho KyongHo <pullip.cho@samsung.com>
Thu, 2 Apr 2015 07:07:11 +0000 (16:07 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:18 +0000 (20:22 +0300)
SMFC supports for back-to-back image compression. It compresses two
images at the same time. Even though SMFC, itself has a single JPEG
compression engine, the users including the driver of SMFC feels that
the two source images are compressed concurrently because it provides
two sets of source image configuration registers and the users receive
two compressed data when the 'frame done' interrupt is asserted.

V4L2 does not consider about the special function like back-to-back
(B2B) compression which requires a couple of source images and results
different output streams for the source images. Thus SMFC driver
requires userspaces to provide the secondary image to compress in a
special way.
 - For B2B, the output and capture buffer type should be multi-plane.
 - All the format and buffer information passed to the driver from the
   the userspace should be the information of the primary image.
 - To B2B compression, the userspace should configure
    * The width and the height of the secondary image should be
      configured in the higher 16-bits of the fields of width and
      height of 'struct v4l2_format'
    * The color format of the secondary image should be the same as
      the primary image. Therefore there is no way to configure
      the color format of the secondary image.
    * The quality factor of the secondary image should be configured
      with the V4L2 control ID 'V4L2_CID_JPEG_CLASS_BASE + 20'.
      The parameter is the same as V4L2_CID_JPEG_COMPRESSION_QUALITY.
    * The buffer information of the secondary image should be
      specified from v4l2_buffer.m.plane[num_planes of the 1st image].
      v4l2_buffer.length should be also doubled because the effective
      planes are doubled. Otherwise, the users get failure.

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

index fc3a5ccd60ac03360299d942575180f77d2c1d26..55af10e2e2e8341e64f507b7c8410cd1e3071caa 100644 (file)
 
 void smfc_hwconfigure_reset(struct smfc_dev *smfc)
 {
-       u32 cfg = __raw_readl(smfc->reg + REG_JPEG_CNTL) & ~((1 << 29) | 3);
-       __raw_writel(cfg, smfc->reg + REG_JPEG_CNTL);
-       __raw_writel(cfg | (1 << 29), smfc->reg + REG_JPEG_CNTL);
+       u32 cfg = __raw_readl(smfc->reg + REG_MAIN_JPEG_CNTL);
+       cfg &= ~((1 << 29) | 3);
+       __raw_writel(cfg, smfc->reg + REG_MAIN_JPEG_CNTL);
+       __raw_writel(cfg | (1 << 29), smfc->reg + REG_MAIN_JPEG_CNTL);
 }
 
 /*
@@ -122,6 +123,15 @@ static inline u32 smfc_calc_quantizers(unsigned int idx, unsigned int factor,
 }
 #pragma GCC diagnostic pop
 
+static void smfc_hwconfigure_qtable(void __iomem *reg, unsigned int factor,
+                                   const unsigned char table[])
+{
+       size_t i;
+
+       for (i = 0; i < SMFC_MCU_SIZE; i += 4)
+               __raw_writel(smfc_calc_quantizers(i, factor, table), reg + i);
+}
+
 void smfc_hwconfigure_tables(struct smfc_ctx *ctx)
 {
        size_t i;
@@ -129,14 +139,10 @@ void smfc_hwconfigure_tables(struct smfc_ctx *ctx)
        unsigned int factor = ctx->quality_factor;
        factor = (factor < 50) ? 5000 / factor : 200 - factor * 2;
 
-       /* Quantizer table 0 */
-       for (i = 0; i < SMFC_MCU_SIZE; i += 4) {
-               __raw_writel(smfc_calc_quantizers(i, factor, default_luma_qtbl),
-                       base + REG_QTBL_BASE + i);
-               __raw_writel(
-                       smfc_calc_quantizers(i, factor, default_chroma_qtbl),
-                       base + REG_QTBL_BASE + SMFC_MCU_SIZE + i);
-       }
+       smfc_hwconfigure_qtable(base + REG_QTBL_BASE,
+                               factor, default_luma_qtbl);
+       smfc_hwconfigure_qtable(base + REG_QTBL_BASE + SMFC_MCU_SIZE,
+                               factor, default_chroma_qtbl);
 
        /* Huffman tables */
        for (i = 0; i < 4; i++) {
@@ -162,46 +168,65 @@ void smfc_hwconfigure_tables(struct smfc_ctx *ctx)
                __raw_writel(ITU_H_TBL_VAL_AC_CHROMINANCE[i],
                                base + REG_HTBL_CHROMA_ACVAL + i * sizeof(u32));
 
-       __raw_writel(VAL_TABLE_SELECT, base + REG_TABLE_SELECT);
-       __raw_writel(SMFC_DHT_LEN, base + REG_DHT_LEN);
+       __raw_writel(VAL_MAIN_TABLE_SELECT, base + REG_MAIN_TABLE_SELECT);
+       __raw_writel(SMFC_DHT_LEN, base + REG_MAIN_DHT_LEN);
 }
 
+void smfc_hwconfigure_2nd_tables(struct smfc_ctx *ctx)
+{
+       /* Qunatiazation table 2 and 3 will be used by the secondary image */
+       void __iomem *base = ctx->smfc->reg;
+       void __iomem *qtblbase = base + REG_QTBL_BASE + SMFC_MCU_SIZE * 2;
+       unsigned int factor = ctx->thumb_quality_factor;
+       factor = (factor < 50) ? 5000 / factor : 200 - factor * 2;
+
+       smfc_hwconfigure_qtable(qtblbase, factor, default_luma_qtbl);
+       smfc_hwconfigure_qtable(qtblbase + SMFC_MCU_SIZE,
+                               factor, default_chroma_qtbl);
+       /* Huffman table for the secondary image is the same as the main image */
+       __raw_writel(VAL_SEC_TABLE_SELECT, base + REG_SEC_TABLE_SELECT);
+       __raw_writel(SMFC_DHT_LEN, base + REG_SEC_DHT_LEN);
+}
 
 static void smfc_hwconfigure_image_base(struct smfc_ctx *ctx,
-                                       struct vb2_buffer *vb2buf)
+                                       struct vb2_buffer *vb2buf,
+                                       bool thumbnail)
 {
        dma_addr_t addr;
        unsigned int i;
-       bool multiplane =
-               (ctx->img_fmt->num_buffers == ctx->img_fmt->num_planes);
+       unsigned int num_buffers = ctx->img_fmt->num_buffers;
+       bool multiplane = (num_buffers == ctx->img_fmt->num_planes);
+       u32 off = thumbnail ? REG_SEC_IMAGE_BASE : REG_MAIN_IMAGE_BASE;
 
        if (multiplane) {
                /* Note that this includes a single-plane format such as YUYV */
-               for (i = 0; i < ctx->img_fmt->num_buffers; i++) {
+               for (i = 0; i < num_buffers; i++) {
                        addr = vb2_dma_sg_plane_dma_addr(vb2buf, i);
                        __raw_writel((u32)addr,
-                               ctx->smfc->reg + REG_MAIN_IMAGE_BASE(i));
+                               ctx->smfc->reg + REG_IMAGE_BASE(off, i));
                }
        } else {
-               addr = vb2_dma_sg_plane_dma_addr(vb2buf, i);
+               u32 width = thumbnail ? ctx->thumb_width : ctx->width;
+               u32 height = thumbnail ? ctx->thumb_height : ctx->height;
+               addr = vb2_dma_sg_plane_dma_addr(vb2buf, thumbnail ? 1 : 0);
 
                for (i = 0; i < ctx->img_fmt->num_planes; i++) {
                        __raw_writel((u32)addr,
-                               ctx->smfc->reg + REG_MAIN_IMAGE_BASE(i));
-                       addr += (ctx->width * ctx->height *
-                                               ctx->img_fmt->bpp_pix[i]) / 8;
+                               ctx->smfc->reg + REG_IMAGE_BASE(off, i));
+                       addr += (width * height * ctx->img_fmt->bpp_pix[i]) / 8;
                }
        }
 }
 
 static u32 smfc_hwconfigure_jpeg_base(struct smfc_ctx *ctx,
-                                       struct vb2_buffer *vb2buf)
+                                       struct vb2_buffer *vb2buf,
+                                       bool thumbnail)
 {
        dma_addr_t addr;
+       u32 off = thumbnail ? REG_SEC_JPEG_BASE : REG_MAIN_JPEG_BASE;
 
-       BUG_ON(vb2buf->num_planes != 1);
-       addr = vb2_dma_sg_plane_dma_addr(vb2buf, 0);
-       __raw_writel((u32)addr, ctx->smfc->reg + REG_MAIN_JPEG_BASE);
+       addr = vb2_dma_sg_plane_dma_addr(vb2buf, thumbnail ? 1 : 0);
+       __raw_writel((u32)addr, ctx->smfc->reg + off);
        return (u32)addr;
 }
 
@@ -220,6 +245,35 @@ static u32 smfc_get_jpeg_format(unsigned int hfactor, unsigned int vfactor)
        return 2 << 24; /* default: YUV422 */
 }
 
+void smfc_hwconfigure_2nd_image(struct smfc_ctx *ctx)
+{
+       struct vb2_buffer *vb2buf_img, *vb2buf_jpg;
+       u32 format;
+
+       if (!(ctx->flags & SMFC_CTX_COMPRESS))
+               return;
+
+       __raw_writel(ctx->thumb_width | (ctx->thumb_height << 16),
+                       ctx->smfc->reg + REG_SEC_IMAGE_SIZE);
+
+       vb2buf_img = v4l2_m2m_next_src_buf(ctx->m2mctx);
+       vb2buf_jpg = v4l2_m2m_next_dst_buf(ctx->m2mctx);
+
+       smfc_hwconfigure_image_base(ctx, vb2buf_img, true);
+       /*
+        * secondary image stream base is not required because there is no
+        * MAX_COMPRESSED_SIZE register for the secondary image
+        */
+       smfc_hwconfigure_jpeg_base(ctx, vb2buf_jpg, true);
+
+       /*
+        * Chroma subsampling is always 1/2 for both of horizontal and vertical
+        * directions to reduce the compressed size of the secondary image
+        */
+       format = ctx->img_fmt->regcfg | smfc_get_jpeg_format(2, 2);
+       __raw_writel(format, ctx->smfc->reg + REG_SEC_IMAGE_FORMAT);
+}
+
 void smfc_hwconfigure_image(struct smfc_ctx *ctx)
 {
        struct vb2_v4l2_buffer *vb2buf_img, *vb2buf_jpg;
@@ -244,8 +298,9 @@ void smfc_hwconfigure_image(struct smfc_ctx *ctx)
                        max(ctx->chroma_vfactor, ctx->img_fmt->chroma_vfactor));
        }
 
-       smfc_hwconfigure_image_base(ctx, &vb2buf_img->vb2_buf);
-       stream_address = smfc_hwconfigure_jpeg_base(ctx, &vb2buf_jpg->vb2_buf);
+       smfc_hwconfigure_image_base(ctx, &vb2buf_img->vb2_buf, false);
+       stream_address = smfc_hwconfigure_jpeg_base(ctx, &vb2buf_jpg->vb2_buf,
+                                                   false);
 
        __raw_writel(format, ctx->smfc->reg + REG_MAIN_IMAGE_FORMAT);
        __raw_writel(vb2_plane_size(&vb2buf_jpg->vb2_buf, 0) -
@@ -258,24 +313,29 @@ void smfc_hwconfigure_start(struct smfc_ctx *ctx)
        u32 cfg;
        void __iomem *base = ctx->smfc->reg;
 
+       __raw_writel(!(ctx->flags & SMFC_CTX_B2B_COMPRESS) ? 0 : 1,
+                       base + REG_SEC_JPEG_CNTL);
+
        /* configure "Error max compressed size" interrupt */
        cfg = __raw_readl(base + REG_INT_EN);
        __raw_writel(cfg | (1 << 11), base + REG_INT_EN);
 
-       cfg = __raw_readl(base + REG_JPEG_CNTL) & ~3;
+       cfg = __raw_readl(base + REG_MAIN_JPEG_CNTL) & ~3;
        cfg |= !(ctx->flags & SMFC_CTX_COMPRESS) ? 1 : 2;
        cfg |= 1 << 19; /* update huffman table from SFR */
        cfg |= 1 << 28; /* enables interrupt */
        cfg |= 1 << 29; /* Release reset */
        if (ctx->restart_interval != 0)
                cfg |= (ctx->restart_interval << 3) | (1 << 2);
+       if (!!(ctx->flags & SMFC_CTX_B2B_COMPRESS))
+               cfg |= 1 << 31; /* back-to-back enable */
 
-       writel(cfg, base + REG_JPEG_CNTL);
+       writel(cfg, base + REG_MAIN_JPEG_CNTL);
 }
 
-bool smfc_hwstatus_okay(struct smfc_dev *smfc)
+bool smfc_hwstatus_okay(struct smfc_dev *smfc, struct smfc_ctx *ctx)
 {
-       u32 val = __raw_readl(smfc->reg + REG_INT_STATUS);
+       u32 val = __raw_readl(smfc->reg + REG_MAIN_INT_STATUS);
        if (!val) {
                dev_err(smfc->dev, "Interrupt with no status change\n");
                return false;
@@ -286,6 +346,20 @@ bool smfc_hwstatus_okay(struct smfc_dev *smfc)
                return false;
        }
 
+       if (!(ctx->flags & SMFC_CTX_B2B_COMPRESS))
+               return  true;
+       val = __raw_readl(smfc->reg + REG_SEC_INT_STATUS);
+       if (!val) {
+               dev_err(smfc->dev, "Secondary image is not completed\n");
+               return false;
+       }
+
+       if ((val & ~2)) {
+               dev_err(smfc->dev, "Error interrupt %#010x for the secondary\n",
+                       val);
+               return false;
+       }
+
        return true;
 }
 
@@ -300,18 +374,18 @@ void smfc_dump_registers(struct smfc_dev *smfc)
        print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 16, 4,
                        smfc->reg + 0x000, 0xD4, false);
        /* Reading quantization tables */
-       val = __raw_readl(smfc->reg + REG_TABLE_SELECT);
+       val = __raw_readl(smfc->reg + REG_MAIN_TABLE_SELECT);
        __raw_writel(val | SMFC_TABLE_READ_REQ_MASK,
-                       smfc->reg + REG_TABLE_SELECT);
+                       smfc->reg + REG_MAIN_TABLE_SELECT);
        for (val = 0; val < 512; val++) {
-               if (!!(__raw_readl(smfc->reg + REG_TABLE_SELECT)
+               if (!!(__raw_readl(smfc->reg + REG_MAIN_TABLE_SELECT)
                                                & SMFC_TABLE_READ_OK_MASK))
                        break;
                cpu_relax();
        }
 
        if ((val == 512) &&
-               !(__raw_readl(smfc->reg + REG_TABLE_SELECT)
+               !(__raw_readl(smfc->reg + REG_MAIN_TABLE_SELECT)
                                                & SMFC_TABLE_READ_OK_MASK)) {
                pr_info("** FAILED TO READ HUFFMAN and QUANTIZER TABLES **\n");
                return;
index 5ba44667142a7df95517f2b886fc5a1bc5c1898e..1167230c07de98d96ca279ec8d564ab9495165f8 100644 (file)
 #define SMFC_ADDR_ALIGN_MASK (16 - 1) /* 128-bit align */
 
 /********** H/W REGISTERS and DEFAULT VALUES **********************************/
-#define REG_JPEG_CNTL                  0x000
+#define REG_MAIN_JPEG_CNTL             0x000
 #define REG_INT_EN                     0x004
 #define REG_TIMER_COUNT                        0x008
-#define REG_INT_STATUS                 0x00C
+#define REG_MAIN_INT_STATUS            0x00C
 #define REG_MAIN_IMAGE_SIZE            0x014
 #define REG_MAIN_JPEG_BASE             0x010
-#define REG_MAIN_IMAGE_BASE(n)         (0x018 + (n) * 12)
+#define REG_MAIN_IMAGE_BASE            0x018
+#define REG_SEC_JPEG_CNTL              0x080
+#define REG_SEC_JPEG_BASE              0x088
+#define REG_SEC_IMAGE_BASE             0x090
+#define REG_SEC_IMAGE_FORMAT           0x0B8
+#define REG_SEC_IMAGE_SIZE             0x08C
+#define REG_SEC_INT_STATUS             0x084
+#define REG_IMAGE_BASE(off, n)         ((off) + (n) * 12)
 #define REG_MAIN_IMAGE_FORMAT          0x040
 #define REG_MAIN_STREAM_SIZE           0x044
+#define REG_SEC_STREAM_SIZE            0x0BC
 #define REG_MAIN_MAX_STREAM_SIZE       0x06C
 
 #define REG_IP_VERSION_NUMBER          0x064
 
-#define REG_SECD_IMAGE_FORMAT          0x0B8
-
-#define REG_TABLE_SELECT               0x03C
+#define REG_MAIN_TABLE_SELECT          0x03C
+#define REG_SEC_TABLE_SELECT           0x0B4
 /*
  * Component 0: Q-table 0, AC/DC table 0
  * Component 1 and 2: Q-table 1, AC/DC table 1
  */
-#define VAL_TABLE_SELECT               0xF14
+#define VAL_MAIN_TABLE_SELECT          0xF14
+/*
+ * Component 0: Q-table 2, AC/DC table 0
+ * Component 1 and 2: Q-table 3, AC/DC table 1
+ */
+#define VAL_SEC_TABLE_SELECT           0xF3E
 #define SMFC_TABLE_READ_REQ_MASK       (1 << 13)
 #define SMFC_TABLE_READ_OK_MASK                (1 << 12)
 
@@ -69,7 +81,8 @@
 #  define REG_HTBL_LUMA_ACVAL  (REG_HTBL_LUMA_ACLEN + 4 * sizeof(u32))
 #  define REG_HTBL_CHROMA_ACLEN        (REG_HTBL_LUMA_ACVAL + 44 * sizeof(u32))
 #  define REG_HTBL_CHROMA_ACVAL        (REG_HTBL_CHROMA_ACLEN + 4 * sizeof(u32))
-#define REG_DHT_LEN                    0x04C
+#define REG_MAIN_DHT_LEN               0x04C
+#define REG_SEC_DHT_LEN                        0x0C4
 #define SMFC_DHT_LEN                   0x1A2
 
 /* Value definitions of MAIN/SECONDARY_IMAGE_FORMAT */
index 16aabcdadc9775a030c28d4f66554644ef0e58ca..579f67336994ee1bf0fee96278af97ee1e39d067 100644 (file)
 /* set if H/W does not have 128-bit alignment constraint for image base */
 #define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN                0x4000
 
+/* SMFC SPECIFIC CONTROLS */
+#define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20)
+
 #define SMFC_DEFAULT_OUTPUT_FORMAT     (&smfc_image_formats[1])
 #define SMFC_DEFAULT_CAPTURE_FORMAT    (&smfc_image_formats[0])
 
+#define SMFC_FMT_MAIN_SIZE(val) ((val) & 0xFFFF)
+#define SMFC_FMT_SEC_SIZE(val) (((val) >> 16) & 0xFFFF)
+
 const struct smfc_image_format smfc_image_formats[] = {
        {
                /* JPEG should be the first format */
@@ -275,8 +281,9 @@ static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
        struct smfc_ctx *ctx = v4l2_m2m_get_curr_priv(smfc->m2mdev);
        enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
        u32 streamsize = smfc_get_streamsize(smfc);
+       u32 thumb_streamsize = smfc_get_2nd_streamsize(smfc);
 
-       if (!smfc_hwstatus_okay(smfc)) {
+       if (!smfc_hwstatus_okay(smfc, ctx)) {
                smfc_dump_registers(smfc);
                state = VB2_BUF_STATE_ERROR;
                smfc_hwconfigure_reset(smfc);
@@ -295,7 +302,13 @@ static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv)
                struct vb2_v4l2_buffer *vb_capture =
                                v4l2_m2m_dst_buf_remove(ctx->m2mctx);
 
-               vb2_set_plane_payload(&vb_capture->vb2_buf, 0, streamsize);
+               if (!!(ctx->flags & SMFC_CTX_COMPRESS)) {
+                       vb2_set_plane_payload(&vb_capture->vb2_buf,
+                                             0, streamsize);
+                       if (!!(ctx->flags & SMFC_CTX_B2B_COMPRESS))
+                               vb2_set_plane_payload(&vb_capture->vb2_buf, 1,
+                                                       thumb_streamsize);
+               }
                v4l2_m2m_buf_done(v4l2_m2m_src_buf_remove(ctx->m2mctx), state);
                v4l2_m2m_buf_done(vb_capture, state);
                v4l2_m2m_job_finish(smfc->m2mdev, ctx->m2mctx);
@@ -322,6 +335,11 @@ static int smfc_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
                sizes[0] = PAGE_SIZE;
                *num_planes = 1;
                alloc_devs[i] = ctx->smfc->dev;
+               if (!!(ctx->flags & SMFC_CTX_B2B_COMPRESS)) {
+                       sizes[1] = PAGE_SIZE;
+                       alloc_devs[1] = ctx->smfc->dev;
+                       (*num_planes)++;
+               }
        } else {
                unsigned int i;
                *num_planes = ctx->img_fmt->num_buffers;
@@ -330,6 +348,20 @@ static int smfc_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
                        sizes[i] = (sizes[i] * ctx->img_fmt->bpp_buf[i]) / 8;
                        alloc_devs[i] = ctx->smfc->dev;
                }
+
+               if (!!(ctx->flags & SMFC_CTX_B2B_COMPRESS)) {
+                       unsigned int idx;
+                       for (i = 0; i < *num_planes; i++) {
+                               idx = *num_planes + i;
+                               sizes[idx] = ctx->thumb_width;
+                               sizes[idx] *= ctx->thumb_height;
+                               sizes[idx] *= ctx->img_fmt->bpp_buf[i];
+                               sizes[idx] /= 8;
+                               alloc_devs[idx] = ctx->smfc->dev;
+                       }
+
+                       *num_planes *= 2;
+               }
        }
 
        return 0;
@@ -433,6 +465,9 @@ static int smfc_s_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_JPEG_COMPRESSION_QUALITY:
                ctx->quality_factor = (unsigned char)ctrl->val;
                break;
+       case V4L2_CID_JPEG_SEC_COMP_QUALITY:
+               ctx->thumb_quality_factor = (unsigned char)ctrl->val;
+               break;
        case V4L2_CID_JPEG_RESTART_INTERVAL:
                ctx->restart_interval = (unsigned char)ctrl->val;
                break;
@@ -480,6 +515,7 @@ static int smfc_init_controls(struct smfc_dev *smfc,
                              struct v4l2_ctrl_handler *hdlr)
 {
        const char *msg;
+       struct v4l2_ctrl_config ctrlcfg;
 
        v4l2_ctrl_handler_init(hdlr, 1);
 
@@ -490,6 +526,20 @@ static int smfc_init_controls(struct smfc_dev *smfc,
                goto err;
        }
 
+       memset(&ctrlcfg, 0, sizeof(ctrlcfg));
+       ctrlcfg.ops = &smfc_ctrl_ops;
+       ctrlcfg.id = V4L2_CID_JPEG_SEC_COMP_QUALITY;
+       ctrlcfg.name = "Quality factor of secondray image";
+       ctrlcfg.type = V4L2_CTRL_TYPE_INTEGER;
+       ctrlcfg.min = 1;
+       ctrlcfg.max = 100;
+       ctrlcfg.step = 1;
+       ctrlcfg.def = 50;
+       if (!v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL)) {
+               msg = "secondary quality factor";
+               goto err;
+       }
+
        if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops,
                                V4L2_CID_JPEG_RESTART_INTERVAL,
                                0, 64, 1, 0)) {
@@ -559,8 +609,10 @@ static int exynos_smfc_open(struct file *filp)
         * default mode: compression
         * default image format: YUYV
         * default size: 16x8
+        * default thumbnail size: 0x0 (back2back comp. not enabled)
         * default chroma subsampling for JPEG: YUV422
         * default quality factor for compression: 96
+        * default quality factor of thumbnail for compression: 50
         */
        ctx->img_fmt = SMFC_DEFAULT_OUTPUT_FORMAT;
        ctx->width = SMFC_MIN_WIDTH << ctx->img_fmt->chroma_hfactor;
@@ -570,6 +622,7 @@ static int exynos_smfc_open(struct file *filp)
        ctx->flags |= SMFC_CTX_COMPRESS;
        ctx->quality_factor = 96;
        ctx->restart_interval = 0;
+       ctx->thumb_quality_factor = 50;
 
        ctx->smfc = smfc;
 
@@ -746,7 +799,7 @@ static int smfc_v4l2_g_fmt(struct file *filp, void *fh, struct v4l2_format *f)
        return 0;
 }
 
-static bool smfc_check_image_size(struct device *dev, __u32 type,
+static bool __smfc_check_image_size(struct device *dev, __u32 type,
                                  const struct smfc_image_format *smfc_fmt,
                                  __u32 width, __u32 height)
 {
@@ -774,6 +827,26 @@ static bool smfc_check_image_size(struct device *dev, __u32 type,
 
        return true;
 }
+static bool smfc_check_image_size(struct device *dev, __u32 type,
+                                 const struct smfc_image_format *smfc_fmt,
+                                 __u32 width, __u32 height)
+{
+       __u32 twidth = SMFC_FMT_SEC_SIZE(width);
+       __u32 theight = SMFC_FMT_SEC_SIZE(height);
+       width = SMFC_FMT_MAIN_SIZE(width);
+       height = SMFC_FMT_MAIN_SIZE(height);
+
+       if (!__smfc_check_image_size(dev, type, smfc_fmt, width, height))
+               return false;
+
+       if (!twidth || !theight) /* thumbnail image size is not specified */
+               return true;
+
+       if (!__smfc_check_image_size(dev, type, smfc_fmt, twidth, theight))
+               return false;
+
+       return true;
+}
 
 static bool smfc_v4l2_init_fmt_mplane(const struct smfc_ctx *ctx,
                        const struct smfc_image_format *smfc_fmt,
@@ -789,15 +862,36 @@ static bool smfc_v4l2_init_fmt_mplane(const struct smfc_ctx *ctx,
        /* 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;
+                               (SMFC_FMT_MAIN_SIZE(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->plane_fmt[i].bytesperline *
+                                       SMFC_FMT_MAIN_SIZE(pix_mp->height);
        }
 
        pix_mp->field = 0;
        pix_mp->num_planes = smfc_fmt->num_buffers;
 
+       if (SMFC_FMT_SEC_SIZE(pix_mp->width) &&
+                               SMFC_FMT_SEC_SIZE(pix_mp->height)) {
+               /*
+                * Format information for the secondary image is stored after
+                * the last plane of the main image. It is okay to use more
+                * planes because the maximum valid planes are eight
+                */
+               unsigned int j;
+               for (j = 0; j < smfc_fmt->num_buffers; j++) {
+                       pix_mp->plane_fmt[i + j].bytesperline =
+                               (SMFC_FMT_SEC_SIZE(pix_mp->width) *
+                                       smfc_fmt->bpp_buf[j]) / 8;
+                       pix_mp->plane_fmt[i + j].sizeimage =
+                               pix_mp->plane_fmt[i + j].bytesperline *
+                                       SMFC_FMT_SEC_SIZE(pix_mp->height);
+               }
+
+               pix_mp->num_planes *= 2;
+       }
+
        return true;
 }
 
@@ -848,7 +942,11 @@ static int smfc_v4l2_check_s_fmt(struct smfc_ctx *ctx,
        return 0;
 }
 
-
+static __u32 v4l2_to_multiplane_type(__u32 type)
+{
+       /* V4L2_BUF_TYPE_VIDEO_CAPTURE+8 = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
+       return V4L2_TYPE_IS_MULTIPLANAR(type) ? type : type + 8;
+}
 
 static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh,
                                    struct v4l2_format *f)
@@ -863,12 +961,31 @@ static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh,
        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;
+       ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.width);
+       ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.height);
+       ctx->thumb_width = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.width);
+       ctx->thumb_height = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.height);
+
+       if (ctx->thumb_width && ctx->thumb_height)
+               ctx->flags |= SMFC_CTX_B2B_COMPRESS;
+       else
+               ctx->flags &= ~SMFC_CTX_B2B_COMPRESS;
 
        if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_JPEG)
                ctx->img_fmt = smfc_fmt;
 
+       if (ctx->flags & SMFC_CTX_B2B_COMPRESS) {
+               struct vb2_queue *othervq = v4l2_m2m_get_vq(ctx->m2mctx,
+                       V4L2_TYPE_IS_OUTPUT(f->type) ?
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE :
+                               V4L2_BUF_TYPE_VIDEO_OUTPUT);
+               /*
+                * type change of the current queue is completed in
+                * smfc_v4l2_check_s_fmt()
+                */
+               othervq->type = v4l2_to_multiplane_type(othervq->type);
+       }
+
        return 0;
 }
 
@@ -913,8 +1030,11 @@ static int smfc_v4l2_s_fmt(struct file *filp, void *fh, struct v4l2_format *f)
        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;
+       ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix.width);
+       ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix.height);
+       /* back-to-back compression is not supported with single plane */
+       ctx->thumb_width = 0;
+       ctx->thumb_height = 0;
 
        if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
                ctx->img_fmt = smfc_fmt;
@@ -982,6 +1102,16 @@ static const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops = {
        .vidioc_streamoff               = smfc_v4l2_streamoff,
 };
 
+static void smfc_configure_secondary_image(struct smfc_ctx *ctx)
+{
+       if (!(ctx->flags & SMFC_CTX_B2B_COMPRESS) ||
+                       WARN_ON(!(ctx->flags & SMFC_CTX_COMPRESS)))
+               return;
+
+       smfc_hwconfigure_2nd_tables(ctx);
+       smfc_hwconfigure_2nd_image(ctx);
+}
+
 static void smfc_m2m_device_run(void *priv)
 {
        struct smfc_ctx *ctx = priv;
@@ -1012,6 +1142,7 @@ static void smfc_m2m_device_run(void *priv)
        smfc_hwconfigure_reset(ctx->smfc);
        smfc_hwconfigure_tables(ctx);
        smfc_hwconfigure_image(ctx);
+       smfc_configure_secondary_image(ctx);
        smfc_hwconfigure_start(ctx);
 }
 
index 1ec785d9d293a2b0d974aed557f1dd098c99fac3..dbe63674083d73b0e3289bdbe82e5aa196ca73ca 100644 (file)
@@ -56,6 +56,7 @@ struct smfc_dev {
 };
 
 #define SMFC_CTX_COMPRESS      (1 << 0)
+#define SMFC_CTX_B2B_COMPRESS  (1 << 1) /* valid if SMFC_CTX_COMPRESS is set */
 
 struct smfc_ctx {
        struct v4l2_fh v4l2_fh;
@@ -72,6 +73,14 @@ struct smfc_ctx {
        unsigned char chroma_vfactor;
        unsigned char restart_interval;
        unsigned char quality_factor;
+       /*
+        * thumbnail information:
+        * format of thumbnail should be the same as the main image
+        * It is not the H/W restriction. Just a choice for simpler S/W design.
+        */
+       __u32 thumb_width;
+       __u32 thumb_height;
+       unsigned char thumb_quality_factor;
 };
 
 static inline struct smfc_ctx *v4l2_fh_to_smfc_ctx(struct v4l2_fh *fh)
@@ -96,7 +105,9 @@ static inline bool smfc_is_compressed_type(struct smfc_ctx *ctx, __u32 type)
 void smfc_hwconfigure_tables(struct smfc_ctx *ctx);
 void smfc_hwconfigure_image(struct smfc_ctx *ctx);
 void smfc_hwconfigure_start(struct smfc_ctx *ctx);
-bool smfc_hwstatus_okay(struct smfc_dev *smfc);
+void smfc_hwconfigure_2nd_tables(struct smfc_ctx *ctx);
+void smfc_hwconfigure_2nd_image(struct smfc_ctx *ctx);
+bool smfc_hwstatus_okay(struct smfc_dev *smfc, struct smfc_ctx *ctx);
 void smfc_hwconfigure_reset(struct smfc_dev *smfc);
 void smfc_dump_registers(struct smfc_dev *smfc);
 static inline u32 smfc_get_streamsize(struct smfc_dev *smfc)
@@ -104,4 +115,9 @@ static inline u32 smfc_get_streamsize(struct smfc_dev *smfc)
        return __raw_readl(smfc->reg + REG_MAIN_STREAM_SIZE);
 }
 
+static inline u32 smfc_get_2nd_streamsize(struct smfc_dev *smfc)
+{
+       return __raw_readl(smfc->reg + REG_SEC_STREAM_SIZE);
+}
+
 #endif /* _MEDIA_EXYNOS_SMFC_H_ */