246 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) Collabora, Ltd.4 *5 * Based on GSPCA and CODA drivers:6 * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr)7 * Copyright (C) 2014 Philipp Zabel, Pengutronix8 */9 10#include <linux/align.h>11#include <linux/build_bug.h>12#include <linux/kernel.h>13#include <linux/string.h>14#include <media/v4l2-jpeg.h>15#include "hantro_jpeg.h"16#include "hantro.h"17 18#define LUMA_QUANT_OFF 2519#define CHROMA_QUANT_OFF 9020#define HEIGHT_OFF 15921#define WIDTH_OFF 16122 23#define HUFF_LUMA_DC_OFF 17824#define HUFF_LUMA_AC_OFF 21125#define HUFF_CHROMA_DC_OFF 39426#define HUFF_CHROMA_AC_OFF 42727 28static const u32 hw_reorder[] = {29 0, 8, 16, 24, 1, 9, 17, 25,30 32, 40, 48, 56, 33, 41, 49, 57,31 2, 10, 18, 26, 3, 11, 19, 27,32 34, 42, 50, 58, 35, 43, 51, 59,33 4, 12, 20, 28, 5, 13, 21, 29,34 36, 44, 52, 60, 37, 45, 53, 61,35 6, 14, 22, 30, 7, 15, 23, 31,36 38, 46, 54, 62, 39, 47, 55, 6337};38 39/* For simplicity, we keep a pre-formatted JPEG header,40 * and we'll use fixed offsets to change the width, height41 * quantization tables, etc.42 */43static const unsigned char hantro_jpeg_header[] = {44 /* SOI */45 0xff, 0xd8,46 47 /* JFIF-APP0 */48 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46,49 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01,50 0x00, 0x00,51 52 /* DQT */53 0xff, 0xdb, 0x00, 0x84,54 55 0x00,56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,64 65 0x01,66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,74 75 /* SOF */76 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01,77 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,78 0x03, 0x11, 0x01,79 80 /* DHT */81 0xff, 0xc4, 0x00, 0x1f, 0x00,82 83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,86 0x00, 0x00, 0x00, 0x00,87 88 /* DHT */89 0xff, 0xc4, 0x00, 0xb5, 0x10,90 91 0x00, 0x00,92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,93 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,114 115 /* DHT */116 0xff, 0xc4, 0x00, 0x1f, 0x01,117 118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,121 0x00, 0x00, 0x00, 0x00,122 123 /* DHT */124 0xff, 0xc4, 0x00, 0xb5, 0x11,125 126 0x00, 0x00,127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,149 150 /* COM */151 0xff, 0xfe, 0x00, 0x03, 0x00,152 153 /* SOS */154 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,155 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,156};157 158/*159 * JPEG_HEADER_SIZE is used in other parts of the driver in lieu of160 * "sizeof(hantro_jpeg_header)". The two must be equal.161 */162static_assert(sizeof(hantro_jpeg_header) == JPEG_HEADER_SIZE);163 164/*165 * hantro_jpeg_header is padded with a COM segment, so that the payload166 * of the SOS segment (the entropy-encoded image scan), which should167 * trail the whole header, is 8-byte aligned for the hardware to write168 * to directly.169 */170static_assert(IS_ALIGNED(sizeof(hantro_jpeg_header), 8),171 "Hantro JPEG header size needs to be 8-byte aligned.");172 173static unsigned char jpeg_scale_qp(const unsigned char qp, int scale)174{175 unsigned int temp;176 177 temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100);178 if (temp <= 0)179 temp = 1;180 if (temp > 255)181 temp = 255;182 183 return (unsigned char)temp;184}185 186static void187jpeg_scale_quant_table(unsigned char *file_q_tab,188 unsigned char *reordered_q_tab,189 const unsigned char *tab, int scale)190{191 int i;192 193 BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE);194 BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE);195 196 for (i = 0; i < JPEG_QUANT_SIZE; i++) {197 file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale);198 reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale);199 }200}201 202static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx)203{204 int scale;205 /*206 * Non-linear scaling factor:207 * [5,50] -> [1000..100], [51,100] -> [98..0]208 */209 if (ctx->quality < 50)210 scale = 5000 / ctx->quality;211 else212 scale = 200 - 2 * ctx->quality;213 214 BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE);215 BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE);216 BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE);217 BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE);218 219 jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF,220 ctx->hw_luma_qtable,221 (const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale);222 jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF,223 ctx->hw_chroma_qtable,224 (const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale);225}226 227void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx)228{229 char *buf = ctx->buffer;230 231 memcpy(buf, hantro_jpeg_header,232 sizeof(hantro_jpeg_header));233 234 buf[HEIGHT_OFF + 0] = ctx->height >> 8;235 buf[HEIGHT_OFF + 1] = ctx->height;236 buf[WIDTH_OFF + 0] = ctx->width >> 8;237 buf[WIDTH_OFF + 1] = ctx->width;238 239 memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN);240 memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);241 memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN);242 memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);243 244 jpeg_set_quality(ctx);245}246