Although I really want to push those work after I added more codec
supports, but I found it is more urge to do those in v4l2 core framework and
userspace.
I would use this driver to present the current problems, write down a
summary here and I would reply to those threads later to push forward.
1. Slice construction is a bad idea. I think I have said my reason in
the IRC and mail before, vp9 is always good example.
And it would request the driver to update QP table/CABAC table every
slice.
I would make something to describe a buffer with some addtional meta
data.
But the current request API limit a buffer with associated with
an request buffer, which prevent sharing some sequence data, but it
still can solve some problems.
2. Advantage DMA memory control.
I think I need to do some work at v4l2 core.
2.1 The DMA address of each planes. I have sent a mail before talked
about why multiple planes is necessary for the rockchip platform. And
it maybe required by the other platforms.
2.2 IOMMU resume
The most effective way to restore the decoder from critical error is
doing a restting by reset controller.
Which would leading its slave IOMMU reset at the same time. Then none of
those v4l2 buffers are mapping in the IOMMU.
3. H.264 and HEVC header
I still think those structure have some not necessary fileds in dpb or
reference part, which I don't think hardware decoder would care about
that or can be predict from the other information.
I would join to talk later.
4. The work flow of V4L2
I need a method to prepare the register set before the device acutally
begin the transaction. Which is necessary for those high frame rate usecase.
Also it is useful for those device would share some hardware resources
with the other device and it can save more power.
I think I need to do some work at v4l2 core.
Randy Li (6):
arm64: dts: rockchip: add power domain to iommu
staging: video: rockchip: add v4l2 decoder
[TEST]: rockchip: mpp: support qptable
staging: video: rockchip: add video codec
arm64: dts: rockchip: boost clocks for rk3328
arm64: dts: rockchip: add video codec for rk3328
.../arm64/boot/dts/rockchip/rk3328-rock64.dts | 32 +
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 116 +-
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/rockchip-mpp/Kconfig | 34 +
drivers/staging/rockchip-mpp/Makefile | 10 +
drivers/staging/rockchip-mpp/mpp_debug.h | 87 ++
drivers/staging/rockchip-mpp/mpp_dev_common.c | 1367 +++++++++++++++++
drivers/staging/rockchip-mpp/mpp_dev_common.h | 212 +++
drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c | 997 ++++++++++++
drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 619 ++++++++
drivers/staging/rockchip-mpp/mpp_service.c | 197 +++
drivers/staging/rockchip-mpp/mpp_service.h | 38 +
drivers/staging/rockchip-mpp/rkvdec/hal.h | 63 +
drivers/staging/rockchip-mpp/rkvdec/hevc.c | 166 ++
drivers/staging/rockchip-mpp/rkvdec/regs.h | 608 ++++++++
drivers/staging/rockchip-mpp/vdpu2/hal.h | 52 +
drivers/staging/rockchip-mpp/vdpu2/mpeg2.c | 277 ++++
drivers/staging/rockchip-mpp/vdpu2/regs.h | 699 +++++++++
20 files changed, 5575 insertions(+), 4 deletions(-)
create mode 100644 drivers/staging/rockchip-mpp/Kconfig
create mode 100644 drivers/staging/rockchip-mpp/Makefile
create mode 100644 drivers/staging/rockchip-mpp/mpp_debug.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_service.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_service.h
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hal.h
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hevc.c
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/regs.h
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/hal.h
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/regs.h
--
2.20.1
I don't care, I don't want to store buffers for a session.
I just want to use it to verify the FFmpeg.
I want the memory region !!!
It can save more time if those data are prepared in userspace.
Signed-off-by: Randy Li <[email protected]>
---
drivers/staging/rockchip-mpp/mpp_dev_common.c | 3 +--
drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 3 +++
drivers/staging/rockchip-mpp/vdpu2/mpeg2.c | 19 +++++++++++++------
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_common.c b/drivers/staging/rockchip-mpp/mpp_dev_common.c
index c43304c3e7b8..1291b642179e 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_common.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.c
@@ -1193,8 +1193,7 @@ static int rockchip_mpp_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->drv_priv = session;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
- DMA_ATTR_NO_KERNEL_MAPPING;
+ src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->min_buffers_needed = 1;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
index ce98aa15025e..00413936623e 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
@@ -147,6 +147,9 @@ static int rkvdpu_s_fmt_vid_out_mplane(struct file *filp, void *priv,
if (sizes >= SZ_16M)
return -EINVAL;
+ /* For those slice header data */
+ pix_mp->plane_fmt[pix_mp->num_planes - 1].sizeimage += SZ_1M;
+
session->fmt_out = *pix_mp;
/* Copy the pixel format information from OUTPUT to CAPUTRE */
diff --git a/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c b/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
index d32958c4cb20..c12d1a8ef2da 100644
--- a/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
+++ b/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
@@ -52,7 +52,7 @@ static const u8 intra_default_q_matrix[64] = {
static void mpeg2_dec_copy_qtable(u8 * qtable, const struct v4l2_ctrl_mpeg2_quantization
*ctrl)
{
- int i, n;
+ int i;
if (!qtable || !ctrl)
return;
@@ -111,21 +111,22 @@ int rkvdpu_mpeg2_gen_reg(struct mpp_session *session, void *regs,
struct vb2_v4l2_buffer *src_buf)
{
const struct v4l2_ctrl_mpeg2_slice_params *params;
- const struct v4l2_ctrl_mpeg2_quantization *quantization;
const struct v4l2_mpeg2_sequence *sequence;
const struct v4l2_mpeg2_picture *picture;
+ const struct v4l2_ctrl_mpeg2_quantization *quantization;
struct vdpu2_regs *p_regs = regs;
+ void *qtable = NULL;
+ size_t stream_len = 0;
params = rockchip_mpp_get_cur_ctrl(session,
V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS);
- quantization = rockchip_mpp_get_cur_ctrl(session,
- V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION);
-
if (!params)
return -EINVAL;
sequence = ¶ms->sequence;
picture = ¶ms->picture;
+ quantization = rockchip_mpp_get_cur_ctrl(session,
+ V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION);
init_hw_cfg(p_regs);
@@ -202,7 +203,13 @@ int rkvdpu_mpeg2_gen_reg(struct mpp_session *session, void *regs,
p_regs->sw64.rlc_vlc_base =
vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
p_regs->sw122.strm_start_bit = params->data_bit_offset;
- p_regs->sw51.stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+ stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+ p_regs->sw51.stream_len = stream_len;
+
+ qtable = vb2_plane_vaddr(&src_buf->vb2_buf, 0) + ALIGN(stream_len, 8);
+ mpeg2_dec_copy_qtable(qtable, quantization);
+ p_regs->sw61.qtable_base = p_regs->sw64.rlc_vlc_base
+ + ALIGN(stream_len, 8);
return 0;
}
--
2.20.1
From: Randy Li <[email protected]>
Signed-off-by: Randy Li <[email protected]>
---
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 4 ++++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 ++
2 files changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
index 84f14b132e8f..117d02e21932 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -581,6 +581,7 @@
clocks = <&cru ACLK_H265>, <&cru PCLK_H265>;
clock-names = "aclk", "iface";
#iommu-cells = <0>;
+ power-domains = <&power RK3328_PD_HEVC>;
status = "disabled";
};
@@ -592,6 +593,7 @@
clocks = <&cru ACLK_VPU>, <&cru HCLK_VPU>;
clock-names = "aclk", "iface";
#iommu-cells = <0>;
+ power-domains = <&power RK3328_PD_HEVC>;
status = "disabled";
};
@@ -603,6 +605,7 @@
clocks = <&cru ACLK_VPU>, <&cru HCLK_VPU>;
clock-names = "aclk", "iface";
#iommu-cells = <0>;
+ power-domains = <&power RK3328_PD_VPU>;
status = "disabled";
};
@@ -614,6 +617,7 @@
clocks = <&cru ACLK_RKVDEC>, <&cru HCLK_RKVDEC>;
clock-names = "aclk", "iface";
#iommu-cells = <0>;
+ power-domains = <&power RK3328_PD_VIDEO>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index db9d948c0b03..23d061ab6fa6 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1263,6 +1263,7 @@
clock-names = "aclk", "iface";
#iommu-cells = <0>;
power-domains = <&power RK3399_PD_VCODEC>;
+ status = "disabled";
};
vdec_mmu: iommu@ff660480 {
@@ -1273,6 +1274,7 @@
clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>;
clock-names = "aclk", "iface";
#iommu-cells = <0>;
+ power-domains = <&power RK3399_PD_VDU>;
status = "disabled";
};
--
2.20.1
From: Randy Li <[email protected]>
Signed-off-by: Randy Li <[email protected]>
---
drivers/staging/Kconfig | 2 ++
drivers/staging/Makefile | 1 +
2 files changed, 3 insertions(+)
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index c0901b96cfe4..5f84035e2a89 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -124,4 +124,6 @@ source "drivers/staging/axis-fifo/Kconfig"
source "drivers/staging/erofs/Kconfig"
+source "drivers/staging/rockchip-mpp/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 57c6bce13ff4..fc3ed97a0eab 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -52,3 +52,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_EROFS_FS) += erofs/
+obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += rockchip-mpp/
--
2.20.1
From: Randy Li <[email protected]>
It is based on the vendor driver sent to mail list before.
Only MPEG2 video for VDPU2 works. And it need a patch to
fill the QP table.
I have finished the register table of the rkvdec and rk hevc
decoder. But I can't feed its proper input stream, I will
talk the reason below.
The slice header reconstruction is not acceptable,
even I don't care about VP9 now, the reference picture set
for HEVC is very complex.
Signed-off-by: Randy Li <[email protected]>
Signed-off-by: Randy Li <[email protected]>
---
drivers/staging/rockchip-mpp/Kconfig | 34 +
drivers/staging/rockchip-mpp/Makefile | 10 +
drivers/staging/rockchip-mpp/mpp_debug.h | 87 ++
drivers/staging/rockchip-mpp/mpp_dev_common.c | 1368 +++++++++++++++++
drivers/staging/rockchip-mpp/mpp_dev_common.h | 212 +++
drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c | 997 ++++++++++++
drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 616 ++++++++
drivers/staging/rockchip-mpp/mpp_service.c | 197 +++
drivers/staging/rockchip-mpp/mpp_service.h | 38 +
drivers/staging/rockchip-mpp/rkvdec/hal.h | 63 +
drivers/staging/rockchip-mpp/rkvdec/hevc.c | 166 ++
drivers/staging/rockchip-mpp/rkvdec/regs.h | 608 ++++++++
drivers/staging/rockchip-mpp/vdpu2/hal.h | 52 +
drivers/staging/rockchip-mpp/vdpu2/mpeg2.c | 270 ++++
drivers/staging/rockchip-mpp/vdpu2/regs.h | 699 +++++++++
15 files changed, 5417 insertions(+)
create mode 100644 drivers/staging/rockchip-mpp/Kconfig
create mode 100644 drivers/staging/rockchip-mpp/Makefile
create mode 100644 drivers/staging/rockchip-mpp/mpp_debug.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_service.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_service.h
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hal.h
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hevc.c
create mode 100644 drivers/staging/rockchip-mpp/rkvdec/regs.h
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/hal.h
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
create mode 100644 drivers/staging/rockchip-mpp/vdpu2/regs.h
diff --git a/drivers/staging/rockchip-mpp/Kconfig b/drivers/staging/rockchip-mpp/Kconfig
new file mode 100644
index 000000000000..75eaa6a2541a
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/Kconfig
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "ROCKCHIP_MPP"
+ depends on ARCH_ROCKCHIP
+
+config ROCKCHIP_MPP_SERVICE
+ tristate "mpp service scheduler"
+ default n
+ help
+ rockchip mpp service.
+
+config ROCKCHIP_MPP_DEVICE
+ tristate "mpp device framework"
+ depends on ROCKCHIP_MPP_SERVICE
+ select V4L2_MEM2MEM_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ help
+ rockchip mpp device framework.
+
+config ROCKCHIP_MPP_VDEC_DEVICE
+ tristate "video decoder device driver"
+ depends on ROCKCHIP_MPP_DEVICE
+ default n
+ help
+ rockchip mpp video decoder and hevc decoder.
+
+config ROCKCHIP_MPP_VDPU2_DEVICE
+ tristate "VPU decoder v2 device driver"
+ depends on ROCKCHIP_MPP_DEVICE
+ default n
+ help
+ rockchip mpp vpu decoder v2.
+
+endmenu
diff --git a/drivers/staging/rockchip-mpp/Makefile b/drivers/staging/rockchip-mpp/Makefile
new file mode 100644
index 000000000000..c35ea3a13461
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+rk-mpp-service-objs := mpp_service.o
+rk-mpp-device-objs := mpp_dev_common.o
+rk-mpp-vdec-objs := mpp_dev_rkvdec.o rkvdec/hevc.o
+rk-mpp-vdpu2-objs := mpp_dev_vdpu2.o vdpu2/mpeg2.o
+
+obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += rk-mpp-service.o
+obj-$(CONFIG_ROCKCHIP_MPP_DEVICE) += rk-mpp-device.o
+obj-$(CONFIG_ROCKCHIP_MPP_VDEC_DEVICE) += rk-mpp-vdec.o
+obj-$(CONFIG_ROCKCHIP_MPP_VDPU2_DEVICE) += rk-mpp-vdpu2.o
diff --git a/drivers/staging/rockchip-mpp/mpp_debug.h b/drivers/staging/rockchip-mpp/mpp_debug.h
new file mode 100644
index 000000000000..bd6c0e594da3
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_debug.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ROCKCHIP_MPP_DEBUG_H_
+#define _ROCKCHIP_MPP_DEBUG_H_
+
+#include <linux/types.h>
+
+/*
+ * debug flag usage:
+ * +------+-------------------+
+ * | 8bit | 24bit |
+ * +------+-------------------+
+ * 0~23 bit is for different information type
+ * 24~31 bit is for information print format
+ */
+
+#define DEBUG_POWER 0x00000001
+#define DEBUG_CLOCK 0x00000002
+#define DEBUG_IRQ_STATUS 0x00000004
+#define DEBUG_IOMMU 0x00000008
+#define DEBUG_IOCTL 0x00000010
+#define DEBUG_FUNCTION 0x00000020
+#define DEBUG_REGISTER 0x00000040
+#define DEBUG_EXTRA_INFO 0x00000080
+#define DEBUG_TIMING 0x00000100
+#define DEBUG_TASK_INFO 0x00000200
+#define DEBUG_DUMP_ERR_REG 0x00000400
+#define DEBUG_LINK_TABLE 0x00000800
+
+#define DEBUG_SET_REG 0x00001000
+#define DEBUG_GET_REG 0x00002000
+#define DEBUG_PPS_FILL 0x00004000
+#define DEBUG_IRQ_CHECK 0x00008000
+#define DEBUG_CACHE_32B 0x00010000
+
+#define DEBUG_RESET 0x00020000
+
+#define PRINT_FUNCTION 0x80000000
+#define PRINT_LINE 0x40000000
+
+#define mpp_debug_func(type, fmt, args...) \
+ do { \
+ if (unlikely(debug & type)) { \
+ pr_info("%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } \
+ } while (0)
+#define mpp_debug(type, fmt, args...) \
+ do { \
+ if (unlikely(debug & type)) { \
+ pr_info(fmt, ##args); \
+ } \
+ } while (0)
+
+#define mpp_debug_enter() \
+ do { \
+ if (unlikely(debug & DEBUG_FUNCTION)) { \
+ pr_info("%s:%d: enter\n", \
+ __func__, __LINE__); \
+ } \
+ } while (0)
+
+#define mpp_debug_leave() \
+ do { \
+ if (unlikely(debug & DEBUG_FUNCTION)) { \
+ pr_info("%s:%d: leave\n", \
+ __func__, __LINE__); \
+ } \
+ } while (0)
+
+#define mpp_err(fmt, args...) \
+ pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
+
+#endif
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_common.c b/drivers/staging/rockchip-mpp/mpp_dev_common.c
new file mode 100644
index 000000000000..c43304c3e7b8
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.c
@@ -0,0 +1,1368 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ * Randy Li, <[email protected]>
+ *
+ * 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 <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mpp_debug.h"
+#include "mpp_dev_common.h"
+#include "mpp_service.h"
+
+#define MPP_TIMEOUT_DELAY (2000)
+#include "mpp_dev_common.h"
+
+#define MPP_SESSION_MAX_DONE_TASK (20)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for mpp device debug information");
+
+static struct class *mpp_device_class;
+
+static int rockchip_mpp_result(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *task);
+
+static const struct media_device_ops mpp_m2m_media_ops = {
+ .req_validate = vb2_request_validate,
+ .req_queue = v4l2_m2m_request_queue,
+};
+
+static void mpp_session_push_pending(struct mpp_session *session,
+ struct mpp_task *task)
+{
+ mutex_lock(&session->lock);
+ list_add_tail(&task->session_link, &session->pending);
+ mutex_unlock(&session->lock);
+}
+
+static void mpp_session_push_done(struct mpp_task *task)
+{
+ struct mpp_session *session = NULL;
+
+ session = task->session;
+
+ mutex_lock(&session->lock);
+ list_del_init(&task->session_link);
+ mutex_unlock(&session->lock);
+
+ //kfifo_in(&session->done_fifo, &task, 1);
+ rockchip_mpp_result(session->mpp_dev, task);
+}
+
+static struct mpp_task *mpp_session_pull_done(struct mpp_session *session)
+{
+ struct mpp_task *task = NULL;
+
+ if (kfifo_out(&session->done_fifo, &task, 1))
+ return task;
+ return NULL;
+}
+
+static void mpp_dev_sched_irq(struct work_struct *work)
+{
+ struct mpp_task *task = container_of(work, struct mpp_task, work);
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+
+ mpp_dev = task->session->mpp_dev;
+
+ mpp_debug_time_diff(task);
+
+ if (mpp_dev->ops->finish)
+ mpp_dev->ops->finish(mpp_dev, task);
+
+ atomic_dec(&task->session->task_running);
+ pm_runtime_mark_last_busy(mpp_dev->dev);
+ pm_runtime_put_autosuspend(mpp_dev->dev);
+ /*
+ * TODO: unlock the reader locker of the device resource locker
+ * here
+ */
+ mpp_srv_done(mpp_dev->srv, task);
+ /* Wake up the GET thread */
+ mpp_session_push_done(task);
+}
+
+static void *mpp_dev_alloc_task(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_session *session, void __user *src,
+ u32 size)
+{
+ if (mpp_dev->ops->alloc_task)
+ return mpp_dev->ops->alloc_task(session, src, size);
+ return NULL;
+}
+
+static int mpp_dev_free_task(struct mpp_session *session, struct mpp_task *task)
+{
+ struct rockchip_mpp_dev *mpp_dev = session->mpp_dev;
+
+ if (mpp_dev->ops->free_task)
+ mpp_dev->ops->free_task(session, task);
+ return 0;
+}
+
+int mpp_dev_task_init(struct mpp_session *session, struct mpp_task *task)
+{
+ INIT_LIST_HEAD(&task->session_link);
+ INIT_LIST_HEAD(&task->service_link);
+ INIT_WORK(&task->work, mpp_dev_sched_irq);
+
+ task->session = session;
+
+ return 0;
+}
+EXPORT_SYMBOL(mpp_dev_task_init);
+
+void mpp_dev_task_finish(struct mpp_session *session, struct mpp_task *task)
+{
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+
+ mpp_dev = session->mpp_dev;
+ queue_work(mpp_dev->irq_workq, &task->work);
+}
+EXPORT_SYMBOL(mpp_dev_task_finish);
+
+void mpp_dev_task_finalize(struct mpp_session *session, struct mpp_task *task)
+{
+#if 0
+ struct vb2_v4l2_buffer *src, *dst;
+
+ src = v4l2_m2m_src_buf_remove(session->fh.m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(session->fh.m2m_ctx);
+ if (WARN_ON(!src))
+ return -EINVAL;
+
+ if (WARN_ON(!dst))
+ return -EINVAL;
+
+ src->sequence = session->sequence_out++;
+ dst->sequence = session->sequence_cap++;
+
+ v4l2_m2m_buf_copy_data(src, dst, true);
+
+ v4l2_m2m_buf_done(src, result);
+ v4l2_m2m_buf_done(dst, result);
+#endif
+}
+EXPORT_SYMBOL(mpp_dev_task_finalize);
+
+static void mpp_dev_session_clear(struct rockchip_mpp_dev *mpp,
+ struct mpp_session *session)
+{
+ struct mpp_task *task, *n;
+
+ list_for_each_entry_safe(task, n, &session->pending, session_link) {
+ list_del(&task->session_link);
+ mpp_dev_free_task(session, task);
+ }
+ while (kfifo_out(&session->done_fifo, &task, 1))
+ mpp_dev_free_task(session, task);
+}
+
+#if 0
+void *mpp_dev_alloc_session(struct rockchip_mpp_dev *mpp_dev)
+{
+ struct mpp_session *session = NULL;
+ int error = 0;
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!session)
+ return ERR_PTR(-ENOMEM);
+
+ session->pid = current->pid;
+ session->mpp_dev = mpp_dev;
+ mutex_init(&session->lock);
+ INIT_LIST_HEAD(&session->pending);
+ init_waitqueue_head(&session->wait);
+ error = kfifo_alloc(&session->done_fifo, MPP_SESSION_MAX_DONE_TASK,
+ GFP_KERNEL);
+ if (error < 0) {
+ kfree(session);
+ return ERR_PTR(error);
+ }
+
+ atomic_set(&session->task_running, 0);
+ INIT_LIST_HEAD(&session->list_session);
+
+ return session;
+}
+EXPORT_SYMBOL(mpp_dev_alloc_session);
+
+#endif
+
+static void mpp_dev_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+ mpp_debug_enter();
+
+ /* FIXME lock resource lock of the other devices in combo */
+ write_lock(&mpp_dev->resource_rwlock);
+ atomic_set(&mpp_dev->reset_request, 0);
+
+ iommu_detach_device(mpp_dev->domain, mpp_dev->dev);
+ mpp_dev->ops->reset(mpp_dev);
+ iommu_attach_device(mpp_dev->domain, mpp_dev->dev);
+
+ write_unlock(&mpp_dev->resource_rwlock);
+ mpp_debug_leave();
+}
+
+static void mpp_dev_abort(struct rockchip_mpp_dev *mpp_dev)
+{
+ int ret = 0;
+
+ mpp_debug_enter();
+
+ /* destroy the current task after hardware reset */
+ ret = mpp_srv_is_running(mpp_dev->srv);
+
+ mpp_dev_reset(mpp_dev);
+
+ if (ret) {
+ struct mpp_task *task = NULL;
+
+ task = mpp_srv_get_cur_task(mpp_dev->srv);
+ cancel_work_sync(&task->work);
+ list_del(&task->session_link);
+ mpp_srv_abort(mpp_dev->srv, task);
+ mpp_dev_free_task(task->session, task);
+ atomic_dec(&task->session->task_running);
+ } else {
+ mpp_srv_abort(mpp_dev->srv, NULL);
+ }
+
+ mpp_debug_leave();
+}
+
+void mpp_dev_power_on(struct rockchip_mpp_dev *mpp_dev)
+{
+ pm_runtime_get_sync(mpp_dev->dev);
+ pm_stay_awake(mpp_dev->dev);
+}
+
+void mpp_dev_power_off(struct rockchip_mpp_dev *mpp_dev)
+{
+ pm_runtime_put_sync(mpp_dev->dev);
+ pm_relax(mpp_dev->dev);
+}
+
+static void rockchip_mpp_run(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *task)
+{
+ mpp_debug_enter();
+ /*
+ * As I got the global lock from the mpp service here,
+ * I am the very task to be run, the device is ready
+ * for me. Wait a gap in the other is operating with the IOMMU.
+ */
+ if (atomic_read(&mpp_dev->reset_request))
+ mpp_dev_reset(mpp_dev);
+
+ mpp_debug_time_record(task);
+
+ mpp_debug(DEBUG_TASK_INFO, "pid %d, start hw %s\n",
+ task->session->pid, dev_name(mpp_dev->dev));
+
+ if (unlikely(debug & DEBUG_REGISTER))
+ mpp_debug_dump_reg(mpp_dev->reg_base,
+ mpp_dev->variant->reg_len);
+
+ /*
+ * TODO: Lock the reader locker of the device resource lock here,
+ * release at the finish operation
+ */
+ if (mpp_dev->ops->run)
+ mpp_dev->ops->run(mpp_dev, task);
+
+ mpp_debug_leave();
+}
+
+static void rockchip_mpp_try_run(struct rockchip_mpp_dev *mpp_dev)
+{
+ int ret = 0;
+ struct mpp_task *task;
+
+ mpp_debug_enter();
+
+ task = mpp_srv_get_pending_task(mpp_dev->srv);
+
+ if (mpp_dev->ops->prepare)
+ ret = mpp_dev->ops->prepare(mpp_dev, task);
+
+ mpp_srv_wait_to_run(mpp_dev->srv, task);
+ /*
+ * FIXME if the hardware supports task query, but we still need to lock
+ * the running list and lock the mpp service in the current state.
+ */
+ /* Push a pending task to running queue */
+ rockchip_mpp_run(mpp_dev, task);
+
+ mpp_debug_leave();
+}
+
+static int rockchip_mpp_result(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *task)
+{
+ struct mpp_session *session = NULL;
+ struct vb2_v4l2_buffer *src, *dst;
+ enum vb2_buffer_state result = VB2_BUF_STATE_DONE;
+
+ mpp_debug_enter();
+
+ if (!mpp_dev || !task)
+ return -EINVAL;
+
+ session = task->session;
+
+ if (mpp_dev->ops->result)
+ result = mpp_dev->ops->result(mpp_dev, task, NULL, 0);
+
+ mpp_dev_free_task(session, task);
+
+ src = v4l2_m2m_src_buf_remove(session->fh.m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(session->fh.m2m_ctx);
+ if (WARN_ON(!src))
+ return -EINVAL;
+
+ if (WARN_ON(!dst))
+ return -EINVAL;
+
+ src->sequence = session->sequence_out++;
+ dst->sequence = session->sequence_cap++;
+
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ v4l2_m2m_buf_done(src, result);
+ v4l2_m2m_buf_done(dst, result);
+
+ v4l2_m2m_job_finish(mpp_dev->m2m_dev, session->fh.m2m_ctx);
+
+ mpp_debug_leave();
+ return 0;
+}
+
+#if 0
+static int rockchip_mpp_wait_result(struct mpp_session *session,
+ struct rockchip_mpp_dev *mpp,
+ struct vpu_request req)
+{
+ struct mpp_task *task;
+ int ret;
+
+ ret = wait_event_timeout(session->wait,
+ !kfifo_is_empty(&session->done_fifo),
+ msecs_to_jiffies(MPP_TIMEOUT_DELAY));
+ if (ret == 0) {
+ mpp_err("error: pid %d wait %d task done timeout\n",
+ session->pid, atomic_read(&session->task_running));
+ ret = -ETIMEDOUT;
+
+ if (unlikely(debug & DEBUG_REGISTER))
+ mpp_debug_dump_reg(mpp->reg_base,
+ mpp->variant->reg_len);
+ mpp_dev_abort(mpp);
+
+ return ret;
+ }
+
+ task = mpp_session_pull_done(session);
+ rockchip_mpp_result(mpp, task, req.req, req.size);
+
+ return 0;
+}
+
+long mpp_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct mpp_session *session = (struct mpp_session *)filp->private_data;
+ struct rockchip_mpp_dev *mpp = NULL;
+
+ mpp_debug_enter();
+ if (!session)
+ return -EINVAL;
+
+ mpp = session->mpp_dev;
+
+ switch (cmd) {
+ case VPU_IOC_SET_CLIENT_TYPE:
+ break;
+ case VPU_IOC_SET_REG: {
+ struct vpu_request req;
+ struct mpp_task *task;
+
+ mpp_debug(DEBUG_IOCTL, "pid %d set reg\n",
+ session->pid);
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct vpu_request))) {
+ mpp_err("error: set reg copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ task = mpp_dev_alloc_task(mpp, session, (void __user *)req.req,
+ req.size);
+ if (IS_ERR_OR_NULL(task))
+ return -EFAULT;
+ mpp_srv_push_pending(mpp->srv, task);
+ mpp_session_push_pending(session, task);
+ atomic_inc(&session->task_running);
+
+ /* TODO: processing the current task */
+ rockchip_mpp_try_run(mpp);
+ } break;
+ case VPU_IOC_GET_REG: {
+ struct vpu_request req;
+
+ mpp_debug(DEBUG_IOCTL, "pid %d get reg\n",
+ session->pid);
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct vpu_request))) {
+ mpp_err("error: get reg copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ return rockchip_mpp_wait_result(session, mpp, req);
+ } break;
+ case VPU_IOC_PROBE_IOMMU_STATUS: {
+ int iommu_enable = 1;
+
+ mpp_debug(DEBUG_IOCTL, "VPU_IOC_PROBE_IOMMU_STATUS\n");
+
+ if (put_user(iommu_enable, ((u32 __user *)arg))) {
+ mpp_err("error: iommu status copy_to_user failed\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ default: {
+ dev_err(mpp->dev, "unknown mpp ioctl cmd %x\n", cmd);
+ return -ENOIOCTLCMD;
+ } break;
+ }
+
+ mpp_debug_leave();
+ return 0;
+}
+EXPORT_SYMBOL(mpp_dev_ioctl);
+
+static unsigned int mpp_dev_poll(struct file *filp, poll_table *wait)
+{
+ struct mpp_session *session = (struct mpp_session *)filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &session->wait, wait);
+ if (kfifo_len(&session->done_fifo))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int mpp_dev_open(struct file *filp)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(flip);
+ struct video_device *vdev = video_devdata(filp);
+ struct mpp_session *session = NULL;
+ int error = 0;
+
+ mpp_debug_enter();
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!session)
+ return -ENOMEM;
+
+ session->pid = current->pid;
+ session->mpp_dev = mpp_dev;
+ mutex_init(&session->lock);
+ INIT_LIST_HEAD(&session->pending);
+ init_waitqueue_head(&session->wait);
+ error = kfifo_alloc(&session->done_fifo, MPP_SESSION_MAX_DONE_TASK,
+ GFP_KERNEL);
+ if (error < 0)
+ goto fail;
+
+ atomic_set(&session->task_running, 0);
+ INIT_LIST_HEAD(&session->list_session);
+#if 0
+ session->fh.m2m_ctx = v4l2_m2m_ctx_init(mpp_dev->m2m_dev, session,
+ default_queue_init);
+ if (IS_ERR(session->fh.m2m_ctx)) {
+ error = PTR_ERR(session->fb.m2m_ctx);
+ goto fail;
+ }
+ v4l2_fh_init(&session->fh, vdev);
+ filp->private_data = &session->fh;
+ v4l2_fh_add(&session->fh);
+
+ /* TODO: setup default formats */
+
+ /* TODO: install v4l2 ctrl */
+ if (error) {
+ dev_err(mpp_dev->dev, "Failed to set up controls\n");
+ goto err_fh;
+ }
+
+ session->fb.ctrl_handler = mpp_dev->ctrl_handler;
+#endif
+
+ mpp_dev_power_on(mpp);
+ mpp_debug_leave();
+
+ return 0;
+
+err_fh:
+ v4l2_fh_del(&session->fh);
+ v4l2_fh_exit(&session->fh);
+fail:
+ kfree(session);
+ return error;
+}
+
+static int mpp_dev_release(struct file *filp)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(flip);
+ int task_running;
+
+ mpp_debug_enter();
+ if (!session)
+ return -EINVAL;
+
+ /* TODO: is it necessary for v4l2? */
+ task_running = atomic_read(&session->task_running);
+ if (task_running) {
+ pr_err("session %d still has %d task running when closing\n",
+ session->pid, task_running);
+ msleep(50);
+ }
+ wake_up(&session->wait);
+
+#if 0
+ v4l2_m2m_ctx_release(session->fh.m2m_ctx);
+ v4l2_fh_del(&seesion->>fh);
+ v4l2_fh_exit(&session->fh);
+ v4l2_ctrl_handler_free(&session->ctrl_handler);
+#endif
+ mpp_dev_session_clear(mpp, session);
+
+#if 0
+ read_lock(&mpp->resource_rwlock);
+ read_unlock(&mpp->resource_rwlock);
+#endif
+ kfifo_free(&session->done_fifo);
+ filp->private_data = NULL;
+
+ mpp_dev_power_off(mpp);
+ kfree(session);
+
+ dev_dbg(mpp->dev, "closed\n");
+ mpp_debug_leave();
+ return 0;
+}
+
+static const struct v4l2_file_operations mpp_v4l2_default_fops = {
+ .owner = THIS_MODULE,
+ .open = mpp_dev_open,
+ .release = mpp_dev_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+#endif
+
+static struct mpp_service_node *mpp_dev_load_srv(struct platform_device *p)
+{
+ struct mpp_service *srv = NULL;
+ struct device_node *np = NULL;
+ struct platform_device *pdev = NULL;
+ struct mpp_service_node *client = NULL;
+
+ np = of_parse_phandle(p->dev.of_node, "rockchip,srv", 0);
+ if (!np || !of_device_is_available(np)) {
+ dev_err(&p->dev,
+ "failed to get the mpp service node\n");
+ return NULL;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ of_node_put(np);
+ dev_err(&p->dev,
+ "failed to get mpp service from node\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ device_lock(&pdev->dev);
+
+ srv = platform_get_drvdata(pdev);
+ if (srv) {
+ client = mpp_srv_attach(srv, NULL);
+ } else {
+ dev_info(&pdev->dev, "defer probe\n");
+ client = ERR_PTR(-EPROBE_DEFER);
+ }
+ device_unlock(&pdev->dev);
+
+ put_device(&pdev->dev);
+ of_node_put(np);
+
+ return client;
+}
+
+static void mpp_device_run(void *priv)
+{
+ struct mpp_session *session = (struct mpp_session *)priv;
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+ struct mpp_task *task;
+
+ mpp_debug_enter();
+ if (!session)
+ return;
+
+ mpp_dev = session->mpp_dev;
+
+ mpp_debug(DEBUG_IOCTL, "pid %d set reg\n", session->pid);
+ /* power on here */
+ if (pm_runtime_get_if_in_use(mpp_dev->dev) <= 0) {
+ /* TODO: destroy the session and report more error */
+ dev_err(mpp_dev->dev, "can't power on device\n");
+ return;
+ }
+
+ task = mpp_dev_alloc_task(mpp_dev, session, NULL, 0);
+ if (IS_ERR_OR_NULL(task))
+ return;
+
+ mpp_srv_push_pending(mpp_dev->srv, task);
+ mpp_session_push_pending(session, task);
+ atomic_inc(&session->task_running);
+
+ /* TODO: processing the current task */
+ rockchip_mpp_try_run(mpp_dev);
+
+ mpp_debug_leave();
+}
+
+#if 0
+void mpp_job_abort(void *priv)
+{
+ struct mpp_session *session = (struct mpp_session *)priv;
+
+ /* TODO: invoke v4l2_m2m_job_finish */
+ mpp_dev_abort(session->mpp_dev);
+}
+#endif
+
+static const struct v4l2_m2m_ops mpp_m2m_ops = {
+ .device_run = mpp_device_run,
+#if 0
+ .job_abort = mpp_job_abort,
+#endif
+};
+
+/* The device will do more probing work after this */
+int mpp_dev_common_probe(struct rockchip_mpp_dev *mpp_dev,
+ struct platform_device *pdev, struct mpp_dev_ops *ops)
+{
+ struct device *dev = NULL;
+ struct resource *res = NULL;
+ int err;
+
+ /* Get and register to MPP service */
+ mpp_dev->srv = mpp_dev_load_srv(pdev);
+ if (IS_ERR_OR_NULL(mpp_dev->srv))
+ return PTR_ERR(mpp_dev->srv);
+
+ dev = &pdev->dev;
+ mpp_dev->dev = dev;
+ mpp_dev->ops = ops;
+
+ rwlock_init(&mpp_dev->resource_rwlock);
+
+ device_init_wakeup(mpp_dev->dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ mpp_dev->irq_workq = alloc_ordered_workqueue("%s_irq_wq",
+ WQ_MEM_RECLAIM
+ | WQ_FREEZABLE,
+ dev_name(mpp_dev->dev));
+ if (!mpp_dev->irq_workq) {
+ dev_err(dev, "failed to create irq workqueue\n");
+ err = -EINVAL;
+ goto failed_irq_workq;
+ }
+
+ mpp_dev->irq = platform_get_irq(pdev, 0);
+ if (mpp_dev->irq < 0) {
+ dev_err(dev, "No interrupt resource found\n");
+ err = -ENODEV;
+ goto failed;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ err = -ENODEV;
+ goto failed;
+ }
+ mpp_dev->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mpp_dev->reg_base)) {
+ err = PTR_ERR(mpp_dev->reg_base);
+ goto failed;
+ }
+
+ /* V4l2 part */
+ mutex_init(&mpp_dev->dev_lock);
+
+ err = v4l2_device_register(dev, &mpp_dev->v4l2_dev);
+ if (err) {
+ dev_err(dev, "Failed to register v4l2 device\n");
+ goto failed;
+ }
+
+ /* TODO */
+ mpp_dev->m2m_dev = v4l2_m2m_init(&mpp_m2m_ops);
+ if (IS_ERR(mpp_dev->m2m_dev)) {
+ v4l2_err(&mpp_dev->v4l2_dev, "Failed to init mem2mem device\n");
+ err = PTR_ERR(mpp_dev->m2m_dev);
+ goto err_v4l2_unreg;
+ }
+
+ mpp_dev->mdev.dev = dev;
+ strlcpy(mpp_dev->mdev.model, MPP_MODULE_NAME,
+ sizeof(mpp_dev->mdev.model));
+ media_device_init(&mpp_dev->mdev);
+ mpp_dev->mdev.ops = &mpp_m2m_media_ops;
+ mpp_dev->v4l2_dev.mdev = &mpp_dev->mdev;
+
+ mpp_dev->domain = iommu_get_domain_for_dev(dev);
+
+ return 0;
+
+err_v4l2_unreg:
+ v4l2_device_unregister(&mpp_dev->v4l2_dev);
+failed_irq_workq:
+ destroy_workqueue(mpp_dev->irq_workq);
+failed:
+ pm_runtime_disable(dev);
+ return err;
+}
+EXPORT_SYMBOL(mpp_dev_common_probe);
+
+/* Remember to set the platform data after this */
+int mpp_dev_register_node(struct rockchip_mpp_dev *mpp_dev,
+ const char *node_name, const void *v4l2_fops,
+ const void *v4l2_ioctl_ops)
+{
+ struct video_device *vfd;
+ int ret = 0;
+
+ /* create a device node */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to allocate video device\n");
+ return -ENOMEM;
+ }
+
+ vfd->fops = v4l2_fops;
+ vfd->release = video_device_release;
+ vfd->lock = &mpp_dev->dev_lock;
+ vfd->v4l2_dev = &mpp_dev->v4l2_dev;
+ vfd->vfl_dir = VFL_DIR_M2M;
+ vfd->device_caps = V4L2_CAP_STREAMING;
+ vfd->ioctl_ops = v4l2_ioctl_ops;
+
+ strlcpy(vfd->name, node_name, sizeof(vfd->name));
+ video_set_drvdata(vfd, mpp_dev);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to register video device\n");
+ goto err_m2m_rel;
+ }
+ v4l2_info(&mpp_dev->v4l2_dev, "registered as /dev/video%d\n", vfd->num);
+
+ ret = v4l2_m2m_register_media_controller(mpp_dev->m2m_dev, vfd,
+ mpp_dev->variant->vfd_func);
+ if (ret) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to init mem2mem media controller\n");
+ goto err_unreg_video;
+ }
+
+ mpp_dev->vfd = vfd;
+
+ ret = media_device_register(&mpp_dev->mdev);
+ if (ret) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to register mem2mem media device\n");
+ goto err_unreg_video_dev;
+ }
+
+ return 0;
+
+err_unreg_video:
+ video_unregister_device(mpp_dev->vfd);
+err_unreg_video_dev:
+ video_device_release(mpp_dev->vfd);
+err_m2m_rel:
+ v4l2_m2m_release(mpp_dev->m2m_dev);
+ return ret;
+}
+EXPORT_SYMBOL(mpp_dev_register_node);
+
+int mpp_dev_common_remove(struct rockchip_mpp_dev *mpp_dev)
+{
+ destroy_workqueue(mpp_dev->irq_workq);
+
+ media_device_unregister(&mpp_dev->mdev);
+ v4l2_m2m_unregister_media_controller(mpp_dev->m2m_dev);
+ media_device_cleanup(&mpp_dev->mdev);
+
+ video_unregister_device(mpp_dev->vfd);
+ video_device_release(mpp_dev->vfd);
+
+ mpp_srv_detach(mpp_dev->srv);
+
+ mpp_dev_power_off(mpp_dev);
+
+ device_init_wakeup(mpp_dev->dev, false);
+ pm_runtime_disable(mpp_dev->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpp_dev_common_remove);
+
+void mpp_debug_dump_reg(void __iomem *regs, int count)
+{
+ int i;
+
+ pr_info("dumping registers: %p\n", regs);
+
+ for (i = 0; i < count; i++)
+ pr_info("reg[%02d]: %08x\n", i, readl_relaxed(regs + i * 4));
+}
+EXPORT_SYMBOL(mpp_debug_dump_reg);
+
+void mpp_debug_dump_reg_mem(u32 *regs, int count)
+{
+ int i;
+
+ pr_info("Dumping registers: %p\n", regs);
+
+ for (i = 0; i < count; i++)
+ pr_info("reg[%03d]: %08x\n", i, regs[i]);
+}
+EXPORT_SYMBOL(mpp_debug_dump_reg_mem);
+
+void mpp_dev_write_seq(struct rockchip_mpp_dev *mpp_dev, unsigned long offset,
+ void *buffer, unsigned long count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ u32 *cur = (u32 *)buffer;
+ u32 pos = offset + i * 4;
+ u32 j = i + (u32)(offset / 4);
+
+ cur += i;
+ mpp_debug(DEBUG_SET_REG, "write reg[%03d]: %08x\n", j, *cur);
+ iowrite32(*cur, mpp_dev->reg_base + pos);
+ }
+}
+EXPORT_SYMBOL(mpp_dev_write_seq);
+
+void mpp_dev_write(struct rockchip_mpp_dev *mpp, u32 reg, u32 val)
+{
+ mpp_debug(DEBUG_SET_REG, "write reg[%03d]: %08x\n", reg / 4, val);
+ iowrite32(val, mpp->reg_base + reg);
+}
+EXPORT_SYMBOL(mpp_dev_write);
+
+void mpp_dev_read_seq(struct rockchip_mpp_dev *mpp_dev,
+ unsigned long offset, void *buffer,
+ unsigned long count)
+{
+ int i = 0;
+
+ for (i = 0; i < count; i++) {
+ u32 *cur = (u32 *)buffer;
+ u32 pos = offset / 4 + i;
+
+ cur += i;
+ *cur = ioread32(mpp_dev->reg_base + pos * 4);
+ mpp_debug(DEBUG_GET_REG, "get reg[%03d]: %08x\n", pos, *cur);
+ }
+}
+EXPORT_SYMBOL(mpp_dev_read_seq);
+
+u32 mpp_dev_read(struct rockchip_mpp_dev *mpp, u32 reg)
+{
+ u32 val = ioread32(mpp->reg_base + reg);
+
+ mpp_debug(DEBUG_GET_REG, "get reg[%03d] 0x%x: %08x\n", reg / 4,
+ reg, val);
+ return val;
+}
+EXPORT_SYMBOL(mpp_dev_read);
+
+void mpp_debug_time_record(struct mpp_task *task)
+{
+ if (unlikely(debug & DEBUG_TIMING) && task)
+ getboottime64(&task->start);
+}
+EXPORT_SYMBOL(mpp_debug_time_record);
+
+void mpp_debug_time_diff(struct mpp_task *task)
+{
+ struct timespec64 end;
+
+ getboottime64(&end);
+ mpp_debug(DEBUG_TIMING, "time: %lld ms\n",
+ (end.tv_sec - task->start.tv_sec) * 1000 +
+ (end.tv_nsec - task->start.tv_nsec) / 1000000);
+}
+EXPORT_SYMBOL(mpp_debug_time_diff);
+
+static int mpp_m2m_querycap(struct file *filp, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+
+ strscpy(cap->driver, MPP_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, MPP_MODULE_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(mpp_dev->dev));
+
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int mpp_g_fmt_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *fmt = NULL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fmt = &session->fmt_cap;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ fmt = &session->fmt_out;
+
+ *pix_mp = *fmt;
+
+ return 0;
+}
+
+static int mpp_enum_fmt_mplane(struct file *filp, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+ const struct v4l2_pix_format_mplane *formats;
+ unsigned int num_fmts;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ num_fmts = ARRAY_SIZE(mpp_dev->fmt_out);
+ formats = mpp_dev->fmt_out;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ num_fmts = ARRAY_SIZE(mpp_dev->fmt_cap);
+ formats = mpp_dev->fmt_cap;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (f->index >= num_fmts)
+ return -EINVAL;
+
+ if (formats[f->index].pixelformat == 0)
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].pixelformat;
+
+ return 0;
+}
+
+static int mpp_try_fmt_mplane(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+ const struct v4l2_pix_format_mplane *formats;
+ unsigned int num_fmts;
+ int i;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ num_fmts = ARRAY_SIZE(mpp_dev->fmt_out);
+ formats = mpp_dev->fmt_out;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ num_fmts = ARRAY_SIZE(mpp_dev->fmt_cap);
+ formats = mpp_dev->fmt_cap;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_fmts; i++) {
+ if (f->fmt.pix_mp.pixelformat == formats[i].pixelformat)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+const struct v4l2_ioctl_ops mpp_ioctl_ops_templ = {
+ .vidioc_querycap = mpp_m2m_querycap,
+#if 0
+ .vidioc_try_fmt_vid_cap = mpp_try_fmt_cap,
+ .vidioc_try_fmt_vid_out = mpp_try_fmt_out,
+ .vidioc_s_fmt_vid_out = mpp_s_fmt_out,
+ .vidioc_s_fmt_vid_cap = mpp_s_fmt_cap,
+#endif
+ .vidioc_try_fmt_vid_out_mplane = mpp_try_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = mpp_try_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = mpp_g_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = mpp_g_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = mpp_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = mpp_enum_fmt_mplane,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+EXPORT_SYMBOL(mpp_ioctl_ops_templ);
+
+static int mpp_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct mpp_session *session = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format_mplane *pixfmt;
+ int i;
+
+ switch (vq->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ pixfmt = &session->fmt_out;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ pixfmt = &session->fmt_cap;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (*num_planes) {
+ if (*num_planes != pixfmt->num_planes)
+ return -EINVAL;
+ for (i = 0; i < pixfmt->num_planes; ++i)
+ if (sizes[i] < pixfmt->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = pixfmt->num_planes;
+ for (i = 0; i < pixfmt->num_planes; i++)
+ sizes[i] = pixfmt->plane_fmt[i].sizeimage;
+
+ return 0;
+}
+
+/* I am sure what is used for */
+static int mpp_buf_out_validata(struct vb2_buffer *vb)
+{
+ return 0;
+}
+
+static int mpp_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mpp_session *session = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ session->sequence_out = 0;
+ else
+ session->sequence_cap = 0;
+
+ return 0;
+}
+
+static void mpp_stop_streaming(struct vb2_queue *vq)
+{
+ struct mpp_session *session = vb2_get_drv_priv(vq);
+
+ for (;;) {
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vbuf = v4l2_m2m_src_buf_remove(session->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(session->fh.m2m_ctx);
+
+ if (!vbuf)
+ break;
+
+ v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
+ &session->ctrl_handler);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static void mpp_buf_queue(struct vb2_buffer *vb) {
+ struct mpp_session *session = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ /* TODO: may alloc registers table here */
+ v4l2_m2m_buf_queue(session->fh.m2m_ctx, vbuf);
+}
+
+static void mpp_buf_request_complete(struct vb2_buffer *vb) {
+ struct mpp_session *session = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_ctrl_request_complete(vb->req_obj.req, &session->ctrl_handler);
+}
+
+static const struct vb2_ops mpp_queue_ops = {
+ .queue_setup = mpp_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ /*
+ * TODO: may write back feedback to userspace .buf_finish for encoder,
+ * not the slice header which the job of the userspace
+ */
+ /* TODO: fill the INPUT buffer with device configure at .buf_prepare */
+ .buf_out_validate = mpp_buf_out_validata,
+ .start_streaming = mpp_start_streaming,
+ .stop_streaming = mpp_stop_streaming,
+ .buf_queue = mpp_buf_queue,
+ .buf_request_complete = mpp_buf_request_complete,
+};
+
+static int rockchip_mpp_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct mpp_session *session = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = session;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->min_buffers_needed = 1;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &session->mpp_dev->dev_lock;
+ src_vq->ops = &mpp_queue_ops;
+ src_vq->dev = session->mpp_dev->v4l2_dev.dev;
+ src_vq->supports_requests = true;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->min_buffers_needed = 1;
+ dst_vq->drv_priv = session;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &session->mpp_dev->dev_lock;
+ dst_vq->ops = &mpp_queue_ops;
+ dst_vq->dev = session->mpp_dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(dst_vq);
+
+ return ret;
+}
+
+void *rockchip_mpp_alloc_session(struct rockchip_mpp_dev *mpp_dev,
+ struct video_device *vdev)
+{
+ struct mpp_session *session = NULL;
+ int error = 0;
+
+ mpp_debug_enter();
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!session)
+ return ERR_PTR(-ENOMEM);
+
+ session->pid = current->pid;
+ session->mpp_dev = mpp_dev;
+ mutex_init(&session->lock);
+ INIT_LIST_HEAD(&session->pending);
+ init_waitqueue_head(&session->wait);
+ error = kfifo_alloc(&session->done_fifo, MPP_SESSION_MAX_DONE_TASK,
+ GFP_KERNEL);
+ if (error < 0)
+ goto fail;
+
+ atomic_set(&session->task_running, 0);
+ INIT_LIST_HEAD(&session->list_session);
+
+ session->fh.m2m_ctx = v4l2_m2m_ctx_init(mpp_dev->m2m_dev, session,
+ rockchip_mpp_queue_init);
+ if (IS_ERR(session->fh.m2m_ctx)) {
+ error = PTR_ERR(session->fh.m2m_ctx);
+ goto fail;
+ }
+ v4l2_fh_init(&session->fh, vdev);
+ v4l2_fh_add(&session->fh);
+
+ mpp_debug_leave();
+
+ return session;
+
+fail:
+ kfree(session);
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL(rockchip_mpp_alloc_session);
+
+int rockchip_mpp_dev_release(struct file *filp)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+
+ mpp_debug_enter();
+ if (!session)
+ return -EINVAL;
+
+ /* TODO: is it necessary for v4l2? */
+#if 0
+ int task_running;
+ task_running = atomic_read(&session->task_running);
+ if (task_running) {
+ pr_err("session %d still has %d task running when closing\n",
+ session->pid, task_running);
+ msleep(50);
+ }
+ wake_up(&session->wait);
+#endif
+
+ v4l2_m2m_ctx_release(session->fh.m2m_ctx);
+ v4l2_fh_del(&session->fh);
+ v4l2_fh_exit(&session->fh);
+ v4l2_ctrl_handler_free(&session->ctrl_handler);
+ mpp_dev_session_clear(mpp_dev, session);
+
+ kfifo_free(&session->done_fifo);
+ filp->private_data = NULL;
+
+ mpp_dev_power_off(mpp_dev);
+ kfree(session);
+
+ dev_dbg(mpp_dev->dev, "closed\n");
+ mpp_debug_leave();
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_mpp_dev_release);
+
+void *rockchip_mpp_get_cur_ctrl(struct mpp_session *session, u32 id)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&session->ctrl_handler, id);
+ return ctrl ? ctrl->p_cur.p : NULL;
+}
+EXPORT_SYMBOL(rockchip_mpp_get_cur_ctrl);
+
+int rockchip_mpp_get_ref_idx(struct vb2_queue *queue,
+ struct vb2_buffer *vb2_buf, u64 timestamp)
+{
+ /* FIXME: TODO: the timestamp is not copied yet before copy_data */
+ if (vb2_buf->timestamp == timestamp)
+ return vb2_buf->index;
+ else
+ return vb2_find_timestamp(queue, timestamp, 0);
+}
+EXPORT_SYMBOL(rockchip_mpp_get_ref_idx);
+
+dma_addr_t rockchip_mpp_find_addr(struct vb2_queue *queue,
+ struct vb2_buffer *vb2_buf, u64 timestamp)
+{
+ int idx = -1;
+
+ idx = rockchip_mpp_get_ref_idx(queue, vb2_buf, timestamp);
+ if (idx < 0)
+ return 0;
+
+ return vb2_dma_contig_plane_dma_addr(queue->bufs[idx], 0);
+}
+EXPORT_SYMBOL(rockchip_mpp_find_addr);
+
+#if 0
+const struct v4l2_file_operations mpp_v4l2_fops_templ = {
+ .release = mpp_dev_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+#endif
+
+static int __init mpp_device_init(void)
+{
+ mpp_device_class = class_create(THIS_MODULE, "mpp_device");
+ if (PTR_ERR_OR_ZERO(mpp_device_class))
+ return PTR_ERR(mpp_device_class);
+
+ return 0;
+}
+
+static void __exit mpp_device_exit(void)
+{
+ class_destroy(mpp_device_class);
+}
+
+module_init(mpp_device_init);
+module_exit(mpp_device_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_common.h b/drivers/staging/rockchip-mpp/mpp_dev_common.h
new file mode 100644
index 000000000000..36770af53a95
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ROCKCHIP_MPP_DEV_COMMON_H_
+#define _ROCKCHIP_MPP_DEV_COMMON_H_
+
+#include <linux/dma-buf.h>
+#include <linux/kfifo.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/reset.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "mpp_service.h"
+
+#define MPP_MODULE_NAME "rk-mpp"
+
+extern const struct v4l2_ioctl_ops mpp_ioctl_ops_templ;
+
+struct mpp_dev_variant {
+ u32 reg_len;
+ const char *node_name;
+ u32 version_bit;
+ int vfd_func;
+};
+
+/* Definition in mpp service file */
+struct mpp_service;
+
+struct rockchip_mpp_dev {
+ struct device *dev;
+
+ const struct mpp_dev_variant *variant;
+ struct mpp_dev_ops *ops;
+ struct v4l2_pix_format_mplane fmt_out[16];
+ struct v4l2_pix_format_mplane fmt_cap[16];
+
+ void __iomem *reg_base;
+ int irq;
+ struct workqueue_struct *irq_workq;
+ struct iommu_domain *domain;
+
+ rwlock_t resource_rwlock;
+ atomic_t reset_request;
+
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct media_device mdev;
+ struct video_device *vfd;
+ struct mutex dev_lock;
+
+ /* MPP Service */
+ struct mpp_service_node *srv;
+};
+
+struct mpp_task;
+
+struct mpp_session {
+ /* the session related device private data */
+ struct rockchip_mpp_dev *mpp_dev;
+ /* a linked list of data so we can access them for debugging */
+ struct list_head list_session;
+
+ /* session tasks list lock */
+ struct mutex lock;
+ struct list_head pending;
+
+ DECLARE_KFIFO_PTR(done_fifo, struct mpp_task *);
+
+ wait_queue_head_t wait;
+ pid_t pid;
+ atomic_t task_running;
+
+ struct v4l2_fh fh;
+ u32 sequence_cap;
+ u32 sequence_out;
+
+ struct v4l2_pix_format_mplane fmt_out;
+ struct v4l2_pix_format_mplane fmt_cap;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* TODO: FIXME: slower than helper function ? */
+ struct v4l2_ctrl **ctrls;
+};
+
+/* The context for the a task */
+struct mpp_task {
+ /* context belong to */
+ struct mpp_session *session;
+
+ /* link to service session */
+ struct list_head session_link;
+ /* link to service list */
+ struct list_head service_link;
+ struct work_struct work;
+
+ /* record context running start time */
+ struct timespec64 start;
+};
+
+/*
+ * struct mpp_dev_ops - context specific operations for a device
+ * The task part
+ * @alloc_task
+ * @prepare Check HW status for determining run next task or not.
+ * @run Start a single {en,de}coding run. Set registers to hardware.
+ * @finish Read back processing results and additional data from hardware.
+ * @result Read status to userspace.
+ * @free_task Release the resource allocate during init.
+ * The device part
+ * @reset
+ */
+struct mpp_dev_ops {
+ /* size: in bytes, data sent from userspace, length in bytes */
+ void *(*alloc_task)(struct mpp_session *session,
+ void __user *src, u32 size);
+ int (*prepare)(struct rockchip_mpp_dev *mpp_dev, struct mpp_task *task);
+ int (*run)(struct rockchip_mpp_dev *mpp_dev, struct mpp_task *task);
+ int (*finish)(struct rockchip_mpp_dev *mpp_dev, struct mpp_task *task);
+ int (*result)(struct rockchip_mpp_dev *mpp_dev, struct mpp_task *task,
+ u32 __user *dst, u32 size);
+ int (*free_task)(struct mpp_session *session,
+ struct mpp_task *task);
+ /* Hardware only operations */
+ int (*reset)(struct rockchip_mpp_dev *mpp_dev);
+};
+
+struct rockchip_mpp_control {
+ u32 codec;
+ u32 id;
+ u32 elem_size;
+};
+
+void *rockchip_mpp_alloc_session(struct rockchip_mpp_dev *mpp_dev,
+ struct video_device *vdev);
+int rockchip_mpp_dev_release(struct file *filp);
+
+void *rockchip_mpp_get_cur_ctrl(struct mpp_session *session, u32 id);
+int rockchip_mpp_get_ref_idx(struct vb2_queue *queue,
+ struct vb2_buffer *vb2_buf, u64 timestamp);
+dma_addr_t rockchip_mpp_find_addr(struct vb2_queue *queue,
+ struct vb2_buffer *vb2_buf, u64 timestamp);
+
+int mpp_dev_task_init(struct mpp_session *session, struct mpp_task *task);
+void mpp_dev_task_finish(struct mpp_session *session, struct mpp_task *task);
+void mpp_dev_task_finalize(struct mpp_session *session, struct mpp_task *task);
+
+void mpp_dev_power_on(struct rockchip_mpp_dev *mpp);
+void mpp_dev_power_off(struct rockchip_mpp_dev *mpp);
+bool mpp_dev_is_power_on(struct rockchip_mpp_dev *mpp);
+
+void mpp_dump_reg(void __iomem *regs, int count);
+void mpp_dump_reg_mem(u32 *regs, int count);
+
+int mpp_dev_common_probe(struct rockchip_mpp_dev *mpp_dev,
+ struct platform_device *pdev,
+ struct mpp_dev_ops *ops);
+int mpp_dev_register_node(struct rockchip_mpp_dev *mpp_dev,
+ const char *node_name, const void *v4l2_fops,
+ const void *v4l2_ioctl_ops);
+int mpp_dev_common_remove(struct rockchip_mpp_dev *mpp_dev);
+
+static inline void safe_reset(struct reset_control *rst)
+{
+ if (rst)
+ reset_control_assert(rst);
+}
+
+static inline void safe_unreset(struct reset_control *rst)
+{
+ if (rst)
+ reset_control_deassert(rst);
+}
+
+void mpp_dev_write_seq(struct rockchip_mpp_dev *mpp_dev,
+ unsigned long offset, void *buffer,
+ unsigned long count);
+
+void mpp_dev_write(struct rockchip_mpp_dev *mpp, u32 val, u32 reg);
+
+void mpp_dev_read_seq(struct rockchip_mpp_dev *mpp_dev,
+ unsigned long offset, void *buffer,
+ unsigned long count);
+
+u32 mpp_dev_read(struct rockchip_mpp_dev *mpp, u32 reg);
+
+void mpp_debug_time_record(struct mpp_task *task);
+void mpp_debug_time_diff(struct mpp_task *task);
+
+void mpp_debug_dump_reg(void __iomem *regs, int count);
+void mpp_debug_dump_reg_mem(u32 *regs, int count);
+
+#endif
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c b/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
new file mode 100644
index 000000000000..30a70670e3b7
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
@@ -0,0 +1,997 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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 <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+#include <soc/rockchip/rockchip_sip.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+
+#include <linux/pm_runtime.h>
+
+#include "mpp_debug.h"
+#include "mpp_dev_common.h"
+#include "rkvdec/hal.h"
+
+#define RKVDEC_DRIVER_NAME "mpp_rkvdec"
+
+#define RKVDEC_VER_RK3328_BIT BIT(1)
+#define IOMMU_GET_BUS_ID(x) (((x) >> 6) & 0x1f)
+#define IOMMU_PAGE_SIZE SZ_4K
+
+#define RKVDEC_NODE_NAME "rkvdec"
+#define RK_HEVCDEC_NODE_NAME "hevc-service"
+
+#define to_rkvdec_task(ctx) \
+ container_of(ctx, struct rkvdec_task, mpp_task)
+#define to_rkvdec_dev(dev) \
+ container_of(dev, struct rockchip_rkvdec_dev, mpp_dev)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for rkvdec debug information");
+
+enum RKVDEC_STATE {
+ RKVDEC_STATE_NORMAL,
+ RKVDEC_STATE_LT_START,
+ RKVDEC_STATE_LT_RUN,
+};
+
+struct rockchip_rkvdec_dev {
+ struct rockchip_mpp_dev mpp_dev;
+
+ struct reset_control *rst_a;
+ struct reset_control *rst_h;
+ struct reset_control *rst_niu_a;
+ struct reset_control *rst_niu_h;
+ struct reset_control *rst_core;
+ struct reset_control *rst_cabac;
+
+ enum RKVDEC_STATE state;
+
+ unsigned long aux_iova;
+ struct page *aux_page;
+
+ void *current_task;
+};
+
+struct rkvdec_task {
+ struct mpp_task mpp_task;
+
+ u32 reg[ROCKCHIP_RKVDEC_REG_NUM];
+ u32 idx;
+
+ u32 irq_status;
+};
+
+static struct rockchip_mpp_control hevc_controls[] = {
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_sps),
+ },
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_pps),
+ },
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params),
+ },
+};
+
+static struct rockchip_mpp_control rkvdec_controls[] = {
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_sps),
+ },
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_pps),
+ },
+ {
+ .codec = V4L2_PIX_FMT_HEVC_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
+ .elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params),
+ },
+
+};
+
+static const struct v4l2_pix_format_mplane fmt_out_templ[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_H264_SLICE,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_HEVC_SLICE,
+ },
+};
+
+static const struct v4l2_pix_format_mplane fmt_cap_templ[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_NV12M,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_NV16M,
+ },
+};
+
+static const struct mpp_dev_variant rkvdec_v1_data = {
+ .reg_len = 76,
+ .node_name = RKVDEC_NODE_NAME,
+ .version_bit = BIT(0),
+};
+
+static const struct mpp_dev_variant rkvdec_v1p_data = {
+ .reg_len = 76,
+ .node_name = RKVDEC_NODE_NAME,
+ .version_bit = RKVDEC_VER_RK3328_BIT,
+};
+
+static const struct mpp_dev_variant rk_hevcdec_data = {
+ .reg_len = 48,
+ .node_name = RK_HEVCDEC_NODE_NAME,
+ .version_bit = BIT(0),
+};
+
+static int rkvdec_open(struct file *filp);
+
+static const struct v4l2_file_operations rkvdec_fops = {
+ .open = rkvdec_open,
+ .release = rockchip_mpp_dev_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static struct v4l2_ioctl_ops rkvdec_ioctl_ops = { 0, };
+
+static void *rockchip_rkvdec_get_drv_data(struct platform_device *pdev);
+
+static int rkvdec_s_fmt_vid_out_mplane(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct vb2_queue *vq;
+ int sizes = 0;
+ int i;
+
+ /* TODO: We can change width and height at streaming on */
+ vq = v4l2_m2m_get_vq(session->fh.m2m_ctx, f->type);
+ if (vb2_is_streaming(vq))
+ return -EBUSY;
+
+ if (!pix_mp->num_planes)
+ pix_mp->num_planes = 1;
+
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ sizes += pix_mp->plane_fmt[i].sizeimage;
+ }
+ /* strm_len is 24 bits */
+ if (sizes >= SZ_16M - SZ_1K)
+ return -EINVAL;
+
+ /* FIXME: For those slice header data, put it in a better place */
+ pix_mp->plane_fmt[pix_mp->num_planes - 1].sizeimage += SZ_4M;
+
+ session->fmt_out = *pix_mp;
+
+ /* Copy the pixel format information from OUTPUT to CAPUTRE */
+ session->fmt_cap.pixelformat = V4L2_PIX_FMT_NV12M;
+ session->fmt_cap.width = pix_mp->width;
+ session->fmt_cap.height = pix_mp->height;
+ session->fmt_cap.colorspace = pix_mp->colorspace;
+ session->fmt_cap.ycbcr_enc = pix_mp->ycbcr_enc;
+ session->fmt_cap.xfer_func = pix_mp->xfer_func;
+ session->fmt_cap.quantization = pix_mp->quantization;
+
+ return 0;
+}
+
+static int rkvdec_s_fmt_vid_cap_mplane(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(session->fh.m2m_ctx, f->type);
+ if (vb2_is_streaming(vq))
+ return -EBUSY;
+
+ switch (pix_mp->pixelformat) {
+ case V4L2_PIX_FMT_NV12M:
+ /* TODO: adaptive based by cache settings */
+ pix_mp->plane_fmt[0].bytesperline =
+ ALIGN(pix_mp->width, 256) | 256;
+ pix_mp->plane_fmt[1].bytesperline =
+ ALIGN(pix_mp->width, 256) | 256;
+#if 0
+ /* TODO: align with 16 for H.264 */
+ pix_mp->plane_fmt[0].sizeimage =
+ pix_mp->plane_fmt[0].bytesperline * ALIGN(pix_mp->height,
+ 8);
+ /* Additional space for motion vector */
+ pix_mp->plane_fmt[1].sizeimage =
+ pix_mp->plane_fmt[1].bytesperline * ALIGN(pix_mp->height,
+ 8);
+#else
+ pix_mp->plane_fmt[0].sizeimage =
+ pix_mp->plane_fmt[0].bytesperline * ALIGN(pix_mp->height,
+ 8);
+ /* Additional space for motion vector */
+ pix_mp->plane_fmt[0].sizeimage *= 2;
+#endif
+ break;
+ case V4L2_PIX_FMT_NV16M:
+ pix_mp->plane_fmt[0].bytesperline =
+ ALIGN(pix_mp->width, 256) | 256;
+ pix_mp->plane_fmt[1].bytesperline =
+ ALIGN(pix_mp->width, 256) | 256;
+ pix_mp->plane_fmt[0].sizeimage =
+ pix_mp->plane_fmt[0].bytesperline * ALIGN(pix_mp->height,
+ 8);
+ /* Additional space for motion vector */
+ pix_mp->plane_fmt[1].sizeimage =
+ pix_mp->plane_fmt[1].bytesperline * ALIGN(pix_mp->height,
+ 8) * 3 / 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ session->fmt_cap = *pix_mp;
+
+ return 0;
+}
+
+static int rkvdec_setup_ctrls(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_session *session)
+{
+ struct v4l2_ctrl_handler *hdl = &session->ctrl_handler;
+ struct v4l2_ctrl *ctrl;
+ unsigned int num_ctrls = ARRAY_SIZE(rkvdec_controls);
+ unsigned int i;
+
+ v4l2_ctrl_handler_init(hdl, num_ctrls);
+ if (hdl->error) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to initialize control handler\n");
+ return hdl->error;
+ }
+
+ for (i = 0; i < num_ctrls; i++) {
+ struct v4l2_ctrl_config cfg = { };
+
+ cfg.id = rkvdec_controls[i].id;
+ cfg.elem_size = rkvdec_controls[i].elem_size;
+
+ ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ if (hdl->error) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to create new custom %d control\n",
+ cfg.id);
+ goto fail;
+ }
+ }
+
+ session->fh.ctrl_handler = hdl;
+ v4l2_ctrl_handler_setup(hdl);
+
+ return 0;
+fail:
+ v4l2_ctrl_handler_free(hdl);
+ return hdl->error;
+}
+
+static int rkvdec_open(struct file *filp)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+ struct video_device *vdev = video_devdata(filp);
+ struct mpp_session *session = NULL;
+ /* TODO: install ctrl based on register report */
+ int error = 0;
+
+ mpp_debug_enter();
+
+ session = rockchip_mpp_alloc_session(mpp_dev, vdev);
+ if (IS_ERR_OR_NULL(session))
+ return PTR_ERR(session);
+
+ error = rkvdec_setup_ctrls(mpp_dev, session);
+ if (error) {
+ kfree(session);
+ return error;
+ }
+
+ filp->private_data = &session->fh;
+ pm_runtime_get_sync(mpp_dev->dev);
+
+ mpp_debug_leave();
+ return 0;
+}
+
+#if 0
+/*
+ * NOTE: rkvdec/rkhevc put scaling list address in pps buffer hardware will read
+ * it by pps id in video stream data.
+ *
+ * So we need to translate the address in iommu case. The address data is also
+ * 10bit fd + 22bit offset mode.
+ * Because userspace decoder do not give the pps id in the register file sets
+ * kernel driver need to translate each scaling list address in pps buffer which
+ * means 256 pps for H.264, 64 pps for H.265.
+ *
+ * In order to optimize the performance kernel driver ask userspace decoder to
+ * set all scaling list address in pps buffer to the same one which will be used
+ * on current decoding task. Then kernel driver can only translate the first
+ * address then copy it all pps buffer.
+ */
+static int fill_scaling_list_pps(struct rkvdec_task *task, int fd, int offset,
+ int count, int pps_info_size,
+ int sub_addr_offset)
+{
+ struct device *dev = NULL;
+ struct dma_buf *dmabuf = NULL;
+ void *vaddr = NULL;
+ u8 *pps = NULL;
+ u32 base = sub_addr_offset;
+ u32 scaling_fd = 0;
+ u32 scaling_offset;
+ int ret = 0;
+
+ /* FIXME: find a better way, it only be used for debugging purpose */
+ dev = task->mpp_task.session->mpp_dev->dev;
+ if (!dev)
+ return -EINVAL;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR_OR_NULL(dmabuf)) {
+ dev_err(dev, "invliad pps buffer\n");
+ return -ENOENT;
+ }
+
+ ret = dma_buf_begin_cpu_access(dmabuf, DMA_FROM_DEVICE);
+ if (ret) {
+ dev_err(dev, "can't access the pps buffer\n");
+ goto done;
+ }
+
+ vaddr = dma_buf_vmap(dmabuf);
+ if (!vaddr) {
+ dev_err(dev, "can't access the pps buffer\n");
+ ret = -EIO;
+ goto done;
+ }
+ pps = vaddr + offset;
+
+ memcpy(&scaling_offset, pps + base, sizeof(scaling_offset));
+ scaling_offset = le32_to_cpu(scaling_offset);
+
+ scaling_fd = scaling_offset & 0x3ff;
+ scaling_offset = scaling_offset >> 10;
+
+ if (scaling_fd > 0) {
+ struct mpp_mem_region *mem_region = NULL;
+ dma_addr_t tmp = 0;
+ int i = 0;
+
+ mem_region = mpp_dev_task_attach_fd(&task->mpp_task,
+ scaling_fd);
+ if (IS_ERR(mem_region)) {
+ ret = PTR_ERR(mem_region);
+ goto done;
+ }
+
+ tmp = mem_region->iova;
+ tmp += scaling_offset;
+ tmp = cpu_to_le32(tmp);
+ mpp_debug(DEBUG_PPS_FILL,
+ "pps at %p, scaling fd: %3d => %pad + offset %10d\n",
+ pps, scaling_fd, &mem_region->iova, offset);
+
+ /* Fill the scaling list address in each pps entries */
+ for (i = 0; i < count; i++, base += pps_info_size)
+ memcpy(pps + base, &tmp, sizeof(tmp));
+ }
+
+done:
+ dma_buf_vunmap(dmabuf, vaddr);
+ dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);
+ dma_buf_put(dmabuf);
+
+ return ret;
+}
+#endif
+
+static void *rockchip_mpp_rkvdec_alloc_task(struct mpp_session *session,
+ void __user * src, u32 size)
+{
+ struct rkvdec_task *task = NULL;
+ struct vb2_v4l2_buffer *src_buf;
+ u32 fmt = 0;
+ int err = -EFAULT;
+
+ mpp_debug_enter();
+
+ task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (!task)
+ return NULL;
+
+ mpp_dev_task_init(session, &task->mpp_task);
+
+ src_buf = v4l2_m2m_next_src_buf(session->fh.m2m_ctx);
+ v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
+ &session->ctrl_handler);
+
+ fmt = session->fmt_out.pixelformat;
+ switch (fmt) {
+ case V4L2_PIX_FMT_HEVC_SLICE:
+ err = rkvdec_hevc_gen_reg(session, task->reg, src_buf);
+ break;
+ default:
+ goto fail;
+ }
+
+ if (err)
+ goto fail;
+
+ v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
+ &session->ctrl_handler);
+
+#if 0
+ if (fmt == RKVDEC_FMT_VP9D) {
+ struct mpp_mem_region *mem_region = NULL;
+ dma_addr_t iova = 0;
+ u32 offset = task->reg[RKVDEC_REG_VP9_REFCOLMV_BASE_INDEX];
+ int fd = task->reg[RKVDEC_REG_VP9_REFCOLMV_BASE_INDEX] & 0x3ff;
+
+ offset = offset >> 10 << 4;
+ mem_region = mpp_dev_task_attach_fd(&task->mpp_task, fd);
+ if (IS_ERR(mem_region)) {
+ err = PTR_ERR(mem_region);
+ goto fail;
+ }
+
+ iova = mem_region->iova;
+ task->reg[RKVDEC_REG_VP9_REFCOLMV_BASE_INDEX] = iova + offset;
+ }
+#endif
+
+#if 0
+ pps_fd = task->reg[RKVDEC_REG_PPS_BASE_INDEX] & 0x3ff;
+ pps_offset = task->reg[RKVDEC_REG_PPS_BASE_INDEX] >> 10;
+ if (pps_fd > 0) {
+ int pps_info_offset;
+ int pps_info_count;
+ int pps_info_size;
+ int scaling_list_addr_offset;
+
+ switch (fmt) {
+ case RKVDEC_FMT_H264D:
+ pps_info_offset = pps_offset;
+ pps_info_count = 256;
+ pps_info_size = 32;
+ scaling_list_addr_offset = 23;
+ break;
+ case RKVDEC_FMT_H265D:
+ pps_info_offset = pps_offset;
+ pps_info_count = 64;
+ pps_info_size = 80;
+ scaling_list_addr_offset = 74;
+ break;
+ default:
+ pps_info_offset = 0;
+ pps_info_count = 0;
+ pps_info_size = 0;
+ scaling_list_addr_offset = 0;
+ break;
+ }
+
+ mpp_debug(DEBUG_PPS_FILL, "scaling list filling parameter:\n");
+ mpp_debug(DEBUG_PPS_FILL,
+ "pps_info_offset %d\n", pps_info_offset);
+ mpp_debug(DEBUG_PPS_FILL,
+ "pps_info_count %d\n", pps_info_count);
+ mpp_debug(DEBUG_PPS_FILL,
+ "pps_info_size %d\n", pps_info_size);
+ mpp_debug(DEBUG_PPS_FILL,
+ "scaling_list_addr_offset %d\n",
+ scaling_list_addr_offset);
+
+ if (pps_info_count) {
+ err = fill_scaling_list_pps(task, pps_fd,
+ pps_info_offset,
+ pps_info_count,
+ pps_info_size,
+ scaling_list_addr_offset);
+ if (err) {
+ mpp_err("fill pps failed\n");
+ goto fail;
+ }
+ }
+ }
+#endif
+
+ mpp_debug_leave();
+
+ return &task->mpp_task;
+
+fail:
+ kfree(task);
+ return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvdec_prepare(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *task)
+{
+ struct rockchip_rkvdec_dev *dec_dev = to_rkvdec_dev(mpp_dev);
+
+ if (dec_dev->state == RKVDEC_STATE_NORMAL)
+ return -EINVAL;
+ /*
+ * Don't do soft reset before running or you will meet 0x00408322
+ * if you will decode a HEVC stream. Different error for the AVC.
+ */
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_run(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task)
+{
+ struct rockchip_rkvdec_dev *dec_dev = NULL;
+ struct rkvdec_task *task = NULL;
+ u32 reg = 0;
+
+ mpp_debug_enter();
+
+ dec_dev = to_rkvdec_dev(mpp_dev);
+ task = to_rkvdec_task(mpp_task);
+
+ switch (dec_dev->state) {
+ case RKVDEC_STATE_NORMAL:
+ /* FIXME: spin lock here */
+ dec_dev->current_task = task;
+
+ reg = RKVDEC_CACHE_PERMIT_CACHEABLE_ACCESS
+ | RKVDEC_CACHE_PERMIT_READ_ALLOCATE;
+ if (!(debug & DEBUG_CACHE_32B))
+ reg |= RKVDEC_CACHE_LINE_SIZE_64_BYTES;
+
+ mpp_dev_write(mpp_dev, RKVDEC_REG_CACHE_ENABLE(0), reg);
+ mpp_dev_write(mpp_dev, RKVDEC_REG_CACHE_ENABLE(1), reg);
+
+ mpp_dev_write_seq(mpp_dev, RKVDEC_REG_SYS_CTRL,
+ &task->reg[RKVDEC_REG_SYS_CTRL_INDEX],
+ mpp_dev->variant->reg_len
+ - RKVDEC_REG_SYS_CTRL_INDEX);
+
+ /* Flush the register before the start the device */
+ wmb();
+ mpp_dev_write(mpp_dev, RKVDEC_REG_DEC_INT_EN,
+ task->reg[RKVDEC_REG_DEC_INT_EN_INDEX]
+ | RKVDEC_DEC_START);
+ break;
+ default:
+ break;
+ }
+
+ mpp_debug_leave();
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_finish(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task)
+{
+ struct rockchip_rkvdec_dev *dec_dev = to_rkvdec_dev(mpp_dev);
+ struct rkvdec_task *task = to_rkvdec_task(mpp_task);
+
+ mpp_debug_enter();
+
+ switch (dec_dev->state) {
+ case RKVDEC_STATE_NORMAL:{
+ mpp_dev_read_seq(mpp_dev, RKVDEC_REG_SYS_CTRL,
+ &task->reg[RKVDEC_REG_SYS_CTRL_INDEX],
+ mpp_dev->variant->reg_len
+ - RKVDEC_REG_SYS_CTRL_INDEX);
+ task->reg[RKVDEC_REG_DEC_INT_EN_INDEX] =
+ task->irq_status;
+ }
+ break;
+ default:
+ break;
+ }
+
+ mpp_debug_leave();
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_result(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task,
+ u32 __user * dst, u32 size)
+{
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_free_task(struct mpp_session *session,
+ struct mpp_task *mpp_task)
+{
+ struct rkvdec_task *task = to_rkvdec_task(mpp_task);
+
+ mpp_dev_task_finalize(session, mpp_task);
+ kfree(task);
+
+ return 0;
+}
+
+static irqreturn_t mpp_rkvdec_isr(int irq, void *dev_id)
+{
+ struct rockchip_rkvdec_dev *dec_dev = dev_id;
+ struct rockchip_mpp_dev *mpp_dev = &dec_dev->mpp_dev;
+ struct rkvdec_task *task = NULL;
+ struct mpp_task *mpp_task = NULL;
+ u32 irq_status;
+ u32 err_mask;
+
+ irq_status = mpp_dev_read(mpp_dev, RKVDEC_REG_DEC_INT_EN);
+ if (!(irq_status & RKVDEC_DEC_INT_RAW))
+ return IRQ_NONE;
+
+ mpp_dev_write(mpp_dev, RKVDEC_REG_DEC_INT_EN, RKVDEC_CLOCK_GATE_EN);
+ /* FIXME use a spin lock here */
+ task = (struct rkvdec_task *)dec_dev->current_task;
+ if (!task) {
+ dev_err(dec_dev->mpp_dev.dev, "no current task\n");
+ return IRQ_HANDLED;
+ }
+ mpp_debug_time_diff(&task->mpp_task);
+
+ task->irq_status = irq_status;
+ switch (dec_dev->state) {
+ case RKVDEC_STATE_NORMAL:
+ mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
+ task->irq_status);
+
+ err_mask = RKVDEC_INT_BUF_EMPTY
+ | RKVDEC_INT_BUS_ERROR
+ | RKVDEC_INT_COLMV_REF_ERROR
+ | RKVDEC_INT_STRM_ERROR | RKVDEC_INT_TIMEOUT;
+
+ if (err_mask & task->irq_status)
+ atomic_set(&mpp_dev->reset_request, 1);
+
+ mpp_task = &task->mpp_task;
+ mpp_dev_task_finish(mpp_task->session, mpp_task);
+
+ mpp_debug_leave();
+ return IRQ_HANDLED;
+ default:
+ goto fail;
+ }
+fail:
+ return IRQ_HANDLED;
+}
+
+static int rockchip_mpp_rkvdec_assign_reset(struct rockchip_rkvdec_dev *dec_dev)
+{
+ struct rockchip_mpp_dev *mpp_dev = &dec_dev->mpp_dev;
+
+ dec_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ dec_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");
+ /* The reset controller below are not shared with VPU */
+ dec_dev->rst_niu_a = devm_reset_control_get(mpp_dev->dev, "niu_a");
+ dec_dev->rst_niu_h = devm_reset_control_get(mpp_dev->dev, "niu_h");
+ dec_dev->rst_core = devm_reset_control_get(mpp_dev->dev, "video_core");
+ dec_dev->rst_cabac = devm_reset_control_get(mpp_dev->dev,
+ "video_cabac");
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_a)) {
+ mpp_err("No aclk reset resource define\n");
+ dec_dev->rst_a = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_h)) {
+ mpp_err("No hclk reset resource define\n");
+ dec_dev->rst_h = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_niu_a)) {
+ mpp_err("No axi niu reset resource define\n");
+ dec_dev->rst_niu_a = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_niu_h)) {
+ mpp_err("No ahb niu reset resource define\n");
+ dec_dev->rst_niu_h = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_core)) {
+ mpp_err("No core reset resource define\n");
+ dec_dev->rst_core = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_cabac)) {
+ mpp_err("No cabac reset resource define\n");
+ dec_dev->rst_cabac = NULL;
+ }
+
+ safe_unreset(dec_dev->rst_a);
+ safe_unreset(dec_dev->rst_h);
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+ struct rockchip_rkvdec_dev *dec = to_rkvdec_dev(mpp_dev);
+
+ if (dec->rst_a && dec->rst_h) {
+ mpp_debug(DEBUG_RESET, "reset in\n");
+ rockchip_pmu_idle_request(mpp_dev->dev, true);
+
+ safe_reset(dec->rst_niu_a);
+ safe_reset(dec->rst_niu_h);
+ safe_reset(dec->rst_a);
+ safe_reset(dec->rst_h);
+ safe_reset(dec->rst_core);
+ safe_reset(dec->rst_cabac);
+ udelay(5);
+ safe_unreset(dec->rst_niu_h);
+ safe_unreset(dec->rst_niu_a);
+ safe_unreset(dec->rst_a);
+ safe_unreset(dec->rst_h);
+ safe_unreset(dec->rst_core);
+ safe_unreset(dec->rst_cabac);
+
+ rockchip_pmu_idle_request(mpp_dev->dev, false);
+
+ mpp_dev_write(mpp_dev, RKVDEC_REG_DEC_INT_EN, 0);
+ dec->current_task = NULL;
+ mpp_debug(DEBUG_RESET, "reset out\n");
+ }
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_sip_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+/* The reset flow in arm trustzone firmware */
+#if CONFIG_ROCKCHIP_SIP
+ sip_smc_vpu_reset(0, 0, 0);
+#else
+ return rockchip_mpp_rkvdec_reset(mpp_dev);
+#endif
+ return 0;
+}
+
+#if 0
+static int rkvdec_rk3328_iommu_hdl(struct iommu_domain *iommu,
+ struct device *iommu_dev, unsigned long iova,
+ int status, void *arg)
+{
+ struct device *dev = (struct device *)arg;
+ struct platform_device *pdev = NULL;
+ struct rockchip_rkvdec_dev *dec_dev = NULL;
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+
+ int ret = -EIO;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ if (!pdev) {
+ dev_err(dev, "invalid platform_device\n");
+ ret = -ENXIO;
+ goto done;
+ }
+
+ dec_dev = platform_get_drvdata(pdev);
+ if (!dec_dev) {
+ dev_err(dev, "invalid device instance\n");
+ ret = -ENXIO;
+ goto done;
+ }
+ mpp_dev = &dec_dev->mpp_dev;
+
+ if (IOMMU_GET_BUS_ID(status) == 2) {
+ unsigned long page_iova = 0;
+
+ /* avoid another page fault occur after page fault */
+ if (dec_dev->aux_iova != 0)
+ iommu_unmap(mpp_dev->iommu_info->domain,
+ dec_dev->aux_iova, IOMMU_PAGE_SIZE);
+
+ page_iova = round_down(iova, IOMMU_PAGE_SIZE);
+ ret = iommu_map(mpp_dev->iommu_info->domain, page_iova,
+ page_to_phys(dec_dev->aux_page),
+ IOMMU_PAGE_SIZE, DMA_FROM_DEVICE);
+ if (!ret)
+ dec_dev->aux_iova = page_iova;
+ }
+
+done:
+ return ret;
+}
+#endif
+
+static struct mpp_dev_ops rkvdec_ops = {
+ .alloc_task = rockchip_mpp_rkvdec_alloc_task,
+ .prepare = rockchip_mpp_rkvdec_prepare,
+ .run = rockchip_mpp_rkvdec_run,
+ .finish = rockchip_mpp_rkvdec_finish,
+ .result = rockchip_mpp_rkvdec_result,
+ .free_task = rockchip_mpp_rkvdec_free_task,
+ .reset = rockchip_mpp_rkvdec_reset,
+};
+
+#if 0
+static struct mpp_dev_ops rkvdec_rk3328_ops = {
+ .alloc_task = rockchip_mpp_rkvdec_alloc_task,
+ .prepare = rockchip_mpp_rkvdec_prepare,
+ .run = rockchip_mpp_rkvdec_run,
+ .finish = rockchip_mpp_rkvdec_finish,
+ .result = rockchip_mpp_rkvdec_result,
+ .free_task = rockchip_mpp_rkvdec_free_task,
+ .reset = rockchip_mpp_rkvdec_sip_reset,
+};
+#endif
+
+static int rockchip_mpp_rkvdec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_rkvdec_dev *dec_dev = NULL;
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+ int ret = 0;
+
+ dec_dev = devm_kzalloc(dev, sizeof(struct rockchip_rkvdec_dev),
+ GFP_KERNEL);
+ if (!dec_dev)
+ return -ENOMEM;
+
+ mpp_dev = &dec_dev->mpp_dev;
+ mpp_dev->variant = rockchip_rkvdec_get_drv_data(pdev);
+
+#if 0
+ if (mpp_dev->variant->version_bit & RKVDEC_VER_RK3328_BIT) {
+ ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvdec_rk3328_ops);
+
+ dec_dev->aux_page = alloc_page(GFP_KERNEL);
+ if (!dec_dev->aux_page) {
+ dev_err(dev,
+ "can't allocate a page for auxiliary usage\n");
+ return ret;
+ }
+ dec_dev->aux_iova = 0;
+
+ iommu_set_fault_handler(mpp_dev->iommu_info->domain,
+ rkvdec_rk3328_iommu_hdl, dev);
+ } else {
+ ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvdec_ops);
+ }
+#else
+ ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvdec_ops);
+#endif
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, mpp_dev->irq, NULL, mpp_rkvdec_isr,
+ IRQF_SHARED | IRQF_ONESHOT,
+ dev_name(dev), dec_dev);
+ if (ret) {
+ dev_err(dev, "register interrupter runtime failed\n");
+ return ret;
+ }
+
+ rockchip_mpp_rkvdec_assign_reset(dec_dev);
+ dec_dev->state = RKVDEC_STATE_NORMAL;
+
+ rkvdec_ioctl_ops = mpp_ioctl_ops_templ;
+ rkvdec_ioctl_ops.vidioc_s_fmt_vid_out_mplane =
+ rkvdec_s_fmt_vid_out_mplane;
+ rkvdec_ioctl_ops.vidioc_s_fmt_vid_cap_mplane =
+ rkvdec_s_fmt_vid_cap_mplane;
+
+ ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name,
+ &rkvdec_fops, &rkvdec_ioctl_ops);
+ if (ret)
+ dev_err(dev, "register char device failed: %d\n", ret);
+
+ memcpy(mpp_dev->fmt_out, fmt_out_templ, sizeof(fmt_out_templ));
+ memcpy(mpp_dev->fmt_cap, fmt_cap_templ, sizeof(fmt_cap_templ));
+ dev_info(dev, "probing finish\n");
+
+ platform_set_drvdata(pdev, dec_dev);
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdec_remove(struct platform_device *pdev)
+{
+ struct rockchip_rkvdec_dev *dec_dev = platform_get_drvdata(pdev);
+
+ mpp_dev_common_remove(&dec_dev->mpp_dev);
+
+ return 0;
+}
+
+static const struct of_device_id mpp_rkvdec_dt_match[] = {
+ {.compatible = "rockchip,video-decoder-v1p",.data = &rkvdec_v1p_data},
+ {.compatible = "rockchip,video-decoder-v1",.data = &rkvdec_v1_data},
+ {.compatible = "rockchip,hevc-decoder-v1",.data = &rk_hevcdec_data},
+ {},
+};
+
+static void *rockchip_rkvdec_get_drv_data(struct platform_device *pdev)
+{
+ struct mpp_dev_variant *driver_data = NULL;
+
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(mpp_rkvdec_dt_match, pdev->dev.of_node);
+ if (match)
+ driver_data = (struct mpp_dev_variant *)match->data;
+ }
+ return driver_data;
+}
+
+static struct platform_driver rockchip_rkvdec_driver = {
+ .probe = rockchip_mpp_rkvdec_probe,
+ .remove = rockchip_mpp_rkvdec_remove,
+ .driver = {
+ .name = RKVDEC_DRIVER_NAME,
+ .of_match_table = of_match_ptr(mpp_rkvdec_dt_match),
+ },
+};
+
+static int __init mpp_dev_rkvdec_init(void)
+{
+ int ret = platform_driver_register(&rockchip_rkvdec_driver);
+
+ if (ret) {
+ mpp_err("Platform device register failed (%d).\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit mpp_dev_rkvdec_exit(void)
+{
+ platform_driver_unregister(&rockchip_rkvdec_driver);
+}
+
+module_init(mpp_dev_rkvdec_init);
+module_exit(mpp_dev_rkvdec_exit);
+
+MODULE_DEVICE_TABLE(of, mpp_rkvdec_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
new file mode 100644
index 000000000000..ce98aa15025e
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ * Randy Li, <[email protected]>
+ *
+ * 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 <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+
+#include <linux/pm_runtime.h>
+
+#include "mpp_debug.h"
+#include "mpp_dev_common.h"
+#include "vdpu2/hal.h"
+
+#define RKVDPU2_DRIVER_NAME "mpp_vdpu2"
+#define RKVDPU2_NODE_NAME "vpu-service"
+
+#define to_rkvdpu_task(ctx) \
+ container_of(ctx, struct rkvdpu_task, mpp_task)
+#define to_rkvdpu_dev(dev) \
+ container_of(dev, struct rockchip_rkvdpu_dev, mpp_dev)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for vdpu2 debug information");
+
+struct rockchip_rkvdpu_dev {
+ struct rockchip_mpp_dev mpp_dev;
+
+ struct reset_control *rst_a;
+ struct reset_control *rst_h;
+
+ void *current_task;
+};
+
+struct rkvdpu_task {
+ struct mpp_task mpp_task;
+
+ u32 reg[ROCKCHIP_VDPU2_REG_NUM];
+ u32 idx;
+
+ u32 strm_base;
+ u32 irq_status;
+};
+
+static struct rockchip_mpp_control vdpu_controls[] = {
+ {
+ .codec = V4L2_PIX_FMT_MPEG2_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
+ .elem_size = sizeof(struct v4l2_ctrl_mpeg2_slice_params),
+ },
+ {
+ .codec = V4L2_PIX_FMT_MPEG2_SLICE,
+ .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION,
+ .elem_size = sizeof(struct v4l2_ctrl_mpeg2_quantization),
+ },
+};
+
+static struct v4l2_pix_format_mplane fmt_out_templ[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE,
+ },
+ {.pixelformat = 0},
+};
+
+static struct v4l2_pix_format_mplane fmt_cap_templ[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_NV12M,
+ },
+ {.pixelformat = 0},
+};
+
+static const struct mpp_dev_variant rkvdpu_v2_data = {
+ /* Exclude the register of the Performance counter */
+ .reg_len = 159,
+ .node_name = RKVDPU2_NODE_NAME,
+ .vfd_func = MEDIA_ENT_F_PROC_VIDEO_DECODER,
+};
+
+static int rkvdpu_open(struct file *filp);
+
+static const struct v4l2_file_operations rkvdpu_fops = {
+ .open = rkvdpu_open,
+ .release = rockchip_mpp_dev_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+#if 1
+static struct v4l2_ioctl_ops rkvdpu_ioctl_ops = { 0, };
+#endif
+
+static void *rockchip_rkvdpu2_get_drv_data(struct platform_device *pdev);
+
+
+static int rkvdpu_s_fmt_vid_out_mplane(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct vb2_queue *vq;
+ int sizes = 0;
+ int i;
+
+ /* TODO: We can change width and height at streaming on */
+ vq = v4l2_m2m_get_vq(session->fh.m2m_ctx, f->type);
+ if (vb2_is_streaming(vq))
+ return -EBUSY;
+
+#if 0
+ ret = rkvdpu_try_fmt_out(filp, priv, f);
+ if (ret)
+ return ret;
+#endif
+ if (!pix_mp->num_planes)
+ pix_mp->num_planes = 1;
+
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ sizes += pix_mp->plane_fmt[i].sizeimage;
+ }
+ /* strm_len is 24 bits */
+ if (sizes >= SZ_16M)
+ return -EINVAL;
+
+ session->fmt_out = *pix_mp;
+
+ /* Copy the pixel format information from OUTPUT to CAPUTRE */
+ session->fmt_cap.pixelformat = V4L2_PIX_FMT_NV12M;
+ session->fmt_cap.width = pix_mp->width;
+ session->fmt_cap.height = pix_mp->height;
+ session->fmt_cap.colorspace = pix_mp->colorspace;
+ session->fmt_cap.ycbcr_enc = pix_mp->ycbcr_enc;
+ session->fmt_cap.xfer_func = pix_mp->xfer_func;
+ session->fmt_cap.quantization = pix_mp->quantization;
+
+ return 0;
+}
+
+static int rkvdpu_s_fmt_vid_cap_mplane(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mpp_session *session = container_of(filp->private_data,
+ struct mpp_session, fh);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(session->fh.m2m_ctx, f->type);
+ if (vb2_is_streaming(vq))
+ return -EBUSY;
+
+#if 0
+ ret = rkvdpu_try_fmt_cap(filp, priv, f);
+ if (ret)
+ return ret;
+#endif
+ switch (pix_mp->pixelformat) {
+ case V4L2_PIX_FMT_NV12M:
+ pix_mp->plane_fmt[0].bytesperline = ALIGN(pix_mp->width, 16);
+ pix_mp->plane_fmt[1].bytesperline = ALIGN(pix_mp->width, 16);
+ pix_mp->plane_fmt[0].sizeimage = ALIGN(pix_mp->width, 16) *
+ /*
+ * FIXME: the plane 1 may map to a lower address than plane 0
+ * before solve this allocator problem, it can pass the test
+ */
+ ALIGN(pix_mp->height, 16) * 2;
+ /* Additional space for motion vector */
+ pix_mp->plane_fmt[1].sizeimage = ALIGN(pix_mp->width, 16) *
+ ALIGN(pix_mp->height, 16);
+ pix_mp->num_planes = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ session->fmt_cap = *pix_mp;
+
+ return 0;
+}
+
+static int vdpu_setup_ctrls(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_session *session)
+{
+ struct v4l2_ctrl_handler *hdl = &session->ctrl_handler;
+ struct v4l2_ctrl *ctrl;
+ unsigned int num_ctrls = ARRAY_SIZE(vdpu_controls);
+ unsigned int i;
+
+ v4l2_ctrl_handler_init(hdl, num_ctrls);
+ if (hdl->error) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to initialize control handler\n");
+ return hdl->error;
+ }
+
+ for (i = 0; i < num_ctrls; i++) {
+ struct v4l2_ctrl_config cfg = { };
+
+ cfg.id = vdpu_controls[i].id;
+ cfg.elem_size = vdpu_controls[i].elem_size;
+
+ ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ if (hdl->error) {
+ v4l2_err(&mpp_dev->v4l2_dev,
+ "Failed to create new custom %d control\n",
+ cfg.id);
+ goto fail;
+ }
+ }
+
+ session->fh.ctrl_handler = hdl;
+ v4l2_ctrl_handler_setup(hdl);
+
+ return 0;
+fail:
+ v4l2_ctrl_handler_free(hdl);
+ return hdl->error;
+}
+
+static int rkvdpu_open(struct file *filp)
+{
+ struct rockchip_mpp_dev *mpp_dev = video_drvdata(filp);
+ struct video_device *vdev = video_devdata(filp);
+ struct mpp_session *session = NULL;
+ /* TODO: install ctrl based on register report */
+ int error = 0;
+
+ mpp_debug_enter();
+
+ session = rockchip_mpp_alloc_session(mpp_dev, vdev);
+ if (IS_ERR_OR_NULL(session))
+ return PTR_ERR(session);
+
+ error = vdpu_setup_ctrls(mpp_dev, session);
+ if (error) {
+ kfree(session);
+ return error;
+ }
+
+ filp->private_data = &session->fh;
+ pm_runtime_get_sync(mpp_dev->dev);
+
+ mpp_debug_leave();
+ return 0;
+}
+
+static void *rockchip_mpp_rkvdpu_alloc_task(struct mpp_session *session,
+ void __user * src, u32 size)
+{
+ struct rkvdpu_task *task = NULL;
+ struct vb2_v4l2_buffer *src_buf;
+ u32 fmt = 0;
+ int err = -EFAULT;
+
+ mpp_debug_enter();
+
+ task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (!task)
+ return NULL;
+
+ mpp_dev_task_init(session, &task->mpp_task);
+
+ src_buf = v4l2_m2m_next_src_buf(session->fh.m2m_ctx);
+ v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
+ &session->ctrl_handler);
+
+ fmt = session->fmt_out.pixelformat;
+ switch (fmt) {
+ case V4L2_PIX_FMT_MPEG2_SLICE:
+ err = rkvdpu_mpeg2_gen_reg(session, task->reg, src_buf);
+ break;
+ default:
+ goto fail;
+ }
+
+ if (err)
+ goto fail;
+
+ v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
+ &session->ctrl_handler);
+
+ mpp_debug_leave();
+
+ return &task->mpp_task;
+
+fail:
+ if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+ mpp_debug_dump_reg_mem(task->reg, ROCKCHIP_VDPU2_REG_NUM);
+
+ kfree(task);
+ return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvdpu_prepare(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task)
+{
+ struct rkvdpu_task *task = container_of(mpp_task, struct rkvdpu_task,
+ mpp_task);
+
+ return rkvdpu_mpeg2_prepare_buf(mpp_task->session, task->reg);
+}
+
+static int rockchip_mpp_rkvdpu_run(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task)
+{
+ struct rkvdpu_task *task = NULL;
+ struct rockchip_rkvdpu_dev *dec_dev = NULL;
+
+ mpp_debug_enter();
+
+ task = to_rkvdpu_task(mpp_task);
+ dec_dev = to_rkvdpu_dev(mpp_dev);
+
+ /* FIXME: spin lock here */
+ dec_dev->current_task = task;
+ /* NOTE: Only write the decoder part */
+ mpp_dev_write_seq(mpp_dev, RKVDPU2_REG_DEC_CTRL,
+ &task->reg[RKVDPU2_REG_DEC_CTRL_INDEX],
+ RKVDPU2_REG_DEC_DEV_CTRL_INDEX
+ - RKVDPU2_REG_DEC_CTRL_INDEX);
+
+ mpp_dev_write_seq(mpp_dev, RKVDPU2_REG59,
+ &task->reg[RKVDPU2_REG59_INDEX],
+ mpp_dev->variant->reg_len - RKVDPU2_REG59_INDEX);
+ /* Flush the registers */
+ wmb();
+ mpp_dev_write(mpp_dev, RKVDPU2_REG_DEC_DEV_CTRL,
+ task->reg[RKVDPU2_REG_DEC_DEV_CTRL_INDEX]
+ | RKVDPU2_DEC_START);
+
+ mpp_debug_leave();
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdpu_finish(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task)
+{
+ struct rkvdpu_task *task = to_rkvdpu_task(mpp_task);
+
+ mpp_debug_enter();
+
+ /* NOTE: Only read the decoder part */
+ mpp_dev_read_seq(mpp_dev, RKVDPU2_REG_DEC_CTRL,
+ &task->reg[RKVDPU2_REG_DEC_CTRL_INDEX],
+ mpp_dev->variant->reg_len
+ - RKVDPU2_REG_DEC_CTRL_INDEX);
+
+ task->reg[RKVDPU2_REG_DEC_INT_EN_INDEX] = task->irq_status;
+
+ mpp_debug_leave();
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdpu_result(struct rockchip_mpp_dev *mpp_dev,
+ struct mpp_task *mpp_task,
+ u32 __user * dst, u32 size)
+{
+ struct rkvdpu_task *task = to_rkvdpu_task(mpp_task);
+ u32 err_mask;
+
+ err_mask = RKVDPU2_INT_TIMEOUT
+ | RKVDPU2_INT_STRM_ERROR
+ | RKVDPU2_INT_ASO_ERROR
+ | RKVDPU2_INT_BUF_EMPTY
+ | RKVDPU2_INT_BUS_ERROR;
+
+ if (err_mask & task->irq_status)
+ return VB2_BUF_STATE_ERROR;
+
+ return VB2_BUF_STATE_DONE;
+}
+
+static int rockchip_mpp_rkvdpu_free_task(struct mpp_session *session,
+ struct mpp_task *mpp_task)
+{
+ struct rkvdpu_task *task = to_rkvdpu_task(mpp_task);
+
+ mpp_dev_task_finalize(session, mpp_task);
+ kfree(task);
+
+ return 0;
+}
+
+static irqreturn_t mpp_rkvdpu_isr(int irq, void *dev_id)
+{
+ struct rockchip_rkvdpu_dev *dec_dev = dev_id;
+ struct rockchip_mpp_dev *mpp_dev = &dec_dev->mpp_dev;
+ struct rkvdpu_task *task = NULL;
+ struct mpp_task *mpp_task = NULL;
+ u32 irq_status;
+ u32 err_mask;
+
+ irq_status = mpp_dev_read(mpp_dev, RKVDPU2_REG_DEC_INT_EN);
+ if (!(irq_status & RKVDPU2_DEC_INT_RAW))
+ return IRQ_NONE;
+
+ mpp_dev_write(mpp_dev, RKVDPU2_REG_DEC_INT_EN, 0);
+ mpp_dev_write(mpp_dev, RKVDPU2_REG_DEC_DEV_CTRL,
+ RKVDPU2_DEC_CLOCK_GATE_EN);
+
+ /* FIXME use a spin lock here */
+ task = (struct rkvdpu_task *)dec_dev->current_task;
+ if (!task) {
+ dev_err(dec_dev->mpp_dev.dev, "no current task\n");
+ return IRQ_HANDLED;
+ }
+
+ mpp_task = &task->mpp_task;
+ mpp_debug_time_diff(mpp_task);
+ task->irq_status = irq_status;
+ mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", task->irq_status);
+
+ err_mask = RKVDPU2_INT_TIMEOUT
+ | RKVDPU2_INT_STRM_ERROR
+ | RKVDPU2_INT_ASO_ERROR
+ | RKVDPU2_INT_BUF_EMPTY
+ | RKVDPU2_INT_BUS_ERROR;
+
+ if (err_mask & task->irq_status)
+ atomic_set(&mpp_dev->reset_request, 1);
+
+ mpp_dev_task_finish(mpp_task->session, mpp_task);
+
+ mpp_debug_leave();
+ return IRQ_HANDLED;
+}
+
+static int rockchip_mpp_rkvdpu_assign_reset(struct rockchip_rkvdpu_dev *dec_dev)
+{
+ struct rockchip_mpp_dev *mpp_dev = &dec_dev->mpp_dev;
+
+ dec_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ dec_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_a)) {
+ mpp_err("No aclk reset resource define\n");
+ dec_dev->rst_a = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(dec_dev->rst_h)) {
+ mpp_err("No hclk reset resource define\n");
+ dec_dev->rst_h = NULL;
+ }
+
+ safe_unreset(dec_dev->rst_h);
+ safe_unreset(dec_dev->rst_a);
+
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdpu_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+ struct rockchip_rkvdpu_dev *dec = to_rkvdpu_dev(mpp_dev);
+
+ if (dec->rst_a && dec->rst_h) {
+ mpp_debug(DEBUG_RESET, "reset in\n");
+
+ safe_reset(dec->rst_a);
+ safe_reset(dec->rst_h);
+ udelay(5);
+ safe_unreset(dec->rst_h);
+ safe_unreset(dec->rst_a);
+
+ mpp_dev_write(mpp_dev, RKVDPU2_REG_DEC_DEV_CTRL, 0);
+ mpp_dev_write(mpp_dev, RKVDPU2_REG_DEC_INT_EN, 0);
+ dec->current_task = NULL;
+ mpp_debug(DEBUG_RESET, "reset out\n");
+ }
+
+ return 0;
+}
+
+static struct mpp_dev_ops rkvdpu_ops = {
+ .alloc_task = rockchip_mpp_rkvdpu_alloc_task,
+ .prepare = rockchip_mpp_rkvdpu_prepare,
+ .run = rockchip_mpp_rkvdpu_run,
+ .finish = rockchip_mpp_rkvdpu_finish,
+ .result = rockchip_mpp_rkvdpu_result,
+ .free_task = rockchip_mpp_rkvdpu_free_task,
+ .reset = rockchip_mpp_rkvdpu_reset,
+};
+
+static int rockchip_mpp_rkvdpu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_rkvdpu_dev *dec_dev = NULL;
+ struct rockchip_mpp_dev *mpp_dev = NULL;
+ int ret = 0;
+
+ dec_dev = devm_kzalloc(dev, sizeof(struct rockchip_rkvdpu_dev),
+ GFP_KERNEL);
+ if (!dec_dev)
+ return -ENOMEM;
+
+ mpp_dev = &dec_dev->mpp_dev;
+ mpp_dev->variant = rockchip_rkvdpu2_get_drv_data(pdev);
+ ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvdpu_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, mpp_dev->irq, NULL, mpp_rkvdpu_isr,
+ IRQF_SHARED | IRQF_ONESHOT,
+ dev_name(dev), dec_dev);
+ if (ret) {
+ dev_err(dev, "register interrupter runtime failed\n");
+ return ret;
+ }
+
+ rockchip_mpp_rkvdpu_assign_reset(dec_dev);
+
+ rkvdpu_ioctl_ops = mpp_ioctl_ops_templ;
+ rkvdpu_ioctl_ops.vidioc_s_fmt_vid_out_mplane =
+ rkvdpu_s_fmt_vid_out_mplane;
+ rkvdpu_ioctl_ops.vidioc_s_fmt_vid_cap_mplane =
+ rkvdpu_s_fmt_vid_cap_mplane;
+
+ ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name,
+ &rkvdpu_fops, &rkvdpu_ioctl_ops);
+ if (ret)
+ dev_err(dev, "register char device failed: %d\n", ret);
+
+ memcpy(mpp_dev->fmt_out, fmt_out_templ, sizeof(fmt_out_templ));
+ memcpy(mpp_dev->fmt_cap, fmt_cap_templ, sizeof(fmt_cap_templ));
+ dev_info(dev, "probing finish\n");
+
+ platform_set_drvdata(pdev, dec_dev);
+
+ return 0;
+}
+
+static int rockchip_mpp_rkvdpu_remove(struct platform_device *pdev)
+{
+ struct rockchip_rkvdpu_dev *dec_dev = platform_get_drvdata(pdev);
+
+ mpp_dev_common_remove(&dec_dev->mpp_dev);
+
+ return 0;
+}
+
+static const struct of_device_id mpp_rkvdpu2_dt_match[] = {
+ {.compatible = "rockchip,vpu-decoder-v2",.data = &rkvdpu_v2_data},
+ {},
+};
+
+static void *rockchip_rkvdpu2_get_drv_data(struct platform_device *pdev)
+{
+ struct mpp_dev_variant *driver_data = NULL;
+
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(mpp_rkvdpu2_dt_match, pdev->dev.of_node);
+ if (match)
+ driver_data = (struct mpp_dev_variant *)match->data;
+ }
+ return driver_data;
+}
+
+static struct platform_driver rockchip_rkvdpu2_driver = {
+ .probe = rockchip_mpp_rkvdpu_probe,
+ .remove = rockchip_mpp_rkvdpu_remove,
+ .driver = {
+ .name = RKVDPU2_DRIVER_NAME,
+ .of_match_table = of_match_ptr(mpp_rkvdpu2_dt_match),
+ },
+};
+
+static int __init mpp_dev_rkvdpu2_init(void)
+{
+ int ret = platform_driver_register(&rockchip_rkvdpu2_driver);
+
+ if (ret) {
+ mpp_err("Platform device register failed (%d).\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit mpp_dev_rkvdpu2_exit(void)
+{
+ platform_driver_unregister(&rockchip_rkvdpu2_driver);
+}
+
+module_init(mpp_dev_rkvdpu2_init);
+module_exit(mpp_dev_rkvdpu2_exit);
+
+MODULE_DEVICE_TABLE(of, mpp_rkvdpu2_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rockchip-mpp/mpp_service.c b/drivers/staging/rockchip-mpp/mpp_service.c
new file mode 100644
index 000000000000..1e45ce141fc4
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_service.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "mpp_dev_common.h"
+#include "mpp_service.h"
+
+struct mpp_service {
+ /* service critical time lock */
+ struct completion running;
+ struct mpp_task *cur_task;
+
+ u32 dev_cnt;
+ struct list_head subdev_list;
+};
+
+struct mpp_service_node {
+ /* node structure global lock */
+ struct mutex lock;
+ struct mpp_service *parent;
+ struct list_head pending;
+};
+
+/* service queue schedule */
+void mpp_srv_push_pending(struct mpp_service_node *node, struct mpp_task *task)
+{
+ mutex_lock(&node->lock);
+ list_add_tail(&task->service_link, &node->pending);
+ mutex_unlock(&node->lock);
+}
+EXPORT_SYMBOL(mpp_srv_push_pending);
+
+struct mpp_task *mpp_srv_get_pending_task(struct mpp_service_node *node)
+{
+ struct mpp_task *task = NULL;
+
+ mutex_lock(&node->lock);
+ if (!list_empty(&node->pending)) {
+ task = list_first_entry(&node->pending, struct mpp_task,
+ service_link);
+ list_del_init(&task->service_link);
+ }
+ mutex_unlock(&node->lock);
+
+ return task;
+}
+EXPORT_SYMBOL(mpp_srv_get_pending_task);
+
+int mpp_srv_is_running(struct mpp_service_node *node)
+{
+ struct mpp_service *pservice = node->parent;
+
+ return !try_wait_for_completion(&pservice->running);
+}
+EXPORT_SYMBOL(mpp_srv_is_running);
+
+void mpp_srv_wait_to_run(struct mpp_service_node *node, struct mpp_task *task)
+{
+ struct mpp_service *pservice = node->parent;
+
+ wait_for_completion(&pservice->running);
+ pservice->cur_task = task;
+}
+EXPORT_SYMBOL(mpp_srv_wait_to_run);
+
+struct mpp_task *mpp_srv_get_cur_task(struct mpp_service_node *node)
+{
+ struct mpp_service *pservice = node->parent;
+
+ return pservice->cur_task;
+}
+EXPORT_SYMBOL(mpp_srv_get_cur_task);
+
+void mpp_srv_done(struct mpp_service_node *node, struct mpp_task *task)
+{
+ struct mpp_service *pservice = node->parent;
+
+ pservice->cur_task = NULL;
+ complete(&pservice->running);
+}
+EXPORT_SYMBOL(mpp_srv_done);
+
+int mpp_srv_abort(struct mpp_service_node *node, struct mpp_task *task)
+{
+ struct mpp_service *pservice = node->parent;
+
+ if (task) {
+ if (pservice->cur_task == task)
+ pservice->cur_task = NULL;
+ }
+ complete(&pservice->running);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpp_srv_abort);
+
+void *mpp_srv_attach(struct mpp_service *pservice, void *data)
+{
+ struct mpp_service_node *node = NULL;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return node;
+
+ node->parent = pservice;
+ mutex_init(&node->lock);
+ INIT_LIST_HEAD(&node->pending);
+
+ return node;
+}
+EXPORT_SYMBOL(mpp_srv_attach);
+
+void mpp_srv_detach(struct mpp_service_node *node)
+{
+ kfree(node);
+}
+EXPORT_SYMBOL(mpp_srv_detach);
+
+static void mpp_init_drvdata(struct mpp_service *pservice)
+{
+ init_completion(&pservice->running);
+ complete(&pservice->running);
+}
+
+static int mpp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpp_service *pservice = devm_kzalloc(dev, sizeof(*pservice),
+ GFP_KERNEL);
+ if (!pservice)
+ return -ENOMEM;
+
+ mpp_init_drvdata(pservice);
+
+ platform_set_drvdata(pdev, pservice);
+ dev_info(dev, "init success\n");
+
+ return 0;
+}
+
+static int mpp_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id mpp_service_dt_ids[] = {
+ { .compatible = "rockchip,mpp-service", },
+ { },
+};
+
+static struct platform_driver mpp_driver = {
+ .probe = mpp_probe,
+ .remove = mpp_remove,
+ .driver = {
+ .name = "mpp",
+ .of_match_table = of_match_ptr(mpp_service_dt_ids),
+ },
+};
+
+static int __init mpp_service_init(void)
+{
+ int ret = platform_driver_register(&mpp_driver);
+
+ if (ret) {
+ pr_err("Platform device register failed (%d).\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit mpp_service_exit(void)
+{
+}
+
+module_init(mpp_service_init);
+module_exit(mpp_service_exit)
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rockchip-mpp/mpp_service.h b/drivers/staging/rockchip-mpp/mpp_service.h
new file mode 100644
index 000000000000..a77cff7b02df
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_service.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ROCKCHIP_MPP_SERVICE_H_
+#define _ROCKCHIP_MPP_SERVICE_H_
+
+struct mpp_service_node;
+struct mpp_service;
+
+struct mpp_task;
+
+void mpp_srv_push_pending(struct mpp_service_node *node, struct mpp_task *task);
+struct mpp_task *mpp_srv_get_pending_task(struct mpp_service_node *node);
+
+void mpp_srv_run(struct mpp_service_node *node, struct mpp_task *task);
+void mpp_srv_done(struct mpp_service_node *node, struct mpp_task *task);
+int mpp_srv_abort(struct mpp_service_node *node, struct mpp_task *task);
+
+void mpp_srv_wait_to_run(struct mpp_service_node *node, struct mpp_task *task);
+struct mpp_task *mpp_srv_get_cur_task(struct mpp_service_node *node);
+
+int mpp_srv_is_running(struct mpp_service_node *node);
+
+void *mpp_srv_attach(struct mpp_service *pservice, void *data);
+void mpp_srv_detach(struct mpp_service_node *node);
+#endif
diff --git a/drivers/staging/rockchip-mpp/rkvdec/hal.h b/drivers/staging/rockchip-mpp/rkvdec/hal.h
new file mode 100644
index 000000000000..0f6b547e4913
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/rkvdec/hal.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ */
+
+#ifndef RKVDEC_HAL_H
+#define RKVDEC_HAL_H
+
+/* The maximum registers number of all the version */
+#define ROCKCHIP_RKVDEC_REG_NUM (109)
+
+#define RKVDEC_REG_DEC_INT_EN 0x004
+#define RKVDEC_REG_DEC_INT_EN_INDEX (1)
+#define RKVDEC_WR_DDR_ALIGN_EN BIT(23)
+#define RKVDEC_FORCE_SOFT_RESET_VALID BIT(21)
+#define RKVDEC_SOFTWARE_RESET_EN BIT(20)
+#define RKVDEC_INT_COLMV_REF_ERROR BIT(17)
+#define RKVDEC_INT_BUF_EMPTY BIT(16)
+#define RKVDEC_INT_TIMEOUT BIT(15)
+#define RKVDEC_INT_STRM_ERROR BIT(14)
+#define RKVDEC_INT_BUS_ERROR BIT(13)
+#define RKVDEC_DEC_INT_RAW BIT(9)
+#define RKVDEC_DEC_INT BIT(8)
+#define RKVDEC_DEC_TIMEOUT_EN BIT(5)
+#define RKVDEC_DEC_IRQ_DIS BIT(4)
+#define RKVDEC_CLOCK_GATE_EN BIT(1)
+#define RKVDEC_DEC_START BIT(0)
+
+#define RKVDEC_REG_SYS_CTRL 0x008
+#define RKVDEC_REG_SYS_CTRL_INDEX (2)
+#define RKVDEC_GET_FORMAT(x) (((x) >> 20) & 0x3)
+#define RKVDEC_FMT_H265D (0)
+#define RKVDEC_FMT_H264D (1)
+#define RKVDEC_FMT_VP9D (2)
+
+#define RKVDEC_REG_STREAM_RLC_BASE 0x010
+#define RKVDEC_REG_STREAM_RLC_BASE_INDEX (4)
+
+#define RKVDEC_REG_PPS_BASE 0x0a0
+#define RKVDEC_REG_PPS_BASE_INDEX (42)
+
+#define RKVDEC_REG_VP9_REFCOLMV_BASE 0x0d0
+#define RKVDEC_REG_VP9_REFCOLMV_BASE_INDEX (52)
+
+#define RKVDEC_REG_CACHE_ENABLE(i) (0x41c + ((i) * 0x40))
+#define RKVDEC_CACHE_PERMIT_CACHEABLE_ACCESS BIT(0)
+#define RKVDEC_CACHE_PERMIT_READ_ALLOCATE BIT(1)
+#define RKVDEC_CACHE_LINE_SIZE_64_BYTES BIT(4)
+
+int rkvdec_hevc_gen_reg(struct mpp_session *session, void *regs,
+ struct vb2_v4l2_buffer *src_buf);
+
+#endif
diff --git a/drivers/staging/rockchip-mpp/rkvdec/hevc.c b/drivers/staging/rockchip-mpp/rkvdec/hevc.c
new file mode 100644
index 000000000000..ac3a17176c5b
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/rkvdec/hevc.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Randy Li, <[email protected]>
+ *
+ * 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 <linux/types.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mpp_dev_common.h"
+#include "hal.h"
+#include "regs.h"
+
+static void init_hw_cfg(struct rkvdec_regs *p_regs)
+{
+ p_regs->sw_interrupt.dec_e = 1;
+ p_regs->sw_interrupt.dec_timeout_e = 1;
+ /* TODO: support HEVC tiles */
+ p_regs->sw_interrupt.wr_ddr_align_en = 1;
+ /* HEVC */
+ p_regs->sw_sysctrl.dec_mode = RKVDEC_FMT_H265D;
+
+ p_regs->sw_ref_valid = 0;
+ /* cabac_error_en */
+ p_regs->sw_strmd_error_en = 0xfdfffffd;
+ /* p_regs->extern_error_en = 0x30000000; */
+ p_regs->extern_error_en.error_en_highbits = 0x3000000;
+}
+
+static void ctb_calc(struct rkvdec_regs *p_regs,
+ const struct v4l2_ctrl_hevc_sps *sps)
+{
+ u32 min_cb_log2_size_y, ctb_log2_size_y, min_cb_size_y, ctb_size_y;
+ u32 pic_width_in_min_cbs_y, pic_height_in_min_bbs_y;
+ u32 stride_y, stride_uv, virstride_y, virstride_yuv;
+ u32 width, height;
+
+ min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ ctb_log2_size_y = min_cb_log2_size_y +
+ sps->log2_diff_max_min_luma_coding_block_size;
+
+ min_cb_size_y = 1 << min_cb_log2_size_y;
+ /* uiMaxCUWidth */
+ ctb_size_y = 1 << ctb_log2_size_y;
+
+ /* PicWidthInCtbsY (7-15) */
+ pic_width_in_min_cbs_y = sps->pic_width_in_luma_samples / min_cb_size_y;
+ pic_height_in_min_bbs_y = sps->pic_height_in_luma_samples /
+ min_cb_size_y;
+
+ width = pic_width_in_min_cbs_y << min_cb_log2_size_y;
+ height = pic_height_in_min_bbs_y << min_cb_log2_size_y;
+
+ stride_y = (roundup(width, ctb_size_y) *
+ ALIGN(sps->bit_depth_luma_minus8, 8)) >> 3;
+ stride_uv = (roundup(height, ctb_size_y) *
+ ALIGN(sps->bit_depth_chroma_minus8, 8)) >> 3;
+ stride_y = ALIGN(stride_y, 256) | 256;
+ stride_uv = ALIGN(stride_uv, 256) | 256;
+
+ virstride_y = stride_y * ALIGN(height, 8);
+ /* FIXME: may only applied for NV12 ? */
+ virstride_yuv = virstride_y + ((stride_uv * ALIGN(height, 8)) >> 1);
+
+ p_regs->sw_picparameter.y_hor_virstride = stride_y >> 4;
+ p_regs->sw_picparameter.uv_hor_virstride = stride_uv >> 4;
+ p_regs->sw_y_virstride = virstride_y;
+ p_regs->sw_yuv_virstride = virstride_yuv;
+}
+
+static int rkvdec_hevc_gen_ref(struct rkvdec_regs *p_regs,
+ struct vb2_v4l2_buffer *dst_buf,
+ const struct v4l2_ctrl_hevc_slice_params *slice_params)
+{
+ struct vb2_queue *cap_q = dst_buf->vb2_buf.vb2_queue;
+ dma_addr_t cur_addr;
+ u16 i = 0;
+
+ cur_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ p_regs->sw_decout_base = cur_addr;
+
+ /* FIXME: use a const value */
+ for (i = 0; i < 15; i++)
+ p_regs->sw_refer_base[i].ref_base = cur_addr >> 4;
+
+ for (i = 0; i < slice_params->num_active_dpb_entries; i++) {
+ dma_addr_t ref_addr;
+ /* FIXME: why two pic_order_cnt */
+ p_regs->sw_refer_poc[i] = slice_params->dpb[i].pic_order_cnt[0];
+
+ ref_addr = rockchip_mpp_find_addr(cap_q, &dst_buf->vb2_buf,
+ slice_params->dpb[i].
+ timestamp);
+ if (!ref_addr)
+ ref_addr = cur_addr;
+
+ p_regs->sw_refer_base[i].ref_base = ref_addr >> 4;
+ cur_addr = ref_addr;
+
+ p_regs->sw_ref_valid |= (1 << i);
+ }
+
+ /* Enable flag for reference picture */
+ p_regs->sw_refer_base[0].ref_valid_flag =
+ (p_regs->sw_ref_valid >> 0) & 0xf;
+ p_regs->sw_refer_base[1].ref_valid_flag =
+ (p_regs->sw_ref_valid >> 4) & 0xf;
+ p_regs->sw_refer_base[2].ref_valid_flag =
+ (p_regs->sw_ref_valid >> 8) & 0xf;
+ p_regs->sw_refer_base[3].ref_valid_flag =
+ (p_regs->sw_ref_valid >> 12) & 0x7;
+
+ return 0;
+}
+
+int rkvdec_hevc_gen_reg(struct mpp_session *session, void *regs,
+ struct vb2_v4l2_buffer *src_buf)
+{
+ const struct v4l2_ctrl_hevc_sps *sps;
+ const struct v4l2_ctrl_hevc_pps *pps;
+ const struct v4l2_ctrl_hevc_slice_params *slice_params;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct rkvdec_regs *p_regs = regs;
+ size_t stream_len = 0;
+
+ sps = rockchip_mpp_get_cur_ctrl(session, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
+ pps = rockchip_mpp_get_cur_ctrl(session, V4L2_CID_MPEG_VIDEO_HEVC_PPS);
+ slice_params = rockchip_mpp_get_cur_ctrl(session,
+ V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
+ if (!sps || !pps || !slice_params)
+ return -EINVAL;
+
+ init_hw_cfg(p_regs);
+
+ ctb_calc(p_regs, sps);
+
+ /* FIXME: support more than 1 slice */
+ p_regs->sw_picparameter.slice_num_lowbits = 0;
+ p_regs->sw_strm_rlc_base =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ /* The bitstream must be 128bit align ? */
+ p_regs->sw_sysctrl.strm_start_bit = slice_params->data_bit_offset;
+
+ /* hardware wants a zerod memory at the stream end */
+ stream_len = DIV_ROUND_UP(slice_params->bit_size, 16) + 64;
+ p_regs->sw_stream_len = stream_len;
+
+ dst_buf = v4l2_m2m_next_dst_buf(session->fh.m2m_ctx);
+ rkvdec_hevc_gen_ref(p_regs, dst_buf, slice_params);
+
+ return 0;
+}
diff --git a/drivers/staging/rockchip-mpp/rkvdec/regs.h b/drivers/staging/rockchip-mpp/rkvdec/regs.h
new file mode 100644
index 000000000000..c1f8ad4f836f
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/rkvdec/regs.h
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Randy Li <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef RKVDEC_REGS_H_
+#define RKVDEC_REGS_H_
+
+#if 0
+struct h264_ref {
+ u32 ref_field:1;
+ u32 ref_topfield_used:1;
+ u32 ref_botfield_used:1;
+ u32 ref_colmv_use_flag:1;
+ u32 ref_base:28;
+};
+
+struct hevc_ref {
+ u32 ref_valid_flag:4;
+ u32 ref_base:28;
+};
+#endif
+
+struct vp9_segid_grp {
+ /* this bit is only used by segid0 */
+ u32 abs_delta:1;
+ u32 frame_qp_delta_en:1;
+ u32 frame_qp_delta:9;
+ u32 frame_loopfitler_value_en:1;
+ u32 frame_loopfilter_value:7;
+ u32 referinfo_en:1;
+ u32 referinfo:2;
+ u32 frame_skip_en:1;
+ u32 reserved0:9;
+};
+
+struct rkvdec_regs {
+ struct {
+ u32 minor_ver:8;
+ u32 level:1;
+ u32 dec_support:3;
+ u32 profile:1;
+ u32 reserved0:1;
+ u32 codec_flag:1;
+ u32 reserved1:1;
+ u32 prod_num:16;
+ } sw_id;
+
+ struct {
+ u32 dec_e:1;
+ u32 dec_clkgate_e:1;
+ u32 reserved0:1;
+ u32 timeout_mode:1;
+ u32 dec_irq_dis:1;
+ u32 dec_timeout_e:1;
+ u32 buf_empty_en:1;
+ u32 stmerror_waitdecfifo_empty:1;
+ u32 dec_irq:1;
+ u32 dec_irq_raw:1;
+ u32 reserved1:2;
+ u32 dec_rdy_sta:1;
+ u32 dec_bus_sta:1;
+ u32 dec_error_sta:1;
+ u32 dec_empty_sta:1;
+ u32 colmv_ref_error_sta:1;
+ u32 cabu_end_sta:1;
+ u32 h264orvp9_error_mode:1;
+ u32 softrst_en_p:1;
+ u32 force_softreset_valid:1;
+ u32 softreset_rdy:1;
+ u32 wr_ddr_align_en:1;
+ u32 scl_down_en:1;
+ u32 allow_not_wr_unref_bframe:1;
+ u32 reserved2:6;
+ } sw_interrupt;
+
+ struct {
+ u32 in_endian:1;
+ u32 in_swap32_e:1;
+ u32 in_swap64_e:1;
+ u32 str_endian:1;
+ u32 str_swap32_e:1;
+ u32 str_swap64_e:1;
+ u32 out_endian:1;
+ u32 out_swap32_e:1;
+ u32 out_cbcr_swap:1;
+ u32 reserved0:1;
+ u32 rlc_mode_direct_write:1;
+ u32 rlc_mode:1;
+ u32 strm_start_bit:7;
+ u32 reserved1:1;
+ u32 dec_mode:2;
+ u32 reserved2:2;
+ u32 rps_mode:1;
+ u32 stream_mode:1;
+ u32 stream_lastpacket:1;
+ u32 firstslice_flag:1;
+ u32 frame_orslice:1;
+ u32 buspr_slot_disable:1;
+ u32 colmv_mode:1;
+ u32 ycacherd_prior:1;
+ } sw_sysctrl;
+
+ struct {
+ u32 y_hor_virstride:10;
+ u32 uv_hor_virstride_highbit:1;
+ u32 slice_num_highbit:1;
+ u32 uv_hor_virstride:9;
+ u32 slice_num_lowbits:11;
+ } sw_picparameter;
+
+ u32 sw_strm_rlc_base;
+ u32 sw_stream_len;
+ u32 sw_cabactbl_base;
+ u32 sw_decout_base;
+ u32 sw_y_virstride;
+ u32 sw_yuv_virstride;
+ /* SWREG 10 */
+#if 1
+ union {
+#if 0
+ struct {
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+ } sw_refer_base[15];
+ u32 sw_refer_poc[15];
+ };
+#else
+ struct {
+ union {
+ struct {
+ u32 ref_valid_flag:4;
+ u32 ref_base:28;
+ };
+ struct {
+ u32 ref_field:1;
+ u32 ref_topfield_used:1;
+ u32 ref_botfield_used:1;
+ u32 ref_colmv_use_flag:1;
+ u32 padding:28;
+ };
+ } sw_refer_base[15];
+ u32 sw_refer_poc[15];
+ };
+#endif
+
+ struct {
+ struct {
+ u32 vp9_cprheader_offset:16;
+ u32 reserved0:16;
+ };
+ struct {
+ u32 reserved1:4;
+ u32 vp9last_base:28;
+ };
+ struct {
+ u32 reserved2:4;
+ u32 vp9golden_base:28;
+ };
+ struct {
+ u32 reserved3:4;
+ u32 vp9alfter_base:28;
+ };
+ struct {
+ u32 vp9count_update_en:1;
+ u32 reserved4:2;
+ u32 vp9count_base:29;
+ };
+ struct {
+ u32 reserved5:4;
+ u32 vp9segidlast_base:28;
+ };
+ struct {
+ u32 reserved6:4;
+ u32 vp9segidcur_base:28;
+ };
+ struct {
+ u32 framewidth_last:16;
+ u32 frameheight_last:28;
+ };
+ struct {
+ u32 framewidth_golden:16;
+ u32 frameheight_golden:28;
+ };
+ struct {
+ u32 framewidth_alfter:16;
+ u32 frameheight_alfter:28;
+ };
+ /* segid_grp0 to segid_grp4 */
+ /* SWREG 25(grp5) to SWREG 27(grp7) */
+ struct vp9_segid_grp grp[8];
+ /* cprheader_config */
+ struct {
+ u32 tx_mode:3;
+ u32 frame_reference_mode:2;
+ u32 reserved7:27;
+ };
+ /* lref_scale */
+ struct {
+ u32 lref_hor_scale:16;
+ u32 lref_ver_scale:16;
+ };
+ /* gref_scale */
+ struct {
+ u32 gref_hor_scale:16;
+ u32 gref_ver_scale:16;
+ };
+ /* aref_scale */
+ struct {
+ u32 aref_hor_scale:16;
+ u32 aref_ver_scale:16;
+ };
+ /* ref_deltas_lastframe */
+ struct {
+ u32 ref_deltas_lastframe:28;
+ u32 reserved8:4;
+ };
+ /* info_lastframe */
+ struct {
+ u32 mode_deltas_lastframe:14;
+ u32 reserved9:2;
+ u32 segmentation_enable_lastframe:1;
+ u32 last_show_frame:1;
+ u32 last_intra_only:1;
+ u32 last_widthheight_eqcur:1;
+ u32 color_space_lastkeyframe:3;
+ u32 reserved10:9;
+ };
+ /* intercmd_base */
+ struct {
+ u32 reserved11:4;
+ u32 intercmd_base:28;
+ };
+ /* intercmd_num */
+ struct {
+ u32 intercmd_num:24;
+ u32 reserved12:8;
+ };
+ /* lasttile_size */
+ struct {
+ u32 lasttile_size:24;
+ u32 reserved13:8;
+ };
+ /* lastf_hor_virstride */
+ struct {
+ u32 lastfy_hor_virstride:10;
+ u32 reserved14:6;
+ u32 lastfuv_hor_virstride:10;
+ u32 reserved15:6;
+ };
+ /* goldenf_hor_virstride */
+ struct {
+ u32 goldenfy_hor_virstride:10;
+ u32 reserved16:6;
+ u32 goldenuv_hor_virstride:10;
+ u32 reserved17:6;
+ };
+ /* altreff_hor_virstride */
+ struct {
+ u32 altrey_hor_virstride:10;
+ u32 reserved18:6;
+ u32 altreuv_hor_virstride:10;
+ u32 reserved19:6;
+ };
+ } vp9;
+ };
+#else
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 vp9_cprheader_offset:16;
+ u32 reserved0:16;
+ };
+ } sw10;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 reserved0:4;
+ u32 vp9last_base:28;
+ };
+ } sw11;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 reserved0:4;
+ u32 vp9golden_base:28;
+ };
+ } sw12;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 reserved0:4;
+ u32 vp9alfter_base:28;
+ };
+ } sw13;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 vp9count_update_en:1;
+ u32 reserved0:2;
+ u32 vp9count_base:29;
+ };
+ } sw14;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 reserved0:4;
+ u32 vp9segidlast_base:28;
+ };
+ } sw15;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 reserved0:4;
+ u32 vp9segidcur_base:28;
+ };
+ } sw16;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 framewidth_last:16;
+ u32 frameheight_last:28;
+ } vp9;
+ } sw17;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 framewidth_golden:16;
+ u32 frameheight_golden:28;
+ } vp9;
+ } sw18;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+
+ struct {
+ u32 framewidth_alfter:16;
+ u32 frameheight_alfter:28;
+ } vp9;
+ } sw19;
+
+ union {
+ struct h264_ref h264;
+ struct hevc_ref hevc;
+ /* segid_grp0 to segid_grp4 */
+ struct vp9_segid_grp vp9;
+ } sw20_sw24[5];
+
+ /* SWREG 25 */
+ union {
+ u32 sw_refer_poc[15];
+
+ struct {
+ /* SWREG 25(grp5) to SWREG 27(grp7) */
+ struct vp9_segid_grp grp5_grp7[3];
+ /* cprheader_config */
+ struct {
+ u32 tx_mode:3;
+ u32 frame_reference_mode:2;
+ u32 reserved0:27;
+ };
+ /* lref_scale */
+ struct {
+ u32 lref_hor_scale:16;
+ u32 lref_ver_scale:16;
+ };
+ /* gref_scale */
+ struct {
+ u32 gref_hor_scale:16;
+ u32 gref_ver_scale:16;
+ };
+ /* aref_scale */
+ struct {
+ u32 aref_hor_scale:16;
+ u32 aref_ver_scale:16;
+ };
+ /* ref_deltas_lastframe */
+ struct {
+ u32 ref_deltas_lastframe:28;
+ u32 reserved1:4;
+ };
+ /* info_lastframe */
+ struct {
+ u32 mode_deltas_lastframe:14;
+ u32 reserved2:2;
+ u32 segmentation_enable_lastframe:1;
+ u32 last_show_frame:1;
+ u32 last_intra_only:1;
+ u32 last_widthheight_eqcur:1;
+ u32 color_space_lastkeyframe:3;
+ u32 reserved3:9;
+ };
+ /* intercmd_base */
+ struct {
+ u32 reserved4:4;
+ u32 intercmd_base:28;
+ };
+ /* intercmd_num */
+ struct {
+ u32 intercmd_num:24;
+ u32 reserved5:8;
+ };
+ /* lasttile_size */
+ struct {
+ u32 lasttile_size:24;
+ u32 reserved6:8;
+ };
+ /* lastf_hor_virstride */
+ struct {
+ u32 lastfy_hor_virstride:10;
+ u32 reserved7:6;
+ u32 lastfuv_hor_virstride:10;
+ u32 reserved8:6;
+ };
+ /* goldenf_hor_virstride */
+ struct {
+ u32 goldenfy_hor_virstride:10;
+ u32 reserved9:6;
+ u32 goldenuv_hor_virstride:10;
+ u32 reserved10:6;
+ };
+ /* altreff_hor_virstride */
+ struct {
+ u32 altrey_hor_virstride:10;
+ u32 reserved9:6;
+ u32 altreuv_hor_virstride:10;
+ u32 reserved10:6;
+ };
+ } vp9;
+ };
+#endif
+ /* SWREG 40 */
+ u32 sw_cur_poc;
+ u32 sw_rlcwrite_base;
+ u32 sw_pps_base;
+ u32 sw_rps_base;
+ /* in HEVC, it is called cabac_error_en */
+ u32 sw_strmd_error_en;
+ /* SWREG 45, cabac_error_status, vp9_error_info0 */
+ struct {
+ u32 strmd_error_status:28;
+ u32 colmv_error_ref_picidx:4;
+ } sw_strmd_error_status;
+
+ struct cabac_error_ctu {
+ u32 strmd_error_ctu_xoffset:8;
+ u32 strmd_error_ctu_yoffset:8;
+ u32 streamfifo_space2full:7;
+ u32 reserved0:1;
+ u32 vp9_error_ctu0_en:1;
+ u32 reversed1:7;
+ } cabac_error_ctu;
+
+ /* SWREG 47 */
+ struct sao_ctu_position {
+ u32 sw_saowr_xoffset:9;
+ u32 reversed0:7;
+ u32 sw_saowr_yoffset:10;
+ u32 reversed1:6;
+ } sao_ctu_position;
+
+ /* SWREG 48 */
+ union {
+ u32 sw_ref_valid;
+ struct {
+ u32 ref15_base:10;
+ u32 ref15_field:1;
+ u32 ref15_topfield_used:1;
+ u32 ref15_botfield_used:1;
+ u32 ref15_colmv_use_flag:1;
+ u32 reverse0:18;
+ } sw48_h264;
+
+ struct {
+ u32 lastfy_virstride:20;
+ u32 reserved0:12;
+ } sw48_vp9;
+ };
+
+ /* SWREG 49 - 63 */
+#if 0
+ u32 sw_refframe_index[15];
+#else
+ union {
+ u32 sw_refframe_index[15];
+ struct {
+ struct {
+ u32 goldeny_virstride:20;
+ u32 reserved0:12;
+ };
+ struct {
+ u32 altrefy_virstride:20;
+ u32 reserved1:12;
+ };
+ struct {
+ u32 lastref_yuv_virstride:20;
+ u32 reserved2:12;
+ };
+ struct {
+ u32 reserved3:4;
+ u32 refcolmv_base:28;
+ };
+ u32 padding[11];
+ } vp9_vir;
+
+ };
+#endif
+
+ /* SWREG 64 */
+ u32 performance_cycle;
+ u32 axi_ddr_rdata;
+ u32 axi_ddr_wdata;
+ /* SWREG67 */
+ u32 fpgadebug_reset;
+
+ struct {
+ u32 perf_cnt0_sel:6;
+ u32 reserved2:2;
+ u32 perf_cnt1_sel:6;
+ u32 reserved1:2;
+ u32 perf_cnt2_sel:6;
+ u32 reserved0:10;
+ } sw68_perf_sel;
+
+ u32 sw69_perf_cnt0;
+ u32 sw70_perf_cnt1;
+ u32 sw71_perf_cnt2;
+ u32 sw72_h264_refer30_poc;
+ u32 sw73_h264_refer31_poc;
+ u32 sw74_h264_cur_poc1;
+ u32 sw75_errorinfo_base;
+
+#if 0
+ struct {
+ u32 slicedec_num:14;
+ u32 reserved1:1;
+ u32 strmd_detect_error_flag:1;
+ u32 sw_error_packet_num:14;
+ u32 reserved0:2;
+ } sw76_errorinfo_num;
+#else
+ union {
+ struct {
+ u32 slicedec_num:14;
+ u32 reserved1:1;
+ u32 strmd_detect_error_flag:1;
+ u32 sw_error_packet_num:14;
+ u32 reserved0:2;
+ } sw76_errorinfo_num;
+
+ struct {
+ u32 error_ctu1_x:6;
+ u32 reserved0:2;
+ u32 vp9_error_ctu1_y:6;
+ u32 reserved1:1;
+ u32 vp9_error_ctu1_en:1;
+ u32 reserved2:16;
+ } swreg76_vp9_error_ctu1;
+ };
+#endif
+
+ /* SWREG 77 */
+ struct {
+ u32 error_en_highbits:28;
+ u32 strmd_error_en:2;
+ u32 reserved0:2;
+ } extern_error_en;
+};
+
+#endif
diff --git a/drivers/staging/rockchip-mpp/vdpu2/hal.h b/drivers/staging/rockchip-mpp/vdpu2/hal.h
new file mode 100644
index 000000000000..3da4b0e04c17
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/vdpu2/hal.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Randy Li, <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VDPU2_HAL_H_
+#define _VDPU2_HAL_H_
+
+#include <linux/types.h>
+
+/* The maximum registers number of all the version */
+#define ROCKCHIP_VDPU2_REG_NUM 159
+
+/* The first register of the decoder is Reg50(0x000c8) */
+#define RKVDPU2_REG_DEC_CTRL 0x0c8
+#define RKVDPU2_REG_DEC_CTRL_INDEX (50)
+
+#define RKVDPU2_REG_DEC_INT_EN 0x0dc
+#define RKVDPU2_REG_DEC_INT_EN_INDEX (55)
+#define RKVDPU2_INT_TIMEOUT BIT(13)
+#define RKVDPU2_INT_STRM_ERROR BIT(12)
+#define RKVDPU2_INT_SLICE BIT(9)
+#define RKVDPU2_INT_ASO_ERROR BIT(8)
+#define RKVDPU2_INT_BUF_EMPTY BIT(6)
+#define RKVDPU2_INT_BUS_ERROR BIT(5)
+#define RKVDPU2_DEC_INT BIT(4)
+#define RKVDPU2_DEC_IRQ_DIS BIT(1)
+#define RKVDPU2_DEC_INT_RAW BIT(0)
+
+#define RKVDPU2_REG_DEC_DEV_CTRL 0x0e4
+#define RKVDPU2_REG_DEC_DEV_CTRL_INDEX (57)
+#define RKVDPU2_DEC_CLOCK_GATE_EN BIT(4)
+#define RKVDPU2_DEC_START BIT(0)
+
+#define RKVDPU2_REG59 0x0ec
+#define RKVDPU2_REG59_INDEX (59)
+
+int rkvdpu_mpeg2_gen_reg(struct mpp_session *session, void *regs,
+ struct vb2_v4l2_buffer *src_buf);
+int rkvdpu_mpeg2_prepare_buf(struct mpp_session *session, void *regs);
+
+#endif
diff --git a/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c b/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
new file mode 100644
index 000000000000..d32958c4cb20
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Randy Li, <[email protected]>
+ *
+ * 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 <linux/types.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mpp_dev_common.h"
+#include "hal.h"
+#include "regs.h"
+
+#define DEC_LITTLE_ENDIAN (1)
+
+static const u8 zigzag[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+static const u8 intra_default_q_matrix[64] = {
+ 8, 16, 19, 22, 26, 27, 29, 34,
+ 16, 16, 22, 24, 27, 29, 34, 37,
+ 19, 22, 26, 27, 29, 34, 34, 38,
+ 22, 22, 26, 27, 29, 34, 37, 40,
+ 22, 26, 27, 29, 32, 35, 40, 48,
+ 26, 27, 29, 32, 35, 40, 48, 58,
+ 26, 27, 29, 34, 38, 46, 56, 69,
+ 27, 29, 35, 38, 46, 56, 69, 83
+};
+
+static void mpeg2_dec_copy_qtable(u8 * qtable, const struct v4l2_ctrl_mpeg2_quantization
+ *ctrl)
+{
+ int i, n;
+
+ if (!qtable || !ctrl)
+ return;
+
+ if (ctrl->load_intra_quantiser_matrix) {
+ for (i = 0; i < 64; i++)
+ qtable[zigzag[i]] = ctrl->intra_quantiser_matrix[i];
+ } else {
+ for (i = 0; i < 64; i++)
+ qtable[zigzag[i]] = intra_default_q_matrix[i];
+
+ }
+
+ if (ctrl->load_non_intra_quantiser_matrix) {
+ for (i = 0; i < 64; i++)
+ qtable[zigzag[i] + 64] =
+ ctrl->non_intra_quantiser_matrix[i];
+ } else {
+ for (i = 0; i < 64; i++)
+ qtable[zigzag[i] + 64] = 16;
+ }
+}
+
+static void init_hw_cfg(struct vdpu2_regs *p_regs)
+{
+ p_regs->sw54.dec_strm_wordsp = 1;
+ p_regs->sw54.dec_strendian_e = DEC_LITTLE_ENDIAN;
+ p_regs->sw54.dec_in_wordsp = 1;
+ p_regs->sw54.dec_out_wordsp = 1;
+ p_regs->sw54.dec_in_endian = DEC_LITTLE_ENDIAN; //change
+ p_regs->sw54.dec_out_endian = DEC_LITTLE_ENDIAN;
+ p_regs->sw57.dec_timeout = 1;
+ p_regs->sw57.dec_timeout_e = 1;
+
+ p_regs->sw57.dec_clk_gate_e = 1;
+
+ p_regs->sw50.tiled_mode_msb = 0;
+ p_regs->sw56.dec_max_burst = 16;
+ p_regs->sw50.dec_scmd_dis = 0;
+ p_regs->sw50.dec_adv_pre_dis = 0;
+ p_regs->sw52.apf_threshold = 8;
+
+ p_regs->sw50.dec_latency = 0;
+ p_regs->sw56.dec_data_disc_e = 0;
+
+ p_regs->sw55.dec_irq = 0;
+ p_regs->sw56.dec_axi_rd_id = 0;
+ p_regs->sw56.dec_axi_wr_id = 0;
+
+ /* default for MPEG-2 */
+ p_regs->sw136.mv_accuracy_fwd = 1;
+ p_regs->sw136.mv_accuracy_bwd = 1;
+}
+
+int rkvdpu_mpeg2_gen_reg(struct mpp_session *session, void *regs,
+ struct vb2_v4l2_buffer *src_buf)
+{
+ const struct v4l2_ctrl_mpeg2_slice_params *params;
+ const struct v4l2_ctrl_mpeg2_quantization *quantization;
+ const struct v4l2_mpeg2_sequence *sequence;
+ const struct v4l2_mpeg2_picture *picture;
+ struct vdpu2_regs *p_regs = regs;
+
+ params = rockchip_mpp_get_cur_ctrl(session,
+ V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS);
+ quantization = rockchip_mpp_get_cur_ctrl(session,
+ V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION);
+
+ if (!params)
+ return -EINVAL;
+
+ sequence = ¶ms->sequence;
+ picture = ¶ms->picture;
+
+ init_hw_cfg(p_regs);
+
+ p_regs->sw120.pic_mb_width = DIV_ROUND_UP(sequence->horizontal_size,
+ 16);
+ p_regs->sw120.pic_mb_height_p = DIV_ROUND_UP(sequence->vertical_size,
+ 16);
+
+ /* PICT_FRAME */
+ if (picture->picture_structure == 3) {
+ p_regs->sw57.pic_fieldmode_e = 0;
+ } else {
+ p_regs->sw57.pic_fieldmode_e = 1;
+ /* PICT_TOP_FIEL */
+ if (picture->picture_structure == 1)
+ p_regs->sw57.pic_topfield_e = 1;
+ }
+
+ switch (picture->picture_coding_type) {
+ case V4L2_MPEG2_PICTURE_CODING_TYPE_B:
+ p_regs->sw57.pic_b_e = 1;
+ case V4L2_MPEG2_PICTURE_CODING_TYPE_P:
+ p_regs->sw57.pic_inter_e = 1;
+ break;
+ case V4L2_MPEG2_PICTURE_CODING_TYPE_I:
+ default:
+ p_regs->sw57.pic_inter_e = 0;
+ p_regs->sw57.pic_b_e = 0;
+ break;
+ }
+
+ if (picture->top_field_first)
+ p_regs->sw120.mpeg.topfieldfirst_e = 1;
+
+ p_regs->sw57.fwd_interlace_e = 0;
+ p_regs->sw57.write_mvs_e = 0;
+
+ p_regs->sw120.mpeg.alt_scan_e = picture->alternate_scan;
+ p_regs->sw136.alt_scan_flag_e = picture->alternate_scan;
+
+ p_regs->sw122.qscale_type = picture->q_scale_type;
+ p_regs->sw122.intra_dc_prec = picture->intra_dc_precision;
+ p_regs->sw122.con_mv_e = picture->concealment_motion_vectors;
+ p_regs->sw122.intra_vlc_tab = picture->intra_vlc_format;
+ p_regs->sw122.frame_pred_dct = picture->frame_pred_frame_dct;
+ p_regs->sw51.qp_init = 1;
+
+ /* MPEG-2 decoding mode */
+ p_regs->sw53.dec_mode = RKVDPU2_FMT_MPEG2D;
+
+ p_regs->sw136.fcode_fwd_hor = picture->f_code[0][0];
+ p_regs->sw136.fcode_fwd_ver = picture->f_code[0][1];
+ p_regs->sw136.fcode_bwd_hor = picture->f_code[1][0];
+ p_regs->sw136.fcode_bwd_ver = picture->f_code[1][1];
+
+ p_regs->sw57.pic_interlace_e = 1 - sequence->progressive_sequence;
+#if 0
+ /* MPEG-1 decoding mode */
+ p_regs->sw53.sw_dec_mode = 6;
+ p_regs->sw136.fcode_fwd_hor = picture->f_code[0][1];
+ p_regs->sw136.fcode_fwd_ver = picture->f_code[0][1];
+ p_regs->sw136.fcode_bwd_hor = picture->f_code[1][1];
+ p_regs->sw136.fcode_bwd_ver = picture->f_code[1][1];
+ if (picture->f_code[0][0])
+ p_regs->sw136.mv_accuracy_fwd = 0;
+ if (picture->f_code[1][0])
+ p_regs->sw136.mv_accuracy_bwd = 0;
+#endif
+ p_regs->sw52.startmb_x = 0;
+ p_regs->sw52.startmb_y = 0;
+ p_regs->sw57.dec_out_dis = 0;
+ p_regs->sw50.filtering_dis = 1;
+
+ p_regs->sw64.rlc_vlc_base =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ p_regs->sw122.strm_start_bit = params->data_bit_offset;
+ p_regs->sw51.stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ return 0;
+}
+
+int rkvdpu_mpeg2_prepare_buf(struct mpp_session *session, void *regs)
+{
+ const struct v4l2_ctrl_mpeg2_slice_params *params;
+ const struct v4l2_mpeg2_sequence *sequence;
+ const struct v4l2_mpeg2_picture *picture;
+ struct vb2_v4l2_buffer *dst_buf;
+ dma_addr_t cur_addr, fwd_addr, bwd_addr;
+ struct vb2_queue *cap_q = &session->fh.m2m_ctx->cap_q_ctx.q;
+ struct vdpu2_regs *p_regs = regs;
+
+ params =
+ rockchip_mpp_get_cur_ctrl(session,
+ V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS);
+ picture = ¶ms->picture;
+ sequence = ¶ms->sequence;
+
+ dst_buf = v4l2_m2m_next_dst_buf(session->fh.m2m_ctx);
+ cur_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+
+ fwd_addr =
+ rockchip_mpp_find_addr(cap_q, &dst_buf->vb2_buf,
+ params->forward_ref_ts);
+ bwd_addr =
+ rockchip_mpp_find_addr(cap_q, &dst_buf->vb2_buf,
+ params->backward_ref_ts);
+ if (!bwd_addr)
+ bwd_addr = cur_addr;
+ if (!fwd_addr)
+ fwd_addr = cur_addr;
+
+ /* Starting from the second horizontal line */
+ if (picture->picture_structure == 2)
+ cur_addr += ALIGN(sequence->horizontal_size, 16);
+ p_regs->sw63.dec_out_base = cur_addr;
+
+ if (picture->picture_coding_type == V4L2_MPEG2_PICTURE_CODING_TYPE_B) {
+ p_regs->sw131.refer0_base = fwd_addr >> 2;
+ p_regs->sw148.refer1_base = fwd_addr >> 2;
+ p_regs->sw134.refer2_base = bwd_addr >> 2;
+ p_regs->sw135.refer3_base = bwd_addr >> 2;
+ } else {
+ if (picture->picture_structure == 3 ||
+ (picture->picture_structure == 1
+ && picture->top_field_first)
+ || (picture->picture_structure == 2
+ && !picture->top_field_first)) {
+ p_regs->sw131.refer0_base = fwd_addr >> 2;
+ p_regs->sw148.refer1_base = fwd_addr >> 2;
+ } else if (picture->picture_structure == 1) {
+ p_regs->sw131.refer0_base = fwd_addr >> 2;
+ p_regs->sw148.refer1_base = cur_addr >> 2;
+ } else if (picture->picture_structure == 2) {
+ p_regs->sw131.refer0_base = cur_addr >> 2;
+ p_regs->sw148.refer1_base = fwd_addr >> 2;
+ }
+ p_regs->sw134.refer2_base = cur_addr >> 2;
+ p_regs->sw135.refer3_base = cur_addr >> 2;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/rockchip-mpp/vdpu2/regs.h b/drivers/staging/rockchip-mpp/vdpu2/regs.h
new file mode 100644
index 000000000000..8708b26d83c5
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/vdpu2/regs.h
@@ -0,0 +1,699 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ * Randy Li, <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VDPU2_REGS_H_
+#define _VDPU2_REGS_H_
+
+#define RKVDPU2_REG_SYS_CTRL 0x0d4
+#define RKVDPU2_REG_SYS_CTRL_INDEX (53)
+#define RKVDPU2_GET_FORMAT(x) ((x) & 0xf)
+#define RKVDPU2_FMT_H264D 0
+#define RKVDPU2_FMT_MPEG4D 1
+#define RKVDPU2_FMT_H263D 2
+#define RKVDPU2_FMT_JPEGD 3
+#define RKVDPU2_FMT_VC1D 4
+#define RKVDPU2_FMT_MPEG2D 5
+#define RKVDPU2_FMT_MPEG1D 6
+#define RKVDPU2_FMT_VP6D 7
+#define RKVDPU2_FMT_RESERVED 8
+#define RKVDPU2_FMT_VP7D 9
+#define RKVDPU2_FMT_VP8D 10
+#define RKVDPU2_FMT_AVSD 11
+
+#define RKVDPU2_REG_DIR_MV_BASE 0x0f8
+#define RKVDPU2_REG_DIR_MV_BASE_INDEX (62)
+
+#define RKVDPU2_REG_STREAM_RLC_BASE 0x100
+#define RKVDPU2_REG_STREAM_RLC_BASE_INDEX (64)
+
+#if 0
+/*
+ * file handle translate information
+ */
+static const char trans_tbl_default[] = {
+ 61, 62, 63, 64, 131, 134, 135, 148
+};
+
+static const char trans_tbl_jpegd[] = {
+ 21, 22, 61, 63, 64, 131
+};
+
+static const char trans_tbl_h264d[] = {
+ 61, 63, 64, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99
+};
+
+static const char trans_tbl_vc1d[] = {
+ 62, 63, 64, 131, 134, 135, 145, 148
+};
+
+static const char trans_tbl_vp6d[] = {
+ 61, 63, 64, 131, 136, 145
+};
+
+static const char trans_tbl_vp8d[] = {
+ 61, 63, 64, 131, 136, 137, 140, 141, 142, 143, 144, 145, 146, 147, 149
+};
+#endif
+
+struct vdpu2_regs {
+ u32 sw00_49[50];
+
+ struct {
+ u32 tiled_mode_msb:1;
+ u32 dec_latency:6;
+ u32 dec_fixed_quant:1;
+ u32 filtering_dis:1;
+ u32 skip_sel:1;
+ u32 dec_scmd_dis:1;
+ u32 dec_adv_pre_dis:1;
+ u32 tiled_mode_lsb:1;
+ u32 refbuf_thrd:12;
+ u32 refbuf_pid:5;
+ u32 reserved0:2;
+ } sw50;
+
+ struct {
+ u32 stream_len:24;
+ u32 stream_len_ext:1;
+ u32 qp_init:6;
+ u32 reserved0:1;
+ } sw51;
+
+ struct {
+ /* ydim_mbst */
+ u32 startmb_y:8;
+ /* xdim_mbst */
+ u32 startmb_x:9;
+ /* adv_pref_thrd */
+ u32 apf_threshold:14;
+ u32 reserved0:1;
+ } sw52;
+
+ struct {
+ u32 dec_mode:4;
+ u32 reserved0:28;
+ } sw53;
+
+ struct {
+ u32 dec_in_endian:1;
+ u32 dec_out_endian:1;
+ u32 dec_in_wordsp:1;
+ u32 dec_out_wordsp:1;
+ u32 dec_strm_wordsp:1;
+ u32 dec_strendian_e:1;
+ u32 reserved0:26;
+ } sw54;
+
+ struct {
+ u32 dec_irq:1;
+ u32 dec_irq_dis:1;
+ u32 reserved0:2;
+ u32 dec_rdy_sts:1;
+ u32 pp_bus_sts:1;
+ u32 buf_emt_sts:1;
+ u32 reserved1:1;
+ u32 aso_det_sts:1;
+ u32 slice_det_sts:1;
+ u32 bslice_det_sts:1;
+ u32 reserved2:1;
+ u32 error_det_sts:1;
+ u32 timeout_det_sts:1;
+ u32 reserved3:18;
+ } sw55;
+
+ struct {
+ u32 dec_axi_rd_id:8;
+ u32 dec_axi_wr_id:8;
+ u32 dec_max_burst:5;
+ u32 bus_pos_sel:1;
+ u32 dec_data_disc_e:1;
+ u32 axi_sel:1;
+ u32 reserved0:8;
+ } sw56;
+
+ struct {
+ u32 dec_e:1;
+ u32 refbuf2_buf_e:1;
+ u32 dec_out_dis:1;
+ u32 reserved2:1;
+ u32 dec_clk_gate_e:1;
+ u32 dec_timeout_e:1;
+ /* rd_cnt_tab_en */
+ u32 picord_count_e:1;
+ u32 seq_mbaff_e:1;
+ u32 reftopfirst_e:1;
+ u32 ref_topfield_e:1;
+ u32 write_mvs_e:1;
+ u32 sorenson_e:1;
+ u32 fwd_interlace_e:1;
+ u32 pic_topfield_e:1;
+ /* sw_pic_type_sel0 */
+ u32 pic_inter_e:1;
+ u32 pic_b_e:1;
+ u32 pic_fieldmode_e:1;
+ u32 pic_interlace_e:1;
+ u32 pjpeg_e:1;
+ u32 divx3_e:1;
+ u32 rlc_mode_e:1;
+ u32 ch_8pix_ileav_e:1;
+ u32 start_code_e:1;
+ u32 reserved1:2;
+ /* sw_init_dc_match0 ? */
+ u32 inter_dblspeed:1;
+ u32 intra_dblspeed:1;
+ u32 intra_dbl3t:1;
+ u32 pref_sigchan:1;
+ u32 cache_en:1;
+ u32 reserved0:1;
+ /* dec_timeout_mode */
+ u32 dec_timeout:1;
+ } sw57;
+
+ struct {
+ u32 soft_rst:1;
+ u32 reserved0:31;
+ } sw58;
+
+ struct {
+ u32 reserved0:2;
+ /* sw_pflt_set0_tap2 */
+ u32 pred_bc_tap_0_2:10;
+ u32 pred_bc_tap_0_1:10;
+ /* pflt_set0_tap0 */
+ u32 pred_bc_tap_0_0:10;
+ } sw59;
+
+ struct {
+ u32 addit_ch_st_adr:32;
+ } sw60;
+
+ struct {
+ u32 qtable_base:32;
+ } sw61;
+
+ struct {
+ u32 dir_mv_base:32;
+ } sw62;
+
+ struct {
+ /* dec_out_st_adr */
+ u32 dec_out_base:32;
+ } sw63;
+
+ struct {
+ u32 rlc_vlc_base:32;
+ } sw64;
+
+ struct {
+ u32 refbuf_y_offset:9;
+ u32 reserve0:3;
+ u32 refbuf_fildpar_mode_e:1;
+ u32 refbuf_idcal_e:1;
+ u32 refbuf_picid:5;
+ u32 refbuf_thr_level:12;
+ u32 refbuf_e:1;
+ } sw65;
+
+ u32 sw66;
+ u32 sw67;
+
+ struct {
+ u32 refbuf_sum_bot:16;
+ u32 refbuf_sum_top:16;
+ } sw68;
+
+ struct {
+ u32 luma_sum_intra:16;
+ u32 refbuf_sum_hit:16;
+ } sw69;
+
+ struct {
+ u32 ycomp_mv_sum:22;
+ u32 reserve0:10;
+ } sw70;
+
+ u32 sw71;
+ u32 sw72;
+ u32 sw73;
+
+ struct {
+ u32 init_reflist_pf4:5;
+ u32 init_reflist_pf5:5;
+ u32 init_reflist_pf6:5;
+ u32 init_reflist_pf7:5;
+ u32 init_reflist_pf8:5;
+ u32 init_reflist_pf9:5;
+ u32 reserved0:2;
+ } sw74;
+
+ struct {
+ u32 init_reflist_pf10:5;
+ u32 init_reflist_pf11:5;
+ u32 init_reflist_pf12:5;
+ u32 init_reflist_pf13:5;
+ u32 init_reflist_pf14:5;
+ u32 init_reflist_pf15:5;
+ u32 reserved0:2;
+ } sw75;
+
+ struct {
+ u32 num_ref_idx0:16;
+ u32 num_ref_idx1:16;
+ } sw76;
+
+ struct {
+ u32 num_ref_idx2:16;
+ u32 num_ref_idx3:16;
+ } sw77;
+
+ struct {
+ u32 num_ref_idx4:16;
+ u32 num_ref_idx5:16;
+ } sw78;
+
+ struct {
+ u32 num_ref_idx6:16;
+ u32 num_ref_idx7:16;
+ } sw79;
+
+ struct {
+ u32 num_ref_idx8:16;
+ u32 num_ref_idx9:16;
+ } sw80;
+
+ struct {
+ u32 num_ref_idx10:16;
+ u32 num_ref_idx11:16;
+ } sw81;
+
+ struct {
+ u32 num_ref_idx12:16;
+ u32 num_ref_idx13:16;
+ } sw82;
+
+ struct {
+ u32 num_ref_idx14:16;
+ u32 num_ref_idx15:16;
+ } sw83;
+
+ /* Used by H.264 */
+ union {
+ u32 ref0_st_addr;
+ struct {
+ u32 ref0_closer_sel:1;
+ u32 ref0_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw84;
+
+ union {
+ u32 ref1_st_addr;
+ struct {
+ u32 ref1_closer_sel:1;
+ u32 ref1_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw85;
+
+ union {
+ u32 ref2_st_addr;
+ struct {
+ u32 ref2_closer_sel:1;
+ u32 ref2_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw86;
+
+ union {
+ u32 ref3_st_addr;
+ struct {
+ u32 ref3_closer_sel:1;
+ u32 ref3_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw87;
+
+ union {
+ u32 ref4_st_addr;
+ struct {
+ u32 ref4_closer_sel:1;
+ u32 ref4_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw88;
+
+ union {
+ u32 ref5_st_addr;
+ struct {
+ u32 ref5_closer_sel:1;
+ u32 ref5_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw89;
+
+ union {
+ u32 ref6_st_addr;
+ struct {
+ u32 ref6_closer_sel:1;
+ u32 ref6_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw90;
+
+ union {
+ u32 ref7_st_addr;
+ struct {
+ u32 ref7_closer_sel:1;
+ u32 ref7_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw91;
+
+ union {
+ u32 ref8_st_addr;
+ struct {
+ u32 ref8_closer_sel:1;
+ u32 ref8_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw92;
+
+ union {
+ u32 ref9_st_addr;
+ struct {
+ u32 ref9_closer_sel:1;
+ u32 ref9_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw93;
+
+ union {
+ u32 ref10_st_addr;
+ struct {
+ u32 ref10_closer_sel:1;
+ u32 ref10_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw94;
+
+ union {
+ u32 ref11_st_addr;
+ struct {
+ u32 ref11_closer_sel:1;
+ u32 ref11_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw95;
+
+ union {
+ u32 ref12_st_addr;
+ struct {
+ u32 ref12_closer_sel:1;
+ u32 ref12_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw96;
+
+ union {
+ u32 ref13_st_addr;
+ struct {
+ u32 ref13_closer_sel:1;
+ u32 ref13_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw97;
+
+ union {
+ u32 ref14_st_addr;
+ struct {
+ u32 ref14_closer_sel:1;
+ u32 ref14_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw98;
+
+ /* Used by H.264 */
+ union {
+ u32 ref15_st_addr;
+ struct {
+ u32 ref15_closer_sel:1;
+ u32 ref15_field_en:1;
+ u32 reserved0:30;
+ };
+ } sw99;
+
+ struct {
+ u32 init_reflist_df0:5;
+ u32 init_reflist_df1:5;
+ u32 init_reflist_df2:5;
+ u32 init_reflist_df3:5;
+ u32 init_reflist_df4:5;
+ u32 init_reflist_df5:5;
+ u32 reserved0:2;
+ } sw100;
+
+ struct {
+ u32 init_reflist_df6:5;
+ u32 init_reflist_df7:5;
+ u32 init_reflist_df8:5;
+ u32 init_reflist_df9:5;
+ u32 init_reflist_df10:5;
+ u32 init_reflist_df11:5;
+ u32 reserved0:2;
+ } sw101;
+
+ struct {
+ u32 init_reflist_df12:5;
+ u32 init_reflist_df13:5;
+ u32 init_reflist_df14:5;
+ u32 init_reflist_df15:5;
+ u32 reserved0:12;
+ } sw102;
+
+ struct {
+ u32 init_reflist_db0:5;
+ u32 init_reflist_db1:5;
+ u32 init_reflist_db2:5;
+ u32 init_reflist_db3:5;
+ u32 init_reflist_db4:5;
+ u32 init_reflist_db5:5;
+ u32 reserved0:2;
+ } sw103;
+
+ struct {
+ u32 init_reflist_db6:5;
+ u32 init_reflist_db7:5;
+ u32 init_reflist_db8:5;
+ u32 init_reflist_db9:5;
+ u32 init_reflist_db10:5;
+ u32 init_reflist_db11:5;
+ u32 reserved0:2;
+ } sw104;
+
+ struct {
+ u32 init_reflist_db12:5;
+ u32 init_reflist_db13:5;
+ u32 init_reflist_db14:5;
+ u32 init_reflist_db15:5;
+ u32 reserved0:12;
+ } sw105;
+
+ struct {
+ u32 init_reflist_pf0:5;
+ u32 init_reflist_pf1:5;
+ u32 init_reflist_pf2:5;
+ u32 init_reflist_pf3:5;
+ u32 reserved0:12;
+ } sw106;
+
+ struct {
+ u32 refpic_term_flag:32;
+ } sw107;
+
+ struct {
+ u32 refpic_valid_flag:32;
+ } sw108;
+
+ struct {
+ u32 strm_start_bit:6;
+ u32 reserved0:26;
+ } sw109;
+
+ struct {
+ u32 pic_mb_w:9;
+ u32 pic_mb_h:8;
+ u32 flt_offset_cb_qp:5;
+ u32 flt_offset_cr_qp:5;
+ u32 reserved0:5;
+ } sw110;
+
+ struct {
+ u32 max_refnum:5;
+ u32 reserved0:11;
+ u32 wp_bslice_sel:2;
+ u32 reserved1:14;
+ } sw111;
+
+ struct {
+ u32 curfrm_num:16;
+ u32 cur_frm_len:5;
+ u32 reserved0:9;
+ u32 rpcp_flag:1;
+ u32 dblk_ctrl_flag:1;
+ } sw112;
+
+ struct {
+ u32 idr_pic_id:16;
+ u32 refpic_mk_len:11;
+ u32 reserved0:5;
+ } sw113;
+
+ struct {
+ u32 poc_field_len:8;
+ u32 reserved0:6;
+ u32 max_refidx0:5;
+ u32 max_refidx1:5;
+ u32 pps_id:5;
+ } sw114;
+
+ struct {
+ u32 fieldpic_flag_exist:1;
+ u32 scl_matrix_en:1;
+ u32 tranf_8x8_flag_en:1;
+ u32 const_intra_en:1;
+ u32 weight_pred_en:1;
+ u32 cabac_en:1;
+ u32 monochr_en:1;
+ u32 dlmv_method_en:1;
+ u32 idr_pic_flag:1;
+ u32 reserved0:23;
+ } sw115;
+
+ /* Not used */
+ u32 sw116_119[4];
+
+ union {
+ struct {
+ u32 pic_refer_flag:1;
+ u32 reserved0:10;
+ } avs;
+
+ struct {
+ u32 pic_mb_w_ext:3;
+ u32 pic_mb_h_ext:3;
+ u32 reserved0:1;
+ u32 mb_height_off:4;
+ } vc1;
+
+ struct {
+ u32 ref_frames:5;
+ u32 reserved0:6;
+ } h264;
+
+ struct {
+ u32 ref_frames:5;
+ u32 topfieldfirst_e:1;
+ u32 alt_scan_e:1;
+ u32 reserved0:4;
+ } mpeg;
+
+ struct {
+ u32 pad:11;
+ u32 pic_mb_height_p:8;
+ /* this register is only used by VC-1 */
+ u32 mb_width_off:4;
+ u32 pic_mb_width:9;
+ };
+ } sw120 __attribute__ ((packed));
+
+ u32 sw121;
+
+ struct {
+ u32 frame_pred_dct:1;
+ u32 intra_vlc_tab:1;
+ u32 intra_dc_prec:1;
+ u32 con_mv_e:1;
+ u32 reserved0:19;
+ u32 qscale_type:1;
+ u32 reserved1:1;
+ u32 strm_start_bit:6;
+ } sw122;
+
+ u32 sw123;
+ u32 sw124;
+ u32 sw125;
+ u32 sw126;
+ u32 sw127;
+ u32 sw128;
+ u32 sw129;
+ u32 sw130;
+
+ struct {
+ u32 refer0_topc_e:1;
+ u32 refer0_field_e:1;
+ u32 refer0_base:30;
+ } sw131;
+
+ u32 sw132;
+ u32 sw133;
+
+ struct {
+ u32 refer2_topc_e:1;
+ u32 refer2_field_e:1;
+ u32 refer2_base:30;
+ } sw134;
+
+ struct {
+ u32 refer3_topc_e:1;
+ u32 refer3_field_e:1;
+ u32 refer3_base:30;
+ } sw135;
+
+ struct {
+ u32 reserved0:1;
+ u32 mv_accuracy_bwd:1;
+ u32 mv_accuracy_fwd:1;
+ u32 fcode_bwd_ver:4;
+ u32 fcode_bwd_hor:4;
+ u32 fcode_fwd_ver:4;
+ u32 fcode_fwd_hor:4;
+ u32 alt_scan_flag_e:1;
+ u32 reserved1:12;
+ } sw136;
+
+ u32 sw137;
+ u32 sw138;
+ u32 sw139;
+ u32 sw140;
+ u32 sw141;
+ u32 sw142;
+ u32 sw143;
+ u32 sw144;
+ u32 sw145;
+ u32 sw146;
+ u32 sw147;
+
+ struct {
+ u32 refer1_topc_e:1;
+ u32 refer1_field_e:1;
+ u32 refer1_base:30;
+ } sw148;
+
+ u32 sw149_sw158[10];
+} __attribute__((packed));
+
+#endif
--
2.20.1
Le jeudi 07 mars 2019 à 18:03 +0800, Randy Li a écrit :
> Although I really want to push those work after I added more codec
> supports, but I found it is more urge to do those in v4l2 core framework and
> userspace.
>
> I would use this driver to present the current problems, write down a
> summary here and I would reply to those threads later to push forward.
>
> 1. Slice construction is a bad idea. I think I have said my reason in
> the IRC and mail before, vp9 is always good example.
>
> And it would request the driver to update QP table/CABAC table every
> slice.
>
> I would make something to describe a buffer with some addtional meta
> data.
>
> But the current request API limit a buffer with associated with
> an request buffer, which prevent sharing some sequence data, but it
> still can solve some problems.
I guess you are trying to make some argument here, but I don't really
understand what you are referring to. Right now there is two concurrent
drivers for the Rockchip, yours and Ezequiel. Ezequiel does not seem to
have raised any blockers around any of this (yet).
>
> 2. Advantage DMA memory control.
> I think I need to do some work at v4l2 core.
>
> 2.1 The DMA address of each planes. I have sent a mail before talked
> about why multiple planes is necessary for the rockchip platform. And
> it maybe required by the other platforms.
>
> 2.2 IOMMU resume
> The most effective way to restore the decoder from critical error is
> doing a restting by reset controller.
> Which would leading its slave IOMMU reset at the same time. Then none of
> those v4l2 buffers are mapping in the IOMMU.
You can't invalidate application memory mapping in V4L2, this is
generic to V4L2 interface. If you still need to do this, you'll have to
tell user-space through the SRC_CHANGE event, forcing a reconfiguration
hence a reallocation of the buffers. Then your driver would be
responsible for caching the allocation in order to not introduce
delays. The framework does not prevent you from doing so, but yet, this
is likely difficult.
>
> 3. H.264 and HEVC header
> I still think those structure have some not necessary fileds in dpb or
> reference part, which I don't think hardware decoder would care about
> that or can be predict from the other information.
This was discussed during the review, but all the information in there
exist in the in the slice headers bitstream. There is no reason some
information should not be made available to the driver, used or not.
This is the only way we can guaranty that we won't prevent other HW to
be integrated in the future. This is specially needed for
H264_SLICE_RAW format used by Allwinner. In Ezequiel H264 decoder
patchset for Rockchip HW, the format is H264_SLICE_ANNEX_B, of course
in this format one could always do a bit of parsing in the kernel, but
we don't want that really.
That being said, I'd like to remind that we don't expose the control
publicly yet, these are still unstable. We will freeze these after
drivers get added to mainline and are considered stable.
>
> I would join to talk later.
>
> 4. The work flow of V4L2
> I need a method to prepare the register set before the device acutally
> begin the transaction. Which is necessary for those high frame rate usecase.
Can't you just increase the buffer queue size ? Then prepare the
buffers on application thread, and process on another or something.
This seems like driver specific, not an API thing.
>
> Also it is useful for those device would share some hardware resources
> with the other device and it can save more power.
>
> I think I need to do some work at v4l2 core.
Seems all possible to optimize, imho, we should aim at getting a
working driver into mainline first and then progressively tweak it to
gain best performance. The driver and userspace need complete re-
implementation, and it won't happen all in one pass. I'm sure there is
multiple years of effort, and multiple iterations on the vendor kernel
and userspace. We can learn from that, but my point is that we are not
yet at a stage where we should focus on driver specific optimization.
From what you have said so far, I haven't found anything for which the
kernel interface that is being merged would prevent doing the suggested
optimization. Instead I read you as some of the interface decision will
require a bit more work for this specific driver. This is often the
tradeoff we have to do to make sure we can expose generically usable
interface. And that's likely why mainline drivers are often a tad more
complex then their vendor equivalent.
>
> Randy Li (6):
> arm64: dts: rockchip: add power domain to iommu
> staging: video: rockchip: add v4l2 decoder
> [TEST]: rockchip: mpp: support qptable
> staging: video: rockchip: add video codec
> arm64: dts: rockchip: boost clocks for rk3328
> arm64: dts: rockchip: add video codec for rk3328
>
> .../arm64/boot/dts/rockchip/rk3328-rock64.dts | 32 +
> arch/arm64/boot/dts/rockchip/rk3328.dtsi | 116 +-
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/rockchip-mpp/Kconfig | 34 +
> drivers/staging/rockchip-mpp/Makefile | 10 +
> drivers/staging/rockchip-mpp/mpp_debug.h | 87 ++
> drivers/staging/rockchip-mpp/mpp_dev_common.c | 1367 +++++++++++++++++
> drivers/staging/rockchip-mpp/mpp_dev_common.h | 212 +++
> drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c | 997 ++++++++++++
> drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 619 ++++++++
> drivers/staging/rockchip-mpp/mpp_service.c | 197 +++
> drivers/staging/rockchip-mpp/mpp_service.h | 38 +
> drivers/staging/rockchip-mpp/rkvdec/hal.h | 63 +
> drivers/staging/rockchip-mpp/rkvdec/hevc.c | 166 ++
> drivers/staging/rockchip-mpp/rkvdec/regs.h | 608 ++++++++
> drivers/staging/rockchip-mpp/vdpu2/hal.h | 52 +
> drivers/staging/rockchip-mpp/vdpu2/mpeg2.c | 277 ++++
> drivers/staging/rockchip-mpp/vdpu2/regs.h | 699 +++++++++
> 20 files changed, 5575 insertions(+), 4 deletions(-)
> create mode 100644 drivers/staging/rockchip-mpp/Kconfig
> create mode 100644 drivers/staging/rockchip-mpp/Makefile
> create mode 100644 drivers/staging/rockchip-mpp/mpp_debug.h
> create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.c
> create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.h
> create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
> create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
> create mode 100644 drivers/staging/rockchip-mpp/mpp_service.c
> create mode 100644 drivers/staging/rockchip-mpp/mpp_service.h
> create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hal.h
> create mode 100644 drivers/staging/rockchip-mpp/rkvdec/hevc.c
> create mode 100644 drivers/staging/rockchip-mpp/rkvdec/regs.h
> create mode 100644 drivers/staging/rockchip-mpp/vdpu2/hal.h
> create mode 100644 drivers/staging/rockchip-mpp/vdpu2/mpeg2.c
> create mode 100644 drivers/staging/rockchip-mpp/vdpu2/regs.h
>
On Thu, 2019-03-07 at 18:03 +0800, Randy Li wrote:
> It is based on the vendor driver sent to mail list before.
trivial notes:
> diff --git a/drivers/staging/rockchip-mpp/mpp_debug.h b/drivers/staging/rockchip-mpp/mpp_debug.h
[]
> +#define mpp_debug_func(type, fmt, args...) \
> + do { \
> + if (unlikely(debug & type)) { \
> + pr_info("%s:%d: " fmt, \
> + __func__, __LINE__, ##args); \
> + } \
> + } while (0)
> +#define mpp_debug(type, fmt, args...) \
> + do { \
> + if (unlikely(debug & type)) { \
> + pr_info(fmt, ##args); \
> + } \
> + } while (0)
> +
It's generally better to emit debug messages at KERN_DEBUG
> +#define mpp_debug_enter() \
> + do { \
> + if (unlikely(debug & DEBUG_FUNCTION)) { \
> + pr_info("%s:%d: enter\n", \
> + __func__, __LINE__); \
> + } \
> + } while (0)
> +
> +#define mpp_debug_leave() \
> + do { \
> + if (unlikely(debug & DEBUG_FUNCTION)) { \
> + pr_info("%s:%d: leave\n", \
> + __func__, __LINE__); \
> + } \
> + } while (0)
I suggest removal of these macros and uses.
There's not much value in enter/leave markings as
the generic ftrace facility does this already.
> +
> +#define mpp_err(fmt, args...) \
> + pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
__func__, __LINE__ markings generally have little value.