From f4104b7851a8d8b9a70899dcbecdb393eb16cd8a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Mar 2022 11:18:17 +0100 Subject: media: platform: rename s5p-jpeg/ to samsung/s5p-jpeg/ As the end goal is to have platform drivers split by vendor, rename s5p-jpeg/ to samsung/s5p-jpeg/. Acked-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 2 +- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 2 +- drivers/media/platform/renesas/rcar_jpu.c | 2 +- drivers/media/platform/s5p-jpeg/Kconfig | 12 - drivers/media/platform/s5p-jpeg/Makefile | 3 - drivers/media/platform/s5p-jpeg/jpeg-core.c | 3182 -------------------- drivers/media/platform/s5p-jpeg/jpeg-core.h | 267 -- .../media/platform/s5p-jpeg/jpeg-hw-exynos3250.c | 486 --- .../media/platform/s5p-jpeg/jpeg-hw-exynos3250.h | 57 - drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c | 321 -- drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h | 44 - drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c | 306 -- drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h | 57 - drivers/media/platform/s5p-jpeg/jpeg-regs.h | 646 ---- drivers/media/platform/samsung/s5p-jpeg/Kconfig | 12 + drivers/media/platform/samsung/s5p-jpeg/Makefile | 3 + .../media/platform/samsung/s5p-jpeg/jpeg-core.c | 3182 ++++++++++++++++++++ .../media/platform/samsung/s5p-jpeg/jpeg-core.h | 267 ++ .../platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c | 486 +++ .../platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h | 57 + .../platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c | 321 ++ .../platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h | 44 + .../media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c | 306 ++ .../media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h | 57 + .../media/platform/samsung/s5p-jpeg/jpeg-regs.h | 646 ++++ 26 files changed, 5385 insertions(+), 5385 deletions(-) delete mode 100644 drivers/media/platform/s5p-jpeg/Kconfig delete mode 100644 drivers/media/platform/s5p-jpeg/Makefile delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-core.c delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-core.h delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h delete mode 100644 drivers/media/platform/s5p-jpeg/jpeg-regs.h create mode 100644 drivers/media/platform/samsung/s5p-jpeg/Kconfig create mode 100644 drivers/media/platform/samsung/s5p-jpeg/Makefile create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h create mode 100644 drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6ab986c96cf2..5f7a89e2af80 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -87,12 +87,12 @@ source "drivers/media/platform/omap3isp/Kconfig" source "drivers/media/platform/qcom/Kconfig" source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" -source "drivers/media/platform/s5p-jpeg/Kconfig" source "drivers/media/platform/s5p-mfc/Kconfig" source "drivers/media/platform/samsung/exynos-gsc/Kconfig" source "drivers/media/platform/samsung/exynos4-is/Kconfig" source "drivers/media/platform/samsung/s3c-camif/Kconfig" source "drivers/media/platform/samsung/s5p-g2d/Kconfig" +source "drivers/media/platform/samsung/s5p-jpeg/Kconfig" source "drivers/media/platform/sti/Kconfig" source "drivers/media/platform/stm32/Kconfig" source "drivers/media/platform/sunxi/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4b3e4209eaf9..86f81e81dadc 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -29,12 +29,12 @@ obj-y += qcom/venus/ obj-y += renesas/ obj-y += rockchip/rga/ obj-y += rockchip/rkisp1/ -obj-y += s5p-jpeg/ obj-y += s5p-mfc/ obj-y += samsung/exynos-gsc/ obj-y += samsung/exynos4-is/ obj-y += samsung/s3c-camif/ obj-y += samsung/s5p-g2d/ +obj-y += samsung/s5p-jpeg/ obj-y += sti/bdisp/ obj-y += sti/c8sectpfe/ obj-y += sti/delta/ diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 3b892c5792b4..d1ec1f4b506b 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -35,7 +35,7 @@ * it, enable dynamic debug for this module and: * echo 1 > /sys/module/mxc_jpeg_encdec/parameters/jpeg_tracing * - * This is inspired by the drivers/media/platform/s5p-jpeg driver + * This is inspired by the drivers/media/platform/samsung/s5p-jpeg driver * * Copyright 2018-2019 NXP */ diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index 56bb464629ed..293beba131e2 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -4,7 +4,7 @@ * Copyright (C) 2014-2015 Cogent Embedded, Inc. * Copyright (C) 2014-2015 Renesas Electronics Corporation * - * This is based on the drivers/media/platform/s5p-jpeg driver by + * This is based on the drivers/media/platform/samsung/s5p-jpeg driver by * Andrzej Pietrasiewicz and Jacek Anaszewski. * Some portions of code inspired by VSP1 driver by Laurent Pinchart. * diff --git a/drivers/media/platform/s5p-jpeg/Kconfig b/drivers/media/platform/s5p-jpeg/Kconfig deleted file mode 100644 index e522860d2b15..000000000000 --- a/drivers/media/platform/s5p-jpeg/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config VIDEO_SAMSUNG_S5P_JPEG - tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" - depends on V4L_MEM2MEM_DRIVERS - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Samsung S5P, EXYNOS3250 - and EXYNOS4 JPEG codec diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile deleted file mode 100644 index 8b0f92e27e70..000000000000 --- a/drivers/media/platform/s5p-jpeg/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos3250.o jpeg-hw-exynos4.o jpeg-hw-s5p.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c deleted file mode 100644 index a8d9159d5ed8..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ /dev/null @@ -1,3182 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c - * - * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - * Author: Jacek Anaszewski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "jpeg-core.h" -#include "jpeg-hw-s5p.h" -#include "jpeg-hw-exynos4.h" -#include "jpeg-hw-exynos3250.h" -#include "jpeg-regs.h" - -static struct s5p_jpeg_fmt sjpeg_formats[] = { - { - .fourcc = V4L2_PIX_FMT_JPEG, - .flags = SJPEG_FMT_FLAG_ENC_CAPTURE | - SJPEG_FMT_FLAG_DEC_OUTPUT | - SJPEG_FMT_FLAG_S5P | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_FLAG_EXYNOS4, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .colplanes = 1, - .h_align = 4, - .v_align = 3, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_S5P | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .colplanes = 1, - .h_align = 1, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_YVYU, - .depth = 16, - .colplanes = 1, - .h_align = 1, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_YVYU, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_VYUY, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .colplanes = 1, - .h_align = 0, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565X, - .depth = 16, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .colplanes = 1, - .h_align = 0, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_S5P | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_RGB32, - .depth = 32, - .colplanes = 1, - .h_align = 0, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_RGB32, - .depth = 32, - .colplanes = 1, - .h_align = 2, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_NV24, - .depth = 24, - .colplanes = 2, - .h_align = 0, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_NV42, - .depth = 24, - .colplanes = 2, - .h_align = 0, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, - }, - { - .fourcc = V4L2_PIX_FMT_NV61, - .depth = 16, - .colplanes = 2, - .h_align = 1, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_NV16, - .depth = 16, - .colplanes = 2, - .h_align = 1, - .v_align = 0, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .depth = 12, - .colplanes = 2, - .h_align = 1, - .v_align = 1, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .depth = 12, - .colplanes = 2, - .h_align = 3, - .v_align = 3, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .depth = 12, - .colplanes = 2, - .h_align = 4, - .v_align = 4, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_S5P | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .depth = 12, - .colplanes = 2, - .h_align = 3, - .v_align = 3, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .depth = 12, - .colplanes = 2, - .h_align = 1, - .v_align = 1, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .colplanes = 3, - .h_align = 1, - .v_align = 1, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .colplanes = 3, - .h_align = 4, - .v_align = 4, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS3250 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8, - .colplanes = 1, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | - SJPEG_FMT_FLAG_EXYNOS4 | - SJPEG_FMT_NON_RGB, - .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, - }, -}; -#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats) - -static const unsigned char qtbl_luminance[4][64] = { - {/*level 0 - high compression quality */ - 20, 16, 25, 39, 50, 46, 62, 68, - 16, 18, 23, 38, 38, 53, 65, 68, - 25, 23, 31, 38, 53, 65, 68, 68, - 39, 38, 38, 53, 65, 68, 68, 68, - 50, 38, 53, 65, 68, 68, 68, 68, - 46, 53, 65, 68, 68, 68, 68, 68, - 62, 65, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - {/* level 1 */ - 16, 11, 11, 16, 23, 27, 31, 30, - 11, 12, 12, 15, 20, 23, 23, 30, - 11, 12, 13, 16, 23, 26, 35, 47, - 16, 15, 16, 23, 26, 37, 47, 64, - 23, 20, 23, 26, 39, 51, 64, 64, - 27, 23, 26, 37, 51, 64, 64, 64, - 31, 23, 35, 47, 64, 64, 64, 64, - 30, 30, 47, 64, 64, 64, 64, 64 - }, - {/* level 2 */ - 12, 8, 8, 12, 17, 21, 24, 23, - 8, 9, 9, 11, 15, 19, 18, 23, - 8, 9, 10, 12, 19, 20, 27, 36, - 12, 11, 12, 21, 20, 28, 36, 53, - 17, 15, 19, 20, 30, 39, 51, 59, - 21, 19, 20, 28, 39, 51, 59, 59, - 24, 18, 27, 36, 51, 59, 59, 59, - 23, 23, 36, 53, 59, 59, 59, 59 - }, - {/* level 3 - low compression quality */ - 8, 6, 6, 8, 12, 14, 16, 17, - 6, 6, 6, 8, 10, 13, 12, 15, - 6, 6, 7, 8, 13, 14, 18, 24, - 8, 8, 8, 14, 13, 19, 24, 35, - 12, 10, 13, 13, 20, 26, 34, 39, - 14, 13, 14, 19, 26, 34, 39, 39, - 16, 12, 18, 24, 34, 39, 39, 39, - 17, 15, 24, 35, 39, 39, 39, 39 - } -}; - -static const unsigned char qtbl_chrominance[4][64] = { - {/*level 0 - high compression quality */ - 21, 25, 32, 38, 54, 68, 68, 68, - 25, 28, 24, 38, 54, 68, 68, 68, - 32, 24, 32, 43, 66, 68, 68, 68, - 38, 38, 43, 53, 68, 68, 68, 68, - 54, 54, 66, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - {/* level 1 */ - 17, 15, 17, 21, 20, 26, 38, 48, - 15, 19, 18, 17, 20, 26, 35, 43, - 17, 18, 20, 22, 26, 30, 46, 53, - 21, 17, 22, 28, 30, 39, 53, 64, - 20, 20, 26, 30, 39, 48, 64, 64, - 26, 26, 30, 39, 48, 63, 64, 64, - 38, 35, 46, 53, 64, 64, 64, 64, - 48, 43, 53, 64, 64, 64, 64, 64 - }, - {/* level 2 */ - 13, 11, 13, 16, 20, 20, 29, 37, - 11, 14, 14, 14, 16, 20, 26, 32, - 13, 14, 15, 17, 20, 23, 35, 40, - 16, 14, 17, 21, 23, 30, 40, 50, - 20, 16, 20, 23, 30, 37, 50, 59, - 20, 20, 23, 30, 37, 48, 59, 59, - 29, 26, 35, 40, 50, 59, 59, 59, - 37, 32, 40, 50, 59, 59, 59, 59 - }, - {/* level 3 - low compression quality */ - 9, 8, 9, 11, 14, 17, 19, 24, - 8, 10, 9, 11, 14, 13, 17, 22, - 9, 9, 13, 14, 13, 15, 23, 26, - 11, 11, 14, 14, 15, 20, 26, 33, - 14, 14, 13, 15, 20, 24, 33, 39, - 17, 13, 15, 20, 24, 32, 39, 39, - 19, 17, 23, 26, 33, 39, 39, 39, - 24, 22, 26, 33, 39, 39, 39, 39 - } -}; - -static const unsigned char hdctbl0[16] = { - 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 -}; - -static const unsigned char hdctblg0[12] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb -}; -static const unsigned char hactbl0[16] = { - 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d -}; -static const unsigned char hactblg0[162] = { - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa -}; - -/* - * Fourcc downgrade schema lookup tables for 422 and 420 - * chroma subsampling - fourcc on each position maps on the - * fourcc from the table fourcc_to_dwngrd_schema_id which allows - * to get the most suitable fourcc counterpart for the given - * downgraded subsampling property. - */ -static const u32 subs422_fourcc_dwngrd_schema[] = { - V4L2_PIX_FMT_NV16, - V4L2_PIX_FMT_NV61, -}; - -static const u32 subs420_fourcc_dwngrd_schema[] = { - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_NV21, - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_NV21, - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_NV21, - V4L2_PIX_FMT_GREY, - V4L2_PIX_FMT_GREY, - V4L2_PIX_FMT_GREY, - V4L2_PIX_FMT_GREY, -}; - -/* - * Lookup table for translation of a fourcc to the position - * of its downgraded counterpart in the *fourcc_dwngrd_schema - * tables. - */ -static const u32 fourcc_to_dwngrd_schema_id[] = { - V4L2_PIX_FMT_NV24, - V4L2_PIX_FMT_NV42, - V4L2_PIX_FMT_NV16, - V4L2_PIX_FMT_NV61, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YVYU, - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_NV21, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_GREY, -}; - -static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) { - if (fourcc_to_dwngrd_schema_id[i] == fourcc) - return i; - } - - return -EINVAL; -} - -static int s5p_jpeg_adjust_fourcc_to_subsampling( - enum v4l2_jpeg_chroma_subsampling subs, - u32 in_fourcc, - u32 *out_fourcc, - struct s5p_jpeg_ctx *ctx) -{ - int dwngrd_sch_id; - - if (ctx->subsampling != V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { - dwngrd_sch_id = - s5p_jpeg_get_dwngrd_sch_id_by_fourcc(in_fourcc); - if (dwngrd_sch_id < 0) - return -EINVAL; - } - - switch (ctx->subsampling) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: - *out_fourcc = V4L2_PIX_FMT_GREY; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - if (dwngrd_sch_id > - ARRAY_SIZE(subs420_fourcc_dwngrd_schema) - 1) - return -EINVAL; - *out_fourcc = subs420_fourcc_dwngrd_schema[dwngrd_sch_id]; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - if (dwngrd_sch_id > - ARRAY_SIZE(subs422_fourcc_dwngrd_schema) - 1) - return -EINVAL; - *out_fourcc = subs422_fourcc_dwngrd_schema[dwngrd_sch_id]; - break; - default: - *out_fourcc = V4L2_PIX_FMT_GREY; - break; - } - - return 0; -} - -static int exynos4x12_decoded_subsampling[] = { - V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, - V4L2_JPEG_CHROMA_SUBSAMPLING_444, - V4L2_JPEG_CHROMA_SUBSAMPLING_422, - V4L2_JPEG_CHROMA_SUBSAMPLING_420, -}; - -static int exynos3250_decoded_subsampling[] = { - V4L2_JPEG_CHROMA_SUBSAMPLING_444, - V4L2_JPEG_CHROMA_SUBSAMPLING_422, - V4L2_JPEG_CHROMA_SUBSAMPLING_420, - V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, - -1, - -1, - V4L2_JPEG_CHROMA_SUBSAMPLING_411, -}; - -static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) -{ - return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); -} - -static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct s5p_jpeg_ctx, fh); -} - -static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) -{ - switch (ctx->jpeg->variant->version) { - case SJPEG_S5P: - WARN_ON(ctx->subsampling > 3); - if (ctx->subsampling > 2) - return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; - return ctx->subsampling; - case SJPEG_EXYNOS3250: - case SJPEG_EXYNOS5420: - WARN_ON(ctx->subsampling > 6); - if (ctx->subsampling > 3) - return V4L2_JPEG_CHROMA_SUBSAMPLING_411; - return exynos3250_decoded_subsampling[ctx->subsampling]; - case SJPEG_EXYNOS4: - WARN_ON(ctx->subsampling > 3); - if (ctx->subsampling > 2) - return V4L2_JPEG_CHROMA_SUBSAMPLING_420; - return exynos4x12_decoded_subsampling[ctx->subsampling]; - case SJPEG_EXYNOS5433: - return ctx->subsampling; /* parsed from header */ - default: - WARN_ON(ctx->subsampling > 3); - return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; - } -} - -static inline void s5p_jpeg_set_qtbl(void __iomem *regs, - const unsigned char *qtbl, - unsigned long tab, int len) -{ - int i; - - for (i = 0; i < len; i++) - writel((unsigned int)qtbl[i], regs + tab + (i * 0x04)); -} - -static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality) -{ - /* this driver fills quantisation table 0 with data for luma */ - s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality], - S5P_JPG_QTBL_CONTENT(0), - ARRAY_SIZE(qtbl_luminance[quality])); -} - -static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality) -{ - /* this driver fills quantisation table 1 with data for chroma */ - s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality], - S5P_JPG_QTBL_CONTENT(1), - ARRAY_SIZE(qtbl_chrominance[quality])); -} - -static inline void s5p_jpeg_set_htbl(void __iomem *regs, - const unsigned char *htbl, - unsigned long tab, int len) -{ - int i; - - for (i = 0; i < len; i++) - writel((unsigned int)htbl[i], regs + tab + (i * 0x04)); -} - -static inline void s5p_jpeg_set_hdctbl(void __iomem *regs) -{ - /* this driver fills table 0 for this component */ - s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), - ARRAY_SIZE(hdctbl0)); -} - -static inline void s5p_jpeg_set_hdctblg(void __iomem *regs) -{ - /* this driver fills table 0 for this component */ - s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), - ARRAY_SIZE(hdctblg0)); -} - -static inline void s5p_jpeg_set_hactbl(void __iomem *regs) -{ - /* this driver fills table 0 for this component */ - s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), - ARRAY_SIZE(hactbl0)); -} - -static inline void s5p_jpeg_set_hactblg(void __iomem *regs) -{ - /* this driver fills table 0 for this component */ - s5p_jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), - ARRAY_SIZE(hactblg0)); -} - -static inline void exynos4_jpeg_set_tbl(void __iomem *regs, - const unsigned char *tbl, - unsigned long tab, int len) -{ - int i; - unsigned int dword; - - for (i = 0; i < len; i += 4) { - dword = tbl[i] | - (tbl[i + 1] << 8) | - (tbl[i + 2] << 16) | - (tbl[i + 3] << 24); - writel(dword, regs + tab + i); - } -} - -static inline void exynos4_jpeg_set_qtbl_lum(void __iomem *regs, int quality) -{ - /* this driver fills quantisation table 0 with data for luma */ - exynos4_jpeg_set_tbl(regs, qtbl_luminance[quality], - EXYNOS4_QTBL_CONTENT(0), - ARRAY_SIZE(qtbl_luminance[quality])); -} - -static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality) -{ - /* this driver fills quantisation table 1 with data for chroma */ - exynos4_jpeg_set_tbl(regs, qtbl_chrominance[quality], - EXYNOS4_QTBL_CONTENT(1), - ARRAY_SIZE(qtbl_chrominance[quality])); -} - -static void exynos4_jpeg_set_huff_tbl(void __iomem *base) -{ - exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL, - ARRAY_SIZE(hdctbl0)); - exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCCL, - ARRAY_SIZE(hdctbl0)); - exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCLV, - ARRAY_SIZE(hdctblg0)); - exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCCV, - ARRAY_SIZE(hdctblg0)); - exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACLL, - ARRAY_SIZE(hactbl0)); - exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACCL, - ARRAY_SIZE(hactbl0)); - exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACLV, - ARRAY_SIZE(hactblg0)); - exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACCV, - ARRAY_SIZE(hactblg0)); -} - -static inline int __exynos4_huff_tbl(int class, int id, bool lenval) -{ - /* - * class: 0 - DC, 1 - AC - * id: 0 - Y, 1 - Cb/Cr - */ - if (class) { - if (id) - return lenval ? EXYNOS4_HUFF_TBL_HACCL : - EXYNOS4_HUFF_TBL_HACCV; - return lenval ? EXYNOS4_HUFF_TBL_HACLL : EXYNOS4_HUFF_TBL_HACLV; - - } - /* class == 0 */ - if (id) - return lenval ? EXYNOS4_HUFF_TBL_HDCCL : EXYNOS4_HUFF_TBL_HDCCV; - - return lenval ? EXYNOS4_HUFF_TBL_HDCLL : EXYNOS4_HUFF_TBL_HDCLV; -} - -static inline int exynos4_huff_tbl_len(int class, int id) -{ - return __exynos4_huff_tbl(class, id, true); -} - -static inline int exynos4_huff_tbl_val(int class, int id) -{ - return __exynos4_huff_tbl(class, id, false); -} - -static int get_byte(struct s5p_jpeg_buffer *buf); -static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word); -static void skip(struct s5p_jpeg_buffer *buf, long len); - -static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - struct s5p_jpeg_buffer jpeg_buffer; - unsigned int word; - int c, x, components; - - jpeg_buffer.size = 2; /* Ls */ - jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2; - jpeg_buffer.curr = 0; - - word = 0; - - if (get_word_be(&jpeg_buffer, &word)) - return; - jpeg_buffer.size = (long)word - 2; - jpeg_buffer.data += 2; - jpeg_buffer.curr = 0; - - components = get_byte(&jpeg_buffer); - if (components == -1) - return; - while (components--) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - x = get_byte(&jpeg_buffer); - if (x == -1) - return; - exynos4_jpeg_select_dec_h_tbl(jpeg->regs, c, - (((x >> 4) & 0x1) << 1) | (x & 0x1)); - } - -} - -static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - struct s5p_jpeg_buffer jpeg_buffer; - unsigned int word; - int c, i, n, j; - - for (j = 0; j < ctx->out_q.dht.n; ++j) { - jpeg_buffer.size = ctx->out_q.dht.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + - ctx->out_q.dht.marker[j]; - jpeg_buffer.curr = 0; - - word = 0; - while (jpeg_buffer.curr < jpeg_buffer.size) { - char id, class; - - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - id = c & 0xf; - class = (c >> 4) & 0xf; - n = 0; - for (i = 0; i < 16; ++i) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - word |= c << ((i % 4) * 8); - if ((i + 1) % 4 == 0) { - writel(word, jpeg->regs + - exynos4_huff_tbl_len(class, id) + - (i / 4) * 4); - word = 0; - } - n += c; - } - word = 0; - for (i = 0; i < n; ++i) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - word |= c << ((i % 4) * 8); - if ((i + 1) % 4 == 0) { - writel(word, jpeg->regs + - exynos4_huff_tbl_val(class, id) + - (i / 4) * 4); - word = 0; - } - } - if (i % 4) { - writel(word, jpeg->regs + - exynos4_huff_tbl_val(class, id) + (i / 4) * 4); - } - word = 0; - } - } -} - -static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - struct s5p_jpeg_buffer jpeg_buffer; - int c, x, components; - - jpeg_buffer.size = ctx->out_q.sof_len; - jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sof; - jpeg_buffer.curr = 0; - - skip(&jpeg_buffer, 5); /* P, Y, X */ - components = get_byte(&jpeg_buffer); - if (components == -1) - return; - - exynos4_jpeg_set_dec_components(jpeg->regs, components); - - while (components--) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - skip(&jpeg_buffer, 1); - x = get_byte(&jpeg_buffer); - if (x == -1) - return; - exynos4_jpeg_select_dec_q_tbl(jpeg->regs, c, x); - } -} - -static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - struct s5p_jpeg_buffer jpeg_buffer; - unsigned int word; - int c, i, j; - - for (j = 0; j < ctx->out_q.dqt.n; ++j) { - jpeg_buffer.size = ctx->out_q.dqt.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + - ctx->out_q.dqt.marker[j]; - jpeg_buffer.curr = 0; - - word = 0; - while (jpeg_buffer.size - jpeg_buffer.curr >= 65) { - char id; - - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - id = c & 0xf; - /* nonzero means extended mode - not supported */ - if ((c >> 4) & 0xf) - return; - for (i = 0; i < 64; ++i) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return; - word |= c << ((i % 4) * 8); - if ((i + 1) % 4 == 0) { - writel(word, jpeg->regs + - EXYNOS4_QTBL_CONTENT(id) + (i / 4) * 4); - word = 0; - } - } - word = 0; - } - } -} - -/* - * ============================================================================ - * Device file operations - * ============================================================================ - */ - -static int queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, - __u32 pixelformat, unsigned int fmt_type); -static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx); - -static int s5p_jpeg_open(struct file *file) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct video_device *vfd = video_devdata(file); - struct s5p_jpeg_ctx *ctx; - struct s5p_jpeg_fmt *out_fmt, *cap_fmt; - int ret = 0; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - if (mutex_lock_interruptible(&jpeg->lock)) { - ret = -ERESTARTSYS; - goto free; - } - - v4l2_fh_init(&ctx->fh, vfd); - /* Use separate control handler per file handle */ - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - ctx->jpeg = jpeg; - if (vfd == jpeg->vfd_encoder) { - ctx->mode = S5P_JPEG_ENCODE; - out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_RGB565, - FMT_TYPE_OUTPUT); - cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, - FMT_TYPE_CAPTURE); - } else { - ctx->mode = S5P_JPEG_DECODE; - out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, - FMT_TYPE_OUTPUT); - cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV, - FMT_TYPE_CAPTURE); - ctx->scale_factor = EXYNOS3250_DEC_SCALE_FACTOR_8_8; - } - - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - goto error; - } - - ctx->out_q.fmt = out_fmt; - ctx->cap_q.fmt = cap_fmt; - - ret = s5p_jpeg_controls_create(ctx); - if (ret < 0) - goto error; - - mutex_unlock(&jpeg->lock); - return 0; - -error: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - mutex_unlock(&jpeg->lock); -free: - kfree(ctx); - return ret; -} - -static int s5p_jpeg_release(struct file *file) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - - mutex_lock(&jpeg->lock); - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - mutex_unlock(&jpeg->lock); - - return 0; -} - -static const struct v4l2_file_operations s5p_jpeg_fops = { - .owner = THIS_MODULE, - .open = s5p_jpeg_open, - .release = s5p_jpeg_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -/* - * ============================================================================ - * video ioctl operations - * ============================================================================ - */ - -static int get_byte(struct s5p_jpeg_buffer *buf) -{ - if (buf->curr >= buf->size) - return -1; - - return ((unsigned char *)buf->data)[buf->curr++]; -} - -static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word) -{ - unsigned int temp; - int byte; - - byte = get_byte(buf); - if (byte == -1) - return -1; - temp = byte << 8; - byte = get_byte(buf); - if (byte == -1) - return -1; - *word = (unsigned int)byte | temp; - return 0; -} - -static void skip(struct s5p_jpeg_buffer *buf, long len) -{ - if (len <= 0) - return; - - while (len--) - get_byte(buf); -} - -static bool s5p_jpeg_subsampling_decode(struct s5p_jpeg_ctx *ctx, - unsigned int subsampling) -{ - unsigned int version; - - switch (subsampling) { - case 0x11: - ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444; - break; - case 0x21: - ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422; - break; - case 0x22: - ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420; - break; - case 0x33: - ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; - break; - case 0x41: - /* - * 4:1:1 subsampling only supported by 3250, 5420, and 5433 - * variants - */ - version = ctx->jpeg->variant->version; - if (version != SJPEG_EXYNOS3250 && - version != SJPEG_EXYNOS5420 && - version != SJPEG_EXYNOS5433) - return false; - - ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_411; - break; - default: - return false; - } - - return true; -} - -static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, - unsigned long buffer, unsigned long size, - struct s5p_jpeg_ctx *ctx) -{ - int c, components = 0, notfound, n_dht = 0, n_dqt = 0; - unsigned int height = 0, width = 0, word, subsampling = 0; - unsigned int sos = 0, sof = 0, sof_len = 0; - unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER]; - unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; - long length; - struct s5p_jpeg_buffer jpeg_buffer; - - jpeg_buffer.size = size; - jpeg_buffer.data = buffer; - jpeg_buffer.curr = 0; - - notfound = 1; - while (notfound || !sos) { - c = get_byte(&jpeg_buffer); - if (c == -1) - return false; - if (c != 0xff) - continue; - do - c = get_byte(&jpeg_buffer); - while (c == 0xff); - if (c == -1) - return false; - if (c == 0) - continue; - length = 0; - switch (c) { - /* JPEG_MARKER_SOF0: baseline JPEG */ - case JPEG_MARKER_SOF0: - if (get_word_be(&jpeg_buffer, &word)) - break; - length = (long)word - 2; - if (!length) - return false; - sof = jpeg_buffer.curr; /* after 0xffc0 */ - sof_len = length; - if (get_byte(&jpeg_buffer) == -1) - break; - if (get_word_be(&jpeg_buffer, &height)) - break; - if (get_word_be(&jpeg_buffer, &width)) - break; - components = get_byte(&jpeg_buffer); - if (components == -1) - break; - - if (components == 1) { - subsampling = 0x33; - } else { - skip(&jpeg_buffer, 1); - subsampling = get_byte(&jpeg_buffer); - skip(&jpeg_buffer, 1); - } - if (components > 3) - return false; - skip(&jpeg_buffer, components * 2); - notfound = 0; - break; - - case JPEG_MARKER_DQT: - if (get_word_be(&jpeg_buffer, &word)) - break; - length = (long)word - 2; - if (!length) - return false; - if (n_dqt >= S5P_JPEG_MAX_MARKER) - return false; - dqt[n_dqt] = jpeg_buffer.curr; /* after 0xffdb */ - dqt_len[n_dqt++] = length; - skip(&jpeg_buffer, length); - break; - - case JPEG_MARKER_DHT: - if (get_word_be(&jpeg_buffer, &word)) - break; - length = (long)word - 2; - if (!length) - return false; - if (n_dht >= S5P_JPEG_MAX_MARKER) - return false; - dht[n_dht] = jpeg_buffer.curr; /* after 0xffc4 */ - dht_len[n_dht++] = length; - skip(&jpeg_buffer, length); - break; - - case JPEG_MARKER_SOS: - sos = jpeg_buffer.curr - 2; /* 0xffda */ - break; - - /* skip payload-less markers */ - case JPEG_MARKER_RST ... JPEG_MARKER_RST + 7: - case JPEG_MARKER_SOI: - case JPEG_MARKER_EOI: - case JPEG_MARKER_TEM: - break; - - /* skip uninteresting payload markers */ - default: - if (get_word_be(&jpeg_buffer, &word)) - break; - length = (long)word - 2; - skip(&jpeg_buffer, length); - break; - } - } - - if (notfound || !sos || !s5p_jpeg_subsampling_decode(ctx, subsampling)) - return false; - - result->w = width; - result->h = height; - result->sos = sos; - result->dht.n = n_dht; - while (n_dht--) { - result->dht.marker[n_dht] = dht[n_dht]; - result->dht.len[n_dht] = dht_len[n_dht]; - } - result->dqt.n = n_dqt; - while (n_dqt--) { - result->dqt.marker[n_dqt] = dqt[n_dqt]; - result->dqt.len[n_dqt] = dqt_len[n_dqt]; - } - result->sof = sof; - result->sof_len = sof_len; - - return true; -} - -static int s5p_jpeg_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - if (ctx->mode == S5P_JPEG_ENCODE) { - strscpy(cap->driver, S5P_JPEG_M2M_NAME, - sizeof(cap->driver)); - strscpy(cap->card, S5P_JPEG_M2M_NAME " encoder", - sizeof(cap->card)); - } else { - strscpy(cap->driver, S5P_JPEG_M2M_NAME, - sizeof(cap->driver)); - strscpy(cap->card, S5P_JPEG_M2M_NAME " decoder", - sizeof(cap->card)); - } - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(ctx->jpeg->dev)); - return 0; -} - -static int enum_fmt(struct s5p_jpeg_ctx *ctx, - struct s5p_jpeg_fmt *sjpeg_formats, int n, - struct v4l2_fmtdesc *f, u32 type) -{ - int i, num = 0; - unsigned int fmt_ver_flag = ctx->jpeg->variant->fmt_ver_flag; - - for (i = 0; i < n; ++i) { - if (sjpeg_formats[i].flags & type && - sjpeg_formats[i].flags & fmt_ver_flag) { - /* index-th format of type type found ? */ - if (num == f->index) - break; - /* Correct type but haven't reached our index yet, - * just increment per-type index - */ - ++num; - } - } - - /* Format not found */ - if (i >= n) - return -EINVAL; - - f->pixelformat = sjpeg_formats[i].fourcc; - - return 0; -} - -static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_ENC_CAPTURE); - - return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_CAPTURE); -} - -static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_ENC_OUTPUT); - - return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_OUTPUT); -} - -static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, - enum v4l2_buf_type type) -{ - if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - return &ctx->out_q; - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return &ctx->cap_q; - - return NULL; -} - -static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) -{ - struct vb2_queue *vq; - struct s5p_jpeg_q_data *q_data = NULL; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); - - vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed) - return -EINVAL; - q_data = get_q_data(ct, f->type); - BUG_ON(q_data == NULL); - - pix->width = q_data->w; - pix->height = q_data->h; - pix->field = V4L2_FIELD_NONE; - pix->pixelformat = q_data->fmt->fourcc; - pix->bytesperline = 0; - if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { - u32 bpl = q_data->w; - - if (q_data->fmt->colplanes == 1) - bpl = (bpl * q_data->fmt->depth) >> 3; - pix->bytesperline = bpl; - } - pix->sizeimage = q_data->size; - - return 0; -} - -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, - u32 pixelformat, unsigned int fmt_type) -{ - unsigned int k, fmt_flag; - - if (ctx->mode == S5P_JPEG_ENCODE) - fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? - SJPEG_FMT_FLAG_ENC_OUTPUT : - SJPEG_FMT_FLAG_ENC_CAPTURE; - else - fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? - SJPEG_FMT_FLAG_DEC_OUTPUT : - SJPEG_FMT_FLAG_DEC_CAPTURE; - - for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { - struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; - - if (fmt->fourcc == pixelformat && - fmt->flags & fmt_flag && - fmt->flags & ctx->jpeg->variant->fmt_ver_flag) { - return fmt; - } - } - - return NULL; -} - -static void jpeg_bound_align_image(struct s5p_jpeg_ctx *ctx, - u32 *w, unsigned int wmin, unsigned int wmax, - unsigned int walign, - u32 *h, unsigned int hmin, unsigned int hmax, - unsigned int halign) -{ - int width, height, w_step, h_step; - - width = *w; - height = *h; - - w_step = 1 << walign; - h_step = 1 << halign; - - if (ctx->jpeg->variant->hw3250_compat) { - /* - * Rightmost and bottommost pixels are cropped by the - * Exynos3250/compatible JPEG IP for RGB formats, for the - * specific width and height values respectively. This - * assignment will result in v4l_bound_align_image returning - * dimensions reduced by 1 for the aforementioned cases. - */ - if (w_step == 4 && ((width & 3) == 1)) { - wmax = width; - hmax = height; - } - } - - v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0); - - if (*w < width && (*w + w_step) < wmax) - *w += w_step; - if (*h < height && (*h + h_step) < hmax) - *h += h_step; -} - -static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, - struct s5p_jpeg_ctx *ctx, int q_type) -{ - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; - else if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported - */ - if (q_type == FMT_TYPE_OUTPUT) - jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, - S5P_JPEG_MAX_WIDTH, 0, - &pix->height, S5P_JPEG_MIN_HEIGHT, - S5P_JPEG_MAX_HEIGHT, 0); - else - jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, - S5P_JPEG_MAX_WIDTH, fmt->h_align, - &pix->height, S5P_JPEG_MIN_HEIGHT, - S5P_JPEG_MAX_HEIGHT, fmt->v_align); - - if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { - if (pix->sizeimage <= 0) - pix->sizeimage = PAGE_SIZE; - pix->bytesperline = 0; - } else { - u32 bpl = pix->bytesperline; - - if (fmt->colplanes > 1 && bpl < pix->width) - bpl = pix->width; /* planar */ - - if (fmt->colplanes == 1 && /* packed */ - (bpl << 3) / fmt->depth < pix->width) - bpl = (pix->width * fmt->depth) >> 3; - - pix->bytesperline = bpl; - pix->sizeimage = (pix->width * pix->height * fmt->depth) >> 3; - } - - return 0; -} - -static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format *pix = &f->fmt.pix; - struct s5p_jpeg_fmt *fmt; - int ret; - - fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, - FMT_TYPE_CAPTURE); - if (!fmt) { - v4l2_err(&ctx->jpeg->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - if (!ctx->jpeg->variant->hw_ex4_compat || ctx->mode != S5P_JPEG_DECODE) - goto exit; - - /* - * The exynos4x12 device requires resulting YUV image - * subsampling not to be lower than the input jpeg subsampling. - * If this requirement is not met then downgrade the requested - * capture format to the one with subsampling equal to the input jpeg. - */ - if ((fmt->flags & SJPEG_FMT_NON_RGB) && - (fmt->subsampling < ctx->subsampling)) { - ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling, - fmt->fourcc, - &pix->pixelformat, - ctx); - if (ret < 0) - pix->pixelformat = V4L2_PIX_FMT_GREY; - - fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, - FMT_TYPE_CAPTURE); - } - - /* - * Decompression of a JPEG file with 4:2:0 subsampling and odd - * width to the YUV 4:2:0 compliant formats produces a raw image - * with broken luma component. Adjust capture format to RGB565 - * in such a case. - */ - if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 && - (ctx->out_q.w & 1) && - (pix->pixelformat == V4L2_PIX_FMT_NV12 || - pix->pixelformat == V4L2_PIX_FMT_NV21 || - pix->pixelformat == V4L2_PIX_FMT_YUV420)) { - pix->pixelformat = V4L2_PIX_FMT_RGB565; - fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, - FMT_TYPE_CAPTURE); - } - -exit: - return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE); -} - -static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - struct s5p_jpeg_fmt *fmt; - - fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, - FMT_TYPE_OUTPUT); - if (!fmt) { - v4l2_err(&ctx->jpeg->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT); -} - -static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, - struct v4l2_format *f, - int fmt_depth) -{ - struct v4l2_pix_format *pix = &f->fmt.pix; - u32 pix_fmt = f->fmt.pix.pixelformat; - int w = pix->width, h = pix->height, wh_align; - int padding = 0; - - if (pix_fmt == V4L2_PIX_FMT_RGB32 || - pix_fmt == V4L2_PIX_FMT_RGB565 || - pix_fmt == V4L2_PIX_FMT_NV24 || - pix_fmt == V4L2_PIX_FMT_NV42 || - pix_fmt == V4L2_PIX_FMT_NV12 || - pix_fmt == V4L2_PIX_FMT_NV21 || - pix_fmt == V4L2_PIX_FMT_YUV420) - wh_align = 4; - else - wh_align = 1; - - jpeg_bound_align_image(ctx, &w, S5P_JPEG_MIN_WIDTH, - S5P_JPEG_MAX_WIDTH, wh_align, - &h, S5P_JPEG_MIN_HEIGHT, - S5P_JPEG_MAX_HEIGHT, wh_align); - - if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) - padding = PAGE_SIZE; - - return (w * h * fmt_depth >> 3) + padding; -} - -static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, - struct v4l2_rect *r); - -static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) -{ - struct vb2_queue *vq; - struct s5p_jpeg_q_data *q_data = NULL; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_ctrl *ctrl_subs; - struct v4l2_rect scale_rect; - unsigned int f_type; - - vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - q_data = get_q_data(ct, f->type); - BUG_ON(q_data == NULL); - - if (vb2_is_busy(vq)) { - v4l2_err(&ct->jpeg->v4l2_dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - f_type = V4L2_TYPE_IS_OUTPUT(f->type) ? - FMT_TYPE_OUTPUT : FMT_TYPE_CAPTURE; - - q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type); - if (ct->mode == S5P_JPEG_ENCODE || - (ct->mode == S5P_JPEG_DECODE && - q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)) { - q_data->w = pix->width; - q_data->h = pix->height; - } - if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { - /* - * During encoding Exynos4x12 SoCs access wider memory area - * than it results from Image_x and Image_y values written to - * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu - * page fault calculate proper buffer size in such a case. - */ - if (ct->jpeg->variant->hw_ex4_compat && - f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE) - q_data->size = exynos4_jpeg_get_output_buffer_size(ct, - f, - q_data->fmt->depth); - else - q_data->size = q_data->w * q_data->h * - q_data->fmt->depth >> 3; - } else { - q_data->size = pix->sizeimage; - } - - if (f_type == FMT_TYPE_OUTPUT) { - ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler, - V4L2_CID_JPEG_CHROMA_SUBSAMPLING); - if (ctrl_subs) - v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling); - ct->crop_altered = false; - } - - /* - * For decoding init crop_rect with capture buffer dimmensions which - * contain aligned dimensions of the input JPEG image and do it only - * if crop rectangle hasn't been altered by the user space e.g. with - * S_SELECTION ioctl. For encoding assign output buffer dimensions. - */ - if (!ct->crop_altered && - ((ct->mode == S5P_JPEG_DECODE && f_type == FMT_TYPE_CAPTURE) || - (ct->mode == S5P_JPEG_ENCODE && f_type == FMT_TYPE_OUTPUT))) { - ct->crop_rect.width = pix->width; - ct->crop_rect.height = pix->height; - } - - /* - * Prevent downscaling to YUV420 format by more than 2 - * for Exynos3250/compatible SoC as it produces broken raw image - * in such cases. - */ - if (ct->mode == S5P_JPEG_DECODE && - f_type == FMT_TYPE_CAPTURE && - ct->jpeg->variant->hw3250_compat && - pix->pixelformat == V4L2_PIX_FMT_YUV420 && - ct->scale_factor > 2) { - scale_rect.width = ct->out_q.w / 2; - scale_rect.height = ct->out_q.h / 2; - exynos3250_jpeg_try_downscale(ct, &scale_rect); - } - - return 0; -} - -static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - int ret; - - ret = s5p_jpeg_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); -} - -static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - int ret; - - ret = s5p_jpeg_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); -} - -static int s5p_jpeg_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - if (sub->type == V4L2_EVENT_SOURCE_CHANGE) - return v4l2_src_change_event_subscribe(fh, sub); - - return -EINVAL; -} - -static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, - struct v4l2_rect *r) -{ - int w_ratio, h_ratio, scale_factor, cur_ratio, i; - - w_ratio = ctx->out_q.w / r->width; - h_ratio = ctx->out_q.h / r->height; - - scale_factor = w_ratio > h_ratio ? w_ratio : h_ratio; - scale_factor = clamp_val(scale_factor, 1, 8); - - /* Align scale ratio to the nearest power of 2 */ - for (i = 0; i <= 3; ++i) { - cur_ratio = 1 << i; - if (scale_factor <= cur_ratio) { - ctx->scale_factor = cur_ratio; - break; - } - } - - r->width = round_down(ctx->out_q.w / ctx->scale_factor, 2); - r->height = round_down(ctx->out_q.h / ctx->scale_factor, 2); - - ctx->crop_rect.width = r->width; - ctx->crop_rect.height = r->height; - ctx->crop_rect.left = 0; - ctx->crop_rect.top = 0; - - ctx->crop_altered = true; - - return 0; -} - -static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, - struct v4l2_rect *r) -{ - struct v4l2_rect base_rect; - int w_step, h_step; - - switch (ctx->cap_q.fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - w_step = 1; - h_step = 2; - break; - case V4L2_PIX_FMT_YUV420: - w_step = 2; - h_step = 2; - break; - default: - w_step = 1; - h_step = 1; - break; - } - - base_rect.top = 0; - base_rect.left = 0; - base_rect.width = ctx->out_q.w; - base_rect.height = ctx->out_q.h; - - r->width = round_down(r->width, w_step); - r->height = round_down(r->height, h_step); - r->left = round_down(r->left, 2); - r->top = round_down(r->top, 2); - - if (!v4l2_rect_enclosed(r, &base_rect)) - return -EINVAL; - - ctx->crop_rect.left = r->left; - ctx->crop_rect.top = r->top; - ctx->crop_rect.width = r->width; - ctx->crop_rect.height = r->height; - - ctx->crop_altered = true; - - return 0; -} - -/* - * V4L2 controls - */ - -static int s5p_jpeg_g_selection(struct file *file, void *priv, - struct v4l2_selection *s) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* For JPEG blob active == default == bounds */ - switch (s->target) { - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - s->r.width = ctx->out_q.w; - s->r.height = ctx->out_q.h; - s->r.left = 0; - s->r.top = 0; - break; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: - s->r.width = ctx->crop_rect.width; - s->r.height = ctx->crop_rect.height; - s->r.left = ctx->crop_rect.left; - s->r.top = ctx->crop_rect.top; - break; - default: - return -EINVAL; - } - return 0; -} - -/* - * V4L2 controls - */ -static int s5p_jpeg_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - struct v4l2_rect *rect = &s->r; - int ret = -EINVAL; - - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (s->target == V4L2_SEL_TGT_COMPOSE) { - if (ctx->mode != S5P_JPEG_DECODE) - return -EINVAL; - if (ctx->jpeg->variant->hw3250_compat) - ret = exynos3250_jpeg_try_downscale(ctx, rect); - } else if (s->target == V4L2_SEL_TGT_CROP) { - if (ctx->mode != S5P_JPEG_ENCODE) - return -EINVAL; - if (ctx->jpeg->variant->hw3250_compat) - ret = exynos3250_jpeg_try_crop(ctx, rect); - } - - return ret; -} - -static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); - struct s5p_jpeg *jpeg = ctx->jpeg; - unsigned long flags; - - switch (ctrl->id) { - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: - spin_lock_irqsave(&jpeg->slock, flags); - ctrl->val = s5p_jpeg_to_user_subsampling(ctx); - spin_unlock_irqrestore(&jpeg->slock, flags); - break; - } - - return 0; -} - -static int s5p_jpeg_adjust_subs_ctrl(struct s5p_jpeg_ctx *ctx, int *ctrl_val) -{ - switch (ctx->jpeg->variant->version) { - case SJPEG_S5P: - return 0; - case SJPEG_EXYNOS3250: - case SJPEG_EXYNOS5420: - /* - * The exynos3250/compatible device can produce JPEG image only - * of 4:4:4 subsampling when given RGB32 source image. - */ - if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) - *ctrl_val = 0; - break; - case SJPEG_EXYNOS4: - /* - * The exynos4x12 device requires input raw image fourcc - * to be V4L2_PIX_FMT_GREY if gray jpeg format - * is to be set. - */ - if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY && - *ctrl_val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) - return -EINVAL; - break; - } - - /* - * The exynos4x12 and exynos3250/compatible devices require resulting - * jpeg subsampling not to be lower than the input raw image - * subsampling. - */ - if (ctx->out_q.fmt->subsampling > *ctrl_val) - *ctrl_val = ctx->out_q.fmt->subsampling; - - return 0; -} - -static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&ctx->jpeg->slock, flags); - - if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) - ret = s5p_jpeg_adjust_subs_ctrl(ctx, &ctrl->val); - - spin_unlock_irqrestore(&ctx->jpeg->slock, flags); - return ret; -} - -static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); - unsigned long flags; - - spin_lock_irqsave(&ctx->jpeg->slock, flags); - - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->compr_quality = ctrl->val; - break; - case V4L2_CID_JPEG_RESTART_INTERVAL: - ctx->restart_interval = ctrl->val; - break; - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: - ctx->subsampling = ctrl->val; - break; - } - - spin_unlock_irqrestore(&ctx->jpeg->slock, flags); - return 0; -} - -static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = { - .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl, - .try_ctrl = s5p_jpeg_try_ctrl, - .s_ctrl = s5p_jpeg_s_ctrl, -}; - -static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) -{ - unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */ - struct v4l2_ctrl *ctrl; - int ret; - - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); - - if (ctx->mode == S5P_JPEG_ENCODE) { - v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - 0, 3, 1, S5P_JPEG_COMPR_QUAL_WORST); - - v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, - V4L2_CID_JPEG_RESTART_INTERVAL, - 0, 0xffff, 1, 0); - if (ctx->jpeg->variant->version == SJPEG_S5P) - mask = ~0x06; /* 422, 420 */ - } - - ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, - V4L2_CID_JPEG_CHROMA_SUBSAMPLING, - V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, - V4L2_JPEG_CHROMA_SUBSAMPLING_422); - - if (ctx->ctrl_handler.error) { - ret = ctx->ctrl_handler.error; - goto error_free; - } - - if (ctx->mode == S5P_JPEG_DECODE) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | - V4L2_CTRL_FLAG_READ_ONLY; - - ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); - if (ret < 0) - goto error_free; - - return ret; - -error_free: - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - return ret; -} - -static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { - .vidioc_querycap = s5p_jpeg_querycap, - - .vidioc_enum_fmt_vid_cap = s5p_jpeg_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out = s5p_jpeg_enum_fmt_vid_out, - - .vidioc_g_fmt_vid_cap = s5p_jpeg_g_fmt, - .vidioc_g_fmt_vid_out = s5p_jpeg_g_fmt, - - .vidioc_try_fmt_vid_cap = s5p_jpeg_try_fmt_vid_cap, - .vidioc_try_fmt_vid_out = s5p_jpeg_try_fmt_vid_out, - - .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap, - .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_g_selection = s5p_jpeg_g_selection, - .vidioc_s_selection = s5p_jpeg_s_selection, - - .vidioc_subscribe_event = s5p_jpeg_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* - * ============================================================================ - * mem2mem callbacks - * ============================================================================ - */ - -static void s5p_jpeg_device_run(void *priv) -{ - struct s5p_jpeg_ctx *ctx = priv; - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - unsigned long src_addr, dst_addr, flags; - - spin_lock_irqsave(&ctx->jpeg->slock, flags); - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - - s5p_jpeg_reset(jpeg->regs); - s5p_jpeg_poweron(jpeg->regs); - s5p_jpeg_proc_mode(jpeg->regs, ctx->mode); - if (ctx->mode == S5P_JPEG_ENCODE) { - if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565) - s5p_jpeg_input_raw_mode(jpeg->regs, - S5P_JPEG_RAW_IN_565); - else - s5p_jpeg_input_raw_mode(jpeg->regs, - S5P_JPEG_RAW_IN_422); - s5p_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); - s5p_jpeg_dri(jpeg->regs, ctx->restart_interval); - s5p_jpeg_x(jpeg->regs, ctx->out_q.w); - s5p_jpeg_y(jpeg->regs, ctx->out_q.h); - s5p_jpeg_imgadr(jpeg->regs, src_addr); - s5p_jpeg_jpgadr(jpeg->regs, dst_addr); - - /* ultimately comes from sizeimage from userspace */ - s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); - - /* JPEG RGB to YCbCr conversion matrix */ - s5p_jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); - s5p_jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); - s5p_jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); - s5p_jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); - s5p_jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); - s5p_jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); - s5p_jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); - s5p_jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); - s5p_jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); - - /* - * JPEG IP allows storing 4 quantization tables - * We fill table 0 for luma and table 1 for chroma - */ - s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); - s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); - /* use table 0 for Y */ - s5p_jpeg_qtbl(jpeg->regs, 1, 0); - /* use table 1 for Cb and Cr*/ - s5p_jpeg_qtbl(jpeg->regs, 2, 1); - s5p_jpeg_qtbl(jpeg->regs, 3, 1); - - /* Y, Cb, Cr use Huffman table 0 */ - s5p_jpeg_htbl_ac(jpeg->regs, 1); - s5p_jpeg_htbl_dc(jpeg->regs, 1); - s5p_jpeg_htbl_ac(jpeg->regs, 2); - s5p_jpeg_htbl_dc(jpeg->regs, 2); - s5p_jpeg_htbl_ac(jpeg->regs, 3); - s5p_jpeg_htbl_dc(jpeg->regs, 3); - } else { /* S5P_JPEG_DECODE */ - s5p_jpeg_rst_int_enable(jpeg->regs, true); - s5p_jpeg_data_num_int_enable(jpeg->regs, true); - s5p_jpeg_final_mcu_num_int_enable(jpeg->regs, true); - if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) - s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); - else - s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); - s5p_jpeg_jpgadr(jpeg->regs, src_addr); - s5p_jpeg_imgadr(jpeg->regs, dst_addr); - } - - s5p_jpeg_start(jpeg->regs); - - spin_unlock_irqrestore(&ctx->jpeg->slock, flags); -} - -static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct s5p_jpeg_fmt *fmt; - struct vb2_v4l2_buffer *vb; - struct s5p_jpeg_addr jpeg_addr = {}; - u32 pix_size, padding_bytes = 0; - - jpeg_addr.cb = 0; - jpeg_addr.cr = 0; - - pix_size = ctx->cap_q.w * ctx->cap_q.h; - - if (ctx->mode == S5P_JPEG_ENCODE) { - vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - fmt = ctx->out_q.fmt; - if (ctx->out_q.w % 2 && fmt->h_align > 0) - padding_bytes = ctx->out_q.h; - } else { - fmt = ctx->cap_q.fmt; - vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - } - - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); - - if (fmt->colplanes == 2) { - jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; - } else if (fmt->colplanes == 3) { - jpeg_addr.cb = jpeg_addr.y + pix_size; - if (fmt->fourcc == V4L2_PIX_FMT_YUV420) - jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; - else - jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; - } - - exynos4_jpeg_set_frame_buf_address(jpeg->regs, &jpeg_addr); -} - -static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb; - unsigned int jpeg_addr = 0; - - if (ctx->mode == S5P_JPEG_ENCODE) - vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - else - vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - - jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); - if (jpeg->variant->version == SJPEG_EXYNOS5433 && - ctx->mode == S5P_JPEG_DECODE) - jpeg_addr += ctx->out_q.sos; - exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr); -} - -static inline void exynos4_jpeg_set_img_fmt(void __iomem *base, - unsigned int img_fmt) -{ - __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS4); -} - -static inline void exynos5433_jpeg_set_img_fmt(void __iomem *base, - unsigned int img_fmt) -{ - __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS5433); -} - -static inline void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, - unsigned int out_fmt) -{ - __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS4); -} - -static inline void exynos5433_jpeg_set_enc_out_fmt(void __iomem *base, - unsigned int out_fmt) -{ - __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS5433); -} - -static void exynos4_jpeg_device_run(void *priv) -{ - struct s5p_jpeg_ctx *ctx = priv; - struct s5p_jpeg *jpeg = ctx->jpeg; - unsigned int bitstream_size; - unsigned long flags; - - spin_lock_irqsave(&jpeg->slock, flags); - - if (ctx->mode == S5P_JPEG_ENCODE) { - exynos4_jpeg_sw_reset(jpeg->regs); - exynos4_jpeg_set_interrupt(jpeg->regs, jpeg->variant->version); - exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); - - exynos4_jpeg_set_huff_tbl(jpeg->regs); - - /* - * JPEG IP allows storing 4 quantization tables - * We fill table 0 for luma and table 1 for chroma - */ - exynos4_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); - exynos4_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); - - exynos4_jpeg_set_encode_tbl_select(jpeg->regs, - ctx->compr_quality); - exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, - ctx->cap_q.h); - - if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) { - exynos4_jpeg_set_enc_out_fmt(jpeg->regs, - ctx->subsampling); - exynos4_jpeg_set_img_fmt(jpeg->regs, - ctx->out_q.fmt->fourcc); - } else { - exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, - ctx->subsampling); - exynos5433_jpeg_set_img_fmt(jpeg->regs, - ctx->out_q.fmt->fourcc); - } - exynos4_jpeg_set_img_addr(ctx); - exynos4_jpeg_set_jpeg_addr(ctx); - exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs, - ctx->out_q.fmt->fourcc); - } else { - exynos4_jpeg_sw_reset(jpeg->regs); - exynos4_jpeg_set_interrupt(jpeg->regs, - jpeg->variant->version); - exynos4_jpeg_set_img_addr(ctx); - exynos4_jpeg_set_jpeg_addr(ctx); - - if (jpeg->variant->version == SJPEG_EXYNOS5433) { - exynos4_jpeg_parse_huff_tbl(ctx); - exynos4_jpeg_parse_decode_h_tbl(ctx); - - exynos4_jpeg_parse_q_tbl(ctx); - exynos4_jpeg_parse_decode_q_tbl(ctx); - - exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); - - exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, - ctx->cap_q.h); - exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, - ctx->subsampling); - exynos5433_jpeg_set_img_fmt(jpeg->regs, - ctx->cap_q.fmt->fourcc); - bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 16); - } else { - exynos4_jpeg_set_img_fmt(jpeg->regs, - ctx->cap_q.fmt->fourcc); - bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); - } - - exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size); - } - - exynos4_jpeg_set_sys_int_enable(jpeg->regs, 1); - exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode); - - spin_unlock_irqrestore(&jpeg->slock, flags); -} - -static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct s5p_jpeg_fmt *fmt; - struct vb2_v4l2_buffer *vb; - struct s5p_jpeg_addr jpeg_addr = {}; - u32 pix_size; - - pix_size = ctx->cap_q.w * ctx->cap_q.h; - - if (ctx->mode == S5P_JPEG_ENCODE) { - vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - fmt = ctx->out_q.fmt; - } else { - vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - fmt = ctx->cap_q.fmt; - } - - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); - - if (fmt->colplanes == 2) { - jpeg_addr.cb = jpeg_addr.y + pix_size; - } else if (fmt->colplanes == 3) { - jpeg_addr.cb = jpeg_addr.y + pix_size; - if (fmt->fourcc == V4L2_PIX_FMT_YUV420) - jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; - else - jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; - } - - exynos3250_jpeg_imgadr(jpeg->regs, &jpeg_addr); -} - -static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_v4l2_buffer *vb; - unsigned int jpeg_addr = 0; - - if (ctx->mode == S5P_JPEG_ENCODE) - vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - else - vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - - jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); - exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr); -} - -static void exynos3250_jpeg_device_run(void *priv) -{ - struct s5p_jpeg_ctx *ctx = priv; - struct s5p_jpeg *jpeg = ctx->jpeg; - unsigned long flags; - - spin_lock_irqsave(&ctx->jpeg->slock, flags); - - exynos3250_jpeg_reset(jpeg->regs); - exynos3250_jpeg_set_dma_num(jpeg->regs); - exynos3250_jpeg_poweron(jpeg->regs); - exynos3250_jpeg_clk_set(jpeg->regs); - exynos3250_jpeg_proc_mode(jpeg->regs, ctx->mode); - - if (ctx->mode == S5P_JPEG_ENCODE) { - exynos3250_jpeg_input_raw_fmt(jpeg->regs, - ctx->out_q.fmt->fourcc); - exynos3250_jpeg_dri(jpeg->regs, ctx->restart_interval); - - /* - * JPEG IP allows storing 4 quantization tables - * We fill table 0 for luma and table 1 for chroma - */ - s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); - s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); - /* use table 0 for Y */ - exynos3250_jpeg_qtbl(jpeg->regs, 1, 0); - /* use table 1 for Cb and Cr*/ - exynos3250_jpeg_qtbl(jpeg->regs, 2, 1); - exynos3250_jpeg_qtbl(jpeg->regs, 3, 1); - - /* - * Some SoCs require setting Huffman tables before each run - */ - if (jpeg->variant->htbl_reinit) { - s5p_jpeg_set_hdctbl(jpeg->regs); - s5p_jpeg_set_hdctblg(jpeg->regs); - s5p_jpeg_set_hactbl(jpeg->regs); - s5p_jpeg_set_hactblg(jpeg->regs); - } - - /* Y, Cb, Cr use Huffman table 0 */ - exynos3250_jpeg_htbl_ac(jpeg->regs, 1); - exynos3250_jpeg_htbl_dc(jpeg->regs, 1); - exynos3250_jpeg_htbl_ac(jpeg->regs, 2); - exynos3250_jpeg_htbl_dc(jpeg->regs, 2); - exynos3250_jpeg_htbl_ac(jpeg->regs, 3); - exynos3250_jpeg_htbl_dc(jpeg->regs, 3); - - exynos3250_jpeg_set_x(jpeg->regs, ctx->crop_rect.width); - exynos3250_jpeg_set_y(jpeg->regs, ctx->crop_rect.height); - exynos3250_jpeg_stride(jpeg->regs, ctx->out_q.fmt->fourcc, - ctx->out_q.w); - exynos3250_jpeg_offset(jpeg->regs, ctx->crop_rect.left, - ctx->crop_rect.top); - exynos3250_jpeg_set_img_addr(ctx); - exynos3250_jpeg_set_jpeg_addr(ctx); - exynos3250_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); - - /* ultimately comes from sizeimage from userspace */ - exynos3250_jpeg_enc_stream_bound(jpeg->regs, ctx->cap_q.size); - - if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565 || - ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565X || - ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) - exynos3250_jpeg_set_y16(jpeg->regs, true); - } else { - exynos3250_jpeg_set_img_addr(ctx); - exynos3250_jpeg_set_jpeg_addr(ctx); - exynos3250_jpeg_stride(jpeg->regs, ctx->cap_q.fmt->fourcc, - ctx->cap_q.w); - exynos3250_jpeg_offset(jpeg->regs, 0, 0); - exynos3250_jpeg_dec_scaling_ratio(jpeg->regs, - ctx->scale_factor); - exynos3250_jpeg_dec_stream_size(jpeg->regs, ctx->out_q.size); - exynos3250_jpeg_output_raw_fmt(jpeg->regs, - ctx->cap_q.fmt->fourcc); - } - - exynos3250_jpeg_interrupts_enable(jpeg->regs); - - /* JPEG RGB to YCbCr conversion matrix */ - exynos3250_jpeg_coef(jpeg->regs, ctx->mode); - - exynos3250_jpeg_set_timer(jpeg->regs, EXYNOS3250_IRQ_TIMEOUT); - jpeg->irq_status = 0; - exynos3250_jpeg_start(jpeg->regs); - - spin_unlock_irqrestore(&ctx->jpeg->slock, flags); -} - -static int s5p_jpeg_job_ready(void *priv) -{ - struct s5p_jpeg_ctx *ctx = priv; - - if (ctx->mode == S5P_JPEG_DECODE) { - /* - * We have only one input buffer and one output buffer. If there - * is a resolution change event, no need to continue decoding. - */ - if (ctx->state == JPEGCTX_RESOLUTION_CHANGE) - return 0; - - return ctx->hdr_parsed; - } - - return 1; -} - -static const struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { - .device_run = s5p_jpeg_device_run, - .job_ready = s5p_jpeg_job_ready, -}; - -static const struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { - .device_run = exynos3250_jpeg_device_run, - .job_ready = s5p_jpeg_job_ready, -}; - -static const struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { - .device_run = exynos4_jpeg_device_run, - .job_ready = s5p_jpeg_job_ready, -}; - -/* - * ============================================================================ - * Queue operations - * ============================================================================ - */ - -static int s5p_jpeg_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - struct s5p_jpeg_q_data *q_data = NULL; - unsigned int size, count = *nbuffers; - - q_data = get_q_data(ctx, vq->type); - BUG_ON(q_data == NULL); - - size = q_data->size; - - /* - * header is parsed during decoding and parsed information stored - * in the context so we do not allow another buffer to overwrite it - */ - if (ctx->mode == S5P_JPEG_DECODE) - count = 1; - - *nbuffers = count; - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct s5p_jpeg_q_data *q_data = NULL; - - q_data = get_q_data(ctx, vb->vb2_queue->type); - BUG_ON(q_data == NULL); - - if (vb2_plane_size(vb, 0) < q_data->size) { - pr_err("%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->size); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, q_data->size); - - return 0; -} - -static void s5p_jpeg_set_capture_queue_data(struct s5p_jpeg_ctx *ctx) -{ - struct s5p_jpeg_q_data *q_data = &ctx->cap_q; - - q_data->w = ctx->out_q.w; - q_data->h = ctx->out_q.h; - - /* - * This call to jpeg_bound_align_image() takes care of width and - * height values alignment when user space calls the QBUF of - * OUTPUT buffer after the S_FMT of CAPTURE buffer. - * Please note that on Exynos4x12 SoCs, resigning from executing - * S_FMT on capture buffer for each JPEG image can result in a - * hardware hangup if subsampling is lower than the one of input - * JPEG. - */ - jpeg_bound_align_image(ctx, &q_data->w, S5P_JPEG_MIN_WIDTH, - S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align, - &q_data->h, S5P_JPEG_MIN_HEIGHT, - S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align); - - q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; -} - -static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - if (ctx->mode == S5P_JPEG_DECODE && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - static const struct v4l2_event ev_src_ch = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, - }; - struct vb2_queue *dst_vq; - u32 ori_w; - u32 ori_h; - - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - ori_w = ctx->out_q.w; - ori_h = ctx->out_q.h; - - ctx->hdr_parsed = s5p_jpeg_parse_hdr(&ctx->out_q, - (unsigned long)vb2_plane_vaddr(vb, 0), - min((unsigned long)ctx->out_q.size, - vb2_get_plane_payload(vb, 0)), ctx); - if (!ctx->hdr_parsed) { - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - return; - } - - /* - * If there is a resolution change event, only update capture - * queue when it is not streaming. Otherwise, update it in - * STREAMOFF. See s5p_jpeg_stop_streaming for detail. - */ - if (ctx->out_q.w != ori_w || ctx->out_q.h != ori_h) { - v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); - if (vb2_is_streaming(dst_vq)) - ctx->state = JPEGCTX_RESOLUTION_CHANGE; - else - s5p_jpeg_set_capture_queue_data(ctx); - } - } - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); - - return pm_runtime_resume_and_get(ctx->jpeg->dev); -} - -static void s5p_jpeg_stop_streaming(struct vb2_queue *q) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); - - /* - * STREAMOFF is an acknowledgment for resolution change event. - * Before STREAMOFF, we still have to return the old resolution and - * subsampling. Update capture queue when the stream is off. - */ - if (ctx->state == JPEGCTX_RESOLUTION_CHANGE && - q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - s5p_jpeg_set_capture_queue_data(ctx); - ctx->state = JPEGCTX_RUNNING; - } - - pm_runtime_put(ctx->jpeg->dev); -} - -static const struct vb2_ops s5p_jpeg_qops = { - .queue_setup = s5p_jpeg_queue_setup, - .buf_prepare = s5p_jpeg_buf_prepare, - .buf_queue = s5p_jpeg_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = s5p_jpeg_start_streaming, - .stop_streaming = s5p_jpeg_stop_streaming, -}; - -static int queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct s5p_jpeg_ctx *ctx = priv; - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = ctx; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->ops = &s5p_jpeg_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->jpeg->lock; - src_vq->dev = ctx->jpeg->dev; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = ctx; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &s5p_jpeg_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->jpeg->lock; - dst_vq->dev = ctx->jpeg->dev; - - return vb2_queue_init(dst_vq); -} - -/* - * ============================================================================ - * ISR - * ============================================================================ - */ - -static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) -{ - struct s5p_jpeg *jpeg = dev_id; - struct s5p_jpeg_ctx *curr_ctx; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - unsigned long payload_size = 0; - enum vb2_buffer_state state = VB2_BUF_STATE_DONE; - bool enc_jpeg_too_large = false; - bool timer_elapsed = false; - bool op_completed = false; - - spin_lock(&jpeg->slock); - - curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - - src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); - - if (curr_ctx->mode == S5P_JPEG_ENCODE) - enc_jpeg_too_large = s5p_jpeg_enc_stream_stat(jpeg->regs); - timer_elapsed = s5p_jpeg_timer_stat(jpeg->regs); - op_completed = s5p_jpeg_result_stat_ok(jpeg->regs); - if (curr_ctx->mode == S5P_JPEG_DECODE) - op_completed = op_completed && - s5p_jpeg_stream_stat_ok(jpeg->regs); - - if (enc_jpeg_too_large) { - state = VB2_BUF_STATE_ERROR; - s5p_jpeg_clear_enc_stream_stat(jpeg->regs); - } else if (timer_elapsed) { - state = VB2_BUF_STATE_ERROR; - s5p_jpeg_clear_timer_stat(jpeg->regs); - } else if (!op_completed) { - state = VB2_BUF_STATE_ERROR; - } else { - payload_size = s5p_jpeg_compressed_size(jpeg->regs); - } - - dst_buf->timecode = src_buf->timecode; - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->flags |= - src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - - v4l2_m2m_buf_done(src_buf, state); - if (curr_ctx->mode == S5P_JPEG_ENCODE) - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); - v4l2_m2m_buf_done(dst_buf, state); - - curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs); - spin_unlock(&jpeg->slock); - - s5p_jpeg_clear_int(jpeg->regs); - - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - return IRQ_HANDLED; -} - -static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) -{ - unsigned int int_status; - struct vb2_v4l2_buffer *src_vb, *dst_vb; - struct s5p_jpeg *jpeg = priv; - struct s5p_jpeg_ctx *curr_ctx; - unsigned long payload_size = 0; - - spin_lock(&jpeg->slock); - - exynos4_jpeg_set_sys_int_enable(jpeg->regs, 0); - - curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - - src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); - - int_status = exynos4_jpeg_get_int_status(jpeg->regs); - - if (int_status) { - switch (int_status & 0x1f) { - case 0x1: - jpeg->irq_ret = ERR_PROT; - break; - case 0x2: - jpeg->irq_ret = OK_ENC_OR_DEC; - break; - case 0x4: - jpeg->irq_ret = ERR_DEC_INVALID_FORMAT; - break; - case 0x8: - jpeg->irq_ret = ERR_MULTI_SCAN; - break; - case 0x10: - jpeg->irq_ret = ERR_FRAME; - break; - default: - jpeg->irq_ret = ERR_UNKNOWN; - break; - } - } else { - jpeg->irq_ret = ERR_UNKNOWN; - } - - if (jpeg->irq_ret == OK_ENC_OR_DEC) { - if (curr_ctx->mode == S5P_JPEG_ENCODE) { - payload_size = exynos4_jpeg_get_stream_size(jpeg->regs); - vb2_set_plane_payload(&dst_vb->vb2_buf, - 0, payload_size); - } - v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); - } else { - v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); - v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); - } - - if (jpeg->variant->version == SJPEG_EXYNOS4) - curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); - - exynos4_jpeg_set_enc_dec_mode(jpeg->regs, S5P_JPEG_DISABLE); - - spin_unlock(&jpeg->slock); - - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - return IRQ_HANDLED; -} - -static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) -{ - struct s5p_jpeg *jpeg = dev_id; - struct s5p_jpeg_ctx *curr_ctx; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - unsigned long payload_size = 0; - enum vb2_buffer_state state = VB2_BUF_STATE_DONE; - bool interrupt_timeout = false; - bool stream_error = false; - u32 irq_status; - - spin_lock(&jpeg->slock); - - irq_status = exynos3250_jpeg_get_timer_status(jpeg->regs); - if (irq_status & EXYNOS3250_TIMER_INT_STAT) { - exynos3250_jpeg_clear_timer_status(jpeg->regs); - interrupt_timeout = true; - dev_err(jpeg->dev, "Interrupt timeout occurred.\n"); - } - - irq_status = exynos3250_jpeg_get_int_status(jpeg->regs); - exynos3250_jpeg_clear_int_status(jpeg->regs, irq_status); - - jpeg->irq_status |= irq_status; - - if (jpeg->variant->version == SJPEG_EXYNOS5420 && - irq_status & EXYNOS3250_STREAM_STAT) { - stream_error = true; - dev_err(jpeg->dev, "Syntax error or unrecoverable error occurred.\n"); - } - - curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - - if (!curr_ctx) - goto exit_unlock; - - if ((irq_status & EXYNOS3250_HEADER_STAT) && - (curr_ctx->mode == S5P_JPEG_DECODE)) { - exynos3250_jpeg_rstart(jpeg->regs); - goto exit_unlock; - } - - if (jpeg->irq_status & (EXYNOS3250_JPEG_DONE | - EXYNOS3250_WDMA_DONE | - EXYNOS3250_RDMA_DONE | - EXYNOS3250_RESULT_STAT)) - payload_size = exynos3250_jpeg_compressed_size(jpeg->regs); - else if (interrupt_timeout || stream_error) - state = VB2_BUF_STATE_ERROR; - else - goto exit_unlock; - - src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); - - dst_buf->timecode = src_buf->timecode; - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - - v4l2_m2m_buf_done(src_buf, state); - if (curr_ctx->mode == S5P_JPEG_ENCODE) - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); - v4l2_m2m_buf_done(dst_buf, state); - - curr_ctx->subsampling = - exynos3250_jpeg_get_subsampling_mode(jpeg->regs); - - spin_unlock(&jpeg->slock); - - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - return IRQ_HANDLED; - -exit_unlock: - spin_unlock(&jpeg->slock); - return IRQ_HANDLED; -} - -static void *jpeg_get_drv_data(struct device *dev); - -/* - * ============================================================================ - * Driver basic infrastructure - * ============================================================================ - */ - -static int s5p_jpeg_probe(struct platform_device *pdev) -{ - struct s5p_jpeg *jpeg; - int i, ret; - - /* JPEG IP abstraction struct */ - jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); - if (!jpeg) - return -ENOMEM; - - jpeg->variant = jpeg_get_drv_data(&pdev->dev); - if (!jpeg->variant) - return -ENODEV; - - mutex_init(&jpeg->lock); - spin_lock_init(&jpeg->slock); - jpeg->dev = &pdev->dev; - - /* memory-mapped registers */ - jpeg->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(jpeg->regs)) - return PTR_ERR(jpeg->regs); - - /* interrupt service routine registration */ - jpeg->irq = ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); - return ret; - } - - ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq, - 0, dev_name(&pdev->dev), jpeg); - if (ret) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq); - return ret; - } - - /* clocks */ - for (i = 0; i < jpeg->variant->num_clocks; i++) { - jpeg->clocks[i] = devm_clk_get(&pdev->dev, - jpeg->variant->clk_names[i]); - if (IS_ERR(jpeg->clocks[i])) { - dev_err(&pdev->dev, "failed to get clock: %s\n", - jpeg->variant->clk_names[i]); - return PTR_ERR(jpeg->clocks[i]); - } - } - - /* v4l2 device */ - ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - return ret; - } - - /* mem2mem device */ - jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops); - if (IS_ERR(jpeg->m2m_dev)) { - v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n"); - ret = PTR_ERR(jpeg->m2m_dev); - goto device_register_rollback; - } - - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - - /* JPEG encoder /dev/videoX node */ - jpeg->vfd_encoder = video_device_alloc(); - if (!jpeg->vfd_encoder) { - v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto m2m_init_rollback; - } - snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name), - "%s-enc", S5P_JPEG_M2M_NAME); - jpeg->vfd_encoder->fops = &s5p_jpeg_fops; - jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops; - jpeg->vfd_encoder->minor = -1; - jpeg->vfd_encoder->release = video_device_release; - jpeg->vfd_encoder->lock = &jpeg->lock; - jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; - jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M; - jpeg->vfd_encoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - - ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_VIDEO, -1); - if (ret) { - v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - video_device_release(jpeg->vfd_encoder); - goto m2m_init_rollback; - } - - video_set_drvdata(jpeg->vfd_encoder, jpeg); - v4l2_info(&jpeg->v4l2_dev, - "encoder device registered as /dev/video%d\n", - jpeg->vfd_encoder->num); - - /* JPEG decoder /dev/videoX node */ - jpeg->vfd_decoder = video_device_alloc(); - if (!jpeg->vfd_decoder) { - v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto enc_vdev_register_rollback; - } - snprintf(jpeg->vfd_decoder->name, sizeof(jpeg->vfd_decoder->name), - "%s-dec", S5P_JPEG_M2M_NAME); - jpeg->vfd_decoder->fops = &s5p_jpeg_fops; - jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops; - jpeg->vfd_decoder->minor = -1; - jpeg->vfd_decoder->release = video_device_release; - jpeg->vfd_decoder->lock = &jpeg->lock; - jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev; - jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M; - jpeg->vfd_decoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - - ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_VIDEO, -1); - if (ret) { - v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - video_device_release(jpeg->vfd_decoder); - goto enc_vdev_register_rollback; - } - - video_set_drvdata(jpeg->vfd_decoder, jpeg); - v4l2_info(&jpeg->v4l2_dev, - "decoder device registered as /dev/video%d\n", - jpeg->vfd_decoder->num); - - /* final statements & power management */ - platform_set_drvdata(pdev, jpeg); - - pm_runtime_enable(&pdev->dev); - - v4l2_info(&jpeg->v4l2_dev, "Samsung S5P JPEG codec\n"); - - return 0; - -enc_vdev_register_rollback: - video_unregister_device(jpeg->vfd_encoder); - -m2m_init_rollback: - v4l2_m2m_release(jpeg->m2m_dev); - -device_register_rollback: - v4l2_device_unregister(&jpeg->v4l2_dev); - - return ret; -} - -static int s5p_jpeg_remove(struct platform_device *pdev) -{ - struct s5p_jpeg *jpeg = platform_get_drvdata(pdev); - int i; - - pm_runtime_disable(jpeg->dev); - - video_unregister_device(jpeg->vfd_decoder); - video_unregister_device(jpeg->vfd_encoder); - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - v4l2_m2m_release(jpeg->m2m_dev); - v4l2_device_unregister(&jpeg->v4l2_dev); - - if (!pm_runtime_status_suspended(&pdev->dev)) { - for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) - clk_disable_unprepare(jpeg->clocks[i]); - } - - return 0; -} - -#ifdef CONFIG_PM -static int s5p_jpeg_runtime_suspend(struct device *dev) -{ - struct s5p_jpeg *jpeg = dev_get_drvdata(dev); - int i; - - for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) - clk_disable_unprepare(jpeg->clocks[i]); - - return 0; -} - -static int s5p_jpeg_runtime_resume(struct device *dev) -{ - struct s5p_jpeg *jpeg = dev_get_drvdata(dev); - unsigned long flags; - int i, ret; - - for (i = 0; i < jpeg->variant->num_clocks; i++) { - ret = clk_prepare_enable(jpeg->clocks[i]); - if (ret) { - while (--i >= 0) - clk_disable_unprepare(jpeg->clocks[i]); - return ret; - } - } - - spin_lock_irqsave(&jpeg->slock, flags); - - /* - * JPEG IP allows storing two Huffman tables for each component. - * We fill table 0 for each component and do this here only - * for S5PC210 and Exynos3250 SoCs. Exynos4x12 and Exynos542x SoC - * require programming their Huffman tables each time the encoding - * process is initialized, and thus it is accomplished in the - * device_run callback of m2m_ops. - */ - if (!jpeg->variant->htbl_reinit) { - s5p_jpeg_set_hdctbl(jpeg->regs); - s5p_jpeg_set_hdctblg(jpeg->regs); - s5p_jpeg_set_hactbl(jpeg->regs); - s5p_jpeg_set_hactblg(jpeg->regs); - } - - spin_unlock_irqrestore(&jpeg->slock, flags); - - return 0; -} -#endif /* CONFIG_PM */ - -static const struct dev_pm_ops s5p_jpeg_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, - NULL) -}; - -static struct s5p_jpeg_variant s5p_jpeg_drvdata = { - .version = SJPEG_S5P, - .jpeg_irq = s5p_jpeg_irq, - .m2m_ops = &s5p_jpeg_m2m_ops, - .fmt_ver_flag = SJPEG_FMT_FLAG_S5P, - .clk_names = {"jpeg"}, - .num_clocks = 1, -}; - -static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { - .version = SJPEG_EXYNOS3250, - .jpeg_irq = exynos3250_jpeg_irq, - .m2m_ops = &exynos3250_jpeg_m2m_ops, - .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, - .hw3250_compat = 1, - .clk_names = {"jpeg", "sclk"}, - .num_clocks = 2, -}; - -static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { - .version = SJPEG_EXYNOS4, - .jpeg_irq = exynos4_jpeg_irq, - .m2m_ops = &exynos4_jpeg_m2m_ops, - .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, - .htbl_reinit = 1, - .clk_names = {"jpeg"}, - .num_clocks = 1, - .hw_ex4_compat = 1, -}; - -static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = { - .version = SJPEG_EXYNOS5420, - .jpeg_irq = exynos3250_jpeg_irq, /* intentionally 3250 */ - .m2m_ops = &exynos3250_jpeg_m2m_ops, /* intentionally 3250 */ - .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, /* intentionally 3250 */ - .hw3250_compat = 1, - .htbl_reinit = 1, - .clk_names = {"jpeg"}, - .num_clocks = 1, -}; - -static struct s5p_jpeg_variant exynos5433_jpeg_drvdata = { - .version = SJPEG_EXYNOS5433, - .jpeg_irq = exynos4_jpeg_irq, - .m2m_ops = &exynos4_jpeg_m2m_ops, - .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, - .htbl_reinit = 1, - .clk_names = {"pclk", "aclk", "aclk_xiu", "sclk"}, - .num_clocks = 4, - .hw_ex4_compat = 1, -}; - -static const struct of_device_id samsung_jpeg_match[] = { - { - .compatible = "samsung,s5pv210-jpeg", - .data = &s5p_jpeg_drvdata, - }, { - .compatible = "samsung,exynos3250-jpeg", - .data = &exynos3250_jpeg_drvdata, - }, { - .compatible = "samsung,exynos4210-jpeg", - .data = &exynos4_jpeg_drvdata, - }, { - .compatible = "samsung,exynos4212-jpeg", - .data = &exynos4_jpeg_drvdata, - }, { - .compatible = "samsung,exynos5420-jpeg", - .data = &exynos5420_jpeg_drvdata, - }, { - .compatible = "samsung,exynos5433-jpeg", - .data = &exynos5433_jpeg_drvdata, - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, samsung_jpeg_match); - -static void *jpeg_get_drv_data(struct device *dev) -{ - struct s5p_jpeg_variant *driver_data = NULL; - const struct of_device_id *match; - - if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) - return &s5p_jpeg_drvdata; - - match = of_match_node(samsung_jpeg_match, dev->of_node); - - if (match) - driver_data = (struct s5p_jpeg_variant *)match->data; - - return driver_data; -} - -static struct platform_driver s5p_jpeg_driver = { - .probe = s5p_jpeg_probe, - .remove = s5p_jpeg_remove, - .driver = { - .of_match_table = of_match_ptr(samsung_jpeg_match), - .name = S5P_JPEG_M2M_NAME, - .pm = &s5p_jpeg_pm_ops, - }, -}; - -module_platform_driver(s5p_jpeg_driver); - -MODULE_AUTHOR("Andrzej Pietrasiewicz "); -MODULE_AUTHOR("Jacek Anaszewski "); -MODULE_DESCRIPTION("Samsung JPEG codec driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h deleted file mode 100644 index 4a5fb1b15455..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ /dev/null @@ -1,267 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - */ - -#ifndef JPEG_CORE_H_ -#define JPEG_CORE_H_ - -#include -#include -#include -#include - -#define S5P_JPEG_M2M_NAME "s5p-jpeg" - -#define JPEG_MAX_CLOCKS 4 - -/* JPEG compression quality setting */ -#define S5P_JPEG_COMPR_QUAL_BEST 0 -#define S5P_JPEG_COMPR_QUAL_WORST 3 - -/* JPEG RGB to YCbCr conversion matrix coefficients */ -#define S5P_JPEG_COEF11 0x4d -#define S5P_JPEG_COEF12 0x97 -#define S5P_JPEG_COEF13 0x1e -#define S5P_JPEG_COEF21 0x2c -#define S5P_JPEG_COEF22 0x57 -#define S5P_JPEG_COEF23 0x83 -#define S5P_JPEG_COEF31 0x83 -#define S5P_JPEG_COEF32 0x6e -#define S5P_JPEG_COEF33 0x13 - -#define EXYNOS3250_IRQ_TIMEOUT 0x10000000 - -/* a selection of JPEG markers */ -#define JPEG_MARKER_TEM 0x01 -#define JPEG_MARKER_SOF0 0xc0 -#define JPEG_MARKER_DHT 0xc4 -#define JPEG_MARKER_RST 0xd0 -#define JPEG_MARKER_SOI 0xd8 -#define JPEG_MARKER_EOI 0xd9 -#define JPEG_MARKER_SOS 0xda -#define JPEG_MARKER_DQT 0xdb -#define JPEG_MARKER_DHP 0xde - -/* Flags that indicate a format can be used for capture/output */ -#define SJPEG_FMT_FLAG_ENC_CAPTURE (1 << 0) -#define SJPEG_FMT_FLAG_ENC_OUTPUT (1 << 1) -#define SJPEG_FMT_FLAG_DEC_CAPTURE (1 << 2) -#define SJPEG_FMT_FLAG_DEC_OUTPUT (1 << 3) -#define SJPEG_FMT_FLAG_S5P (1 << 4) -#define SJPEG_FMT_FLAG_EXYNOS3250 (1 << 5) -#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 6) -#define SJPEG_FMT_RGB (1 << 7) -#define SJPEG_FMT_NON_RGB (1 << 8) - -#define S5P_JPEG_ENCODE 0 -#define S5P_JPEG_DECODE 1 -#define S5P_JPEG_DISABLE -1 - -#define FMT_TYPE_OUTPUT 0 -#define FMT_TYPE_CAPTURE 1 - -#define SJPEG_SUBSAMPLING_444 0x11 -#define SJPEG_SUBSAMPLING_422 0x21 -#define SJPEG_SUBSAMPLING_420 0x22 - -#define S5P_JPEG_MAX_MARKER 4 - -/* Version numbers */ -enum sjpeg_version { - SJPEG_S5P, - SJPEG_EXYNOS3250, - SJPEG_EXYNOS4, - SJPEG_EXYNOS5420, - SJPEG_EXYNOS5433, -}; - -enum exynos4_jpeg_result { - OK_ENC_OR_DEC, - ERR_PROT, - ERR_DEC_INVALID_FORMAT, - ERR_MULTI_SCAN, - ERR_FRAME, - ERR_UNKNOWN, -}; - -enum exynos4_jpeg_img_quality_level { - QUALITY_LEVEL_1 = 0, /* high */ - QUALITY_LEVEL_2, - QUALITY_LEVEL_3, - QUALITY_LEVEL_4, /* low */ -}; - -enum s5p_jpeg_ctx_state { - JPEGCTX_RUNNING = 0, - JPEGCTX_RESOLUTION_CHANGE, -}; - -/** - * struct s5p_jpeg - JPEG IP abstraction - * @lock: the mutex protecting this structure - * @slock: spinlock protecting the device contexts - * @v4l2_dev: v4l2 device for mem2mem mode - * @vfd_encoder: video device node for encoder mem2mem mode - * @vfd_decoder: video device node for decoder mem2mem mode - * @m2m_dev: v4l2 mem2mem device data - * @regs: JPEG IP registers mapping - * @irq: JPEG IP irq - * @irq_ret: JPEG IP irq result value - * @clocks: JPEG IP clock(s) - * @dev: JPEG IP struct device - * @variant: driver variant to be used - * @irq_status: interrupt flags set during single encode/decode - * operation - */ -struct s5p_jpeg { - struct mutex lock; - spinlock_t slock; - - struct v4l2_device v4l2_dev; - struct video_device *vfd_encoder; - struct video_device *vfd_decoder; - struct v4l2_m2m_dev *m2m_dev; - - void __iomem *regs; - unsigned int irq; - enum exynos4_jpeg_result irq_ret; - struct clk *clocks[JPEG_MAX_CLOCKS]; - struct device *dev; - struct s5p_jpeg_variant *variant; - u32 irq_status; -}; - -struct s5p_jpeg_variant { - unsigned int version; - unsigned int fmt_ver_flag; - unsigned int hw3250_compat:1; - unsigned int htbl_reinit:1; - unsigned int hw_ex4_compat:1; - const struct v4l2_m2m_ops *m2m_ops; - irqreturn_t (*jpeg_irq)(int irq, void *priv); - const char *clk_names[JPEG_MAX_CLOCKS]; - int num_clocks; -}; - -/** - * struct s5p_jpeg_fmt - driver's internal color format data - * @fourcc: the fourcc code, 0 if not applicable - * @depth: number of bits per pixel - * @colplanes: number of color planes (1 for packed formats) - * @memplanes: number of memory planes (1 for packed formats) - * @h_align: horizontal alignment order (align to 2^h_align) - * @v_align: vertical alignment order (align to 2^v_align) - * @subsampling:subsampling of a raw format or a JPEG - * @flags: flags describing format applicability - */ -struct s5p_jpeg_fmt { - u32 fourcc; - int depth; - int colplanes; - int memplanes; - int h_align; - int v_align; - int subsampling; - u32 flags; -}; - -/** - * struct s5p_jpeg_marker - collection of markers from jpeg header - * @marker: markers' positions relative to the buffer beginning - * @len: markers' payload lengths (without length field) - * @n: number of markers in collection - */ -struct s5p_jpeg_marker { - u32 marker[S5P_JPEG_MAX_MARKER]; - u32 len[S5P_JPEG_MAX_MARKER]; - u32 n; -}; - -/** - * struct s5p_jpeg_q_data - parameters of one queue - * @fmt: driver-specific format of this queue - * @w: image width - * @h: image height - * @sos: JPEG_MARKER_SOS's position relative to the buffer beginning - * @dht: JPEG_MARKER_DHT' positions relative to the buffer beginning - * @dqt: JPEG_MARKER_DQT' positions relative to the buffer beginning - * @sof: JPEG_MARKER_SOF0's position relative to the buffer beginning - * @sof_len: JPEG_MARKER_SOF0's payload length (without length field itself) - * @size: image buffer size in bytes - */ -struct s5p_jpeg_q_data { - struct s5p_jpeg_fmt *fmt; - u32 w; - u32 h; - u32 sos; - struct s5p_jpeg_marker dht; - struct s5p_jpeg_marker dqt; - u32 sof; - u32 sof_len; - u32 size; -}; - -/** - * struct s5p_jpeg_ctx - the device context data - * @jpeg: JPEG IP device for this context - * @mode: compression (encode) operation or decompression (decode) - * @compr_quality: destination image quality in compression (encode) mode - * @restart_interval: JPEG restart interval for JPEG encoding - * @subsampling: subsampling of a raw format or a JPEG - * @out_q: source (output) queue information - * @cap_q: destination (capture) queue queue information - * @scale_factor: scale factor for JPEG decoding - * @crop_rect: a rectangle representing crop area of the output buffer - * @fh: V4L2 file handle - * @hdr_parsed: set if header has been parsed during decompression - * @crop_altered: set if crop rectangle has been altered by the user space - * @ctrl_handler: controls handler - * @state: state of the context - */ -struct s5p_jpeg_ctx { - struct s5p_jpeg *jpeg; - unsigned int mode; - unsigned short compr_quality; - unsigned short restart_interval; - unsigned short subsampling; - struct s5p_jpeg_q_data out_q; - struct s5p_jpeg_q_data cap_q; - unsigned int scale_factor; - struct v4l2_rect crop_rect; - struct v4l2_fh fh; - bool hdr_parsed; - bool crop_altered; - struct v4l2_ctrl_handler ctrl_handler; - enum s5p_jpeg_ctx_state state; -}; - -/** - * struct s5p_jpeg_buffer - description of memory containing input JPEG data - * @size: buffer size - * @curr: current position in the buffer - * @data: pointer to the data - */ -struct s5p_jpeg_buffer { - unsigned long size; - unsigned long curr; - unsigned long data; -}; - -/** - * struct s5p_jpeg_addr - JPEG converter physical address set for DMA - * @y: luminance plane physical address - * @cb: Cb plane physical address - * @cr: Cr plane physical address - */ -struct s5p_jpeg_addr { - u32 y; - u32 cb; - u32 cr; -}; - -#endif /* JPEG_CORE_H */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c deleted file mode 100644 index 637a5104d948..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ /dev/null @@ -1,486 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* linux/drivers/media/platform/exynos3250-jpeg/jpeg-hw.h - * - * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jacek Anaszewski - */ - -#include -#include -#include - -#include "jpeg-core.h" -#include "jpeg-regs.h" -#include "jpeg-hw-exynos3250.h" - -void exynos3250_jpeg_reset(void __iomem *regs) -{ - u32 reg = 1; - int count = 1000; - - writel(1, regs + EXYNOS3250_SW_RESET); - /* no other way but polling for when JPEG IP becomes operational */ - while (reg != 0 && --count > 0) { - udelay(1); - cpu_relax(); - reg = readl(regs + EXYNOS3250_SW_RESET); - } - - reg = 0; - count = 1000; - - while (reg != 1 && --count > 0) { - writel(1, regs + EXYNOS3250_JPGDRI); - udelay(1); - cpu_relax(); - reg = readl(regs + EXYNOS3250_JPGDRI); - } - - writel(0, regs + EXYNOS3250_JPGDRI); -} - -void exynos3250_jpeg_poweron(void __iomem *regs) -{ - writel(EXYNOS3250_POWER_ON, regs + EXYNOS3250_JPGCLKCON); -} - -void exynos3250_jpeg_set_dma_num(void __iomem *regs) -{ - writel(((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) & - EXYNOS3250_WDMA_ISSUE_NUM_MASK) | - ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) & - EXYNOS3250_RDMA_ISSUE_NUM_MASK) | - ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) & - EXYNOS3250_ISSUE_GATHER_NUM_MASK), - regs + EXYNOS3250_DMA_ISSUE_NUM); -} - -void exynos3250_jpeg_clk_set(void __iomem *base) -{ - u32 reg; - - reg = readl(base + EXYNOS3250_JPGCMOD) & ~EXYNOS3250_HALF_EN_MASK; - - writel(reg | EXYNOS3250_HALF_EN, base + EXYNOS3250_JPGCMOD); -} - -void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt) -{ - u32 reg; - - reg = readl(regs + EXYNOS3250_JPGCMOD) & - EXYNOS3250_MODE_Y16_MASK; - - switch (fmt) { - case V4L2_PIX_FMT_RGB32: - reg |= EXYNOS3250_MODE_SEL_ARGB8888; - break; - case V4L2_PIX_FMT_BGR32: - reg |= EXYNOS3250_MODE_SEL_ARGB8888 | EXYNOS3250_SRC_SWAP_RGB; - break; - case V4L2_PIX_FMT_RGB565: - reg |= EXYNOS3250_MODE_SEL_RGB565; - break; - case V4L2_PIX_FMT_RGB565X: - reg |= EXYNOS3250_MODE_SEL_RGB565 | EXYNOS3250_SRC_SWAP_RGB; - break; - case V4L2_PIX_FMT_YUYV: - reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR; - break; - case V4L2_PIX_FMT_YVYU: - reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR | - EXYNOS3250_SRC_SWAP_UV; - break; - case V4L2_PIX_FMT_UYVY: - reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM; - break; - case V4L2_PIX_FMT_VYUY: - reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM | - EXYNOS3250_SRC_SWAP_UV; - break; - case V4L2_PIX_FMT_NV12: - reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV12; - break; - case V4L2_PIX_FMT_NV21: - reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV21; - break; - case V4L2_PIX_FMT_YUV420: - reg |= EXYNOS3250_MODE_SEL_420_3P; - break; - default: - break; - - } - - writel(reg, regs + EXYNOS3250_JPGCMOD); -} - -void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16) -{ - u32 reg; - - reg = readl(regs + EXYNOS3250_JPGCMOD); - if (y16) - reg |= EXYNOS3250_MODE_Y16; - else - reg &= ~EXYNOS3250_MODE_Y16_MASK; - writel(reg, regs + EXYNOS3250_JPGCMOD); -} - -void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode) -{ - u32 reg, m; - - if (mode == S5P_JPEG_ENCODE) - m = EXYNOS3250_PROC_MODE_COMPR; - else - m = EXYNOS3250_PROC_MODE_DECOMPR; - reg = readl(regs + EXYNOS3250_JPGMOD); - reg &= ~EXYNOS3250_PROC_MODE_MASK; - reg |= m; - writel(reg, regs + EXYNOS3250_JPGMOD); -} - -void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) -{ - u32 reg, m = 0; - - switch (mode) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_444: - m = EXYNOS3250_SUBSAMPLING_MODE_444; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - m = EXYNOS3250_SUBSAMPLING_MODE_422; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - m = EXYNOS3250_SUBSAMPLING_MODE_420; - break; - } - - reg = readl(regs + EXYNOS3250_JPGMOD); - reg &= ~EXYNOS3250_SUBSAMPLING_MODE_MASK; - reg |= m; - writel(reg, regs + EXYNOS3250_JPGMOD); -} - -unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_JPGMOD) & - EXYNOS3250_SUBSAMPLING_MODE_MASK; -} - -void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri) -{ - u32 reg; - - reg = dri & EXYNOS3250_JPGDRI_MASK; - writel(reg, regs + EXYNOS3250_JPGDRI); -} - -void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) -{ - unsigned long reg; - - reg = readl(regs + EXYNOS3250_QHTBL); - reg &= ~EXYNOS3250_QT_NUM_MASK(t); - reg |= (n << EXYNOS3250_QT_NUM_SHIFT(t)) & - EXYNOS3250_QT_NUM_MASK(t); - writel(reg, regs + EXYNOS3250_QHTBL); -} - -void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + EXYNOS3250_QHTBL); - reg &= ~EXYNOS3250_HT_NUM_AC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) & - EXYNOS3250_HT_NUM_AC_MASK(t); - writel(reg, regs + EXYNOS3250_QHTBL); -} - -void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + EXYNOS3250_QHTBL); - reg &= ~EXYNOS3250_HT_NUM_DC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) & - EXYNOS3250_HT_NUM_DC_MASK(t); - writel(reg, regs + EXYNOS3250_QHTBL); -} - -void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y) -{ - u32 reg; - - reg = y & EXYNOS3250_JPGY_MASK; - writel(reg, regs + EXYNOS3250_JPGY); -} - -void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x) -{ - u32 reg; - - reg = x & EXYNOS3250_JPGX_MASK; - writel(reg, regs + EXYNOS3250_JPGX); -} - -#if 0 /* Currently unused */ -unsigned int exynos3250_jpeg_get_y(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_JPGY); -} - -unsigned int exynos3250_jpeg_get_x(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_JPGX); -} -#endif - -void exynos3250_jpeg_interrupts_enable(void __iomem *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS3250_JPGINTSE); - reg |= (EXYNOS3250_JPEG_DONE_EN | - EXYNOS3250_WDMA_DONE_EN | - EXYNOS3250_RDMA_DONE_EN | - EXYNOS3250_ENC_STREAM_INT_EN | - EXYNOS3250_CORE_DONE_EN | - EXYNOS3250_ERR_INT_EN | - EXYNOS3250_HEAD_INT_EN); - writel(reg, regs + EXYNOS3250_JPGINTSE); -} - -void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size) -{ - u32 reg; - - reg = size & EXYNOS3250_ENC_STREAM_BOUND_MASK; - writel(reg, regs + EXYNOS3250_ENC_STREAM_BOUND); -} - -void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt) -{ - u32 reg; - - switch (fmt) { - case V4L2_PIX_FMT_RGB32: - reg = EXYNOS3250_OUT_FMT_ARGB8888; - break; - case V4L2_PIX_FMT_BGR32: - reg = EXYNOS3250_OUT_FMT_ARGB8888 | EXYNOS3250_OUT_SWAP_RGB; - break; - case V4L2_PIX_FMT_RGB565: - reg = EXYNOS3250_OUT_FMT_RGB565; - break; - case V4L2_PIX_FMT_RGB565X: - reg = EXYNOS3250_OUT_FMT_RGB565 | EXYNOS3250_OUT_SWAP_RGB; - break; - case V4L2_PIX_FMT_YUYV: - reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR; - break; - case V4L2_PIX_FMT_YVYU: - reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR | - EXYNOS3250_OUT_SWAP_UV; - break; - case V4L2_PIX_FMT_UYVY: - reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM; - break; - case V4L2_PIX_FMT_VYUY: - reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM | - EXYNOS3250_OUT_SWAP_UV; - break; - case V4L2_PIX_FMT_NV12: - reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV12; - break; - case V4L2_PIX_FMT_NV21: - reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV21; - break; - case V4L2_PIX_FMT_YUV420: - reg = EXYNOS3250_OUT_FMT_420_3P; - break; - default: - reg = 0; - break; - } - - writel(reg, regs + EXYNOS3250_OUTFORM); -} - -void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr) -{ - writel(addr, regs + EXYNOS3250_JPG_JPGADR); -} - -void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr) -{ - writel(img_addr->y, regs + EXYNOS3250_LUMA_BASE); - writel(img_addr->cb, regs + EXYNOS3250_CHROMA_BASE); - writel(img_addr->cr, regs + EXYNOS3250_CHROMA_CR_BASE); -} - -void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, - unsigned int width) -{ - u32 reg_luma = 0, reg_cr = 0, reg_cb = 0; - - switch (img_fmt) { - case V4L2_PIX_FMT_RGB32: - reg_luma = 4 * width; - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - reg_luma = 2 * width; - break; - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - reg_luma = width; - reg_cb = reg_luma; - break; - case V4L2_PIX_FMT_YUV420: - reg_luma = width; - reg_cb = reg_cr = reg_luma / 2; - break; - default: - break; - } - - writel(reg_luma, regs + EXYNOS3250_LUMA_STRIDE); - writel(reg_cb, regs + EXYNOS3250_CHROMA_STRIDE); - writel(reg_cr, regs + EXYNOS3250_CHROMA_CR_STRIDE); -} - -void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, - unsigned int y_offset) -{ - u32 reg; - - reg = (y_offset << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) & - EXYNOS3250_LUMA_YY_OFFSET_MASK; - reg |= (x_offset << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) & - EXYNOS3250_LUMA_YX_OFFSET_MASK; - - writel(reg, regs + EXYNOS3250_LUMA_XY_OFFSET); - - reg = (y_offset << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) & - EXYNOS3250_CHROMA_YY_OFFSET_MASK; - reg |= (x_offset << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) & - EXYNOS3250_CHROMA_YX_OFFSET_MASK; - - writel(reg, regs + EXYNOS3250_CHROMA_XY_OFFSET); - - reg = (y_offset << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) & - EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK; - reg |= (x_offset << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) & - EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK; - - writel(reg, regs + EXYNOS3250_CHROMA_CR_XY_OFFSET); -} - -void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode) -{ - if (mode == S5P_JPEG_ENCODE) { - writel(EXYNOS3250_JPEG_ENC_COEF1, - base + EXYNOS3250_JPG_COEF(1)); - writel(EXYNOS3250_JPEG_ENC_COEF2, - base + EXYNOS3250_JPG_COEF(2)); - writel(EXYNOS3250_JPEG_ENC_COEF3, - base + EXYNOS3250_JPG_COEF(3)); - } else { - writel(EXYNOS3250_JPEG_DEC_COEF1, - base + EXYNOS3250_JPG_COEF(1)); - writel(EXYNOS3250_JPEG_DEC_COEF2, - base + EXYNOS3250_JPG_COEF(2)); - writel(EXYNOS3250_JPEG_DEC_COEF3, - base + EXYNOS3250_JPG_COEF(3)); - } -} - -void exynos3250_jpeg_start(void __iomem *regs) -{ - writel(1, regs + EXYNOS3250_JSTART); -} - -void exynos3250_jpeg_rstart(void __iomem *regs) -{ - writel(1, regs + EXYNOS3250_JRSTART); -} - -unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_JPGINTST); -} - -void exynos3250_jpeg_clear_int_status(void __iomem *regs, - unsigned int value) -{ - writel(value, regs + EXYNOS3250_JPGINTST); -} - -unsigned int exynos3250_jpeg_operating(void __iomem *regs) -{ - return readl(regs + S5P_JPGOPR) & EXYNOS3250_JPGOPR_MASK; -} - -unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_JPGCNT) & EXYNOS3250_JPGCNT_MASK; -} - -void exynos3250_jpeg_dec_stream_size(void __iomem *regs, - unsigned int size) -{ - writel(size & EXYNOS3250_DEC_STREAM_MASK, - regs + EXYNOS3250_DEC_STREAM_SIZE); -} - -void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, - unsigned int sratio) -{ - switch (sratio) { - case 1: - default: - sratio = EXYNOS3250_DEC_SCALE_FACTOR_8_8; - break; - case 2: - sratio = EXYNOS3250_DEC_SCALE_FACTOR_4_8; - break; - case 4: - sratio = EXYNOS3250_DEC_SCALE_FACTOR_2_8; - break; - case 8: - sratio = EXYNOS3250_DEC_SCALE_FACTOR_1_8; - break; - } - - writel(sratio & EXYNOS3250_DEC_SCALE_FACTOR_MASK, - regs + EXYNOS3250_DEC_SCALING_RATIO); -} - -void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value) -{ - time_value &= EXYNOS3250_TIMER_INIT_MASK; - - writel(EXYNOS3250_TIMER_INT_STAT | time_value, - regs + EXYNOS3250_TIMER_SE); -} - -unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs) -{ - return readl(regs + EXYNOS3250_TIMER_ST); -} - -void exynos3250_jpeg_clear_timer_status(void __iomem *regs) -{ - writel(EXYNOS3250_TIMER_INT_STAT, regs + EXYNOS3250_TIMER_ST); -} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h deleted file mode 100644 index 68160befce39..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h - * - * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jacek Anaszewski - */ -#ifndef JPEG_HW_EXYNOS3250_H_ -#define JPEG_HW_EXYNOS3250_H_ - -#include -#include - -#include "jpeg-regs.h" - -void exynos3250_jpeg_reset(void __iomem *regs); -void exynos3250_jpeg_poweron(void __iomem *regs); -void exynos3250_jpeg_set_dma_num(void __iomem *regs); -void exynos3250_jpeg_clk_set(void __iomem *base); -void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt); -void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt); -void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16); -void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode); -void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); -unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs); -void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri); -void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); -void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t); -void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t); -void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y); -void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x); -void exynos3250_jpeg_interrupts_enable(void __iomem *regs); -void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size); -void exynos3250_jpeg_outform_raw(void __iomem *regs, unsigned long format); -void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr); -void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr); -void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, - unsigned int width); -void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, - unsigned int y_offset); -void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode); -void exynos3250_jpeg_start(void __iomem *regs); -void exynos3250_jpeg_rstart(void __iomem *regs); -unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs); -void exynos3250_jpeg_clear_int_status(void __iomem *regs, - unsigned int value); -unsigned int exynos3250_jpeg_operating(void __iomem *regs); -unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs); -void exynos3250_jpeg_dec_stream_size(void __iomem *regs, unsigned int size); -void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, unsigned int sratio); -void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value); -unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs); -void exynos3250_jpeg_set_timer_status(void __iomem *regs); -void exynos3250_jpeg_clear_timer_status(void __iomem *regs); - -#endif /* JPEG_HW_EXYNOS3250_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c deleted file mode 100644 index 0828cfa783fe..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c +++ /dev/null @@ -1,321 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Author: Jacek Anaszewski - * - * Register interface file for JPEG driver on Exynos4x12. - */ -#include -#include - -#include "jpeg-core.h" -#include "jpeg-hw-exynos4.h" -#include "jpeg-regs.h" - -void exynos4_jpeg_sw_reset(void __iomem *base) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_JPEG_CNTL_REG); - writel(reg & ~(EXYNOS4_DEC_MODE | EXYNOS4_ENC_MODE), - base + EXYNOS4_JPEG_CNTL_REG); - - reg = readl(base + EXYNOS4_JPEG_CNTL_REG); - writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); - - udelay(100); - - writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); -} - -void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_JPEG_CNTL_REG); - /* set exynos4_jpeg mod register */ - if (mode == S5P_JPEG_DECODE) { - writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | - EXYNOS4_DEC_MODE, - base + EXYNOS4_JPEG_CNTL_REG); - } else if (mode == S5P_JPEG_ENCODE) {/* encode */ - writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | - EXYNOS4_ENC_MODE, - base + EXYNOS4_JPEG_CNTL_REG); - } else { /* disable both */ - writel(reg & EXYNOS4_ENC_DEC_MODE_MASK, - base + EXYNOS4_JPEG_CNTL_REG); - } -} - -void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, - unsigned int version) -{ - unsigned int reg; - unsigned int exynos4_swap_chroma_cbcr; - unsigned int exynos4_swap_chroma_crcb; - - if (version == SJPEG_EXYNOS4) { - exynos4_swap_chroma_cbcr = EXYNOS4_SWAP_CHROMA_CBCR; - exynos4_swap_chroma_crcb = EXYNOS4_SWAP_CHROMA_CRCB; - } else { - exynos4_swap_chroma_cbcr = EXYNOS5433_SWAP_CHROMA_CBCR; - exynos4_swap_chroma_crcb = EXYNOS5433_SWAP_CHROMA_CRCB; - } - - reg = readl(base + EXYNOS4_IMG_FMT_REG) & - EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */ - - switch (img_fmt) { - case V4L2_PIX_FMT_GREY: - reg = reg | EXYNOS4_ENC_GRAY_IMG | EXYNOS4_GRAY_IMG_IP; - break; - case V4L2_PIX_FMT_RGB32: - reg = reg | EXYNOS4_ENC_RGB_IMG | - EXYNOS4_RGB_IP_RGB_32BIT_IMG; - break; - case V4L2_PIX_FMT_RGB565: - reg = reg | EXYNOS4_ENC_RGB_IMG | - EXYNOS4_RGB_IP_RGB_16BIT_IMG; - break; - case V4L2_PIX_FMT_NV24: - reg = reg | EXYNOS4_ENC_YUV_444_IMG | - EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | - exynos4_swap_chroma_cbcr; - break; - case V4L2_PIX_FMT_NV42: - reg = reg | EXYNOS4_ENC_YUV_444_IMG | - EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | - exynos4_swap_chroma_crcb; - break; - case V4L2_PIX_FMT_YUYV: - reg = reg | EXYNOS4_DEC_YUV_422_IMG | - EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | - exynos4_swap_chroma_cbcr; - break; - - case V4L2_PIX_FMT_YVYU: - reg = reg | EXYNOS4_DEC_YUV_422_IMG | - EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | - exynos4_swap_chroma_crcb; - break; - case V4L2_PIX_FMT_NV16: - reg = reg | EXYNOS4_DEC_YUV_422_IMG | - EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | - exynos4_swap_chroma_cbcr; - break; - case V4L2_PIX_FMT_NV61: - reg = reg | EXYNOS4_DEC_YUV_422_IMG | - EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | - exynos4_swap_chroma_crcb; - break; - case V4L2_PIX_FMT_NV12: - reg = reg | EXYNOS4_DEC_YUV_420_IMG | - EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | - exynos4_swap_chroma_cbcr; - break; - case V4L2_PIX_FMT_NV21: - reg = reg | EXYNOS4_DEC_YUV_420_IMG | - EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | - exynos4_swap_chroma_crcb; - break; - case V4L2_PIX_FMT_YUV420: - reg = reg | EXYNOS4_DEC_YUV_420_IMG | - EXYNOS4_YUV_420_IP_YUV_420_3P_IMG | - exynos4_swap_chroma_cbcr; - break; - default: - break; - - } - - writel(reg, base + EXYNOS4_IMG_FMT_REG); -} - -void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, - unsigned int version) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_IMG_FMT_REG) & - ~(version == SJPEG_EXYNOS4 ? EXYNOS4_ENC_FMT_MASK : - EXYNOS5433_ENC_FMT_MASK); /* clear enc format */ - - switch (out_fmt) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: - reg = reg | EXYNOS4_ENC_FMT_GRAY; - break; - - case V4L2_JPEG_CHROMA_SUBSAMPLING_444: - reg = reg | EXYNOS4_ENC_FMT_YUV_444; - break; - - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - reg = reg | EXYNOS4_ENC_FMT_YUV_422; - break; - - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - reg = reg | EXYNOS4_ENC_FMT_YUV_420; - break; - - default: - break; - } - - writel(reg, base + EXYNOS4_IMG_FMT_REG); -} - -void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version) -{ - unsigned int reg; - - if (version == SJPEG_EXYNOS4) { - reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; - writel(reg | EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); - } else { - reg = readl(base + EXYNOS4_INT_EN_REG) & - ~EXYNOS5433_INT_EN_MASK; - writel(reg | EXYNOS5433_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); - } -} - -unsigned int exynos4_jpeg_get_int_status(void __iomem *base) -{ - return readl(base + EXYNOS4_INT_STATUS_REG); -} - -unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base) -{ - return readl(base + EXYNOS4_FIFO_STATUS_REG); -} - -void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~EXYNOS4_HUF_TBL_EN; - - if (value == 1) - writel(reg | EXYNOS4_HUF_TBL_EN, - base + EXYNOS4_JPEG_CNTL_REG); - else - writel(reg & ~EXYNOS4_HUF_TBL_EN, - base + EXYNOS4_JPEG_CNTL_REG); -} - -void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN); - - if (value == 1) - writel(reg | EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); - else - writel(reg & ~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); -} - -void exynos4_jpeg_set_stream_buf_address(void __iomem *base, - unsigned int address) -{ - writel(address, base + EXYNOS4_OUT_MEM_BASE_REG); -} - -void exynos4_jpeg_set_stream_size(void __iomem *base, - unsigned int x_value, unsigned int y_value) -{ - writel(0x0, base + EXYNOS4_JPEG_IMG_SIZE_REG); /* clear */ - writel(EXYNOS4_X_SIZE(x_value) | EXYNOS4_Y_SIZE(y_value), - base + EXYNOS4_JPEG_IMG_SIZE_REG); -} - -void exynos4_jpeg_set_frame_buf_address(void __iomem *base, - struct s5p_jpeg_addr *exynos4_jpeg_addr) -{ - writel(exynos4_jpeg_addr->y, base + EXYNOS4_IMG_BA_PLANE_1_REG); - writel(exynos4_jpeg_addr->cb, base + EXYNOS4_IMG_BA_PLANE_2_REG); - writel(exynos4_jpeg_addr->cr, base + EXYNOS4_IMG_BA_PLANE_3_REG); -} - -void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, - enum exynos4_jpeg_img_quality_level level) -{ - unsigned int reg; - - reg = EXYNOS4_Q_TBL_COMP1_0 | EXYNOS4_Q_TBL_COMP2_1 | - EXYNOS4_Q_TBL_COMP3_1 | - EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 | - EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 | - EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1; - - writel(reg, base + EXYNOS4_TBL_SEL_REG); -} - -void exynos4_jpeg_set_dec_components(void __iomem *base, int n) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_TBL_SEL_REG); - - reg |= EXYNOS4_NF(n); - writel(reg, base + EXYNOS4_TBL_SEL_REG); -} - -void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_TBL_SEL_REG); - - reg |= EXYNOS4_Q_TBL_COMP(c, x); - writel(reg, base + EXYNOS4_TBL_SEL_REG); -} - -void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x) -{ - unsigned int reg; - - reg = readl(base + EXYNOS4_TBL_SEL_REG); - - reg |= EXYNOS4_HUFF_TBL_COMP(c, x); - writel(reg, base + EXYNOS4_TBL_SEL_REG); -} - -void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt) -{ - if (fmt == V4L2_PIX_FMT_GREY) - writel(0xd2, base + EXYNOS4_HUFF_CNT_REG); - else - writel(0x1a2, base + EXYNOS4_HUFF_CNT_REG); -} - -unsigned int exynos4_jpeg_get_stream_size(void __iomem *base) -{ - return readl(base + EXYNOS4_BITSTREAM_SIZE_REG); -} - -void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size) -{ - writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG); -} - -void exynos4_jpeg_get_frame_size(void __iomem *base, - unsigned int *width, unsigned int *height) -{ - *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) & - EXYNOS4_DECODED_SIZE_MASK); - *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) & - EXYNOS4_DECODED_SIZE_MASK; -} - -unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base) -{ - return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) & - EXYNOS4_JPEG_DECODED_IMG_FMT_MASK; -} - -void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size) -{ - writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG); -} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h deleted file mode 100644 index 3e2887526960..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Author: Jacek Anaszewski - * - * Header file of the register interface for JPEG driver on Exynos4x12. -*/ - -#ifndef JPEG_HW_EXYNOS4_H_ -#define JPEG_HW_EXYNOS4_H_ - -void exynos4_jpeg_sw_reset(void __iomem *base); -void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode); -void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, - unsigned int version); -void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, - unsigned int version); -void exynos4_jpeg_set_enc_tbl(void __iomem *base); -void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version); -unsigned int exynos4_jpeg_get_int_status(void __iomem *base); -void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value); -void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value); -void exynos4_jpeg_set_stream_buf_address(void __iomem *base, - unsigned int address); -void exynos4_jpeg_set_stream_size(void __iomem *base, - unsigned int x_value, unsigned int y_value); -void exynos4_jpeg_set_frame_buf_address(void __iomem *base, - struct s5p_jpeg_addr *jpeg_addr); -void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, - enum exynos4_jpeg_img_quality_level level); -void exynos4_jpeg_set_dec_components(void __iomem *base, int n); -void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x); -void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x); -void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); -void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); -unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); -void exynos4_jpeg_get_frame_size(void __iomem *base, - unsigned int *width, unsigned int *height); -unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base); -unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base); -void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size); - -#endif /* JPEG_HW_EXYNOS4_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c deleted file mode 100644 index 491e9248286c..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - */ - -#include -#include - -#include "jpeg-core.h" -#include "jpeg-regs.h" -#include "jpeg-hw-s5p.h" - -void s5p_jpeg_reset(void __iomem *regs) -{ - unsigned long reg; - - writel(1, regs + S5P_JPG_SW_RESET); - reg = readl(regs + S5P_JPG_SW_RESET); - /* no other way but polling for when JPEG IP becomes operational */ - while (reg != 0) { - cpu_relax(); - reg = readl(regs + S5P_JPG_SW_RESET); - } -} - -void s5p_jpeg_poweron(void __iomem *regs) -{ - writel(S5P_POWER_ON, regs + S5P_JPGCLKCON); -} - -void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) -{ - unsigned long reg, m; - - m = S5P_MOD_SEL_565; - if (mode == S5P_JPEG_RAW_IN_565) - m = S5P_MOD_SEL_565; - else if (mode == S5P_JPEG_RAW_IN_422) - m = S5P_MOD_SEL_422; - - reg = readl(regs + S5P_JPGCMOD); - reg &= ~S5P_MOD_SEL_MASK; - reg |= m; - writel(reg, regs + S5P_JPGCMOD); -} - -void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode) -{ - unsigned long reg, m; - - m = S5P_PROC_MODE_DECOMPR; - if (mode == S5P_JPEG_ENCODE) - m = S5P_PROC_MODE_COMPR; - else - m = S5P_PROC_MODE_DECOMPR; - reg = readl(regs + S5P_JPGMOD); - reg &= ~S5P_PROC_MODE_MASK; - reg |= m; - writel(reg, regs + S5P_JPGMOD); -} - -void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) -{ - unsigned long reg, m; - - if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) - m = S5P_SUBSAMPLING_MODE_420; - else - m = S5P_SUBSAMPLING_MODE_422; - - reg = readl(regs + S5P_JPGMOD); - reg &= ~S5P_SUBSAMPLING_MODE_MASK; - reg |= m; - writel(reg, regs + S5P_JPGMOD); -} - -unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs) -{ - return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; -} - -void s5p_jpeg_dri(void __iomem *regs, unsigned int dri) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGDRI_U); - reg &= ~0xff; - reg |= (dri >> 8) & 0xff; - writel(reg, regs + S5P_JPGDRI_U); - - reg = readl(regs + S5P_JPGDRI_L); - reg &= ~0xff; - reg |= dri & 0xff; - writel(reg, regs + S5P_JPGDRI_L); -} - -void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_QTBL); - reg &= ~S5P_QT_NUMt_MASK(t); - reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t); - writel(reg, regs + S5P_JPG_QTBL); -} - -void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_HTBL); - reg &= ~S5P_HT_NUMt_AC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t); - writel(reg, regs + S5P_JPG_HTBL); -} - -void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_HTBL); - reg &= ~S5P_HT_NUMt_DC_MASK(t); - /* this driver uses table 0 for all color components */ - reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t); - writel(reg, regs + S5P_JPG_HTBL); -} - -void s5p_jpeg_y(void __iomem *regs, unsigned int y) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGY_U); - reg &= ~0xff; - reg |= (y >> 8) & 0xff; - writel(reg, regs + S5P_JPGY_U); - - reg = readl(regs + S5P_JPGY_L); - reg &= ~0xff; - reg |= y & 0xff; - writel(reg, regs + S5P_JPGY_L); -} - -void s5p_jpeg_x(void __iomem *regs, unsigned int x) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGX_U); - reg &= ~0xff; - reg |= (x >> 8) & 0xff; - writel(reg, regs + S5P_JPGX_U); - - reg = readl(regs + S5P_JPGX_L); - reg &= ~0xff; - reg |= x & 0xff; - writel(reg, regs + S5P_JPGX_L); -} - -void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_RSTm_INT_EN_MASK; - if (enable) - reg |= S5P_RSTm_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_DATA_NUM_INT_EN_MASK; - if (enable) - reg |= S5P_DATA_NUM_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPGINTSE); - reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK; - if (enbl) - reg |= S5P_FINAL_MCU_NUM_INT_EN; - writel(reg, regs + S5P_JPGINTSE); -} - -int s5p_jpeg_timer_stat(void __iomem *regs) -{ - return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK) - >> S5P_TIMER_INT_STAT_SHIFT); -} - -void s5p_jpeg_clear_timer_stat(void __iomem *regs) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_TIMER_SE); - reg &= ~S5P_TIMER_INT_STAT_MASK; - writel(reg, regs + S5P_JPG_TIMER_SE); -} - -void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); - reg &= ~S5P_ENC_STREAM_BOUND_MASK; - reg |= S5P_ENC_STREAM_INT_EN; - reg |= size & S5P_ENC_STREAM_BOUND_MASK; - writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); -} - -int s5p_jpeg_enc_stream_stat(void __iomem *regs) -{ - return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) & - S5P_ENC_STREAM_INT_STAT_MASK); -} - -void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); - reg &= ~S5P_ENC_STREAM_INT_MASK; - writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); -} - -void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format) -{ - unsigned long reg, f; - - f = S5P_DEC_OUT_FORMAT_422; - if (format == S5P_JPEG_RAW_OUT_422) - f = S5P_DEC_OUT_FORMAT_422; - else if (format == S5P_JPEG_RAW_OUT_420) - f = S5P_DEC_OUT_FORMAT_420; - reg = readl(regs + S5P_JPG_OUTFORM); - reg &= ~S5P_DEC_OUT_FORMAT_MASK; - reg |= f; - writel(reg, regs + S5P_JPG_OUTFORM); -} - -void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr) -{ - writel(addr, regs + S5P_JPG_JPGADR); -} - -void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr) -{ - writel(addr, regs + S5P_JPG_IMGADR); -} - -void s5p_jpeg_coef(void __iomem *regs, unsigned int i, - unsigned int j, unsigned int coef) -{ - unsigned long reg; - - reg = readl(regs + S5P_JPG_COEF(i)); - reg &= ~S5P_COEFn_MASK(j); - reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j); - writel(reg, regs + S5P_JPG_COEF(i)); -} - -void s5p_jpeg_start(void __iomem *regs) -{ - writel(1, regs + S5P_JSTART); -} - -int s5p_jpeg_result_stat_ok(void __iomem *regs) -{ - return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK) - >> S5P_RESULT_STAT_SHIFT); -} - -int s5p_jpeg_stream_stat_ok(void __iomem *regs) -{ - return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK) - >> S5P_STREAM_STAT_SHIFT); -} - -void s5p_jpeg_clear_int(void __iomem *regs) -{ - readl(regs + S5P_JPGINTST); - writel(S5P_INT_RELEASE, regs + S5P_JPGCOM); - readl(regs + S5P_JPGOPR); -} - -unsigned int s5p_jpeg_compressed_size(void __iomem *regs) -{ - unsigned long jpeg_size = 0; - - jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16; - jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8; - jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff); - - return (unsigned int)jpeg_size; -} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h deleted file mode 100644 index 98ddf7097562..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - */ -#ifndef JPEG_HW_S5P_H_ -#define JPEG_HW_S5P_H_ - -#include -#include - -#include "jpeg-regs.h" - -#define S5P_JPEG_MIN_WIDTH 32 -#define S5P_JPEG_MIN_HEIGHT 32 -#define S5P_JPEG_MAX_WIDTH 8192 -#define S5P_JPEG_MAX_HEIGHT 8192 -#define S5P_JPEG_RAW_IN_565 0 -#define S5P_JPEG_RAW_IN_422 1 -#define S5P_JPEG_RAW_OUT_422 0 -#define S5P_JPEG_RAW_OUT_420 1 - -void s5p_jpeg_reset(void __iomem *regs); -void s5p_jpeg_poweron(void __iomem *regs); -void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode); -void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode); -void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); -unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs); -void s5p_jpeg_dri(void __iomem *regs, unsigned int dri); -void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); -void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t); -void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t); -void s5p_jpeg_y(void __iomem *regs, unsigned int y); -void s5p_jpeg_x(void __iomem *regs, unsigned int x); -void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable); -void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable); -void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl); -int s5p_jpeg_timer_stat(void __iomem *regs); -void s5p_jpeg_clear_timer_stat(void __iomem *regs); -void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size); -int s5p_jpeg_enc_stream_stat(void __iomem *regs); -void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs); -void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format); -void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr); -void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr); -void s5p_jpeg_coef(void __iomem *regs, unsigned int i, - unsigned int j, unsigned int coef); -void s5p_jpeg_start(void __iomem *regs); -int s5p_jpeg_result_stat_ok(void __iomem *regs); -int s5p_jpeg_stream_stat_ok(void __iomem *regs); -void s5p_jpeg_clear_int(void __iomem *regs); -unsigned int s5p_jpeg_compressed_size(void __iomem *regs); - -#endif /* JPEG_HW_S5P_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h deleted file mode 100644 index 86f376b50581..000000000000 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ /dev/null @@ -1,646 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* linux/drivers/media/platform/s5p-jpeg/jpeg-regs.h - * - * Register definition file for Samsung JPEG codec driver - * - * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Andrzej Pietrasiewicz - * Author: Jacek Anaszewski - */ - -#ifndef JPEG_REGS_H_ -#define JPEG_REGS_H_ - -/* Register and bit definitions for S5PC210 */ - -/* JPEG mode register */ -#define S5P_JPGMOD 0x00 -#define S5P_PROC_MODE_MASK (0x1 << 3) -#define S5P_PROC_MODE_DECOMPR (0x1 << 3) -#define S5P_PROC_MODE_COMPR (0x0 << 3) -#define S5P_SUBSAMPLING_MODE_MASK 0x7 -#define S5P_SUBSAMPLING_MODE_444 (0x0 << 0) -#define S5P_SUBSAMPLING_MODE_422 (0x1 << 0) -#define S5P_SUBSAMPLING_MODE_420 (0x2 << 0) -#define S5P_SUBSAMPLING_MODE_GRAY (0x3 << 0) - -/* JPEG operation status register */ -#define S5P_JPGOPR 0x04 - -/* Quantization tables*/ -#define S5P_JPG_QTBL 0x08 -#define S5P_QT_NUMt_SHIFT(t) (((t) - 1) << 1) -#define S5P_QT_NUMt_MASK(t) (0x3 << S5P_QT_NUMt_SHIFT(t)) - -/* Huffman tables */ -#define S5P_JPG_HTBL 0x0c -#define S5P_HT_NUMt_AC_SHIFT(t) (((t) << 1) - 1) -#define S5P_HT_NUMt_AC_MASK(t) (0x1 << S5P_HT_NUMt_AC_SHIFT(t)) - -#define S5P_HT_NUMt_DC_SHIFT(t) (((t) - 1) << 1) -#define S5P_HT_NUMt_DC_MASK(t) (0x1 << S5P_HT_NUMt_DC_SHIFT(t)) - -/* JPEG restart interval register upper byte */ -#define S5P_JPGDRI_U 0x10 - -/* JPEG restart interval register lower byte */ -#define S5P_JPGDRI_L 0x14 - -/* JPEG vertical resolution register upper byte */ -#define S5P_JPGY_U 0x18 - -/* JPEG vertical resolution register lower byte */ -#define S5P_JPGY_L 0x1c - -/* JPEG horizontal resolution register upper byte */ -#define S5P_JPGX_U 0x20 - -/* JPEG horizontal resolution register lower byte */ -#define S5P_JPGX_L 0x24 - -/* JPEG byte count register upper byte */ -#define S5P_JPGCNT_U 0x28 - -/* JPEG byte count register middle byte */ -#define S5P_JPGCNT_M 0x2c - -/* JPEG byte count register lower byte */ -#define S5P_JPGCNT_L 0x30 - -/* JPEG interrupt setting register */ -#define S5P_JPGINTSE 0x34 -#define S5P_RSTm_INT_EN_MASK (0x1 << 7) -#define S5P_RSTm_INT_EN (0x1 << 7) -#define S5P_DATA_NUM_INT_EN_MASK (0x1 << 6) -#define S5P_DATA_NUM_INT_EN (0x1 << 6) -#define S5P_FINAL_MCU_NUM_INT_EN_MASK (0x1 << 5) -#define S5P_FINAL_MCU_NUM_INT_EN (0x1 << 5) - -/* JPEG interrupt status register */ -#define S5P_JPGINTST 0x38 -#define S5P_RESULT_STAT_SHIFT 6 -#define S5P_RESULT_STAT_MASK (0x1 << S5P_RESULT_STAT_SHIFT) -#define S5P_STREAM_STAT_SHIFT 5 -#define S5P_STREAM_STAT_MASK (0x1 << S5P_STREAM_STAT_SHIFT) - -/* JPEG command register */ -#define S5P_JPGCOM 0x4c -#define S5P_INT_RELEASE (0x1 << 2) - -/* Raw image data r/w address register */ -#define S5P_JPG_IMGADR 0x50 - -/* JPEG file r/w address register */ -#define S5P_JPG_JPGADR 0x58 - -/* Coefficient for RGB-to-YCbCr converter register */ -#define S5P_JPG_COEF(n) (0x5c + (((n) - 1) << 2)) -#define S5P_COEFn_SHIFT(j) ((3 - (j)) << 3) -#define S5P_COEFn_MASK(j) (0xff << S5P_COEFn_SHIFT(j)) - -/* JPEG color mode register */ -#define S5P_JPGCMOD 0x68 -#define S5P_MOD_SEL_MASK (0x7 << 5) -#define S5P_MOD_SEL_422 (0x1 << 5) -#define S5P_MOD_SEL_565 (0x2 << 5) -#define S5P_MODE_Y16_MASK (0x1 << 1) -#define S5P_MODE_Y16 (0x1 << 1) - -/* JPEG clock control register */ -#define S5P_JPGCLKCON 0x6c -#define S5P_CLK_DOWN_READY (0x1 << 1) -#define S5P_POWER_ON (0x1 << 0) - -/* JPEG start register */ -#define S5P_JSTART 0x70 - -/* JPEG SW reset register */ -#define S5P_JPG_SW_RESET 0x78 - -/* JPEG timer setting register */ -#define S5P_JPG_TIMER_SE 0x7c -#define S5P_TIMER_INT_EN_MASK (0x1UL << 31) -#define S5P_TIMER_INT_EN (0x1UL << 31) -#define S5P_TIMER_INIT_MASK 0x7fffffff - -/* JPEG timer status register */ -#define S5P_JPG_TIMER_ST 0x80 -#define S5P_TIMER_INT_STAT_SHIFT 31 -#define S5P_TIMER_INT_STAT_MASK (0x1UL << S5P_TIMER_INT_STAT_SHIFT) -#define S5P_TIMER_CNT_SHIFT 0 -#define S5P_TIMER_CNT_MASK 0x7fffffff - -/* JPEG decompression output format register */ -#define S5P_JPG_OUTFORM 0x88 -#define S5P_DEC_OUT_FORMAT_MASK (0x1 << 0) -#define S5P_DEC_OUT_FORMAT_422 (0x0 << 0) -#define S5P_DEC_OUT_FORMAT_420 (0x1 << 0) - -/* JPEG version register */ -#define S5P_JPG_VERSION 0x8c - -/* JPEG compressed stream size interrupt setting register */ -#define S5P_JPG_ENC_STREAM_INTSE 0x98 -#define S5P_ENC_STREAM_INT_MASK (0x1 << 24) -#define S5P_ENC_STREAM_INT_EN (0x1 << 24) -#define S5P_ENC_STREAM_BOUND_MASK 0xffffff - -/* JPEG compressed stream size interrupt status register */ -#define S5P_JPG_ENC_STREAM_INTST 0x9c -#define S5P_ENC_STREAM_INT_STAT_MASK 0x1 - -/* JPEG quantizer table register */ -#define S5P_JPG_QTBL_CONTENT(n) (0x400 + (n) * 0x100) - -/* JPEG DC Huffman table register */ -#define S5P_JPG_HDCTBL(n) (0x800 + (n) * 0x400) - -/* JPEG DC Huffman table register */ -#define S5P_JPG_HDCTBLG(n) (0x840 + (n) * 0x400) - -/* JPEG AC Huffman table register */ -#define S5P_JPG_HACTBL(n) (0x880 + (n) * 0x400) - -/* JPEG AC Huffman table register */ -#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400) - - -/* Register and bit definitions for Exynos 4x12 */ - -/* JPEG Codec Control Registers */ -#define EXYNOS4_JPEG_CNTL_REG 0x00 -#define EXYNOS4_INT_EN_REG 0x04 -#define EXYNOS4_INT_TIMER_COUNT_REG 0x08 -#define EXYNOS4_INT_STATUS_REG 0x0c -#define EXYNOS4_OUT_MEM_BASE_REG 0x10 -#define EXYNOS4_JPEG_IMG_SIZE_REG 0x14 -#define EXYNOS4_IMG_BA_PLANE_1_REG 0x18 -#define EXYNOS4_IMG_SO_PLANE_1_REG 0x1c -#define EXYNOS4_IMG_PO_PLANE_1_REG 0x20 -#define EXYNOS4_IMG_BA_PLANE_2_REG 0x24 -#define EXYNOS4_IMG_SO_PLANE_2_REG 0x28 -#define EXYNOS4_IMG_PO_PLANE_2_REG 0x2c -#define EXYNOS4_IMG_BA_PLANE_3_REG 0x30 -#define EXYNOS4_IMG_SO_PLANE_3_REG 0x34 -#define EXYNOS4_IMG_PO_PLANE_3_REG 0x38 - -#define EXYNOS4_TBL_SEL_REG 0x3c - -#define EXYNOS4_IMG_FMT_REG 0x40 - -#define EXYNOS4_BITSTREAM_SIZE_REG 0x44 -#define EXYNOS4_PADDING_REG 0x48 -#define EXYNOS4_HUFF_CNT_REG 0x4c -#define EXYNOS4_FIFO_STATUS_REG 0x50 -#define EXYNOS4_DECODE_XY_SIZE_REG 0x54 -#define EXYNOS4_DECODE_IMG_FMT_REG 0x58 - -#define EXYNOS4_QUAN_TBL_ENTRY_REG 0x100 -#define EXYNOS4_HUFF_TBL_ENTRY_REG 0x200 - - -/****************************************************************/ -/* Bit definition part */ -/****************************************************************/ - -/* JPEG CNTL Register bit */ -#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0) -#define EXYNOS4_DEC_MODE (1 << 0) -#define EXYNOS4_ENC_MODE (1 << 1) -#define EXYNOS4_AUTO_RST_MARKER (1 << 2) -#define EXYNOS4_RST_INTERVAL_SHIFT 3 -#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \ - << EXYNOS4_RST_INTERVAL_SHIFT) -#define EXYNOS4_HUF_TBL_EN (1 << 19) -#define EXYNOS4_HOR_SCALING_SHIFT 20 -#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT) -#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \ - << EXYNOS4_HOR_SCALING_SHIFT) -#define EXYNOS4_VER_SCALING_SHIFT 22 -#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT) -#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \ - << EXYNOS4_VER_SCALING_SHIFT) -#define EXYNOS4_PADDING (1 << 27) -#define EXYNOS4_SYS_INT_EN (1 << 28) -#define EXYNOS4_SOFT_RESET_HI (1 << 29) - -/* JPEG INT Register bit */ -#define EXYNOS4_INT_EN_MASK (0x1f << 0) -#define EXYNOS5433_INT_EN_MASK (0x1ff << 0) -#define EXYNOS4_PROT_ERR_INT_EN (1 << 0) -#define EXYNOS4_IMG_COMPLETION_INT_EN (1 << 1) -#define EXYNOS4_DEC_INVALID_FORMAT_EN (1 << 2) -#define EXYNOS4_MULTI_SCAN_ERROR_EN (1 << 3) -#define EXYNOS4_FRAME_ERR_EN (1 << 4) -#define EXYNOS4_INT_EN_ALL (0x1f << 0) -#define EXYNOS5433_INT_EN_ALL (0x1b6 << 0) - -#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) -#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) - -#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0) -#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0) -#define EXYNOS4_MOD_REG_SUBSAMPLE_420 (2 << 0) -#define EXYNOS4_MOD_REG_SUBSAMPLE_GRAY (3 << 0) - - -/* JPEG IMAGE SIZE Register bit */ -#define EXYNOS4_X_SIZE_SHIFT 0 -#define EXYNOS4_X_SIZE_MASK (0xffff << EXYNOS4_X_SIZE_SHIFT) -#define EXYNOS4_X_SIZE(x) (((x) & 0xffff) << EXYNOS4_X_SIZE_SHIFT) -#define EXYNOS4_Y_SIZE_SHIFT 16 -#define EXYNOS4_Y_SIZE_MASK (0xffff << EXYNOS4_Y_SIZE_SHIFT) -#define EXYNOS4_Y_SIZE(x) (((x) & 0xffff) << EXYNOS4_Y_SIZE_SHIFT) - -/* JPEG IMAGE FORMAT Register bit */ -#define EXYNOS4_ENC_IN_FMT_MASK 0xffff0000 -#define EXYNOS4_ENC_GRAY_IMG (0 << 0) -#define EXYNOS4_ENC_RGB_IMG (1 << 0) -#define EXYNOS4_ENC_YUV_444_IMG (2 << 0) -#define EXYNOS4_ENC_YUV_422_IMG (3 << 0) -#define EXYNOS4_ENC_YUV_440_IMG (4 << 0) - -#define EXYNOS4_DEC_GRAY_IMG (0 << 0) -#define EXYNOS4_DEC_RGB_IMG (1 << 0) -#define EXYNOS4_DEC_YUV_444_IMG (2 << 0) -#define EXYNOS4_DEC_YUV_422_IMG (3 << 0) -#define EXYNOS4_DEC_YUV_420_IMG (4 << 0) - -#define EXYNOS4_GRAY_IMG_IP_SHIFT 3 -#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT) -#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT) - -#define EXYNOS4_RGB_IP_SHIFT 6 -#define EXYNOS4_RGB_IP_MASK (7 << EXYNOS4_RGB_IP_SHIFT) -#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT) -#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT) - -#define EXYNOS4_YUV_444_IP_SHIFT 9 -#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT) -#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT) -#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT) - -#define EXYNOS4_YUV_422_IP_SHIFT 12 -#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT) -#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT) -#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT) -#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT) - -#define EXYNOS4_YUV_420_IP_SHIFT 15 -#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT) -#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT) -#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT) - -#define EXYNOS4_ENC_FMT_SHIFT 24 -#define EXYNOS4_ENC_FMT_MASK (3 << EXYNOS4_ENC_FMT_SHIFT) -#define EXYNOS5433_ENC_FMT_MASK (7 << EXYNOS4_ENC_FMT_SHIFT) - -#define EXYNOS4_ENC_FMT_GRAY (0 << EXYNOS4_ENC_FMT_SHIFT) -#define EXYNOS4_ENC_FMT_YUV_444 (1 << EXYNOS4_ENC_FMT_SHIFT) -#define EXYNOS4_ENC_FMT_YUV_422 (2 << EXYNOS4_ENC_FMT_SHIFT) -#define EXYNOS4_ENC_FMT_YUV_420 (3 << EXYNOS4_ENC_FMT_SHIFT) - -#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03 - -#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) -#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) -#define EXYNOS5433_SWAP_CHROMA_CRCB (1 << 27) -#define EXYNOS5433_SWAP_CHROMA_CBCR (0 << 27) - -/* JPEG HUFF count Register bit */ -#define EXYNOS4_HUFF_COUNT_MASK 0xffff - -/* JPEG Decoded_img_x_y_size Register bit */ -#define EXYNOS4_DECODED_SIZE_MASK 0x0000ffff - -/* JPEG Decoded image format Register bit */ -#define EXYNOS4_DECODED_IMG_FMT_MASK 0x3 - -/* JPEG TBL SEL Register bit */ -#define EXYNOS4_Q_TBL_COMP(c, n) ((n) << (((c) - 1) << 1)) - -#define EXYNOS4_Q_TBL_COMP1_0 EXYNOS4_Q_TBL_COMP(1, 0) -#define EXYNOS4_Q_TBL_COMP1_1 EXYNOS4_Q_TBL_COMP(1, 1) -#define EXYNOS4_Q_TBL_COMP1_2 EXYNOS4_Q_TBL_COMP(1, 2) -#define EXYNOS4_Q_TBL_COMP1_3 EXYNOS4_Q_TBL_COMP(1, 3) - -#define EXYNOS4_Q_TBL_COMP2_0 EXYNOS4_Q_TBL_COMP(2, 0) -#define EXYNOS4_Q_TBL_COMP2_1 EXYNOS4_Q_TBL_COMP(2, 1) -#define EXYNOS4_Q_TBL_COMP2_2 EXYNOS4_Q_TBL_COMP(2, 2) -#define EXYNOS4_Q_TBL_COMP2_3 EXYNOS4_Q_TBL_COMP(2, 3) - -#define EXYNOS4_Q_TBL_COMP3_0 EXYNOS4_Q_TBL_COMP(3, 0) -#define EXYNOS4_Q_TBL_COMP3_1 EXYNOS4_Q_TBL_COMP(3, 1) -#define EXYNOS4_Q_TBL_COMP3_2 EXYNOS4_Q_TBL_COMP(3, 2) -#define EXYNOS4_Q_TBL_COMP3_3 EXYNOS4_Q_TBL_COMP(3, 3) - -#define EXYNOS4_HUFF_TBL_COMP(c, n) ((n) << ((((c) - 1) << 1) + 6)) - -#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(1, 0) -#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(1, 1) -#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(1, 2) -#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(1, 3) - -#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(2, 0) -#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(2, 1) -#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(2, 2) -#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(2, 3) - -#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(3, 0) -#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(3, 1) -#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 \ - EXYNOS4_HUFF_TBL_COMP(3, 2) -#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 \ - EXYNOS4_HUFF_TBL_COMP(3, 3) - -#define EXYNOS4_NF_SHIFT 16 -#define EXYNOS4_NF_MASK 0xff -#define EXYNOS4_NF(x) \ - (((x) & EXYNOS4_NF_MASK) << EXYNOS4_NF_SHIFT) - -/* JPEG quantizer table register */ -#define EXYNOS4_QTBL_CONTENT(n) (0x100 + (n) * 0x40) - -/* JPEG DC luminance (code length) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HDCLL 0x200 - -/* JPEG DC luminance (values) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HDCLV 0x210 - -/* JPEG DC chrominance (code length) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HDCCL 0x220 - -/* JPEG DC chrominance (values) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HDCCV 0x230 - -/* JPEG AC luminance (code length) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HACLL 0x240 - -/* JPEG AC luminance (values) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HACLV 0x250 - -/* JPEG AC chrominance (code length) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HACCL 0x300 - -/* JPEG AC chrominance (values) Huffman table register */ -#define EXYNOS4_HUFF_TBL_HACCV 0x310 - -/* Register and bit definitions for Exynos 3250 */ - -/* JPEG mode register */ -#define EXYNOS3250_JPGMOD 0x00 -#define EXYNOS3250_PROC_MODE_MASK (0x1 << 3) -#define EXYNOS3250_PROC_MODE_DECOMPR (0x1 << 3) -#define EXYNOS3250_PROC_MODE_COMPR (0x0 << 3) -#define EXYNOS3250_SUBSAMPLING_MODE_MASK (0x7 << 0) -#define EXYNOS3250_SUBSAMPLING_MODE_444 (0x0 << 0) -#define EXYNOS3250_SUBSAMPLING_MODE_422 (0x1 << 0) -#define EXYNOS3250_SUBSAMPLING_MODE_420 (0x2 << 0) -#define EXYNOS3250_SUBSAMPLING_MODE_411 (0x6 << 0) -#define EXYNOS3250_SUBSAMPLING_MODE_GRAY (0x3 << 0) - -/* JPEG operation status register */ -#define EXYNOS3250_JPGOPR 0x04 -#define EXYNOS3250_JPGOPR_MASK 0x01 - -/* Quantization and Huffman tables register */ -#define EXYNOS3250_QHTBL 0x08 -#define EXYNOS3250_QT_NUM_SHIFT(t) ((((t) - 1) << 1) + 8) -#define EXYNOS3250_QT_NUM_MASK(t) (0x3 << EXYNOS3250_QT_NUM_SHIFT(t)) - -/* Huffman tables */ -#define EXYNOS3250_HT_NUM_AC_SHIFT(t) (((t) << 1) - 1) -#define EXYNOS3250_HT_NUM_AC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) - -#define EXYNOS3250_HT_NUM_DC_SHIFT(t) (((t) - 1) << 1) -#define EXYNOS3250_HT_NUM_DC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) - -/* JPEG restart interval register */ -#define EXYNOS3250_JPGDRI 0x0c -#define EXYNOS3250_JPGDRI_MASK 0xffff - -/* JPEG vertical resolution register */ -#define EXYNOS3250_JPGY 0x10 -#define EXYNOS3250_JPGY_MASK 0xffff - -/* JPEG horizontal resolution register */ -#define EXYNOS3250_JPGX 0x14 -#define EXYNOS3250_JPGX_MASK 0xffff - -/* JPEG byte count register */ -#define EXYNOS3250_JPGCNT 0x18 -#define EXYNOS3250_JPGCNT_MASK 0xffffff - -/* JPEG interrupt mask register */ -#define EXYNOS3250_JPGINTSE 0x1c -#define EXYNOS3250_JPEG_DONE_EN (1 << 11) -#define EXYNOS3250_WDMA_DONE_EN (1 << 10) -#define EXYNOS3250_RDMA_DONE_EN (1 << 9) -#define EXYNOS3250_ENC_STREAM_INT_EN (1 << 8) -#define EXYNOS3250_CORE_DONE_EN (1 << 5) -#define EXYNOS3250_ERR_INT_EN (1 << 4) -#define EXYNOS3250_HEAD_INT_EN (1 << 3) - -/* JPEG interrupt status register */ -#define EXYNOS3250_JPGINTST 0x20 -#define EXYNOS3250_JPEG_DONE (1 << 11) -#define EXYNOS3250_WDMA_DONE (1 << 10) -#define EXYNOS3250_RDMA_DONE (1 << 9) -#define EXYNOS3250_ENC_STREAM_STAT (1 << 8) -#define EXYNOS3250_RESULT_STAT (1 << 5) -#define EXYNOS3250_STREAM_STAT (1 << 4) -#define EXYNOS3250_HEADER_STAT (1 << 3) - -/* - * Base address of the luma component DMA buffer - * of the raw input or output image. - */ -#define EXYNOS3250_LUMA_BASE 0x100 -#define EXYNOS3250_SRC_TILE_EN_MASK 0x100 - -/* Stride of source or destination luma raw image buffer */ -#define EXYNOS3250_LUMA_STRIDE 0x104 - -/* Horizontal/vertical offset of active region in luma raw image buffer */ -#define EXYNOS3250_LUMA_XY_OFFSET 0x108 -#define EXYNOS3250_LUMA_YY_OFFSET_SHIFT 18 -#define EXYNOS3250_LUMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) -#define EXYNOS3250_LUMA_YX_OFFSET_SHIFT 2 -#define EXYNOS3250_LUMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) - -/* - * Base address of the chroma(Cb) component DMA buffer - * of the raw input or output image. - */ -#define EXYNOS3250_CHROMA_BASE 0x10c - -/* Stride of source or destination chroma(Cb) raw image buffer */ -#define EXYNOS3250_CHROMA_STRIDE 0x110 - -/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ -#define EXYNOS3250_CHROMA_XY_OFFSET 0x114 -#define EXYNOS3250_CHROMA_YY_OFFSET_SHIFT 18 -#define EXYNOS3250_CHROMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) -#define EXYNOS3250_CHROMA_YX_OFFSET_SHIFT 2 -#define EXYNOS3250_CHROMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) - -/* - * Base address of the chroma(Cr) component DMA buffer - * of the raw input or output image. - */ -#define EXYNOS3250_CHROMA_CR_BASE 0x118 - -/* Stride of source or destination chroma(Cr) raw image buffer */ -#define EXYNOS3250_CHROMA_CR_STRIDE 0x11c - -/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ -#define EXYNOS3250_CHROMA_CR_XY_OFFSET 0x120 -#define EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT 18 -#define EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) -#define EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT 2 -#define EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) - -/* Raw image data r/w address register */ -#define EXYNOS3250_JPG_IMGADR 0x50 - -/* Source or destination JPEG file DMA buffer address */ -#define EXYNOS3250_JPG_JPGADR 0x124 - -/* Coefficients for RGB-to-YCbCr converter register */ -#define EXYNOS3250_JPG_COEF(n) (0x128 + (((n) - 1) << 2)) -#define EXYNOS3250_COEF_SHIFT(j) ((3 - (j)) << 3) -#define EXYNOS3250_COEF_MASK(j) (0xff << EXYNOS3250_COEF_SHIFT(j)) - -/* Raw input format setting */ -#define EXYNOS3250_JPGCMOD 0x134 -#define EXYNOS3250_SRC_TILE_EN (0x1 << 10) -#define EXYNOS3250_SRC_NV_MASK (0x1 << 9) -#define EXYNOS3250_SRC_NV12 (0x0 << 9) -#define EXYNOS3250_SRC_NV21 (0x1 << 9) -#define EXYNOS3250_SRC_BIG_ENDIAN_MASK (0x1 << 8) -#define EXYNOS3250_SRC_BIG_ENDIAN (0x1 << 8) -#define EXYNOS3250_MODE_SEL_MASK (0x7 << 5) -#define EXYNOS3250_MODE_SEL_420_2P (0x0 << 5) -#define EXYNOS3250_MODE_SEL_422_1P_LUM_CHR (0x1 << 5) -#define EXYNOS3250_MODE_SEL_RGB565 (0x2 << 5) -#define EXYNOS3250_MODE_SEL_422_1P_CHR_LUM (0x3 << 5) -#define EXYNOS3250_MODE_SEL_ARGB8888 (0x4 << 5) -#define EXYNOS3250_MODE_SEL_420_3P (0x5 << 5) -#define EXYNOS3250_SRC_SWAP_RGB (0x1 << 3) -#define EXYNOS3250_SRC_SWAP_UV (0x1 << 2) -#define EXYNOS3250_MODE_Y16_MASK (0x1 << 1) -#define EXYNOS3250_MODE_Y16 (0x1 << 1) -#define EXYNOS3250_HALF_EN_MASK (0x1 << 0) -#define EXYNOS3250_HALF_EN (0x1 << 0) - -/* Power on/off and clock down control */ -#define EXYNOS3250_JPGCLKCON 0x138 -#define EXYNOS3250_CLK_DOWN_READY (0x1 << 1) -#define EXYNOS3250_POWER_ON (0x1 << 0) - -/* Start compression or decompression */ -#define EXYNOS3250_JSTART 0x13c - -/* Restart decompression after header analysis */ -#define EXYNOS3250_JRSTART 0x140 - -/* JPEG SW reset register */ -#define EXYNOS3250_SW_RESET 0x144 - -/* JPEG timer setting register */ -#define EXYNOS3250_TIMER_SE 0x148 -#define EXYNOS3250_TIMER_INT_EN_SHIFT 31 -#define EXYNOS3250_TIMER_INT_EN (1UL << EXYNOS3250_TIMER_INT_EN_SHIFT) -#define EXYNOS3250_TIMER_INIT_MASK 0x7fffffff - -/* JPEG timer status register */ -#define EXYNOS3250_TIMER_ST 0x14c -#define EXYNOS3250_TIMER_INT_STAT_SHIFT 31 -#define EXYNOS3250_TIMER_INT_STAT (1UL << EXYNOS3250_TIMER_INT_STAT_SHIFT) -#define EXYNOS3250_TIMER_CNT_SHIFT 0 -#define EXYNOS3250_TIMER_CNT_MASK 0x7fffffff - -/* Command status register */ -#define EXYNOS3250_COMSTAT 0x150 -#define EXYNOS3250_CUR_PROC_MODE (0x1 << 1) -#define EXYNOS3250_CUR_COM_MODE (0x1 << 0) - -/* JPEG decompression output format register */ -#define EXYNOS3250_OUTFORM 0x154 -#define EXYNOS3250_OUT_ALPHA_MASK (0xff << 24) -#define EXYNOS3250_OUT_TILE_EN (0x1 << 10) -#define EXYNOS3250_OUT_NV_MASK (0x1 << 9) -#define EXYNOS3250_OUT_NV12 (0x0 << 9) -#define EXYNOS3250_OUT_NV21 (0x1 << 9) -#define EXYNOS3250_OUT_BIG_ENDIAN_MASK (0x1 << 8) -#define EXYNOS3250_OUT_BIG_ENDIAN (0x1 << 8) -#define EXYNOS3250_OUT_SWAP_RGB (0x1 << 7) -#define EXYNOS3250_OUT_SWAP_UV (0x1 << 6) -#define EXYNOS3250_OUT_FMT_MASK (0x7 << 0) -#define EXYNOS3250_OUT_FMT_420_2P (0x0 << 0) -#define EXYNOS3250_OUT_FMT_422_1P_LUM_CHR (0x1 << 0) -#define EXYNOS3250_OUT_FMT_422_1P_CHR_LUM (0x3 << 0) -#define EXYNOS3250_OUT_FMT_420_3P (0x4 << 0) -#define EXYNOS3250_OUT_FMT_RGB565 (0x5 << 0) -#define EXYNOS3250_OUT_FMT_ARGB8888 (0x6 << 0) - -/* Input JPEG stream byte size for decompression */ -#define EXYNOS3250_DEC_STREAM_SIZE 0x158 -#define EXYNOS3250_DEC_STREAM_MASK 0x1fffffff - -/* The upper bound of the byte size of output compressed stream */ -#define EXYNOS3250_ENC_STREAM_BOUND 0x15c -#define EXYNOS3250_ENC_STREAM_BOUND_MASK 0xffffc0 - -/* Scale-down ratio when decoding */ -#define EXYNOS3250_DEC_SCALING_RATIO 0x160 -#define EXYNOS3250_DEC_SCALE_FACTOR_MASK 0x3 -#define EXYNOS3250_DEC_SCALE_FACTOR_8_8 0x0 -#define EXYNOS3250_DEC_SCALE_FACTOR_4_8 0x1 -#define EXYNOS3250_DEC_SCALE_FACTOR_2_8 0x2 -#define EXYNOS3250_DEC_SCALE_FACTOR_1_8 0x3 - -/* Error check */ -#define EXYNOS3250_CRC_RESULT 0x164 - -/* RDMA and WDMA operation status register */ -#define EXYNOS3250_DMA_OPER_STATUS 0x168 -#define EXYNOS3250_WDMA_OPER_STATUS (0x1 << 1) -#define EXYNOS3250_RDMA_OPER_STATUS (0x1 << 0) - -/* DMA issue gathering number and issue number settings */ -#define EXYNOS3250_DMA_ISSUE_NUM 0x16c -#define EXYNOS3250_WDMA_ISSUE_NUM_SHIFT 16 -#define EXYNOS3250_WDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) -#define EXYNOS3250_RDMA_ISSUE_NUM_SHIFT 8 -#define EXYNOS3250_RDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) -#define EXYNOS3250_ISSUE_GATHER_NUM_SHIFT 0 -#define EXYNOS3250_ISSUE_GATHER_NUM_MASK (0x7 << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) -#define EXYNOS3250_DMA_MO_COUNT 0x7 - -/* Version register */ -#define EXYNOS3250_VERSION 0x1fc - -/* RGB <-> YUV conversion coefficients */ -#define EXYNOS3250_JPEG_ENC_COEF1 0x01352e1e -#define EXYNOS3250_JPEG_ENC_COEF2 0x00b0ae83 -#define EXYNOS3250_JPEG_ENC_COEF3 0x020cdc13 - -#define EXYNOS3250_JPEG_DEC_COEF1 0x04a80199 -#define EXYNOS3250_JPEG_DEC_COEF2 0x04a9a064 -#define EXYNOS3250_JPEG_DEC_COEF3 0x04a80102 - -#endif /* JPEG_REGS_H_ */ - diff --git a/drivers/media/platform/samsung/s5p-jpeg/Kconfig b/drivers/media/platform/samsung/s5p-jpeg/Kconfig new file mode 100644 index 000000000000..e522860d2b15 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_SAMSUNG_S5P_JPEG + tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung S5P, EXYNOS3250 + and EXYNOS4 JPEG codec diff --git a/drivers/media/platform/samsung/s5p-jpeg/Makefile b/drivers/media/platform/samsung/s5p-jpeg/Makefile new file mode 100644 index 000000000000..8b0f92e27e70 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos3250.o jpeg-hw-exynos4.o jpeg-hw-s5p.o +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c new file mode 100644 index 000000000000..5479bc8d474d --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -0,0 +1,3182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c + * + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * Author: Jacek Anaszewski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jpeg-core.h" +#include "jpeg-hw-s5p.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-hw-exynos3250.h" +#include "jpeg-regs.h" + +static struct s5p_jpeg_fmt sjpeg_formats[] = { + { + .fourcc = V4L2_PIX_FMT_JPEG, + .flags = SJPEG_FMT_FLAG_ENC_CAPTURE | + SJPEG_FMT_FLAG_DEC_OUTPUT | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_FLAG_EXYNOS4, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .colplanes = 1, + .h_align = 4, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .colplanes = 1, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .colplanes = 1, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .colplanes = 1, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .colplanes = 1, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .colplanes = 1, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_NV24, + .depth = 24, + .colplanes = 2, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_NV42, + .depth = 24, + .colplanes = 2, + .h_align = 0, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .depth = 16, + .colplanes = 2, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .depth = 16, + .colplanes = 2, + .h_align = 1, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .colplanes = 2, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .colplanes = 2, + .h_align = 3, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .colplanes = 2, + .h_align = 4, + .v_align = 4, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 12, + .colplanes = 2, + .h_align = 3, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 12, + .colplanes = 2, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .colplanes = 3, + .h_align = 1, + .v_align = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .colplanes = 3, + .h_align = 4, + .v_align = 4, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, + .colplanes = 1, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS4 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + }, +}; +#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats) + +static const unsigned char qtbl_luminance[4][64] = { + {/*level 0 - high compression quality */ + 20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68 + }, + {/* level 1 */ + 16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64 + }, + {/* level 2 */ + 12, 8, 8, 12, 17, 21, 24, 23, + 8, 9, 9, 11, 15, 19, 18, 23, + 8, 9, 10, 12, 19, 20, 27, 36, + 12, 11, 12, 21, 20, 28, 36, 53, + 17, 15, 19, 20, 30, 39, 51, 59, + 21, 19, 20, 28, 39, 51, 59, 59, + 24, 18, 27, 36, 51, 59, 59, 59, + 23, 23, 36, 53, 59, 59, 59, 59 + }, + {/* level 3 - low compression quality */ + 8, 6, 6, 8, 12, 14, 16, 17, + 6, 6, 6, 8, 10, 13, 12, 15, + 6, 6, 7, 8, 13, 14, 18, 24, + 8, 8, 8, 14, 13, 19, 24, 35, + 12, 10, 13, 13, 20, 26, 34, 39, + 14, 13, 14, 19, 26, 34, 39, 39, + 16, 12, 18, 24, 34, 39, 39, 39, + 17, 15, 24, 35, 39, 39, 39, 39 + } +}; + +static const unsigned char qtbl_chrominance[4][64] = { + {/*level 0 - high compression quality */ + 21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68 + }, + {/* level 1 */ + 17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64 + }, + {/* level 2 */ + 13, 11, 13, 16, 20, 20, 29, 37, + 11, 14, 14, 14, 16, 20, 26, 32, + 13, 14, 15, 17, 20, 23, 35, 40, + 16, 14, 17, 21, 23, 30, 40, 50, + 20, 16, 20, 23, 30, 37, 50, 59, + 20, 20, 23, 30, 37, 48, 59, 59, + 29, 26, 35, 40, 50, 59, 59, 59, + 37, 32, 40, 50, 59, 59, 59, 59 + }, + {/* level 3 - low compression quality */ + 9, 8, 9, 11, 14, 17, 19, 24, + 8, 10, 9, 11, 14, 13, 17, 22, + 9, 9, 13, 14, 13, 15, 23, 26, + 11, 11, 14, 14, 15, 20, 26, 33, + 14, 14, 13, 15, 20, 24, 33, 39, + 17, 13, 15, 20, 24, 32, 39, 39, + 19, 17, 23, 26, 33, 39, 39, 39, + 24, 22, 26, 33, 39, 39, 39, 39 + } +}; + +static const unsigned char hdctbl0[16] = { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char hdctblg0[12] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb +}; +static const unsigned char hactbl0[16] = { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; +static const unsigned char hactblg0[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/* + * Fourcc downgrade schema lookup tables for 422 and 420 + * chroma subsampling - fourcc on each position maps on the + * fourcc from the table fourcc_to_dwngrd_schema_id which allows + * to get the most suitable fourcc counterpart for the given + * downgraded subsampling property. + */ +static const u32 subs422_fourcc_dwngrd_schema[] = { + V4L2_PIX_FMT_NV16, + V4L2_PIX_FMT_NV61, +}; + +static const u32 subs420_fourcc_dwngrd_schema[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_GREY, +}; + +/* + * Lookup table for translation of a fourcc to the position + * of its downgraded counterpart in the *fourcc_dwngrd_schema + * tables. + */ +static const u32 fourcc_to_dwngrd_schema_id[] = { + V4L2_PIX_FMT_NV24, + V4L2_PIX_FMT_NV42, + V4L2_PIX_FMT_NV16, + V4L2_PIX_FMT_NV61, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_YVYU, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_GREY, +}; + +static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) { + if (fourcc_to_dwngrd_schema_id[i] == fourcc) + return i; + } + + return -EINVAL; +} + +static int s5p_jpeg_adjust_fourcc_to_subsampling( + enum v4l2_jpeg_chroma_subsampling subs, + u32 in_fourcc, + u32 *out_fourcc, + struct s5p_jpeg_ctx *ctx) +{ + int dwngrd_sch_id; + + if (ctx->subsampling != V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { + dwngrd_sch_id = + s5p_jpeg_get_dwngrd_sch_id_by_fourcc(in_fourcc); + if (dwngrd_sch_id < 0) + return -EINVAL; + } + + switch (ctx->subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: + *out_fourcc = V4L2_PIX_FMT_GREY; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + if (dwngrd_sch_id > + ARRAY_SIZE(subs420_fourcc_dwngrd_schema) - 1) + return -EINVAL; + *out_fourcc = subs420_fourcc_dwngrd_schema[dwngrd_sch_id]; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + if (dwngrd_sch_id > + ARRAY_SIZE(subs422_fourcc_dwngrd_schema) - 1) + return -EINVAL; + *out_fourcc = subs422_fourcc_dwngrd_schema[dwngrd_sch_id]; + break; + default: + *out_fourcc = V4L2_PIX_FMT_GREY; + break; + } + + return 0; +} + +static int exynos4x12_decoded_subsampling[] = { + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + V4L2_JPEG_CHROMA_SUBSAMPLING_444, + V4L2_JPEG_CHROMA_SUBSAMPLING_422, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, +}; + +static int exynos3250_decoded_subsampling[] = { + V4L2_JPEG_CHROMA_SUBSAMPLING_444, + V4L2_JPEG_CHROMA_SUBSAMPLING_422, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + -1, + -1, + V4L2_JPEG_CHROMA_SUBSAMPLING_411, +}; + +static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) +{ + return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); +} + +static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct s5p_jpeg_ctx, fh); +} + +static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) +{ + switch (ctx->jpeg->variant->version) { + case SJPEG_S5P: + WARN_ON(ctx->subsampling > 3); + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + return ctx->subsampling; + case SJPEG_EXYNOS3250: + case SJPEG_EXYNOS5420: + WARN_ON(ctx->subsampling > 6); + if (ctx->subsampling > 3) + return V4L2_JPEG_CHROMA_SUBSAMPLING_411; + return exynos3250_decoded_subsampling[ctx->subsampling]; + case SJPEG_EXYNOS4: + WARN_ON(ctx->subsampling > 3); + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_420; + return exynos4x12_decoded_subsampling[ctx->subsampling]; + case SJPEG_EXYNOS5433: + return ctx->subsampling; /* parsed from header */ + default: + WARN_ON(ctx->subsampling > 3); + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + } +} + +static inline void s5p_jpeg_set_qtbl(void __iomem *regs, + const unsigned char *qtbl, + unsigned long tab, int len) +{ + int i; + + for (i = 0; i < len; i++) + writel((unsigned int)qtbl[i], regs + tab + (i * 0x04)); +} + +static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 0 with data for luma */ + s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality], + S5P_JPG_QTBL_CONTENT(0), + ARRAY_SIZE(qtbl_luminance[quality])); +} + +static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 1 with data for chroma */ + s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality], + S5P_JPG_QTBL_CONTENT(1), + ARRAY_SIZE(qtbl_chrominance[quality])); +} + +static inline void s5p_jpeg_set_htbl(void __iomem *regs, + const unsigned char *htbl, + unsigned long tab, int len) +{ + int i; + + for (i = 0; i < len; i++) + writel((unsigned int)htbl[i], regs + tab + (i * 0x04)); +} + +static inline void s5p_jpeg_set_hdctbl(void __iomem *regs) +{ + /* this driver fills table 0 for this component */ + s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), + ARRAY_SIZE(hdctbl0)); +} + +static inline void s5p_jpeg_set_hdctblg(void __iomem *regs) +{ + /* this driver fills table 0 for this component */ + s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), + ARRAY_SIZE(hdctblg0)); +} + +static inline void s5p_jpeg_set_hactbl(void __iomem *regs) +{ + /* this driver fills table 0 for this component */ + s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), + ARRAY_SIZE(hactbl0)); +} + +static inline void s5p_jpeg_set_hactblg(void __iomem *regs) +{ + /* this driver fills table 0 for this component */ + s5p_jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), + ARRAY_SIZE(hactblg0)); +} + +static inline void exynos4_jpeg_set_tbl(void __iomem *regs, + const unsigned char *tbl, + unsigned long tab, int len) +{ + int i; + unsigned int dword; + + for (i = 0; i < len; i += 4) { + dword = tbl[i] | + (tbl[i + 1] << 8) | + (tbl[i + 2] << 16) | + (tbl[i + 3] << 24); + writel(dword, regs + tab + i); + } +} + +static inline void exynos4_jpeg_set_qtbl_lum(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 0 with data for luma */ + exynos4_jpeg_set_tbl(regs, qtbl_luminance[quality], + EXYNOS4_QTBL_CONTENT(0), + ARRAY_SIZE(qtbl_luminance[quality])); +} + +static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality) +{ + /* this driver fills quantisation table 1 with data for chroma */ + exynos4_jpeg_set_tbl(regs, qtbl_chrominance[quality], + EXYNOS4_QTBL_CONTENT(1), + ARRAY_SIZE(qtbl_chrominance[quality])); +} + +static void exynos4_jpeg_set_huff_tbl(void __iomem *base) +{ + exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL, + ARRAY_SIZE(hdctbl0)); + exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCCL, + ARRAY_SIZE(hdctbl0)); + exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCLV, + ARRAY_SIZE(hdctblg0)); + exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCCV, + ARRAY_SIZE(hdctblg0)); + exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACLL, + ARRAY_SIZE(hactbl0)); + exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACCL, + ARRAY_SIZE(hactbl0)); + exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACLV, + ARRAY_SIZE(hactblg0)); + exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACCV, + ARRAY_SIZE(hactblg0)); +} + +static inline int __exynos4_huff_tbl(int class, int id, bool lenval) +{ + /* + * class: 0 - DC, 1 - AC + * id: 0 - Y, 1 - Cb/Cr + */ + if (class) { + if (id) + return lenval ? EXYNOS4_HUFF_TBL_HACCL : + EXYNOS4_HUFF_TBL_HACCV; + return lenval ? EXYNOS4_HUFF_TBL_HACLL : EXYNOS4_HUFF_TBL_HACLV; + + } + /* class == 0 */ + if (id) + return lenval ? EXYNOS4_HUFF_TBL_HDCCL : EXYNOS4_HUFF_TBL_HDCCV; + + return lenval ? EXYNOS4_HUFF_TBL_HDCLL : EXYNOS4_HUFF_TBL_HDCLV; +} + +static inline int exynos4_huff_tbl_len(int class, int id) +{ + return __exynos4_huff_tbl(class, id, true); +} + +static inline int exynos4_huff_tbl_val(int class, int id) +{ + return __exynos4_huff_tbl(class, id, false); +} + +static int get_byte(struct s5p_jpeg_buffer *buf); +static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word); +static void skip(struct s5p_jpeg_buffer *buf, long len); + +static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, x, components; + + jpeg_buffer.size = 2; /* Ls */ + jpeg_buffer.data = + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2; + jpeg_buffer.curr = 0; + + word = 0; + + if (get_word_be(&jpeg_buffer, &word)) + return; + jpeg_buffer.size = (long)word - 2; + jpeg_buffer.data += 2; + jpeg_buffer.curr = 0; + + components = get_byte(&jpeg_buffer); + if (components == -1) + return; + while (components--) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + x = get_byte(&jpeg_buffer); + if (x == -1) + return; + exynos4_jpeg_select_dec_h_tbl(jpeg->regs, c, + (((x >> 4) & 0x1) << 1) | (x & 0x1)); + } + +} + +static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, i, n, j; + + for (j = 0; j < ctx->out_q.dht.n; ++j) { + jpeg_buffer.size = ctx->out_q.dht.len[j]; + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + + ctx->out_q.dht.marker[j]; + jpeg_buffer.curr = 0; + + word = 0; + while (jpeg_buffer.curr < jpeg_buffer.size) { + char id, class; + + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + id = c & 0xf; + class = (c >> 4) & 0xf; + n = 0; + for (i = 0; i < 16; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + exynos4_huff_tbl_len(class, id) + + (i / 4) * 4); + word = 0; + } + n += c; + } + word = 0; + for (i = 0; i < n; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + exynos4_huff_tbl_val(class, id) + + (i / 4) * 4); + word = 0; + } + } + if (i % 4) { + writel(word, jpeg->regs + + exynos4_huff_tbl_val(class, id) + (i / 4) * 4); + } + word = 0; + } + } +} + +static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + int c, x, components; + + jpeg_buffer.size = ctx->out_q.sof_len; + jpeg_buffer.data = + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sof; + jpeg_buffer.curr = 0; + + skip(&jpeg_buffer, 5); /* P, Y, X */ + components = get_byte(&jpeg_buffer); + if (components == -1) + return; + + exynos4_jpeg_set_dec_components(jpeg->regs, components); + + while (components--) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + skip(&jpeg_buffer, 1); + x = get_byte(&jpeg_buffer); + if (x == -1) + return; + exynos4_jpeg_select_dec_q_tbl(jpeg->regs, c, x); + } +} + +static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, i, j; + + for (j = 0; j < ctx->out_q.dqt.n; ++j) { + jpeg_buffer.size = ctx->out_q.dqt.len[j]; + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + + ctx->out_q.dqt.marker[j]; + jpeg_buffer.curr = 0; + + word = 0; + while (jpeg_buffer.size - jpeg_buffer.curr >= 65) { + char id; + + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + id = c & 0xf; + /* nonzero means extended mode - not supported */ + if ((c >> 4) & 0xf) + return; + for (i = 0; i < 64; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + EXYNOS4_QTBL_CONTENT(id) + (i / 4) * 4); + word = 0; + } + } + word = 0; + } + } +} + +/* + * ============================================================================ + * Device file operations + * ============================================================================ + */ + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, + __u32 pixelformat, unsigned int fmt_type); +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx); + +static int s5p_jpeg_open(struct file *file) +{ + struct s5p_jpeg *jpeg = video_drvdata(file); + struct video_device *vfd = video_devdata(file); + struct s5p_jpeg_ctx *ctx; + struct s5p_jpeg_fmt *out_fmt, *cap_fmt; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (mutex_lock_interruptible(&jpeg->lock)) { + ret = -ERESTARTSYS; + goto free; + } + + v4l2_fh_init(&ctx->fh, vfd); + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ctx->jpeg = jpeg; + if (vfd == jpeg->vfd_encoder) { + ctx->mode = S5P_JPEG_ENCODE; + out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_RGB565, + FMT_TYPE_OUTPUT); + cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, + FMT_TYPE_CAPTURE); + } else { + ctx->mode = S5P_JPEG_DECODE; + out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, + FMT_TYPE_OUTPUT); + cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV, + FMT_TYPE_CAPTURE); + ctx->scale_factor = EXYNOS3250_DEC_SCALE_FACTOR_8_8; + } + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto error; + } + + ctx->out_q.fmt = out_fmt; + ctx->cap_q.fmt = cap_fmt; + + ret = s5p_jpeg_controls_create(ctx); + if (ret < 0) + goto error; + + mutex_unlock(&jpeg->lock); + return 0; + +error: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + mutex_unlock(&jpeg->lock); +free: + kfree(ctx); + return ret; +} + +static int s5p_jpeg_release(struct file *file) +{ + struct s5p_jpeg *jpeg = video_drvdata(file); + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); + + mutex_lock(&jpeg->lock); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&jpeg->lock); + + return 0; +} + +static const struct v4l2_file_operations s5p_jpeg_fops = { + .owner = THIS_MODULE, + .open = s5p_jpeg_open, + .release = s5p_jpeg_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +/* + * ============================================================================ + * video ioctl operations + * ============================================================================ + */ + +static int get_byte(struct s5p_jpeg_buffer *buf) +{ + if (buf->curr >= buf->size) + return -1; + + return ((unsigned char *)buf->data)[buf->curr++]; +} + +static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word) +{ + unsigned int temp; + int byte; + + byte = get_byte(buf); + if (byte == -1) + return -1; + temp = byte << 8; + byte = get_byte(buf); + if (byte == -1) + return -1; + *word = (unsigned int)byte | temp; + return 0; +} + +static void skip(struct s5p_jpeg_buffer *buf, long len) +{ + if (len <= 0) + return; + + while (len--) + get_byte(buf); +} + +static bool s5p_jpeg_subsampling_decode(struct s5p_jpeg_ctx *ctx, + unsigned int subsampling) +{ + unsigned int version; + + switch (subsampling) { + case 0x11: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444; + break; + case 0x21: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422; + break; + case 0x22: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420; + break; + case 0x33: + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + break; + case 0x41: + /* + * 4:1:1 subsampling only supported by 3250, 5420, and 5433 + * variants + */ + version = ctx->jpeg->variant->version; + if (version != SJPEG_EXYNOS3250 && + version != SJPEG_EXYNOS5420 && + version != SJPEG_EXYNOS5433) + return false; + + ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_411; + break; + default: + return false; + } + + return true; +} + +static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, + unsigned long buffer, unsigned long size, + struct s5p_jpeg_ctx *ctx) +{ + int c, components = 0, notfound, n_dht = 0, n_dqt = 0; + unsigned int height = 0, width = 0, word, subsampling = 0; + unsigned int sos = 0, sof = 0, sof_len = 0; + unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER]; + unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; + long length; + struct s5p_jpeg_buffer jpeg_buffer; + + jpeg_buffer.size = size; + jpeg_buffer.data = buffer; + jpeg_buffer.curr = 0; + + notfound = 1; + while (notfound || !sos) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return false; + if (c != 0xff) + continue; + do + c = get_byte(&jpeg_buffer); + while (c == 0xff); + if (c == -1) + return false; + if (c == 0) + continue; + length = 0; + switch (c) { + /* JPEG_MARKER_SOF0: baseline JPEG */ + case JPEG_MARKER_SOF0: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + if (!length) + return false; + sof = jpeg_buffer.curr; /* after 0xffc0 */ + sof_len = length; + if (get_byte(&jpeg_buffer) == -1) + break; + if (get_word_be(&jpeg_buffer, &height)) + break; + if (get_word_be(&jpeg_buffer, &width)) + break; + components = get_byte(&jpeg_buffer); + if (components == -1) + break; + + if (components == 1) { + subsampling = 0x33; + } else { + skip(&jpeg_buffer, 1); + subsampling = get_byte(&jpeg_buffer); + skip(&jpeg_buffer, 1); + } + if (components > 3) + return false; + skip(&jpeg_buffer, components * 2); + notfound = 0; + break; + + case JPEG_MARKER_DQT: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + if (!length) + return false; + if (n_dqt >= S5P_JPEG_MAX_MARKER) + return false; + dqt[n_dqt] = jpeg_buffer.curr; /* after 0xffdb */ + dqt_len[n_dqt++] = length; + skip(&jpeg_buffer, length); + break; + + case JPEG_MARKER_DHT: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + if (!length) + return false; + if (n_dht >= S5P_JPEG_MAX_MARKER) + return false; + dht[n_dht] = jpeg_buffer.curr; /* after 0xffc4 */ + dht_len[n_dht++] = length; + skip(&jpeg_buffer, length); + break; + + case JPEG_MARKER_SOS: + sos = jpeg_buffer.curr - 2; /* 0xffda */ + break; + + /* skip payload-less markers */ + case JPEG_MARKER_RST ... JPEG_MARKER_RST + 7: + case JPEG_MARKER_SOI: + case JPEG_MARKER_EOI: + case JPEG_MARKER_TEM: + break; + + /* skip uninteresting payload markers */ + default: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + skip(&jpeg_buffer, length); + break; + } + } + + if (notfound || !sos || !s5p_jpeg_subsampling_decode(ctx, subsampling)) + return false; + + result->w = width; + result->h = height; + result->sos = sos; + result->dht.n = n_dht; + while (n_dht--) { + result->dht.marker[n_dht] = dht[n_dht]; + result->dht.len[n_dht] = dht_len[n_dht]; + } + result->dqt.n = n_dqt; + while (n_dqt--) { + result->dqt.marker[n_dqt] = dqt[n_dqt]; + result->dqt.len[n_dqt] = dqt_len[n_dqt]; + } + result->sof = sof; + result->sof_len = sof_len; + + return true; +} + +static int s5p_jpeg_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + + if (ctx->mode == S5P_JPEG_ENCODE) { + strscpy(cap->driver, S5P_JPEG_M2M_NAME, + sizeof(cap->driver)); + strscpy(cap->card, S5P_JPEG_M2M_NAME " encoder", + sizeof(cap->card)); + } else { + strscpy(cap->driver, S5P_JPEG_M2M_NAME, + sizeof(cap->driver)); + strscpy(cap->card, S5P_JPEG_M2M_NAME " decoder", + sizeof(cap->card)); + } + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->jpeg->dev)); + return 0; +} + +static int enum_fmt(struct s5p_jpeg_ctx *ctx, + struct s5p_jpeg_fmt *sjpeg_formats, int n, + struct v4l2_fmtdesc *f, u32 type) +{ + int i, num = 0; + unsigned int fmt_ver_flag = ctx->jpeg->variant->fmt_ver_flag; + + for (i = 0; i < n; ++i) { + if (sjpeg_formats[i].flags & type && + sjpeg_formats[i].flags & fmt_ver_flag) { + /* index-th format of type type found ? */ + if (num == f->index) + break; + /* Correct type but haven't reached our index yet, + * just increment per-type index + */ + ++num; + } + } + + /* Format not found */ + if (i >= n) + return -EINVAL; + + f->pixelformat = sjpeg_formats[i].fourcc; + + return 0; +} + +static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + + if (ctx->mode == S5P_JPEG_ENCODE) + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_CAPTURE); + + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_CAPTURE); +} + +static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + + if (ctx->mode == S5P_JPEG_ENCODE) + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_OUTPUT); + + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_OUTPUT); +} + +static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, + enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return &ctx->out_q; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return &ctx->cap_q; + + return NULL; +} + +static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct s5p_jpeg_q_data *q_data = NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); + + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed) + return -EINVAL; + q_data = get_q_data(ct, f->type); + BUG_ON(q_data == NULL); + + pix->width = q_data->w; + pix->height = q_data->h; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = q_data->fmt->fourcc; + pix->bytesperline = 0; + if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { + u32 bpl = q_data->w; + + if (q_data->fmt->colplanes == 1) + bpl = (bpl * q_data->fmt->depth) >> 3; + pix->bytesperline = bpl; + } + pix->sizeimage = q_data->size; + + return 0; +} + +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, + u32 pixelformat, unsigned int fmt_type) +{ + unsigned int k, fmt_flag; + + if (ctx->mode == S5P_JPEG_ENCODE) + fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? + SJPEG_FMT_FLAG_ENC_OUTPUT : + SJPEG_FMT_FLAG_ENC_CAPTURE; + else + fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? + SJPEG_FMT_FLAG_DEC_OUTPUT : + SJPEG_FMT_FLAG_DEC_CAPTURE; + + for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { + struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; + + if (fmt->fourcc == pixelformat && + fmt->flags & fmt_flag && + fmt->flags & ctx->jpeg->variant->fmt_ver_flag) { + return fmt; + } + } + + return NULL; +} + +static void jpeg_bound_align_image(struct s5p_jpeg_ctx *ctx, + u32 *w, unsigned int wmin, unsigned int wmax, + unsigned int walign, + u32 *h, unsigned int hmin, unsigned int hmax, + unsigned int halign) +{ + int width, height, w_step, h_step; + + width = *w; + height = *h; + + w_step = 1 << walign; + h_step = 1 << halign; + + if (ctx->jpeg->variant->hw3250_compat) { + /* + * Rightmost and bottommost pixels are cropped by the + * Exynos3250/compatible JPEG IP for RGB formats, for the + * specific width and height values respectively. This + * assignment will result in v4l_bound_align_image returning + * dimensions reduced by 1 for the aforementioned cases. + */ + if (w_step == 4 && ((width & 3) == 1)) { + wmax = width; + hmax = height; + } + } + + v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0); + + if (*w < width && (*w + w_step) < wmax) + *w += w_step; + if (*h < height && (*h + h_step) < hmax) + *h += h_step; +} + +static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, + struct s5p_jpeg_ctx *ctx, int q_type) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported + */ + if (q_type == FMT_TYPE_OUTPUT) + jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, + S5P_JPEG_MAX_WIDTH, 0, + &pix->height, S5P_JPEG_MIN_HEIGHT, + S5P_JPEG_MAX_HEIGHT, 0); + else + jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, + S5P_JPEG_MAX_WIDTH, fmt->h_align, + &pix->height, S5P_JPEG_MIN_HEIGHT, + S5P_JPEG_MAX_HEIGHT, fmt->v_align); + + if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { + if (pix->sizeimage <= 0) + pix->sizeimage = PAGE_SIZE; + pix->bytesperline = 0; + } else { + u32 bpl = pix->bytesperline; + + if (fmt->colplanes > 1 && bpl < pix->width) + bpl = pix->width; /* planar */ + + if (fmt->colplanes == 1 && /* packed */ + (bpl << 3) / fmt->depth < pix->width) + bpl = (pix->width * fmt->depth) >> 3; + + pix->bytesperline = bpl; + pix->sizeimage = (pix->width * pix->height * fmt->depth) >> 3; + } + + return 0; +} + +static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct s5p_jpeg_fmt *fmt; + int ret; + + fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, + FMT_TYPE_CAPTURE); + if (!fmt) { + v4l2_err(&ctx->jpeg->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + if (!ctx->jpeg->variant->hw_ex4_compat || ctx->mode != S5P_JPEG_DECODE) + goto exit; + + /* + * The exynos4x12 device requires resulting YUV image + * subsampling not to be lower than the input jpeg subsampling. + * If this requirement is not met then downgrade the requested + * capture format to the one with subsampling equal to the input jpeg. + */ + if ((fmt->flags & SJPEG_FMT_NON_RGB) && + (fmt->subsampling < ctx->subsampling)) { + ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling, + fmt->fourcc, + &pix->pixelformat, + ctx); + if (ret < 0) + pix->pixelformat = V4L2_PIX_FMT_GREY; + + fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, + FMT_TYPE_CAPTURE); + } + + /* + * Decompression of a JPEG file with 4:2:0 subsampling and odd + * width to the YUV 4:2:0 compliant formats produces a raw image + * with broken luma component. Adjust capture format to RGB565 + * in such a case. + */ + if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 && + (ctx->out_q.w & 1) && + (pix->pixelformat == V4L2_PIX_FMT_NV12 || + pix->pixelformat == V4L2_PIX_FMT_NV21 || + pix->pixelformat == V4L2_PIX_FMT_YUV420)) { + pix->pixelformat = V4L2_PIX_FMT_RGB565; + fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, + FMT_TYPE_CAPTURE); + } + +exit: + return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE); +} + +static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + struct s5p_jpeg_fmt *fmt; + + fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, + FMT_TYPE_OUTPUT); + if (!fmt) { + v4l2_err(&ctx->jpeg->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT); +} + +static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, + struct v4l2_format *f, + int fmt_depth) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 pix_fmt = f->fmt.pix.pixelformat; + int w = pix->width, h = pix->height, wh_align; + int padding = 0; + + if (pix_fmt == V4L2_PIX_FMT_RGB32 || + pix_fmt == V4L2_PIX_FMT_RGB565 || + pix_fmt == V4L2_PIX_FMT_NV24 || + pix_fmt == V4L2_PIX_FMT_NV42 || + pix_fmt == V4L2_PIX_FMT_NV12 || + pix_fmt == V4L2_PIX_FMT_NV21 || + pix_fmt == V4L2_PIX_FMT_YUV420) + wh_align = 4; + else + wh_align = 1; + + jpeg_bound_align_image(ctx, &w, S5P_JPEG_MIN_WIDTH, + S5P_JPEG_MAX_WIDTH, wh_align, + &h, S5P_JPEG_MIN_HEIGHT, + S5P_JPEG_MAX_HEIGHT, wh_align); + + if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) + padding = PAGE_SIZE; + + return (w * h * fmt_depth >> 3) + padding; +} + +static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r); + +static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct s5p_jpeg_q_data *q_data = NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_ctrl *ctrl_subs; + struct v4l2_rect scale_rect; + unsigned int f_type; + + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ct, f->type); + BUG_ON(q_data == NULL); + + if (vb2_is_busy(vq)) { + v4l2_err(&ct->jpeg->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + f_type = V4L2_TYPE_IS_OUTPUT(f->type) ? + FMT_TYPE_OUTPUT : FMT_TYPE_CAPTURE; + + q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type); + if (ct->mode == S5P_JPEG_ENCODE || + (ct->mode == S5P_JPEG_DECODE && + q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)) { + q_data->w = pix->width; + q_data->h = pix->height; + } + if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { + /* + * During encoding Exynos4x12 SoCs access wider memory area + * than it results from Image_x and Image_y values written to + * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu + * page fault calculate proper buffer size in such a case. + */ + if (ct->jpeg->variant->hw_ex4_compat && + f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE) + q_data->size = exynos4_jpeg_get_output_buffer_size(ct, + f, + q_data->fmt->depth); + else + q_data->size = q_data->w * q_data->h * + q_data->fmt->depth >> 3; + } else { + q_data->size = pix->sizeimage; + } + + if (f_type == FMT_TYPE_OUTPUT) { + ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING); + if (ctrl_subs) + v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling); + ct->crop_altered = false; + } + + /* + * For decoding init crop_rect with capture buffer dimmensions which + * contain aligned dimensions of the input JPEG image and do it only + * if crop rectangle hasn't been altered by the user space e.g. with + * S_SELECTION ioctl. For encoding assign output buffer dimensions. + */ + if (!ct->crop_altered && + ((ct->mode == S5P_JPEG_DECODE && f_type == FMT_TYPE_CAPTURE) || + (ct->mode == S5P_JPEG_ENCODE && f_type == FMT_TYPE_OUTPUT))) { + ct->crop_rect.width = pix->width; + ct->crop_rect.height = pix->height; + } + + /* + * Prevent downscaling to YUV420 format by more than 2 + * for Exynos3250/compatible SoC as it produces broken raw image + * in such cases. + */ + if (ct->mode == S5P_JPEG_DECODE && + f_type == FMT_TYPE_CAPTURE && + ct->jpeg->variant->hw3250_compat && + pix->pixelformat == V4L2_PIX_FMT_YUV420 && + ct->scale_factor > 2) { + scale_rect.width = ct->out_q.w / 2; + scale_rect.height = ct->out_q.h / 2; + exynos3250_jpeg_try_downscale(ct, &scale_rect); + } + + return 0; +} + +static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = s5p_jpeg_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); +} + +static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = s5p_jpeg_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); +} + +static int s5p_jpeg_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_SOURCE_CHANGE) + return v4l2_src_change_event_subscribe(fh, sub); + + return -EINVAL; +} + +static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) +{ + int w_ratio, h_ratio, scale_factor, cur_ratio, i; + + w_ratio = ctx->out_q.w / r->width; + h_ratio = ctx->out_q.h / r->height; + + scale_factor = w_ratio > h_ratio ? w_ratio : h_ratio; + scale_factor = clamp_val(scale_factor, 1, 8); + + /* Align scale ratio to the nearest power of 2 */ + for (i = 0; i <= 3; ++i) { + cur_ratio = 1 << i; + if (scale_factor <= cur_ratio) { + ctx->scale_factor = cur_ratio; + break; + } + } + + r->width = round_down(ctx->out_q.w / ctx->scale_factor, 2); + r->height = round_down(ctx->out_q.h / ctx->scale_factor, 2); + + ctx->crop_rect.width = r->width; + ctx->crop_rect.height = r->height; + ctx->crop_rect.left = 0; + ctx->crop_rect.top = 0; + + ctx->crop_altered = true; + + return 0; +} + +static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) +{ + struct v4l2_rect base_rect; + int w_step, h_step; + + switch (ctx->cap_q.fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + w_step = 1; + h_step = 2; + break; + case V4L2_PIX_FMT_YUV420: + w_step = 2; + h_step = 2; + break; + default: + w_step = 1; + h_step = 1; + break; + } + + base_rect.top = 0; + base_rect.left = 0; + base_rect.width = ctx->out_q.w; + base_rect.height = ctx->out_q.h; + + r->width = round_down(r->width, w_step); + r->height = round_down(r->height, h_step); + r->left = round_down(r->left, 2); + r->top = round_down(r->top, 2); + + if (!v4l2_rect_enclosed(r, &base_rect)) + return -EINVAL; + + ctx->crop_rect.left = r->left; + ctx->crop_rect.top = r->top; + ctx->crop_rect.width = r->width; + ctx->crop_rect.height = r->height; + + ctx->crop_altered = true; + + return 0; +} + +/* + * V4L2 controls + */ + +static int s5p_jpeg_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* For JPEG blob active == default == bounds */ + switch (s->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.width = ctx->out_q.w; + s->r.height = ctx->out_q.h; + s->r.left = 0; + s->r.top = 0; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r.width = ctx->crop_rect.width; + s->r.height = ctx->crop_rect.height; + s->r.left = ctx->crop_rect.left; + s->r.top = ctx->crop_rect.top; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * V4L2 controls + */ +static int s5p_jpeg_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_rect *rect = &s->r; + int ret = -EINVAL; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target == V4L2_SEL_TGT_COMPOSE) { + if (ctx->mode != S5P_JPEG_DECODE) + return -EINVAL; + if (ctx->jpeg->variant->hw3250_compat) + ret = exynos3250_jpeg_try_downscale(ctx, rect); + } else if (s->target == V4L2_SEL_TGT_CROP) { + if (ctx->mode != S5P_JPEG_ENCODE) + return -EINVAL; + if (ctx->jpeg->variant->hw3250_compat) + ret = exynos3250_jpeg_try_crop(ctx, rect); + } + + return ret; +} + +static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned long flags; + + switch (ctrl->id) { + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + spin_lock_irqsave(&jpeg->slock, flags); + ctrl->val = s5p_jpeg_to_user_subsampling(ctx); + spin_unlock_irqrestore(&jpeg->slock, flags); + break; + } + + return 0; +} + +static int s5p_jpeg_adjust_subs_ctrl(struct s5p_jpeg_ctx *ctx, int *ctrl_val) +{ + switch (ctx->jpeg->variant->version) { + case SJPEG_S5P: + return 0; + case SJPEG_EXYNOS3250: + case SJPEG_EXYNOS5420: + /* + * The exynos3250/compatible device can produce JPEG image only + * of 4:4:4 subsampling when given RGB32 source image. + */ + if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) + *ctrl_val = 0; + break; + case SJPEG_EXYNOS4: + /* + * The exynos4x12 device requires input raw image fourcc + * to be V4L2_PIX_FMT_GREY if gray jpeg format + * is to be set. + */ + if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY && + *ctrl_val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) + return -EINVAL; + break; + } + + /* + * The exynos4x12 and exynos3250/compatible devices require resulting + * jpeg subsampling not to be lower than the input raw image + * subsampling. + */ + if (ctx->out_q.fmt->subsampling > *ctrl_val) + *ctrl_val = ctx->out_q.fmt->subsampling; + + return 0; +} + +static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) + ret = s5p_jpeg_adjust_subs_ctrl(ctx, &ctrl->val); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + return ret; +} + +static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->compr_quality = ctrl->val; + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->restart_interval = ctrl->val; + break; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + ctx->subsampling = ctrl->val; + break; + } + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + return 0; +} + +static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = { + .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl, + .try_ctrl = s5p_jpeg_try_ctrl, + .s_ctrl = s5p_jpeg_s_ctrl, +}; + +static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) +{ + unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */ + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + if (ctx->mode == S5P_JPEG_ENCODE) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 3, 1, S5P_JPEG_COMPR_QUAL_WORST); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, + 0, 0xffff, 1, 0); + if (ctx->jpeg->variant->version == SJPEG_S5P) + mask = ~0x06; /* 422, 420 */ + } + + ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_422); + + if (ctx->ctrl_handler.error) { + ret = ctx->ctrl_handler.error; + goto error_free; + } + + if (ctx->mode == S5P_JPEG_DECODE) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (ret < 0) + goto error_free; + + return ret; + +error_free: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ret; +} + +static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { + .vidioc_querycap = s5p_jpeg_querycap, + + .vidioc_enum_fmt_vid_cap = s5p_jpeg_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = s5p_jpeg_enum_fmt_vid_out, + + .vidioc_g_fmt_vid_cap = s5p_jpeg_g_fmt, + .vidioc_g_fmt_vid_out = s5p_jpeg_g_fmt, + + .vidioc_try_fmt_vid_cap = s5p_jpeg_try_fmt_vid_cap, + .vidioc_try_fmt_vid_out = s5p_jpeg_try_fmt_vid_out, + + .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap, + .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = s5p_jpeg_g_selection, + .vidioc_s_selection = s5p_jpeg_s_selection, + + .vidioc_subscribe_event = s5p_jpeg_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * ============================================================================ + * mem2mem callbacks + * ============================================================================ + */ + +static void s5p_jpeg_device_run(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned long src_addr, dst_addr, flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + + s5p_jpeg_reset(jpeg->regs); + s5p_jpeg_poweron(jpeg->regs); + s5p_jpeg_proc_mode(jpeg->regs, ctx->mode); + if (ctx->mode == S5P_JPEG_ENCODE) { + if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565) + s5p_jpeg_input_raw_mode(jpeg->regs, + S5P_JPEG_RAW_IN_565); + else + s5p_jpeg_input_raw_mode(jpeg->regs, + S5P_JPEG_RAW_IN_422); + s5p_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + s5p_jpeg_dri(jpeg->regs, ctx->restart_interval); + s5p_jpeg_x(jpeg->regs, ctx->out_q.w); + s5p_jpeg_y(jpeg->regs, ctx->out_q.h); + s5p_jpeg_imgadr(jpeg->regs, src_addr); + s5p_jpeg_jpgadr(jpeg->regs, dst_addr); + + /* ultimately comes from sizeimage from userspace */ + s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); + + /* JPEG RGB to YCbCr conversion matrix */ + s5p_jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); + s5p_jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); + s5p_jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); + s5p_jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); + s5p_jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); + s5p_jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); + s5p_jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); + s5p_jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); + s5p_jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); + + /* + * JPEG IP allows storing 4 quantization tables + * We fill table 0 for luma and table 1 for chroma + */ + s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + /* use table 0 for Y */ + s5p_jpeg_qtbl(jpeg->regs, 1, 0); + /* use table 1 for Cb and Cr*/ + s5p_jpeg_qtbl(jpeg->regs, 2, 1); + s5p_jpeg_qtbl(jpeg->regs, 3, 1); + + /* Y, Cb, Cr use Huffman table 0 */ + s5p_jpeg_htbl_ac(jpeg->regs, 1); + s5p_jpeg_htbl_dc(jpeg->regs, 1); + s5p_jpeg_htbl_ac(jpeg->regs, 2); + s5p_jpeg_htbl_dc(jpeg->regs, 2); + s5p_jpeg_htbl_ac(jpeg->regs, 3); + s5p_jpeg_htbl_dc(jpeg->regs, 3); + } else { /* S5P_JPEG_DECODE */ + s5p_jpeg_rst_int_enable(jpeg->regs, true); + s5p_jpeg_data_num_int_enable(jpeg->regs, true); + s5p_jpeg_final_mcu_num_int_enable(jpeg->regs, true); + if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) + s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + else + s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); + s5p_jpeg_jpgadr(jpeg->regs, src_addr); + s5p_jpeg_imgadr(jpeg->regs, dst_addr); + } + + s5p_jpeg_start(jpeg->regs); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +} + +static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct s5p_jpeg_fmt *fmt; + struct vb2_v4l2_buffer *vb; + struct s5p_jpeg_addr jpeg_addr = {}; + u32 pix_size, padding_bytes = 0; + + jpeg_addr.cb = 0; + jpeg_addr.cr = 0; + + pix_size = ctx->cap_q.w * ctx->cap_q.h; + + if (ctx->mode == S5P_JPEG_ENCODE) { + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + fmt = ctx->out_q.fmt; + if (ctx->out_q.w % 2 && fmt->h_align > 0) + padding_bytes = ctx->out_q.h; + } else { + fmt = ctx->cap_q.fmt; + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + } + + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); + + if (fmt->colplanes == 2) { + jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; + } else if (fmt->colplanes == 3) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + if (fmt->fourcc == V4L2_PIX_FMT_YUV420) + jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; + else + jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; + } + + exynos4_jpeg_set_frame_buf_address(jpeg->regs, &jpeg_addr); +} + +static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb; + unsigned int jpeg_addr = 0; + + if (ctx->mode == S5P_JPEG_ENCODE) + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); + if (jpeg->variant->version == SJPEG_EXYNOS5433 && + ctx->mode == S5P_JPEG_DECODE) + jpeg_addr += ctx->out_q.sos; + exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr); +} + +static inline void exynos4_jpeg_set_img_fmt(void __iomem *base, + unsigned int img_fmt) +{ + __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS4); +} + +static inline void exynos5433_jpeg_set_img_fmt(void __iomem *base, + unsigned int img_fmt) +{ + __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS5433); +} + +static inline void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, + unsigned int out_fmt) +{ + __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS4); +} + +static inline void exynos5433_jpeg_set_enc_out_fmt(void __iomem *base, + unsigned int out_fmt) +{ + __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS5433); +} + +static void exynos4_jpeg_device_run(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned int bitstream_size; + unsigned long flags; + + spin_lock_irqsave(&jpeg->slock, flags); + + if (ctx->mode == S5P_JPEG_ENCODE) { + exynos4_jpeg_sw_reset(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs, jpeg->variant->version); + exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); + + exynos4_jpeg_set_huff_tbl(jpeg->regs); + + /* + * JPEG IP allows storing 4 quantization tables + * We fill table 0 for luma and table 1 for chroma + */ + exynos4_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + exynos4_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + + exynos4_jpeg_set_encode_tbl_select(jpeg->regs, + ctx->compr_quality); + exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, + ctx->cap_q.h); + + if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) { + exynos4_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos4_jpeg_set_img_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } else { + exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos5433_jpeg_set_img_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } + exynos4_jpeg_set_img_addr(ctx); + exynos4_jpeg_set_jpeg_addr(ctx); + exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } else { + exynos4_jpeg_sw_reset(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs, + jpeg->variant->version); + exynos4_jpeg_set_img_addr(ctx); + exynos4_jpeg_set_jpeg_addr(ctx); + + if (jpeg->variant->version == SJPEG_EXYNOS5433) { + exynos4_jpeg_parse_huff_tbl(ctx); + exynos4_jpeg_parse_decode_h_tbl(ctx); + + exynos4_jpeg_parse_q_tbl(ctx); + exynos4_jpeg_parse_decode_q_tbl(ctx); + + exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); + + exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, + ctx->cap_q.h); + exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos5433_jpeg_set_img_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 16); + } else { + exynos4_jpeg_set_img_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); + } + + exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size); + } + + exynos4_jpeg_set_sys_int_enable(jpeg->regs, 1); + exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode); + + spin_unlock_irqrestore(&jpeg->slock, flags); +} + +static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct s5p_jpeg_fmt *fmt; + struct vb2_v4l2_buffer *vb; + struct s5p_jpeg_addr jpeg_addr = {}; + u32 pix_size; + + pix_size = ctx->cap_q.w * ctx->cap_q.h; + + if (ctx->mode == S5P_JPEG_ENCODE) { + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + fmt = ctx->out_q.fmt; + } else { + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + fmt = ctx->cap_q.fmt; + } + + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); + + if (fmt->colplanes == 2) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + } else if (fmt->colplanes == 3) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + if (fmt->fourcc == V4L2_PIX_FMT_YUV420) + jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; + else + jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; + } + + exynos3250_jpeg_imgadr(jpeg->regs, &jpeg_addr); +} + +static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *vb; + unsigned int jpeg_addr = 0; + + if (ctx->mode == S5P_JPEG_ENCODE) + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); + exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr); +} + +static void exynos3250_jpeg_device_run(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned long flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + exynos3250_jpeg_reset(jpeg->regs); + exynos3250_jpeg_set_dma_num(jpeg->regs); + exynos3250_jpeg_poweron(jpeg->regs); + exynos3250_jpeg_clk_set(jpeg->regs); + exynos3250_jpeg_proc_mode(jpeg->regs, ctx->mode); + + if (ctx->mode == S5P_JPEG_ENCODE) { + exynos3250_jpeg_input_raw_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + exynos3250_jpeg_dri(jpeg->regs, ctx->restart_interval); + + /* + * JPEG IP allows storing 4 quantization tables + * We fill table 0 for luma and table 1 for chroma + */ + s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + /* use table 0 for Y */ + exynos3250_jpeg_qtbl(jpeg->regs, 1, 0); + /* use table 1 for Cb and Cr*/ + exynos3250_jpeg_qtbl(jpeg->regs, 2, 1); + exynos3250_jpeg_qtbl(jpeg->regs, 3, 1); + + /* + * Some SoCs require setting Huffman tables before each run + */ + if (jpeg->variant->htbl_reinit) { + s5p_jpeg_set_hdctbl(jpeg->regs); + s5p_jpeg_set_hdctblg(jpeg->regs); + s5p_jpeg_set_hactbl(jpeg->regs); + s5p_jpeg_set_hactblg(jpeg->regs); + } + + /* Y, Cb, Cr use Huffman table 0 */ + exynos3250_jpeg_htbl_ac(jpeg->regs, 1); + exynos3250_jpeg_htbl_dc(jpeg->regs, 1); + exynos3250_jpeg_htbl_ac(jpeg->regs, 2); + exynos3250_jpeg_htbl_dc(jpeg->regs, 2); + exynos3250_jpeg_htbl_ac(jpeg->regs, 3); + exynos3250_jpeg_htbl_dc(jpeg->regs, 3); + + exynos3250_jpeg_set_x(jpeg->regs, ctx->crop_rect.width); + exynos3250_jpeg_set_y(jpeg->regs, ctx->crop_rect.height); + exynos3250_jpeg_stride(jpeg->regs, ctx->out_q.fmt->fourcc, + ctx->out_q.w); + exynos3250_jpeg_offset(jpeg->regs, ctx->crop_rect.left, + ctx->crop_rect.top); + exynos3250_jpeg_set_img_addr(ctx); + exynos3250_jpeg_set_jpeg_addr(ctx); + exynos3250_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + + /* ultimately comes from sizeimage from userspace */ + exynos3250_jpeg_enc_stream_bound(jpeg->regs, ctx->cap_q.size); + + if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565 || + ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565X || + ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) + exynos3250_jpeg_set_y16(jpeg->regs, true); + } else { + exynos3250_jpeg_set_img_addr(ctx); + exynos3250_jpeg_set_jpeg_addr(ctx); + exynos3250_jpeg_stride(jpeg->regs, ctx->cap_q.fmt->fourcc, + ctx->cap_q.w); + exynos3250_jpeg_offset(jpeg->regs, 0, 0); + exynos3250_jpeg_dec_scaling_ratio(jpeg->regs, + ctx->scale_factor); + exynos3250_jpeg_dec_stream_size(jpeg->regs, ctx->out_q.size); + exynos3250_jpeg_output_raw_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + } + + exynos3250_jpeg_interrupts_enable(jpeg->regs); + + /* JPEG RGB to YCbCr conversion matrix */ + exynos3250_jpeg_coef(jpeg->regs, ctx->mode); + + exynos3250_jpeg_set_timer(jpeg->regs, EXYNOS3250_IRQ_TIMEOUT); + jpeg->irq_status = 0; + exynos3250_jpeg_start(jpeg->regs); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +} + +static int s5p_jpeg_job_ready(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + + if (ctx->mode == S5P_JPEG_DECODE) { + /* + * We have only one input buffer and one output buffer. If there + * is a resolution change event, no need to continue decoding. + */ + if (ctx->state == JPEGCTX_RESOLUTION_CHANGE) + return 0; + + return ctx->hdr_parsed; + } + + return 1; +} + +static const struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { + .device_run = s5p_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, +}; + +static const struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { + .device_run = exynos3250_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, +}; + +static const struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { + .device_run = exynos4_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, +}; + +/* + * ============================================================================ + * Queue operations + * ============================================================================ + */ + +static int s5p_jpeg_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); + struct s5p_jpeg_q_data *q_data = NULL; + unsigned int size, count = *nbuffers; + + q_data = get_q_data(ctx, vq->type); + BUG_ON(q_data == NULL); + + size = q_data->size; + + /* + * header is parsed during decoding and parsed information stored + * in the context so we do not allow another buffer to overwrite it + */ + if (ctx->mode == S5P_JPEG_DECODE) + count = 1; + + *nbuffers = count; + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb) +{ + struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct s5p_jpeg_q_data *q_data = NULL; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + BUG_ON(q_data == NULL); + + if (vb2_plane_size(vb, 0) < q_data->size) { + pr_err("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, q_data->size); + + return 0; +} + +static void s5p_jpeg_set_capture_queue_data(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg_q_data *q_data = &ctx->cap_q; + + q_data->w = ctx->out_q.w; + q_data->h = ctx->out_q.h; + + /* + * This call to jpeg_bound_align_image() takes care of width and + * height values alignment when user space calls the QBUF of + * OUTPUT buffer after the S_FMT of CAPTURE buffer. + * Please note that on Exynos4x12 SoCs, resigning from executing + * S_FMT on capture buffer for each JPEG image can result in a + * hardware hangup if subsampling is lower than the one of input + * JPEG. + */ + jpeg_bound_align_image(ctx, &q_data->w, S5P_JPEG_MIN_WIDTH, + S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align, + &q_data->h, S5P_JPEG_MIN_HEIGHT, + S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align); + + q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; +} + +static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (ctx->mode == S5P_JPEG_DECODE && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + static const struct v4l2_event ev_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + struct vb2_queue *dst_vq; + u32 ori_w; + u32 ori_h; + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + ori_w = ctx->out_q.w; + ori_h = ctx->out_q.h; + + ctx->hdr_parsed = s5p_jpeg_parse_hdr(&ctx->out_q, + (unsigned long)vb2_plane_vaddr(vb, 0), + min((unsigned long)ctx->out_q.size, + vb2_get_plane_payload(vb, 0)), ctx); + if (!ctx->hdr_parsed) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + return; + } + + /* + * If there is a resolution change event, only update capture + * queue when it is not streaming. Otherwise, update it in + * STREAMOFF. See s5p_jpeg_stop_streaming for detail. + */ + if (ctx->out_q.w != ori_w || ctx->out_q.h != ori_h) { + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); + if (vb2_is_streaming(dst_vq)) + ctx->state = JPEGCTX_RESOLUTION_CHANGE; + else + s5p_jpeg_set_capture_queue_data(ctx); + } + } + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); + + return pm_runtime_resume_and_get(ctx->jpeg->dev); +} + +static void s5p_jpeg_stop_streaming(struct vb2_queue *q) +{ + struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); + + /* + * STREAMOFF is an acknowledgment for resolution change event. + * Before STREAMOFF, we still have to return the old resolution and + * subsampling. Update capture queue when the stream is off. + */ + if (ctx->state == JPEGCTX_RESOLUTION_CHANGE && + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + s5p_jpeg_set_capture_queue_data(ctx); + ctx->state = JPEGCTX_RUNNING; + } + + pm_runtime_put(ctx->jpeg->dev); +} + +static const struct vb2_ops s5p_jpeg_qops = { + .queue_setup = s5p_jpeg_queue_setup, + .buf_prepare = s5p_jpeg_buf_prepare, + .buf_queue = s5p_jpeg_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = s5p_jpeg_start_streaming, + .stop_streaming = s5p_jpeg_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct s5p_jpeg_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &s5p_jpeg_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->jpeg->lock; + src_vq->dev = ctx->jpeg->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &s5p_jpeg_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->jpeg->lock; + dst_vq->dev = ctx->jpeg->dev; + + return vb2_queue_init(dst_vq); +} + +/* + * ============================================================================ + * ISR + * ============================================================================ + */ + +static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) +{ + struct s5p_jpeg *jpeg = dev_id; + struct s5p_jpeg_ctx *curr_ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned long payload_size = 0; + enum vb2_buffer_state state = VB2_BUF_STATE_DONE; + bool enc_jpeg_too_large = false; + bool timer_elapsed = false; + bool op_completed = false; + + spin_lock(&jpeg->slock); + + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + if (curr_ctx->mode == S5P_JPEG_ENCODE) + enc_jpeg_too_large = s5p_jpeg_enc_stream_stat(jpeg->regs); + timer_elapsed = s5p_jpeg_timer_stat(jpeg->regs); + op_completed = s5p_jpeg_result_stat_ok(jpeg->regs); + if (curr_ctx->mode == S5P_JPEG_DECODE) + op_completed = op_completed && + s5p_jpeg_stream_stat_ok(jpeg->regs); + + if (enc_jpeg_too_large) { + state = VB2_BUF_STATE_ERROR; + s5p_jpeg_clear_enc_stream_stat(jpeg->regs); + } else if (timer_elapsed) { + state = VB2_BUF_STATE_ERROR; + s5p_jpeg_clear_timer_stat(jpeg->regs); + } else if (!op_completed) { + state = VB2_BUF_STATE_ERROR; + } else { + payload_size = s5p_jpeg_compressed_size(jpeg->regs); + } + + dst_buf->timecode = src_buf->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->flags |= + src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + v4l2_m2m_buf_done(src_buf, state); + if (curr_ctx->mode == S5P_JPEG_ENCODE) + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); + v4l2_m2m_buf_done(dst_buf, state); + + curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs); + spin_unlock(&jpeg->slock); + + s5p_jpeg_clear_int(jpeg->regs); + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + return IRQ_HANDLED; +} + +static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) +{ + unsigned int int_status; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + struct s5p_jpeg *jpeg = priv; + struct s5p_jpeg_ctx *curr_ctx; + unsigned long payload_size = 0; + + spin_lock(&jpeg->slock); + + exynos4_jpeg_set_sys_int_enable(jpeg->regs, 0); + + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + int_status = exynos4_jpeg_get_int_status(jpeg->regs); + + if (int_status) { + switch (int_status & 0x1f) { + case 0x1: + jpeg->irq_ret = ERR_PROT; + break; + case 0x2: + jpeg->irq_ret = OK_ENC_OR_DEC; + break; + case 0x4: + jpeg->irq_ret = ERR_DEC_INVALID_FORMAT; + break; + case 0x8: + jpeg->irq_ret = ERR_MULTI_SCAN; + break; + case 0x10: + jpeg->irq_ret = ERR_FRAME; + break; + default: + jpeg->irq_ret = ERR_UNKNOWN; + break; + } + } else { + jpeg->irq_ret = ERR_UNKNOWN; + } + + if (jpeg->irq_ret == OK_ENC_OR_DEC) { + if (curr_ctx->mode == S5P_JPEG_ENCODE) { + payload_size = exynos4_jpeg_get_stream_size(jpeg->regs); + vb2_set_plane_payload(&dst_vb->vb2_buf, + 0, payload_size); + } + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + } else { + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); + } + + if (jpeg->variant->version == SJPEG_EXYNOS4) + curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); + + exynos4_jpeg_set_enc_dec_mode(jpeg->regs, S5P_JPEG_DISABLE); + + spin_unlock(&jpeg->slock); + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + return IRQ_HANDLED; +} + +static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) +{ + struct s5p_jpeg *jpeg = dev_id; + struct s5p_jpeg_ctx *curr_ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned long payload_size = 0; + enum vb2_buffer_state state = VB2_BUF_STATE_DONE; + bool interrupt_timeout = false; + bool stream_error = false; + u32 irq_status; + + spin_lock(&jpeg->slock); + + irq_status = exynos3250_jpeg_get_timer_status(jpeg->regs); + if (irq_status & EXYNOS3250_TIMER_INT_STAT) { + exynos3250_jpeg_clear_timer_status(jpeg->regs); + interrupt_timeout = true; + dev_err(jpeg->dev, "Interrupt timeout occurred.\n"); + } + + irq_status = exynos3250_jpeg_get_int_status(jpeg->regs); + exynos3250_jpeg_clear_int_status(jpeg->regs, irq_status); + + jpeg->irq_status |= irq_status; + + if (jpeg->variant->version == SJPEG_EXYNOS5420 && + irq_status & EXYNOS3250_STREAM_STAT) { + stream_error = true; + dev_err(jpeg->dev, "Syntax error or unrecoverable error occurred.\n"); + } + + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + + if (!curr_ctx) + goto exit_unlock; + + if ((irq_status & EXYNOS3250_HEADER_STAT) && + (curr_ctx->mode == S5P_JPEG_DECODE)) { + exynos3250_jpeg_rstart(jpeg->regs); + goto exit_unlock; + } + + if (jpeg->irq_status & (EXYNOS3250_JPEG_DONE | + EXYNOS3250_WDMA_DONE | + EXYNOS3250_RDMA_DONE | + EXYNOS3250_RESULT_STAT)) + payload_size = exynos3250_jpeg_compressed_size(jpeg->regs); + else if (interrupt_timeout || stream_error) + state = VB2_BUF_STATE_ERROR; + else + goto exit_unlock; + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + dst_buf->timecode = src_buf->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + + v4l2_m2m_buf_done(src_buf, state); + if (curr_ctx->mode == S5P_JPEG_ENCODE) + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); + v4l2_m2m_buf_done(dst_buf, state); + + curr_ctx->subsampling = + exynos3250_jpeg_get_subsampling_mode(jpeg->regs); + + spin_unlock(&jpeg->slock); + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + return IRQ_HANDLED; + +exit_unlock: + spin_unlock(&jpeg->slock); + return IRQ_HANDLED; +} + +static void *jpeg_get_drv_data(struct device *dev); + +/* + * ============================================================================ + * Driver basic infrastructure + * ============================================================================ + */ + +static int s5p_jpeg_probe(struct platform_device *pdev) +{ + struct s5p_jpeg *jpeg; + int i, ret; + + /* JPEG IP abstraction struct */ + jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); + if (!jpeg) + return -ENOMEM; + + jpeg->variant = jpeg_get_drv_data(&pdev->dev); + if (!jpeg->variant) + return -ENODEV; + + mutex_init(&jpeg->lock); + spin_lock_init(&jpeg->slock); + jpeg->dev = &pdev->dev; + + /* memory-mapped registers */ + jpeg->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(jpeg->regs)) + return PTR_ERR(jpeg->regs); + + /* interrupt service routine registration */ + jpeg->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq, + 0, dev_name(&pdev->dev), jpeg); + if (ret) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq); + return ret; + } + + /* clocks */ + for (i = 0; i < jpeg->variant->num_clocks; i++) { + jpeg->clocks[i] = devm_clk_get(&pdev->dev, + jpeg->variant->clk_names[i]); + if (IS_ERR(jpeg->clocks[i])) { + dev_err(&pdev->dev, "failed to get clock: %s\n", + jpeg->variant->clk_names[i]); + return PTR_ERR(jpeg->clocks[i]); + } + } + + /* v4l2 device */ + ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* mem2mem device */ + jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops); + if (IS_ERR(jpeg->m2m_dev)) { + v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(jpeg->m2m_dev); + goto device_register_rollback; + } + + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + + /* JPEG encoder /dev/videoX node */ + jpeg->vfd_encoder = video_device_alloc(); + if (!jpeg->vfd_encoder) { + v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto m2m_init_rollback; + } + snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name), + "%s-enc", S5P_JPEG_M2M_NAME); + jpeg->vfd_encoder->fops = &s5p_jpeg_fops; + jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops; + jpeg->vfd_encoder->minor = -1; + jpeg->vfd_encoder->release = video_device_release; + jpeg->vfd_encoder->lock = &jpeg->lock; + jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; + jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M; + jpeg->vfd_encoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + + ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); + video_device_release(jpeg->vfd_encoder); + goto m2m_init_rollback; + } + + video_set_drvdata(jpeg->vfd_encoder, jpeg); + v4l2_info(&jpeg->v4l2_dev, + "encoder device registered as /dev/video%d\n", + jpeg->vfd_encoder->num); + + /* JPEG decoder /dev/videoX node */ + jpeg->vfd_decoder = video_device_alloc(); + if (!jpeg->vfd_decoder) { + v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto enc_vdev_register_rollback; + } + snprintf(jpeg->vfd_decoder->name, sizeof(jpeg->vfd_decoder->name), + "%s-dec", S5P_JPEG_M2M_NAME); + jpeg->vfd_decoder->fops = &s5p_jpeg_fops; + jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops; + jpeg->vfd_decoder->minor = -1; + jpeg->vfd_decoder->release = video_device_release; + jpeg->vfd_decoder->lock = &jpeg->lock; + jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev; + jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M; + jpeg->vfd_decoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + + ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); + video_device_release(jpeg->vfd_decoder); + goto enc_vdev_register_rollback; + } + + video_set_drvdata(jpeg->vfd_decoder, jpeg); + v4l2_info(&jpeg->v4l2_dev, + "decoder device registered as /dev/video%d\n", + jpeg->vfd_decoder->num); + + /* final statements & power management */ + platform_set_drvdata(pdev, jpeg); + + pm_runtime_enable(&pdev->dev); + + v4l2_info(&jpeg->v4l2_dev, "Samsung S5P JPEG codec\n"); + + return 0; + +enc_vdev_register_rollback: + video_unregister_device(jpeg->vfd_encoder); + +m2m_init_rollback: + v4l2_m2m_release(jpeg->m2m_dev); + +device_register_rollback: + v4l2_device_unregister(&jpeg->v4l2_dev); + + return ret; +} + +static int s5p_jpeg_remove(struct platform_device *pdev) +{ + struct s5p_jpeg *jpeg = platform_get_drvdata(pdev); + int i; + + pm_runtime_disable(jpeg->dev); + + video_unregister_device(jpeg->vfd_decoder); + video_unregister_device(jpeg->vfd_encoder); + vb2_dma_contig_clear_max_seg_size(&pdev->dev); + v4l2_m2m_release(jpeg->m2m_dev); + v4l2_device_unregister(&jpeg->v4l2_dev); + + if (!pm_runtime_status_suspended(&pdev->dev)) { + for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(jpeg->clocks[i]); + } + + return 0; +} + +#ifdef CONFIG_PM +static int s5p_jpeg_runtime_suspend(struct device *dev) +{ + struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + int i; + + for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(jpeg->clocks[i]); + + return 0; +} + +static int s5p_jpeg_runtime_resume(struct device *dev) +{ + struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + unsigned long flags; + int i, ret; + + for (i = 0; i < jpeg->variant->num_clocks; i++) { + ret = clk_prepare_enable(jpeg->clocks[i]); + if (ret) { + while (--i >= 0) + clk_disable_unprepare(jpeg->clocks[i]); + return ret; + } + } + + spin_lock_irqsave(&jpeg->slock, flags); + + /* + * JPEG IP allows storing two Huffman tables for each component. + * We fill table 0 for each component and do this here only + * for S5PC210 and Exynos3250 SoCs. Exynos4x12 and Exynos542x SoC + * require programming their Huffman tables each time the encoding + * process is initialized, and thus it is accomplished in the + * device_run callback of m2m_ops. + */ + if (!jpeg->variant->htbl_reinit) { + s5p_jpeg_set_hdctbl(jpeg->regs); + s5p_jpeg_set_hdctblg(jpeg->regs); + s5p_jpeg_set_hactbl(jpeg->regs); + s5p_jpeg_set_hactblg(jpeg->regs); + } + + spin_unlock_irqrestore(&jpeg->slock, flags); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops s5p_jpeg_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, + NULL) +}; + +static struct s5p_jpeg_variant s5p_jpeg_drvdata = { + .version = SJPEG_S5P, + .jpeg_irq = s5p_jpeg_irq, + .m2m_ops = &s5p_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_S5P, + .clk_names = {"jpeg"}, + .num_clocks = 1, +}; + +static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { + .version = SJPEG_EXYNOS3250, + .jpeg_irq = exynos3250_jpeg_irq, + .m2m_ops = &exynos3250_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, + .hw3250_compat = 1, + .clk_names = {"jpeg", "sclk"}, + .num_clocks = 2, +}; + +static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { + .version = SJPEG_EXYNOS4, + .jpeg_irq = exynos4_jpeg_irq, + .m2m_ops = &exynos4_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, + .htbl_reinit = 1, + .clk_names = {"jpeg"}, + .num_clocks = 1, + .hw_ex4_compat = 1, +}; + +static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = { + .version = SJPEG_EXYNOS5420, + .jpeg_irq = exynos3250_jpeg_irq, /* intentionally 3250 */ + .m2m_ops = &exynos3250_jpeg_m2m_ops, /* intentionally 3250 */ + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, /* intentionally 3250 */ + .hw3250_compat = 1, + .htbl_reinit = 1, + .clk_names = {"jpeg"}, + .num_clocks = 1, +}; + +static struct s5p_jpeg_variant exynos5433_jpeg_drvdata = { + .version = SJPEG_EXYNOS5433, + .jpeg_irq = exynos4_jpeg_irq, + .m2m_ops = &exynos4_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, + .htbl_reinit = 1, + .clk_names = {"pclk", "aclk", "aclk_xiu", "sclk"}, + .num_clocks = 4, + .hw_ex4_compat = 1, +}; + +static const struct of_device_id samsung_jpeg_match[] = { + { + .compatible = "samsung,s5pv210-jpeg", + .data = &s5p_jpeg_drvdata, + }, { + .compatible = "samsung,exynos3250-jpeg", + .data = &exynos3250_jpeg_drvdata, + }, { + .compatible = "samsung,exynos4210-jpeg", + .data = &exynos4_jpeg_drvdata, + }, { + .compatible = "samsung,exynos4212-jpeg", + .data = &exynos4_jpeg_drvdata, + }, { + .compatible = "samsung,exynos5420-jpeg", + .data = &exynos5420_jpeg_drvdata, + }, { + .compatible = "samsung,exynos5433-jpeg", + .data = &exynos5433_jpeg_drvdata, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, samsung_jpeg_match); + +static void *jpeg_get_drv_data(struct device *dev) +{ + struct s5p_jpeg_variant *driver_data = NULL; + const struct of_device_id *match; + + if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) + return &s5p_jpeg_drvdata; + + match = of_match_node(samsung_jpeg_match, dev->of_node); + + if (match) + driver_data = (struct s5p_jpeg_variant *)match->data; + + return driver_data; +} + +static struct platform_driver s5p_jpeg_driver = { + .probe = s5p_jpeg_probe, + .remove = s5p_jpeg_remove, + .driver = { + .of_match_table = of_match_ptr(samsung_jpeg_match), + .name = S5P_JPEG_M2M_NAME, + .pm = &s5p_jpeg_pm_ops, + }, +}; + +module_platform_driver(s5p_jpeg_driver); + +MODULE_AUTHOR("Andrzej Pietrasiewicz "); +MODULE_AUTHOR("Jacek Anaszewski "); +MODULE_DESCRIPTION("Samsung JPEG codec driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h new file mode 100644 index 000000000000..5570c79f122f --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + */ + +#ifndef JPEG_CORE_H_ +#define JPEG_CORE_H_ + +#include +#include +#include +#include + +#define S5P_JPEG_M2M_NAME "s5p-jpeg" + +#define JPEG_MAX_CLOCKS 4 + +/* JPEG compression quality setting */ +#define S5P_JPEG_COMPR_QUAL_BEST 0 +#define S5P_JPEG_COMPR_QUAL_WORST 3 + +/* JPEG RGB to YCbCr conversion matrix coefficients */ +#define S5P_JPEG_COEF11 0x4d +#define S5P_JPEG_COEF12 0x97 +#define S5P_JPEG_COEF13 0x1e +#define S5P_JPEG_COEF21 0x2c +#define S5P_JPEG_COEF22 0x57 +#define S5P_JPEG_COEF23 0x83 +#define S5P_JPEG_COEF31 0x83 +#define S5P_JPEG_COEF32 0x6e +#define S5P_JPEG_COEF33 0x13 + +#define EXYNOS3250_IRQ_TIMEOUT 0x10000000 + +/* a selection of JPEG markers */ +#define JPEG_MARKER_TEM 0x01 +#define JPEG_MARKER_SOF0 0xc0 +#define JPEG_MARKER_DHT 0xc4 +#define JPEG_MARKER_RST 0xd0 +#define JPEG_MARKER_SOI 0xd8 +#define JPEG_MARKER_EOI 0xd9 +#define JPEG_MARKER_SOS 0xda +#define JPEG_MARKER_DQT 0xdb +#define JPEG_MARKER_DHP 0xde + +/* Flags that indicate a format can be used for capture/output */ +#define SJPEG_FMT_FLAG_ENC_CAPTURE (1 << 0) +#define SJPEG_FMT_FLAG_ENC_OUTPUT (1 << 1) +#define SJPEG_FMT_FLAG_DEC_CAPTURE (1 << 2) +#define SJPEG_FMT_FLAG_DEC_OUTPUT (1 << 3) +#define SJPEG_FMT_FLAG_S5P (1 << 4) +#define SJPEG_FMT_FLAG_EXYNOS3250 (1 << 5) +#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 6) +#define SJPEG_FMT_RGB (1 << 7) +#define SJPEG_FMT_NON_RGB (1 << 8) + +#define S5P_JPEG_ENCODE 0 +#define S5P_JPEG_DECODE 1 +#define S5P_JPEG_DISABLE -1 + +#define FMT_TYPE_OUTPUT 0 +#define FMT_TYPE_CAPTURE 1 + +#define SJPEG_SUBSAMPLING_444 0x11 +#define SJPEG_SUBSAMPLING_422 0x21 +#define SJPEG_SUBSAMPLING_420 0x22 + +#define S5P_JPEG_MAX_MARKER 4 + +/* Version numbers */ +enum sjpeg_version { + SJPEG_S5P, + SJPEG_EXYNOS3250, + SJPEG_EXYNOS4, + SJPEG_EXYNOS5420, + SJPEG_EXYNOS5433, +}; + +enum exynos4_jpeg_result { + OK_ENC_OR_DEC, + ERR_PROT, + ERR_DEC_INVALID_FORMAT, + ERR_MULTI_SCAN, + ERR_FRAME, + ERR_UNKNOWN, +}; + +enum exynos4_jpeg_img_quality_level { + QUALITY_LEVEL_1 = 0, /* high */ + QUALITY_LEVEL_2, + QUALITY_LEVEL_3, + QUALITY_LEVEL_4, /* low */ +}; + +enum s5p_jpeg_ctx_state { + JPEGCTX_RUNNING = 0, + JPEGCTX_RESOLUTION_CHANGE, +}; + +/** + * struct s5p_jpeg - JPEG IP abstraction + * @lock: the mutex protecting this structure + * @slock: spinlock protecting the device contexts + * @v4l2_dev: v4l2 device for mem2mem mode + * @vfd_encoder: video device node for encoder mem2mem mode + * @vfd_decoder: video device node for decoder mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @regs: JPEG IP registers mapping + * @irq: JPEG IP irq + * @irq_ret: JPEG IP irq result value + * @clocks: JPEG IP clock(s) + * @dev: JPEG IP struct device + * @variant: driver variant to be used + * @irq_status: interrupt flags set during single encode/decode + * operation + */ +struct s5p_jpeg { + struct mutex lock; + spinlock_t slock; + + struct v4l2_device v4l2_dev; + struct video_device *vfd_encoder; + struct video_device *vfd_decoder; + struct v4l2_m2m_dev *m2m_dev; + + void __iomem *regs; + unsigned int irq; + enum exynos4_jpeg_result irq_ret; + struct clk *clocks[JPEG_MAX_CLOCKS]; + struct device *dev; + struct s5p_jpeg_variant *variant; + u32 irq_status; +}; + +struct s5p_jpeg_variant { + unsigned int version; + unsigned int fmt_ver_flag; + unsigned int hw3250_compat:1; + unsigned int htbl_reinit:1; + unsigned int hw_ex4_compat:1; + const struct v4l2_m2m_ops *m2m_ops; + irqreturn_t (*jpeg_irq)(int irq, void *priv); + const char *clk_names[JPEG_MAX_CLOCKS]; + int num_clocks; +}; + +/** + * struct s5p_jpeg_fmt - driver's internal color format data + * @fourcc: the fourcc code, 0 if not applicable + * @depth: number of bits per pixel + * @colplanes: number of color planes (1 for packed formats) + * @memplanes: number of memory planes (1 for packed formats) + * @h_align: horizontal alignment order (align to 2^h_align) + * @v_align: vertical alignment order (align to 2^v_align) + * @subsampling:subsampling of a raw format or a JPEG + * @flags: flags describing format applicability + */ +struct s5p_jpeg_fmt { + u32 fourcc; + int depth; + int colplanes; + int memplanes; + int h_align; + int v_align; + int subsampling; + u32 flags; +}; + +/** + * struct s5p_jpeg_marker - collection of markers from jpeg header + * @marker: markers' positions relative to the buffer beginning + * @len: markers' payload lengths (without length field) + * @n: number of markers in collection + */ +struct s5p_jpeg_marker { + u32 marker[S5P_JPEG_MAX_MARKER]; + u32 len[S5P_JPEG_MAX_MARKER]; + u32 n; +}; + +/** + * struct s5p_jpeg_q_data - parameters of one queue + * @fmt: driver-specific format of this queue + * @w: image width + * @h: image height + * @sos: JPEG_MARKER_SOS's position relative to the buffer beginning + * @dht: JPEG_MARKER_DHT' positions relative to the buffer beginning + * @dqt: JPEG_MARKER_DQT' positions relative to the buffer beginning + * @sof: JPEG_MARKER_SOF0's position relative to the buffer beginning + * @sof_len: JPEG_MARKER_SOF0's payload length (without length field itself) + * @size: image buffer size in bytes + */ +struct s5p_jpeg_q_data { + struct s5p_jpeg_fmt *fmt; + u32 w; + u32 h; + u32 sos; + struct s5p_jpeg_marker dht; + struct s5p_jpeg_marker dqt; + u32 sof; + u32 sof_len; + u32 size; +}; + +/** + * struct s5p_jpeg_ctx - the device context data + * @jpeg: JPEG IP device for this context + * @mode: compression (encode) operation or decompression (decode) + * @compr_quality: destination image quality in compression (encode) mode + * @restart_interval: JPEG restart interval for JPEG encoding + * @subsampling: subsampling of a raw format or a JPEG + * @out_q: source (output) queue information + * @cap_q: destination (capture) queue queue information + * @scale_factor: scale factor for JPEG decoding + * @crop_rect: a rectangle representing crop area of the output buffer + * @fh: V4L2 file handle + * @hdr_parsed: set if header has been parsed during decompression + * @crop_altered: set if crop rectangle has been altered by the user space + * @ctrl_handler: controls handler + * @state: state of the context + */ +struct s5p_jpeg_ctx { + struct s5p_jpeg *jpeg; + unsigned int mode; + unsigned short compr_quality; + unsigned short restart_interval; + unsigned short subsampling; + struct s5p_jpeg_q_data out_q; + struct s5p_jpeg_q_data cap_q; + unsigned int scale_factor; + struct v4l2_rect crop_rect; + struct v4l2_fh fh; + bool hdr_parsed; + bool crop_altered; + struct v4l2_ctrl_handler ctrl_handler; + enum s5p_jpeg_ctx_state state; +}; + +/** + * struct s5p_jpeg_buffer - description of memory containing input JPEG data + * @size: buffer size + * @curr: current position in the buffer + * @data: pointer to the data + */ +struct s5p_jpeg_buffer { + unsigned long size; + unsigned long curr; + unsigned long data; +}; + +/** + * struct s5p_jpeg_addr - JPEG converter physical address set for DMA + * @y: luminance plane physical address + * @cb: Cb plane physical address + * @cr: Cr plane physical address + */ +struct s5p_jpeg_addr { + u32 y; + u32 cb; + u32 cr; +}; + +#endif /* JPEG_CORE_H */ diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c new file mode 100644 index 000000000000..637a5104d948 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* linux/drivers/media/platform/exynos3250-jpeg/jpeg-hw.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jacek Anaszewski + */ + +#include +#include +#include + +#include "jpeg-core.h" +#include "jpeg-regs.h" +#include "jpeg-hw-exynos3250.h" + +void exynos3250_jpeg_reset(void __iomem *regs) +{ + u32 reg = 1; + int count = 1000; + + writel(1, regs + EXYNOS3250_SW_RESET); + /* no other way but polling for when JPEG IP becomes operational */ + while (reg != 0 && --count > 0) { + udelay(1); + cpu_relax(); + reg = readl(regs + EXYNOS3250_SW_RESET); + } + + reg = 0; + count = 1000; + + while (reg != 1 && --count > 0) { + writel(1, regs + EXYNOS3250_JPGDRI); + udelay(1); + cpu_relax(); + reg = readl(regs + EXYNOS3250_JPGDRI); + } + + writel(0, regs + EXYNOS3250_JPGDRI); +} + +void exynos3250_jpeg_poweron(void __iomem *regs) +{ + writel(EXYNOS3250_POWER_ON, regs + EXYNOS3250_JPGCLKCON); +} + +void exynos3250_jpeg_set_dma_num(void __iomem *regs) +{ + writel(((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) & + EXYNOS3250_WDMA_ISSUE_NUM_MASK) | + ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) & + EXYNOS3250_RDMA_ISSUE_NUM_MASK) | + ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) & + EXYNOS3250_ISSUE_GATHER_NUM_MASK), + regs + EXYNOS3250_DMA_ISSUE_NUM); +} + +void exynos3250_jpeg_clk_set(void __iomem *base) +{ + u32 reg; + + reg = readl(base + EXYNOS3250_JPGCMOD) & ~EXYNOS3250_HALF_EN_MASK; + + writel(reg | EXYNOS3250_HALF_EN, base + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGCMOD) & + EXYNOS3250_MODE_Y16_MASK; + + switch (fmt) { + case V4L2_PIX_FMT_RGB32: + reg |= EXYNOS3250_MODE_SEL_ARGB8888; + break; + case V4L2_PIX_FMT_BGR32: + reg |= EXYNOS3250_MODE_SEL_ARGB8888 | EXYNOS3250_SRC_SWAP_RGB; + break; + case V4L2_PIX_FMT_RGB565: + reg |= EXYNOS3250_MODE_SEL_RGB565; + break; + case V4L2_PIX_FMT_RGB565X: + reg |= EXYNOS3250_MODE_SEL_RGB565 | EXYNOS3250_SRC_SWAP_RGB; + break; + case V4L2_PIX_FMT_YUYV: + reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR; + break; + case V4L2_PIX_FMT_YVYU: + reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR | + EXYNOS3250_SRC_SWAP_UV; + break; + case V4L2_PIX_FMT_UYVY: + reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM; + break; + case V4L2_PIX_FMT_VYUY: + reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM | + EXYNOS3250_SRC_SWAP_UV; + break; + case V4L2_PIX_FMT_NV12: + reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV12; + break; + case V4L2_PIX_FMT_NV21: + reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV21; + break; + case V4L2_PIX_FMT_YUV420: + reg |= EXYNOS3250_MODE_SEL_420_3P; + break; + default: + break; + + } + + writel(reg, regs + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGCMOD); + if (y16) + reg |= EXYNOS3250_MODE_Y16; + else + reg &= ~EXYNOS3250_MODE_Y16_MASK; + writel(reg, regs + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode) +{ + u32 reg, m; + + if (mode == S5P_JPEG_ENCODE) + m = EXYNOS3250_PROC_MODE_COMPR; + else + m = EXYNOS3250_PROC_MODE_DECOMPR; + reg = readl(regs + EXYNOS3250_JPGMOD); + reg &= ~EXYNOS3250_PROC_MODE_MASK; + reg |= m; + writel(reg, regs + EXYNOS3250_JPGMOD); +} + +void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +{ + u32 reg, m = 0; + + switch (mode) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_444: + m = EXYNOS3250_SUBSAMPLING_MODE_444; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + m = EXYNOS3250_SUBSAMPLING_MODE_422; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + m = EXYNOS3250_SUBSAMPLING_MODE_420; + break; + } + + reg = readl(regs + EXYNOS3250_JPGMOD); + reg &= ~EXYNOS3250_SUBSAMPLING_MODE_MASK; + reg |= m; + writel(reg, regs + EXYNOS3250_JPGMOD); +} + +unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGMOD) & + EXYNOS3250_SUBSAMPLING_MODE_MASK; +} + +void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri) +{ + u32 reg; + + reg = dri & EXYNOS3250_JPGDRI_MASK; + writel(reg, regs + EXYNOS3250_JPGDRI); +} + +void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_QT_NUM_MASK(t); + reg |= (n << EXYNOS3250_QT_NUM_SHIFT(t)) & + EXYNOS3250_QT_NUM_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_HT_NUM_AC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) & + EXYNOS3250_HT_NUM_AC_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_HT_NUM_DC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) & + EXYNOS3250_HT_NUM_DC_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y) +{ + u32 reg; + + reg = y & EXYNOS3250_JPGY_MASK; + writel(reg, regs + EXYNOS3250_JPGY); +} + +void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x) +{ + u32 reg; + + reg = x & EXYNOS3250_JPGX_MASK; + writel(reg, regs + EXYNOS3250_JPGX); +} + +#if 0 /* Currently unused */ +unsigned int exynos3250_jpeg_get_y(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGY); +} + +unsigned int exynos3250_jpeg_get_x(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGX); +} +#endif + +void exynos3250_jpeg_interrupts_enable(void __iomem *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGINTSE); + reg |= (EXYNOS3250_JPEG_DONE_EN | + EXYNOS3250_WDMA_DONE_EN | + EXYNOS3250_RDMA_DONE_EN | + EXYNOS3250_ENC_STREAM_INT_EN | + EXYNOS3250_CORE_DONE_EN | + EXYNOS3250_ERR_INT_EN | + EXYNOS3250_HEAD_INT_EN); + writel(reg, regs + EXYNOS3250_JPGINTSE); +} + +void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size) +{ + u32 reg; + + reg = size & EXYNOS3250_ENC_STREAM_BOUND_MASK; + writel(reg, regs + EXYNOS3250_ENC_STREAM_BOUND); +} + +void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt) +{ + u32 reg; + + switch (fmt) { + case V4L2_PIX_FMT_RGB32: + reg = EXYNOS3250_OUT_FMT_ARGB8888; + break; + case V4L2_PIX_FMT_BGR32: + reg = EXYNOS3250_OUT_FMT_ARGB8888 | EXYNOS3250_OUT_SWAP_RGB; + break; + case V4L2_PIX_FMT_RGB565: + reg = EXYNOS3250_OUT_FMT_RGB565; + break; + case V4L2_PIX_FMT_RGB565X: + reg = EXYNOS3250_OUT_FMT_RGB565 | EXYNOS3250_OUT_SWAP_RGB; + break; + case V4L2_PIX_FMT_YUYV: + reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR; + break; + case V4L2_PIX_FMT_YVYU: + reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR | + EXYNOS3250_OUT_SWAP_UV; + break; + case V4L2_PIX_FMT_UYVY: + reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM; + break; + case V4L2_PIX_FMT_VYUY: + reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM | + EXYNOS3250_OUT_SWAP_UV; + break; + case V4L2_PIX_FMT_NV12: + reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV12; + break; + case V4L2_PIX_FMT_NV21: + reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV21; + break; + case V4L2_PIX_FMT_YUV420: + reg = EXYNOS3250_OUT_FMT_420_3P; + break; + default: + reg = 0; + break; + } + + writel(reg, regs + EXYNOS3250_OUTFORM); +} + +void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr) +{ + writel(addr, regs + EXYNOS3250_JPG_JPGADR); +} + +void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr) +{ + writel(img_addr->y, regs + EXYNOS3250_LUMA_BASE); + writel(img_addr->cb, regs + EXYNOS3250_CHROMA_BASE); + writel(img_addr->cr, regs + EXYNOS3250_CHROMA_CR_BASE); +} + +void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, + unsigned int width) +{ + u32 reg_luma = 0, reg_cr = 0, reg_cb = 0; + + switch (img_fmt) { + case V4L2_PIX_FMT_RGB32: + reg_luma = 4 * width; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + reg_luma = 2 * width; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + reg_luma = width; + reg_cb = reg_luma; + break; + case V4L2_PIX_FMT_YUV420: + reg_luma = width; + reg_cb = reg_cr = reg_luma / 2; + break; + default: + break; + } + + writel(reg_luma, regs + EXYNOS3250_LUMA_STRIDE); + writel(reg_cb, regs + EXYNOS3250_CHROMA_STRIDE); + writel(reg_cr, regs + EXYNOS3250_CHROMA_CR_STRIDE); +} + +void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, + unsigned int y_offset) +{ + u32 reg; + + reg = (y_offset << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) & + EXYNOS3250_LUMA_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) & + EXYNOS3250_LUMA_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_LUMA_XY_OFFSET); + + reg = (y_offset << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_CHROMA_XY_OFFSET); + + reg = (y_offset << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_CHROMA_CR_XY_OFFSET); +} + +void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode) +{ + if (mode == S5P_JPEG_ENCODE) { + writel(EXYNOS3250_JPEG_ENC_COEF1, + base + EXYNOS3250_JPG_COEF(1)); + writel(EXYNOS3250_JPEG_ENC_COEF2, + base + EXYNOS3250_JPG_COEF(2)); + writel(EXYNOS3250_JPEG_ENC_COEF3, + base + EXYNOS3250_JPG_COEF(3)); + } else { + writel(EXYNOS3250_JPEG_DEC_COEF1, + base + EXYNOS3250_JPG_COEF(1)); + writel(EXYNOS3250_JPEG_DEC_COEF2, + base + EXYNOS3250_JPG_COEF(2)); + writel(EXYNOS3250_JPEG_DEC_COEF3, + base + EXYNOS3250_JPG_COEF(3)); + } +} + +void exynos3250_jpeg_start(void __iomem *regs) +{ + writel(1, regs + EXYNOS3250_JSTART); +} + +void exynos3250_jpeg_rstart(void __iomem *regs) +{ + writel(1, regs + EXYNOS3250_JRSTART); +} + +unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGINTST); +} + +void exynos3250_jpeg_clear_int_status(void __iomem *regs, + unsigned int value) +{ + writel(value, regs + EXYNOS3250_JPGINTST); +} + +unsigned int exynos3250_jpeg_operating(void __iomem *regs) +{ + return readl(regs + S5P_JPGOPR) & EXYNOS3250_JPGOPR_MASK; +} + +unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGCNT) & EXYNOS3250_JPGCNT_MASK; +} + +void exynos3250_jpeg_dec_stream_size(void __iomem *regs, + unsigned int size) +{ + writel(size & EXYNOS3250_DEC_STREAM_MASK, + regs + EXYNOS3250_DEC_STREAM_SIZE); +} + +void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, + unsigned int sratio) +{ + switch (sratio) { + case 1: + default: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_8_8; + break; + case 2: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_4_8; + break; + case 4: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_2_8; + break; + case 8: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_1_8; + break; + } + + writel(sratio & EXYNOS3250_DEC_SCALE_FACTOR_MASK, + regs + EXYNOS3250_DEC_SCALING_RATIO); +} + +void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value) +{ + time_value &= EXYNOS3250_TIMER_INIT_MASK; + + writel(EXYNOS3250_TIMER_INT_STAT | time_value, + regs + EXYNOS3250_TIMER_SE); +} + +unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_TIMER_ST); +} + +void exynos3250_jpeg_clear_timer_status(void __iomem *regs) +{ + writel(EXYNOS3250_TIMER_INT_STAT, regs + EXYNOS3250_TIMER_ST); +} diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h new file mode 100644 index 000000000000..15af928fad76 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jacek Anaszewski + */ +#ifndef JPEG_HW_EXYNOS3250_H_ +#define JPEG_HW_EXYNOS3250_H_ + +#include +#include + +#include "jpeg-regs.h" + +void exynos3250_jpeg_reset(void __iomem *regs); +void exynos3250_jpeg_poweron(void __iomem *regs); +void exynos3250_jpeg_set_dma_num(void __iomem *regs); +void exynos3250_jpeg_clk_set(void __iomem *base); +void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt); +void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt); +void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16); +void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode); +void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); +unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs); +void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri); +void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); +void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t); +void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t); +void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y); +void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x); +void exynos3250_jpeg_interrupts_enable(void __iomem *regs); +void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size); +void exynos3250_jpeg_outform_raw(void __iomem *regs, unsigned long format); +void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr); +void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr); +void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, + unsigned int width); +void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, + unsigned int y_offset); +void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode); +void exynos3250_jpeg_start(void __iomem *regs); +void exynos3250_jpeg_rstart(void __iomem *regs); +unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs); +void exynos3250_jpeg_clear_int_status(void __iomem *regs, + unsigned int value); +unsigned int exynos3250_jpeg_operating(void __iomem *regs); +unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs); +void exynos3250_jpeg_dec_stream_size(void __iomem *regs, unsigned int size); +void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, unsigned int sratio); +void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value); +unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs); +void exynos3250_jpeg_set_timer_status(void __iomem *regs); +void exynos3250_jpeg_clear_timer_status(void __iomem *regs); + +#endif /* JPEG_HW_EXYNOS3250_H_ */ diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c new file mode 100644 index 000000000000..0828cfa783fe --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Author: Jacek Anaszewski + * + * Register interface file for JPEG driver on Exynos4x12. + */ +#include +#include + +#include "jpeg-core.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-regs.h" + +void exynos4_jpeg_sw_reset(void __iomem *base) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG); + writel(reg & ~(EXYNOS4_DEC_MODE | EXYNOS4_ENC_MODE), + base + EXYNOS4_JPEG_CNTL_REG); + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG); + writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); + + udelay(100); + + writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG); + /* set exynos4_jpeg mod register */ + if (mode == S5P_JPEG_DECODE) { + writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | + EXYNOS4_DEC_MODE, + base + EXYNOS4_JPEG_CNTL_REG); + } else if (mode == S5P_JPEG_ENCODE) {/* encode */ + writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | + EXYNOS4_ENC_MODE, + base + EXYNOS4_JPEG_CNTL_REG); + } else { /* disable both */ + writel(reg & EXYNOS4_ENC_DEC_MODE_MASK, + base + EXYNOS4_JPEG_CNTL_REG); + } +} + +void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, + unsigned int version) +{ + unsigned int reg; + unsigned int exynos4_swap_chroma_cbcr; + unsigned int exynos4_swap_chroma_crcb; + + if (version == SJPEG_EXYNOS4) { + exynos4_swap_chroma_cbcr = EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_crcb = EXYNOS4_SWAP_CHROMA_CRCB; + } else { + exynos4_swap_chroma_cbcr = EXYNOS5433_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_crcb = EXYNOS5433_SWAP_CHROMA_CRCB; + } + + reg = readl(base + EXYNOS4_IMG_FMT_REG) & + EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */ + + switch (img_fmt) { + case V4L2_PIX_FMT_GREY: + reg = reg | EXYNOS4_ENC_GRAY_IMG | EXYNOS4_GRAY_IMG_IP; + break; + case V4L2_PIX_FMT_RGB32: + reg = reg | EXYNOS4_ENC_RGB_IMG | + EXYNOS4_RGB_IP_RGB_32BIT_IMG; + break; + case V4L2_PIX_FMT_RGB565: + reg = reg | EXYNOS4_ENC_RGB_IMG | + EXYNOS4_RGB_IP_RGB_16BIT_IMG; + break; + case V4L2_PIX_FMT_NV24: + reg = reg | EXYNOS4_ENC_YUV_444_IMG | + EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | + exynos4_swap_chroma_cbcr; + break; + case V4L2_PIX_FMT_NV42: + reg = reg | EXYNOS4_ENC_YUV_444_IMG | + EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | + exynos4_swap_chroma_crcb; + break; + case V4L2_PIX_FMT_YUYV: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | + exynos4_swap_chroma_cbcr; + break; + + case V4L2_PIX_FMT_YVYU: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | + exynos4_swap_chroma_crcb; + break; + case V4L2_PIX_FMT_NV16: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | + exynos4_swap_chroma_cbcr; + break; + case V4L2_PIX_FMT_NV61: + reg = reg | EXYNOS4_DEC_YUV_422_IMG | + EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | + exynos4_swap_chroma_crcb; + break; + case V4L2_PIX_FMT_NV12: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | + exynos4_swap_chroma_cbcr; + break; + case V4L2_PIX_FMT_NV21: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | + exynos4_swap_chroma_crcb; + break; + case V4L2_PIX_FMT_YUV420: + reg = reg | EXYNOS4_DEC_YUV_420_IMG | + EXYNOS4_YUV_420_IP_YUV_420_3P_IMG | + exynos4_swap_chroma_cbcr; + break; + default: + break; + + } + + writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, + unsigned int version) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_IMG_FMT_REG) & + ~(version == SJPEG_EXYNOS4 ? EXYNOS4_ENC_FMT_MASK : + EXYNOS5433_ENC_FMT_MASK); /* clear enc format */ + + switch (out_fmt) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: + reg = reg | EXYNOS4_ENC_FMT_GRAY; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_444: + reg = reg | EXYNOS4_ENC_FMT_YUV_444; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + reg = reg | EXYNOS4_ENC_FMT_YUV_422; + break; + + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + reg = reg | EXYNOS4_ENC_FMT_YUV_420; + break; + + default: + break; + } + + writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version) +{ + unsigned int reg; + + if (version == SJPEG_EXYNOS4) { + reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; + writel(reg | EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); + } else { + reg = readl(base + EXYNOS4_INT_EN_REG) & + ~EXYNOS5433_INT_EN_MASK; + writel(reg | EXYNOS5433_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); + } +} + +unsigned int exynos4_jpeg_get_int_status(void __iomem *base) +{ + return readl(base + EXYNOS4_INT_STATUS_REG); +} + +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base) +{ + return readl(base + EXYNOS4_FIFO_STATUS_REG); +} + +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~EXYNOS4_HUF_TBL_EN; + + if (value == 1) + writel(reg | EXYNOS4_HUF_TBL_EN, + base + EXYNOS4_JPEG_CNTL_REG); + else + writel(reg & ~EXYNOS4_HUF_TBL_EN, + base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN); + + if (value == 1) + writel(reg | EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); + else + writel(reg & ~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, + unsigned int address) +{ + writel(address, base + EXYNOS4_OUT_MEM_BASE_REG); +} + +void exynos4_jpeg_set_stream_size(void __iomem *base, + unsigned int x_value, unsigned int y_value) +{ + writel(0x0, base + EXYNOS4_JPEG_IMG_SIZE_REG); /* clear */ + writel(EXYNOS4_X_SIZE(x_value) | EXYNOS4_Y_SIZE(y_value), + base + EXYNOS4_JPEG_IMG_SIZE_REG); +} + +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, + struct s5p_jpeg_addr *exynos4_jpeg_addr) +{ + writel(exynos4_jpeg_addr->y, base + EXYNOS4_IMG_BA_PLANE_1_REG); + writel(exynos4_jpeg_addr->cb, base + EXYNOS4_IMG_BA_PLANE_2_REG); + writel(exynos4_jpeg_addr->cr, base + EXYNOS4_IMG_BA_PLANE_3_REG); +} + +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, + enum exynos4_jpeg_img_quality_level level) +{ + unsigned int reg; + + reg = EXYNOS4_Q_TBL_COMP1_0 | EXYNOS4_Q_TBL_COMP2_1 | + EXYNOS4_Q_TBL_COMP3_1 | + EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 | + EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 | + EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1; + + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_set_dec_components(void __iomem *base, int n) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_NF(n); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_Q_TBL_COMP(c, x); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_HUFF_TBL_COMP(c, x); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt) +{ + if (fmt == V4L2_PIX_FMT_GREY) + writel(0xd2, base + EXYNOS4_HUFF_CNT_REG); + else + writel(0x1a2, base + EXYNOS4_HUFF_CNT_REG); +} + +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base) +{ + return readl(base + EXYNOS4_BITSTREAM_SIZE_REG); +} + +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size) +{ + writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG); +} + +void exynos4_jpeg_get_frame_size(void __iomem *base, + unsigned int *width, unsigned int *height) +{ + *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) & + EXYNOS4_DECODED_SIZE_MASK); + *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) & + EXYNOS4_DECODED_SIZE_MASK; +} + +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base) +{ + return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) & + EXYNOS4_JPEG_DECODED_IMG_FMT_MASK; +} + +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size) +{ + writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG); +} diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h new file mode 100644 index 000000000000..3e2887526960 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Author: Jacek Anaszewski + * + * Header file of the register interface for JPEG driver on Exynos4x12. +*/ + +#ifndef JPEG_HW_EXYNOS4_H_ +#define JPEG_HW_EXYNOS4_H_ + +void exynos4_jpeg_sw_reset(void __iomem *base); +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode); +void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, + unsigned int version); +void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, + unsigned int version); +void exynos4_jpeg_set_enc_tbl(void __iomem *base); +void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version); +unsigned int exynos4_jpeg_get_int_status(void __iomem *base); +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value); +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value); +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, + unsigned int address); +void exynos4_jpeg_set_stream_size(void __iomem *base, + unsigned int x_value, unsigned int y_value); +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, + struct s5p_jpeg_addr *jpeg_addr); +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, + enum exynos4_jpeg_img_quality_level level); +void exynos4_jpeg_set_dec_components(void __iomem *base, int n); +void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x); +void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x); +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); +void exynos4_jpeg_get_frame_size(void __iomem *base, + unsigned int *width, unsigned int *height); +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base); +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base); +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size); + +#endif /* JPEG_HW_EXYNOS4_H_ */ diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c new file mode 100644 index 000000000000..01b47b3df1e7 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + */ + +#include +#include + +#include "jpeg-core.h" +#include "jpeg-regs.h" +#include "jpeg-hw-s5p.h" + +void s5p_jpeg_reset(void __iomem *regs) +{ + unsigned long reg; + + writel(1, regs + S5P_JPG_SW_RESET); + reg = readl(regs + S5P_JPG_SW_RESET); + /* no other way but polling for when JPEG IP becomes operational */ + while (reg != 0) { + cpu_relax(); + reg = readl(regs + S5P_JPG_SW_RESET); + } +} + +void s5p_jpeg_poweron(void __iomem *regs) +{ + writel(S5P_POWER_ON, regs + S5P_JPGCLKCON); +} + +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) +{ + unsigned long reg, m; + + m = S5P_MOD_SEL_565; + if (mode == S5P_JPEG_RAW_IN_565) + m = S5P_MOD_SEL_565; + else if (mode == S5P_JPEG_RAW_IN_422) + m = S5P_MOD_SEL_422; + + reg = readl(regs + S5P_JPGCMOD); + reg &= ~S5P_MOD_SEL_MASK; + reg |= m; + writel(reg, regs + S5P_JPGCMOD); +} + +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode) +{ + unsigned long reg, m; + + m = S5P_PROC_MODE_DECOMPR; + if (mode == S5P_JPEG_ENCODE) + m = S5P_PROC_MODE_COMPR; + else + m = S5P_PROC_MODE_DECOMPR; + reg = readl(regs + S5P_JPGMOD); + reg &= ~S5P_PROC_MODE_MASK; + reg |= m; + writel(reg, regs + S5P_JPGMOD); +} + +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +{ + unsigned long reg, m; + + if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420) + m = S5P_SUBSAMPLING_MODE_420; + else + m = S5P_SUBSAMPLING_MODE_422; + + reg = readl(regs + S5P_JPGMOD); + reg &= ~S5P_SUBSAMPLING_MODE_MASK; + reg |= m; + writel(reg, regs + S5P_JPGMOD); +} + +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK; +} + +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGDRI_U); + reg &= ~0xff; + reg |= (dri >> 8) & 0xff; + writel(reg, regs + S5P_JPGDRI_U); + + reg = readl(regs + S5P_JPGDRI_L); + reg &= ~0xff; + reg |= dri & 0xff; + writel(reg, regs + S5P_JPGDRI_L); +} + +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_QTBL); + reg &= ~S5P_QT_NUMt_MASK(t); + reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t); + writel(reg, regs + S5P_JPG_QTBL); +} + +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_HTBL); + reg &= ~S5P_HT_NUMt_AC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t); + writel(reg, regs + S5P_JPG_HTBL); +} + +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_HTBL); + reg &= ~S5P_HT_NUMt_DC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t); + writel(reg, regs + S5P_JPG_HTBL); +} + +void s5p_jpeg_y(void __iomem *regs, unsigned int y) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGY_U); + reg &= ~0xff; + reg |= (y >> 8) & 0xff; + writel(reg, regs + S5P_JPGY_U); + + reg = readl(regs + S5P_JPGY_L); + reg &= ~0xff; + reg |= y & 0xff; + writel(reg, regs + S5P_JPGY_L); +} + +void s5p_jpeg_x(void __iomem *regs, unsigned int x) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGX_U); + reg &= ~0xff; + reg |= (x >> 8) & 0xff; + writel(reg, regs + S5P_JPGX_U); + + reg = readl(regs + S5P_JPGX_L); + reg &= ~0xff; + reg |= x & 0xff; + writel(reg, regs + S5P_JPGX_L); +} + +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_RSTm_INT_EN_MASK; + if (enable) + reg |= S5P_RSTm_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_DATA_NUM_INT_EN_MASK; + if (enable) + reg |= S5P_DATA_NUM_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPGINTSE); + reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK; + if (enbl) + reg |= S5P_FINAL_MCU_NUM_INT_EN; + writel(reg, regs + S5P_JPGINTSE); +} + +int s5p_jpeg_timer_stat(void __iomem *regs) +{ + return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK) + >> S5P_TIMER_INT_STAT_SHIFT); +} + +void s5p_jpeg_clear_timer_stat(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_TIMER_SE); + reg &= ~S5P_TIMER_INT_STAT_MASK; + writel(reg, regs + S5P_JPG_TIMER_SE); +} + +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); + reg &= ~S5P_ENC_STREAM_BOUND_MASK; + reg |= S5P_ENC_STREAM_INT_EN; + reg |= size & S5P_ENC_STREAM_BOUND_MASK; + writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); +} + +int s5p_jpeg_enc_stream_stat(void __iomem *regs) +{ + return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) & + S5P_ENC_STREAM_INT_STAT_MASK); +} + +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE); + reg &= ~S5P_ENC_STREAM_INT_MASK; + writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE); +} + +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format) +{ + unsigned long reg, f; + + f = S5P_DEC_OUT_FORMAT_422; + if (format == S5P_JPEG_RAW_OUT_422) + f = S5P_DEC_OUT_FORMAT_422; + else if (format == S5P_JPEG_RAW_OUT_420) + f = S5P_DEC_OUT_FORMAT_420; + reg = readl(regs + S5P_JPG_OUTFORM); + reg &= ~S5P_DEC_OUT_FORMAT_MASK; + reg |= f; + writel(reg, regs + S5P_JPG_OUTFORM); +} + +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr) +{ + writel(addr, regs + S5P_JPG_JPGADR); +} + +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr) +{ + writel(addr, regs + S5P_JPG_IMGADR); +} + +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, + unsigned int j, unsigned int coef) +{ + unsigned long reg; + + reg = readl(regs + S5P_JPG_COEF(i)); + reg &= ~S5P_COEFn_MASK(j); + reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j); + writel(reg, regs + S5P_JPG_COEF(i)); +} + +void s5p_jpeg_start(void __iomem *regs) +{ + writel(1, regs + S5P_JSTART); +} + +int s5p_jpeg_result_stat_ok(void __iomem *regs) +{ + return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK) + >> S5P_RESULT_STAT_SHIFT); +} + +int s5p_jpeg_stream_stat_ok(void __iomem *regs) +{ + return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK) + >> S5P_STREAM_STAT_SHIFT); +} + +void s5p_jpeg_clear_int(void __iomem *regs) +{ + readl(regs + S5P_JPGINTST); + writel(S5P_INT_RELEASE, regs + S5P_JPGCOM); + readl(regs + S5P_JPGOPR); +} + +unsigned int s5p_jpeg_compressed_size(void __iomem *regs) +{ + unsigned long jpeg_size = 0; + + jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16; + jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8; + jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff); + + return (unsigned int)jpeg_size; +} diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h new file mode 100644 index 000000000000..f068d52c66b7 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + */ +#ifndef JPEG_HW_S5P_H_ +#define JPEG_HW_S5P_H_ + +#include +#include + +#include "jpeg-regs.h" + +#define S5P_JPEG_MIN_WIDTH 32 +#define S5P_JPEG_MIN_HEIGHT 32 +#define S5P_JPEG_MAX_WIDTH 8192 +#define S5P_JPEG_MAX_HEIGHT 8192 +#define S5P_JPEG_RAW_IN_565 0 +#define S5P_JPEG_RAW_IN_422 1 +#define S5P_JPEG_RAW_OUT_422 0 +#define S5P_JPEG_RAW_OUT_420 1 + +void s5p_jpeg_reset(void __iomem *regs); +void s5p_jpeg_poweron(void __iomem *regs); +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs); +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri); +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t); +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t); +void s5p_jpeg_y(void __iomem *regs, unsigned int y); +void s5p_jpeg_x(void __iomem *regs, unsigned int x); +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl); +int s5p_jpeg_timer_stat(void __iomem *regs); +void s5p_jpeg_clear_timer_stat(void __iomem *regs); +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size); +int s5p_jpeg_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format); +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, + unsigned int j, unsigned int coef); +void s5p_jpeg_start(void __iomem *regs); +int s5p_jpeg_result_stat_ok(void __iomem *regs); +int s5p_jpeg_stream_stat_ok(void __iomem *regs); +void s5p_jpeg_clear_int(void __iomem *regs); +unsigned int s5p_jpeg_compressed_size(void __iomem *regs); + +#endif /* JPEG_HW_S5P_H_ */ diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h new file mode 100644 index 000000000000..c2298b680022 --- /dev/null +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h @@ -0,0 +1,646 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h + * + * Register definition file for Samsung JPEG codec driver + * + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * Author: Jacek Anaszewski + */ + +#ifndef JPEG_REGS_H_ +#define JPEG_REGS_H_ + +/* Register and bit definitions for S5PC210 */ + +/* JPEG mode register */ +#define S5P_JPGMOD 0x00 +#define S5P_PROC_MODE_MASK (0x1 << 3) +#define S5P_PROC_MODE_DECOMPR (0x1 << 3) +#define S5P_PROC_MODE_COMPR (0x0 << 3) +#define S5P_SUBSAMPLING_MODE_MASK 0x7 +#define S5P_SUBSAMPLING_MODE_444 (0x0 << 0) +#define S5P_SUBSAMPLING_MODE_422 (0x1 << 0) +#define S5P_SUBSAMPLING_MODE_420 (0x2 << 0) +#define S5P_SUBSAMPLING_MODE_GRAY (0x3 << 0) + +/* JPEG operation status register */ +#define S5P_JPGOPR 0x04 + +/* Quantization tables*/ +#define S5P_JPG_QTBL 0x08 +#define S5P_QT_NUMt_SHIFT(t) (((t) - 1) << 1) +#define S5P_QT_NUMt_MASK(t) (0x3 << S5P_QT_NUMt_SHIFT(t)) + +/* Huffman tables */ +#define S5P_JPG_HTBL 0x0c +#define S5P_HT_NUMt_AC_SHIFT(t) (((t) << 1) - 1) +#define S5P_HT_NUMt_AC_MASK(t) (0x1 << S5P_HT_NUMt_AC_SHIFT(t)) + +#define S5P_HT_NUMt_DC_SHIFT(t) (((t) - 1) << 1) +#define S5P_HT_NUMt_DC_MASK(t) (0x1 << S5P_HT_NUMt_DC_SHIFT(t)) + +/* JPEG restart interval register upper byte */ +#define S5P_JPGDRI_U 0x10 + +/* JPEG restart interval register lower byte */ +#define S5P_JPGDRI_L 0x14 + +/* JPEG vertical resolution register upper byte */ +#define S5P_JPGY_U 0x18 + +/* JPEG vertical resolution register lower byte */ +#define S5P_JPGY_L 0x1c + +/* JPEG horizontal resolution register upper byte */ +#define S5P_JPGX_U 0x20 + +/* JPEG horizontal resolution register lower byte */ +#define S5P_JPGX_L 0x24 + +/* JPEG byte count register upper byte */ +#define S5P_JPGCNT_U 0x28 + +/* JPEG byte count register middle byte */ +#define S5P_JPGCNT_M 0x2c + +/* JPEG byte count register lower byte */ +#define S5P_JPGCNT_L 0x30 + +/* JPEG interrupt setting register */ +#define S5P_JPGINTSE 0x34 +#define S5P_RSTm_INT_EN_MASK (0x1 << 7) +#define S5P_RSTm_INT_EN (0x1 << 7) +#define S5P_DATA_NUM_INT_EN_MASK (0x1 << 6) +#define S5P_DATA_NUM_INT_EN (0x1 << 6) +#define S5P_FINAL_MCU_NUM_INT_EN_MASK (0x1 << 5) +#define S5P_FINAL_MCU_NUM_INT_EN (0x1 << 5) + +/* JPEG interrupt status register */ +#define S5P_JPGINTST 0x38 +#define S5P_RESULT_STAT_SHIFT 6 +#define S5P_RESULT_STAT_MASK (0x1 << S5P_RESULT_STAT_SHIFT) +#define S5P_STREAM_STAT_SHIFT 5 +#define S5P_STREAM_STAT_MASK (0x1 << S5P_STREAM_STAT_SHIFT) + +/* JPEG command register */ +#define S5P_JPGCOM 0x4c +#define S5P_INT_RELEASE (0x1 << 2) + +/* Raw image data r/w address register */ +#define S5P_JPG_IMGADR 0x50 + +/* JPEG file r/w address register */ +#define S5P_JPG_JPGADR 0x58 + +/* Coefficient for RGB-to-YCbCr converter register */ +#define S5P_JPG_COEF(n) (0x5c + (((n) - 1) << 2)) +#define S5P_COEFn_SHIFT(j) ((3 - (j)) << 3) +#define S5P_COEFn_MASK(j) (0xff << S5P_COEFn_SHIFT(j)) + +/* JPEG color mode register */ +#define S5P_JPGCMOD 0x68 +#define S5P_MOD_SEL_MASK (0x7 << 5) +#define S5P_MOD_SEL_422 (0x1 << 5) +#define S5P_MOD_SEL_565 (0x2 << 5) +#define S5P_MODE_Y16_MASK (0x1 << 1) +#define S5P_MODE_Y16 (0x1 << 1) + +/* JPEG clock control register */ +#define S5P_JPGCLKCON 0x6c +#define S5P_CLK_DOWN_READY (0x1 << 1) +#define S5P_POWER_ON (0x1 << 0) + +/* JPEG start register */ +#define S5P_JSTART 0x70 + +/* JPEG SW reset register */ +#define S5P_JPG_SW_RESET 0x78 + +/* JPEG timer setting register */ +#define S5P_JPG_TIMER_SE 0x7c +#define S5P_TIMER_INT_EN_MASK (0x1UL << 31) +#define S5P_TIMER_INT_EN (0x1UL << 31) +#define S5P_TIMER_INIT_MASK 0x7fffffff + +/* JPEG timer status register */ +#define S5P_JPG_TIMER_ST 0x80 +#define S5P_TIMER_INT_STAT_SHIFT 31 +#define S5P_TIMER_INT_STAT_MASK (0x1UL << S5P_TIMER_INT_STAT_SHIFT) +#define S5P_TIMER_CNT_SHIFT 0 +#define S5P_TIMER_CNT_MASK 0x7fffffff + +/* JPEG decompression output format register */ +#define S5P_JPG_OUTFORM 0x88 +#define S5P_DEC_OUT_FORMAT_MASK (0x1 << 0) +#define S5P_DEC_OUT_FORMAT_422 (0x0 << 0) +#define S5P_DEC_OUT_FORMAT_420 (0x1 << 0) + +/* JPEG version register */ +#define S5P_JPG_VERSION 0x8c + +/* JPEG compressed stream size interrupt setting register */ +#define S5P_JPG_ENC_STREAM_INTSE 0x98 +#define S5P_ENC_STREAM_INT_MASK (0x1 << 24) +#define S5P_ENC_STREAM_INT_EN (0x1 << 24) +#define S5P_ENC_STREAM_BOUND_MASK 0xffffff + +/* JPEG compressed stream size interrupt status register */ +#define S5P_JPG_ENC_STREAM_INTST 0x9c +#define S5P_ENC_STREAM_INT_STAT_MASK 0x1 + +/* JPEG quantizer table register */ +#define S5P_JPG_QTBL_CONTENT(n) (0x400 + (n) * 0x100) + +/* JPEG DC Huffman table register */ +#define S5P_JPG_HDCTBL(n) (0x800 + (n) * 0x400) + +/* JPEG DC Huffman table register */ +#define S5P_JPG_HDCTBLG(n) (0x840 + (n) * 0x400) + +/* JPEG AC Huffman table register */ +#define S5P_JPG_HACTBL(n) (0x880 + (n) * 0x400) + +/* JPEG AC Huffman table register */ +#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400) + + +/* Register and bit definitions for Exynos 4x12 */ + +/* JPEG Codec Control Registers */ +#define EXYNOS4_JPEG_CNTL_REG 0x00 +#define EXYNOS4_INT_EN_REG 0x04 +#define EXYNOS4_INT_TIMER_COUNT_REG 0x08 +#define EXYNOS4_INT_STATUS_REG 0x0c +#define EXYNOS4_OUT_MEM_BASE_REG 0x10 +#define EXYNOS4_JPEG_IMG_SIZE_REG 0x14 +#define EXYNOS4_IMG_BA_PLANE_1_REG 0x18 +#define EXYNOS4_IMG_SO_PLANE_1_REG 0x1c +#define EXYNOS4_IMG_PO_PLANE_1_REG 0x20 +#define EXYNOS4_IMG_BA_PLANE_2_REG 0x24 +#define EXYNOS4_IMG_SO_PLANE_2_REG 0x28 +#define EXYNOS4_IMG_PO_PLANE_2_REG 0x2c +#define EXYNOS4_IMG_BA_PLANE_3_REG 0x30 +#define EXYNOS4_IMG_SO_PLANE_3_REG 0x34 +#define EXYNOS4_IMG_PO_PLANE_3_REG 0x38 + +#define EXYNOS4_TBL_SEL_REG 0x3c + +#define EXYNOS4_IMG_FMT_REG 0x40 + +#define EXYNOS4_BITSTREAM_SIZE_REG 0x44 +#define EXYNOS4_PADDING_REG 0x48 +#define EXYNOS4_HUFF_CNT_REG 0x4c +#define EXYNOS4_FIFO_STATUS_REG 0x50 +#define EXYNOS4_DECODE_XY_SIZE_REG 0x54 +#define EXYNOS4_DECODE_IMG_FMT_REG 0x58 + +#define EXYNOS4_QUAN_TBL_ENTRY_REG 0x100 +#define EXYNOS4_HUFF_TBL_ENTRY_REG 0x200 + + +/****************************************************************/ +/* Bit definition part */ +/****************************************************************/ + +/* JPEG CNTL Register bit */ +#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0) +#define EXYNOS4_DEC_MODE (1 << 0) +#define EXYNOS4_ENC_MODE (1 << 1) +#define EXYNOS4_AUTO_RST_MARKER (1 << 2) +#define EXYNOS4_RST_INTERVAL_SHIFT 3 +#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \ + << EXYNOS4_RST_INTERVAL_SHIFT) +#define EXYNOS4_HUF_TBL_EN (1 << 19) +#define EXYNOS4_HOR_SCALING_SHIFT 20 +#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \ + << EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING_SHIFT 22 +#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \ + << EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_PADDING (1 << 27) +#define EXYNOS4_SYS_INT_EN (1 << 28) +#define EXYNOS4_SOFT_RESET_HI (1 << 29) + +/* JPEG INT Register bit */ +#define EXYNOS4_INT_EN_MASK (0x1f << 0) +#define EXYNOS5433_INT_EN_MASK (0x1ff << 0) +#define EXYNOS4_PROT_ERR_INT_EN (1 << 0) +#define EXYNOS4_IMG_COMPLETION_INT_EN (1 << 1) +#define EXYNOS4_DEC_INVALID_FORMAT_EN (1 << 2) +#define EXYNOS4_MULTI_SCAN_ERROR_EN (1 << 3) +#define EXYNOS4_FRAME_ERR_EN (1 << 4) +#define EXYNOS4_INT_EN_ALL (0x1f << 0) +#define EXYNOS5433_INT_EN_ALL (0x1b6 << 0) + +#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) +#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) + +#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_420 (2 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_GRAY (3 << 0) + + +/* JPEG IMAGE SIZE Register bit */ +#define EXYNOS4_X_SIZE_SHIFT 0 +#define EXYNOS4_X_SIZE_MASK (0xffff << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_X_SIZE(x) (((x) & 0xffff) << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE_SHIFT 16 +#define EXYNOS4_Y_SIZE_MASK (0xffff << EXYNOS4_Y_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE(x) (((x) & 0xffff) << EXYNOS4_Y_SIZE_SHIFT) + +/* JPEG IMAGE FORMAT Register bit */ +#define EXYNOS4_ENC_IN_FMT_MASK 0xffff0000 +#define EXYNOS4_ENC_GRAY_IMG (0 << 0) +#define EXYNOS4_ENC_RGB_IMG (1 << 0) +#define EXYNOS4_ENC_YUV_444_IMG (2 << 0) +#define EXYNOS4_ENC_YUV_422_IMG (3 << 0) +#define EXYNOS4_ENC_YUV_440_IMG (4 << 0) + +#define EXYNOS4_DEC_GRAY_IMG (0 << 0) +#define EXYNOS4_DEC_RGB_IMG (1 << 0) +#define EXYNOS4_DEC_YUV_444_IMG (2 << 0) +#define EXYNOS4_DEC_YUV_422_IMG (3 << 0) +#define EXYNOS4_DEC_YUV_420_IMG (4 << 0) + +#define EXYNOS4_GRAY_IMG_IP_SHIFT 3 +#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT) +#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT) + +#define EXYNOS4_RGB_IP_SHIFT 6 +#define EXYNOS4_RGB_IP_MASK (7 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT) + +#define EXYNOS4_YUV_444_IP_SHIFT 9 +#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT) + +#define EXYNOS4_YUV_422_IP_SHIFT 12 +#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT) + +#define EXYNOS4_YUV_420_IP_SHIFT 15 +#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT) + +#define EXYNOS4_ENC_FMT_SHIFT 24 +#define EXYNOS4_ENC_FMT_MASK (3 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS5433_ENC_FMT_MASK (7 << EXYNOS4_ENC_FMT_SHIFT) + +#define EXYNOS4_ENC_FMT_GRAY (0 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_444 (1 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_422 (2 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_420 (3 << EXYNOS4_ENC_FMT_SHIFT) + +#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03 + +#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) +#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) +#define EXYNOS5433_SWAP_CHROMA_CRCB (1 << 27) +#define EXYNOS5433_SWAP_CHROMA_CBCR (0 << 27) + +/* JPEG HUFF count Register bit */ +#define EXYNOS4_HUFF_COUNT_MASK 0xffff + +/* JPEG Decoded_img_x_y_size Register bit */ +#define EXYNOS4_DECODED_SIZE_MASK 0x0000ffff + +/* JPEG Decoded image format Register bit */ +#define EXYNOS4_DECODED_IMG_FMT_MASK 0x3 + +/* JPEG TBL SEL Register bit */ +#define EXYNOS4_Q_TBL_COMP(c, n) ((n) << (((c) - 1) << 1)) + +#define EXYNOS4_Q_TBL_COMP1_0 EXYNOS4_Q_TBL_COMP(1, 0) +#define EXYNOS4_Q_TBL_COMP1_1 EXYNOS4_Q_TBL_COMP(1, 1) +#define EXYNOS4_Q_TBL_COMP1_2 EXYNOS4_Q_TBL_COMP(1, 2) +#define EXYNOS4_Q_TBL_COMP1_3 EXYNOS4_Q_TBL_COMP(1, 3) + +#define EXYNOS4_Q_TBL_COMP2_0 EXYNOS4_Q_TBL_COMP(2, 0) +#define EXYNOS4_Q_TBL_COMP2_1 EXYNOS4_Q_TBL_COMP(2, 1) +#define EXYNOS4_Q_TBL_COMP2_2 EXYNOS4_Q_TBL_COMP(2, 2) +#define EXYNOS4_Q_TBL_COMP2_3 EXYNOS4_Q_TBL_COMP(2, 3) + +#define EXYNOS4_Q_TBL_COMP3_0 EXYNOS4_Q_TBL_COMP(3, 0) +#define EXYNOS4_Q_TBL_COMP3_1 EXYNOS4_Q_TBL_COMP(3, 1) +#define EXYNOS4_Q_TBL_COMP3_2 EXYNOS4_Q_TBL_COMP(3, 2) +#define EXYNOS4_Q_TBL_COMP3_3 EXYNOS4_Q_TBL_COMP(3, 3) + +#define EXYNOS4_HUFF_TBL_COMP(c, n) ((n) << ((((c) - 1) << 1) + 6)) + +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(1, 0) +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(1, 1) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(1, 2) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(1, 3) + +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(2, 0) +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(2, 1) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(2, 2) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(2, 3) + +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(3, 0) +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(3, 1) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(3, 2) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(3, 3) + +#define EXYNOS4_NF_SHIFT 16 +#define EXYNOS4_NF_MASK 0xff +#define EXYNOS4_NF(x) \ + (((x) & EXYNOS4_NF_MASK) << EXYNOS4_NF_SHIFT) + +/* JPEG quantizer table register */ +#define EXYNOS4_QTBL_CONTENT(n) (0x100 + (n) * 0x40) + +/* JPEG DC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLL 0x200 + +/* JPEG DC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLV 0x210 + +/* JPEG DC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCL 0x220 + +/* JPEG DC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCV 0x230 + +/* JPEG AC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLL 0x240 + +/* JPEG AC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLV 0x250 + +/* JPEG AC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCL 0x300 + +/* JPEG AC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCV 0x310 + +/* Register and bit definitions for Exynos 3250 */ + +/* JPEG mode register */ +#define EXYNOS3250_JPGMOD 0x00 +#define EXYNOS3250_PROC_MODE_MASK (0x1 << 3) +#define EXYNOS3250_PROC_MODE_DECOMPR (0x1 << 3) +#define EXYNOS3250_PROC_MODE_COMPR (0x0 << 3) +#define EXYNOS3250_SUBSAMPLING_MODE_MASK (0x7 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_444 (0x0 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_422 (0x1 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_420 (0x2 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_411 (0x6 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_GRAY (0x3 << 0) + +/* JPEG operation status register */ +#define EXYNOS3250_JPGOPR 0x04 +#define EXYNOS3250_JPGOPR_MASK 0x01 + +/* Quantization and Huffman tables register */ +#define EXYNOS3250_QHTBL 0x08 +#define EXYNOS3250_QT_NUM_SHIFT(t) ((((t) - 1) << 1) + 8) +#define EXYNOS3250_QT_NUM_MASK(t) (0x3 << EXYNOS3250_QT_NUM_SHIFT(t)) + +/* Huffman tables */ +#define EXYNOS3250_HT_NUM_AC_SHIFT(t) (((t) << 1) - 1) +#define EXYNOS3250_HT_NUM_AC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) + +#define EXYNOS3250_HT_NUM_DC_SHIFT(t) (((t) - 1) << 1) +#define EXYNOS3250_HT_NUM_DC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) + +/* JPEG restart interval register */ +#define EXYNOS3250_JPGDRI 0x0c +#define EXYNOS3250_JPGDRI_MASK 0xffff + +/* JPEG vertical resolution register */ +#define EXYNOS3250_JPGY 0x10 +#define EXYNOS3250_JPGY_MASK 0xffff + +/* JPEG horizontal resolution register */ +#define EXYNOS3250_JPGX 0x14 +#define EXYNOS3250_JPGX_MASK 0xffff + +/* JPEG byte count register */ +#define EXYNOS3250_JPGCNT 0x18 +#define EXYNOS3250_JPGCNT_MASK 0xffffff + +/* JPEG interrupt mask register */ +#define EXYNOS3250_JPGINTSE 0x1c +#define EXYNOS3250_JPEG_DONE_EN (1 << 11) +#define EXYNOS3250_WDMA_DONE_EN (1 << 10) +#define EXYNOS3250_RDMA_DONE_EN (1 << 9) +#define EXYNOS3250_ENC_STREAM_INT_EN (1 << 8) +#define EXYNOS3250_CORE_DONE_EN (1 << 5) +#define EXYNOS3250_ERR_INT_EN (1 << 4) +#define EXYNOS3250_HEAD_INT_EN (1 << 3) + +/* JPEG interrupt status register */ +#define EXYNOS3250_JPGINTST 0x20 +#define EXYNOS3250_JPEG_DONE (1 << 11) +#define EXYNOS3250_WDMA_DONE (1 << 10) +#define EXYNOS3250_RDMA_DONE (1 << 9) +#define EXYNOS3250_ENC_STREAM_STAT (1 << 8) +#define EXYNOS3250_RESULT_STAT (1 << 5) +#define EXYNOS3250_STREAM_STAT (1 << 4) +#define EXYNOS3250_HEADER_STAT (1 << 3) + +/* + * Base address of the luma component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_LUMA_BASE 0x100 +#define EXYNOS3250_SRC_TILE_EN_MASK 0x100 + +/* Stride of source or destination luma raw image buffer */ +#define EXYNOS3250_LUMA_STRIDE 0x104 + +/* Horizontal/vertical offset of active region in luma raw image buffer */ +#define EXYNOS3250_LUMA_XY_OFFSET 0x108 +#define EXYNOS3250_LUMA_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_LUMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) +#define EXYNOS3250_LUMA_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_LUMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) + +/* + * Base address of the chroma(Cb) component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_CHROMA_BASE 0x10c + +/* Stride of source or destination chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_STRIDE 0x110 + +/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_XY_OFFSET 0x114 +#define EXYNOS3250_CHROMA_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_CHROMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) +#define EXYNOS3250_CHROMA_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_CHROMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) + +/* + * Base address of the chroma(Cr) component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_CHROMA_CR_BASE 0x118 + +/* Stride of source or destination chroma(Cr) raw image buffer */ +#define EXYNOS3250_CHROMA_CR_STRIDE 0x11c + +/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_CR_XY_OFFSET 0x120 +#define EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) +#define EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) + +/* Raw image data r/w address register */ +#define EXYNOS3250_JPG_IMGADR 0x50 + +/* Source or destination JPEG file DMA buffer address */ +#define EXYNOS3250_JPG_JPGADR 0x124 + +/* Coefficients for RGB-to-YCbCr converter register */ +#define EXYNOS3250_JPG_COEF(n) (0x128 + (((n) - 1) << 2)) +#define EXYNOS3250_COEF_SHIFT(j) ((3 - (j)) << 3) +#define EXYNOS3250_COEF_MASK(j) (0xff << EXYNOS3250_COEF_SHIFT(j)) + +/* Raw input format setting */ +#define EXYNOS3250_JPGCMOD 0x134 +#define EXYNOS3250_SRC_TILE_EN (0x1 << 10) +#define EXYNOS3250_SRC_NV_MASK (0x1 << 9) +#define EXYNOS3250_SRC_NV12 (0x0 << 9) +#define EXYNOS3250_SRC_NV21 (0x1 << 9) +#define EXYNOS3250_SRC_BIG_ENDIAN_MASK (0x1 << 8) +#define EXYNOS3250_SRC_BIG_ENDIAN (0x1 << 8) +#define EXYNOS3250_MODE_SEL_MASK (0x7 << 5) +#define EXYNOS3250_MODE_SEL_420_2P (0x0 << 5) +#define EXYNOS3250_MODE_SEL_422_1P_LUM_CHR (0x1 << 5) +#define EXYNOS3250_MODE_SEL_RGB565 (0x2 << 5) +#define EXYNOS3250_MODE_SEL_422_1P_CHR_LUM (0x3 << 5) +#define EXYNOS3250_MODE_SEL_ARGB8888 (0x4 << 5) +#define EXYNOS3250_MODE_SEL_420_3P (0x5 << 5) +#define EXYNOS3250_SRC_SWAP_RGB (0x1 << 3) +#define EXYNOS3250_SRC_SWAP_UV (0x1 << 2) +#define EXYNOS3250_MODE_Y16_MASK (0x1 << 1) +#define EXYNOS3250_MODE_Y16 (0x1 << 1) +#define EXYNOS3250_HALF_EN_MASK (0x1 << 0) +#define EXYNOS3250_HALF_EN (0x1 << 0) + +/* Power on/off and clock down control */ +#define EXYNOS3250_JPGCLKCON 0x138 +#define EXYNOS3250_CLK_DOWN_READY (0x1 << 1) +#define EXYNOS3250_POWER_ON (0x1 << 0) + +/* Start compression or decompression */ +#define EXYNOS3250_JSTART 0x13c + +/* Restart decompression after header analysis */ +#define EXYNOS3250_JRSTART 0x140 + +/* JPEG SW reset register */ +#define EXYNOS3250_SW_RESET 0x144 + +/* JPEG timer setting register */ +#define EXYNOS3250_TIMER_SE 0x148 +#define EXYNOS3250_TIMER_INT_EN_SHIFT 31 +#define EXYNOS3250_TIMER_INT_EN (1UL << EXYNOS3250_TIMER_INT_EN_SHIFT) +#define EXYNOS3250_TIMER_INIT_MASK 0x7fffffff + +/* JPEG timer status register */ +#define EXYNOS3250_TIMER_ST 0x14c +#define EXYNOS3250_TIMER_INT_STAT_SHIFT 31 +#define EXYNOS3250_TIMER_INT_STAT (1UL << EXYNOS3250_TIMER_INT_STAT_SHIFT) +#define EXYNOS3250_TIMER_CNT_SHIFT 0 +#define EXYNOS3250_TIMER_CNT_MASK 0x7fffffff + +/* Command status register */ +#define EXYNOS3250_COMSTAT 0x150 +#define EXYNOS3250_CUR_PROC_MODE (0x1 << 1) +#define EXYNOS3250_CUR_COM_MODE (0x1 << 0) + +/* JPEG decompression output format register */ +#define EXYNOS3250_OUTFORM 0x154 +#define EXYNOS3250_OUT_ALPHA_MASK (0xff << 24) +#define EXYNOS3250_OUT_TILE_EN (0x1 << 10) +#define EXYNOS3250_OUT_NV_MASK (0x1 << 9) +#define EXYNOS3250_OUT_NV12 (0x0 << 9) +#define EXYNOS3250_OUT_NV21 (0x1 << 9) +#define EXYNOS3250_OUT_BIG_ENDIAN_MASK (0x1 << 8) +#define EXYNOS3250_OUT_BIG_ENDIAN (0x1 << 8) +#define EXYNOS3250_OUT_SWAP_RGB (0x1 << 7) +#define EXYNOS3250_OUT_SWAP_UV (0x1 << 6) +#define EXYNOS3250_OUT_FMT_MASK (0x7 << 0) +#define EXYNOS3250_OUT_FMT_420_2P (0x0 << 0) +#define EXYNOS3250_OUT_FMT_422_1P_LUM_CHR (0x1 << 0) +#define EXYNOS3250_OUT_FMT_422_1P_CHR_LUM (0x3 << 0) +#define EXYNOS3250_OUT_FMT_420_3P (0x4 << 0) +#define EXYNOS3250_OUT_FMT_RGB565 (0x5 << 0) +#define EXYNOS3250_OUT_FMT_ARGB8888 (0x6 << 0) + +/* Input JPEG stream byte size for decompression */ +#define EXYNOS3250_DEC_STREAM_SIZE 0x158 +#define EXYNOS3250_DEC_STREAM_MASK 0x1fffffff + +/* The upper bound of the byte size of output compressed stream */ +#define EXYNOS3250_ENC_STREAM_BOUND 0x15c +#define EXYNOS3250_ENC_STREAM_BOUND_MASK 0xffffc0 + +/* Scale-down ratio when decoding */ +#define EXYNOS3250_DEC_SCALING_RATIO 0x160 +#define EXYNOS3250_DEC_SCALE_FACTOR_MASK 0x3 +#define EXYNOS3250_DEC_SCALE_FACTOR_8_8 0x0 +#define EXYNOS3250_DEC_SCALE_FACTOR_4_8 0x1 +#define EXYNOS3250_DEC_SCALE_FACTOR_2_8 0x2 +#define EXYNOS3250_DEC_SCALE_FACTOR_1_8 0x3 + +/* Error check */ +#define EXYNOS3250_CRC_RESULT 0x164 + +/* RDMA and WDMA operation status register */ +#define EXYNOS3250_DMA_OPER_STATUS 0x168 +#define EXYNOS3250_WDMA_OPER_STATUS (0x1 << 1) +#define EXYNOS3250_RDMA_OPER_STATUS (0x1 << 0) + +/* DMA issue gathering number and issue number settings */ +#define EXYNOS3250_DMA_ISSUE_NUM 0x16c +#define EXYNOS3250_WDMA_ISSUE_NUM_SHIFT 16 +#define EXYNOS3250_WDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) +#define EXYNOS3250_RDMA_ISSUE_NUM_SHIFT 8 +#define EXYNOS3250_RDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) +#define EXYNOS3250_ISSUE_GATHER_NUM_SHIFT 0 +#define EXYNOS3250_ISSUE_GATHER_NUM_MASK (0x7 << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) +#define EXYNOS3250_DMA_MO_COUNT 0x7 + +/* Version register */ +#define EXYNOS3250_VERSION 0x1fc + +/* RGB <-> YUV conversion coefficients */ +#define EXYNOS3250_JPEG_ENC_COEF1 0x01352e1e +#define EXYNOS3250_JPEG_ENC_COEF2 0x00b0ae83 +#define EXYNOS3250_JPEG_ENC_COEF3 0x020cdc13 + +#define EXYNOS3250_JPEG_DEC_COEF1 0x04a80199 +#define EXYNOS3250_JPEG_DEC_COEF2 0x04a9a064 +#define EXYNOS3250_JPEG_DEC_COEF3 0x04a80102 + +#endif /* JPEG_REGS_H_ */ + -- cgit v1.2.3