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);
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);
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);
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);
__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");
#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
#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)
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:
| 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;
}
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;
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;
{
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;
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);
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");
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)
{
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 = {
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. */
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;
#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 {