162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Coda multi-standard codec IP - JPEG support functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Philipp Zabel, Pengutronix 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/unaligned.h> 962306a36Sopenharmony_ci#include <linux/irqreturn.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/ktime.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/swab.h> 1462306a36Sopenharmony_ci#include <linux/videodev2.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <media/v4l2-common.h> 1762306a36Sopenharmony_ci#include <media/v4l2-fh.h> 1862306a36Sopenharmony_ci#include <media/v4l2-jpeg.h> 1962306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 2062306a36Sopenharmony_ci#include <media/videobuf2-core.h> 2162306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "coda.h" 2462306a36Sopenharmony_ci#include "trace.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SOI_MARKER 0xffd8 2762306a36Sopenharmony_ci#define APP9_MARKER 0xffe9 2862306a36Sopenharmony_ci#define DRI_MARKER 0xffdd 2962306a36Sopenharmony_ci#define DQT_MARKER 0xffdb 3062306a36Sopenharmony_ci#define DHT_MARKER 0xffc4 3162306a36Sopenharmony_ci#define SOF_MARKER 0xffc0 3262306a36Sopenharmony_ci#define SOS_MARKER 0xffda 3362306a36Sopenharmony_ci#define EOI_MARKER 0xffd9 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum { 3662306a36Sopenharmony_ci CODA9_JPEG_FORMAT_420, 3762306a36Sopenharmony_ci CODA9_JPEG_FORMAT_422, 3862306a36Sopenharmony_ci CODA9_JPEG_FORMAT_224, 3962306a36Sopenharmony_ci CODA9_JPEG_FORMAT_444, 4062306a36Sopenharmony_ci CODA9_JPEG_FORMAT_400, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct coda_huff_tab { 4462306a36Sopenharmony_ci u8 luma_dc[16 + 12]; 4562306a36Sopenharmony_ci u8 chroma_dc[16 + 12]; 4662306a36Sopenharmony_ci u8 luma_ac[16 + 162]; 4762306a36Sopenharmony_ci u8 chroma_ac[16 + 162]; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* DC Luma, DC Chroma, AC Luma, AC Chroma */ 5062306a36Sopenharmony_ci s16 min[4 * 16]; 5162306a36Sopenharmony_ci s16 max[4 * 16]; 5262306a36Sopenharmony_ci s8 ptr[4 * 16]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define CODA9_JPEG_ENC_HUFF_DATA_SIZE (256 + 256 + 16 + 16) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * Typical Huffman tables for 8-bit precision luminance and 5962306a36Sopenharmony_ci * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const unsigned char luma_dc[16 + 12] = { 6362306a36Sopenharmony_ci /* bits */ 6462306a36Sopenharmony_ci 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 6562306a36Sopenharmony_ci 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6662306a36Sopenharmony_ci /* values */ 6762306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 6862306a36Sopenharmony_ci 0x08, 0x09, 0x0a, 0x0b, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic const unsigned char chroma_dc[16 + 12] = { 7262306a36Sopenharmony_ci /* bits */ 7362306a36Sopenharmony_ci 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 7462306a36Sopenharmony_ci 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 7562306a36Sopenharmony_ci /* values */ 7662306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 7762306a36Sopenharmony_ci 0x08, 0x09, 0x0a, 0x0b, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic const unsigned char luma_ac[16 + 162 + 2] = { 8162306a36Sopenharmony_ci /* bits */ 8262306a36Sopenharmony_ci 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 8362306a36Sopenharmony_ci 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 8462306a36Sopenharmony_ci /* values */ 8562306a36Sopenharmony_ci 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 8662306a36Sopenharmony_ci 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 8762306a36Sopenharmony_ci 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 8862306a36Sopenharmony_ci 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 8962306a36Sopenharmony_ci 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 9062306a36Sopenharmony_ci 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 9162306a36Sopenharmony_ci 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 9262306a36Sopenharmony_ci 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 9362306a36Sopenharmony_ci 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 9462306a36Sopenharmony_ci 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 9562306a36Sopenharmony_ci 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 9662306a36Sopenharmony_ci 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 9762306a36Sopenharmony_ci 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 9862306a36Sopenharmony_ci 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 9962306a36Sopenharmony_ci 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 10062306a36Sopenharmony_ci 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 10162306a36Sopenharmony_ci 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 10262306a36Sopenharmony_ci 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 10362306a36Sopenharmony_ci 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 10462306a36Sopenharmony_ci 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 10562306a36Sopenharmony_ci 0xf9, 0xfa, /* padded to 32-bit */ 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const unsigned char chroma_ac[16 + 162 + 2] = { 10962306a36Sopenharmony_ci /* bits */ 11062306a36Sopenharmony_ci 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 11162306a36Sopenharmony_ci 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 11262306a36Sopenharmony_ci /* values */ 11362306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 11462306a36Sopenharmony_ci 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 11562306a36Sopenharmony_ci 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 11662306a36Sopenharmony_ci 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 11762306a36Sopenharmony_ci 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 11862306a36Sopenharmony_ci 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 11962306a36Sopenharmony_ci 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 12062306a36Sopenharmony_ci 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 12162306a36Sopenharmony_ci 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 12262306a36Sopenharmony_ci 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 12362306a36Sopenharmony_ci 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 12462306a36Sopenharmony_ci 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 12562306a36Sopenharmony_ci 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 12662306a36Sopenharmony_ci 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 12762306a36Sopenharmony_ci 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 12862306a36Sopenharmony_ci 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 12962306a36Sopenharmony_ci 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 13062306a36Sopenharmony_ci 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 13162306a36Sopenharmony_ci 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 13262306a36Sopenharmony_ci 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 13362306a36Sopenharmony_ci 0xf9, 0xfa, /* padded to 32-bit */ 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * Quantization tables for luminance and chrominance components in 13862306a36Sopenharmony_ci * zig-zag scan order from the Freescale i.MX VPU libraries 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic unsigned char luma_q[64] = { 14262306a36Sopenharmony_ci 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05, 14362306a36Sopenharmony_ci 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b, 14462306a36Sopenharmony_ci 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a, 14562306a36Sopenharmony_ci 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 14662306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c, 14762306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 14862306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 14962306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic unsigned char chroma_q[64] = { 15362306a36Sopenharmony_ci 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10, 15462306a36Sopenharmony_ci 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 15562306a36Sopenharmony_ci 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 15662306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 15762306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 15862306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 15962306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 16062306a36Sopenharmony_ci 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic const unsigned char width_align[] = { 16462306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_420] = 16, 16562306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_422] = 16, 16662306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_224] = 8, 16762306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_444] = 8, 16862306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_400] = 8, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const unsigned char height_align[] = { 17262306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_420] = 16, 17362306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_422] = 8, 17462306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_224] = 16, 17562306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_444] = 8, 17662306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_400] = 8, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int coda9_jpeg_chroma_format(u32 pixfmt) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci switch (pixfmt) { 18262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 18362306a36Sopenharmony_ci case V4L2_PIX_FMT_NV12: 18462306a36Sopenharmony_ci return CODA9_JPEG_FORMAT_420; 18562306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 18662306a36Sopenharmony_ci return CODA9_JPEG_FORMAT_422; 18762306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV444: 18862306a36Sopenharmony_ci return CODA9_JPEG_FORMAT_444; 18962306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 19062306a36Sopenharmony_ci return CODA9_JPEG_FORMAT_400; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistruct coda_memcpy_desc { 19662306a36Sopenharmony_ci int offset; 19762306a36Sopenharmony_ci const void *src; 19862306a36Sopenharmony_ci size_t len; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void coda_memcpy_parabuf(void *parabuf, 20262306a36Sopenharmony_ci const struct coda_memcpy_desc *desc) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci u32 *dst = parabuf + desc->offset; 20562306a36Sopenharmony_ci const u32 *src = desc->src; 20662306a36Sopenharmony_ci int len = desc->len / 4; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = 0; i < len; i += 2) { 21062306a36Sopenharmony_ci dst[i + 1] = swab32(src[i]); 21162306a36Sopenharmony_ci dst[i] = swab32(src[i + 1]); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciint coda_jpeg_write_tables(struct coda_ctx *ctx) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci int i; 21862306a36Sopenharmony_ci static const struct coda_memcpy_desc huff[8] = { 21962306a36Sopenharmony_ci { 0, luma_dc, sizeof(luma_dc) }, 22062306a36Sopenharmony_ci { 32, luma_ac, sizeof(luma_ac) }, 22162306a36Sopenharmony_ci { 216, chroma_dc, sizeof(chroma_dc) }, 22262306a36Sopenharmony_ci { 248, chroma_ac, sizeof(chroma_ac) }, 22362306a36Sopenharmony_ci }; 22462306a36Sopenharmony_ci struct coda_memcpy_desc qmat[3] = { 22562306a36Sopenharmony_ci { 512, ctx->params.jpeg_qmat_tab[0], 64 }, 22662306a36Sopenharmony_ci { 576, ctx->params.jpeg_qmat_tab[1], 64 }, 22762306a36Sopenharmony_ci { 640, ctx->params.jpeg_qmat_tab[1], 64 }, 22862306a36Sopenharmony_ci }; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Write huffman tables to parameter memory */ 23162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(huff); i++) 23262306a36Sopenharmony_ci coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Write Q-matrix to parameter memory */ 23562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qmat); i++) 23662306a36Sopenharmony_ci coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cibool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci void *vaddr = vb2_plane_vaddr(vb, 0); 24462306a36Sopenharmony_ci u16 soi, eoi; 24562306a36Sopenharmony_ci int len, i; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci soi = be16_to_cpup((__be16 *)vaddr); 24862306a36Sopenharmony_ci if (soi != SOI_MARKER) 24962306a36Sopenharmony_ci return false; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci len = vb2_get_plane_payload(vb, 0); 25262306a36Sopenharmony_ci vaddr += len - 2; 25362306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 25462306a36Sopenharmony_ci eoi = be16_to_cpup((__be16 *)(vaddr - i)); 25562306a36Sopenharmony_ci if (eoi == EOI_MARKER) { 25662306a36Sopenharmony_ci if (i > 0) 25762306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, len - i); 25862306a36Sopenharmony_ci return true; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return false; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciint coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 27062306a36Sopenharmony_ci u8 *buf = vb2_plane_vaddr(vb, 0); 27162306a36Sopenharmony_ci size_t len = vb2_get_plane_payload(vb, 0); 27262306a36Sopenharmony_ci struct v4l2_jpeg_scan_header scan_header; 27362306a36Sopenharmony_ci struct v4l2_jpeg_reference quantization_tables[4] = { }; 27462306a36Sopenharmony_ci struct v4l2_jpeg_reference huffman_tables[4] = { }; 27562306a36Sopenharmony_ci struct v4l2_jpeg_header header = { 27662306a36Sopenharmony_ci .scan = &scan_header, 27762306a36Sopenharmony_ci .quantization_tables = quantization_tables, 27862306a36Sopenharmony_ci .huffman_tables = huffman_tables, 27962306a36Sopenharmony_ci }; 28062306a36Sopenharmony_ci struct coda_q_data *q_data_src; 28162306a36Sopenharmony_ci struct coda_huff_tab *huff_tab; 28262306a36Sopenharmony_ci int i, j, ret; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = v4l2_jpeg_parse_header(buf, len, &header); 28562306a36Sopenharmony_ci if (ret < 0) { 28662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "failed to parse JPEG header: %pe\n", 28762306a36Sopenharmony_ci ERR_PTR(ret)); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ctx->params.jpeg_restart_interval = header.restart_interval; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* check frame header */ 29462306a36Sopenharmony_ci if (header.frame.height > ctx->codec->max_h || 29562306a36Sopenharmony_ci header.frame.width > ctx->codec->max_w) { 29662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "invalid dimensions: %dx%d\n", 29762306a36Sopenharmony_ci header.frame.width, header.frame.height); 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 30262306a36Sopenharmony_ci if (header.frame.height != q_data_src->height || 30362306a36Sopenharmony_ci header.frame.width != q_data_src->width) { 30462306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 30562306a36Sopenharmony_ci "dimensions don't match format: %dx%d\n", 30662306a36Sopenharmony_ci header.frame.width, header.frame.height); 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (header.frame.num_components != 3) { 31162306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 31262306a36Sopenharmony_ci "unsupported number of components: %d\n", 31362306a36Sopenharmony_ci header.frame.num_components); 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* install quantization tables */ 31862306a36Sopenharmony_ci if (quantization_tables[3].start) { 31962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 32062306a36Sopenharmony_ci "only 3 quantization tables supported\n"); 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 32462306a36Sopenharmony_ci if (!quantization_tables[i].start) 32562306a36Sopenharmony_ci continue; 32662306a36Sopenharmony_ci if (quantization_tables[i].length != 64) { 32762306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 32862306a36Sopenharmony_ci "only 8-bit quantization tables supported\n"); 32962306a36Sopenharmony_ci continue; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[i]) { 33262306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); 33362306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[i]) 33462306a36Sopenharmony_ci return -ENOMEM; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci memcpy(ctx->params.jpeg_qmat_tab[i], 33762306a36Sopenharmony_ci quantization_tables[i].start, 64); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* install Huffman tables */ 34162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 34262306a36Sopenharmony_ci if (!huffman_tables[i].start) { 34362306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "missing Huffman table\n"); 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci /* AC tables should be between 17 -> 178, DC between 17 -> 28 */ 34762306a36Sopenharmony_ci if (huffman_tables[i].length < 17 || 34862306a36Sopenharmony_ci huffman_tables[i].length > 178 || 34962306a36Sopenharmony_ci ((i & 2) == 0 && huffman_tables[i].length > 28)) { 35062306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 35162306a36Sopenharmony_ci "invalid Huffman table %d length: %zu\n", 35262306a36Sopenharmony_ci i, huffman_tables[i].length); 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci huff_tab = ctx->params.jpeg_huff_tab; 35762306a36Sopenharmony_ci if (!huff_tab) { 35862306a36Sopenharmony_ci huff_tab = kzalloc(sizeof(struct coda_huff_tab), GFP_KERNEL); 35962306a36Sopenharmony_ci if (!huff_tab) 36062306a36Sopenharmony_ci return -ENOMEM; 36162306a36Sopenharmony_ci ctx->params.jpeg_huff_tab = huff_tab; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci memset(huff_tab, 0, sizeof(*huff_tab)); 36562306a36Sopenharmony_ci memcpy(huff_tab->luma_dc, huffman_tables[0].start, huffman_tables[0].length); 36662306a36Sopenharmony_ci memcpy(huff_tab->chroma_dc, huffman_tables[1].start, huffman_tables[1].length); 36762306a36Sopenharmony_ci memcpy(huff_tab->luma_ac, huffman_tables[2].start, huffman_tables[2].length); 36862306a36Sopenharmony_ci memcpy(huff_tab->chroma_ac, huffman_tables[3].start, huffman_tables[3].length); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* check scan header */ 37162306a36Sopenharmony_ci for (i = 0; i < scan_header.num_components; i++) { 37262306a36Sopenharmony_ci struct v4l2_jpeg_scan_component_spec *scan_component; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci scan_component = &scan_header.component[i]; 37562306a36Sopenharmony_ci for (j = 0; j < header.frame.num_components; j++) { 37662306a36Sopenharmony_ci if (header.frame.component[j].component_identifier == 37762306a36Sopenharmony_ci scan_component->component_selector) 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci if (j == header.frame.num_components) 38162306a36Sopenharmony_ci continue; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ctx->params.jpeg_huff_dc_index[j] = 38462306a36Sopenharmony_ci scan_component->dc_entropy_coding_table_selector; 38562306a36Sopenharmony_ci ctx->params.jpeg_huff_ac_index[j] = 38662306a36Sopenharmony_ci scan_component->ac_entropy_coding_table_selector; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Generate Huffman table information */ 39062306a36Sopenharmony_ci for (i = 0; i < 4; i++) 39162306a36Sopenharmony_ci coda9_jpeg_gen_dec_huff_tab(ctx, i); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* start of entropy coded segment */ 39462306a36Sopenharmony_ci ctx->jpeg_ecs_offset = header.ecs_offset; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci switch (header.frame.subsampling) { 39762306a36Sopenharmony_ci case V4L2_JPEG_CHROMA_SUBSAMPLING_420: 39862306a36Sopenharmony_ci case V4L2_JPEG_CHROMA_SUBSAMPLING_422: 39962306a36Sopenharmony_ci ctx->params.jpeg_chroma_subsampling = header.frame.subsampling; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci default: 40262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "chroma subsampling not supported: %d", 40362306a36Sopenharmony_ci header.frame.subsampling); 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits, 41162306a36Sopenharmony_ci int num_values) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci s8 *values = (s8 *)(bits + 16); 41462306a36Sopenharmony_ci int huff_length, i; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci for (huff_length = 0, i = 0; i < 16; i++) 41762306a36Sopenharmony_ci huff_length += bits[i]; 41862306a36Sopenharmony_ci for (i = huff_length; i < num_values; i++) 41962306a36Sopenharmony_ci values[i] = -1; 42062306a36Sopenharmony_ci for (i = 0; i < num_values; i++) 42162306a36Sopenharmony_ci coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; 42762306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 42862306a36Sopenharmony_ci s16 *huff_min = huff_tab->min; 42962306a36Sopenharmony_ci s16 *huff_max = huff_tab->max; 43062306a36Sopenharmony_ci s8 *huff_ptr = huff_tab->ptr; 43162306a36Sopenharmony_ci int i; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* MIN Tables */ 43462306a36Sopenharmony_ci coda_write(dev, 0x003, CODA9_REG_JPEG_HUFF_CTRL); 43562306a36Sopenharmony_ci coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_ADDR); 43662306a36Sopenharmony_ci for (i = 0; i < 4 * 16; i++) 43762306a36Sopenharmony_ci coda_write(dev, (s32)huff_min[i], CODA9_REG_JPEG_HUFF_DATA); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* MAX Tables */ 44062306a36Sopenharmony_ci coda_write(dev, 0x403, CODA9_REG_JPEG_HUFF_CTRL); 44162306a36Sopenharmony_ci coda_write(dev, 0x440, CODA9_REG_JPEG_HUFF_ADDR); 44262306a36Sopenharmony_ci for (i = 0; i < 4 * 16; i++) 44362306a36Sopenharmony_ci coda_write(dev, (s32)huff_max[i], CODA9_REG_JPEG_HUFF_DATA); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* PTR Tables */ 44662306a36Sopenharmony_ci coda_write(dev, 0x803, CODA9_REG_JPEG_HUFF_CTRL); 44762306a36Sopenharmony_ci coda_write(dev, 0x880, CODA9_REG_JPEG_HUFF_ADDR); 44862306a36Sopenharmony_ci for (i = 0; i < 4 * 16; i++) 44962306a36Sopenharmony_ci coda_write(dev, (s32)huff_ptr[i], CODA9_REG_JPEG_HUFF_DATA); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* VAL Tables: DC Luma, DC Chroma, AC Luma, AC Chroma */ 45262306a36Sopenharmony_ci coda_write(dev, 0xc03, CODA9_REG_JPEG_HUFF_CTRL); 45362306a36Sopenharmony_ci coda9_jpeg_write_huff_values(dev, huff_tab->luma_dc, 12); 45462306a36Sopenharmony_ci coda9_jpeg_write_huff_values(dev, huff_tab->chroma_dc, 12); 45562306a36Sopenharmony_ci coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162); 45662306a36Sopenharmony_ci coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162); 45762306a36Sopenharmony_ci coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev, 46162306a36Sopenharmony_ci u8 *qmat, int index) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int i; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); 46662306a36Sopenharmony_ci for (i = 0; i < 64; i++) 46762306a36Sopenharmony_ci coda_write(dev, qmat[i], CODA9_REG_JPEG_QMAT_DATA); 46862306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_QMAT_CTRL); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic void coda9_jpeg_qmat_setup(struct coda_ctx *ctx) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 47462306a36Sopenharmony_ci int *qmat_index = ctx->params.jpeg_qmat_index; 47562306a36Sopenharmony_ci u8 **qmat_tab = ctx->params.jpeg_qmat_tab; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[0]], 0x00); 47862306a36Sopenharmony_ci coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[1]], 0x40); 47962306a36Sopenharmony_ci coda9_jpeg_write_qmat_tab(dev, qmat_tab[qmat_index[2]], 0x80); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void coda9_jpeg_dec_bbc_gbu_setup(struct coda_ctx *ctx, 48362306a36Sopenharmony_ci struct vb2_buffer *buf, u32 ecs_offset) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 48662306a36Sopenharmony_ci int page_ptr, word_ptr, bit_ptr; 48762306a36Sopenharmony_ci u32 bbc_base_addr, end_addr; 48862306a36Sopenharmony_ci int bbc_cur_pos; 48962306a36Sopenharmony_ci int ret, val; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci bbc_base_addr = vb2_dma_contig_plane_dma_addr(buf, 0); 49262306a36Sopenharmony_ci end_addr = bbc_base_addr + vb2_get_plane_payload(buf, 0); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci page_ptr = ecs_offset / 256; 49562306a36Sopenharmony_ci word_ptr = (ecs_offset % 256) / 4; 49662306a36Sopenharmony_ci if (page_ptr & 1) 49762306a36Sopenharmony_ci word_ptr += 64; 49862306a36Sopenharmony_ci bit_ptr = (ecs_offset % 4) * 8; 49962306a36Sopenharmony_ci if (word_ptr & 1) 50062306a36Sopenharmony_ci bit_ptr += 32; 50162306a36Sopenharmony_ci word_ptr &= ~0x1; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_WR_PTR); 50462306a36Sopenharmony_ci coda_write(dev, bbc_base_addr, CODA9_REG_JPEG_BBC_BAS_ADDR); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* Leave 3 256-byte page margin to avoid a BBC interrupt */ 50762306a36Sopenharmony_ci coda_write(dev, end_addr + 256 * 3 + 256, CODA9_REG_JPEG_BBC_END_ADDR); 50862306a36Sopenharmony_ci val = DIV_ROUND_UP(vb2_plane_size(buf, 0), 256) + 3; 50962306a36Sopenharmony_ci coda_write(dev, BIT(31) | val, CODA9_REG_JPEG_BBC_STRM_CTRL); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci bbc_cur_pos = page_ptr; 51262306a36Sopenharmony_ci coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); 51362306a36Sopenharmony_ci coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), 51462306a36Sopenharmony_ci CODA9_REG_JPEG_BBC_EXT_ADDR); 51562306a36Sopenharmony_ci coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); 51662306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); 51762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); 51862306a36Sopenharmony_ci do { 51962306a36Sopenharmony_ci ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); 52062306a36Sopenharmony_ci } while (ret == 1); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci bbc_cur_pos++; 52362306a36Sopenharmony_ci coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); 52462306a36Sopenharmony_ci coda_write(dev, bbc_base_addr + (bbc_cur_pos << 8), 52562306a36Sopenharmony_ci CODA9_REG_JPEG_BBC_EXT_ADDR); 52662306a36Sopenharmony_ci coda_write(dev, (bbc_cur_pos & 1) << 6, CODA9_REG_JPEG_BBC_INT_ADDR); 52762306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); 52862306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_COMMAND); 52962306a36Sopenharmony_ci do { 53062306a36Sopenharmony_ci ret = coda_read(dev, CODA9_REG_JPEG_BBC_BUSY); 53162306a36Sopenharmony_ci } while (ret == 1); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci bbc_cur_pos++; 53462306a36Sopenharmony_ci coda_write(dev, bbc_cur_pos, CODA9_REG_JPEG_BBC_CUR_POS); 53562306a36Sopenharmony_ci coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_TT_CNT); 53862306a36Sopenharmony_ci coda_write(dev, word_ptr, CODA9_REG_JPEG_GBU_WD_PTR); 53962306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); 54062306a36Sopenharmony_ci coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); 54162306a36Sopenharmony_ci if (page_ptr & 1) { 54262306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBIR); 54362306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBHR); 54462306a36Sopenharmony_ci } else { 54562306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); 54662306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci coda_write(dev, 4, CODA9_REG_JPEG_GBU_CTRL); 54962306a36Sopenharmony_ci coda_write(dev, bit_ptr, CODA9_REG_JPEG_GBU_FF_RPTR); 55062306a36Sopenharmony_ci coda_write(dev, 3, CODA9_REG_JPEG_GBU_CTRL); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic const int bus_req_num[] = { 55462306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_420] = 2, 55562306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_422] = 3, 55662306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_224] = 3, 55762306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_444] = 4, 55862306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_400] = 4, 55962306a36Sopenharmony_ci}; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci#define MCU_INFO(mcu_block_num, comp_num, comp0_info, comp1_info, comp2_info) \ 56262306a36Sopenharmony_ci (((mcu_block_num) << CODA9_JPEG_MCU_BLOCK_NUM_OFFSET) | \ 56362306a36Sopenharmony_ci ((comp_num) << CODA9_JPEG_COMP_NUM_OFFSET) | \ 56462306a36Sopenharmony_ci ((comp0_info) << CODA9_JPEG_COMP0_INFO_OFFSET) | \ 56562306a36Sopenharmony_ci ((comp1_info) << CODA9_JPEG_COMP1_INFO_OFFSET) | \ 56662306a36Sopenharmony_ci ((comp2_info) << CODA9_JPEG_COMP2_INFO_OFFSET)) 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic const u32 mcu_info[] = { 56962306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_420] = MCU_INFO(6, 3, 10, 5, 5), 57062306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_422] = MCU_INFO(4, 3, 9, 5, 5), 57162306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_224] = MCU_INFO(4, 3, 6, 5, 5), 57262306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_444] = MCU_INFO(3, 3, 5, 5, 5), 57362306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_400] = MCU_INFO(1, 1, 5, 0, 0), 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* 57762306a36Sopenharmony_ci * Convert Huffman table specifcations to tables of codes and code lengths. 57862306a36Sopenharmony_ci * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic int coda9_jpeg_gen_enc_huff_tab(struct coda_ctx *ctx, int tab_num, 58362306a36Sopenharmony_ci int *ehufsi, int *ehufco) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int i, j, k, lastk, si, code, maxsymbol; 58662306a36Sopenharmony_ci const u8 *bits, *huffval; 58762306a36Sopenharmony_ci struct { 58862306a36Sopenharmony_ci int size[256]; 58962306a36Sopenharmony_ci int code[256]; 59062306a36Sopenharmony_ci } *huff; 59162306a36Sopenharmony_ci static const unsigned char *huff_tabs[4] = { 59262306a36Sopenharmony_ci luma_dc, luma_ac, chroma_dc, chroma_ac, 59362306a36Sopenharmony_ci }; 59462306a36Sopenharmony_ci int ret = -EINVAL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci huff = kzalloc(sizeof(*huff), GFP_KERNEL); 59762306a36Sopenharmony_ci if (!huff) 59862306a36Sopenharmony_ci return -ENOMEM; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci bits = huff_tabs[tab_num]; 60162306a36Sopenharmony_ci huffval = huff_tabs[tab_num] + 16; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci maxsymbol = tab_num & 1 ? 256 : 16; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Figure C.1 - Generation of table of Huffman code sizes */ 60662306a36Sopenharmony_ci k = 0; 60762306a36Sopenharmony_ci for (i = 1; i <= 16; i++) { 60862306a36Sopenharmony_ci j = bits[i - 1]; 60962306a36Sopenharmony_ci if (k + j > maxsymbol) 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci while (j--) 61262306a36Sopenharmony_ci huff->size[k++] = i; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci lastk = k; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Figure C.2 - Generation of table of Huffman codes */ 61762306a36Sopenharmony_ci k = 0; 61862306a36Sopenharmony_ci code = 0; 61962306a36Sopenharmony_ci si = huff->size[0]; 62062306a36Sopenharmony_ci while (k < lastk) { 62162306a36Sopenharmony_ci while (huff->size[k] == si) { 62262306a36Sopenharmony_ci huff->code[k++] = code; 62362306a36Sopenharmony_ci code++; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci if (code >= (1 << si)) 62662306a36Sopenharmony_ci goto out; 62762306a36Sopenharmony_ci code <<= 1; 62862306a36Sopenharmony_ci si++; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Figure C.3 - Ordering procedure for encoding procedure code tables */ 63262306a36Sopenharmony_ci for (k = 0; k < lastk; k++) { 63362306a36Sopenharmony_ci i = huffval[k]; 63462306a36Sopenharmony_ci if (i >= maxsymbol || ehufsi[i]) 63562306a36Sopenharmony_ci goto out; 63662306a36Sopenharmony_ci ehufco[i] = huff->code[k]; 63762306a36Sopenharmony_ci ehufsi[i] = huff->size[k]; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ret = 0; 64162306a36Sopenharmony_ciout: 64262306a36Sopenharmony_ci kfree(huff); 64362306a36Sopenharmony_ci return ret; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci#define DC_TABLE_INDEX0 0 64762306a36Sopenharmony_ci#define AC_TABLE_INDEX0 1 64862306a36Sopenharmony_ci#define DC_TABLE_INDEX1 2 64962306a36Sopenharmony_ci#define AC_TABLE_INDEX1 3 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic u8 *coda9_jpeg_get_huff_bits(struct coda_ctx *ctx, int tab_num) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (!huff_tab) 65662306a36Sopenharmony_ci return NULL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci switch (tab_num) { 65962306a36Sopenharmony_ci case DC_TABLE_INDEX0: return huff_tab->luma_dc; 66062306a36Sopenharmony_ci case AC_TABLE_INDEX0: return huff_tab->luma_ac; 66162306a36Sopenharmony_ci case DC_TABLE_INDEX1: return huff_tab->chroma_dc; 66262306a36Sopenharmony_ci case AC_TABLE_INDEX1: return huff_tab->chroma_ac; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return NULL; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int coda9_jpeg_gen_dec_huff_tab(struct coda_ctx *ctx, int tab_num) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci int ptr_cnt = 0, huff_code = 0, zero_flag = 0, data_flag = 0; 67162306a36Sopenharmony_ci u8 *huff_bits; 67262306a36Sopenharmony_ci s16 *huff_max; 67362306a36Sopenharmony_ci s16 *huff_min; 67462306a36Sopenharmony_ci s8 *huff_ptr; 67562306a36Sopenharmony_ci int ofs; 67662306a36Sopenharmony_ci int i; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci huff_bits = coda9_jpeg_get_huff_bits(ctx, tab_num); 67962306a36Sopenharmony_ci if (!huff_bits) 68062306a36Sopenharmony_ci return -EINVAL; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* DC/AC Luma, DC/AC Chroma -> DC Luma/Chroma, AC Luma/Chroma */ 68362306a36Sopenharmony_ci ofs = ((tab_num & 1) << 1) | ((tab_num >> 1) & 1); 68462306a36Sopenharmony_ci ofs *= 16; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci huff_ptr = ctx->params.jpeg_huff_tab->ptr + ofs; 68762306a36Sopenharmony_ci huff_max = ctx->params.jpeg_huff_tab->max + ofs; 68862306a36Sopenharmony_ci huff_min = ctx->params.jpeg_huff_tab->min + ofs; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 69162306a36Sopenharmony_ci if (huff_bits[i]) { 69262306a36Sopenharmony_ci huff_ptr[i] = ptr_cnt; 69362306a36Sopenharmony_ci ptr_cnt += huff_bits[i]; 69462306a36Sopenharmony_ci huff_min[i] = huff_code; 69562306a36Sopenharmony_ci huff_max[i] = huff_code + (huff_bits[i] - 1); 69662306a36Sopenharmony_ci data_flag = 1; 69762306a36Sopenharmony_ci zero_flag = 0; 69862306a36Sopenharmony_ci } else { 69962306a36Sopenharmony_ci huff_ptr[i] = -1; 70062306a36Sopenharmony_ci huff_min[i] = -1; 70162306a36Sopenharmony_ci huff_max[i] = -1; 70262306a36Sopenharmony_ci zero_flag = 1; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (data_flag == 1) { 70662306a36Sopenharmony_ci if (zero_flag == 1) 70762306a36Sopenharmony_ci huff_code <<= 1; 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci huff_code = (huff_max[i] + 1) << 1; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int coda9_jpeg_load_huff_tab(struct coda_ctx *ctx) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct { 71962306a36Sopenharmony_ci int size[4][256]; 72062306a36Sopenharmony_ci int code[4][256]; 72162306a36Sopenharmony_ci } *huff; 72262306a36Sopenharmony_ci u32 *huff_data; 72362306a36Sopenharmony_ci int i, j; 72462306a36Sopenharmony_ci int ret; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci huff = kzalloc(sizeof(*huff), GFP_KERNEL); 72762306a36Sopenharmony_ci if (!huff) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Generate all four (luma/chroma DC/AC) code/size lookup tables */ 73162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 73262306a36Sopenharmony_ci ret = coda9_jpeg_gen_enc_huff_tab(ctx, i, huff->size[i], 73362306a36Sopenharmony_ci huff->code[i]); 73462306a36Sopenharmony_ci if (ret) 73562306a36Sopenharmony_ci goto out; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!ctx->params.jpeg_huff_data) { 73962306a36Sopenharmony_ci ctx->params.jpeg_huff_data = 74062306a36Sopenharmony_ci kzalloc(sizeof(u32) * CODA9_JPEG_ENC_HUFF_DATA_SIZE, 74162306a36Sopenharmony_ci GFP_KERNEL); 74262306a36Sopenharmony_ci if (!ctx->params.jpeg_huff_data) { 74362306a36Sopenharmony_ci ret = -ENOMEM; 74462306a36Sopenharmony_ci goto out; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci huff_data = ctx->params.jpeg_huff_data; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 75062306a36Sopenharmony_ci /* Store Huffman lookup tables in AC0, AC1, DC0, DC1 order */ 75162306a36Sopenharmony_ci int t = (j == 0) ? AC_TABLE_INDEX0 : 75262306a36Sopenharmony_ci (j == 1) ? AC_TABLE_INDEX1 : 75362306a36Sopenharmony_ci (j == 2) ? DC_TABLE_INDEX0 : 75462306a36Sopenharmony_ci DC_TABLE_INDEX1; 75562306a36Sopenharmony_ci /* DC tables only have 16 entries */ 75662306a36Sopenharmony_ci int len = (j < 2) ? 256 : 16; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for (i = 0; i < len; i++) { 75962306a36Sopenharmony_ci if (huff->size[t][i] == 0 && huff->code[t][i] == 0) 76062306a36Sopenharmony_ci *(huff_data++) = 0; 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci *(huff_data++) = 76362306a36Sopenharmony_ci ((huff->size[t][i] - 1) << 16) | 76462306a36Sopenharmony_ci huff->code[t][i]; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci ret = 0; 76962306a36Sopenharmony_ciout: 77062306a36Sopenharmony_ci kfree(huff); 77162306a36Sopenharmony_ci return ret; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void coda9_jpeg_write_huff_tab(struct coda_ctx *ctx) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 77762306a36Sopenharmony_ci u32 *huff_data = ctx->params.jpeg_huff_data; 77862306a36Sopenharmony_ci int i; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Write Huffman size/code lookup tables in AC0, AC1, DC0, DC1 order */ 78162306a36Sopenharmony_ci coda_write(dev, 0x3, CODA9_REG_JPEG_HUFF_CTRL); 78262306a36Sopenharmony_ci for (i = 0; i < CODA9_JPEG_ENC_HUFF_DATA_SIZE; i++) 78362306a36Sopenharmony_ci coda_write(dev, *(huff_data++), CODA9_REG_JPEG_HUFF_DATA); 78462306a36Sopenharmony_ci coda_write(dev, 0x0, CODA9_REG_JPEG_HUFF_CTRL); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic inline void coda9_jpeg_write_qmat_quotients(struct coda_dev *dev, 78862306a36Sopenharmony_ci u8 *qmat, int index) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci int i; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci coda_write(dev, index | 0x3, CODA9_REG_JPEG_QMAT_CTRL); 79362306a36Sopenharmony_ci for (i = 0; i < 64; i++) 79462306a36Sopenharmony_ci coda_write(dev, 0x80000 / qmat[i], CODA9_REG_JPEG_QMAT_DATA); 79562306a36Sopenharmony_ci coda_write(dev, index, CODA9_REG_JPEG_QMAT_CTRL); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic void coda9_jpeg_load_qmat_tab(struct coda_ctx *ctx) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 80162306a36Sopenharmony_ci u8 *luma_tab; 80262306a36Sopenharmony_ci u8 *chroma_tab; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci luma_tab = ctx->params.jpeg_qmat_tab[0]; 80562306a36Sopenharmony_ci if (!luma_tab) 80662306a36Sopenharmony_ci luma_tab = luma_q; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci chroma_tab = ctx->params.jpeg_qmat_tab[1]; 80962306a36Sopenharmony_ci if (!chroma_tab) 81062306a36Sopenharmony_ci chroma_tab = chroma_q; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci coda9_jpeg_write_qmat_quotients(dev, luma_tab, 0x00); 81362306a36Sopenharmony_ci coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x40); 81462306a36Sopenharmony_ci coda9_jpeg_write_qmat_quotients(dev, chroma_tab, 0x80); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistruct coda_jpeg_stream { 81862306a36Sopenharmony_ci u8 *curr; 81962306a36Sopenharmony_ci u8 *end; 82062306a36Sopenharmony_ci}; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic inline int coda_jpeg_put_byte(u8 byte, struct coda_jpeg_stream *stream) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci if (stream->curr >= stream->end) 82562306a36Sopenharmony_ci return -EINVAL; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci *stream->curr++ = byte; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic inline int coda_jpeg_put_word(u16 word, struct coda_jpeg_stream *stream) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci if (stream->curr + sizeof(__be16) > stream->end) 83562306a36Sopenharmony_ci return -EINVAL; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci put_unaligned_be16(word, stream->curr); 83862306a36Sopenharmony_ci stream->curr += sizeof(__be16); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int coda_jpeg_put_table(u16 marker, u8 index, const u8 *table, 84462306a36Sopenharmony_ci size_t len, struct coda_jpeg_stream *stream) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci int i, ret; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ret = coda_jpeg_put_word(marker, stream); 84962306a36Sopenharmony_ci if (ret < 0) 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci ret = coda_jpeg_put_word(3 + len, stream); 85262306a36Sopenharmony_ci if (ret < 0) 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci ret = coda_jpeg_put_byte(index, stream); 85562306a36Sopenharmony_ci for (i = 0; i < len && ret == 0; i++) 85662306a36Sopenharmony_ci ret = coda_jpeg_put_byte(table[i], stream); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return ret; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic int coda_jpeg_define_quantization_table(struct coda_ctx *ctx, u8 index, 86262306a36Sopenharmony_ci struct coda_jpeg_stream *stream) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci return coda_jpeg_put_table(DQT_MARKER, index, 86562306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[index], 64, 86662306a36Sopenharmony_ci stream); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int coda_jpeg_define_huffman_table(u8 index, const u8 *table, size_t len, 87062306a36Sopenharmony_ci struct coda_jpeg_stream *stream) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci return coda_jpeg_put_table(DHT_MARKER, index, table, len, stream); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int coda9_jpeg_encode_header(struct coda_ctx *ctx, int len, u8 *buf) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct coda_jpeg_stream stream = { buf, buf + len }; 87862306a36Sopenharmony_ci struct coda_q_data *q_data_src; 87962306a36Sopenharmony_ci int chroma_format, comp_num; 88062306a36Sopenharmony_ci int i, ret, pad; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 88362306a36Sopenharmony_ci chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); 88462306a36Sopenharmony_ci if (chroma_format < 0) 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Start Of Image */ 88862306a36Sopenharmony_ci ret = coda_jpeg_put_word(SOI_MARKER, &stream); 88962306a36Sopenharmony_ci if (ret < 0) 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Define Restart Interval */ 89362306a36Sopenharmony_ci if (ctx->params.jpeg_restart_interval) { 89462306a36Sopenharmony_ci ret = coda_jpeg_put_word(DRI_MARKER, &stream); 89562306a36Sopenharmony_ci if (ret < 0) 89662306a36Sopenharmony_ci return ret; 89762306a36Sopenharmony_ci ret = coda_jpeg_put_word(4, &stream); 89862306a36Sopenharmony_ci if (ret < 0) 89962306a36Sopenharmony_ci return ret; 90062306a36Sopenharmony_ci ret = coda_jpeg_put_word(ctx->params.jpeg_restart_interval, 90162306a36Sopenharmony_ci &stream); 90262306a36Sopenharmony_ci if (ret < 0) 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Define Quantization Tables */ 90762306a36Sopenharmony_ci ret = coda_jpeg_define_quantization_table(ctx, 0x00, &stream); 90862306a36Sopenharmony_ci if (ret < 0) 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci if (chroma_format != CODA9_JPEG_FORMAT_400) { 91162306a36Sopenharmony_ci ret = coda_jpeg_define_quantization_table(ctx, 0x01, &stream); 91262306a36Sopenharmony_ci if (ret < 0) 91362306a36Sopenharmony_ci return ret; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Define Huffman Tables */ 91762306a36Sopenharmony_ci ret = coda_jpeg_define_huffman_table(0x00, luma_dc, 16 + 12, &stream); 91862306a36Sopenharmony_ci if (ret < 0) 91962306a36Sopenharmony_ci return ret; 92062306a36Sopenharmony_ci ret = coda_jpeg_define_huffman_table(0x10, luma_ac, 16 + 162, &stream); 92162306a36Sopenharmony_ci if (ret < 0) 92262306a36Sopenharmony_ci return ret; 92362306a36Sopenharmony_ci if (chroma_format != CODA9_JPEG_FORMAT_400) { 92462306a36Sopenharmony_ci ret = coda_jpeg_define_huffman_table(0x01, chroma_dc, 16 + 12, 92562306a36Sopenharmony_ci &stream); 92662306a36Sopenharmony_ci if (ret < 0) 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci ret = coda_jpeg_define_huffman_table(0x11, chroma_ac, 16 + 162, 92962306a36Sopenharmony_ci &stream); 93062306a36Sopenharmony_ci if (ret < 0) 93162306a36Sopenharmony_ci return ret; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Start Of Frame */ 93562306a36Sopenharmony_ci ret = coda_jpeg_put_word(SOF_MARKER, &stream); 93662306a36Sopenharmony_ci if (ret < 0) 93762306a36Sopenharmony_ci return ret; 93862306a36Sopenharmony_ci comp_num = (chroma_format == CODA9_JPEG_FORMAT_400) ? 1 : 3; 93962306a36Sopenharmony_ci ret = coda_jpeg_put_word(8 + comp_num * 3, &stream); 94062306a36Sopenharmony_ci if (ret < 0) 94162306a36Sopenharmony_ci return ret; 94262306a36Sopenharmony_ci ret = coda_jpeg_put_byte(0x08, &stream); 94362306a36Sopenharmony_ci if (ret < 0) 94462306a36Sopenharmony_ci return ret; 94562306a36Sopenharmony_ci ret = coda_jpeg_put_word(q_data_src->height, &stream); 94662306a36Sopenharmony_ci if (ret < 0) 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci ret = coda_jpeg_put_word(q_data_src->width, &stream); 94962306a36Sopenharmony_ci if (ret < 0) 95062306a36Sopenharmony_ci return ret; 95162306a36Sopenharmony_ci ret = coda_jpeg_put_byte(comp_num, &stream); 95262306a36Sopenharmony_ci if (ret < 0) 95362306a36Sopenharmony_ci return ret; 95462306a36Sopenharmony_ci for (i = 0; i < comp_num; i++) { 95562306a36Sopenharmony_ci static unsigned char subsampling[5][3] = { 95662306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_420] = { 0x22, 0x11, 0x11 }, 95762306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_422] = { 0x21, 0x11, 0x11 }, 95862306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_224] = { 0x12, 0x11, 0x11 }, 95962306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_444] = { 0x11, 0x11, 0x11 }, 96062306a36Sopenharmony_ci [CODA9_JPEG_FORMAT_400] = { 0x11 }, 96162306a36Sopenharmony_ci }; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Component identifier, matches SOS */ 96462306a36Sopenharmony_ci ret = coda_jpeg_put_byte(i + 1, &stream); 96562306a36Sopenharmony_ci if (ret < 0) 96662306a36Sopenharmony_ci return ret; 96762306a36Sopenharmony_ci ret = coda_jpeg_put_byte(subsampling[chroma_format][i], 96862306a36Sopenharmony_ci &stream); 96962306a36Sopenharmony_ci if (ret < 0) 97062306a36Sopenharmony_ci return ret; 97162306a36Sopenharmony_ci /* Chroma table index */ 97262306a36Sopenharmony_ci ret = coda_jpeg_put_byte((i == 0) ? 0 : 1, &stream); 97362306a36Sopenharmony_ci if (ret < 0) 97462306a36Sopenharmony_ci return ret; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Pad to multiple of 8 bytes */ 97862306a36Sopenharmony_ci pad = (stream.curr - buf) % 8; 97962306a36Sopenharmony_ci if (pad) { 98062306a36Sopenharmony_ci pad = 8 - pad; 98162306a36Sopenharmony_ci while (pad--) { 98262306a36Sopenharmony_ci ret = coda_jpeg_put_byte(0x00, &stream); 98362306a36Sopenharmony_ci if (ret < 0) 98462306a36Sopenharmony_ci return ret; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return stream.curr - buf; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* 99262306a36Sopenharmony_ci * Scale quantization table using nonlinear scaling factor 99362306a36Sopenharmony_ci * u8 qtab[64], scale [50,190] 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_cistatic void coda_scale_quant_table(u8 *q_tab, int scale) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci unsigned int temp; 99862306a36Sopenharmony_ci int i; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci for (i = 0; i < 64; i++) { 100162306a36Sopenharmony_ci temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100); 100262306a36Sopenharmony_ci if (temp <= 0) 100362306a36Sopenharmony_ci temp = 1; 100462306a36Sopenharmony_ci if (temp > 255) 100562306a36Sopenharmony_ci temp = 255; 100662306a36Sopenharmony_ci q_tab[i] = (unsigned char)temp; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_civoid coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci unsigned int scale; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci ctx->params.jpeg_quality = quality; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* Clip quality setting to [5,100] interval */ 101762306a36Sopenharmony_ci if (quality > 100) 101862306a36Sopenharmony_ci quality = 100; 101962306a36Sopenharmony_ci if (quality < 5) 102062306a36Sopenharmony_ci quality = 5; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* 102362306a36Sopenharmony_ci * Non-linear scaling factor: 102462306a36Sopenharmony_ci * [5,50] -> [1000..100], [51,100] -> [98..0] 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci if (quality < 50) 102762306a36Sopenharmony_ci scale = 5000 / quality; 102862306a36Sopenharmony_ci else 102962306a36Sopenharmony_ci scale = 200 - 2 * quality; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (ctx->params.jpeg_qmat_tab[0]) { 103262306a36Sopenharmony_ci memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64); 103362306a36Sopenharmony_ci coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale); 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci if (ctx->params.jpeg_qmat_tab[1]) { 103662306a36Sopenharmony_ci memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64); 103762306a36Sopenharmony_ci coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale); 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci/* 104262306a36Sopenharmony_ci * Encoder context operations 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int coda9_jpeg_start_encoding(struct coda_ctx *ctx) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 104862306a36Sopenharmony_ci int ret; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci ret = coda9_jpeg_load_huff_tab(ctx); 105162306a36Sopenharmony_ci if (ret < 0) { 105262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); 105362306a36Sopenharmony_ci return ret; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[0]) { 105662306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); 105762306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[0]) 105862306a36Sopenharmony_ci return -ENOMEM; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[1]) { 106162306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); 106262306a36Sopenharmony_ci if (!ctx->params.jpeg_qmat_tab[1]) 106362306a36Sopenharmony_ci return -ENOMEM; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci return 0; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_cistatic int coda9_jpeg_prepare_encode(struct coda_ctx *ctx) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct coda_q_data *q_data_src; 107362306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 107462306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 107562306a36Sopenharmony_ci u32 start_addr, end_addr; 107662306a36Sopenharmony_ci u16 aligned_width, aligned_height; 107762306a36Sopenharmony_ci bool chroma_interleave; 107862306a36Sopenharmony_ci int chroma_format; 107962306a36Sopenharmony_ci int header_len; 108062306a36Sopenharmony_ci int ret; 108162306a36Sopenharmony_ci ktime_t timeout; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 108462306a36Sopenharmony_ci dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 108562306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) 108862306a36Sopenharmony_ci vb2_set_plane_payload(&src_buf->vb2_buf, 0, 108962306a36Sopenharmony_ci vb2_plane_size(&src_buf->vb2_buf, 0)); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci src_buf->sequence = ctx->osequence; 109262306a36Sopenharmony_ci dst_buf->sequence = ctx->osequence; 109362306a36Sopenharmony_ci ctx->osequence++; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; 109662306a36Sopenharmony_ci src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci coda_set_gdi_regs(ctx); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci start_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); 110162306a36Sopenharmony_ci end_addr = start_addr + vb2_plane_size(&dst_buf->vb2_buf, 0); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci chroma_format = coda9_jpeg_chroma_format(q_data_src->fourcc); 110462306a36Sopenharmony_ci if (chroma_format < 0) 110562306a36Sopenharmony_ci return chroma_format; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Round image dimensions to multiple of MCU size */ 110862306a36Sopenharmony_ci aligned_width = round_up(q_data_src->width, width_align[chroma_format]); 110962306a36Sopenharmony_ci aligned_height = round_up(q_data_src->height, 111062306a36Sopenharmony_ci height_align[chroma_format]); 111162306a36Sopenharmony_ci if (aligned_width != q_data_src->bytesperline) { 111262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "wrong stride: %d instead of %d\n", 111362306a36Sopenharmony_ci aligned_width, q_data_src->bytesperline); 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci header_len = 111762306a36Sopenharmony_ci coda9_jpeg_encode_header(ctx, 111862306a36Sopenharmony_ci vb2_plane_size(&dst_buf->vb2_buf, 0), 111962306a36Sopenharmony_ci vb2_plane_vaddr(&dst_buf->vb2_buf, 0)); 112062306a36Sopenharmony_ci if (header_len < 0) 112162306a36Sopenharmony_ci return header_len; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_BAS_ADDR); 112462306a36Sopenharmony_ci coda_write(dev, end_addr, CODA9_REG_JPEG_BBC_END_ADDR); 112562306a36Sopenharmony_ci coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_WR_PTR); 112662306a36Sopenharmony_ci coda_write(dev, start_addr + header_len, CODA9_REG_JPEG_BBC_RD_PTR); 112762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_CUR_POS); 112862306a36Sopenharmony_ci /* 64 words per 256-byte page */ 112962306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_BBC_DATA_CNT); 113062306a36Sopenharmony_ci coda_write(dev, start_addr, CODA9_REG_JPEG_BBC_EXT_ADDR); 113162306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_INT_ADDR); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_BT_PTR); 113462306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_WD_PTR); 113562306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_BBSR); 113662306a36Sopenharmony_ci coda_write(dev, BIT(31) | ((end_addr - start_addr - header_len) / 256), 113762306a36Sopenharmony_ci CODA9_REG_JPEG_BBC_STRM_CTRL); 113862306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_CTRL); 113962306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_GBU_FF_RPTR); 114062306a36Sopenharmony_ci coda_write(dev, 127, CODA9_REG_JPEG_GBU_BBER); 114162306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBIR); 114262306a36Sopenharmony_ci coda_write(dev, 64, CODA9_REG_JPEG_GBU_BBHR); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci chroma_interleave = (q_data_src->fourcc == V4L2_PIX_FMT_NV12); 114562306a36Sopenharmony_ci coda_write(dev, CODA9_JPEG_PIC_CTRL_TC_DIRECTION | 114662306a36Sopenharmony_ci CODA9_JPEG_PIC_CTRL_ENCODER_EN, CODA9_REG_JPEG_PIC_CTRL); 114762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_SCL_INFO); 114862306a36Sopenharmony_ci coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); 114962306a36Sopenharmony_ci coda_write(dev, ctx->params.jpeg_restart_interval, 115062306a36Sopenharmony_ci CODA9_REG_JPEG_RST_INTVAL); 115162306a36Sopenharmony_ci coda_write(dev, 1, CODA9_REG_JPEG_BBC_CTRL); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci coda9_jpeg_write_huff_tab(ctx); 115662306a36Sopenharmony_ci coda9_jpeg_load_qmat_tab(ctx); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (ctx->params.rot_mode & CODA_ROT_90) { 115962306a36Sopenharmony_ci aligned_width = aligned_height; 116062306a36Sopenharmony_ci aligned_height = q_data_src->bytesperline; 116162306a36Sopenharmony_ci if (chroma_format == CODA9_JPEG_FORMAT_422) 116262306a36Sopenharmony_ci chroma_format = CODA9_JPEG_FORMAT_224; 116362306a36Sopenharmony_ci else if (chroma_format == CODA9_JPEG_FORMAT_224) 116462306a36Sopenharmony_ci chroma_format = CODA9_JPEG_FORMAT_422; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci /* These need to be multiples of MCU size */ 116762306a36Sopenharmony_ci coda_write(dev, aligned_width << 16 | aligned_height, 116862306a36Sopenharmony_ci CODA9_REG_JPEG_PIC_SIZE); 116962306a36Sopenharmony_ci coda_write(dev, ctx->params.rot_mode ? 117062306a36Sopenharmony_ci (CODA_ROT_MIR_ENABLE | ctx->params.rot_mode) : 0, 117162306a36Sopenharmony_ci CODA9_REG_JPEG_ROT_INFO); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci coda_write(dev, 1, CODA9_GDI_CONTROL); 117662306a36Sopenharmony_ci timeout = ktime_add_us(ktime_get(), 100000); 117762306a36Sopenharmony_ci do { 117862306a36Sopenharmony_ci ret = coda_read(dev, CODA9_GDI_STATUS); 117962306a36Sopenharmony_ci if (ktime_compare(ktime_get(), timeout) > 0) { 118062306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "timeout waiting for GDI\n"); 118162306a36Sopenharmony_ci return -ETIMEDOUT; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci } while (!ret); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci coda_write(dev, (chroma_format << 17) | (chroma_interleave << 16) | 118662306a36Sopenharmony_ci q_data_src->bytesperline, CODA9_GDI_INFO_CONTROL); 118762306a36Sopenharmony_ci /* The content of this register seems to be irrelevant: */ 118862306a36Sopenharmony_ci coda_write(dev, aligned_width << 16 | aligned_height, 118962306a36Sopenharmony_ci CODA9_GDI_INFO_PIC_SIZE); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci coda_write_base(ctx, q_data_src, src_buf, CODA9_GDI_INFO_BASE_Y); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); 119462306a36Sopenharmony_ci coda_write(dev, 0, CODA9_GDI_CONTROL); 119562306a36Sopenharmony_ci coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); 119862306a36Sopenharmony_ci coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci trace_coda_jpeg_run(ctx, src_buf); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return 0; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic void coda9_jpeg_finish_encode(struct coda_ctx *ctx) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 121062306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 121162306a36Sopenharmony_ci u32 wr_ptr, start_ptr; 121262306a36Sopenharmony_ci u32 err_mb; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (ctx->aborting) { 121562306a36Sopenharmony_ci coda_write(ctx->dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); 121662306a36Sopenharmony_ci return; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* 122062306a36Sopenharmony_ci * Lock to make sure that an encoder stop command running in parallel 122162306a36Sopenharmony_ci * will either already have marked src_buf as last, or it will wake up 122262306a36Sopenharmony_ci * the capture queue after the buffers are returned. 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_ci mutex_lock(&ctx->wakeup_mutex); 122562306a36Sopenharmony_ci src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 122662306a36Sopenharmony_ci dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci trace_coda_jpeg_done(ctx, dst_buf); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* 123162306a36Sopenharmony_ci * Set plane payload to the number of bytes written out 123262306a36Sopenharmony_ci * by the JPEG processing unit 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci start_ptr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); 123562306a36Sopenharmony_ci wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); 123662306a36Sopenharmony_ci vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); 123962306a36Sopenharmony_ci if (err_mb) 124062306a36Sopenharmony_ci coda_dbg(1, ctx, "ERRMB: 0x%x\n", err_mb); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); 124562306a36Sopenharmony_ci dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; 124662306a36Sopenharmony_ci dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); 125162306a36Sopenharmony_ci coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : 125262306a36Sopenharmony_ci VB2_BUF_STATE_DONE); 125362306a36Sopenharmony_ci mutex_unlock(&ctx->wakeup_mutex); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci coda_dbg(1, ctx, "job finished: encoded frame (%u)%s\n", 125662306a36Sopenharmony_ci dst_buf->sequence, 125762306a36Sopenharmony_ci (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* 126062306a36Sopenharmony_ci * Reset JPEG processing unit after each encode run to work 126162306a36Sopenharmony_ci * around hangups when switching context between encoder and 126262306a36Sopenharmony_ci * decoder. 126362306a36Sopenharmony_ci */ 126462306a36Sopenharmony_ci coda_hw_reset(ctx); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic void coda9_jpeg_encode_timeout(struct coda_ctx *ctx) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 127062306a36Sopenharmony_ci u32 end_addr, wr_ptr; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* Handle missing BBC overflow interrupt via timeout */ 127362306a36Sopenharmony_ci end_addr = coda_read(dev, CODA9_REG_JPEG_BBC_END_ADDR); 127462306a36Sopenharmony_ci wr_ptr = coda_read(dev, CODA9_REG_JPEG_BBC_WR_PTR); 127562306a36Sopenharmony_ci if (wr_ptr >= end_addr - 256) { 127662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "JPEG too large for capture buffer\n"); 127762306a36Sopenharmony_ci coda9_jpeg_finish_encode(ctx); 127862306a36Sopenharmony_ci return; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci coda_hw_reset(ctx); 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic void coda9_jpeg_release(struct coda_ctx *ctx) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci int i; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (ctx->params.jpeg_qmat_tab[0] == luma_q) 128962306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[0] = NULL; 129062306a36Sopenharmony_ci if (ctx->params.jpeg_qmat_tab[1] == chroma_q) 129162306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[1] = NULL; 129262306a36Sopenharmony_ci for (i = 0; i < 3; i++) 129362306a36Sopenharmony_ci kfree(ctx->params.jpeg_qmat_tab[i]); 129462306a36Sopenharmony_ci kfree(ctx->params.jpeg_huff_data); 129562306a36Sopenharmony_ci kfree(ctx->params.jpeg_huff_tab); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ciconst struct coda_context_ops coda9_jpeg_encode_ops = { 129962306a36Sopenharmony_ci .queue_init = coda_encoder_queue_init, 130062306a36Sopenharmony_ci .start_streaming = coda9_jpeg_start_encoding, 130162306a36Sopenharmony_ci .prepare_run = coda9_jpeg_prepare_encode, 130262306a36Sopenharmony_ci .finish_run = coda9_jpeg_finish_encode, 130362306a36Sopenharmony_ci .run_timeout = coda9_jpeg_encode_timeout, 130462306a36Sopenharmony_ci .release = coda9_jpeg_release, 130562306a36Sopenharmony_ci}; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/* 130862306a36Sopenharmony_ci * Decoder context operations 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic int coda9_jpeg_start_decoding(struct coda_ctx *ctx) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci ctx->params.jpeg_qmat_index[0] = 0; 131462306a36Sopenharmony_ci ctx->params.jpeg_qmat_index[1] = 1; 131562306a36Sopenharmony_ci ctx->params.jpeg_qmat_index[2] = 1; 131662306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[0] = luma_q; 131762306a36Sopenharmony_ci ctx->params.jpeg_qmat_tab[1] = chroma_q; 131862306a36Sopenharmony_ci /* nothing more to do here */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* TODO: we could already scan the first header to get the chroma 132162306a36Sopenharmony_ci * format. 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci return 0; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic int coda9_jpeg_prepare_decode(struct coda_ctx *ctx) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 133062306a36Sopenharmony_ci int aligned_width, aligned_height; 133162306a36Sopenharmony_ci int chroma_format; 133262306a36Sopenharmony_ci int ret; 133362306a36Sopenharmony_ci u32 val, dst_fourcc; 133462306a36Sopenharmony_ci struct coda_q_data *q_data_src, *q_data_dst; 133562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 133662306a36Sopenharmony_ci int chroma_interleave; 133762306a36Sopenharmony_ci int scl_hor_mode, scl_ver_mode; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 134062306a36Sopenharmony_ci dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 134162306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 134262306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 134362306a36Sopenharmony_ci dst_fourcc = q_data_dst->fourcc; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci scl_hor_mode = coda_jpeg_scale(q_data_src->width, q_data_dst->width); 134662306a36Sopenharmony_ci scl_ver_mode = coda_jpeg_scale(q_data_src->height, q_data_dst->height); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == 0) 134962306a36Sopenharmony_ci vb2_set_plane_payload(&src_buf->vb2_buf, 0, 135062306a36Sopenharmony_ci vb2_plane_size(&src_buf->vb2_buf, 0)); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci chroma_format = coda9_jpeg_chroma_format(q_data_dst->fourcc); 135362306a36Sopenharmony_ci if (chroma_format < 0) 135462306a36Sopenharmony_ci return chroma_format; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci ret = coda_jpeg_decode_header(ctx, &src_buf->vb2_buf); 135762306a36Sopenharmony_ci if (ret < 0) { 135862306a36Sopenharmony_ci src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 135962306a36Sopenharmony_ci dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 136062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); 136162306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci return ret; 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* Round image dimensions to multiple of MCU size */ 136762306a36Sopenharmony_ci aligned_width = round_up(q_data_src->width, width_align[chroma_format]); 136862306a36Sopenharmony_ci aligned_height = round_up(q_data_src->height, height_align[chroma_format]); 136962306a36Sopenharmony_ci if (aligned_width != q_data_dst->bytesperline) { 137062306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "stride mismatch: %d != %d\n", 137162306a36Sopenharmony_ci aligned_width, q_data_dst->bytesperline); 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci coda_set_gdi_regs(ctx); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci val = ctx->params.jpeg_huff_ac_index[0] << 12 | 137762306a36Sopenharmony_ci ctx->params.jpeg_huff_ac_index[1] << 11 | 137862306a36Sopenharmony_ci ctx->params.jpeg_huff_ac_index[2] << 10 | 137962306a36Sopenharmony_ci ctx->params.jpeg_huff_dc_index[0] << 9 | 138062306a36Sopenharmony_ci ctx->params.jpeg_huff_dc_index[1] << 8 | 138162306a36Sopenharmony_ci ctx->params.jpeg_huff_dc_index[2] << 7; 138262306a36Sopenharmony_ci if (ctx->params.jpeg_huff_tab) 138362306a36Sopenharmony_ci val |= CODA9_JPEG_PIC_CTRL_USER_HUFFMAN_EN; 138462306a36Sopenharmony_ci coda_write(dev, val, CODA9_REG_JPEG_PIC_CTRL); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci coda_write(dev, aligned_width << 16 | aligned_height, 138762306a36Sopenharmony_ci CODA9_REG_JPEG_PIC_SIZE); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci chroma_interleave = (dst_fourcc == V4L2_PIX_FMT_NV12); 139062306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); 139162306a36Sopenharmony_ci coda_write(dev, bus_req_num[chroma_format], CODA9_REG_JPEG_OP_INFO); 139262306a36Sopenharmony_ci coda_write(dev, mcu_info[chroma_format], CODA9_REG_JPEG_MCU_INFO); 139362306a36Sopenharmony_ci if (scl_hor_mode || scl_ver_mode) 139462306a36Sopenharmony_ci val = CODA9_JPEG_SCL_ENABLE | (scl_hor_mode << 2) | scl_ver_mode; 139562306a36Sopenharmony_ci else 139662306a36Sopenharmony_ci val = 0; 139762306a36Sopenharmony_ci coda_write(dev, val, CODA9_REG_JPEG_SCL_INFO); 139862306a36Sopenharmony_ci coda_write(dev, chroma_interleave, CODA9_REG_JPEG_DPB_CONFIG); 139962306a36Sopenharmony_ci coda_write(dev, ctx->params.jpeg_restart_interval, 140062306a36Sopenharmony_ci CODA9_REG_JPEG_RST_INTVAL); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (ctx->params.jpeg_huff_tab) 140362306a36Sopenharmony_ci coda9_jpeg_dec_huff_setup(ctx); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci coda9_jpeg_qmat_setup(ctx); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci coda9_jpeg_dec_bbc_gbu_setup(ctx, &src_buf->vb2_buf, 140862306a36Sopenharmony_ci ctx->jpeg_ecs_offset); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_RST_INDEX); 141162306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_RST_COUNT); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_Y); 141462306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CB); 141562306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_DPCM_DIFF_CR); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_ROT_INFO); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci coda_write(dev, 1, CODA9_GDI_CONTROL); 142062306a36Sopenharmony_ci do { 142162306a36Sopenharmony_ci ret = coda_read(dev, CODA9_GDI_STATUS); 142262306a36Sopenharmony_ci } while (!ret); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci val = (chroma_format << 17) | (chroma_interleave << 16) | 142562306a36Sopenharmony_ci q_data_dst->bytesperline; 142662306a36Sopenharmony_ci if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) 142762306a36Sopenharmony_ci val |= 3 << 20; 142862306a36Sopenharmony_ci coda_write(dev, val, CODA9_GDI_INFO_CONTROL); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci coda_write(dev, aligned_width << 16 | aligned_height, 143162306a36Sopenharmony_ci CODA9_GDI_INFO_PIC_SIZE); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci coda_write_base(ctx, q_data_dst, dst_buf, CODA9_GDI_INFO_BASE_Y); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_DPB_BASE00); 143662306a36Sopenharmony_ci coda_write(dev, 0, CODA9_GDI_CONTROL); 143762306a36Sopenharmony_ci coda_write(dev, 1, CODA9_GDI_PIC_INIT_HOST); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci trace_coda_jpeg_run(ctx, src_buf); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci coda_write(dev, 1, CODA9_REG_JPEG_PIC_START); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci return 0; 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic void coda9_jpeg_finish_decode(struct coda_ctx *ctx) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 144962306a36Sopenharmony_ci struct vb2_v4l2_buffer *dst_buf, *src_buf; 145062306a36Sopenharmony_ci struct coda_q_data *q_data_dst; 145162306a36Sopenharmony_ci u32 err_mb; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); 145462306a36Sopenharmony_ci if (err_mb) 145562306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "ERRMB: 0x%x\n", err_mb); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci coda_write(dev, 0, CODA9_REG_JPEG_BBC_FLUSH_CMD); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* 146062306a36Sopenharmony_ci * Lock to make sure that a decoder stop command running in parallel 146162306a36Sopenharmony_ci * will either already have marked src_buf as last, or it will wake up 146262306a36Sopenharmony_ci * the capture queue after the buffers are returned. 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_ci mutex_lock(&ctx->wakeup_mutex); 146562306a36Sopenharmony_ci src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 146662306a36Sopenharmony_ci dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 146762306a36Sopenharmony_ci dst_buf->sequence = ctx->osequence++; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci trace_coda_jpeg_done(ctx, dst_buf); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci dst_buf->flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_LAST); 147262306a36Sopenharmony_ci dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; 147362306a36Sopenharmony_ci dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 147862306a36Sopenharmony_ci vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); 148162306a36Sopenharmony_ci coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : 148262306a36Sopenharmony_ci VB2_BUF_STATE_DONE); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci mutex_unlock(&ctx->wakeup_mutex); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci coda_dbg(1, ctx, "job finished: decoded frame (%u)%s\n", 148762306a36Sopenharmony_ci dst_buf->sequence, 148862306a36Sopenharmony_ci (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* 149162306a36Sopenharmony_ci * Reset JPEG processing unit after each decode run to work 149262306a36Sopenharmony_ci * around hangups when switching context between encoder and 149362306a36Sopenharmony_ci * decoder. 149462306a36Sopenharmony_ci */ 149562306a36Sopenharmony_ci coda_hw_reset(ctx); 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ciconst struct coda_context_ops coda9_jpeg_decode_ops = { 149962306a36Sopenharmony_ci .queue_init = coda_encoder_queue_init, /* non-bitstream operation */ 150062306a36Sopenharmony_ci .start_streaming = coda9_jpeg_start_decoding, 150162306a36Sopenharmony_ci .prepare_run = coda9_jpeg_prepare_decode, 150262306a36Sopenharmony_ci .finish_run = coda9_jpeg_finish_decode, 150362306a36Sopenharmony_ci .release = coda9_jpeg_release, 150462306a36Sopenharmony_ci}; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ciirqreturn_t coda9_jpeg_irq_handler(int irq, void *data) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci struct coda_dev *dev = data; 150962306a36Sopenharmony_ci struct coda_ctx *ctx; 151062306a36Sopenharmony_ci int status; 151162306a36Sopenharmony_ci int err_mb; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci status = coda_read(dev, CODA9_REG_JPEG_PIC_STATUS); 151462306a36Sopenharmony_ci if (status == 0) 151562306a36Sopenharmony_ci return IRQ_HANDLED; 151662306a36Sopenharmony_ci coda_write(dev, status, CODA9_REG_JPEG_PIC_STATUS); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (status & CODA9_JPEG_STATUS_OVERFLOW) 151962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "JPEG overflow\n"); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (status & CODA9_JPEG_STATUS_BBC_INT) 152262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "JPEG BBC interrupt\n"); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (status & CODA9_JPEG_STATUS_ERROR) { 152562306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "JPEG error\n"); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci err_mb = coda_read(dev, CODA9_REG_JPEG_PIC_ERRMB); 152862306a36Sopenharmony_ci if (err_mb) { 152962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 153062306a36Sopenharmony_ci "ERRMB: 0x%x: rst idx %d, mcu pos (%d,%d)\n", 153162306a36Sopenharmony_ci err_mb, err_mb >> 24, (err_mb >> 12) & 0xfff, 153262306a36Sopenharmony_ci err_mb & 0xfff); 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); 153762306a36Sopenharmony_ci if (!ctx) { 153862306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 153962306a36Sopenharmony_ci "Instance released before the end of transaction\n"); 154062306a36Sopenharmony_ci mutex_unlock(&dev->coda_mutex); 154162306a36Sopenharmony_ci return IRQ_HANDLED; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci complete(&ctx->completion); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci return IRQ_HANDLED; 154762306a36Sopenharmony_ci} 1548