// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) Collabora, Ltd.
*
* Based on GSPCA and CODA drivers:
* Copyright (C) Jean-Francois Moine (http://moinejf.free.fr)
* Copyright (C) 2014 Philipp Zabel, Pengutronix
*/
#include <linux/align.h>
#include <linux/build_bug.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <media/v4l2-jpeg.h>
#include "hantro_jpeg.h"
#include "hantro.h"
#define LUMA_QUANT_OFF
25
#define CHROMA_QUANT_OFF
90
#define HEIGHT_OFF
159
#define WIDTH_OFF
161
#define HUFF_LUMA_DC_OFF
178
#define HUFF_LUMA_AC_OFF
211
#define HUFF_CHROMA_DC_OFF
394
#define HUFF_CHROMA_AC_OFF
427
static const u32 hw_reorder[] = {
0 ,
8 ,
16 ,
24 ,
1 ,
9 ,
17 ,
25 ,
32 ,
40 ,
48 ,
56 ,
33 ,
41 ,
49 ,
57 ,
2 ,
10 ,
18 ,
26 ,
3 ,
11 ,
19 ,
27 ,
34 ,
42 ,
50 ,
58 ,
35 ,
43 ,
51 ,
59 ,
4 ,
12 ,
20 ,
28 ,
5 ,
13 ,
21 ,
29 ,
36 ,
44 ,
52 ,
60 ,
37 ,
45 ,
53 ,
61 ,
6 ,
14 ,
22 ,
30 ,
7 ,
15 ,
23 ,
31 ,
38 ,
46 ,
54 ,
62 ,
39 ,
47 ,
55 ,
63
};
/* For simplicity, we keep a pre-formatted JPEG header,
* and we'll use fixed offsets to change the width, height
* quantization tables, etc.
*/
static const unsigned char hantro_jpeg_header[] = {
/* SOI */
0 xff,
0 xd8,
/* JFIF-APP0 */
0 xff,
0 xe0,
0 x00,
0 x10,
0 x4a,
0 x46,
0 x49,
0 x46,
0 x00,
0 x01,
0 x01,
0 x00,
0 x00,
0 x01,
0 x00,
0 x01,
0 x00,
0 x00,
/* DQT */
0 xff,
0 xdb,
0 x00,
0 x84,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x01,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* SOF */
0 xff,
0 xc0,
0 x00,
0 x11,
0 x08,
0 x00,
0 xf0,
0 x01,
0 x40,
0 x03,
0 x01,
0 x22,
0 x00,
0 x02,
0 x11,
0 x01,
0 x03,
0 x11,
0 x01,
/* DHT */
0 xff,
0 xc4,
0 x00,
0 x1f,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* DHT */
0 xff,
0 xc4,
0 x00,
0 xb5,
0 x10,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* DHT */
0 xff,
0 xc4,
0 x00,
0 x1f,
0 x01,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* DHT */
0 xff,
0 xc4,
0 x00,
0 xb5,
0 x11,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* COM */
0 xff,
0 xfe,
0 x00,
0 x03,
0 x00,
/* SOS */
0 xff,
0 xda,
0 x00,
0 x0c,
0 x03,
0 x01,
0 x00,
0 x02,
0 x11,
0 x03,
0 x11,
0 x00,
0 x3f,
0 x00,
};
/*
* JPEG_HEADER_SIZE is used in other parts of the driver in lieu of
* "sizeof(hantro_jpeg_header)". The two must be equal.
*/
static_assert(
sizeof (hantro_jpeg_header) == JPEG_HEADER_SIZE);
/*
* hantro_jpeg_header is padded with a COM segment, so that the payload
* of the SOS segment (the entropy-encoded image scan), which should
* trail the whole header, is 8-byte aligned for the hardware to write
* to directly.
*/
static_assert(IS_ALIGNED(
sizeof (hantro_jpeg_header),
8 ),
"Hantro JPEG header size needs to be 8-byte aligned." );
static unsigned char jpeg_scale_qp(
const unsigned char qp,
int scale)
{
unsigned int temp;
temp = DIV_ROUND_CLOSEST((
unsigned int )qp * scale,
100 );
if (temp <=
0 )
temp =
1 ;
if (temp >
255 )
temp =
255 ;
return (
unsigned char )temp;
}
static void
jpeg_scale_quant_table(
unsigned char *file_q_tab,
unsigned char *reordered_q_tab,
const unsigned char *tab,
int scale)
{
int i;
BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE);
for (i =
0 ; i < JPEG_QUANT_SIZE; i++) {
file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale);
reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale);
}
}
static void jpeg_set_quality(
struct hantro_jpeg_ctx *ctx)
{
int scale;
/*
* Non-linear scaling factor:
* [5,50] -> [1000..100], [51,100] -> [98..0]
*/
if (ctx->quality <
50 )
scale =
5000 / ctx->quality;
else
scale =
200 -
2 * ctx->quality;
BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE);
jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF,
ctx->hw_luma_qtable,
(
const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale);
jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF,
ctx->hw_chroma_qtable,
(
const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale);
}
void hantro_jpeg_header_assemble(
struct hantro_jpeg_ctx *ctx)
{
char *buf = ctx->buffer;
memcpy(buf, hantro_jpeg_header,
sizeof (hantro_jpeg_header));
buf[HEIGHT_OFF +
0 ] = ctx->height >>
8 ;
buf[HEIGHT_OFF +
1 ] = ctx->height;
buf[WIDTH_OFF +
0 ] = ctx->width >>
8 ;
buf[WIDTH_OFF +
1 ] = ctx->width;
memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_L
EN);
memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);
memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN);
memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);
jpeg_set_quality(ctx);
}
Messung V0.5 in Prozent C=95 H=89 G=91
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland