Commit 775fec69 authored by Ezequiel Garcia's avatar Ezequiel Garcia Committed by Mauro Carvalho Chehab

media: add Rockchip VPU JPEG encoder driver

Add a mem2mem driver for the VPU available on Rockchip SoCs.
Currently only JPEG encoding is supported, for RK3399 and RK3288
platforms.
Signed-off-by: default avatarEzequiel Garcia <ezequiel@collabora.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
[hverkuil-cisco@xs4all.nl: fix checkpatch.pl alignment warning]
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 7f22507b
......@@ -12841,6 +12841,13 @@ S: Maintained
F: drivers/media/platform/rockchip/rga/
F: Documentation/devicetree/bindings/media/rockchip-rga.txt
ROCKCHIP VPU CODEC DRIVER
M: Ezequiel Garcia <ezequiel@collabora.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/staging/media/platform/rockchip/vpu/
F: Documentation/devicetree/bindings/media/rockchip-vpu.txt
ROCKER DRIVER
M: Jiri Pirko <jiri@resnulli.us>
L: netdev@vger.kernel.org
......
......@@ -31,6 +31,8 @@ source "drivers/staging/media/mt9t031/Kconfig"
source "drivers/staging/media/omap4iss/Kconfig"
source "drivers/staging/media/rockchip/vpu/Kconfig"
source "drivers/staging/media/sunxi/Kconfig"
source "drivers/staging/media/tegra-vde/Kconfig"
......
......@@ -8,3 +8,4 @@ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
obj-$(CONFIG_VIDEO_ZORAN) += zoran/
obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/
config VIDEO_ROCKCHIP_VPU
tristate "Rockchip VPU driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
default n
help
Support for the Video Processing Unit present on Rockchip SoC,
which accelerates video and image encoding and decoding.
To compile this driver as a module, choose M here: the module
will be called rockchip-vpu.
obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o
rockchip-vpu-y += \
rockchip_vpu_drv.o \
rockchip_vpu_enc.o \
rk3288_vpu_hw.o \
rk3288_vpu_hw_jpeg_enc.o \
rk3399_vpu_hw.o \
rk3399_vpu_hw_jpeg_enc.o \
rockchip_vpu_jpeg.o
* Support for VP8, VP9 and H264 is planned for this driver.
Given the V4L controls for those CODECs will be part of
the uABI, it will be required to have the driver in staging.
For this reason, we are keeping this driver in staging for now.
* Add support for the S_SELECTION API.
See the comment for VEPU_REG_ENC_OVER_FILL_STRM_OFFSET.
* Instead of having a DMA bounce buffer, it could be possible to use a
normal buffer and memmove() the payload to make space for the header.
This might need to use extra JPEG markers for padding reasons.
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VPU codec driver
*
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <linux/clk.h>
#include "rockchip_vpu.h"
#include "rockchip_vpu_jpeg.h"
#include "rk3288_vpu_regs.h"
#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000)
/*
* Supported formats.
*/
static const struct rockchip_vpu_fmt rk3288_vpu_enc_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_YUV420M,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUV420P,
},
{
.fourcc = V4L2_PIX_FMT_NV12M,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUV420SP,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUYV422,
},
{
.fourcc = V4L2_PIX_FMT_UYVY,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_UYVY422,
},
{
.fourcc = V4L2_PIX_FMT_JPEG,
.codec_mode = RK_VPU_MODE_JPEG_ENC,
.max_depth = 2,
.header_size = JPEG_HEADER_SIZE,
.frmsize = {
.min_width = 96,
.max_width = 8192,
.step_width = JPEG_MB_DIM,
.min_height = 32,
.max_height = 8192,
.step_height = JPEG_MB_DIM,
},
},
};
static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id)
{
struct rockchip_vpu_dev *vpu = dev_id;
enum vb2_buffer_state state;
u32 status, bytesused;
status = vepu_read(vpu, VEPU_REG_INTERRUPT);
bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8;
state = (status & VEPU_REG_INTERRUPT_FRAME_RDY) ?
VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
vepu_write(vpu, 0, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
rockchip_vpu_irq_done(vpu, bytesused, state);
return IRQ_HANDLED;
}
static int rk3288_vpu_hw_init(struct rockchip_vpu_dev *vpu)
{
/* Bump ACLK to max. possible freq. to improve performance. */
clk_set_rate(vpu->clocks[0].clk, RK3288_ACLK_MAX_FREQ);
return 0;
}
static void rk3288_vpu_enc_reset(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_ENC_CTRL);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
}
/*
* Supported codec ops.
*/
static const struct rockchip_vpu_codec_ops rk3288_vpu_codec_ops[] = {
[RK_VPU_MODE_JPEG_ENC] = {
.run = rk3288_vpu_jpeg_enc_run,
.reset = rk3288_vpu_enc_reset,
},
};
/*
* VPU variant.
*/
const struct rockchip_vpu_variant rk3288_vpu_variant = {
.enc_offset = 0x0,
.enc_fmts = rk3288_vpu_enc_fmts,
.num_enc_fmts = ARRAY_SIZE(rk3288_vpu_enc_fmts),
.codec_ops = rk3288_vpu_codec_ops,
.codec = RK_VPU_CODEC_JPEG,
.vepu_irq = rk3288_vepu_irq,
.init = rk3288_vpu_hw_init,
.clk_names = {"aclk", "hclk"},
.num_clocks = 2
};
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VPU codec driver
*
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*/
#include <asm/unaligned.h>
#include <media/v4l2-mem2mem.h>
#include "rockchip_vpu_jpeg.h"
#include "rockchip_vpu.h"
#include "rockchip_vpu_common.h"
#include "rockchip_vpu_hw.h"
#include "rk3288_vpu_regs.h"
#define VEPU_JPEG_QUANT_TABLE_COUNT 16
static void rk3288_vpu_set_src_img_ctrl(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx)
{
struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt;
u32 reg;
reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(pix_fmt->width)
| VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(0)
| VEPU_REG_IN_IMG_CTRL_OVRFLB_D4(0)
| VEPU_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt);
vepu_write_relaxed(vpu, reg, VEPU_REG_IN_IMG_CTRL);
}
static void rk3288_vpu_jpeg_enc_set_buffers(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx,
struct vb2_buffer *src_buf)
{
struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt;
dma_addr_t src[3];
WARN_ON(pix_fmt->num_planes > 3);
vepu_write_relaxed(vpu, ctx->bounce_dma_addr,
VEPU_REG_ADDR_OUTPUT_STREAM);
vepu_write_relaxed(vpu, ctx->bounce_size,
VEPU_REG_STR_BUF_LIMIT);
if (pix_fmt->num_planes == 1) {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
/* single plane formats we supported are all interlaced */
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
} else if (pix_fmt->num_planes == 2) {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1);
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1);
} else {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1);
src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2);
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1);
vepu_write_relaxed(vpu, src[2], VEPU_REG_ADDR_IN_PLANE_2);
}
}
static void
rk3288_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu,
unsigned char *luma_qtable,
unsigned char *chroma_qtable)
{
__be32 *luma_qtable_p;
__be32 *chroma_qtable_p;
u32 reg, i;
luma_qtable_p = (__be32 *)luma_qtable;
chroma_qtable_p = (__be32 *)chroma_qtable;
for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) {
reg = get_unaligned_be32(&luma_qtable[i]);
vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i));
reg = get_unaligned_be32(&chroma_qtable[i]);
vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i));
}
}
void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
struct vb2_buffer *src_buf, *dst_buf;
struct rockchip_vpu_jpeg_ctx jpeg_ctx;
u32 reg;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
memset(&jpeg_ctx, 0, sizeof(jpeg_ctx));
jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0);
jpeg_ctx.width = ctx->dst_fmt.width;
jpeg_ctx.height = ctx->dst_fmt.height;
jpeg_ctx.quality = ctx->jpeg_quality;
rockchip_vpu_jpeg_header_assemble(&jpeg_ctx);
/* Switch to JPEG encoder mode before writing registers */
vepu_write_relaxed(vpu, VEPU_REG_ENC_CTRL_ENC_MODE_JPEG,
VEPU_REG_ENC_CTRL);
rk3288_vpu_set_src_img_ctrl(vpu, ctx);
rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf);
rk3288_vpu_jpeg_enc_set_qtable(vpu,
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0),
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1));
reg = VEPU_REG_AXI_CTRL_OUTPUT_SWAP16
| VEPU_REG_AXI_CTRL_INPUT_SWAP16
| VEPU_REG_AXI_CTRL_BURST_LEN(16)
| VEPU_REG_AXI_CTRL_OUTPUT_SWAP32
| VEPU_REG_AXI_CTRL_INPUT_SWAP32
| VEPU_REG_AXI_CTRL_OUTPUT_SWAP8
| VEPU_REG_AXI_CTRL_INPUT_SWAP8;
/* Make sure that all registers are written at this point. */
vepu_write(vpu, reg, VEPU_REG_AXI_CTRL);
reg = VEPU_REG_ENC_CTRL_WIDTH(JPEG_MB_WIDTH(ctx->src_fmt.width))
| VEPU_REG_ENC_CTRL_HEIGHT(JPEG_MB_HEIGHT(ctx->src_fmt.height))
| VEPU_REG_ENC_CTRL_ENC_MODE_JPEG
| VEPU_REG_ENC_PIC_INTRA
| VEPU_REG_ENC_CTRL_EN_BIT;
/* Kick the watchdog and start encoding */
schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000));
vepu_write(vpu, reg, VEPU_REG_ENC_CTRL);
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VPU codec driver
*
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <linux/clk.h>
#include "rockchip_vpu.h"
#include "rockchip_vpu_jpeg.h"
#include "rk3399_vpu_regs.h"
#define RK3399_ACLK_MAX_FREQ (400 * 1000 * 1000)
/*
* Supported formats.
*/
static const struct rockchip_vpu_fmt rk3399_vpu_enc_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_YUV420M,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUV420P,
},
{
.fourcc = V4L2_PIX_FMT_NV12M,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUV420SP,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_YUYV422,
},
{
.fourcc = V4L2_PIX_FMT_UYVY,
.codec_mode = RK_VPU_MODE_NONE,
.enc_fmt = RK3288_VPU_ENC_FMT_UYVY422,
},
{
.fourcc = V4L2_PIX_FMT_JPEG,
.codec_mode = RK_VPU_MODE_JPEG_ENC,
.max_depth = 2,
.header_size = JPEG_HEADER_SIZE,
.frmsize = {
.min_width = 96,
.max_width = 8192,
.step_width = JPEG_MB_DIM,
.min_height = 32,
.max_height = 8192,
.step_height = JPEG_MB_DIM,
},
},
};
static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id)
{
struct rockchip_vpu_dev *vpu = dev_id;
enum vb2_buffer_state state;
u32 status, bytesused;
status = vepu_read(vpu, VEPU_REG_INTERRUPT);
bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8;
state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ?
VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
vepu_write(vpu, 0, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
rockchip_vpu_irq_done(vpu, bytesused, state);
return IRQ_HANDLED;
}
static int rk3399_vpu_hw_init(struct rockchip_vpu_dev *vpu)
{
/* Bump ACLK to max. possible freq. to improve performance. */
clk_set_rate(vpu->clocks[0].clk, RK3399_ACLK_MAX_FREQ);
return 0;
}
static void rk3399_vpu_enc_reset(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_ENCODE_START);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
}
/*
* Supported codec ops.
*/
static const struct rockchip_vpu_codec_ops rk3399_vpu_codec_ops[] = {
[RK_VPU_MODE_JPEG_ENC] = {
.run = rk3399_vpu_jpeg_enc_run,
.reset = rk3399_vpu_enc_reset,
},
};
/*
* VPU variant.
*/
const struct rockchip_vpu_variant rk3399_vpu_variant = {
.enc_offset = 0x0,
.enc_fmts = rk3399_vpu_enc_fmts,
.num_enc_fmts = ARRAY_SIZE(rk3399_vpu_enc_fmts),
.codec = RK_VPU_CODEC_JPEG,
.codec_ops = rk3399_vpu_codec_ops,
.vepu_irq = rk3399_vepu_irq,
.init = rk3399_vpu_hw_init,
.clk_names = {"aclk", "hclk"},
.num_clocks = 2
};
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VPU codec driver
*
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*
* JPEG encoder
* ------------
* The VPU JPEG encoder produces JPEG baseline sequential format.
* The quantization coefficients are 8-bit values, complying with
* the baseline specification. Therefore, it requires
* luma and chroma quantization tables. The hardware does entropy
* encoding using internal Huffman tables, as specified in the JPEG
* specification.
*
* In other words, only the luma and chroma quantization tables are
* required for the encoding operation.
*
* Quantization luma table values are written to registers
* VEPU_swreg_0-VEPU_swreg_15, and chroma table values to
* VEPU_swreg_16-VEPU_swreg_31.
*
* JPEG zigzag order is expected on the quantization tables.
*/
#include <asm/unaligned.h>
#include <media/v4l2-mem2mem.h>
#include "rockchip_vpu_jpeg.h"
#include "rockchip_vpu.h"
#include "rockchip_vpu_common.h"
#include "rockchip_vpu_hw.h"
#include "rk3399_vpu_regs.h"
#define VEPU_JPEG_QUANT_TABLE_COUNT 16
static void rk3399_vpu_set_src_img_ctrl(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx)
{
struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt;
u32 reg;
/*
* The pix fmt width/height are already macroblock aligned
* by .vidioc_s_fmt_vid_cap_mplane() callback
*/
reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(pix_fmt->width);
vepu_write_relaxed(vpu, reg, VEPU_REG_INPUT_LUMA_INFO);
reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(0) |
VEPU_REG_IN_IMG_CTRL_OVRFLB(0);
/*
* This register controls the input crop, as the offset
* from the right/bottom within the last macroblock. The offset from the
* right must be divided by 4 and so the crop must be aligned to 4 pixels
* horizontally.
*/
vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_OVER_FILL_STRM_OFFSET);
reg = VEPU_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt);
vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_CTRL1);
}
static void rk3399_vpu_jpeg_enc_set_buffers(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx,
struct vb2_buffer *src_buf)
{
struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt;
dma_addr_t src[3];
WARN_ON(pix_fmt->num_planes > 3);
vepu_write_relaxed(vpu, ctx->bounce_dma_addr,
VEPU_REG_ADDR_OUTPUT_STREAM);
vepu_write_relaxed(vpu, ctx->bounce_size,
VEPU_REG_STR_BUF_LIMIT);
if (pix_fmt->num_planes == 1) {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
} else if (pix_fmt->num_planes == 2) {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1);
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1);
} else {
src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0);
src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1);
src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2);
vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0);
vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1);
vepu_write_relaxed(vpu, src[2], VEPU_REG_ADDR_IN_PLANE_2);
}
}
static void
rk3399_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu,
unsigned char *luma_qtable,
unsigned char *chroma_qtable)
{
__be32 *luma_qtable_p;
__be32 *chroma_qtable_p;
u32 reg, i;
luma_qtable_p = (__be32 *)luma_qtable;
chroma_qtable_p = (__be32 *)chroma_qtable;
for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) {
reg = get_unaligned_be32(&luma_qtable[i]);
vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i));
reg = get_unaligned_be32(&chroma_qtable[i]);
vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i));
}
}
void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx)
{
struct rockchip_vpu_dev *vpu = ctx->dev;
struct vb2_buffer *src_buf, *dst_buf;
struct rockchip_vpu_jpeg_ctx jpeg_ctx;
u32 reg;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
memset(&jpeg_ctx, 0, sizeof(jpeg_ctx));
jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0);
jpeg_ctx.width = ctx->dst_fmt.width;
jpeg_ctx.height = ctx->dst_fmt.height;
jpeg_ctx.quality = ctx->jpeg_quality;
rockchip_vpu_jpeg_header_assemble(&jpeg_ctx);
/* Switch to JPEG encoder mode before writing registers */
vepu_write_relaxed(vpu, VEPU_REG_ENCODE_FORMAT_JPEG,
VEPU_REG_ENCODE_START);
rk3399_vpu_set_src_img_ctrl(vpu, ctx);
rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf);
rk3399_vpu_jpeg_enc_set_qtable(vpu,
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0),
rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1));
reg = VEPU_REG_OUTPUT_SWAP32
| VEPU_REG_OUTPUT_SWAP16
| VEPU_REG_OUTPUT_SWAP8
| VEPU_REG_INPUT_SWAP8
| VEPU_REG_INPUT_SWAP16
| VEPU_REG_INPUT_SWAP32;
/* Make sure that all registers are written at this point. */
vepu_write(vpu, reg, VEPU_REG_DATA_ENDIAN);
reg = VEPU_REG_AXI_CTRL_BURST_LEN(16);
vepu_write_relaxed(vpu, reg, VEPU_REG_AXI_CTRL);
reg = VEPU_REG_MB_WIDTH(JPEG_MB_WIDTH(ctx->src_fmt.width))
| VEPU_REG_MB_HEIGHT(JPEG_MB_HEIGHT(ctx->src_fmt.height))
| VEPU_REG_FRAME_TYPE_INTRA
| VEPU_REG_ENCODE_FORMAT_JPEG
| VEPU_REG_ENCODE_ENABLE;
/* Kick the watchdog and start encoding */
schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000));
vepu_write(vpu, reg, VEPU_REG_ENCODE_START);
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip VPU codec driver
*
* Copyright 2018 Google LLC.
* Tomasz Figa <tfiga@chromium.org>
*
* Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*/
#ifndef ROCKCHIP_VPU_H_
#define ROCKCHIP_VPU_H_
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <linux/wait.h>
#include <linux/clk.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
#include "rockchip_vpu_hw.h"
#define ROCKCHIP_VPU_MAX_CLOCKS 4
#define JPEG_MB_DIM 16
#define JPEG_MB_WIDTH(w) DIV_ROUND_UP(w, JPEG_MB_DIM)
#define JPEG_MB_HEIGHT(h) DIV_ROUND_UP(h, JPEG_MB_DIM)
struct rockchip_vpu_ctx;
struct rockchip_vpu_codec_ops;
#define RK_VPU_CODEC_JPEG BIT(0)
/**
* struct rockchip_vpu_variant - information about VPU hardware variant
*
* @enc_offset: Offset from VPU base to encoder registers.
* @enc_fmts: Encoder formats.
* @num_enc_fmts: Number of encoder formats.
* @codec: Supported codecs
* @codec_ops: Codec ops.
* @init: Initialize hardware.
* @vepu_irq: encoder interrupt handler
* @clk_names: array of clock names
* @num_clocks: number of clocks in the array
*/
struct rockchip_vpu_variant {
unsigned int enc_offset;
const struct rockchip_vpu_fmt *enc_fmts;
unsigned int num_enc_fmts;
unsigned int codec;
const struct rockchip_vpu_codec_ops *codec_ops;
int (*init)(struct rockchip_vpu_dev *vpu);
irqreturn_t (*vepu_irq)(int irq, void *priv);
const char *clk_names[ROCKCHIP_VPU_MAX_CLOCKS];
int num_clocks;
};
/**
* enum rockchip_vpu_codec_mode - codec operating mode.
* @RK_VPU_MODE_NONE: No operating mode. Used for RAW video formats.
* @RK_VPU_MODE_JPEG_ENC: JPEG encoder.
*/
enum rockchip_vpu_codec_mode {
RK_VPU_MODE_NONE = -1,
RK_VPU_MODE_JPEG_ENC,
};
/**
* struct rockchip_vpu_dev - driver data
* @v4l2_dev: V4L2 device to register video devices for.
* @m2m_dev: mem2mem device associated to this device.
* @mdev: media device associated to this device.
* @vfd_enc: Video device for encoder.
* @pdev: Pointer to VPU platform device.
* @dev: Pointer to device for convenient logging using
* dev_ macros.
* @clocks: Array of clock handles.
* @base: Mapped address of VPU registers.
* @enc_base: Mapped address of VPU encoder register for convenience.
* @vpu_mutex: Mutex to synchronize V4L2 calls.
* @irqlock: Spinlock to synchronize access to data structures
* shared with interrupt handlers.
* @variant: Hardware variant-specific parameters.
* @watchdog_work: Delayed work for hardware timeout handling.
*/
struct rockchip_vpu_dev {
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct media_device mdev;
struct video_device *vfd_enc;
struct platform_device *pdev;
struct device *dev;
struct clk_bulk_data clocks[ROCKCHIP_VPU_MAX_CLOCKS];
void __iomem *base;
void __iomem *enc_base;
struct mutex vpu_mutex; /* video_device lock */
spinlock_t irqlock;
const struct rockchip_vpu_variant *variant;
struct delayed_work watchdog_work;
};
/**
* struct rockchip_vpu_ctx - Context (instance) private data.
*
* @dev: VPU driver data to which the context belongs.
* @fh: V4L2 file handler.
*
* @sequence_cap: Sequence counter for capture queue
* @sequence_out: Sequence counter for output queue
*
* @vpu_src_fmt: Descriptor of active source format.
* @src_fmt: V4L2 pixel format of active source format.
* @vpu_dst_fmt: Descriptor of active destination format.
* @dst_fmt: V4L2 pixel format of active destination format.
*
* @ctrl_handler: Control handler used to register controls.
* @jpeg_quality: User-specified JPEG compression quality.
*
* @codec_ops: Set of operations related to codec mode.
*
* @bounce_dma_addr: Bounce buffer bus address.
* @bounce_buf: Bounce buffer pointer.
* @bounce_size: Bounce buffer size.
*/
struct rockchip_vpu_ctx {
struct rockchip_vpu_dev *dev;
struct v4l2_fh fh;
u32 sequence_cap;
u32 sequence_out;
const struct rockchip_vpu_fmt *vpu_src_fmt;
struct v4l2_pix_format_mplane src_fmt;
const struct rockchip_vpu_fmt *vpu_dst_fmt;
struct v4l2_pix_format_mplane dst_fmt;
struct v4l2_ctrl_handler ctrl_handler;
int jpeg_quality;
const struct rockchip_vpu_codec_ops *codec_ops;
dma_addr_t bounce_dma_addr;
void *bounce_buf;
size_t bounce_size;
};
/**
* struct rockchip_vpu_fmt - information about supported video formats.
* @name: Human readable name of the format.
* @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*.
* @codec_mode: Codec mode related to this format. See
* enum rockchip_vpu_codec_mode.
* @header_size: Optional header size. Currently used by JPEG encoder.
* @max_depth: Maximum depth, for bitstream formats
* @enc_fmt: Format identifier for encoder registers.
* @frmsize: Supported range of frame sizes (only for bitstream formats).
*/
struct rockchip_vpu_fmt {
char *name;
u32 fourcc;
enum rockchip_vpu_codec_mode codec_mode;
int header_size;
int max_depth;
enum rockchip_vpu_enc_fmt enc_fmt;
struct v4l2_frmsize_stepwise frmsize;
};
/* Logging helpers */
/**
* debug - Module parameter to control level of debugging messages.
*
* Level of debugging messages can be controlled by bits of
* module parameter called "debug". Meaning of particular
* bits is as follows:
*
* bit 0 - global information: mode, size, init, release
* bit 1 - each run start/result information
* bit 2 - contents of small controls from userspace
* bit 3 - contents of big controls from userspace
* bit 4 - detail fmt, ctrl, buffer q/dq information
* bit 5 - detail function enter/leave trace information
* bit 6 - register write/read information
*/
extern int rockchip_vpu_debug;
#define vpu_debug(level, fmt, args...) \
do { \
if (rockchip_vpu_debug & BIT(level)) \
pr_info("%s:%d: " fmt, \
__func__, __LINE__, ##args); \
} while (0)
#define vpu_err(fmt, args...) \
pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
/* Structure access helpers. */
static inline struct rockchip_vpu_ctx *fh_to_ctx(struct v4l2_fh *fh)
{
return container_of(fh, struct rockchip_vpu_ctx, fh);
}
/* Register accessors. */
static inline void vepu_write_relaxed(struct rockchip_vpu_dev *vpu,
u32 val, u32 reg)
{
vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val);
writel_relaxed(val, vpu->enc_base + reg);
}
static inline void vepu_write(struct rockchip_vpu_dev *vpu, u32 val, u32 reg)
{
vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val);
writel(val, vpu->enc_base + reg);
}
static inline u32 vepu_read(struct rockchip_vpu_dev *vpu, u32 reg)
{
u32 val = readl(vpu->enc_base + reg);
vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val);
return val;
}
#endif /* ROCKCHIP_VPU_H_ */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip VPU codec driver
*
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
* Alpha Lin <Alpha.Lin@rock-chips.com>
* Jeffy Chen <jeffy.chen@rock-chips.com>
*
* Copyright 2018 Google LLC.
* Tomasz Figa <tfiga@chromium.org>
*
* Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*/
#ifndef ROCKCHIP_VPU_COMMON_H_
#define ROCKCHIP_VPU_COMMON_H_
#include "rockchip_vpu.h"
extern const struct v4l2_ioctl_ops rockchip_vpu_enc_ioctl_ops;
extern const struct vb2_ops rockchip_vpu_enc_queue_ops;
void rockchip_vpu_enc_reset_src_fmt(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx);
void rockchip_vpu_enc_reset_dst_fmt(struct rockchip_vpu_dev *vpu,
struct rockchip_vpu_ctx *ctx);
#endif /* ROCKCHIP_VPU_COMMON_H_ */
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip VPU codec driver
*
* Copyright 2018 Google LLC.
* Tomasz Figa <tfiga@chromium.org>
*/
#ifndef ROCKCHIP_VPU_HW_H_
#define ROCKCHIP_VPU_HW_H_
#include <linux/interrupt.h>
#include <linux/v4l2-controls.h>
#include <media/videobuf2-core.h>
struct rockchip_vpu_dev;
struct rockchip_vpu_ctx;
struct rockchip_vpu_buf;
struct rockchip_vpu_variant;
/**
* struct rockchip_vpu_codec_ops - codec mode specific operations
*
* @run: Start single {en,de)coding job. Called from atomic context
* to indicate that a pair of buffers is ready and the hardware
* should be programmed and started.
* @done: Read back processing results and additional data from hardware.
* @reset: Reset the hardware in case of a timeout.
*/
struct rockchip_vpu_codec_ops {
void (*run)(struct rockchip_vpu_ctx *ctx);
void (*done)(struct rockchip_vpu_ctx *ctx, enum vb2_buffer_state);
void (*reset)(struct rockchip_vpu_ctx *ctx);
};
/**
* enum rockchip_vpu_enc_fmt - source format ID for hardware registers.
*/
enum rockchip_vpu_enc_fmt {
RK3288_VPU_ENC_FMT_YUV420P = 0,
RK3288_VPU_ENC_FMT_YUV420SP = 1,
RK3288_VPU_ENC_FMT_YUYV422 = 2,
RK3288_VPU_ENC_FMT_UYVY422 = 3,
};
extern const struct rockchip_vpu_variant rk3399_vpu_variant;
extern const struct rockchip_vpu_variant rk3288_vpu_variant;
void rockchip_vpu_watchdog(struct work_struct *work);
void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx);
void rockchip_vpu_irq_done(struct rockchip_vpu_dev *vpu,
unsigned int bytesused,
enum vb2_buffer_state result);
void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx);
void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx);
#endif /* ROCKCHIP_VPU_HW_H_ */
// 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/kernel.h>
#include <linux/string.h>
#include "rockchip_vpu_jpeg.h"
#define LUMA_QUANT_OFF 7
#define CHROMA_QUANT_OFF 72
#define HEIGHT_OFF 141
#define WIDTH_OFF 143
#define HUFF_LUMA_DC_OFF 160
#define HUFF_LUMA_AC_OFF 193
#define HUFF_CHROMA_DC_OFF 376
#define HUFF_CHROMA_AC_OFF 409
/* Default tables from JPEG ITU-T.81
* (ISO/IEC 10918-1) Annex K.3, I
*/
static const unsigned char luma_q_table[] = {
0x10, 0x0b, 0x0a, 0x10, 0x7c, 0x8c, 0x97, 0xa1,
0x0c, 0x0c, 0x0e, 0x13, 0x7e, 0x9e, 0xa0, 0x9b,
0x0e, 0x0d, 0x10, 0x18, 0x8c, 0x9d, 0xa9, 0x9c,
0x0e, 0x11, 0x16, 0x1d, 0x97, 0xbb, 0xb4, 0xa2,
0x12, 0x16, 0x25, 0x38, 0xa8, 0x6d, 0x67, 0xb1,
0x18, 0x23, 0x37, 0x40, 0xb5, 0x68, 0x71, 0xc0,
0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65,
0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0xc7,
};
static const unsigned char chroma_q_table[] = {
0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63,
0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63,
0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63,
0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
};
/* Huffman tables are shared with CODA */
static const unsigned char luma_dc_table[] = {
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
};
static const unsigned char chroma_dc_table[] = {
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
};
static const unsigned char luma_ac_table[] = {
0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa,
};
static const unsigned char chroma_ac_table[] = {
0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa,
};
/* 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 rockchip_vpu_jpeg_header[JPEG_HEADER_SIZE] = {
/* SOI */
0xff, 0xd8,
/* DQT */
0xff, 0xdb, 0x00, 0x84,
0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* SOF */
0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01,
0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,
0x03, 0x11, 0x01,
/* DHT */
0xff, 0xc4, 0x00, 0x1f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
/* DHT */
0xff, 0xc4, 0x00, 0xb5, 0x10,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* DHT */
0xff, 0xc4, 0x00, 0x1f, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
/* DHT */
0xff, 0xc4, 0x00, 0xb5, 0x11,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* SOS */
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
};
static void
jpeg_scale_quant_table(unsigned char *q_tab,
const unsigned char *tab, int scale)
{
unsigned int temp;
int i;
for (i = 0; i < 64; i++) {
temp = DIV_ROUND_CLOSEST((unsigned int)tab[i] * scale, 100);
if (temp <= 0)
temp = 1;
if (temp > 255)
temp = 255;
q_tab[i] = (unsigned char)temp;
}
}
static void jpeg_set_quality(unsigned char *buffer, int quality)
{
int scale;
/*
* Non-linear scaling factor:
* [5,50] -> [1000..100], [51,100] -> [98..0]
*/
if (quality < 50)
scale = 5000 / quality;
else
scale = 200 - 2 * quality;
jpeg_scale_quant_table(buffer + LUMA_QUANT_OFF,
luma_q_table, scale);
jpeg_scale_quant_table(buffer + CHROMA_QUANT_OFF,
chroma_q_table, scale);
}
unsigned char *
rockchip_vpu_jpeg_get_qtable(struct rockchip_vpu_jpeg_ctx *ctx, int index)
{
if (index == 0)
return ctx->buffer + LUMA_QUANT_OFF;
return ctx->buffer + CHROMA_QUANT_OFF;
}
void rockchip_vpu_jpeg_header_assemble(struct rockchip_vpu_jpeg_ctx *ctx)
{
char *buf = ctx->buffer;
memcpy(buf, rockchip_vpu_jpeg_header,
sizeof(rockchip_vpu_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, luma_dc_table, sizeof(luma_dc_table));
memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table));
memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table,
sizeof(chroma_dc_table));
memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table,
sizeof(chroma_ac_table));
jpeg_set_quality(buf, ctx->quality);
}
/* SPDX-License-Identifier: GPL-2.0+ */
#define JPEG_HEADER_SIZE 601
struct rockchip_vpu_jpeg_ctx {
int width;
int height;
int quality;
unsigned char *buffer;
};
unsigned char *
rockchip_vpu_jpeg_get_qtable(struct rockchip_vpu_jpeg_ctx *ctx, int index);
void rockchip_vpu_jpeg_header_assemble(struct rockchip_vpu_jpeg_ctx *ctx);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment