// 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_LEN);
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=94 H=97 G=95