[COMMON] media: smfc: add support for custom quantization tables
authorCho KyongHo <pullip.cho@samsung.com>
Mon, 5 Oct 2015 16:08:22 +0000 (01:08 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:19 +0000 (20:22 +0300)
Some users needs to configure their own quantization tables instead of
the quality factors that is the factor to calculate the quantization
tables based on the ones suggested by ITU-81 Appendix K.1 and K.2.

Now the driver supports for the new custom control variable,
V4L2_CID_JPEG_QTABLES2 that is an array of V4L2_CTRL_TYPE_U8 with
128 values (two arrays with 64 elements). One of the table is for
the quantization table of the Luma and the other table is for the
chroma components. The values in the quantization tables should be
specified in the zig-zag scan order.

Note that once the custom quantization tables are configured, it is
used for the compressions until a new quality factor or a new
quantization is configured.
Also note that the custom quantization tables are configured to H/W
even though the table stored in the v4l2_ctrl is in transient state.
Therefore, configuring new custom quantization tables with s_ctrl or
s_ext_ctrl during the H/W is working for the current context may cause
an unpredictable result which means the quantization tables that is
used by the H/W may be incorrect and it impacts the compression
quality of the compressed stream.

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

index d88a4d5514e8ff4e7923c1ce0d244d60d04c434d..9ed95af186a3f9c71cbfa89ec6896711feaf7292 100644 (file)
@@ -33,7 +33,7 @@ void smfc_hwconfigure_reset(struct smfc_dev *smfc)
  * Quantization tables in ZIG-ZAG order provided by IOC/IEC 10918-1 K.1 and K.2
  * for YUV420 and YUV422
  */
-static const unsigned char default_luma_qtbl[SMFC_MCU_SIZE] __aligned(64) = {
+static const u8 default_luma_qtbl[SMFC_MCU_SIZE] __aligned(64) = {
         16,  11,  12,  14,  12,  10,  16,  14,
         13,  14,  18,  17,  16,  19,  24,  40,
         26,  24,  22,  22,  24,  49,  35,  37,
@@ -44,7 +44,7 @@ static const unsigned char default_luma_qtbl[SMFC_MCU_SIZE] __aligned(64) = {
        121, 112, 100, 120,  92, 101, 103,  99,
 };
 
-static const unsigned char default_chroma_qtbl[SMFC_MCU_SIZE] __aligned(64) = {
+static const u8 default_chroma_qtbl[SMFC_MCU_SIZE] __aligned(64) = {
        17,  18,  18,  24,  21,  24,  47,  26,
        26,  47,  99,  66,  56,  66,  99,  99,
        99,  99,  99,  99,  99,  99,  99,  99,
@@ -124,7 +124,7 @@ 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[])
+                                   const u8 table[])
 {
        size_t i;
 
@@ -132,16 +132,39 @@ static void smfc_hwconfigure_qtable(void __iomem *reg, unsigned int factor,
                __raw_writel(smfc_calc_quantizers(i, factor, table), reg + i);
 }
 
-void smfc_hwconfigure_tables(struct smfc_ctx *ctx, unsigned int qfactor)
+static void smfc_hwconfigure_custom_qtable(void __iomem *reg, const u8 table[])
+{
+       size_t i;
+
+       for (i = 0; i < SMFC_MCU_SIZE; i += 4) {
+               u32 val;
+
+               val = table[i];
+               val |= table[i + 1] << 8;
+               val |= table[i + 2] << 16;
+               val |= table[i + 3] << 24;
+               __raw_writel(val, reg + i);
+       }
+}
+
+void smfc_hwconfigure_tables(struct smfc_ctx *ctx,
+                            unsigned int qfactor, const u8 qtbl[])
 {
        size_t i;
        void __iomem *base = ctx->smfc->reg;
-       qfactor = (qfactor < 50) ? 5000 / qfactor : 200 - qfactor * 2;
 
-       smfc_hwconfigure_qtable(base + REG_QTBL_BASE,
-                               qfactor, default_luma_qtbl);
-       smfc_hwconfigure_qtable(base + REG_QTBL_BASE + SMFC_MCU_SIZE,
-                               qfactor, default_chroma_qtbl);
+       if (qfactor > 0) {
+               qfactor = (qfactor < 50) ? 5000 / qfactor : 200 - qfactor * 2;
+               smfc_hwconfigure_qtable(base + REG_QTBL_BASE,
+                                       qfactor, default_luma_qtbl);
+               smfc_hwconfigure_qtable(base + REG_QTBL_BASE + SMFC_MCU_SIZE,
+                                       qfactor, default_chroma_qtbl);
+       } else {
+               smfc_hwconfigure_custom_qtable(base + REG_QTBL_BASE, qtbl);
+               smfc_hwconfigure_custom_qtable(
+                                       base + REG_QTBL_BASE + SMFC_MCU_SIZE,
+                                       qtbl + SMFC_MCU_SIZE);
+       }
 
        /* Huffman tables */
        for (i = 0; i < 4; i++) {
index 75112e5825a2ea2b8f7c75847d312d0138584700..c7faeeed2522be5541763078eb7a8108246a89e7 100644 (file)
@@ -15,6 +15,7 @@
 
 /* SMFC SPECIFIC CONTROLS */
 #define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20)
+#define V4L2_CID_JPEG_QTABLES2         (V4L2_CID_JPEG_CLASS_BASE + 22)
 #define V4L2_CID_JPEG_HWFC_ENABLE      (V4L2_CID_JPEG_CLASS_BASE + 25)
 
 #define SMFC_FMT_MAIN_SIZE(val) ((val) & 0xFFFF)
@@ -312,8 +313,17 @@ static int smfc_s_ctrl(struct v4l2_ctrl *ctrl)
                        break;
                }
                break;
-       default:
+       case V4L2_CID_JPEG_QTABLES2:
+               /*
+                * reset the quality factor to indicate that the custom q-table
+                * is configured. It is sustained until the new quality factor
+                * is configured.
+                */
+               ctx->quality_factor = 0;
                break;
+       default:
+               dev_err(ctx->smfc->dev, "Unsupported CID %#x\n", ctrl->id);
+               return -EINVAL;
        }
 
        return 0;
@@ -326,6 +336,8 @@ static const struct v4l2_ctrl_ops smfc_ctrl_ops = {
 int smfc_init_controls(struct smfc_dev *smfc,
                        struct v4l2_ctrl_handler *hdlr)
 {
+       struct smfc_ctx *ctx =
+                       container_of(hdlr, struct smfc_ctx, v4l2_ctrlhdlr);
        const char *msg;
        struct v4l2_ctrl_config ctrlcfg;
 
@@ -338,6 +350,21 @@ int smfc_init_controls(struct smfc_dev *smfc,
                goto err;
        }
 
+       if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops,
+                               V4L2_CID_JPEG_RESTART_INTERVAL,
+                               0, 64, 1, 0)) {
+               msg = "restart interval";
+               goto err;
+       }
+
+       if (!v4l2_ctrl_new_std_menu(hdlr, &smfc_ctrl_ops,
+                               V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
+                               V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, 0,
+                               V4L2_JPEG_CHROMA_SUBSAMPLING_422)) {
+               msg = "chroma subsampling";
+               goto err;
+       }
+
        memset(&ctrlcfg, 0, sizeof(ctrlcfg));
        ctrlcfg.ops = &smfc_ctrl_ops;
        ctrlcfg.id = V4L2_CID_JPEG_SEC_COMP_QUALITY;
@@ -366,18 +393,20 @@ int smfc_init_controls(struct smfc_dev *smfc,
                goto err;
        }
 
-       if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops,
-                               V4L2_CID_JPEG_RESTART_INTERVAL,
-                               0, 64, 1, 0)) {
-               msg = "restart interval";
-               goto err;
-       }
-
-       if (!v4l2_ctrl_new_std_menu(hdlr, &smfc_ctrl_ops,
-                               V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
-                               V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, 0,
-                               V4L2_JPEG_CHROMA_SUBSAMPLING_422)) {
-               msg = "chroma subsampling";
+       memset(&ctrlcfg, 0, sizeof(ctrlcfg));
+       ctrlcfg.ops = &smfc_ctrl_ops;
+       ctrlcfg.id = V4L2_CID_JPEG_QTABLES2;
+       ctrlcfg.name = "Quantization table configration";
+       ctrlcfg.type = V4L2_CTRL_TYPE_U8;
+       ctrlcfg.min = 1;
+       ctrlcfg.max = 255;
+       ctrlcfg.step = 1;
+       ctrlcfg.def = 1;
+       ctrlcfg.dims[0] = SMFC_MCU_SIZE; /* A qtable has 64 values */
+       ctrlcfg.dims[1] = 2; /* 2 qtables are required for 0:luma, 1:chroma */
+       ctx->ctrl_qtbl2 = v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL);
+       if (!ctx->ctrl_qtbl2) {
+               msg = "Q-Table";
                goto err;
        }
 
index 5da080119b12ee7e5354a3ac99d87de329a411fc..384d1cea22df0a74cf3e616cd3e25f659303aac2 100644 (file)
@@ -546,7 +546,8 @@ static void smfc_m2m_device_run(void *priv)
        smfc_hwconfigure_reset(ctx->smfc);
 
        if (!!(ctx->flags & SMFC_CTX_COMPRESS)) {
-               smfc_hwconfigure_tables(ctx, quality_factor);
+               smfc_hwconfigure_tables(ctx, quality_factor,
+                                       ctx->ctrl_qtbl2->p_cur.p_u8);
                smfc_hwconfigure_image(ctx, chroma_hfactor, chroma_vfactor);
                if (!!(ctx->flags & SMFC_CTX_B2B_COMPRESS)) {
                        smfc_hwconfigure_2nd_tables(ctx, thumb_quality_factor);
index 222f53ff3ab35bfe2d7e5b0cebccd94f352f2d4d..a0caee204ff0d1841165cbfcd80a63eff8735563 100644 (file)
@@ -167,6 +167,7 @@ struct smfc_ctx {
        unsigned char chroma_vfactor; /* vertical chroma subsampling factor */
        unsigned char restart_interval;
        unsigned char quality_factor;
+       struct v4l2_ctrl *ctrl_qtbl2;
        /*
         * thumbnail information:
         * format of thumbnail should be the same as the main image
@@ -213,7 +214,8 @@ int smfc_init_controls(struct smfc_dev *smfc, struct v4l2_ctrl_handler *hdlr);
 int smfc_parse_jpeg_header(struct smfc_ctx *ctx, struct vb2_buffer *vb);
 
 /* H/W Configuration */
-void smfc_hwconfigure_tables(struct smfc_ctx *ctx, unsigned int qfactor);
+void smfc_hwconfigure_tables(struct smfc_ctx *ctx,
+                            unsigned int qfactor, const u8 qtbl[]);
 void smfc_hwconfigure_tables_for_decompression(struct smfc_ctx *ctx);
 void smfc_hwconfigure_image(struct smfc_ctx *ctx,
                            unsigned int hfactor, unsigned int vfactor);