--- /dev/null
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_vpu_common.h"
+
+#include "rk3288_vpu_regs.h"
+
+/*
+ * Interrupt handlers.
+ */
+
+int rk3288_vpu_enc_irq(int irq, struct rockchip_vpu_dev *vpu)
+{
+ u32 status = vepu_read(vpu, VEPU_REG_INTERRUPT);
+
+ vepu_write(vpu, 0, VEPU_REG_INTERRUPT);
+
+ if (status & VEPU_REG_INTERRUPT_BIT) {
+ vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
+ return 0;
+ }
+
+ return -1;
+}
+
+int rk3288_vpu_dec_irq(int irq, struct rockchip_vpu_dev *vpu)
+{
+ u32 status = vdpu_read(vpu, VDPU_REG_INTERRUPT);
+
+ vdpu_write(vpu, 0, VDPU_REG_INTERRUPT);
+
+ vpu_debug(3, "vdpu_irq status: %08x\n", status);
+
+ if (status & VDPU_REG_INTERRUPT_DEC_IRQ) {
+ vdpu_write(vpu, 0, VDPU_REG_CONFIG);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Initialization/clean-up.
+ */
+
+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);
+}
+
+void rk3288_vpu_dec_reset(struct rockchip_vpu_ctx *ctx)
+{
+ struct rockchip_vpu_dev *vpu = ctx->dev;
+
+ vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT);
+ vdpu_write(vpu, 0, VDPU_REG_CONFIG);
+}
#define VP8_CABAC_CTX_OFFSET 192
#define VP8_CABAC_CTX_SIZE ((55 + 96) << 3)
-#define VP8_KEY_FRAME_HDR_SIZE 10
-#define VP8_INTER_FRAME_HDR_SIZE 3
-
-#define VP8_FRAME_TAG_KEY_FRAME_BIT BIT(0)
-#define VP8_FRAME_TAG_LENGTH_SHIFT 5
-#define VP8_FRAME_TAG_LENGTH_MASK (0x7ffff << 5)
-
/**
* struct rk3288_vpu_vp8e_ctrl_buf - hardware control buffer layout
* @ext_hdr_size: Ext header size in bytes (written by hardware).
u8 rsvd[1016];
};
-/*
- * The hardware takes care only of ext hdr and dct partition. The software
- * must take care of frame header.
- *
- * Buffer layout as received from hardware:
- * |<--gap-->|<--ext hdr-->|<-gap->|<---dct part---
- * |<-------dct part offset------->|
- *
- * Required buffer layout:
- * |<--hdr-->|<--ext hdr-->|<---dct part---
- */
-void rk3288_vpu_vp8e_assemble_bitstream(struct rockchip_vpu_ctx *ctx,
- struct rockchip_vpu_buf *dst_buf)
-{
- struct vb2_v4l2_buffer *vb2_dst = to_vb2_v4l2_buffer(&dst_buf->vb.vb2_buf);
- size_t ext_hdr_size = dst_buf->vp8e.ext_hdr_size;
- size_t dct_size = dst_buf->vp8e.dct_size;
- size_t hdr_size = dst_buf->vp8e.hdr_size;
- size_t dst_size;
- size_t tag_size;
- void *dst;
- u32 *tag;
-
- dst_size = vb2_plane_size(&dst_buf->vb.vb2_buf, 0);
- dst = vb2_plane_vaddr(&dst_buf->vb.vb2_buf, 0);
- tag = dst; /* To access frame tag words. */
-
- if (WARN_ON(hdr_size + ext_hdr_size + dct_size > dst_size))
- return;
- if (WARN_ON(dst_buf->vp8e.dct_offset + dct_size > dst_size))
- return;
-
- memmove(dst + hdr_size + ext_hdr_size,
- dst + dst_buf->vp8e.dct_offset, dct_size);
- memcpy(dst, dst_buf->vp8e.header, hdr_size);
-
- /* Patch frame tag at first 32-bit word of the frame. */
- if (vb2_dst->flags & V4L2_BUF_FLAG_KEYFRAME) {
- tag_size = VP8_KEY_FRAME_HDR_SIZE;
- tag[0] &= ~VP8_FRAME_TAG_KEY_FRAME_BIT;
- } else {
- tag_size = VP8_INTER_FRAME_HDR_SIZE;
- tag[0] |= VP8_FRAME_TAG_KEY_FRAME_BIT;
- }
-
- tag[0] &= ~VP8_FRAME_TAG_LENGTH_MASK;
- tag[0] |= (hdr_size + ext_hdr_size - tag_size)
- << VP8_FRAME_TAG_LENGTH_SHIFT;
-
- vb2_set_plane_payload(&dst_buf->vb.vb2_buf, 0,
- hdr_size + ext_hdr_size + dct_size);
-}
-
static inline unsigned int ref_luma_size(unsigned int w, unsigned int h)
{
return round_up(w, MB_DIM) * round_up(h, MB_DIM);
#include <linux/dma-iommu.h>
-#include "rk3288_vpu_regs.h"
+/* Various parameters specific to VP8 encoder. */
+#define VP8_KEY_FRAME_HDR_SIZE 10
+#define VP8_INTER_FRAME_HDR_SIZE 3
+
+#define VP8_FRAME_TAG_KEY_FRAME_BIT BIT(0)
+#define VP8_FRAME_TAG_LENGTH_SHIFT 5
+#define VP8_FRAME_TAG_LENGTH_MASK (0x7ffff << 5)
/**
* struct rockchip_vpu_variant - information about VPU hardware variant
* @exit: Clean-up after streaming. Called from VB2 .stop_streaming()
* when streaming from first of both enabled queues is being
* disabled.
+ * @irq: Handle {en,de}code irq. Check and clear interrupt.
* @run: Start single {en,de)coding run. Called from non-atomic context
* to indicate that a pair of buffers is ready and the hardware
* should be programmed and started.
int (*init)(struct rockchip_vpu_ctx *);
void (*exit)(struct rockchip_vpu_ctx *);
+ int (*irq)(int, struct rockchip_vpu_dev *);
void (*run)(struct rockchip_vpu_ctx *);
void (*done)(struct rockchip_vpu_ctx *, enum vb2_buffer_state);
void (*reset)(struct rockchip_vpu_ctx *);
static irqreturn_t vepu_irq(int irq, void *dev_id)
{
struct rockchip_vpu_dev *vpu = dev_id;
- u32 status = vepu_read(vpu, VEPU_REG_INTERRUPT);
-
- vepu_write(vpu, 0, VEPU_REG_INTERRUPT);
-
- if (status & VEPU_REG_INTERRUPT_BIT) {
- struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
+ struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
- vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
+ if (!ctx->hw.codec_ops->irq(irq, vpu)) {
rockchip_vpu_power_off(vpu);
cancel_delayed_work(&vpu->watchdog_work);
static irqreturn_t vdpu_irq(int irq, void *dev_id)
{
struct rockchip_vpu_dev *vpu = dev_id;
- u32 status = vdpu_read(vpu, VDPU_REG_INTERRUPT);
-
- vdpu_write(vpu, 0, VDPU_REG_INTERRUPT);
-
- vpu_debug(3, "vdpu_irq status: %08x\n", status);
-
- if (status & VDPU_REG_INTERRUPT_DEC_IRQ) {
- struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
+ struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
- vdpu_write(vpu, 0, VDPU_REG_CONFIG);
+ if (!ctx->hw.codec_ops->irq(irq, vpu)) {
rockchip_vpu_power_off(vpu);
cancel_delayed_work(&vpu->watchdog_work);
clk_disable_unprepare(vpu->aclk_vcodec);
}
-static void rockchip_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);
-}
-
-static void rockchip_vpu_dec_reset(struct rockchip_vpu_ctx *ctx)
-{
- struct rockchip_vpu_dev *vpu = ctx->dev;
-
- vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT);
- vdpu_write(vpu, 0, VDPU_REG_CONFIG);
-}
-
static const struct rockchip_vpu_codec_ops mode_ops[] = {
[RK3288_VPU_CODEC_VP8E] = {
.init = rk3288_vpu_vp8e_init,
.exit = rk3288_vpu_vp8e_exit,
+ .irq = rk3288_vpu_enc_irq,
.run = rk3288_vpu_vp8e_run,
.done = rk3288_vpu_vp8e_done,
- .reset = rockchip_vpu_enc_reset,
+ .reset = rk3288_vpu_enc_reset,
},
[RK3288_VPU_CODEC_VP8D] = {
.init = rk3288_vpu_vp8d_init,
.exit = rk3288_vpu_vp8d_exit,
+ .irq = rk3288_vpu_dec_irq,
.run = rk3288_vpu_vp8d_run,
.done = rockchip_vpu_run_done,
- .reset = rockchip_vpu_dec_reset,
+ .reset = rk3288_vpu_dec_reset,
},
[RK3288_VPU_CODEC_H264D] = {
.init = rk3288_vpu_h264d_init,
.exit = rk3288_vpu_h264d_exit,
+ .irq = rk3288_vpu_dec_irq,
.run = rk3288_vpu_h264d_run,
.done = rockchip_vpu_run_done,
- .reset = rockchip_vpu_dec_reset,
+ .reset = rk3288_vpu_dec_reset,
},
};
{
ctx->hw.codec_ops->exit(ctx);
}
+
+/*
+ * The hardware takes care only of ext hdr and dct partition. The software
+ * must take care of frame header.
+ *
+ * Buffer layout as received from hardware:
+ * |<--gap-->|<--ext hdr-->|<-gap->|<---dct part---
+ * |<-------dct part offset------->|
+ *
+ * Required buffer layout:
+ * |<--hdr-->|<--ext hdr-->|<---dct part---
+ */
+void rockchip_vpu_vp8e_assemble_bitstream(struct rockchip_vpu_ctx *ctx,
+ struct rockchip_vpu_buf *dst_buf)
+{
+ struct vb2_v4l2_buffer *vb2_dst = to_vb2_v4l2_buffer(&dst_buf->vb.vb2_buf);
+ size_t ext_hdr_size = dst_buf->vp8e.ext_hdr_size;
+ size_t dct_size = dst_buf->vp8e.dct_size;
+ size_t hdr_size = dst_buf->vp8e.hdr_size;
+ size_t dst_size;
+ size_t tag_size;
+ void *dst;
+ u32 *tag;
+
+ dst_size = vb2_plane_size(&dst_buf->vb.vb2_buf, 0);
+ dst = vb2_plane_vaddr(&dst_buf->vb.vb2_buf, 0);
+ tag = dst; /* To access frame tag words. */
+
+ if (WARN_ON(hdr_size + ext_hdr_size + dct_size > dst_size))
+ return;
+ if (WARN_ON(dst_buf->vp8e.dct_offset + dct_size > dst_size))
+ return;
+
+ vpu_debug(1, "%s: hdr_size = %d, ext_hdr_size = %d, dct_size = %d\n",
+ __func__, hdr_size, ext_hdr_size, dct_size);
+
+ memmove(dst + hdr_size + ext_hdr_size,
+ dst + dst_buf->vp8e.dct_offset, dct_size);
+ memcpy(dst, dst_buf->vp8e.header, hdr_size);
+
+ /* Patch frame tag at first 32-bit word of the frame. */
+ if (vb2_dst->flags & V4L2_BUF_FLAG_KEYFRAME) {
+ tag_size = VP8_KEY_FRAME_HDR_SIZE;
+ tag[0] &= ~VP8_FRAME_TAG_KEY_FRAME_BIT;
+ } else {
+ tag_size = VP8_INTER_FRAME_HDR_SIZE;
+ tag[0] |= VP8_FRAME_TAG_KEY_FRAME_BIT;
+ }
+
+ tag[0] &= ~VP8_FRAME_TAG_LENGTH_MASK;
+ tag[0] |= (hdr_size + ext_hdr_size - tag_size)
+ << VP8_FRAME_TAG_LENGTH_SHIFT;
+
+ vb2_set_plane_payload(&dst_buf->vb.vb2_buf, 0,
+ hdr_size + ext_hdr_size + dct_size);
+}