[COMMON] exynos: smfc: add support for the same H/W of different versions
authorCho KyongHo <pullip.cho@samsung.com>
Wed, 29 Jul 2015 12:13:17 +0000 (21:13 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:31:13 +0000 (14:31 +0900)
SMFC that is also known as FIMP-JPEG has various H/W versions with
different capabilities. The HWJPEG of Exynos7420 is capable of
downscaling and cropping during decompression while that of Exynos8890
is not. HWJPEG of Exynos3450 does not have the decompression fuction.
Exynos8890, on the other hand, has the advanced functions such as the
hardware flow control(HWFC) and back-to-back compression.

The various version information is distinguished by different
compatible string and the detailed information about each version is
stored in the data field of of_device_id. The driver then uses the
version information in .probe().

Change-Id: Idd847a48167cdef872f7c770dd29d1d441a77eef
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-v4l2-ioctls.c
drivers/media/platform/exynos/smfc/smfc.c
drivers/media/platform/exynos/smfc/smfc.h

index d81bbab58406768430921a09aaee3c52cff85137..ceda9d57721a586ac2c23d7e23592d34c1b45932 100644 (file)
@@ -347,6 +347,7 @@ void smfc_hwconfigure_image(struct smfc_ctx *ctx,
        struct vb2_v4l2_buffer *vb2buf_img, *vb2buf_jpg;
        u32 stream_address;
        u32 format = ctx->img_fmt->regcfg;
+       u32 burstlen = 1 << ctx->smfc->devdata->burstlenth_bits;
 
        __raw_writel(ctx->width | (ctx->height << 16),
                        ctx->smfc->reg + REG_MAIN_IMAGE_SIZE);
@@ -378,16 +379,18 @@ void smfc_hwconfigure_image(struct smfc_ctx *ctx,
                u32 streamsize = vb2_plane_size(&vb2buf_jpg->vb2_buf, 0);
 
                streamsize -= ctx->offset_of_sos;
-               streamsize += stream_address & SMFC_ADDR_ALIGN_MASK;
-               streamsize = ALIGN(streamsize, SMFC_ADDR_ALIGN);
-               streamsize /= SMFC_ADDR_ALIGN;
+               streamsize += stream_address & SMFC_ADDR_ALIGN_MASK(burstlen);
+               streamsize = ALIGN(streamsize, burstlen);
+               streamsize >>= ctx->smfc->devdata->burstlenth_bits;
                __raw_writel(streamsize, ctx->smfc->reg + REG_MAIN_STREAM_SIZE);
-       } else {
+       } else if (smfc_is_capable(ctx->smfc,
+                                  V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE)) {
                u32 maxstreamsize = vb2_plane_size(&vb2buf_jpg->vb2_buf, 0);
 
                maxstreamsize = round_down(maxstreamsize, SMFC_STREAMSIZE_ALIGN);
                if (!IS_ALIGNED(stream_address, 16))
-                       maxstreamsize += SMFC_EXTRA_STREAMSIZE(stream_address);
+                       maxstreamsize +=
+                               SMFC_EXTRA_STREAMSIZE(stream_address, burstlen);
 
                __raw_writel(maxstreamsize,
                             ctx->smfc->reg + REG_MAIN_MAX_STREAM_SIZE);
@@ -400,8 +403,9 @@ 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);
+       if (smfc_is_capable(ctx->smfc, V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION))
+               __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);
@@ -435,7 +439,8 @@ void smfc_hwconfigure_start(struct smfc_ctx *ctx,
 
 bool smfc_hwstatus_okay(struct smfc_dev *smfc, struct smfc_ctx *ctx)
 {
-       u32 val, val2, reg;
+       u32 val, reg;
+       u32 val2 = 0;
 
        /* Disable global interrupt */
        reg = __raw_readl(smfc->reg + REG_MAIN_JPEG_CNTL);
@@ -443,7 +448,8 @@ bool smfc_hwstatus_okay(struct smfc_dev *smfc, struct smfc_ctx *ctx)
        __raw_writel(reg, smfc->reg + REG_MAIN_JPEG_CNTL);
 
        val = __raw_readl(smfc->reg + REG_MAIN_INT_STATUS);
-       val2 = __raw_readl(smfc->reg + REG_SEC_INT_STATUS);
+       if (smfc_is_capable(ctx->smfc, V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION))
+               val2 = __raw_readl(smfc->reg + REG_SEC_INT_STATUS);
 
        if (!val && !val2) {
                dev_err(smfc->dev, "Interrupt with no state change\n");
index 56895ff58bcb5c120269ab345f6eee0026689d71..18e141a778b2f5e2f96746194ef22161465e4fc8 100644 (file)
 #define SMFC_MAX_HEIGHT        16368U
 #define SMFC_MIN_WIDTH 8U
 #define SMFC_MIN_HEIGHT        8U
-#define SMFC_ADDR_ALIGN 16     /* 128-bit align */
-#define SMFC_ADDR_ALIGN_MASK (SMFC_ADDR_ALIGN - 1)
+#define SMFC_ADDR_ALIGN_MASK(burstlen) ((burstlen) - 1)
 #define SMFC_STREAMSIZE_ALIGN 64
 #define SMFC_STREAMSIZE_ALIGN_MASK (64 - 1)
-#define SMFC_EXTRA_STREAMSIZE(base)    \
-               (SMFC_STREAMSIZE_ALIGN - ((base) & SMFC_ADDR_ALIGN_MASK))
+#define SMFC_EXTRA_STREAMSIZE(base, burstlen)  \
+       (SMFC_STREAMSIZE_ALIGN - ((base) & SMFC_ADDR_ALIGN_MASK(burstlen)))
 
 /********** H/W REGISTERS and DEFAULT VALUES **********************************/
 #define REG_MAIN_JPEG_CNTL             0x000
index 67401f42c650945eb6d2d346de274a5560a2453f..bfb09046045def683a2ffcbf2ff33cab1004762f 100644 (file)
 
 #include "smfc.h"
 
-/* SMFC SPECIFIC DEVICE CAPABILITIES */
-/* set if H/W supports for decompression */
-#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION             0x0100
-/* set if H/W can compress dual images */
-#define V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION           0x0200
-/* set if H/W supports for Hardware Flow Control */
-#define V4L2_CAP_EXYNOS_JPEG_HWFC                      0x0400
-/* set if H/W supports for HWFC on internal buffers */
-#define V4L2_CAP_EXYNOS_JPEG_HWFC_EMBEDDED             0x0800
-/* set if H/W has a register to configure stream buffer size */
-#define V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE            0x1000
-/* set if H/W does not have 128-bit alignment constraint for stream base */
-#define V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN       0x2000
-/* set if H/W does not have 128-bit alignment constraint for image base */
-#define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN                0x4000
-/*
- * Set if the driver requires the address of SOS marker for the start address
- * of the JPEG stream. Unset if the driver requires the address of SOI marker
- * for the start address of the JPEG stream even though H/W requires the address
- * of SOS marker to decompress when the driver is able to find the address of
- * SOS marker from the given address of SOI marker.
- */
-#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION_FROM_SOS    0x10000
-/* set if H/W supports for cropping during decompression */
-#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION_CROP                0x20000
-/* set if H/W supports for downscaling(1/2, 1/4 and 1/8) during decompression */
-#define V4L2_CAP_EXYNOS_JPEG_DOWNSCALING               0x40000
-
 /* SMFC SPECIFIC CONTROLS */
 #define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20)
 #define V4L2_CID_JPEG_HWFC_ENABLE      (V4L2_CID_JPEG_CLASS_BASE + 25)
@@ -299,6 +271,13 @@ static int smfc_s_ctrl(struct v4l2_ctrl *ctrl)
                ctx->thumb_quality_factor = (unsigned char)ctrl->val;
                break;
        case V4L2_CID_JPEG_HWFC_ENABLE:
+               if (!smfc_is_capable(ctx->smfc,
+                               V4L2_CAP_EXYNOS_JPEG_HWFC)) {
+                       dev_err(ctx->smfc->dev,
+                               "HWFC(OTF mode) not supported (ver.%08X)",
+                               ctx->smfc->hwver);
+                       return -EINVAL;
+               }
                ctx->enable_hwfc = (unsigned char)ctrl->val;
                break;
        case V4L2_CID_JPEG_RESTART_INTERVAL:
@@ -428,13 +407,7 @@ static int smfc_v4l2_querycap(struct file *filp, void *fh,
                                | V4L2_CAP_VIDEO_M2M;
        cap->capabilities |= V4L2_CAP_DEVICE_CAPS;
 
-       cap->device_caps = V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION;
-       cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_HWFC;
-       cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE;
-       cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN;
-       cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN;
-
-       cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION;
+       cap->device_caps = smfc->devdata->device_caps;
 
        return 0;
 }
@@ -585,12 +558,30 @@ static bool smfc_check_image_size(struct device *dev, __u32 type,
        return true;
 }
 
+static bool smfc_check_capable_of_decompression(const struct smfc_dev *smfc,
+               const struct smfc_image_format *smfc_fmt, __u32 type)
+{
+       if (smfc_is_capable(smfc, V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION))
+               return true;
+
+       if (is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(type)) /* compression? */
+               return true;
+
+       dev_err(smfc->dev, "Decompression is not supported (ver.%08X)",
+               smfc->hwver);
+
+       return false;
+}
+
 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_capable_of_decompression(ctx->smfc, smfc_fmt, type))
+               return false;
+
        if (!smfc_check_image_size(ctx->smfc->dev, type,
                                smfc_fmt, pix_mp->width, pix_mp->height))
                return false;
@@ -703,10 +694,18 @@ static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh,
        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)
+       if (ctx->thumb_width && ctx->thumb_height) {
+               if (!smfc_is_capable(ctx->smfc,
+                               V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) {
+                       dev_err(ctx->smfc->dev,
+                               "back-to-back mode not supported (ver.%08X)",
+                               ctx->smfc->hwver);
+                       return -EINVAL;
+               }
                ctx->flags |= SMFC_CTX_B2B_COMPRESS;
-       else
+       } else {
                ctx->flags &= ~SMFC_CTX_B2B_COMPRESS;
+       }
 
        if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_JPEG)
                ctx->img_fmt = smfc_fmt;
@@ -732,6 +731,9 @@ static bool smfc_v4l2_init_fmt(const struct smfc_ctx *ctx,
 {
        BUG_ON(V4L2_TYPE_IS_MULTIPLANAR(pix->pixelformat));
 
+       if (!smfc_check_capable_of_decompression(ctx->smfc, smfc_fmt, type))
+               return false;
+
        if (!smfc_check_image_size(ctx->smfc->dev, type,
                                smfc_fmt, pix->width, pix->height))
                return false;
index 5ad94b3a658889a19387aa63e3757d3291cbf5c8..e41681ce082666b44eec08eb5c8a72124433a4b3 100644 (file)
@@ -747,10 +747,54 @@ static int __attribute__((unused)) smfc_iommu_fault_handler(
        return 0;
 }
 
+static const struct smfc_device_data smfc_8890_data = {
+       .device_caps = V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION
+                       | V4L2_CAP_EXYNOS_JPEG_HWFC
+                       | V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE
+                       | V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN
+                       | V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN
+                       | V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION,
+       .burstlenth_bits = 4, /* 16 bytes: 1 burst */
+};
+
+static const struct smfc_device_data smfc_7420_data = {
+       .device_caps = V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN
+                       | V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN
+                       | V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION
+                       | V4L2_CAP_EXYNOS_JPEG_DOWNSCALING
+                       | V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION_CROP,
+       .burstlenth_bits = 5, /* 32 bytes: 2 bursts */
+};
+
+static const struct smfc_device_data smfc_3475_data = {
+       .device_caps = V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN
+                       | V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN,
+       .burstlenth_bits = 5, /* 32 bytes: 2 bursts */
+};
+
+static const struct of_device_id exynos_smfc_match[] __initconst = {
+       {
+               .compatible = "samsung,exynos-jpeg",
+               .data = &smfc_8890_data,
+       }, {
+               .compatible = "samsung,exynos8890-jpeg",
+               .data = &smfc_8890_data,
+       }, {
+               .compatible = "samsung,exynos7420-jpeg",
+               .data = &smfc_7420_data,
+       }, {
+               .compatible = "samsung,exynos3475-jpeg",
+               .data = &smfc_3475_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_smfc_match);
+
 static int exynos_smfc_probe(struct platform_device *pdev)
 {
        struct smfc_dev *smfc;
        struct resource *res;
+       const struct of_device_id *of_id;
        int ret;
 
        smfc = devm_kzalloc(&pdev->dev, sizeof(*smfc), GFP_KERNEL);
@@ -766,6 +810,9 @@ static int exynos_smfc_probe(struct platform_device *pdev)
        if (IS_ERR(smfc->reg))
                return PTR_ERR(smfc->reg);
 
+       of_id = of_match_node(exynos_smfc_match, pdev->dev.of_node);
+       smfc->devdata = of_id->data;
+
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "Failed to get IRQ resource");
@@ -835,14 +882,6 @@ static int exynos_smfc_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id exynos_smfc_match[] = {
-       {
-               .compatible = "samsung,exynos-jpeg",
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, exynos_smfc_match);
-
 #ifdef CONFIG_PM_SLEEP
 static int smfc_suspend(struct device *dev)
 {
@@ -899,7 +938,7 @@ static const struct dev_pm_ops exynos_smfc_pm_ops = {
        SET_RUNTIME_PM_OPS(NULL, smfc_runtime_resume, smfc_runtime_suspend)
 };
 
-static struct platform_driver exynos_smfc_driver = {
+static struct platform_driver exynos_smfc_driver __refdata = {
        .probe          = exynos_smfc_probe,
        .remove         = exynos_smfc_remove,
        .driver = {
index edf9746ffff9efa4c9c66ff27a6c4bc3bb944310..1b02196bdf55fc1d788561dc195952247cae3c5a 100644 (file)
@@ -48,6 +48,39 @@ static inline bool is_jpeg(const struct smfc_image_format *fmt)
        return fmt->bpp_buf[0] == 0;
 }
 
+/* SMFC SPECIFIC DEVICE CAPABILITIES */
+/* set if H/W supports for decompression */
+#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION             0x0100
+/* set if H/W can compress dual images */
+#define V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION           0x0200
+/* set if H/W supports for Hardware Flow Control */
+#define V4L2_CAP_EXYNOS_JPEG_HWFC                      0x0400
+/* set if H/W supports for HWFC on internal buffers */
+#define V4L2_CAP_EXYNOS_JPEG_HWFC_EMBEDDED             0x0800
+/* set if H/W has a register to configure stream buffer size */
+#define V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE            0x1000
+/* set if H/W does not have 128-bit alignment constraint for stream base */
+#define V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN       0x2000
+/* set if H/W does not have 128-bit alignment constraint for image base */
+#define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN                0x4000
+/*
+ * Set if the driver requires the address of SOS marker for the start address
+ * of the JPEG stream. Unset if the driver requires the address of SOI marker
+ * for the start address of the JPEG stream even though H/W requires the address
+ * of SOS marker to decompress when the driver is able to find the address of
+ * SOS marker from the given address of SOI marker.
+ */
+#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION_FROM_SOS    0x10000
+/* set if H/W supports for cropping during decompression */
+#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION_CROP                0x20000
+/* set if H/W supports for downscaling(1/2, 1/4 and 1/8) during decompression */
+#define V4L2_CAP_EXYNOS_JPEG_DOWNSCALING               0x40000
+
+struct smfc_device_data {
+       __u32 device_caps;
+       unsigned char burstlenth_bits;
+};
+
 /* Set when H/W starts, cleared in irq/timeout handler */
 #define SMFC_DEV_RUNNING       (1 << 0)
 /* Set when suspend handler is called, cleared before irq handler returns. */
@@ -63,6 +96,7 @@ struct smfc_dev {
        struct v4l2_m2m_dev *m2mdev;
        struct device *dev;
        void __iomem *reg;
+       const struct smfc_device_data *devdata;
        spinlock_t flag_lock;
        struct mutex video_device_mutex;
        struct timer_list timer;
@@ -76,6 +110,12 @@ struct smfc_dev {
 
 #define SMFC_CTX_COMPRESS      (1 << 0)
 #define SMFC_CTX_B2B_COMPRESS  (1 << 1) /* valid if SMFC_CTX_COMPRESS is set */
+
+static inline bool smfc_is_capable(const struct smfc_dev *smfc, u32 capability)
+{
+       return (smfc->devdata->device_caps & capability) == capability;
+}
+
 struct smfc_decomp_htable {
        struct {
                union {