--- zzzz-none-000/linux-3.10.107/drivers/media/platform/s5p-jpeg/jpeg-core.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/media/platform/s5p-jpeg/jpeg-core.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,9 +1,10 @@ /* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Andrzej Pietrasiewicz + * Author: Jacek Anaszewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -24,74 +26,371 @@ #include #include #include -#include +#include #include #include "jpeg-core.h" -#include "jpeg-hw.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 formats_enc[] = { +static struct s5p_jpeg_fmt sjpeg_formats[] = { { .name = "JPEG JFIF", .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, + }, + { + .name = "YUV 4:2:2 packed, YCbYCr", + .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, + }, + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, .colplanes = 1, - .types = MEM2MEM_CAPTURE, + .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, }, { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .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, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .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, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .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, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .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, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .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, }, { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .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, }, -}; -#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc) - -static struct s5p_jpeg_fmt formats_dec[] = { { - .name = "YUV 4:2:0 planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .colplanes = 3, - .h_align = 4, - .v_align = 4, - .types = MEM2MEM_CAPTURE, + .name = "RGB565", + .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, }, { - .name = "YUV 4:2:2 packed, YCbYCr", - .fourcc = V4L2_PIX_FMT_YUYV, + .name = "RGB565X", + .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, + }, + { + .name = "RGB565", + .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, + }, + { + .name = "ARGB8888, 32 bpp", + .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, + }, + { + .name = "ARGB8888, 32 bpp", + .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, + }, + { + .name = "YUV 4:4:4 planar, Y/CbCr", + .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, + }, + { + .name = "YUV 4:4:4 planar, Y/CrCb", + .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, + }, + { + .name = "YUV 4:2:2 planar, Y/CrCb", + .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, + }, + { + .name = "YUV 4:2:2 planar, Y/CbCr", + .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, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .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, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .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, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .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, + }, + { + .name = "YUV 4:2:0 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 12, + .colplanes = 2, + .h_align = 3, .v_align = 3, - .types = MEM2MEM_CAPTURE, + .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, }, { - .name = "JPEG JFIF", - .fourcc = V4L2_PIX_FMT_JPEG, + .name = "YUV 4:2:0 planar, Y/CrCb", + .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, + }, + { + .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr", + .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, + }, + { + .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr", + .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, + }, + { + .name = "Gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, .colplanes = 1, - .types = MEM2MEM_OUTPUT, + .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 NUM_FORMATS_DEC ARRAY_SIZE(formats_dec) +#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats) static const unsigned char qtbl_luminance[4][64] = { - {/* level 1 - high 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 + {/*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, @@ -103,38 +402,38 @@ 24, 18, 27, 36, 51, 59, 59, 59, 23, 23, 36, 53, 59, 59, 59, 59 }, - {/* level 3 */ - 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 4 - low 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 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 1 - high 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 + {/*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, @@ -146,25 +445,15 @@ 29, 26, 35, 40, 50, 59, 59, 59, 37, 32, 40, 50, 59, 59, 59, 59 }, - {/* level 3 */ - 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 4 - low 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 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 } }; @@ -202,6 +491,116 @@ 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); @@ -212,8 +611,33 @@ return container_of(fh, struct s5p_jpeg_ctx, fh); } -static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, - unsigned long tab, int len) +static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) +{ + WARN_ON(ctx->subsampling > 3); + + switch (ctx->jpeg->variant->version) { + case SJPEG_S5P: + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; + return ctx->subsampling; + case SJPEG_EXYNOS3250: + case SJPEG_EXYNOS5420: + if (ctx->subsampling > 3) + return V4L2_JPEG_CHROMA_SUBSAMPLING_411; + return exynos3250_decoded_subsampling[ctx->subsampling]; + case SJPEG_EXYNOS4: + case SJPEG_EXYNOS5433: + if (ctx->subsampling > 2) + return V4L2_JPEG_CHROMA_SUBSAMPLING_420; + return exynos4x12_decoded_subsampling[ctx->subsampling]; + default: + 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; @@ -221,22 +645,25 @@ writel((unsigned int)qtbl[i], regs + tab + (i * 0x04)); } -static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality) { /* this driver fills quantisation table 0 with data for luma */ - jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0), - ARRAY_SIZE(qtbl_luminance[quality])); + s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality], + S5P_JPG_QTBL_CONTENT(0), + ARRAY_SIZE(qtbl_luminance[quality])); } -static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality) { /* this driver fills quantisation table 1 with data for chroma */ - jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1), - ARRAY_SIZE(qtbl_chrominance[quality])); + s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality], + S5P_JPG_QTBL_CONTENT(1), + ARRAY_SIZE(qtbl_chrominance[quality])); } -static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl, - unsigned long tab, int len) +static inline void s5p_jpeg_set_htbl(void __iomem *regs, + const unsigned char *htbl, + unsigned long tab, int len) { int i; @@ -244,28 +671,286 @@ writel((unsigned int)htbl[i], regs + tab + (i * 0x04)); } -static inline void jpeg_set_hdctbl(void __iomem *regs) +static inline void s5p_jpeg_set_hdctbl(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0)); + s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), + ARRAY_SIZE(hdctbl0)); } -static inline void jpeg_set_hdctblg(void __iomem *regs) +static inline void s5p_jpeg_set_hdctblg(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0)); + s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), + ARRAY_SIZE(hdctblg0)); } -static inline void jpeg_set_hactbl(void __iomem *regs) +static inline void s5p_jpeg_set_hactbl(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0)); + s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), + ARRAY_SIZE(hactbl0)); } -static inline void jpeg_set_hactblg(void __iomem *regs) +static inline void s5p_jpeg_set_hactblg(void __iomem *regs) { /* this driver fills table 0 for this component */ - jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0)); + 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_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, 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_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, 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_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, 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_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, 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; + } + } } /* @@ -276,8 +961,8 @@ 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(unsigned int mode, - __u32 pixelformat); +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) @@ -285,7 +970,7 @@ 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; + struct s5p_jpeg_fmt *out_fmt, *cap_fmt; int ret = 0; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -306,24 +991,32 @@ ctx->jpeg = jpeg; if (vfd == jpeg->vfd_encoder) { ctx->mode = S5P_JPEG_ENCODE; - out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565); + 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->mode, V4L2_PIX_FMT_JPEG); + 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; } - ret = s5p_jpeg_controls_create(ctx); - if (ret < 0) - goto error; - - ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + 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 = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV); + ctx->cap_q.fmt = cap_fmt; + + ret = s5p_jpeg_controls_create(ctx); + if (ret < 0) + goto error; + mutex_unlock(&jpeg->lock); return 0; @@ -342,49 +1035,23 @@ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); mutex_lock(&jpeg->lock); - v4l2_m2m_ctx_release(ctx->m2m_ctx); - mutex_unlock(&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); - - return 0; -} - -static unsigned int s5p_jpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - unsigned int res; - - mutex_lock(&jpeg->lock); - res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); mutex_unlock(&jpeg->lock); - return res; -} - -static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct s5p_jpeg *jpeg = video_drvdata(file); - struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); - int ret; - if (mutex_lock_interruptible(&jpeg->lock)) - return -ERESTARTSYS; - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&jpeg->lock); - return ret; + return 0; } static const struct v4l2_file_operations s5p_jpeg_fops = { .owner = THIS_MODULE, .open = s5p_jpeg_open, .release = s5p_jpeg_release, - .poll = s5p_jpeg_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = s5p_jpeg_mmap, + .mmap = v4l2_m2m_fop_mmap, }; /* @@ -427,10 +1094,14 @@ } static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, - unsigned long buffer, unsigned long size) + unsigned long buffer, unsigned long size, + struct s5p_jpeg_ctx *ctx) { - int c, components, notfound; - unsigned int height, width, word; + int c, components = 0, notfound, n_dht = 0, n_dqt = 0; + unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0, + sof_len = 0; + unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER], + dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER]; long length; struct s5p_jpeg_buffer jpeg_buffer; @@ -439,17 +1110,17 @@ jpeg_buffer.curr = 0; notfound = 1; - while (notfound) { + while (notfound || !sos) { c = get_byte(&jpeg_buffer); if (c == -1) - break; + return false; if (c != 0xff) continue; do c = get_byte(&jpeg_buffer); while (c == 0xff); if (c == -1) - break; + return false; if (c == 0) continue; length = 0; @@ -458,6 +1129,11 @@ case 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)) @@ -467,9 +1143,48 @@ 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; - skip(&jpeg_buffer, components * 3); + case 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 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 SOS: + sos = jpeg_buffer.curr - 2; /* 0xffda */ break; /* skip payload-less markers */ @@ -490,8 +1205,39 @@ } result->w = width; result->h = height; - result->size = components; - return !notfound; + 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; + result->size = result->components = components; + + 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; + default: + return false; + } + + return !notfound && sos; } static int s5p_jpeg_querycap(struct file *file, void *priv, @@ -511,23 +1257,18 @@ sizeof(cap->card)); } cap->bus_info[0] = 0; - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, +static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, struct v4l2_fmtdesc *f, u32 type) { int i, num = 0; for (i = 0; i < n; ++i) { - if (formats[i].types & type) { + if (sjpeg_formats[i].flags & type) { /* index-th format of type type found ? */ if (num == f->index) break; @@ -541,8 +1282,8 @@ if (i >= n) return -EINVAL; - strlcpy(f->description, formats[i].name, sizeof(f->description)); - f->pixelformat = formats[i].fourcc; + strlcpy(f->description, sjpeg_formats[i].name, sizeof(f->description)); + f->pixelformat = sjpeg_formats[i].fourcc; return 0; } @@ -553,10 +1294,11 @@ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, - MEM2MEM_CAPTURE); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_CAPTURE); - return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_CAPTURE); } static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, @@ -565,10 +1307,11 @@ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, - MEM2MEM_OUTPUT); + return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_ENC_OUTPUT); - return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT); + return enum_fmt(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, @@ -589,7 +1332,7 @@ struct v4l2_pix_format *pix = &f->fmt.pix; struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); - vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -615,32 +1358,34 @@ return 0; } -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, - u32 pixelformat) +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, + u32 pixelformat, unsigned int fmt_type) { - unsigned int k; - struct s5p_jpeg_fmt *formats; - int n; - - if (mode == S5P_JPEG_ENCODE) { - formats = formats_enc; - n = NUM_FORMATS_ENC; - } else { - formats = formats_dec; - n = NUM_FORMATS_DEC; - } + unsigned int k, fmt_flag; - for (k = 0; k < n; k++) { - struct s5p_jpeg_fmt *fmt = &formats[k]; - if (fmt->fourcc == pixelformat) + 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(u32 *w, unsigned int wmin, unsigned int wmax, +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) @@ -652,13 +1397,27 @@ 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, @@ -673,13 +1432,13 @@ /* V4L2 specification suggests the driver corrects the format struct * if any of the dimensions is unsupported */ - if (q_type == MEM2MEM_OUTPUT) - jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH, + 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(&pix->width, S5P_JPEG_MIN_WIDTH, + 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); @@ -695,7 +1454,7 @@ bpl = pix->width; /* planar */ if (fmt->colplanes == 1 && /* packed */ - (bpl << 3) * fmt->depth < pix->width) + (bpl << 3) / fmt->depth < pix->width) bpl = (pix->width * fmt->depth) >> 3; pix->bytesperline = bpl; @@ -709,17 +1468,59 @@ 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->mode, f->fmt.pix.pixelformat); - if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { + 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; } - return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE); + 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, @@ -728,24 +1529,57 @@ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); struct s5p_jpeg_fmt *fmt; - fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); - if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { + 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, MEM2MEM_OUTPUT); + 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; + + if (pix_fmt == V4L2_PIX_FMT_RGB32 || + 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); + + return w * h * fmt_depth >> 3; } +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->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -757,13 +1591,66 @@ return -EBUSY; } - q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat); + 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); q_data->w = pix->width; q_data->h = pix->height; - if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) - q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; - else + 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; } @@ -792,53 +1679,101 @@ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } -static int s5p_jpeg_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) +static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) { - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + int w_ratio, h_ratio, scale_factor, cur_ratio, i; - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} + w_ratio = ctx->out_q.w / r->width; + h_ratio = ctx->out_q.h / r->height; -static int s5p_jpeg_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + 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; + } + } - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} + r->width = round_down(ctx->out_q.w / ctx->scale_factor, 2); + r->height = round_down(ctx->out_q.h / ctx->scale_factor, 2); -static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + ctx->crop_rect.width = r->width; + ctx->crop_rect.height = r->height; + ctx->crop_rect.left = 0; + ctx->crop_rect.top = 0; - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); + ctx->crop_altered = true; + + return 0; } -static int s5p_jpeg_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) { - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); + return 1; } -static int s5p_jpeg_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) +static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) { - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + struct v4l2_rect base_rect; + int w_step, h_step; - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} + 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; + } -static int s5p_jpeg_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); + 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 (!enclosed_rectangle(r, &base_rect)) + return -EINVAL; - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); + 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) { @@ -853,27 +1788,53 @@ case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE: 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->cap_q.w; - s->r.height = ctx->cap_q.h; + 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; } - s->r.left = 0; - s->r.top = 0; 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) { @@ -884,12 +1845,7 @@ switch (ctrl->id) { case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: spin_lock_irqsave(&jpeg->slock, flags); - - WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY); - if (ctx->subsampling > 2) - ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; - else - ctrl->val = ctx->subsampling; + ctrl->val = s5p_jpeg_to_user_subsampling(ctx); spin_unlock_irqrestore(&jpeg->slock, flags); break; } @@ -897,6 +1853,58 @@ 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); @@ -906,7 +1914,7 @@ switch (ctrl->id) { case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val; + ctx->compr_quality = ctrl->val; break; case V4L2_CID_JPEG_RESTART_INTERVAL: ctx->restart_interval = ctrl->val; @@ -922,6 +1930,7 @@ 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, }; @@ -929,18 +1938,20 @@ { 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, 3); + 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, 3, 0xffff, 0); - mask = ~0x06; /* 422, 420 */ + if (ctx->jpeg->variant->version == SJPEG_S5P) + mask = ~0x06; /* 422, 420 */ } ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, @@ -948,13 +1959,24 @@ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask, V4L2_JPEG_CHROMA_SUBSAMPLING_422); - if (ctx->ctrl_handler.error) - return ctx->ctrl_handler.error; + 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; - return 0; + + 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 = { @@ -972,16 +1994,16 @@ .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap, .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out, - .vidioc_reqbufs = s5p_jpeg_reqbufs, - .vidioc_querybuf = s5p_jpeg_querybuf, - - .vidioc_qbuf = s5p_jpeg_qbuf, - .vidioc_dqbuf = s5p_jpeg_dqbuf, + .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 = s5p_jpeg_streamon, - .vidioc_streamoff = s5p_jpeg_streamoff, + .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, }; /* @@ -995,74 +2017,379 @@ struct s5p_jpeg_ctx *ctx = priv; struct s5p_jpeg *jpeg = ctx->jpeg; struct vb2_buffer *src_buf, *dst_buf; - unsigned long src_addr, dst_addr; + unsigned long src_addr, dst_addr, flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + 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, 0); dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - jpeg_reset(jpeg->regs); - jpeg_poweron(jpeg->regs); - jpeg_proc_mode(jpeg->regs, ctx->mode); + 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) - jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565); + s5p_jpeg_input_raw_mode(jpeg->regs, + S5P_JPEG_RAW_IN_565); else - jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422); - jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); - jpeg_dri(jpeg->regs, ctx->restart_interval); - jpeg_x(jpeg->regs, ctx->out_q.w); - jpeg_y(jpeg->regs, ctx->out_q.h); - jpeg_imgadr(jpeg->regs, src_addr); - jpeg_jpgadr(jpeg->regs, dst_addr); + 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 */ - jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); + s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); /* JPEG RGB to YCbCr conversion matrix */ - jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); - jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); - jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); - jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); - jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); - jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); - jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); - jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); - jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); + 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 */ - jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); - jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + 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 */ - jpeg_qtbl(jpeg->regs, 1, 0); + s5p_jpeg_qtbl(jpeg->regs, 1, 0); /* use table 1 for Cb and Cr*/ - jpeg_qtbl(jpeg->regs, 2, 1); - jpeg_qtbl(jpeg->regs, 3, 1); + s5p_jpeg_qtbl(jpeg->regs, 2, 1); + s5p_jpeg_qtbl(jpeg->regs, 3, 1); /* Y, Cb, Cr use Huffman table 0 */ - jpeg_htbl_ac(jpeg->regs, 1); - jpeg_htbl_dc(jpeg->regs, 1); - jpeg_htbl_ac(jpeg->regs, 2); - jpeg_htbl_dc(jpeg->regs, 2); - jpeg_htbl_ac(jpeg->regs, 3); - jpeg_htbl_dc(jpeg->regs, 3); + 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 */ - jpeg_rst_int_enable(jpeg->regs, true); - jpeg_data_num_int_enable(jpeg->regs, true); - jpeg_final_mcu_num_int_enable(jpeg->regs, true); + 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) - jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); + 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_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, 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_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, 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_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_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, 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_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); - jpeg_jpgadr(jpeg->regs, src_addr); - jpeg_imgadr(jpeg->regs, dst_addr); + jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; } - jpeg_start(jpeg->regs); + 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_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, 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) @@ -1084,6 +2411,18 @@ .job_abort = s5p_jpeg_job_abort, }; +static struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { + .device_run = exynos3250_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, + .job_abort = s5p_jpeg_job_abort, +}; + +static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { + .device_run = exynos4_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, + .job_abort = s5p_jpeg_job_abort, +}; + /* * ============================================================================ * Queue operations @@ -1091,7 +2430,7 @@ */ static int s5p_jpeg_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -1141,6 +2480,7 @@ 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 && @@ -1149,7 +2489,7 @@ ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp, (unsigned long)vb2_plane_vaddr(vb, 0), min((unsigned long)ctx->out_q.size, - vb2_get_plane_payload(vb, 0))); + vb2_get_plane_payload(vb, 0)), ctx); if (!ctx->hdr_parsed) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); return; @@ -1158,34 +2498,24 @@ q_data = &ctx->out_q; q_data->w = tmp.w; q_data->h = tmp.h; + q_data->sos = tmp.sos; + memcpy(q_data->dht.marker, tmp.dht.marker, + sizeof(tmp.dht.marker)); + memcpy(q_data->dht.len, tmp.dht.len, sizeof(tmp.dht.len)); + q_data->dht.n = tmp.dht.n; + memcpy(q_data->dqt.marker, tmp.dqt.marker, + sizeof(tmp.dqt.marker)); + memcpy(q_data->dqt.len, tmp.dqt.len, sizeof(tmp.dqt.len)); + q_data->dqt.n = tmp.dqt.n; + q_data->sof = tmp.sof; + q_data->sof_len = tmp.sof_len; q_data = &ctx->cap_q; q_data->w = tmp.w; q_data->h = tmp.h; - - jpeg_bound_align_image(&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; } - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void s5p_jpeg_wait_prepare(struct vb2_queue *vq) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->jpeg->lock); -} - -static void s5p_jpeg_wait_finish(struct vb2_queue *vq) -{ - struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_lock(&ctx->jpeg->lock); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) @@ -1198,21 +2528,19 @@ return ret > 0 ? 0 : ret; } -static int s5p_jpeg_stop_streaming(struct vb2_queue *q) +static void s5p_jpeg_stop_streaming(struct vb2_queue *q) { struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); pm_runtime_put(ctx->jpeg->dev); - - return 0; } static 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 = s5p_jpeg_wait_prepare, - .wait_finish = s5p_jpeg_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = s5p_jpeg_start_streaming, .stop_streaming = s5p_jpeg_stop_streaming, }; @@ -1229,7 +2557,8 @@ 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_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->jpeg->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -1241,7 +2570,8 @@ 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_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->jpeg->lock; return vb2_queue_init(dst_vq); } @@ -1256,7 +2586,7 @@ { struct s5p_jpeg *jpeg = dev_id; struct s5p_jpeg_ctx *curr_ctx; - struct vb2_buffer *src_buf, *dst_buf; + 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; @@ -1267,45 +2597,178 @@ curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + 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 = jpeg_enc_stream_stat(jpeg->regs); - timer_elapsed = jpeg_timer_stat(jpeg->regs); - op_completed = jpeg_result_stat_ok(jpeg->regs); + 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 && jpeg_stream_stat_ok(jpeg->regs); + op_completed = op_completed && + s5p_jpeg_stream_stat_ok(jpeg->regs); if (enc_jpeg_too_large) { state = VB2_BUF_STATE_ERROR; - jpeg_clear_enc_stream_stat(jpeg->regs); + s5p_jpeg_clear_enc_stream_stat(jpeg->regs); } else if (timer_elapsed) { state = VB2_BUF_STATE_ERROR; - jpeg_clear_timer_stat(jpeg->regs); + s5p_jpeg_clear_timer_stat(jpeg->regs); } else if (!op_completed) { state = VB2_BUF_STATE_ERROR; } else { - payload_size = jpeg_compressed_size(jpeg->regs); + payload_size = s5p_jpeg_compressed_size(jpeg->regs); } - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; + dst_buf->timestamp = src_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, 0, payload_size); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size); v4l2_m2m_buf_done(dst_buf, state); - v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx); + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs); + curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs); spin_unlock(&jpeg->slock); - jpeg_clear_int(jpeg->regs); + s5p_jpeg_clear_int(jpeg->regs); 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); + + 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); + } + + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + if (jpeg->variant->version == SJPEG_EXYNOS4) + curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); + + spin_unlock(&jpeg->slock); + 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; + 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; + + 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) + 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->timestamp = src_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); + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + + curr_ctx->subsampling = + exynos3250_jpeg_get_subsampling_mode(jpeg->regs); +exit_unlock: + spin_unlock(&jpeg->slock); + return IRQ_HANDLED; +} + +static void *jpeg_get_drv_data(struct device *dev); + /* * ============================================================================ * Driver basic infrastructure @@ -1316,13 +2779,15 @@ { struct s5p_jpeg *jpeg; struct resource *res; - int ret; + 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); + mutex_init(&jpeg->lock); spin_lock_init(&jpeg->slock); jpeg->dev = &pdev->dev; @@ -1341,32 +2806,33 @@ return ret; } - ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0, - dev_name(&pdev->dev), jpeg); + 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 */ - jpeg->clk = clk_get(&pdev->dev, "jpeg"); - if (IS_ERR(jpeg->clk)) { - dev_err(&pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(jpeg->clk); - return ret; + 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]); + } } - dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); - clk_prepare_enable(jpeg->clk); /* v4l2 device */ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - goto clk_get_rollback; + return ret; } /* mem2mem device */ - jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops); + 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); @@ -1387,8 +2853,8 @@ ret = -ENOMEM; goto vb2_allocator_rollback; } - strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME, - sizeof(jpeg->vfd_encoder->name)); + 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; @@ -1400,7 +2866,8 @@ ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto enc_vdev_alloc_rollback; + video_device_release(jpeg->vfd_encoder); + goto vb2_allocator_rollback; } video_set_drvdata(jpeg->vfd_encoder, jpeg); @@ -1415,19 +2882,21 @@ ret = -ENOMEM; goto enc_vdev_register_rollback; } - strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME, - sizeof(jpeg->vfd_decoder->name)); + 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; ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto dec_vdev_alloc_rollback; + video_device_release(jpeg->vfd_decoder); + goto enc_vdev_register_rollback; } video_set_drvdata(jpeg->vfd_decoder, jpeg); @@ -1444,15 +2913,9 @@ return 0; -dec_vdev_alloc_rollback: - video_device_release(jpeg->vfd_decoder); - enc_vdev_register_rollback: video_unregister_device(jpeg->vfd_encoder); -enc_vdev_alloc_rollback: - video_device_release(jpeg->vfd_encoder); - vb2_allocator_rollback: vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); @@ -1462,70 +2925,209 @@ device_register_rollback: v4l2_device_unregister(&jpeg->v4l2_dev); -clk_get_rollback: - clk_disable_unprepare(jpeg->clk); - clk_put(jpeg->clk); - 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_device_release(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); - video_device_release(jpeg->vfd_encoder); vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); - clk_disable_unprepare(jpeg->clk); - clk_put(jpeg->clk); + 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 + * 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. */ - jpeg_set_hdctbl(jpeg->regs); - jpeg_set_hdctblg(jpeg->regs); - jpeg_set_hactbl(jpeg->regs); - jpeg_set_hactblg(jpeg->regs); + 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 */ + +#ifdef CONFIG_PM_SLEEP +static int s5p_jpeg_suspend(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return s5p_jpeg_runtime_suspend(dev); +} + +static int s5p_jpeg_resume(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return s5p_jpeg_runtime_resume(dev); +} +#endif static const struct dev_pm_ops s5p_jpeg_pm_ops = { - .runtime_suspend = s5p_jpeg_runtime_suspend, - .runtime_resume = s5p_jpeg_runtime_resume, + SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_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 = { - .owner = THIS_MODULE, - .name = S5P_JPEG_M2M_NAME, - .pm = &s5p_jpeg_pm_ops, + .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"); -