Skip to content

Commit

Permalink
media: add Rockchip VPU JPEG encoder driver
Browse files Browse the repository at this point in the history
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: Ezequiel Garcia <ezequiel@collabora.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
[hverkuil-cisco@xs4all.nl: fix checkpatch.pl alignment warning]
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
  • Loading branch information
Ezequiel Garcia authored and Mauro Carvalho Chehab committed Dec 5, 2018
1 parent 7f22507 commit 775fec6
Show file tree
Hide file tree
Showing 19 changed files with 3,454 additions and 0 deletions.
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions drivers/staging/media/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions drivers/staging/media/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
13 changes: 13 additions & 0 deletions drivers/staging/media/rockchip/vpu/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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.
10 changes: 10 additions & 0 deletions drivers/staging/media/rockchip/vpu/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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
13 changes: 13 additions & 0 deletions drivers/staging/media/rockchip/vpu/TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
* 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.
118 changes: 118 additions & 0 deletions drivers/staging/media/rockchip/vpu/rk3288_vpu_hw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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
};
130 changes: 130 additions & 0 deletions drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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);
}
Loading

0 comments on commit 775fec6

Please sign in to comment.